18a16b7a1SPedro F. Giffuni /*-- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 4dea673e9SRodney W. Grimes * Copyright (c) 1990, 1993, 1994 5dea673e9SRodney W. Grimes * The Regents of the University of California. All rights reserved. 6e2ef54deSDag-Erling Smørgrav * Copyright (c) 2002 Networks Associates Technology, Inc. 7e2ef54deSDag-Erling Smørgrav * All rights reserved. 8e2ef54deSDag-Erling Smørgrav * 9e2ef54deSDag-Erling Smørgrav * Portions of this software were developed for the FreeBSD Project by 10e2ef54deSDag-Erling Smørgrav * ThinkSec AS and NAI Labs, the Security Research Division of Network 11e2ef54deSDag-Erling Smørgrav * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 12e2ef54deSDag-Erling Smørgrav * ("CBOSS"), as part of the DARPA CHATS research program. 13dea673e9SRodney W. Grimes * 14dea673e9SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 15dea673e9SRodney W. Grimes * modification, are permitted provided that the following conditions 16dea673e9SRodney W. Grimes * are met: 17dea673e9SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 18dea673e9SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 19dea673e9SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 20dea673e9SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 21dea673e9SRodney W. Grimes * documentation and/or other materials provided with the distribution. 22fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 23dea673e9SRodney W. Grimes * may be used to endorse or promote products derived from this software 24dea673e9SRodney W. Grimes * without specific prior written permission. 25dea673e9SRodney W. Grimes * 26dea673e9SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27dea673e9SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28dea673e9SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29dea673e9SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30dea673e9SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31dea673e9SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32dea673e9SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33dea673e9SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34dea673e9SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35dea673e9SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36dea673e9SRodney W. Grimes * SUCH DAMAGE. 37dea673e9SRodney W. Grimes */ 38dea673e9SRodney W. Grimes 39636402a7SJohn Baldwin #include <sys/cdefs.h> 40636402a7SJohn Baldwin __FBSDID("$FreeBSD$"); 41636402a7SJohn Baldwin __SCCSID("@(#)pw_util.c 8.3 (Berkeley) 4/2/94"); 42dea673e9SRodney W. Grimes 43dea673e9SRodney W. Grimes /* 44dea673e9SRodney W. Grimes * This file is used by all the "password" programs; vipw(8), chpass(1), 45dea673e9SRodney W. Grimes * and passwd(1). 46dea673e9SRodney W. Grimes */ 47dea673e9SRodney W. Grimes 48dea673e9SRodney W. Grimes #include <sys/param.h> 494da8edd5SDag-Erling Smørgrav #include <sys/errno.h> 50dea673e9SRodney W. Grimes #include <sys/time.h> 51dea673e9SRodney W. Grimes #include <sys/resource.h> 52dea673e9SRodney W. Grimes #include <sys/stat.h> 53dea673e9SRodney W. Grimes #include <sys/wait.h> 54dea673e9SRodney W. Grimes 55e2ef54deSDag-Erling Smørgrav #include <ctype.h> 560ebec5d3SMark Murray #include <err.h> 57dea673e9SRodney W. Grimes #include <fcntl.h> 58e2ef54deSDag-Erling Smørgrav #include <inttypes.h> 59dea673e9SRodney W. Grimes #include <paths.h> 60dea673e9SRodney W. Grimes #include <pwd.h> 61dea673e9SRodney W. Grimes #include <signal.h> 62dea673e9SRodney W. Grimes #include <stdio.h> 63dea673e9SRodney W. Grimes #include <stdlib.h> 64dea673e9SRodney W. Grimes #include <string.h> 65dea673e9SRodney W. Grimes #include <unistd.h> 66dea673e9SRodney W. Grimes 67f4fda767SDag-Erling Smørgrav #include "libutil.h" 68dea673e9SRodney W. Grimes 69b603d90cSAndrey A. Chernov static pid_t editpid = -1; 70e2ef54deSDag-Erling Smørgrav static int lockfd = -1; 71e2ef54deSDag-Erling Smørgrav static char masterpasswd[PATH_MAX]; 72e2ef54deSDag-Erling Smørgrav static char passwd_dir[PATH_MAX]; 73e2ef54deSDag-Erling Smørgrav static char tempname[PATH_MAX]; 74e2ef54deSDag-Erling Smørgrav static int initialized; 75b603d90cSAndrey A. Chernov 76547fa0d9SMark Murray #if 0 77b603d90cSAndrey A. Chernov void 7893deb2aeSDag-Erling Smørgrav pw_cont(int sig) 79b603d90cSAndrey A. Chernov { 80b603d90cSAndrey A. Chernov 81b603d90cSAndrey A. Chernov if (editpid != -1) 82b603d90cSAndrey A. Chernov kill(editpid, sig); 83b603d90cSAndrey A. Chernov } 84547fa0d9SMark Murray #endif 85dea673e9SRodney W. Grimes 86e2ef54deSDag-Erling Smørgrav /* 87e2ef54deSDag-Erling Smørgrav * Initialize statics and set limits, signals & umask to try to avoid 88e2ef54deSDag-Erling Smørgrav * interruptions, crashes etc. that might expose passord data. 89e2ef54deSDag-Erling Smørgrav */ 90e2ef54deSDag-Erling Smørgrav int 91e2ef54deSDag-Erling Smørgrav pw_init(const char *dir, const char *master) 92dea673e9SRodney W. Grimes { 93e2ef54deSDag-Erling Smørgrav #if 0 94dea673e9SRodney W. Grimes struct rlimit rlim; 95e2ef54deSDag-Erling Smørgrav #endif 96dea673e9SRodney W. Grimes 97e2ef54deSDag-Erling Smørgrav if (dir == NULL) { 98e2ef54deSDag-Erling Smørgrav strcpy(passwd_dir, _PATH_ETC); 99e2ef54deSDag-Erling Smørgrav } else { 100547fa0d9SMark Murray if (strlen(dir) >= sizeof(passwd_dir)) { 101e2ef54deSDag-Erling Smørgrav errno = ENAMETOOLONG; 102e2ef54deSDag-Erling Smørgrav return (-1); 103e2ef54deSDag-Erling Smørgrav } 104e2ef54deSDag-Erling Smørgrav strcpy(passwd_dir, dir); 105e2ef54deSDag-Erling Smørgrav } 106e2ef54deSDag-Erling Smørgrav 107e2ef54deSDag-Erling Smørgrav if (master == NULL) { 108e2ef54deSDag-Erling Smørgrav if (dir == NULL) { 109e2ef54deSDag-Erling Smørgrav strcpy(masterpasswd, _PATH_MASTERPASSWD); 110547fa0d9SMark Murray } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s", 111547fa0d9SMark Murray passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) { 112e2ef54deSDag-Erling Smørgrav errno = ENAMETOOLONG; 113e2ef54deSDag-Erling Smørgrav return (-1); 114e2ef54deSDag-Erling Smørgrav } 115e2ef54deSDag-Erling Smørgrav } else { 116547fa0d9SMark Murray if (strlen(master) >= sizeof(masterpasswd)) { 117e2ef54deSDag-Erling Smørgrav errno = ENAMETOOLONG; 118e2ef54deSDag-Erling Smørgrav return (-1); 119e2ef54deSDag-Erling Smørgrav } 120e2ef54deSDag-Erling Smørgrav strcpy(masterpasswd, master); 121e2ef54deSDag-Erling Smørgrav } 122e2ef54deSDag-Erling Smørgrav 123e2ef54deSDag-Erling Smørgrav /* 124e2ef54deSDag-Erling Smørgrav * The code that follows is extremely disruptive to the calling 125e2ef54deSDag-Erling Smørgrav * process, and is therefore disabled until someone can conceive 126e2ef54deSDag-Erling Smørgrav * of a realistic scenario where it would fend off a compromise. 127e2ef54deSDag-Erling Smørgrav * Race conditions concerning the temporary files can be guarded 128e2ef54deSDag-Erling Smørgrav * against in other ways than masking signals (by checking stat(2) 129e2ef54deSDag-Erling Smørgrav * results after creation). 130e2ef54deSDag-Erling Smørgrav */ 131e2ef54deSDag-Erling Smørgrav #if 0 132dea673e9SRodney W. Grimes /* Unlimited resource limits. */ 133dea673e9SRodney W. Grimes rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 134dea673e9SRodney W. Grimes (void)setrlimit(RLIMIT_CPU, &rlim); 135dea673e9SRodney W. Grimes (void)setrlimit(RLIMIT_FSIZE, &rlim); 136dea673e9SRodney W. Grimes (void)setrlimit(RLIMIT_STACK, &rlim); 137dea673e9SRodney W. Grimes (void)setrlimit(RLIMIT_DATA, &rlim); 138dea673e9SRodney W. Grimes (void)setrlimit(RLIMIT_RSS, &rlim); 139dea673e9SRodney W. Grimes 140dea673e9SRodney W. Grimes /* Don't drop core (not really necessary, but GP's). */ 141dea673e9SRodney W. Grimes rlim.rlim_cur = rlim.rlim_max = 0; 142dea673e9SRodney W. Grimes (void)setrlimit(RLIMIT_CORE, &rlim); 143dea673e9SRodney W. Grimes 144dea673e9SRodney W. Grimes /* Turn off signals. */ 145dea673e9SRodney W. Grimes (void)signal(SIGALRM, SIG_IGN); 146dea673e9SRodney W. Grimes (void)signal(SIGHUP, SIG_IGN); 147dea673e9SRodney W. Grimes (void)signal(SIGINT, SIG_IGN); 148dea673e9SRodney W. Grimes (void)signal(SIGPIPE, SIG_IGN); 149dea673e9SRodney W. Grimes (void)signal(SIGQUIT, SIG_IGN); 150dea673e9SRodney W. Grimes (void)signal(SIGTERM, SIG_IGN); 151b603d90cSAndrey A. Chernov (void)signal(SIGCONT, pw_cont); 1529d1163f7SPierre Beyssac 1539d1163f7SPierre Beyssac /* Create with exact permissions. */ 1549d1163f7SPierre Beyssac (void)umask(0); 155e2ef54deSDag-Erling Smørgrav #endif 156e2ef54deSDag-Erling Smørgrav initialized = 1; 157e2ef54deSDag-Erling Smørgrav return (0); 158dea673e9SRodney W. Grimes } 159dea673e9SRodney W. Grimes 160e2ef54deSDag-Erling Smørgrav /* 161e2ef54deSDag-Erling Smørgrav * Lock the master password file. 162e2ef54deSDag-Erling Smørgrav */ 163dea673e9SRodney W. Grimes int 16493deb2aeSDag-Erling Smørgrav pw_lock(void) 165dea673e9SRodney W. Grimes { 166e2ef54deSDag-Erling Smørgrav 167e2ef54deSDag-Erling Smørgrav if (*masterpasswd == '\0') 168e2ef54deSDag-Erling Smørgrav return (-1); 169e2ef54deSDag-Erling Smørgrav 170dea673e9SRodney W. Grimes /* 171dea673e9SRodney W. Grimes * If the master password file doesn't exist, the system is hosed. 172dea673e9SRodney W. Grimes * Might as well try to build one. Set the close-on-exec bit so 173dea673e9SRodney W. Grimes * that users can't get at the encrypted passwords while editing. 174dea673e9SRodney W. Grimes * Open should allow flock'ing the file; see 4.4BSD. XXX 175dea673e9SRodney W. Grimes */ 176f16d2ab2SMatthew Dillon for (;;) { 177f16d2ab2SMatthew Dillon struct stat st; 178f16d2ab2SMatthew Dillon 179ede89d5dSBaptiste Daroussin lockfd = flopen(masterpasswd, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); 18098e79fb1SBaptiste Daroussin if (lockfd == -1) { 181074dccd5SNick Hibma if (errno == EWOULDBLOCK) { 182dea673e9SRodney W. Grimes errx(1, "the password db file is busy"); 183074dccd5SNick Hibma } else { 184074dccd5SNick Hibma err(1, "could not lock the passwd file: "); 185074dccd5SNick Hibma } 186074dccd5SNick Hibma } 187f16d2ab2SMatthew Dillon 188f16d2ab2SMatthew Dillon /* 189f16d2ab2SMatthew Dillon * If the password file was replaced while we were trying to 190f16d2ab2SMatthew Dillon * get the lock, our hardlink count will be 0 and we have to 191f16d2ab2SMatthew Dillon * close and retry. 192f16d2ab2SMatthew Dillon */ 193074dccd5SNick Hibma if (fstat(lockfd, &st) == -1) 194074dccd5SNick Hibma err(1, "fstat() failed: "); 195f16d2ab2SMatthew Dillon if (st.st_nlink != 0) 196f16d2ab2SMatthew Dillon break; 197f16d2ab2SMatthew Dillon close(lockfd); 198f16d2ab2SMatthew Dillon lockfd = -1; 199f16d2ab2SMatthew Dillon } 200dea673e9SRodney W. Grimes return (lockfd); 201dea673e9SRodney W. Grimes } 202dea673e9SRodney W. Grimes 203e2ef54deSDag-Erling Smørgrav /* 204e2ef54deSDag-Erling Smørgrav * Create and open a presumably safe temp file for editing the password 205e2ef54deSDag-Erling Smørgrav * data, and copy the master password file into it. 206e2ef54deSDag-Erling Smørgrav */ 207dea673e9SRodney W. Grimes int 208e2ef54deSDag-Erling Smørgrav pw_tmp(int mfd) 209dea673e9SRodney W. Grimes { 210e2ef54deSDag-Erling Smørgrav char buf[8192]; 211547fa0d9SMark Murray ssize_t nr; 212e2ef54deSDag-Erling Smørgrav const char *p; 213e2ef54deSDag-Erling Smørgrav int tfd; 214dea673e9SRodney W. Grimes 215e2ef54deSDag-Erling Smørgrav if (*masterpasswd == '\0') 216e2ef54deSDag-Erling Smørgrav return (-1); 217e2ef54deSDag-Erling Smørgrav if ((p = strrchr(masterpasswd, '/'))) 218dea673e9SRodney W. Grimes ++p; 219dea673e9SRodney W. Grimes else 220e2ef54deSDag-Erling Smørgrav p = masterpasswd; 221547fa0d9SMark Murray if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", 222547fa0d9SMark Murray (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { 223e2ef54deSDag-Erling Smørgrav errno = ENAMETOOLONG; 224e2ef54deSDag-Erling Smørgrav return (-1); 225e2ef54deSDag-Erling Smørgrav } 226cbaba16bSAlan Somers if ((tfd = mkostemp(tempname, 0)) == -1) 227e2ef54deSDag-Erling Smørgrav return (-1); 228e2ef54deSDag-Erling Smørgrav if (mfd != -1) { 229547fa0d9SMark Murray while ((nr = read(mfd, buf, sizeof(buf))) > 0) 230547fa0d9SMark Murray if (write(tfd, buf, (size_t)nr) != nr) 231e2ef54deSDag-Erling Smørgrav break; 232e2ef54deSDag-Erling Smørgrav if (nr != 0) { 233e2ef54deSDag-Erling Smørgrav unlink(tempname); 234e2ef54deSDag-Erling Smørgrav *tempname = '\0'; 235e2ef54deSDag-Erling Smørgrav close(tfd); 236e2ef54deSDag-Erling Smørgrav return (-1); 237e2ef54deSDag-Erling Smørgrav } 238e2ef54deSDag-Erling Smørgrav } 239e2ef54deSDag-Erling Smørgrav return (tfd); 240dea673e9SRodney W. Grimes } 241dea673e9SRodney W. Grimes 242e2ef54deSDag-Erling Smørgrav /* 243e2ef54deSDag-Erling Smørgrav * Regenerate the password database. 244e2ef54deSDag-Erling Smørgrav */ 245dea673e9SRodney W. Grimes int 246c24c3080SBaptiste Daroussin pw_mkdb(const char *user) 247dea673e9SRodney W. Grimes { 248dea673e9SRodney W. Grimes int pstat; 249dea673e9SRodney W. Grimes pid_t pid; 250dea673e9SRodney W. Grimes 251dea673e9SRodney W. Grimes (void)fflush(stderr); 252e2ef54deSDag-Erling Smørgrav switch ((pid = fork())) { 253e2ef54deSDag-Erling Smørgrav case -1: 254e2ef54deSDag-Erling Smørgrav return (-1); 255e2ef54deSDag-Erling Smørgrav case 0: 256e2ef54deSDag-Erling Smørgrav /* child */ 257e2ef54deSDag-Erling Smørgrav if (user == NULL) 258c24c3080SBaptiste Daroussin execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 259ec18ee18SStefan Farfeleder "-d", passwd_dir, tempname, (char *)NULL); 260e2ef54deSDag-Erling Smørgrav else 261c24c3080SBaptiste Daroussin execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 262ec18ee18SStefan Farfeleder "-d", passwd_dir, "-u", user, tempname, 263ec18ee18SStefan Farfeleder (char *)NULL); 264e2ef54deSDag-Erling Smørgrav _exit(1); 265547fa0d9SMark Murray /* NOTREACHED */ 266e2ef54deSDag-Erling Smørgrav default: 267e2ef54deSDag-Erling Smørgrav /* parent */ 268e2ef54deSDag-Erling Smørgrav break; 26979a1b8d9SGuido van Rooij } 270e2ef54deSDag-Erling Smørgrav if (waitpid(pid, &pstat, 0) == -1) 271e2ef54deSDag-Erling Smørgrav return (-1); 272e2ef54deSDag-Erling Smørgrav if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 273dea673e9SRodney W. Grimes return (0); 274e2ef54deSDag-Erling Smørgrav errno = 0; 275e2ef54deSDag-Erling Smørgrav return (-1); 276dea673e9SRodney W. Grimes } 277dea673e9SRodney W. Grimes 278e2ef54deSDag-Erling Smørgrav /* 279e2ef54deSDag-Erling Smørgrav * Edit the temp file. Return -1 on error, >0 if the file was modified, 0 280e2ef54deSDag-Erling Smørgrav * if it was not. 281e2ef54deSDag-Erling Smørgrav */ 282e2ef54deSDag-Erling Smørgrav int 28393deb2aeSDag-Erling Smørgrav pw_edit(int notsetuid) 284dea673e9SRodney W. Grimes { 285e947f78cSDag-Erling Smørgrav struct sigaction sa, sa_int, sa_quit; 2865dc1529cSKonstantin Belousov sigset_t oldsigset, nsigset; 287e2ef54deSDag-Erling Smørgrav struct stat st1, st2; 288e2ef54deSDag-Erling Smørgrav const char *editor; 289dea673e9SRodney W. Grimes int pstat; 290dea673e9SRodney W. Grimes 291e2ef54deSDag-Erling Smørgrav if ((editor = getenv("EDITOR")) == NULL) 292e2ef54deSDag-Erling Smørgrav editor = _PATH_VI; 293e2ef54deSDag-Erling Smørgrav if (stat(tempname, &st1) == -1) 294e2ef54deSDag-Erling Smørgrav return (-1); 295e947f78cSDag-Erling Smørgrav sa.sa_handler = SIG_IGN; 296e947f78cSDag-Erling Smørgrav sigemptyset(&sa.sa_mask); 297e947f78cSDag-Erling Smørgrav sa.sa_flags = 0; 298e947f78cSDag-Erling Smørgrav sigaction(SIGINT, &sa, &sa_int); 299e947f78cSDag-Erling Smørgrav sigaction(SIGQUIT, &sa, &sa_quit); 3005dc1529cSKonstantin Belousov sigemptyset(&nsigset); 3015dc1529cSKonstantin Belousov sigaddset(&nsigset, SIGCHLD); 3025dc1529cSKonstantin Belousov sigprocmask(SIG_BLOCK, &nsigset, &oldsigset); 303e2ef54deSDag-Erling Smørgrav switch ((editpid = fork())) { 304e2ef54deSDag-Erling Smørgrav case -1: 305e2ef54deSDag-Erling Smørgrav return (-1); 306e2ef54deSDag-Erling Smørgrav case 0: 307e947f78cSDag-Erling Smørgrav sigaction(SIGINT, &sa_int, NULL); 308e947f78cSDag-Erling Smørgrav sigaction(SIGQUIT, &sa_quit, NULL); 309e947f78cSDag-Erling Smørgrav sigprocmask(SIG_SETMASK, &oldsigset, NULL); 310dea673e9SRodney W. Grimes if (notsetuid) { 311dea673e9SRodney W. Grimes (void)setgid(getgid()); 312dea673e9SRodney W. Grimes (void)setuid(getuid()); 313dea673e9SRodney W. Grimes } 3144da8edd5SDag-Erling Smørgrav errno = 0; 315e33d251eSEd Schouten execlp(editor, editor, tempname, (char *)NULL); 3164da8edd5SDag-Erling Smørgrav _exit(errno); 317e2ef54deSDag-Erling Smørgrav default: 318e2ef54deSDag-Erling Smørgrav /* parent */ 319e2ef54deSDag-Erling Smørgrav break; 320dea673e9SRodney W. Grimes } 321b603d90cSAndrey A. Chernov for (;;) { 322c794881fSDag-Erling Smørgrav if (waitpid(editpid, &pstat, WUNTRACED) == -1) { 323b7d6bb08SDag-Erling Smørgrav if (errno == EINTR) 324b7d6bb08SDag-Erling Smørgrav continue; 325e2ef54deSDag-Erling Smørgrav unlink(tempname); 326b7d6bb08SDag-Erling Smørgrav editpid = -1; 327e947f78cSDag-Erling Smørgrav break; 328e2ef54deSDag-Erling Smørgrav } else if (WIFSTOPPED(pstat)) { 329b603d90cSAndrey A. Chernov raise(WSTOPSIG(pstat)); 330e2ef54deSDag-Erling Smørgrav } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { 331e2ef54deSDag-Erling Smørgrav editpid = -1; 332b603d90cSAndrey A. Chernov break; 333e2ef54deSDag-Erling Smørgrav } else { 334e2ef54deSDag-Erling Smørgrav unlink(tempname); 335e2ef54deSDag-Erling Smørgrav editpid = -1; 336e947f78cSDag-Erling Smørgrav break; 337b603d90cSAndrey A. Chernov } 338e2ef54deSDag-Erling Smørgrav } 339e947f78cSDag-Erling Smørgrav sigaction(SIGINT, &sa_int, NULL); 340e947f78cSDag-Erling Smørgrav sigaction(SIGQUIT, &sa_quit, NULL); 341e947f78cSDag-Erling Smørgrav sigprocmask(SIG_SETMASK, &oldsigset, NULL); 342e2ef54deSDag-Erling Smørgrav if (stat(tempname, &st2) == -1) 343e2ef54deSDag-Erling Smørgrav return (-1); 344e6ad3d22SEd Schouten return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec || 345e6ad3d22SEd Schouten st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec); 346e2ef54deSDag-Erling Smørgrav } 347e2ef54deSDag-Erling Smørgrav 348e2ef54deSDag-Erling Smørgrav /* 349e2ef54deSDag-Erling Smørgrav * Clean up. Preserve errno for the caller's convenience. 350e2ef54deSDag-Erling Smørgrav */ 351e2ef54deSDag-Erling Smørgrav void 352e2ef54deSDag-Erling Smørgrav pw_fini(void) 353e2ef54deSDag-Erling Smørgrav { 354e2ef54deSDag-Erling Smørgrav int serrno, status; 355e2ef54deSDag-Erling Smørgrav 356e2ef54deSDag-Erling Smørgrav if (!initialized) 357e2ef54deSDag-Erling Smørgrav return; 358e2ef54deSDag-Erling Smørgrav initialized = 0; 359e2ef54deSDag-Erling Smørgrav serrno = errno; 360e2ef54deSDag-Erling Smørgrav if (editpid != -1) { 361e2ef54deSDag-Erling Smørgrav kill(editpid, SIGTERM); 362e2ef54deSDag-Erling Smørgrav kill(editpid, SIGCONT); 363e2ef54deSDag-Erling Smørgrav waitpid(editpid, &status, 0); 364b603d90cSAndrey A. Chernov editpid = -1; 365dea673e9SRodney W. Grimes } 366e2ef54deSDag-Erling Smørgrav if (*tempname != '\0') { 367e2ef54deSDag-Erling Smørgrav unlink(tempname); 368e2ef54deSDag-Erling Smørgrav *tempname = '\0'; 369e2ef54deSDag-Erling Smørgrav } 370e2ef54deSDag-Erling Smørgrav if (lockfd != -1) 371e2ef54deSDag-Erling Smørgrav close(lockfd); 372e2ef54deSDag-Erling Smørgrav errno = serrno; 373dea673e9SRodney W. Grimes } 374dea673e9SRodney W. Grimes 375e2ef54deSDag-Erling Smørgrav /* 376e2ef54deSDag-Erling Smørgrav * Compares two struct pwds. 377e2ef54deSDag-Erling Smørgrav */ 378e2ef54deSDag-Erling Smørgrav int 379547fa0d9SMark Murray pw_equal(const struct passwd *pw1, const struct passwd *pw2) 380dea673e9SRodney W. Grimes { 381e2ef54deSDag-Erling Smørgrav return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 382e2ef54deSDag-Erling Smørgrav pw1->pw_uid == pw2->pw_uid && 383e2ef54deSDag-Erling Smørgrav pw1->pw_gid == pw2->pw_gid && 384e2ef54deSDag-Erling Smørgrav strcmp(pw1->pw_class, pw2->pw_class) == 0 && 385e2ef54deSDag-Erling Smørgrav pw1->pw_change == pw2->pw_change && 386e2ef54deSDag-Erling Smørgrav pw1->pw_expire == pw2->pw_expire && 387e2ef54deSDag-Erling Smørgrav strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 388e2ef54deSDag-Erling Smørgrav strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 389e2ef54deSDag-Erling Smørgrav strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 3903babad2eSDima Dorfman } 391e2ef54deSDag-Erling Smørgrav 392e2ef54deSDag-Erling Smørgrav /* 393e2ef54deSDag-Erling Smørgrav * Make a passwd line out of a struct passwd. 394e2ef54deSDag-Erling Smørgrav */ 395e2ef54deSDag-Erling Smørgrav char * 396547fa0d9SMark Murray pw_make(const struct passwd *pw) 397e2ef54deSDag-Erling Smørgrav { 398e2ef54deSDag-Erling Smørgrav char *line; 399e2ef54deSDag-Erling Smørgrav 400e2ef54deSDag-Erling Smørgrav asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, 401e2ef54deSDag-Erling Smørgrav pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 402e2ef54deSDag-Erling Smørgrav pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, 403e2ef54deSDag-Erling Smørgrav pw->pw_gecos, pw->pw_dir, pw->pw_shell); 404a9e4a478SBaptiste Daroussin return (line); 405a9e4a478SBaptiste Daroussin } 406a9e4a478SBaptiste Daroussin 407a9e4a478SBaptiste Daroussin /* 408a9e4a478SBaptiste Daroussin * Make a passwd line (in v7 format) out of a struct passwd 409a9e4a478SBaptiste Daroussin */ 410a9e4a478SBaptiste Daroussin char * 411a9e4a478SBaptiste Daroussin pw_make_v7(const struct passwd *pw) 412a9e4a478SBaptiste Daroussin { 413a9e4a478SBaptiste Daroussin char *line; 414a9e4a478SBaptiste Daroussin 415a9e4a478SBaptiste Daroussin asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name, 416a9e4a478SBaptiste Daroussin (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 417a9e4a478SBaptiste Daroussin pw->pw_gecos, pw->pw_dir, pw->pw_shell); 418a9e4a478SBaptiste Daroussin return (line); 419e2ef54deSDag-Erling Smørgrav } 420e2ef54deSDag-Erling Smørgrav 421e2ef54deSDag-Erling Smørgrav /* 4221926f2f6SBaptiste Daroussin * Copy password file from one descriptor to another, replacing, deleting 4231926f2f6SBaptiste Daroussin * or adding a single record on the way. 424e2ef54deSDag-Erling Smørgrav */ 425e2ef54deSDag-Erling Smørgrav int 426547fa0d9SMark Murray pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) 427e2ef54deSDag-Erling Smørgrav { 428e68bca50SDag-Erling Smørgrav char *buf, *end, *line, *p, *q, *r, *tmp; 429e2ef54deSDag-Erling Smørgrav struct passwd *fpw; 4301926f2f6SBaptiste Daroussin const struct passwd *spw; 431e68bca50SDag-Erling Smørgrav size_t len, size; 432547fa0d9SMark Murray int eof, readlen; 433e68bca50SDag-Erling Smørgrav char t; 434e2ef54deSDag-Erling Smørgrav 4352f1b1e91SBaptiste Daroussin if (old_pw == NULL && pw == NULL) 4362f1b1e91SBaptiste Daroussin return (-1); 4372f1b1e91SBaptiste Daroussin 4382f1b1e91SBaptiste Daroussin spw = old_pw; 4392f1b1e91SBaptiste Daroussin /* deleting a user */ 4401926f2f6SBaptiste Daroussin if (pw == NULL) { 4411926f2f6SBaptiste Daroussin line = NULL; 4422f1b1e91SBaptiste Daroussin } else { 4432f1b1e91SBaptiste Daroussin if ((line = pw_make(pw)) == NULL) 4441926f2f6SBaptiste Daroussin return (-1); 4452f1b1e91SBaptiste Daroussin } 4462f1b1e91SBaptiste Daroussin 4472f1b1e91SBaptiste Daroussin /* adding a user */ 4482f1b1e91SBaptiste Daroussin if (spw == NULL) 4492f1b1e91SBaptiste Daroussin spw = pw; 450e2ef54deSDag-Erling Smørgrav 451e68bca50SDag-Erling Smørgrav /* initialize the buffer */ 452e68bca50SDag-Erling Smørgrav if ((buf = malloc(size = 1024)) == NULL) 453e68bca50SDag-Erling Smørgrav goto err; 454e68bca50SDag-Erling Smørgrav 455e2ef54deSDag-Erling Smørgrav eof = 0; 456e2ef54deSDag-Erling Smørgrav len = 0; 457e2ef54deSDag-Erling Smørgrav p = q = end = buf; 458e2ef54deSDag-Erling Smørgrav for (;;) { 459e2ef54deSDag-Erling Smørgrav /* find the end of the current line */ 460e2ef54deSDag-Erling Smørgrav for (p = q; q < end && *q != '\0'; ++q) 461e2ef54deSDag-Erling Smørgrav if (*q == '\n') 462e2ef54deSDag-Erling Smørgrav break; 463e2ef54deSDag-Erling Smørgrav 464e2ef54deSDag-Erling Smørgrav /* if we don't have a complete line, fill up the buffer */ 465e2ef54deSDag-Erling Smørgrav if (q >= end) { 466e2ef54deSDag-Erling Smørgrav if (eof) 467e2ef54deSDag-Erling Smørgrav break; 468e68bca50SDag-Erling Smørgrav while ((size_t)(q - p) >= size) { 469efa8af7cSPedro F. Giffuni if ((tmp = reallocarray(buf, 2, size)) == NULL) { 470e2ef54deSDag-Erling Smørgrav warnx("passwd line too long"); 471e2ef54deSDag-Erling Smørgrav goto err; 472e2ef54deSDag-Erling Smørgrav } 473e68bca50SDag-Erling Smørgrav p = tmp + (p - buf); 474e68bca50SDag-Erling Smørgrav q = tmp + (q - buf); 475e68bca50SDag-Erling Smørgrav end = tmp + (end - buf); 476e68bca50SDag-Erling Smørgrav buf = tmp; 477e68bca50SDag-Erling Smørgrav size = size * 2; 478e68bca50SDag-Erling Smørgrav } 479e2ef54deSDag-Erling Smørgrav if (p < end) { 480e2ef54deSDag-Erling Smørgrav q = memmove(buf, p, end - p); 481e2ef54deSDag-Erling Smørgrav end -= p - buf; 482e2ef54deSDag-Erling Smørgrav } else { 483e2ef54deSDag-Erling Smørgrav p = q = end = buf; 484e2ef54deSDag-Erling Smørgrav } 485e68bca50SDag-Erling Smørgrav readlen = read(ffd, end, size - (end - buf)); 486547fa0d9SMark Murray if (readlen == -1) 487e2ef54deSDag-Erling Smørgrav goto err; 488547fa0d9SMark Murray else 489547fa0d9SMark Murray len = (size_t)readlen; 490e2ef54deSDag-Erling Smørgrav if (len == 0 && p == buf) 491e2ef54deSDag-Erling Smørgrav break; 492e2ef54deSDag-Erling Smørgrav end += len; 493e2ef54deSDag-Erling Smørgrav len = end - buf; 494e68bca50SDag-Erling Smørgrav if (len < size) { 495e2ef54deSDag-Erling Smørgrav eof = 1; 496e2ef54deSDag-Erling Smørgrav if (len > 0 && buf[len - 1] != '\n') 497e2ef54deSDag-Erling Smørgrav ++len, *end++ = '\n'; 498e2ef54deSDag-Erling Smørgrav } 499e2ef54deSDag-Erling Smørgrav continue; 500e2ef54deSDag-Erling Smørgrav } 501e2ef54deSDag-Erling Smørgrav 502e2ef54deSDag-Erling Smørgrav /* is it a blank line or a comment? */ 503e2ef54deSDag-Erling Smørgrav for (r = p; r < q && isspace(*r); ++r) 504e2ef54deSDag-Erling Smørgrav /* nothing */ ; 505e2ef54deSDag-Erling Smørgrav if (r == q || *r == '#') { 506e2ef54deSDag-Erling Smørgrav /* yep */ 507e2ef54deSDag-Erling Smørgrav if (write(tfd, p, q - p + 1) != q - p + 1) 508e2ef54deSDag-Erling Smørgrav goto err; 509e2ef54deSDag-Erling Smørgrav ++q; 510e2ef54deSDag-Erling Smørgrav continue; 511e2ef54deSDag-Erling Smørgrav } 512e2ef54deSDag-Erling Smørgrav 513e2ef54deSDag-Erling Smørgrav /* is it the one we're looking for? */ 51471219ddbSThomas Quinot 515e2ef54deSDag-Erling Smørgrav t = *q; 516e2ef54deSDag-Erling Smørgrav *q = '\0'; 51771219ddbSThomas Quinot 518e2ef54deSDag-Erling Smørgrav fpw = pw_scan(r, PWSCAN_MASTER); 51971219ddbSThomas Quinot 52071219ddbSThomas Quinot /* 5213a1d9c27SThomas Quinot * fpw is either the struct passwd for the current line, 52271219ddbSThomas Quinot * or NULL if the line is malformed. 52371219ddbSThomas Quinot */ 52471219ddbSThomas Quinot 525e2ef54deSDag-Erling Smørgrav *q = t; 5262f1b1e91SBaptiste Daroussin if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) { 527e2ef54deSDag-Erling Smørgrav /* nope */ 52871219ddbSThomas Quinot if (fpw != NULL) 529e2ef54deSDag-Erling Smørgrav free(fpw); 530e2ef54deSDag-Erling Smørgrav if (write(tfd, p, q - p + 1) != q - p + 1) 531e2ef54deSDag-Erling Smørgrav goto err; 532e2ef54deSDag-Erling Smørgrav ++q; 533e2ef54deSDag-Erling Smørgrav continue; 534e2ef54deSDag-Erling Smørgrav } 535e7d9d921SDag-Erling Smørgrav if (old_pw && !pw_equal(fpw, old_pw)) { 536e7d9d921SDag-Erling Smørgrav warnx("entry inconsistent"); 537e7d9d921SDag-Erling Smørgrav free(fpw); 538e7d9d921SDag-Erling Smørgrav errno = EINVAL; /* hack */ 539e7d9d921SDag-Erling Smørgrav goto err; 540e7d9d921SDag-Erling Smørgrav } 541e2ef54deSDag-Erling Smørgrav free(fpw); 542e2ef54deSDag-Erling Smørgrav 5431926f2f6SBaptiste Daroussin /* it is, replace or remove it */ 5441926f2f6SBaptiste Daroussin if (line != NULL) { 545e2ef54deSDag-Erling Smørgrav len = strlen(line); 546547fa0d9SMark Murray if (write(tfd, line, len) != (int)len) 547e2ef54deSDag-Erling Smørgrav goto err; 5481926f2f6SBaptiste Daroussin } else { 5491926f2f6SBaptiste Daroussin /* when removed, avoid the \n */ 5501926f2f6SBaptiste Daroussin q++; 5511926f2f6SBaptiste Daroussin } 552e2ef54deSDag-Erling Smørgrav /* we're done, just copy the rest over */ 553e2ef54deSDag-Erling Smørgrav for (;;) { 554e2ef54deSDag-Erling Smørgrav if (write(tfd, q, end - q) != end - q) 555e2ef54deSDag-Erling Smørgrav goto err; 556e2ef54deSDag-Erling Smørgrav q = buf; 557e68bca50SDag-Erling Smørgrav readlen = read(ffd, buf, size); 558547fa0d9SMark Murray if (readlen == 0) 559e2ef54deSDag-Erling Smørgrav break; 560547fa0d9SMark Murray else 561547fa0d9SMark Murray len = (size_t)readlen; 562547fa0d9SMark Murray if (readlen == -1) 563e2ef54deSDag-Erling Smørgrav goto err; 564e2ef54deSDag-Erling Smørgrav end = buf + len; 565e2ef54deSDag-Erling Smørgrav } 566e2ef54deSDag-Erling Smørgrav goto done; 567e2ef54deSDag-Erling Smørgrav } 568e2ef54deSDag-Erling Smørgrav 5691926f2f6SBaptiste Daroussin /* if we got here, we didn't find the old entry */ 5701926f2f6SBaptiste Daroussin if (line == NULL) { 5711926f2f6SBaptiste Daroussin errno = ENOENT; 5721926f2f6SBaptiste Daroussin goto err; 5731926f2f6SBaptiste Daroussin } 574e2ef54deSDag-Erling Smørgrav len = strlen(line); 575547fa0d9SMark Murray if ((size_t)write(tfd, line, len) != len || 5766dcfea0fSDag-Erling Smørgrav write(tfd, "\n", 1) != 1) 577e2ef54deSDag-Erling Smørgrav goto err; 578e2ef54deSDag-Erling Smørgrav done: 579e2ef54deSDag-Erling Smørgrav free(line); 580e68bca50SDag-Erling Smørgrav free(buf); 581e2ef54deSDag-Erling Smørgrav return (0); 582e2ef54deSDag-Erling Smørgrav err: 583e2ef54deSDag-Erling Smørgrav free(line); 584e68bca50SDag-Erling Smørgrav free(buf); 585e2ef54deSDag-Erling Smørgrav return (-1); 586e2ef54deSDag-Erling Smørgrav } 587e2ef54deSDag-Erling Smørgrav 588e2ef54deSDag-Erling Smørgrav /* 589e2ef54deSDag-Erling Smørgrav * Return the current value of tempname. 590e2ef54deSDag-Erling Smørgrav */ 591e2ef54deSDag-Erling Smørgrav const char * 592e2ef54deSDag-Erling Smørgrav pw_tempname(void) 593e2ef54deSDag-Erling Smørgrav { 594e2ef54deSDag-Erling Smørgrav 595e2ef54deSDag-Erling Smørgrav return (tempname); 596e2ef54deSDag-Erling Smørgrav } 597e2ef54deSDag-Erling Smørgrav 598e2ef54deSDag-Erling Smørgrav /* 599e2ef54deSDag-Erling Smørgrav * Duplicate a struct passwd. 600e2ef54deSDag-Erling Smørgrav */ 601e2ef54deSDag-Erling Smørgrav struct passwd * 602547fa0d9SMark Murray pw_dup(const struct passwd *pw) 603e2ef54deSDag-Erling Smørgrav { 604f4fda767SDag-Erling Smørgrav char *dst; 605e2ef54deSDag-Erling Smørgrav struct passwd *npw; 606547fa0d9SMark Murray ssize_t len; 607e2ef54deSDag-Erling Smørgrav 608f4fda767SDag-Erling Smørgrav len = sizeof(*npw); 609f4fda767SDag-Erling Smørgrav if (pw->pw_name != NULL) 610f4fda767SDag-Erling Smørgrav len += strlen(pw->pw_name) + 1; 611f4fda767SDag-Erling Smørgrav if (pw->pw_passwd != NULL) 612f4fda767SDag-Erling Smørgrav len += strlen(pw->pw_passwd) + 1; 613f4fda767SDag-Erling Smørgrav if (pw->pw_class != NULL) 614f4fda767SDag-Erling Smørgrav len += strlen(pw->pw_class) + 1; 615f4fda767SDag-Erling Smørgrav if (pw->pw_gecos != NULL) 616f4fda767SDag-Erling Smørgrav len += strlen(pw->pw_gecos) + 1; 617f4fda767SDag-Erling Smørgrav if (pw->pw_dir != NULL) 618f4fda767SDag-Erling Smørgrav len += strlen(pw->pw_dir) + 1; 619f4fda767SDag-Erling Smørgrav if (pw->pw_shell != NULL) 620f4fda767SDag-Erling Smørgrav len += strlen(pw->pw_shell) + 1; 621547fa0d9SMark Murray if ((npw = malloc((size_t)len)) == NULL) 622e2ef54deSDag-Erling Smørgrav return (NULL); 623547fa0d9SMark Murray memcpy(npw, pw, sizeof(*npw)); 624f4fda767SDag-Erling Smørgrav dst = (char *)npw + sizeof(*npw); 625f4fda767SDag-Erling Smørgrav if (pw->pw_name != NULL) { 626f4fda767SDag-Erling Smørgrav npw->pw_name = dst; 627f4fda767SDag-Erling Smørgrav dst = stpcpy(npw->pw_name, pw->pw_name) + 1; 628e2ef54deSDag-Erling Smørgrav } 629f4fda767SDag-Erling Smørgrav if (pw->pw_passwd != NULL) { 630f4fda767SDag-Erling Smørgrav npw->pw_passwd = dst; 631f4fda767SDag-Erling Smørgrav dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; 632e2ef54deSDag-Erling Smørgrav } 633f4fda767SDag-Erling Smørgrav if (pw->pw_class != NULL) { 634f4fda767SDag-Erling Smørgrav npw->pw_class = dst; 635f4fda767SDag-Erling Smørgrav dst = stpcpy(npw->pw_class, pw->pw_class) + 1; 636e2ef54deSDag-Erling Smørgrav } 637f4fda767SDag-Erling Smørgrav if (pw->pw_gecos != NULL) { 638f4fda767SDag-Erling Smørgrav npw->pw_gecos = dst; 639f4fda767SDag-Erling Smørgrav dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; 640e2ef54deSDag-Erling Smørgrav } 641f4fda767SDag-Erling Smørgrav if (pw->pw_dir != NULL) { 642f4fda767SDag-Erling Smørgrav npw->pw_dir = dst; 643f4fda767SDag-Erling Smørgrav dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; 644e2ef54deSDag-Erling Smørgrav } 645f4fda767SDag-Erling Smørgrav if (pw->pw_shell != NULL) { 646f4fda767SDag-Erling Smørgrav npw->pw_shell = dst; 647f4fda767SDag-Erling Smørgrav dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; 648e2ef54deSDag-Erling Smørgrav } 649e2ef54deSDag-Erling Smørgrav return (npw); 650e2ef54deSDag-Erling Smørgrav } 651e2ef54deSDag-Erling Smørgrav 652e2ef54deSDag-Erling Smørgrav #include "pw_scan.h" 653e2ef54deSDag-Erling Smørgrav 654e2ef54deSDag-Erling Smørgrav /* 655*34e9190dSIan Lepore * Wrapper around some internal libc functions. 656e2ef54deSDag-Erling Smørgrav */ 657*34e9190dSIan Lepore 658*34e9190dSIan Lepore void 659*34e9190dSIan Lepore pw_initpwd(struct passwd *pw) 660*34e9190dSIan Lepore { 661*34e9190dSIan Lepore 662*34e9190dSIan Lepore __pw_initpwd(pw); 663*34e9190dSIan Lepore } 664*34e9190dSIan Lepore 665e2ef54deSDag-Erling Smørgrav struct passwd * 666e2ef54deSDag-Erling Smørgrav pw_scan(const char *line, int flags) 667e2ef54deSDag-Erling Smørgrav { 668e2ef54deSDag-Erling Smørgrav struct passwd pw, *ret; 669e2ef54deSDag-Erling Smørgrav char *bp; 670e2ef54deSDag-Erling Smørgrav 671e2ef54deSDag-Erling Smørgrav if ((bp = strdup(line)) == NULL) 672e2ef54deSDag-Erling Smørgrav return (NULL); 673*34e9190dSIan Lepore __pw_initpwd(&pw); 674e2ef54deSDag-Erling Smørgrav if (!__pw_scan(bp, &pw, flags)) { 675e2ef54deSDag-Erling Smørgrav free(bp); 676e2ef54deSDag-Erling Smørgrav return (NULL); 677e2ef54deSDag-Erling Smørgrav } 678e2ef54deSDag-Erling Smørgrav ret = pw_dup(&pw); 679e2ef54deSDag-Erling Smørgrav free(bp); 680e2ef54deSDag-Erling Smørgrav return (ret); 681dea673e9SRodney W. Grimes } 682