xref: /f-stack/tools/libutil/pw_util.c (revision 22ce4aff)
1*22ce4affSfengbojiang /*--
2*22ce4affSfengbojiang  * SPDX-License-Identifier: BSD-3-Clause
3*22ce4affSfengbojiang  *
41eaf0ac3Slogwang  * Copyright (c) 1990, 1993, 1994
51eaf0ac3Slogwang  *	The Regents of the University of California.  All rights reserved.
61eaf0ac3Slogwang  * Copyright (c) 2002 Networks Associates Technology, Inc.
71eaf0ac3Slogwang  * All rights reserved.
81eaf0ac3Slogwang  *
91eaf0ac3Slogwang  * Portions of this software were developed for the FreeBSD Project by
101eaf0ac3Slogwang  * ThinkSec AS and NAI Labs, the Security Research Division of Network
111eaf0ac3Slogwang  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
121eaf0ac3Slogwang  * ("CBOSS"), as part of the DARPA CHATS research program.
131eaf0ac3Slogwang  *
141eaf0ac3Slogwang  * Redistribution and use in source and binary forms, with or without
151eaf0ac3Slogwang  * modification, are permitted provided that the following conditions
161eaf0ac3Slogwang  * are met:
171eaf0ac3Slogwang  * 1. Redistributions of source code must retain the above copyright
181eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer.
191eaf0ac3Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
201eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer in the
211eaf0ac3Slogwang  *    documentation and/or other materials provided with the distribution.
22*22ce4affSfengbojiang  * 3. Neither the name of the University nor the names of its contributors
231eaf0ac3Slogwang  *    may be used to endorse or promote products derived from this software
241eaf0ac3Slogwang  *    without specific prior written permission.
251eaf0ac3Slogwang  *
261eaf0ac3Slogwang  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
271eaf0ac3Slogwang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
281eaf0ac3Slogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
291eaf0ac3Slogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
301eaf0ac3Slogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
311eaf0ac3Slogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
321eaf0ac3Slogwang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
331eaf0ac3Slogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
341eaf0ac3Slogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
351eaf0ac3Slogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
361eaf0ac3Slogwang  * SUCH DAMAGE.
371eaf0ac3Slogwang  */
381eaf0ac3Slogwang 
39*22ce4affSfengbojiang #include <sys/cdefs.h>
40*22ce4affSfengbojiang __FBSDID("$FreeBSD$");
41*22ce4affSfengbojiang __SCCSID("@(#)pw_util.c	8.3 (Berkeley) 4/2/94");
421eaf0ac3Slogwang 
431eaf0ac3Slogwang /*
441eaf0ac3Slogwang  * This file is used by all the "password" programs; vipw(8), chpass(1),
451eaf0ac3Slogwang  * and passwd(1).
461eaf0ac3Slogwang  */
471eaf0ac3Slogwang 
481eaf0ac3Slogwang #include <sys/param.h>
491eaf0ac3Slogwang #include <sys/errno.h>
501eaf0ac3Slogwang #include <sys/time.h>
511eaf0ac3Slogwang #include <sys/resource.h>
521eaf0ac3Slogwang #include <sys/stat.h>
531eaf0ac3Slogwang #include <sys/wait.h>
541eaf0ac3Slogwang 
551eaf0ac3Slogwang #include <ctype.h>
561eaf0ac3Slogwang #include <err.h>
571eaf0ac3Slogwang #include <fcntl.h>
581eaf0ac3Slogwang #include <inttypes.h>
591eaf0ac3Slogwang #include <paths.h>
601eaf0ac3Slogwang #include <pwd.h>
611eaf0ac3Slogwang #include <signal.h>
621eaf0ac3Slogwang #include <stdio.h>
631eaf0ac3Slogwang #include <stdlib.h>
641eaf0ac3Slogwang #include <string.h>
651eaf0ac3Slogwang #include <unistd.h>
661eaf0ac3Slogwang 
671eaf0ac3Slogwang #include "libutil.h"
681eaf0ac3Slogwang 
691eaf0ac3Slogwang static pid_t editpid = -1;
701eaf0ac3Slogwang static int lockfd = -1;
711eaf0ac3Slogwang static char masterpasswd[PATH_MAX];
721eaf0ac3Slogwang static char passwd_dir[PATH_MAX];
731eaf0ac3Slogwang static char tempname[PATH_MAX];
741eaf0ac3Slogwang static int initialized;
751eaf0ac3Slogwang 
761eaf0ac3Slogwang #if 0
771eaf0ac3Slogwang void
781eaf0ac3Slogwang pw_cont(int sig)
791eaf0ac3Slogwang {
801eaf0ac3Slogwang 
811eaf0ac3Slogwang 	if (editpid != -1)
821eaf0ac3Slogwang 		kill(editpid, sig);
831eaf0ac3Slogwang }
841eaf0ac3Slogwang #endif
851eaf0ac3Slogwang 
861eaf0ac3Slogwang /*
871eaf0ac3Slogwang  * Initialize statics and set limits, signals & umask to try to avoid
881eaf0ac3Slogwang  * interruptions, crashes etc. that might expose passord data.
891eaf0ac3Slogwang  */
901eaf0ac3Slogwang int
pw_init(const char * dir,const char * master)911eaf0ac3Slogwang pw_init(const char *dir, const char *master)
921eaf0ac3Slogwang {
931eaf0ac3Slogwang #if 0
941eaf0ac3Slogwang 	struct rlimit rlim;
951eaf0ac3Slogwang #endif
961eaf0ac3Slogwang 
971eaf0ac3Slogwang 	if (dir == NULL) {
981eaf0ac3Slogwang 		strcpy(passwd_dir, _PATH_ETC);
991eaf0ac3Slogwang 	} else {
1001eaf0ac3Slogwang 		if (strlen(dir) >= sizeof(passwd_dir)) {
1011eaf0ac3Slogwang 			errno = ENAMETOOLONG;
1021eaf0ac3Slogwang 			return (-1);
1031eaf0ac3Slogwang 		}
1041eaf0ac3Slogwang 		strcpy(passwd_dir, dir);
1051eaf0ac3Slogwang 	}
1061eaf0ac3Slogwang 
1071eaf0ac3Slogwang 	if (master == NULL) {
1081eaf0ac3Slogwang 		if (dir == NULL) {
1091eaf0ac3Slogwang 			strcpy(masterpasswd, _PATH_MASTERPASSWD);
1101eaf0ac3Slogwang 		} else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s",
1111eaf0ac3Slogwang 		    passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) {
1121eaf0ac3Slogwang 			errno = ENAMETOOLONG;
1131eaf0ac3Slogwang 			return (-1);
1141eaf0ac3Slogwang 		}
1151eaf0ac3Slogwang 	} else {
1161eaf0ac3Slogwang 		if (strlen(master) >= sizeof(masterpasswd)) {
1171eaf0ac3Slogwang 			errno = ENAMETOOLONG;
1181eaf0ac3Slogwang 			return (-1);
1191eaf0ac3Slogwang 		}
1201eaf0ac3Slogwang 		strcpy(masterpasswd, master);
1211eaf0ac3Slogwang 	}
1221eaf0ac3Slogwang 
1231eaf0ac3Slogwang 	/*
1241eaf0ac3Slogwang 	 * The code that follows is extremely disruptive to the calling
1251eaf0ac3Slogwang 	 * process, and is therefore disabled until someone can conceive
1261eaf0ac3Slogwang 	 * of a realistic scenario where it would fend off a compromise.
1271eaf0ac3Slogwang 	 * Race conditions concerning the temporary files can be guarded
1281eaf0ac3Slogwang 	 * against in other ways than masking signals (by checking stat(2)
1291eaf0ac3Slogwang 	 * results after creation).
1301eaf0ac3Slogwang 	 */
1311eaf0ac3Slogwang #if 0
1321eaf0ac3Slogwang 	/* Unlimited resource limits. */
1331eaf0ac3Slogwang 	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
1341eaf0ac3Slogwang 	(void)setrlimit(RLIMIT_CPU, &rlim);
1351eaf0ac3Slogwang 	(void)setrlimit(RLIMIT_FSIZE, &rlim);
1361eaf0ac3Slogwang 	(void)setrlimit(RLIMIT_STACK, &rlim);
1371eaf0ac3Slogwang 	(void)setrlimit(RLIMIT_DATA, &rlim);
1381eaf0ac3Slogwang 	(void)setrlimit(RLIMIT_RSS, &rlim);
1391eaf0ac3Slogwang 
1401eaf0ac3Slogwang 	/* Don't drop core (not really necessary, but GP's). */
1411eaf0ac3Slogwang 	rlim.rlim_cur = rlim.rlim_max = 0;
1421eaf0ac3Slogwang 	(void)setrlimit(RLIMIT_CORE, &rlim);
1431eaf0ac3Slogwang 
1441eaf0ac3Slogwang 	/* Turn off signals. */
1451eaf0ac3Slogwang 	(void)signal(SIGALRM, SIG_IGN);
1461eaf0ac3Slogwang 	(void)signal(SIGHUP, SIG_IGN);
1471eaf0ac3Slogwang 	(void)signal(SIGINT, SIG_IGN);
1481eaf0ac3Slogwang 	(void)signal(SIGPIPE, SIG_IGN);
1491eaf0ac3Slogwang 	(void)signal(SIGQUIT, SIG_IGN);
1501eaf0ac3Slogwang 	(void)signal(SIGTERM, SIG_IGN);
1511eaf0ac3Slogwang 	(void)signal(SIGCONT, pw_cont);
1521eaf0ac3Slogwang 
1531eaf0ac3Slogwang 	/* Create with exact permissions. */
1541eaf0ac3Slogwang 	(void)umask(0);
1551eaf0ac3Slogwang #endif
1561eaf0ac3Slogwang 	initialized = 1;
1571eaf0ac3Slogwang 	return (0);
1581eaf0ac3Slogwang }
1591eaf0ac3Slogwang 
1601eaf0ac3Slogwang /*
1611eaf0ac3Slogwang  * Lock the master password file.
1621eaf0ac3Slogwang  */
1631eaf0ac3Slogwang int
pw_lock(void)1641eaf0ac3Slogwang pw_lock(void)
1651eaf0ac3Slogwang {
1661eaf0ac3Slogwang 
1671eaf0ac3Slogwang 	if (*masterpasswd == '\0')
1681eaf0ac3Slogwang 		return (-1);
1691eaf0ac3Slogwang 
1701eaf0ac3Slogwang 	/*
1711eaf0ac3Slogwang 	 * If the master password file doesn't exist, the system is hosed.
1721eaf0ac3Slogwang 	 * Might as well try to build one.  Set the close-on-exec bit so
1731eaf0ac3Slogwang 	 * that users can't get at the encrypted passwords while editing.
1741eaf0ac3Slogwang 	 * Open should allow flock'ing the file; see 4.4BSD.	XXX
1751eaf0ac3Slogwang 	 */
1761eaf0ac3Slogwang 	for (;;) {
1771eaf0ac3Slogwang 		struct stat st;
1781eaf0ac3Slogwang 
1791eaf0ac3Slogwang 		lockfd = flopen(masterpasswd, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0);
1801eaf0ac3Slogwang 		if (lockfd == -1) {
1811eaf0ac3Slogwang 			if (errno == EWOULDBLOCK) {
1821eaf0ac3Slogwang 				errx(1, "the password db file is busy");
1831eaf0ac3Slogwang 			} else {
184*22ce4affSfengbojiang 				err(1, "could not lock the passwd file");
1851eaf0ac3Slogwang 			}
1861eaf0ac3Slogwang 		}
1871eaf0ac3Slogwang 
1881eaf0ac3Slogwang 		/*
1891eaf0ac3Slogwang 		 * If the password file was replaced while we were trying to
1901eaf0ac3Slogwang 		 * get the lock, our hardlink count will be 0 and we have to
1911eaf0ac3Slogwang 		 * close and retry.
1921eaf0ac3Slogwang 		 */
1931eaf0ac3Slogwang 		if (fstat(lockfd, &st) == -1)
194*22ce4affSfengbojiang 			err(1, "fstat() failed");
1951eaf0ac3Slogwang 		if (st.st_nlink != 0)
1961eaf0ac3Slogwang 			break;
1971eaf0ac3Slogwang 		close(lockfd);
1981eaf0ac3Slogwang 		lockfd = -1;
1991eaf0ac3Slogwang 	}
2001eaf0ac3Slogwang 	return (lockfd);
2011eaf0ac3Slogwang }
2021eaf0ac3Slogwang 
2031eaf0ac3Slogwang /*
2041eaf0ac3Slogwang  * Create and open a presumably safe temp file for editing the password
2051eaf0ac3Slogwang  * data, and copy the master password file into it.
2061eaf0ac3Slogwang  */
2071eaf0ac3Slogwang int
pw_tmp(int mfd)2081eaf0ac3Slogwang pw_tmp(int mfd)
2091eaf0ac3Slogwang {
2101eaf0ac3Slogwang 	char buf[8192];
2111eaf0ac3Slogwang 	ssize_t nr;
2121eaf0ac3Slogwang 	const char *p;
2131eaf0ac3Slogwang 	int tfd;
2141eaf0ac3Slogwang 
2151eaf0ac3Slogwang 	if (*masterpasswd == '\0')
2161eaf0ac3Slogwang 		return (-1);
2171eaf0ac3Slogwang 	if ((p = strrchr(masterpasswd, '/')))
2181eaf0ac3Slogwang 		++p;
2191eaf0ac3Slogwang 	else
2201eaf0ac3Slogwang 		p = masterpasswd;
2211eaf0ac3Slogwang 	if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX",
2221eaf0ac3Slogwang 		(int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) {
2231eaf0ac3Slogwang 		errno = ENAMETOOLONG;
2241eaf0ac3Slogwang 		return (-1);
2251eaf0ac3Slogwang 	}
226*22ce4affSfengbojiang 	if ((tfd = mkostemp(tempname, 0)) == -1)
2271eaf0ac3Slogwang 		return (-1);
2281eaf0ac3Slogwang 	if (mfd != -1) {
2291eaf0ac3Slogwang 		while ((nr = read(mfd, buf, sizeof(buf))) > 0)
2301eaf0ac3Slogwang 			if (write(tfd, buf, (size_t)nr) != nr)
2311eaf0ac3Slogwang 				break;
2321eaf0ac3Slogwang 		if (nr != 0) {
2331eaf0ac3Slogwang 			unlink(tempname);
2341eaf0ac3Slogwang 			*tempname = '\0';
2351eaf0ac3Slogwang 			close(tfd);
2361eaf0ac3Slogwang 			return (-1);
2371eaf0ac3Slogwang 		}
2381eaf0ac3Slogwang 	}
2391eaf0ac3Slogwang 	return (tfd);
2401eaf0ac3Slogwang }
2411eaf0ac3Slogwang 
2421eaf0ac3Slogwang /*
2431eaf0ac3Slogwang  * Regenerate the password database.
2441eaf0ac3Slogwang  */
2451eaf0ac3Slogwang int
pw_mkdb(const char * user)2461eaf0ac3Slogwang pw_mkdb(const char *user)
2471eaf0ac3Slogwang {
2481eaf0ac3Slogwang 	int pstat;
2491eaf0ac3Slogwang 	pid_t pid;
2501eaf0ac3Slogwang 
2511eaf0ac3Slogwang 	(void)fflush(stderr);
2521eaf0ac3Slogwang 	switch ((pid = fork())) {
2531eaf0ac3Slogwang 	case -1:
2541eaf0ac3Slogwang 		return (-1);
2551eaf0ac3Slogwang 	case 0:
2561eaf0ac3Slogwang 		/* child */
2571eaf0ac3Slogwang 		if (user == NULL)
2581eaf0ac3Slogwang 			execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
2591eaf0ac3Slogwang 			    "-d", passwd_dir, tempname, (char *)NULL);
2601eaf0ac3Slogwang 		else
2611eaf0ac3Slogwang 			execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
2621eaf0ac3Slogwang 			    "-d", passwd_dir, "-u", user, tempname,
2631eaf0ac3Slogwang 			    (char *)NULL);
2641eaf0ac3Slogwang 		_exit(1);
2651eaf0ac3Slogwang 		/* NOTREACHED */
2661eaf0ac3Slogwang 	default:
2671eaf0ac3Slogwang 		/* parent */
2681eaf0ac3Slogwang 		break;
2691eaf0ac3Slogwang 	}
2701eaf0ac3Slogwang 	if (waitpid(pid, &pstat, 0) == -1)
2711eaf0ac3Slogwang 		return (-1);
2721eaf0ac3Slogwang 	if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
2731eaf0ac3Slogwang 		return (0);
2741eaf0ac3Slogwang 	errno = 0;
2751eaf0ac3Slogwang 	return (-1);
2761eaf0ac3Slogwang }
2771eaf0ac3Slogwang 
2781eaf0ac3Slogwang /*
2791eaf0ac3Slogwang  * Edit the temp file.  Return -1 on error, >0 if the file was modified, 0
2801eaf0ac3Slogwang  * if it was not.
2811eaf0ac3Slogwang  */
2821eaf0ac3Slogwang int
pw_edit(int notsetuid)2831eaf0ac3Slogwang pw_edit(int notsetuid)
2841eaf0ac3Slogwang {
2851eaf0ac3Slogwang 	struct sigaction sa, sa_int, sa_quit;
2861eaf0ac3Slogwang 	sigset_t oldsigset, nsigset;
2871eaf0ac3Slogwang 	struct stat st1, st2;
2881eaf0ac3Slogwang 	const char *editor;
2891eaf0ac3Slogwang 	int pstat;
2901eaf0ac3Slogwang 
2911eaf0ac3Slogwang 	if ((editor = getenv("EDITOR")) == NULL)
2921eaf0ac3Slogwang 		editor = _PATH_VI;
2931eaf0ac3Slogwang 	if (stat(tempname, &st1) == -1)
2941eaf0ac3Slogwang 		return (-1);
2951eaf0ac3Slogwang 	sa.sa_handler = SIG_IGN;
2961eaf0ac3Slogwang 	sigemptyset(&sa.sa_mask);
2971eaf0ac3Slogwang 	sa.sa_flags = 0;
2981eaf0ac3Slogwang 	sigaction(SIGINT, &sa, &sa_int);
2991eaf0ac3Slogwang 	sigaction(SIGQUIT, &sa, &sa_quit);
3001eaf0ac3Slogwang 	sigemptyset(&nsigset);
3011eaf0ac3Slogwang 	sigaddset(&nsigset, SIGCHLD);
3021eaf0ac3Slogwang 	sigprocmask(SIG_BLOCK, &nsigset, &oldsigset);
3031eaf0ac3Slogwang 	switch ((editpid = fork())) {
3041eaf0ac3Slogwang 	case -1:
3051eaf0ac3Slogwang 		return (-1);
3061eaf0ac3Slogwang 	case 0:
3071eaf0ac3Slogwang 		sigaction(SIGINT, &sa_int, NULL);
3081eaf0ac3Slogwang 		sigaction(SIGQUIT, &sa_quit, NULL);
3091eaf0ac3Slogwang 		sigprocmask(SIG_SETMASK, &oldsigset, NULL);
3101eaf0ac3Slogwang 		if (notsetuid) {
311*22ce4affSfengbojiang 			if (setgid(getgid()) == -1)
312*22ce4affSfengbojiang 				err(1, "setgid");
313*22ce4affSfengbojiang 			if (setuid(getuid()) == -1)
314*22ce4affSfengbojiang 				err(1, "setuid");
3151eaf0ac3Slogwang 		}
3161eaf0ac3Slogwang 		execlp(editor, editor, tempname, (char *)NULL);
317*22ce4affSfengbojiang 		err(1, "%s", editor);
3181eaf0ac3Slogwang 	default:
3191eaf0ac3Slogwang 		/* parent */
3201eaf0ac3Slogwang 		break;
3211eaf0ac3Slogwang 	}
3221eaf0ac3Slogwang 	for (;;) {
3231eaf0ac3Slogwang 		if (waitpid(editpid, &pstat, WUNTRACED) == -1) {
3241eaf0ac3Slogwang 			if (errno == EINTR)
3251eaf0ac3Slogwang 				continue;
3261eaf0ac3Slogwang 			unlink(tempname);
3271eaf0ac3Slogwang 			editpid = -1;
3281eaf0ac3Slogwang 			break;
3291eaf0ac3Slogwang 		} else if (WIFSTOPPED(pstat)) {
3301eaf0ac3Slogwang 			raise(WSTOPSIG(pstat));
331*22ce4affSfengbojiang 		} else if (WIFEXITED(pstat)) {
332*22ce4affSfengbojiang 			if (WEXITSTATUS(pstat) != 0)
333*22ce4affSfengbojiang 				errx(1, "\"%s\" exited with status %d", editor, WEXITSTATUS(pstat));
3341eaf0ac3Slogwang 			editpid = -1;
3351eaf0ac3Slogwang 			break;
3361eaf0ac3Slogwang 		} else {
3371eaf0ac3Slogwang 			unlink(tempname);
3381eaf0ac3Slogwang 			editpid = -1;
3391eaf0ac3Slogwang 			break;
3401eaf0ac3Slogwang 		}
3411eaf0ac3Slogwang 	}
3421eaf0ac3Slogwang 	sigaction(SIGINT, &sa_int, NULL);
3431eaf0ac3Slogwang 	sigaction(SIGQUIT, &sa_quit, NULL);
3441eaf0ac3Slogwang 	sigprocmask(SIG_SETMASK, &oldsigset, NULL);
3451eaf0ac3Slogwang 	if (stat(tempname, &st2) == -1)
3461eaf0ac3Slogwang 		return (-1);
3471eaf0ac3Slogwang 	return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec ||
3481eaf0ac3Slogwang 	    st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec);
3491eaf0ac3Slogwang }
3501eaf0ac3Slogwang 
3511eaf0ac3Slogwang /*
3521eaf0ac3Slogwang  * Clean up.  Preserve errno for the caller's convenience.
3531eaf0ac3Slogwang  */
3541eaf0ac3Slogwang void
pw_fini(void)3551eaf0ac3Slogwang pw_fini(void)
3561eaf0ac3Slogwang {
3571eaf0ac3Slogwang 	int serrno, status;
3581eaf0ac3Slogwang 
3591eaf0ac3Slogwang 	if (!initialized)
3601eaf0ac3Slogwang 		return;
3611eaf0ac3Slogwang 	initialized = 0;
3621eaf0ac3Slogwang 	serrno = errno;
3631eaf0ac3Slogwang 	if (editpid != -1) {
3641eaf0ac3Slogwang 		kill(editpid, SIGTERM);
3651eaf0ac3Slogwang 		kill(editpid, SIGCONT);
3661eaf0ac3Slogwang 		waitpid(editpid, &status, 0);
3671eaf0ac3Slogwang 		editpid = -1;
3681eaf0ac3Slogwang 	}
3691eaf0ac3Slogwang 	if (*tempname != '\0') {
3701eaf0ac3Slogwang 		unlink(tempname);
3711eaf0ac3Slogwang 		*tempname = '\0';
3721eaf0ac3Slogwang 	}
3731eaf0ac3Slogwang 	if (lockfd != -1)
3741eaf0ac3Slogwang 		close(lockfd);
3751eaf0ac3Slogwang 	errno = serrno;
3761eaf0ac3Slogwang }
3771eaf0ac3Slogwang 
3781eaf0ac3Slogwang /*
3791eaf0ac3Slogwang  * Compares two struct pwds.
3801eaf0ac3Slogwang  */
3811eaf0ac3Slogwang int
pw_equal(const struct passwd * pw1,const struct passwd * pw2)3821eaf0ac3Slogwang pw_equal(const struct passwd *pw1, const struct passwd *pw2)
3831eaf0ac3Slogwang {
3841eaf0ac3Slogwang 	return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
3851eaf0ac3Slogwang 	    pw1->pw_uid == pw2->pw_uid &&
3861eaf0ac3Slogwang 	    pw1->pw_gid == pw2->pw_gid &&
3871eaf0ac3Slogwang 	    strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
3881eaf0ac3Slogwang 	    pw1->pw_change == pw2->pw_change &&
3891eaf0ac3Slogwang 	    pw1->pw_expire == pw2->pw_expire &&
3901eaf0ac3Slogwang 	    strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
3911eaf0ac3Slogwang 	    strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
3921eaf0ac3Slogwang 	    strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
3931eaf0ac3Slogwang }
3941eaf0ac3Slogwang 
3951eaf0ac3Slogwang /*
3961eaf0ac3Slogwang  * Make a passwd line out of a struct passwd.
3971eaf0ac3Slogwang  */
3981eaf0ac3Slogwang char *
pw_make(const struct passwd * pw)3991eaf0ac3Slogwang pw_make(const struct passwd *pw)
4001eaf0ac3Slogwang {
4011eaf0ac3Slogwang 	char *line;
4021eaf0ac3Slogwang 
4031eaf0ac3Slogwang 	asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name,
4041eaf0ac3Slogwang 	    pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid,
4051eaf0ac3Slogwang 	    pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire,
4061eaf0ac3Slogwang 	    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
4071eaf0ac3Slogwang 	return (line);
4081eaf0ac3Slogwang }
4091eaf0ac3Slogwang 
4101eaf0ac3Slogwang /*
4111eaf0ac3Slogwang  * Make a passwd line (in v7 format) out of a struct passwd
4121eaf0ac3Slogwang  */
4131eaf0ac3Slogwang char *
pw_make_v7(const struct passwd * pw)4141eaf0ac3Slogwang pw_make_v7(const struct passwd *pw)
4151eaf0ac3Slogwang {
4161eaf0ac3Slogwang 	char *line;
4171eaf0ac3Slogwang 
4181eaf0ac3Slogwang 	asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name,
4191eaf0ac3Slogwang 	    (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid,
4201eaf0ac3Slogwang 	    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
4211eaf0ac3Slogwang 	return (line);
4221eaf0ac3Slogwang }
4231eaf0ac3Slogwang 
4241eaf0ac3Slogwang /*
4251eaf0ac3Slogwang  * Copy password file from one descriptor to another, replacing, deleting
4261eaf0ac3Slogwang  * or adding a single record on the way.
4271eaf0ac3Slogwang  */
4281eaf0ac3Slogwang int
pw_copy(int ffd,int tfd,const struct passwd * pw,struct passwd * old_pw)4291eaf0ac3Slogwang pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw)
4301eaf0ac3Slogwang {
431*22ce4affSfengbojiang 	char *buf, *end, *line, *p, *q, *r, *tmp;
4321eaf0ac3Slogwang 	struct passwd *fpw;
4331eaf0ac3Slogwang 	const struct passwd *spw;
434*22ce4affSfengbojiang 	size_t len, size;
4351eaf0ac3Slogwang 	int eof, readlen;
436*22ce4affSfengbojiang 	char t;
4371eaf0ac3Slogwang 
4381eaf0ac3Slogwang 	if (old_pw == NULL && pw == NULL)
4391eaf0ac3Slogwang 			return (-1);
4401eaf0ac3Slogwang 
4411eaf0ac3Slogwang 	spw = old_pw;
4421eaf0ac3Slogwang 	/* deleting a user */
4431eaf0ac3Slogwang 	if (pw == NULL) {
4441eaf0ac3Slogwang 		line = NULL;
4451eaf0ac3Slogwang 	} else {
4461eaf0ac3Slogwang 		if ((line = pw_make(pw)) == NULL)
4471eaf0ac3Slogwang 			return (-1);
4481eaf0ac3Slogwang 	}
4491eaf0ac3Slogwang 
4501eaf0ac3Slogwang 	/* adding a user */
4511eaf0ac3Slogwang 	if (spw == NULL)
4521eaf0ac3Slogwang 		spw = pw;
4531eaf0ac3Slogwang 
454*22ce4affSfengbojiang 	/* initialize the buffer */
455*22ce4affSfengbojiang 	if ((buf = malloc(size = 1024)) == NULL)
456*22ce4affSfengbojiang 		goto err;
457*22ce4affSfengbojiang 
4581eaf0ac3Slogwang 	eof = 0;
4591eaf0ac3Slogwang 	len = 0;
4601eaf0ac3Slogwang 	p = q = end = buf;
4611eaf0ac3Slogwang 	for (;;) {
4621eaf0ac3Slogwang 		/* find the end of the current line */
4631eaf0ac3Slogwang 		for (p = q; q < end && *q != '\0'; ++q)
4641eaf0ac3Slogwang 			if (*q == '\n')
4651eaf0ac3Slogwang 				break;
4661eaf0ac3Slogwang 
4671eaf0ac3Slogwang 		/* if we don't have a complete line, fill up the buffer */
4681eaf0ac3Slogwang 		if (q >= end) {
4691eaf0ac3Slogwang 			if (eof)
4701eaf0ac3Slogwang 				break;
471*22ce4affSfengbojiang 			while ((size_t)(q - p) >= size) {
472*22ce4affSfengbojiang 				if ((tmp = reallocarray(buf, 2, size)) == NULL) {
4731eaf0ac3Slogwang 					warnx("passwd line too long");
4741eaf0ac3Slogwang 					goto err;
4751eaf0ac3Slogwang 				}
476*22ce4affSfengbojiang 				p = tmp + (p - buf);
477*22ce4affSfengbojiang 				q = tmp + (q - buf);
478*22ce4affSfengbojiang 				end = tmp + (end - buf);
479*22ce4affSfengbojiang 				buf = tmp;
480*22ce4affSfengbojiang 				size = size * 2;
481*22ce4affSfengbojiang 			}
4821eaf0ac3Slogwang 			if (p < end) {
4831eaf0ac3Slogwang 				q = memmove(buf, p, end - p);
4841eaf0ac3Slogwang 				end -= p - buf;
4851eaf0ac3Slogwang 			} else {
4861eaf0ac3Slogwang 				p = q = end = buf;
4871eaf0ac3Slogwang 			}
488*22ce4affSfengbojiang 			readlen = read(ffd, end, size - (end - buf));
4891eaf0ac3Slogwang 			if (readlen == -1)
4901eaf0ac3Slogwang 				goto err;
4911eaf0ac3Slogwang 			else
4921eaf0ac3Slogwang 				len = (size_t)readlen;
4931eaf0ac3Slogwang 			if (len == 0 && p == buf)
4941eaf0ac3Slogwang 				break;
4951eaf0ac3Slogwang 			end += len;
4961eaf0ac3Slogwang 			len = end - buf;
497*22ce4affSfengbojiang 			if (len < size) {
4981eaf0ac3Slogwang 				eof = 1;
4991eaf0ac3Slogwang 				if (len > 0 && buf[len - 1] != '\n')
5001eaf0ac3Slogwang 					++len, *end++ = '\n';
5011eaf0ac3Slogwang 			}
5021eaf0ac3Slogwang 			continue;
5031eaf0ac3Slogwang 		}
5041eaf0ac3Slogwang 
5051eaf0ac3Slogwang 		/* is it a blank line or a comment? */
5061eaf0ac3Slogwang 		for (r = p; r < q && isspace(*r); ++r)
5071eaf0ac3Slogwang 			/* nothing */ ;
5081eaf0ac3Slogwang 		if (r == q || *r == '#') {
5091eaf0ac3Slogwang 			/* yep */
5101eaf0ac3Slogwang 			if (write(tfd, p, q - p + 1) != q - p + 1)
5111eaf0ac3Slogwang 				goto err;
5121eaf0ac3Slogwang 			++q;
5131eaf0ac3Slogwang 			continue;
5141eaf0ac3Slogwang 		}
5151eaf0ac3Slogwang 
5161eaf0ac3Slogwang 		/* is it the one we're looking for? */
5171eaf0ac3Slogwang 
5181eaf0ac3Slogwang 		t = *q;
5191eaf0ac3Slogwang 		*q = '\0';
5201eaf0ac3Slogwang 
5211eaf0ac3Slogwang 		fpw = pw_scan(r, PWSCAN_MASTER);
5221eaf0ac3Slogwang 
5231eaf0ac3Slogwang 		/*
5241eaf0ac3Slogwang 		 * fpw is either the struct passwd for the current line,
5251eaf0ac3Slogwang 		 * or NULL if the line is malformed.
5261eaf0ac3Slogwang 		 */
5271eaf0ac3Slogwang 
5281eaf0ac3Slogwang 		*q = t;
5291eaf0ac3Slogwang 		if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) {
5301eaf0ac3Slogwang 			/* nope */
5311eaf0ac3Slogwang 			if (fpw != NULL)
5321eaf0ac3Slogwang 				free(fpw);
5331eaf0ac3Slogwang 			if (write(tfd, p, q - p + 1) != q - p + 1)
5341eaf0ac3Slogwang 				goto err;
5351eaf0ac3Slogwang 			++q;
5361eaf0ac3Slogwang 			continue;
5371eaf0ac3Slogwang 		}
5381eaf0ac3Slogwang 		if (old_pw && !pw_equal(fpw, old_pw)) {
5391eaf0ac3Slogwang 			warnx("entry inconsistent");
5401eaf0ac3Slogwang 			free(fpw);
5411eaf0ac3Slogwang 			errno = EINVAL; /* hack */
5421eaf0ac3Slogwang 			goto err;
5431eaf0ac3Slogwang 		}
5441eaf0ac3Slogwang 		free(fpw);
5451eaf0ac3Slogwang 
5461eaf0ac3Slogwang 		/* it is, replace or remove it */
5471eaf0ac3Slogwang 		if (line != NULL) {
5481eaf0ac3Slogwang 			len = strlen(line);
5491eaf0ac3Slogwang 			if (write(tfd, line, len) != (int)len)
5501eaf0ac3Slogwang 				goto err;
5511eaf0ac3Slogwang 		} else {
5521eaf0ac3Slogwang 			/* when removed, avoid the \n */
5531eaf0ac3Slogwang 			q++;
5541eaf0ac3Slogwang 		}
5551eaf0ac3Slogwang 		/* we're done, just copy the rest over */
5561eaf0ac3Slogwang 		for (;;) {
5571eaf0ac3Slogwang 			if (write(tfd, q, end - q) != end - q)
5581eaf0ac3Slogwang 				goto err;
5591eaf0ac3Slogwang 			q = buf;
560*22ce4affSfengbojiang 			readlen = read(ffd, buf, size);
5611eaf0ac3Slogwang 			if (readlen == 0)
5621eaf0ac3Slogwang 				break;
5631eaf0ac3Slogwang 			else
5641eaf0ac3Slogwang 				len = (size_t)readlen;
5651eaf0ac3Slogwang 			if (readlen == -1)
5661eaf0ac3Slogwang 				goto err;
5671eaf0ac3Slogwang 			end = buf + len;
5681eaf0ac3Slogwang 		}
5691eaf0ac3Slogwang 		goto done;
5701eaf0ac3Slogwang 	}
5711eaf0ac3Slogwang 
5721eaf0ac3Slogwang 	/* if we got here, we didn't find the old entry */
5731eaf0ac3Slogwang 	if (line == NULL) {
5741eaf0ac3Slogwang 		errno = ENOENT;
5751eaf0ac3Slogwang 		goto err;
5761eaf0ac3Slogwang 	}
5771eaf0ac3Slogwang 	len = strlen(line);
5781eaf0ac3Slogwang 	if ((size_t)write(tfd, line, len) != len ||
5791eaf0ac3Slogwang 	    write(tfd, "\n", 1) != 1)
5801eaf0ac3Slogwang 		goto err;
5811eaf0ac3Slogwang  done:
5821eaf0ac3Slogwang 	free(line);
583*22ce4affSfengbojiang 	free(buf);
5841eaf0ac3Slogwang 	return (0);
5851eaf0ac3Slogwang  err:
5861eaf0ac3Slogwang 	free(line);
587*22ce4affSfengbojiang 	free(buf);
5881eaf0ac3Slogwang 	return (-1);
5891eaf0ac3Slogwang }
5901eaf0ac3Slogwang 
5911eaf0ac3Slogwang /*
5921eaf0ac3Slogwang  * Return the current value of tempname.
5931eaf0ac3Slogwang  */
5941eaf0ac3Slogwang const char *
pw_tempname(void)5951eaf0ac3Slogwang pw_tempname(void)
5961eaf0ac3Slogwang {
5971eaf0ac3Slogwang 
5981eaf0ac3Slogwang 	return (tempname);
5991eaf0ac3Slogwang }
6001eaf0ac3Slogwang 
6011eaf0ac3Slogwang /*
6021eaf0ac3Slogwang  * Duplicate a struct passwd.
6031eaf0ac3Slogwang  */
6041eaf0ac3Slogwang struct passwd *
pw_dup(const struct passwd * pw)6051eaf0ac3Slogwang pw_dup(const struct passwd *pw)
6061eaf0ac3Slogwang {
6071eaf0ac3Slogwang 	char *dst;
6081eaf0ac3Slogwang 	struct passwd *npw;
6091eaf0ac3Slogwang 	ssize_t len;
6101eaf0ac3Slogwang 
6111eaf0ac3Slogwang 	len = sizeof(*npw);
6121eaf0ac3Slogwang 	if (pw->pw_name != NULL)
6131eaf0ac3Slogwang 		len += strlen(pw->pw_name) + 1;
6141eaf0ac3Slogwang 	if (pw->pw_passwd != NULL)
6151eaf0ac3Slogwang 		len += strlen(pw->pw_passwd) + 1;
6161eaf0ac3Slogwang 	if (pw->pw_class != NULL)
6171eaf0ac3Slogwang 		len += strlen(pw->pw_class) + 1;
6181eaf0ac3Slogwang 	if (pw->pw_gecos != NULL)
6191eaf0ac3Slogwang 		len += strlen(pw->pw_gecos) + 1;
6201eaf0ac3Slogwang 	if (pw->pw_dir != NULL)
6211eaf0ac3Slogwang 		len += strlen(pw->pw_dir) + 1;
6221eaf0ac3Slogwang 	if (pw->pw_shell != NULL)
6231eaf0ac3Slogwang 		len += strlen(pw->pw_shell) + 1;
6241eaf0ac3Slogwang 	if ((npw = malloc((size_t)len)) == NULL)
6251eaf0ac3Slogwang 		return (NULL);
6261eaf0ac3Slogwang 	memcpy(npw, pw, sizeof(*npw));
6271eaf0ac3Slogwang 	dst = (char *)npw + sizeof(*npw);
6281eaf0ac3Slogwang 	if (pw->pw_name != NULL) {
6291eaf0ac3Slogwang 		npw->pw_name = dst;
6301eaf0ac3Slogwang 		dst = stpcpy(npw->pw_name, pw->pw_name) + 1;
6311eaf0ac3Slogwang 	}
6321eaf0ac3Slogwang 	if (pw->pw_passwd != NULL) {
6331eaf0ac3Slogwang 		npw->pw_passwd = dst;
6341eaf0ac3Slogwang 		dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1;
6351eaf0ac3Slogwang 	}
6361eaf0ac3Slogwang 	if (pw->pw_class != NULL) {
6371eaf0ac3Slogwang 		npw->pw_class = dst;
6381eaf0ac3Slogwang 		dst = stpcpy(npw->pw_class, pw->pw_class) + 1;
6391eaf0ac3Slogwang 	}
6401eaf0ac3Slogwang 	if (pw->pw_gecos != NULL) {
6411eaf0ac3Slogwang 		npw->pw_gecos = dst;
6421eaf0ac3Slogwang 		dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1;
6431eaf0ac3Slogwang 	}
6441eaf0ac3Slogwang 	if (pw->pw_dir != NULL) {
6451eaf0ac3Slogwang 		npw->pw_dir = dst;
6461eaf0ac3Slogwang 		dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1;
6471eaf0ac3Slogwang 	}
6481eaf0ac3Slogwang 	if (pw->pw_shell != NULL) {
6491eaf0ac3Slogwang 		npw->pw_shell = dst;
6501eaf0ac3Slogwang 		dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1;
6511eaf0ac3Slogwang 	}
6521eaf0ac3Slogwang 	return (npw);
6531eaf0ac3Slogwang }
6541eaf0ac3Slogwang 
6551eaf0ac3Slogwang #include "pw_scan.h"
6561eaf0ac3Slogwang 
6571eaf0ac3Slogwang /*
658*22ce4affSfengbojiang  * Wrapper around some internal libc functions.
6591eaf0ac3Slogwang  */
660*22ce4affSfengbojiang 
661*22ce4affSfengbojiang void
pw_initpwd(struct passwd * pw)662*22ce4affSfengbojiang pw_initpwd(struct passwd *pw)
663*22ce4affSfengbojiang {
664*22ce4affSfengbojiang 
665*22ce4affSfengbojiang 	__pw_initpwd(pw);
666*22ce4affSfengbojiang }
667*22ce4affSfengbojiang 
6681eaf0ac3Slogwang struct passwd *
pw_scan(const char * line,int flags)6691eaf0ac3Slogwang pw_scan(const char *line, int flags)
6701eaf0ac3Slogwang {
6711eaf0ac3Slogwang 	struct passwd pw, *ret;
6721eaf0ac3Slogwang 	char *bp;
6731eaf0ac3Slogwang 
6741eaf0ac3Slogwang 	if ((bp = strdup(line)) == NULL)
6751eaf0ac3Slogwang 		return (NULL);
676*22ce4affSfengbojiang 	__pw_initpwd(&pw);
6771eaf0ac3Slogwang 	if (!__pw_scan(bp, &pw, flags)) {
6781eaf0ac3Slogwang 		free(bp);
6791eaf0ac3Slogwang 		return (NULL);
6801eaf0ac3Slogwang 	}
6811eaf0ac3Slogwang 	ret = pw_dup(&pw);
6821eaf0ac3Slogwang 	free(bp);
6831eaf0ac3Slogwang 	return (ret);
6841eaf0ac3Slogwang }
685