Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

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