libexec exec fix
[minix.git] / usr.sbin / user / user.c
blob045ff1aa6ee33122579807e5ba1c74fbdb0b98d2
1 /* $NetBSD: user.c,v 1.126 2011/01/04 10:30:21 wiz Exp $ */
3 /*
4 * Copyright (c) 1999 Alistair G. Crooks. All rights reserved.
5 * Copyright (c) 2005 Liam J. Foy. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1999\
35 The NetBSD Foundation, Inc. All rights reserved.");
36 __RCSID("$NetBSD: user.c,v 1.126 2011/01/04 10:30:21 wiz Exp $");
37 #endif
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
43 #include <ctype.h>
44 #include <dirent.h>
45 #include <err.h>
46 #include <fcntl.h>
47 #include <grp.h>
48 #ifdef EXTENSIONS
49 #include <login_cap.h>
50 #endif
51 #include <paths.h>
52 #include <pwd.h>
53 #include <regex.h>
54 #include <stdarg.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <syslog.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include <util.h>
62 #include <errno.h>
64 #include "defs.h"
65 #include "usermgmt.h"
68 /* this struct describes a uid range */
69 typedef struct range_t {
70 int r_from; /* low uid */
71 int r_to; /* high uid */
72 } range_t;
74 typedef struct rangelist_t {
75 unsigned rl_rsize; /* size of range array */
76 unsigned rl_rc; /* # of ranges */
77 range_t *rl_rv; /* the ranges */
78 unsigned rl_defrc; /* # of ranges in defaults */
79 } rangelist_t;
81 /* this struct encapsulates the user and group information */
82 typedef struct user_t {
83 int u_flags; /* see below */
84 int u_uid; /* uid of user */
85 char *u_password; /* encrypted password */
86 char *u_comment; /* comment field */
87 char *u_home; /* home directory */
88 mode_t u_homeperm; /* permissions of home dir */
89 char *u_primgrp; /* primary group */
90 int u_groupc; /* # of secondary groups */
91 const char *u_groupv[NGROUPS_MAX]; /* secondary groups */
92 char *u_shell; /* user's shell */
93 char *u_basedir; /* base directory for home */
94 char *u_expire; /* when password will expire */
95 char *u_inactive; /* when account will expire */
96 char *u_skeldir; /* directory for startup files */
97 char *u_class; /* login class */
98 rangelist_t u_r; /* list of ranges */
99 unsigned u_defrc; /* # of ranges in defaults */
100 int u_preserve; /* preserve uids on deletion */
101 int u_allow_samba; /* allow trailing '$' for samba login names */
102 int u_locked; /* user account lock */
103 } user_t;
104 #define u_rsize u_r.rl_rsize
105 #define u_rc u_r.rl_rc
106 #define u_rv u_r.rl_rv
107 #define u_defrc u_r.rl_defrc
109 /* this struct encapsulates the user and group information */
110 typedef struct group_t {
111 rangelist_t g_r; /* list of ranges */
112 } group_t;
113 #define g_rsize g_r.rl_rsize
114 #define g_rc g_r.rl_rc
115 #define g_rv g_r.rl_rv
116 #define g_defrc g_r.rl_defrc
118 typedef struct def_t {
119 user_t user;
120 group_t group;
121 } def_t;
123 /* flags for which fields of the user_t replace the passwd entry */
124 enum {
125 F_COMMENT = 0x0001,
126 F_DUPUID = 0x0002,
127 F_EXPIRE = 0x0004,
128 F_GROUP = 0x0008,
129 F_HOMEDIR = 0x0010,
130 F_MKDIR = 0x0020,
131 F_INACTIVE = 0x0040,
132 F_PASSWORD = 0x0080,
133 F_SECGROUP = 0x0100,
134 F_SHELL = 0x0200,
135 F_UID = 0x0400,
136 F_USERNAME = 0x0800,
137 F_CLASS = 0x1000
140 #define UNLOCK 0
141 #define LOCK 1
142 #define LOCKED "*LOCKED*"
144 #define PATH_LOGINCONF "/etc/login.conf"
146 #ifndef DEF_GROUP
147 #define DEF_GROUP "users"
148 #endif
150 #ifndef DEF_BASEDIR
151 #define DEF_BASEDIR "/home"
152 #endif
154 #ifndef DEF_SKELDIR
155 #ifdef __minix
156 #define DEF_SKELDIR "/usr/ast"
157 #else
158 #define DEF_SKELDIR "/etc/skel"
159 #endif
160 #endif
162 #ifndef DEF_SHELL
163 #define DEF_SHELL _PATH_BSHELL
164 #endif
166 #ifndef DEF_COMMENT
167 #define DEF_COMMENT ""
168 #endif
170 #ifndef DEF_LOWUID
171 #define DEF_LOWUID 1000
172 #endif
174 #ifndef DEF_HIGHUID
175 #define DEF_HIGHUID 60000
176 #endif
178 #ifndef DEF_INACTIVE
179 #define DEF_INACTIVE 0
180 #endif
182 #ifndef DEF_EXPIRE
183 #define DEF_EXPIRE NULL
184 #endif
186 #ifndef DEF_CLASS
187 #define DEF_CLASS ""
188 #endif
190 #ifndef WAITSECS
191 #define WAITSECS 10
192 #endif
194 #ifndef NOBODY_UID
195 #define NOBODY_UID 32767
196 #endif
198 #ifndef DEF_HOMEPERM
199 #define DEF_HOMEPERM 0755
200 #endif
202 /* some useful constants */
203 enum {
204 MaxShellNameLen = 256,
205 MaxFileNameLen = MAXPATHLEN,
206 MaxUserNameLen = LOGIN_NAME_MAX - 1,
207 MaxCommandLen = 2048,
208 MaxEntryLen = 2048,
209 PasswordLength = 2048,
211 DES_Len = 13,
214 /* Full paths of programs used here */
215 #define CHMOD "/bin/chmod"
216 #define CHOWN "/usr/bin/chown"
217 #define MKDIR "/bin/mkdir"
218 #define MV "/bin/mv"
219 #define NOLOGIN "/sbin/nologin"
220 #define PAX "/bin/pax"
221 #define RM "/bin/rm"
223 #define UNSET_INACTIVE "Null (unset)"
224 #define UNSET_EXPIRY "Null (unset)"
226 static int asystem(const char *fmt, ...)
227 __attribute__((__format__(__printf__, 1, 2)));
228 static int is_number(const char *);
229 static struct group *find_group_info(const char *);
230 static int verbose;
232 static char *
233 skipspace(char *s)
235 for (; *s && isspace((unsigned char)*s) ; s++) {
237 return s;
240 static int
241 check_numeric(const char *val, const char *name)
243 if (!is_number(val)) {
244 errx(EXIT_FAILURE, "When using [-%c %s], "
245 "the %s must be numeric", *name, name, name);
247 return atoi(val);
250 /* resize *cpp appropriately then assign `n' chars of `s' to it */
251 static void
252 memsave(char **cpp, const char *s, size_t n)
254 RENEW(char, *cpp, n + 1, exit(1));
255 (void)memcpy(*cpp, s, n);
256 (*cpp)[n] = '\0';
259 /* a replacement for system(3) */
260 static int
261 asystem(const char *fmt, ...)
263 va_list vp;
264 char buf[MaxCommandLen];
265 int ret;
267 va_start(vp, fmt);
268 (void)vsnprintf(buf, sizeof(buf), fmt, vp);
269 va_end(vp);
270 if (verbose) {
271 (void)printf("Command: %s\n", buf);
273 if ((ret = system(buf)) != 0) {
274 warn("Error running `%s'", buf);
276 return ret;
279 /* remove a users home directory, returning 1 for success (ie, no problems encountered) */
280 static int
281 removehomedir(struct passwd *pwp)
283 struct stat st;
285 /* userid not root? */
286 if (pwp->pw_uid == 0) {
287 warnx("Not deleting home directory `%s'; userid is 0", pwp->pw_dir);
288 return 0;
291 /* directory exists (and is a directory!) */
292 if (stat(pwp->pw_dir, &st) < 0) {
293 warn("Cannot access home directory `%s'", pwp->pw_dir);
294 return 0;
296 if (!S_ISDIR(st.st_mode)) {
297 warnx("Home directory `%s' is not a directory", pwp->pw_dir);
298 return 0;
301 /* userid matches directory owner? */
302 if (st.st_uid != pwp->pw_uid) {
303 warnx("User `%s' doesn't own directory `%s', not removed",
304 pwp->pw_name, pwp->pw_dir);
305 return 0;
308 (void)seteuid(pwp->pw_uid);
309 /* we add the "|| true" to keep asystem() quiet if there is a non-zero exit status. */
310 (void)asystem("%s -rf %s > /dev/null 2>&1 || true", RM, pwp->pw_dir);
311 (void)seteuid(0);
312 if (rmdir(pwp->pw_dir) < 0) {
313 warn("Unable to remove all files in `%s'", pwp->pw_dir);
314 return 0;
316 return 1;
319 /* return 1 if all of `s' is numeric */
320 static int
321 is_number(const char *s)
323 for ( ; *s ; s++) {
324 if (!isdigit((unsigned char) *s)) {
325 return 0;
328 return 1;
332 * check that the effective uid is 0 - called from funcs which will
333 * modify data and config files.
335 static void
336 checkeuid(void)
338 if (geteuid() != 0) {
339 errx(EXIT_FAILURE, "Program must be run as root");
343 /* copy any dot files into the user's home directory */
344 static int
345 copydotfiles(char *skeldir, int uid, int gid, char *dir, mode_t homeperm)
347 struct dirent *dp;
348 DIR *dirp;
349 int n;
351 if ((dirp = opendir(skeldir)) == NULL) {
352 warn("Can't open source . files dir `%s'", skeldir);
353 return 0;
355 for (n = 0; (dp = readdir(dirp)) != NULL && n == 0 ; ) {
356 if (strcmp(dp->d_name, ".") == 0 ||
357 strcmp(dp->d_name, "..") == 0) {
358 continue;
360 n = 1;
362 (void)closedir(dirp);
363 if (n == 0) {
364 warnx("No \"dot\" initialisation files found");
365 } else {
366 (void)asystem("cd %s && %s -rw -pe %s . %s",
367 skeldir, PAX, (verbose) ? "-v" : "", dir);
369 (void)asystem("%s -R -h %d:%d %s", CHOWN, uid, gid, dir);
370 (void)asystem("%s -R u+w %s", CHMOD, dir);
371 #ifdef EXTENSIONS
372 (void)asystem("%s 0%o %s", CHMOD, homeperm, dir);
373 #endif
374 return n;
377 /* create a group entry with gid `gid' */
378 static int
379 creategid(char *group, int gid, const char *name)
381 struct stat st;
382 FILE *from;
383 FILE *to;
384 char buf[MaxEntryLen];
385 char f[MaxFileNameLen];
386 int fd;
387 int cc;
389 if (getgrnam(group) != NULL) {
390 warnx("Can't create group `%s': already exists", group);
391 return 0;
393 if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
394 warn("Can't create group `%s': can't open `%s'", name,
395 _PATH_GROUP);
396 return 0;
398 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
399 warn("Can't lock `%s'", _PATH_GROUP);
400 (void)fclose(from);
401 return 0;
403 (void)fstat(fileno(from), &st);
404 (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
405 if ((fd = mkstemp(f)) < 0) {
406 warn("Can't create group `%s': mkstemp failed", group);
407 (void)fclose(from);
408 return 0;
410 if ((to = fdopen(fd, "w")) == NULL) {
411 warn("Can't create group `%s': fdopen `%s' failed",
412 group, f);
413 (void)fclose(from);
414 (void)close(fd);
415 (void)unlink(f);
416 return 0;
418 while ((cc = fread(buf, sizeof(char), sizeof(buf), from)) > 0) {
419 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
420 warn("Can't create group `%s': short write to `%s'",
421 group, f);
422 (void)fclose(from);
423 (void)close(fd);
424 (void)unlink(f);
425 return 0;
428 (void)fprintf(to, "%s:*:%d:%s\n", group, gid, name);
429 (void)fclose(from);
430 (void)fclose(to);
431 if (rename(f, _PATH_GROUP) < 0) {
432 warn("Can't create group `%s': can't rename `%s' to `%s'",
433 group, f, _PATH_GROUP);
434 (void)unlink(f);
435 return 0;
437 (void)chmod(_PATH_GROUP, st.st_mode & 07777);
438 syslog(LOG_INFO, "New group added: name=%s, gid=%d", group, gid);
439 return 1;
442 /* modify the group entry with name `group' to be newent */
443 static int
444 modify_gid(char *group, char *newent)
446 struct stat st;
447 FILE *from;
448 FILE *to;
449 char buf[MaxEntryLen];
450 char f[MaxFileNameLen];
451 char *colon;
452 int groupc;
453 int entc;
454 int fd;
455 int cc;
457 if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
458 warn("Can't modify group `%s': can't open `%s'",
459 group, _PATH_GROUP);
460 return 0;
462 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
463 warn("Can't modify group `%s': can't lock `%s'",
464 group, _PATH_GROUP);
465 (void)fclose(from);
466 return 0;
468 (void)fstat(fileno(from), &st);
469 (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
470 if ((fd = mkstemp(f)) < 0) {
471 warn("Can't modify group `%s': mkstemp failed", group);
472 (void)fclose(from);
473 return 0;
475 if ((to = fdopen(fd, "w")) == NULL) {
476 warn("Can't modify group `%s': fdopen `%s' failed", group, f);
477 (void)fclose(from);
478 (void)close(fd);
479 (void)unlink(f);
480 return 0;
482 groupc = strlen(group);
483 while (fgets(buf, sizeof(buf), from) != NULL) {
484 cc = strlen(buf);
485 if ((colon = strchr(buf, ':')) == NULL) {
486 warnx("Badly formed entry `%s'", buf);
487 continue;
489 entc = (int)(colon - buf);
490 if (entc == groupc &&
491 strncmp(group, buf, (unsigned) entc) == 0) {
492 if (newent == NULL) {
493 struct group *grp_rm;
494 struct passwd *user_pwd;
497 * Check that the group being removed
498 * isn't any user's Primary group. Just
499 * warn if it is. This could cause problems
500 * if the group GID was reused for a
501 * different purpose.
504 grp_rm = find_group_info(group);
505 while ((user_pwd = getpwent()) != NULL) {
506 if (user_pwd->pw_gid == grp_rm->gr_gid) {
507 warnx("Warning: group `%s'(%d)"
508 " is the primary group of"
509 " `%s'. Use caution if you"
510 " later add this GID.",
511 grp_rm->gr_name,
512 grp_rm->gr_gid, user_pwd->pw_name);
515 endpwent();
516 continue;
517 } else {
518 cc = strlen(newent);
519 (void)strlcpy(buf, newent, sizeof(buf));
522 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
523 warn("Can't modify group `%s': short write to `%s'",
524 group, f);
525 (void)fclose(from);
526 (void)close(fd);
527 (void)unlink(f);
528 return 0;
531 (void)fclose(from);
532 (void)fclose(to);
533 if (rename(f, _PATH_GROUP) < 0) {
534 warn("Can't modify group `%s': can't rename `%s' to `%s'",
535 group, f, _PATH_GROUP);
536 (void)unlink(f);
537 return 0;
539 (void)chmod(_PATH_GROUP, st.st_mode & 07777);
540 if (newent == NULL) {
541 syslog(LOG_INFO, "group deleted: name=%s", group);
542 } else {
543 syslog(LOG_INFO, "group information modified: name=%s", group);
545 return 1;
548 /* modify the group entries for all `groups', by adding `user' */
549 static int
550 append_group(char *user, int ngroups, const char **groups)
552 struct group *grp;
553 struct stat st;
554 FILE *from;
555 FILE *to;
556 char buf[MaxEntryLen];
557 char f[MaxFileNameLen];
558 char *colon;
559 int groupc;
560 int entc;
561 int fd;
562 int nc;
563 int cc;
564 int i;
565 int j;
567 for (i = 0 ; i < ngroups ; i++) {
568 if ((grp = getgrnam(groups[i])) == NULL) {
569 warnx("Can't append group `%s' for user `%s'",
570 groups[i], user);
571 } else {
572 for (j = 0 ; grp->gr_mem[j] ; j++) {
573 if (strcmp(user, grp->gr_mem[j]) == 0) {
574 /* already in it */
575 groups[i] = "";
580 if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
581 warn("Can't append group(s) for `%s': can't open `%s'",
582 user, _PATH_GROUP);
583 return 0;
585 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
586 warn("Can't append group(s) for `%s': can't lock `%s'",
587 user, _PATH_GROUP);
588 (void)fclose(from);
589 return 0;
591 (void)fstat(fileno(from), &st);
592 (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
593 if ((fd = mkstemp(f)) < 0) {
594 warn("Can't append group(s) for `%s': mkstemp failed",
595 user);
596 (void)fclose(from);
597 return 0;
599 if ((to = fdopen(fd, "w")) == NULL) {
600 warn("Can't append group(s) for `%s': fdopen `%s' failed",
601 user, f);
602 (void)fclose(from);
603 (void)close(fd);
604 (void)unlink(f);
605 return 0;
607 while (fgets(buf, sizeof(buf), from) != NULL) {
608 cc = strlen(buf);
609 if ((colon = strchr(buf, ':')) == NULL) {
610 warnx("Badly formed entry `%s'", buf);
611 continue;
613 entc = (int)(colon - buf);
614 for (i = 0 ; i < ngroups ; i++) {
615 if ((groupc = strlen(groups[i])) == 0) {
616 continue;
618 if (entc == groupc &&
619 strncmp(groups[i], buf, (unsigned) entc) == 0) {
620 if ((nc = snprintf(&buf[cc - 1],
621 sizeof(buf) - cc + 1, "%s%s\n",
622 (buf[cc - 2] == ':') ? "" : ",", user)) < 0) {
623 warnx("Warning: group `%s' "
624 "entry too long", groups[i]);
626 cc += nc - 1;
629 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
630 warn("Can't append group(s) for `%s':"
631 " short write to `%s'", user, f);
632 (void)fclose(from);
633 (void)close(fd);
634 (void)unlink(f);
635 return 0;
638 (void)fclose(from);
639 (void)fclose(to);
640 if (rename(f, _PATH_GROUP) < 0) {
641 warn("Can't append group(s) for `%s': "
642 "can't rename `%s' to `%s'", user, f, _PATH_GROUP);
643 (void)unlink(f);
644 return 0;
646 (void)chmod(_PATH_GROUP, st.st_mode & 07777);
647 return 1;
650 /* the valid characters for login and group names */
651 #define VALID_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '_' || (c) == '-')
653 /* return 1 if `login' is a valid login name */
654 static int
655 valid_login(char *login_name, int allow_samba)
657 unsigned char *cp;
659 /* First character of a login name cannot be '-'. */
660 if (*login_name == '-') {
661 return 0;
663 if (strlen(login_name) >= LOGIN_NAME_MAX) {
664 return 0;
666 for (cp = (unsigned char *)login_name ; *cp ; cp++) {
667 if (!VALID_CHAR(*cp)) {
668 #ifdef EXTENSIONS
669 /* check for a trailing '$' in a Samba user name */
670 if (allow_samba && *cp == '$' && *(cp + 1) == 0x0) {
671 return 1;
673 #endif
674 return 0;
677 return 1;
680 /* return 1 if `group' is a valid group name */
681 static int
682 valid_group(char *group)
684 unsigned char *cp;
686 for (cp = (unsigned char *)group; *cp; cp++) {
687 if (!VALID_CHAR(*cp)) {
688 return 0;
691 return 1;
694 /* find the next gid in the range lo .. hi */
695 static int
696 getnextgid(int *gidp, int lo, int hi)
698 for (*gidp = lo ; *gidp < hi ; *gidp += 1) {
699 if (getgrgid((gid_t)*gidp) == NULL) {
700 return 1;
703 return 0;
706 #ifdef EXTENSIONS
707 /* save a range of uids */
708 static int
709 save_range(rangelist_t *rlp, char *cp)
711 int from;
712 int to;
713 int i;
715 if (rlp->rl_rsize == 0) {
716 rlp->rl_rsize = 32;
717 NEWARRAY(range_t, rlp->rl_rv, rlp->rl_rsize, return(0));
718 } else if (rlp->rl_rc == rlp->rl_rsize) {
719 rlp->rl_rsize *= 2;
720 RENEW(range_t, rlp->rl_rv, rlp->rl_rsize, return(0));
722 if (rlp->rl_rv && sscanf(cp, "%d..%d", &from, &to) == 2) {
723 for (i = rlp->rl_defrc ; i < rlp->rl_rc ; i++) {
724 if (rlp->rl_rv[i].r_from == from &&
725 rlp->rl_rv[i].r_to == to) {
726 break;
729 if (i == rlp->rl_rc) {
730 rlp->rl_rv[rlp->rl_rc].r_from = from;
731 rlp->rl_rv[rlp->rl_rc].r_to = to;
732 rlp->rl_rc += 1;
734 } else {
735 warnx("Bad range `%s'", cp);
736 return 0;
738 return 1;
740 #endif
742 /* set the defaults in the defaults file */
743 static int
744 setdefaults(user_t *up)
746 char template[MaxFileNameLen];
747 FILE *fp;
748 int ret;
749 int fd;
750 #ifdef EXTENSIONS
751 int i;
752 #endif
754 (void)snprintf(template, sizeof(template), "%s.XXXXXX",
755 _PATH_USERMGMT_CONF);
756 if ((fd = mkstemp(template)) < 0) {
757 warn("Can't set defaults: can't mkstemp `%s' for writing",
758 _PATH_USERMGMT_CONF);
759 return 0;
761 if ((fp = fdopen(fd, "w")) == NULL) {
762 warn("Can't set defaults: can't fdopen `%s' for writing",
763 _PATH_USERMGMT_CONF);
764 return 0;
766 ret = 1;
767 if (fprintf(fp, "group\t\t%s\n", up->u_primgrp) <= 0 ||
768 fprintf(fp, "base_dir\t%s\n", up->u_basedir) <= 0 ||
769 fprintf(fp, "skel_dir\t%s\n", up->u_skeldir) <= 0 ||
770 fprintf(fp, "shell\t\t%s\n", up->u_shell) <= 0 ||
771 #ifdef EXTENSIONS
772 fprintf(fp, "class\t\t%s\n", up->u_class) <= 0 ||
773 fprintf(fp, "homeperm\t0%o\n", up->u_homeperm) <= 0 ||
774 #endif
775 fprintf(fp, "inactive\t%s\n", (up->u_inactive == NULL) ?
776 UNSET_INACTIVE : up->u_inactive) <= 0 ||
777 fprintf(fp, "expire\t\t%s\n", (up->u_expire == NULL) ?
778 UNSET_EXPIRY : up->u_expire) <= 0 ||
779 fprintf(fp, "preserve\t%s\n", (up->u_preserve == 0) ?
780 "false" : "true") <= 0) {
781 warn("Can't write to `%s'", _PATH_USERMGMT_CONF);
782 ret = 0;
784 #ifdef EXTENSIONS
785 for (i = (up->u_defrc != up->u_rc) ? up->u_defrc : 0;
786 i < up->u_rc ; i++) {
787 if (fprintf(fp, "range\t\t%d..%d\n", up->u_rv[i].r_from,
788 up->u_rv[i].r_to) <= 0) {
789 warn("Can't set defaults: can't write to `%s'",
790 _PATH_USERMGMT_CONF);
791 ret = 0;
794 #endif
795 (void)fclose(fp);
796 if (ret) {
797 ret = ((rename(template, _PATH_USERMGMT_CONF) == 0) &&
798 (chmod(_PATH_USERMGMT_CONF, 0644) == 0));
800 return ret;
803 /* read the defaults file */
804 static void
805 read_defaults(def_t *dp)
807 struct stat st;
808 size_t lineno;
809 size_t len;
810 FILE *fp;
811 char *cp;
812 char *s;
813 user_t *up = &dp->user;
814 group_t *gp = &dp->group;
816 (void)memset(dp, 0, sizeof(*dp));
818 memsave(&up->u_primgrp, DEF_GROUP, strlen(DEF_GROUP));
819 memsave(&up->u_basedir, DEF_BASEDIR, strlen(DEF_BASEDIR));
820 memsave(&up->u_skeldir, DEF_SKELDIR, strlen(DEF_SKELDIR));
821 memsave(&up->u_shell, DEF_SHELL, strlen(DEF_SHELL));
822 memsave(&up->u_comment, DEF_COMMENT, strlen(DEF_COMMENT));
823 #ifdef EXTENSIONS
824 memsave(&up->u_class, DEF_CLASS, strlen(DEF_CLASS));
825 #endif
826 up->u_rsize = 16;
827 up->u_defrc = 0;
828 NEWARRAY(range_t, up->u_rv, up->u_rsize, exit(1));
829 up->u_inactive = DEF_INACTIVE;
830 up->u_expire = DEF_EXPIRE;
831 gp->g_rsize = 16;
832 gp->g_defrc = 0;
833 NEWARRAY(range_t, gp->g_rv, gp->g_rsize, exit(1));
834 if ((fp = fopen(_PATH_USERMGMT_CONF, "r")) == NULL) {
835 if (stat(_PATH_USERMGMT_CONF, &st) < 0 && !setdefaults(up)) {
836 warn("Can't create `%s' defaults file",
837 _PATH_USERMGMT_CONF);
839 fp = fopen(_PATH_USERMGMT_CONF, "r");
841 if (fp != NULL) {
842 while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) {
843 if (strncmp(s, "group", 5) == 0) {
844 cp = skipspace(s + 5);
845 memsave(&up->u_primgrp, (char *)cp, strlen(cp));
846 } else if (strncmp(s, "base_dir", 8) == 0) {
847 cp = skipspace(s + 8);
848 memsave(&up->u_basedir, (char *)cp, strlen(cp));
849 } else if (strncmp(s, "skel_dir", 8) == 0) {
850 cp = skipspace(s + 8);
851 memsave(&up->u_skeldir, (char *)cp, strlen(cp));
852 } else if (strncmp(s, "shell", 5) == 0) {
853 cp = skipspace(s + 5);
854 memsave(&up->u_shell, cp, strlen(cp));
855 #ifdef EXTENSIONS
856 } else if (strncmp((char *)s, "class", 5) == 0) {
857 cp = skipspace(s + 5);
858 memsave(&up->u_class, cp, strlen(cp));
859 #endif
860 #ifdef EXTENSIONS
861 } else if (strncmp(s, "homeperm", 8) == 0) {
862 for (cp = s + 8; *cp &&
863 isspace((unsigned char)*cp); cp++)
865 up->u_homeperm = strtoul(cp, NULL, 8);
866 #endif
867 } else if (strncmp(s, "inactive", 8) == 0) {
868 cp = skipspace(s + 8);
869 if (strcmp(cp, UNSET_INACTIVE) == 0) {
870 if (up->u_inactive) {
871 FREE(up->u_inactive);
873 up->u_inactive = NULL;
874 } else {
875 memsave(&up->u_inactive, cp, strlen(cp));
877 #ifdef EXTENSIONS
878 } else if (strncmp(s, "range", 5) == 0) {
879 cp = skipspace(s + 5);
880 (void)save_range(&up->u_r, cp);
881 #endif
882 #ifdef EXTENSIONS
883 } else if (strncmp(s, "preserve", 8) == 0) {
884 cp = skipspace(s + 8);
885 up->u_preserve =
886 (strncmp(cp, "true", 4) == 0) ? 1 :
887 (strncmp(cp, "yes", 3) == 0) ? 1 : atoi(cp);
888 #endif
889 } else if (strncmp(s, "expire", 6) == 0) {
890 cp = skipspace(s + 6);
891 if (strcmp(cp, UNSET_EXPIRY) == 0) {
892 if (up->u_expire) {
893 FREE(up->u_expire);
895 up->u_expire = NULL;
896 } else {
897 memsave(&up->u_expire, cp, strlen(cp));
899 #ifdef EXTENSIONS
900 } else if (strncmp(s, "gid_range", 9) == 0) {
901 cp = skipspace(s + 9);
902 (void)save_range(&gp->g_r, cp);
903 #endif
905 (void)free(s);
907 (void)fclose(fp);
909 if (up->u_rc == 0) {
910 up->u_rv[up->u_rc].r_from = DEF_LOWUID;
911 up->u_rv[up->u_rc].r_to = DEF_HIGHUID;
912 up->u_rc += 1;
914 up->u_defrc = up->u_rc;
915 up->u_homeperm = DEF_HOMEPERM;
918 /* return the next valid unused uid */
919 static int
920 getnextuid(int sync_uid_gid, int *uid, int low_uid, int high_uid)
922 for (*uid = low_uid ; *uid <= high_uid ; (*uid)++) {
923 if (getpwuid((uid_t)(*uid)) == NULL && *uid != NOBODY_UID) {
924 if (sync_uid_gid) {
925 if (getgrgid((gid_t)(*uid)) == NULL) {
926 return 1;
928 } else {
929 return 1;
933 return 0;
936 /* structure which defines a password type */
937 typedef struct passwd_type_t {
938 const char *type; /* optional type descriptor */
939 size_t desc_length; /* length of type descriptor */
940 size_t length; /* length of password */
941 const char *regex; /* regexp to output the password */
942 size_t re_sub; /* subscript of regexp to use */
943 } passwd_type_t;
945 static passwd_type_t passwd_types[] = {
946 { "$sha1", 5, 28, "\\$[^$]+\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* SHA1 */
947 { "$2a", 3, 53, "\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* Blowfish */
948 { "$1", 2, 34, NULL, 0 }, /* MD5 */
949 { "", 0, DES_Len,NULL, 0 }, /* standard DES */
950 { NULL, (size_t)~0, (size_t)~0, NULL, 0 }
951 /* none - terminate search */
954 /* return non-zero if it's a valid password - check length for cipher type */
955 static int
956 valid_password_length(char *newpasswd)
958 passwd_type_t *pwtp;
959 regmatch_t matchv[10];
960 regex_t r;
962 for (pwtp = passwd_types; pwtp->desc_length != (size_t)~0; pwtp++) {
963 if (strncmp(newpasswd, pwtp->type, pwtp->desc_length) == 0) {
964 if (pwtp->regex == NULL) {
965 return strlen(newpasswd) == pwtp->length;
967 (void)regcomp(&r, pwtp->regex, REG_EXTENDED);
968 if (regexec(&r, newpasswd, 10, matchv, 0) == 0) {
969 regfree(&r);
970 return (int)(matchv[pwtp->re_sub].rm_eo -
971 matchv[pwtp->re_sub].rm_so) ==
972 pwtp->length;
974 regfree(&r);
977 return 0;
980 #ifdef EXTENSIONS
981 /* return 1 if `class' is a valid login class */
982 static int
983 valid_class(char *class)
985 login_cap_t *lc;
987 if (class == NULL || *class == '\0') {
988 return 1;
991 * Check if /etc/login.conf exists. login_getclass() will
992 * return 1 due to it not existing, so not informing the
993 * user the actual login class does not exist.
996 if (access(PATH_LOGINCONF, R_OK) == -1) {
997 warn("Access failed for `%s'; will not validate class `%s'",
998 PATH_LOGINCONF, class);
999 return 1;
1002 if ((lc = login_getclass(class)) != NULL) {
1003 login_close(lc);
1004 return 1;
1006 return 0;
1009 /* return 1 if the `shellname' is a valid user shell */
1010 static int
1011 valid_shell(const char *shellname)
1013 char *shellp;
1015 if (access(_PATH_SHELLS, R_OK) == -1) {
1016 /* Don't exit */
1017 warn("Access failed for `%s'; will not validate shell `%s'",
1018 _PATH_SHELLS, shellname);
1019 return 1;
1022 /* if nologin is used as a shell, consider it a valid shell */
1023 if (strcmp(shellname, NOLOGIN) == 0)
1024 return 1;
1026 while ((shellp = getusershell()) != NULL)
1027 if (strcmp(shellp, shellname) == 0)
1028 return 1;
1030 warnx("Shell `%s' not found in `%s'", shellname, _PATH_SHELLS);
1032 return access(shellname, X_OK) != -1;
1034 #endif
1036 /* look for a valid time, return 0 if it was specified but bad */
1037 static int
1038 scantime(time_t *tp, char *s)
1040 struct tm tm;
1041 char *ep;
1042 long val;
1044 *tp = 0;
1045 if (s != NULL) {
1046 (void)memset(&tm, 0, sizeof(tm));
1047 if (strptime(s, "%c", &tm) != NULL) {
1048 *tp = mktime(&tm);
1049 return (*tp == -1) ? 0 : 1;
1050 } else if (strptime(s, "%B %d %Y", &tm) != NULL) {
1051 *tp = mktime(&tm);
1052 return (*tp == -1) ? 0 : 1;
1053 } else {
1054 errno = 0;
1055 *tp = val = strtol(s, &ep, 10);
1056 if (*ep != '\0' || *tp < -1 || errno == ERANGE) {
1057 *tp = 0;
1058 return 0;
1060 if (*tp != val) {
1061 return 0;
1065 return 1;
1068 /* add a user */
1069 static int
1070 adduser(char *login_name, user_t *up)
1072 struct group *grp;
1073 struct stat st;
1074 time_t expire;
1075 time_t inactive;
1076 char password[PasswordLength + 1];
1077 char home[MaxFileNameLen];
1078 char buf[MaxFileNameLen];
1079 int sync_uid_gid;
1080 int masterfd;
1081 int ptmpfd;
1082 int gid;
1083 int cc;
1084 int i;
1086 if (!valid_login(login_name, up->u_allow_samba)) {
1087 errx(EXIT_FAILURE, "Can't add user `%s': invalid login name", login_name);
1089 #ifdef EXTENSIONS
1090 if (!valid_class(up->u_class)) {
1091 errx(EXIT_FAILURE, "Can't add user `%s': no such login class `%s'",
1092 login_name, up->u_class);
1094 #endif
1095 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDWR)) < 0) {
1096 err(EXIT_FAILURE, "Can't add user `%s': can't open `%s'",
1097 login_name, _PATH_MASTERPASSWD);
1099 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) {
1100 err(EXIT_FAILURE, "Can't add user `%s': can't lock `%s'",
1101 login_name, _PATH_MASTERPASSWD);
1103 pw_init();
1104 if ((ptmpfd = pw_lock(WAITSECS)) < 0) {
1105 int serrno = errno;
1106 (void)close(masterfd);
1107 errno = serrno;
1108 err(EXIT_FAILURE, "Can't add user `%s': can't obtain pw_lock",
1109 login_name);
1111 while ((cc = read(masterfd, buf, sizeof(buf))) > 0) {
1112 if (write(ptmpfd, buf, (size_t)(cc)) != cc) {
1113 int serrno = errno;
1114 (void)close(masterfd);
1115 (void)close(ptmpfd);
1116 (void)pw_abort();
1117 errno = serrno;
1118 err(EXIT_FAILURE, "Can't add user `%s': "
1119 "short write to /etc/ptmp", login_name);
1122 (void)close(masterfd);
1123 /* if no uid was specified, get next one in [low_uid..high_uid] range */
1124 sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0);
1125 if (up->u_uid == -1) {
1126 int got_id = 0;
1129 * Look for a free UID in the command line ranges (if any).
1130 * These start after the ranges specified in the config file.
1132 for (i = up->u_defrc; !got_id && i < up->u_rc ; i++) {
1133 got_id = getnextuid(sync_uid_gid, &up->u_uid,
1134 up->u_rv[i].r_from, up->u_rv[i].r_to);
1137 * If there were no free UIDs in the command line ranges,
1138 * try the ranges from the config file (there will always
1139 * be at least one default).
1141 for (i = 0; !got_id && i < up->u_defrc; i++) {
1142 got_id = getnextuid(sync_uid_gid, &up->u_uid,
1143 up->u_rv[i].r_from, up->u_rv[i].r_to);
1145 if (!got_id) {
1146 (void)close(ptmpfd);
1147 (void)pw_abort();
1148 errx(EXIT_FAILURE, "Can't add user `%s': "
1149 "can't get next uid for %d", login_name,
1150 up->u_uid);
1153 /* check uid isn't already allocated */
1154 if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) {
1155 (void)close(ptmpfd);
1156 (void)pw_abort();
1157 errx(EXIT_FAILURE, "Can't add user `%s': "
1158 "uid %d is already in use", login_name, up->u_uid);
1160 /* if -g=uid was specified, check gid is unused */
1161 if (sync_uid_gid) {
1162 if (getgrgid((gid_t)(up->u_uid)) != NULL) {
1163 (void)close(ptmpfd);
1164 (void)pw_abort();
1165 errx(EXIT_FAILURE, "Can't add user `%s': "
1166 "gid %d is already in use", login_name,
1167 up->u_uid);
1169 gid = up->u_uid;
1170 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) {
1171 gid = grp->gr_gid;
1172 } else if (is_number(up->u_primgrp) &&
1173 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) {
1174 gid = grp->gr_gid;
1175 } else {
1176 (void)close(ptmpfd);
1177 (void)pw_abort();
1178 errx(EXIT_FAILURE, "Can't add user `%s': group %s not found",
1179 login_name, up->u_primgrp);
1181 /* check name isn't already in use */
1182 if (!(up->u_flags & F_DUPUID) && getpwnam(login_name) != NULL) {
1183 (void)close(ptmpfd);
1184 (void)pw_abort();
1185 errx(EXIT_FAILURE, "Can't add user `%s': "
1186 "`%s' is already a user", login_name, login_name);
1188 if (up->u_flags & F_HOMEDIR) {
1189 (void)strlcpy(home, up->u_home, sizeof(home));
1190 } else {
1191 /* if home directory hasn't been given, make it up */
1192 (void)snprintf(home, sizeof(home), "%s/%s", up->u_basedir,
1193 login_name);
1195 if (up->u_flags & F_SHELL) {
1196 #ifdef EXTENSIONS
1197 if (!valid_shell(up->u_shell)) {
1198 int oerrno = errno;
1199 (void)close(ptmpfd);
1200 (void)pw_abort();
1201 errno = oerrno;
1202 errx(EXIT_FAILURE, "Can't add user `%s': "
1203 "Cannot access shell `%s'",
1204 login_name, up->u_shell);
1206 #endif
1209 if (!scantime(&inactive, up->u_inactive)) {
1210 warnx("Warning: inactive time `%s' invalid, password expiry off",
1211 up->u_inactive);
1213 if (!scantime(&expire, up->u_expire) || expire == -1) {
1214 warnx("Warning: expire time `%s' invalid, account expiry off",
1215 up->u_expire);
1216 expire = 0; /* Just in case. */
1218 if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR)) {
1219 warnx("Warning: home directory `%s' doesn't exist, "
1220 "and -m was not specified", home);
1222 password[sizeof(password) - 1] = '\0';
1223 if (up->u_password != NULL && valid_password_length(up->u_password)) {
1224 (void)strlcpy(password, up->u_password, sizeof(password));
1225 } else {
1226 (void)memset(password, '*', DES_Len);
1227 password[DES_Len] = 0;
1228 if (up->u_password != NULL) {
1229 warnx("Password `%s' is invalid: setting it to `%s'",
1230 up->u_password, password);
1233 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
1234 login_name,
1235 password,
1236 up->u_uid,
1237 gid,
1238 #ifdef EXTENSIONS
1239 up->u_class,
1240 #else
1242 #endif
1243 (long) inactive,
1244 (long) expire,
1245 up->u_comment,
1246 home,
1247 up->u_shell);
1248 if (write(ptmpfd, buf, (size_t) cc) != cc) {
1249 int serrno = errno;
1250 (void)close(ptmpfd);
1251 (void)pw_abort();
1252 errno = serrno;
1253 err(EXIT_FAILURE, "Can't add user `%s': write failed",
1254 login_name);
1256 if (up->u_flags & F_MKDIR) {
1257 if (lstat(home, &st) == 0) {
1258 (void)close(ptmpfd);
1259 (void)pw_abort();
1260 errx(EXIT_FAILURE,
1261 "Can't add user `%s': home directory `%s' "
1262 "already exists", login_name, home);
1263 } else {
1264 if (asystem("%s -p %s", MKDIR, home) != 0) {
1265 (void)close(ptmpfd);
1266 (void)pw_abort();
1267 errx(EXIT_FAILURE, "Can't add user `%s': "
1268 "can't mkdir `%s'", login_name, home);
1270 (void)copydotfiles(up->u_skeldir, up->u_uid, gid, home,
1271 up->u_homeperm);
1274 if (strcmp(up->u_primgrp, "=uid") == 0 &&
1275 getgrnam(login_name) == NULL &&
1276 !creategid(login_name, gid, login_name)) {
1277 (void)close(ptmpfd);
1278 (void)pw_abort();
1279 errx(EXIT_FAILURE, "Can't add user `%s': can't create gid %d ",
1280 login_name, gid);
1282 if (up->u_groupc > 0 &&
1283 !append_group(login_name, up->u_groupc, up->u_groupv)) {
1284 (void)close(ptmpfd);
1285 (void)pw_abort();
1286 errx(EXIT_FAILURE, "Can't add user `%s': can't append "
1287 "to new groups", login_name);
1289 (void)close(ptmpfd);
1290 #if PW_MKDB_ARGC == 2
1291 if (pw_mkdb(login_name, 0) < 0)
1292 #else
1293 if (pw_mkdb() < 0)
1294 #endif
1296 (void)pw_abort();
1297 errx(EXIT_FAILURE, "Can't add user `%s': pw_mkdb failed",
1298 login_name);
1300 syslog(LOG_INFO, "New user added: name=%s, uid=%d, gid=%d, home=%s, "
1301 "shell=%s", login_name, up->u_uid, gid, home, up->u_shell);
1302 return 1;
1305 /* remove a user from the groups file */
1306 static int
1307 rm_user_from_groups(char *login_name)
1309 struct stat st;
1310 regmatch_t matchv[10];
1311 regex_t r;
1312 FILE *from;
1313 FILE *to;
1314 char line[MaxEntryLen];
1315 char buf[MaxEntryLen];
1316 char f[MaxFileNameLen];
1317 int fd;
1318 int cc;
1319 int sc;
1321 (void)snprintf(line, sizeof(line), "(:|,)(%s)(,|$)", login_name);
1322 if ((sc = regcomp(&r, line, REG_EXTENDED|REG_NEWLINE)) != 0) {
1323 (void)regerror(sc, &r, buf, sizeof(buf));
1324 warnx("Can't compile regular expression `%s' (%s)", line,
1325 buf);
1326 return 0;
1328 if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
1329 warn("Can't remove user `%s' from `%s': can't open `%s'",
1330 login_name, _PATH_GROUP, _PATH_GROUP);
1331 return 0;
1333 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
1334 warn("Can't remove user `%s' from `%s': can't lock `%s'",
1335 login_name, _PATH_GROUP, _PATH_GROUP);
1336 (void)fclose(from);
1337 return 0;
1339 (void)fstat(fileno(from), &st);
1340 (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
1341 if ((fd = mkstemp(f)) < 0) {
1342 warn("Can't remove user `%s' from `%s': mkstemp failed",
1343 login_name, _PATH_GROUP);
1344 (void)fclose(from);
1345 return 0;
1347 if ((to = fdopen(fd, "w")) == NULL) {
1348 warn("Can't remove user `%s' from `%s': fdopen `%s' failed",
1349 login_name, _PATH_GROUP, f);
1350 (void)fclose(from);
1351 (void)close(fd);
1352 (void)unlink(f);
1353 return 0;
1355 while (fgets(buf, sizeof(buf), from) != NULL) {
1356 cc = strlen(buf);
1357 if (regexec(&r, buf, 10, matchv, 0) == 0) {
1358 if (buf[(int)matchv[1].rm_so] == ',') {
1359 matchv[2].rm_so = matchv[1].rm_so;
1360 } else if (matchv[2].rm_eo != matchv[3].rm_eo) {
1361 matchv[2].rm_eo = matchv[3].rm_eo;
1363 cc -= (int) matchv[2].rm_eo;
1364 sc = (int) matchv[2].rm_so;
1365 if (fwrite(buf, sizeof(char), (size_t)sc, to) != sc ||
1366 fwrite(&buf[(int)matchv[2].rm_eo], sizeof(char),
1367 (size_t)cc, to) != cc) {
1368 warn("Can't remove user `%s' from `%s': "
1369 "short write to `%s'", login_name,
1370 _PATH_GROUP, f);
1371 (void)fclose(from);
1372 (void)close(fd);
1373 (void)unlink(f);
1374 return 0;
1376 } else if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
1377 warn("Can't remove user `%s' from `%s': "
1378 "short write to `%s'", login_name, _PATH_GROUP, f);
1379 (void)fclose(from);
1380 (void)close(fd);
1381 (void)unlink(f);
1382 return 0;
1385 (void)fclose(from);
1386 (void)fclose(to);
1387 if (rename(f, _PATH_GROUP) < 0) {
1388 warn("Can't remove user `%s' from `%s': "
1389 "can't rename `%s' to `%s'",
1390 login_name, _PATH_GROUP, f, _PATH_GROUP);
1391 (void)unlink(f);
1392 return 0;
1394 (void)chmod(_PATH_GROUP, st.st_mode & 07777);
1395 return 1;
1398 /* check that the user or group is local, not from YP/NIS */
1399 static int
1400 is_local(char *name, const char *file)
1402 FILE *fp;
1403 char buf[MaxEntryLen];
1404 size_t len;
1405 int ret;
1407 if ((fp = fopen(file, "r")) == NULL) {
1408 err(EXIT_FAILURE, "Can't open `%s'", file);
1410 len = strlen(name);
1411 for (ret = 0 ; fgets(buf, sizeof(buf), fp) != NULL ; ) {
1412 if (strncmp(buf, name, len) == 0 && buf[len] == ':') {
1413 ret = 1;
1414 break;
1417 (void)fclose(fp);
1418 return ret;
1421 /* modify a user */
1422 static int
1423 moduser(char *login_name, char *newlogin, user_t *up, int allow_samba)
1425 struct passwd *pwp, pw;
1426 struct group *grp;
1427 const char *homedir;
1428 char *locked_pwd;
1429 size_t colonc;
1430 size_t loginc;
1431 size_t len;
1432 FILE *master;
1433 char newdir[MaxFileNameLen];
1434 char buf[MaxEntryLen];
1435 char pwbuf[MaxEntryLen];
1436 char *colon;
1437 int masterfd;
1438 int ptmpfd;
1439 int error;
1441 if (!valid_login(newlogin, allow_samba)) {
1442 errx(EXIT_FAILURE, "Can't modify user `%s': invalid login name",
1443 login_name);
1445 if (getpwnam_r(login_name, &pw, pwbuf, sizeof(pwbuf), &pwp) != 0
1446 || pwp == NULL) {
1447 errx(EXIT_FAILURE, "Can't modify user `%s': no such user",
1448 login_name);
1450 if (!is_local(login_name, _PATH_MASTERPASSWD)) {
1451 errx(EXIT_FAILURE, "Can't modify user `%s': must be a local user",
1452 login_name);
1454 /* keep dir name in case we need it for '-m' */
1455 homedir = pwp->pw_dir;
1457 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDWR)) < 0) {
1458 err(EXIT_FAILURE, "Can't modify user `%s': can't open `%s'",
1459 login_name, _PATH_MASTERPASSWD);
1461 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) {
1462 err(EXIT_FAILURE, "Can't modify user `%s': can't lock `%s'",
1463 login_name, _PATH_MASTERPASSWD);
1465 pw_init();
1466 if ((ptmpfd = pw_lock(WAITSECS)) < 0) {
1467 int serrno = errno;
1468 (void)close(masterfd);
1469 errno = serrno;
1470 err(EXIT_FAILURE, "Can't modify user `%s': "
1471 "can't obtain pw_lock", login_name);
1473 if ((master = fdopen(masterfd, "r")) == NULL) {
1474 int serrno = errno;
1475 (void)close(masterfd);
1476 (void)close(ptmpfd);
1477 (void)pw_abort();
1478 errno = serrno;
1479 err(EXIT_FAILURE, "Can't modify user `%s': "
1480 "fdopen fd for %s", login_name, _PATH_MASTERPASSWD);
1482 if (up != NULL) {
1483 if (up->u_flags & F_USERNAME) {
1485 * If changing name,
1486 * check new name isn't already in use
1488 if (strcmp(login_name, newlogin) != 0 &&
1489 getpwnam(newlogin) != NULL) {
1490 (void)close(ptmpfd);
1491 (void)pw_abort();
1492 errx(EXIT_FAILURE, "Can't modify user `%s': "
1493 "`%s' is already a user", login_name,
1494 newlogin);
1496 pwp->pw_name = newlogin;
1499 * Provide a new directory name in case the
1500 * home directory is to be moved.
1502 if (up->u_flags & F_MKDIR) {
1503 (void)snprintf(newdir, sizeof(newdir), "%s/%s",
1504 up->u_basedir, newlogin);
1505 pwp->pw_dir = newdir;
1508 if (up->u_flags & F_PASSWORD) {
1509 if (up->u_password != NULL) {
1510 if (!valid_password_length(up->u_password)) {
1511 (void)close(ptmpfd);
1512 (void)pw_abort();
1513 errx(EXIT_FAILURE,
1514 "Can't modify user `%s': "
1515 "invalid password: `%s'",
1516 login_name, up->u_password);
1518 if ((locked_pwd =
1519 strstr(pwp->pw_passwd, LOCKED)) != NULL) {
1521 * account is locked - keep it locked
1522 * and just change the password.
1524 if (asprintf(&locked_pwd, "%s%s",
1525 LOCKED, up->u_password) == -1) {
1526 (void)close(ptmpfd);
1527 (void)pw_abort();
1528 err(EXIT_FAILURE,
1529 "Can't modify user `%s': "
1530 "asprintf failed",
1531 login_name);
1533 pwp->pw_passwd = locked_pwd;
1534 } else {
1535 pwp->pw_passwd = up->u_password;
1540 /* check whether we should lock the account. */
1541 if (up->u_locked == LOCK) {
1542 /* check to see account if already locked. */
1543 if ((locked_pwd = strstr(pwp->pw_passwd, LOCKED))
1544 != NULL) {
1545 warnx("Account is already locked");
1546 } else {
1547 if (asprintf(&locked_pwd, "%s%s", LOCKED,
1548 pwp->pw_passwd) == -1) {
1549 (void)close(ptmpfd);
1550 (void)pw_abort();
1551 err(EXIT_FAILURE,
1552 "Can't modify user `%s': "
1553 "asprintf failed", login_name);
1555 pwp->pw_passwd = locked_pwd;
1557 } else if (up->u_locked == UNLOCK) {
1558 if ((locked_pwd = strstr(pwp->pw_passwd, LOCKED))
1559 == NULL) {
1560 warnx("Can't modify user `%s': "
1561 "account is not locked", login_name);
1562 } else {
1563 pwp->pw_passwd = locked_pwd + strlen(LOCKED);
1567 if (up->u_flags & F_UID) {
1568 /* check uid isn't already allocated */
1569 if (!(up->u_flags & F_DUPUID) &&
1570 getpwuid((uid_t)(up->u_uid)) != NULL) {
1571 (void)close(ptmpfd);
1572 (void)pw_abort();
1573 errx(EXIT_FAILURE, "Can't modify user `%s': "
1574 "uid `%d' is already in use", login_name,
1575 up->u_uid);
1577 pwp->pw_uid = up->u_uid;
1579 if (up->u_flags & F_GROUP) {
1580 /* if -g=uid was specified, check gid is unused */
1581 if (strcmp(up->u_primgrp, "=uid") == 0) {
1582 if (getgrgid((gid_t)(pwp->pw_uid)) != NULL) {
1583 (void)close(ptmpfd);
1584 (void)pw_abort();
1585 errx(EXIT_FAILURE,
1586 "Can't modify user `%s': "
1587 "gid %d is already in use",
1588 login_name, up->u_uid);
1590 pwp->pw_gid = pwp->pw_uid;
1591 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) {
1592 pwp->pw_gid = grp->gr_gid;
1593 } else if (is_number(up->u_primgrp) &&
1594 (grp = getgrgid(
1595 (gid_t)atoi(up->u_primgrp))) != NULL) {
1596 pwp->pw_gid = grp->gr_gid;
1597 } else {
1598 (void)close(ptmpfd);
1599 (void)pw_abort();
1600 errx(EXIT_FAILURE, "Can't modify user `%s': "
1601 "group %s not found", login_name,
1602 up->u_primgrp);
1605 if (up->u_flags & F_INACTIVE) {
1606 if (!scantime(&pwp->pw_change, up->u_inactive)) {
1607 warnx("Warning: inactive time `%s' invalid, "
1608 "password expiry off",
1609 up->u_inactive);
1612 if (up->u_flags & F_EXPIRE) {
1613 if (!scantime(&pwp->pw_expire, up->u_expire) ||
1614 pwp->pw_expire == -1) {
1615 warnx("Warning: expire time `%s' invalid, "
1616 "account expiry off",
1617 up->u_expire);
1618 pwp->pw_expire = 0;
1621 if (up->u_flags & F_COMMENT) {
1622 pwp->pw_gecos = up->u_comment;
1624 if (up->u_flags & F_HOMEDIR) {
1625 pwp->pw_dir = up->u_home;
1627 if (up->u_flags & F_SHELL) {
1628 #ifdef EXTENSIONS
1629 if (!valid_shell(up->u_shell)) {
1630 int oerrno = errno;
1631 (void)close(ptmpfd);
1632 (void)pw_abort();
1633 errno = oerrno;
1634 errx(EXIT_FAILURE, "Can't modify user `%s': "
1635 "Cannot access shell `%s'",
1636 login_name, up->u_shell);
1638 pwp->pw_shell = up->u_shell;
1639 #else
1640 pwp->pw_shell = up->u_shell;
1641 #endif
1643 #ifdef EXTENSIONS
1644 if (up->u_flags & F_CLASS) {
1645 if (!valid_class(up->u_class)) {
1646 (void)close(ptmpfd);
1647 (void)pw_abort();
1648 errx(EXIT_FAILURE, "Can't modify user `%s': "
1649 "no such login class `%s'", login_name,
1650 up->u_class);
1652 pwp->pw_class = up->u_class;
1654 #endif
1656 loginc = strlen(login_name);
1657 while (fgets(buf, sizeof(buf), master) != NULL) {
1658 if ((colon = strchr(buf, ':')) == NULL) {
1659 warnx("Malformed entry `%s'. Skipping", buf);
1660 continue;
1662 colonc = (size_t)(colon - buf);
1663 if (strncmp(login_name, buf, loginc) == 0 && loginc == colonc) {
1664 if (up != NULL) {
1665 len = snprintf(buf, sizeof(buf), "%s:%s:%d:%d:"
1666 #ifdef EXTENSIONS
1667 "%s"
1668 #endif
1669 ":%ld:%ld:%s:%s:%s\n",
1670 newlogin,
1671 pwp->pw_passwd,
1672 pwp->pw_uid,
1673 pwp->pw_gid,
1674 #ifdef EXTENSIONS
1675 pwp->pw_class,
1676 #endif
1677 (long)pwp->pw_change,
1678 (long)pwp->pw_expire,
1679 pwp->pw_gecos,
1680 pwp->pw_dir,
1681 pwp->pw_shell);
1682 if (write(ptmpfd, buf, len) != len) {
1683 int serrno = errno;
1684 (void)close(ptmpfd);
1685 (void)pw_abort();
1686 errno = serrno;
1687 err(EXIT_FAILURE, "Can't modify user "
1688 "`%s': write", login_name);
1691 } else {
1692 len = strlen(buf);
1693 if (write(ptmpfd, buf, len) != len) {
1694 int serrno = errno;
1695 (void)close(masterfd);
1696 (void)close(ptmpfd);
1697 (void)pw_abort();
1698 errno = serrno;
1699 err(EXIT_FAILURE, "Can't modify `%s': "
1700 "write", login_name);
1704 if (up != NULL) {
1705 if ((up->u_flags & F_MKDIR) &&
1706 asystem("%s %s %s", MV, homedir, pwp->pw_dir) != 0) {
1707 (void)close(ptmpfd);
1708 (void)pw_abort();
1709 errx(EXIT_FAILURE, "Can't modify user `%s': "
1710 "can't move `%s' to `%s'",
1711 login_name, homedir, pwp->pw_dir);
1713 if (up->u_groupc > 0 &&
1714 !append_group(newlogin, up->u_groupc, up->u_groupv)) {
1715 (void)close(ptmpfd);
1716 (void)pw_abort();
1717 errx(EXIT_FAILURE, "Can't modify user `%s': "
1718 "can't append `%s' to new groups",
1719 login_name, newlogin);
1722 (void)close(ptmpfd);
1723 (void)fclose(master);
1724 #if PW_MKDB_ARGC == 2
1725 if (up != NULL && strcmp(login_name, newlogin) == 0) {
1726 error = pw_mkdb(login_name, 0);
1727 } else {
1728 error = pw_mkdb(NULL, 0);
1730 #else
1731 error = pw_mkdb();
1732 #endif
1733 if (error < 0) {
1734 (void)pw_abort();
1735 errx(EXIT_FAILURE, "Can't modify user `%s': pw_mkdb failed",
1736 login_name);
1738 if (up == NULL) {
1739 syslog(LOG_INFO, "User removed: name=%s", login_name);
1740 } else if (strcmp(login_name, newlogin) == 0) {
1741 syslog(LOG_INFO, "User information modified: name=%s, uid=%d, "
1742 "gid=%d, home=%s, shell=%s",
1743 login_name, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir,
1744 pwp->pw_shell);
1745 } else {
1746 syslog(LOG_INFO, "User information modified: name=%s, "
1747 "new name=%s, uid=%d, gid=%d, home=%s, shell=%s",
1748 login_name, newlogin, pwp->pw_uid, pwp->pw_gid,
1749 pwp->pw_dir, pwp->pw_shell);
1751 return 1;
1754 #ifdef EXTENSIONS
1755 /* see if we can find out the user struct */
1756 static struct passwd *
1757 find_user_info(const char *name)
1759 struct passwd *pwp;
1761 if ((pwp = getpwnam(name)) != NULL) {
1762 return pwp;
1764 if (is_number(name) && (pwp = getpwuid((uid_t)atoi(name))) != NULL) {
1765 return pwp;
1767 return NULL;
1769 #endif
1771 /* see if we can find out the group struct */
1772 static struct group *
1773 find_group_info(const char *name)
1775 struct group *grp;
1777 if ((grp = getgrnam(name)) != NULL) {
1778 return grp;
1780 if (is_number(name) && (grp = getgrgid((gid_t)atoi(name))) != NULL) {
1781 return grp;
1783 return NULL;
1786 /* print out usage message, and then exit */
1787 void
1788 usermgmt_usage(const char *prog)
1790 if (strcmp(prog, "useradd") == 0) {
1791 (void)fprintf(stderr, "usage: %s -D [-F] [-b base-dir] "
1792 "[-e expiry-time] [-f inactive-time]\n"
1793 "\t[-g gid | name | =uid] [-k skel-dir] [-L login-class]\n"
1794 "\t[-M homeperm] [-r lowuid..highuid] [-s shell]\n", prog);
1795 (void)fprintf(stderr, "usage: %s [-moSv] [-b base-dir] "
1796 "[-c comment] [-d home-dir] [-e expiry-time]\n"
1797 "\t[-f inactive-time] [-G secondary-group] "
1798 "[-g gid | name | =uid]\n"
1799 "\t[-k skeletondir] [-L login-class] [-M homeperm] "
1800 "[-p password]\n"
1801 "\t[-r lowuid..highuid] [-s shell] [-u uid] user\n",
1802 prog);
1803 } else if (strcmp(prog, "usermod") == 0) {
1804 (void)fprintf(stderr, "usage: %s [-FmoSv] [-C yes/no] "
1805 "[-c comment] [-d home-dir] [-e expiry-time]\n"
1806 "\t[-f inactive] [-G secondary-group] "
1807 "[-g gid | name | =uid]\n"
1808 "\t[-L login-class] [-l new-login] [-p password] "
1809 "[-s shell] [-u uid]\n"
1810 "\tuser\n", prog);
1811 } else if (strcmp(prog, "userdel") == 0) {
1812 (void)fprintf(stderr, "usage: %s -D [-p preserve-value]\n", prog);
1813 (void)fprintf(stderr,
1814 "usage: %s [-rSv] [-p preserve-value] user\n", prog);
1815 #ifdef EXTENSIONS
1816 } else if (strcmp(prog, "userinfo") == 0) {
1817 (void)fprintf(stderr, "usage: %s [-e] user\n", prog);
1818 #endif
1819 } else if (strcmp(prog, "groupadd") == 0) {
1820 (void)fprintf(stderr, "usage: %s [-ov] [-g gid]"
1821 " [-r lowgid..highgid] group\n", prog);
1822 } else if (strcmp(prog, "groupdel") == 0) {
1823 (void)fprintf(stderr, "usage: %s [-v] group\n", prog);
1824 } else if (strcmp(prog, "groupmod") == 0) {
1825 (void)fprintf(stderr,
1826 "usage: %s [-ov] [-g gid] [-n newname] group\n", prog);
1827 } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) {
1828 (void)fprintf(stderr,
1829 "usage: %s ( add | del | mod | info ) ...\n", prog);
1830 #ifdef EXTENSIONS
1831 } else if (strcmp(prog, "groupinfo") == 0) {
1832 (void)fprintf(stderr, "usage: %s [-ev] group\n", prog);
1833 #endif
1835 exit(EXIT_FAILURE);
1836 /* NOTREACHED */
1839 #ifdef EXTENSIONS
1840 #define ADD_OPT_EXTENSIONS "M:p:r:vL:S"
1841 #else
1842 #define ADD_OPT_EXTENSIONS
1843 #endif
1846 useradd(int argc, char **argv)
1848 def_t def;
1849 user_t *up = &def.user;
1850 int defaultfield;
1851 int bigD;
1852 int c;
1853 #ifdef EXTENSIONS
1854 int i;
1855 #endif
1857 read_defaults(&def);
1858 up->u_uid = -1;
1859 defaultfield = bigD = 0;
1860 while ((c = getopt(argc, argv, "DFG:b:c:d:e:f:g:k:mou:s:"
1861 ADD_OPT_EXTENSIONS)) != -1) {
1862 switch(c) {
1863 case 'D':
1864 bigD = 1;
1865 break;
1866 case 'F':
1868 * Setting -1 will force the new user to
1869 * change their password as soon as they
1870 * next log in - passwd(5).
1872 defaultfield = 1;
1873 memsave(&up->u_inactive, "-1", strlen("-1"));
1874 break;
1875 case 'G':
1876 while (up->u_groupc < NGROUPS_MAX &&
1877 (up->u_groupv[up->u_groupc] = strsep(&optarg, ",")) != NULL) {
1878 if (up->u_groupv[up->u_groupc][0] != 0) {
1879 up->u_groupc++;
1882 if (optarg != NULL) {
1883 warnx("Truncated list of secondary groups "
1884 "to %d entries", NGROUPS_MAX);
1886 break;
1887 #ifdef EXTENSIONS
1888 case 'S':
1889 up->u_allow_samba = 1;
1890 break;
1891 #endif
1892 case 'b':
1893 defaultfield = 1;
1894 memsave(&up->u_basedir, optarg, strlen(optarg));
1895 break;
1896 case 'c':
1897 memsave(&up->u_comment, optarg, strlen(optarg));
1898 break;
1899 case 'd':
1900 memsave(&up->u_home, optarg, strlen(optarg));
1901 up->u_flags |= F_HOMEDIR;
1902 break;
1903 case 'e':
1904 defaultfield = 1;
1905 memsave(&up->u_expire, optarg, strlen(optarg));
1906 break;
1907 case 'f':
1908 defaultfield = 1;
1909 memsave(&up->u_inactive, optarg, strlen(optarg));
1910 break;
1911 case 'g':
1912 defaultfield = 1;
1913 memsave(&up->u_primgrp, optarg, strlen(optarg));
1914 break;
1915 case 'k':
1916 defaultfield = 1;
1917 memsave(&up->u_skeldir, optarg, strlen(optarg));
1918 break;
1919 #ifdef EXTENSIONS
1920 case 'L':
1921 defaultfield = 1;
1922 memsave(&up->u_class, optarg, strlen(optarg));
1923 break;
1924 #endif
1925 case 'm':
1926 up->u_flags |= F_MKDIR;
1927 break;
1928 #ifdef EXTENSIONS
1929 case 'M':
1930 defaultfield = 1;
1931 up->u_homeperm = strtoul(optarg, NULL, 8);
1932 break;
1933 #endif
1934 case 'o':
1935 up->u_flags |= F_DUPUID;
1936 break;
1937 #ifdef EXTENSIONS
1938 case 'p':
1939 memsave(&up->u_password, optarg, strlen(optarg));
1940 break;
1941 #endif
1942 #ifdef EXTENSIONS
1943 case 'r':
1944 defaultfield = 1;
1945 (void)save_range(&up->u_r, optarg);
1946 break;
1947 #endif
1948 case 's':
1949 up->u_flags |= F_SHELL;
1950 defaultfield = 1;
1951 memsave(&up->u_shell, optarg, strlen(optarg));
1952 break;
1953 case 'u':
1954 up->u_uid = check_numeric(optarg, "uid");
1955 break;
1956 #ifdef EXTENSIONS
1957 case 'v':
1958 verbose = 1;
1959 break;
1960 #endif
1961 default:
1962 usermgmt_usage("useradd");
1963 /* NOTREACHED */
1966 if (bigD) {
1967 if (defaultfield) {
1968 checkeuid();
1969 return setdefaults(up) ? EXIT_SUCCESS : EXIT_FAILURE;
1971 (void)printf("group\t\t%s\n", up->u_primgrp);
1972 (void)printf("base_dir\t%s\n", up->u_basedir);
1973 (void)printf("skel_dir\t%s\n", up->u_skeldir);
1974 (void)printf("shell\t\t%s\n", up->u_shell);
1975 #ifdef EXTENSIONS
1976 (void)printf("class\t\t%s\n", up->u_class);
1977 (void)printf("homeperm\t0%o\n", up->u_homeperm);
1978 #endif
1979 (void)printf("inactive\t%s\n", (up->u_inactive == NULL) ?
1980 UNSET_INACTIVE : up->u_inactive);
1981 (void)printf("expire\t\t%s\n", (up->u_expire == NULL) ?
1982 UNSET_EXPIRY : up->u_expire);
1983 #ifdef EXTENSIONS
1984 for (i = 0 ; i < up->u_rc ; i++) {
1985 (void)printf("range\t\t%d..%d\n",
1986 up->u_rv[i].r_from, up->u_rv[i].r_to);
1988 #endif
1989 return EXIT_SUCCESS;
1991 argc -= optind;
1992 argv += optind;
1993 if (argc != 1) {
1994 usermgmt_usage("useradd");
1996 checkeuid();
1997 openlog("useradd", LOG_PID, LOG_USER);
1998 return adduser(*argv, up) ? EXIT_SUCCESS : EXIT_FAILURE;
2001 #ifdef EXTENSIONS
2002 #define MOD_OPT_EXTENSIONS "p:vL:S"
2003 #else
2004 #define MOD_OPT_EXTENSIONS
2005 #endif
2008 usermod(int argc, char **argv)
2010 def_t def;
2011 user_t *up = &def.user;
2012 char newuser[MaxUserNameLen + 1];
2013 int c, have_new_user;
2015 (void)memset(newuser, 0, sizeof(newuser));
2016 read_defaults(&def);
2017 have_new_user = 0;
2018 up->u_locked = -1;
2019 while ((c = getopt(argc, argv, "C:FG:c:d:e:f:g:l:mos:u:"
2020 MOD_OPT_EXTENSIONS)) != -1) {
2021 switch(c) {
2022 case 'G':
2023 while (up->u_groupc < NGROUPS_MAX &&
2024 (up->u_groupv[up->u_groupc] =
2025 strsep(&optarg, ",")) != NULL) {
2026 if (up->u_groupv[up->u_groupc][0] != 0) {
2027 up->u_groupc++;
2030 if (optarg != NULL) {
2031 warnx("Truncated list of secondary groups "
2032 "to %d entries", NGROUPS_MAX);
2034 up->u_flags |= F_SECGROUP;
2035 break;
2036 #ifdef EXTENSIONS
2037 case 'S':
2038 up->u_allow_samba = 1;
2039 break;
2040 #endif
2041 case 'c':
2042 memsave(&up->u_comment, optarg, strlen(optarg));
2043 up->u_flags |= F_COMMENT;
2044 break;
2045 case 'C':
2046 if (strcasecmp(optarg, "yes") == 0) {
2047 up->u_locked = LOCK;
2048 } else if (strcasecmp(optarg, "no") == 0) {
2049 up->u_locked = UNLOCK;
2050 } else {
2051 /* No idea. */
2052 errx(EXIT_FAILURE,
2053 "Please type 'yes' or 'no'");
2055 break;
2056 case 'F':
2057 memsave(&up->u_inactive, "-1", strlen("-1"));
2058 up->u_flags |= F_INACTIVE;
2059 break;
2060 case 'd':
2061 memsave(&up->u_home, optarg, strlen(optarg));
2062 up->u_flags |= F_HOMEDIR;
2063 break;
2064 case 'e':
2065 memsave(&up->u_expire, optarg, strlen(optarg));
2066 up->u_flags |= F_EXPIRE;
2067 break;
2068 case 'f':
2069 memsave(&up->u_inactive, optarg, strlen(optarg));
2070 up->u_flags |= F_INACTIVE;
2071 break;
2072 case 'g':
2073 memsave(&up->u_primgrp, optarg, strlen(optarg));
2074 up->u_flags |= F_GROUP;
2075 break;
2076 case 'l':
2077 (void)strlcpy(newuser, optarg, sizeof(newuser));
2078 have_new_user = 1;
2079 up->u_flags |= F_USERNAME;
2080 break;
2081 #ifdef EXTENSIONS
2082 case 'L':
2083 memsave(&up->u_class, optarg, strlen(optarg));
2084 up->u_flags |= F_CLASS;
2085 break;
2086 #endif
2087 case 'm':
2088 up->u_flags |= F_MKDIR;
2089 break;
2090 case 'o':
2091 up->u_flags |= F_DUPUID;
2092 break;
2093 #ifdef EXTENSIONS
2094 case 'p':
2095 memsave(&up->u_password, optarg, strlen(optarg));
2096 up->u_flags |= F_PASSWORD;
2097 break;
2098 #endif
2099 case 's':
2100 memsave(&up->u_shell, optarg, strlen(optarg));
2101 up->u_flags |= F_SHELL;
2102 break;
2103 case 'u':
2104 up->u_uid = check_numeric(optarg, "uid");
2105 up->u_flags |= F_UID;
2106 break;
2107 #ifdef EXTENSIONS
2108 case 'v':
2109 verbose = 1;
2110 break;
2111 #endif
2112 default:
2113 usermgmt_usage("usermod");
2114 /* NOTREACHED */
2117 if ((up->u_flags & F_MKDIR) && !(up->u_flags & F_HOMEDIR) &&
2118 !(up->u_flags & F_USERNAME)) {
2119 warnx("Option 'm' useless without 'd' or 'l' -- ignored");
2120 up->u_flags &= ~F_MKDIR;
2122 argc -= optind;
2123 argv += optind;
2124 if (argc != 1) {
2125 usermgmt_usage("usermod");
2127 checkeuid();
2128 openlog("usermod", LOG_PID, LOG_USER);
2129 return moduser(*argv, (have_new_user) ? newuser : *argv, up,
2130 up->u_allow_samba) ? EXIT_SUCCESS : EXIT_FAILURE;
2133 #ifdef EXTENSIONS
2134 #define DEL_OPT_EXTENSIONS "Dp:vS"
2135 #else
2136 #define DEL_OPT_EXTENSIONS
2137 #endif
2140 userdel(int argc, char **argv)
2142 struct passwd *pwp;
2143 def_t def;
2144 user_t *up = &def.user;
2145 char password[PasswordLength + 1];
2146 int defaultfield;
2147 int rmhome;
2148 int bigD;
2149 int c;
2151 read_defaults(&def);
2152 defaultfield = bigD = rmhome = 0;
2153 while ((c = getopt(argc, argv, "r" DEL_OPT_EXTENSIONS)) != -1) {
2154 switch(c) {
2155 #ifdef EXTENSIONS
2156 case 'D':
2157 bigD = 1;
2158 break;
2159 #endif
2160 #ifdef EXTENSIONS
2161 case 'S':
2162 up->u_allow_samba = 1;
2163 break;
2164 #endif
2165 #ifdef EXTENSIONS
2166 case 'p':
2167 defaultfield = 1;
2168 up->u_preserve = (strcmp(optarg, "true") == 0) ? 1 :
2169 (strcmp(optarg, "yes") == 0) ? 1 :
2170 atoi(optarg);
2171 break;
2172 #endif
2173 case 'r':
2174 rmhome = 1;
2175 break;
2176 #ifdef EXTENSIONS
2177 case 'v':
2178 verbose = 1;
2179 break;
2180 #endif
2181 default:
2182 usermgmt_usage("userdel");
2183 /* NOTREACHED */
2186 #ifdef EXTENSIONS
2187 if (bigD) {
2188 if (defaultfield) {
2189 checkeuid();
2190 return setdefaults(up) ? EXIT_SUCCESS : EXIT_FAILURE;
2192 (void)printf("preserve\t%s\n", (up->u_preserve) ? "true" :
2193 "false");
2194 return EXIT_SUCCESS;
2196 #endif
2197 argc -= optind;
2198 argv += optind;
2199 if (argc != 1) {
2200 usermgmt_usage("userdel");
2202 checkeuid();
2203 if ((pwp = getpwnam(*argv)) == NULL) {
2204 warnx("No such user `%s'", *argv);
2205 return EXIT_FAILURE;
2207 if (rmhome) {
2208 (void)removehomedir(pwp);
2210 if (up->u_preserve) {
2211 up->u_flags |= F_SHELL;
2212 memsave(&up->u_shell, NOLOGIN, strlen(NOLOGIN));
2213 (void)memset(password, '*', DES_Len);
2214 password[DES_Len] = 0;
2215 memsave(&up->u_password, password, strlen(password));
2216 up->u_flags |= F_PASSWORD;
2217 openlog("userdel", LOG_PID, LOG_USER);
2218 return moduser(*argv, *argv, up, up->u_allow_samba) ?
2219 EXIT_SUCCESS : EXIT_FAILURE;
2221 if (!rm_user_from_groups(*argv)) {
2222 return 0;
2224 openlog("userdel", LOG_PID, LOG_USER);
2225 return moduser(*argv, *argv, NULL, up->u_allow_samba) ?
2226 EXIT_SUCCESS : EXIT_FAILURE;
2229 #ifdef EXTENSIONS
2230 #define GROUP_ADD_OPT_EXTENSIONS "r:v"
2231 #else
2232 #define GROUP_ADD_OPT_EXTENSIONS
2233 #endif
2235 /* add a group */
2237 groupadd(int argc, char **argv)
2239 def_t def;
2240 group_t *gp = &def.group;
2241 int dupgid;
2242 int gid;
2243 int c;
2245 gid = -1;
2246 dupgid = 0;
2247 read_defaults(&def);
2248 while ((c = getopt(argc, argv, "g:o" GROUP_ADD_OPT_EXTENSIONS)) != -1) {
2249 switch(c) {
2250 case 'g':
2251 gid = check_numeric(optarg, "gid");
2252 break;
2253 case 'o':
2254 dupgid = 1;
2255 break;
2256 #ifdef EXTENSIONS
2257 case 'r':
2258 (void)save_range(&gp->g_r, optarg);
2259 break;
2260 case 'v':
2261 verbose = 1;
2262 break;
2263 #endif
2264 default:
2265 usermgmt_usage("groupadd");
2266 /* NOTREACHED */
2269 argc -= optind;
2270 argv += optind;
2271 if (argc != 1) {
2272 usermgmt_usage("groupadd");
2274 if (gp->g_rc == 0) {
2275 gp->g_rv[gp->g_rc].r_from = DEF_LOWUID;
2276 gp->g_rv[gp->g_rc].r_to = DEF_HIGHUID;
2277 gp->g_rc += 1;
2279 gp->g_defrc = gp->g_rc;
2280 checkeuid();
2281 if (gid == -1) {
2282 int got_id = 0;
2283 int i;
2286 * Look for a free GID in the command line ranges (if any).
2287 * These start after the ranges specified in the config file.
2289 for (i = gp->g_defrc; !got_id && i < gp->g_rc ; i++) {
2290 got_id = getnextgid(&gid,
2291 gp->g_rv[i].r_from, gp->g_rv[i].r_to);
2294 * If there were no free GIDs in the command line ranges,
2295 * try the ranges from the config file (there will always
2296 * be at least one default).
2298 for (i = 0; !got_id && i < gp->g_defrc; i++) {
2299 got_id = getnextgid(&gid,
2300 gp->g_rv[i].r_from, gp->g_rv[i].r_to);
2302 if (!got_id)
2303 errx(EXIT_FAILURE, "Can't add group: can't get next gid");
2305 if (!dupgid && getgrgid((gid_t) gid) != NULL) {
2306 errx(EXIT_FAILURE, "Can't add group: gid %d is a duplicate",
2307 gid);
2309 if (!valid_group(*argv)) {
2310 warnx("Invalid group name `%s'", *argv);
2312 openlog("groupadd", LOG_PID, LOG_USER);
2313 if (!creategid(*argv, gid, ""))
2314 exit(EXIT_FAILURE);
2316 return EXIT_SUCCESS;
2319 #ifdef EXTENSIONS
2320 #define GROUP_DEL_OPT_EXTENSIONS "v"
2321 #else
2322 #define GROUP_DEL_OPT_EXTENSIONS
2323 #endif
2325 /* remove a group */
2327 groupdel(int argc, char **argv)
2329 int c;
2331 while ((c = getopt(argc, argv, "" GROUP_DEL_OPT_EXTENSIONS)) != -1) {
2332 switch(c) {
2333 #ifdef EXTENSIONS
2334 case 'v':
2335 verbose = 1;
2336 break;
2337 #endif
2338 default:
2339 usermgmt_usage("groupdel");
2340 /* NOTREACHED */
2343 argc -= optind;
2344 argv += optind;
2345 if (argc != 1) {
2346 usermgmt_usage("groupdel");
2348 if (getgrnam(*argv) == NULL) {
2349 errx(EXIT_FAILURE, "No such group `%s'", *argv);
2351 checkeuid();
2352 openlog("groupdel", LOG_PID, LOG_USER);
2353 if (!modify_gid(*argv, NULL))
2354 exit(EXIT_FAILURE);
2356 return EXIT_SUCCESS;
2359 #ifdef EXTENSIONS
2360 #define GROUP_MOD_OPT_EXTENSIONS "v"
2361 #else
2362 #define GROUP_MOD_OPT_EXTENSIONS
2363 #endif
2365 /* modify a group */
2367 groupmod(int argc, char **argv)
2369 struct group *grp;
2370 char buf[MaxEntryLen];
2371 char *newname;
2372 char **cpp;
2373 int dupgid;
2374 int gid;
2375 int cc;
2376 int c;
2378 gid = -1;
2379 dupgid = 0;
2380 newname = NULL;
2381 while ((c = getopt(argc, argv, "g:on:" GROUP_MOD_OPT_EXTENSIONS)) != -1) {
2382 switch(c) {
2383 case 'g':
2384 gid = check_numeric(optarg, "gid");
2385 break;
2386 case 'o':
2387 dupgid = 1;
2388 break;
2389 case 'n':
2390 memsave(&newname, optarg, strlen(optarg));
2391 break;
2392 #ifdef EXTENSIONS
2393 case 'v':
2394 verbose = 1;
2395 break;
2396 #endif
2397 default:
2398 usermgmt_usage("groupmod");
2399 /* NOTREACHED */
2402 argc -= optind;
2403 argv += optind;
2404 if (argc != 1) {
2405 usermgmt_usage("groupmod");
2407 checkeuid();
2408 if (gid < 0 && newname == NULL) {
2409 errx(EXIT_FAILURE, "Nothing to change");
2411 if (dupgid && gid < 0) {
2412 errx(EXIT_FAILURE, "Duplicate which gid?");
2414 if (!dupgid && getgrgid((gid_t) gid) != NULL) {
2415 errx(EXIT_FAILURE, "Can't modify group: gid %d is a duplicate",
2416 gid);
2418 if ((grp = find_group_info(*argv)) == NULL) {
2419 errx(EXIT_FAILURE, "Can't find group `%s' to modify", *argv);
2421 if (!is_local(*argv, _PATH_GROUP)) {
2422 errx(EXIT_FAILURE, "Group `%s' must be a local group", *argv);
2424 if (newname != NULL && !valid_group(newname)) {
2425 warnx("Invalid group name `%s'", newname);
2427 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:",
2428 (newname) ? newname : grp->gr_name,
2429 grp->gr_passwd,
2430 (gid < 0) ? grp->gr_gid : gid);
2431 for (cpp = grp->gr_mem ; *cpp && cc < sizeof(buf) ; cpp++) {
2432 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s%s", *cpp,
2433 (cpp[1] == NULL) ? "" : ",");
2435 cc += snprintf(&buf[cc], sizeof(buf) - cc, "\n");
2436 if (newname != NULL)
2437 free(newname);
2438 openlog("groupmod", LOG_PID, LOG_USER);
2439 if (!modify_gid(*argv, buf))
2440 exit(EXIT_FAILURE);
2442 return EXIT_SUCCESS;
2445 #ifdef EXTENSIONS
2446 /* display user information */
2448 userinfo(int argc, char **argv)
2450 struct passwd *pwp;
2451 struct group *grp;
2452 char buf[MaxEntryLen];
2453 char **cpp;
2454 int exists;
2455 int cc;
2456 int i;
2458 exists = 0;
2459 buf[0] = '\0';
2460 while ((i = getopt(argc, argv, "e")) != -1) {
2461 switch(i) {
2462 case 'e':
2463 exists = 1;
2464 break;
2465 default:
2466 usermgmt_usage("userinfo");
2467 /* NOTREACHED */
2470 argc -= optind;
2471 argv += optind;
2472 if (argc != 1) {
2473 usermgmt_usage("userinfo");
2475 pwp = find_user_info(*argv);
2476 if (exists) {
2477 exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE);
2479 if (pwp == NULL) {
2480 errx(EXIT_FAILURE, "Can't find user `%s'", *argv);
2482 (void)printf("login\t%s\n", pwp->pw_name);
2483 (void)printf("passwd\t%s\n", pwp->pw_passwd);
2484 (void)printf("uid\t%d\n", pwp->pw_uid);
2485 for (cc = 0 ; (grp = getgrent()) != NULL ; ) {
2486 for (cpp = grp->gr_mem ; *cpp ; cpp++) {
2487 if (strcmp(*cpp, *argv) == 0 &&
2488 grp->gr_gid != pwp->pw_gid) {
2489 cc += snprintf(&buf[cc], sizeof(buf) - cc,
2490 "%s ", grp->gr_name);
2494 if ((grp = getgrgid(pwp->pw_gid)) == NULL) {
2495 (void)printf("groups\t%d %s\n", pwp->pw_gid, buf);
2496 } else {
2497 (void)printf("groups\t%s %s\n", grp->gr_name, buf);
2499 (void)printf("change\t%s", pwp->pw_change > 0 ?
2500 ctime(&pwp->pw_change) : pwp->pw_change == -1 ?
2501 "NEXT LOGIN\n" : "NEVER\n");
2502 (void)printf("class\t%s\n", pwp->pw_class);
2503 (void)printf("gecos\t%s\n", pwp->pw_gecos);
2504 (void)printf("dir\t%s\n", pwp->pw_dir);
2505 (void)printf("shell\t%s\n", pwp->pw_shell);
2506 (void)printf("expire\t%s", pwp->pw_expire ?
2507 ctime(&pwp->pw_expire) : "NEVER\n");
2508 return EXIT_SUCCESS;
2510 #endif
2512 #ifdef EXTENSIONS
2513 /* display user information */
2515 groupinfo(int argc, char **argv)
2517 struct group *grp;
2518 char **cpp;
2519 int exists;
2520 int i;
2522 exists = 0;
2523 while ((i = getopt(argc, argv, "ev")) != -1) {
2524 switch(i) {
2525 case 'e':
2526 exists = 1;
2527 break;
2528 case 'v':
2529 verbose = 1;
2530 break;
2531 default:
2532 usermgmt_usage("groupinfo");
2533 /* NOTREACHED */
2536 argc -= optind;
2537 argv += optind;
2538 if (argc != 1) {
2539 usermgmt_usage("groupinfo");
2541 grp = find_group_info(*argv);
2542 if (exists) {
2543 exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE);
2545 if (grp == NULL) {
2546 errx(EXIT_FAILURE, "Can't find group `%s'", *argv);
2548 (void)printf("name\t%s\n", grp->gr_name);
2549 (void)printf("passwd\t%s\n", grp->gr_passwd);
2550 (void)printf("gid\t%d\n", grp->gr_gid);
2551 (void)printf("members\t");
2552 for (cpp = grp->gr_mem ; *cpp ; cpp++) {
2553 (void)printf("%s", *cpp);
2554 if (*(cpp + 1)) {
2555 (void) printf(", ");
2558 (void)fputc('\n', stdout);
2559 return EXIT_SUCCESS;
2561 #endif