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