From 98713a7b959ff3dd7197e3718b3002635c4e975d Mon Sep 17 00:00:00 2001 From: Charl Botha Date: Mon, 22 May 2000 20:55:34 +0000 Subject: Release 0.2 version. --- README | 21 ++- changelog | 15 ++ pam_pwdfile.c | 448 +++++++++++++++++++++++++++++++--------------------------- 3 files changed, 273 insertions(+), 211 deletions(-) create mode 100644 changelog diff --git a/README b/README index 4934098..3d65e7c 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ README for pam_pwdfile PAM module - Charl P. Botha -$Id: README,v 1.1.1.1 1999-08-05 13:09:07 cpbotha Exp $ +$Id: README,v 1.2 2000-05-22 20:55:34 cpbotha Exp $ --------------------------------------------------------------------------- -Let's say that this is version 0.1 of pam_pwdfile. +This is version 0.2 of pam_pwdfile. This pam module can be used for the authentication service only, in cases where one wants to use a different set of passwords than those in the main @@ -10,14 +10,21 @@ system password database. E.g. in our case we have an imap server running, and prefer to keep the imap passwords different from the system passwords for security reasons. -The /etc/pam.d/imap looks like this: +The /etc/pam.d/imap looks like this (e.g.) #%PAM-1.0 auth required /lib/security/pam_pwdfile.so pwdfile /etc/imap.passwd account required /lib/security/pam_pwdb.so At the moment the only parameters that pam_pwdfile.so parses for is "pwdfile", followed by the name of the ASCII password database, as in the -above example. +above example. Also, thanks to Jacob Schroeder , +pam_pwdfile now supports password file locking. Adding an "flock" parameter +activates this feature: pam_pwdfile uses and honours flock() file locking on +the specified password file. Specifying "noflock" or no flock-type +parameter at all deactivates this feature. + +Example: +auth required /lib/security/pam_pwdfile.so pwdfile /etc/blah.passwd flock The ASCII password file is simply a list of lines, each looking like this: username:crypted_passwd[13] @@ -25,6 +32,10 @@ username:crypted_passwd[13] Note that we still expect users to have accounts in the usual place, as we make use of the pam_pwdb.so module for the account service. This module is just so that one can have multiple sets of passwords for different services, -e.g. with our /etc/imap.passwd. +e.g. with our /etc/imap.passwd. It is however possible with certain +applications patched for pam (Cyrus IMAP server e.g.) that one does not need +the users to exist in the system database. These files have been created for inclusion into the PAM source tree. +Thanks to Michael-John Turner pam_pwdfile is available as a +debian package (libpam-pwdfile) from potato onwards. diff --git a/changelog b/changelog new file mode 100644 index 0000000..b8a4561 --- /dev/null +++ b/changelog @@ -0,0 +1,15 @@ +changelog for pam_pwdfile PAM module - Charl P. Botha +$Id: changelog,v 1.1 2000-05-22 20:55:34 cpbotha Exp $ +--------------------------------------------------------------------------- + +0.2: Mon May 22 22:41:30 SAST 2000 + +* integrated patch by Jacob Schroeder to implement + flock()-based password file locking +* minor code tweaks (indentation, unnecessary variables removed) +* minor README file updates +* this file + +0.1: ? + +* Initial release diff --git a/pam_pwdfile.c b/pam_pwdfile.c index cd5c828..2a3abd2 100644 --- a/pam_pwdfile.c +++ b/pam_pwdfile.c @@ -1,6 +1,6 @@ /* pam_pwdfile.c copyright 1999 by Charl P. Botha * - * $Id: pam_pwdfile.c,v 1.1.1.1 1999-08-05 13:09:07 cpbotha Exp $ + * $Id: pam_pwdfile.c,v 1.2 2000-05-22 20:55:34 cpbotha Exp $ * * pam authentication module that can be pointed at any username/crypted * text file so that pam using application can use an alternate set of @@ -52,6 +52,11 @@ #include #include #include +#include +#include +#include +#include +#include #define _XOPEN_SOURCE #include @@ -60,6 +65,7 @@ extern char *crypt(const char *key, const char *salt); #define PWDF_PARAM "pwdfile" +#define FLOCK_PARAM "flock" #define PWDFN_LEN 256 #define CRYPTEDPWD_LEN 13 @@ -74,83 +80,96 @@ int converse(pam_handle_t *, int, struct pam_message **, struct pam_response **) int _set_auth_tok(pam_handle_t *, int, int, const char **); /* logging function ripped from pam_listfile.c */ -static void _pam_log(int err, const char *format, ...) -{ - va_list args; +static void _pam_log(int err, const char *format, ...) { + va_list args; + + va_start(args, format); + openlog("pam_pwdfile", LOG_CONS|LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +static int lock_fd(int fd) { + int delay; - va_start(args, format); - openlog("pam_pwdfile", LOG_CONS|LOG_PID, LOG_AUTH); - vsyslog(err, format, args); - va_end(args); - closelog(); + for (delay = 5; delay <= 40; delay *= 2) { + if (flock(fd, LOCK_SH | LOCK_NB) == -1) { + /* failed */ + if (errno != EWOULDBLOCK) goto failed; + sleep(delay); + }else{ + return 0; + } + } + if (flock(fd, LOCK_SH | LOCK_NB) != -1) return 0; +failed: + return -1; } /* this function ripped from pam_unix/support.c */ int converse( pam_handle_t *pamh, int nargs, struct pam_message **message, - struct pam_response **response ) - -{ - int retval; - struct pam_conv *conv; - - retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; - if ( retval == PAM_SUCCESS ) - { - retval = conv->conv( nargs, - ( const struct pam_message ** ) message, - response, - conv->appdata_ptr ); - } - return retval; + struct pam_response **response ) { + int retval; + struct pam_conv *conv; + + retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; + if ( retval == PAM_SUCCESS ) { + retval = conv->conv( nargs, + ( const struct pam_message ** ) message, + response, + conv->appdata_ptr ); + } + return retval; } /* this function ripped from pam_unix/support.c */ int _set_auth_tok( pam_handle_t *pamh, int flags, int argc, const char **argv ) { - int retval; - char *p; + int retval; + char *p; + + struct pam_message msg[1],*pmsg[1]; + struct pam_response *resp; + + /* set up conversation call */ + + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_PROMPT_ECHO_OFF; + msg[0].msg = "Password: "; + resp = NULL; + + if ( ( retval = converse( pamh, 1 , pmsg, &resp ) ) != PAM_SUCCESS ) + return retval; + + if ( resp ) + { + if ( ( flags & PAM_DISALLOW_NULL_AUTHTOK ) && + resp[0].resp == NULL ) + { + free( resp ); + return PAM_AUTH_ERR; + } - struct pam_message msg[1],*pmsg[1]; - struct pam_response *resp; - - /* set up conversation call */ - - pmsg[0] = &msg[0]; - msg[0].msg_style = PAM_PROMPT_ECHO_OFF; - msg[0].msg = "Password: "; - resp = NULL; - - if ( ( retval = converse( pamh, 1 , pmsg, &resp ) ) != PAM_SUCCESS ) - return retval; - - if ( resp ) - { - if ( ( flags & PAM_DISALLOW_NULL_AUTHTOK ) && - resp[0].resp == NULL ) - { - free( resp ); - return PAM_AUTH_ERR; - } - - p = resp[ 0 ].resp; - - /* This could be a memory leak. If resp[0].resp - is malloc()ed, then it has to be free()ed! - -- alex - */ - - resp[ 0 ].resp = NULL; - - } - else - return PAM_CONV_ERR; - - free( resp ); - pam_set_item( pamh, PAM_AUTHTOK, p ); - return PAM_SUCCESS; + p = resp[ 0 ].resp; + + /* This could be a memory leak. If resp[0].resp + is malloc()ed, then it has to be free()ed! + -- alex + */ + + resp[ 0 ].resp = NULL; + + } + else + return PAM_CONV_ERR; + + free( resp ); + pam_set_item( pamh, PAM_AUTHTOK, p ); + return PAM_SUCCESS; } @@ -159,168 +178,185 @@ int _set_auth_tok( pam_handle_t *pamh, * if unsucessful, returns 0 */ static int fgetpwnam(FILE *stream, const char *name, char *password) { - char tempLine[256], *tpointer, *curname, *curpass, *fgr; - int loopdone, pwdfound; - - /* go to beginning of file */ - rewind(stream); - /* some control variables */ - loopdone = pwdfound = 0; - /* iterate through lines in file, until end of file */ - do { - /* get the current line */ - fgr = fgets(tempLine,256,stream); - /* if it's valid, go on */ - if ( fgr != NULL) { - /* first get the username out */ - tpointer = tempLine; - curname = strsep(&tpointer,":"); - /* check to see if it's the right one */ - if (strcmp(curname,name)==0) { - /* at least we know our loop is done */ - loopdone = 1; - /* get the password and put it in its place */ - curpass = strsep(&tpointer,":"); - if (curpass != NULL) { - strncpy(password,curpass,CRYPTEDPWD_LEN+1); - pwdfound = 1; - } /* if (curpass... */ - } /* if (strcmp(curname... */ - } /* if (tempLine... */ - } while (fgr != NULL); - return pwdfound; + char tempLine[256], *tpointer, *curname, *curpass, *fgr; + int loopdone, pwdfound; + + /* go to beginning of file */ + rewind(stream); + /* some control variables */ + loopdone = pwdfound = 0; + /* iterate through lines in file, until end of file */ + do { + /* get the current line */ + fgr = fgets(tempLine,256,stream); + /* if it's valid, go on */ + if ( fgr != NULL) { + /* first get the username out */ + tpointer = tempLine; + curname = strsep(&tpointer,":"); + /* check to see if it's the right one */ + if (strcmp(curname,name)==0) { + /* at least we know our loop is done */ + loopdone = 1; + /* get the password and put it in its place */ + curpass = strsep(&tpointer,":"); + if (curpass != NULL) { + strncpy(password,curpass,CRYPTEDPWD_LEN+1); + pwdfound = 1; + } /* if (curpass... */ + } /* if (strcmp(curname... */ + } /* if (tempLine... */ + } while (fgr != NULL); + return pwdfound; } /* expected hook for auth service */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { - int retval, pcnt, pwdfilename_found, loopdone; - const char *name; - char *password; - char pwdfilename[PWDFN_LEN]; - char salt[3], crypted_password[CRYPTEDPWD_LEN+1]; - FILE *pwdfile; + int retval, pcnt, pwdfilename_found; + const char *name; + char *password; + char pwdfilename[PWDFN_LEN]; + char salt[3], crypted_password[CRYPTEDPWD_LEN+1]; + FILE *pwdfile; + int use_flock = 0; - /* we require the pwdfile switch and argument to be present, else we don't work */ - /* pcnt is the parameter counter variable for iterating through argv - * loopdone is an extra loop control variable */ - pcnt = loopdone = pwdfilename_found = 0; - do { - /* see if the current parameter looks like "pwdfile" */ - if (strcmp(argv[pcnt],PWDF_PARAM)==0) { - /* if argv is long enough, grab the subsequent parameter */ - if (pcnt+1 < argc) { - /* make sure we can't overflow */ - strncpy(pwdfilename,argv[pcnt+1],PWDFN_LEN); - /* indicate that we've found it */ - pwdfilename_found = 1; + /* we require the pwdfile switch and argument to be present, else we don't work */ + /* pcnt is the parameter counter variable for iterating through argv */ + pcnt = pwdfilename_found = 0; + do { + /* see if the current parameter looks like "pwdfile" */ + if (strcmp(argv[pcnt],PWDF_PARAM)==0) { + /* if argv is long enough, grab the subsequent parameter */ + if (pcnt+1 < argc) { + /* make sure we can't overflow */ + strncpy(pwdfilename,argv[++pcnt],PWDFN_LEN); + /* indicate that we've found it */ + pwdfilename_found = 1; + } + /* also check for "pwdfile=blah" */ + } else if (strncmp(argv[pcnt],PWDF_PARAM "=",sizeof(PWDF_PARAM "=")-1)==0) { + /* make sure we can't overflow */ + strncpy(pwdfilename,argv[pcnt]+sizeof(PWDF_PARAM),PWDFN_LEN); + /* indicate that we've found it */ + pwdfilename_found = 1; + } else if (strcmp(argv[pcnt],FLOCK_PARAM)==0) { + /* we have a "flock" parameter */ + use_flock = 1; + } else if (strcmp(argv[pcnt],"no" FLOCK_PARAM)==0) { + /* or a "noflock" parameter */ + use_flock = 0; } - /* whether we actually found the name or not, this loop is done, - * as we have found the pwdfile switch itself */ - loopdone = 1; - } - } while (!loopdone && pcnt++ < argc); - - /* for some or other reason, the password file wasn't specified */ - if (!pwdfilename_found) { - _pam_log(LOG_ERR,"password file name not specified"); - return PAM_AUTHINFO_UNAVAIL; - } - - /* DEBUG */ - D(_pam_log(LOG_ERR, "password filename extracted")); - - /* now try to open the password file */ - if ((pwdfile=fopen(pwdfilename,"r"))==NULL) { - _pam_log(LOG_ERR,"couldn't open password file %s",pwdfilename); - return PAM_AUTHINFO_UNAVAIL; - } - - /* get user name */ - if ((retval = pam_get_user(pamh,&name,"login: ")) != PAM_SUCCESS) { - _pam_log(LOG_ERR, "username not found"); - fclose(pwdfile); - return retval; - } - - /* DEBUG */ - D(_pam_log(LOG_ERR,"username is %s", name)); - /* get password - code from pam_unix_auth.c */ - pam_get_item(pamh, PAM_AUTHTOK, (void *)&password); - if (!password) { - retval = _set_auth_tok(pamh, flags, argc, argv); - if (retval!=PAM_SUCCESS) { + } while (++pcnt < argc); + + /* for some or other reason, the password file wasn't specified */ + if (!pwdfilename_found) { + _pam_log(LOG_ERR,"password file name not specified"); + return PAM_AUTHINFO_UNAVAIL; + } + + /* DEBUG */ + D(_pam_log(LOG_ERR, "password filename extracted")); + + /* now try to open the password file */ + if ((pwdfile=fopen(pwdfilename,"r"))==NULL) { + _pam_log(LOG_ERR,"couldn't open password file %s",pwdfilename); + return PAM_AUTHINFO_UNAVAIL; + } + + /* set a lock on the password file */ + if (use_flock && lock_fd(fileno(pwdfile)) == -1) { + _pam_log(LOG_ERR,"couldn't lock password file %s",pwdfilename); + return PAM_AUTHINFO_UNAVAIL; + } + + /* get user name */ + if ((retval = pam_get_user(pamh,&name,"login: ")) != PAM_SUCCESS) { + _pam_log(LOG_ERR, "username not found"); fclose(pwdfile); return retval; - } - } - pam_get_item(pamh, PAM_AUTHTOK, (void *)&password); - - if ((retval = pam_get_item(pamh, PAM_AUTHTOK, (void *)&password)) != PAM_SUCCESS) { - _pam_log(LOG_ERR, "auth token not found"); - fclose(pwdfile); - return retval; - } - - /* DEBUG */ - D(_pam_log(LOG_ERR,"got password from user", password)); - - /* now crypt password and compare to the user entry in the password file */ - /* first make sure password is long enough -- may I do this? */ - if (strlen(password)<2 || password==NULL) { - _pam_log(LOG_ERR,"password too short or NULL"); - fclose(pwdfile); - return PAM_AUTH_ERR; - } - - /* get the crypted password corresponding to this user */ - if (!fgetpwnam(pwdfile, name, crypted_password)) { - _pam_log(LOG_ERR,"password file corrupt"); - fclose(pwdfile); - return PAM_AUTHINFO_UNAVAIL; - } - - /* DEBUG */ - D(_pam_log(LOG_ERR,"got crypted password == %s", crypted_password)); - - /* extract the salt */ - salt[0] = crypted_password[0]; salt[1] = crypted_password[1]; salt[2] = '\0'; - - /* DEBUG */ - D(_pam_log(LOG_ERR,"user password crypted is %s", crypt(password,salt))); - - /* if things don't match up, complain */ - crypted_password[CRYPTEDPWD_LEN] = '\0'; - if (strcmp(crypt(password,salt),crypted_password)!=0) { - _pam_log(LOG_ERR,"wrong password for user %s",name); - fclose(pwdfile); - return PAM_AUTH_ERR; - } + } - /* DEBUG */ - D(_pam_log(LOG_ERR,"passwords match")); - - /* we've gotten here, i.e. authentication was sucessful! */ - fclose(pwdfile); - return PAM_SUCCESS; + + /* DEBUG */ + D(_pam_log(LOG_ERR,"username is %s", name)); + + /* get password - code from pam_unix_auth.c */ + pam_get_item(pamh, PAM_AUTHTOK, (void *)&password); + if (!password) { + retval = _set_auth_tok(pamh, flags, argc, argv); + if (retval!=PAM_SUCCESS) { + fclose(pwdfile); + return retval; + } + } + pam_get_item(pamh, PAM_AUTHTOK, (void *)&password); + + if ((retval = pam_get_item(pamh, PAM_AUTHTOK, (void *)&password)) != PAM_SUCCESS) { + _pam_log(LOG_ERR, "auth token not found"); + fclose(pwdfile); + return retval; + } + + /* DEBUG */ + D(_pam_log(LOG_ERR,"got password from user", password)); + + /* now crypt password and compare to the user entry in the password file */ + /* first make sure password is long enough -- may I do this? */ + if (strlen(password)<2 || password==NULL) { + _pam_log(LOG_ERR,"password too short or NULL"); + fclose(pwdfile); + return PAM_AUTH_ERR; + } + + /* get the crypted password corresponding to this user */ + if (!fgetpwnam(pwdfile, name, crypted_password)) { + _pam_log(LOG_ERR,"password file corrupt"); + fclose(pwdfile); + return PAM_AUTHINFO_UNAVAIL; + } + + /* DEBUG */ + D(_pam_log(LOG_ERR,"got crypted password == %s", crypted_password)); + + /* extract the salt */ + salt[0] = crypted_password[0]; salt[1] = crypted_password[1]; salt[2] = '\0'; + + /* DEBUG */ + D(_pam_log(LOG_ERR,"user password crypted is %s", crypt(password,salt))); + + /* if things don't match up, complain */ + crypted_password[CRYPTEDPWD_LEN] = '\0'; + if (strcmp(crypt(password,salt),crypted_password)!=0) { + _pam_log(LOG_ERR,"wrong password for user %s",name); + fclose(pwdfile); + return PAM_AUTH_ERR; + } + + /* DEBUG */ + D(_pam_log(LOG_ERR,"passwords match")); + + /* we've gotten here, i.e. authentication was sucessful! */ + fclose(pwdfile); + return PAM_SUCCESS; } /* another expected hook */ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { - return PAM_SUCCESS; + return PAM_SUCCESS; } #ifdef PAM_STATIC struct pam_module _pam_listfile_modstruct = { - "pam_pwdfile", - pam_sm_authenticate, - pam_sm_setcred, - NULL, - NULL, - NULL, - NULL, + "pam_pwdfile", + pam_sm_authenticate, + pam_sm_setcred, + NULL, + NULL, + NULL, + NULL, }; #endif -- cgit v1.2.3