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$"; 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 } 22996199Sdes if ((tfd = mkstemp(tempname)) == -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{ 43196199Sdes char buf[8192], *end, *line, *p, *q, *r, t; 43296199Sdes struct passwd *fpw; 433228545Sbapt const struct passwd *spw; 434121193Smarkm size_t len; 435121193Smarkm int eof, readlen; 43696199Sdes 437237268Sbapt if (old_pw == NULL && pw == NULL) 438237268Sbapt return (-1); 439237268Sbapt 440237268Sbapt spw = old_pw; 441237268Sbapt /* deleting a user */ 442228545Sbapt if (pw == NULL) { 443228545Sbapt line = NULL; 444237268Sbapt } else { 445237268Sbapt if ((line = pw_make(pw)) == NULL) 446228545Sbapt return (-1); 447237268Sbapt } 44896199Sdes 449237268Sbapt /* adding a user */ 450237268Sbapt if (spw == NULL) 451237268Sbapt spw = pw; 452237268Sbapt 45396199Sdes eof = 0; 45496199Sdes len = 0; 45596199Sdes p = q = end = buf; 45696199Sdes for (;;) { 45796199Sdes /* find the end of the current line */ 45896199Sdes for (p = q; q < end && *q != '\0'; ++q) 45996199Sdes if (*q == '\n') 46096199Sdes break; 46196199Sdes 46296199Sdes /* if we don't have a complete line, fill up the buffer */ 46396199Sdes if (q >= end) { 46496199Sdes if (eof) 46596199Sdes break; 466121193Smarkm if ((size_t)(q - p) >= sizeof(buf)) { 46796199Sdes warnx("passwd line too long"); 46896199Sdes errno = EINVAL; /* hack */ 46996199Sdes goto err; 47096199Sdes } 47196199Sdes if (p < end) { 47296199Sdes q = memmove(buf, p, end - p); 47396199Sdes end -= p - buf; 47496199Sdes } else { 47596199Sdes p = q = end = buf; 47696199Sdes } 477121193Smarkm readlen = read(ffd, end, sizeof(buf) - (end - buf)); 478121193Smarkm if (readlen == -1) 47996199Sdes goto err; 480121193Smarkm else 481121193Smarkm len = (size_t)readlen; 48296199Sdes if (len == 0 && p == buf) 48396199Sdes break; 48496199Sdes end += len; 48596199Sdes len = end - buf; 486121193Smarkm if (len < (ssize_t)sizeof(buf)) { 48796199Sdes eof = 1; 48896199Sdes if (len > 0 && buf[len - 1] != '\n') 48996199Sdes ++len, *end++ = '\n'; 49096199Sdes } 49196199Sdes continue; 49296199Sdes } 49396199Sdes 49496199Sdes /* is it a blank line or a comment? */ 49596199Sdes for (r = p; r < q && isspace(*r); ++r) 49696199Sdes /* nothing */ ; 49796199Sdes if (r == q || *r == '#') { 49896199Sdes /* yep */ 49996199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 50096199Sdes goto err; 50196199Sdes ++q; 50296199Sdes continue; 50396199Sdes } 50496199Sdes 50596199Sdes /* is it the one we're looking for? */ 506161997Sthomas 50796199Sdes t = *q; 50896199Sdes *q = '\0'; 509161997Sthomas 51096199Sdes fpw = pw_scan(r, PWSCAN_MASTER); 511161997Sthomas 512161997Sthomas /* 513162141Sthomas * fpw is either the struct passwd for the current line, 514161997Sthomas * or NULL if the line is malformed. 515161997Sthomas */ 516161997Sthomas 51796199Sdes *q = t; 518237268Sbapt if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) { 51996199Sdes /* nope */ 520161997Sthomas if (fpw != NULL) 521161997Sthomas free(fpw); 52296199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 52396199Sdes goto err; 52496199Sdes ++q; 52596199Sdes continue; 52696199Sdes } 527113305Sdes if (old_pw && !pw_equal(fpw, old_pw)) { 528113305Sdes warnx("entry inconsistent"); 529113305Sdes free(fpw); 530113305Sdes errno = EINVAL; /* hack */ 531113305Sdes goto err; 532113305Sdes } 53396199Sdes free(fpw); 53496199Sdes 535228545Sbapt /* it is, replace or remove it */ 536228545Sbapt if (line != NULL) { 537228545Sbapt len = strlen(line); 538228545Sbapt if (write(tfd, line, len) != (int)len) 539228545Sbapt goto err; 540228545Sbapt } else { 541228545Sbapt /* when removed, avoid the \n */ 542228545Sbapt q++; 543228545Sbapt } 54496199Sdes /* we're done, just copy the rest over */ 54596199Sdes for (;;) { 54696199Sdes if (write(tfd, q, end - q) != end - q) 54796199Sdes goto err; 54896199Sdes q = buf; 549121193Smarkm readlen = read(ffd, buf, sizeof(buf)); 550121193Smarkm if (readlen == 0) 55196199Sdes break; 552121193Smarkm else 553121193Smarkm len = (size_t)readlen; 554121193Smarkm if (readlen == -1) 55596199Sdes goto err; 55696199Sdes end = buf + len; 55796199Sdes } 55896199Sdes goto done; 55975821Sdd } 56096199Sdes 561228545Sbapt /* if we got here, we didn't find the old entry */ 562228545Sbapt if (line == NULL) { 563228545Sbapt errno = ENOENT; 564228545Sbapt goto err; 565228545Sbapt } 56696199Sdes len = strlen(line); 567121193Smarkm if ((size_t)write(tfd, line, len) != len || 568106140Sdes write(tfd, "\n", 1) != 1) 56996199Sdes goto err; 57096199Sdes done: 571228545Sbapt if (line != NULL) 572228545Sbapt free(line); 57396199Sdes return (0); 57496199Sdes err: 575228545Sbapt if (line != NULL) 576228545Sbapt free(line); 57796199Sdes return (-1); 5781553Srgrimes} 57996199Sdes 58096199Sdes/* 58196199Sdes * Return the current value of tempname. 58296199Sdes */ 58396199Sdesconst char * 58496199Sdespw_tempname(void) 58596199Sdes{ 58696199Sdes 58796199Sdes return (tempname); 58896199Sdes} 58996199Sdes 59096199Sdes/* 59196199Sdes * Duplicate a struct passwd. 59296199Sdes */ 59396199Sdesstruct passwd * 594121193Smarkmpw_dup(const struct passwd *pw) 59596199Sdes{ 596211392Sdes char *dst; 59796199Sdes struct passwd *npw; 598121193Smarkm ssize_t len; 59996199Sdes 600211392Sdes len = sizeof(*npw); 601211392Sdes if (pw->pw_name != NULL) 602211392Sdes len += strlen(pw->pw_name) + 1; 603211392Sdes if (pw->pw_passwd != NULL) 604211392Sdes len += strlen(pw->pw_passwd) + 1; 605211392Sdes if (pw->pw_class != NULL) 606211392Sdes len += strlen(pw->pw_class) + 1; 607211392Sdes if (pw->pw_gecos != NULL) 608211392Sdes len += strlen(pw->pw_gecos) + 1; 609211392Sdes if (pw->pw_dir != NULL) 610211392Sdes len += strlen(pw->pw_dir) + 1; 611211392Sdes if (pw->pw_shell != NULL) 612211392Sdes len += strlen(pw->pw_shell) + 1; 613121193Smarkm if ((npw = malloc((size_t)len)) == NULL) 61496199Sdes return (NULL); 615121193Smarkm memcpy(npw, pw, sizeof(*npw)); 616211392Sdes dst = (char *)npw + sizeof(*npw); 617211392Sdes if (pw->pw_name != NULL) { 618211392Sdes npw->pw_name = dst; 619211392Sdes dst = stpcpy(npw->pw_name, pw->pw_name) + 1; 62096199Sdes } 621211392Sdes if (pw->pw_passwd != NULL) { 622211392Sdes npw->pw_passwd = dst; 623211392Sdes dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; 62496199Sdes } 625211392Sdes if (pw->pw_class != NULL) { 626211392Sdes npw->pw_class = dst; 627211392Sdes dst = stpcpy(npw->pw_class, pw->pw_class) + 1; 62896199Sdes } 629211392Sdes if (pw->pw_gecos != NULL) { 630211392Sdes npw->pw_gecos = dst; 631211392Sdes dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; 63296199Sdes } 633211392Sdes if (pw->pw_dir != NULL) { 634211392Sdes npw->pw_dir = dst; 635211392Sdes dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; 63696199Sdes } 637211392Sdes if (pw->pw_shell != NULL) { 638211392Sdes npw->pw_shell = dst; 639211392Sdes dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; 64096199Sdes } 64196199Sdes return (npw); 64296199Sdes} 64396199Sdes 64496199Sdes#include "pw_scan.h" 64596199Sdes 64696199Sdes/* 64796199Sdes * Wrapper around an internal libc function 64896199Sdes */ 64996199Sdesstruct passwd * 65096199Sdespw_scan(const char *line, int flags) 65196199Sdes{ 65296199Sdes struct passwd pw, *ret; 65396199Sdes char *bp; 65496199Sdes 65596199Sdes if ((bp = strdup(line)) == NULL) 65696199Sdes return (NULL); 65796199Sdes if (!__pw_scan(bp, &pw, flags)) { 65896199Sdes free(bp); 65996199Sdes return (NULL); 66096199Sdes } 66196199Sdes ret = pw_dup(&pw); 66296199Sdes free(bp); 66396199Sdes return (ret); 66496199Sdes} 665