diff options
Diffstat (limited to 'kcheckpass')
-rw-r--r-- | kcheckpass/CMakeLists.txt | 33 | ||||
-rw-r--r-- | kcheckpass/ConfigureChecks.cmake | 16 | ||||
-rw-r--r-- | kcheckpass/Makefile.am | 26 | ||||
-rw-r--r-- | kcheckpass/README | 51 | ||||
-rw-r--r-- | kcheckpass/checkpass_aix.c | 95 | ||||
-rw-r--r-- | kcheckpass/checkpass_etcpasswd.c | 60 | ||||
-rw-r--r-- | kcheckpass/checkpass_osfc2passwd.c | 203 | ||||
-rw-r--r-- | kcheckpass/checkpass_pam.c | 205 | ||||
-rw-r--r-- | kcheckpass/checkpass_shadow.c | 86 | ||||
-rw-r--r-- | kcheckpass/kcheckpass.c | 448 | ||||
-rw-r--r-- | kcheckpass/kcheckpass.h | 142 |
11 files changed, 1365 insertions, 0 deletions
diff --git a/kcheckpass/CMakeLists.txt b/kcheckpass/CMakeLists.txt new file mode 100644 index 000000000..5e83ee41e --- /dev/null +++ b/kcheckpass/CMakeLists.txt @@ -0,0 +1,33 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include( ConfigureChecks.cmake ) + +if( HAVE_PAM ) + add_definitions( -DHAVE_PAM ) +endif( ) + +include_directories( + ${CMAKE_BINARY_DIR} +) + + +##### kcheckpass (executable) ################### + +tde_add_executable( kcheckpass AUTOMOC + SOURCES + kcheckpass.c checkpass_etcpasswd.c checkpass_pam.c + checkpass_shadow.c checkpass_osfc2passwd.c checkpass_aix.c + LINK tdefakes-shared ${CRYPT_LIBRARY} ${PAM_LIBRARY} + DESTINATION ${BIN_INSTALL_DIR} + SETUID +) + diff --git a/kcheckpass/ConfigureChecks.cmake b/kcheckpass/ConfigureChecks.cmake new file mode 100644 index 000000000..120869dac --- /dev/null +++ b/kcheckpass/ConfigureChecks.cmake @@ -0,0 +1,16 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +find_library( CRYPT_LIBRARY crypt ) + +if( WITH_PAM AND (NOT DEFINED KCHECKPASS_PAM_SERVICE) ) + set( KCHECKPASS_PAM_SERVICE "kde" CACHE INTERNAL "" ) +endif( ) diff --git a/kcheckpass/Makefile.am b/kcheckpass/Makefile.am new file mode 100644 index 000000000..0ccd2174e --- /dev/null +++ b/kcheckpass/Makefile.am @@ -0,0 +1,26 @@ +## Makefile.am for kcheckpass +## written by Christian Esken +## + +INCLUDES= $(KDE_USE_FPIE) $(all_includes) + +bin_PROGRAMS = kcheckpass + +kcheckpass_SOURCES = kcheckpass.c \ + checkpass_etcpasswd.c checkpass_pam.c checkpass_shadow.c \ + checkpass_osfc2passwd.c checkpass_aix.c +kcheckpass_LDADD = -ltdefakes $(PASSWDLIBS) $(LIBSOCKET) +kcheckpass_LDFLAGS = $(KDE_USE_PIE) $(all_libraries) + +noinst_HEADERS = kcheckpass.h + +EXTRA_DIST = README + +PAM = $(KCHECKPASS_PAM_SERVICE) + +install-data-local: + -@test -n "$(DESTDIR)" || test -z "$(PAM)" || $(top_srcdir)/mkpamserv $(PAM) + +install-exec-hook: + @(chown 0 $(DESTDIR)$(bindir)/kcheckpass && chmod 4755 $(DESTDIR)$(bindir)/kcheckpass) \ + || echo "Error: Could not install kcheckpass as setuid root (possibly you won't be able to unlock)!!" diff --git a/kcheckpass/README b/kcheckpass/README new file mode 100644 index 000000000..181c4c531 --- /dev/null +++ b/kcheckpass/README @@ -0,0 +1,51 @@ +The KCheckPass authentication software: +----------------------------------------- + +KCheckPass is KDE's authentication program. It is meant to be +used by any software in need of user authentication, most +notably screensavers. + +It enhances security be the following means: + +- It's only a small program, which is hopefully simple enough to + allow it to be SUID root. Setting it to SUID root is necessary + on Shadow Password systems. +- No other program in need of user authentication, must be + SUID root. +- It provides a single implementation to check passwords. So one + only must take a closer look at KCheckPass to ensure password + security. It's much easier for programs using KCheckPass to + preserve security. + + +Technique: +---------- +KCheckPass is a simple password checker. Just invoke and +send it the password on stdin. + +If the password was accepted, the program exits with 0; +if it was rejected, it exits with 1. Any other exit +code signals an error. + + + +Compilation hints: +------------------ +Compile with -DHAVE_VSYSLOG if you have vsyslog(). +Compile with -DHAVE_PAM if you have a PAM system, and link with -lpam -ldl + (If libdl is present). +Compile with -DHAVE_SHADOW if you have a shadow password system. + +Copyright, Author and License notice: +------------------------------------- +Copyright (C) 1998, Caldera, Inc. +Released under the GNU General Public License + +Olaf Kirch <okir@caldera.de> General Framework and PAM support +Christian Esken <esken@kde.org> Shadow and /etc/passwd support +Oswald Buddenhagen <ossi@kde.org> Binary conversation interface, etc. + +Some parts were taken from tdescreensaver's passwd.cpp + +Currently this software is maintained by Oswald Buddenhagen <ossi@kde.org>. +Please send new authentication modules (checkpass_*.c) to me. diff --git a/kcheckpass/checkpass_aix.c b/kcheckpass/checkpass_aix.c new file mode 100644 index 000000000..8f06cfe67 --- /dev/null +++ b/kcheckpass/checkpass_aix.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2001 Reza Arbab <arbab@austin.ibm.com> + * Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "kcheckpass.h" + +#ifdef HAVE_AIX_AUTH +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/* + * The AIX builtin authenticate() uses whichever method the system + * has been configured for. (/etc/passwd, DCE, etc.) + */ +int authenticate(const char *, const char *, int *, char **); + +AuthReturn Authenticate(const char *method, + const char *login, char *(*conv) (ConvRequest, const char *)) +{ + int result; + int reenter; /* Tells if authenticate is done processing or not. */ + char *passwd; + char *msg; /* Contains a prompt message or failure reason. */ + + if (!strcmp(method, "classic")) { + + if (!(passwd = conv(ConvGetHidden, 0))) + return AuthAbort; + + if ((result = authenticate(login, passwd, &reenter, &msg))) { + if (msg) { + conv(ConvPutError, msg); + free(msg); + } + dispose(passwd); + return AuthBad; + } + if (reenter) { + char buf[256]; + snprintf(buf, sizeof(buf), "More authentication data requested: %s\n", msg); + conv(ConvPutError, buf); + free(msg); + dispose(passwd); + return result == ENOENT || result == ESAD ? AuthBad : AuthError; + } + dispose(passwd); + return AuthOk; + + } else if (!strcmp(method, "generic")) { + + for (passwd = 0;;) { + if ((result = authenticate(login, passwd, &reenter, &msg))) { + if (msg) { + conv(ConvPutError, msg); + free(msg); + } + if (passwd) + dispose(passwd); + return result == ENOENT || result == ESAD ? AuthBad : AuthError; + } + if (passwd) + dispose(passwd); + if (!reenter) + break; + passwd = conv(ConvGetHidden, msg); + free(msg); + if (!passwd) + return AuthAbort; + } + return AuthOk; + + } else + return AuthError; + +} + +#endif diff --git a/kcheckpass/checkpass_etcpasswd.c b/kcheckpass/checkpass_etcpasswd.c new file mode 100644 index 000000000..1dbe06f70 --- /dev/null +++ b/kcheckpass/checkpass_etcpasswd.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1998 Christian Esken <esken@kde.org> + * Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) 1998, Christian Esken <esken@kde.org> + */ + +#include "kcheckpass.h" + +#ifdef HAVE_ETCPASSWD + +/******************************************************************* + * This is the authentication code for /etc/passwd passwords + *******************************************************************/ + +#include <string.h> +#include <stdlib.h> + +AuthReturn Authenticate(const char *method, + const char *login, char *(*conv) (ConvRequest, const char *)) +{ + struct passwd *pw; + char *passwd; + + if (strcmp(method, "classic")) + return AuthError; + + /* Get the password entry for the user we want */ + if (!(pw = getpwnam(login))) + return AuthBad; + + if (!*pw->pw_passwd) + return AuthOk; + + if (!(passwd = conv(ConvGetHidden, 0))) + return AuthAbort; + + if (!strcmp(pw->pw_passwd, crypt(passwd, pw->pw_passwd))) { + dispose(passwd); + return AuthOk; /* Success */ + } + dispose(passwd); + return AuthBad; /* Password wrong or account locked */ +} + +#endif diff --git a/kcheckpass/checkpass_osfc2passwd.c b/kcheckpass/checkpass_osfc2passwd.c new file mode 100644 index 000000000..d7663bdcc --- /dev/null +++ b/kcheckpass/checkpass_osfc2passwd.c @@ -0,0 +1,203 @@ +/* + * + * Copyright (C) 1999 Mark Davies <mark@MCS.VUW.AC.NZ> + * Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "kcheckpass.h" + +#ifdef HAVE_OSF_C2_PASSWD + +static char *osf1c2crypt(const char *pw, char *salt); +static int osf1c2_getprpwent(char *p, char *n, int len); + +/******************************************************************* + * This is the authentication code for OSF C2 security passwords + *******************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +AuthReturn Authenticate(const char *method, + const char *login, char *(*conv) (ConvRequest, const char *)) +{ + char *passwd; + char c2passwd[256]; + + if (strcmp(method, "classic")) + return AuthError; + + if (!osf1c2_getprpwent(c2passwd, login, sizeof(c2passwd))) + return AuthBad; + + if (!*c2passwd) + return AuthOk; + + if (!(passwd = conv(ConvGetHidden, 0))) + return AuthAbort; + + if (!strcmp(c2passwd, osf1c2crypt(passwd, c2passwd))) { + dispose(passwd); + return AuthOk; /* Success */ + } + dispose(passwd); + return AuthBad; /* Password wrong or account locked */ +} + + +/* +The following code was lifted from the file osfc2.c from the ssh 1.2.26 +distribution. Parts of the code that were not needed by kcheckpass +(notably the osf1c2_check_account_and_terminal() function and the code +to set the external variable days_before_password_expires have been +removed). The original copyright from the osfc2.c file is included +below. +*/ + +/* + +osfc2.c + +Author: Christophe Wolfhugel + +Copyright (c) 1995 Christophe Wolfhugel + +Free use of this file is permitted for any purpose as long as +this copyright is preserved in the header. + +This program implements the use of the OSF/1 C2 security extensions +within ssh. See the file COPYING for full licensing informations. + +*/ + +#include <sys/security.h> +#include <prot.h> +#include <sia.h> + +static int c2security = -1; +static int crypt_algo; + +static void +initialize_osf_security(int ac, char **av) +{ + FILE *f; + char buf[256]; + char siad[] = "siad_ses_init="; + + if (access(SIAIGOODFILE, F_OK) == -1) + { + /* Broken OSF/1 system, better don't run on it. */ + fprintf(stderr, SIAIGOODFILE); + fprintf(stderr, " does not exist. Your OSF/1 system is probably broken\n"); + exit(1); + } + if ((f = fopen(MATRIX_CONF, "r")) == NULL) + { + /* Another way OSF/1 is probably broken. */ + fprintf(stderr, "%s unreadable. Your OSF/1 system is probably broken.\n" + + MATRIX_CONF); + exit(1); + } + + /* Read matrix.conf to check if we run C2 or not */ + while (fgets(buf, sizeof(buf), f) != NULL) + { + if (strncmp(buf, siad, sizeof(siad) - 1) == 0) + { + if (strstr(buf, "OSFC2") != NULL) + c2security = 1; + else if (strstr(buf, "BSD") != NULL) + c2security = 0; + break; + } + } + fclose(f); + if (c2security == -1) + { + fprintf(stderr, "C2 security initialization failed : could not determine security level.\n"); + exit(1); + } + if (c2security == 1) + set_auth_parameters(ac, av); +} + + +static int +osf1c2_getprpwent(char *p, char *n, int len) +{ + time_t pschg, tnow; + + if (c2security == 1) + { + struct es_passwd *es; + struct pr_passwd *pr = getprpwnam(n); + if (pr) + { + strlcpy(p, pr->ufld.fd_encrypt, len); + crypt_algo = pr->ufld.fd_oldcrypt; + + tnow = time(NULL); + if (pr->uflg.fg_schange == 1) + pschg = pr->ufld.fd_schange; + else + pschg = 0; + if (pr->uflg.fg_template == 0) + { + /** default template, system values **/ + if (pr->sflg.fg_lifetime == 1) + if (pr->sfld.fd_lifetime > 0 && + pschg + pr->sfld.fd_lifetime < tnow) + return 1; + } + else /** user template, specific values **/ + { + es = getespwnam(pr->ufld.fd_template); + if (es) + { + if (es->uflg->fg_expire == 1) + if (es->ufld->fd_expire > 0 && + pschg + es->ufld->fd_expire < tnow) + return 1; + } + } + } + } + else + { + struct passwd *pw = getpwnam(n); + if (pw) + { + strlcpy(p, pw->pw_passwd, len); + return 1; + } + } + return 0; +} + +static char * +osf1c2crypt(const char *pw, char *salt) +{ + if (c2security == 1) { + return(dispcrypt(pw, salt, crypt_algo)); + } else + return(crypt(pw, salt)); +} + +#endif diff --git a/kcheckpass/checkpass_pam.c b/kcheckpass/checkpass_pam.c new file mode 100644 index 000000000..7a35eeddf --- /dev/null +++ b/kcheckpass/checkpass_pam.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 1998 Caldera, Inc. + * Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcheckpass.h" + +#ifdef HAVE_PAM + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#ifdef HAVE_PAM_PAM_APPL_H +#include <pam/pam_appl.h> +#else +#include <security/pam_appl.h> +#endif + +struct pam_data { + char *(*conv) (ConvRequest, const char *); + int abort:1; + int classic:1; +}; + +#ifdef PAM_MESSAGE_NONCONST +typedef struct pam_message pam_message_type; +typedef void *pam_gi_type; +#else +typedef const struct pam_message pam_message_type; +typedef const void *pam_gi_type; +#endif + +static int +PAM_conv (int num_msg, pam_message_type **msg, + struct pam_response **resp, + void *appdata_ptr) +{ + int count; + struct pam_response *repl; + struct pam_data *pd = (struct pam_data *)appdata_ptr; + + if (!(repl = calloc(num_msg, sizeof(struct pam_response)))) + return PAM_CONV_ERR; + + for (count = 0; count < num_msg; count++) + switch (msg[count]->msg_style) { + case PAM_TEXT_INFO: + pd->conv(ConvPutInfo, msg[count]->msg); + break; + case PAM_ERROR_MSG: + pd->conv(ConvPutError, msg[count]->msg); + break; + default: + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + repl[count].resp = pd->conv(ConvGetNormal, msg[count]->msg); + break; + case PAM_PROMPT_ECHO_OFF: + repl[count].resp = + pd->conv(ConvGetHidden, pd->classic ? 0 : msg[count]->msg); + break; +#ifdef PAM_BINARY_PROMPT + case PAM_BINARY_PROMPT: + repl[count].resp = pd->conv(ConvGetBinary, msg[count]->msg); + break; +#endif + default: + /* Must be an error of some sort... */ + goto conv_err; + } + if (!repl[count].resp) { + pd->abort = 1; + goto conv_err; + } + repl[count].resp_retcode = PAM_SUCCESS; + break; + } + *resp = repl; + return PAM_SUCCESS; + + conv_err: + for (; count >= 0; count--) + if (repl[count].resp) + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + dispose(repl[count].resp); + break; +#ifdef PAM_BINARY_PROMPT + case PAM_BINARY_PROMPT: /* handle differently? */ +#endif + case PAM_PROMPT_ECHO_ON: + free(repl[count].resp); + break; + } + free(repl); + return PAM_CONV_ERR; +} + +static struct pam_data PAM_data; + +static struct pam_conv PAM_conversation = { + &PAM_conv, + &PAM_data +}; + +#ifdef PAM_FAIL_DELAY +static void +fail_delay(int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED, + void *appdata_ptr ATTR_UNUSED) +{} +#endif + + +AuthReturn Authenticate(const char *caller, const char *method, + const char *user, char *(*conv) (ConvRequest, const char *)) +{ + const char *tty; + pam_handle_t *pamh; + pam_gi_type pam_item; + const char *pam_service; + char pservb[64]; + int pam_error; + + openlog("kcheckpass", LOG_PID, LOG_AUTH); + + PAM_data.conv = conv; + if (!strcmp(method, "classic")) { + PAM_data.classic = 1; + pam_service = caller; + } + else if (!strcmp(method, "pam")) { + pam_service = caller; + } else { + sprintf(pservb, "%.31s-%.31s", caller, method); + pam_service = pservb; + } + pam_error = pam_start(pam_service, user, &PAM_conversation, &pamh); + if (pam_error != PAM_SUCCESS) + return AuthError; + + tty = ttyname(0); + if (!tty) + tty = getenv ("DISPLAY"); + + pam_error = pam_set_item (pamh, PAM_TTY, tty); + if (pam_error != PAM_SUCCESS) { + pam_end(pamh, pam_error); + return AuthError; + } + +# ifdef PAM_FAIL_DELAY + pam_set_item (pamh, PAM_FAIL_DELAY, (void *)fail_delay); +# endif + + pam_error = pam_authenticate(pamh, 0); + if (pam_error != PAM_SUCCESS) { + pam_end(pamh, pam_error); + switch (pam_error) { + case PAM_USER_UNKNOWN: + case PAM_AUTH_ERR: + case PAM_MAXTRIES: /* should handle this better ... */ + case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */ + return AuthBad; + case PAM_SERVICE_ERR: + return AuthError; + default: + return AuthError; + } + } + + /* just in case some module is stupid enough to ignore a preset PAM_USER */ + pam_error = pam_get_item (pamh, PAM_USER, &pam_item); + if (pam_error != PAM_SUCCESS) { + pam_end(pamh, pam_error); + return AuthError; + } + if (strcmp((const char *)pam_item, user)) { + pam_end(pamh, PAM_SUCCESS); /* maybe use PAM_AUTH_ERR? */ + return AuthBad; + } + + pam_error = pam_setcred(pamh, PAM_REFRESH_CRED); + /* ignore errors on refresh credentials. If this did not work we use the old once. */ + + pam_end(pamh, PAM_SUCCESS); + return AuthOk; +} + +#endif diff --git a/kcheckpass/checkpass_shadow.c b/kcheckpass/checkpass_shadow.c new file mode 100644 index 000000000..ec3a4e02a --- /dev/null +++ b/kcheckpass/checkpass_shadow.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 1998 Christian Esken <esken@kde.org> + * Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org> + * + * This is a modified version of checkpass_shadow.cpp + * + * Modifications made by Thorsten Kukuk <kukuk@suse.de> + * Mathias Kettner <kettner@suse.de> + * + * ------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kcheckpass.h" + +/******************************************************************* + * This is the authentication code for Shadow-Passwords + *******************************************************************/ + +#ifdef HAVE_SHADOW +#include <string.h> +#include <stdlib.h> +#include <pwd.h> + +#ifndef __hpux +#include <shadow.h> +#endif + +AuthReturn Authenticate(const char *method, + const char *login, char *(*conv) (ConvRequest, const char *)) +{ + char *typed_in_password; + char *crpt_passwd; + char *password; + struct passwd *pw; + struct spwd *spw; + + if (strcmp(method, "classic")) + return AuthError; + + if (!(pw = getpwnam(login))) + return AuthAbort; + + spw = getspnam(login); + password = spw ? spw->sp_pwdp : pw->pw_passwd; + + if (!*password) + return AuthOk; + + if (!(typed_in_password = conv(ConvGetHidden, 0))) + return AuthAbort; + +#if defined( __linux__ ) && defined( HAVE_PW_ENCRYPT ) + crpt_passwd = pw_encrypt(typed_in_password, password); /* (1) */ +#else + crpt_passwd = crypt(typed_in_password, password); +#endif + + if (!strcmp(password, crpt_passwd )) { + dispose(typed_in_password); + return AuthOk; /* Success */ + } + dispose(typed_in_password); + return AuthBad; /* Password wrong or account locked */ +} + +/* + (1) Deprecated - long passwords have known weaknesses. Also, + pw_encrypt is non-standard (requires libshadow.a) while + everything else you need to support shadow passwords is in + the standard (ELF) libc. + */ +#endif diff --git a/kcheckpass/kcheckpass.c b/kcheckpass/kcheckpass.c new file mode 100644 index 000000000..9b6b7abd7 --- /dev/null +++ b/kcheckpass/kcheckpass.c @@ -0,0 +1,448 @@ +/***************************************************************** + * + * kcheckpass - Simple password checker + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + * kcheckpass is a simple password checker. Just invoke and + * send it the password on stdin. + * + * If the password was accepted, the program exits with 0; + * if it was rejected, it exits with 1. Any other exit + * code signals an error. + * + * It's hopefully simple enough to allow it to be setuid + * root. + * + * Compile with -DHAVE_VSYSLOG if you have vsyslog(). + * Compile with -DHAVE_PAM if you have a PAM system, + * and link with -lpam -ldl. + * Compile with -DHAVE_SHADOW if you have a shadow + * password system. + * + * Copyright (C) 1998, Caldera, Inc. + * Released under the GNU General Public License + * + * Olaf Kirch <okir@caldera.de> General Framework and PAM support + * Christian Esken <esken@kde.org> Shadow and /etc/passwd support + * Roberto Teixeira <maragato@kde.org> other user (-U) support + * Oswald Buddenhagen <ossi@kde.org> Binary server mode + * + * Other parts were taken from tdescreensaver's passwd.cpp. + * + *****************************************************************/ + +#include "kcheckpass.h" + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <syslog.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> + +/* Compatibility: accept some options from environment variables */ +#define ACCEPT_ENV + +#define THROTTLE 3 + +static int havetty, sfd = -1, nullpass; + +static char * +conv_legacy (ConvRequest what, const char *prompt) +{ + char *p, *p2; + int len; + char buf[1024]; + + switch (what) { + case ConvGetBinary: + break; + case ConvGetNormal: + /* there is no prompt == 0 case */ + if (!havetty) + break; + /* i guess we should use /dev/tty ... */ + fputs(prompt, stdout); + fflush(stdout); + if (!fgets(buf, sizeof(buf), stdin)) + return 0; + len = strlen(buf); + if (len && buf[len - 1] == '\n') + buf[--len] = 0; + return strdup(buf); + case ConvGetHidden: + if (havetty) { +#ifdef HAVE_GETPASSPHRASE + p = getpassphrase(prompt ? prompt : "Password: "); +#else + p = getpass(prompt ? prompt : "Password: "); +#endif + p2 = strdup(p); + memset(p, 0, strlen(p)); + return p2; + } else { + if (prompt) + break; + if ((len = read(0, buf, sizeof(buf) - 1)) < 0) { + message("Cannot read password\n"); + return 0; + } else { + if (len && buf[len - 1] == '\n') + --len; + buf[len] = 0; + p2 = strdup(buf); + memset(buf, 0, len); + return p2; + } + } + case ConvPutInfo: + message("Information: %s\n", prompt); + return 0; + case ConvPutError: + message("Error: %s\n", prompt); + return 0; + } + message("Authentication backend requested data type which cannot be handled.\n"); + return 0; +} + + +static int +Reader (void *buf, int count) +{ + int ret, rlen; + + for (rlen = 0; rlen < count; ) { + dord: + ret = read (sfd, (void *)((char *)buf + rlen), count - rlen); + if (ret < 0) { + if (errno == EINTR) + goto dord; + if (errno == EAGAIN) + break; + return -1; + } + if (!ret) + break; + rlen += ret; + } + return rlen; +} + +static void +GRead (void *buf, int count) +{ + if (Reader (buf, count) != count) { + message ("Communication breakdown on read\n"); + exit(15); + } +} + +static void +GWrite (const void *buf, int count) +{ + if (write (sfd, buf, count) != count) { + message ("Communication breakdown on write\n"); + exit(15); + } +} + +static void +GSendInt (int val) +{ + GWrite (&val, sizeof(val)); +} + +static void +GSendStr (const char *buf) +{ + unsigned len = buf ? strlen (buf) + 1 : 0; + GWrite (&len, sizeof(len)); + GWrite (buf, len); +} + +static void +GSendArr (int len, const char *buf) +{ + GWrite (&len, sizeof(len)); + GWrite (buf, len); +} + +static int +GRecvInt (void) +{ + int val; + + GRead (&val, sizeof(val)); + return val; +} + +static char * +GRecvStr (void) +{ + unsigned len; + char *buf; + + if (!(len = GRecvInt())) + return (char *)0; + if (len > 0x1000 || !(buf = malloc (len))) { + message ("No memory for read buffer\n"); + exit(15); + } + GRead (buf, len); + buf[len - 1] = 0; /* we're setuid ... don't trust "them" */ + return buf; +} + +static char * +GRecvArr (void) +{ + unsigned len; + char *arr; + + if (!(len = (unsigned) GRecvInt())) + return (char *)0; + if (len > 0x10000 || !(arr = malloc (len))) { + message ("No memory for read buffer\n"); + exit(15); + } + GRead (arr, len); + return arr; +} + + +static char * +conv_server (ConvRequest what, const char *prompt) +{ + GSendInt (what); + switch (what) { + case ConvGetBinary: + { + unsigned const char *up = (unsigned const char *)prompt; + int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24); + GSendArr (len, prompt); + return GRecvArr (); + } + case ConvGetNormal: + case ConvGetHidden: + { + char *msg; + GSendStr (prompt); + msg = GRecvStr (); + if (msg && (GRecvInt() & IsPassword) && !*msg) + nullpass = 1; + return msg; + } + case ConvPutInfo: + case ConvPutError: + default: + GSendStr (prompt); + return 0; + } +} + +void +message(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +#ifndef O_NOFOLLOW +# define O_NOFOLLOW 0 +#endif + +static void ATTR_NORETURN +usage(int exitval) +{ + message( + "usage: kcheckpass {-h|[-c caller] [-m method] [-U username|-S handle]}\n" + " options:\n" + " -h this help message\n" + " -U username authenticate the specified user instead of current user\n" + " -S handle operate in binary server mode on file descriptor handle\n" + " -c caller the calling application, effectively the PAM service basename\n" + " -m method use the specified authentication method (default: \"classic\")\n" + " exit codes:\n" + " 0 success\n" + " 1 invalid password\n" + " 2 cannot read password database\n" + " Anything else tells you something's badly hosed.\n" + ); + exit(exitval); +} + +int +main(int argc, char **argv) +{ +#ifdef HAVE_PAM + const char *caller = KCHECKPASS_PAM_SERVICE; +#endif + const char *method = "classic"; + const char *username = 0; +#ifdef ACCEPT_ENV + char *p; +#endif + struct passwd *pw; + int c, nfd, lfd; + uid_t uid; + time_t nexttime; + AuthReturn ret; + struct flock lk; + char fname[64], fcont[64]; + +#ifdef HAVE_OSF_C2_PASSWD + initialize_osf_security(argc, argv); +#endif + + /* Make sure stdout/stderr are open */ + for (c = 1; c <= 2; c++) { + if (fcntl(c, F_GETFL) == -1) { + if ((nfd = open("/dev/null", O_WRONLY)) < 0) { + message("cannot open /dev/null: %s\n", strerror(errno)); + exit(10); + } + if (c != nfd) { + dup2(nfd, c); + close(nfd); + } + } + } + + havetty = isatty(0); + + while ((c = getopt(argc, argv, "hc:m:U:S:")) != -1) { + switch (c) { + case 'h': + usage(0); + break; + case 'c': +#ifdef HAVE_PAM + caller = optarg; +#endif + break; + case 'm': + method = optarg; + break; + case 'U': + username = optarg; + break; + case 'S': + sfd = atoi(optarg); + break; + default: + message("Command line option parsing error\n"); + usage(10); + } + } + +#ifdef ACCEPT_ENV +# ifdef HAVE_PAM + if ((p = getenv("TDE_PAM_ACTION"))) + caller = p; +# endif + if ((p = getenv("TCHECKPASS_USER"))) + username = p; +#endif + + uid = getuid(); + if (!username) { + if (!(p = getenv("LOGNAME")) || !(pw = getpwnam(p)) || pw->pw_uid != uid) + if (!(p = getenv("USER")) || !(pw = getpwnam(p)) || pw->pw_uid != uid) + if (!(pw = getpwuid(uid))) { + message("Cannot determinate current user\n"); + return AuthError; + } + if (!(username = strdup(pw->pw_name))) { + message("Out of memory\n"); + return AuthError; + } + } + + /* + * Throttle kcheckpass invocations to avoid abusing it for bruteforcing + * the password. This delay belongs to the *previous* invocation, where + * we can't enforce it reliably (without risking giving away the result + * before it is due). We don't differentiate between success and failure - + * it's not expected to have a noticable adverse effect. + */ + if ( uid != geteuid() ) { + sprintf(fname, "/var/run/kcheckpass.%d", uid); + if ((lfd = open(fname, O_RDWR | O_CREAT | O_NOFOLLOW, 0600)) < 0) { + message("Cannot open lockfile\n"); + return AuthError; + } + + lk.l_type = F_WRLCK; + lk.l_whence = SEEK_SET; + lk.l_start = lk.l_len = 0; + if (fcntl(lfd, F_SETLKW, &lk)) { + message("Cannot obtain lock\n"); + return AuthError; + } + + if ((c = read(lfd, fcont, sizeof(fcont)-1)) > 0 && + (fcont[c] = '\0', sscanf(fcont, "%ld", &nexttime) == 1)) + { + time_t ct = time(0); + if (nexttime > ct && nexttime < ct + THROTTLE) + sleep(nexttime - ct); + } + + lseek(lfd, 0, SEEK_SET); + write(lfd, fcont, sprintf(fcont, "%lu\n", time(0) + THROTTLE)); + + close(lfd); + } + + /* Now do the fandango */ + ret = Authenticate( +#ifdef HAVE_PAM + caller, +#endif + method, + username, + sfd < 0 ? conv_legacy : conv_server); + + if (ret == AuthBad) { + message("Authentication failure\n"); + if (!nullpass) { + openlog("kcheckpass", LOG_PID, LOG_AUTH); + syslog(LOG_NOTICE, "Authentication failure for %s (invoked by uid %d)", username, uid); + } + } + + return ret; +} + +void +dispose(char *str) +{ + memset(str, 0, strlen(str)); + free(str); +} + +/***************************************************************** + The real authentication methods are in separate source files. + Look in checkpass_*.c +*****************************************************************/ diff --git a/kcheckpass/kcheckpass.h b/kcheckpass/kcheckpass.h new file mode 100644 index 000000000..e1351375a --- /dev/null +++ b/kcheckpass/kcheckpass.h @@ -0,0 +1,142 @@ +/***************************************************************** + * + * kcheckpass + * + * Simple password checker. Just invoke and send it + * the password on stdin. + * + * If the password was accepted, the program exits with 0; + * if it was rejected, it exits with 1. Any other exit + * code signals an error. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) 1998, Caldera, Inc. + * Released under the GNU General Public License + * + * Olaf Kirch <okir@caldera.de> General Framework and PAM support + * Christian Esken <esken@kde.org> Shadow and /etc/passwd support + * Oswald Buddenhagen <ossi@kde.org> Binary server mode + * + * Other parts were taken from tdescreensaver's passwd.cpp + *****************************************************************/ + +#ifndef KCHECKPASS_H_ +#define KCHECKPASS_H_ + +#include <config.h> + +#ifdef HAVE_CRYPT_H +#include <crypt.h> +#endif + +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +#include <pwd.h> +#include <sys/types.h> + +#ifndef _PATH_TMP +#define _PATH_TMP "/tmp/" +#endif + + +#ifdef ultrix +#include <auth.h> +#endif + +#include <unistd.h> + +#ifdef OSF1_ENH_SEC +#include <sys/security.h> +#include <prot.h> +#endif + +/* Make sure there is only one! */ +#if defined(HAVE_PAM) +# undef HAVE_OSF_C2_PASSWD +# undef HAVE_SHADOW +#elif defined(HAVE_OSF_C2_PASSWD) +# undef HAVE_SHADOW +#elif defined(_AIX) +# define HAVE_AIX_AUTH +# undef HAVE_SHADOW +#elif !defined(HAVE_SHADOW) +# define HAVE_ETCPASSWD +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define ATTR_UNUSED __attribute__((unused)) +# define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) +#else +# define ATTR_UNUSED +# define ATTR_NORETURN +# define ATTR_PRINTFLIKE(fmt,var) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* these must match kcheckpass' exit codes */ +typedef enum { + AuthOk = 0, + AuthBad = 1, + AuthError = 2, + AuthAbort = 3 +} AuthReturn; + +typedef enum { + ConvGetBinary, + ConvGetNormal, + ConvGetHidden, + ConvPutInfo, + ConvPutError +} ConvRequest; + +/* these must match the defs in kgreeterplugin.h */ +typedef enum { + IsUser = 1, /* unused in kcheckpass */ + IsPassword = 2 +} DataTag; + +/***************************************************************** + * Authenticates user + *****************************************************************/ +AuthReturn Authenticate( +#ifdef HAVE_PAM + const char *caller, +#endif + const char *method, + const char *user, + char *(*conv) (ConvRequest, const char *)); + +/***************************************************************** + * Output a message to stderr + *****************************************************************/ +void message(const char *, ...) ATTR_PRINTFLIKE(1, 2); + +/***************************************************************** + * Overwrite and free the passed string + *****************************************************************/ +void dispose(char *); + +#ifdef __cplusplus +} +#endif +#endif |