Update bktr(4) to FreeBSD current's version. This most importantly
[dragonfly.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  * $FreeBSD: src/usr.sbin/pw/pw_user.c,v 1.34.2.13 2003/02/01 21:20:10 gad Exp $
28  * $DragonFly: src/usr.sbin/pw/pw_user.c,v 1.3 2004/02/10 02:59:43 rob Exp $
29  */
30
31 #include <ctype.h>
32 #include <err.h>
33 #include <fcntl.h>
34 #include <sys/param.h>
35 #include <dirent.h>
36 #include <paths.h>
37 #include <termios.h>
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <sys/resource.h>
41 #include <unistd.h>
42 #include <utmp.h>
43 #include <login_cap.h>
44 #if defined(USE_MD5RAND)
45 #include <md5.h>
46 #endif
47 #include "pw.h"
48 #include "bitmap.h"
49
50 #if (MAXLOGNAME-1) > UT_NAMESIZE
51 #define LOGNAMESIZE UT_NAMESIZE
52 #else
53 #define LOGNAMESIZE (MAXLOGNAME-1)
54 #endif
55
56 static          char locked_str[] = "*LOCKED*";
57
58 static int      print_user(struct passwd * pwd, int pretty, int v7);
59 static uid_t    pw_uidpolicy(struct userconf * cnf, struct cargs * args);
60 static uid_t    pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer);
61 static time_t   pw_pwdpolicy(struct userconf * cnf, struct cargs * args);
62 static time_t   pw_exppolicy(struct userconf * cnf, struct cargs * args);
63 static char    *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user);
64 static char    *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell);
65 static char    *pw_password(struct userconf * cnf, struct cargs * args, char const * user);
66 static char    *shell_path(char const * path, char *shells[], char *sh);
67 static void     rmat(uid_t uid);
68 static void     rmskey(char const * name);
69
70 /*-
71  * -C config      configuration file
72  * -q             quiet operation
73  * -n name        login name
74  * -u uid         user id
75  * -c comment     user name/comment
76  * -d directory   home directory
77  * -e date        account expiry date
78  * -p date        password expiry date
79  * -g grp         primary group
80  * -G grp1,grp2   additional groups
81  * -m [ -k dir ]  create and set up home
82  * -s shell       name of login shell
83  * -o             duplicate uid ok
84  * -L class       user class
85  * -l name        new login name
86  * -h fd          password filehandle
87  * -F             force print or add
88  *   Setting defaults:
89  * -D             set user defaults
90  * -b dir         default home root dir
91  * -e period      default expiry period
92  * -p period      default password change period
93  * -g group       default group
94  * -G             grp1,grp2.. default additional groups
95  * -L class       default login class
96  * -k dir         default home skeleton
97  * -s shell       default shell
98  * -w method      default password method
99  */
100
101 int
102 pw_user(struct userconf * cnf, int mode, struct cargs * args)
103 {
104         int             rc, edited = 0;
105         char           *p = NULL;
106         char                                     *passtmp;
107         struct carg    *a_name;
108         struct carg    *a_uid;
109         struct carg    *arg;
110         struct passwd  *pwd = NULL;
111         struct group   *grp;
112         struct stat     st;
113         char            line[_PASSWORD_LEN+1];
114         FILE           *fp;
115
116         static struct passwd fakeuser =
117         {
118                 NULL,
119                 "*",
120                 -1,
121                 -1,
122                 0,
123                 "",
124                 "User &",
125                 "/nonexistent",
126                 "/bin/sh",
127                 0
128 #if defined(__DragonFly__)
129                 ,0
130 #endif
131         };
132
133
134         /*
135          * With M_NEXT, we only need to return the
136          * next uid to stdout
137          */
138         if (mode == M_NEXT)
139         {
140                 uid_t next = pw_uidpolicy(cnf, args);
141                 if (getarg(args, 'q'))
142                         return next;
143                 printf("%ld:", (long)next);
144                 pw_group(cnf, mode, args);
145                 return EXIT_SUCCESS;
146         }
147
148         /*
149          * We can do all of the common legwork here
150          */
151
152         if ((arg = getarg(args, 'b')) != NULL) {
153                 cnf->home = arg->val;
154         }
155
156         /*
157          * If we'll need to use it or we're updating it,
158          * then create the base home directory if necessary
159          */
160         if (arg != NULL || getarg(args, 'm') != NULL) {
161                 int     l = strlen(cnf->home);
162
163                 if (l > 1 && cnf->home[l-1] == '/')     /* Shave off any trailing path delimiter */
164                         cnf->home[--l] = '\0';
165
166                 if (l < 2 || *cnf->home != '/')         /* Check for absolute path name */
167                         errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home);
168
169                 if (stat(cnf->home, &st) == -1) {
170                         char    dbuf[MAXPATHLEN];
171
172                         /*
173                          * This is a kludge especially for Joerg :)
174                          * If the home directory would be created in the root partition, then
175                          * we really create it under /usr which is likely to have more space.
176                          * But we create a symlink from cnf->home -> "/usr" -> cnf->home
177                          */
178                         if (strchr(cnf->home+1, '/') == NULL) {
179                                 strcpy(dbuf, "/usr");
180                                 strncat(dbuf, cnf->home, MAXPATHLEN-5);
181                                 if (mkdir(dbuf, 0755) != -1 || errno == EEXIST) {
182                                         chown(dbuf, 0, 0);
183                                         symlink(dbuf, cnf->home);
184                                 }
185                                 /* If this falls, fall back to old method */
186                         }
187                         p = strncpy(dbuf, cnf->home, sizeof dbuf);
188                         dbuf[MAXPATHLEN-1] = '\0';
189                         if (stat(dbuf, &st) == -1) {
190                                 while ((p = strchr(++p, '/')) != NULL) {
191                                         *p = '\0';
192                                         if (stat(dbuf, &st) == -1) {
193                                                 if (mkdir(dbuf, 0755) == -1)
194                                                         goto direrr;
195                                                 chown(dbuf, 0, 0);
196                                         } else if (!S_ISDIR(st.st_mode))
197                                                 errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf);
198                                         *p = '/';
199                                 }
200                         }
201                         if (stat(dbuf, &st) == -1) {
202                                 if (mkdir(dbuf, 0755) == -1) {
203                                 direrr: err(EX_OSFILE, "mkdir '%s'", dbuf);
204                                 }
205                                 chown(dbuf, 0, 0);
206                         }
207                 } else if (!S_ISDIR(st.st_mode))
208                         errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home);
209         }
210
211         if ((arg = getarg(args, 'e')) != NULL)
212                 cnf->expire_days = atoi(arg->val);
213
214         if ((arg = getarg(args, 'y')) != NULL)
215                 cnf->nispasswd = arg->val;
216
217         if ((arg = getarg(args, 'p')) != NULL && arg->val)
218                 cnf->password_days = atoi(arg->val);
219
220         if ((arg = getarg(args, 'g')) != NULL) {
221                 if (!*(p = arg->val))   /* Handle empty group list specially */
222                         cnf->default_group = "";
223                 else {
224                         if ((grp = GETGRNAM(p)) == NULL) {
225                                 if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
226                                         errx(EX_NOUSER, "group `%s' does not exist", p);
227                         }
228                         cnf->default_group = newstr(grp->gr_name);
229                 }
230         }
231         if ((arg = getarg(args, 'L')) != NULL)
232                 cnf->default_class = pw_checkname((u_char *)arg->val, 0);
233
234         if ((arg = getarg(args, 'G')) != NULL && arg->val) {
235                 int i = 0;
236
237                 for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
238                         if ((grp = GETGRNAM(p)) == NULL) {
239                                 if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
240                                         errx(EX_NOUSER, "group `%s' does not exist", p);
241                         }
242                         if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1)
243                                 cnf->groups[i++] = newstr(grp->gr_name);
244                 }
245                 while (i < cnf->numgroups)
246                         cnf->groups[i++] = NULL;
247         }
248
249         if ((arg = getarg(args, 'k')) != NULL) {
250                 if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode))
251                         errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir);
252         }
253
254         if ((arg = getarg(args, 's')) != NULL)
255                 cnf->shell_default = arg->val;
256
257         if ((arg = getarg(args, 'w')) != NULL)
258                 cnf->default_password = boolean_val(arg->val, cnf->default_password);
259         if (mode == M_ADD && getarg(args, 'D')) {
260                 if (getarg(args, 'n') != NULL)
261                         errx(EX_DATAERR, "can't combine `-D' with `-n name'");
262                 if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
263                         if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
264                                 cnf->min_uid = 1000;
265                         if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid)
266                                 cnf->max_uid = 32000;
267                 }
268                 if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
269                         if ((cnf->min_gid = (gid_t) atoi(p)) == 0)
270                                 cnf->min_gid = 1000;
271                         if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid)
272                                 cnf->max_gid = 32000;
273                 }
274
275                 arg = getarg(args, 'C');
276                 if (write_userconfig(arg ? arg->val : NULL))
277                         return EXIT_SUCCESS;
278                 warn("config update");
279                 return EX_IOERR;
280         }
281
282         if (mode == M_PRINT && getarg(args, 'a')) {
283                 int             pretty = getarg(args, 'P') != NULL;
284                 int             v7 = getarg(args, '7') != NULL;
285
286                 SETPWENT();
287                 while ((pwd = GETPWENT()) != NULL)
288                         print_user(pwd, pretty, v7);
289                 ENDPWENT();
290                 return EXIT_SUCCESS;
291         }
292
293         if ((a_name = getarg(args, 'n')) != NULL)
294                 pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0));
295         a_uid = getarg(args, 'u');
296
297         if (a_uid == NULL) {
298                 if (a_name == NULL)
299                         errx(EX_DATAERR, "user name or id required");
300
301                 /*
302                  * Determine whether 'n' switch is name or uid - we don't
303                  * really don't really care which we have, but we need to
304                  * know.
305                  */
306                 if (mode != M_ADD && pwd == NULL
307                     && strspn(a_name->val, "0123456789") == strlen(a_name->val)
308                     && atoi(a_name->val) > 0) { /* Assume uid */
309                         (a_uid = a_name)->ch = 'u';
310                         a_name = NULL;
311                 }
312         }
313
314         /*
315          * Update, delete & print require that the user exists
316          */
317         if (mode == M_UPDATE || mode == M_DELETE ||
318             mode == M_PRINT  || mode == M_LOCK   || mode == M_UNLOCK) {
319
320                 if (a_name == NULL && pwd == NULL)      /* Try harder */
321                         pwd = GETPWUID(atoi(a_uid->val));
322
323                 if (pwd == NULL) {
324                         if (mode == M_PRINT && getarg(args, 'F')) {
325                                 fakeuser.pw_name = a_name ? a_name->val : "nouser";
326                                 fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1;
327                                 return print_user(&fakeuser,
328                                                   getarg(args, 'P') != NULL,
329                                                   getarg(args, '7') != NULL);
330                         }
331                         if (a_name == NULL)
332                                 errx(EX_NOUSER, "no such uid `%s'", a_uid->val);
333                         errx(EX_NOUSER, "no such user `%s'", a_name->val);
334                 }
335
336                 if (a_name == NULL)     /* May be needed later */
337                         a_name = addarg(args, 'n', newstr(pwd->pw_name));
338
339                 /*
340                  * The M_LOCK and M_UNLOCK functions simply add or remove
341                  * a "*LOCKED*" prefix from in front of the password to
342                  * prevent it decoding correctly, and therefore prevents
343                  * access. Of course, this only prevents access via
344                  * password authentication (not ssh, kerberos or any
345                  * other method that does not use the UNIX password) but
346                  * that is a known limitation.
347                  */
348
349                 if (mode == M_LOCK) {
350                         if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) == 0)
351                                 errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
352                         passtmp = malloc(strlen(pwd->pw_passwd) + sizeof(locked_str));
353                         if (passtmp == NULL)    /* disaster */
354                                 errx(EX_UNAVAILABLE, "out of memory");
355                         strcpy(passtmp, locked_str);
356                         strcat(passtmp, pwd->pw_passwd);
357                         pwd->pw_passwd = passtmp;
358                         edited = 1;
359                 } else if (mode == M_UNLOCK) {
360                         if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) != 0)
361                                 errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name);
362                         pwd->pw_passwd += sizeof(locked_str)-1;
363                         edited = 1;
364                 } else if (mode == M_DELETE) {
365                         /*
366                          * Handle deletions now
367                          */
368                         char            file[MAXPATHLEN];
369                         char            home[MAXPATHLEN];
370                         uid_t           uid = pwd->pw_uid;
371
372                         if (strcmp(pwd->pw_name, "root") == 0)
373                                 errx(EX_DATAERR, "cannot remove user 'root'");
374
375                         if (!PWALTDIR()) {
376                                 /*
377                                  * Remove skey record from /etc/skeykeys
378                                  */
379
380                                 rmskey(pwd->pw_name);
381
382                                 /*
383                                  * Remove crontabs
384                                  */
385                                 sprintf(file, "/var/cron/tabs/%s", pwd->pw_name);
386                                 if (access(file, F_OK) == 0) {
387                                         sprintf(file, "crontab -u %s -r", pwd->pw_name);
388                                         system(file);
389                                 }
390                         }
391                         /*
392                          * Save these for later, since contents of pwd may be
393                          * invalidated by deletion
394                          */
395                         sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
396                         strncpy(home, pwd->pw_dir, sizeof home);
397                         home[sizeof home - 1] = '\0';
398
399                         rc = delpwent(pwd);
400                         if (rc == -1)
401                                 err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
402                         else if (rc != 0) {
403                                 warn("passwd update");
404                                 return EX_IOERR;
405                         }
406
407                         if (cnf->nispasswd && *cnf->nispasswd=='/') {
408                                 rc = delnispwent(cnf->nispasswd, a_name->val);
409                                 if (rc == -1)
410                                         warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name);
411                                 else if (rc != 0)
412                                         warn("WARNING: NIS passwd update");
413                                 /* non-fatal */
414                         }
415
416                         editgroups(a_name->val, NULL);
417
418                         pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
419
420                         if (!PWALTDIR()) {
421                                 /*
422                                  * Remove mail file
423                                  */
424                                 remove(file);
425
426                                 /*
427                                  * Remove at jobs
428                                  */
429                                 if (getpwuid(uid) == NULL)
430                                         rmat(uid);
431
432                                 /*
433                                  * Remove home directory and contents
434                                  */
435                                 if (getarg(args, 'r') != NULL && *home == '/' && getpwuid(uid) == NULL) {
436                                         if (stat(home, &st) != -1) {
437                                                 rm_r(home, uid);
438                                                 pw_log(cnf, mode, W_USER, "%s(%ld) home '%s' %sremoved",
439                                                        a_name->val, (long) uid, home,
440                                                        stat(home, &st) == -1 ? "" : "not completely ");
441                                         }
442                                 }
443                         }
444                         return EXIT_SUCCESS;
445                 } else if (mode == M_PRINT)
446                         return print_user(pwd,
447                                           getarg(args, 'P') != NULL,
448                                           getarg(args, '7') != NULL);
449
450                 /*
451                  * The rest is edit code
452                  */
453                 if ((arg = getarg(args, 'l')) != NULL) {
454                         if (strcmp(pwd->pw_name, "root") == 0)
455                                 errx(EX_DATAERR, "can't rename `root' account");
456                         pwd->pw_name = pw_checkname((u_char *)arg->val, 0);
457                         edited = 1;
458                 }
459
460                 if ((arg = getarg(args, 'u')) != NULL && isdigit((unsigned char)*arg->val)) {
461                         pwd->pw_uid = (uid_t) atol(arg->val);
462                         edited = 1;
463                         if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
464                                 errx(EX_DATAERR, "can't change uid of `root' account");
465                         if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
466                                 warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name);
467                 }
468
469                 if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) {    /* Already checked this */
470                         gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid;
471                         if (newgid != pwd->pw_gid) {
472                                 edited = 1;
473                                 pwd->pw_gid = newgid;
474                         }
475                 }
476
477                 if ((arg = getarg(args, 'p')) != NULL) {
478                         if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
479                                 if (pwd->pw_change != 0) {
480                                         pwd->pw_change = 0;
481                                         edited = 1;
482                                 }
483                         }
484                         else {
485                                 time_t          now = time(NULL);
486                                 time_t          expire = parse_date(now, arg->val);
487
488                                 if (now == expire)
489                                         errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
490                                 if (pwd->pw_change != expire) {
491                                         pwd->pw_change = expire;
492                                         edited = 1;
493                                 }
494                         }
495                 }
496
497                 if ((arg = getarg(args, 'e')) != NULL) {
498                         if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
499                                 if (pwd->pw_expire != 0) {
500                                         pwd->pw_expire = 0;
501                                         edited = 1;
502                                 }
503                         }
504                         else {
505                                 time_t          now = time(NULL);
506                                 time_t          expire = parse_date(now, arg->val);
507
508                                 if (now == expire)
509                                         errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
510                                 if (pwd->pw_expire != expire) {
511                                         pwd->pw_expire = expire;
512                                         edited = 1;
513                                 }
514                         }
515                 }
516
517                 if ((arg = getarg(args, 's')) != NULL) {
518                         char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
519                         if (shell == NULL)
520                                 shell = "";
521                         if (strcmp(shell, pwd->pw_shell) != 0) {
522                                 pwd->pw_shell = shell;
523                                 edited = 1;
524                         }
525                 }
526
527                 if (getarg(args, 'L')) {
528                         if (cnf->default_class == NULL)
529                                 cnf->default_class = "";
530                         if (strcmp(pwd->pw_class, cnf->default_class) != 0) {
531                                 pwd->pw_class = cnf->default_class;
532                                 edited = 1;
533                         }
534                 }
535
536                 if ((arg  = getarg(args, 'd')) != NULL) {
537                         edited = strcmp(pwd->pw_dir, arg->val) != 0;
538                         if (stat(pwd->pw_dir = arg->val, &st) == -1) {
539                                 if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0)
540                                   warnx("WARNING: home `%s' does not exist", pwd->pw_dir);
541                         } else if (!S_ISDIR(st.st_mode))
542                                 warnx("WARNING: home `%s' is not a directory", pwd->pw_dir);
543                 }
544
545                 if ((arg = getarg(args, 'w')) != NULL && getarg(args, 'h') == NULL) {
546                         login_cap_t *lc;
547
548                         lc = login_getpwclass(pwd);
549                         if (lc == NULL ||
550                             login_setcryptfmt(lc, "md5", NULL) == NULL)
551                                 warn("setting crypt(3) format");
552                         login_close(lc);
553                         pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
554                         edited = 1;
555                 }
556
557         } else {
558                 login_cap_t *lc;
559
560                 /*
561                  * Add code
562                  */
563
564                 if (a_name == NULL)     /* Required */
565                         errx(EX_DATAERR, "login name required");
566                 else if ((pwd = GETPWNAM(a_name->val)) != NULL) /* Exists */
567                         errx(EX_DATAERR, "login name `%s' already exists", a_name->val);
568
569                 /*
570                  * Now, set up defaults for a new user
571                  */
572                 pwd = &fakeuser;
573                 pwd->pw_name = a_name->val;
574                 pwd->pw_class = cnf->default_class ? cnf->default_class : "";
575                 pwd->pw_uid = pw_uidpolicy(cnf, args);
576                 pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid);
577                 pwd->pw_change = pw_pwdpolicy(cnf, args);
578                 pwd->pw_expire = pw_exppolicy(cnf, args);
579                 pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name);
580                 pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
581                 lc = login_getpwclass(pwd);
582                 if (lc == NULL || login_setcryptfmt(lc, "md5", NULL) == NULL)
583                         warn("setting crypt(3) format");
584                 login_close(lc);
585                 pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
586                 edited = 1;
587
588                 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
589                         warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name);
590         }
591
592         /*
593          * Shared add/edit code
594          */
595         if ((arg = getarg(args, 'c')) != NULL) {
596                 char    *gecos = pw_checkname((u_char *)arg->val, 1);
597                 if (strcmp(pwd->pw_gecos, gecos) != 0) {
598                         pwd->pw_gecos = gecos;
599                         edited = 1;
600                 }
601         }
602
603         if ((arg = getarg(args, 'h')) != NULL) {
604                 if (strcmp(arg->val, "-") == 0) {
605                         if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
606                                 pwd->pw_passwd = "*";   /* No access */
607                                 edited = 1;
608                         }
609                 } else {
610                         int             fd = atoi(arg->val);
611                         int             b;
612                         int             istty = isatty(fd);
613                         struct termios  t;
614                         login_cap_t     *lc;
615
616                         if (istty) {
617                                 if (tcgetattr(fd, &t) == -1)
618                                         istty = 0;
619                                 else {
620                                         struct termios  n = t;
621
622                                         /* Disable echo */
623                                         n.c_lflag &= ~(ECHO);
624                                         tcsetattr(fd, TCSANOW, &n);
625                                         printf("%sassword for user %s:", (mode == M_UPDATE) ? "New p" : "P", pwd->pw_name);
626                                         fflush(stdout);
627                                 }
628                         }
629                         b = read(fd, line, sizeof(line) - 1);
630                         if (istty) {    /* Restore state */
631                                 tcsetattr(fd, TCSANOW, &t);
632                                 fputc('\n', stdout);
633                                 fflush(stdout);
634                         }
635                         if (b < 0) {
636                                 warn("-h file descriptor");
637                                 return EX_IOERR;
638                         }
639                         line[b] = '\0';
640                         if ((p = strpbrk(line, " \t\r\n")) != NULL)
641                                 *p = '\0';
642                         if (!*line)
643                                 errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
644                         lc = login_getpwclass(pwd);
645                         if (lc == NULL ||
646                             login_setcryptfmt(lc, "md5", NULL) == NULL)
647                                 warn("setting crypt(3) format");
648                         login_close(lc);
649                         pwd->pw_passwd = pw_pwcrypt(line);
650                         edited = 1;
651                 }
652         }
653
654         /*
655          * Special case: -N only displays & exits
656          */
657         if (getarg(args, 'N') != NULL)
658                 return print_user(pwd,
659                                   getarg(args, 'P') != NULL,
660                                   getarg(args, '7') != NULL);
661
662         if (mode == M_ADD) {
663                 edited = 1;     /* Always */
664                 rc = addpwent(pwd);
665                 if (rc == -1) {
666                         warnx("user '%s' already exists", pwd->pw_name);
667                         return EX_IOERR;
668                 } else if (rc != 0) {
669                         warn("passwd file update");
670                         return EX_IOERR;
671                 }
672                 if (cnf->nispasswd && *cnf->nispasswd=='/') {
673                         rc = addnispwent(cnf->nispasswd, pwd);
674                         if (rc == -1)
675                                 warnx("User '%s' already exists in NIS passwd", pwd->pw_name);
676                         else
677                                 warn("NIS passwd update");
678                         /* NOTE: we treat NIS-only update errors as non-fatal */
679                 }
680         } else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) {
681                 if (edited) {   /* Only updated this if required */
682                         rc = chgpwent(a_name->val, pwd);
683                         if (rc == -1) {
684                                 warnx("user '%s' does not exist (NIS?)", pwd->pw_name);
685                                 return EX_IOERR;
686                         } else if (rc != 0) {
687                                 warn("passwd file update");
688                                 return EX_IOERR;
689                         }
690                         if ( cnf->nispasswd && *cnf->nispasswd=='/') {
691                                 rc = chgnispwent(cnf->nispasswd, a_name->val, pwd);
692                                 if (rc == -1)
693                                         warn("User '%s' not found in NIS passwd", pwd->pw_name);
694                                 else
695                                         warn("NIS passwd update");
696                                 /* NOTE: NIS-only update errors are not fatal */
697                         }
698                 }
699         }
700
701         /*
702          * Ok, user is created or changed - now edit group file
703          */
704
705         if (mode == M_ADD || getarg(args, 'G') != NULL)
706                 editgroups(pwd->pw_name, cnf->groups);
707
708         /* go get a current version of pwd */
709         pwd = GETPWNAM(a_name->val);
710         if (pwd == NULL) {
711                 /* This will fail when we rename, so special case that */
712                 if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) {
713                         a_name->val = arg->val;         /* update new name */
714                         pwd = GETPWNAM(a_name->val);    /* refetch renamed rec */
715                 }
716         }
717         if (pwd == NULL)        /* can't go on without this */
718                 errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val);
719
720         grp = GETGRGID(pwd->pw_gid);
721         pw_log(cnf, mode, W_USER, "%s(%ld):%s(%ld):%s:%s:%s",
722                pwd->pw_name, (long) pwd->pw_uid,
723             grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1),
724                pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
725
726         /*
727          * If adding, let's touch and chown the user's mail file. This is not
728          * strictly necessary under BSD with a 0755 maildir but it also
729          * doesn't hurt anything to create the empty mailfile
730          */
731         if (mode == M_ADD) {
732                 if (!PWALTDIR()) {
733                         sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
734                         close(open(line, O_RDWR | O_CREAT, 0600));      /* Preserve contents &
735                                                                          * mtime */
736                         chown(line, pwd->pw_uid, pwd->pw_gid);
737                 }
738         }
739
740         /*
741          * Let's create and populate the user's home directory. Note
742          * that this also `works' for editing users if -m is used, but
743          * existing files will *not* be overwritten.
744          */
745         if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
746                 copymkdir(pwd->pw_dir, cnf->dotdir, 0755, pwd->pw_uid, pwd->pw_gid);
747                 pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
748                        pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
749         }
750
751
752         /*
753          * Finally, send mail to the new user as well, if we are asked to
754          */
755         if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
756                 FILE           *pfp = popen(_PATH_SENDMAIL " -t", "w");
757                 
758                 if (pfp == NULL)
759                         warn("sendmail");
760                 else {
761                         fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
762                         while (fgets(line, sizeof(line), fp) != NULL) {
763                                 /* Do substitutions? */
764                                 fputs(line, pfp);
765                         }
766                         pclose(pfp);
767                         pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent",
768                             pwd->pw_name, (long) pwd->pw_uid);
769                 }
770                 fclose(fp);
771         }
772
773         return EXIT_SUCCESS;
774 }
775
776
777 static          uid_t
778 pw_uidpolicy(struct userconf * cnf, struct cargs * args)
779 {
780         struct passwd  *pwd;
781         uid_t           uid = (uid_t) - 1;
782         struct carg    *a_uid = getarg(args, 'u');
783
784         /*
785          * Check the given uid, if any
786          */
787         if (a_uid != NULL) {
788                 uid = (uid_t) atol(a_uid->val);
789
790                 if ((pwd = GETPWUID(uid)) != NULL && getarg(args, 'o') == NULL)
791                         errx(EX_DATAERR, "uid `%ld' has already been allocated", (long) pwd->pw_uid);
792         } else {
793                 struct bitmap   bm;
794
795                 /*
796                  * We need to allocate the next available uid under one of
797                  * two policies a) Grab the first unused uid b) Grab the
798                  * highest possible unused uid
799                  */
800                 if (cnf->min_uid >= cnf->max_uid) {     /* Sanity
801                                                          * claus^H^H^H^Hheck */
802                         cnf->min_uid = 1000;
803                         cnf->max_uid = 32000;
804                 }
805                 bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
806
807                 /*
808                  * Now, let's fill the bitmap from the password file
809                  */
810                 SETPWENT();
811                 while ((pwd = GETPWENT()) != NULL)
812                         if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
813                                 bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
814                 ENDPWENT();
815
816                 /*
817                  * Then apply the policy, with fallback to reuse if necessary
818                  */
819                 if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
820                         uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
821
822                 /*
823                  * Another sanity check
824                  */
825                 if (uid < cnf->min_uid || uid > cnf->max_uid)
826                         errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
827                 bm_dealloc(&bm);
828         }
829         return uid;
830 }
831
832
833 static          uid_t
834 pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer)
835 {
836         struct group   *grp;
837         gid_t           gid = (uid_t) - 1;
838         struct carg    *a_gid = getarg(args, 'g');
839
840         /*
841          * If no arg given, see if default can help out
842          */
843         if (a_gid == NULL && cnf->default_group && *cnf->default_group)
844                 a_gid = addarg(args, 'g', cnf->default_group);
845
846         /*
847          * Check the given gid, if any
848          */
849         SETGRENT();
850         if (a_gid != NULL) {
851                 if ((grp = GETGRNAM(a_gid->val)) == NULL) {
852                         gid = (gid_t) atol(a_gid->val);
853                         if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL)
854                                 errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
855                 }
856                 gid = grp->gr_gid;
857         } else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
858                 gid = grp->gr_gid;  /* Already created? Use it anyway... */
859         } else {
860                 struct cargs    grpargs;
861                 char            tmp[32];
862
863                 LIST_INIT(&grpargs);
864                 addarg(&grpargs, 'n', nam);
865
866                 /*
867                  * We need to auto-create a group with the user's name. We
868                  * can send all the appropriate output to our sister routine
869                  * bit first see if we can create a group with gid==uid so we
870                  * can keep the user and group ids in sync. We purposely do
871                  * NOT check the gid range if we can force the sync. If the
872                  * user's name dups an existing group, then the group add
873                  * function will happily handle that case for us and exit.
874                  */
875                 if (GETGRGID(prefer) == NULL) {
876                         sprintf(tmp, "%lu", (unsigned long) prefer);
877                         addarg(&grpargs, 'g', tmp);
878                 }
879                 if (getarg(args, 'N'))
880                 {
881                         addarg(&grpargs, 'N', NULL);
882                         addarg(&grpargs, 'q', NULL);
883                         gid = pw_group(cnf, M_NEXT, &grpargs);
884                 }
885                 else
886                 {
887                         pw_group(cnf, M_ADD, &grpargs);
888                         if ((grp = GETGRNAM(nam)) != NULL)
889                                 gid = grp->gr_gid;
890                 }
891                 a_gid = grpargs.lh_first;
892                 while (a_gid != NULL) {
893                         struct carg    *t = a_gid->list.le_next;
894                         LIST_REMOVE(a_gid, list);
895                         a_gid = t;
896                 }
897         }
898         ENDGRENT();
899         return gid;
900 }
901
902
903 static          time_t
904 pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
905 {
906         time_t          result = 0;
907         time_t          now = time(NULL);
908         struct carg    *arg = getarg(args, 'p');
909
910         if (arg != NULL) {
911                 if ((result = parse_date(now, arg->val)) == now)
912                         errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
913         } else if (cnf->password_days > 0)
914                 result = now + ((long) cnf->password_days * 86400L);
915         return result;
916 }
917
918
919 static          time_t
920 pw_exppolicy(struct userconf * cnf, struct cargs * args)
921 {
922         time_t          result = 0;
923         time_t          now = time(NULL);
924         struct carg    *arg = getarg(args, 'e');
925
926         if (arg != NULL) {
927                 if ((result = parse_date(now, arg->val)) == now)
928                         errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
929         } else if (cnf->expire_days > 0)
930                 result = now + ((long) cnf->expire_days * 86400L);
931         return result;
932 }
933
934
935 static char    *
936 pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
937 {
938         struct carg    *arg = getarg(args, 'd');
939
940         if (arg)
941                 return arg->val;
942         else {
943                 static char     home[128];
944
945                 if (cnf->home == NULL || *cnf->home == '\0')
946                         errx(EX_CONFIG, "no base home directory set");
947                 sprintf(home, "%s/%s", cnf->home, user);
948                 return home;
949         }
950 }
951
952 static char    *
953 shell_path(char const * path, char *shells[], char *sh)
954 {
955         if (sh != NULL && (*sh == '/' || *sh == '\0'))
956                 return sh;      /* specified full path or forced none */
957         else {
958                 char           *p;
959                 char            paths[_UC_MAXLINE];
960
961                 /*
962                  * We need to search paths
963                  */
964                 strncpy(paths, path, sizeof paths);
965                 paths[sizeof paths - 1] = '\0';
966                 for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
967                         int             i;
968                         static char     shellpath[256];
969
970                         if (sh != NULL) {
971                                 sprintf(shellpath, "%s/%s", p, sh);
972                                 if (access(shellpath, X_OK) == 0)
973                                         return shellpath;
974                         } else
975                                 for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
976                                         sprintf(shellpath, "%s/%s", p, shells[i]);
977                                         if (access(shellpath, X_OK) == 0)
978                                                 return shellpath;
979                                 }
980                 }
981                 if (sh == NULL)
982                         errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
983                 errx(EX_CONFIG, "no default shell available or defined");
984                 return NULL;
985         }
986 }
987
988
989 static char    *
990 pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
991 {
992         char           *sh = newshell;
993         struct carg    *arg = getarg(args, 's');
994
995         if (newshell == NULL && arg != NULL)
996                 sh = arg->val;
997         return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
998 }
999
1000 static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
1001
1002 char           *
1003 pw_pwcrypt(char *password)
1004 {
1005         int             i;
1006         char            salt[12];
1007
1008         static char     buf[256];
1009
1010         /*
1011          * Calculate a salt value
1012          */
1013         for (i = 0; i < 8; i++)
1014                 salt[i] = chars[arc4random() % 63];
1015         salt[i] = '\0';
1016
1017         return strcpy(buf, crypt(password, salt));
1018 }
1019
1020 #if defined(USE_MD5RAND)
1021 u_char *
1022 pw_getrand(u_char *buf, int len)        /* cryptographically secure rng */
1023 {
1024         int i;
1025         for (i=0;i<len;i+=16) {
1026                 u_char ubuf[16];
1027
1028                 MD5_CTX md5_ctx;
1029                 struct timeval tv, tvo;
1030                 struct rusage ru;
1031                 int n=0;
1032                 int t;
1033
1034                 MD5Init (&md5_ctx);
1035                 t=getpid();
1036                 MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
1037                 t=getppid();
1038                 MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
1039                 gettimeofday (&tvo, NULL);
1040                 do {
1041                         getrusage (RUSAGE_SELF, &ru);
1042                         MD5Update (&md5_ctx, (u_char*)&ru, sizeof ru);
1043                         gettimeofday (&tv, NULL);
1044                         MD5Update (&md5_ctx, (u_char*)&tv, sizeof tv);
1045                 } while (n++<20 || tv.tv_usec-tvo.tv_usec<100*1000);
1046                 MD5Final (ubuf, &md5_ctx);
1047                 memcpy(buf+i, ubuf, MIN(16, len-i));
1048         }
1049         return buf;
1050 }
1051
1052 #else   /* Portable version */
1053
1054 static u_char *
1055 pw_getrand(u_char *buf, int len)
1056 {
1057         int i;
1058
1059         srandomdev();
1060         for (i = 0; i < len; i++) {
1061                 unsigned long val = random();
1062                 /* Use all bits in the random value */
1063                 buf[i]=(u_char)((val >> 24) ^ (val >> 16) ^ (val >> 8) ^ val);
1064         }
1065         return buf;
1066 }
1067
1068 #endif
1069
1070 static char    *
1071 pw_password(struct userconf * cnf, struct cargs * args, char const * user)
1072 {
1073         int             i, l;
1074         char            pwbuf[32];
1075         u_char          rndbuf[sizeof pwbuf];
1076
1077         switch (cnf->default_password) {
1078         case -1:                /* Random password */
1079                 l = (arc4random() % 8 + 8);     /* 8 - 16 chars */
1080                 pw_getrand(rndbuf, l);
1081                 for (i = 0; i < l; i++)
1082                         pwbuf[i] = chars[rndbuf[i] % (sizeof(chars)-1)];
1083                 pwbuf[i] = '\0';
1084
1085                 /*
1086                  * We give this information back to the user
1087                  */
1088                 if (getarg(args, 'h') == NULL && getarg(args, 'N') == NULL) {
1089                         if (isatty(STDOUT_FILENO))
1090                                 printf("Password for '%s' is: ", user);
1091                         printf("%s\n", pwbuf);
1092                         fflush(stdout);
1093                 }
1094                 break;
1095
1096         case -2:                /* No password at all! */
1097                 return "";
1098
1099         case 0:         /* No login - default */
1100         default:
1101                 return "*";
1102
1103         case 1:         /* user's name */
1104                 strncpy(pwbuf, user, sizeof pwbuf);
1105                 pwbuf[sizeof pwbuf - 1] = '\0';
1106                 break;
1107         }
1108         return pw_pwcrypt(pwbuf);
1109 }
1110
1111
1112 static int
1113 print_user(struct passwd * pwd, int pretty, int v7)
1114 {
1115         if (!pretty) {
1116                 char            buf[_UC_MAXLINE];
1117
1118                 fmtpwentry(buf, pwd, v7 ? PWF_PASSWD : PWF_STANDARD);
1119                 fputs(buf, stdout);
1120         } else {
1121                 int             j;
1122                 char           *p;
1123                 struct group   *grp = GETGRGID(pwd->pw_gid);
1124                 char            uname[60] = "User &", office[60] = "[None]",
1125                                 wphone[60] = "[None]", hphone[60] = "[None]";
1126                 char            acexpire[32] = "[None]", pwexpire[32] = "[None]";
1127                 struct tm *    tptr;
1128
1129                 if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
1130                         strncpy(uname, p, sizeof uname);
1131                         uname[sizeof uname - 1] = '\0';
1132                         if ((p = strtok(NULL, ",")) != NULL) {
1133                                 strncpy(office, p, sizeof office);
1134                                 office[sizeof office - 1] = '\0';
1135                                 if ((p = strtok(NULL, ",")) != NULL) {
1136                                         strncpy(wphone, p, sizeof wphone);
1137                                         wphone[sizeof wphone - 1] = '\0';
1138                                         if ((p = strtok(NULL, "")) != NULL) {
1139                                                 strncpy(hphone, p, sizeof hphone);
1140                                                 hphone[sizeof hphone - 1] = '\0';
1141                                         }
1142                                 }
1143                         }
1144                 }
1145                 /*
1146                  * Handle '&' in gecos field
1147                  */
1148                 if ((p = strchr(uname, '&')) != NULL) {
1149                         int             l = strlen(pwd->pw_name);
1150                         int             m = strlen(p);
1151
1152                         memmove(p + l, p + 1, m);
1153                         memmove(p, pwd->pw_name, l);
1154                         *p = (char) toupper((unsigned char)*p);
1155                 }
1156                 if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
1157                         strftime(acexpire, sizeof acexpire, "%c", tptr);
1158                 if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
1159                         strftime(pwexpire, sizeof pwexpire, "%c", tptr);
1160                 printf("Login Name: %-15s   #%-12ld Group: %-15s   #%ld\n"
1161                        " Full Name: %s\n"
1162                        "      Home: %-26.26s      Class: %s\n"
1163                        "     Shell: %-26.26s     Office: %s\n"
1164                        "Work Phone: %-26.26s Home Phone: %s\n"
1165                        "Acc Expire: %-26.26s Pwd Expire: %s\n",
1166                        pwd->pw_name, (long) pwd->pw_uid,
1167                        grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid,
1168                        uname, pwd->pw_dir, pwd->pw_class,
1169                        pwd->pw_shell, office, wphone, hphone,
1170                        acexpire, pwexpire);
1171                 SETGRENT();
1172                 j = 0;
1173                 while ((grp=GETGRENT()) != NULL)
1174                 {
1175                         int     i = 0;
1176                         while (grp->gr_mem[i] != NULL)
1177                         {
1178                                 if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
1179                                 {
1180                                         printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
1181                                         break;
1182                                 }
1183                                 ++i;
1184                         }
1185                 }
1186                 ENDGRENT();
1187                 printf("%s", j ? "\n" : "");
1188         }
1189         return EXIT_SUCCESS;
1190 }
1191
1192 char    *
1193 pw_checkname(u_char *name, int gecos)
1194 {
1195         char showch[8];
1196         u_char const *badchars, *ch, *showtype;
1197         int reject;
1198
1199         ch = name;
1200         reject = 0;
1201         if (gecos) {
1202                 /* See if the name is valid as a gecos (comment) field. */
1203                 badchars = ":!@";
1204                 showtype = "gecos field";
1205         } else {
1206                 /* See if the name is valid as a userid or group. */
1207                 badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
1208                 showtype = "userid/group name";
1209                 /* Userids and groups can not have a leading '-'. */
1210                 if (*ch == '-')
1211                         reject = 1;
1212         }
1213         if (!reject) {
1214                 while (*ch) {
1215                         if (strchr(badchars, *ch) != NULL || *ch < ' ' ||
1216                             *ch == 127) {
1217                                 reject = 1;
1218                                 break;
1219                         }
1220                         /* 8-bit characters are only allowed in GECOS fields */
1221                         if (!gecos && (*ch & 0x80)) {
1222                                 reject = 1;
1223                                 break;
1224                         }
1225                         ch++;
1226                 }
1227         }
1228         /*
1229          * A `$' is allowed as the final character for userids and groups,
1230          * mainly for the benefit of samba.
1231          */
1232         if (reject && !gecos) {
1233                 if (*ch == '$' && *(ch + 1) == '\0') {
1234                         reject = 0;
1235                         ch++;
1236                 }
1237         }
1238         if (reject) {
1239                 snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
1240                     ? "`%c'" : "0x%02x", *ch);
1241                 errx(EX_DATAERR, "invalid character %s at position %d in %s",
1242                     showch, (ch - name), showtype);
1243         }
1244         if (!gecos && (ch - name) > LOGNAMESIZE)
1245                 errx(EX_DATAERR, "name too long `%s' (max is %d)", name,
1246                     LOGNAMESIZE);
1247         return (char *)name;
1248 }
1249
1250
1251 static void
1252 rmat(uid_t uid)
1253 {
1254         DIR            *d = opendir("/var/at/jobs");
1255
1256         if (d != NULL) {
1257                 struct dirent  *e;
1258
1259                 while ((e = readdir(d)) != NULL) {
1260                         struct stat     st;
1261
1262                         if (strncmp(e->d_name, ".lock", 5) != 0 &&
1263                             stat(e->d_name, &st) == 0 &&
1264                             !S_ISDIR(st.st_mode) &&
1265                             st.st_uid == uid) {
1266                                 char            tmp[MAXPATHLEN];
1267
1268                                 sprintf(tmp, "/usr/bin/atrm %s", e->d_name);
1269                                 system(tmp);
1270                         }
1271                 }
1272                 closedir(d);
1273         }
1274 }
1275
1276 static void
1277 rmskey(char const * name)
1278 {
1279         static const char etcskey[] = "/etc/skeykeys";
1280         FILE   *fp = fopen(etcskey, "r+");
1281
1282         if (fp != NULL) {
1283                 char    tmp[1024];
1284                 off_t   atofs = 0;
1285                 int     length = strlen(name);
1286
1287                 while (fgets(tmp, sizeof tmp, fp) != NULL) {
1288                         if (strncmp(name, tmp, length) == 0 && tmp[length]==' ') {
1289                                 if (fseek(fp, atofs, SEEK_SET) == 0) {
1290                                         fwrite("#", 1, 1, fp);  /* Comment username out */
1291                                 }
1292                                 break;
1293                         }
1294                         atofs = ftell(fp);
1295                 }
1296                 /*
1297                  * If we got an error of any sort, don't update!
1298                  */
1299                 fclose(fp);
1300         }
1301 }
1302