Commit | Line | Data |
---|---|---|
ee8287f8 JS |
1 | /*- |
2 | * Copyright (c) 1990, 1993, 1994 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * Copyright (c) 2002 Networks Associates Technology, Inc. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Portions of this software were developed for the FreeBSD Project by | |
8 | * ThinkSec AS and NAI Labs, the Security Research Division of Network | |
9 | * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 | |
10 | * ("CBOSS"), as part of the DARPA CHATS research program. | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or without | |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions and the following disclaimer. | |
17 | * 2. Redistributions in binary form must reproduce the above copyright | |
18 | * notice, this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
dc71b7ab | 20 | * 3. Neither the name of the University nor the names of its contributors |
ee8287f8 JS |
21 | * may be used to endorse or promote products derived from this software |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
feed9a49 | 36 | * @(#)pw_util.c 8.3 (Berkeley) 4/2/94 |
d316f7c9 | 37 | * $FreeBSD: head/lib/libutil/pw_util.c 244744 2012-12-27 20:24:44Z bapt $ |
ee8287f8 JS |
38 | */ |
39 | ||
40 | /* | |
41 | * This file is used by all the "password" programs; vipw(8), chpass(1), | |
42 | * and passwd(1). | |
43 | */ | |
44 | ||
45 | #include <sys/param.h> | |
46 | #include <sys/errno.h> | |
47 | #include <sys/time.h> | |
48 | #include <sys/resource.h> | |
49 | #include <sys/stat.h> | |
50 | #include <sys/wait.h> | |
51 | ||
52 | #include <ctype.h> | |
53 | #include <err.h> | |
54 | #include <fcntl.h> | |
55 | #include <inttypes.h> | |
56 | #include <libgen.h> | |
57 | #include <paths.h> | |
58 | #include <pwd.h> | |
59 | #include <signal.h> | |
60 | #include <stdio.h> | |
61 | #include <stdlib.h> | |
62 | #include <string.h> | |
63 | #include <unistd.h> | |
ee8287f8 JS |
64 | #include <libutil.h> |
65 | ||
66 | static pid_t editpid = -1; | |
67 | static int lockfd = -1; | |
68 | static char masterpasswd[PATH_MAX]; | |
69 | static char passwd_dir[PATH_MAX]; | |
70 | static char tempname[PATH_MAX]; | |
71 | static int initialized; | |
72 | ||
73 | #if 0 | |
74 | void | |
75 | pw_cont(int sig) | |
76 | { | |
77 | ||
78 | if (editpid != -1) | |
79 | kill(editpid, sig); | |
80 | } | |
81 | #endif | |
82 | ||
83 | /* | |
84 | * Initialize statics and set limits, signals & umask to try to avoid | |
85 | * interruptions, crashes etc. that might expose passord data. | |
86 | */ | |
87 | int | |
88 | pw_init(const char *dir, const char *master) | |
89 | { | |
90 | #if 0 | |
91 | struct rlimit rlim; | |
92 | #endif | |
93 | ||
94 | if (dir == NULL) { | |
95 | strcpy(passwd_dir, _PATH_ETC); | |
96 | } else { | |
97 | if (strlen(dir) >= sizeof(passwd_dir)) { | |
98 | errno = ENAMETOOLONG; | |
99 | return (-1); | |
100 | } | |
101 | strcpy(passwd_dir, dir); | |
102 | } | |
103 | ||
104 | if (master == NULL) { | |
105 | if (dir == NULL) { | |
106 | strcpy(masterpasswd, _PATH_MASTERPASSWD); | |
107 | } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s", | |
108 | passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) { | |
109 | errno = ENAMETOOLONG; | |
110 | return (-1); | |
111 | } | |
112 | } else { | |
113 | if (strlen(master) >= sizeof(masterpasswd)) { | |
114 | errno = ENAMETOOLONG; | |
115 | return (-1); | |
116 | } | |
117 | strcpy(masterpasswd, master); | |
118 | } | |
119 | ||
120 | /* | |
121 | * The code that follows is extremely disruptive to the calling | |
122 | * process, and is therefore disabled until someone can conceive | |
123 | * of a realistic scenario where it would fend off a compromise. | |
124 | * Race conditions concerning the temporary files can be guarded | |
125 | * against in other ways than masking signals (by checking stat(2) | |
126 | * results after creation). | |
127 | */ | |
128 | #if 0 | |
129 | /* Unlimited resource limits. */ | |
130 | rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; | |
131 | (void)setrlimit(RLIMIT_CPU, &rlim); | |
132 | (void)setrlimit(RLIMIT_FSIZE, &rlim); | |
133 | (void)setrlimit(RLIMIT_STACK, &rlim); | |
134 | (void)setrlimit(RLIMIT_DATA, &rlim); | |
135 | (void)setrlimit(RLIMIT_RSS, &rlim); | |
136 | ||
137 | /* Don't drop core (not really necessary, but GP's). */ | |
138 | rlim.rlim_cur = rlim.rlim_max = 0; | |
139 | (void)setrlimit(RLIMIT_CORE, &rlim); | |
140 | ||
141 | /* Turn off signals. */ | |
142 | (void)signal(SIGALRM, SIG_IGN); | |
143 | (void)signal(SIGHUP, SIG_IGN); | |
144 | (void)signal(SIGINT, SIG_IGN); | |
145 | (void)signal(SIGPIPE, SIG_IGN); | |
146 | (void)signal(SIGQUIT, SIG_IGN); | |
147 | (void)signal(SIGTERM, SIG_IGN); | |
148 | (void)signal(SIGCONT, pw_cont); | |
149 | ||
150 | /* Create with exact permissions. */ | |
151 | (void)umask(0); | |
152 | #endif | |
153 | initialized = 1; | |
154 | return (0); | |
155 | } | |
156 | ||
157 | /* | |
158 | * Lock the master password file. | |
159 | */ | |
160 | int | |
161 | pw_lock(void) | |
162 | { | |
163 | ||
164 | if (*masterpasswd == '\0') | |
165 | return (-1); | |
166 | ||
167 | /* | |
168 | * If the master password file doesn't exist, the system is hosed. | |
169 | * Might as well try to build one. Set the close-on-exec bit so | |
170 | * that users can't get at the encrypted passwords while editing. | |
171 | * Open should allow flock'ing the file; see 4.4BSD. XXX | |
172 | */ | |
173 | for (;;) { | |
174 | struct stat st; | |
175 | ||
d316f7c9 JM |
176 | lockfd = flopen(masterpasswd, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); |
177 | if (lockfd == -1) { | |
ee8287f8 JS |
178 | if (errno == EWOULDBLOCK) { |
179 | errx(1, "the password db file is busy"); | |
180 | } else { | |
181 | err(1, "could not lock the passwd file: "); | |
182 | } | |
183 | } | |
184 | ||
185 | /* | |
186 | * If the password file was replaced while we were trying to | |
187 | * get the lock, our hardlink count will be 0 and we have to | |
188 | * close and retry. | |
189 | */ | |
190 | if (fstat(lockfd, &st) == -1) | |
191 | err(1, "fstat() failed: "); | |
192 | if (st.st_nlink != 0) | |
193 | break; | |
194 | close(lockfd); | |
195 | lockfd = -1; | |
196 | } | |
197 | return (lockfd); | |
198 | } | |
199 | ||
200 | /* | |
201 | * Create and open a presumably safe temp file for editing the password | |
202 | * data, and copy the master password file into it. | |
203 | */ | |
204 | int | |
205 | pw_tmp(int mfd) | |
206 | { | |
207 | char buf[8192]; | |
208 | ssize_t nr; | |
209 | const char *p; | |
210 | int tfd; | |
211 | ||
212 | if (*masterpasswd == '\0') | |
213 | return (-1); | |
214 | if ((p = strrchr(masterpasswd, '/'))) | |
215 | ++p; | |
216 | else | |
217 | p = masterpasswd; | |
218 | if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", | |
219 | (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { | |
220 | errno = ENAMETOOLONG; | |
221 | return (-1); | |
222 | } | |
223 | if ((tfd = mkstemp(tempname)) == -1) | |
224 | return (-1); | |
225 | if (mfd != -1) { | |
226 | while ((nr = read(mfd, buf, sizeof(buf))) > 0) | |
227 | if (write(tfd, buf, (size_t)nr) != nr) | |
228 | break; | |
229 | if (nr != 0) { | |
230 | unlink(tempname); | |
231 | *tempname = '\0'; | |
232 | close(tfd); | |
233 | return (-1); | |
234 | } | |
235 | } | |
236 | return (tfd); | |
237 | } | |
238 | ||
239 | /* | |
240 | * Regenerate the password database. | |
241 | */ | |
242 | int | |
243 | pw_mkdb(const char *user) | |
244 | { | |
245 | int pstat; | |
246 | pid_t pid; | |
247 | ||
248 | (void)fflush(stderr); | |
249 | switch ((pid = fork())) { | |
250 | case -1: | |
251 | return (-1); | |
252 | case 0: | |
253 | /* child */ | |
254 | if (user == NULL) | |
255 | execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", | |
79ac1d4a | 256 | "-d", passwd_dir, tempname, NULL); |
ee8287f8 JS |
257 | else |
258 | execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", | |
259 | "-d", passwd_dir, "-u", user, tempname, | |
79ac1d4a | 260 | NULL); |
ee8287f8 JS |
261 | _exit(1); |
262 | /* NOTREACHED */ | |
263 | default: | |
264 | /* parent */ | |
265 | break; | |
266 | } | |
267 | if (waitpid(pid, &pstat, 0) == -1) | |
268 | return (-1); | |
269 | if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) | |
270 | return (0); | |
271 | errno = 0; | |
272 | return (-1); | |
273 | } | |
274 | ||
275 | /* | |
276 | * Edit the temp file. Return -1 on error, >0 if the file was modified, 0 | |
277 | * if it was not. | |
278 | */ | |
279 | int | |
280 | pw_edit(int notsetuid) | |
281 | { | |
282 | struct sigaction sa, sa_int, sa_quit; | |
d316f7c9 | 283 | sigset_t oldsigset, nsigset; |
ee8287f8 JS |
284 | struct stat st1, st2; |
285 | const char *editor; | |
286 | int pstat; | |
287 | ||
288 | if ((editor = getenv("EDITOR")) == NULL) | |
289 | editor = _PATH_VI; | |
290 | if (stat(tempname, &st1) == -1) | |
291 | return (-1); | |
292 | sa.sa_handler = SIG_IGN; | |
293 | sigemptyset(&sa.sa_mask); | |
294 | sa.sa_flags = 0; | |
295 | sigaction(SIGINT, &sa, &sa_int); | |
296 | sigaction(SIGQUIT, &sa, &sa_quit); | |
d316f7c9 JM |
297 | sigemptyset(&nsigset); |
298 | sigaddset(&nsigset, SIGCHLD); | |
299 | sigprocmask(SIG_BLOCK, &nsigset, &oldsigset); | |
ee8287f8 JS |
300 | switch ((editpid = fork())) { |
301 | case -1: | |
302 | return (-1); | |
303 | case 0: | |
304 | sigaction(SIGINT, &sa_int, NULL); | |
305 | sigaction(SIGQUIT, &sa_quit, NULL); | |
306 | sigprocmask(SIG_SETMASK, &oldsigset, NULL); | |
307 | if (notsetuid) { | |
d316f7c9 JM |
308 | (void)setgid(getgid()); |
309 | (void)setuid(getuid()); | |
ee8287f8 JS |
310 | } |
311 | errno = 0; | |
79ac1d4a | 312 | execlp(editor, basename(editor), tempname, NULL); |
ee8287f8 JS |
313 | _exit(errno); |
314 | default: | |
315 | /* parent */ | |
316 | break; | |
317 | } | |
318 | for (;;) { | |
319 | if (waitpid(editpid, &pstat, WUNTRACED) == -1) { | |
320 | if (errno == EINTR) | |
321 | continue; | |
322 | unlink(tempname); | |
323 | editpid = -1; | |
324 | break; | |
325 | } else if (WIFSTOPPED(pstat)) { | |
326 | raise(WSTOPSIG(pstat)); | |
327 | } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { | |
328 | editpid = -1; | |
329 | break; | |
330 | } else { | |
331 | unlink(tempname); | |
332 | editpid = -1; | |
333 | break; | |
334 | } | |
335 | } | |
336 | sigaction(SIGINT, &sa_int, NULL); | |
337 | sigaction(SIGQUIT, &sa_quit, NULL); | |
338 | sigprocmask(SIG_SETMASK, &oldsigset, NULL); | |
339 | if (stat(tempname, &st2) == -1) | |
340 | return (-1); | |
d316f7c9 JM |
341 | return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec || |
342 | st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec); | |
ee8287f8 JS |
343 | } |
344 | ||
345 | /* | |
346 | * Clean up. Preserve errno for the caller's convenience. | |
347 | */ | |
348 | void | |
349 | pw_fini(void) | |
350 | { | |
351 | int serrno, status; | |
352 | ||
353 | if (!initialized) | |
354 | return; | |
355 | initialized = 0; | |
356 | serrno = errno; | |
357 | if (editpid != -1) { | |
358 | kill(editpid, SIGTERM); | |
359 | kill(editpid, SIGCONT); | |
360 | waitpid(editpid, &status, 0); | |
361 | editpid = -1; | |
362 | } | |
363 | if (*tempname != '\0') { | |
364 | unlink(tempname); | |
365 | *tempname = '\0'; | |
366 | } | |
367 | if (lockfd != -1) | |
368 | close(lockfd); | |
369 | errno = serrno; | |
370 | } | |
371 | ||
372 | /* | |
373 | * Compares two struct pwds. | |
374 | */ | |
375 | int | |
376 | pw_equal(const struct passwd *pw1, const struct passwd *pw2) | |
377 | { | |
378 | return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && | |
379 | pw1->pw_uid == pw2->pw_uid && | |
380 | pw1->pw_gid == pw2->pw_gid && | |
381 | strcmp(pw1->pw_class, pw2->pw_class) == 0 && | |
382 | pw1->pw_change == pw2->pw_change && | |
383 | pw1->pw_expire == pw2->pw_expire && | |
384 | strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && | |
385 | strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && | |
386 | strcmp(pw1->pw_shell, pw2->pw_shell) == 0); | |
387 | } | |
388 | ||
389 | /* | |
390 | * Make a passwd line out of a struct passwd. | |
391 | */ | |
392 | char * | |
393 | pw_make(const struct passwd *pw) | |
394 | { | |
395 | char *line; | |
396 | ||
397 | asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, | |
398 | pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, | |
399 | pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, | |
400 | pw->pw_gecos, pw->pw_dir, pw->pw_shell); | |
d316f7c9 | 401 | return (line); |
ee8287f8 JS |
402 | } |
403 | ||
404 | /* | |
d316f7c9 JM |
405 | * Make a passwd line (in v7 format) out of a struct passwd |
406 | */ | |
407 | char * | |
408 | pw_make_v7(const struct passwd *pw) | |
409 | { | |
410 | char *line; | |
411 | ||
412 | asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name, | |
413 | (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, | |
414 | pw->pw_gecos, pw->pw_dir, pw->pw_shell); | |
415 | return (line); | |
416 | } | |
417 | ||
418 | /* | |
419 | * Copy password file from one descriptor to another, replacing, deleting | |
420 | * or adding a single record on the way. | |
ee8287f8 JS |
421 | */ |
422 | int | |
423 | pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) | |
424 | { | |
425 | char buf[8192], *end, *line, *p, *q, *r, t; | |
426 | struct passwd *fpw; | |
d316f7c9 | 427 | const struct passwd *spw; |
ee8287f8 JS |
428 | size_t len; |
429 | int eof, readlen; | |
430 | ||
d316f7c9 JM |
431 | if (old_pw == NULL && pw == NULL) |
432 | return (-1); | |
433 | ||
434 | spw = old_pw; | |
435 | /* deleting a user */ | |
436 | if (pw == NULL) { | |
437 | line = NULL; | |
438 | } else { | |
ee8287f8 JS |
439 | if ((line = pw_make(pw)) == NULL) |
440 | return (-1); | |
d316f7c9 JM |
441 | } |
442 | ||
443 | /* adding a user */ | |
444 | if (spw == NULL) | |
445 | spw = pw; | |
ee8287f8 JS |
446 | |
447 | eof = 0; | |
d316f7c9 JM |
448 | len = 0; |
449 | p = q = end = buf; | |
ee8287f8 JS |
450 | for (;;) { |
451 | /* find the end of the current line */ | |
452 | for (p = q; q < end && *q != '\0'; ++q) | |
453 | if (*q == '\n') | |
454 | break; | |
455 | ||
456 | /* if we don't have a complete line, fill up the buffer */ | |
457 | if (q >= end) { | |
458 | if (eof) | |
459 | break; | |
460 | if ((size_t)(q - p) >= sizeof(buf)) { | |
461 | warnx("passwd line too long"); | |
462 | errno = EINVAL; /* hack */ | |
463 | goto err; | |
464 | } | |
465 | if (p < end) { | |
466 | q = memmove(buf, p, end - p); | |
467 | end -= p - buf; | |
468 | } else { | |
469 | p = q = end = buf; | |
470 | } | |
471 | readlen = read(ffd, end, sizeof(buf) - (end - buf)); | |
472 | if (readlen == -1) | |
473 | goto err; | |
474 | else | |
475 | len = (size_t)readlen; | |
476 | if (len == 0 && p == buf) | |
477 | break; | |
478 | end += len; | |
479 | len = end - buf; | |
480 | if (len < (ssize_t)sizeof(buf)) { | |
481 | eof = 1; | |
482 | if (len > 0 && buf[len - 1] != '\n') | |
d316f7c9 | 483 | ++len, *end++ = '\n'; |
ee8287f8 JS |
484 | } |
485 | continue; | |
486 | } | |
487 | ||
488 | /* is it a blank line or a comment? */ | |
489 | for (r = p; r < q && isspace(*r); ++r) | |
490 | /* nothing */ ; | |
491 | if (r == q || *r == '#') { | |
492 | /* yep */ | |
493 | if (write(tfd, p, q - p + 1) != q - p + 1) | |
494 | goto err; | |
495 | ++q; | |
496 | continue; | |
497 | } | |
498 | ||
499 | /* is it the one we're looking for? */ | |
feed9a49 | 500 | |
ee8287f8 JS |
501 | t = *q; |
502 | *q = '\0'; | |
feed9a49 PA |
503 | |
504 | fpw = pw_scan(r, PWSCAN_MASTER); | |
505 | ||
506 | /* | |
507 | * fpw is either the struct passwd for the current line, | |
508 | * or NULL if the line is malformed. | |
509 | */ | |
510 | ||
ee8287f8 | 511 | *q = t; |
d316f7c9 | 512 | if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) { |
ee8287f8 | 513 | /* nope */ |
feed9a49 PA |
514 | if (fpw != NULL) |
515 | free(fpw); | |
ee8287f8 JS |
516 | if (write(tfd, p, q - p + 1) != q - p + 1) |
517 | goto err; | |
518 | ++q; | |
519 | continue; | |
520 | } | |
521 | if (old_pw && !pw_equal(fpw, old_pw)) { | |
522 | warnx("entry inconsistent"); | |
523 | free(fpw); | |
524 | errno = EINVAL; /* hack */ | |
525 | goto err; | |
526 | } | |
527 | free(fpw); | |
528 | ||
d316f7c9 JM |
529 | /* it is, replace or remove it */ |
530 | if (line != NULL) { | |
ee8287f8 JS |
531 | len = strlen(line); |
532 | if (write(tfd, line, len) != (int)len) | |
533 | goto err; | |
d316f7c9 JM |
534 | } else { |
535 | /* when removed, avoid the \n */ | |
536 | q++; | |
537 | } | |
ee8287f8 JS |
538 | /* we're done, just copy the rest over */ |
539 | for (;;) { | |
540 | if (write(tfd, q, end - q) != end - q) | |
541 | goto err; | |
542 | q = buf; | |
543 | readlen = read(ffd, buf, sizeof(buf)); | |
544 | if (readlen == 0) | |
545 | break; | |
546 | else | |
547 | len = (size_t)readlen; | |
548 | if (readlen == -1) | |
549 | goto err; | |
550 | end = buf + len; | |
551 | } | |
552 | goto done; | |
553 | } | |
554 | ||
d316f7c9 JM |
555 | /* if we got here, we didn't find the old entry */ |
556 | if (line == NULL) { | |
557 | errno = ENOENT; | |
558 | goto err; | |
559 | } | |
ee8287f8 JS |
560 | len = strlen(line); |
561 | if ((size_t)write(tfd, line, len) != len || | |
562 | write(tfd, "\n", 1) != 1) | |
563 | goto err; | |
564 | done: | |
d316f7c9 | 565 | if (line != NULL) |
d2083496 | 566 | free(line); |
ee8287f8 JS |
567 | return (0); |
568 | err: | |
d316f7c9 | 569 | if (line != NULL) |
d2083496 | 570 | free(line); |
ee8287f8 JS |
571 | return (-1); |
572 | } | |
573 | ||
574 | /* | |
575 | * Return the current value of tempname. | |
576 | */ | |
577 | const char * | |
578 | pw_tempname(void) | |
579 | { | |
580 | ||
581 | return (tempname); | |
582 | } | |
583 | ||
584 | /* | |
585 | * Duplicate a struct passwd. | |
586 | */ | |
587 | struct passwd * | |
588 | pw_dup(const struct passwd *pw) | |
589 | { | |
d316f7c9 | 590 | char *dst; |
ee8287f8 JS |
591 | struct passwd *npw; |
592 | ssize_t len; | |
593 | ||
d316f7c9 JM |
594 | len = sizeof(*npw); |
595 | if (pw->pw_name != NULL) | |
596 | len += strlen(pw->pw_name) + 1; | |
597 | if (pw->pw_passwd != NULL) | |
598 | len += strlen(pw->pw_passwd) + 1; | |
599 | if (pw->pw_class != NULL) | |
600 | len += strlen(pw->pw_class) + 1; | |
601 | if (pw->pw_gecos != NULL) | |
602 | len += strlen(pw->pw_gecos) + 1; | |
603 | if (pw->pw_dir != NULL) | |
604 | len += strlen(pw->pw_dir) + 1; | |
605 | if (pw->pw_shell != NULL) | |
606 | len += strlen(pw->pw_shell) + 1; | |
ee8287f8 JS |
607 | if ((npw = malloc((size_t)len)) == NULL) |
608 | return (NULL); | |
609 | memcpy(npw, pw, sizeof(*npw)); | |
d316f7c9 JM |
610 | dst = (char *)npw + sizeof(*npw); |
611 | if (pw->pw_name != NULL) { | |
612 | npw->pw_name = dst; | |
613 | dst = stpcpy(npw->pw_name, pw->pw_name) + 1; | |
ee8287f8 | 614 | } |
d316f7c9 JM |
615 | if (pw->pw_passwd != NULL) { |
616 | npw->pw_passwd = dst; | |
617 | dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; | |
ee8287f8 | 618 | } |
d316f7c9 JM |
619 | if (pw->pw_class != NULL) { |
620 | npw->pw_class = dst; | |
621 | dst = stpcpy(npw->pw_class, pw->pw_class) + 1; | |
ee8287f8 | 622 | } |
d316f7c9 JM |
623 | if (pw->pw_gecos != NULL) { |
624 | npw->pw_gecos = dst; | |
625 | dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; | |
ee8287f8 | 626 | } |
d316f7c9 JM |
627 | if (pw->pw_dir != NULL) { |
628 | npw->pw_dir = dst; | |
629 | dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; | |
ee8287f8 | 630 | } |
d316f7c9 JM |
631 | if (pw->pw_shell != NULL) { |
632 | npw->pw_shell = dst; | |
633 | dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; | |
ee8287f8 JS |
634 | } |
635 | return (npw); | |
636 | } | |
637 | ||
feed9a49 PA |
638 | #include "pw_scan.h" |
639 | ||
ee8287f8 JS |
640 | /* |
641 | * Wrapper around an internal libc function | |
642 | */ | |
643 | struct passwd * | |
644 | pw_scan(const char *line, int flags) | |
645 | { | |
646 | struct passwd pw, *ret; | |
647 | char *bp; | |
648 | ||
649 | if ((bp = strdup(line)) == NULL) | |
650 | return (NULL); | |
651 | if (!__pw_scan(bp, &pw, flags)) { | |
652 | free(bp); | |
653 | return (NULL); | |
654 | } | |
655 | ret = pw_dup(&pw); | |
656 | free(bp); | |
657 | return (ret); | |
658 | } |