pw_util.c revision 228545
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 228545 2011-12-15 22:07:36Z bapt $"; 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); 35096199Sdes return (st1.st_mtime != st2.st_mtime); 3511553Srgrimes} 3521553Srgrimes 35396199Sdes/* 35496199Sdes * Clean up. Preserve errno for the caller's convenience. 35596199Sdes */ 3561553Srgrimesvoid 35796199Sdespw_fini(void) 3581553Srgrimes{ 35996199Sdes int serrno, status; 3601553Srgrimes 36196199Sdes if (!initialized) 36296199Sdes return; 36396199Sdes initialized = 0; 36496199Sdes serrno = errno; 36596199Sdes if (editpid != -1) { 36696199Sdes kill(editpid, SIGTERM); 36796199Sdes kill(editpid, SIGCONT); 36896199Sdes waitpid(editpid, &status, 0); 36996199Sdes editpid = -1; 37096199Sdes } 37196199Sdes if (*tempname != '\0') { 37296199Sdes unlink(tempname); 37396199Sdes *tempname = '\0'; 37496199Sdes } 37596199Sdes if (lockfd != -1) 37696199Sdes close(lockfd); 37796199Sdes errno = serrno; 3781553Srgrimes} 3791553Srgrimes 38096199Sdes/* 38196199Sdes * Compares two struct pwds. 38296199Sdes */ 38396199Sdesint 384121193Smarkmpw_equal(const struct passwd *pw1, const struct passwd *pw2) 3851553Srgrimes{ 38696199Sdes return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 38796199Sdes pw1->pw_uid == pw2->pw_uid && 38896199Sdes pw1->pw_gid == pw2->pw_gid && 38996199Sdes strcmp(pw1->pw_class, pw2->pw_class) == 0 && 39096199Sdes pw1->pw_change == pw2->pw_change && 39196199Sdes pw1->pw_expire == pw2->pw_expire && 39296199Sdes strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 39396199Sdes strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 39496199Sdes strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 39596199Sdes} 39696199Sdes 39796199Sdes/* 39896199Sdes * Make a passwd line out of a struct passwd. 39996199Sdes */ 40096199Sdeschar * 401121193Smarkmpw_make(const struct passwd *pw) 40296199Sdes{ 40396199Sdes char *line; 40496199Sdes 40596199Sdes asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, 40696199Sdes pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 40796199Sdes pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, 40896199Sdes pw->pw_gecos, pw->pw_dir, pw->pw_shell); 40996199Sdes return line; 41096199Sdes} 41196199Sdes 41296199Sdes/* 413228545Sbapt * Copy password file from one descriptor to another, replacing, deleting 414228545Sbapt * or adding a single record on the way. 41596199Sdes */ 41696199Sdesint 417121193Smarkmpw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) 41896199Sdes{ 41996199Sdes char buf[8192], *end, *line, *p, *q, *r, t; 42096199Sdes struct passwd *fpw; 421228545Sbapt const struct passwd *spw; 422121193Smarkm size_t len; 423121193Smarkm int eof, readlen; 42496199Sdes 425228545Sbapt spw = pw; 426228545Sbapt if (pw == NULL) { 427228545Sbapt line = NULL; 428228545Sbapt if (old_pw == NULL) 429228545Sbapt return (-1); 430228545Sbapt spw = old_pw; 431228545Sbapt } else if ((line = pw_make(pw)) == NULL) 43296199Sdes return (-1); 43396199Sdes 43496199Sdes eof = 0; 43596199Sdes len = 0; 43696199Sdes p = q = end = buf; 43796199Sdes for (;;) { 43896199Sdes /* find the end of the current line */ 43996199Sdes for (p = q; q < end && *q != '\0'; ++q) 44096199Sdes if (*q == '\n') 44196199Sdes break; 44296199Sdes 44396199Sdes /* if we don't have a complete line, fill up the buffer */ 44496199Sdes if (q >= end) { 44596199Sdes if (eof) 44696199Sdes break; 447121193Smarkm if ((size_t)(q - p) >= sizeof(buf)) { 44896199Sdes warnx("passwd line too long"); 44996199Sdes errno = EINVAL; /* hack */ 45096199Sdes goto err; 45196199Sdes } 45296199Sdes if (p < end) { 45396199Sdes q = memmove(buf, p, end - p); 45496199Sdes end -= p - buf; 45596199Sdes } else { 45696199Sdes p = q = end = buf; 45796199Sdes } 458121193Smarkm readlen = read(ffd, end, sizeof(buf) - (end - buf)); 459121193Smarkm if (readlen == -1) 46096199Sdes goto err; 461121193Smarkm else 462121193Smarkm len = (size_t)readlen; 46396199Sdes if (len == 0 && p == buf) 46496199Sdes break; 46596199Sdes end += len; 46696199Sdes len = end - buf; 467121193Smarkm if (len < (ssize_t)sizeof(buf)) { 46896199Sdes eof = 1; 46996199Sdes if (len > 0 && buf[len - 1] != '\n') 47096199Sdes ++len, *end++ = '\n'; 47196199Sdes } 47296199Sdes continue; 47396199Sdes } 47496199Sdes 47596199Sdes /* is it a blank line or a comment? */ 47696199Sdes for (r = p; r < q && isspace(*r); ++r) 47796199Sdes /* nothing */ ; 47896199Sdes if (r == q || *r == '#') { 47996199Sdes /* yep */ 48096199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 48196199Sdes goto err; 48296199Sdes ++q; 48396199Sdes continue; 48496199Sdes } 48596199Sdes 48696199Sdes /* is it the one we're looking for? */ 487161997Sthomas 48896199Sdes t = *q; 48996199Sdes *q = '\0'; 490161997Sthomas 49196199Sdes fpw = pw_scan(r, PWSCAN_MASTER); 492161997Sthomas 493161997Sthomas /* 494162141Sthomas * fpw is either the struct passwd for the current line, 495161997Sthomas * or NULL if the line is malformed. 496161997Sthomas */ 497161997Sthomas 49896199Sdes *q = t; 499228545Sbapt if (fpw == NULL || fpw->pw_uid != spw->pw_uid) { 50096199Sdes /* nope */ 501161997Sthomas if (fpw != NULL) 502161997Sthomas free(fpw); 50396199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 50496199Sdes goto err; 50596199Sdes ++q; 50696199Sdes continue; 50796199Sdes } 508113305Sdes if (old_pw && !pw_equal(fpw, old_pw)) { 509113305Sdes warnx("entry inconsistent"); 510113305Sdes free(fpw); 511113305Sdes errno = EINVAL; /* hack */ 512113305Sdes goto err; 513113305Sdes } 51496199Sdes free(fpw); 51596199Sdes 516228545Sbapt /* it is, replace or remove it */ 517228545Sbapt if (line != NULL) { 518228545Sbapt len = strlen(line); 519228545Sbapt if (write(tfd, line, len) != (int)len) 520228545Sbapt goto err; 521228545Sbapt } else { 522228545Sbapt /* when removed, avoid the \n */ 523228545Sbapt q++; 524228545Sbapt } 52596199Sdes /* we're done, just copy the rest over */ 52696199Sdes for (;;) { 52796199Sdes if (write(tfd, q, end - q) != end - q) 52896199Sdes goto err; 52996199Sdes q = buf; 530121193Smarkm readlen = read(ffd, buf, sizeof(buf)); 531121193Smarkm if (readlen == 0) 53296199Sdes break; 533121193Smarkm else 534121193Smarkm len = (size_t)readlen; 535121193Smarkm if (readlen == -1) 53696199Sdes goto err; 53796199Sdes end = buf + len; 53896199Sdes } 53996199Sdes goto done; 54075821Sdd } 54196199Sdes 542228545Sbapt /* if we got here, we didn't find the old entry */ 543228545Sbapt if (line == NULL) { 544228545Sbapt errno = ENOENT; 545228545Sbapt goto err; 546228545Sbapt } 54796199Sdes len = strlen(line); 548121193Smarkm if ((size_t)write(tfd, line, len) != len || 549106140Sdes write(tfd, "\n", 1) != 1) 55096199Sdes goto err; 55196199Sdes done: 552228545Sbapt if (line != NULL) 553228545Sbapt free(line); 55496199Sdes return (0); 55596199Sdes err: 556228545Sbapt if (line != NULL) 557228545Sbapt free(line); 55896199Sdes return (-1); 5591553Srgrimes} 56096199Sdes 56196199Sdes/* 56296199Sdes * Return the current value of tempname. 56396199Sdes */ 56496199Sdesconst char * 56596199Sdespw_tempname(void) 56696199Sdes{ 56796199Sdes 56896199Sdes return (tempname); 56996199Sdes} 57096199Sdes 57196199Sdes/* 57296199Sdes * Duplicate a struct passwd. 57396199Sdes */ 57496199Sdesstruct passwd * 575121193Smarkmpw_dup(const struct passwd *pw) 57696199Sdes{ 577211392Sdes char *dst; 57896199Sdes struct passwd *npw; 579121193Smarkm ssize_t len; 58096199Sdes 581211392Sdes len = sizeof(*npw); 582211392Sdes if (pw->pw_name != NULL) 583211392Sdes len += strlen(pw->pw_name) + 1; 584211392Sdes if (pw->pw_passwd != NULL) 585211392Sdes len += strlen(pw->pw_passwd) + 1; 586211392Sdes if (pw->pw_class != NULL) 587211392Sdes len += strlen(pw->pw_class) + 1; 588211392Sdes if (pw->pw_gecos != NULL) 589211392Sdes len += strlen(pw->pw_gecos) + 1; 590211392Sdes if (pw->pw_dir != NULL) 591211392Sdes len += strlen(pw->pw_dir) + 1; 592211392Sdes if (pw->pw_shell != NULL) 593211392Sdes len += strlen(pw->pw_shell) + 1; 594121193Smarkm if ((npw = malloc((size_t)len)) == NULL) 59596199Sdes return (NULL); 596121193Smarkm memcpy(npw, pw, sizeof(*npw)); 597211392Sdes dst = (char *)npw + sizeof(*npw); 598211392Sdes if (pw->pw_name != NULL) { 599211392Sdes npw->pw_name = dst; 600211392Sdes dst = stpcpy(npw->pw_name, pw->pw_name) + 1; 60196199Sdes } 602211392Sdes if (pw->pw_passwd != NULL) { 603211392Sdes npw->pw_passwd = dst; 604211392Sdes dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; 60596199Sdes } 606211392Sdes if (pw->pw_class != NULL) { 607211392Sdes npw->pw_class = dst; 608211392Sdes dst = stpcpy(npw->pw_class, pw->pw_class) + 1; 60996199Sdes } 610211392Sdes if (pw->pw_gecos != NULL) { 611211392Sdes npw->pw_gecos = dst; 612211392Sdes dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; 61396199Sdes } 614211392Sdes if (pw->pw_dir != NULL) { 615211392Sdes npw->pw_dir = dst; 616211392Sdes dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; 61796199Sdes } 618211392Sdes if (pw->pw_shell != NULL) { 619211392Sdes npw->pw_shell = dst; 620211392Sdes dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; 62196199Sdes } 62296199Sdes return (npw); 62396199Sdes} 62496199Sdes 62596199Sdes#include "pw_scan.h" 62696199Sdes 62796199Sdes/* 62896199Sdes * Wrapper around an internal libc function 62996199Sdes */ 63096199Sdesstruct passwd * 63196199Sdespw_scan(const char *line, int flags) 63296199Sdes{ 63396199Sdes struct passwd pw, *ret; 63496199Sdes char *bp; 63596199Sdes 63696199Sdes if ((bp = strdup(line)) == NULL) 63796199Sdes return (NULL); 63896199Sdes if (!__pw_scan(bp, &pw, flags)) { 63996199Sdes free(bp); 64096199Sdes return (NULL); 64196199Sdes } 64296199Sdes ret = pw_dup(&pw); 64396199Sdes free(bp); 64496199Sdes return (ret); 64596199Sdes} 646