Subject: bin/7366: pwd_mkdb improvement
To: None <gnats-bugs@gnats.netbsd.org>
From: Takahiro Kambe <taca@sky.yamashina.kyoto.jp>
List: netbsd-bugs
Date: 04/13/1999 01:05:19
>Number:         7366
>Category:       bin
>Synopsis:       pwd_mkdb(8) -p option.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Apr 12 09:20:01 1999
>Last-Modified:
>Originator:     Takahiro Kambe
>Organization:
>Release:        NetBSD-current 1999/4/2
>Environment:
System: NetBSD edge.sky.yamashina.kyoto.jp 1.4_ALPHA NetBSD 1.4_ALPHA (ALN4PCVT) #34: Sun Apr 4 20:14:43 JST 1999 taca@edge.sky.yamashina.kyoto.jp:/usr/src/sys/arch/i386/compile/ALN4PCVT i386


>Description:
	FreeBSD 2.2.8's pwd_mkdb(8) has -u option, which update only one user's
	DB recored.  This imporve perforemanse under many users in the passwd
	file.  I ported this option into NetBSD current, borrows codes from
	FreeBSD.
>How-To-Repeat:
>Fix:
	I attached the patch, which simply introduce -u option to pwd_mkdb(8),
	and some modification to passwd(1) and chpass(1) for utilizing it.

	Some discussion:

	1. I want to add an argument pw_mkdb(3) which lives in libutil(3),
	   src/lib/libutil/passwd.c:

		int pw_mkdb(char *username);

	   If username is NULL, it act as current pw_mkdb(3).  But this breaks
	   binary compatibility, at least some period.
	   So I added pw_mkdb_user() to commands which use pw_mkdb(3).

	2. Pwd_mkdb(8) scan the file twice which supplied it's argument.
	   So, more improvement might be done.

	3. Handling of temporary file might be buggy in this patch.

Thnaks in advise.

--
taca


Index: usr.sbin/pwd_mkdb/pwd_mkdb.c
--- usr.sbin/pwd_mkdb/pwd_mkdb.c.orig	Thu Jan 14 00:45:30 1999
+++ usr.sbin/pwd_mkdb/pwd_mkdb.c	Mon Apr 12 00:00:18 1999
@@ -91,9 +91,11 @@
 void	error __P((char *));
 void	wr_error __P((char *));
 int	main __P((int, char **));
+void	cp __P((char *, char *, mode_t));
 void	mv __P((char *, char *));
 void	rm __P((char *));
 int	scan __P((FILE *, struct passwd *, int *));
+int	check_user_db __P((char *));
 void	usage __P((void));
 
 int
@@ -105,17 +107,22 @@
 	DBT data, key;
 	FILE *fp, *oldfp;
 	sigset_t set;
-	int ch, cnt, len, makeold, tfd, flags;
+	int ch, cnt, len, makeold, tfd, flags, fflag;
 	char *p;
 	const char *t;
 	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
+	char pwd_db[MAXPATHLEN], pwd_sdb[MAXPATHLEN];
 	int hasyp = 0;
 	DBT ypdata, ypkey;
+	char *username;
+	int method, methoduid;
 
 	oldfp = NULL;
 	strcpy(prefix, "/");
 	makeold = 0;
-	while ((ch = getopt(argc, argv, "d:pv")) != -1)
+	username = NULL;
+
+	while ((ch = getopt(argc, argv, "cd:pu:v")) != -1)
 		switch(ch) {
 		case 'd':
 			strncpy(prefix, optarg, sizeof(prefix));
@@ -124,6 +131,9 @@
 		case 'p':			/* create V7 "file.orig" */
 			makeold = 1;
 			break;
+		case 'u':			/* only update this record */
+			username = optarg;
+			break;
 		case 'v':			/* backward compatible */
 			break;
 		case '?':
@@ -133,7 +143,7 @@
 	argc -= optind;
 	argv += optind;
 
-	if (argc != 1)
+	if (argc != 1 || (username && (*username == '+' || *username == '-')))
 		usage();
 
 	/*
@@ -156,11 +166,25 @@
 	if (!(fp = fopen(pname, "r")))
 		error(pname);
 
-	/* Open the temporary insecure password database. */
 	(void)snprintf(pwd_db_tmp, sizeof(pwd_db_tmp), "%s%s.tmp", prefix,
 		_PATH_MP_DB);
-	dp = dbopen(pwd_db_tmp,
-	    O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
+
+	if (username) {
+		(void)snprintf(pwd_db, sizeof(pwd_db), "%s/%s", prefix,
+			_PATH_MP_DB);
+		clean = FILE_INSECURE;
+		cp(pwd_db, pwd_db_tmp, PERM_INSECURE);
+		fflag = O_RDWR;
+		method = 0;
+		methoduid = check_user_db(username);
+	} else {
+		fflag = O_RDWR|O_CREAT|O_EXCL;
+		method = R_NOOVERWRITE;
+		methoduid = R_NOOVERWRITE;
+	}
+
+	/* Open the temporary insecure password database. */
+	dp = dbopen(pwd_db_tmp, fflag, PERM_INSECURE, DB_HASH, &openinfo);
 	if (dp == NULL)
 		error(pwd_db_tmp);
 	clean = FILE_INSECURE;
@@ -220,47 +244,49 @@
 				    cnt);
 		}
 
-		/* Create insecure data. */
-		p = buf;
-		COMPACT(pwd.pw_name);
-		COMPACT("*");
-		memmove(p, &pwd.pw_uid, sizeof(int));
-		p += sizeof(int);
-		memmove(p, &pwd.pw_gid, sizeof(int));
-		p += sizeof(int);
-		memmove(p, &pwd.pw_change, sizeof(time_t));
-		p += sizeof(time_t);
-		COMPACT(pwd.pw_class);
-		COMPACT(pwd.pw_gecos);
-		COMPACT(pwd.pw_dir);
-		COMPACT(pwd.pw_shell);
-		memmove(p, &pwd.pw_expire, sizeof(time_t));
-		p += sizeof(time_t);
-		memmove(p, &flags, sizeof(int));
-		p += sizeof(int);
-		data.size = p - buf;
-
-		/* Store insecure by name. */
-		tbuf[0] = _PW_KEYBYNAME;
-		len = strlen(pwd.pw_name);
-		memmove(tbuf + 1, pwd.pw_name, len);
-		key.size = len + 1;
-		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
-			wr_error(pwd_db_tmp);
-
-		/* Store insecure by number. */
-		tbuf[0] = _PW_KEYBYNUM;
-		memmove(tbuf + 1, &cnt, sizeof(cnt));
-		key.size = sizeof(cnt) + 1;
-		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
+		if (!username || (strcmp(username, pwd.pw_name) == 0)) {
+			/* Create insecure data. */
+			p = buf;
+			COMPACT(pwd.pw_name);
+			COMPACT("*");
+			memmove(p, &pwd.pw_uid, sizeof(int));
+			p += sizeof(int);
+			memmove(p, &pwd.pw_gid, sizeof(int));
+			p += sizeof(int);
+			memmove(p, &pwd.pw_change, sizeof(time_t));
+			p += sizeof(time_t);
+			COMPACT(pwd.pw_class);
+			COMPACT(pwd.pw_gecos);
+			COMPACT(pwd.pw_dir);
+			COMPACT(pwd.pw_shell);
+			memmove(p, &pwd.pw_expire, sizeof(time_t));
+			p += sizeof(time_t);
+			memmove(p, &flags, sizeof(int));
+			p += sizeof(int);
+			data.size = p - buf;
+
+			/* Store insecure by name. */
+			tbuf[0] = _PW_KEYBYNAME;
+			len = strlen(pwd.pw_name);
+			memmove(tbuf + 1, pwd.pw_name, len);
+			key.size = len + 1;
+			if ((dp->put)(dp, &key, &data, method) == -1)
 			wr_error(pwd_db_tmp);
 
-		/* Store insecure by uid. */
-		tbuf[0] = _PW_KEYBYUID;
-		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
-		key.size = sizeof(pwd.pw_uid) + 1;
-		if ((dp->put)(dp, &key, &data, R_NOOVERWRITE) == -1)
-			wr_error(pwd_db_tmp);
+			/* Store insecure by number. */
+			tbuf[0] = _PW_KEYBYNUM;
+			memmove(tbuf + 1, &cnt, sizeof(cnt));
+			key.size = sizeof(cnt) + 1;
+			if ((dp->put)(dp, &key, &data, method) == -1)
+				wr_error(pwd_db_tmp);
+
+			/* Store insecure by uid. */
+			tbuf[0] = _PW_KEYBYUID;
+			memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
+			key.size = sizeof(pwd.pw_uid) + 1;
+			if ((dp->put)(dp, &key, &data, methoduid) == -1)
+				wr_error(pwd_db_tmp);
+		}
 
 		/* Create original format password file entry */
 		if (makeold) {
@@ -274,13 +300,13 @@
 	}
 
 	/* Store YP token, if needed. */
-	if(hasyp) {
+	if (hasyp) {
 		ypkey.data = (u_char *)__yp_token;
 		ypkey.size = strlen(__yp_token);
 		ypdata.data = (u_char *)NULL;
 		ypdata.size = 0;
 
-		if ((dp->put)(dp, &ypkey, &ypdata, R_NOOVERWRITE) == -1)
+		if ((dp->put)(dp, &ypkey, &ypdata, method) == -1)
 			wr_error(pwd_db_tmp);
 	}
 
@@ -296,11 +322,22 @@
 		}
 	}
 
-	/* Open the temporary encrypted password database. */
 	(void)snprintf(pwd_Sdb_tmp, sizeof(pwd_Sdb_tmp), "%s%s.tmp", prefix,
 		_PATH_SMP_DB);
-	edp = dbopen(pwd_Sdb_tmp,
-	    O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
+
+	if (username) {
+		(void)snprintf(pwd_sdb, sizeof(pwd_sdb), "%s/%s", prefix,
+			_PATH_SMP_DB);
+
+		clean = FILE_SECURE;
+		cp(pwd_sdb, pwd_Sdb_tmp, PERM_SECURE);
+		fflag = O_RDWR;
+	} else {
+		fflag = O_RDWR|O_CREAT|O_EXCL;
+	}
+
+	/* Open the temporary encrypted password database. */
+	edp = dbopen(pwd_Sdb_tmp, fflag, PERM_SECURE, DB_HASH, &openinfo);
 	if (!edp)
 		error(pwd_Sdb_tmp);
 	clean = FILE_SECURE;
@@ -308,57 +345,59 @@
 	rewind(fp);
 	for (cnt = 1; scan(fp, &pwd, &flags); ++cnt) {
 
-		/* Create secure data. */
-		p = buf;
-		COMPACT(pwd.pw_name);
-		COMPACT(pwd.pw_passwd);
-		memmove(p, &pwd.pw_uid, sizeof(int));
-		p += sizeof(int);
-		memmove(p, &pwd.pw_gid, sizeof(int));
-		p += sizeof(int);
-		memmove(p, &pwd.pw_change, sizeof(time_t));
-		p += sizeof(time_t);
-		COMPACT(pwd.pw_class);
-		COMPACT(pwd.pw_gecos);
-		COMPACT(pwd.pw_dir);
-		COMPACT(pwd.pw_shell);
-		memmove(p, &pwd.pw_expire, sizeof(time_t));
-		p += sizeof(time_t);
-		memmove(p, &flags, sizeof(int));
-		p += sizeof(int);
-		data.size = p - buf;
-
-		/* Store secure by name. */
-		tbuf[0] = _PW_KEYBYNAME;
-		len = strlen(pwd.pw_name);
-		memmove(tbuf + 1, pwd.pw_name, len);
-		key.size = len + 1;
-		if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
-			wr_error(pwd_Sdb_tmp);
-
-		/* Store secure by number. */
-		tbuf[0] = _PW_KEYBYNUM;
-		memmove(tbuf + 1, &cnt, sizeof(cnt));
-		key.size = sizeof(cnt) + 1;
-		if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
-			wr_error(pwd_Sdb_tmp);
-
-		/* Store secure by uid. */
-		tbuf[0] = _PW_KEYBYUID;
-		memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
-		key.size = sizeof(pwd.pw_uid) + 1;
-		if ((edp->put)(edp, &key, &data, R_NOOVERWRITE) == -1)
-			wr_error(pwd_Sdb_tmp);
+		if (!username || (strcmp(username, pwd.pw_name) == 0)) {
+			/* Create secure data. */
+			p = buf;
+			COMPACT(pwd.pw_name);
+			COMPACT(pwd.pw_passwd);
+			memmove(p, &pwd.pw_uid, sizeof(int));
+			p += sizeof(int);
+			memmove(p, &pwd.pw_gid, sizeof(int));
+			p += sizeof(int);
+			memmove(p, &pwd.pw_change, sizeof(time_t));
+			p += sizeof(time_t);
+			COMPACT(pwd.pw_class);
+			COMPACT(pwd.pw_gecos);
+			COMPACT(pwd.pw_dir);
+			COMPACT(pwd.pw_shell);
+			memmove(p, &pwd.pw_expire, sizeof(time_t));
+			p += sizeof(time_t);
+			memmove(p, &flags, sizeof(int));
+			p += sizeof(int);
+			data.size = p - buf;
+
+			/* Store secure by name. */
+			tbuf[0] = _PW_KEYBYNAME;
+			len = strlen(pwd.pw_name);
+			memmove(tbuf + 1, pwd.pw_name, len);
+			key.size = len + 1;
+			if ((edp->put)(edp, &key, &data, method) == -1)
+				wr_error(pwd_Sdb_tmp);
+
+			/* Store secure by number. */
+			tbuf[0] = _PW_KEYBYNUM;
+			memmove(tbuf + 1, &cnt, sizeof(cnt));
+			key.size = sizeof(cnt) + 1;
+			if ((edp->put)(edp, &key, &data, method) == -1)
+				wr_error(pwd_Sdb_tmp);
+
+			/* Store secure by uid. */
+			tbuf[0] = _PW_KEYBYUID;
+			memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
+			key.size = sizeof(pwd.pw_uid) + 1;
+			if ((edp->put)(edp, &key, &data, methoduid) == -1)
+				wr_error(pwd_Sdb_tmp);
+		}
 	}
 
 	/* Store YP token, if needed. */
-	if(hasyp) {
+	if (hasyp) {
 		ypkey.data = (u_char *)__yp_token;
 		ypkey.size = strlen(__yp_token);
 		ypdata.data = (u_char *)NULL;
 		ypdata.size = 0;
 
-		if((edp->put)(edp, &ypkey, &ypdata, R_NOOVERWRITE) == -1)
+		if((edp->put)(edp, &ypkey, &ypdata, method) == -1)
 			wr_error(pwd_Sdb_tmp);
 	}
 
@@ -405,6 +444,61 @@
 }
 
 int
+check_user_db(username)
+	char *username;
+{
+	DB *pw_db;
+	DBT data, key;
+	int len;
+	char *p;
+	char buf[MAX(MAXPATHLEN, LINE_MAX * 2)];
+	int methoduid = 0;
+
+	/*
+	 * Do some trouble to check if we should store this users 
+	 * uid. Don't use getpwnam/getpwuid as that interferes 
+	 * with NIS.
+	 */
+	pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
+	if (!pw_db)
+		error(_PATH_MP_DB);
+	buf[0] = _PW_KEYBYNAME;
+	len = strlen(username);
+
+	/* Only check that username fits in buffer */
+	memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
+	key.data = (u_char *)buf;
+	key.size = len + 1;
+	if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
+		p = (char *)data.data;
+
+		/* jump over pw_name and pw_passwd, to get to pw_uid */
+		while (*p++)
+			;
+		while (*p++)
+			;
+
+		buf[0] = _PW_KEYBYUID;
+		memmove(buf + 1, p, sizeof(int));
+		key.data = (u_char *)buf;
+		key.size = sizeof(int) + 1;
+
+		if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
+			/* First field of data.data holds pw_pwname */
+			if (strcmp(data.data, username))
+				methoduid = R_NOOVERWRITE;
+		} else {
+			methoduid = R_NOOVERWRITE;
+		}
+	} else {
+		methoduid = R_NOOVERWRITE;
+	}
+	if ((pw_db->close)(pw_db))
+		error("close pw_db");
+	return methoduid;
+}
+
+int
 scan(fp, pw, flags)
 	FILE *fp;
 	struct passwd *pw;
@@ -440,6 +534,37 @@
 	return (1);
 }
 
+void                    
+cp(from, to, mode)              
+	char *from, *to;
+	mode_t mode;    
+{               
+	static char buf[MAXBSIZE];
+	int from_fd, rcount, to_fd, wcount;
+
+	if ((from_fd = open(from, O_RDONLY, 0)) < 0)
+		error(from);
+	if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
+		error(to);
+	while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+		wcount = write(to_fd, buf, rcount);
+		if (rcount != wcount || wcount == -1) {
+			int sverrno = errno;
+
+			(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+			errno = sverrno;
+			error(buf);
+		}
+	}
+	if (rcount < 0) {
+		int sverrno = errno;
+
+		(void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+		errno = sverrno;
+		error(buf);
+	}
+}
+
 void
 mv(from, to)
 	char *from, *to;
@@ -510,6 +635,6 @@
 usage()
 {
 
-	(void)fprintf(stderr, "usage: pwd_mkdb [-p] [-d directory] file\n");
+	(void)fprintf(stderr, "usage: pwd_mkdb [-p] [-d directory] [-p user] file\n");
 	exit(1);
 }
Index: usr.bin/chpass/chpass.c
--- usr.bin/chpass/chpass.c.orig	Mon Feb 15 08:38:04 1999
+++ usr.bin/chpass/chpass.c	Mon Apr 12 00:03:50 1999
@@ -82,6 +82,7 @@
 void	baduser __P((void));
 int	main __P((int, char **));
 void	usage __P((void));
+int	pw_mkdb_user __P((char *));
 
 int
 main(argc, argv)
@@ -279,7 +280,7 @@
 	pw_copy(pfd, tfd, pw, (op == LOADENTRY) ? NULL : &old_pw);
 
 	/* Now finish the passwd file update. */
-	if (pw_mkdb() < 0)
+	if (pw_mkdb_user(pw->pw_name) < 0)
 		pw_error(NULL, 0, 1);
 
 	exit(0);
@@ -303,3 +304,37 @@
 #endif
 	exit(1);
 }
+
+#include <sys/wait.h>
+
+int
+pw_mkdb_user(username)
+	char *username;
+{
+	int pstat;
+	pid_t pid;
+
+	pid = vfork();
+	if (pid == 0) {
+		if (username)
+			execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+			      "-u", username,
+			      _PATH_MASTERPASSWD_LOCK, NULL);
+		else
+			execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+			      _PATH_MASTERPASSWD_LOCK, NULL);
+		_exit(1);
+	}
+	pid = waitpid(pid, &pstat, 0);
+	if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
+		return(-1);
+	return(0);
+}
+
+#if 0
+int
+pw_mkdb()
+{
+	return pw_mkdb_user(NULL);
+}
+#endif
Index: usr.bin/passwd/local_passwd.c
--- usr.bin/passwd/local_passwd.c.orig	Thu Jan 14 00:44:43 1999
+++ usr.bin/passwd/local_passwd.c	Mon Apr 12 00:03:58 1999
@@ -60,6 +60,8 @@
 
 static	char   *getnewpasswd __P((struct passwd *));
 
+int pw_mkdb_user __P((char *username));
+
 static uid_t uid;
 
 char *tempname;
@@ -182,7 +184,41 @@
 
 	pw_copy(pfd, tfd, pw, &old_pw);
 
-	if (pw_mkdb() < 0)
+	if (pw_mkdb_user(uname) < 0)
 		pw_error((char *)NULL, 0, 1);
 	return (0);
 }
+
+#include <sys/wait.h>
+
+int
+pw_mkdb_user(username)
+	char *username;
+{
+	int pstat;
+	pid_t pid;
+
+	pid = vfork();
+	if (pid == 0) {
+		if (username)
+			execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+			      "-u", username,
+			      _PATH_MASTERPASSWD_LOCK, NULL);
+		else
+			execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+			      _PATH_MASTERPASSWD_LOCK, NULL);
+		_exit(1);
+	}
+	pid = waitpid(pid, &pstat, 0);
+	if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
+		return(-1);
+	return(0);
+}
+
+#if 0
+int
+pw_mkdb()
+{
+	return pw_mkdb_user(NULL);
+}
+#endif
>Audit-Trail:
>Unformatted: