diff options
Diffstat (limited to 'modules/pam_unix/passverify.c')
-rw-r--r-- | modules/pam_unix/passverify.c | 133 |
1 files changed, 102 insertions, 31 deletions
diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c index 9c1771e2..a571b4f7 100644 --- a/modules/pam_unix/passverify.c +++ b/modules/pam_unix/passverify.c @@ -25,6 +25,8 @@ #include <crypt.h> #endif +#include "pam_cc_compat.h" +#include "pam_inline.h" #include "md5.h" #include "bigcrypt.h" #include "passverify.h" @@ -65,8 +67,8 @@ strip_hpux_aging(char *hash) } } -int -verify_pwd_hash(const char *p, char *hash, unsigned int nullok) +PAMH_ARG_DECL(int verify_pwd_hash, + const char *p, char *hash, unsigned int nullok) { size_t hash_len; char *pp = NULL; @@ -87,7 +89,7 @@ verify_pwd_hash(const char *p, char *hash, unsigned int nullok) } else if (!p || *hash == '*' || *hash == '!') { retval = PAM_AUTH_ERR; } else { - if (!strncmp(hash, "$1$", 3)) { + if (pam_str_skip_prefix(hash, "$1$") != NULL) { pp = Goodcrypt_md5(p, hash); if (pp && strcmp(pp, hash) != 0) { _pam_delete(pp); @@ -103,6 +105,44 @@ verify_pwd_hash(const char *p, char *hash, unsigned int nullok) * Ok, we don't know the crypt algorithm, but maybe * libcrypt knows about it? We should try it. */ +#if defined(CRYPT_CHECKSALT_AVAILABLE) && CRYPT_CHECKSALT_AVAILABLE + /* Get the status of the hash from checksalt */ + int retval_checksalt = crypt_checksalt(hash); + + /* + * Check for hashing methods that are disabled by + * libcrypt configuration and/or system preset. + */ + if (retval_checksalt == CRYPT_SALT_METHOD_DISABLED) { + /* + * pam_syslog() needs a pam handle, + * but that's not available here. + */ + pam_syslog(pamh, LOG_ERR, + "The support for password hash \"%.6s\" " + "has been disabled in libcrypt " + "configuration.", + hash); + } + /* + * Check for malformed hashes, like descrypt hashes + * starting with "$2...", which might have been + * generated by unsafe base64 encoding functions + * as used in glibc <= 2.16. + * Such hashes are likely to be rejected by many + * recent implementations of libcrypt. + */ + if (retval_checksalt == CRYPT_SALT_INVALID) { + pam_syslog(pamh, LOG_ERR, + "The password hash \"%.6s\" is unknown to " + "libcrypt.", + hash); + } +#else +#ifndef HELPER_COMPILE + (void)pamh; +#endif +#endif #ifdef HAVE_CRYPT_R struct crypt_data *cdata; cdata = malloc(sizeof(*cdata)); @@ -166,25 +206,30 @@ PAMH_ARG_DECL(int get_account_info, save_euid = geteuid(); save_uid = getuid(); - if (save_uid == (*pwd)->pw_uid) - setreuid(save_euid, save_uid); - else { - setreuid(0, -1); - if (setreuid(-1, (*pwd)->pw_uid) == -1) { - setreuid(-1, 0); - setreuid(0, -1); - if(setreuid(-1, (*pwd)->pw_uid) == -1) + if (save_uid == (*pwd)->pw_uid) { + if (setreuid(save_euid, save_uid)) + return PAM_CRED_INSUFFICIENT; + } else { + if (setreuid(0, -1)) + return PAM_CRED_INSUFFICIENT; + if (setreuid(-1, (*pwd)->pw_uid)) { + if (setreuid(-1, 0) + || setreuid(0, -1) + || setreuid(-1, (*pwd)->pw_uid)) { return PAM_CRED_INSUFFICIENT; + } } } *spwdent = pam_modutil_getspnam(pamh, name); - if (save_uid == (*pwd)->pw_uid) - setreuid(save_uid, save_euid); - else { - setreuid(-1, 0); - setreuid(save_uid, -1); - setreuid(-1, save_euid); + if (save_uid == (*pwd)->pw_uid) { + if (setreuid(save_uid, save_euid)) + return PAM_CRED_INSUFFICIENT; + } else { + if (setreuid(-1, 0) + || setreuid(save_uid, -1) + || setreuid(-1, save_euid)) + return PAM_CRED_INSUFFICIENT; } if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) @@ -244,7 +289,13 @@ PAMH_ARG_DECL(int check_shadow_expiry, D(("account expired")); return PAM_ACCT_EXPIRED; } +#if defined(CRYPT_CHECKSALT_AVAILABLE) && CRYPT_CHECKSALT_AVAILABLE + if (spent->sp_lstchg == 0 || + crypt_checksalt(spent->sp_pwdp) == CRYPT_SALT_METHOD_LEGACY || + crypt_checksalt(spent->sp_pwdp) == CRYPT_SALT_TOO_CHEAP) { +#else if (spent->sp_lstchg == 0) { +#endif D(("need a new password")); *daysleft = 0; return PAM_NEW_AUTHTOK_REQD; @@ -372,10 +423,15 @@ crypt_md5_wrapper(const char *pass_new) } PAMH_ARG_DECL(char * create_password_hash, - const char *password, unsigned int ctrl, int rounds) + const char *password, unsigned long long ctrl, int rounds) { const char *algoid; +#if defined(CRYPT_GENSALT_OUTPUT_SIZE) && CRYPT_GENSALT_OUTPUT_SIZE > 64 + /* Strings returned by crypt_gensalt_rn will be no longer than this. */ + char salt[CRYPT_GENSALT_OUTPUT_SIZE]; +#else char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */ +#endif char *sp; #ifdef HAVE_CRYPT_R struct crypt_data *cdata = NULL; @@ -384,8 +440,12 @@ PAMH_ARG_DECL(char * create_password_hash, if (on(UNIX_MD5_PASS, ctrl)) { /* algoid = "$1" */ return crypt_md5_wrapper(password); + } else if (on(UNIX_YESCRYPT_PASS, ctrl)) { + algoid = "$y$"; + } else if (on(UNIX_GOST_YESCRYPT_PASS, ctrl)) { + algoid = "$gy$"; } else if (on(UNIX_BLOWFISH_PASS, ctrl)) { - algoid = "$2a$"; + algoid = "$2b$"; } else if (on(UNIX_SHA256_PASS, ctrl)) { algoid = "$5$"; } else if (on(UNIX_SHA512_PASS, ctrl)) { @@ -406,6 +466,13 @@ PAMH_ARG_DECL(char * create_password_hash, return crypted; } +#if defined(CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY) && CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY + /* + * Any version of libcrypt supporting auto entropy is + * guaranteed to have crypt_gensalt_rn(). + */ + sp = crypt_gensalt_rn(algoid, rounds, NULL, 0, salt, sizeof(salt)); +#else #ifdef HAVE_CRYPT_GENSALT_R if (on(UNIX_BLOWFISH_PASS, ctrl)) { char entropy[17]; @@ -423,6 +490,7 @@ PAMH_ARG_DECL(char * create_password_hash, #ifdef HAVE_CRYPT_GENSALT_R } #endif +#endif /* CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY */ #ifdef HAVE_CRYPT_R sp = NULL; cdata = malloc(sizeof(*cdata)); @@ -434,10 +502,11 @@ PAMH_ARG_DECL(char * create_password_hash, sp = crypt(password, salt); #endif if (!sp || strncmp(algoid, sp, strlen(algoid)) != 0) { - /* libxcrypt/libc doesn't know the algorithm, use MD5 */ + /* libxcrypt/libc doesn't know the algorithm, error out */ pam_syslog(pamh, LOG_ERR, - "Algo %s not supported by the crypto backend, " - "falling back to MD5\n", + "Algo %s not supported by the crypto backend.\n", + on(UNIX_YESCRYPT_PASS, ctrl) ? "yescrypt" : + on(UNIX_GOST_YESCRYPT_PASS, ctrl) ? "gost_yescrypt" : on(UNIX_BLOWFISH_PASS, ctrl) ? "blowfish" : on(UNIX_SHA256_PASS, ctrl) ? "sha256" : on(UNIX_SHA512_PASS, ctrl) ? "sha512" : algoid); @@ -447,7 +516,7 @@ PAMH_ARG_DECL(char * create_password_hash, #ifdef HAVE_CRYPT_R free(cdata); #endif - return crypt_md5_wrapper(password); + return NULL; } sp = x_strdup(sp); #ifdef HAVE_CRYPT_R @@ -958,7 +1027,9 @@ PAMH_ARG_DECL(int unix_update_shadow, fclose(opwfile); if (!wroteentry && !err) { + DIAG_PUSH_IGNORE_CAST_QUAL; spwdent.sp_namp = (char *)forwho; + DIAG_POP_IGNORE_CAST_QUAL; spwdent.sp_pwdp = towhat; spwdent.sp_lstchg = time(NULL) / (60 * 60 * 24); if (spwdent.sp_lstchg == 0) @@ -1017,21 +1088,21 @@ int helper_verify_password(const char *name, const char *p, int nullok) { struct passwd *pwd = NULL; - char *salt = NULL; + char *hash = NULL; int retval; - retval = get_pwd_hash(name, &pwd, &salt); + retval = get_pwd_hash(name, &pwd, &hash); - if (pwd == NULL || salt == NULL) { + if (pwd == NULL || hash == NULL) { helper_log_err(LOG_NOTICE, "check pass; user unknown"); retval = PAM_USER_UNKNOWN; } else { - retval = verify_pwd_hash(p, salt, nullok); + retval = verify_pwd_hash(p, hash, nullok); } - if (salt) { - _pam_overwrite(salt); - _pam_drop(salt); + if (hash) { + _pam_overwrite(hash); + _pam_drop(hash); } p = NULL; /* no longer needed here */ @@ -1154,7 +1225,7 @@ read_passwords(int fd, int npass, char **passwords) #endif /* ****************************************************************** * - * Copyright (c) Jan Rêkorajski 1999. + * Copyright (c) Jan Rękorajski 1999. * Copyright (c) Andrew G. Morgan 1996-8. * Copyright (c) Alex O. Yuriev, 1996. * Copyright (c) Cristian Gafton 1996. |