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