xref: /dflybsd-src/usr.sbin/pw/pw_user.c (revision 4238ce6f0c6df33ce677ae298b245c62cd60fb43)
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) {	/* Already checked this */
466  			if (strcmp(pwd->pw_name, "root") == 0)
467  				errx(EX_DATAERR, "can't change gid of `root' account");
468  			gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid;
469  			if (newgid != pwd->pw_gid) {
470  				edited = 1;
471  				pwd->pw_gid = newgid;
472  			}
473  		}
474  
475  		if ((arg = getarg(args, 'p')) != NULL) {
476  			if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
477  				if (pwd->pw_change != 0) {
478  					pwd->pw_change = 0;
479  					edited = 1;
480  				}
481  			}
482  			else {
483  				time_t          now = time(NULL);
484  				time_t          expire = parse_date(now, arg->val);
485  
486  				if (now == expire)
487  					errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
488  				if (pwd->pw_change != expire) {
489  					pwd->pw_change = expire;
490  					edited = 1;
491  				}
492  			}
493  		}
494  
495  		if ((arg = getarg(args, 'e')) != NULL) {
496  			if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
497  				if (pwd->pw_expire != 0) {
498  					pwd->pw_expire = 0;
499  					edited = 1;
500  				}
501  			}
502  			else {
503  				time_t          now = time(NULL);
504  				time_t          expire = parse_date(now, arg->val);
505  
506  				if (now == expire)
507  					errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
508  				if (pwd->pw_expire != expire) {
509  					pwd->pw_expire = expire;
510  					edited = 1;
511  				}
512  			}
513  		}
514  
515  		if ((arg = getarg(args, 's')) != NULL) {
516  			char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
517  			if (shell == NULL)
518  				shell = "";
519  			if (strcmp(shell, pwd->pw_shell) != 0) {
520  				pwd->pw_shell = shell;
521  				edited = 1;
522  			}
523  		}
524  
525  		if (getarg(args, 'L')) {
526  			if (cnf->default_class == NULL)
527  				cnf->default_class = "";
528  			if (strcmp(pwd->pw_class, cnf->default_class) != 0) {
529  				pwd->pw_class = cnf->default_class;
530  				edited = 1;
531  			}
532  		}
533  
534  		if ((arg  = getarg(args, 'd')) != NULL) {
535  			if (strcmp(pwd->pw_dir, arg->val))
536  				edited = 1;
537  			if (stat(pwd->pw_dir = arg->val, &st) == -1) {
538  				if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0)
539  				  warnx("WARNING: home `%s' does not exist", pwd->pw_dir);
540  			} else if (!S_ISDIR(st.st_mode))
541  				warnx("WARNING: home `%s' is not a directory", pwd->pw_dir);
542  		}
543  
544  		if ((arg = getarg(args, 'w')) != NULL &&
545  		    getarg(args, 'h') == 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  	    (arg = getarg(args, 'H')) != NULL) {
605  		if (strcmp(arg->val, "-") == 0) {
606  			if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
607  				pwd->pw_passwd = "*";	/* No access */
608  				edited = 1;
609  			}
610  		} else {
611  			int             fd = atoi(arg->val);
612  			int		precrypt = (arg->ch == 'H');
613  			int             b;
614  			int             istty = isatty(fd);
615  			struct termios  t;
616  			login_cap_t	*lc;
617  
618  			if (istty) {
619  				if (tcgetattr(fd, &t) == -1)
620  					istty = 0;
621  				else {
622  					struct termios  n = t;
623  
624  					/* Disable echo */
625  					n.c_lflag &= ~(ECHO);
626  					tcsetattr(fd, TCSANOW, &n);
627  					printf("%s%spassword for user %s:",
628  					     (mode == M_UPDATE) ? "new " : "",
629  					     precrypt ? "encrypted " : "",
630  					     pwd->pw_name);
631  					fflush(stdout);
632  				}
633  			}
634  			b = read(fd, line, sizeof(line) - 1);
635  			if (istty) {	/* Restore state */
636  				tcsetattr(fd, TCSANOW, &t);
637  				fputc('\n', stdout);
638  				fflush(stdout);
639  			}
640  			if (b < 0) {
641  				warn("-%c file descriptor", precrypt ? 'H' :
642  				    'h');
643  				return EX_IOERR;
644  			}
645  			line[b] = '\0';
646  			if ((p = strpbrk(line, "\r\n")) != NULL)
647  				*p = '\0';
648  			if (!*line)
649  				errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
650  			if (precrypt) {
651  				if (strchr(line, ':') != NULL)
652  					return EX_DATAERR;
653  				pwd->pw_passwd = line;
654  			} else {
655  				lc = login_getpwclass(pwd);
656  				if (lc == NULL ||
657  				    login_setcryptfmt(lc, "md5", NULL) == NULL)
658  					warn("setting crypt(3) format");
659  				login_close(lc);
660  				pwd->pw_passwd = pw_pwcrypt(line);
661  			}
662  			edited = 1;
663  		}
664  	}
665  
666  	/*
667  	 * Special case: -N only displays & exits
668  	 */
669  	if (getarg(args, 'N') != NULL)
670  		return print_user(pwd,
671  				  getarg(args, 'P') != NULL,
672  				  getarg(args, '7') != NULL);
673  
674  	if (mode == M_ADD) {
675  		edited = 1;	/* Always */
676  		rc = addpwent(pwd);
677  		if (rc == -1) {
678  			warnx("user '%s' already exists", pwd->pw_name);
679  			return EX_IOERR;
680  		} else if (rc != 0) {
681  			warn("passwd file update");
682  			return EX_IOERR;
683  		}
684  		if (cnf->nispasswd && *cnf->nispasswd=='/') {
685  			rc = addnispwent(cnf->nispasswd, pwd);
686  			if (rc == -1)
687  				warnx("User '%s' already exists in NIS passwd", pwd->pw_name);
688  			else
689  				warn("NIS passwd update");
690  			/* NOTE: we treat NIS-only update errors as non-fatal */
691  		}
692  	} else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) {
693  		if (edited) {	/* Only updated this if required */
694  			rc = chgpwent(a_name->val, pwd);
695  			if (rc == -1) {
696  				warnx("user '%s' does not exist (NIS?)", pwd->pw_name);
697  				return EX_IOERR;
698  			} else if (rc != 0) {
699  				warn("passwd file update");
700  				return EX_IOERR;
701  			}
702  			if ( cnf->nispasswd && *cnf->nispasswd=='/') {
703  				rc = chgnispwent(cnf->nispasswd, a_name->val, pwd);
704  				if (rc == -1)
705  					warn("User '%s' not found in NIS passwd", pwd->pw_name);
706  				else
707  					warn("NIS passwd update");
708  				/* NOTE: NIS-only update errors are not fatal */
709  			}
710  		}
711  	}
712  
713  	/*
714  	 * Ok, user is created or changed - now edit group file
715  	 */
716  
717  	if (mode == M_ADD || getarg(args, 'G') != NULL)
718  		editgroups(pwd->pw_name, cnf->groups);
719  
720  	/* go get a current version of pwd */
721  	pwd = GETPWNAM(a_name->val);
722  	if (pwd == NULL) {
723  		/* This will fail when we rename, so special case that */
724  		if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) {
725  			a_name->val = arg->val;		/* update new name */
726  			pwd = GETPWNAM(a_name->val);	/* refetch renamed rec */
727  		}
728  	}
729  	if (pwd == NULL)	/* can't go on without this */
730  		errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val);
731  
732  	grp = GETGRGID(pwd->pw_gid);
733  	pw_log(cnf, mode, W_USER, "%s(%ld):%s(%ld):%s:%s:%s",
734  	       pwd->pw_name, (long) pwd->pw_uid,
735  	    grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1),
736  	       pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
737  
738  	/*
739  	 * If adding, let's touch and chown the user's mail file. This is not
740  	 * strictly necessary under BSD with a 0755 maildir but it also
741  	 * doesn't hurt anything to create the empty mailfile
742  	 */
743  	if (mode == M_ADD) {
744  		if (!PWALTDIR()) {
745  			sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
746  			close(open(line, O_RDWR | O_CREAT, 0600));	/* Preserve contents &
747  									 * mtime */
748  			chown(line, pwd->pw_uid, pwd->pw_gid);
749  		}
750  	}
751  
752  	/*
753  	 * Let's create and populate the user's home directory. Note
754  	 * that this also `works' for editing users if -m is used, but
755  	 * existing files will *not* be overwritten.
756  	 */
757  	if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
758  		copymkdir(pwd->pw_dir, cnf->dotdir, cnf->homemode, pwd->pw_uid, pwd->pw_gid);
759  		pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
760  		       pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
761  	}
762  
763  
764  	/*
765  	 * Finally, send mail to the new user as well, if we are asked to
766  	 */
767  	if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
768  		FILE           *pfp = popen(_PATH_SENDMAIL " -t", "w");
769  
770  		if (pfp == NULL)
771  			warn("sendmail");
772  		else {
773  			fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
774  			while (fgets(line, sizeof(line), fp) != NULL) {
775  				/* Do substitutions? */
776  				fputs(line, pfp);
777  			}
778  			pclose(pfp);
779  			pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent",
780  			    pwd->pw_name, (long) pwd->pw_uid);
781  		}
782  		fclose(fp);
783  	}
784  
785  	return EXIT_SUCCESS;
786  }
787  
788  
789  static          uid_t
790  pw_uidpolicy(struct userconf * cnf, struct cargs * args)
791  {
792  	struct passwd  *pwd;
793  	uid_t           uid = (uid_t) - 1;
794  	struct carg    *a_uid = getarg(args, 'u');
795  
796  	/*
797  	 * Check the given uid, if any
798  	 */
799  	if (a_uid != NULL) {
800  		uid = (uid_t) atol(a_uid->val);
801  
802  		if ((pwd = GETPWUID(uid)) != NULL && getarg(args, 'o') == NULL)
803  			errx(EX_DATAERR, "uid `%ld' has already been allocated", (long) pwd->pw_uid);
804  	} else {
805  		struct bitmap   bm;
806  
807  		/*
808  		 * We need to allocate the next available uid under one of
809  		 * two policies a) Grab the first unused uid b) Grab the
810  		 * highest possible unused uid
811  		 */
812  		if (cnf->min_uid >= cnf->max_uid) {	/* Sanity
813  							 * claus^H^H^H^Hheck */
814  			cnf->min_uid = 1000;
815  			cnf->max_uid = 32000;
816  		}
817  		bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
818  
819  		/*
820  		 * Now, let's fill the bitmap from the password file
821  		 */
822  		SETPWENT();
823  		while ((pwd = GETPWENT()) != NULL)
824  			if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
825  				bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
826  		ENDPWENT();
827  
828  		/*
829  		 * Then apply the policy, with fallback to reuse if necessary
830  		 */
831  		if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
832  			uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
833  
834  		/*
835  		 * Another sanity check
836  		 */
837  		if (uid < cnf->min_uid || uid > cnf->max_uid)
838  			errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
839  		bm_dealloc(&bm);
840  	}
841  	return uid;
842  }
843  
844  
845  static          uid_t
846  pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer)
847  {
848  	struct group   *grp;
849  	gid_t           gid = (uid_t) - 1;
850  	struct carg    *a_gid = getarg(args, 'g');
851  
852  	/*
853  	 * If no arg given, see if default can help out
854  	 */
855  	if (a_gid == NULL && cnf->default_group && *cnf->default_group)
856  		a_gid = addarg(args, 'g', cnf->default_group);
857  
858  	/*
859  	 * Check the given gid, if any
860  	 */
861  	SETGRENT();
862  	if (a_gid != NULL) {
863  		if ((grp = GETGRNAM(a_gid->val)) == NULL) {
864  			gid = (gid_t) atol(a_gid->val);
865  			if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL)
866  				errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
867  		}
868  		gid = grp->gr_gid;
869  	} else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
870  		gid = grp->gr_gid;  /* Already created? Use it anyway... */
871  	} else {
872  		struct cargs    grpargs;
873  		char            tmp[32];
874  
875  		LIST_INIT(&grpargs);
876  		addarg(&grpargs, 'n', nam);
877  
878  		/*
879  		 * We need to auto-create a group with the user's name. We
880  		 * can send all the appropriate output to our sister routine
881  		 * bit first see if we can create a group with gid==uid so we
882  		 * can keep the user and group ids in sync. We purposely do
883  		 * NOT check the gid range if we can force the sync. If the
884  		 * user's name dups an existing group, then the group add
885  		 * function will happily handle that case for us and exit.
886  		 */
887  		if (GETGRGID(prefer) == NULL) {
888  			sprintf(tmp, "%lu", (unsigned long) prefer);
889  			addarg(&grpargs, 'g', tmp);
890  		}
891  		if (getarg(args, 'N'))
892  		{
893  			addarg(&grpargs, 'N', NULL);
894  			addarg(&grpargs, 'q', NULL);
895  			gid = pw_group(cnf, M_NEXT, &grpargs);
896  		}
897  		else
898  		{
899  			pw_group(cnf, M_ADD, &grpargs);
900  			if ((grp = GETGRNAM(nam)) != NULL)
901  				gid = grp->gr_gid;
902  		}
903  		a_gid = LIST_FIRST(&grpargs);
904  		while (a_gid != NULL) {
905  			struct carg    *t = LIST_NEXT(a_gid, list);
906  			LIST_REMOVE(a_gid, list);
907  			a_gid = t;
908  		}
909  	}
910  	ENDGRENT();
911  	return gid;
912  }
913  
914  
915  static          time_t
916  pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
917  {
918  	time_t          result = 0;
919  	time_t          now = time(NULL);
920  	struct carg    *arg = getarg(args, 'p');
921  
922  	if (arg != NULL) {
923  		if ((result = parse_date(now, arg->val)) == now)
924  			errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
925  	} else if (cnf->password_days > 0)
926  		result = now + ((long) cnf->password_days * 86400L);
927  	return result;
928  }
929  
930  
931  static          time_t
932  pw_exppolicy(struct userconf * cnf, struct cargs * args)
933  {
934  	time_t          result = 0;
935  	time_t          now = time(NULL);
936  	struct carg    *arg = getarg(args, 'e');
937  
938  	if (arg != NULL) {
939  		if ((result = parse_date(now, arg->val)) == now)
940  			errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
941  	} else if (cnf->expire_days > 0)
942  		result = now + ((long) cnf->expire_days * 86400L);
943  	return result;
944  }
945  
946  
947  static char    *
948  pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
949  {
950  	struct carg    *arg = getarg(args, 'd');
951  
952  	if (arg)
953  		return arg->val;
954  	else {
955  		static char     home[128];
956  
957  		if (cnf->home == NULL || *cnf->home == '\0')
958  			errx(EX_CONFIG, "no base home directory set");
959  		sprintf(home, "%s/%s", cnf->home, user);
960  		return home;
961  	}
962  }
963  
964  static char    *
965  shell_path(char const * path, char *shells[], char *sh)
966  {
967  	if (sh != NULL && (*sh == '/' || *sh == '\0'))
968  		return sh;	/* specified full path or forced none */
969  	else {
970  		char           *p;
971  		char            paths[_UC_MAXLINE];
972  
973  		/*
974  		 * We need to search paths
975  		 */
976  		strlcpy(paths, path, sizeof(paths));
977  		for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
978  			int             i;
979  			static char     shellpath[256];
980  
981  			if (sh != NULL) {
982  				sprintf(shellpath, "%s/%s", p, sh);
983  				if (access(shellpath, X_OK) == 0)
984  					return shellpath;
985  			} else
986  				for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
987  					sprintf(shellpath, "%s/%s", p, shells[i]);
988  					if (access(shellpath, X_OK) == 0)
989  						return shellpath;
990  				}
991  		}
992  		if (sh == NULL)
993  			errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
994  		errx(EX_CONFIG, "no default shell available or defined");
995  		return NULL;
996  	}
997  }
998  
999  
1000  static char    *
1001  pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
1002  {
1003  	char           *sh = newshell;
1004  	struct carg    *arg = getarg(args, 's');
1005  
1006  	if (newshell == NULL && arg != NULL)
1007  		sh = arg->val;
1008  	return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
1009  }
1010  
1011  #define	SALTSIZE	32
1012  
1013  static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
1014  
1015  char           *
1016  pw_pwcrypt(char *password)
1017  {
1018  	int             i;
1019  	char            salt[SALTSIZE + 1];
1020  	char		*cryptpw;
1021  
1022  	static char     buf[256];
1023  
1024  	/*
1025  	 * Calculate a salt value
1026  	 */
1027  	for (i = 0; i < SALTSIZE; i++)
1028  		salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
1029  	salt[SALTSIZE] = '\0';
1030  
1031  	cryptpw = crypt(password, salt);
1032  	if (cryptpw == NULL)
1033  		errx(EX_CONFIG, "crypt(3) failure");
1034  	return strcpy(buf, cryptpw);
1035  }
1036  
1037  
1038  static char    *
1039  pw_password(struct userconf * cnf, struct cargs * args, char const * user)
1040  {
1041  	int             i, l;
1042  	char            pwbuf[32];
1043  
1044  	switch (cnf->default_password) {
1045  	case -1:		/* Random password */
1046  		l = (arc4random() % 8 + 8);	/* 8 - 16 chars */
1047  		for (i = 0; i < l; i++)
1048  			pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
1049  		pwbuf[i] = '\0';
1050  
1051  		/*
1052  		 * We give this information back to the user
1053  		 */
1054  		if (getarg(args, 'h') == NULL && getarg(args, 'H') == NULL &&
1055  		    getarg(args, 'N') == NULL) {
1056  			if (isatty(STDOUT_FILENO))
1057  				printf("Password for '%s' is: ", user);
1058  			printf("%s\n", pwbuf);
1059  			fflush(stdout);
1060  		}
1061  		break;
1062  
1063  	case -2:		/* No password at all! */
1064  		return "";
1065  
1066  	case 0:		/* No login - default */
1067  	default:
1068  		return "*";
1069  
1070  	case 1:		/* user's name */
1071  		strlcpy(pwbuf, user, sizeof(pwbuf));
1072  		break;
1073  	}
1074  	return pw_pwcrypt(pwbuf);
1075  }
1076  
1077  
1078  static int
1079  print_user(struct passwd * pwd, int pretty, int v7)
1080  {
1081  	if (!pretty) {
1082  		char            buf[_UC_MAXLINE];
1083  
1084  		fmtpwentry(buf, pwd, v7 ? PWF_PASSWD : PWF_STANDARD);
1085  		fputs(buf, stdout);
1086  	} else {
1087  		int		j;
1088  		char           *p;
1089  		struct group   *grp = GETGRGID(pwd->pw_gid);
1090  		char            uname[60] = "User &", office[60] = "[None]",
1091  		                wphone[60] = "[None]", hphone[60] = "[None]";
1092  		char		acexpire[32] = "[None]", pwexpire[32] = "[None]";
1093  		struct tm *    tptr;
1094  
1095  		if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
1096  			strlcpy(uname, p, sizeof(uname));
1097  			if ((p = strtok(NULL, ",")) != NULL) {
1098  				strlcpy(office, p, sizeof(office));
1099  				if ((p = strtok(NULL, ",")) != NULL) {
1100  					strlcpy(wphone, p, sizeof(wphone));
1101  					if ((p = strtok(NULL, "")) != NULL) {
1102  						strlcpy(hphone, p,
1103  						    sizeof(hphone));
1104  					}
1105  				}
1106  			}
1107  		}
1108  		/*
1109  		 * Handle '&' in gecos field
1110  		 */
1111  		if ((p = strchr(uname, '&')) != NULL) {
1112  			int             l = strlen(pwd->pw_name);
1113  			int             m = strlen(p);
1114  
1115  			memmove(p + l, p + 1, m);
1116  			memmove(p, pwd->pw_name, l);
1117  			*p = (char) toupper((unsigned char)*p);
1118  		}
1119  		if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
1120  			strftime(acexpire, sizeof acexpire, "%c", tptr);
1121  		if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
1122  			strftime(pwexpire, sizeof pwexpire, "%c", tptr);
1123  		printf("Login Name: %-15s   #%-12ld Group: %-15s   #%ld\n"
1124  		       " Full Name: %s\n"
1125  		       "      Home: %-26.26s      Class: %s\n"
1126  		       "     Shell: %-26.26s     Office: %s\n"
1127  		       "Work Phone: %-26.26s Home Phone: %s\n"
1128  		       "Acc Expire: %-26.26s Pwd Expire: %s\n",
1129  		       pwd->pw_name, (long) pwd->pw_uid,
1130  		       grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid,
1131  		       uname, pwd->pw_dir, pwd->pw_class,
1132  		       pwd->pw_shell, office, wphone, hphone,
1133  		       acexpire, pwexpire);
1134  	        SETGRENT();
1135  		j = 0;
1136  		while ((grp=GETGRENT()) != NULL)
1137  		{
1138  			int     i = 0;
1139  			while (grp->gr_mem[i] != NULL)
1140  			{
1141  				if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
1142  				{
1143  					printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
1144  					break;
1145  				}
1146  				++i;
1147  			}
1148  		}
1149  		ENDGRENT();
1150  		printf("%s", j ? "\n" : "");
1151  	}
1152  	return EXIT_SUCCESS;
1153  }
1154  
1155  char    *
1156  pw_checkname(u_char *name, int gecos)
1157  {
1158  	char showch[8];
1159  	u_char const *badchars, *ch, *showtype;
1160  	int reject;
1161  
1162  	ch = name;
1163  	reject = 0;
1164  	if (gecos) {
1165  		/* See if the name is valid as a gecos (comment) field. */
1166  		badchars = ":!@";
1167  		showtype = "gecos field";
1168  	} else {
1169  		/* See if the name is valid as a userid or group. */
1170  		badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
1171  		showtype = "userid/group name";
1172  		/* Userids and groups can not have a leading '-'. */
1173  		if (*ch == '-')
1174  			reject = 1;
1175  	}
1176  	if (!reject) {
1177  		while (*ch) {
1178  			if (strchr(badchars, *ch) != NULL || *ch < ' ' ||
1179  			    *ch == 127) {
1180  				reject = 1;
1181  				break;
1182  			}
1183  			/* 8-bit characters are only allowed in GECOS fields */
1184  			if (!gecos && (*ch & 0x80)) {
1185  				reject = 1;
1186  				break;
1187  			}
1188  			ch++;
1189  		}
1190  	}
1191  	/*
1192  	 * A `$' is allowed as the final character for userids and groups,
1193  	 * mainly for the benefit of samba.
1194  	 */
1195  	if (reject && !gecos) {
1196  		if (*ch == '$' && *(ch + 1) == '\0') {
1197  			reject = 0;
1198  			ch++;
1199  		}
1200  	}
1201  	if (reject) {
1202  		snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
1203  		    ? "`%c'" : "0x%02x", *ch);
1204  		errx(EX_DATAERR, "invalid character %s at position %td in %s",
1205  		    showch, (ch - name), showtype);
1206  	}
1207  	if (!gecos && (ch - name) > LOGNAMESIZE)
1208  		errx(EX_DATAERR, "name too long `%s' (max is %d)", name,
1209  		    LOGNAMESIZE);
1210  	return (char *)name;
1211  }
1212  
1213  
1214  static void
1215  rmat(uid_t uid)
1216  {
1217  	DIR            *d = opendir("/var/at/jobs");
1218  
1219  	if (d != NULL) {
1220  		struct dirent  *e;
1221  
1222  		while ((e = readdir(d)) != NULL) {
1223  			struct stat     st;
1224  
1225  			if (strncmp(e->d_name, ".lock", 5) != 0 &&
1226  			    stat(e->d_name, &st) == 0 &&
1227  			    !S_ISDIR(st.st_mode) &&
1228  			    st.st_uid == uid) {
1229  				char            tmp[MAXPATHLEN];
1230  
1231  				sprintf(tmp, "/usr/bin/atrm %s", e->d_name);
1232  				system(tmp);
1233  			}
1234  		}
1235  		closedir(d);
1236  	}
1237  }
1238