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