pw_util.c revision 231383
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: head/lib/libutil/pw_util.c 231383 2012-02-10 13:40:32Z ed $"; 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 18248232Ssheldonh lockfd = open(masterpasswd, O_RDONLY, 0); 18341711Sdillon if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) 18448232Ssheldonh err(1, "%s", masterpasswd); 18596199Sdes /* XXX vulnerable to race conditions */ 18698693Sn_hibma if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { 18798693Sn_hibma if (errno == EWOULDBLOCK) { 18898693Sn_hibma errx(1, "the password db file is busy"); 18998693Sn_hibma } else { 19098693Sn_hibma err(1, "could not lock the passwd file: "); 19198693Sn_hibma } 19298693Sn_hibma } 19341710Sdillon 19441711Sdillon /* 19541711Sdillon * If the password file was replaced while we were trying to 19641711Sdillon * get the lock, our hardlink count will be 0 and we have to 19741711Sdillon * close and retry. 19841711Sdillon */ 19998693Sn_hibma if (fstat(lockfd, &st) == -1) 20098693Sn_hibma err(1, "fstat() failed: "); 20141711Sdillon if (st.st_nlink != 0) 20241711Sdillon break; 20341711Sdillon close(lockfd); 20441711Sdillon lockfd = -1; 20541710Sdillon } 2061553Srgrimes return (lockfd); 2071553Srgrimes} 2081553Srgrimes 20996199Sdes/* 21096199Sdes * Create and open a presumably safe temp file for editing the password 21196199Sdes * data, and copy the master password file into it. 21296199Sdes */ 2131553Srgrimesint 21496199Sdespw_tmp(int mfd) 2151553Srgrimes{ 21696199Sdes char buf[8192]; 217121193Smarkm ssize_t nr; 21896199Sdes const char *p; 21996199Sdes int tfd; 2201553Srgrimes 22196199Sdes if (*masterpasswd == '\0') 22296199Sdes return (-1); 22396199Sdes if ((p = strrchr(masterpasswd, '/'))) 2241553Srgrimes ++p; 2251553Srgrimes else 22696199Sdes p = masterpasswd; 227121193Smarkm if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", 228121193Smarkm (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { 22996199Sdes errno = ENAMETOOLONG; 23096199Sdes return (-1); 23196199Sdes } 23296199Sdes if ((tfd = mkstemp(tempname)) == -1) 23396199Sdes return (-1); 23496199Sdes if (mfd != -1) { 235121193Smarkm while ((nr = read(mfd, buf, sizeof(buf))) > 0) 236121193Smarkm if (write(tfd, buf, (size_t)nr) != nr) 23796199Sdes break; 23896199Sdes if (nr != 0) { 23996199Sdes unlink(tempname); 24096199Sdes *tempname = '\0'; 24196199Sdes close(tfd); 24296199Sdes return (-1); 24396199Sdes } 24496199Sdes } 24596199Sdes return (tfd); 2461553Srgrimes} 2471553Srgrimes 24896199Sdes/* 24996199Sdes * Regenerate the password database. 25096199Sdes */ 2511553Srgrimesint 25296199Sdespw_mkdb(const char *user) 2531553Srgrimes{ 2541553Srgrimes int pstat; 2551553Srgrimes pid_t pid; 2561553Srgrimes 2571553Srgrimes (void)fflush(stderr); 25896199Sdes switch ((pid = fork())) { 25996199Sdes case -1: 26096199Sdes return (-1); 26196199Sdes case 0: 26296199Sdes /* child */ 26396199Sdes if (user == NULL) 26496199Sdes execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 265129392Sstefanf "-d", passwd_dir, tempname, (char *)NULL); 26696199Sdes else 26796199Sdes execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 268129392Sstefanf "-d", passwd_dir, "-u", user, tempname, 269129392Sstefanf (char *)NULL); 27096199Sdes _exit(1); 271121193Smarkm /* NOTREACHED */ 27296199Sdes default: 27396199Sdes /* parent */ 27496199Sdes break; 2751553Srgrimes } 27696199Sdes if (waitpid(pid, &pstat, 0) == -1) 27796199Sdes return (-1); 27896199Sdes if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 2791553Srgrimes return (0); 28096199Sdes errno = 0; 28196199Sdes return (-1); 2821553Srgrimes} 2831553Srgrimes 28496199Sdes/* 28596199Sdes * Edit the temp file. Return -1 on error, >0 if the file was modified, 0 28696199Sdes * if it was not. 28796199Sdes */ 28896199Sdesint 28990233Sdespw_edit(int notsetuid) 2901553Srgrimes{ 291113301Sdes struct sigaction sa, sa_int, sa_quit; 292199826Skib sigset_t oldsigset, nsigset; 29396199Sdes struct stat st1, st2; 29496199Sdes const char *editor; 2951553Srgrimes int pstat; 2961553Srgrimes 29796199Sdes if ((editor = getenv("EDITOR")) == NULL) 29896199Sdes editor = _PATH_VI; 29996199Sdes if (stat(tempname, &st1) == -1) 30096199Sdes return (-1); 301113301Sdes sa.sa_handler = SIG_IGN; 302113301Sdes sigemptyset(&sa.sa_mask); 303113301Sdes sa.sa_flags = 0; 304113301Sdes sigaction(SIGINT, &sa, &sa_int); 305113301Sdes sigaction(SIGQUIT, &sa, &sa_quit); 306199826Skib sigemptyset(&nsigset); 307199826Skib sigaddset(&nsigset, SIGCHLD); 308199826Skib sigprocmask(SIG_BLOCK, &nsigset, &oldsigset); 30996199Sdes switch ((editpid = fork())) { 31096199Sdes case -1: 31196199Sdes return (-1); 31296199Sdes case 0: 313113301Sdes sigaction(SIGINT, &sa_int, NULL); 314113333Sdes sigaction(SIGQUIT, &sa_quit, NULL); 315113301Sdes sigprocmask(SIG_SETMASK, &oldsigset, NULL); 3161553Srgrimes if (notsetuid) { 3171553Srgrimes (void)setgid(getgid()); 3181553Srgrimes (void)setuid(getuid()); 3191553Srgrimes } 32040538Sdes errno = 0; 321129392Sstefanf execlp(editor, basename(editor), tempname, (char *)NULL); 32240538Sdes _exit(errno); 32396199Sdes default: 32496199Sdes /* parent */ 32596199Sdes break; 3261553Srgrimes } 3276972Sache for (;;) { 328113265Sdes if (waitpid(editpid, &pstat, WUNTRACED) == -1) { 329113333Sdes if (errno == EINTR) 330113333Sdes continue; 33196199Sdes unlink(tempname); 332113333Sdes editpid = -1; 333113301Sdes break; 33496199Sdes } else if (WIFSTOPPED(pstat)) { 3356972Sache raise(WSTOPSIG(pstat)); 33696199Sdes } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { 33796199Sdes editpid = -1; 3386972Sache break; 33996199Sdes } else { 34096199Sdes unlink(tempname); 34196199Sdes editpid = -1; 342113301Sdes break; 34396199Sdes } 3446972Sache } 345113301Sdes sigaction(SIGINT, &sa_int, NULL); 346113333Sdes sigaction(SIGQUIT, &sa_quit, NULL); 347113301Sdes sigprocmask(SIG_SETMASK, &oldsigset, NULL); 34896199Sdes if (stat(tempname, &st2) == -1) 34996199Sdes return (-1); 350231383Sed return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec || 351231383Sed st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec); 3521553Srgrimes} 3531553Srgrimes 35496199Sdes/* 35596199Sdes * Clean up. Preserve errno for the caller's convenience. 35696199Sdes */ 3571553Srgrimesvoid 35896199Sdespw_fini(void) 3591553Srgrimes{ 36096199Sdes int serrno, status; 3611553Srgrimes 36296199Sdes if (!initialized) 36396199Sdes return; 36496199Sdes initialized = 0; 36596199Sdes serrno = errno; 36696199Sdes if (editpid != -1) { 36796199Sdes kill(editpid, SIGTERM); 36896199Sdes kill(editpid, SIGCONT); 36996199Sdes waitpid(editpid, &status, 0); 37096199Sdes editpid = -1; 37196199Sdes } 37296199Sdes if (*tempname != '\0') { 37396199Sdes unlink(tempname); 37496199Sdes *tempname = '\0'; 37596199Sdes } 37696199Sdes if (lockfd != -1) 37796199Sdes close(lockfd); 37896199Sdes errno = serrno; 3791553Srgrimes} 3801553Srgrimes 38196199Sdes/* 38296199Sdes * Compares two struct pwds. 38396199Sdes */ 38496199Sdesint 385121193Smarkmpw_equal(const struct passwd *pw1, const struct passwd *pw2) 3861553Srgrimes{ 38796199Sdes return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 38896199Sdes pw1->pw_uid == pw2->pw_uid && 38996199Sdes pw1->pw_gid == pw2->pw_gid && 39096199Sdes strcmp(pw1->pw_class, pw2->pw_class) == 0 && 39196199Sdes pw1->pw_change == pw2->pw_change && 39296199Sdes pw1->pw_expire == pw2->pw_expire && 39396199Sdes strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 39496199Sdes strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 39596199Sdes strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 39696199Sdes} 39796199Sdes 39896199Sdes/* 39996199Sdes * Make a passwd line out of a struct passwd. 40096199Sdes */ 40196199Sdeschar * 402121193Smarkmpw_make(const struct passwd *pw) 40396199Sdes{ 40496199Sdes char *line; 40596199Sdes 40696199Sdes asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, 40796199Sdes pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 40896199Sdes pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, 40996199Sdes pw->pw_gecos, pw->pw_dir, pw->pw_shell); 410229572Sbapt return (line); 41196199Sdes} 41296199Sdes 41396199Sdes/* 414229572Sbapt * Make a passwd line (in v7 format) out of a struct passwd 415229572Sbapt */ 416229572Sbaptchar * 417229572Sbaptpw_make_v7(const struct passwd *pw) 418229572Sbapt{ 419229572Sbapt char *line; 420229572Sbapt 421229572Sbapt asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name, 422229572Sbapt (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 423229572Sbapt pw->pw_gecos, pw->pw_dir, pw->pw_shell); 424229572Sbapt return (line); 425229572Sbapt} 426229572Sbapt 427229572Sbapt/* 428228545Sbapt * Copy password file from one descriptor to another, replacing, deleting 429228545Sbapt * or adding a single record on the way. 43096199Sdes */ 43196199Sdesint 432121193Smarkmpw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) 43396199Sdes{ 43496199Sdes char buf[8192], *end, *line, *p, *q, *r, t; 43596199Sdes struct passwd *fpw; 436228545Sbapt const struct passwd *spw; 437121193Smarkm size_t len; 438121193Smarkm int eof, readlen; 43996199Sdes 440228545Sbapt spw = pw; 441228545Sbapt if (pw == NULL) { 442228545Sbapt line = NULL; 443228545Sbapt if (old_pw == NULL) 444228545Sbapt return (-1); 445228545Sbapt spw = old_pw; 446228545Sbapt } else if ((line = pw_make(pw)) == NULL) 44796199Sdes return (-1); 44896199Sdes 44996199Sdes eof = 0; 45096199Sdes len = 0; 45196199Sdes p = q = end = buf; 45296199Sdes for (;;) { 45396199Sdes /* find the end of the current line */ 45496199Sdes for (p = q; q < end && *q != '\0'; ++q) 45596199Sdes if (*q == '\n') 45696199Sdes break; 45796199Sdes 45896199Sdes /* if we don't have a complete line, fill up the buffer */ 45996199Sdes if (q >= end) { 46096199Sdes if (eof) 46196199Sdes break; 462121193Smarkm if ((size_t)(q - p) >= sizeof(buf)) { 46396199Sdes warnx("passwd line too long"); 46496199Sdes errno = EINVAL; /* hack */ 46596199Sdes goto err; 46696199Sdes } 46796199Sdes if (p < end) { 46896199Sdes q = memmove(buf, p, end - p); 46996199Sdes end -= p - buf; 47096199Sdes } else { 47196199Sdes p = q = end = buf; 47296199Sdes } 473121193Smarkm readlen = read(ffd, end, sizeof(buf) - (end - buf)); 474121193Smarkm if (readlen == -1) 47596199Sdes goto err; 476121193Smarkm else 477121193Smarkm len = (size_t)readlen; 47896199Sdes if (len == 0 && p == buf) 47996199Sdes break; 48096199Sdes end += len; 48196199Sdes len = end - buf; 482121193Smarkm if (len < (ssize_t)sizeof(buf)) { 48396199Sdes eof = 1; 48496199Sdes if (len > 0 && buf[len - 1] != '\n') 48596199Sdes ++len, *end++ = '\n'; 48696199Sdes } 48796199Sdes continue; 48896199Sdes } 48996199Sdes 49096199Sdes /* is it a blank line or a comment? */ 49196199Sdes for (r = p; r < q && isspace(*r); ++r) 49296199Sdes /* nothing */ ; 49396199Sdes if (r == q || *r == '#') { 49496199Sdes /* yep */ 49596199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 49696199Sdes goto err; 49796199Sdes ++q; 49896199Sdes continue; 49996199Sdes } 50096199Sdes 50196199Sdes /* is it the one we're looking for? */ 502161997Sthomas 50396199Sdes t = *q; 50496199Sdes *q = '\0'; 505161997Sthomas 50696199Sdes fpw = pw_scan(r, PWSCAN_MASTER); 507161997Sthomas 508161997Sthomas /* 509162141Sthomas * fpw is either the struct passwd for the current line, 510161997Sthomas * or NULL if the line is malformed. 511161997Sthomas */ 512161997Sthomas 51396199Sdes *q = t; 514228545Sbapt if (fpw == NULL || fpw->pw_uid != spw->pw_uid) { 51596199Sdes /* nope */ 516161997Sthomas if (fpw != NULL) 517161997Sthomas free(fpw); 51896199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 51996199Sdes goto err; 52096199Sdes ++q; 52196199Sdes continue; 52296199Sdes } 523113305Sdes if (old_pw && !pw_equal(fpw, old_pw)) { 524113305Sdes warnx("entry inconsistent"); 525113305Sdes free(fpw); 526113305Sdes errno = EINVAL; /* hack */ 527113305Sdes goto err; 528113305Sdes } 52996199Sdes free(fpw); 53096199Sdes 531228545Sbapt /* it is, replace or remove it */ 532228545Sbapt if (line != NULL) { 533228545Sbapt len = strlen(line); 534228545Sbapt if (write(tfd, line, len) != (int)len) 535228545Sbapt goto err; 536228545Sbapt } else { 537228545Sbapt /* when removed, avoid the \n */ 538228545Sbapt q++; 539228545Sbapt } 54096199Sdes /* we're done, just copy the rest over */ 54196199Sdes for (;;) { 54296199Sdes if (write(tfd, q, end - q) != end - q) 54396199Sdes goto err; 54496199Sdes q = buf; 545121193Smarkm readlen = read(ffd, buf, sizeof(buf)); 546121193Smarkm if (readlen == 0) 54796199Sdes break; 548121193Smarkm else 549121193Smarkm len = (size_t)readlen; 550121193Smarkm if (readlen == -1) 55196199Sdes goto err; 55296199Sdes end = buf + len; 55396199Sdes } 55496199Sdes goto done; 55575821Sdd } 55696199Sdes 557228545Sbapt /* if we got here, we didn't find the old entry */ 558228545Sbapt if (line == NULL) { 559228545Sbapt errno = ENOENT; 560228545Sbapt goto err; 561228545Sbapt } 56296199Sdes len = strlen(line); 563121193Smarkm if ((size_t)write(tfd, line, len) != len || 564106140Sdes write(tfd, "\n", 1) != 1) 56596199Sdes goto err; 56696199Sdes done: 567228545Sbapt if (line != NULL) 568228545Sbapt free(line); 56996199Sdes return (0); 57096199Sdes err: 571228545Sbapt if (line != NULL) 572228545Sbapt free(line); 57396199Sdes return (-1); 5741553Srgrimes} 57596199Sdes 57696199Sdes/* 57796199Sdes * Return the current value of tempname. 57896199Sdes */ 57996199Sdesconst char * 58096199Sdespw_tempname(void) 58196199Sdes{ 58296199Sdes 58396199Sdes return (tempname); 58496199Sdes} 58596199Sdes 58696199Sdes/* 58796199Sdes * Duplicate a struct passwd. 58896199Sdes */ 58996199Sdesstruct passwd * 590121193Smarkmpw_dup(const struct passwd *pw) 59196199Sdes{ 592211392Sdes char *dst; 59396199Sdes struct passwd *npw; 594121193Smarkm ssize_t len; 59596199Sdes 596211392Sdes len = sizeof(*npw); 597211392Sdes if (pw->pw_name != NULL) 598211392Sdes len += strlen(pw->pw_name) + 1; 599211392Sdes if (pw->pw_passwd != NULL) 600211392Sdes len += strlen(pw->pw_passwd) + 1; 601211392Sdes if (pw->pw_class != NULL) 602211392Sdes len += strlen(pw->pw_class) + 1; 603211392Sdes if (pw->pw_gecos != NULL) 604211392Sdes len += strlen(pw->pw_gecos) + 1; 605211392Sdes if (pw->pw_dir != NULL) 606211392Sdes len += strlen(pw->pw_dir) + 1; 607211392Sdes if (pw->pw_shell != NULL) 608211392Sdes len += strlen(pw->pw_shell) + 1; 609121193Smarkm if ((npw = malloc((size_t)len)) == NULL) 61096199Sdes return (NULL); 611121193Smarkm memcpy(npw, pw, sizeof(*npw)); 612211392Sdes dst = (char *)npw + sizeof(*npw); 613211392Sdes if (pw->pw_name != NULL) { 614211392Sdes npw->pw_name = dst; 615211392Sdes dst = stpcpy(npw->pw_name, pw->pw_name) + 1; 61696199Sdes } 617211392Sdes if (pw->pw_passwd != NULL) { 618211392Sdes npw->pw_passwd = dst; 619211392Sdes dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; 62096199Sdes } 621211392Sdes if (pw->pw_class != NULL) { 622211392Sdes npw->pw_class = dst; 623211392Sdes dst = stpcpy(npw->pw_class, pw->pw_class) + 1; 62496199Sdes } 625211392Sdes if (pw->pw_gecos != NULL) { 626211392Sdes npw->pw_gecos = dst; 627211392Sdes dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; 62896199Sdes } 629211392Sdes if (pw->pw_dir != NULL) { 630211392Sdes npw->pw_dir = dst; 631211392Sdes dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; 63296199Sdes } 633211392Sdes if (pw->pw_shell != NULL) { 634211392Sdes npw->pw_shell = dst; 635211392Sdes dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; 63696199Sdes } 63796199Sdes return (npw); 63896199Sdes} 63996199Sdes 64096199Sdes#include "pw_scan.h" 64196199Sdes 64296199Sdes/* 64396199Sdes * Wrapper around an internal libc function 64496199Sdes */ 64596199Sdesstruct passwd * 64696199Sdespw_scan(const char *line, int flags) 64796199Sdes{ 64896199Sdes struct passwd pw, *ret; 64996199Sdes char *bp; 65096199Sdes 65196199Sdes if ((bp = strdup(line)) == NULL) 65296199Sdes return (NULL); 65396199Sdes if (!__pw_scan(bp, &pw, flags)) { 65496199Sdes free(bp); 65596199Sdes return (NULL); 65696199Sdes } 65796199Sdes ret = pw_dup(&pw); 65896199Sdes free(bp); 65996199Sdes return (ret); 66096199Sdes} 661