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