From af0faf666c5008e54dfe43684f210e3581ff1bca Mon Sep 17 00:00:00 2001 From: ikerexxe Date: Tue, 16 Jun 2020 14:32:36 +0200 Subject: pam_unix: avoid determining if user exists Taking a look at the time for the password prompt to appear it was possible to determine if a user existed in a system. Solved it by matching the runtime until the password prompt was shown by always checking the password hash for an existing and a non-existing user. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1629598 --- modules/pam_unix/support.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'modules/pam_unix/support.c') diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c index 41db1f04..dc67238c 100644 --- a/modules/pam_unix/support.c +++ b/modules/pam_unix/support.c @@ -601,6 +601,8 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name char *salt = NULL; int daysleft; int retval; + int execloop = 1; + int nonexistent = 1; D(("called")); @@ -624,14 +626,31 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name /* UNIX passwords area */ - retval = get_pwd_hash(pamh, name, &pwd, &salt); + /* + * Execute this loop twice: one checking the password hash of an existing + * user and another one for a non-existing user. This way the runtimes + * are equal, making it more difficult to differentiate existing from + * non-existing users. + */ + while (execloop) { + retval = get_pwd_hash(pamh, name, &pwd, &salt); - if (retval == PAM_UNIX_RUN_HELPER) { - /* salt will not be set here so we can return immediately */ - if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS) - return 1; - else - return 0; + if (retval == PAM_UNIX_RUN_HELPER) { + execloop = 0; + if(nonexistent) { + get_pwd_hash(pamh, "pam_unix_non_existent:", &pwd, &salt); + } + /* salt will not be set here so we can return immediately */ + if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS) + return 1; + else + return 0; + } else if (retval == PAM_USER_UNKNOWN) { + name = "root"; + nonexistent = 0; + } else { + execloop = 0; + } } /* Does this user have a password? */ -- cgit v1.2.3 From b36351dd0137034e79194023c8d687b495e785c4 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 15 Jul 2020 08:00:00 +0000 Subject: pam_unix: use PAM_MAX_RESP_SIZE instead of its alias MAXPASS * modules/pam_unix/passverify.h (MAXPASS): Remove. * modules/pam_unix/passverify.c (read_passwords): Replace MAXPASS with PAM_MAX_RESP_SIZE. * modules/pam_unix/pam_unix_passwd.c (_pam_unix_approve_pass): Likewise. * modules/pam_unix/support.c (_unix_verify_password): Likewise. * modules/pam_unix/unix_chkpwd.c (main): Likewise. * modules/pam_unix/unix_update.c (set_password): Likewise. --- modules/pam_unix/pam_unix_passwd.c | 2 +- modules/pam_unix/passverify.c | 4 ++-- modules/pam_unix/passverify.h | 2 -- modules/pam_unix/support.c | 4 ++-- modules/pam_unix/unix_chkpwd.c | 4 ++-- modules/pam_unix/unix_update.c | 10 +++++----- 6 files changed, 12 insertions(+), 14 deletions(-) (limited to 'modules/pam_unix/support.c') diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c index e988b2e3..a20e919e 100644 --- a/modules/pam_unix/pam_unix_passwd.c +++ b/modules/pam_unix/pam_unix_passwd.c @@ -577,7 +577,7 @@ static int _pam_unix_approve_pass(pam_handle_t * pamh } } - if (strlen(pass_new) > MAXPASS) { + if (strlen(pass_new) > PAM_MAX_RESP_SIZE) { remark = _("You must choose a shorter password."); D(("length exceeded [%s]", remark)); } else if (off(UNIX__IAMROOT, ctrl)) { diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c index 7455eae6..d5cfd269 100644 --- a/modules/pam_unix/passverify.c +++ b/modules/pam_unix/passverify.c @@ -1190,14 +1190,14 @@ int read_passwords(int fd, int npass, char **passwords) { /* The passwords array must contain npass preallocated - * buffers of length MAXPASS + 1 + * buffers of length PAM_MAX_RESP_SIZE + 1 */ int rbytes = 0; int offset = 0; int i = 0; char *pptr; while (npass > 0) { - rbytes = read(fd, passwords[i]+offset, MAXPASS+1-offset); + rbytes = read(fd, passwords[i]+offset, PAM_MAX_RESP_SIZE+1-offset); if (rbytes < 0) { if (errno == EINTR) continue; diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h index e9a88fbf..47d9d4db 100644 --- a/modules/pam_unix/passverify.h +++ b/modules/pam_unix/passverify.h @@ -8,8 +8,6 @@ #define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT -#define MAXPASS PAM_MAX_RESP_SIZE /* the maximum length of a password */ - #define OLD_PASSWORDS_FILE "/etc/security/opasswd" int diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c index dc67238c..d669e951 100644 --- a/modules/pam_unix/support.c +++ b/modules/pam_unix/support.c @@ -677,7 +677,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name struct passwd *pwd = NULL; char *salt = NULL; char *data_name; - char pw[MAXPASS + 1]; + char pw[PAM_MAX_RESP_SIZE + 1]; int retval; @@ -704,7 +704,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name); } - if (p != NULL && strlen(p) > MAXPASS) { + if (p != NULL && strlen(p) > PAM_MAX_RESP_SIZE) { memset(pw, 0, sizeof(pw)); p = strncpy(pw, p, sizeof(pw) - 1); } diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c index 88647e58..c61759a6 100644 --- a/modules/pam_unix/unix_chkpwd.c +++ b/modules/pam_unix/unix_chkpwd.c @@ -89,7 +89,7 @@ static int _audit_log(int type, const char *uname, int rc) int main(int argc, char *argv[]) { - char pass[MAXPASS + 1]; + char pass[PAM_MAX_RESP_SIZE + 1]; char *option; int npass, nullok; int blankpass = 0; @@ -175,7 +175,7 @@ int main(int argc, char *argv[]) retval = helper_verify_password(user, pass, nullok); - memset(pass, '\0', MAXPASS); /* clear memory of the password */ + memset(pass, '\0', PAM_MAX_RESP_SIZE); /* clear memory of the password */ /* return pass or fail */ diff --git a/modules/pam_unix/unix_update.c b/modules/pam_unix/unix_update.c index 6ea7ea51..ae77fe2e 100644 --- a/modules/pam_unix/unix_update.c +++ b/modules/pam_unix/unix_update.c @@ -38,8 +38,8 @@ set_password(const char *forwho, const char *shadow, const char *remember) { struct passwd *pwd = NULL; int retval; - char pass[MAXPASS + 1]; - char towhat[MAXPASS + 1]; + char pass[PAM_MAX_RESP_SIZE + 1]; + char towhat[PAM_MAX_RESP_SIZE + 1]; int npass = 0; /* we don't care about number format errors because the helper should be called internally only */ @@ -54,7 +54,7 @@ set_password(const char *forwho, const char *shadow, const char *remember) if (npass != 2) { /* is it a valid password? */ if (npass == 1) { helper_log_err(LOG_DEBUG, "no new password supplied"); - memset(pass, '\0', MAXPASS); + memset(pass, '\0', PAM_MAX_RESP_SIZE); } else { helper_log_err(LOG_DEBUG, "no valid passwords supplied"); } @@ -97,8 +97,8 @@ set_password(const char *forwho, const char *shadow, const char *remember) } done: - memset(pass, '\0', MAXPASS); - memset(towhat, '\0', MAXPASS); + memset(pass, '\0', PAM_MAX_RESP_SIZE); + memset(towhat, '\0', PAM_MAX_RESP_SIZE); unlock_pwdf(); -- cgit v1.2.3 From 30fdfb90d9864bcc254a62760aaa149d373fd4eb Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 20 Nov 2020 13:38:23 +0100 Subject: Second blank check with root for non-existent users must never return 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit af0faf66 ("pam_unix: avoid determining if user exists") introduced a regression where the blank check could return 1 if root had an empty password hash because in the second case the password hash of root was used. We now always return 0 in this case. The issue was found by Johannes Löthberg. Fixes #284 * modules/pam_unix/support.c (_unix_blankpasswd): Make the loop to cover the complete blank check so both existing and non existing cases are identical except for the possible return value. --- modules/pam_unix/support.c | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) (limited to 'modules/pam_unix/support.c') diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c index d669e951..27ca7127 100644 --- a/modules/pam_unix/support.c +++ b/modules/pam_unix/support.c @@ -601,8 +601,9 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name char *salt = NULL; int daysleft; int retval; - int execloop = 1; - int nonexistent = 1; + int blank = 0; + int execloop; + int nonexistent_check = 1; D(("called")); @@ -632,43 +633,29 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name * are equal, making it more difficult to differentiate existing from * non-existing users. */ - while (execloop) { + for (execloop = 0; execloop < 2; ++execloop) { retval = get_pwd_hash(pamh, name, &pwd, &salt); if (retval == PAM_UNIX_RUN_HELPER) { - execloop = 0; - if(nonexistent) { - get_pwd_hash(pamh, "pam_unix_non_existent:", &pwd, &salt); - } - /* salt will not be set here so we can return immediately */ if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS) - return 1; - else - return 0; + blank = nonexistent_check; } else if (retval == PAM_USER_UNKNOWN) { name = "root"; - nonexistent = 0; - } else { - execloop = 0; + nonexistent_check = 0; + continue; + } else if (salt != NULL) { + if (strlen(salt) == 0) + blank = nonexistent_check; } - } - - /* Does this user have a password? */ - if (salt == NULL) { - retval = 0; - } else { - if (strlen(salt) == 0) - retval = 1; - else - retval = 0; + name = "pam_unix_non_existent:"; + /* non-existent user check will not affect the blank value */ } /* tidy up */ - if (salt) _pam_delete(salt); - return retval; + return blank; } int _unix_verify_password(pam_handle_t * pamh, const char *name -- cgit v1.2.3