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 /*
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 pid_t pid;
673
674 if (strncmp(e->d_name, ".lock", 5) != 0 &&
675 stat(e->d_name, &st) == 0 &&
676 !S_ISDIR(st.st_mode) &&
677 st.st_uid == uid) {
678 const char *argv[] = {
679 "/usr/sbin/atrm",
680 e->d_name,
681 NULL
682 };
683 if (posix_spawn(&pid, argv[0], NULL, NULL,
684 (char *const *) argv, environ)) {
685 warn("Failed to execute '%s %s'",
686 argv[0], argv[1]);
687 } else
688 (void) waitpid(pid, NULL, 0);
689 }
690 }
691 closedir(d);
692 }
693 }
694
695 int
pw_user_next(int argc,char ** argv,char * name __unused)696 pw_user_next(int argc, char **argv, char *name __unused)
697 {
698 struct userconf *cnf = NULL;
699 const char *cfg = NULL;
700 int ch;
701 bool quiet = false;
702 uid_t next;
703
704 while ((ch = getopt(argc, argv, "C:q")) != -1) {
705 switch (ch) {
706 case 'C':
707 cfg = optarg;
708 break;
709 case 'q':
710 quiet = true;
711 break;
712 default:
713 usage();
714 }
715 }
716 argc -= optind;
717 argv += optind;
718 if (argc > 0)
719 usage();
720
721 if (quiet)
722 freopen(_PATH_DEVNULL, "w", stderr);
723
724 cnf = get_userconfig(cfg);
725
726 next = pw_uidpolicy(cnf, -1);
727
728 printf("%ju:", (uintmax_t)next);
729 pw_groupnext(cnf, quiet);
730
731 return (EXIT_SUCCESS);
732 }
733
734 int
pw_user_show(int argc,char ** argv,char * arg1)735 pw_user_show(int argc, char **argv, char *arg1)
736 {
737 struct passwd *pwd = NULL;
738 char *name = NULL;
739 intmax_t id = -1;
740 int ch;
741 bool all = false;
742 bool pretty = false;
743 bool force = false;
744 bool v7 = false;
745 bool quiet = false;
746
747 if (arg1 != NULL) {
748 if (arg1[strspn(arg1, "0123456789")] == '\0')
749 id = pw_checkid(arg1, UID_MAX);
750 else
751 name = arg1;
752 }
753
754 while ((ch = getopt(argc, argv, "C:qn:u:FPa7")) != -1) {
755 switch (ch) {
756 case 'C':
757 /* ignore compatibility */
758 break;
759 case 'q':
760 quiet = true;
761 break;
762 case 'n':
763 name = optarg;
764 break;
765 case 'u':
766 id = pw_checkid(optarg, UID_MAX);
767 break;
768 case 'F':
769 force = true;
770 break;
771 case 'P':
772 pretty = true;
773 break;
774 case 'a':
775 all = true;
776 break;
777 case '7':
778 v7 = true;
779 break;
780 default:
781 usage();
782 }
783 }
784 argc -= optind;
785 argv += optind;
786 if (argc > 0)
787 usage();
788
789 if (quiet)
790 freopen(_PATH_DEVNULL, "w", stderr);
791
792 if (all) {
793 SETPWENT();
794 while ((pwd = GETPWENT()) != NULL)
795 print_user(pwd, pretty, v7);
796 ENDPWENT();
797 return (EXIT_SUCCESS);
798 }
799
800 if (id < 0 && name == NULL)
801 errx(EX_DATAERR, "username or id required");
802
803 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
804 if (pwd == NULL) {
805 if (force) {
806 pwd = &fakeuser;
807 } else {
808 if (name == NULL)
809 errx(EX_NOUSER, "no such uid `%ju'",
810 (uintmax_t) id);
811 errx(EX_NOUSER, "no such user `%s'", name);
812 }
813 }
814
815 return (print_user(pwd, pretty, v7));
816 }
817
818 int
pw_user_del(int argc,char ** argv,char * arg1)819 pw_user_del(int argc, char **argv, char *arg1)
820 {
821 struct userconf *cnf = NULL;
822 struct passwd *pwd = NULL;
823 struct group *gr, *grp;
824 char *name = NULL;
825 char grname[MAXLOGNAME];
826 char *nispasswd = NULL;
827 char file[MAXPATHLEN];
828 char home[MAXPATHLEN];
829 const char *cfg = NULL;
830 struct stat st;
831 intmax_t id = -1;
832 int ch, rc;
833 bool nis = false;
834 bool deletehome = false;
835 bool quiet = false;
836
837 if (arg1 != NULL) {
838 if (arg1[strspn(arg1, "0123456789")] == '\0')
839 id = pw_checkid(arg1, UID_MAX);
840 else
841 name = arg1;
842 }
843
844 while ((ch = getopt(argc, argv, "C:qn:u:rYy:")) != -1) {
845 switch (ch) {
846 case 'C':
847 cfg = optarg;
848 break;
849 case 'q':
850 quiet = true;
851 break;
852 case 'n':
853 name = optarg;
854 break;
855 case 'u':
856 id = pw_checkid(optarg, UID_MAX);
857 break;
858 case 'r':
859 deletehome = true;
860 break;
861 case 'y':
862 nispasswd = optarg;
863 break;
864 case 'Y':
865 nis = true;
866 break;
867 default:
868 usage();
869 }
870 }
871 argc -= optind;
872 argv += optind;
873 if (argc > 0)
874 usage();
875
876 if (quiet)
877 freopen(_PATH_DEVNULL, "w", stderr);
878
879 if (id < 0 && name == NULL)
880 errx(EX_DATAERR, "username or id required");
881
882 cnf = get_userconfig(cfg);
883
884 if (nispasswd == NULL)
885 nispasswd = cnf->nispasswd;
886
887 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
888 if (pwd == NULL) {
889 if (name == NULL)
890 errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id);
891 errx(EX_NOUSER, "no such user `%s'", name);
892 }
893
894 if (PWF._altdir == PWF_REGULAR &&
895 ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
896 if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
897 if (!nis && nispasswd && *nispasswd != '/')
898 errx(EX_NOUSER, "Cannot remove NIS user `%s'",
899 name);
900 } else {
901 errx(EX_NOUSER, "Cannot remove non local user `%s'",
902 name);
903 }
904 }
905
906 id = pwd->pw_uid;
907 if (name == NULL)
908 name = pwd->pw_name;
909
910 if (strcmp(pwd->pw_name, "root") == 0)
911 errx(EX_DATAERR, "cannot remove user 'root'");
912
913 if (!PWALTDIR()) {
914 /* Remove crontabs */
915 snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name);
916 if (access(file, F_OK) == 0) {
917 const char *argv[] = {
918 "crontab",
919 "-u",
920 pwd->pw_name,
921 "-r",
922 NULL
923 };
924 pid_t pid;
925
926 if (posix_spawnp(&pid, argv[0], NULL, NULL,
927 (char *const *) argv, environ)) {
928 warn("Failed to execute '%s %s'",
929 argv[0], argv[1]);
930 } else
931 (void) waitpid(pid, NULL, 0);
932 }
933 }
934
935 /*
936 * Save these for later, since contents of pwd may be
937 * invalidated by deletion
938 */
939 snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
940 strlcpy(home, pwd->pw_dir, sizeof(home));
941 gr = GETGRGID(pwd->pw_gid);
942 if (gr != NULL)
943 strlcpy(grname, gr->gr_name, LOGNAMESIZE);
944 else
945 grname[0] = '\0';
946
947 rc = delpwent(pwd);
948 if (rc == -1)
949 err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
950 else if (rc != 0)
951 err(EX_IOERR, "passwd update");
952
953 if (nis && nispasswd && *nispasswd=='/') {
954 rc = delnispwent(nispasswd, name);
955 if (rc == -1)
956 warnx("WARNING: user '%s' does not exist in NIS passwd",
957 pwd->pw_name);
958 else if (rc != 0)
959 warn("WARNING: NIS passwd update");
960 }
961
962 grp = GETGRNAM(name);
963 if (grp != NULL &&
964 (grp->gr_mem == NULL || *grp->gr_mem == NULL) &&
965 strcmp(name, grname) == 0)
966 delgrent(GETGRNAM(name));
967 SETGRENT();
968 while ((grp = GETGRENT()) != NULL) {
969 int i, j;
970 char group[MAXLOGNAME];
971 if (grp->gr_mem == NULL)
972 continue;
973
974 for (i = 0; grp->gr_mem[i] != NULL; i++) {
975 if (strcmp(grp->gr_mem[i], name) != 0)
976 continue;
977
978 for (j = i; grp->gr_mem[j] != NULL; j++)
979 grp->gr_mem[j] = grp->gr_mem[j+1];
980 strlcpy(group, grp->gr_name, MAXLOGNAME);
981 chggrent(group, grp);
982 }
983 }
984 ENDGRENT();
985
986 pw_log(cnf, M_DELETE, W_USER, "%s(%ju) account removed", name,
987 (uintmax_t)id);
988
989 /* Remove mail file */
990 if (PWALTDIR() != PWF_ALT)
991 unlinkat(conf.rootfd, file + 1, 0);
992
993 /* Remove at jobs */
994 if (!PWALTDIR() && getpwuid(id) == NULL)
995 rmat(id);
996
997 /* Remove home directory and contents */
998 if (PWALTDIR() != PWF_ALT && deletehome && *home == '/' &&
999 GETPWUID(id) == NULL &&
1000 fstatat(conf.rootfd, home + 1, &st, 0) != -1) {
1001 rm_r(conf.rootfd, home, id);
1002 pw_log(cnf, M_DELETE, W_USER, "%s(%ju) home '%s' %s"
1003 "removed", name, (uintmax_t)id, home,
1004 fstatat(conf.rootfd, home + 1, &st, 0) == -1 ? "" : "not "
1005 "completely ");
1006 }
1007
1008 return (EXIT_SUCCESS);
1009 }
1010
1011 int
pw_user_lock(int argc,char ** argv,char * arg1)1012 pw_user_lock(int argc, char **argv, char *arg1)
1013 {
1014 int ch;
1015
1016 while ((ch = getopt(argc, argv, "Cq")) != -1) {
1017 switch (ch) {
1018 case 'C':
1019 case 'q':
1020 /* compatibility */
1021 break;
1022 default:
1023 usage();
1024 }
1025 }
1026 argc -= optind;
1027 argv += optind;
1028 if (argc > 0)
1029 usage();
1030
1031 return (pw_userlock(arg1, M_LOCK));
1032 }
1033
1034 int
pw_user_unlock(int argc,char ** argv,char * arg1)1035 pw_user_unlock(int argc, char **argv, char *arg1)
1036 {
1037 int ch;
1038
1039 while ((ch = getopt(argc, argv, "Cq")) != -1) {
1040 switch (ch) {
1041 case 'C':
1042 case 'q':
1043 /* compatibility */
1044 break;
1045 default:
1046 usage();
1047 }
1048 }
1049 argc -= optind;
1050 argv += optind;
1051 if (argc > 0)
1052 usage();
1053
1054 return (pw_userlock(arg1, M_UNLOCK));
1055 }
1056
1057 static struct group *
group_from_name_or_id(char * name)1058 group_from_name_or_id(char *name)
1059 {
1060 const char *errstr = NULL;
1061 struct group *grp;
1062 uintmax_t id;
1063
1064 if ((grp = GETGRNAM(name)) == NULL) {
1065 id = strtounum(name, 0, GID_MAX, &errstr);
1066 if (errstr)
1067 errx(EX_NOUSER, "group `%s' does not exist", name);
1068 grp = GETGRGID(id);
1069 if (grp == NULL)
1070 errx(EX_NOUSER, "group `%s' does not exist", name);
1071 }
1072
1073 return (grp);
1074 }
1075
1076 static void
split_groups(StringList ** groups,char * groupsstr)1077 split_groups(StringList **groups, char *groupsstr)
1078 {
1079 struct group *grp;
1080 char *p;
1081 char tok[] = ", \t";
1082
1083 if (*groups == NULL)
1084 *groups = sl_init();
1085 for (p = strtok(groupsstr, tok); p != NULL; p = strtok(NULL, tok)) {
1086 grp = group_from_name_or_id(p);
1087 sl_add(*groups, newstr(grp->gr_name));
1088 }
1089 }
1090
1091 static void
validate_grname(struct userconf * cnf,char * group)1092 validate_grname(struct userconf *cnf, char *group)
1093 {
1094 struct group *grp;
1095
1096 if (group == NULL || *group == '\0') {
1097 cnf->default_group = "";
1098 return;
1099 }
1100 grp = group_from_name_or_id(group);
1101 cnf->default_group = newstr(grp->gr_name);
1102 }
1103
1104 static mode_t
validate_mode(char * mode)1105 validate_mode(char *mode)
1106 {
1107 mode_t m;
1108 void *set;
1109
1110 if ((set = setmode(mode)) == NULL)
1111 errx(EX_DATAERR, "invalid directory creation mode '%s'", mode);
1112
1113 m = getmode(set, _DEF_DIRMODE);
1114 free(set);
1115 return (m);
1116 }
1117
1118 static long
validate_expire(char * str,int opt)1119 validate_expire(char *str, int opt)
1120 {
1121 if (!numerics(str))
1122 errx(EX_DATAERR, "-%c argument must be numeric "
1123 "when setting defaults: %s", (char)opt, str);
1124 return strtol(str, NULL, 0);
1125 }
1126
1127 static void
mix_config(struct userconf * cmdcnf,struct userconf * cfg)1128 mix_config(struct userconf *cmdcnf, struct userconf *cfg)
1129 {
1130
1131 if (cmdcnf->default_password < 0)
1132 cmdcnf->default_password = cfg->default_password;
1133 if (cmdcnf->reuse_uids == 0)
1134 cmdcnf->reuse_uids = cfg->reuse_uids;
1135 if (cmdcnf->reuse_gids == 0)
1136 cmdcnf->reuse_gids = cfg->reuse_gids;
1137 if (cmdcnf->nispasswd == NULL)
1138 cmdcnf->nispasswd = cfg->nispasswd;
1139 if (cmdcnf->dotdir == NULL)
1140 cmdcnf->dotdir = cfg->dotdir;
1141 if (cmdcnf->newmail == NULL)
1142 cmdcnf->newmail = cfg->newmail;
1143 if (cmdcnf->logfile == NULL)
1144 cmdcnf->logfile = cfg->logfile;
1145 if (cmdcnf->home == NULL)
1146 cmdcnf->home = cfg->home;
1147 if (cmdcnf->homemode == 0)
1148 cmdcnf->homemode = cfg->homemode;
1149 if (cmdcnf->shelldir == NULL)
1150 cmdcnf->shelldir = cfg->shelldir;
1151 if (cmdcnf->shells == NULL)
1152 cmdcnf->shells = cfg->shells;
1153 if (cmdcnf->shell_default == NULL)
1154 cmdcnf->shell_default = cfg->shell_default;
1155 if (cmdcnf->default_group == NULL)
1156 cmdcnf->default_group = cfg->default_group;
1157 if (cmdcnf->groups == NULL)
1158 cmdcnf->groups = cfg->groups;
1159 if (cmdcnf->default_class == NULL)
1160 cmdcnf->default_class = cfg->default_class;
1161 if (cmdcnf->min_uid == 0)
1162 cmdcnf->min_uid = cfg->min_uid;
1163 if (cmdcnf->max_uid == 0)
1164 cmdcnf->max_uid = cfg->max_uid;
1165 if (cmdcnf->min_gid == 0)
1166 cmdcnf->min_gid = cfg->min_gid;
1167 if (cmdcnf->max_gid == 0)
1168 cmdcnf->max_gid = cfg->max_gid;
1169 if (cmdcnf->expire_days < 0)
1170 cmdcnf->expire_days = cfg->expire_days;
1171 if (cmdcnf->password_days < 0)
1172 cmdcnf->password_days = cfg->password_days;
1173 }
1174
1175 int
pw_user_add(int argc,char ** argv,char * arg1)1176 pw_user_add(int argc, char **argv, char *arg1)
1177 {
1178 struct userconf *cnf, *cmdcnf;
1179 struct passwd *pwd;
1180 struct group *grp;
1181 struct stat st;
1182 char args[] = "C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y";
1183 char line[_PASSWORD_LEN+1], path[MAXPATHLEN];
1184 char *gecos, *homedir, *skel, *walk, *userid, *groupid, *grname;
1185 char *default_passwd, *name, *p;
1186 const char *cfg = NULL;
1187 login_cap_t *lc;
1188 FILE *pfp, *fp;
1189 intmax_t id = -1;
1190 time_t now;
1191 int rc, ch, fd = -1;
1192 size_t i;
1193 bool dryrun, nis, pretty, quiet, createhome, precrypted, genconf;
1194
1195 dryrun = nis = pretty = quiet = createhome = precrypted = false;
1196 genconf = false;
1197 gecos = homedir = skel = userid = groupid = default_passwd = NULL;
1198 grname = name = NULL;
1199
1200 if ((cmdcnf = calloc(1, sizeof(struct userconf))) == NULL)
1201 err(EXIT_FAILURE, "calloc()");
1202
1203 cmdcnf->default_password = cmdcnf->expire_days = cmdcnf->password_days = -1;
1204 now = time(NULL);
1205
1206 if (arg1 != NULL) {
1207 if (arg1[strspn(arg1, "0123456789")] == '\0')
1208 id = pw_checkid(arg1, UID_MAX);
1209 else
1210 name = pw_checkname(arg1, 0);
1211 }
1212
1213 while ((ch = getopt(argc, argv, args)) != -1) {
1214 switch (ch) {
1215 case 'C':
1216 cfg = optarg;
1217 break;
1218 case 'q':
1219 quiet = true;
1220 break;
1221 case 'n':
1222 name = pw_checkname(optarg, 0);
1223 break;
1224 case 'u':
1225 userid = optarg;
1226 break;
1227 case 'c':
1228 gecos = pw_checkname(optarg, 1);
1229 break;
1230 case 'd':
1231 homedir = optarg;
1232 break;
1233 case 'e':
1234 if (genconf)
1235 cmdcnf->expire_days = validate_expire(optarg, ch);
1236 else
1237 cmdcnf->expire_days = parse_date(now, optarg);
1238 break;
1239 case 'p':
1240 if (genconf)
1241 cmdcnf->password_days = validate_expire(optarg, ch);
1242 else
1243 cmdcnf->password_days = parse_date(now, optarg);
1244 break;
1245 case 'g':
1246 validate_grname(cmdcnf, optarg);
1247 grname = optarg;
1248 break;
1249 case 'G':
1250 split_groups(&cmdcnf->groups, optarg);
1251 break;
1252 case 'm':
1253 createhome = true;
1254 break;
1255 case 'M':
1256 cmdcnf->homemode = validate_mode(optarg);
1257 break;
1258 case 'k':
1259 walk = skel = optarg;
1260 if (*walk == '/')
1261 walk++;
1262 if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1263 errx(EX_OSFILE, "skeleton `%s' does not "
1264 "exists", skel);
1265 if (!S_ISDIR(st.st_mode))
1266 errx(EX_OSFILE, "skeleton `%s' is not a "
1267 "directory", skel);
1268 cmdcnf->dotdir = skel;
1269 break;
1270 case 's':
1271 cmdcnf->shell_default = optarg;
1272 break;
1273 case 'o':
1274 conf.checkduplicate = false;
1275 break;
1276 case 'L':
1277 cmdcnf->default_class = pw_checkname(optarg, 0);
1278 break;
1279 case 'i':
1280 groupid = optarg;
1281 break;
1282 case 'w':
1283 default_passwd = optarg;
1284 break;
1285 case 'H':
1286 if (fd != -1)
1287 errx(EX_USAGE, "'-h' and '-H' are mutually "
1288 "exclusive options");
1289 fd = pw_checkfd(optarg);
1290 precrypted = true;
1291 if (fd == '-')
1292 errx(EX_USAGE, "-H expects a file descriptor");
1293 break;
1294 case 'h':
1295 if (fd != -1)
1296 errx(EX_USAGE, "'-h' and '-H' are mutually "
1297 "exclusive options");
1298 fd = pw_checkfd(optarg);
1299 break;
1300 case 'D':
1301 genconf = true;
1302 break;
1303 case 'b':
1304 cmdcnf->home = optarg;
1305 break;
1306 case 'N':
1307 dryrun = true;
1308 break;
1309 case 'P':
1310 pretty = true;
1311 break;
1312 case 'y':
1313 cmdcnf->nispasswd = optarg;
1314 break;
1315 case 'Y':
1316 nis = true;
1317 break;
1318 default:
1319 usage();
1320 }
1321 }
1322 argc -= optind;
1323 argv += optind;
1324 if (argc > 0)
1325 usage();
1326
1327 if (geteuid() != 0 && ! dryrun)
1328 errx(EX_NOPERM, "you must be root");
1329
1330 if (quiet)
1331 freopen(_PATH_DEVNULL, "w", stderr);
1332
1333 cnf = get_userconfig(cfg);
1334
1335 mix_config(cmdcnf, cnf);
1336 if (default_passwd)
1337 cmdcnf->default_password = passwd_val(default_passwd,
1338 cnf->default_password);
1339 if (genconf) {
1340 if (name != NULL)
1341 errx(EX_DATAERR, "can't combine `-D' with `-n name'");
1342 if (userid != NULL) {
1343 if ((p = strtok(userid, ", \t")) != NULL)
1344 cmdcnf->min_uid = pw_checkid(p, UID_MAX);
1345 if (cmdcnf->min_uid == 0)
1346 cmdcnf->min_uid = 1000;
1347 if ((p = strtok(NULL, " ,\t")) != NULL)
1348 cmdcnf->max_uid = pw_checkid(p, UID_MAX);
1349 if (cmdcnf->max_uid == 0)
1350 cmdcnf->max_uid = 32000;
1351 }
1352 if (groupid != NULL) {
1353 if ((p = strtok(groupid, ", \t")) != NULL)
1354 cmdcnf->min_gid = pw_checkid(p, GID_MAX);
1355 if (cmdcnf->min_gid == 0)
1356 cmdcnf->min_gid = 1000;
1357 if ((p = strtok(NULL, " ,\t")) != NULL)
1358 cmdcnf->max_gid = pw_checkid(p, GID_MAX);
1359 if (cmdcnf->max_gid == 0)
1360 cmdcnf->max_gid = 32000;
1361 }
1362 if (write_userconfig(cmdcnf, cfg))
1363 return (EXIT_SUCCESS);
1364 err(EX_IOERR, "config update");
1365 }
1366
1367 if (userid)
1368 id = pw_checkid(userid, UID_MAX);
1369 if (id < 0 && name == NULL)
1370 errx(EX_DATAERR, "user name or id required");
1371
1372 if (name == NULL)
1373 errx(EX_DATAERR, "login name required");
1374
1375 if (GETPWNAM(name) != NULL)
1376 errx(EX_DATAERR, "login name `%s' already exists", name);
1377
1378 if (!grname)
1379 grname = cmdcnf->default_group;
1380
1381 pwd = &fakeuser;
1382 pwd->pw_name = name;
1383 pwd->pw_class = cmdcnf->default_class ? cmdcnf->default_class : "";
1384 pwd->pw_uid = pw_uidpolicy(cmdcnf, id);
1385 pwd->pw_gid = pw_gidpolicy(cnf, grname, pwd->pw_name,
1386 (gid_t) pwd->pw_uid, dryrun);
1387
1388 /* cmdcnf->password_days and cmdcnf->expire_days hold unixtime here */
1389 if (cmdcnf->password_days > 0)
1390 pwd->pw_change = cmdcnf->password_days;
1391 if (cmdcnf->expire_days > 0)
1392 pwd->pw_expire = cmdcnf->expire_days;
1393
1394 pwd->pw_dir = pw_homepolicy(cmdcnf, homedir, pwd->pw_name);
1395 pwd->pw_shell = pw_shellpolicy(cmdcnf);
1396 lc = login_getpwclass(pwd);
1397 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1398 warn("setting crypt(3) format");
1399 login_close(lc);
1400 pwd->pw_passwd = pw_password(cmdcnf, pwd->pw_name);
1401 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1402 warnx("WARNING: new account `%s' has a uid of 0 "
1403 "(superuser access!)", pwd->pw_name);
1404 if (gecos)
1405 pwd->pw_gecos = gecos;
1406
1407 if (fd != -1)
1408 pw_set_passwd(pwd, fd, precrypted, false);
1409
1410 if (dryrun)
1411 return (print_user(pwd, pretty, false));
1412
1413 if ((rc = addpwent(pwd)) != 0) {
1414 if (rc == -1)
1415 errx(EX_IOERR, "user '%s' already exists",
1416 pwd->pw_name);
1417 else if (rc != 0)
1418 err(EX_IOERR, "passwd file update");
1419 }
1420 if (nis && cmdcnf->nispasswd && *cmdcnf->nispasswd == '/') {
1421 printf("%s\n", cmdcnf->nispasswd);
1422 rc = addnispwent(cmdcnf->nispasswd, pwd);
1423 if (rc == -1)
1424 warnx("User '%s' already exists in NIS passwd",
1425 pwd->pw_name);
1426 else if (rc != 0)
1427 warn("NIS passwd update");
1428 /* NOTE: we treat NIS-only update errors as non-fatal */
1429 }
1430
1431 if (cmdcnf->groups != NULL) {
1432 for (i = 0; i < cmdcnf->groups->sl_cur; i++) {
1433 grp = GETGRNAM(cmdcnf->groups->sl_str[i]);
1434 /* gr_add doesn't check if new member is already in group */
1435 if (grp_has_member(grp, pwd->pw_name))
1436 continue;
1437 grp = gr_add(grp, pwd->pw_name);
1438 /*
1439 * grp can only be NULL in 2 cases:
1440 * - the new member is already a member
1441 * - a problem with memory occurs
1442 * in both cases we want to skip now.
1443 */
1444 if (grp == NULL)
1445 continue;
1446 chggrent(grp->gr_name, grp);
1447 free(grp);
1448 }
1449 }
1450
1451 pwd = GETPWNAM(name);
1452 if (pwd == NULL)
1453 errx(EX_NOUSER, "user '%s' disappeared during update", name);
1454
1455 grp = GETGRGID(pwd->pw_gid);
1456 pw_log(cnf, M_ADD, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1457 pwd->pw_name, (uintmax_t)pwd->pw_uid,
1458 grp ? grp->gr_name : "unknown",
1459 (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1460 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1461
1462 /*
1463 * let's touch and chown the user's mail file. This is not
1464 * strictly necessary under BSD with a 0755 maildir but it also
1465 * doesn't hurt anything to create the empty mailfile
1466 */
1467 if (PWALTDIR() != PWF_ALT) {
1468 snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR,
1469 pwd->pw_name);
1470 /* Preserve contents & mtime */
1471 close(openat(conf.rootfd, path +1, O_RDWR | O_CREAT, 0600));
1472 fchownat(conf.rootfd, path + 1, pwd->pw_uid, pwd->pw_gid,
1473 AT_SYMLINK_NOFOLLOW);
1474 }
1475
1476 /*
1477 * Let's create and populate the user's home directory. Note
1478 * that this also `works' for editing users if -m is used, but
1479 * existing files will *not* be overwritten.
1480 */
1481 if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir &&
1482 *pwd->pw_dir == '/' && pwd->pw_dir[1])
1483 create_and_populate_homedir(cmdcnf, pwd, cmdcnf->dotdir,
1484 cmdcnf->homemode, false);
1485
1486 if (!PWALTDIR() && cmdcnf->newmail && *cmdcnf->newmail &&
1487 (fp = fopen(cnf->newmail, "r")) != NULL) {
1488 if ((pfp = popen(_PATH_SENDMAIL " -t", "w")) == NULL)
1489 warn("sendmail");
1490 else {
1491 fprintf(pfp, "From: root\n" "To: %s\n"
1492 "Subject: Welcome!\n\n", pwd->pw_name);
1493 while (fgets(line, sizeof(line), fp) != NULL) {
1494 /* Do substitutions? */
1495 fputs(line, pfp);
1496 }
1497 pclose(pfp);
1498 pw_log(cnf, M_ADD, W_USER, "%s(%ju) new user mail sent",
1499 pwd->pw_name, (uintmax_t)pwd->pw_uid);
1500 }
1501 fclose(fp);
1502 }
1503
1504 if (nis && nis_update() == 0)
1505 pw_log(cnf, M_ADD, W_USER, "NIS maps updated");
1506
1507 return (EXIT_SUCCESS);
1508 }
1509
1510 int
pw_user_mod(int argc,char ** argv,char * arg1)1511 pw_user_mod(int argc, char **argv, char *arg1)
1512 {
1513 struct userconf *cnf;
1514 struct passwd *pwd;
1515 struct group *grp;
1516 StringList *groups = NULL;
1517 char args[] = "C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:NPYy:";
1518 const char *cfg = NULL;
1519 char *gecos, *homedir, *grname, *name, *newname, *walk, *skel, *shell;
1520 char *passwd, *class, *nispasswd;
1521 login_cap_t *lc;
1522 struct stat st;
1523 intmax_t id = -1;
1524 int ch, fd = -1;
1525 size_t i, j;
1526 bool quiet, createhome, pretty, dryrun, nis, edited;
1527 bool precrypted;
1528 mode_t homemode = 0;
1529 time_t expire_time, password_time, now;
1530
1531 expire_time = password_time = -1;
1532 gecos = homedir = grname = name = newname = skel = shell =NULL;
1533 passwd = NULL;
1534 class = nispasswd = NULL;
1535 quiet = createhome = pretty = dryrun = nis = precrypted = false;
1536 edited = false;
1537 now = time(NULL);
1538
1539 if (arg1 != NULL) {
1540 if (arg1[strspn(arg1, "0123456789")] == '\0')
1541 id = pw_checkid(arg1, UID_MAX);
1542 else
1543 name = arg1;
1544 }
1545
1546 while ((ch = getopt(argc, argv, args)) != -1) {
1547 switch (ch) {
1548 case 'C':
1549 cfg = optarg;
1550 break;
1551 case 'q':
1552 quiet = true;
1553 break;
1554 case 'n':
1555 name = optarg;
1556 break;
1557 case 'u':
1558 id = pw_checkid(optarg, UID_MAX);
1559 break;
1560 case 'c':
1561 gecos = pw_checkname(optarg, 1);
1562 break;
1563 case 'd':
1564 homedir = optarg;
1565 break;
1566 case 'e':
1567 expire_time = parse_date(now, optarg);
1568 break;
1569 case 'p':
1570 password_time = parse_date(now, optarg);
1571 break;
1572 case 'g':
1573 group_from_name_or_id(optarg);
1574 grname = optarg;
1575 break;
1576 case 'G':
1577 split_groups(&groups, optarg);
1578 break;
1579 case 'm':
1580 createhome = true;
1581 break;
1582 case 'M':
1583 homemode = validate_mode(optarg);
1584 break;
1585 case 'l':
1586 newname = optarg;
1587 break;
1588 case 'k':
1589 walk = skel = optarg;
1590 if (*walk == '/')
1591 walk++;
1592 if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1593 errx(EX_OSFILE, "skeleton `%s' does not "
1594 "exists", skel);
1595 if (!S_ISDIR(st.st_mode))
1596 errx(EX_OSFILE, "skeleton `%s' is not a "
1597 "directory", skel);
1598 break;
1599 case 's':
1600 shell = optarg;
1601 break;
1602 case 'w':
1603 passwd = optarg;
1604 break;
1605 case 'L':
1606 class = pw_checkname(optarg, 0);
1607 break;
1608 case 'H':
1609 if (fd != -1)
1610 errx(EX_USAGE, "'-h' and '-H' are mutually "
1611 "exclusive options");
1612 fd = pw_checkfd(optarg);
1613 precrypted = true;
1614 if (fd == '-')
1615 errx(EX_USAGE, "-H expects a file descriptor");
1616 break;
1617 case 'h':
1618 if (fd != -1)
1619 errx(EX_USAGE, "'-h' and '-H' are mutually "
1620 "exclusive options");
1621 fd = pw_checkfd(optarg);
1622 break;
1623 case 'N':
1624 dryrun = true;
1625 break;
1626 case 'P':
1627 pretty = true;
1628 break;
1629 case 'y':
1630 nispasswd = optarg;
1631 break;
1632 case 'Y':
1633 nis = true;
1634 break;
1635 default:
1636 usage();
1637 }
1638 }
1639 argc -= optind;
1640 argv += optind;
1641 if (argc > 0)
1642 usage();
1643
1644 if (geteuid() != 0 && ! dryrun)
1645 errx(EX_NOPERM, "you must be root");
1646
1647 if (quiet)
1648 freopen(_PATH_DEVNULL, "w", stderr);
1649
1650 cnf = get_userconfig(cfg);
1651
1652 if (id < 0 && name == NULL)
1653 errx(EX_DATAERR, "username or id required");
1654
1655 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
1656 if (pwd == NULL) {
1657 if (name == NULL)
1658 errx(EX_NOUSER, "no such uid `%ju'",
1659 (uintmax_t) id);
1660 errx(EX_NOUSER, "no such user `%s'", name);
1661 }
1662
1663 if (name == NULL)
1664 name = pwd->pw_name;
1665
1666 if (nis && nispasswd == NULL)
1667 nispasswd = cnf->nispasswd;
1668
1669 if (PWF._altdir == PWF_REGULAR &&
1670 ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
1671 if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
1672 if (!nis && nispasswd && *nispasswd != '/')
1673 errx(EX_NOUSER, "Cannot modify NIS user `%s'",
1674 name);
1675 } else {
1676 errx(EX_NOUSER, "Cannot modify non local user `%s'",
1677 name);
1678 }
1679 }
1680
1681 if (newname) {
1682 if (strcmp(pwd->pw_name, "root") == 0)
1683 errx(EX_DATAERR, "can't rename `root' account");
1684 if (strcmp(pwd->pw_name, newname) != 0) {
1685 pwd->pw_name = pw_checkname(newname, 0);
1686 edited = true;
1687 }
1688 }
1689
1690 if (id >= 0 && pwd->pw_uid != id) {
1691 pwd->pw_uid = id;
1692 edited = true;
1693 if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
1694 errx(EX_DATAERR, "can't change uid of `root' account");
1695 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1696 warnx("WARNING: account `%s' will have a uid of 0 "
1697 "(superuser access!)", pwd->pw_name);
1698 }
1699
1700 if (grname && pwd->pw_uid != 0) {
1701 grp = GETGRNAM(grname);
1702 if (grp == NULL)
1703 grp = GETGRGID(pw_checkid(grname, GID_MAX));
1704 if (grp->gr_gid != pwd->pw_gid) {
1705 pwd->pw_gid = grp->gr_gid;
1706 edited = true;
1707 }
1708 }
1709
1710
1711 if (password_time >= 0 && pwd->pw_change != password_time) {
1712 pwd->pw_change = password_time;
1713 edited = true;
1714 }
1715
1716 if (expire_time >= 0 && pwd->pw_expire != expire_time) {
1717 pwd->pw_expire = expire_time;
1718 edited = true;
1719 }
1720
1721 if (shell) {
1722 shell = shell_path(cnf->shelldir, cnf->shells, shell);
1723 if (shell == NULL)
1724 shell = "";
1725 if (strcmp(shell, pwd->pw_shell) != 0) {
1726 pwd->pw_shell = shell;
1727 edited = true;
1728 }
1729 }
1730
1731 if (class && strcmp(pwd->pw_class, class) != 0) {
1732 pwd->pw_class = class;
1733 edited = true;
1734 }
1735
1736 if (homedir && strcmp(pwd->pw_dir, homedir) != 0) {
1737 pwd->pw_dir = homedir;
1738 edited = true;
1739 if (fstatat(conf.rootfd, pwd->pw_dir, &st, 0) == -1) {
1740 if (!createhome)
1741 warnx("WARNING: home `%s' does not exist",
1742 pwd->pw_dir);
1743 } else if (!S_ISDIR(st.st_mode)) {
1744 warnx("WARNING: home `%s' is not a directory",
1745 pwd->pw_dir);
1746 }
1747 }
1748
1749 if (passwd && conf.fd == -1) {
1750 lc = login_getpwclass(pwd);
1751 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1752 warn("setting crypt(3) format");
1753 login_close(lc);
1754 cnf->default_password = passwd_val(passwd,
1755 cnf->default_password);
1756 pwd->pw_passwd = pw_password(cnf, pwd->pw_name);
1757 edited = true;
1758 }
1759
1760 if (gecos && strcmp(pwd->pw_gecos, gecos) != 0) {
1761 pwd->pw_gecos = gecos;
1762 edited = true;
1763 }
1764
1765 if (fd != -1)
1766 edited = pw_set_passwd(pwd, fd, precrypted, true);
1767
1768 if (dryrun)
1769 return (print_user(pwd, pretty, false));
1770
1771 if (edited) /* Only updated this if required */
1772 perform_chgpwent(name, pwd, nis ? nispasswd : NULL);
1773 /* Now perform the needed changes concern groups */
1774 if (groups != NULL) {
1775 /* Delete User from groups using old name */
1776 SETGRENT();
1777 while ((grp = GETGRENT()) != NULL) {
1778 if (grp->gr_mem == NULL)
1779 continue;
1780 for (i = 0; grp->gr_mem[i] != NULL; i++) {
1781 if (strcmp(grp->gr_mem[i] , name) != 0)
1782 continue;
1783 for (j = i; grp->gr_mem[j] != NULL ; j++)
1784 grp->gr_mem[j] = grp->gr_mem[j+1];
1785 chggrent(grp->gr_name, grp);
1786 break;
1787 }
1788 }
1789 ENDGRENT();
1790 /* Add the user to the needed groups */
1791 for (i = 0; i < groups->sl_cur; i++) {
1792 grp = GETGRNAM(groups->sl_str[i]);
1793 grp = gr_add(grp, pwd->pw_name);
1794 if (grp == NULL)
1795 continue;
1796 chggrent(grp->gr_name, grp);
1797 free(grp);
1798 }
1799 }
1800 /* In case of rename we need to walk over the different groups */
1801 if (newname) {
1802 SETGRENT();
1803 while ((grp = GETGRENT()) != NULL) {
1804 if (grp->gr_mem == NULL)
1805 continue;
1806 for (i = 0; grp->gr_mem[i] != NULL; i++) {
1807 if (strcmp(grp->gr_mem[i], name) != 0)
1808 continue;
1809 grp->gr_mem[i] = newname;
1810 chggrent(grp->gr_name, grp);
1811 break;
1812 }
1813 }
1814 }
1815
1816 /* go get a current version of pwd */
1817 if (newname)
1818 name = newname;
1819 pwd = GETPWNAM(name);
1820 if (pwd == NULL)
1821 errx(EX_NOUSER, "user '%s' disappeared during update", name);
1822 grp = GETGRGID(pwd->pw_gid);
1823 pw_log(cnf, M_MODIFY, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1824 pwd->pw_name, (uintmax_t)pwd->pw_uid,
1825 grp ? grp->gr_name : "unknown",
1826 (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1827 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1828
1829 /*
1830 * Let's create and populate the user's home directory. Note
1831 * that this also `works' for editing users if -m is used, but
1832 * existing files will *not* be overwritten.
1833 */
1834 if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir &&
1835 *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
1836 if (!skel)
1837 skel = cnf->dotdir;
1838 if (homemode == 0)
1839 homemode = cnf->homemode;
1840 create_and_populate_homedir(cnf, pwd, skel, homemode, true);
1841 }
1842
1843 if (nis && nis_update() == 0)
1844 pw_log(cnf, M_MODIFY, W_USER, "NIS maps updated");
1845
1846 return (EXIT_SUCCESS);
1847 }
1848