Hi, In a cluster environment it'd be quite good if one could specify alternate passwd/shadow/group files for the pam_unix module. I know, one could use LDAP, SQL or db files to store the user data, but all of them have got some shortcomings: LDAP and SQL can be slow or complex to setup in a redundan configuration; db files lack password expiration information, etc. In the patch below I implemented the 'rootdir=directory' option for the pam_unix module, by which one can define an alternate root directory when looking up the files. So one can store alternate passwd, etc. files with the user data on a cluster (shared) filesystem, without the need of additional services running. Example: /etc/pam.d/common-auth # Check the user in the host-local files auth sufficient pam_unix.so # Check the user in the cluster files: # /gfs/system/etc/passwd, etc auth required pam_unix.so rootdir=/gfs/system Best regards, Jozsef Kadlecsik - E-mail : kadlec@xxxxxxxxxxxxxxxxx, kadlec@xxxxxxxxxxxxxxx PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt Address : KFKI Research Institute for Particle and Nuclear Physics H-1525 Budapest 114, POB. 49, Hungary diff -urN pam_unix.orig/pam_unix_passwd.c pam_unix/pam_unix_passwd.c --- pam_unix.orig/pam_unix_passwd.c 2007-04-30 12:47:30.000000000 +0200 +++ pam_unix/pam_unix_passwd.c 2007-09-28 13:02:42.000000000 +0200 @@ -119,13 +119,9 @@ #define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS" #define MAX_PASSWD_TRIES 3 -#define PW_TMPFILE "/etc/npasswd" -#define SH_TMPFILE "/etc/nshadow" #ifndef CRACKLIB_DICTS #define CRACKLIB_DICTS NULL #endif -#define OPW_TMPFILE "/etc/security/nopasswd" -#define OLD_PASSWORDS_FILE "/etc/security/opasswd" /* * i64c - convert an integer to a radix 64 character @@ -247,7 +243,7 @@ size_t i=0; struct rlimit rlim; static char *envp[] = { NULL }; - char *args[] = { NULL, NULL, NULL, NULL }; + char *args[] = { NULL, NULL, NULL, NULL, NULL }; /* XXX - should really tidy up PAM here too */ @@ -273,6 +269,8 @@ args[0] = x_strdup(CHKPWD_HELPER); args[1] = x_strdup(user); args[2] = x_strdup("shadow"); + if (strlen(rootdir) > 0) + args[3] = x_strdup(rootdir)); execve(CHKPWD_HELPER, args, envp); @@ -324,7 +322,7 @@ int retval = PAM_SUCCESS; FILE *opwfile; - opwfile = fopen(OLD_PASSWORDS_FILE, "r"); + opwfile = fopen(unix_filename(OLD_PASSWORDS_FILE), "r"); if (opwfile == NULL) return PAM_ABORT; @@ -382,7 +380,7 @@ #ifdef WITH_SELINUX if (SELINUX_ENABLED) { security_context_t passwd_context=NULL; - if (getfilecon("/etc/passwd",&passwd_context)<0) { + if (getfilecon(unix_filename(PASSWD_FILE),&passwd_context)<0) { return PAM_AUTHTOK_ERR; }; if (getfscreatecon(&prev_context)<0) { @@ -397,14 +395,14 @@ freecon(passwd_context); } #endif - pwfile = fopen(OPW_TMPFILE, "w"); + pwfile = fopen(unix_filename(OLD_PASSWORDS_TMP_FILE), "w"); umask(oldmask); if (pwfile == NULL) { err = 1; goto done; } - opwfile = fopen(OLD_PASSWORDS_FILE, "r"); + opwfile = fopen(unix_filename(OLD_PASSWORDS_FILE), "r"); if (opwfile == NULL) { fclose(pwfile); err = 1; @@ -488,7 +486,8 @@ done: if (!err) { - if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE)) + if (rename(unix_filename(OLD_PASSWORDS_TMP_FILE), + unix_filename(OLD_PASSWORDS_FILE))) err = 1; } #ifdef WITH_SELINUX @@ -504,7 +503,7 @@ if (!err) { return PAM_SUCCESS; } else { - unlink(OPW_TMPFILE); + unlink(unix_filename(OLD_PASSWORDS_TMP_FILE)); return PAM_AUTHTOK_ERR; } } @@ -522,7 +521,7 @@ #ifdef WITH_SELINUX if (SELINUX_ENABLED) { security_context_t passwd_context=NULL; - if (getfilecon("/etc/passwd",&passwd_context)<0) { + if (getfilecon(unix_filename(PASSWD_FILE),&passwd_context)<0) { return PAM_AUTHTOK_ERR; }; if (getfscreatecon(&prev_context)<0) { @@ -537,14 +536,14 @@ freecon(passwd_context); } #endif - pwfile = fopen(PW_TMPFILE, "w"); + pwfile = fopen(unix_filename(PASSWD_TMP_FILE), "w"); umask(oldmask); if (pwfile == NULL) { err = 1; goto done; } - opwfile = fopen("/etc/passwd", "r"); + opwfile = fopen(unix_filename(PASSWD_FILE), "r"); if (opwfile == NULL) { fclose(pwfile); err = 1; @@ -600,7 +599,7 @@ done: if (!err) { - if (!rename(PW_TMPFILE, "/etc/passwd")) + if (!rename(unix_filename(PASSWD_TMP_FILE), unix_filename(PASSWD_FILE))) pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho); else err = 1; @@ -618,7 +617,7 @@ if (!err) { return PAM_SUCCESS; } else { - unlink(PW_TMPFILE); + unlink(unix_filename(PASSWD_TMP_FILE)); return PAM_AUTHTOK_ERR; } } @@ -640,7 +639,7 @@ #ifdef WITH_SELINUX if (SELINUX_ENABLED) { security_context_t shadow_context=NULL; - if (getfilecon("/etc/shadow",&shadow_context)<0) { + if (getfilecon(unix_filename(SHADOW_FILE),&shadow_context)<0) { return PAM_AUTHTOK_ERR; }; if (getfscreatecon(&prev_context)<0) { @@ -655,14 +654,14 @@ freecon(shadow_context); } #endif - pwfile = fopen(SH_TMPFILE, "w"); + pwfile = fopen(unix_filename(SHADOW_TMP_FILE), "w"); umask(oldmask); if (pwfile == NULL) { err = 1; goto done; } - opwfile = fopen("/etc/shadow", "r"); + opwfile = fopen(unix_filename(SHADOW_FILE), "r"); if (opwfile == NULL) { fclose(pwfile); err = 1; @@ -716,7 +715,7 @@ done: if (!err) { - if (!rename(SH_TMPFILE, "/etc/shadow")) + if (!rename(unix_filename(SHADOW_TMP_FILE), unix_filename(SHADOW_FILE))) pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho); else err = 1; @@ -736,7 +735,7 @@ if (!err) { return PAM_SUCCESS; } else { - unlink(SH_TMPFILE); + unlink(unix_filename(SHADOW_TMP_FILE)); return PAM_AUTHTOK_ERR; } } @@ -1003,7 +1002,7 @@ remark = _("Password has been already used. Choose another."); if (retval == PAM_ABORT) { pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords", - OLD_PASSWORDS_FILE); + unix_filename(OLD_PASSWORDS_FILE)); return retval; } } @@ -1067,8 +1066,10 @@ */ if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) { pam_syslog(pamh, LOG_DEBUG, - "user \"%s\" does not exist in /etc/passwd%s", - user, on(UNIX_NIS, ctrl) ? " or NIS" : ""); + "user \"%s\" does not exist in %s%s", + user, + unix_filename(PASSWD_FILE), + on(UNIX_NIS, ctrl) ? " or NIS" : ""); return PAM_USER_UNKNOWN; } else { struct passwd *pwd; diff -urN pam_unix.orig/support.c pam_unix/support.c --- pam_unix.orig/support.c 2007-02-06 17:06:45.000000000 +0100 +++ pam_unix/support.c 2007-09-28 13:32:22.000000000 +0200 @@ -36,6 +36,41 @@ #define SELINUX_ENABLED 0 #endif +/* The actual files we may open under rootdir */ +static char unix_files[UNIX_FILES_MAX][1024+22] = { + [PASSWD_FILE] = "/etc/passwd", + [PASSWD_TMP_FILE] = "/etc/npasswd", + [SHADOW_FILE] = "/etc/shadow", + [SHADOW_TMP_FILE] = "/etc/nshadow", + [OLD_PASSWORDS_FILE] = "/etc/security/opasswd", + [OLD_PASSWORDS_TMP_FILE] = "/etc/security/nopasswd", +}; +char rootdir[1024] = ""; + +/* Map file type to filename */ +char *unix_filename(unix_file_t type) +{ + return unix_files[type]; +} + +static void prepend_rootdir(const char *dir) +{ + unsigned int i; + char filename[1024]; + size_t len; + + if (strcmp(dir, rootdir) == 0) + return; + + len = strlen(rootdir); + strcpy(rootdir, dir); + for (i = 0; i < UNIX_FILES_MAX; i++) { + strcpy(filename, rootdir); + strcat(filename, unix_files[i]+len); + strcpy(unix_files[i], filename); + } +} + /* this is a front-end for module-application conversations */ int _make_remark(pam_handle_t * pamh, unsigned int ctrl, @@ -110,6 +145,18 @@ *remember = 400; } } + if (j == UNIX_ROOTDIR) { + char *filename = strchr(*argv, '='); + + if (filename == NULL) + pam_syslog(pamh, LOG_ERR, + "invalid rootdir option [%s]", *argv); + else if (strlen(filename) > 1024) + pam_syslog(pamh, LOG_ERR, + "rootdir option too long [%s]", *argv); + else + prepend_rootdir(++filename); + } } ++argv; /* step to next argument */ @@ -237,7 +284,7 @@ if (!matched && files) { int userlen = strlen(name); - passwd = fopen("/etc/passwd", "r"); + passwd = fopen(unix_filename(PASSWD_FILE), "r"); if (passwd != NULL) { while (fgets(buf, sizeof(buf), passwd) != NULL) { if ((buf[userlen] == ':') && diff -urN pam_unix.orig/support.h pam_unix/support.h --- pam_unix.orig/support.h 2007-01-23 10:30:23.000000000 +0100 +++ pam_unix/support.h 2007-09-28 13:22:29.000000000 +0200 @@ -84,8 +84,9 @@ #define UNIX_NOREAP 21 /* don't reap child process */ #define UNIX_BROKEN_SHADOW 22 /* ignore errors reading password aging * information during acct management */ +#define UNIX_ROOTDIR 23 /* Directory instead of '/' to use */ /* -------------- */ -#define UNIX_CTRLS_ 23 /* number of ctrl arguments defined */ +#define UNIX_CTRLS_ 24 /* number of ctrl arguments defined */ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = @@ -116,10 +117,24 @@ /* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000}, /* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000}, /* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000}, +/* UNIX_ROOTDIR */ {"rootdir=", _ALL_ON_, 020000000}, }; #define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag) +/* Files we may open under rootdir */ +typedef enum { + PASSWD_FILE = 0, + PASSWD_TMP_FILE, + SHADOW_FILE, + SHADOW_TMP_FILE, + OLD_PASSWORDS_FILE, + OLD_PASSWORDS_TMP_FILE, + UNIX_FILES_MAX, +} unix_file_t; + +extern char *unix_filename(unix_file_t type); +extern char rootdir[1024]; /* use this to free strings. ESPECIALLY password strings */ diff -urN pam_unix.orig/unix_chkpwd.c pam_unix/unix_chkpwd.c --- pam_unix.orig/unix_chkpwd.c 2007-03-12 15:35:14.000000000 +0100 +++ pam_unix/unix_chkpwd.c 2007-09-28 12:58:09.000000000 +0200 @@ -42,6 +42,9 @@ #include "md5.h" #include "bigcrypt.h" +static char shadowfile[1024+12] = "/etc/shadow"; +static char nshadowfile[1024+12] = "/etc/nshadow"; + /* syslogging function for errors and other information */ static void _log_err(int err, const char *format,...) @@ -261,8 +264,7 @@ return username; } -#define SH_TMPFILE "/etc/nshadow" -static int _update_shadow(const char *forwho) +static int _update_shadow(const char *forwho, const char *rootdir) { struct spwd *spwdent = NULL, *stmpent = NULL; FILE *pwfile, *opwfile; @@ -273,6 +275,17 @@ char towhat[MAXPASS + 1]; int npass=0; + if (rootdir != NULL) { + if (strlen(rootdir) >= 1024) { + _log_err(LOG_DEBUG, "rootdir argument too long"); + return PAM_AUTHTOK_ERR; + } + strcpy(shadowfile, rootdir); + strcpy(nshadowfile, rootdir); + strcat(shadowfile, "/etc/shadow"); + strcat(nshadowfile, "/etc/nshadow"); + } + /* read the password from stdin (a pipe from the pam_unix module) */ npass = read(STDIN_FILENO, pass, MAXPASS); @@ -323,7 +336,7 @@ #ifdef WITH_SELINUX if (SELINUX_ENABLED) { security_context_t shadow_context=NULL; - if (getfilecon("/etc/shadow",&shadow_context)<0) { + if (getfilecon(shadowfile,&shadow_context)<0) { return PAM_AUTHTOK_ERR; }; if (getfscreatecon(&prev_context)<0) { @@ -338,14 +351,14 @@ freecon(shadow_context); } #endif - pwfile = fopen(SH_TMPFILE, "w"); + pwfile = fopen(nshadowfile, "w"); umask(oldmask); if (pwfile == NULL) { err = 1; goto done; } - opwfile = fopen("/etc/shadow", "r"); + opwfile = fopen(shadowfile, "r"); if (opwfile == NULL) { fclose(pwfile); err = 1; @@ -399,7 +412,7 @@ done: if (!err) { - if (rename(SH_TMPFILE, "/etc/shadow")) + if (rename(nshadowfile, shadowfile)) err = 1; } @@ -417,7 +430,7 @@ if (!err) { return PAM_SUCCESS; } else { - unlink(SH_TMPFILE); + unlink(nshadowfile); return PAM_AUTHTOK_ERR; } } @@ -445,7 +458,7 @@ * account). */ - if (isatty(STDIN_FILENO) || argc != 3 ) { + if (isatty(STDIN_FILENO) || argc < 3 ) { _log_err(LOG_NOTICE ,"inappropriate use of Unix helper binary [UID=%d]" ,getuid()); @@ -476,14 +489,14 @@ option=argv[2]; - if (strncmp(argv[2], "verify", 8) == 0) { + if (strncmp(option, "verify", 8) == 0) { /* Get the account information from the shadow file */ return _verify_account(argv[1]); } if (strncmp(option, "shadow", 8) == 0) { /* Attempting to change the password */ - return _update_shadow(argv[1]); + return _update_shadow(argv[1], argc > 3 ? argv[3] : NULL); } /* read the nullok/nonull option */ _______________________________________________ Pam-list mailing list Pam-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/pam-list