user.c revision 1.25 1 /* $NetBSD: user.c,v 1.25 2000/10/11 20:23:58 is Exp $ */
2
3 /*
4 * Copyright (c) 1999 Alistair G. Crooks. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Alistair G. Crooks.
17 * 4. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior written
19 * permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 #include <sys/cdefs.h>
34
35 #ifndef lint
36 __COPYRIGHT(
37 "@(#) Copyright (c) 1999 \
38 The NetBSD Foundation, Inc. All rights reserved.");
39 __RCSID("$NetBSD: user.c,v 1.25 2000/10/11 20:23:58 is Exp $");
40 #endif
41
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/stat.h>
45
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <err.h>
49 #include <fcntl.h>
50 #include <grp.h>
51 #include <paths.h>
52 #include <pwd.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <time.h>
58 #include <unistd.h>
59 #include <util.h>
60
61 #include "defs.h"
62 #include "usermgmt.h"
63
64 /* this struct describes a uid range */
65 typedef struct range_t {
66 int r_from; /* low uid */
67 int r_to; /* high uid */
68 } range_t;
69
70 /* this struct encapsulates the user information */
71 typedef struct user_t {
72 int u_uid; /* uid of user */
73 char *u_password; /* encrypted password */
74 char *u_comment; /* comment field */
75 int u_homeset; /* home dir has been set */
76 char *u_home; /* home directory */
77 char *u_primgrp; /* primary group */
78 int u_groupc; /* # of secondary groups */
79 char *u_groupv[NGROUPS_MAX]; /* secondary groups */
80 char *u_shell; /* user's shell */
81 char *u_basedir; /* base directory for home */
82 char *u_expire; /* when password will expire */
83 int u_inactive; /* inactive */
84 int u_mkdir; /* make the home directory */
85 int u_dupuid; /* duplicate uids are allowed */
86 char *u_skeldir; /* directory for startup files */
87 unsigned u_rsize; /* size of range array */
88 unsigned u_rc; /* # of ranges */
89 range_t *u_rv; /* the ranges */
90 unsigned u_defrc; /* # of ranges in defaults */
91 int u_preserve; /* preserve uids on deletion */
92 } user_t;
93
94 #define CONFFILE "/etc/usermgmt.conf"
95
96 #ifndef DEF_GROUP
97 #define DEF_GROUP "users"
98 #endif
99
100 #ifndef DEF_BASEDIR
101 #define DEF_BASEDIR "/home"
102 #endif
103
104 #ifndef DEF_SKELDIR
105 #define DEF_SKELDIR "/etc/skel"
106 #endif
107
108 #ifndef DEF_SHELL
109 #define DEF_SHELL _PATH_CSHELL
110 #endif
111
112 #ifndef DEF_COMMENT
113 #define DEF_COMMENT ""
114 #endif
115
116 #ifndef DEF_LOWUID
117 #define DEF_LOWUID 1000
118 #endif
119
120 #ifndef DEF_HIGHUID
121 #define DEF_HIGHUID 60000
122 #endif
123
124 #ifndef DEF_INACTIVE
125 #define DEF_INACTIVE 0
126 #endif
127
128 #ifndef DEF_EXPIRE
129 #define DEF_EXPIRE NULL
130 #endif
131
132 #ifndef WAITSECS
133 #define WAITSECS 10
134 #endif
135
136 #ifndef NOBODY_UID
137 #define NOBODY_UID 32767
138 #endif
139
140 /* some useful constants */
141 enum {
142 MaxShellNameLen = 256,
143 MaxFileNameLen = MAXPATHLEN,
144 MaxUserNameLen = 32,
145 MaxFieldNameLen = 32,
146 MaxCommandLen = 2048,
147 MaxEntryLen = 2048,
148 PasswordLength = 13,
149
150 LowGid = DEF_LOWUID,
151 HighGid = DEF_HIGHUID
152 };
153
154 /* Full paths of programs used here */
155 #define CHOWN "/usr/sbin/chown"
156 #define MKDIR "/bin/mkdir"
157 #define MV "/bin/mv"
158 #define NOLOGIN "/sbin/nologin"
159 #define PAX "/bin/pax"
160 #define RM "/bin/rm"
161
162 #define UNSET_EXPIRY "Null (unset)"
163
164 static int asystem(const char *fmt, ...)
165 __attribute__((__format__(__printf__, 1, 2)));
166
167 static int verbose;
168
169 /* if *cpp is non-null, free it, then assign `n' chars of `s' to it */
170 static void
171 memsave(char **cpp, char *s, size_t n)
172 {
173 if (*cpp != NULL) {
174 FREE(*cpp);
175 }
176 NEWARRAY(char, *cpp, n + 1, exit(1));
177 (void) memcpy(*cpp, s, n);
178 (*cpp)[n] = '\0';
179 }
180
181 /* a replacement for system(3) */
182 static int
183 asystem(const char *fmt, ...)
184 {
185 va_list vp;
186 char buf[MaxCommandLen];
187 int ret;
188
189 va_start(vp, fmt);
190 (void) vsnprintf(buf, sizeof(buf), fmt, vp);
191 va_end(vp);
192 if (verbose) {
193 (void) printf("Command: %s\n", buf);
194 }
195 if ((ret = system(buf)) != 0) {
196 warnx("[Warning] can't system `%s'", buf);
197 }
198 return ret;
199 }
200
201 #define NetBSD_1_4_K 104110000
202
203 #if defined(__NetBSD_Version__) && (__NetBSD_Version__ < NetBSD_1_4_K)
204 /* bounds checking strncpy */
205 static int
206 strlcpy(char *to, char *from, size_t tosize)
207 {
208 size_t n;
209 int fromsize;
210
211 fromsize = strlen(from);
212 n = MIN(tosize - 1, fromsize);
213 (void) memcpy(to, from, n);
214 to[n] = '\0';
215 return fromsize;
216 }
217 #endif
218
219 /* return 1 if all of `s' is numeric */
220 static int
221 is_number(char *s)
222 {
223 for ( ; *s ; s++) {
224 if (!isdigit(*s)) {
225 return 0;
226 }
227 }
228 return 1;
229 }
230
231 /*
232 * check that the effective uid is 0 - called from funcs which will
233 * modify data and config files.
234 */
235 static void
236 checkeuid(void)
237 {
238 if (geteuid() != 0) {
239 errx(EXIT_FAILURE, "Program must be run as root");
240 }
241 }
242
243 /* copy any dot files into the user's home directory */
244 static int
245 copydotfiles(char *skeldir, int uid, int gid, char *dir)
246 {
247 struct dirent *dp;
248 DIR *dirp;
249 int n;
250
251 if ((dirp = opendir(skeldir)) == NULL) {
252 warn("can't open source . files dir `%s'", skeldir);
253 return 0;
254 }
255 for (n = 0; (dp = readdir(dirp)) != NULL && n == 0 ; ) {
256 if (strcmp(dp->d_name, ".") == 0 ||
257 strcmp(dp->d_name, "..") == 0) {
258 continue;
259 }
260 n = 1;
261 }
262 (void) closedir(dirp);
263 if (n == 0) {
264 warnx("No \"dot\" initialisation files found");
265 } else {
266 (void) asystem("cd %s; %s -rw -pe %s . %s",
267 skeldir, PAX, (verbose) ? "-v" : "", dir);
268 }
269 (void) asystem("%s -R -h %d:%d %s", CHOWN, uid, gid, dir);
270 return n;
271 }
272
273 /* create a group entry with gid `gid' */
274 static int
275 creategid(char *group, int gid, char *name)
276 {
277 struct stat st;
278 FILE *from;
279 FILE *to;
280 char buf[MaxEntryLen];
281 char f[MaxFileNameLen];
282 int fd;
283 int cc;
284
285 if (getgrnam(group) != NULL) {
286 warnx("group `%s' already exists", group);
287 return 0;
288 }
289 if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
290 warn("can't create gid for %s: can't open %s", name, _PATH_GROUP);
291 return 0;
292 }
293 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
294 warn("can't lock `%s'", _PATH_GROUP);
295 }
296 (void) fstat(fileno(from), &st);
297 (void) snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
298 if ((fd = mkstemp(f)) < 0) {
299 (void) fclose(from);
300 warn("can't create gid: mkstemp failed");
301 return 0;
302 }
303 if ((to = fdopen(fd, "w")) == NULL) {
304 (void) fclose(from);
305 (void) close(fd);
306 (void) unlink(f);
307 warn("can't create gid: fdopen `%s' failed", f);
308 return 0;
309 }
310 while ((cc = fread(buf, sizeof(char), sizeof(buf), from)) > 0) {
311 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
312 (void) fclose(from);
313 (void) close(fd);
314 (void) unlink(f);
315 warn("can't create gid: short write to `%s'", f);
316 return 0;
317 }
318 }
319 (void) fprintf(to, "%s:*:%d:%s\n", group, gid, name);
320 (void) fclose(from);
321 (void) fclose(to);
322 if (rename(f, _PATH_GROUP) < 0) {
323 warn("can't create gid: can't rename `%s' to `%s'", f, _PATH_GROUP);
324 return 0;
325 }
326 (void) chmod(_PATH_GROUP, st.st_mode & 07777);
327 return 1;
328 }
329
330 /* modify the group entry with name `group' to be newent */
331 static int
332 modify_gid(char *group, char *newent)
333 {
334 struct stat st;
335 FILE *from;
336 FILE *to;
337 char buf[MaxEntryLen];
338 char f[MaxFileNameLen];
339 char *colon;
340 int groupc;
341 int entc;
342 int fd;
343 int cc;
344
345 if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
346 warn("can't create gid for %s: can't open %s", group, _PATH_GROUP);
347 return 0;
348 }
349 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
350 warn("can't lock `%s'", _PATH_GROUP);
351 }
352 (void) fstat(fileno(from), &st);
353 (void) snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
354 if ((fd = mkstemp(f)) < 0) {
355 (void) fclose(from);
356 warn("can't create gid: mkstemp failed");
357 return 0;
358 }
359 if ((to = fdopen(fd, "w")) == NULL) {
360 (void) fclose(from);
361 (void) close(fd);
362 (void) unlink(f);
363 warn("can't create gid: fdopen `%s' failed", f);
364 return 0;
365 }
366 groupc = strlen(group);
367 while (fgets(buf, sizeof(buf), from) != NULL) {
368 cc = strlen(buf);
369 if ((colon = strchr(buf, ':')) == NULL) {
370 warn("badly formed entry `%s'", buf);
371 continue;
372 }
373 entc = (int)(colon - buf);
374 if (entc == groupc && strncmp(group, buf, (unsigned) entc) == 0) {
375 if (newent == NULL) {
376 continue;
377 } else {
378 cc = strlen(newent);
379 (void) strlcpy(buf, newent, sizeof(buf));
380 }
381 }
382 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
383 (void) fclose(from);
384 (void) close(fd);
385 (void) unlink(f);
386 warn("can't create gid: short write to `%s'", f);
387 return 0;
388 }
389 }
390 (void) fclose(from);
391 (void) fclose(to);
392 if (rename(f, _PATH_GROUP) < 0) {
393 warn("can't create gid: can't rename `%s' to `%s'", f, _PATH_GROUP);
394 return 0;
395 }
396 (void) chmod(_PATH_GROUP, st.st_mode & 07777);
397 return 1;
398 }
399
400 /* modify the group entries for all `groups', by adding `user' */
401 static int
402 append_group(char *user, int ngroups, char **groups)
403 {
404 struct group *grp;
405 struct stat st;
406 FILE *from;
407 FILE *to;
408 char buf[MaxEntryLen];
409 char f[MaxFileNameLen];
410 char *colon;
411 int groupc;
412 int entc;
413 int fd;
414 int nc;
415 int cc;
416 int i;
417 int j;
418
419 for (i = 0 ; i < ngroups ; i++) {
420 if ((grp = getgrnam(groups[i])) != NULL) {
421 for (j = 0 ; grp->gr_mem[j] ; j++) {
422 if (strcmp(user, grp->gr_mem[j]) == 0) {
423 /* already in it */
424 groups[i] = "";
425 }
426 }
427 }
428 }
429 if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
430 warn("can't append group for %s: can't open %s", user, _PATH_GROUP);
431 return 0;
432 }
433 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
434 warn("can't lock `%s'", _PATH_GROUP);
435 }
436 (void) fstat(fileno(from), &st);
437 (void) snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
438 if ((fd = mkstemp(f)) < 0) {
439 (void) fclose(from);
440 warn("can't create gid: mkstemp failed");
441 return 0;
442 }
443 if ((to = fdopen(fd, "w")) == NULL) {
444 (void) fclose(from);
445 (void) close(fd);
446 (void) unlink(f);
447 warn("can't create gid: fdopen `%s' failed", f);
448 return 0;
449 }
450 while (fgets(buf, sizeof(buf), from) != NULL) {
451 cc = strlen(buf);
452 if ((colon = strchr(buf, ':')) == NULL) {
453 warn("badly formed entry `%s'", buf);
454 continue;
455 }
456 entc = (int)(colon - buf);
457 for (i = 0 ; i < ngroups ; i++) {
458 if ((groupc = strlen(groups[i])) == 0) {
459 continue;
460 }
461 if (entc == groupc && strncmp(groups[i], buf, (unsigned) entc) == 0) {
462 if ((nc = snprintf(&buf[cc - 1],
463 sizeof(buf) - cc + 1,
464 "%s%s\n",
465 (buf[cc - 2] == ':') ? "" : ",",
466 user)) < 0) {
467 warnx(
468 "Warning: group `%s' entry too long", groups[i]);
469 }
470 cc += nc - 1;
471 }
472 }
473 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
474 (void) fclose(from);
475 (void) close(fd);
476 (void) unlink(f);
477 warn("can't create gid: short write to `%s'", f);
478 return 0;
479 }
480 }
481 (void) fclose(from);
482 (void) fclose(to);
483 if (rename(f, _PATH_GROUP) < 0) {
484 warn("can't create gid: can't rename `%s' to `%s'", f, _PATH_GROUP);
485 return 0;
486 }
487 (void) chmod(_PATH_GROUP, st.st_mode & 07777);
488 return 1;
489 }
490
491 /* return 1 if `login' is a valid login name */
492 static int
493 valid_login(char *login)
494 {
495 char *cp;
496
497 for (cp = login ; *cp ; cp++) {
498 if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-') {
499 return 0;
500 }
501 }
502 return 1;
503 }
504
505 /* return 1 if `group' is a valid group name */
506 static int
507 valid_group(char *group)
508 {
509 char *cp;
510
511 for (cp = group ; *cp ; cp++) {
512 if (!isalnum(*cp)) {
513 return 0;
514 }
515 }
516 return 1;
517 }
518
519 /* find the next gid in the range lo .. hi */
520 static int
521 getnextgid(int *gidp, int lo, int hi)
522 {
523 for (*gidp = lo ; *gidp < hi ; *gidp += 1) {
524 if (getgrgid((gid_t)*gidp) == NULL) {
525 return 1;
526 }
527 }
528 return 0;
529 }
530
531 #ifdef EXTENSIONS
532 /* save a range of uids */
533 static int
534 save_range(user_t *up, char *cp)
535 {
536 int from;
537 int to;
538 int i;
539
540 if (up->u_rsize == 0) {
541 up->u_rsize = 32;
542 NEWARRAY(range_t, up->u_rv, up->u_rsize, return(0));
543 } else if (up->u_rc == up->u_rsize) {
544 up->u_rsize *= 2;
545 RENEW(range_t, up->u_rv, up->u_rsize, return(0));
546 }
547 if (up->u_rv && sscanf(cp, "%d..%d", &from, &to) == 2) {
548 for (i = 0 ; i < up->u_rc ; i++) {
549 if (up->u_rv[i].r_from == from && up->u_rv[i].r_to == to) {
550 break;
551 }
552 }
553 if (i == up->u_rc) {
554 up->u_rv[up->u_rc].r_from = from;
555 up->u_rv[up->u_rc].r_to = to;
556 up->u_rc += 1;
557 }
558 } else {
559 warnx("Bad range `%s'", cp);
560 return 0;
561 }
562 return 1;
563 }
564 #endif
565
566 /* set the defaults in the defaults file */
567 static int
568 setdefaults(user_t *up)
569 {
570 char template[MaxFileNameLen];
571 FILE *fp;
572 int ret;
573 int fd;
574 int i;
575
576 (void) snprintf(template, sizeof(template), "%s.XXXXXX", CONFFILE);
577 if ((fd = mkstemp(template)) < 0) {
578 warnx("can't mkstemp `%s' for writing", CONFFILE);
579 return 0;
580 }
581 if ((fp = fdopen(fd, "w")) == NULL) {
582 warn("can't fdopen `%s' for writing", CONFFILE);
583 return 0;
584 }
585 ret = 1;
586 if (fprintf(fp, "group\t\t%s\n", up->u_primgrp) <= 0 ||
587 fprintf(fp, "base_dir\t%s\n", up->u_basedir) <= 0 ||
588 fprintf(fp, "skel_dir\t%s\n", up->u_skeldir) <= 0 ||
589 fprintf(fp, "shell\t\t%s\n", up->u_shell) <= 0 ||
590 fprintf(fp, "inactive\t%d\n", up->u_inactive) <= 0 ||
591 fprintf(fp, "expire\t\t%s\n", (up->u_expire == NULL) ? UNSET_EXPIRY : up->u_expire) <= 0 ||
592 fprintf(fp, "preserve\t%s\n", (up->u_preserve == 0) ? "false" : "true") <= 0) {
593 warn("can't write to `%s'", CONFFILE);
594 ret = 0;
595 }
596 #ifdef EXTENSIONS
597 for (i = (up->u_defrc != up->u_rc) ? up->u_defrc : 0 ; i < up->u_rc ; i++) {
598 if (fprintf(fp, "range\t\t%d..%d\n", up->u_rv[i].r_from, up->u_rv[i].r_to) <= 0) {
599 warn("can't write to `%s'", CONFFILE);
600 ret = 0;
601 }
602 }
603 #endif
604 (void) fclose(fp);
605 if (ret) {
606 ret = ((rename(template, CONFFILE) == 0) && (chmod(CONFFILE, 0644) == 0));
607 }
608 return ret;
609 }
610
611 /* read the defaults file */
612 static void
613 read_defaults(user_t *up)
614 {
615 struct stat st;
616 size_t lineno;
617 size_t len;
618 FILE *fp;
619 char *cp;
620 char *s;
621
622 memsave(&up->u_primgrp, DEF_GROUP, strlen(DEF_GROUP));
623 memsave(&up->u_basedir, DEF_BASEDIR, strlen(DEF_BASEDIR));
624 memsave(&up->u_skeldir, DEF_SKELDIR, strlen(DEF_SKELDIR));
625 memsave(&up->u_shell, DEF_SHELL, strlen(DEF_SHELL));
626 memsave(&up->u_comment, DEF_COMMENT, strlen(DEF_COMMENT));
627 up->u_rsize = 16;
628 NEWARRAY(range_t, up->u_rv, up->u_rsize, exit(1));
629 up->u_inactive = DEF_INACTIVE;
630 up->u_expire = DEF_EXPIRE;
631 if ((fp = fopen(CONFFILE, "r")) == NULL) {
632 if (stat(CONFFILE, &st) < 0 && !setdefaults(up)) {
633 warn("can't create `%s' defaults file", CONFFILE);
634 }
635 fp = fopen(CONFFILE, "r");
636 }
637 if (fp != NULL) {
638 while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) {
639 if (strncmp(s, "group", 5) == 0) {
640 for (cp = s + 5 ; *cp && isspace(*cp) ; cp++) {
641 }
642 memsave(&up->u_primgrp, cp, strlen(cp));
643 } else if (strncmp(s, "base_dir", 8) == 0) {
644 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) {
645 }
646 memsave(&up->u_basedir, cp, strlen(cp));
647 } else if (strncmp(s, "skel_dir", 8) == 0) {
648 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) {
649 }
650 memsave(&up->u_skeldir, cp, strlen(cp));
651 } else if (strncmp(s, "shell", 5) == 0) {
652 for (cp = s + 5 ; *cp && isspace(*cp) ; cp++) {
653 }
654 memsave(&up->u_shell, cp, strlen(cp));
655 } else if (strncmp(s, "inactive", 8) == 0) {
656 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) {
657 }
658 up->u_inactive = atoi(cp);
659 #ifdef EXTENSIONS
660 } else if (strncmp(s, "range", 5) == 0) {
661 for (cp = s + 5 ; *cp && isspace(*cp) ; cp++) {
662 }
663 (void) save_range(up, cp);
664 #endif
665 #ifdef EXTENSIONS
666 } else if (strncmp(s, "preserve", 8) == 0) {
667 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) {
668 }
669 up->u_preserve = (strncmp(cp, "true", 4) == 0) ? 1 :
670 (strncmp(cp, "yes", 3) == 0) ? 1 :
671 atoi(cp);
672 #endif
673 } else if (strncmp(s, "expire", 6) == 0) {
674 for (cp = s + 6 ; *cp && isspace(*cp) ; cp++) {
675 }
676 if (strcmp(cp, UNSET_EXPIRY) == 0) {
677 if (up->u_expire) {
678 FREE(up->u_expire);
679 }
680 up->u_expire = NULL;
681 } else {
682 memsave(&up->u_expire, cp, strlen(cp));
683 }
684 }
685 (void) free(s);
686 }
687 (void) fclose(fp);
688 }
689 if (up->u_rc == 0) {
690 up->u_rv[up->u_rc].r_from = DEF_LOWUID;
691 up->u_rv[up->u_rc].r_to = DEF_HIGHUID;
692 up->u_rc += 1;
693 }
694 up->u_defrc = up->u_rc;
695 }
696
697 /* return the next valid unused uid */
698 static int
699 getnextuid(int sync_uid_gid, int *uid, int low_uid, int high_uid)
700 {
701 for (*uid = low_uid ; *uid <= high_uid ; (*uid)++) {
702 if (getpwuid((uid_t)(*uid)) == NULL && *uid != NOBODY_UID) {
703 if (sync_uid_gid) {
704 if (getgrgid((gid_t)(*uid)) == NULL) {
705 return 1;
706 }
707 } else {
708 return 1;
709 }
710 }
711 }
712 return 0;
713 }
714
715 /* add a user */
716 static int
717 adduser(char *login, user_t *up)
718 {
719 struct group *grp;
720 struct stat st;
721 struct tm tm;
722 time_t expire;
723 char password[PasswordLength + 1];
724 char home[MaxFileNameLen];
725 char buf[MaxFileNameLen];
726 int sync_uid_gid;
727 int masterfd;
728 int ptmpfd;
729 int gid;
730 int cc;
731 int i;
732
733 if (!valid_login(login)) {
734 errx(EXIT_FAILURE, "`%s' is not a valid login name", login);
735 }
736 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) {
737 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD);
738 }
739 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) {
740 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD);
741 }
742 pw_init();
743 if ((ptmpfd = pw_lock(WAITSECS)) < 0) {
744 (void) close(masterfd);
745 err(EXIT_FAILURE, "can't obtain pw_lock");
746 }
747 while ((cc = read(masterfd, buf, sizeof(buf))) > 0) {
748 if (write(ptmpfd, buf, (size_t)(cc)) != cc) {
749 (void) close(masterfd);
750 (void) close(ptmpfd);
751 (void) pw_abort();
752 err(EXIT_FAILURE, "short write to /etc/ptmp (not %d chars)", cc);
753 }
754 }
755 /* if no uid was specified, get next one in [low_uid..high_uid] range */
756 sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0);
757 if (up->u_uid == -1) {
758 for (i = 0 ; i < up->u_rc ; i++) {
759 if (getnextuid(sync_uid_gid, &up->u_uid, up->u_rv[i].r_from, up->u_rv[i].r_to)) {
760 break;
761 }
762 }
763 if (i == up->u_rc) {
764 (void) close(ptmpfd);
765 (void) pw_abort();
766 errx(EXIT_FAILURE, "can't get next uid for %d", up->u_uid);
767 }
768 }
769 /* check uid isn't already allocated */
770 if (!up->u_dupuid && getpwuid((uid_t)(up->u_uid)) != NULL) {
771 (void) close(ptmpfd);
772 (void) pw_abort();
773 errx(EXIT_FAILURE, "uid %d is already in use", up->u_uid);
774 }
775 /* if -g=uid was specified, check gid is unused */
776 if (sync_uid_gid) {
777 if (getgrgid((gid_t)(up->u_uid)) != NULL) {
778 (void) close(ptmpfd);
779 (void) pw_abort();
780 errx(EXIT_FAILURE, "gid %d is already in use", up->u_uid);
781 }
782 gid = up->u_uid;
783 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) {
784 gid = grp->gr_gid;
785 } else if (is_number(up->u_primgrp) &&
786 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) {
787 gid = grp->gr_gid;
788 } else {
789 (void) close(ptmpfd);
790 (void) pw_abort();
791 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp);
792 }
793 /* check name isn't already in use */
794 if (!up->u_dupuid && getpwnam(login) != NULL) {
795 (void) close(ptmpfd);
796 (void) pw_abort();
797 errx(EXIT_FAILURE, "already a `%s' user", login);
798 }
799 if (up->u_homeset) {
800 (void) strlcpy(home, up->u_home, sizeof(home));
801 } else {
802 /* if home directory hasn't been given, make it up */
803 (void) snprintf(home, sizeof(home), "%s/%s", up->u_basedir, login);
804 }
805 expire = 0;
806 if (up->u_expire != NULL) {
807 (void) memset(&tm, 0, sizeof(tm));
808 if (strptime(up->u_expire, "%c", &tm) == NULL) {
809 warnx("invalid time format `%s'", optarg);
810 } else {
811 expire = mktime(&tm);
812 }
813 }
814 if (lstat(home, &st) < 0 && !up->u_mkdir) {
815 warnx(
816 "Warning: home directory `%s' doesn't exist, and -m was not specified",
817 home);
818 }
819 password[PasswordLength] = '\0';
820 if (up->u_password != NULL &&
821 strlen(up->u_password) == PasswordLength) {
822 (void) memcpy(password, up->u_password, PasswordLength);
823 } else {
824 (void) memset(password, '*', PasswordLength);
825 if (up->u_password != NULL) {
826 warnx("Password `%s' is invalid: setting it to `%s'",
827 up->u_password, password);
828 }
829 }
830 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d::%d:%ld:%s:%s:%s\n",
831 login,
832 password,
833 up->u_uid,
834 gid,
835 up->u_inactive,
836 (long) expire,
837 up->u_comment,
838 home,
839 up->u_shell);
840 if (write(ptmpfd, buf, (size_t) cc) != cc) {
841 (void) close(ptmpfd);
842 (void) pw_abort();
843 err(EXIT_FAILURE, "can't add `%s'", buf);
844 }
845 if (up->u_mkdir) {
846 if (lstat(home, &st) < 0 && asystem("%s -p %s", MKDIR, home) != 0) {
847 (void) close(ptmpfd);
848 (void) pw_abort();
849 err(EXIT_FAILURE, "can't mkdir `%s'", home);
850 }
851 (void) copydotfiles(up->u_skeldir, up->u_uid, gid, home);
852 }
853 if (strcmp(up->u_primgrp, "=uid") == 0 &&
854 getgrnam(login) == NULL &&
855 !creategid(login, gid, login)) {
856 (void) close(ptmpfd);
857 (void) pw_abort();
858 err(EXIT_FAILURE, "can't create gid %d for login name %s", gid, login);
859 }
860 (void) close(ptmpfd);
861 if (pw_mkdb() < 0) {
862 err(EXIT_FAILURE, "pw_mkdb failed");
863 }
864 return 1;
865 }
866
867 /* modify a user */
868 static int
869 moduser(char *login, char *newlogin, user_t *up)
870 {
871 struct passwd *pwp;
872 struct group *grp;
873 struct tm tm;
874 time_t expire;
875 size_t loginc;
876 size_t colonc;
877 FILE *master;
878 char password[PasswordLength + 1];
879 char oldhome[MaxFileNameLen];
880 char home[MaxFileNameLen];
881 char buf[MaxFileNameLen];
882 char *colon;
883 int masterfd;
884 int ptmpfd;
885 int gid;
886 int cc;
887
888 if (!valid_login(newlogin)) {
889 errx(EXIT_FAILURE, "`%s' is not a valid login name", login);
890 }
891 if ((pwp = getpwnam(login)) == NULL) {
892 errx(EXIT_FAILURE, "No such user `%s'", login);
893 }
894 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) {
895 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD);
896 }
897 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) {
898 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD);
899 }
900 pw_init();
901 if ((ptmpfd = pw_lock(WAITSECS)) < 0) {
902 (void) close(masterfd);
903 err(EXIT_FAILURE, "can't obtain pw_lock");
904 }
905 if ((master = fdopen(masterfd, "r")) == NULL) {
906 (void) close(masterfd);
907 (void) close(ptmpfd);
908 (void) pw_abort();
909 err(EXIT_FAILURE, "can't fdopen fd for %s", _PATH_MASTERPASSWD);
910 }
911 if (up != NULL) {
912 if (up->u_mkdir) {
913 (void) strcpy(oldhome, pwp->pw_dir);
914 }
915 if (up->u_uid == -1) {
916 up->u_uid = pwp->pw_uid;
917 }
918 /* if -g=uid was specified, check gid is unused */
919 if (strcmp(up->u_primgrp, "=uid") == 0) {
920 if (getgrgid((gid_t)(up->u_uid)) != NULL) {
921 (void) close(ptmpfd);
922 (void) pw_abort();
923 errx(EXIT_FAILURE, "gid %d is already in use", up->u_uid);
924 }
925 gid = up->u_uid;
926 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) {
927 gid = grp->gr_gid;
928 } else if (is_number(up->u_primgrp) &&
929 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) {
930 gid = grp->gr_gid;
931 } else {
932 (void) close(ptmpfd);
933 (void) pw_abort();
934 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp);
935 }
936 /* if changing name, check new name isn't already in use */
937 if (strcmp(login, newlogin) != 0 && getpwnam(newlogin) != NULL) {
938 (void) close(ptmpfd);
939 (void) pw_abort();
940 errx(EXIT_FAILURE, "already a `%s' user", newlogin);
941 }
942 /* if home directory hasn't been given, use the old one */
943 if (!up->u_homeset) {
944 (void) strcpy(home, pwp->pw_dir);
945 }
946 expire = 0;
947 if (up->u_expire != NULL) {
948 (void) memset(&tm, 0, sizeof(tm));
949 if (strptime(up->u_expire, "%c", &tm) == NULL) {
950 warnx("invalid time format `%s'", optarg);
951 } else {
952 expire = mktime(&tm);
953 }
954 }
955 password[PasswordLength] = '\0';
956 if (up->u_password != NULL &&
957 strlen(up->u_password) == PasswordLength) {
958 (void) memcpy(password, up->u_password, PasswordLength);
959 } else {
960 (void) memcpy(password, pwp->pw_passwd, PasswordLength);
961 }
962 if (strcmp(up->u_comment, DEF_COMMENT) == 0) {
963 memsave(&up->u_comment, pwp->pw_gecos, strlen(pwp->pw_gecos));
964 }
965 if (strcmp(up->u_shell, DEF_SHELL) == 0 && strcmp(pwp->pw_shell, DEF_SHELL) != 0) {
966 memsave(&up->u_shell, pwp->pw_shell, strlen(pwp->pw_shell));
967 }
968 }
969 loginc = strlen(login);
970 while (fgets(buf, sizeof(buf), master) != NULL) {
971 cc = strlen(buf);
972 if ((colon = strchr(buf, ':')) == NULL) {
973 warnx("Malformed entry `%s'. Skipping", buf);
974 continue;
975 }
976 colonc = (size_t)(colon - buf);
977 if (strncmp(login, buf, loginc) == 0 && loginc == colonc) {
978 if (up != NULL) {
979 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d::%d:%ld:%s:%s:%s\n",
980 newlogin,
981 password,
982 up->u_uid,
983 gid,
984 up->u_inactive,
985 (long) expire,
986 up->u_comment,
987 home,
988 up->u_shell);
989 if (write(ptmpfd, buf, (size_t) cc) != cc) {
990 (void) close(ptmpfd);
991 (void) pw_abort();
992 err(EXIT_FAILURE, "can't add `%s'", buf);
993 }
994 }
995 } else if (write(ptmpfd, buf, (size_t)(cc)) != cc) {
996 (void) close(masterfd);
997 (void) close(ptmpfd);
998 (void) pw_abort();
999 err(EXIT_FAILURE,
1000 "short write to /etc/ptmp (not %d chars)",
1001 cc);
1002 }
1003 }
1004 if (up != NULL) {
1005 if (up->u_mkdir &&
1006 asystem("%s %s %s", MV, oldhome, home) != 0) {
1007 (void) close(ptmpfd);
1008 (void) pw_abort();
1009 err(EXIT_FAILURE, "can't move `%s' to `%s'",
1010 oldhome, home);
1011 }
1012 if (up->u_groupc > 0 &&
1013 !append_group(newlogin, up->u_groupc, up->u_groupv)) {
1014 (void) close(ptmpfd);
1015 (void) pw_abort();
1016 errx(EXIT_FAILURE,
1017 "can't append `%s' to new groups",
1018 newlogin);
1019 }
1020 }
1021 (void) close(ptmpfd);
1022 if (pw_mkdb() < 0) {
1023 err(EXIT_FAILURE, "pw_mkdb failed");
1024 }
1025 return 1;
1026 }
1027
1028
1029 #ifdef EXTENSIONS
1030 /* see if we can find out the user struct */
1031 static struct passwd *
1032 find_user_info(char *name)
1033 {
1034 struct passwd *pwp;
1035
1036 if ((pwp = getpwnam(name)) != NULL) {
1037 return pwp;
1038 }
1039 if (is_number(name) && (pwp = getpwuid((uid_t)atoi(name))) != NULL) {
1040 return pwp;
1041 }
1042 return NULL;
1043 }
1044 #endif
1045
1046 #ifdef EXTENSIONS
1047 /* see if we can find out the group struct */
1048 static struct group *
1049 find_group_info(char *name)
1050 {
1051 struct group *grp;
1052
1053 if ((grp = getgrnam(name)) != NULL) {
1054 return grp;
1055 }
1056 if (is_number(name) && (grp = getgrgid((gid_t)atoi(name))) != NULL) {
1057 return grp;
1058 }
1059 return NULL;
1060 }
1061 #endif
1062
1063 /* print out usage message, and then exit */
1064 void
1065 usermgmt_usage(char *prog)
1066 {
1067 if (strcmp(prog, "useradd") == 0) {
1068 (void) fprintf(stderr, "Usage: %s -D [-b basedir] [-e expiry] "
1069 "[-f inactive] [-g group] [-r lowuid..highuid] [-s shell]"
1070 "\n", prog);
1071 (void) fprintf(stderr, "Usage: %s [-G group] [-b basedir] "
1072 "[-c comment] [-d homedir] [-e expiry] [-f inactive]\n"
1073 "\t[-g group] [-k skeletondir] [-m] [-o] [-p password] "
1074 "[-r lowuid..highuid] [-s shell]\n\t[-u uid] [-v] user\n",
1075 prog);
1076 } else if (strcmp(prog, "usermod") == 0) {
1077 (void) fprintf(stderr, "Usage: %s [-G group] [-c comment] "
1078 "[-d homedir] [-e expire] [-f inactive] [-g group] "
1079 "[-l newname] [-m] [-o] [-p password] [-s shell] [-u uid] "
1080 "[-v] user\n", prog);
1081 } else if (strcmp(prog, "userdel") == 0) {
1082 (void) fprintf(stderr, "Usage: %s -D [-p preserve]\n", prog);
1083 (void) fprintf(stderr, "Usage: %s [-p preserve] [-r] [-v] "
1084 "user\n", prog);
1085 #ifdef EXTENSIONS
1086 } else if (strcmp(prog, "userinfo") == 0) {
1087 (void) fprintf(stderr, "Usage: %s [-e] [-v] user\n", prog);
1088 #endif
1089 } else if (strcmp(prog, "groupadd") == 0) {
1090 (void) fprintf(stderr, "Usage: %s [-g gid] [-o] [-v] group\n",
1091 prog);
1092 } else if (strcmp(prog, "groupdel") == 0) {
1093 (void) fprintf(stderr, "Usage: %s [-v] group\n", prog);
1094 } else if (strcmp(prog, "groupmod") == 0) {
1095 (void) fprintf(stderr, "Usage: %s [-g gid] [-o] [-n newname] "
1096 "[-v] group\n", prog);
1097 } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) {
1098 (void) fprintf(stderr,
1099 "Usage: %s ( add | del | mod | info ) ...\n",
1100 prog);
1101 #ifdef EXTENSIONS
1102 } else if (strcmp(prog, "groupinfo") == 0) {
1103 (void) fprintf(stderr, "Usage: %s [-e] [-v] group\n", prog);
1104 #endif
1105 }
1106 exit(EXIT_FAILURE);
1107 /* NOTREACHED */
1108 }
1109
1110 #ifdef EXTENSIONS
1111 #define ADD_OPT_EXTENSIONS "p:r:v"
1112 #else
1113 #define ADD_OPT_EXTENSIONS
1114 #endif
1115
1116 int
1117 useradd(int argc, char **argv)
1118 {
1119 user_t u;
1120 int defaultfield;
1121 int bigD;
1122 int c;
1123 int i;
1124
1125 (void) memset(&u, 0, sizeof(u));
1126 read_defaults(&u);
1127 u.u_uid = -1;
1128 defaultfield = bigD = 0;
1129 while ((c = getopt(argc, argv, "DG:b:c:d:e:f:g:k:mou:s:" ADD_OPT_EXTENSIONS)) != -1) {
1130 switch(c) {
1131 case 'D':
1132 bigD = 1;
1133 break;
1134 case 'G':
1135 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL &&
1136 u.u_groupc < NGROUPS_MAX) {
1137 if (u.u_groupv[u.u_groupc][0] != 0) {
1138 u.u_groupc++;
1139 }
1140 }
1141 if (optarg != NULL) {
1142 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX);
1143 }
1144 break;
1145 case 'b':
1146 defaultfield = 1;
1147 memsave(&u.u_basedir, optarg, strlen(optarg));
1148 break;
1149 case 'c':
1150 memsave(&u.u_comment, optarg, strlen(optarg));
1151 break;
1152 case 'd':
1153 u.u_homeset = 1;
1154 memsave(&u.u_home, optarg, strlen(optarg));
1155 break;
1156 case 'e':
1157 defaultfield = 1;
1158 memsave(&u.u_expire, optarg, strlen(optarg));
1159 break;
1160 case 'f':
1161 defaultfield = 1;
1162 u.u_inactive = atoi(optarg);
1163 break;
1164 case 'g':
1165 defaultfield = 1;
1166 memsave(&u.u_primgrp, optarg, strlen(optarg));
1167 break;
1168 case 'k':
1169 memsave(&u.u_skeldir, optarg, strlen(optarg));
1170 break;
1171 case 'm':
1172 u.u_mkdir = 1;
1173 break;
1174 case 'o':
1175 u.u_dupuid = 1;
1176 break;
1177 #ifdef EXTENSIONS
1178 case 'p':
1179 memsave(&u.u_password, optarg, strlen(optarg));
1180 break;
1181 #endif
1182 #ifdef EXTENSIONS
1183 case 'r':
1184 defaultfield = 1;
1185 (void) save_range(&u, optarg);
1186 break;
1187 #endif
1188 case 's':
1189 defaultfield = 1;
1190 memsave(&u.u_shell, optarg, strlen(optarg));
1191 break;
1192 case 'u':
1193 if (!is_number(optarg)) {
1194 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric");
1195 }
1196 u.u_uid = atoi(optarg);
1197 break;
1198 #ifdef EXTENSIONS
1199 case 'v':
1200 verbose = 1;
1201 break;
1202 #endif
1203 }
1204 }
1205 if (bigD) {
1206 if (defaultfield) {
1207 checkeuid();
1208 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE;
1209 }
1210 (void) printf("group\t\t%s\n", u.u_primgrp);
1211 (void) printf("base_dir\t%s\n", u.u_basedir);
1212 (void) printf("skel_dir\t%s\n", u.u_skeldir);
1213 (void) printf("shell\t\t%s\n", u.u_shell);
1214 (void) printf("inactive\t%d\n", u.u_inactive);
1215 (void) printf("expire\t\t%s\n", (u.u_expire == NULL) ? UNSET_EXPIRY : u.u_expire);
1216 #ifdef EXTENSIONS
1217 for (i = 0 ; i < u.u_rc ; i++) {
1218 (void) printf("range\t\t%d..%d\n", u.u_rv[i].r_from, u.u_rv[i].r_to);
1219 }
1220 #endif
1221 return EXIT_SUCCESS;
1222 }
1223 if (argc == optind) {
1224 usermgmt_usage("useradd");
1225 }
1226 checkeuid();
1227 return adduser(argv[optind], &u) ? EXIT_SUCCESS : EXIT_FAILURE;
1228 }
1229
1230 #ifdef EXTENSIONS
1231 #define MOD_OPT_EXTENSIONS "p:v"
1232 #else
1233 #define MOD_OPT_EXTENSIONS
1234 #endif
1235
1236 int
1237 usermod(int argc, char **argv)
1238 {
1239 user_t u;
1240 char newuser[MaxUserNameLen + 1];
1241 int have_new_user;
1242 int c;
1243
1244 (void) memset(&u, 0, sizeof(u));
1245 (void) memset(newuser, 0, sizeof(newuser));
1246 read_defaults(&u);
1247 u.u_uid = -1;
1248 have_new_user = 0;
1249 while ((c = getopt(argc, argv, "G:c:d:e:f:g:l:mos:u:" MOD_OPT_EXTENSIONS)) != -1) {
1250 switch(c) {
1251 case 'G':
1252 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL &&
1253 u.u_groupc < NGROUPS_MAX) {
1254 if (u.u_groupv[u.u_groupc][0] != 0) {
1255 u.u_groupc++;
1256 }
1257 }
1258 if (optarg != NULL) {
1259 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX);
1260 }
1261 break;
1262 case 'c':
1263 memsave(&u.u_comment, optarg, strlen(optarg));
1264 break;
1265 case 'd':
1266 u.u_homeset = 1;
1267 memsave(&u.u_home, optarg, strlen(optarg));
1268 break;
1269 case 'e':
1270 memsave(&u.u_expire, optarg, strlen(optarg));
1271 break;
1272 case 'f':
1273 u.u_inactive = atoi(optarg);
1274 break;
1275 case 'g':
1276 memsave(&u.u_primgrp, optarg, strlen(optarg));
1277 break;
1278 case 'l':
1279 have_new_user = 1;
1280 (void) strlcpy(newuser, optarg, sizeof(newuser));
1281 break;
1282 case 'm':
1283 u.u_mkdir = 1;
1284 break;
1285 case 'o':
1286 u.u_dupuid = 1;
1287 break;
1288 #ifdef EXTENSIONS
1289 case 'p':
1290 memsave(&u.u_password, optarg, strlen(optarg));
1291 break;
1292 #endif
1293 case 's':
1294 memsave(&u.u_shell, optarg, strlen(optarg));
1295 break;
1296 case 'u':
1297 if (!is_number(optarg)) {
1298 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric");
1299 }
1300 u.u_uid = atoi(optarg);
1301 break;
1302 #ifdef EXTENSIONS
1303 case 'v':
1304 verbose = 1;
1305 break;
1306 #endif
1307 }
1308 }
1309 if (argc == optind) {
1310 usermgmt_usage("usermod");
1311 }
1312 checkeuid();
1313 return moduser(argv[optind], (have_new_user) ? newuser : argv[optind], &u) ? EXIT_SUCCESS : EXIT_FAILURE;
1314 }
1315
1316 #ifdef EXTENSIONS
1317 #define DEL_OPT_EXTENSIONS "Dp:v"
1318 #else
1319 #define DEL_OPT_EXTENSIONS
1320 #endif
1321
1322 int
1323 userdel(int argc, char **argv)
1324 {
1325 struct passwd *pwp;
1326 struct stat st;
1327 user_t u;
1328 char password[PasswordLength + 1];
1329 int defaultfield;
1330 int rmhome;
1331 int bigD;
1332 int c;
1333
1334 (void) memset(&u, 0, sizeof(u));
1335 read_defaults(&u);
1336 defaultfield = bigD = rmhome = 0;
1337 while ((c = getopt(argc, argv, "r" DEL_OPT_EXTENSIONS)) != -1) {
1338 switch(c) {
1339 #ifdef EXTENSIONS
1340 case 'D':
1341 bigD = 1;
1342 break;
1343 #endif
1344 #ifdef EXTENSIONS
1345 case 'p':
1346 defaultfield = 1;
1347 u.u_preserve = (strcmp(optarg, "true") == 0) ? 1 :
1348 (strcmp(optarg, "yes") == 0) ? 1 :
1349 atoi(optarg);
1350 break;
1351 #endif
1352 case 'r':
1353 rmhome = 1;
1354 break;
1355 #ifdef EXTENSIONS
1356 case 'v':
1357 verbose = 1;
1358 break;
1359 #endif
1360 }
1361 }
1362 #ifdef EXTENSIONS
1363 if (bigD) {
1364 if (defaultfield) {
1365 checkeuid();
1366 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE;
1367 }
1368 (void) printf("preserve\t%s\n", (u.u_preserve) ? "true" : "false");
1369 return EXIT_SUCCESS;
1370 }
1371 #endif
1372 if (argc == optind) {
1373 usermgmt_usage("userdel");
1374 }
1375 checkeuid();
1376 if ((pwp = getpwnam(argv[optind])) == NULL) {
1377 warnx("No such user `%s'", argv[optind]);
1378 return EXIT_FAILURE;
1379 }
1380 if (rmhome) {
1381 if (stat(pwp->pw_dir, &st) < 0) {
1382 warn("Home directory `%s' does not exist", pwp->pw_dir);
1383 return EXIT_FAILURE;
1384 }
1385 (void) asystem("%s -rf %s", RM, pwp->pw_dir);
1386 }
1387 if (u.u_preserve) {
1388 memsave(&u.u_shell, NOLOGIN, strlen(NOLOGIN));
1389 (void) memset(password, '*', PasswordLength);
1390 password[PasswordLength] = '\0';
1391 memsave(&u.u_password, password, PasswordLength);
1392 return moduser(argv[optind], argv[optind], &u) ? EXIT_SUCCESS : EXIT_FAILURE;
1393 }
1394 return moduser(argv[optind], argv[optind], NULL) ? EXIT_SUCCESS : EXIT_FAILURE;
1395 }
1396
1397 #ifdef EXTENSIONS
1398 #define GROUP_ADD_OPT_EXTENSIONS "v"
1399 #else
1400 #define GROUP_ADD_OPT_EXTENSIONS
1401 #endif
1402
1403 /* add a group */
1404 int
1405 groupadd(int argc, char **argv)
1406 {
1407 int dupgid;
1408 int gid;
1409 int c;
1410
1411 gid = -1;
1412 dupgid = 0;
1413 while ((c = getopt(argc, argv, "g:o" GROUP_ADD_OPT_EXTENSIONS)) != -1) {
1414 switch(c) {
1415 case 'g':
1416 if (!is_number(optarg)) {
1417 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric");
1418 }
1419 gid = atoi(optarg);
1420 break;
1421 case 'o':
1422 dupgid = 1;
1423 break;
1424 #ifdef EXTENSIONS
1425 case 'v':
1426 verbose = 1;
1427 break;
1428 #endif
1429 }
1430 }
1431 if (argc == optind) {
1432 usermgmt_usage("groupadd");
1433 }
1434 checkeuid();
1435 if (gid < 0 && !getnextgid(&gid, LowGid, HighGid)) {
1436 err(EXIT_FAILURE, "can't add group: can't get next gid");
1437 }
1438 if (!dupgid && getgrgid((gid_t) gid) != NULL) {
1439 errx(EXIT_FAILURE, "can't add group: gid %d is a duplicate", gid);
1440 }
1441 if (!valid_group(argv[optind])) {
1442 warnx("warning - invalid group name `%s'", argv[optind]);
1443 }
1444 if (!creategid(argv[optind], gid, "")) {
1445 err(EXIT_FAILURE, "can't add group: problems with %s file", _PATH_GROUP);
1446 }
1447 return EXIT_SUCCESS;
1448 }
1449
1450 #ifdef EXTENSIONS
1451 #define GROUP_DEL_OPT_EXTENSIONS "v"
1452 #else
1453 #define GROUP_DEL_OPT_EXTENSIONS
1454 #endif
1455
1456 /* remove a group */
1457 int
1458 groupdel(int argc, char **argv)
1459 {
1460 int c;
1461
1462 while ((c = getopt(argc, argv, "" GROUP_DEL_OPT_EXTENSIONS)) != -1) {
1463 switch(c) {
1464 #ifdef EXTENSIONS
1465 case 'v':
1466 verbose = 1;
1467 break;
1468 #endif
1469 }
1470 }
1471 if (argc == optind) {
1472 usermgmt_usage("groupdel");
1473 }
1474 checkeuid();
1475 if (!modify_gid(argv[optind], NULL)) {
1476 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP);
1477 }
1478 return EXIT_SUCCESS;
1479 }
1480
1481 #ifdef EXTENSIONS
1482 #define GROUP_MOD_OPT_EXTENSIONS "v"
1483 #else
1484 #define GROUP_MOD_OPT_EXTENSIONS
1485 #endif
1486
1487 /* modify a group */
1488 int
1489 groupmod(int argc, char **argv)
1490 {
1491 struct group *grp;
1492 char buf[MaxEntryLen];
1493 char *newname;
1494 char **cpp;
1495 int dupgid;
1496 int gid;
1497 int cc;
1498 int c;
1499
1500 gid = -1;
1501 dupgid = 0;
1502 newname = NULL;
1503 while ((c = getopt(argc, argv, "g:on:" GROUP_MOD_OPT_EXTENSIONS)) != -1) {
1504 switch(c) {
1505 case 'g':
1506 if (!is_number(optarg)) {
1507 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric");
1508 }
1509 gid = atoi(optarg);
1510 break;
1511 case 'o':
1512 dupgid = 1;
1513 break;
1514 case 'n':
1515 memsave(&newname, optarg, strlen(optarg));
1516 break;
1517 #ifdef EXTENSIONS
1518 case 'v':
1519 verbose = 1;
1520 break;
1521 #endif
1522 }
1523 }
1524 if (argc == optind) {
1525 usermgmt_usage("groupmod");
1526 }
1527 checkeuid();
1528 if (gid < 0 && newname == NULL) {
1529 err(EXIT_FAILURE, "Nothing to change");
1530 }
1531 if (dupgid && gid < 0) {
1532 err(EXIT_FAILURE, "Duplicate which gid?");
1533 }
1534 if ((grp = getgrnam(argv[optind])) == NULL) {
1535 err(EXIT_FAILURE, "can't find group `%s' to modify", argv[optind]);
1536 }
1537 if (newname != NULL && !valid_group(newname)) {
1538 warn("warning - invalid group name `%s'", newname);
1539 }
1540 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:",
1541 (newname) ? newname : grp->gr_name,
1542 grp->gr_passwd,
1543 (gid < 0) ? grp->gr_gid : gid);
1544 for (cpp = grp->gr_mem ; *cpp && cc < sizeof(buf) ; cpp++) {
1545 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s%s", *cpp,
1546 (cpp[1] == NULL) ? "" : ",");
1547 }
1548 if (!modify_gid(argv[optind], buf)) {
1549 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP);
1550 }
1551 return EXIT_SUCCESS;
1552 }
1553
1554 #ifdef EXTENSIONS
1555 /* display user information */
1556 int
1557 userinfo(int argc, char **argv)
1558 {
1559 struct passwd *pwp;
1560 struct group *grp;
1561 char buf[MaxEntryLen];
1562 char **cpp;
1563 int exists;
1564 int cc;
1565 int i;
1566
1567 exists = 0;
1568 while ((i = getopt(argc, argv, "ev")) != -1) {
1569 switch(i) {
1570 case 'e':
1571 exists = 1;
1572 break;
1573 case 'v':
1574 verbose = 1;
1575 break;
1576 }
1577 }
1578 if (argc == optind) {
1579 usermgmt_usage("userinfo");
1580 }
1581 pwp = find_user_info(argv[optind]);
1582 if (exists) {
1583 exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE);
1584 }
1585 if (pwp == NULL) {
1586 errx(EXIT_FAILURE, "can't find user `%s'", argv[optind]);
1587 }
1588 (void) printf("login\t%s\n", pwp->pw_name);
1589 (void) printf("passwd\t%s\n", pwp->pw_passwd);
1590 (void) printf("uid\t%d\n", pwp->pw_uid);
1591 for (cc = 0 ; (grp = getgrent()) != NULL ; ) {
1592 for (cpp = grp->gr_mem ; *cpp ; cpp++) {
1593 if (strcmp(*cpp, argv[optind]) == 0 && grp->gr_gid != pwp->pw_gid) {
1594 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s ", grp->gr_name);
1595 }
1596 }
1597 }
1598 if ((grp = getgrgid(pwp->pw_gid)) == NULL) {
1599 (void) printf("groups\t%d %s\n", pwp->pw_gid, buf);
1600 } else {
1601 (void) printf("groups\t%s %s\n", grp->gr_name, buf);
1602 }
1603 (void) printf("change\t%s", ctime(&pwp->pw_change));
1604 (void) printf("class\t%s\n", pwp->pw_class);
1605 (void) printf("gecos\t%s\n", pwp->pw_gecos);
1606 (void) printf("dir\t%s\n", pwp->pw_dir);
1607 (void) printf("shell\t%s\n", pwp->pw_shell);
1608 (void) printf("expire\t%s", ctime(&pwp->pw_expire));
1609 return EXIT_SUCCESS;
1610 }
1611 #endif
1612
1613 #ifdef EXTENSIONS
1614 /* display user information */
1615 int
1616 groupinfo(int argc, char **argv)
1617 {
1618 struct group *grp;
1619 char **cpp;
1620 int exists;
1621 int i;
1622
1623 exists = 0;
1624 while ((i = getopt(argc, argv, "ev")) != -1) {
1625 switch(i) {
1626 case 'e':
1627 exists = 1;
1628 break;
1629 case 'v':
1630 verbose = 1;
1631 break;
1632 }
1633 }
1634 if (argc == optind) {
1635 usermgmt_usage("groupinfo");
1636 }
1637 grp = find_group_info(argv[optind]);
1638 if (exists) {
1639 exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE);
1640 }
1641 if (grp == NULL) {
1642 errx(EXIT_FAILURE, "can't find group `%s'", argv[optind]);
1643 }
1644 (void) printf("name\t%s\n", grp->gr_name);
1645 (void) printf("passwd\t%s\n", grp->gr_passwd);
1646 (void) printf("gid\t%d\n", grp->gr_gid);
1647 (void) printf("members\t");
1648 for (cpp = grp->gr_mem ; *cpp ; cpp++) {
1649 (void) printf("%s ", *cpp);
1650 }
1651 (void) fputc('\n', stdout);
1652 return EXIT_SUCCESS;
1653 }
1654 #endif
1655