Bug Summary

File:src/usr.sbin/user/user.c
Warning:line 2139, column 54
Potential leak of memory pointed to by 'u.u_basedir'

Annotated Source Code

Press '?' to see keyboard shortcuts

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