From 7e7b7c099fa15b0e2511253d999f214821765549 Mon Sep 17 00:00:00 2001 From: Seraphim Mellos Date: Mon, 16 Jun 2008 18:48:53 +0300 Subject: Worked on pam_chauthtok for pam_unix --- modules/pam_unix/pam_unix.c | 151 +++++++++++++++++++++++++++++++++++++++++-- modules/pam_unix/pam_unix.c~ | 150 ++++++++++++++++++++++++++++++++++++++++-- modules/pam_unix/pam_unix.o | Bin 9760 -> 12128 bytes modules/pam_unix/pam_unix.so | Bin 15088 -> 15981 bytes 4 files changed, 293 insertions(+), 8 deletions(-) diff --git a/modules/pam_unix/pam_unix.c b/modules/pam_unix/pam_unix.c index 54b6f12..64a2eb2 100644 --- a/modules/pam_unix/pam_unix.c +++ b/modules/pam_unix/pam_unix.c @@ -23,6 +23,7 @@ #define PASSWORD_HASH "md5" +#define MAX_RETRIES 3 #define DEFAULT_WARN (2L * 7L * 86400L) /* two weeks */ #define SALTSIZE 32 @@ -264,8 +265,11 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, */ struct passwd *new_pwd, *old_pwd; const char *user, *old_pass, *new_pass; - char *hashedpwd; - int pam_err; + char *hashedpwd ; +#ifndef __linux__ + login_cap_t * lc; +#endif + int pam_err, retries; /* identify user */ @@ -307,7 +311,7 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, if (flags & PAM_PRELIM_CHECK) { - PAM_LOG("PRELIM round"); + PAM_LOG("Doing preliminary actions."); if (getuid() == 0 ) { /* root doesn't need old passwd */ @@ -322,14 +326,153 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, * ask for a password. */ old_pass = ""; + } else { + pam_err = pam_get_authtok(pamh,PAM_OLDAUTHTOK, + &old_pass, NULL); + if (pam_err != PAM_SUCCESS ) + return (pam_err); + + } + + PAM_LOG("Got old token for user [%s].",user); + + hashedpwd = crypt(old_pass, old_pwd->pw_passwd); + + if (old_pass[0] == '\0' && !openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_PERM_DENIED); + + if (strcmp(hashedpwd, old_pwd->pw_passwd) != 0) + return (PAM_PERM_DENIED); + + } else if ( flags & PAM_UPDATE_AUTHTOK ) { + + PAM_LOG("Doing actual update."); + + pam_err= pam_get_authtok(pamh, PAM_OLDAUTHTOK ,&old_pass, NULL); + + if (pam_err != PAM_SUCCESS) + return (pam_err); + + PAM_LOG("Got old password"); + + retries = 0; + pam_err = PAM_AUTHTOK_ERR; + + while ((pam_err != PAM_SUCCESS) && ( retries++ < MAX_RETRIES)) { + + pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, + &new_pass, NULL); + + pam_error(pamh, "Unable to get new passwd. Please \ + try again"); + + } + + if (pam_err != PAM_SUCCESS) { + PAM_ERROR("Unable to get new password!"); + return (pam_err); } + + PAM_LOG("Got new password"); + + /* + * checking has to be done (?) for the new passwd to + * verify it's not weak. + */ + + if (getuid() != 0 && new_pass[0] == '\0' && + !openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_PERM_DENIED); + + + + +#ifndef __linux__ + + /* + * The BSD way to update the passwd entry. Taken as is + * from the freebsd-lib module pam_unix. Unfortunately, + * the following won't work under Linux. + */ + + if ((new_pwd = pw_dup(old_pwd)) == NULL) + return (PAM_BUF_ERR); + + new_pwd->pw_change = 0; + lc = login_getclass(new_pwd->pw_class); + if (login_setcryptfmt(lc, password_hash, NULL) == NULL) + openpam_log(PAM_LOG_ERROR, + "can't set password cipher, relying on default"); + + login_close(lc); + makesalt(salt); + new_pwd->pw_passwd = crypt(new_pass, salt); + + + pam_err = PAM_SERVICE_ERR; + if (pw_init(NULL, NULL)) + openpam_log(PAM_LOG_ERROR, "pw_init() failed"); + else if ((pfd = pw_lock()) == -1) + openpam_log(PAM_LOG_ERROR, "pw_lock() failed"); + else if ((tfd = pw_tmp(-1)) == -1) + openpam_log(PAM_LOG_ERROR, "pw_tmp() failed"); + else if (pw_copy(pfd, tfd, new_pwd, old_pwd) == -1) + openpam_log(PAM_LOG_ERROR, "pw_copy() failed"); + else if (pw_mkdb(new_pwd->pw_name) == -1) + openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed"); + else + pam_err = PAM_SUCCESS; + pw_fini(); + + free(old_pwd); +#endif + + /* Update shadow/passwd entries for Linux */ + + + } else { + pam_err = PAM_ABORT; + PAM_ERROR("Unrecognized flags."); + return (pam_err); } + + - return (PAM_SUCCESS); } +/* + * Mostly stolen from freebsd-lib's pam_unix module which was mostly + * stolen from passwd(1)'s local_passwd.c + * + * Good ideas are meant to be reused ;) + */ + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, long v, int n) { + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +/* Salt suitable for traditional DES and MD5 */ +void +makesalt(char salt[SALTSIZE]) { + int i; + /* These are not really random numbers, they are just + * numbers that change to thwart construction of a + * dictionary. This is exposed to the public. + */ + + for (i = 0; i < SALTSIZE; i += 4) + to64(&salt[i], arc4random(), 4); + salt[SALTSIZE] = '\0'; +} + PAM_MODULE_ENTRY("pam_unix") diff --git a/modules/pam_unix/pam_unix.c~ b/modules/pam_unix/pam_unix.c~ index 72dbac0..06f335c 100644 --- a/modules/pam_unix/pam_unix.c~ +++ b/modules/pam_unix/pam_unix.c~ @@ -23,6 +23,7 @@ #define PASSWORD_HASH "md5" +#define MAX_RETRIES 3 #define DEFAULT_WARN (2L * 7L * 86400L) /* two weeks */ #define SALTSIZE 32 @@ -264,8 +265,11 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, */ struct passwd *new_pwd, *old_pwd; const char *user, *old_pass, *new_pass; - char *hashedpwd; - int pam_err; + char *hashedpwd ; +#ifndef __linux__ + login_cap_t * lc; +#endif + int pam_err, retries; /* identify user */ @@ -307,7 +311,7 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, if (flags & PAM_PRELIM_CHECK) { - PAM_LOG("PRELIM round"); + PAM_LOG("Doing preliminary actions."); if (getuid() == 0 ) { /* root doesn't need old passwd */ @@ -322,15 +326,153 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, * ask for a password. */ old_pass = ""; + } else { + pam_err = pam_get_authtok(pamh,PAM_OLDAUTHTOK, + &old_pass, NULL); + if (pam_err != PAM_SUCCESS ) + return (pam_err); + + } + + PAM_LOG("Got old token for user [%s].",user); + + hashedpwd = crypt(old_pass, old_pwd->pw_passwd); + + if (old_pass[0] == '\0' && !openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_PERM_DENIED); + + if (strcmp(hashedpwd, old_pwd->pw_passwd) != 0) + return (PAM_PERM_DENIED); + + } else if ( flags & PAM_UPDATE_AUTHTOK ) { + + PAM_LOG("Doing actual update."); + + pam_err= pam_get_authtok(pamh, PAM_OLDAUTHTOK ,&old_pass, NULL); + + if (pam_err != PAM_SUCCESS) + return (pam_err); + + PAM_LOG("Got old password"); + + retries = 0; + pam_err = PAM_AUTHTOK_ERR; + + while ((pam_err != PAM_SUCCESS) && ( retries++ < MAX_RETRIES)) { + + pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, + &new_pass, NULL); + + pam_error(pamh, "Unable to get new passwd. Please \ + try again"); + + } + + if (pam_err != PAM_SUCCESS) { + PAM_ERROR("Unable to get new password!"); + return (pam_err); } + PAM_LOG("Got new password"); + + /* + * checking has to be done (?) for the new passwd to + * verify it's not weak. + */ + + if (getuid() != 0 && new_pass[0] == '\0' && + !openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_PERM_DENIED); + + + + +#ifndef __linux__ + + /* + * The BSD way to update the passwd entry. Taken as is + * from the freebsd-lib module pam_unix. Unfortunately, + * the following won't work under Linux. + */ + + if ((new_pwd = pw_dup(old_pwd)) == NULL) + return (PAM_BUF_ERR); + + new_pwd->pw_change = 0; + lc = login_getclass(new_pwd->pw_class); + if (login_setcryptfmt(lc, password_hash, NULL) == NULL) + openpam_log(PAM_LOG_ERROR, + "can't set password cipher, relying on default"); + + login_close(lc); + makesalt(salt); + new_pwd->pw_passwd = crypt(new_pass, salt); + + + pam_err = PAM_SERVICE_ERR; + if (pw_init(NULL, NULL)) + openpam_log(PAM_LOG_ERROR, "pw_init() failed"); + else if ((pfd = pw_lock()) == -1) + openpam_log(PAM_LOG_ERROR, "pw_lock() failed"); + else if ((tfd = pw_tmp(-1)) == -1) + openpam_log(PAM_LOG_ERROR, "pw_tmp() failed"); + else if (pw_copy(pfd, tfd, new_pwd, old_pwd) == -1) + openpam_log(PAM_LOG_ERROR, "pw_copy() failed"); + else if (pw_mkdb(new_pwd->pw_name) == -1) + openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed"); + else + pam_err = PAM_SUCCESS; + pw_fini(); + + free(old_pwd); +#endif + + /* Update shadow/passwd entries for Linux */ + + + } else { + pam_err = PAM_ABORT; + PAM_ERROR("Unrecognized flags."); + return (pam_err); + } + - return (PAM_SUCCESS); } +/* + * Mostly stolen from freebsd-lib's pam_unix module which was mostly + * stolen from passwd(1)'s local_passwd.c + * + * Good ideas are meant to be reused ;) + */ + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, long v, int n) { + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +/* Salt suitable for traditional DES and MD5 */ +void +makesalt(char salt[SALTSIZE]) { + int i; + /* These are not really random numbers, they are just + * numbers that change to thwart construction of a + * dictionary. This is exposed to the public. + */ + + for (i = 0; i < SALTSIZE; i += 4) + to64(&salt[i], arc4random(), 4); + salt[SALTSIZE] = '\0'; +} + PAM_MODULE_ENTRY("pam_unix") diff --git a/modules/pam_unix/pam_unix.o b/modules/pam_unix/pam_unix.o index 1dd928a..f179bea 100644 Binary files a/modules/pam_unix/pam_unix.o and b/modules/pam_unix/pam_unix.o differ diff --git a/modules/pam_unix/pam_unix.so b/modules/pam_unix/pam_unix.so index 6be3fac..c87b5df 100755 Binary files a/modules/pam_unix/pam_unix.so and b/modules/pam_unix/pam_unix.so differ -- cgit v1.2.3-65-gdbad