]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_user.c
Use intmax_t rather than long long
[pw-darwin.git] / pw / pw_user.c
1 /*-
2 * Copyright (C) 1996
3 * David L. Nugent. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28 #ifndef lint
29 static const char rcsid[] =
30 "$FreeBSD$";
31 #endif /* not lint */
32
33 #include <sys/param.h>
34 #include <sys/resource.h>
35 #include <sys/time.h>
36 #include <sys/types.h>
37
38 #include <ctype.h>
39 #include <dirent.h>
40 #include <err.h>
41 #include <fcntl.h>
42 #include <grp.h>
43 #include <pwd.h>
44 #include <libutil.h>
45 #include <login_cap.h>
46 #include <paths.h>
47 #include <string.h>
48 #include <sysexits.h>
49 #include <termios.h>
50 #include <unistd.h>
51
52 #include "pw.h"
53 #include "bitmap.h"
54 #include "psdate.h"
55
56 #define LOGNAMESIZE (MAXLOGNAME-1)
57
58 static char locked_str[] = "*LOCKED*";
59
60 static struct passwd fakeuser = {
61 "nouser",
62 "*",
63 -1,
64 -1,
65 0,
66 "",
67 "User &",
68 "/nonexistent",
69 "/bin/sh",
70 0,
71 0
72 };
73
74 static int print_user(struct passwd *pwd, bool pretty, bool v7);
75 static uid_t pw_uidpolicy(struct userconf *cnf, intmax_t id);
76 static uid_t pw_gidpolicy(struct userconf *cnf, char *grname, char *nam,
77 gid_t prefer, bool dryrun);
78 static char *pw_homepolicy(struct userconf * cnf, char *homedir,
79 const char *user);
80 static char *pw_shellpolicy(struct userconf * cnf);
81 static char *pw_password(struct userconf * cnf, char const * user,
82 bool dryrun);
83 static char *shell_path(char const * path, char *shells[], char *sh);
84 static void rmat(uid_t uid);
85 static void rmopie(char const * name);
86
87 static void
88 create_and_populate_homedir(struct userconf *cnf, struct passwd *pwd,
89 const char *skeldir, mode_t homemode, bool update)
90 {
91 int skelfd = -1;
92
93 if (skeldir != NULL && *skeldir != '\0') {
94 if (*skeldir == '/')
95 skeldir++;
96 skelfd = openat(conf.rootfd, skeldir, O_DIRECTORY|O_CLOEXEC);
97 }
98
99 copymkdir(conf.rootfd, pwd->pw_dir, skelfd, homemode, pwd->pw_uid,
100 pwd->pw_gid, 0);
101 pw_log(cnf, update ? M_UPDATE : M_ADD, W_USER, "%s(%ju) home %s made",
102 pwd->pw_name, (uintmax_t)pwd->pw_uid, pwd->pw_dir);
103 }
104
105 static int
106 pw_set_passwd(struct passwd *pwd, int fd, bool precrypted, bool update)
107 {
108 int b, istty;
109 struct termios t, n;
110 login_cap_t *lc;
111 char line[_PASSWORD_LEN+1];
112 char *p;
113
114 if (fd == '-') {
115 if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
116 pwd->pw_passwd = "*"; /* No access */
117 return (1);
118 }
119 return (0);
120 }
121
122 if ((istty = isatty(fd))) {
123 if (tcgetattr(fd, &t) == -1)
124 istty = 0;
125 else {
126 n = t;
127 n.c_lflag &= ~(ECHO);
128 tcsetattr(fd, TCSANOW, &n);
129 printf("%s%spassword for user %s:",
130 update ? "new " : "",
131 precrypted ? "encrypted " : "",
132 pwd->pw_name);
133 fflush(stdout);
134 }
135 }
136 b = read(fd, line, sizeof(line) - 1);
137 if (istty) { /* Restore state */
138 tcsetattr(fd, TCSANOW, &t);
139 fputc('\n', stdout);
140 fflush(stdout);
141 }
142
143 if (b < 0)
144 err(EX_IOERR, "-%c file descriptor",
145 precrypted ? 'H' : 'h');
146 line[b] = '\0';
147 if ((p = strpbrk(line, "\r\n")) != NULL)
148 *p = '\0';
149 if (!*line)
150 errx(EX_DATAERR, "empty password read on file descriptor %d",
151 fd);
152 if (precrypted) {
153 if (strchr(line, ':') != NULL)
154 errx(EX_DATAERR, "bad encrypted password");
155 pwd->pw_passwd = strdup(line);
156 } else {
157 lc = login_getpwclass(pwd);
158 if (lc == NULL ||
159 login_setcryptfmt(lc, "sha512", NULL) == NULL)
160 warn("setting crypt(3) format");
161 login_close(lc);
162 pwd->pw_passwd = pw_pwcrypt(line);
163 }
164 return (1);
165 }
166
167 static void
168 perform_chgpwent(const char *name, struct passwd *pwd, char *nispasswd)
169 {
170 int rc;
171 struct passwd *nispwd;
172
173 /* duplicate for nis so that chgpwent is not modifying before NIS */
174 if (nispasswd && *nispasswd == '/')
175 nispwd = pw_dup(pwd);
176
177 rc = chgpwent(name, pwd);
178 if (rc == -1)
179 errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name);
180 else if (rc != 0)
181 err(EX_IOERR, "passwd file update");
182
183 if (nispasswd && *nispasswd == '/') {
184 rc = chgnispwent(nispasswd, name, nispwd);
185 if (rc == -1)
186 warn("User '%s' not found in NIS passwd", pwd->pw_name);
187 else if (rc != 0)
188 warn("NIS passwd update");
189 /* NOTE: NIS-only update errors are not fatal */
190 }
191 }
192
193 /*
194 * The M_LOCK and M_UNLOCK functions simply add or remove
195 * a "*LOCKED*" prefix from in front of the password to
196 * prevent it decoding correctly, and therefore prevents
197 * access. Of course, this only prevents access via
198 * password authentication (not ssh, kerberos or any
199 * other method that does not use the UNIX password) but
200 * that is a known limitation.
201 */
202 static int
203 pw_userlock(char *arg1, int mode)
204 {
205 struct passwd *pwd = NULL;
206 char *passtmp = NULL;
207 char *name;
208 bool locked = false;
209 uid_t id;
210
211 if (geteuid() != 0)
212 errx(EX_NOPERM, "you must be root");
213
214 if (arg1 == NULL)
215 errx(EX_DATAERR, "username or id required");
216
217 if (strspn(arg1, "0123456789") == strlen(arg1))
218 id = pw_checkid(arg1, UID_MAX);
219 else
220 name = arg1;
221
222 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
223 if (pwd == NULL) {
224 if (name == NULL)
225 errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id);
226 errx(EX_NOUSER, "no such user `%s'", name);
227 }
228
229 if (name == NULL)
230 name = pwd->pw_name;
231
232 if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0)
233 locked = true;
234 if (mode == M_LOCK && locked)
235 errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
236 if (mode == M_UNLOCK && !locked)
237 errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name);
238
239 if (mode == M_LOCK) {
240 asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd);
241 if (passtmp == NULL) /* disaster */
242 errx(EX_UNAVAILABLE, "out of memory");
243 pwd->pw_passwd = passtmp;
244 } else {
245 pwd->pw_passwd += sizeof(locked_str)-1;
246 }
247
248 perform_chgpwent(name, pwd, NULL);
249 free(passtmp);
250
251 return (EXIT_SUCCESS);
252 }
253
254 static uid_t
255 pw_uidpolicy(struct userconf * cnf, intmax_t id)
256 {
257 struct passwd *pwd;
258 struct bitmap bm;
259 uid_t uid = (uid_t) - 1;
260
261 /*
262 * Check the given uid, if any
263 */
264 if (id >= 0) {
265 uid = (uid_t) id;
266
267 if ((pwd = GETPWUID(uid)) != NULL && conf.checkduplicate)
268 errx(EX_DATAERR, "uid `%ju' has already been allocated",
269 (uintmax_t)pwd->pw_uid);
270 return (uid);
271 }
272 /*
273 * We need to allocate the next available uid under one of
274 * two policies a) Grab the first unused uid b) Grab the
275 * highest possible unused uid
276 */
277 if (cnf->min_uid >= cnf->max_uid) { /* Sanity
278 * claus^H^H^H^Hheck */
279 cnf->min_uid = 1000;
280 cnf->max_uid = 32000;
281 }
282 bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
283
284 /*
285 * Now, let's fill the bitmap from the password file
286 */
287 SETPWENT();
288 while ((pwd = GETPWENT()) != NULL)
289 if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
290 bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
291 ENDPWENT();
292
293 /*
294 * Then apply the policy, with fallback to reuse if necessary
295 */
296 if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
297 uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
298
299 /*
300 * Another sanity check
301 */
302 if (uid < cnf->min_uid || uid > cnf->max_uid)
303 errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
304 bm_dealloc(&bm);
305 return (uid);
306 }
307
308 static uid_t
309 pw_gidpolicy(struct userconf *cnf, char *grname, char *nam, gid_t prefer, bool dryrun)
310 {
311 struct group *grp;
312 gid_t gid = (uid_t) - 1;
313
314 /*
315 * Check the given gid, if any
316 */
317 SETGRENT();
318 if (grname) {
319 if ((grp = GETGRNAM(grname)) == NULL) {
320 gid = pw_checkid(grname, GID_MAX);
321 grp = GETGRGID(gid);
322 }
323 gid = grp->gr_gid;
324 } else if ((grp = GETGRNAM(nam)) != NULL &&
325 (grp->gr_mem == NULL || grp->gr_mem[0] == NULL)) {
326 gid = grp->gr_gid; /* Already created? Use it anyway... */
327 } else {
328 intmax_t grid = -1;
329
330 /*
331 * We need to auto-create a group with the user's name. We
332 * can send all the appropriate output to our sister routine
333 * bit first see if we can create a group with gid==uid so we
334 * can keep the user and group ids in sync. We purposely do
335 * NOT check the gid range if we can force the sync. If the
336 * user's name dups an existing group, then the group add
337 * function will happily handle that case for us and exit.
338 */
339 if (GETGRGID(prefer) == NULL)
340 grid = prefer;
341 if (dryrun) {
342 gid = pw_groupnext(cnf, true);
343 } else {
344 if (grid == -1)
345 grid = pw_groupnext(cnf, true);
346 groupadd(cnf, nam, grid, NULL, -1, false, false, false);
347 if ((grp = GETGRNAM(nam)) != NULL)
348 gid = grp->gr_gid;
349 }
350 }
351 ENDGRENT();
352 return (gid);
353 }
354
355 static char *
356 pw_homepolicy(struct userconf * cnf, char *homedir, const char *user)
357 {
358 static char home[128];
359
360 if (homedir)
361 return (homedir);
362
363 if (cnf->home == NULL || *cnf->home == '\0')
364 errx(EX_CONFIG, "no base home directory set");
365 snprintf(home, sizeof(home), "%s/%s", cnf->home, user);
366
367 return (home);
368 }
369
370 static char *
371 shell_path(char const * path, char *shells[], char *sh)
372 {
373 if (sh != NULL && (*sh == '/' || *sh == '\0'))
374 return sh; /* specified full path or forced none */
375 else {
376 char *p;
377 char paths[_UC_MAXLINE];
378
379 /*
380 * We need to search paths
381 */
382 strlcpy(paths, path, sizeof(paths));
383 for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
384 int i;
385 static char shellpath[256];
386
387 if (sh != NULL) {
388 snprintf(shellpath, sizeof(shellpath), "%s/%s", p, sh);
389 if (access(shellpath, X_OK) == 0)
390 return shellpath;
391 } else
392 for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
393 snprintf(shellpath, sizeof(shellpath), "%s/%s", p, shells[i]);
394 if (access(shellpath, X_OK) == 0)
395 return shellpath;
396 }
397 }
398 if (sh == NULL)
399 errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
400 errx(EX_CONFIG, "no default shell available or defined");
401 return NULL;
402 }
403 }
404
405 static char *
406 pw_shellpolicy(struct userconf * cnf)
407 {
408
409 return shell_path(cnf->shelldir, cnf->shells, cnf->shell_default);
410 }
411
412 #define SALTSIZE 32
413
414 static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
415
416 char *
417 pw_pwcrypt(char *password)
418 {
419 int i;
420 char salt[SALTSIZE + 1];
421 char *cryptpw;
422 static char buf[256];
423
424 /*
425 * Calculate a salt value
426 */
427 for (i = 0; i < SALTSIZE; i++)
428 salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
429 salt[SALTSIZE] = '\0';
430
431 cryptpw = crypt(password, salt);
432 if (cryptpw == NULL)
433 errx(EX_CONFIG, "crypt(3) failure");
434 return strcpy(buf, cryptpw);
435 }
436
437 static char *
438 pw_password(struct userconf * cnf, char const * user, bool dryrun)
439 {
440 int i, l;
441 char pwbuf[32];
442
443 switch (cnf->default_password) {
444 case -1: /* Random password */
445 l = (arc4random() % 8 + 8); /* 8 - 16 chars */
446 for (i = 0; i < l; i++)
447 pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
448 pwbuf[i] = '\0';
449
450 /*
451 * We give this information back to the user
452 */
453 if (conf.fd == -1 && !dryrun) {
454 if (isatty(STDOUT_FILENO))
455 printf("Password for '%s' is: ", user);
456 printf("%s\n", pwbuf);
457 fflush(stdout);
458 }
459 break;
460
461 case -2: /* No password at all! */
462 return "";
463
464 case 0: /* No login - default */
465 default:
466 return "*";
467
468 case 1: /* user's name */
469 strlcpy(pwbuf, user, sizeof(pwbuf));
470 break;
471 }
472 return pw_pwcrypt(pwbuf);
473 }
474
475 static int
476 print_user(struct passwd * pwd, bool pretty, bool v7)
477 {
478 int j;
479 char *p;
480 struct group *grp = GETGRGID(pwd->pw_gid);
481 char uname[60] = "User &", office[60] = "[None]",
482 wphone[60] = "[None]", hphone[60] = "[None]";
483 char acexpire[32] = "[None]", pwexpire[32] = "[None]";
484 struct tm * tptr;
485
486 if (!pretty) {
487 p = v7 ? pw_make_v7(pwd) : pw_make(pwd);
488 printf("%s\n", p);
489 free(p);
490 return (EXIT_SUCCESS);
491 }
492
493 if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
494 strlcpy(uname, p, sizeof(uname));
495 if ((p = strtok(NULL, ",")) != NULL) {
496 strlcpy(office, p, sizeof(office));
497 if ((p = strtok(NULL, ",")) != NULL) {
498 strlcpy(wphone, p, sizeof(wphone));
499 if ((p = strtok(NULL, "")) != NULL) {
500 strlcpy(hphone, p, sizeof(hphone));
501 }
502 }
503 }
504 }
505 /*
506 * Handle '&' in gecos field
507 */
508 if ((p = strchr(uname, '&')) != NULL) {
509 int l = strlen(pwd->pw_name);
510 int m = strlen(p);
511
512 memmove(p + l, p + 1, m);
513 memmove(p, pwd->pw_name, l);
514 *p = (char) toupper((unsigned char)*p);
515 }
516 if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
517 strftime(acexpire, sizeof acexpire, "%c", tptr);
518 if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
519 strftime(pwexpire, sizeof pwexpire, "%c", tptr);
520 printf("Login Name: %-15s #%-12ju Group: %-15s #%ju\n"
521 " Full Name: %s\n"
522 " Home: %-26.26s Class: %s\n"
523 " Shell: %-26.26s Office: %s\n"
524 "Work Phone: %-26.26s Home Phone: %s\n"
525 "Acc Expire: %-26.26s Pwd Expire: %s\n",
526 pwd->pw_name, (uintmax_t)pwd->pw_uid,
527 grp ? grp->gr_name : "(invalid)", (uintmax_t)pwd->pw_gid,
528 uname, pwd->pw_dir, pwd->pw_class,
529 pwd->pw_shell, office, wphone, hphone,
530 acexpire, pwexpire);
531 SETGRENT();
532 j = 0;
533 while ((grp=GETGRENT()) != NULL) {
534 int i = 0;
535 if (grp->gr_mem != NULL) {
536 while (grp->gr_mem[i] != NULL) {
537 if (strcmp(grp->gr_mem[i], pwd->pw_name)==0) {
538 printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
539 break;
540 }
541 ++i;
542 }
543 }
544 }
545 ENDGRENT();
546 printf("%s", j ? "\n" : "");
547 return (EXIT_SUCCESS);
548 }
549
550 char *
551 pw_checkname(char *name, int gecos)
552 {
553 char showch[8];
554 const char *badchars, *ch, *showtype;
555 int reject;
556
557 ch = name;
558 reject = 0;
559 if (gecos) {
560 /* See if the name is valid as a gecos (comment) field. */
561 badchars = ":!@";
562 showtype = "gecos field";
563 } else {
564 /* See if the name is valid as a userid or group. */
565 badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
566 showtype = "userid/group name";
567 /* Userids and groups can not have a leading '-'. */
568 if (*ch == '-')
569 reject = 1;
570 }
571 if (!reject) {
572 while (*ch) {
573 if (strchr(badchars, *ch) != NULL || *ch < ' ' ||
574 *ch == 127) {
575 reject = 1;
576 break;
577 }
578 /* 8-bit characters are only allowed in GECOS fields */
579 if (!gecos && (*ch & 0x80)) {
580 reject = 1;
581 break;
582 }
583 ch++;
584 }
585 }
586 /*
587 * A `$' is allowed as the final character for userids and groups,
588 * mainly for the benefit of samba.
589 */
590 if (reject && !gecos) {
591 if (*ch == '$' && *(ch + 1) == '\0') {
592 reject = 0;
593 ch++;
594 }
595 }
596 if (reject) {
597 snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
598 ? "`%c'" : "0x%02x", *ch);
599 errx(EX_DATAERR, "invalid character %s at position %td in %s",
600 showch, (ch - name), showtype);
601 }
602 if (!gecos && (ch - name) > LOGNAMESIZE)
603 errx(EX_USAGE, "name too long `%s' (max is %d)", name,
604 LOGNAMESIZE);
605
606 return (name);
607 }
608
609 static void
610 rmat(uid_t uid)
611 {
612 DIR *d = opendir("/var/at/jobs");
613
614 if (d != NULL) {
615 struct dirent *e;
616
617 while ((e = readdir(d)) != NULL) {
618 struct stat st;
619
620 if (strncmp(e->d_name, ".lock", 5) != 0 &&
621 stat(e->d_name, &st) == 0 &&
622 !S_ISDIR(st.st_mode) &&
623 st.st_uid == uid) {
624 char tmp[MAXPATHLEN];
625
626 snprintf(tmp, sizeof(tmp), "/usr/bin/atrm %s",
627 e->d_name);
628 system(tmp);
629 }
630 }
631 closedir(d);
632 }
633 }
634
635 static void
636 rmopie(char const * name)
637 {
638 char tmp[1014];
639 FILE *fp;
640 int fd;
641 size_t len;
642 off_t atofs = 0;
643
644 if ((fd = openat(conf.rootfd, "etc/opiekeys", O_RDWR)) == -1)
645 return;
646
647 fp = fdopen(fd, "r+");
648 len = strlen(name);
649
650 while (fgets(tmp, sizeof(tmp), fp) != NULL) {
651 if (strncmp(name, tmp, len) == 0 && tmp[len]==' ') {
652 /* Comment username out */
653 if (fseek(fp, atofs, SEEK_SET) == 0)
654 fwrite("#", 1, 1, fp);
655 break;
656 }
657 atofs = ftell(fp);
658 }
659 /*
660 * If we got an error of any sort, don't update!
661 */
662 fclose(fp);
663 }
664
665 int
666 pw_user_next(int argc, char **argv, char *name __unused)
667 {
668 struct userconf *cnf = NULL;
669 const char *cfg = NULL;
670 int ch;
671 bool quiet = false;
672 uid_t next;
673
674 while ((ch = getopt(argc, argv, "Cq")) != -1) {
675 switch (ch) {
676 case 'C':
677 cfg = optarg;
678 break;
679 case 'q':
680 quiet;
681 break;
682 }
683 }
684
685 if (quiet)
686 freopen(_PATH_DEVNULL, "w", stderr);
687
688 cnf = get_userconfig(cfg);
689
690 next = pw_uidpolicy(cnf, -1);
691
692 printf("%ju:", (uintmax_t)next);
693 pw_groupnext(cnf, quiet);
694
695 return (EXIT_SUCCESS);
696 }
697
698 int
699 pw_user_show(int argc, char **argv, char *arg1)
700 {
701 struct passwd *pwd = NULL;
702 char *name = NULL;
703 uid_t id = -1;
704 int ch;
705 bool all = false;
706 bool pretty = false;
707 bool force = false;
708 bool v7 = false;
709 bool quiet = false;
710
711 if (arg1 != NULL) {
712 if (strspn(arg1, "0123456789") == strlen(arg1))
713 id = pw_checkid(arg1, UID_MAX);
714 else
715 name = arg1;
716 }
717
718 while ((ch = getopt(argc, argv, "C:qn:u:FPa7")) != -1) {
719 switch (ch) {
720 case 'C':
721 /* ignore compatibility */
722 break;
723 case 'q':
724 quiet = true;
725 break;
726 case 'n':
727 name = optarg;
728 break;
729 case 'u':
730 id = pw_checkid(optarg, UID_MAX);
731 break;
732 case 'F':
733 force = true;
734 break;
735 case 'P':
736 pretty = true;
737 break;
738 case 'a':
739 all = true;
740 break;
741 case 7:
742 v7 = true;
743 break;
744 }
745 }
746
747 if (quiet)
748 freopen(_PATH_DEVNULL, "w", stderr);
749
750 if (all) {
751 SETPWENT();
752 while ((pwd = GETPWENT()) != NULL)
753 print_user(pwd, pretty, v7);
754 ENDPWENT();
755 return (EXIT_SUCCESS);
756 }
757
758 if (id < 0 && name == NULL)
759 errx(EX_DATAERR, "username or id required");
760
761 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
762 if (pwd == NULL) {
763 if (force) {
764 pwd = &fakeuser;
765 } else {
766 if (name == NULL)
767 errx(EX_NOUSER, "no such uid `%ju'",
768 (uintmax_t) id);
769 errx(EX_NOUSER, "no such user `%s'", name);
770 }
771 }
772
773 return (print_user(pwd, pretty, v7));
774 }
775
776 int
777 pw_user_del(int argc, char **argv, char *arg1)
778 {
779 struct userconf *cnf = NULL;
780 struct passwd *pwd = NULL;
781 struct group *gr, *grp;
782 char *name = NULL;
783 char grname[MAXLOGNAME];
784 char *nispasswd = NULL;
785 char file[MAXPATHLEN];
786 char home[MAXPATHLEN];
787 const char *cfg = NULL;
788 struct stat st;
789 uid_t id;
790 int ch, rc;
791 bool nis = false;
792 bool deletehome = false;
793 bool quiet = false;
794
795 if (arg1 != NULL) {
796 if (strspn(arg1, "0123456789") == strlen(arg1))
797 id = pw_checkid(arg1, UID_MAX);
798 else
799 name = arg1;
800 }
801
802 while ((ch = getopt(argc, argv, "C:qn:u:rYy:")) != -1) {
803 switch (ch) {
804 case 'C':
805 cfg = optarg;
806 break;
807 case 'q':
808 quiet = true;
809 break;
810 case 'n':
811 name = optarg;
812 break;
813 case 'u':
814 id = pw_checkid(optarg, UID_MAX);
815 break;
816 case 'r':
817 deletehome = true;
818 break;
819 case 'y':
820 nispasswd = optarg;
821 break;
822 case 'Y':
823 nis = true;
824 break;
825 }
826 }
827
828 if (quiet)
829 freopen(_PATH_DEVNULL, "w", stderr);
830
831 if (id < 0 && name == NULL)
832 errx(EX_DATAERR, "username or id required");
833
834 cnf = get_userconfig(cfg);
835
836 if (nispasswd == NULL)
837 nispasswd = cnf->nispasswd;
838
839 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
840 if (pwd == NULL) {
841 if (name == NULL)
842 errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id);
843 errx(EX_NOUSER, "no such user `%s'", name);
844 }
845
846 if (PWF._altdir == PWF_REGULAR &&
847 ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
848 if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
849 if (!nis && nispasswd && *nispasswd != '/')
850 errx(EX_NOUSER, "Cannot remove NIS user `%s'",
851 name);
852 } else {
853 errx(EX_NOUSER, "Cannot remove non local user `%s'",
854 name);
855 }
856 }
857
858 id = pwd->pw_uid;
859 if (name == NULL)
860 name = pwd->pw_name;
861
862 if (strcmp(pwd->pw_name, "root") == 0)
863 errx(EX_DATAERR, "cannot remove user 'root'");
864
865 /* Remove opie record from /etc/opiekeys */
866 if (PWALTDIR() != PWF_ALT)
867 rmopie(pwd->pw_name);
868
869 if (!PWALTDIR()) {
870 /* Remove crontabs */
871 snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name);
872 if (access(file, F_OK) == 0) {
873 snprintf(file, sizeof(file), "crontab -u %s -r",
874 pwd->pw_name);
875 system(file);
876 }
877 }
878
879 /*
880 * Save these for later, since contents of pwd may be
881 * invalidated by deletion
882 */
883 snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
884 strlcpy(home, pwd->pw_dir, sizeof(home));
885 gr = GETGRGID(pwd->pw_gid);
886 if (gr != NULL)
887 strlcpy(grname, gr->gr_name, LOGNAMESIZE);
888 else
889 grname[0] = '\0';
890
891 rc = delpwent(pwd);
892 if (rc == -1)
893 err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
894 else if (rc != 0)
895 err(EX_IOERR, "passwd update");
896
897 if (nis && nispasswd && *nispasswd=='/') {
898 rc = delnispwent(nispasswd, name);
899 if (rc == -1)
900 warnx("WARNING: user '%s' does not exist in NIS passwd",
901 pwd->pw_name);
902 else if (rc != 0)
903 warn("WARNING: NIS passwd update");
904 }
905
906 grp = GETGRNAM(name);
907 if (grp != NULL &&
908 (grp->gr_mem == NULL || *grp->gr_mem == NULL) &&
909 strcmp(name, grname) == 0)
910 delgrent(GETGRNAM(name));
911 SETGRENT();
912 while ((grp = GETGRENT()) != NULL) {
913 int i, j;
914 char group[MAXLOGNAME];
915 if (grp->gr_mem == NULL)
916 continue;
917
918 for (i = 0; grp->gr_mem[i] != NULL; i++) {
919 if (strcmp(grp->gr_mem[i], name) != 0)
920 continue;
921
922 for (j = i; grp->gr_mem[j] != NULL; j++)
923 grp->gr_mem[j] = grp->gr_mem[j+1];
924 strlcpy(group, grp->gr_name, MAXLOGNAME);
925 chggrent(group, grp);
926 }
927 }
928 ENDGRENT();
929
930 pw_log(cnf, M_DELETE, W_USER, "%s(%ju) account removed", name,
931 (uintmax_t)id);
932
933 /* Remove mail file */
934 if (PWALTDIR() != PWF_ALT)
935 unlinkat(conf.rootfd, file + 1, 0);
936
937 /* Remove at jobs */
938 if (!PWALTDIR() && getpwuid(id) == NULL)
939 rmat(id);
940
941 /* Remove home directory and contents */
942 if (PWALTDIR() != PWF_ALT && deletehome && *home == '/' &&
943 GETPWUID(id) == NULL &&
944 fstatat(conf.rootfd, home + 1, &st, 0) != -1) {
945 rm_r(conf.rootfd, home, id);
946 pw_log(cnf, M_DELETE, W_USER, "%s(%ju) home '%s' %s"
947 "removed", name, (uintmax_t)id, home,
948 fstatat(conf.rootfd, home + 1, &st, 0) == -1 ? "" : "not "
949 "completely ");
950 }
951
952 return (EXIT_SUCCESS);
953 }
954
955 int
956 pw_user_lock(int argc, char **argv, char *arg1)
957 {
958 int ch;
959
960 while ((ch = getopt(argc, argv, "Cq")) != -1) {
961 switch (ch) {
962 case 'C':
963 case 'q':
964 /* compatibility */
965 break;
966 }
967 }
968
969 return (pw_userlock(arg1, M_LOCK));
970 }
971
972 int
973 pw_user_unlock(int argc, char **argv, char *arg1)
974 {
975 int ch;
976
977 while ((ch = getopt(argc, argv, "Cq")) != -1) {
978 switch (ch) {
979 case 'C':
980 case 'q':
981 /* compatibility */
982 break;
983 }
984 }
985
986 return (pw_userlock(arg1, M_UNLOCK));
987 }
988
989 static struct group *
990 group_from_name_or_id(char *name)
991 {
992 const char *errstr = NULL;
993 struct group *grp;
994 uintmax_t id;
995
996 if ((grp = GETGRNAM(name)) == NULL) {
997 id = strtounum(name, 0, GID_MAX, &errstr);
998 if (errstr)
999 errx(EX_NOUSER, "group `%s' does not exist", name);
1000 grp = GETGRGID(id);
1001 if (grp == NULL)
1002 errx(EX_NOUSER, "group `%s' does not exist", name);
1003 }
1004
1005 return (grp);
1006 }
1007
1008 static void
1009 split_groups(StringList **groups, char *groupsstr)
1010 {
1011 struct group *grp;
1012 char *p;
1013 char tok[] = ", \t";
1014
1015 for (p = strtok(groupsstr, tok); p != NULL; p = strtok(NULL, tok)) {
1016 grp = group_from_name_or_id(p);
1017 if (*groups == NULL)
1018 *groups = sl_init();
1019 sl_add(*groups, newstr(grp->gr_name));
1020 }
1021 }
1022
1023 static void
1024 validate_grname(struct userconf *cnf, char *group)
1025 {
1026 struct group *grp;
1027
1028 if (group == NULL || *group == '\0') {
1029 cnf->default_group = "";
1030 return;
1031 }
1032 grp = group_from_name_or_id(group);
1033 cnf->default_group = newstr(grp->gr_name);
1034 }
1035
1036 static mode_t
1037 validate_mode(char *mode)
1038 {
1039 mode_t m;
1040 void *set;
1041
1042 if ((set = setmode(mode)) == NULL)
1043 errx(EX_DATAERR, "invalid directory creation mode '%s'", mode);
1044
1045 m = getmode(set, _DEF_DIRMODE);
1046 free(set);
1047 return (m);
1048 }
1049
1050 static void
1051 mix_config(struct userconf *cmdcnf, struct userconf *cfg)
1052 {
1053
1054 if (cmdcnf->default_password == 0)
1055 cmdcnf->default_password = cfg->default_password;
1056 if (cmdcnf->reuse_uids == 0)
1057 cmdcnf->reuse_uids = cfg->reuse_uids;
1058 if (cmdcnf->reuse_gids == 0)
1059 cmdcnf->reuse_gids = cfg->reuse_gids;
1060 if (cmdcnf->nispasswd == NULL)
1061 cmdcnf->nispasswd = cfg->nispasswd;
1062 if (cmdcnf->dotdir == NULL)
1063 cmdcnf->dotdir = cfg->dotdir;
1064 if (cmdcnf->newmail == NULL)
1065 cmdcnf->newmail = cfg->newmail;
1066 if (cmdcnf->logfile == NULL)
1067 cmdcnf->logfile = cfg->logfile;
1068 if (cmdcnf->home == NULL)
1069 cmdcnf->home = cfg->home;
1070 if (cmdcnf->homemode == 0)
1071 cmdcnf->homemode = cfg->homemode;
1072 if (cmdcnf->shelldir == NULL)
1073 cmdcnf->shelldir = cfg->shelldir;
1074 if (cmdcnf->shells == NULL)
1075 cmdcnf->shells = cfg->shells;
1076 if (cmdcnf->shell_default == NULL)
1077 cmdcnf->shell_default = cfg->shell_default;
1078 if (cmdcnf->default_group == NULL)
1079 cmdcnf->default_group = cfg->default_group;
1080 if (cmdcnf->groups == NULL)
1081 cmdcnf->groups = cfg->groups;
1082 if (cmdcnf->default_class == NULL)
1083 cmdcnf->default_class = cfg->default_class;
1084 if (cmdcnf->min_uid == 0)
1085 cmdcnf->min_uid = cfg->min_uid;
1086 if (cmdcnf->max_uid == 0)
1087 cmdcnf->max_uid = cfg->max_uid;
1088 if (cmdcnf->min_gid == 0)
1089 cmdcnf->min_gid = cfg->min_gid;
1090 if (cmdcnf->max_gid == 0)
1091 cmdcnf->max_gid = cfg->max_gid;
1092 if (cmdcnf->expire_days == 0)
1093 cmdcnf->expire_days = cfg->expire_days;
1094 if (cmdcnf->password_days == 0)
1095 cmdcnf->password_days = cfg->password_days;
1096 }
1097
1098 int
1099 pw_user_add(int argc, char **argv, char *arg1)
1100 {
1101 struct userconf *cnf, *cmdcnf;
1102 struct passwd *pwd;
1103 struct group *grp;
1104 struct stat st;
1105 char args[] = "C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y";
1106 char line[_PASSWORD_LEN+1], path[MAXPATHLEN];
1107 char *gecos, *homedir, *skel, *walk, *userid, *groupid, *grname;
1108 char *default_passwd, *name, *p;
1109 const char *cfg;
1110 login_cap_t *lc;
1111 FILE *pfp, *fp;
1112 intmax_t id = -1;
1113 time_t now;
1114 int rc, ch, fd = -1;
1115 size_t i;
1116 bool dryrun, nis, pretty, quiet, createhome, precrypted, genconf;
1117
1118 dryrun = nis = pretty = quiet = createhome = precrypted = false;
1119 genconf = false;
1120 gecos = homedir = skel = userid = groupid = default_passwd = NULL;
1121 grname = name = NULL;
1122
1123 if ((cmdcnf = calloc(1, sizeof(struct userconf))) == NULL)
1124 err(EXIT_FAILURE, "calloc()");
1125
1126 if (arg1 != NULL) {
1127 if (strspn(arg1, "0123456789") == strlen(arg1))
1128 id = pw_checkid(arg1, UID_MAX);
1129 else
1130 name = arg1;
1131 }
1132
1133 while ((ch = getopt(argc, argv, args)) != -1) {
1134 switch (ch) {
1135 case 'C':
1136 cfg = optarg;
1137 break;
1138 case 'q':
1139 quiet = true;
1140 break;
1141 case 'n':
1142 name = optarg;
1143 break;
1144 case 'u':
1145 userid = optarg;
1146 break;
1147 case 'c':
1148 gecos = pw_checkname(optarg, 1);
1149 break;
1150 case 'd':
1151 homedir = optarg;
1152 break;
1153 case 'e':
1154 now = time(NULL);
1155 cmdcnf->expire_days = parse_date(now, optarg);
1156 break;
1157 case 'p':
1158 now = time(NULL);
1159 cmdcnf->password_days = parse_date(now, optarg);
1160 break;
1161 case 'g':
1162 validate_grname(cmdcnf, optarg);
1163 grname = optarg;
1164 break;
1165 case 'G':
1166 split_groups(&cmdcnf->groups, optarg);
1167 break;
1168 case 'm':
1169 createhome = true;
1170 break;
1171 case 'M':
1172 cmdcnf->homemode = validate_mode(optarg);
1173 break;
1174 case 'k':
1175 walk = skel = optarg;
1176 if (*walk == '/')
1177 walk++;
1178 if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1179 errx(EX_OSFILE, "skeleton `%s' does not "
1180 "exists", skel);
1181 if (!S_ISDIR(st.st_mode))
1182 errx(EX_OSFILE, "skeleton `%s' is not a "
1183 "directory", skel);
1184 cmdcnf->dotdir = skel;
1185 break;
1186 case 's':
1187 cmdcnf->shell_default = optarg;
1188 break;
1189 case 'o':
1190 conf.checkduplicate = false;
1191 break;
1192 case 'L':
1193 cmdcnf->default_class = pw_checkname(optarg, 0);
1194 break;
1195 case 'i':
1196 groupid = optarg;
1197 break;
1198 case 'w':
1199 default_passwd = optarg;
1200 break;
1201 case 'H':
1202 if (fd != -1)
1203 errx(EX_USAGE, "'-h' and '-H' are mutually "
1204 "exclusive options");
1205 fd = pw_checkfd(optarg);
1206 precrypted = true;
1207 if (fd == '-')
1208 errx(EX_USAGE, "-H expects a file descriptor");
1209 break;
1210 case 'h':
1211 if (fd != -1)
1212 errx(EX_USAGE, "'-h' and '-H' are mutually "
1213 "exclusive options");
1214 fd = pw_checkfd(optarg);
1215 break;
1216 case 'D':
1217 genconf = true;
1218 break;
1219 case 'b':
1220 cmdcnf->home = optarg;
1221 break;
1222 case 'N':
1223 dryrun = true;
1224 break;
1225 case 'P':
1226 pretty = true;
1227 break;
1228 case 'y':
1229 cmdcnf->nispasswd = optarg;
1230 break;
1231 case 'Y':
1232 nis = true;
1233 break;
1234 }
1235 }
1236
1237 if (geteuid() != 0 && ! dryrun)
1238 errx(EX_NOPERM, "you must be root");
1239
1240 if (quiet)
1241 freopen(_PATH_DEVNULL, "w", stderr);
1242
1243 cnf = get_userconfig(cfg);
1244
1245 mix_config(cmdcnf, cnf);
1246 if (default_passwd)
1247 cmdcnf->default_password = boolean_val(default_passwd,
1248 cnf->default_password);
1249 if (genconf) {
1250 if (name != NULL)
1251 errx(EX_DATAERR, "can't combine `-D' with `-n name'");
1252 if (userid != NULL) {
1253 if ((p = strtok(userid, ", \t")) != NULL)
1254 cmdcnf->min_uid = pw_checkid(p, UID_MAX);
1255 if (cmdcnf->min_uid == 0)
1256 cmdcnf->min_uid = 1000;
1257 if ((p = strtok(NULL, " ,\t")) != NULL)
1258 cmdcnf->max_uid = pw_checkid(p, UID_MAX);
1259 if (cmdcnf->max_uid == 0)
1260 cmdcnf->max_uid = 32000;
1261 }
1262 if (groupid != NULL) {
1263 if ((p = strtok(groupid, ", \t")) != NULL)
1264 cmdcnf->min_gid = pw_checkid(p, GID_MAX);
1265 if (cmdcnf->min_gid == 0)
1266 cmdcnf->min_gid = 1000;
1267 if ((p = strtok(NULL, " ,\t")) != NULL)
1268 cmdcnf->max_gid = pw_checkid(p, GID_MAX);
1269 if (cmdcnf->max_gid == 0)
1270 cmdcnf->max_gid = 32000;
1271 }
1272 if (write_userconfig(cmdcnf, cfg))
1273 return (EXIT_SUCCESS);
1274 err(EX_IOERR, "config update");
1275 }
1276
1277 if (userid)
1278 id = pw_checkid(userid, UID_MAX);
1279 if (id < 0 && name == NULL)
1280 errx(EX_DATAERR, "user name or id required");
1281
1282 if (name == NULL)
1283 errx(EX_DATAERR, "login name required");
1284
1285 if (GETPWNAM(name) != NULL)
1286 errx(EX_DATAERR, "login name `%s' already exists", name);
1287
1288 pwd = &fakeuser;
1289 pwd->pw_name = name;
1290 pwd->pw_class = cmdcnf->default_class ? cmdcnf->default_class : "";
1291 pwd->pw_uid = pw_uidpolicy(cmdcnf, id);
1292 pwd->pw_gid = pw_gidpolicy(cnf, grname, pwd->pw_name,
1293 (gid_t) pwd->pw_uid, dryrun);
1294 pwd->pw_change = cmdcnf->password_days;
1295 pwd->pw_expire = cmdcnf->expire_days;
1296 pwd->pw_dir = pw_homepolicy(cmdcnf, homedir, pwd->pw_name);
1297 pwd->pw_shell = pw_shellpolicy(cmdcnf);
1298 lc = login_getpwclass(pwd);
1299 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1300 warn("setting crypt(3) format");
1301 login_close(lc);
1302 pwd->pw_passwd = pw_password(cmdcnf, pwd->pw_name, dryrun);
1303 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1304 warnx("WARNING: new account `%s' has a uid of 0 "
1305 "(superuser access!)", pwd->pw_name);
1306 if (gecos)
1307 pwd->pw_gecos = gecos;
1308
1309 if (fd != -1)
1310 pw_set_passwd(pwd, fd, precrypted, false);
1311
1312 if (dryrun)
1313 return (print_user(pwd, pretty, false));
1314
1315 if ((rc = addpwent(pwd)) != 0) {
1316 if (rc == -1)
1317 errx(EX_IOERR, "user '%s' already exists",
1318 pwd->pw_name);
1319 else if (rc != 0)
1320 err(EX_IOERR, "passwd file update");
1321 }
1322 if (nis && cmdcnf->nispasswd && *cmdcnf->nispasswd == '/') {
1323 printf("%s\n", cmdcnf->nispasswd);
1324 rc = addnispwent(cmdcnf->nispasswd, pwd);
1325 if (rc == -1)
1326 warnx("User '%s' already exists in NIS passwd",
1327 pwd->pw_name);
1328 else if (rc != 0)
1329 warn("NIS passwd update");
1330 /* NOTE: we treat NIS-only update errors as non-fatal */
1331 }
1332
1333 if (cmdcnf->groups != NULL) {
1334 for (i = 0; i < cmdcnf->groups->sl_cur; i++) {
1335 grp = GETGRNAM(cmdcnf->groups->sl_str[i]);
1336 grp = gr_add(grp, pwd->pw_name);
1337 /*
1338 * grp can only be NULL in 2 cases:
1339 * - the new member is already a member
1340 * - a problem with memory occurs
1341 * in both cases we want to skip now.
1342 */
1343 if (grp == NULL)
1344 continue;
1345 chggrent(grp->gr_name, grp);
1346 free(grp);
1347 }
1348 }
1349
1350 pwd = GETPWNAM(name);
1351 if (pwd == NULL)
1352 errx(EX_NOUSER, "user '%s' disappeared during update", name);
1353
1354 grp = GETGRGID(pwd->pw_gid);
1355 pw_log(cnf, M_ADD, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1356 pwd->pw_name, (uintmax_t)pwd->pw_uid,
1357 grp ? grp->gr_name : "unknown",
1358 (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1359 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1360
1361 /*
1362 * let's touch and chown the user's mail file. This is not
1363 * strictly necessary under BSD with a 0755 maildir but it also
1364 * doesn't hurt anything to create the empty mailfile
1365 */
1366 if (PWALTDIR() != PWF_ALT) {
1367 snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR,
1368 pwd->pw_name);
1369 /* Preserve contents & mtime */
1370 close(openat(conf.rootfd, path +1, O_RDWR | O_CREAT, 0600));
1371 fchownat(conf.rootfd, path + 1, pwd->pw_uid, pwd->pw_gid,
1372 AT_SYMLINK_NOFOLLOW);
1373 }
1374
1375 /*
1376 * Let's create and populate the user's home directory. Note
1377 * that this also `works' for editing users if -m is used, but
1378 * existing files will *not* be overwritten.
1379 */
1380 if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir &&
1381 *pwd->pw_dir == '/' && pwd->pw_dir[1])
1382 create_and_populate_homedir(cmdcnf, pwd, cmdcnf->dotdir,
1383 cmdcnf->homemode, false);
1384
1385 if (!PWALTDIR() && cmdcnf->newmail && *cmdcnf->newmail &&
1386 (fp = fopen(cnf->newmail, "r")) != NULL) {
1387 if ((pfp = popen(_PATH_SENDMAIL " -t", "w")) == NULL)
1388 warn("sendmail");
1389 else {
1390 fprintf(pfp, "From: root\n" "To: %s\n"
1391 "Subject: Welcome!\n\n", pwd->pw_name);
1392 while (fgets(line, sizeof(line), fp) != NULL) {
1393 /* Do substitutions? */
1394 fputs(line, pfp);
1395 }
1396 pclose(pfp);
1397 pw_log(cnf, M_ADD, W_USER, "%s(%ju) new user mail sent",
1398 pwd->pw_name, (uintmax_t)pwd->pw_uid);
1399 }
1400 fclose(fp);
1401 }
1402
1403 if (nis && nis_update() == 0)
1404 pw_log(cnf, M_ADD, W_USER, "NIS maps updated");
1405
1406 return (EXIT_SUCCESS);
1407 }
1408
1409 int
1410 pw_user_mod(int argc, char **argv, char *arg1)
1411 {
1412 struct userconf *cnf;
1413 struct passwd *pwd;
1414 struct group *grp;
1415 StringList *groups = NULL;
1416 char args[] = "C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:NPYy:";
1417 const char *cfg;
1418 char *gecos, *homedir, *grname, *name, *newname, *walk, *skel, *shell;
1419 char *passwd, *class, *nispasswd;
1420 login_cap_t *lc;
1421 struct stat st;
1422 intmax_t id = -1;
1423 int ch, fd = -1;
1424 size_t i, j;
1425 bool quiet, createhome, pretty, dryrun, nis, edited, docreatehome;
1426 mode_t homemode = 0;
1427 time_t expire_days, password_days, now, precrypted;
1428
1429 expire_days = password_days = -1;
1430 gecos = homedir = grname = name = newname = skel = shell =NULL;
1431 passwd = NULL;
1432 class = nispasswd = NULL;
1433 quiet = createhome = pretty = dryrun = nis = precrypted = false;
1434 edited = docreatehome = false;
1435
1436 if (arg1 != NULL) {
1437 if (strspn(arg1, "0123456789") == strlen(arg1))
1438 id = pw_checkid(arg1, UID_MAX);
1439 else
1440 name = arg1;
1441 }
1442
1443 while ((ch = getopt(argc, argv, args)) != -1) {
1444 switch (ch) {
1445 case 'C':
1446 cfg = optarg;
1447 break;
1448 case 'q':
1449 quiet = true;
1450 break;
1451 case 'n':
1452 name = optarg;
1453 break;
1454 case 'u':
1455 id = pw_checkid(optarg, UID_MAX);
1456 break;
1457 case 'c':
1458 gecos = pw_checkname(optarg, 1);
1459 break;
1460 case 'd':
1461 homedir = optarg;
1462 break;
1463 case 'e':
1464 now = time(NULL);
1465 expire_days = parse_date(now, optarg);
1466 break;
1467 case 'p':
1468 now = time(NULL);
1469 password_days = parse_date(now, optarg);
1470 break;
1471 case 'g':
1472 group_from_name_or_id(optarg);
1473 grname = optarg;
1474 break;
1475 case 'G':
1476 split_groups(&groups, optarg);
1477 break;
1478 case 'm':
1479 createhome = true;
1480 break;
1481 case 'M':
1482 homemode = validate_mode(optarg);
1483 break;
1484 case 'l':
1485 newname = optarg;
1486 break;
1487 case 'k':
1488 walk = skel = optarg;
1489 if (*walk == '/')
1490 walk++;
1491 if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1492 errx(EX_OSFILE, "skeleton `%s' does not "
1493 "exists", skel);
1494 if (!S_ISDIR(st.st_mode))
1495 errx(EX_OSFILE, "skeleton `%s' is not a "
1496 "directory", skel);
1497 break;
1498 case 's':
1499 shell = optarg;
1500 break;
1501 case 'w':
1502 passwd = optarg;
1503 break;
1504 case 'L':
1505 class = pw_checkname(optarg, 0);
1506 break;
1507 case 'H':
1508 if (fd != -1)
1509 errx(EX_USAGE, "'-h' and '-H' are mutually "
1510 "exclusive options");
1511 fd = pw_checkfd(optarg);
1512 precrypted = true;
1513 if (fd == '-')
1514 errx(EX_USAGE, "-H expects a file descriptor");
1515 break;
1516 case 'h':
1517 if (fd != -1)
1518 errx(EX_USAGE, "'-h' and '-H' are mutually "
1519 "exclusive options");
1520 fd = pw_checkfd(optarg);
1521 break;
1522 case 'N':
1523 dryrun = true;
1524 break;
1525 case 'P':
1526 pretty = true;
1527 break;
1528 case 'y':
1529 nispasswd = optarg;
1530 break;
1531 case 'Y':
1532 nis = true;
1533 break;
1534 }
1535 }
1536
1537 if (geteuid() != 0 && ! dryrun)
1538 errx(EX_NOPERM, "you must be root");
1539
1540 if (quiet)
1541 freopen(_PATH_DEVNULL, "w", stderr);
1542
1543 cnf = get_userconfig(cfg);
1544
1545 if (id < 0 && name == NULL)
1546 errx(EX_DATAERR, "username or id required");
1547
1548 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
1549 if (pwd == NULL) {
1550 if (name == NULL)
1551 errx(EX_NOUSER, "no such uid `%ju'",
1552 (uintmax_t) id);
1553 errx(EX_NOUSER, "no such user `%s'", name);
1554 }
1555
1556 if (name == NULL)
1557 name = pwd->pw_name;
1558
1559 if (nis && nispasswd == NULL)
1560 nispasswd = cnf->nispasswd;
1561
1562 if (PWF._altdir == PWF_REGULAR &&
1563 ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
1564 if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
1565 if (!nis && nispasswd && *nispasswd != '/')
1566 errx(EX_NOUSER, "Cannot modify NIS user `%s'",
1567 name);
1568 } else {
1569 errx(EX_NOUSER, "Cannot modify non local user `%s'",
1570 name);
1571 }
1572 }
1573
1574 if (newname) {
1575 if (strcmp(pwd->pw_name, "root") == 0)
1576 errx(EX_DATAERR, "can't rename `root' account");
1577 if (strcmp(pwd->pw_name, newname) != 0) {
1578 pwd->pw_name = pw_checkname(newname, 0);
1579 edited = true;
1580 }
1581 }
1582
1583 if (id > 0 && pwd->pw_uid != id) {
1584 pwd->pw_uid = id;
1585 edited = true;
1586 if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
1587 errx(EX_DATAERR, "can't change uid of `root' account");
1588 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1589 warnx("WARNING: account `%s' will have a uid of 0 "
1590 "(superuser access!)", pwd->pw_name);
1591 }
1592
1593 if (grname && pwd->pw_uid != 0) {
1594 grp = GETGRNAM(grname);
1595 if (grp == NULL)
1596 grp = GETGRGID(pw_checkid(grname, GID_MAX));
1597 if (grp->gr_gid != pwd->pw_gid) {
1598 pwd->pw_gid = grp->gr_gid;
1599 edited = true;
1600 }
1601 }
1602
1603 if (password_days >= 0 && pwd->pw_change != password_days) {
1604 pwd->pw_change = password_days;
1605 edited = true;
1606 }
1607
1608 if (expire_days >= 0 && pwd->pw_expire != expire_days) {
1609 pwd->pw_expire = expire_days;
1610 edited = true;
1611 }
1612
1613 if (shell) {
1614 shell = shell_path(cnf->shelldir, cnf->shells, shell);
1615 if (shell == NULL)
1616 shell = "";
1617 if (strcmp(shell, pwd->pw_shell) != 0) {
1618 pwd->pw_shell = shell;
1619 edited = true;
1620 }
1621 }
1622
1623 if (class && strcmp(pwd->pw_class, class) != 0) {
1624 pwd->pw_class = class;
1625 edited = true;
1626 }
1627
1628 if (homedir && strcmp(pwd->pw_dir, homedir) != 0) {
1629 pwd->pw_dir = homedir;
1630 if (fstatat(conf.rootfd, pwd->pw_dir, &st, 0) == -1) {
1631 if (!createhome)
1632 warnx("WARNING: home `%s' does not exist",
1633 pwd->pw_dir);
1634 else
1635 docreatehome = true;
1636 } else if (!S_ISDIR(st.st_mode)) {
1637 warnx("WARNING: home `%s' is not a directory",
1638 pwd->pw_dir);
1639 }
1640 }
1641
1642 if (passwd && conf.fd == -1) {
1643 lc = login_getpwclass(pwd);
1644 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1645 warn("setting crypt(3) format");
1646 login_close(lc);
1647 pwd->pw_passwd = pw_password(cnf, pwd->pw_name, dryrun);
1648 edited = true;
1649 }
1650
1651 if (gecos && strcmp(pwd->pw_gecos, gecos) != 0) {
1652 pwd->pw_gecos = gecos;
1653 edited = true;
1654 }
1655
1656 if (fd != -1)
1657 edited = pw_set_passwd(pwd, fd, precrypted, true);
1658
1659 if (dryrun)
1660 return (print_user(pwd, pretty, false));
1661
1662 if (edited) /* Only updated this if required */
1663 perform_chgpwent(name, pwd, nis ? nispasswd : NULL);
1664 /* Now perform the needed changes concern groups */
1665 if (groups != NULL) {
1666 /* Delete User from groups using old name */
1667 SETGRENT();
1668 while ((grp = GETGRENT()) != NULL) {
1669 if (grp->gr_mem == NULL)
1670 continue;
1671 for (i = 0; grp->gr_mem[i] != NULL; i++) {
1672 if (strcmp(grp->gr_mem[i] , name) != 0)
1673 continue;
1674 for (j = i; grp->gr_mem[j] != NULL ; j++)
1675 grp->gr_mem[j] = grp->gr_mem[j+1];
1676 chggrent(grp->gr_name, grp);
1677 break;
1678 }
1679 }
1680 ENDGRENT();
1681 /* Add the user to the needed groups */
1682 for (i = 0; i < groups->sl_cur; i++) {
1683 grp = GETGRNAM(groups->sl_str[i]);
1684 grp = gr_add(grp, pwd->pw_name);
1685 if (grp == NULL)
1686 continue;
1687 chggrent(grp->gr_name, grp);
1688 free(grp);
1689 }
1690 }
1691 /* In case of rename we need to walk over the different groups */
1692 if (newname) {
1693 SETGRENT();
1694 while ((grp = GETGRENT()) != NULL) {
1695 if (grp->gr_mem == NULL)
1696 continue;
1697 for (i = 0; grp->gr_mem[i] != NULL; i++) {
1698 if (strcmp(grp->gr_mem[i], name) != 0)
1699 continue;
1700 grp->gr_mem[i] = newname;
1701 chggrent(grp->gr_name, grp);
1702 break;
1703 }
1704 }
1705 }
1706
1707 /* go get a current version of pwd */
1708 if (newname)
1709 name = newname;
1710 pwd = GETPWNAM(name);
1711 if (pwd == NULL)
1712 errx(EX_NOUSER, "user '%s' disappeared during update", name);
1713 grp = GETGRGID(pwd->pw_gid);
1714 pw_log(cnf, M_UPDATE, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1715 pwd->pw_name, (uintmax_t)pwd->pw_uid,
1716 grp ? grp->gr_name : "unknown",
1717 (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1718 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1719
1720 /*
1721 * Let's create and populate the user's home directory. Note
1722 * that this also `works' for editing users if -m is used, but
1723 * existing files will *not* be overwritten.
1724 */
1725 if (PWALTDIR() != PWF_ALT && docreatehome && pwd->pw_dir &&
1726 *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
1727 if (!skel)
1728 skel = cnf->dotdir;
1729 if (homemode == 0)
1730 homemode = cnf->homemode;
1731 create_and_populate_homedir(cnf, pwd, skel, homemode, true);
1732 }
1733
1734 if (nis && nis_update() == 0)
1735 pw_log(cnf, M_UPDATE, W_USER, "NIS maps updated");
1736
1737 return (EXIT_SUCCESS);
1738 }