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