From 8b00f4aacb15ecad5227d1f4c5354c1938ba8fbf Mon Sep 17 00:00:00 2001 From: Seraphim Mellos Date: Thu, 19 Jun 2008 19:54:42 +0300 Subject: Completed shadow update mechanisms for pam_unix --- modules/pam_unix/pam_unix.c | 148 +++++++++++++++++++++++++++++++++++++----- modules/pam_unix/pam_unix.c~ | 149 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 267 insertions(+), 30 deletions(-) diff --git a/modules/pam_unix/pam_unix.c b/modules/pam_unix/pam_unix.c index ae824f5..dd3c519 100644 --- a/modules/pam_unix/pam_unix.c +++ b/modules/pam_unix/pam_unix.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -20,7 +23,7 @@ #include /* for BSD login classes */ #include /* libutil functions */ #else -#include /* for linux boxes */ +#include #endif #define PASSWORD_HASH "md5" @@ -33,7 +36,19 @@ #include #include + +/* + * Helper functions for internal use + */ + +#ifdef __linux__ +static int update_shadow( pam_handle_t * pamh , const char * user , const char * newhashedpwd ) ; static char * read_shadow(const char * user) ; +#endif + +static void to64(char *s, long v, int n); +void makesalt(char salt[SALTSIZE]); + /* * User authentication */ @@ -278,7 +293,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags , */ PAM_EXTERN int -pam_sm_chautok(pam_handle_t *pamh, int flags, +pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { @@ -288,9 +303,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 ; + char *hashedpwd, salt[SALTSIZE+1]; + #ifndef __linux__ login_cap_t * lc; + int pfd, tfd; #endif int pam_err, retries; @@ -387,7 +404,7 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, &new_pass, NULL); - pam_error(pamh, "Unable to get new passwd. Please \ + PAM_ERROR("Unable to get new passwd. Please \ try again"); } @@ -408,9 +425,6 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, !openpam_get_option(pamh, PAM_OPT_NULLOK)) return (PAM_PERM_DENIED); - - - #ifndef __linux__ /* @@ -449,10 +463,13 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, pw_fini(); free(old_pwd); -#endif - +#else + makesalt(salt); + update_shadow( pamh ,user,crypt(new_pass, salt) ); + free(old_pwd); /* Update shadow/passwd entries for Linux */ - + +#endif } else { pam_err = PAM_ABORT; @@ -460,13 +477,109 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, return (pam_err); } + return (PAM_SUCCESS); +} - return (PAM_SUCCESS); +#ifdef __linux__ -} +#define NEW_SHADOW "/etc/.shadow" +/* + * Update shadow with new user password + */ +static int update_shadow( pam_handle_t * pamh , const char * user ,const char * newhashedpwd ) { + FILE *oldshadow, *newshadow; + struct spwd *pwd,*cur_pwd; + struct stat filestat; + + + if ( (pwd = getspnam(user)) == NULL) + return PAM_USER_UNKNOWN; + + if ( (oldshadow = fopen ("/etc/shadow", "r")) == NULL ) { + PAM_ERROR("Could not open /etc/shadow. Updating shadow \ + database cancelled."); + return (PAM_AUTHTOK_ERR); + } + + if ( (newshadow = fopen (NEW_SHADOW, "w")) == NULL ) { + PAM_ERROR("Could not open temp file. Updating shadow \ + database cancelled."); + fclose(oldshadow); + return (PAM_AUTHTOK_ERR); + } + + if (fstat(fileno(oldshadow), &filestat) == -1 ) { + PAM_ERROR("Could not get stat for /etc/shadow. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + if (fchown(fileno(newshadow), filestat.st_uid, filestat.st_gid) == -1 ) { + PAM_ERROR("Could not set uid/gid for new shadwow file. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + if (fchmod(fileno(newshadow), filestat.st_mode) == -1 ) { + PAM_ERROR("Could not chmod for new shadow file. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + while ( (cur_pwd = fgetspent(oldshadow)) ) { + if( strlen(user) == strlen(cur_pwd->sp_namp) + && !strncmp(cur_pwd->sp_namp, user, strlen(user))) { + cur_pwd->sp_pwdp = newhashedpwd; + cur_pwd->sp_lstchg = time(NULL) / (60 * 60 * 24); + PAM_LOG("Updated password for user [%s]",user); + } + + if(putspent(cur_pwd, newshadow)) { + PAM_ERROR("Error writing entry to new shadow file. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + } + + fclose(oldshadow); + + if (fclose(newshadow)) { + PAM_ERROR("Error updating new shadow file."); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + /* + * If program flow has come up to here, all is good + * and it's safe to update the shadow file. + */ + + if( rename(NEW_SHADOW, "/etc/shadow") == 0 ) { + PAM_LOG("Password updated successfully for user [%s]",user); + } else { + PAM_ERROR("Error updating shadow file."); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + return (PAM_SUCCESS); + +} /* * Read hashed password for user from shadow entry. @@ -485,8 +598,8 @@ static char * read_shadow(const char * user) { } +#endif -#ifndef __linux__ /* * Mostly stolen from freebsd-lib's pam_unix module which was mostly * stolen from passwd(1)'s local_passwd.c @@ -514,8 +627,13 @@ makesalt(char salt[SALTSIZE]) { */ for (i = 0; i < SALTSIZE; i += 4) -// to64(&salt[i], arc4random(), 4); + +#ifndef __linux__ + to64(&salt[i], arc4random(), 4); +#else + to64(&salt[i], random(), 4); +#endif + salt[SALTSIZE] = '\0'; } -#endif PAM_MODULE_ENTRY("pam_unix") diff --git a/modules/pam_unix/pam_unix.c~ b/modules/pam_unix/pam_unix.c~ index 79e1131..dd3c519 100644 --- a/modules/pam_unix/pam_unix.c~ +++ b/modules/pam_unix/pam_unix.c~ @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -18,8 +21,9 @@ #ifndef __linux__ #include /* for BSD login classes */ +#include /* libutil functions */ #else -#include /* for linux boxes */ +#include #endif #define PASSWORD_HASH "md5" @@ -32,7 +36,19 @@ #include #include + +/* + * Helper functions for internal use + */ + +#ifdef __linux__ +static int update_shadow( pam_handle_t * pamh , const char * user , const char * newhashedpwd ) ; static char * read_shadow(const char * user) ; +#endif + +static void to64(char *s, long v, int n); +void makesalt(char salt[SALTSIZE]); + /* * User authentication */ @@ -277,7 +293,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags , */ PAM_EXTERN int -pam_sm_chautok(pam_handle_t *pamh, int flags, +pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { @@ -287,9 +303,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 ; + char *hashedpwd, salt[SALTSIZE+1]; + #ifndef __linux__ login_cap_t * lc; + int pfd, tfd; #endif int pam_err, retries; @@ -386,7 +404,7 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, &new_pass, NULL); - pam_error(pamh, "Unable to get new passwd. Please \ + PAM_ERROR("Unable to get new passwd. Please \ try again"); } @@ -407,9 +425,6 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, !openpam_get_option(pamh, PAM_OPT_NULLOK)) return (PAM_PERM_DENIED); - - - #ifndef __linux__ /* @@ -448,10 +463,13 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, pw_fini(); free(old_pwd); -#endif - +#else + makesalt(salt); + update_shadow( pamh ,user,crypt(new_pass, salt) ); + free(old_pwd); /* Update shadow/passwd entries for Linux */ - + +#endif } else { pam_err = PAM_ABORT; @@ -459,13 +477,109 @@ pam_sm_chautok(pam_handle_t *pamh, int flags, return (pam_err); } + return (PAM_SUCCESS); +} - return (PAM_SUCCESS); +#ifdef __linux__ -} +#define NEW_SHADOW "/etc/.shadow" +/* + * Update shadow with new user password + */ +static int update_shadow( pam_handle_t * pamh , const char * user ,const char * newhashedpwd ) { + FILE *oldshadow, *newshadow; + struct spwd *pwd,*cur_pwd; + struct stat filestat; + + + if ( (pwd = getspnam(user)) == NULL) + return PAM_USER_UNKNOWN; + + if ( (oldshadow = fopen ("/etc/shadow", "r")) == NULL ) { + PAM_ERROR("Could not open /etc/shadow. Updating shadow \ + database cancelled."); + return (PAM_AUTHTOK_ERR); + } + + if ( (newshadow = fopen (NEW_SHADOW, "w")) == NULL ) { + PAM_ERROR("Could not open temp file. Updating shadow \ + database cancelled."); + fclose(oldshadow); + return (PAM_AUTHTOK_ERR); + } + + if (fstat(fileno(oldshadow), &filestat) == -1 ) { + PAM_ERROR("Could not get stat for /etc/shadow. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + if (fchown(fileno(newshadow), filestat.st_uid, filestat.st_gid) == -1 ) { + PAM_ERROR("Could not set uid/gid for new shadwow file. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + if (fchmod(fileno(newshadow), filestat.st_mode) == -1 ) { + PAM_ERROR("Could not chmod for new shadow file. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + while ( (cur_pwd = fgetspent(oldshadow)) ) { + if( strlen(user) == strlen(cur_pwd->sp_namp) + && !strncmp(cur_pwd->sp_namp, user, strlen(user))) { + cur_pwd->sp_pwdp = newhashedpwd; + cur_pwd->sp_lstchg = time(NULL) / (60 * 60 * 24); + PAM_LOG("Updated password for user [%s]",user); + } + + if(putspent(cur_pwd, newshadow)) { + PAM_ERROR("Error writing entry to new shadow file. \ + Updating shadow database cancelled."); + fclose(oldshadow); + fclose(newshadow); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + } + + fclose(oldshadow); + + if (fclose(newshadow)) { + PAM_ERROR("Error updating new shadow file."); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + /* + * If program flow has come up to here, all is good + * and it's safe to update the shadow file. + */ + + if( rename(NEW_SHADOW, "/etc/shadow") == 0 ) { + PAM_LOG("Password updated successfully for user [%s]",user); + } else { + PAM_ERROR("Error updating shadow file."); + unlink(NEW_SHADOW); + return (PAM_AUTHTOK_ERR); + } + + return (PAM_SUCCESS); + +} /* * Read hashed password for user from shadow entry. @@ -484,8 +598,8 @@ static char * read_shadow(const char * user) { } +#endif -#ifndef __linux__ /* * Mostly stolen from freebsd-lib's pam_unix module which was mostly * stolen from passwd(1)'s local_passwd.c @@ -513,8 +627,13 @@ makesalt(char salt[SALTSIZE]) { */ for (i = 0; i < SALTSIZE; i += 4) -// to64(&salt[i], arc4random(), 4); + +#ifndef __linux__ + to64(&salt[i], arc4random(), 4); +#else + to64(&salt[i], random(), 4); +#endif + salt[SALTSIZE] = '\0'; } -#endif PAM_MODULE_ENTRY("pam_unix") -- cgit v1.2.3-65-gdbad