11553Srgrimes/*- 21553Srgrimes * Copyright (c) 1990, 1993, 1994 31553Srgrimes * The Regents of the University of California. All rights reserved. 496199Sdes * Copyright (c) 2002 Networks Associates Technology, Inc. 596199Sdes * All rights reserved. 61553Srgrimes * 796199Sdes * Portions of this software were developed for the FreeBSD Project by 896199Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 996199Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1096199Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1196199Sdes * 121553Srgrimes * Redistribution and use in source and binary forms, with or without 131553Srgrimes * modification, are permitted provided that the following conditions 141553Srgrimes * are met: 151553Srgrimes * 1. Redistributions of source code must retain the above copyright 161553Srgrimes * notice, this list of conditions and the following disclaimer. 171553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181553Srgrimes * notice, this list of conditions and the following disclaimer in the 191553Srgrimes * documentation and/or other materials provided with the distribution. 201553Srgrimes * 4. Neither the name of the University nor the names of its contributors 211553Srgrimes * may be used to endorse or promote products derived from this software 221553Srgrimes * without specific prior written permission. 231553Srgrimes * 241553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341553Srgrimes * SUCH DAMAGE. 351553Srgrimes */ 361553Srgrimes 371553Srgrimes#ifndef lint 3830765Scharnier#if 0 3930113Sjkhstatic const char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94"; 4030765Scharnier#endif 4130765Scharnierstatic const char rcsid[] = 4250479Speter "$FreeBSD: stable/10/lib/libutil/pw_util.c 310480 2016-12-23 15:05:41Z des $"; 431553Srgrimes#endif /* not lint */ 441553Srgrimes 451553Srgrimes/* 461553Srgrimes * This file is used by all the "password" programs; vipw(8), chpass(1), 471553Srgrimes * and passwd(1). 481553Srgrimes */ 491553Srgrimes 501553Srgrimes#include <sys/param.h> 5140538Sdes#include <sys/errno.h> 521553Srgrimes#include <sys/time.h> 531553Srgrimes#include <sys/resource.h> 541553Srgrimes#include <sys/stat.h> 551553Srgrimes#include <sys/wait.h> 561553Srgrimes 57116344Smarkm#include <ctype.h> 581553Srgrimes#include <err.h> 591553Srgrimes#include <fcntl.h> 6096199Sdes#include <inttypes.h> 61113333Sdes#include <libgen.h> 621553Srgrimes#include <paths.h> 631553Srgrimes#include <pwd.h> 641553Srgrimes#include <signal.h> 651553Srgrimes#include <stdio.h> 661553Srgrimes#include <stdlib.h> 671553Srgrimes#include <string.h> 681553Srgrimes#include <unistd.h> 691553Srgrimes 70211392Sdes#include "libutil.h" 711553Srgrimes 726972Sachestatic pid_t editpid = -1; 7396199Sdesstatic int lockfd = -1; 7496199Sdesstatic char masterpasswd[PATH_MAX]; 7596199Sdesstatic char passwd_dir[PATH_MAX]; 7696199Sdesstatic char tempname[PATH_MAX]; 7796199Sdesstatic int initialized; 781553Srgrimes 79121193Smarkm#if 0 801553Srgrimesvoid 8190233Sdespw_cont(int sig) 826972Sache{ 836972Sache 846972Sache if (editpid != -1) 856972Sache kill(editpid, sig); 866972Sache} 87121193Smarkm#endif 886972Sache 8996199Sdes/* 9096199Sdes * Initialize statics and set limits, signals & umask to try to avoid 9196199Sdes * interruptions, crashes etc. that might expose passord data. 9296199Sdes */ 9396199Sdesint 9496199Sdespw_init(const char *dir, const char *master) 951553Srgrimes{ 9696199Sdes#if 0 971553Srgrimes struct rlimit rlim; 9896199Sdes#endif 991553Srgrimes 10096199Sdes if (dir == NULL) { 10196199Sdes strcpy(passwd_dir, _PATH_ETC); 10296199Sdes } else { 103121193Smarkm if (strlen(dir) >= sizeof(passwd_dir)) { 10496199Sdes errno = ENAMETOOLONG; 10596199Sdes return (-1); 10696199Sdes } 10796199Sdes strcpy(passwd_dir, dir); 10896199Sdes } 10996199Sdes 11096199Sdes if (master == NULL) { 11196199Sdes if (dir == NULL) { 11296199Sdes strcpy(masterpasswd, _PATH_MASTERPASSWD); 113121193Smarkm } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s", 114121193Smarkm passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) { 11596199Sdes errno = ENAMETOOLONG; 11696199Sdes return (-1); 11796199Sdes } 11896199Sdes } else { 119121193Smarkm if (strlen(master) >= sizeof(masterpasswd)) { 12096199Sdes errno = ENAMETOOLONG; 12196199Sdes return (-1); 12296199Sdes } 12396199Sdes strcpy(masterpasswd, master); 12496199Sdes } 12596199Sdes 12696199Sdes /* 12796199Sdes * The code that follows is extremely disruptive to the calling 12896199Sdes * process, and is therefore disabled until someone can conceive 12996199Sdes * of a realistic scenario where it would fend off a compromise. 13096199Sdes * Race conditions concerning the temporary files can be guarded 13196199Sdes * against in other ways than masking signals (by checking stat(2) 13296199Sdes * results after creation). 13396199Sdes */ 13496199Sdes#if 0 1351553Srgrimes /* Unlimited resource limits. */ 1361553Srgrimes rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 1371553Srgrimes (void)setrlimit(RLIMIT_CPU, &rlim); 1381553Srgrimes (void)setrlimit(RLIMIT_FSIZE, &rlim); 1391553Srgrimes (void)setrlimit(RLIMIT_STACK, &rlim); 1401553Srgrimes (void)setrlimit(RLIMIT_DATA, &rlim); 1411553Srgrimes (void)setrlimit(RLIMIT_RSS, &rlim); 1421553Srgrimes 1431553Srgrimes /* Don't drop core (not really necessary, but GP's). */ 1441553Srgrimes rlim.rlim_cur = rlim.rlim_max = 0; 1451553Srgrimes (void)setrlimit(RLIMIT_CORE, &rlim); 1461553Srgrimes 1471553Srgrimes /* Turn off signals. */ 1481553Srgrimes (void)signal(SIGALRM, SIG_IGN); 1491553Srgrimes (void)signal(SIGHUP, SIG_IGN); 1501553Srgrimes (void)signal(SIGINT, SIG_IGN); 1511553Srgrimes (void)signal(SIGPIPE, SIG_IGN); 1521553Srgrimes (void)signal(SIGQUIT, SIG_IGN); 1531553Srgrimes (void)signal(SIGTERM, SIG_IGN); 1546972Sache (void)signal(SIGCONT, pw_cont); 15548328Spb 15648328Spb /* Create with exact permissions. */ 15748328Spb (void)umask(0); 15896199Sdes#endif 15996199Sdes initialized = 1; 16096199Sdes return (0); 1611553Srgrimes} 1621553Srgrimes 16396199Sdes/* 16496199Sdes * Lock the master password file. 16596199Sdes */ 1661553Srgrimesint 16790233Sdespw_lock(void) 1681553Srgrimes{ 16996199Sdes 17096199Sdes if (*masterpasswd == '\0') 17196199Sdes return (-1); 17296199Sdes 1738857Srgrimes /* 1741553Srgrimes * If the master password file doesn't exist, the system is hosed. 1751553Srgrimes * Might as well try to build one. Set the close-on-exec bit so 1761553Srgrimes * that users can't get at the encrypted passwords while editing. 1771553Srgrimes * Open should allow flock'ing the file; see 4.4BSD. XXX 1781553Srgrimes */ 17941710Sdillon for (;;) { 18041711Sdillon struct stat st; 18141710Sdillon 182244744Sbapt lockfd = flopen(masterpasswd, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); 183244735Sbapt if (lockfd == -1) { 18498693Sn_hibma if (errno == EWOULDBLOCK) { 18598693Sn_hibma errx(1, "the password db file is busy"); 18698693Sn_hibma } else { 18798693Sn_hibma err(1, "could not lock the passwd file: "); 18898693Sn_hibma } 18998693Sn_hibma } 19041710Sdillon 19141711Sdillon /* 19241711Sdillon * If the password file was replaced while we were trying to 19341711Sdillon * get the lock, our hardlink count will be 0 and we have to 19441711Sdillon * close and retry. 19541711Sdillon */ 19698693Sn_hibma if (fstat(lockfd, &st) == -1) 19798693Sn_hibma err(1, "fstat() failed: "); 19841711Sdillon if (st.st_nlink != 0) 19941711Sdillon break; 20041711Sdillon close(lockfd); 20141711Sdillon lockfd = -1; 20241710Sdillon } 2031553Srgrimes return (lockfd); 2041553Srgrimes} 2051553Srgrimes 20696199Sdes/* 20796199Sdes * Create and open a presumably safe temp file for editing the password 20896199Sdes * data, and copy the master password file into it. 20996199Sdes */ 2101553Srgrimesint 21196199Sdespw_tmp(int mfd) 2121553Srgrimes{ 21396199Sdes char buf[8192]; 214121193Smarkm ssize_t nr; 21596199Sdes const char *p; 21696199Sdes int tfd; 2171553Srgrimes 21896199Sdes if (*masterpasswd == '\0') 21996199Sdes return (-1); 22096199Sdes if ((p = strrchr(masterpasswd, '/'))) 2211553Srgrimes ++p; 2221553Srgrimes else 22396199Sdes p = masterpasswd; 224121193Smarkm if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", 225121193Smarkm (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { 22696199Sdes errno = ENAMETOOLONG; 22796199Sdes return (-1); 22896199Sdes } 229310173Sasomers if ((tfd = mkostemp(tempname, 0)) == -1) 23096199Sdes return (-1); 23196199Sdes if (mfd != -1) { 232121193Smarkm while ((nr = read(mfd, buf, sizeof(buf))) > 0) 233121193Smarkm if (write(tfd, buf, (size_t)nr) != nr) 23496199Sdes break; 23596199Sdes if (nr != 0) { 23696199Sdes unlink(tempname); 23796199Sdes *tempname = '\0'; 23896199Sdes close(tfd); 23996199Sdes return (-1); 24096199Sdes } 24196199Sdes } 24296199Sdes return (tfd); 2431553Srgrimes} 2441553Srgrimes 24596199Sdes/* 24696199Sdes * Regenerate the password database. 24796199Sdes */ 2481553Srgrimesint 24996199Sdespw_mkdb(const char *user) 2501553Srgrimes{ 2511553Srgrimes int pstat; 2521553Srgrimes pid_t pid; 2531553Srgrimes 2541553Srgrimes (void)fflush(stderr); 25596199Sdes switch ((pid = fork())) { 25696199Sdes case -1: 25796199Sdes return (-1); 25896199Sdes case 0: 25996199Sdes /* child */ 26096199Sdes if (user == NULL) 26196199Sdes execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 262129392Sstefanf "-d", passwd_dir, tempname, (char *)NULL); 26396199Sdes else 26496199Sdes execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 265129392Sstefanf "-d", passwd_dir, "-u", user, tempname, 266129392Sstefanf (char *)NULL); 26796199Sdes _exit(1); 268121193Smarkm /* NOTREACHED */ 26996199Sdes default: 27096199Sdes /* parent */ 27196199Sdes break; 2721553Srgrimes } 27396199Sdes if (waitpid(pid, &pstat, 0) == -1) 27496199Sdes return (-1); 27596199Sdes if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 2761553Srgrimes return (0); 27796199Sdes errno = 0; 27896199Sdes return (-1); 2791553Srgrimes} 2801553Srgrimes 28196199Sdes/* 28296199Sdes * Edit the temp file. Return -1 on error, >0 if the file was modified, 0 28396199Sdes * if it was not. 28496199Sdes */ 28596199Sdesint 28690233Sdespw_edit(int notsetuid) 2871553Srgrimes{ 288113301Sdes struct sigaction sa, sa_int, sa_quit; 289199826Skib sigset_t oldsigset, nsigset; 29096199Sdes struct stat st1, st2; 29196199Sdes const char *editor; 2921553Srgrimes int pstat; 2931553Srgrimes 29496199Sdes if ((editor = getenv("EDITOR")) == NULL) 29596199Sdes editor = _PATH_VI; 29696199Sdes if (stat(tempname, &st1) == -1) 29796199Sdes return (-1); 298113301Sdes sa.sa_handler = SIG_IGN; 299113301Sdes sigemptyset(&sa.sa_mask); 300113301Sdes sa.sa_flags = 0; 301113301Sdes sigaction(SIGINT, &sa, &sa_int); 302113301Sdes sigaction(SIGQUIT, &sa, &sa_quit); 303199826Skib sigemptyset(&nsigset); 304199826Skib sigaddset(&nsigset, SIGCHLD); 305199826Skib sigprocmask(SIG_BLOCK, &nsigset, &oldsigset); 30696199Sdes switch ((editpid = fork())) { 30796199Sdes case -1: 30896199Sdes return (-1); 30996199Sdes case 0: 310113301Sdes sigaction(SIGINT, &sa_int, NULL); 311113333Sdes sigaction(SIGQUIT, &sa_quit, NULL); 312113301Sdes sigprocmask(SIG_SETMASK, &oldsigset, NULL); 3131553Srgrimes if (notsetuid) { 3141553Srgrimes (void)setgid(getgid()); 3151553Srgrimes (void)setuid(getuid()); 3161553Srgrimes } 31740538Sdes errno = 0; 318129392Sstefanf execlp(editor, basename(editor), tempname, (char *)NULL); 31940538Sdes _exit(errno); 32096199Sdes default: 32196199Sdes /* parent */ 32296199Sdes break; 3231553Srgrimes } 3246972Sache for (;;) { 325113265Sdes if (waitpid(editpid, &pstat, WUNTRACED) == -1) { 326113333Sdes if (errno == EINTR) 327113333Sdes continue; 32896199Sdes unlink(tempname); 329113333Sdes editpid = -1; 330113301Sdes break; 33196199Sdes } else if (WIFSTOPPED(pstat)) { 3326972Sache raise(WSTOPSIG(pstat)); 33396199Sdes } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { 33496199Sdes editpid = -1; 3356972Sache break; 33696199Sdes } else { 33796199Sdes unlink(tempname); 33896199Sdes editpid = -1; 339113301Sdes break; 34096199Sdes } 3416972Sache } 342113301Sdes sigaction(SIGINT, &sa_int, NULL); 343113333Sdes sigaction(SIGQUIT, &sa_quit, NULL); 344113301Sdes sigprocmask(SIG_SETMASK, &oldsigset, NULL); 34596199Sdes if (stat(tempname, &st2) == -1) 34696199Sdes return (-1); 347231383Sed return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec || 348231383Sed st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec); 3491553Srgrimes} 3501553Srgrimes 35196199Sdes/* 35296199Sdes * Clean up. Preserve errno for the caller's convenience. 35396199Sdes */ 3541553Srgrimesvoid 35596199Sdespw_fini(void) 3561553Srgrimes{ 35796199Sdes int serrno, status; 3581553Srgrimes 35996199Sdes if (!initialized) 36096199Sdes return; 36196199Sdes initialized = 0; 36296199Sdes serrno = errno; 36396199Sdes if (editpid != -1) { 36496199Sdes kill(editpid, SIGTERM); 36596199Sdes kill(editpid, SIGCONT); 36696199Sdes waitpid(editpid, &status, 0); 36796199Sdes editpid = -1; 36896199Sdes } 36996199Sdes if (*tempname != '\0') { 37096199Sdes unlink(tempname); 37196199Sdes *tempname = '\0'; 37296199Sdes } 37396199Sdes if (lockfd != -1) 37496199Sdes close(lockfd); 37596199Sdes errno = serrno; 3761553Srgrimes} 3771553Srgrimes 37896199Sdes/* 37996199Sdes * Compares two struct pwds. 38096199Sdes */ 38196199Sdesint 382121193Smarkmpw_equal(const struct passwd *pw1, const struct passwd *pw2) 3831553Srgrimes{ 38496199Sdes return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 38596199Sdes pw1->pw_uid == pw2->pw_uid && 38696199Sdes pw1->pw_gid == pw2->pw_gid && 38796199Sdes strcmp(pw1->pw_class, pw2->pw_class) == 0 && 38896199Sdes pw1->pw_change == pw2->pw_change && 38996199Sdes pw1->pw_expire == pw2->pw_expire && 39096199Sdes strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 39196199Sdes strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 39296199Sdes strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 39396199Sdes} 39496199Sdes 39596199Sdes/* 39696199Sdes * Make a passwd line out of a struct passwd. 39796199Sdes */ 39896199Sdeschar * 399121193Smarkmpw_make(const struct passwd *pw) 40096199Sdes{ 40196199Sdes char *line; 40296199Sdes 40396199Sdes asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, 40496199Sdes pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 40596199Sdes pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, 40696199Sdes pw->pw_gecos, pw->pw_dir, pw->pw_shell); 407229572Sbapt return (line); 40896199Sdes} 40996199Sdes 41096199Sdes/* 411229572Sbapt * Make a passwd line (in v7 format) out of a struct passwd 412229572Sbapt */ 413229572Sbaptchar * 414229572Sbaptpw_make_v7(const struct passwd *pw) 415229572Sbapt{ 416229572Sbapt char *line; 417229572Sbapt 418229572Sbapt asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name, 419229572Sbapt (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 420229572Sbapt pw->pw_gecos, pw->pw_dir, pw->pw_shell); 421229572Sbapt return (line); 422229572Sbapt} 423229572Sbapt 424229572Sbapt/* 425228545Sbapt * Copy password file from one descriptor to another, replacing, deleting 426228545Sbapt * or adding a single record on the way. 42796199Sdes */ 42896199Sdesint 429121193Smarkmpw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) 43096199Sdes{ 431310480Sdes char *buf, *end, *line, *p, *q, *r, *tmp; 43296199Sdes struct passwd *fpw; 433228545Sbapt const struct passwd *spw; 434310480Sdes size_t len, size; 435121193Smarkm int eof, readlen; 436310480Sdes char t; 43796199Sdes 438237268Sbapt if (old_pw == NULL && pw == NULL) 439237268Sbapt return (-1); 440237268Sbapt 441237268Sbapt spw = old_pw; 442237268Sbapt /* deleting a user */ 443228545Sbapt if (pw == NULL) { 444228545Sbapt line = NULL; 445237268Sbapt } else { 446237268Sbapt if ((line = pw_make(pw)) == NULL) 447228545Sbapt return (-1); 448237268Sbapt } 44996199Sdes 450237268Sbapt /* adding a user */ 451237268Sbapt if (spw == NULL) 452237268Sbapt spw = pw; 453237268Sbapt 454310480Sdes /* initialize the buffer */ 455310480Sdes if ((buf = malloc(size = 1024)) == NULL) 456310480Sdes goto err; 457310480Sdes 45896199Sdes eof = 0; 45996199Sdes len = 0; 46096199Sdes p = q = end = buf; 46196199Sdes for (;;) { 46296199Sdes /* find the end of the current line */ 46396199Sdes for (p = q; q < end && *q != '\0'; ++q) 46496199Sdes if (*q == '\n') 46596199Sdes break; 46696199Sdes 46796199Sdes /* if we don't have a complete line, fill up the buffer */ 46896199Sdes if (q >= end) { 46996199Sdes if (eof) 47096199Sdes break; 471310480Sdes while ((size_t)(q - p) >= size) { 472310480Sdes if ((tmp = realloc(buf, size * 2)) == NULL) { 473310480Sdes warnx("passwd line too long"); 474310480Sdes goto err; 475310480Sdes } 476310480Sdes p = tmp + (p - buf); 477310480Sdes q = tmp + (q - buf); 478310480Sdes end = tmp + (end - buf); 479310480Sdes buf = tmp; 480310480Sdes size = size * 2; 48196199Sdes } 48296199Sdes if (p < end) { 48396199Sdes q = memmove(buf, p, end - p); 48496199Sdes end -= p - buf; 48596199Sdes } else { 48696199Sdes p = q = end = buf; 48796199Sdes } 488310480Sdes readlen = read(ffd, end, size - (end - buf)); 489121193Smarkm if (readlen == -1) 49096199Sdes goto err; 491121193Smarkm else 492121193Smarkm len = (size_t)readlen; 49396199Sdes if (len == 0 && p == buf) 49496199Sdes break; 49596199Sdes end += len; 49696199Sdes len = end - buf; 497310480Sdes if (len < size) { 49896199Sdes eof = 1; 49996199Sdes if (len > 0 && buf[len - 1] != '\n') 50096199Sdes ++len, *end++ = '\n'; 50196199Sdes } 50296199Sdes continue; 50396199Sdes } 50496199Sdes 50596199Sdes /* is it a blank line or a comment? */ 50696199Sdes for (r = p; r < q && isspace(*r); ++r) 50796199Sdes /* nothing */ ; 50896199Sdes if (r == q || *r == '#') { 50996199Sdes /* yep */ 51096199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 51196199Sdes goto err; 51296199Sdes ++q; 51396199Sdes continue; 51496199Sdes } 51596199Sdes 51696199Sdes /* is it the one we're looking for? */ 517161997Sthomas 51896199Sdes t = *q; 51996199Sdes *q = '\0'; 520161997Sthomas 52196199Sdes fpw = pw_scan(r, PWSCAN_MASTER); 522161997Sthomas 523161997Sthomas /* 524162141Sthomas * fpw is either the struct passwd for the current line, 525161997Sthomas * or NULL if the line is malformed. 526161997Sthomas */ 527161997Sthomas 52896199Sdes *q = t; 529237268Sbapt if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) { 53096199Sdes /* nope */ 531161997Sthomas if (fpw != NULL) 532161997Sthomas free(fpw); 53396199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 53496199Sdes goto err; 53596199Sdes ++q; 53696199Sdes continue; 53796199Sdes } 538113305Sdes if (old_pw && !pw_equal(fpw, old_pw)) { 539113305Sdes warnx("entry inconsistent"); 540113305Sdes free(fpw); 541113305Sdes errno = EINVAL; /* hack */ 542113305Sdes goto err; 543113305Sdes } 54496199Sdes free(fpw); 54596199Sdes 546228545Sbapt /* it is, replace or remove it */ 547228545Sbapt if (line != NULL) { 548228545Sbapt len = strlen(line); 549228545Sbapt if (write(tfd, line, len) != (int)len) 550228545Sbapt goto err; 551228545Sbapt } else { 552228545Sbapt /* when removed, avoid the \n */ 553228545Sbapt q++; 554228545Sbapt } 55596199Sdes /* we're done, just copy the rest over */ 55696199Sdes for (;;) { 55796199Sdes if (write(tfd, q, end - q) != end - q) 55896199Sdes goto err; 55996199Sdes q = buf; 560310480Sdes readlen = read(ffd, buf, size); 561121193Smarkm if (readlen == 0) 56296199Sdes break; 563121193Smarkm else 564121193Smarkm len = (size_t)readlen; 565121193Smarkm if (readlen == -1) 56696199Sdes goto err; 56796199Sdes end = buf + len; 56896199Sdes } 56996199Sdes goto done; 57075821Sdd } 57196199Sdes 572228545Sbapt /* if we got here, we didn't find the old entry */ 573228545Sbapt if (line == NULL) { 574228545Sbapt errno = ENOENT; 575228545Sbapt goto err; 576228545Sbapt } 57796199Sdes len = strlen(line); 578121193Smarkm if ((size_t)write(tfd, line, len) != len || 579106140Sdes write(tfd, "\n", 1) != 1) 58096199Sdes goto err; 58196199Sdes done: 582310480Sdes free(line); 583310480Sdes free(buf); 58496199Sdes return (0); 58596199Sdes err: 586310480Sdes free(line); 587310480Sdes free(buf); 58896199Sdes return (-1); 5891553Srgrimes} 59096199Sdes 59196199Sdes/* 59296199Sdes * Return the current value of tempname. 59396199Sdes */ 59496199Sdesconst char * 59596199Sdespw_tempname(void) 59696199Sdes{ 59796199Sdes 59896199Sdes return (tempname); 59996199Sdes} 60096199Sdes 60196199Sdes/* 60296199Sdes * Duplicate a struct passwd. 60396199Sdes */ 60496199Sdesstruct passwd * 605121193Smarkmpw_dup(const struct passwd *pw) 60696199Sdes{ 607211392Sdes char *dst; 60896199Sdes struct passwd *npw; 609121193Smarkm ssize_t len; 61096199Sdes 611211392Sdes len = sizeof(*npw); 612211392Sdes if (pw->pw_name != NULL) 613211392Sdes len += strlen(pw->pw_name) + 1; 614211392Sdes if (pw->pw_passwd != NULL) 615211392Sdes len += strlen(pw->pw_passwd) + 1; 616211392Sdes if (pw->pw_class != NULL) 617211392Sdes len += strlen(pw->pw_class) + 1; 618211392Sdes if (pw->pw_gecos != NULL) 619211392Sdes len += strlen(pw->pw_gecos) + 1; 620211392Sdes if (pw->pw_dir != NULL) 621211392Sdes len += strlen(pw->pw_dir) + 1; 622211392Sdes if (pw->pw_shell != NULL) 623211392Sdes len += strlen(pw->pw_shell) + 1; 624121193Smarkm if ((npw = malloc((size_t)len)) == NULL) 62596199Sdes return (NULL); 626121193Smarkm memcpy(npw, pw, sizeof(*npw)); 627211392Sdes dst = (char *)npw + sizeof(*npw); 628211392Sdes if (pw->pw_name != NULL) { 629211392Sdes npw->pw_name = dst; 630211392Sdes dst = stpcpy(npw->pw_name, pw->pw_name) + 1; 63196199Sdes } 632211392Sdes if (pw->pw_passwd != NULL) { 633211392Sdes npw->pw_passwd = dst; 634211392Sdes dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; 63596199Sdes } 636211392Sdes if (pw->pw_class != NULL) { 637211392Sdes npw->pw_class = dst; 638211392Sdes dst = stpcpy(npw->pw_class, pw->pw_class) + 1; 63996199Sdes } 640211392Sdes if (pw->pw_gecos != NULL) { 641211392Sdes npw->pw_gecos = dst; 642211392Sdes dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; 64396199Sdes } 644211392Sdes if (pw->pw_dir != NULL) { 645211392Sdes npw->pw_dir = dst; 646211392Sdes dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; 64796199Sdes } 648211392Sdes if (pw->pw_shell != NULL) { 649211392Sdes npw->pw_shell = dst; 650211392Sdes dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; 65196199Sdes } 65296199Sdes return (npw); 65396199Sdes} 65496199Sdes 65596199Sdes#include "pw_scan.h" 65696199Sdes 65796199Sdes/* 65896199Sdes * Wrapper around an internal libc function 65996199Sdes */ 66096199Sdesstruct passwd * 66196199Sdespw_scan(const char *line, int flags) 66296199Sdes{ 66396199Sdes struct passwd pw, *ret; 66496199Sdes char *bp; 66596199Sdes 66696199Sdes if ((bp = strdup(line)) == NULL) 66796199Sdes return (NULL); 66896199Sdes if (!__pw_scan(bp, &pw, flags)) { 66996199Sdes free(bp); 67096199Sdes return (NULL); 67196199Sdes } 67296199Sdes ret = pw_dup(&pw); 67396199Sdes free(bp); 67496199Sdes return (ret); 67596199Sdes} 676