From 3f42e813b61a2492f5b58d514aacf459f0799cdf Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Wed, 6 Oct 2004 13:42:36 +0000 Subject: Relevant BUGIDs: Purpose of commit: Commit summary: --------------- bugfix: Last part of fixes from Red Hat --- modules/pam_limits/pam_limits.c | 4 +- modules/pam_unix/pam_unix_acct.c | 5 +- modules/pam_unix/pam_unix_passwd.c | 50 ++++++++-- modules/pam_unix/support.c | 194 +++++++++++++++++++++++++++++++++++-- modules/pam_unix/support.h | 8 +- modules/pam_unix/unix_chkpwd.c | 37 +++++-- 6 files changed, 266 insertions(+), 32 deletions(-) (limited to 'modules') diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c index 4354d3e1..66eae8e9 100644 --- a/modules/pam_limits/pam_limits.c +++ b/modules/pam_limits/pam_limits.c @@ -207,12 +207,12 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, continue; } } - if (++count > limit) { + if (++count >= limit) { break; } } endutent(); - if (count > limit) { + if (count >= limit) { if (name) { _pam_log(LOG_WARNING, "Too many logins (max %d) for %s", limit, name); diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c index f87b13b1..2cd26792 100644 --- a/modules/pam_unix/pam_unix_acct.c +++ b/modules/pam_unix/pam_unix_acct.c @@ -123,11 +123,10 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, setreuid( -1, save_euid ); } - } else if (!strcmp( pwent->pw_passwd, "x" )) { + } else if (_unix_shadowed (pwent)) spent = _pammodutil_getspnam (pamh, uname); - } else { + else return PAM_SUCCESS; - } if (!spent) if (on(UNIX_BROKEN_SHADOW,ctrl)) diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c index 91625c61..4e90b764 100644 --- a/modules/pam_unix/pam_unix_passwd.c +++ b/modules/pam_unix/pam_unix_passwd.c @@ -444,7 +444,7 @@ static int _update_passwd(pam_handle_t *pamh, } } -static int _update_shadow(const char *forwho, char *towhat) +static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat) { struct spwd *spwdent = NULL, *stmpent = NULL; struct stat st; @@ -516,6 +516,7 @@ static int _update_shadow(const char *forwho, char *towhat) if (!err) { rename(SH_TMPFILE, "/etc/shadow"); + _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho); return PAM_SUCCESS; } else { unlink(SH_TMPFILE); @@ -535,7 +536,7 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat, if (pwd == NULL) return PAM_AUTHTOK_ERR; - if (on(UNIX_NIS, ctrl)) { + if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) { struct timeval timeout; struct yppasswd yppwd; CLIENT *clnt; @@ -619,13 +620,18 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat, } #endif /* def USE_LCKPWDF */ - if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) { - retval = _update_shadow(forwho, towhat); + if (_unix_comesfromsource (pamh, forwho, 1, 0)) + { + if (on(UNIX_SHADOW, ctrl) || _unix_shadowed (pwd)) + { + retval = _update_shadow (pamh, forwho, towhat); if (retval == PAM_SUCCESS) - retval = _update_passwd(pamh, forwho, "x"); - } else { - retval = _update_passwd(pamh, forwho, towhat); - } + if (!_unix_shadowed (pwd)) + retval = _update_passwd (pamh, forwho, "x"); + } + else + retval = _update_passwd (pamh, forwho, towhat); + } #ifdef USE_LCKPWDF ulckpwdf(); @@ -646,7 +652,7 @@ static int _unix_verify_shadow(const char *user, unsigned int ctrl) if (pwd == NULL) return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */ - if (strcmp(pwd->pw_passwd, "x") == 0) { + if (_unix_shadowed(pwd)) { /* ...and shadow password file entry for this user, if shadowing is enabled */ setspent(); @@ -738,7 +744,7 @@ static int _pam_unix_approve_pass(pam_handle_t * pamh #else if (strlen(pass_new) < 6) remark = "You must choose a longer password"; - D(("lenth check [%s]", remark)); + D(("length check [%s]", remark)); #endif if (on(UNIX_REMEMBER_PASSWD, ctrl)) if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS) @@ -795,6 +801,30 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, D(("Got username of %s", user)); + /* + * Before we do anything else, check to make sure that the user's + * info is in one of the databases we can modify from this module, + * which currently is 'files' and 'nis'. We have to do this because + * getpwnam() doesn't tell you *where* the information it gives you + * came from, nor should it. That's our job. + */ + if (_unix_comesfromsource(pamh, user, 1, 1) == 0) { + _log_err(LOG_DEBUG, pamh, + "user \"%s\" does not exist in /etc/passwd or NIS", + user); + return PAM_USER_UNKNOWN; + } else { + struct passwd *pwd; + _unix_getpwnam(pamh, user, 1, 1, &pwd); + if (!_unix_shadowed(pwd) && + (strchr(pwd->pw_passwd, '*') != NULL)) { + _log_err(LOG_DEBUG, pamh, + "user \"%s\" does not have modifiable password", + user); + return PAM_USER_UNKNOWN; + } + } + /* * This is not an AUTH module! */ diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c index 05c51fed..5b23b8e9 100644 --- a/modules/pam_unix/support.c +++ b/modules/pam_unix/support.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -135,10 +138,6 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc, D(("PRELIM_CHECK")); set(UNIX__PRELIM, ctrl); } - if (flags & PAM_DISALLOW_NULL_AUTHTOK) { - D(("DISALLOW_NULL_AUTHTOK")); - set(UNIX__NONULL, ctrl); - } if (flags & PAM_SILENT) { D(("SILENT")); set(UNIX__QUIET, ctrl); @@ -178,6 +177,11 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc, ++argv; /* step to next argument */ } + if (flags & PAM_DISALLOW_NULL_AUTHTOK) { + D(("DISALLOW_NULL_AUTHTOK")); + set(UNIX__NONULL, ctrl); + } + /* auditing is a more sensitive version of debug */ if (on(UNIX_AUDIT, ctrl)) { @@ -275,6 +279,165 @@ static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err) } } +/* + * _unix_getpwnam() searches only /etc/passwd and NIS to find user information + */ +static void _unix_cleanup(pam_handle_t *pamh, void *data, int error_status) +{ + free(data); +} + +int _unix_getpwnam(pam_handle_t *pamh, const char *name, + int files, int nis, struct passwd **ret) +{ + FILE *passwd; + char buf[16384]; + int matched = 0, buflen; + char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p; + + memset(buf, 0, sizeof(buf)); + + if (!matched && files) { + int userlen = strlen(name); + passwd = fopen("/etc/passwd", "r"); + if (passwd != NULL) { + while (fgets(buf, sizeof(buf), passwd) != NULL) { + if ((buf[userlen] == ':') && + (strncmp(name, buf, userlen) == 0)) { + p = buf + strlen(buf) - 1; + while (isspace(*p) && (p >= buf)) { + *p-- = '\0'; + } + matched = 1; + break; + } + } + fclose(passwd); + } + } + + if (!matched && nis) { + char *userinfo = NULL, *domain = NULL; + int len = 0, i; + len = yp_get_default_domain(&domain); + if (len == YPERR_SUCCESS) { + len = yp_bind(domain); + } + if (len == YPERR_SUCCESS) { + i = yp_match(domain, "passwd.byname", name, + strlen(name), &userinfo, &len); + yp_unbind(domain); + if ((i == YPERR_SUCCESS) && (len < sizeof(buf))) { + strncpy(buf, userinfo, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + matched = 1; + } + } + } + + if (matched && (ret != NULL)) { + *ret = NULL; + + slogin = buf; + + spasswd = strchr(slogin, ':'); + if (spasswd == NULL) { + return matched; + } + *spasswd++ = '\0'; + + suid = strchr(spasswd, ':'); + if (suid == NULL) { + return matched; + } + *suid++ = '\0'; + + sgid = strchr(suid, ':'); + if (sgid == NULL) { + return matched; + } + *sgid++ = '\0'; + + sgecos = strchr(sgid, ':'); + if (sgecos == NULL) { + return matched; + } + *sgecos++ = '\0'; + + shome = strchr(sgecos, ':'); + if (shome == NULL) { + return matched; + } + *shome++ = '\0'; + + sshell = strchr(shome, ':'); + if (sshell == NULL) { + return matched; + } + *sshell++ = '\0'; + + buflen = sizeof(struct passwd) + + strlen(slogin) + 1 + + strlen(spasswd) + 1 + + strlen(suid) + 1 + + strlen(sgid) + 1 + + strlen(sgecos) + 1 + + strlen(shome) + 1 + + strlen(sshell) + 1; + *ret = malloc(buflen); + if (*ret == NULL) { + return matched; + } + memset(*ret, '\0', buflen); + + (*ret)->pw_uid = strtol(suid, &p, 0); + if ((strlen(sgid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + (*ret)->pw_gid = strtol(sgid, &p, 0); + if ((strlen(sgid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + p = ((char*)(*ret)) + sizeof(struct passwd); + (*ret)->pw_name = strcpy(p, slogin); + p += strlen(p) + 1; + (*ret)->pw_passwd = strcpy(p, spasswd); + p += strlen(p) + 1; + (*ret)->pw_gecos = strcpy(p, sgecos); + p += strlen(p) + 1; + (*ret)->pw_dir = strcpy(p, shome); + p += strlen(p) + 1; + (*ret)->pw_shell = strcpy(p, sshell); + + snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name); + + if (pam_set_data(pamh, buf, + *ret, _unix_cleanup) != PAM_SUCCESS) { + free(*ret); + *ret = NULL; + } + } + + return matched; +} + +/* + * _unix_comsefromsource() is a quick check to see if information about a given + * user comes from a particular source (just files and nis for now) + * + */ +int _unix_comesfromsource(pam_handle_t *pamh, + const char *name, int files, int nis) +{ + return _unix_getpwnam(pamh, name, files, nis, NULL); +} + /* * _unix_blankpasswd() is a quick check for a blank password * @@ -331,10 +494,10 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name) setreuid( save_uid, save_euid ); else { if (setreuid( -1, 0 ) == -1) - setreuid( save_uid, -1 ); + setreuid( save_uid, -1 ); setreuid( -1, save_euid ); } - } else if (strcmp(pwd->pw_passwd, "x") == 0) { + } else if (_unix_shadowed(pwd)) { /* * ...and shadow password file entry for this user, * if shadowing is enabled @@ -501,7 +664,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name setreuid( save_uid, -1 ); setreuid( -1, save_euid ); } - } else if (strcmp(pwd->pw_passwd, "x") == 0) { + } else if (_unix_shadowed(pwd)) { /* * ...and shadow password file entry for this user, * if shadowing is enabled @@ -523,7 +686,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name } retval = PAM_SUCCESS; - if (pwd == NULL || salt == NULL || strlen(salt) == 1) { + if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) { if (geteuid()) { /* we are not root perhaps this is the reason? Run helper */ D(("running helper binary")); @@ -850,6 +1013,21 @@ int _unix_read_password(pam_handle_t * pamh return PAM_SUCCESS; } +int _unix_shadowed(const struct passwd *pwd) +{ + if (pwd != NULL) { + if (strcmp(pwd->pw_passwd, "x") == 0) { + return 1; + } + if ((pwd->pw_passwd[0] == '#') && + (pwd->pw_passwd[1] == '#') && + (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) { + return 1; + } + } + return 0; +} + /* ****************************************************************** * * Copyright (c) Jan Rêkorajski 1999. * Copyright (c) Andrew G. Morgan 1996-8. diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h index 7219cd99..b2aa4b40 100644 --- a/modules/pam_unix/support.h +++ b/modules/pam_unix/support.h @@ -5,6 +5,7 @@ #ifndef _PAM_UNIX_SUPPORT_H #define _PAM_UNIX_SUPPORT_H +#include /* * here is the string to inform the user that the new passwords they @@ -133,6 +134,11 @@ extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl ,int type, const char *text); extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int argc, const char **argv); +extern int _unix_getpwnam (pam_handle_t *pamh, + const char *name, int files, int nis, + struct passwd **ret); +extern int _unix_comesfromsource (pam_handle_t *pamh, + const char *name, int files, int nis); extern int _unix_blankpasswd(pam_handle_t *pamh,unsigned int ctrl, const char *name); extern int _unix_verify_password(pam_handle_t * pamh, const char *name @@ -144,6 +150,6 @@ extern int _unix_read_password(pam_handle_t * pamh ,const char *prompt2 ,const char *data_name ,const char **pass); +extern int _unix_shadowed(const struct passwd *pwd); #endif /* _PAM_UNIX_SUPPORT_H */ - diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c index dd07960c..e65728d8 100644 --- a/modules/pam_unix/unix_chkpwd.c +++ b/modules/pam_unix/unix_chkpwd.c @@ -57,6 +57,24 @@ static void _log_err(int err, const char *format,...) closelog(); } +static int _unix_shadowed(const struct passwd *pwd) +{ + char hashpass[1024]; + if (pwd != NULL) { + if (strcmp(pwd->pw_passwd, "x") == 0) { + return 1; + } + if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) { + strcpy(hashpass, "##"); + strcpy(hashpass + 2, pwd->pw_name); + if (strcmp(pwd->pw_passwd, hashpass) == 0) { + return 1; + } + } + } + return 0; +} + static void su_sighandler(int sig) { if (sig > 0) { @@ -87,7 +105,7 @@ static void setup_signals(void) (void) sigaction(SIGQUIT, &action, NULL); } -static int _unix_verify_password(const char *name, const char *p, int opt) +static int _unix_verify_password(const char *name, const char *p, int nullok) { struct passwd *pwd = NULL; struct spwd *spwdent = NULL; @@ -101,7 +119,7 @@ static int _unix_verify_password(const char *name, const char *p, int opt) pwd = getpwnam(name); /* Get password file entry... */ endpwent(); if (pwd != NULL) { - if (strcmp(pwd->pw_passwd, "x") == 0) { + if (_unix_shadowed(pwd)) { /* * ...and shadow password file entry for this user, * if shadowing is enabled @@ -136,7 +154,10 @@ static int _unix_verify_password(const char *name, const char *p, int opt) salt_len = strlen(salt); if (salt_len == 0) { - return (opt == 0) ? UNIX_FAILED : UNIX_PASSED; + return (nullok == 0) ? UNIX_FAILED : UNIX_PASSED; + } + if (p == NULL) { + return UNIX_FAILED; } /* the moment of truth -- do we agree with the password? */ @@ -202,7 +223,7 @@ int main(int argc, char *argv[]) { char pass[MAXPASS + 1]; char option[8]; - int npass, opt; + int npass, nullok; int force_failure = 0; int retval = UNIX_FAILED; char *user; @@ -255,9 +276,9 @@ int main(int argc, char *argv[]) } else { option[7] = '\0'; if (strncmp(option, "nullok", 8) == 0) - opt = 1; + nullok = 1; else - opt = 0; + nullok = 0; } /* read the password from stdin (a pipe from the pam_unix module) */ @@ -276,13 +297,13 @@ int main(int argc, char *argv[]) if (npass == 0) { /* the password is NULL */ - retval = _unix_verify_password(user, NULL, opt); + retval = _unix_verify_password(user, NULL, nullok); } else { /* does pass agree with the official one? */ pass[npass] = '\0'; /* NUL terminate */ - retval = _unix_verify_password(user, pass, opt); + retval = _unix_verify_password(user, pass, nullok); } } -- cgit v1.2.3