Feature request: alternate '/' for pam_unix

Jozsef Kadlecsik kadlec at blackhole.kfki.hu
Fri Sep 28 12:36:25 UTC 2007


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 at blackhole.kfki.hu, kadlec at sunserv.kfki.hu
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 */




More information about the Pam-list mailing list