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