pw_util.c revision 121193
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 * 3. All advertising materials mentioning features or use of this software 211553Srgrimes * must display the following acknowledgement: 221553Srgrimes * This product includes software developed by the University of 231553Srgrimes * California, Berkeley and its contributors. 241553Srgrimes * 4. Neither the name of the University nor the names of its contributors 251553Srgrimes * may be used to endorse or promote products derived from this software 261553Srgrimes * without specific prior written permission. 271553Srgrimes * 281553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 291553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 301553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 311553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 321553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 331553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 341553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 351553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 361553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 371553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 381553Srgrimes * SUCH DAMAGE. 391553Srgrimes */ 401553Srgrimes 411553Srgrimes#ifndef lint 4230765Scharnier#if 0 4330113Sjkhstatic const char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94"; 4430765Scharnier#endif 4530765Scharnierstatic const char rcsid[] = 4650479Speter "$FreeBSD: head/lib/libutil/pw_util.c 121193 2003-10-18 10:04:16Z markm $"; 471553Srgrimes#endif /* not lint */ 481553Srgrimes 491553Srgrimes/* 501553Srgrimes * This file is used by all the "password" programs; vipw(8), chpass(1), 511553Srgrimes * and passwd(1). 521553Srgrimes */ 531553Srgrimes 541553Srgrimes#include <sys/param.h> 5540538Sdes#include <sys/errno.h> 561553Srgrimes#include <sys/time.h> 571553Srgrimes#include <sys/resource.h> 581553Srgrimes#include <sys/stat.h> 591553Srgrimes#include <sys/wait.h> 601553Srgrimes 61116344Smarkm#include <ctype.h> 621553Srgrimes#include <err.h> 631553Srgrimes#include <fcntl.h> 6496199Sdes#include <inttypes.h> 65113333Sdes#include <libgen.h> 661553Srgrimes#include <paths.h> 671553Srgrimes#include <pwd.h> 681553Srgrimes#include <signal.h> 691553Srgrimes#include <stdio.h> 701553Srgrimes#include <stdlib.h> 711553Srgrimes#include <string.h> 721553Srgrimes#include <unistd.h> 731553Srgrimes 7496199Sdes#include <libutil.h> 751553Srgrimes 766972Sachestatic pid_t editpid = -1; 7796199Sdesstatic int lockfd = -1; 7896199Sdesstatic char masterpasswd[PATH_MAX]; 7996199Sdesstatic char passwd_dir[PATH_MAX]; 8096199Sdesstatic char tempname[PATH_MAX]; 8196199Sdesstatic int initialized; 821553Srgrimes 83121193Smarkm#if 0 841553Srgrimesvoid 8590233Sdespw_cont(int sig) 866972Sache{ 876972Sache 886972Sache if (editpid != -1) 896972Sache kill(editpid, sig); 906972Sache} 91121193Smarkm#endif 926972Sache 9396199Sdes/* 9496199Sdes * Initialize statics and set limits, signals & umask to try to avoid 9596199Sdes * interruptions, crashes etc. that might expose passord data. 9696199Sdes */ 9796199Sdesint 9896199Sdespw_init(const char *dir, const char *master) 991553Srgrimes{ 10096199Sdes#if 0 1011553Srgrimes struct rlimit rlim; 10296199Sdes#endif 1031553Srgrimes 10496199Sdes if (dir == NULL) { 10596199Sdes strcpy(passwd_dir, _PATH_ETC); 10696199Sdes } else { 107121193Smarkm if (strlen(dir) >= sizeof(passwd_dir)) { 10896199Sdes errno = ENAMETOOLONG; 10996199Sdes return (-1); 11096199Sdes } 11196199Sdes strcpy(passwd_dir, dir); 11296199Sdes } 11396199Sdes 11496199Sdes if (master == NULL) { 11596199Sdes if (dir == NULL) { 11696199Sdes strcpy(masterpasswd, _PATH_MASTERPASSWD); 117121193Smarkm } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s", 118121193Smarkm passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) { 11996199Sdes errno = ENAMETOOLONG; 12096199Sdes return (-1); 12196199Sdes } 12296199Sdes } else { 123121193Smarkm if (strlen(master) >= sizeof(masterpasswd)) { 12496199Sdes errno = ENAMETOOLONG; 12596199Sdes return (-1); 12696199Sdes } 12796199Sdes strcpy(masterpasswd, master); 12896199Sdes } 12996199Sdes 13096199Sdes /* 13196199Sdes * The code that follows is extremely disruptive to the calling 13296199Sdes * process, and is therefore disabled until someone can conceive 13396199Sdes * of a realistic scenario where it would fend off a compromise. 13496199Sdes * Race conditions concerning the temporary files can be guarded 13596199Sdes * against in other ways than masking signals (by checking stat(2) 13696199Sdes * results after creation). 13796199Sdes */ 13896199Sdes#if 0 1391553Srgrimes /* Unlimited resource limits. */ 1401553Srgrimes rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 1411553Srgrimes (void)setrlimit(RLIMIT_CPU, &rlim); 1421553Srgrimes (void)setrlimit(RLIMIT_FSIZE, &rlim); 1431553Srgrimes (void)setrlimit(RLIMIT_STACK, &rlim); 1441553Srgrimes (void)setrlimit(RLIMIT_DATA, &rlim); 1451553Srgrimes (void)setrlimit(RLIMIT_RSS, &rlim); 1461553Srgrimes 1471553Srgrimes /* Don't drop core (not really necessary, but GP's). */ 1481553Srgrimes rlim.rlim_cur = rlim.rlim_max = 0; 1491553Srgrimes (void)setrlimit(RLIMIT_CORE, &rlim); 1501553Srgrimes 1511553Srgrimes /* Turn off signals. */ 1521553Srgrimes (void)signal(SIGALRM, SIG_IGN); 1531553Srgrimes (void)signal(SIGHUP, SIG_IGN); 1541553Srgrimes (void)signal(SIGINT, SIG_IGN); 1551553Srgrimes (void)signal(SIGPIPE, SIG_IGN); 1561553Srgrimes (void)signal(SIGQUIT, SIG_IGN); 1571553Srgrimes (void)signal(SIGTERM, SIG_IGN); 1586972Sache (void)signal(SIGCONT, pw_cont); 15948328Spb 16048328Spb /* Create with exact permissions. */ 16148328Spb (void)umask(0); 16296199Sdes#endif 16396199Sdes initialized = 1; 16496199Sdes return (0); 1651553Srgrimes} 1661553Srgrimes 16796199Sdes/* 16896199Sdes * Lock the master password file. 16996199Sdes */ 1701553Srgrimesint 17190233Sdespw_lock(void) 1721553Srgrimes{ 17396199Sdes 17496199Sdes if (*masterpasswd == '\0') 17596199Sdes return (-1); 17696199Sdes 1778857Srgrimes /* 1781553Srgrimes * If the master password file doesn't exist, the system is hosed. 1791553Srgrimes * Might as well try to build one. Set the close-on-exec bit so 1801553Srgrimes * that users can't get at the encrypted passwords while editing. 1811553Srgrimes * Open should allow flock'ing the file; see 4.4BSD. XXX 1821553Srgrimes */ 18341710Sdillon for (;;) { 18441711Sdillon struct stat st; 18541710Sdillon 18648232Ssheldonh lockfd = open(masterpasswd, O_RDONLY, 0); 18741711Sdillon if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) 18848232Ssheldonh err(1, "%s", masterpasswd); 18996199Sdes /* XXX vulnerable to race conditions */ 19098693Sn_hibma if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { 19198693Sn_hibma if (errno == EWOULDBLOCK) { 19298693Sn_hibma errx(1, "the password db file is busy"); 19398693Sn_hibma } else { 19498693Sn_hibma err(1, "could not lock the passwd file: "); 19598693Sn_hibma } 19698693Sn_hibma } 19741710Sdillon 19841711Sdillon /* 19941711Sdillon * If the password file was replaced while we were trying to 20041711Sdillon * get the lock, our hardlink count will be 0 and we have to 20141711Sdillon * close and retry. 20241711Sdillon */ 20398693Sn_hibma if (fstat(lockfd, &st) == -1) 20498693Sn_hibma err(1, "fstat() failed: "); 20541711Sdillon if (st.st_nlink != 0) 20641711Sdillon break; 20741711Sdillon close(lockfd); 20841711Sdillon lockfd = -1; 20941710Sdillon } 2101553Srgrimes return (lockfd); 2111553Srgrimes} 2121553Srgrimes 21396199Sdes/* 21496199Sdes * Create and open a presumably safe temp file for editing the password 21596199Sdes * data, and copy the master password file into it. 21696199Sdes */ 2171553Srgrimesint 21896199Sdespw_tmp(int mfd) 2191553Srgrimes{ 22096199Sdes char buf[8192]; 221121193Smarkm ssize_t nr; 22296199Sdes const char *p; 22396199Sdes int tfd; 2241553Srgrimes 22596199Sdes if (*masterpasswd == '\0') 22696199Sdes return (-1); 22796199Sdes if ((p = strrchr(masterpasswd, '/'))) 2281553Srgrimes ++p; 2291553Srgrimes else 23096199Sdes p = masterpasswd; 231121193Smarkm if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", 232121193Smarkm (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { 23396199Sdes errno = ENAMETOOLONG; 23496199Sdes return (-1); 23596199Sdes } 23696199Sdes if ((tfd = mkstemp(tempname)) == -1) 23796199Sdes return (-1); 23896199Sdes if (mfd != -1) { 239121193Smarkm while ((nr = read(mfd, buf, sizeof(buf))) > 0) 240121193Smarkm if (write(tfd, buf, (size_t)nr) != nr) 24196199Sdes break; 24296199Sdes if (nr != 0) { 24396199Sdes unlink(tempname); 24496199Sdes *tempname = '\0'; 24596199Sdes close(tfd); 24696199Sdes return (-1); 24796199Sdes } 24896199Sdes } 24996199Sdes return (tfd); 2501553Srgrimes} 2511553Srgrimes 25296199Sdes/* 25396199Sdes * Regenerate the password database. 25496199Sdes */ 2551553Srgrimesint 25696199Sdespw_mkdb(const char *user) 2571553Srgrimes{ 2581553Srgrimes int pstat; 2591553Srgrimes pid_t pid; 2601553Srgrimes 2611553Srgrimes (void)fflush(stderr); 26296199Sdes switch ((pid = fork())) { 26396199Sdes case -1: 26496199Sdes return (-1); 26596199Sdes case 0: 26696199Sdes /* child */ 26796199Sdes if (user == NULL) 26896199Sdes execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 26996199Sdes "-d", passwd_dir, tempname, NULL); 27096199Sdes else 27196199Sdes execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", 27296199Sdes "-d", passwd_dir, "-u", user, tempname, NULL); 27396199Sdes _exit(1); 274121193Smarkm /* NOTREACHED */ 27596199Sdes default: 27696199Sdes /* parent */ 27796199Sdes break; 2781553Srgrimes } 27996199Sdes if (waitpid(pid, &pstat, 0) == -1) 28096199Sdes return (-1); 28196199Sdes if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) 2821553Srgrimes return (0); 28396199Sdes errno = 0; 28496199Sdes return (-1); 2851553Srgrimes} 2861553Srgrimes 28796199Sdes/* 28896199Sdes * Edit the temp file. Return -1 on error, >0 if the file was modified, 0 28996199Sdes * if it was not. 29096199Sdes */ 29196199Sdesint 29290233Sdespw_edit(int notsetuid) 2931553Srgrimes{ 294113301Sdes struct sigaction sa, sa_int, sa_quit; 295113333Sdes sigset_t oldsigset, sigset; 29696199Sdes struct stat st1, st2; 29796199Sdes const char *editor; 2981553Srgrimes int pstat; 2991553Srgrimes 30096199Sdes if ((editor = getenv("EDITOR")) == NULL) 30196199Sdes editor = _PATH_VI; 30296199Sdes if (stat(tempname, &st1) == -1) 30396199Sdes return (-1); 304113301Sdes sa.sa_handler = SIG_IGN; 305113301Sdes sigemptyset(&sa.sa_mask); 306113301Sdes sa.sa_flags = 0; 307113301Sdes sigaction(SIGINT, &sa, &sa_int); 308113301Sdes sigaction(SIGQUIT, &sa, &sa_quit); 309113301Sdes sigemptyset(&sigset); 310113301Sdes sigaddset(&sigset, SIGCHLD); 311113301Sdes sigprocmask(SIG_BLOCK, &sigset, &oldsigset); 31296199Sdes switch ((editpid = fork())) { 31396199Sdes case -1: 31496199Sdes return (-1); 31596199Sdes case 0: 316113301Sdes sigaction(SIGINT, &sa_int, NULL); 317113333Sdes sigaction(SIGQUIT, &sa_quit, NULL); 318113301Sdes sigprocmask(SIG_SETMASK, &oldsigset, NULL); 3191553Srgrimes if (notsetuid) { 3201553Srgrimes (void)setgid(getgid()); 3211553Srgrimes (void)setuid(getuid()); 3221553Srgrimes } 32340538Sdes errno = 0; 324113333Sdes execlp(editor, basename(editor), tempname, NULL); 32540538Sdes _exit(errno); 32696199Sdes default: 32796199Sdes /* parent */ 32896199Sdes break; 3291553Srgrimes } 3306972Sache for (;;) { 331113265Sdes if (waitpid(editpid, &pstat, WUNTRACED) == -1) { 332113333Sdes if (errno == EINTR) 333113333Sdes continue; 33496199Sdes unlink(tempname); 335113333Sdes editpid = -1; 336113301Sdes break; 33796199Sdes } else if (WIFSTOPPED(pstat)) { 3386972Sache raise(WSTOPSIG(pstat)); 33996199Sdes } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { 34096199Sdes editpid = -1; 3416972Sache break; 34296199Sdes } else { 34396199Sdes unlink(tempname); 34496199Sdes editpid = -1; 345113301Sdes break; 34696199Sdes } 3476972Sache } 348113301Sdes sigaction(SIGINT, &sa_int, NULL); 349113333Sdes sigaction(SIGQUIT, &sa_quit, NULL); 350113301Sdes sigprocmask(SIG_SETMASK, &oldsigset, NULL); 35196199Sdes if (stat(tempname, &st2) == -1) 35296199Sdes return (-1); 35396199Sdes return (st1.st_mtime != st2.st_mtime); 3541553Srgrimes} 3551553Srgrimes 35696199Sdes/* 35796199Sdes * Clean up. Preserve errno for the caller's convenience. 35896199Sdes */ 3591553Srgrimesvoid 36096199Sdespw_fini(void) 3611553Srgrimes{ 36296199Sdes int serrno, status; 3631553Srgrimes 36496199Sdes if (!initialized) 36596199Sdes return; 36696199Sdes initialized = 0; 36796199Sdes serrno = errno; 36896199Sdes if (editpid != -1) { 36996199Sdes kill(editpid, SIGTERM); 37096199Sdes kill(editpid, SIGCONT); 37196199Sdes waitpid(editpid, &status, 0); 37296199Sdes editpid = -1; 37396199Sdes } 37496199Sdes if (*tempname != '\0') { 37596199Sdes unlink(tempname); 37696199Sdes *tempname = '\0'; 37796199Sdes } 37896199Sdes if (lockfd != -1) 37996199Sdes close(lockfd); 38096199Sdes errno = serrno; 3811553Srgrimes} 3821553Srgrimes 38396199Sdes/* 38496199Sdes * Compares two struct pwds. 38596199Sdes */ 38696199Sdesint 387121193Smarkmpw_equal(const struct passwd *pw1, const struct passwd *pw2) 3881553Srgrimes{ 38996199Sdes return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && 39096199Sdes pw1->pw_uid == pw2->pw_uid && 39196199Sdes pw1->pw_gid == pw2->pw_gid && 39296199Sdes strcmp(pw1->pw_class, pw2->pw_class) == 0 && 39396199Sdes pw1->pw_change == pw2->pw_change && 39496199Sdes pw1->pw_expire == pw2->pw_expire && 39596199Sdes strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && 39696199Sdes strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && 39796199Sdes strcmp(pw1->pw_shell, pw2->pw_shell) == 0); 39896199Sdes} 39996199Sdes 40096199Sdes/* 40196199Sdes * Make a passwd line out of a struct passwd. 40296199Sdes */ 40396199Sdeschar * 404121193Smarkmpw_make(const struct passwd *pw) 40596199Sdes{ 40696199Sdes char *line; 40796199Sdes 40896199Sdes asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, 40996199Sdes pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, 41096199Sdes pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, 41196199Sdes pw->pw_gecos, pw->pw_dir, pw->pw_shell); 41296199Sdes return line; 41396199Sdes} 41496199Sdes 41596199Sdes/* 41696199Sdes * Copy password file from one descriptor to another, replacing or adding 41796199Sdes * a single record on the way. 41896199Sdes */ 41996199Sdesint 420121193Smarkmpw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) 42196199Sdes{ 42296199Sdes char buf[8192], *end, *line, *p, *q, *r, t; 42396199Sdes struct passwd *fpw; 424121193Smarkm size_t len; 425121193Smarkm int eof, readlen; 42696199Sdes 42796199Sdes if ((line = pw_make(pw)) == NULL) 42896199Sdes return (-1); 42996199Sdes 43096199Sdes eof = 0; 43196199Sdes len = 0; 43296199Sdes p = q = end = buf; 43396199Sdes for (;;) { 43496199Sdes /* find the end of the current line */ 43596199Sdes for (p = q; q < end && *q != '\0'; ++q) 43696199Sdes if (*q == '\n') 43796199Sdes break; 43896199Sdes 43996199Sdes /* if we don't have a complete line, fill up the buffer */ 44096199Sdes if (q >= end) { 44196199Sdes if (eof) 44296199Sdes break; 443121193Smarkm if ((size_t)(q - p) >= sizeof(buf)) { 44496199Sdes warnx("passwd line too long"); 44596199Sdes errno = EINVAL; /* hack */ 44696199Sdes goto err; 44796199Sdes } 44896199Sdes if (p < end) { 44996199Sdes q = memmove(buf, p, end - p); 45096199Sdes end -= p - buf; 45196199Sdes } else { 45296199Sdes p = q = end = buf; 45396199Sdes } 454121193Smarkm readlen = read(ffd, end, sizeof(buf) - (end - buf)); 455121193Smarkm if (readlen == -1) 45696199Sdes goto err; 457121193Smarkm else 458121193Smarkm len = (size_t)readlen; 45996199Sdes if (len == 0 && p == buf) 46096199Sdes break; 46196199Sdes end += len; 46296199Sdes len = end - buf; 463121193Smarkm if (len < (ssize_t)sizeof(buf)) { 46496199Sdes eof = 1; 46596199Sdes if (len > 0 && buf[len - 1] != '\n') 46696199Sdes ++len, *end++ = '\n'; 46796199Sdes } 46896199Sdes continue; 46996199Sdes } 47096199Sdes 47196199Sdes /* is it a blank line or a comment? */ 47296199Sdes for (r = p; r < q && isspace(*r); ++r) 47396199Sdes /* nothing */ ; 47496199Sdes if (r == q || *r == '#') { 47596199Sdes /* yep */ 47696199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 47796199Sdes goto err; 47896199Sdes ++q; 47996199Sdes continue; 48096199Sdes } 48196199Sdes 48296199Sdes /* is it the one we're looking for? */ 48396199Sdes t = *q; 48496199Sdes *q = '\0'; 48596199Sdes fpw = pw_scan(r, PWSCAN_MASTER); 48696199Sdes *q = t; 487113305Sdes if (strcmp(fpw->pw_name, pw->pw_name) != 0) { 48896199Sdes /* nope */ 48996199Sdes free(fpw); 49096199Sdes if (write(tfd, p, q - p + 1) != q - p + 1) 49196199Sdes goto err; 49296199Sdes ++q; 49396199Sdes continue; 49496199Sdes } 495113305Sdes if (old_pw && !pw_equal(fpw, old_pw)) { 496113305Sdes warnx("entry inconsistent"); 497113305Sdes free(fpw); 498113305Sdes errno = EINVAL; /* hack */ 499113305Sdes goto err; 500113305Sdes } 50196199Sdes free(fpw); 50296199Sdes 50396199Sdes /* it is, replace it */ 50496199Sdes len = strlen(line); 505121193Smarkm if (write(tfd, line, len) != (int)len) 50696199Sdes goto err; 50796199Sdes 50896199Sdes /* we're done, just copy the rest over */ 50996199Sdes for (;;) { 51096199Sdes if (write(tfd, q, end - q) != end - q) 51196199Sdes goto err; 51296199Sdes q = buf; 513121193Smarkm readlen = read(ffd, buf, sizeof(buf)); 514121193Smarkm if (readlen == 0) 51596199Sdes break; 516121193Smarkm else 517121193Smarkm len = (size_t)readlen; 518121193Smarkm if (readlen == -1) 51996199Sdes goto err; 52096199Sdes end = buf + len; 52196199Sdes } 52296199Sdes goto done; 52375821Sdd } 52496199Sdes 52596199Sdes /* if we got here, we have a new entry */ 52696199Sdes len = strlen(line); 527121193Smarkm if ((size_t)write(tfd, line, len) != len || 528106140Sdes write(tfd, "\n", 1) != 1) 52996199Sdes goto err; 53096199Sdes done: 53196199Sdes free(line); 53296199Sdes return (0); 53396199Sdes err: 53496199Sdes free(line); 53596199Sdes return (-1); 5361553Srgrimes} 53796199Sdes 53896199Sdes/* 53996199Sdes * Return the current value of tempname. 54096199Sdes */ 54196199Sdesconst char * 54296199Sdespw_tempname(void) 54396199Sdes{ 54496199Sdes 54596199Sdes return (tempname); 54696199Sdes} 54796199Sdes 54896199Sdes/* 54996199Sdes * Duplicate a struct passwd. 55096199Sdes */ 55196199Sdesstruct passwd * 552121193Smarkmpw_dup(const struct passwd *pw) 55396199Sdes{ 55496199Sdes struct passwd *npw; 555121193Smarkm ssize_t len; 55696199Sdes 557121193Smarkm len = sizeof(*npw) + 55896199Sdes (pw->pw_name ? strlen(pw->pw_name) + 1 : 0) + 55996199Sdes (pw->pw_passwd ? strlen(pw->pw_passwd) + 1 : 0) + 56096199Sdes (pw->pw_class ? strlen(pw->pw_class) + 1 : 0) + 56196199Sdes (pw->pw_gecos ? strlen(pw->pw_gecos) + 1 : 0) + 56296199Sdes (pw->pw_dir ? strlen(pw->pw_dir) + 1 : 0) + 56396199Sdes (pw->pw_shell ? strlen(pw->pw_shell) + 1 : 0); 564121193Smarkm if ((npw = malloc((size_t)len)) == NULL) 56596199Sdes return (NULL); 566121193Smarkm memcpy(npw, pw, sizeof(*npw)); 567121193Smarkm len = sizeof(*npw); 56896199Sdes if (pw->pw_name) { 56996199Sdes npw->pw_name = ((char *)npw) + len; 57096199Sdes len += sprintf(npw->pw_name, "%s", pw->pw_name) + 1; 57196199Sdes } 57296199Sdes if (pw->pw_passwd) { 57396199Sdes npw->pw_passwd = ((char *)npw) + len; 57496199Sdes len += sprintf(npw->pw_passwd, "%s", pw->pw_passwd) + 1; 57596199Sdes } 57696199Sdes if (pw->pw_class) { 57796199Sdes npw->pw_class = ((char *)npw) + len; 57896199Sdes len += sprintf(npw->pw_class, "%s", pw->pw_class) + 1; 57996199Sdes } 58096199Sdes if (pw->pw_gecos) { 58196199Sdes npw->pw_gecos = ((char *)npw) + len; 58296199Sdes len += sprintf(npw->pw_gecos, "%s", pw->pw_gecos) + 1; 58396199Sdes } 58496199Sdes if (pw->pw_dir) { 58596199Sdes npw->pw_dir = ((char *)npw) + len; 58696199Sdes len += sprintf(npw->pw_dir, "%s", pw->pw_dir) + 1; 58796199Sdes } 58896199Sdes if (pw->pw_shell) { 58996199Sdes npw->pw_shell = ((char *)npw) + len; 59096199Sdes len += sprintf(npw->pw_shell, "%s", pw->pw_shell) + 1; 59196199Sdes } 59296199Sdes return (npw); 59396199Sdes} 59496199Sdes 59596199Sdes#include "pw_scan.h" 59696199Sdes 59796199Sdes/* 59896199Sdes * Wrapper around an internal libc function 59996199Sdes */ 60096199Sdesstruct passwd * 60196199Sdespw_scan(const char *line, int flags) 60296199Sdes{ 60396199Sdes struct passwd pw, *ret; 60496199Sdes char *bp; 60596199Sdes 60696199Sdes if ((bp = strdup(line)) == NULL) 60796199Sdes return (NULL); 60896199Sdes if (!__pw_scan(bp, &pw, flags)) { 60996199Sdes free(bp); 61096199Sdes return (NULL); 61196199Sdes } 61296199Sdes ret = pw_dup(&pw); 61396199Sdes free(bp); 61496199Sdes return (ret); 61596199Sdes} 616