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