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