184d9c625SLionel Sambuc /* $NetBSD: user.c,v 1.131 2012/11/28 11:31:27 blymn Exp $ */
25c007436SBen Gras
35c007436SBen Gras /*
45c007436SBen Gras * Copyright (c) 1999 Alistair G. Crooks. All rights reserved.
55c007436SBen Gras * Copyright (c) 2005 Liam J. Foy. All rights reserved.
65c007436SBen Gras *
75c007436SBen Gras * Redistribution and use in source and binary forms, with or without
85c007436SBen Gras * modification, are permitted provided that the following conditions
95c007436SBen Gras * are met:
105c007436SBen Gras * 1. Redistributions of source code must retain the above copyright
115c007436SBen Gras * notice, this list of conditions and the following disclaimer.
125c007436SBen Gras * 2. Redistributions in binary form must reproduce the above copyright
135c007436SBen Gras * notice, this list of conditions and the following disclaimer in the
145c007436SBen Gras * documentation and/or other materials provided with the distribution.
155c007436SBen Gras * 3. The name of the author may not be used to endorse or promote
165c007436SBen Gras * products derived from this software without specific prior written
175c007436SBen Gras * permission.
185c007436SBen Gras *
195c007436SBen Gras * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
205c007436SBen Gras * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
215c007436SBen Gras * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
225c007436SBen Gras * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
235c007436SBen Gras * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
245c007436SBen Gras * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
255c007436SBen Gras * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
265c007436SBen Gras * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
275c007436SBen Gras * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
285c007436SBen Gras * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
295c007436SBen Gras * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
305c007436SBen Gras */
315c007436SBen Gras #include <sys/cdefs.h>
325c007436SBen Gras
335c007436SBen Gras #ifndef lint
345c007436SBen Gras __COPYRIGHT("@(#) Copyright (c) 1999\
355c007436SBen Gras The NetBSD Foundation, Inc. All rights reserved.");
3684d9c625SLionel Sambuc __RCSID("$NetBSD: user.c,v 1.131 2012/11/28 11:31:27 blymn Exp $");
375c007436SBen Gras #endif
385c007436SBen Gras
395c007436SBen Gras #include <sys/types.h>
405c007436SBen Gras #include <sys/param.h>
415c007436SBen Gras #include <sys/stat.h>
423eaea2dcSLionel Sambuc #include <sys/wait.h>
435c007436SBen Gras
445c007436SBen Gras #include <ctype.h>
455c007436SBen Gras #include <dirent.h>
465c007436SBen Gras #include <err.h>
475c007436SBen Gras #include <fcntl.h>
485c007436SBen Gras #include <grp.h>
495c007436SBen Gras #ifdef EXTENSIONS
505c007436SBen Gras #include <login_cap.h>
515c007436SBen Gras #endif
525c007436SBen Gras #include <pwd.h>
535c007436SBen Gras #include <regex.h>
545c007436SBen Gras #include <stdarg.h>
555c007436SBen Gras #include <stdio.h>
565c007436SBen Gras #include <stdlib.h>
575c007436SBen Gras #include <string.h>
585c007436SBen Gras #include <syslog.h>
595c007436SBen Gras #include <time.h>
605c007436SBen Gras #include <unistd.h>
615c007436SBen Gras #include <util.h>
625c007436SBen Gras #include <errno.h>
635c007436SBen Gras
643eaea2dcSLionel Sambuc #include "pathnames.h"
655c007436SBen Gras #include "defs.h"
665c007436SBen Gras #include "usermgmt.h"
675c007436SBen Gras
685c007436SBen Gras
695c007436SBen Gras /* this struct describes a uid range */
705c007436SBen Gras typedef struct range_t {
715c007436SBen Gras int r_from; /* low uid */
725c007436SBen Gras int r_to; /* high uid */
735c007436SBen Gras } range_t;
745c007436SBen Gras
755c007436SBen Gras typedef struct rangelist_t {
765c007436SBen Gras unsigned rl_rsize; /* size of range array */
775c007436SBen Gras unsigned rl_rc; /* # of ranges */
785c007436SBen Gras range_t *rl_rv; /* the ranges */
795c007436SBen Gras unsigned rl_defrc; /* # of ranges in defaults */
805c007436SBen Gras } rangelist_t;
815c007436SBen Gras
825c007436SBen Gras /* this struct encapsulates the user and group information */
835c007436SBen Gras typedef struct user_t {
845c007436SBen Gras int u_flags; /* see below */
855c007436SBen Gras int u_uid; /* uid of user */
865c007436SBen Gras char *u_password; /* encrypted password */
875c007436SBen Gras char *u_comment; /* comment field */
885c007436SBen Gras char *u_home; /* home directory */
895c007436SBen Gras mode_t u_homeperm; /* permissions of home dir */
905c007436SBen Gras char *u_primgrp; /* primary group */
915c007436SBen Gras int u_groupc; /* # of secondary groups */
925c007436SBen Gras const char *u_groupv[NGROUPS_MAX]; /* secondary groups */
935c007436SBen Gras char *u_shell; /* user's shell */
945c007436SBen Gras char *u_basedir; /* base directory for home */
955c007436SBen Gras char *u_expire; /* when password will expire */
965c007436SBen Gras char *u_inactive; /* when account will expire */
975c007436SBen Gras char *u_skeldir; /* directory for startup files */
985c007436SBen Gras char *u_class; /* login class */
995c007436SBen Gras rangelist_t u_r; /* list of ranges */
1005c007436SBen Gras unsigned u_defrc; /* # of ranges in defaults */
1015c007436SBen Gras int u_preserve; /* preserve uids on deletion */
1025c007436SBen Gras int u_allow_samba; /* allow trailing '$' for samba login names */
1035c007436SBen Gras int u_locked; /* user account lock */
1045c007436SBen Gras } user_t;
1055c007436SBen Gras #define u_rsize u_r.rl_rsize
1065c007436SBen Gras #define u_rc u_r.rl_rc
1075c007436SBen Gras #define u_rv u_r.rl_rv
1085c007436SBen Gras #define u_defrc u_r.rl_defrc
1095c007436SBen Gras
1105c007436SBen Gras /* this struct encapsulates the user and group information */
1115c007436SBen Gras typedef struct group_t {
1125c007436SBen Gras rangelist_t g_r; /* list of ranges */
1135c007436SBen Gras } group_t;
1145c007436SBen Gras #define g_rsize g_r.rl_rsize
1155c007436SBen Gras #define g_rc g_r.rl_rc
1165c007436SBen Gras #define g_rv g_r.rl_rv
1175c007436SBen Gras #define g_defrc g_r.rl_defrc
1185c007436SBen Gras
1195c007436SBen Gras typedef struct def_t {
1205c007436SBen Gras user_t user;
1215c007436SBen Gras group_t group;
1225c007436SBen Gras } def_t;
1235c007436SBen Gras
1245c007436SBen Gras /* flags for which fields of the user_t replace the passwd entry */
1255c007436SBen Gras enum {
1265c007436SBen Gras F_COMMENT = 0x0001,
1275c007436SBen Gras F_DUPUID = 0x0002,
1285c007436SBen Gras F_EXPIRE = 0x0004,
1295c007436SBen Gras F_GROUP = 0x0008,
1305c007436SBen Gras F_HOMEDIR = 0x0010,
1315c007436SBen Gras F_MKDIR = 0x0020,
1325c007436SBen Gras F_INACTIVE = 0x0040,
1335c007436SBen Gras F_PASSWORD = 0x0080,
1345c007436SBen Gras F_SECGROUP = 0x0100,
1355c007436SBen Gras F_SHELL = 0x0200,
1365c007436SBen Gras F_UID = 0x0400,
1375c007436SBen Gras F_USERNAME = 0x0800,
1385c007436SBen Gras F_CLASS = 0x1000
1395c007436SBen Gras };
1405c007436SBen Gras
1415c007436SBen Gras #define UNLOCK 0
1425c007436SBen Gras #define LOCK 1
1435c007436SBen Gras #define LOCKED "*LOCKED*"
1445c007436SBen Gras
1455c007436SBen Gras #ifndef DEF_GROUP
1465c007436SBen Gras #define DEF_GROUP "users"
1475c007436SBen Gras #endif
1485c007436SBen Gras
1495c007436SBen Gras #ifndef DEF_BASEDIR
1505c007436SBen Gras #define DEF_BASEDIR "/home"
1515c007436SBen Gras #endif
1525c007436SBen Gras
1535c007436SBen Gras #ifndef DEF_SKELDIR
1545c007436SBen Gras #define DEF_SKELDIR "/etc/skel"
1555c007436SBen Gras #endif
1565c007436SBen Gras
1575c007436SBen Gras #ifndef DEF_SHELL
1585c007436SBen Gras #define DEF_SHELL _PATH_BSHELL
1595c007436SBen Gras #endif
1605c007436SBen Gras
1615c007436SBen Gras #ifndef DEF_COMMENT
1625c007436SBen Gras #define DEF_COMMENT ""
1635c007436SBen Gras #endif
1645c007436SBen Gras
1655c007436SBen Gras #ifndef DEF_LOWUID
1665c007436SBen Gras #define DEF_LOWUID 1000
1675c007436SBen Gras #endif
1685c007436SBen Gras
1695c007436SBen Gras #ifndef DEF_HIGHUID
1705c007436SBen Gras #define DEF_HIGHUID 60000
1715c007436SBen Gras #endif
1725c007436SBen Gras
1735c007436SBen Gras #ifndef DEF_INACTIVE
1745c007436SBen Gras #define DEF_INACTIVE 0
1755c007436SBen Gras #endif
1765c007436SBen Gras
1775c007436SBen Gras #ifndef DEF_EXPIRE
1785c007436SBen Gras #define DEF_EXPIRE NULL
1795c007436SBen Gras #endif
1805c007436SBen Gras
1815c007436SBen Gras #ifndef DEF_CLASS
1825c007436SBen Gras #define DEF_CLASS ""
1835c007436SBen Gras #endif
1845c007436SBen Gras
1855c007436SBen Gras #ifndef WAITSECS
1865c007436SBen Gras #define WAITSECS 10
1875c007436SBen Gras #endif
1885c007436SBen Gras
1895c007436SBen Gras #ifndef NOBODY_UID
1905c007436SBen Gras #define NOBODY_UID 32767
1915c007436SBen Gras #endif
1925c007436SBen Gras
1935c007436SBen Gras #ifndef DEF_HOMEPERM
1945c007436SBen Gras #define DEF_HOMEPERM 0755
1955c007436SBen Gras #endif
1965c007436SBen Gras
1975c007436SBen Gras /* some useful constants */
1985c007436SBen Gras enum {
1995c007436SBen Gras MaxShellNameLen = 256,
2005c007436SBen Gras MaxFileNameLen = MAXPATHLEN,
2015c007436SBen Gras MaxUserNameLen = LOGIN_NAME_MAX - 1,
2025c007436SBen Gras MaxCommandLen = 2048,
2035c007436SBen Gras MaxEntryLen = 2048,
2045c007436SBen Gras PasswordLength = 2048,
2055c007436SBen Gras
2065c007436SBen Gras DES_Len = 13,
2075c007436SBen Gras };
2085c007436SBen Gras
2095c007436SBen Gras #define UNSET_INACTIVE "Null (unset)"
2105c007436SBen Gras #define UNSET_EXPIRY "Null (unset)"
2115c007436SBen Gras
2123eaea2dcSLionel Sambuc static int asystem(const char *fmt, ...) __printflike(1, 2);
2135c007436SBen Gras static int is_number(const char *);
2145c007436SBen Gras static struct group *find_group_info(const char *);
2155c007436SBen Gras static int verbose;
2165c007436SBen Gras
2175c007436SBen Gras static char *
skipspace(char * s)2185c007436SBen Gras skipspace(char *s)
2195c007436SBen Gras {
2205c007436SBen Gras for (; *s && isspace((unsigned char)*s) ; s++) {
2215c007436SBen Gras }
2225c007436SBen Gras return s;
2235c007436SBen Gras }
2245c007436SBen Gras
2255c007436SBen Gras static int
check_numeric(const char * val,const char * name)2265c007436SBen Gras check_numeric(const char *val, const char *name)
2275c007436SBen Gras {
2285c007436SBen Gras if (!is_number(val)) {
2295c007436SBen Gras errx(EXIT_FAILURE, "When using [-%c %s], "
2305c007436SBen Gras "the %s must be numeric", *name, name, name);
2315c007436SBen Gras }
2325c007436SBen Gras return atoi(val);
2335c007436SBen Gras }
2345c007436SBen Gras
2355c007436SBen Gras /* resize *cpp appropriately then assign `n' chars of `s' to it */
2365c007436SBen Gras static void
memsave(char ** cpp,const char * s,size_t n)2375c007436SBen Gras memsave(char **cpp, const char *s, size_t n)
2385c007436SBen Gras {
2395c007436SBen Gras RENEW(char, *cpp, n + 1, exit(1));
2405c007436SBen Gras (void)memcpy(*cpp, s, n);
2415c007436SBen Gras (*cpp)[n] = '\0';
2425c007436SBen Gras }
2435c007436SBen Gras
2445c007436SBen Gras /* a replacement for system(3) */
2455c007436SBen Gras static int
asystem(const char * fmt,...)2465c007436SBen Gras asystem(const char *fmt, ...)
2475c007436SBen Gras {
2485c007436SBen Gras va_list vp;
2495c007436SBen Gras char buf[MaxCommandLen];
2505c007436SBen Gras int ret;
2515c007436SBen Gras
2525c007436SBen Gras va_start(vp, fmt);
2535c007436SBen Gras (void)vsnprintf(buf, sizeof(buf), fmt, vp);
2545c007436SBen Gras va_end(vp);
2555c007436SBen Gras if (verbose) {
2565c007436SBen Gras (void)printf("Command: %s\n", buf);
2575c007436SBen Gras }
2583eaea2dcSLionel Sambuc ret = system(buf);
2593eaea2dcSLionel Sambuc if (ret == -1) {
2605c007436SBen Gras warn("Error running `%s'", buf);
2613eaea2dcSLionel Sambuc } else if (WIFSIGNALED(ret)) {
2623eaea2dcSLionel Sambuc warnx("Error running `%s': Signal %d", buf, WTERMSIG(ret));
2633eaea2dcSLionel Sambuc } else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) {
2643eaea2dcSLionel Sambuc warnx("Error running `%s': Exit %d", buf, WEXITSTATUS(ret));
2655c007436SBen Gras }
2665c007436SBen Gras return ret;
2675c007436SBen Gras }
2685c007436SBen Gras
2695c007436SBen Gras /* remove a users home directory, returning 1 for success (ie, no problems encountered) */
2705c007436SBen Gras static int
removehomedir(struct passwd * pwp)2715c007436SBen Gras removehomedir(struct passwd *pwp)
2725c007436SBen Gras {
2735c007436SBen Gras struct stat st;
2745c007436SBen Gras
2755c007436SBen Gras /* userid not root? */
2765c007436SBen Gras if (pwp->pw_uid == 0) {
2775c007436SBen Gras warnx("Not deleting home directory `%s'; userid is 0", pwp->pw_dir);
2785c007436SBen Gras return 0;
2795c007436SBen Gras }
2805c007436SBen Gras
2815c007436SBen Gras /* directory exists (and is a directory!) */
2825c007436SBen Gras if (stat(pwp->pw_dir, &st) < 0) {
2835c007436SBen Gras warn("Cannot access home directory `%s'", pwp->pw_dir);
2845c007436SBen Gras return 0;
2855c007436SBen Gras }
2865c007436SBen Gras if (!S_ISDIR(st.st_mode)) {
2875c007436SBen Gras warnx("Home directory `%s' is not a directory", pwp->pw_dir);
2885c007436SBen Gras return 0;
2895c007436SBen Gras }
2905c007436SBen Gras
2915c007436SBen Gras /* userid matches directory owner? */
2925c007436SBen Gras if (st.st_uid != pwp->pw_uid) {
2935c007436SBen Gras warnx("User `%s' doesn't own directory `%s', not removed",
2945c007436SBen Gras pwp->pw_name, pwp->pw_dir);
2955c007436SBen Gras return 0;
2965c007436SBen Gras }
2975c007436SBen Gras
2985c007436SBen Gras (void)seteuid(pwp->pw_uid);
2995c007436SBen Gras /* we add the "|| true" to keep asystem() quiet if there is a non-zero exit status. */
3003eaea2dcSLionel Sambuc (void)asystem("%s -rf %s > /dev/null 2>&1 || true", _PATH_RM,
3013eaea2dcSLionel Sambuc pwp->pw_dir);
3025c007436SBen Gras (void)seteuid(0);
3035c007436SBen Gras if (rmdir(pwp->pw_dir) < 0) {
3045c007436SBen Gras warn("Unable to remove all files in `%s'", pwp->pw_dir);
3055c007436SBen Gras return 0;
3065c007436SBen Gras }
3075c007436SBen Gras return 1;
3085c007436SBen Gras }
3095c007436SBen Gras
3105c007436SBen Gras /* return 1 if all of `s' is numeric */
3115c007436SBen Gras static int
is_number(const char * s)3125c007436SBen Gras is_number(const char *s)
3135c007436SBen Gras {
3145c007436SBen Gras for ( ; *s ; s++) {
3155c007436SBen Gras if (!isdigit((unsigned char) *s)) {
3165c007436SBen Gras return 0;
3175c007436SBen Gras }
3185c007436SBen Gras }
3195c007436SBen Gras return 1;
3205c007436SBen Gras }
3215c007436SBen Gras
3225c007436SBen Gras /*
3235c007436SBen Gras * check that the effective uid is 0 - called from funcs which will
3245c007436SBen Gras * modify data and config files.
3255c007436SBen Gras */
3265c007436SBen Gras static void
checkeuid(void)3275c007436SBen Gras checkeuid(void)
3285c007436SBen Gras {
3295c007436SBen Gras if (geteuid() != 0) {
3305c007436SBen Gras errx(EXIT_FAILURE, "Program must be run as root");
3315c007436SBen Gras }
3325c007436SBen Gras }
3335c007436SBen Gras
3345c007436SBen Gras /* copy any dot files into the user's home directory */
3355c007436SBen Gras static int
copydotfiles(char * skeldir,int uid,int gid,char * dir,mode_t homeperm)3365c007436SBen Gras copydotfiles(char *skeldir, int uid, int gid, char *dir, mode_t homeperm)
3375c007436SBen Gras {
3385c007436SBen Gras struct dirent *dp;
3395c007436SBen Gras DIR *dirp;
3405c007436SBen Gras int n;
3415c007436SBen Gras
3425c007436SBen Gras if ((dirp = opendir(skeldir)) == NULL) {
3435c007436SBen Gras warn("Can't open source . files dir `%s'", skeldir);
3445c007436SBen Gras return 0;
3455c007436SBen Gras }
3465c007436SBen Gras for (n = 0; (dp = readdir(dirp)) != NULL && n == 0 ; ) {
3475c007436SBen Gras if (strcmp(dp->d_name, ".") == 0 ||
3485c007436SBen Gras strcmp(dp->d_name, "..") == 0) {
3495c007436SBen Gras continue;
3505c007436SBen Gras }
3515c007436SBen Gras n = 1;
3525c007436SBen Gras }
3535c007436SBen Gras (void)closedir(dirp);
3545c007436SBen Gras if (n == 0) {
3555c007436SBen Gras warnx("No \"dot\" initialisation files found");
3565c007436SBen Gras } else {
3575c007436SBen Gras (void)asystem("cd %s && %s -rw -pe %s . %s",
3583eaea2dcSLionel Sambuc skeldir, _PATH_PAX, (verbose) ? "-v" : "", dir);
3595c007436SBen Gras }
3603eaea2dcSLionel Sambuc (void)asystem("%s -R -h %d:%d %s", _PATH_CHOWN, uid, gid, dir);
3613eaea2dcSLionel Sambuc (void)asystem("%s -R u+w %s", _PATH_CHMOD, dir);
3625c007436SBen Gras #ifdef EXTENSIONS
3633eaea2dcSLionel Sambuc (void)asystem("%s 0%o %s", _PATH_CHMOD, homeperm, dir);
3645c007436SBen Gras #endif
3655c007436SBen Gras return n;
3665c007436SBen Gras }
3675c007436SBen Gras
3685c007436SBen Gras /* create a group entry with gid `gid' */
3695c007436SBen Gras static int
creategid(char * group,int gid,const char * name)3705c007436SBen Gras creategid(char *group, int gid, const char *name)
3715c007436SBen Gras {
3725c007436SBen Gras struct stat st;
3735c007436SBen Gras FILE *from;
3745c007436SBen Gras FILE *to;
3755c007436SBen Gras char buf[MaxEntryLen];
3765c007436SBen Gras char f[MaxFileNameLen];
3775c007436SBen Gras int fd;
3785c007436SBen Gras int cc;
3795c007436SBen Gras
3805c007436SBen Gras if (getgrnam(group) != NULL) {
3815c007436SBen Gras warnx("Can't create group `%s': already exists", group);
3825c007436SBen Gras return 0;
3835c007436SBen Gras }
384*0a4059eeSLionel Sambuc #if defined(__minix)
385*0a4059eeSLionel Sambuc /* LSC: Minix flock implementation is a wrapper around fctl, which
386*0a4059eeSLionel Sambuc * requires writeable fds for LOCK_EX to succeed. */
387*0a4059eeSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
388*0a4059eeSLionel Sambuc #else
3893eaea2dcSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
390*0a4059eeSLionel Sambuc #endif /* defined(__minix) */
3915c007436SBen Gras warn("Can't create group `%s': can't open `%s'", name,
3925c007436SBen Gras _PATH_GROUP);
3935c007436SBen Gras return 0;
3945c007436SBen Gras }
3955c007436SBen Gras if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
3965c007436SBen Gras warn("Can't lock `%s'", _PATH_GROUP);
3975c007436SBen Gras (void)fclose(from);
3985c007436SBen Gras return 0;
3995c007436SBen Gras }
4005c007436SBen Gras (void)fstat(fileno(from), &st);
4015c007436SBen Gras (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
4025c007436SBen Gras if ((fd = mkstemp(f)) < 0) {
4035c007436SBen Gras warn("Can't create group `%s': mkstemp failed", group);
4045c007436SBen Gras (void)fclose(from);
4055c007436SBen Gras return 0;
4065c007436SBen Gras }
4075c007436SBen Gras if ((to = fdopen(fd, "w")) == NULL) {
4085c007436SBen Gras warn("Can't create group `%s': fdopen `%s' failed",
4095c007436SBen Gras group, f);
4105c007436SBen Gras (void)fclose(from);
4115c007436SBen Gras (void)close(fd);
4125c007436SBen Gras (void)unlink(f);
4135c007436SBen Gras return 0;
4145c007436SBen Gras }
4155c007436SBen Gras while ((cc = fread(buf, sizeof(char), sizeof(buf), from)) > 0) {
4165c007436SBen Gras if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
4175c007436SBen Gras warn("Can't create group `%s': short write to `%s'",
4185c007436SBen Gras group, f);
4195c007436SBen Gras (void)fclose(from);
4205c007436SBen Gras (void)close(fd);
4215c007436SBen Gras (void)unlink(f);
4225c007436SBen Gras return 0;
4235c007436SBen Gras }
4245c007436SBen Gras }
4255c007436SBen Gras (void)fprintf(to, "%s:*:%d:%s\n", group, gid, name);
4265c007436SBen Gras (void)fclose(from);
4275c007436SBen Gras (void)fclose(to);
4285c007436SBen Gras if (rename(f, _PATH_GROUP) < 0) {
4295c007436SBen Gras warn("Can't create group `%s': can't rename `%s' to `%s'",
4305c007436SBen Gras group, f, _PATH_GROUP);
4315c007436SBen Gras (void)unlink(f);
4325c007436SBen Gras return 0;
4335c007436SBen Gras }
4345c007436SBen Gras (void)chmod(_PATH_GROUP, st.st_mode & 07777);
4355c007436SBen Gras syslog(LOG_INFO, "New group added: name=%s, gid=%d", group, gid);
4365c007436SBen Gras return 1;
4375c007436SBen Gras }
4385c007436SBen Gras
4395c007436SBen Gras /* modify the group entry with name `group' to be newent */
4405c007436SBen Gras static int
4415c007436SBen Gras modify_gid(char *group, char *newent)
4425c007436SBen Gras {
4435c007436SBen Gras struct stat st;
4445c007436SBen Gras FILE *from;
4455c007436SBen Gras FILE *to;
4465c007436SBen Gras char buf[MaxEntryLen];
4475c007436SBen Gras char f[MaxFileNameLen];
4485c007436SBen Gras char *colon;
4495c007436SBen Gras int groupc;
4505c007436SBen Gras int entc;
4515c007436SBen Gras int fd;
4525c007436SBen Gras int cc;
4535c007436SBen Gras
454*0a4059eeSLionel Sambuc #if defined(__minix)
455*0a4059eeSLionel Sambuc /* LSC: Minix flock implementation is a wrapper around fctl, which
456*0a4059eeSLionel Sambuc * requires writeable fds for LOCK_EX to succeed. */
457*0a4059eeSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
458*0a4059eeSLionel Sambuc #else
4593eaea2dcSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
460*0a4059eeSLionel Sambuc #endif /* defined(__minix) */
4615c007436SBen Gras warn("Can't modify group `%s': can't open `%s'",
4625c007436SBen Gras group, _PATH_GROUP);
4635c007436SBen Gras return 0;
4645c007436SBen Gras }
4655c007436SBen Gras if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
4665c007436SBen Gras warn("Can't modify group `%s': can't lock `%s'",
4675c007436SBen Gras group, _PATH_GROUP);
4685c007436SBen Gras (void)fclose(from);
4695c007436SBen Gras return 0;
4705c007436SBen Gras }
4715c007436SBen Gras (void)fstat(fileno(from), &st);
4725c007436SBen Gras (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
4735c007436SBen Gras if ((fd = mkstemp(f)) < 0) {
4745c007436SBen Gras warn("Can't modify group `%s': mkstemp failed", group);
4755c007436SBen Gras (void)fclose(from);
4765c007436SBen Gras return 0;
4775c007436SBen Gras }
4785c007436SBen Gras if ((to = fdopen(fd, "w")) == NULL) {
4795c007436SBen Gras warn("Can't modify group `%s': fdopen `%s' failed", group, f);
4805c007436SBen Gras (void)fclose(from);
4815c007436SBen Gras (void)close(fd);
4825c007436SBen Gras (void)unlink(f);
4835c007436SBen Gras return 0;
4845c007436SBen Gras }
4855c007436SBen Gras groupc = strlen(group);
4865c007436SBen Gras while (fgets(buf, sizeof(buf), from) != NULL) {
4875c007436SBen Gras cc = strlen(buf);
4885c007436SBen Gras if ((colon = strchr(buf, ':')) == NULL) {
4895c007436SBen Gras warnx("Badly formed entry `%s'", buf);
4905c007436SBen Gras continue;
4915c007436SBen Gras }
4925c007436SBen Gras entc = (int)(colon - buf);
4935c007436SBen Gras if (entc == groupc &&
4945c007436SBen Gras strncmp(group, buf, (unsigned) entc) == 0) {
4955c007436SBen Gras if (newent == NULL) {
4965c007436SBen Gras struct group *grp_rm;
4975c007436SBen Gras struct passwd *user_pwd;
4985c007436SBen Gras
4995c007436SBen Gras /*
5005c007436SBen Gras * Check that the group being removed
5015c007436SBen Gras * isn't any user's Primary group. Just
5025c007436SBen Gras * warn if it is. This could cause problems
5035c007436SBen Gras * if the group GID was reused for a
5045c007436SBen Gras * different purpose.
5055c007436SBen Gras */
5065c007436SBen Gras
5075c007436SBen Gras grp_rm = find_group_info(group);
5085c007436SBen Gras while ((user_pwd = getpwent()) != NULL) {
5095c007436SBen Gras if (user_pwd->pw_gid == grp_rm->gr_gid) {
5105c007436SBen Gras warnx("Warning: group `%s'(%d)"
5115c007436SBen Gras " is the primary group of"
5125c007436SBen Gras " `%s'. Use caution if you"
5135c007436SBen Gras " later add this GID.",
5145c007436SBen Gras grp_rm->gr_name,
5155c007436SBen Gras grp_rm->gr_gid, user_pwd->pw_name);
5165c007436SBen Gras }
5175c007436SBen Gras }
5185c007436SBen Gras endpwent();
5195c007436SBen Gras continue;
5205c007436SBen Gras } else {
5215c007436SBen Gras cc = strlen(newent);
5225c007436SBen Gras (void)strlcpy(buf, newent, sizeof(buf));
5235c007436SBen Gras }
5245c007436SBen Gras }
5255c007436SBen Gras if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
5265c007436SBen Gras warn("Can't modify group `%s': short write to `%s'",
5275c007436SBen Gras group, f);
5285c007436SBen Gras (void)fclose(from);
5295c007436SBen Gras (void)close(fd);
5305c007436SBen Gras (void)unlink(f);
5315c007436SBen Gras return 0;
5325c007436SBen Gras }
5335c007436SBen Gras }
5345c007436SBen Gras (void)fclose(from);
5355c007436SBen Gras (void)fclose(to);
5365c007436SBen Gras if (rename(f, _PATH_GROUP) < 0) {
5375c007436SBen Gras warn("Can't modify group `%s': can't rename `%s' to `%s'",
5385c007436SBen Gras group, f, _PATH_GROUP);
5395c007436SBen Gras (void)unlink(f);
5405c007436SBen Gras return 0;
5415c007436SBen Gras }
5425c007436SBen Gras (void)chmod(_PATH_GROUP, st.st_mode & 07777);
5435c007436SBen Gras if (newent == NULL) {
5445c007436SBen Gras syslog(LOG_INFO, "group deleted: name=%s", group);
5455c007436SBen Gras } else {
5465c007436SBen Gras syslog(LOG_INFO, "group information modified: name=%s", group);
5475c007436SBen Gras }
5485c007436SBen Gras return 1;
5495c007436SBen Gras }
5505c007436SBen Gras
5515c007436SBen Gras /* modify the group entries for all `groups', by adding `user' */
5525c007436SBen Gras static int
5535c007436SBen Gras append_group(char *user, int ngroups, const char **groups)
5545c007436SBen Gras {
5555c007436SBen Gras struct group *grp;
5565c007436SBen Gras struct stat st;
5575c007436SBen Gras FILE *from;
5585c007436SBen Gras FILE *to;
5595c007436SBen Gras char buf[MaxEntryLen];
5605c007436SBen Gras char f[MaxFileNameLen];
5615c007436SBen Gras char *colon;
5625c007436SBen Gras int groupc;
5635c007436SBen Gras int entc;
5645c007436SBen Gras int fd;
5655c007436SBen Gras int nc;
5665c007436SBen Gras int cc;
5675c007436SBen Gras int i;
5685c007436SBen Gras int j;
5695c007436SBen Gras
5705c007436SBen Gras for (i = 0 ; i < ngroups ; i++) {
5715c007436SBen Gras if ((grp = getgrnam(groups[i])) == NULL) {
5725c007436SBen Gras warnx("Can't append group `%s' for user `%s'",
5735c007436SBen Gras groups[i], user);
5745c007436SBen Gras } else {
5755c007436SBen Gras for (j = 0 ; grp->gr_mem[j] ; j++) {
5765c007436SBen Gras if (strcmp(user, grp->gr_mem[j]) == 0) {
5775c007436SBen Gras /* already in it */
5785c007436SBen Gras groups[i] = "";
5795c007436SBen Gras }
5805c007436SBen Gras }
5815c007436SBen Gras }
5825c007436SBen Gras }
583*0a4059eeSLionel Sambuc #if defined(__minix)
584*0a4059eeSLionel Sambuc /* LSC: Minix flock implementation is a wrapper around fctl, which
585*0a4059eeSLionel Sambuc * requires writeable fds for LOCK_EX to succeed. */
586*0a4059eeSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
587*0a4059eeSLionel Sambuc #else
5883eaea2dcSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
589*0a4059eeSLionel Sambuc #endif /* defined(__minix) */
5905c007436SBen Gras warn("Can't append group(s) for `%s': can't open `%s'",
5915c007436SBen Gras user, _PATH_GROUP);
5925c007436SBen Gras return 0;
5935c007436SBen Gras }
5945c007436SBen Gras if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
5955c007436SBen Gras warn("Can't append group(s) for `%s': can't lock `%s'",
5965c007436SBen Gras user, _PATH_GROUP);
5975c007436SBen Gras (void)fclose(from);
5985c007436SBen Gras return 0;
5995c007436SBen Gras }
6005c007436SBen Gras (void)fstat(fileno(from), &st);
6015c007436SBen Gras (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
6025c007436SBen Gras if ((fd = mkstemp(f)) < 0) {
6035c007436SBen Gras warn("Can't append group(s) for `%s': mkstemp failed",
6045c007436SBen Gras user);
6055c007436SBen Gras (void)fclose(from);
6065c007436SBen Gras return 0;
6075c007436SBen Gras }
6085c007436SBen Gras if ((to = fdopen(fd, "w")) == NULL) {
6095c007436SBen Gras warn("Can't append group(s) for `%s': fdopen `%s' failed",
6105c007436SBen Gras user, f);
6115c007436SBen Gras (void)fclose(from);
6125c007436SBen Gras (void)close(fd);
6135c007436SBen Gras (void)unlink(f);
6145c007436SBen Gras return 0;
6155c007436SBen Gras }
6165c007436SBen Gras while (fgets(buf, sizeof(buf), from) != NULL) {
6175c007436SBen Gras cc = strlen(buf);
6185c007436SBen Gras if ((colon = strchr(buf, ':')) == NULL) {
6195c007436SBen Gras warnx("Badly formed entry `%s'", buf);
6205c007436SBen Gras continue;
6215c007436SBen Gras }
6225c007436SBen Gras entc = (int)(colon - buf);
6235c007436SBen Gras for (i = 0 ; i < ngroups ; i++) {
6245c007436SBen Gras if ((groupc = strlen(groups[i])) == 0) {
6255c007436SBen Gras continue;
6265c007436SBen Gras }
6275c007436SBen Gras if (entc == groupc &&
6285c007436SBen Gras strncmp(groups[i], buf, (unsigned) entc) == 0) {
6295c007436SBen Gras if ((nc = snprintf(&buf[cc - 1],
6305c007436SBen Gras sizeof(buf) - cc + 1, "%s%s\n",
6315c007436SBen Gras (buf[cc - 2] == ':') ? "" : ",", user)) < 0) {
6325c007436SBen Gras warnx("Warning: group `%s' "
6335c007436SBen Gras "entry too long", groups[i]);
6345c007436SBen Gras }
6355c007436SBen Gras cc += nc - 1;
6365c007436SBen Gras }
6375c007436SBen Gras }
6385c007436SBen Gras if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
6395c007436SBen Gras warn("Can't append group(s) for `%s':"
6405c007436SBen Gras " short write to `%s'", user, f);
6415c007436SBen Gras (void)fclose(from);
6425c007436SBen Gras (void)close(fd);
6435c007436SBen Gras (void)unlink(f);
6445c007436SBen Gras return 0;
6455c007436SBen Gras }
6465c007436SBen Gras }
6475c007436SBen Gras (void)fclose(from);
6485c007436SBen Gras (void)fclose(to);
6495c007436SBen Gras if (rename(f, _PATH_GROUP) < 0) {
6505c007436SBen Gras warn("Can't append group(s) for `%s': "
6515c007436SBen Gras "can't rename `%s' to `%s'", user, f, _PATH_GROUP);
6525c007436SBen Gras (void)unlink(f);
6535c007436SBen Gras return 0;
6545c007436SBen Gras }
6555c007436SBen Gras (void)chmod(_PATH_GROUP, st.st_mode & 07777);
6565c007436SBen Gras return 1;
6575c007436SBen Gras }
6585c007436SBen Gras
6595c007436SBen Gras /* the valid characters for login and group names */
6605c007436SBen Gras #define VALID_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '_' || (c) == '-')
6615c007436SBen Gras
6625c007436SBen Gras /* return 1 if `login' is a valid login name */
6635c007436SBen Gras static int
6645c007436SBen Gras valid_login(char *login_name, int allow_samba)
6655c007436SBen Gras {
6665c007436SBen Gras unsigned char *cp;
6675c007436SBen Gras
6685c007436SBen Gras /* First character of a login name cannot be '-'. */
6695c007436SBen Gras if (*login_name == '-') {
6705c007436SBen Gras return 0;
6715c007436SBen Gras }
6725c007436SBen Gras if (strlen(login_name) >= LOGIN_NAME_MAX) {
6735c007436SBen Gras return 0;
6745c007436SBen Gras }
6755c007436SBen Gras for (cp = (unsigned char *)login_name ; *cp ; cp++) {
6765c007436SBen Gras if (!VALID_CHAR(*cp)) {
6775c007436SBen Gras #ifdef EXTENSIONS
6785c007436SBen Gras /* check for a trailing '$' in a Samba user name */
6795c007436SBen Gras if (allow_samba && *cp == '$' && *(cp + 1) == 0x0) {
6805c007436SBen Gras return 1;
6815c007436SBen Gras }
6825c007436SBen Gras #endif
6835c007436SBen Gras return 0;
6845c007436SBen Gras }
6855c007436SBen Gras }
6865c007436SBen Gras return 1;
6875c007436SBen Gras }
6885c007436SBen Gras
6895c007436SBen Gras /* return 1 if `group' is a valid group name */
6905c007436SBen Gras static int
6915c007436SBen Gras valid_group(char *group)
6925c007436SBen Gras {
6935c007436SBen Gras unsigned char *cp;
6945c007436SBen Gras
6955c007436SBen Gras for (cp = (unsigned char *)group; *cp; cp++) {
6965c007436SBen Gras if (!VALID_CHAR(*cp)) {
6975c007436SBen Gras return 0;
6985c007436SBen Gras }
6995c007436SBen Gras }
7005c007436SBen Gras return 1;
7015c007436SBen Gras }
7025c007436SBen Gras
7035c007436SBen Gras /* find the next gid in the range lo .. hi */
7045c007436SBen Gras static int
7055c007436SBen Gras getnextgid(int *gidp, int lo, int hi)
7065c007436SBen Gras {
7075c007436SBen Gras for (*gidp = lo ; *gidp < hi ; *gidp += 1) {
7085c007436SBen Gras if (getgrgid((gid_t)*gidp) == NULL) {
7095c007436SBen Gras return 1;
7105c007436SBen Gras }
7115c007436SBen Gras }
7125c007436SBen Gras return 0;
7135c007436SBen Gras }
7145c007436SBen Gras
7155c007436SBen Gras #ifdef EXTENSIONS
7165c007436SBen Gras /* save a range of uids */
7175c007436SBen Gras static int
7185c007436SBen Gras save_range(rangelist_t *rlp, char *cp)
7195c007436SBen Gras {
7205c007436SBen Gras int from;
7215c007436SBen Gras int to;
7225c007436SBen Gras int i;
7235c007436SBen Gras
7245c007436SBen Gras if (rlp->rl_rsize == 0) {
7255c007436SBen Gras rlp->rl_rsize = 32;
7265c007436SBen Gras NEWARRAY(range_t, rlp->rl_rv, rlp->rl_rsize, return(0));
7275c007436SBen Gras } else if (rlp->rl_rc == rlp->rl_rsize) {
7285c007436SBen Gras rlp->rl_rsize *= 2;
7295c007436SBen Gras RENEW(range_t, rlp->rl_rv, rlp->rl_rsize, return(0));
7305c007436SBen Gras }
7315c007436SBen Gras if (rlp->rl_rv && sscanf(cp, "%d..%d", &from, &to) == 2) {
7325c007436SBen Gras for (i = rlp->rl_defrc ; i < rlp->rl_rc ; i++) {
7335c007436SBen Gras if (rlp->rl_rv[i].r_from == from &&
7345c007436SBen Gras rlp->rl_rv[i].r_to == to) {
7355c007436SBen Gras break;
7365c007436SBen Gras }
7375c007436SBen Gras }
7385c007436SBen Gras if (i == rlp->rl_rc) {
7395c007436SBen Gras rlp->rl_rv[rlp->rl_rc].r_from = from;
7405c007436SBen Gras rlp->rl_rv[rlp->rl_rc].r_to = to;
7415c007436SBen Gras rlp->rl_rc += 1;
7425c007436SBen Gras }
7435c007436SBen Gras } else {
7445c007436SBen Gras warnx("Bad range `%s'", cp);
7455c007436SBen Gras return 0;
7465c007436SBen Gras }
7475c007436SBen Gras return 1;
7485c007436SBen Gras }
7495c007436SBen Gras #endif
7505c007436SBen Gras
7515c007436SBen Gras /* set the defaults in the defaults file */
7525c007436SBen Gras static int
7535c007436SBen Gras setdefaults(user_t *up)
7545c007436SBen Gras {
7555c007436SBen Gras char template[MaxFileNameLen];
7565c007436SBen Gras FILE *fp;
7575c007436SBen Gras int ret;
7585c007436SBen Gras int fd;
7595c007436SBen Gras #ifdef EXTENSIONS
7605c007436SBen Gras int i;
7615c007436SBen Gras #endif
7625c007436SBen Gras
7635c007436SBen Gras (void)snprintf(template, sizeof(template), "%s.XXXXXX",
7645c007436SBen Gras _PATH_USERMGMT_CONF);
7655c007436SBen Gras if ((fd = mkstemp(template)) < 0) {
7665c007436SBen Gras warn("Can't set defaults: can't mkstemp `%s' for writing",
7675c007436SBen Gras _PATH_USERMGMT_CONF);
7685c007436SBen Gras return 0;
7695c007436SBen Gras }
7705c007436SBen Gras if ((fp = fdopen(fd, "w")) == NULL) {
7715c007436SBen Gras warn("Can't set defaults: can't fdopen `%s' for writing",
7725c007436SBen Gras _PATH_USERMGMT_CONF);
7735c007436SBen Gras return 0;
7745c007436SBen Gras }
7755c007436SBen Gras ret = 1;
7765c007436SBen Gras if (fprintf(fp, "group\t\t%s\n", up->u_primgrp) <= 0 ||
7775c007436SBen Gras fprintf(fp, "base_dir\t%s\n", up->u_basedir) <= 0 ||
7785c007436SBen Gras fprintf(fp, "skel_dir\t%s\n", up->u_skeldir) <= 0 ||
7795c007436SBen Gras fprintf(fp, "shell\t\t%s\n", up->u_shell) <= 0 ||
7805c007436SBen Gras #ifdef EXTENSIONS
7815c007436SBen Gras fprintf(fp, "class\t\t%s\n", up->u_class) <= 0 ||
7825c007436SBen Gras fprintf(fp, "homeperm\t0%o\n", up->u_homeperm) <= 0 ||
7835c007436SBen Gras #endif
7845c007436SBen Gras fprintf(fp, "inactive\t%s\n", (up->u_inactive == NULL) ?
7855c007436SBen Gras UNSET_INACTIVE : up->u_inactive) <= 0 ||
7865c007436SBen Gras fprintf(fp, "expire\t\t%s\n", (up->u_expire == NULL) ?
7875c007436SBen Gras UNSET_EXPIRY : up->u_expire) <= 0 ||
7885c007436SBen Gras fprintf(fp, "preserve\t%s\n", (up->u_preserve == 0) ?
7895c007436SBen Gras "false" : "true") <= 0) {
7905c007436SBen Gras warn("Can't write to `%s'", _PATH_USERMGMT_CONF);
7915c007436SBen Gras ret = 0;
7925c007436SBen Gras }
7935c007436SBen Gras #ifdef EXTENSIONS
7945c007436SBen Gras for (i = (up->u_defrc != up->u_rc) ? up->u_defrc : 0;
7955c007436SBen Gras i < up->u_rc ; i++) {
7965c007436SBen Gras if (fprintf(fp, "range\t\t%d..%d\n", up->u_rv[i].r_from,
7975c007436SBen Gras up->u_rv[i].r_to) <= 0) {
7985c007436SBen Gras warn("Can't set defaults: can't write to `%s'",
7995c007436SBen Gras _PATH_USERMGMT_CONF);
8005c007436SBen Gras ret = 0;
8015c007436SBen Gras }
8025c007436SBen Gras }
8035c007436SBen Gras #endif
8045c007436SBen Gras (void)fclose(fp);
8055c007436SBen Gras if (ret) {
8065c007436SBen Gras ret = ((rename(template, _PATH_USERMGMT_CONF) == 0) &&
8075c007436SBen Gras (chmod(_PATH_USERMGMT_CONF, 0644) == 0));
8085c007436SBen Gras }
8095c007436SBen Gras return ret;
8105c007436SBen Gras }
8115c007436SBen Gras
8125c007436SBen Gras /* read the defaults file */
8135c007436SBen Gras static void
8145c007436SBen Gras read_defaults(def_t *dp)
8155c007436SBen Gras {
8165c007436SBen Gras struct stat st;
8175c007436SBen Gras size_t lineno;
8185c007436SBen Gras size_t len;
8195c007436SBen Gras FILE *fp;
8205c007436SBen Gras char *cp;
8215c007436SBen Gras char *s;
8225c007436SBen Gras user_t *up = &dp->user;
8235c007436SBen Gras group_t *gp = &dp->group;
8245c007436SBen Gras
8255c007436SBen Gras (void)memset(dp, 0, sizeof(*dp));
8265c007436SBen Gras
8275c007436SBen Gras memsave(&up->u_primgrp, DEF_GROUP, strlen(DEF_GROUP));
8285c007436SBen Gras memsave(&up->u_basedir, DEF_BASEDIR, strlen(DEF_BASEDIR));
8295c007436SBen Gras memsave(&up->u_skeldir, DEF_SKELDIR, strlen(DEF_SKELDIR));
8305c007436SBen Gras memsave(&up->u_shell, DEF_SHELL, strlen(DEF_SHELL));
8315c007436SBen Gras memsave(&up->u_comment, DEF_COMMENT, strlen(DEF_COMMENT));
8325c007436SBen Gras #ifdef EXTENSIONS
8335c007436SBen Gras memsave(&up->u_class, DEF_CLASS, strlen(DEF_CLASS));
8345c007436SBen Gras #endif
8355c007436SBen Gras up->u_rsize = 16;
8365c007436SBen Gras up->u_defrc = 0;
8375c007436SBen Gras NEWARRAY(range_t, up->u_rv, up->u_rsize, exit(1));
8385c007436SBen Gras up->u_inactive = DEF_INACTIVE;
8395c007436SBen Gras up->u_expire = DEF_EXPIRE;
8405c007436SBen Gras gp->g_rsize = 16;
8415c007436SBen Gras gp->g_defrc = 0;
8425c007436SBen Gras NEWARRAY(range_t, gp->g_rv, gp->g_rsize, exit(1));
8435c007436SBen Gras if ((fp = fopen(_PATH_USERMGMT_CONF, "r")) == NULL) {
8445c007436SBen Gras if (stat(_PATH_USERMGMT_CONF, &st) < 0 && !setdefaults(up)) {
8455c007436SBen Gras warn("Can't create `%s' defaults file",
8465c007436SBen Gras _PATH_USERMGMT_CONF);
8475c007436SBen Gras }
8485c007436SBen Gras fp = fopen(_PATH_USERMGMT_CONF, "r");
8495c007436SBen Gras }
8505c007436SBen Gras if (fp != NULL) {
8515c007436SBen Gras while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) {
8525c007436SBen Gras if (strncmp(s, "group", 5) == 0) {
8535c007436SBen Gras cp = skipspace(s + 5);
8545c007436SBen Gras memsave(&up->u_primgrp, (char *)cp, strlen(cp));
8555c007436SBen Gras } else if (strncmp(s, "base_dir", 8) == 0) {
8565c007436SBen Gras cp = skipspace(s + 8);
8575c007436SBen Gras memsave(&up->u_basedir, (char *)cp, strlen(cp));
8585c007436SBen Gras } else if (strncmp(s, "skel_dir", 8) == 0) {
8595c007436SBen Gras cp = skipspace(s + 8);
8605c007436SBen Gras memsave(&up->u_skeldir, (char *)cp, strlen(cp));
8615c007436SBen Gras } else if (strncmp(s, "shell", 5) == 0) {
8625c007436SBen Gras cp = skipspace(s + 5);
8635c007436SBen Gras memsave(&up->u_shell, cp, strlen(cp));
8645c007436SBen Gras #ifdef EXTENSIONS
8655c007436SBen Gras } else if (strncmp((char *)s, "class", 5) == 0) {
8665c007436SBen Gras cp = skipspace(s + 5);
8675c007436SBen Gras memsave(&up->u_class, cp, strlen(cp));
8685c007436SBen Gras #endif
8695c007436SBen Gras #ifdef EXTENSIONS
8705c007436SBen Gras } else if (strncmp(s, "homeperm", 8) == 0) {
8715c007436SBen Gras for (cp = s + 8; *cp &&
8725c007436SBen Gras isspace((unsigned char)*cp); cp++)
8735c007436SBen Gras ;
8745c007436SBen Gras up->u_homeperm = strtoul(cp, NULL, 8);
8755c007436SBen Gras #endif
8765c007436SBen Gras } else if (strncmp(s, "inactive", 8) == 0) {
8775c007436SBen Gras cp = skipspace(s + 8);
8785c007436SBen Gras if (strcmp(cp, UNSET_INACTIVE) == 0) {
8795c007436SBen Gras if (up->u_inactive) {
8805c007436SBen Gras FREE(up->u_inactive);
8815c007436SBen Gras }
8825c007436SBen Gras up->u_inactive = NULL;
8835c007436SBen Gras } else {
8845c007436SBen Gras memsave(&up->u_inactive, cp, strlen(cp));
8855c007436SBen Gras }
8865c007436SBen Gras #ifdef EXTENSIONS
8875c007436SBen Gras } else if (strncmp(s, "range", 5) == 0) {
8885c007436SBen Gras cp = skipspace(s + 5);
8895c007436SBen Gras (void)save_range(&up->u_r, cp);
8905c007436SBen Gras #endif
8915c007436SBen Gras #ifdef EXTENSIONS
8925c007436SBen Gras } else if (strncmp(s, "preserve", 8) == 0) {
8935c007436SBen Gras cp = skipspace(s + 8);
8945c007436SBen Gras up->u_preserve =
8955c007436SBen Gras (strncmp(cp, "true", 4) == 0) ? 1 :
8965c007436SBen Gras (strncmp(cp, "yes", 3) == 0) ? 1 : atoi(cp);
8975c007436SBen Gras #endif
8985c007436SBen Gras } else if (strncmp(s, "expire", 6) == 0) {
8995c007436SBen Gras cp = skipspace(s + 6);
9005c007436SBen Gras if (strcmp(cp, UNSET_EXPIRY) == 0) {
9015c007436SBen Gras if (up->u_expire) {
9025c007436SBen Gras FREE(up->u_expire);
9035c007436SBen Gras }
9045c007436SBen Gras up->u_expire = NULL;
9055c007436SBen Gras } else {
9065c007436SBen Gras memsave(&up->u_expire, cp, strlen(cp));
9075c007436SBen Gras }
9085c007436SBen Gras #ifdef EXTENSIONS
9095c007436SBen Gras } else if (strncmp(s, "gid_range", 9) == 0) {
9105c007436SBen Gras cp = skipspace(s + 9);
9115c007436SBen Gras (void)save_range(&gp->g_r, cp);
9125c007436SBen Gras #endif
9135c007436SBen Gras }
9145c007436SBen Gras (void)free(s);
9155c007436SBen Gras }
9165c007436SBen Gras (void)fclose(fp);
9175c007436SBen Gras }
9185c007436SBen Gras if (up->u_rc == 0) {
9195c007436SBen Gras up->u_rv[up->u_rc].r_from = DEF_LOWUID;
9205c007436SBen Gras up->u_rv[up->u_rc].r_to = DEF_HIGHUID;
9215c007436SBen Gras up->u_rc += 1;
9225c007436SBen Gras }
9235c007436SBen Gras up->u_defrc = up->u_rc;
9245c007436SBen Gras up->u_homeperm = DEF_HOMEPERM;
9255c007436SBen Gras }
9265c007436SBen Gras
9275c007436SBen Gras /* return the next valid unused uid */
9285c007436SBen Gras static int
9295c007436SBen Gras getnextuid(int sync_uid_gid, int *uid, int low_uid, int high_uid)
9305c007436SBen Gras {
9315c007436SBen Gras for (*uid = low_uid ; *uid <= high_uid ; (*uid)++) {
9325c007436SBen Gras if (getpwuid((uid_t)(*uid)) == NULL && *uid != NOBODY_UID) {
9335c007436SBen Gras if (sync_uid_gid) {
9345c007436SBen Gras if (getgrgid((gid_t)(*uid)) == NULL) {
9355c007436SBen Gras return 1;
9365c007436SBen Gras }
9375c007436SBen Gras } else {
9385c007436SBen Gras return 1;
9395c007436SBen Gras }
9405c007436SBen Gras }
9415c007436SBen Gras }
9425c007436SBen Gras return 0;
9435c007436SBen Gras }
9445c007436SBen Gras
9455c007436SBen Gras /* structure which defines a password type */
9465c007436SBen Gras typedef struct passwd_type_t {
9475c007436SBen Gras const char *type; /* optional type descriptor */
9485c007436SBen Gras size_t desc_length; /* length of type descriptor */
9495c007436SBen Gras size_t length; /* length of password */
9505c007436SBen Gras const char *regex; /* regexp to output the password */
9515c007436SBen Gras size_t re_sub; /* subscript of regexp to use */
9525c007436SBen Gras } passwd_type_t;
9535c007436SBen Gras
9545c007436SBen Gras static passwd_type_t passwd_types[] = {
9555c007436SBen Gras { "$sha1", 5, 28, "\\$[^$]+\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* SHA1 */
9565c007436SBen Gras { "$2a", 3, 53, "\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* Blowfish */
9575c007436SBen Gras { "$1", 2, 34, NULL, 0 }, /* MD5 */
9585c007436SBen Gras { "", 0, DES_Len,NULL, 0 }, /* standard DES */
9595c007436SBen Gras { NULL, (size_t)~0, (size_t)~0, NULL, 0 }
9605c007436SBen Gras /* none - terminate search */
9615c007436SBen Gras };
9625c007436SBen Gras
9635c007436SBen Gras /* return non-zero if it's a valid password - check length for cipher type */
9645c007436SBen Gras static int
9655c007436SBen Gras valid_password_length(char *newpasswd)
9665c007436SBen Gras {
9675c007436SBen Gras passwd_type_t *pwtp;
9685c007436SBen Gras regmatch_t matchv[10];
9695c007436SBen Gras regex_t r;
9705c007436SBen Gras
9715c007436SBen Gras for (pwtp = passwd_types; pwtp->desc_length != (size_t)~0; pwtp++) {
9725c007436SBen Gras if (strncmp(newpasswd, pwtp->type, pwtp->desc_length) == 0) {
9735c007436SBen Gras if (pwtp->regex == NULL) {
9745c007436SBen Gras return strlen(newpasswd) == pwtp->length;
9755c007436SBen Gras }
9765c007436SBen Gras (void)regcomp(&r, pwtp->regex, REG_EXTENDED);
9775c007436SBen Gras if (regexec(&r, newpasswd, 10, matchv, 0) == 0) {
9785c007436SBen Gras regfree(&r);
9795c007436SBen Gras return (int)(matchv[pwtp->re_sub].rm_eo -
9805c007436SBen Gras matchv[pwtp->re_sub].rm_so) ==
9815c007436SBen Gras pwtp->length;
9825c007436SBen Gras }
9835c007436SBen Gras regfree(&r);
9845c007436SBen Gras }
9855c007436SBen Gras }
9865c007436SBen Gras return 0;
9875c007436SBen Gras }
9885c007436SBen Gras
9895c007436SBen Gras #ifdef EXTENSIONS
9905c007436SBen Gras /* return 1 if `class' is a valid login class */
9915c007436SBen Gras static int
9925c007436SBen Gras valid_class(char *class)
9935c007436SBen Gras {
9945c007436SBen Gras login_cap_t *lc;
9955c007436SBen Gras
9965c007436SBen Gras if (class == NULL || *class == '\0') {
9975c007436SBen Gras return 1;
9985c007436SBen Gras }
9995c007436SBen Gras /*
10005c007436SBen Gras * Check if /etc/login.conf exists. login_getclass() will
10015c007436SBen Gras * return 1 due to it not existing, so not informing the
10025c007436SBen Gras * user the actual login class does not exist.
10035c007436SBen Gras */
10045c007436SBen Gras
10053eaea2dcSLionel Sambuc if (access(_PATH_LOGINCONF, R_OK) == -1) {
10065c007436SBen Gras warn("Access failed for `%s'; will not validate class `%s'",
10073eaea2dcSLionel Sambuc _PATH_LOGINCONF, class);
10085c007436SBen Gras return 1;
10095c007436SBen Gras }
10105c007436SBen Gras
10115c007436SBen Gras if ((lc = login_getclass(class)) != NULL) {
10125c007436SBen Gras login_close(lc);
10135c007436SBen Gras return 1;
10145c007436SBen Gras }
10155c007436SBen Gras return 0;
10165c007436SBen Gras }
10175c007436SBen Gras
10185c007436SBen Gras /* return 1 if the `shellname' is a valid user shell */
10195c007436SBen Gras static int
10205c007436SBen Gras valid_shell(const char *shellname)
10215c007436SBen Gras {
10225c007436SBen Gras char *shellp;
10235c007436SBen Gras
10245c007436SBen Gras if (access(_PATH_SHELLS, R_OK) == -1) {
10255c007436SBen Gras /* Don't exit */
10265c007436SBen Gras warn("Access failed for `%s'; will not validate shell `%s'",
10275c007436SBen Gras _PATH_SHELLS, shellname);
10285c007436SBen Gras return 1;
10295c007436SBen Gras }
10305c007436SBen Gras
10315c007436SBen Gras /* if nologin is used as a shell, consider it a valid shell */
10323eaea2dcSLionel Sambuc if (strcmp(shellname, _PATH_SBIN_NOLOGIN) == 0)
10335c007436SBen Gras return 1;
10345c007436SBen Gras
10355c007436SBen Gras while ((shellp = getusershell()) != NULL)
10365c007436SBen Gras if (strcmp(shellp, shellname) == 0)
10375c007436SBen Gras return 1;
10385c007436SBen Gras
10395c007436SBen Gras warnx("Shell `%s' not found in `%s'", shellname, _PATH_SHELLS);
10405c007436SBen Gras
10415c007436SBen Gras return access(shellname, X_OK) != -1;
10425c007436SBen Gras }
10435c007436SBen Gras #endif
10445c007436SBen Gras
10455c007436SBen Gras /* look for a valid time, return 0 if it was specified but bad */
10465c007436SBen Gras static int
10475c007436SBen Gras scantime(time_t *tp, char *s)
10485c007436SBen Gras {
10495c007436SBen Gras struct tm tm;
10505c007436SBen Gras char *ep;
10515c007436SBen Gras long val;
10525c007436SBen Gras
10535c007436SBen Gras *tp = 0;
10545c007436SBen Gras if (s != NULL) {
10555c007436SBen Gras (void)memset(&tm, 0, sizeof(tm));
10565c007436SBen Gras if (strptime(s, "%c", &tm) != NULL) {
10575c007436SBen Gras *tp = mktime(&tm);
10585c007436SBen Gras return (*tp == -1) ? 0 : 1;
10595c007436SBen Gras } else if (strptime(s, "%B %d %Y", &tm) != NULL) {
10605c007436SBen Gras *tp = mktime(&tm);
10615c007436SBen Gras return (*tp == -1) ? 0 : 1;
10625c007436SBen Gras } else {
10635c007436SBen Gras errno = 0;
10645c007436SBen Gras *tp = val = strtol(s, &ep, 10);
10655c007436SBen Gras if (*ep != '\0' || *tp < -1 || errno == ERANGE) {
10665c007436SBen Gras *tp = 0;
10675c007436SBen Gras return 0;
10685c007436SBen Gras }
10695c007436SBen Gras if (*tp != val) {
10705c007436SBen Gras return 0;
10715c007436SBen Gras }
10725c007436SBen Gras }
10735c007436SBen Gras }
10745c007436SBen Gras return 1;
10755c007436SBen Gras }
10765c007436SBen Gras
10775c007436SBen Gras /* add a user */
10785c007436SBen Gras static int
10795c007436SBen Gras adduser(char *login_name, user_t *up)
10805c007436SBen Gras {
10815c007436SBen Gras struct group *grp;
10825c007436SBen Gras struct stat st;
10835c007436SBen Gras time_t expire;
10845c007436SBen Gras time_t inactive;
10855c007436SBen Gras char password[PasswordLength + 1];
10865c007436SBen Gras char home[MaxFileNameLen];
10875c007436SBen Gras char buf[MaxFileNameLen];
10885c007436SBen Gras int sync_uid_gid;
10895c007436SBen Gras int masterfd;
10905c007436SBen Gras int ptmpfd;
10915c007436SBen Gras int gid;
10925c007436SBen Gras int cc;
10935c007436SBen Gras int i;
10945c007436SBen Gras
10955c007436SBen Gras if (!valid_login(login_name, up->u_allow_samba)) {
10965c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': invalid login name", login_name);
10975c007436SBen Gras }
10985c007436SBen Gras #ifdef EXTENSIONS
10995c007436SBen Gras if (!valid_class(up->u_class)) {
11005c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': no such login class `%s'",
11015c007436SBen Gras login_name, up->u_class);
11025c007436SBen Gras }
11035c007436SBen Gras #endif
1104*0a4059eeSLionel Sambuc #if defined(__minix)
1105*0a4059eeSLionel Sambuc /* LSC: Minix flock implementation is a wrapper around fctl, which
1106*0a4059eeSLionel Sambuc * requires writeable fds for LOCK_EX to succeed. */
1107*0a4059eeSLionel Sambuc if ((masterfd = open(_PATH_MASTERPASSWD, O_RDWR)) < 0) {
1108*0a4059eeSLionel Sambuc #else
11093eaea2dcSLionel Sambuc if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) {
1110*0a4059eeSLionel Sambuc #endif /* defined(__minix) */
11115c007436SBen Gras err(EXIT_FAILURE, "Can't add user `%s': can't open `%s'",
11125c007436SBen Gras login_name, _PATH_MASTERPASSWD);
11135c007436SBen Gras }
11145c007436SBen Gras if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) {
11155c007436SBen Gras err(EXIT_FAILURE, "Can't add user `%s': can't lock `%s'",
11165c007436SBen Gras login_name, _PATH_MASTERPASSWD);
11175c007436SBen Gras }
11185c007436SBen Gras pw_init();
11195c007436SBen Gras if ((ptmpfd = pw_lock(WAITSECS)) < 0) {
11205c007436SBen Gras int serrno = errno;
11215c007436SBen Gras (void)close(masterfd);
11225c007436SBen Gras errno = serrno;
11235c007436SBen Gras err(EXIT_FAILURE, "Can't add user `%s': can't obtain pw_lock",
11245c007436SBen Gras login_name);
11255c007436SBen Gras }
11265c007436SBen Gras while ((cc = read(masterfd, buf, sizeof(buf))) > 0) {
11275c007436SBen Gras if (write(ptmpfd, buf, (size_t)(cc)) != cc) {
11285c007436SBen Gras int serrno = errno;
11295c007436SBen Gras (void)close(masterfd);
11305c007436SBen Gras (void)close(ptmpfd);
11315c007436SBen Gras (void)pw_abort();
11325c007436SBen Gras errno = serrno;
11335c007436SBen Gras err(EXIT_FAILURE, "Can't add user `%s': "
11345c007436SBen Gras "short write to /etc/ptmp", login_name);
11355c007436SBen Gras }
11365c007436SBen Gras }
11375c007436SBen Gras (void)close(masterfd);
11385c007436SBen Gras /* if no uid was specified, get next one in [low_uid..high_uid] range */
11395c007436SBen Gras sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0);
11405c007436SBen Gras if (up->u_uid == -1) {
11415c007436SBen Gras int got_id = 0;
11425c007436SBen Gras
11435c007436SBen Gras /*
11445c007436SBen Gras * Look for a free UID in the command line ranges (if any).
11455c007436SBen Gras * These start after the ranges specified in the config file.
11465c007436SBen Gras */
11475c007436SBen Gras for (i = up->u_defrc; !got_id && i < up->u_rc ; i++) {
11485c007436SBen Gras got_id = getnextuid(sync_uid_gid, &up->u_uid,
11495c007436SBen Gras up->u_rv[i].r_from, up->u_rv[i].r_to);
11505c007436SBen Gras }
11515c007436SBen Gras /*
11525c007436SBen Gras * If there were no free UIDs in the command line ranges,
11535c007436SBen Gras * try the ranges from the config file (there will always
11545c007436SBen Gras * be at least one default).
11555c007436SBen Gras */
11565c007436SBen Gras for (i = 0; !got_id && i < up->u_defrc; i++) {
11575c007436SBen Gras got_id = getnextuid(sync_uid_gid, &up->u_uid,
11585c007436SBen Gras up->u_rv[i].r_from, up->u_rv[i].r_to);
11595c007436SBen Gras }
11605c007436SBen Gras if (!got_id) {
11615c007436SBen Gras (void)close(ptmpfd);
11625c007436SBen Gras (void)pw_abort();
11635c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': "
11645c007436SBen Gras "can't get next uid for %d", login_name,
11655c007436SBen Gras up->u_uid);
11665c007436SBen Gras }
11675c007436SBen Gras }
11685c007436SBen Gras /* check uid isn't already allocated */
11695c007436SBen Gras if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) {
11705c007436SBen Gras (void)close(ptmpfd);
11715c007436SBen Gras (void)pw_abort();
11725c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': "
11735c007436SBen Gras "uid %d is already in use", login_name, up->u_uid);
11745c007436SBen Gras }
11755c007436SBen Gras /* if -g=uid was specified, check gid is unused */
11765c007436SBen Gras if (sync_uid_gid) {
11775c007436SBen Gras if (getgrgid((gid_t)(up->u_uid)) != NULL) {
11785c007436SBen Gras (void)close(ptmpfd);
11795c007436SBen Gras (void)pw_abort();
11805c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': "
11815c007436SBen Gras "gid %d is already in use", login_name,
11825c007436SBen Gras up->u_uid);
11835c007436SBen Gras }
11845c007436SBen Gras gid = up->u_uid;
11855c007436SBen Gras } else if ((grp = getgrnam(up->u_primgrp)) != NULL) {
11865c007436SBen Gras gid = grp->gr_gid;
11875c007436SBen Gras } else if (is_number(up->u_primgrp) &&
11885c007436SBen Gras (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) {
11895c007436SBen Gras gid = grp->gr_gid;
11905c007436SBen Gras } else {
11915c007436SBen Gras (void)close(ptmpfd);
11925c007436SBen Gras (void)pw_abort();
11935c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': group %s not found",
11945c007436SBen Gras login_name, up->u_primgrp);
11955c007436SBen Gras }
11965c007436SBen Gras /* check name isn't already in use */
11975c007436SBen Gras if (!(up->u_flags & F_DUPUID) && getpwnam(login_name) != NULL) {
11985c007436SBen Gras (void)close(ptmpfd);
11995c007436SBen Gras (void)pw_abort();
12005c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': "
12015c007436SBen Gras "`%s' is already a user", login_name, login_name);
12025c007436SBen Gras }
12035c007436SBen Gras if (up->u_flags & F_HOMEDIR) {
12045c007436SBen Gras (void)strlcpy(home, up->u_home, sizeof(home));
12055c007436SBen Gras } else {
12065c007436SBen Gras /* if home directory hasn't been given, make it up */
12075c007436SBen Gras (void)snprintf(home, sizeof(home), "%s/%s", up->u_basedir,
12085c007436SBen Gras login_name);
12095c007436SBen Gras }
12105c007436SBen Gras if (up->u_flags & F_SHELL) {
12115c007436SBen Gras #ifdef EXTENSIONS
12125c007436SBen Gras if (!valid_shell(up->u_shell)) {
12135c007436SBen Gras int oerrno = errno;
12145c007436SBen Gras (void)close(ptmpfd);
12155c007436SBen Gras (void)pw_abort();
12165c007436SBen Gras errno = oerrno;
12175c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': "
12185c007436SBen Gras "Cannot access shell `%s'",
12195c007436SBen Gras login_name, up->u_shell);
12205c007436SBen Gras }
12215c007436SBen Gras #endif
12225c007436SBen Gras }
12235c007436SBen Gras
12245c007436SBen Gras if (!scantime(&inactive, up->u_inactive)) {
12255c007436SBen Gras warnx("Warning: inactive time `%s' invalid, password expiry off",
12265c007436SBen Gras up->u_inactive);
12275c007436SBen Gras }
12285c007436SBen Gras if (!scantime(&expire, up->u_expire) || expire == -1) {
12295c007436SBen Gras warnx("Warning: expire time `%s' invalid, account expiry off",
12305c007436SBen Gras up->u_expire);
12315c007436SBen Gras expire = 0; /* Just in case. */
12325c007436SBen Gras }
12335c007436SBen Gras if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR)) {
12345c007436SBen Gras warnx("Warning: home directory `%s' doesn't exist, "
12355c007436SBen Gras "and -m was not specified", home);
12365c007436SBen Gras }
12375c007436SBen Gras password[sizeof(password) - 1] = '\0';
12385c007436SBen Gras if (up->u_password != NULL && valid_password_length(up->u_password)) {
12395c007436SBen Gras (void)strlcpy(password, up->u_password, sizeof(password));
12405c007436SBen Gras } else {
12415c007436SBen Gras (void)memset(password, '*', DES_Len);
12425c007436SBen Gras password[DES_Len] = 0;
12435c007436SBen Gras if (up->u_password != NULL) {
12445c007436SBen Gras warnx("Password `%s' is invalid: setting it to `%s'",
12455c007436SBen Gras up->u_password, password);
12465c007436SBen Gras }
12475c007436SBen Gras }
12485c007436SBen Gras cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
12495c007436SBen Gras login_name,
12505c007436SBen Gras password,
12515c007436SBen Gras up->u_uid,
12525c007436SBen Gras gid,
12535c007436SBen Gras #ifdef EXTENSIONS
12545c007436SBen Gras up->u_class,
12555c007436SBen Gras #else
12565c007436SBen Gras "",
12575c007436SBen Gras #endif
12585c007436SBen Gras (long) inactive,
12595c007436SBen Gras (long) expire,
12605c007436SBen Gras up->u_comment,
12615c007436SBen Gras home,
12625c007436SBen Gras up->u_shell);
12635c007436SBen Gras if (write(ptmpfd, buf, (size_t) cc) != cc) {
12645c007436SBen Gras int serrno = errno;
12655c007436SBen Gras (void)close(ptmpfd);
12665c007436SBen Gras (void)pw_abort();
12675c007436SBen Gras errno = serrno;
12685c007436SBen Gras err(EXIT_FAILURE, "Can't add user `%s': write failed",
12695c007436SBen Gras login_name);
12705c007436SBen Gras }
12715c007436SBen Gras if (up->u_flags & F_MKDIR) {
12725c007436SBen Gras if (lstat(home, &st) == 0) {
12735c007436SBen Gras (void)close(ptmpfd);
12745c007436SBen Gras (void)pw_abort();
12755c007436SBen Gras errx(EXIT_FAILURE,
12765c007436SBen Gras "Can't add user `%s': home directory `%s' "
12775c007436SBen Gras "already exists", login_name, home);
12785c007436SBen Gras } else {
12793eaea2dcSLionel Sambuc if (asystem("%s -p %s", _PATH_MKDIR, home) != 0) {
12805c007436SBen Gras (void)close(ptmpfd);
12815c007436SBen Gras (void)pw_abort();
12825c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': "
12835c007436SBen Gras "can't mkdir `%s'", login_name, home);
12845c007436SBen Gras }
12855c007436SBen Gras (void)copydotfiles(up->u_skeldir, up->u_uid, gid, home,
12865c007436SBen Gras up->u_homeperm);
12875c007436SBen Gras }
12885c007436SBen Gras }
12895c007436SBen Gras if (strcmp(up->u_primgrp, "=uid") == 0 &&
12905c007436SBen Gras getgrnam(login_name) == NULL &&
12915c007436SBen Gras !creategid(login_name, gid, login_name)) {
12925c007436SBen Gras (void)close(ptmpfd);
12935c007436SBen Gras (void)pw_abort();
12945c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': can't create gid %d ",
12955c007436SBen Gras login_name, gid);
12965c007436SBen Gras }
12975c007436SBen Gras if (up->u_groupc > 0 &&
12985c007436SBen Gras !append_group(login_name, up->u_groupc, up->u_groupv)) {
12995c007436SBen Gras (void)close(ptmpfd);
13005c007436SBen Gras (void)pw_abort();
13015c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': can't append "
13025c007436SBen Gras "to new groups", login_name);
13035c007436SBen Gras }
13045c007436SBen Gras (void)close(ptmpfd);
13055c007436SBen Gras #if PW_MKDB_ARGC == 2
13065c007436SBen Gras if (pw_mkdb(login_name, 0) < 0)
13075c007436SBen Gras #else
13085c007436SBen Gras if (pw_mkdb() < 0)
13095c007436SBen Gras #endif
13105c007436SBen Gras {
13115c007436SBen Gras (void)pw_abort();
13125c007436SBen Gras errx(EXIT_FAILURE, "Can't add user `%s': pw_mkdb failed",
13135c007436SBen Gras login_name);
13145c007436SBen Gras }
13155c007436SBen Gras syslog(LOG_INFO, "New user added: name=%s, uid=%d, gid=%d, home=%s, "
13165c007436SBen Gras "shell=%s", login_name, up->u_uid, gid, home, up->u_shell);
13175c007436SBen Gras return 1;
13185c007436SBen Gras }
13195c007436SBen Gras
13205c007436SBen Gras /* remove a user from the groups file */
13215c007436SBen Gras static int
13225c007436SBen Gras rm_user_from_groups(char *login_name)
13235c007436SBen Gras {
13245c007436SBen Gras struct stat st;
13255c007436SBen Gras regmatch_t matchv[10];
13265c007436SBen Gras regex_t r;
13275c007436SBen Gras FILE *from;
13285c007436SBen Gras FILE *to;
13295c007436SBen Gras char line[MaxEntryLen];
13305c007436SBen Gras char buf[MaxEntryLen];
13315c007436SBen Gras char f[MaxFileNameLen];
13325c007436SBen Gras int fd;
13335c007436SBen Gras int cc;
13345c007436SBen Gras int sc;
13355c007436SBen Gras
13365c007436SBen Gras (void)snprintf(line, sizeof(line), "(:|,)(%s)(,|$)", login_name);
13375c007436SBen Gras if ((sc = regcomp(&r, line, REG_EXTENDED|REG_NEWLINE)) != 0) {
13385c007436SBen Gras (void)regerror(sc, &r, buf, sizeof(buf));
13395c007436SBen Gras warnx("Can't compile regular expression `%s' (%s)", line,
13405c007436SBen Gras buf);
13415c007436SBen Gras return 0;
13425c007436SBen Gras }
1343*0a4059eeSLionel Sambuc #if defined(__minix)
1344*0a4059eeSLionel Sambuc /* LSC: Minix flock implementation is a wrapper around fctl, which
1345*0a4059eeSLionel Sambuc * requires writeable fds for LOCK_EX to succeed. */
1346*0a4059eeSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r+")) == NULL) {
1347*0a4059eeSLionel Sambuc #else
13483eaea2dcSLionel Sambuc if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
1349*0a4059eeSLionel Sambuc #endif /* defined(__minix) */
13505c007436SBen Gras warn("Can't remove user `%s' from `%s': can't open `%s'",
13515c007436SBen Gras login_name, _PATH_GROUP, _PATH_GROUP);
13525c007436SBen Gras return 0;
13535c007436SBen Gras }
13545c007436SBen Gras if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
13555c007436SBen Gras warn("Can't remove user `%s' from `%s': can't lock `%s'",
13565c007436SBen Gras login_name, _PATH_GROUP, _PATH_GROUP);
13575c007436SBen Gras (void)fclose(from);
13585c007436SBen Gras return 0;
13595c007436SBen Gras }
13605c007436SBen Gras (void)fstat(fileno(from), &st);
13615c007436SBen Gras (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP);
13625c007436SBen Gras if ((fd = mkstemp(f)) < 0) {
13635c007436SBen Gras warn("Can't remove user `%s' from `%s': mkstemp failed",
13645c007436SBen Gras login_name, _PATH_GROUP);
13655c007436SBen Gras (void)fclose(from);
13665c007436SBen Gras return 0;
13675c007436SBen Gras }
13685c007436SBen Gras if ((to = fdopen(fd, "w")) == NULL) {
13695c007436SBen Gras warn("Can't remove user `%s' from `%s': fdopen `%s' failed",
13705c007436SBen Gras login_name, _PATH_GROUP, f);
13715c007436SBen Gras (void)fclose(from);
13725c007436SBen Gras (void)close(fd);
13735c007436SBen Gras (void)unlink(f);
13745c007436SBen Gras return 0;
13755c007436SBen Gras }
13765c007436SBen Gras while (fgets(buf, sizeof(buf), from) != NULL) {
13775c007436SBen Gras cc = strlen(buf);
13785c007436SBen Gras if (regexec(&r, buf, 10, matchv, 0) == 0) {
13795c007436SBen Gras if (buf[(int)matchv[1].rm_so] == ',') {
13805c007436SBen Gras matchv[2].rm_so = matchv[1].rm_so;
13815c007436SBen Gras } else if (matchv[2].rm_eo != matchv[3].rm_eo) {
13825c007436SBen Gras matchv[2].rm_eo = matchv[3].rm_eo;
13835c007436SBen Gras }
13845c007436SBen Gras cc -= (int) matchv[2].rm_eo;
13855c007436SBen Gras sc = (int) matchv[2].rm_so;
13865c007436SBen Gras if (fwrite(buf, sizeof(char), (size_t)sc, to) != sc ||
13875c007436SBen Gras fwrite(&buf[(int)matchv[2].rm_eo], sizeof(char),
13885c007436SBen Gras (size_t)cc, to) != cc) {
13895c007436SBen Gras warn("Can't remove user `%s' from `%s': "
13905c007436SBen Gras "short write to `%s'", login_name,
13915c007436SBen Gras _PATH_GROUP, f);
13925c007436SBen Gras (void)fclose(from);
13935c007436SBen Gras (void)close(fd);
13945c007436SBen Gras (void)unlink(f);
13955c007436SBen Gras return 0;
13965c007436SBen Gras }
13975c007436SBen Gras } else if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) {
13985c007436SBen Gras warn("Can't remove user `%s' from `%s': "
13995c007436SBen Gras "short write to `%s'", login_name, _PATH_GROUP, f);
14005c007436SBen Gras (void)fclose(from);
14015c007436SBen Gras (void)close(fd);
14025c007436SBen Gras (void)unlink(f);
14035c007436SBen Gras return 0;
14045c007436SBen Gras }
14055c007436SBen Gras }
14065c007436SBen Gras (void)fclose(from);
14075c007436SBen Gras (void)fclose(to);
14085c007436SBen Gras if (rename(f, _PATH_GROUP) < 0) {
14095c007436SBen Gras warn("Can't remove user `%s' from `%s': "
14105c007436SBen Gras "can't rename `%s' to `%s'",
14115c007436SBen Gras login_name, _PATH_GROUP, f, _PATH_GROUP);
14125c007436SBen Gras (void)unlink(f);
14135c007436SBen Gras return 0;
14145c007436SBen Gras }
14155c007436SBen Gras (void)chmod(_PATH_GROUP, st.st_mode & 07777);
14165c007436SBen Gras return 1;
14175c007436SBen Gras }
14185c007436SBen Gras
14195c007436SBen Gras /* check that the user or group is local, not from YP/NIS */
14205c007436SBen Gras static int
14215c007436SBen Gras is_local(char *name, const char *file)
14225c007436SBen Gras {
14235c007436SBen Gras FILE *fp;
14245c007436SBen Gras char buf[MaxEntryLen];
14255c007436SBen Gras size_t len;
14265c007436SBen Gras int ret;
14275c007436SBen Gras
14285c007436SBen Gras if ((fp = fopen(file, "r")) == NULL) {
14295c007436SBen Gras err(EXIT_FAILURE, "Can't open `%s'", file);
14305c007436SBen Gras }
14315c007436SBen Gras len = strlen(name);
14325c007436SBen Gras for (ret = 0 ; fgets(buf, sizeof(buf), fp) != NULL ; ) {
14335c007436SBen Gras if (strncmp(buf, name, len) == 0 && buf[len] == ':') {
14345c007436SBen Gras ret = 1;
14355c007436SBen Gras break;
14365c007436SBen Gras }
14375c007436SBen Gras }
14385c007436SBen Gras (void)fclose(fp);
14395c007436SBen Gras return ret;
14405c007436SBen Gras }
14415c007436SBen Gras
14425c007436SBen Gras /* modify a user */
14435c007436SBen Gras static int
14445c007436SBen Gras moduser(char *login_name, char *newlogin, user_t *up, int allow_samba)
14455c007436SBen Gras {
14465c007436SBen Gras struct passwd *pwp, pw;
14475c007436SBen Gras struct group *grp;
14485c007436SBen Gras const char *homedir;
14495c007436SBen Gras char *locked_pwd;
14505c007436SBen Gras size_t colonc;
14515c007436SBen Gras size_t loginc;
14525c007436SBen Gras size_t len;
14535c007436SBen Gras FILE *master;
14545c007436SBen Gras char newdir[MaxFileNameLen];
14555c007436SBen Gras char buf[MaxEntryLen];
14565c007436SBen Gras char pwbuf[MaxEntryLen];
14575c007436SBen Gras char *colon;
14585c007436SBen Gras int masterfd;
14595c007436SBen Gras int ptmpfd;
14605c007436SBen Gras int error;
14615c007436SBen Gras
14625c007436SBen Gras if (!valid_login(newlogin, allow_samba)) {
14635c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': invalid login name",
14645c007436SBen Gras login_name);
14655c007436SBen Gras }
14665c007436SBen Gras if (getpwnam_r(login_name, &pw, pwbuf, sizeof(pwbuf), &pwp) != 0
14675c007436SBen Gras || pwp == NULL) {
14685c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': no such user",
14695c007436SBen Gras login_name);
14705c007436SBen Gras }
14715c007436SBen Gras if (!is_local(login_name, _PATH_MASTERPASSWD)) {
14725c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': must be a local user",
14735c007436SBen Gras login_name);
14745c007436SBen Gras }
14755c007436SBen Gras /* keep dir name in case we need it for '-m' */
14765c007436SBen Gras homedir = pwp->pw_dir;
14775c007436SBen Gras
1478*0a4059eeSLionel Sambuc #if defined(__minix)
1479*0a4059eeSLionel Sambuc /* LSC: Minix flock implementation is a wrapper around fctl, which
1480*0a4059eeSLionel Sambuc * requires writeable fds for LOCK_EX to succeed. */
1481*0a4059eeSLionel Sambuc if ((masterfd = open(_PATH_MASTERPASSWD, O_RDWR)) < 0) {
1482*0a4059eeSLionel Sambuc #else
14833eaea2dcSLionel Sambuc if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) {
1484*0a4059eeSLionel Sambuc #endif /* defined(__minix) */
14855c007436SBen Gras err(EXIT_FAILURE, "Can't modify user `%s': can't open `%s'",
14865c007436SBen Gras login_name, _PATH_MASTERPASSWD);
14875c007436SBen Gras }
14885c007436SBen Gras if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) {
14895c007436SBen Gras err(EXIT_FAILURE, "Can't modify user `%s': can't lock `%s'",
14905c007436SBen Gras login_name, _PATH_MASTERPASSWD);
14915c007436SBen Gras }
14925c007436SBen Gras pw_init();
14935c007436SBen Gras if ((ptmpfd = pw_lock(WAITSECS)) < 0) {
14945c007436SBen Gras int serrno = errno;
14955c007436SBen Gras (void)close(masterfd);
14965c007436SBen Gras errno = serrno;
14975c007436SBen Gras err(EXIT_FAILURE, "Can't modify user `%s': "
14985c007436SBen Gras "can't obtain pw_lock", login_name);
14995c007436SBen Gras }
15005c007436SBen Gras if ((master = fdopen(masterfd, "r")) == NULL) {
15015c007436SBen Gras int serrno = errno;
15025c007436SBen Gras (void)close(masterfd);
15035c007436SBen Gras (void)close(ptmpfd);
15045c007436SBen Gras (void)pw_abort();
15055c007436SBen Gras errno = serrno;
15065c007436SBen Gras err(EXIT_FAILURE, "Can't modify user `%s': "
15075c007436SBen Gras "fdopen fd for %s", login_name, _PATH_MASTERPASSWD);
15085c007436SBen Gras }
15095c007436SBen Gras if (up != NULL) {
15105c007436SBen Gras if (up->u_flags & F_USERNAME) {
15115c007436SBen Gras /*
15125c007436SBen Gras * If changing name,
15135c007436SBen Gras * check new name isn't already in use
15145c007436SBen Gras */
15155c007436SBen Gras if (strcmp(login_name, newlogin) != 0 &&
15165c007436SBen Gras getpwnam(newlogin) != NULL) {
15175c007436SBen Gras (void)close(ptmpfd);
15185c007436SBen Gras (void)pw_abort();
15195c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': "
15205c007436SBen Gras "`%s' is already a user", login_name,
15215c007436SBen Gras newlogin);
15225c007436SBen Gras }
15235c007436SBen Gras pwp->pw_name = newlogin;
15245c007436SBen Gras
15255c007436SBen Gras /*
15265c007436SBen Gras * Provide a new directory name in case the
15275c007436SBen Gras * home directory is to be moved.
15285c007436SBen Gras */
15295c007436SBen Gras if (up->u_flags & F_MKDIR) {
15305c007436SBen Gras (void)snprintf(newdir, sizeof(newdir), "%s/%s",
15315c007436SBen Gras up->u_basedir, newlogin);
15325c007436SBen Gras pwp->pw_dir = newdir;
15335c007436SBen Gras }
15345c007436SBen Gras }
15355c007436SBen Gras if (up->u_flags & F_PASSWORD) {
15365c007436SBen Gras if (up->u_password != NULL) {
15375c007436SBen Gras if (!valid_password_length(up->u_password)) {
15385c007436SBen Gras (void)close(ptmpfd);
15395c007436SBen Gras (void)pw_abort();
15405c007436SBen Gras errx(EXIT_FAILURE,
15415c007436SBen Gras "Can't modify user `%s': "
15425c007436SBen Gras "invalid password: `%s'",
15435c007436SBen Gras login_name, up->u_password);
15445c007436SBen Gras }
15455c007436SBen Gras if ((locked_pwd =
15465c007436SBen Gras strstr(pwp->pw_passwd, LOCKED)) != NULL) {
15475c007436SBen Gras /*
15485c007436SBen Gras * account is locked - keep it locked
15495c007436SBen Gras * and just change the password.
15505c007436SBen Gras */
15515c007436SBen Gras if (asprintf(&locked_pwd, "%s%s",
15525c007436SBen Gras LOCKED, up->u_password) == -1) {
15535c007436SBen Gras (void)close(ptmpfd);
15545c007436SBen Gras (void)pw_abort();
15555c007436SBen Gras err(EXIT_FAILURE,
15565c007436SBen Gras "Can't modify user `%s': "
15575c007436SBen Gras "asprintf failed",
15585c007436SBen Gras login_name);
15595c007436SBen Gras }
15605c007436SBen Gras pwp->pw_passwd = locked_pwd;
15615c007436SBen Gras } else {
15625c007436SBen Gras pwp->pw_passwd = up->u_password;
15635c007436SBen Gras }
15645c007436SBen Gras }
15655c007436SBen Gras }
15665c007436SBen Gras
15675c007436SBen Gras /* check whether we should lock the account. */
15685c007436SBen Gras if (up->u_locked == LOCK) {
15695c007436SBen Gras /* check to see account if already locked. */
15705c007436SBen Gras if ((locked_pwd = strstr(pwp->pw_passwd, LOCKED))
15715c007436SBen Gras != NULL) {
15725c007436SBen Gras warnx("Account is already locked");
15735c007436SBen Gras } else {
15745c007436SBen Gras if (asprintf(&locked_pwd, "%s%s", LOCKED,
15755c007436SBen Gras pwp->pw_passwd) == -1) {
15765c007436SBen Gras (void)close(ptmpfd);
15775c007436SBen Gras (void)pw_abort();
15785c007436SBen Gras err(EXIT_FAILURE,
15795c007436SBen Gras "Can't modify user `%s': "
15805c007436SBen Gras "asprintf failed", login_name);
15815c007436SBen Gras }
15825c007436SBen Gras pwp->pw_passwd = locked_pwd;
15835c007436SBen Gras }
15845c007436SBen Gras } else if (up->u_locked == UNLOCK) {
15855c007436SBen Gras if ((locked_pwd = strstr(pwp->pw_passwd, LOCKED))
15865c007436SBen Gras == NULL) {
15875c007436SBen Gras warnx("Can't modify user `%s': "
15885c007436SBen Gras "account is not locked", login_name);
15895c007436SBen Gras } else {
15905c007436SBen Gras pwp->pw_passwd = locked_pwd + strlen(LOCKED);
15915c007436SBen Gras }
15925c007436SBen Gras }
15935c007436SBen Gras
15945c007436SBen Gras if (up->u_flags & F_UID) {
15955c007436SBen Gras /* check uid isn't already allocated */
15965c007436SBen Gras if (!(up->u_flags & F_DUPUID) &&
15975c007436SBen Gras getpwuid((uid_t)(up->u_uid)) != NULL) {
15985c007436SBen Gras (void)close(ptmpfd);
15995c007436SBen Gras (void)pw_abort();
16005c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': "
16015c007436SBen Gras "uid `%d' is already in use", login_name,
16025c007436SBen Gras up->u_uid);
16035c007436SBen Gras }
16045c007436SBen Gras pwp->pw_uid = up->u_uid;
16055c007436SBen Gras }
16065c007436SBen Gras if (up->u_flags & F_GROUP) {
16075c007436SBen Gras /* if -g=uid was specified, check gid is unused */
16085c007436SBen Gras if (strcmp(up->u_primgrp, "=uid") == 0) {
16095c007436SBen Gras if (getgrgid((gid_t)(pwp->pw_uid)) != NULL) {
16105c007436SBen Gras (void)close(ptmpfd);
16115c007436SBen Gras (void)pw_abort();
16125c007436SBen Gras errx(EXIT_FAILURE,
16135c007436SBen Gras "Can't modify user `%s': "
16145c007436SBen Gras "gid %d is already in use",
161584d9c625SLionel Sambuc login_name, pwp->pw_uid);
16165c007436SBen Gras }
16175c007436SBen Gras pwp->pw_gid = pwp->pw_uid;
161884d9c625SLionel Sambuc if (!creategid(newlogin, pwp->pw_uid, "")) {
161984d9c625SLionel Sambuc errx(EXIT_FAILURE,
162084d9c625SLionel Sambuc "Could not create group %s "
162184d9c625SLionel Sambuc "with uid %d", newlogin,
162284d9c625SLionel Sambuc up->u_uid);
162384d9c625SLionel Sambuc }
16245c007436SBen Gras } else if ((grp = getgrnam(up->u_primgrp)) != NULL) {
16255c007436SBen Gras pwp->pw_gid = grp->gr_gid;
16265c007436SBen Gras } else if (is_number(up->u_primgrp) &&
16275c007436SBen Gras (grp = getgrgid(
16285c007436SBen Gras (gid_t)atoi(up->u_primgrp))) != NULL) {
16295c007436SBen Gras pwp->pw_gid = grp->gr_gid;
16305c007436SBen Gras } else {
16315c007436SBen Gras (void)close(ptmpfd);
16325c007436SBen Gras (void)pw_abort();
16335c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': "
16345c007436SBen Gras "group %s not found", login_name,
16355c007436SBen Gras up->u_primgrp);
16365c007436SBen Gras }
16375c007436SBen Gras }
16385c007436SBen Gras if (up->u_flags & F_INACTIVE) {
16395c007436SBen Gras if (!scantime(&pwp->pw_change, up->u_inactive)) {
16405c007436SBen Gras warnx("Warning: inactive time `%s' invalid, "
16415c007436SBen Gras "password expiry off",
16425c007436SBen Gras up->u_inactive);
16435c007436SBen Gras }
16445c007436SBen Gras }
16455c007436SBen Gras if (up->u_flags & F_EXPIRE) {
16465c007436SBen Gras if (!scantime(&pwp->pw_expire, up->u_expire) ||
16475c007436SBen Gras pwp->pw_expire == -1) {
16485c007436SBen Gras warnx("Warning: expire time `%s' invalid, "
16495c007436SBen Gras "account expiry off",
16505c007436SBen Gras up->u_expire);
16515c007436SBen Gras pwp->pw_expire = 0;
16525c007436SBen Gras }
16535c007436SBen Gras }
16545c007436SBen Gras if (up->u_flags & F_COMMENT) {
16555c007436SBen Gras pwp->pw_gecos = up->u_comment;
16565c007436SBen Gras }
16575c007436SBen Gras if (up->u_flags & F_HOMEDIR) {
16585c007436SBen Gras pwp->pw_dir = up->u_home;
16595c007436SBen Gras }
16605c007436SBen Gras if (up->u_flags & F_SHELL) {
16615c007436SBen Gras #ifdef EXTENSIONS
16625c007436SBen Gras if (!valid_shell(up->u_shell)) {
16635c007436SBen Gras int oerrno = errno;
16645c007436SBen Gras (void)close(ptmpfd);
16655c007436SBen Gras (void)pw_abort();
16665c007436SBen Gras errno = oerrno;
16675c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': "
16685c007436SBen Gras "Cannot access shell `%s'",
16695c007436SBen Gras login_name, up->u_shell);
16705c007436SBen Gras }
16715c007436SBen Gras pwp->pw_shell = up->u_shell;
16725c007436SBen Gras #else
16735c007436SBen Gras pwp->pw_shell = up->u_shell;
16745c007436SBen Gras #endif
16755c007436SBen Gras }
16765c007436SBen Gras #ifdef EXTENSIONS
16775c007436SBen Gras if (up->u_flags & F_CLASS) {
16785c007436SBen Gras if (!valid_class(up->u_class)) {
16795c007436SBen Gras (void)close(ptmpfd);
16805c007436SBen Gras (void)pw_abort();
16815c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': "
16825c007436SBen Gras "no such login class `%s'", login_name,
16835c007436SBen Gras up->u_class);
16845c007436SBen Gras }
16855c007436SBen Gras pwp->pw_class = up->u_class;
16865c007436SBen Gras }
16875c007436SBen Gras #endif
16885c007436SBen Gras }
16895c007436SBen Gras loginc = strlen(login_name);
16905c007436SBen Gras while (fgets(buf, sizeof(buf), master) != NULL) {
16915c007436SBen Gras if ((colon = strchr(buf, ':')) == NULL) {
16925c007436SBen Gras warnx("Malformed entry `%s'. Skipping", buf);
16935c007436SBen Gras continue;
16945c007436SBen Gras }
16955c007436SBen Gras colonc = (size_t)(colon - buf);
16965c007436SBen Gras if (strncmp(login_name, buf, loginc) == 0 && loginc == colonc) {
16975c007436SBen Gras if (up != NULL) {
16985c007436SBen Gras len = snprintf(buf, sizeof(buf), "%s:%s:%d:%d:"
16995c007436SBen Gras #ifdef EXTENSIONS
17005c007436SBen Gras "%s"
17015c007436SBen Gras #endif
17025c007436SBen Gras ":%ld:%ld:%s:%s:%s\n",
17035c007436SBen Gras newlogin,
17045c007436SBen Gras pwp->pw_passwd,
17055c007436SBen Gras pwp->pw_uid,
17065c007436SBen Gras pwp->pw_gid,
17075c007436SBen Gras #ifdef EXTENSIONS
17085c007436SBen Gras pwp->pw_class,
17095c007436SBen Gras #endif
17105c007436SBen Gras (long)pwp->pw_change,
17115c007436SBen Gras (long)pwp->pw_expire,
17125c007436SBen Gras pwp->pw_gecos,
17135c007436SBen Gras pwp->pw_dir,
17145c007436SBen Gras pwp->pw_shell);
17155c007436SBen Gras if (write(ptmpfd, buf, len) != len) {
17165c007436SBen Gras int serrno = errno;
17175c007436SBen Gras (void)close(ptmpfd);
17185c007436SBen Gras (void)pw_abort();
17195c007436SBen Gras errno = serrno;
17205c007436SBen Gras err(EXIT_FAILURE, "Can't modify user "
17215c007436SBen Gras "`%s': write", login_name);
17225c007436SBen Gras }
17235c007436SBen Gras }
17245c007436SBen Gras } else {
17255c007436SBen Gras len = strlen(buf);
17265c007436SBen Gras if (write(ptmpfd, buf, len) != len) {
17275c007436SBen Gras int serrno = errno;
17285c007436SBen Gras (void)close(masterfd);
17295c007436SBen Gras (void)close(ptmpfd);
17305c007436SBen Gras (void)pw_abort();
17315c007436SBen Gras errno = serrno;
17325c007436SBen Gras err(EXIT_FAILURE, "Can't modify `%s': "
17335c007436SBen Gras "write", login_name);
17345c007436SBen Gras }
17355c007436SBen Gras }
17365c007436SBen Gras }
17375c007436SBen Gras if (up != NULL) {
17385c007436SBen Gras if ((up->u_flags & F_MKDIR) &&
17393eaea2dcSLionel Sambuc asystem("%s %s %s", _PATH_MV, homedir, pwp->pw_dir) != 0) {
17405c007436SBen Gras (void)close(ptmpfd);
17415c007436SBen Gras (void)pw_abort();
17425c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': "
17435c007436SBen Gras "can't move `%s' to `%s'",
17445c007436SBen Gras login_name, homedir, pwp->pw_dir);
17455c007436SBen Gras }
17465c007436SBen Gras if (up->u_groupc > 0 &&
17475c007436SBen Gras !append_group(newlogin, up->u_groupc, up->u_groupv)) {
17485c007436SBen Gras (void)close(ptmpfd);
17495c007436SBen Gras (void)pw_abort();
17505c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': "
17515c007436SBen Gras "can't append `%s' to new groups",
17525c007436SBen Gras login_name, newlogin);
17535c007436SBen Gras }
17545c007436SBen Gras }
17555c007436SBen Gras (void)close(ptmpfd);
17565c007436SBen Gras (void)fclose(master);
17575c007436SBen Gras #if PW_MKDB_ARGC == 2
17585c007436SBen Gras if (up != NULL && strcmp(login_name, newlogin) == 0) {
17595c007436SBen Gras error = pw_mkdb(login_name, 0);
17605c007436SBen Gras } else {
17615c007436SBen Gras error = pw_mkdb(NULL, 0);
17625c007436SBen Gras }
17635c007436SBen Gras #else
17645c007436SBen Gras error = pw_mkdb();
17655c007436SBen Gras #endif
17665c007436SBen Gras if (error < 0) {
17675c007436SBen Gras (void)pw_abort();
17685c007436SBen Gras errx(EXIT_FAILURE, "Can't modify user `%s': pw_mkdb failed",
17695c007436SBen Gras login_name);
17705c007436SBen Gras }
17715c007436SBen Gras if (up == NULL) {
17725c007436SBen Gras syslog(LOG_INFO, "User removed: name=%s", login_name);
17735c007436SBen Gras } else if (strcmp(login_name, newlogin) == 0) {
17745c007436SBen Gras syslog(LOG_INFO, "User information modified: name=%s, uid=%d, "
17755c007436SBen Gras "gid=%d, home=%s, shell=%s",
17765c007436SBen Gras login_name, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir,
17775c007436SBen Gras pwp->pw_shell);
17785c007436SBen Gras } else {
17795c007436SBen Gras syslog(LOG_INFO, "User information modified: name=%s, "
17805c007436SBen Gras "new name=%s, uid=%d, gid=%d, home=%s, shell=%s",
17815c007436SBen Gras login_name, newlogin, pwp->pw_uid, pwp->pw_gid,
17825c007436SBen Gras pwp->pw_dir, pwp->pw_shell);
17835c007436SBen Gras }
17845c007436SBen Gras return 1;
17855c007436SBen Gras }
17865c007436SBen Gras
17875c007436SBen Gras #ifdef EXTENSIONS
17885c007436SBen Gras /* see if we can find out the user struct */
17895c007436SBen Gras static struct passwd *
17905c007436SBen Gras find_user_info(const char *name)
17915c007436SBen Gras {
17925c007436SBen Gras struct passwd *pwp;
17935c007436SBen Gras
17945c007436SBen Gras if ((pwp = getpwnam(name)) != NULL) {
17955c007436SBen Gras return pwp;
17965c007436SBen Gras }
17975c007436SBen Gras if (is_number(name) && (pwp = getpwuid((uid_t)atoi(name))) != NULL) {
17985c007436SBen Gras return pwp;
17995c007436SBen Gras }
18005c007436SBen Gras return NULL;
18015c007436SBen Gras }
18025c007436SBen Gras #endif
18035c007436SBen Gras
18045c007436SBen Gras /* see if we can find out the group struct */
18055c007436SBen Gras static struct group *
18065c007436SBen Gras find_group_info(const char *name)
18075c007436SBen Gras {
18085c007436SBen Gras struct group *grp;
18095c007436SBen Gras
18105c007436SBen Gras if ((grp = getgrnam(name)) != NULL) {
18115c007436SBen Gras return grp;
18125c007436SBen Gras }
18135c007436SBen Gras if (is_number(name) && (grp = getgrgid((gid_t)atoi(name))) != NULL) {
18145c007436SBen Gras return grp;
18155c007436SBen Gras }
18165c007436SBen Gras return NULL;
18175c007436SBen Gras }
18185c007436SBen Gras
18195c007436SBen Gras /* print out usage message, and then exit */
18205c007436SBen Gras void
18215c007436SBen Gras usermgmt_usage(const char *prog)
18225c007436SBen Gras {
18235c007436SBen Gras if (strcmp(prog, "useradd") == 0) {
18245c007436SBen Gras (void)fprintf(stderr, "usage: %s -D [-F] [-b base-dir] "
18255c007436SBen Gras "[-e expiry-time] [-f inactive-time]\n"
18265c007436SBen Gras "\t[-g gid | name | =uid] [-k skel-dir] [-L login-class]\n"
18275c007436SBen Gras "\t[-M homeperm] [-r lowuid..highuid] [-s shell]\n", prog);
18285c007436SBen Gras (void)fprintf(stderr, "usage: %s [-moSv] [-b base-dir] "
18295c007436SBen Gras "[-c comment] [-d home-dir] [-e expiry-time]\n"
18305c007436SBen Gras "\t[-f inactive-time] [-G secondary-group] "
18315c007436SBen Gras "[-g gid | name | =uid]\n"
18325c007436SBen Gras "\t[-k skeletondir] [-L login-class] [-M homeperm] "
18335c007436SBen Gras "[-p password]\n"
18345c007436SBen Gras "\t[-r lowuid..highuid] [-s shell] [-u uid] user\n",
18355c007436SBen Gras prog);
18365c007436SBen Gras } else if (strcmp(prog, "usermod") == 0) {
18375c007436SBen Gras (void)fprintf(stderr, "usage: %s [-FmoSv] [-C yes/no] "
18385c007436SBen Gras "[-c comment] [-d home-dir] [-e expiry-time]\n"
18395c007436SBen Gras "\t[-f inactive] [-G secondary-group] "
18405c007436SBen Gras "[-g gid | name | =uid]\n"
18415c007436SBen Gras "\t[-L login-class] [-l new-login] [-p password] "
18425c007436SBen Gras "[-s shell] [-u uid]\n"
18435c007436SBen Gras "\tuser\n", prog);
18445c007436SBen Gras } else if (strcmp(prog, "userdel") == 0) {
18455c007436SBen Gras (void)fprintf(stderr, "usage: %s -D [-p preserve-value]\n", prog);
18465c007436SBen Gras (void)fprintf(stderr,
18475c007436SBen Gras "usage: %s [-rSv] [-p preserve-value] user\n", prog);
18485c007436SBen Gras #ifdef EXTENSIONS
18495c007436SBen Gras } else if (strcmp(prog, "userinfo") == 0) {
18505c007436SBen Gras (void)fprintf(stderr, "usage: %s [-e] user\n", prog);
18515c007436SBen Gras #endif
18525c007436SBen Gras } else if (strcmp(prog, "groupadd") == 0) {
18535c007436SBen Gras (void)fprintf(stderr, "usage: %s [-ov] [-g gid]"
18545c007436SBen Gras " [-r lowgid..highgid] group\n", prog);
18555c007436SBen Gras } else if (strcmp(prog, "groupdel") == 0) {
18565c007436SBen Gras (void)fprintf(stderr, "usage: %s [-v] group\n", prog);
18575c007436SBen Gras } else if (strcmp(prog, "groupmod") == 0) {
18585c007436SBen Gras (void)fprintf(stderr,
18595c007436SBen Gras "usage: %s [-ov] [-g gid] [-n newname] group\n", prog);
18605c007436SBen Gras } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) {
18615c007436SBen Gras (void)fprintf(stderr,
18625c007436SBen Gras "usage: %s ( add | del | mod | info ) ...\n", prog);
18635c007436SBen Gras #ifdef EXTENSIONS
18645c007436SBen Gras } else if (strcmp(prog, "groupinfo") == 0) {
18655c007436SBen Gras (void)fprintf(stderr, "usage: %s [-ev] group\n", prog);
18665c007436SBen Gras #endif
18675c007436SBen Gras }
18685c007436SBen Gras exit(EXIT_FAILURE);
18695c007436SBen Gras /* NOTREACHED */
18705c007436SBen Gras }
18715c007436SBen Gras
18725c007436SBen Gras #ifdef EXTENSIONS
18735c007436SBen Gras #define ADD_OPT_EXTENSIONS "M:p:r:vL:S"
18745c007436SBen Gras #else
18755c007436SBen Gras #define ADD_OPT_EXTENSIONS
18765c007436SBen Gras #endif
18775c007436SBen Gras
18785c007436SBen Gras int
18795c007436SBen Gras useradd(int argc, char **argv)
18805c007436SBen Gras {
18815c007436SBen Gras def_t def;
18825c007436SBen Gras user_t *up = &def.user;
18835c007436SBen Gras int defaultfield;
18845c007436SBen Gras int bigD;
18855c007436SBen Gras int c;
18865c007436SBen Gras #ifdef EXTENSIONS
18875c007436SBen Gras int i;
18885c007436SBen Gras #endif
18895c007436SBen Gras
18905c007436SBen Gras read_defaults(&def);
18915c007436SBen Gras up->u_uid = -1;
18925c007436SBen Gras defaultfield = bigD = 0;
18935c007436SBen Gras while ((c = getopt(argc, argv, "DFG:b:c:d:e:f:g:k:mou:s:"
18945c007436SBen Gras ADD_OPT_EXTENSIONS)) != -1) {
18955c007436SBen Gras switch(c) {
18965c007436SBen Gras case 'D':
18975c007436SBen Gras bigD = 1;
18985c007436SBen Gras break;
18995c007436SBen Gras case 'F':
19005c007436SBen Gras /*
19015c007436SBen Gras * Setting -1 will force the new user to
19025c007436SBen Gras * change their password as soon as they
19035c007436SBen Gras * next log in - passwd(5).
19045c007436SBen Gras */
19055c007436SBen Gras defaultfield = 1;
19065c007436SBen Gras memsave(&up->u_inactive, "-1", strlen("-1"));
19075c007436SBen Gras break;
19085c007436SBen Gras case 'G':
19095c007436SBen Gras while (up->u_groupc < NGROUPS_MAX &&
19105c007436SBen Gras (up->u_groupv[up->u_groupc] = strsep(&optarg, ",")) != NULL) {
19115c007436SBen Gras if (up->u_groupv[up->u_groupc][0] != 0) {
19125c007436SBen Gras up->u_groupc++;
19135c007436SBen Gras }
19145c007436SBen Gras }
19155c007436SBen Gras if (optarg != NULL) {
19165c007436SBen Gras warnx("Truncated list of secondary groups "
19175c007436SBen Gras "to %d entries", NGROUPS_MAX);
19185c007436SBen Gras }
19195c007436SBen Gras break;
19205c007436SBen Gras #ifdef EXTENSIONS
19215c007436SBen Gras case 'S':
19225c007436SBen Gras up->u_allow_samba = 1;
19235c007436SBen Gras break;
19245c007436SBen Gras #endif
19255c007436SBen Gras case 'b':
19265c007436SBen Gras defaultfield = 1;
19275c007436SBen Gras memsave(&up->u_basedir, optarg, strlen(optarg));
19285c007436SBen Gras break;
19295c007436SBen Gras case 'c':
19305c007436SBen Gras memsave(&up->u_comment, optarg, strlen(optarg));
19315c007436SBen Gras break;
19325c007436SBen Gras case 'd':
19335c007436SBen Gras memsave(&up->u_home, optarg, strlen(optarg));
19345c007436SBen Gras up->u_flags |= F_HOMEDIR;
19355c007436SBen Gras break;
19365c007436SBen Gras case 'e':
19375c007436SBen Gras defaultfield = 1;
19385c007436SBen Gras memsave(&up->u_expire, optarg, strlen(optarg));
19395c007436SBen Gras break;
19405c007436SBen Gras case 'f':
19415c007436SBen Gras defaultfield = 1;
19425c007436SBen Gras memsave(&up->u_inactive, optarg, strlen(optarg));
19435c007436SBen Gras break;
19445c007436SBen Gras case 'g':
19455c007436SBen Gras defaultfield = 1;
19465c007436SBen Gras memsave(&up->u_primgrp, optarg, strlen(optarg));
19475c007436SBen Gras break;
19485c007436SBen Gras case 'k':
19495c007436SBen Gras defaultfield = 1;
19505c007436SBen Gras memsave(&up->u_skeldir, optarg, strlen(optarg));
19515c007436SBen Gras break;
19525c007436SBen Gras #ifdef EXTENSIONS
19535c007436SBen Gras case 'L':
19545c007436SBen Gras defaultfield = 1;
19555c007436SBen Gras memsave(&up->u_class, optarg, strlen(optarg));
19565c007436SBen Gras break;
19575c007436SBen Gras #endif
19585c007436SBen Gras case 'm':
19595c007436SBen Gras up->u_flags |= F_MKDIR;
19605c007436SBen Gras break;
19615c007436SBen Gras #ifdef EXTENSIONS
19625c007436SBen Gras case 'M':
19635c007436SBen Gras defaultfield = 1;
19645c007436SBen Gras up->u_homeperm = strtoul(optarg, NULL, 8);
19655c007436SBen Gras break;
19665c007436SBen Gras #endif
19675c007436SBen Gras case 'o':
19685c007436SBen Gras up->u_flags |= F_DUPUID;
19695c007436SBen Gras break;
19705c007436SBen Gras #ifdef EXTENSIONS
19715c007436SBen Gras case 'p':
19725c007436SBen Gras memsave(&up->u_password, optarg, strlen(optarg));
19735c007436SBen Gras break;
19745c007436SBen Gras #endif
19755c007436SBen Gras #ifdef EXTENSIONS
19765c007436SBen Gras case 'r':
19775c007436SBen Gras defaultfield = 1;
19785c007436SBen Gras (void)save_range(&up->u_r, optarg);
19795c007436SBen Gras break;
19805c007436SBen Gras #endif
19815c007436SBen Gras case 's':
19825c007436SBen Gras up->u_flags |= F_SHELL;
19835c007436SBen Gras defaultfield = 1;
19845c007436SBen Gras memsave(&up->u_shell, optarg, strlen(optarg));
19855c007436SBen Gras break;
19865c007436SBen Gras case 'u':
19875c007436SBen Gras up->u_uid = check_numeric(optarg, "uid");
19885c007436SBen Gras break;
19895c007436SBen Gras #ifdef EXTENSIONS
19905c007436SBen Gras case 'v':
19915c007436SBen Gras verbose = 1;
19925c007436SBen Gras break;
19935c007436SBen Gras #endif
19945c007436SBen Gras default:
19955c007436SBen Gras usermgmt_usage("useradd");
19965c007436SBen Gras /* NOTREACHED */
19975c007436SBen Gras }
19985c007436SBen Gras }
19995c007436SBen Gras if (bigD) {
20005c007436SBen Gras if (defaultfield) {
20015c007436SBen Gras checkeuid();
20025c007436SBen Gras return setdefaults(up) ? EXIT_SUCCESS : EXIT_FAILURE;
20035c007436SBen Gras }
20045c007436SBen Gras (void)printf("group\t\t%s\n", up->u_primgrp);
20055c007436SBen Gras (void)printf("base_dir\t%s\n", up->u_basedir);
20065c007436SBen Gras (void)printf("skel_dir\t%s\n", up->u_skeldir);
20075c007436SBen Gras (void)printf("shell\t\t%s\n", up->u_shell);
20085c007436SBen Gras #ifdef EXTENSIONS
20095c007436SBen Gras (void)printf("class\t\t%s\n", up->u_class);
20105c007436SBen Gras (void)printf("homeperm\t0%o\n", up->u_homeperm);
20115c007436SBen Gras #endif
20125c007436SBen Gras (void)printf("inactive\t%s\n", (up->u_inactive == NULL) ?
20135c007436SBen Gras UNSET_INACTIVE : up->u_inactive);
20145c007436SBen Gras (void)printf("expire\t\t%s\n", (up->u_expire == NULL) ?
20155c007436SBen Gras UNSET_EXPIRY : up->u_expire);
20165c007436SBen Gras #ifdef EXTENSIONS
20175c007436SBen Gras for (i = 0 ; i < up->u_rc ; i++) {
20185c007436SBen Gras (void)printf("range\t\t%d..%d\n",
20195c007436SBen Gras up->u_rv[i].r_from, up->u_rv[i].r_to);
20205c007436SBen Gras }
20215c007436SBen Gras #endif
20225c007436SBen Gras return EXIT_SUCCESS;
20235c007436SBen Gras }
20245c007436SBen Gras argc -= optind;
20255c007436SBen Gras argv += optind;
20265c007436SBen Gras if (argc != 1) {
20275c007436SBen Gras usermgmt_usage("useradd");
20285c007436SBen Gras }
20295c007436SBen Gras checkeuid();
20305c007436SBen Gras openlog("useradd", LOG_PID, LOG_USER);
20315c007436SBen Gras return adduser(*argv, up) ? EXIT_SUCCESS : EXIT_FAILURE;
20325c007436SBen Gras }
20335c007436SBen Gras
20345c007436SBen Gras #ifdef EXTENSIONS
20355c007436SBen Gras #define MOD_OPT_EXTENSIONS "p:vL:S"
20365c007436SBen Gras #else
20375c007436SBen Gras #define MOD_OPT_EXTENSIONS
20385c007436SBen Gras #endif
20395c007436SBen Gras
20405c007436SBen Gras int
20415c007436SBen Gras usermod(int argc, char **argv)
20425c007436SBen Gras {
20435c007436SBen Gras def_t def;
20445c007436SBen Gras user_t *up = &def.user;
20455c007436SBen Gras char newuser[MaxUserNameLen + 1];
20465c007436SBen Gras int c, have_new_user;
20475c007436SBen Gras
20485c007436SBen Gras (void)memset(newuser, 0, sizeof(newuser));
20495c007436SBen Gras read_defaults(&def);
20505c007436SBen Gras have_new_user = 0;
20515c007436SBen Gras up->u_locked = -1;
20525c007436SBen Gras while ((c = getopt(argc, argv, "C:FG:c:d:e:f:g:l:mos:u:"
20535c007436SBen Gras MOD_OPT_EXTENSIONS)) != -1) {
20545c007436SBen Gras switch(c) {
20555c007436SBen Gras case 'G':
20565c007436SBen Gras while (up->u_groupc < NGROUPS_MAX &&
20575c007436SBen Gras (up->u_groupv[up->u_groupc] =
20585c007436SBen Gras strsep(&optarg, ",")) != NULL) {
20595c007436SBen Gras if (up->u_groupv[up->u_groupc][0] != 0) {
20605c007436SBen Gras up->u_groupc++;
20615c007436SBen Gras }
20625c007436SBen Gras }
20635c007436SBen Gras if (optarg != NULL) {
20645c007436SBen Gras warnx("Truncated list of secondary groups "
20655c007436SBen Gras "to %d entries", NGROUPS_MAX);
20665c007436SBen Gras }
20675c007436SBen Gras up->u_flags |= F_SECGROUP;
20685c007436SBen Gras break;
20695c007436SBen Gras #ifdef EXTENSIONS
20705c007436SBen Gras case 'S':
20715c007436SBen Gras up->u_allow_samba = 1;
20725c007436SBen Gras break;
20735c007436SBen Gras #endif
20745c007436SBen Gras case 'c':
20755c007436SBen Gras memsave(&up->u_comment, optarg, strlen(optarg));
20765c007436SBen Gras up->u_flags |= F_COMMENT;
20775c007436SBen Gras break;
20785c007436SBen Gras case 'C':
20795c007436SBen Gras if (strcasecmp(optarg, "yes") == 0) {
20805c007436SBen Gras up->u_locked = LOCK;
20815c007436SBen Gras } else if (strcasecmp(optarg, "no") == 0) {
20825c007436SBen Gras up->u_locked = UNLOCK;
20835c007436SBen Gras } else {
20845c007436SBen Gras /* No idea. */
20855c007436SBen Gras errx(EXIT_FAILURE,
20865c007436SBen Gras "Please type 'yes' or 'no'");
20875c007436SBen Gras }
20885c007436SBen Gras break;
20895c007436SBen Gras case 'F':
20905c007436SBen Gras memsave(&up->u_inactive, "-1", strlen("-1"));
20915c007436SBen Gras up->u_flags |= F_INACTIVE;
20925c007436SBen Gras break;
20935c007436SBen Gras case 'd':
20945c007436SBen Gras memsave(&up->u_home, optarg, strlen(optarg));
20955c007436SBen Gras up->u_flags |= F_HOMEDIR;
20965c007436SBen Gras break;
20975c007436SBen Gras case 'e':
20985c007436SBen Gras memsave(&up->u_expire, optarg, strlen(optarg));
20995c007436SBen Gras up->u_flags |= F_EXPIRE;
21005c007436SBen Gras break;
21015c007436SBen Gras case 'f':
21025c007436SBen Gras memsave(&up->u_inactive, optarg, strlen(optarg));
21035c007436SBen Gras up->u_flags |= F_INACTIVE;
21045c007436SBen Gras break;
21055c007436SBen Gras case 'g':
21065c007436SBen Gras memsave(&up->u_primgrp, optarg, strlen(optarg));
21075c007436SBen Gras up->u_flags |= F_GROUP;
21085c007436SBen Gras break;
21095c007436SBen Gras case 'l':
21105c007436SBen Gras (void)strlcpy(newuser, optarg, sizeof(newuser));
21115c007436SBen Gras have_new_user = 1;
21125c007436SBen Gras up->u_flags |= F_USERNAME;
21135c007436SBen Gras break;
21145c007436SBen Gras #ifdef EXTENSIONS
21155c007436SBen Gras case 'L':
21165c007436SBen Gras memsave(&up->u_class, optarg, strlen(optarg));
21175c007436SBen Gras up->u_flags |= F_CLASS;
21185c007436SBen Gras break;
21195c007436SBen Gras #endif
21205c007436SBen Gras case 'm':
21215c007436SBen Gras up->u_flags |= F_MKDIR;
21225c007436SBen Gras break;
21235c007436SBen Gras case 'o':
21245c007436SBen Gras up->u_flags |= F_DUPUID;
21255c007436SBen Gras break;
21265c007436SBen Gras #ifdef EXTENSIONS
21275c007436SBen Gras case 'p':
21285c007436SBen Gras memsave(&up->u_password, optarg, strlen(optarg));
21295c007436SBen Gras up->u_flags |= F_PASSWORD;
21305c007436SBen Gras break;
21315c007436SBen Gras #endif
21325c007436SBen Gras case 's':
21335c007436SBen Gras memsave(&up->u_shell, optarg, strlen(optarg));
21345c007436SBen Gras up->u_flags |= F_SHELL;
21355c007436SBen Gras break;
21365c007436SBen Gras case 'u':
21375c007436SBen Gras up->u_uid = check_numeric(optarg, "uid");
21385c007436SBen Gras up->u_flags |= F_UID;
21395c007436SBen Gras break;
21405c007436SBen Gras #ifdef EXTENSIONS
21415c007436SBen Gras case 'v':
21425c007436SBen Gras verbose = 1;
21435c007436SBen Gras break;
21445c007436SBen Gras #endif
21455c007436SBen Gras default:
21465c007436SBen Gras usermgmt_usage("usermod");
21475c007436SBen Gras /* NOTREACHED */
21485c007436SBen Gras }
21495c007436SBen Gras }
21505c007436SBen Gras if ((up->u_flags & F_MKDIR) && !(up->u_flags & F_HOMEDIR) &&
21515c007436SBen Gras !(up->u_flags & F_USERNAME)) {
21525c007436SBen Gras warnx("Option 'm' useless without 'd' or 'l' -- ignored");
21535c007436SBen Gras up->u_flags &= ~F_MKDIR;
21545c007436SBen Gras }
21555c007436SBen Gras argc -= optind;
21565c007436SBen Gras argv += optind;
21575c007436SBen Gras if (argc != 1) {
21585c007436SBen Gras usermgmt_usage("usermod");
21595c007436SBen Gras }
21605c007436SBen Gras checkeuid();
21615c007436SBen Gras openlog("usermod", LOG_PID, LOG_USER);
21625c007436SBen Gras return moduser(*argv, (have_new_user) ? newuser : *argv, up,
21635c007436SBen Gras up->u_allow_samba) ? EXIT_SUCCESS : EXIT_FAILURE;
21645c007436SBen Gras }
21655c007436SBen Gras
21665c007436SBen Gras #ifdef EXTENSIONS
21675c007436SBen Gras #define DEL_OPT_EXTENSIONS "Dp:vS"
21685c007436SBen Gras #else
21695c007436SBen Gras #define DEL_OPT_EXTENSIONS
21705c007436SBen Gras #endif
21715c007436SBen Gras
21725c007436SBen Gras int
21735c007436SBen Gras userdel(int argc, char **argv)
21745c007436SBen Gras {
21755c007436SBen Gras struct passwd *pwp;
21765c007436SBen Gras def_t def;
21775c007436SBen Gras user_t *up = &def.user;
21785c007436SBen Gras char password[PasswordLength + 1];
21795c007436SBen Gras int defaultfield;
21805c007436SBen Gras int rmhome;
21815c007436SBen Gras int bigD;
21825c007436SBen Gras int c;
21835c007436SBen Gras
21845c007436SBen Gras read_defaults(&def);
21855c007436SBen Gras defaultfield = bigD = rmhome = 0;
21865c007436SBen Gras while ((c = getopt(argc, argv, "r" DEL_OPT_EXTENSIONS)) != -1) {
21875c007436SBen Gras switch(c) {
21885c007436SBen Gras #ifdef EXTENSIONS
21895c007436SBen Gras case 'D':
21905c007436SBen Gras bigD = 1;
21915c007436SBen Gras break;
21925c007436SBen Gras #endif
21935c007436SBen Gras #ifdef EXTENSIONS
21945c007436SBen Gras case 'S':
21955c007436SBen Gras up->u_allow_samba = 1;
21965c007436SBen Gras break;
21975c007436SBen Gras #endif
21985c007436SBen Gras #ifdef EXTENSIONS
21995c007436SBen Gras case 'p':
22005c007436SBen Gras defaultfield = 1;
22015c007436SBen Gras up->u_preserve = (strcmp(optarg, "true") == 0) ? 1 :
22025c007436SBen Gras (strcmp(optarg, "yes") == 0) ? 1 :
22035c007436SBen Gras atoi(optarg);
22045c007436SBen Gras break;
22055c007436SBen Gras #endif
22065c007436SBen Gras case 'r':
22075c007436SBen Gras rmhome = 1;
22085c007436SBen Gras break;
22095c007436SBen Gras #ifdef EXTENSIONS
22105c007436SBen Gras case 'v':
22115c007436SBen Gras verbose = 1;
22125c007436SBen Gras break;
22135c007436SBen Gras #endif
22145c007436SBen Gras default:
22155c007436SBen Gras usermgmt_usage("userdel");
22165c007436SBen Gras /* NOTREACHED */
22175c007436SBen Gras }
22185c007436SBen Gras }
22195c007436SBen Gras #ifdef EXTENSIONS
22205c007436SBen Gras if (bigD) {
22215c007436SBen Gras if (defaultfield) {
22225c007436SBen Gras checkeuid();
22235c007436SBen Gras return setdefaults(up) ? EXIT_SUCCESS : EXIT_FAILURE;
22245c007436SBen Gras }
22255c007436SBen Gras (void)printf("preserve\t%s\n", (up->u_preserve) ? "true" :
22265c007436SBen Gras "false");
22275c007436SBen Gras return EXIT_SUCCESS;
22285c007436SBen Gras }
22295c007436SBen Gras #endif
22305c007436SBen Gras argc -= optind;
22315c007436SBen Gras argv += optind;
22325c007436SBen Gras if (argc != 1) {
22335c007436SBen Gras usermgmt_usage("userdel");
22345c007436SBen Gras }
22355c007436SBen Gras checkeuid();
22365c007436SBen Gras if ((pwp = getpwnam(*argv)) == NULL) {
22375c007436SBen Gras warnx("No such user `%s'", *argv);
22385c007436SBen Gras return EXIT_FAILURE;
22395c007436SBen Gras }
22405c007436SBen Gras if (rmhome) {
22415c007436SBen Gras (void)removehomedir(pwp);
22425c007436SBen Gras }
22435c007436SBen Gras if (up->u_preserve) {
22445c007436SBen Gras up->u_flags |= F_SHELL;
22453eaea2dcSLionel Sambuc memsave(&up->u_shell, _PATH_SBIN_NOLOGIN,
22463eaea2dcSLionel Sambuc strlen(_PATH_SBIN_NOLOGIN));
22475c007436SBen Gras (void)memset(password, '*', DES_Len);
22485c007436SBen Gras password[DES_Len] = 0;
22495c007436SBen Gras memsave(&up->u_password, password, strlen(password));
22505c007436SBen Gras up->u_flags |= F_PASSWORD;
22515c007436SBen Gras openlog("userdel", LOG_PID, LOG_USER);
22525c007436SBen Gras return moduser(*argv, *argv, up, up->u_allow_samba) ?
22535c007436SBen Gras EXIT_SUCCESS : EXIT_FAILURE;
22545c007436SBen Gras }
22555c007436SBen Gras if (!rm_user_from_groups(*argv)) {
22565c007436SBen Gras return 0;
22575c007436SBen Gras }
22585c007436SBen Gras openlog("userdel", LOG_PID, LOG_USER);
22595c007436SBen Gras return moduser(*argv, *argv, NULL, up->u_allow_samba) ?
22605c007436SBen Gras EXIT_SUCCESS : EXIT_FAILURE;
22615c007436SBen Gras }
22625c007436SBen Gras
22635c007436SBen Gras #ifdef EXTENSIONS
22645c007436SBen Gras #define GROUP_ADD_OPT_EXTENSIONS "r:v"
22655c007436SBen Gras #else
22665c007436SBen Gras #define GROUP_ADD_OPT_EXTENSIONS
22675c007436SBen Gras #endif
22685c007436SBen Gras
22695c007436SBen Gras /* add a group */
22705c007436SBen Gras int
22715c007436SBen Gras groupadd(int argc, char **argv)
22725c007436SBen Gras {
22735c007436SBen Gras def_t def;
22745c007436SBen Gras group_t *gp = &def.group;
22755c007436SBen Gras int dupgid;
22765c007436SBen Gras int gid;
22775c007436SBen Gras int c;
22785c007436SBen Gras
22795c007436SBen Gras gid = -1;
22805c007436SBen Gras dupgid = 0;
22815c007436SBen Gras read_defaults(&def);
22825c007436SBen Gras while ((c = getopt(argc, argv, "g:o" GROUP_ADD_OPT_EXTENSIONS)) != -1) {
22835c007436SBen Gras switch(c) {
22845c007436SBen Gras case 'g':
22855c007436SBen Gras gid = check_numeric(optarg, "gid");
22865c007436SBen Gras break;
22875c007436SBen Gras case 'o':
22885c007436SBen Gras dupgid = 1;
22895c007436SBen Gras break;
22905c007436SBen Gras #ifdef EXTENSIONS
22915c007436SBen Gras case 'r':
22925c007436SBen Gras (void)save_range(&gp->g_r, optarg);
22935c007436SBen Gras break;
22945c007436SBen Gras case 'v':
22955c007436SBen Gras verbose = 1;
22965c007436SBen Gras break;
22975c007436SBen Gras #endif
22985c007436SBen Gras default:
22995c007436SBen Gras usermgmt_usage("groupadd");
23005c007436SBen Gras /* NOTREACHED */
23015c007436SBen Gras }
23025c007436SBen Gras }
23035c007436SBen Gras argc -= optind;
23045c007436SBen Gras argv += optind;
23055c007436SBen Gras if (argc != 1) {
23065c007436SBen Gras usermgmt_usage("groupadd");
23075c007436SBen Gras }
23085c007436SBen Gras if (gp->g_rc == 0) {
23095c007436SBen Gras gp->g_rv[gp->g_rc].r_from = DEF_LOWUID;
23105c007436SBen Gras gp->g_rv[gp->g_rc].r_to = DEF_HIGHUID;
23115c007436SBen Gras gp->g_rc += 1;
23125c007436SBen Gras }
23135c007436SBen Gras gp->g_defrc = gp->g_rc;
23145c007436SBen Gras checkeuid();
23155c007436SBen Gras if (gid == -1) {
23165c007436SBen Gras int got_id = 0;
23175c007436SBen Gras int i;
23185c007436SBen Gras
23195c007436SBen Gras /*
23205c007436SBen Gras * Look for a free GID in the command line ranges (if any).
23215c007436SBen Gras * These start after the ranges specified in the config file.
23225c007436SBen Gras */
23235c007436SBen Gras for (i = gp->g_defrc; !got_id && i < gp->g_rc ; i++) {
23245c007436SBen Gras got_id = getnextgid(&gid,
23255c007436SBen Gras gp->g_rv[i].r_from, gp->g_rv[i].r_to);
23265c007436SBen Gras }
23275c007436SBen Gras /*
23285c007436SBen Gras * If there were no free GIDs in the command line ranges,
23295c007436SBen Gras * try the ranges from the config file (there will always
23305c007436SBen Gras * be at least one default).
23315c007436SBen Gras */
23325c007436SBen Gras for (i = 0; !got_id && i < gp->g_defrc; i++) {
23335c007436SBen Gras got_id = getnextgid(&gid,
23345c007436SBen Gras gp->g_rv[i].r_from, gp->g_rv[i].r_to);
23355c007436SBen Gras }
23365c007436SBen Gras if (!got_id)
23375c007436SBen Gras errx(EXIT_FAILURE, "Can't add group: can't get next gid");
23385c007436SBen Gras }
23395c007436SBen Gras if (!dupgid && getgrgid((gid_t) gid) != NULL) {
23405c007436SBen Gras errx(EXIT_FAILURE, "Can't add group: gid %d is a duplicate",
23415c007436SBen Gras gid);
23425c007436SBen Gras }
23435c007436SBen Gras if (!valid_group(*argv)) {
23445c007436SBen Gras warnx("Invalid group name `%s'", *argv);
23455c007436SBen Gras }
23465c007436SBen Gras openlog("groupadd", LOG_PID, LOG_USER);
23475c007436SBen Gras if (!creategid(*argv, gid, ""))
23485c007436SBen Gras exit(EXIT_FAILURE);
23495c007436SBen Gras
23505c007436SBen Gras return EXIT_SUCCESS;
23515c007436SBen Gras }
23525c007436SBen Gras
23535c007436SBen Gras #ifdef EXTENSIONS
23545c007436SBen Gras #define GROUP_DEL_OPT_EXTENSIONS "v"
23555c007436SBen Gras #else
23565c007436SBen Gras #define GROUP_DEL_OPT_EXTENSIONS
23575c007436SBen Gras #endif
23585c007436SBen Gras
23595c007436SBen Gras /* remove a group */
23605c007436SBen Gras int
23615c007436SBen Gras groupdel(int argc, char **argv)
23625c007436SBen Gras {
23635c007436SBen Gras int c;
23645c007436SBen Gras
23655c007436SBen Gras while ((c = getopt(argc, argv, "" GROUP_DEL_OPT_EXTENSIONS)) != -1) {
23665c007436SBen Gras switch(c) {
23675c007436SBen Gras #ifdef EXTENSIONS
23685c007436SBen Gras case 'v':
23695c007436SBen Gras verbose = 1;
23705c007436SBen Gras break;
23715c007436SBen Gras #endif
23725c007436SBen Gras default:
23735c007436SBen Gras usermgmt_usage("groupdel");
23745c007436SBen Gras /* NOTREACHED */
23755c007436SBen Gras }
23765c007436SBen Gras }
23775c007436SBen Gras argc -= optind;
23785c007436SBen Gras argv += optind;
23795c007436SBen Gras if (argc != 1) {
23805c007436SBen Gras usermgmt_usage("groupdel");
23815c007436SBen Gras }
23825c007436SBen Gras if (getgrnam(*argv) == NULL) {
23835c007436SBen Gras errx(EXIT_FAILURE, "No such group `%s'", *argv);
23845c007436SBen Gras }
23855c007436SBen Gras checkeuid();
23865c007436SBen Gras openlog("groupdel", LOG_PID, LOG_USER);
23875c007436SBen Gras if (!modify_gid(*argv, NULL))
23885c007436SBen Gras exit(EXIT_FAILURE);
23895c007436SBen Gras
23905c007436SBen Gras return EXIT_SUCCESS;
23915c007436SBen Gras }
23925c007436SBen Gras
23935c007436SBen Gras #ifdef EXTENSIONS
23945c007436SBen Gras #define GROUP_MOD_OPT_EXTENSIONS "v"
23955c007436SBen Gras #else
23965c007436SBen Gras #define GROUP_MOD_OPT_EXTENSIONS
23975c007436SBen Gras #endif
23985c007436SBen Gras
23995c007436SBen Gras /* modify a group */
24005c007436SBen Gras int
24015c007436SBen Gras groupmod(int argc, char **argv)
24025c007436SBen Gras {
24035c007436SBen Gras struct group *grp;
24045c007436SBen Gras char buf[MaxEntryLen];
24055c007436SBen Gras char *newname;
24065c007436SBen Gras char **cpp;
24075c007436SBen Gras int dupgid;
24085c007436SBen Gras int gid;
24095c007436SBen Gras int cc;
24105c007436SBen Gras int c;
24115c007436SBen Gras
24125c007436SBen Gras gid = -1;
24135c007436SBen Gras dupgid = 0;
24145c007436SBen Gras newname = NULL;
24155c007436SBen Gras while ((c = getopt(argc, argv, "g:on:" GROUP_MOD_OPT_EXTENSIONS)) != -1) {
24165c007436SBen Gras switch(c) {
24175c007436SBen Gras case 'g':
24185c007436SBen Gras gid = check_numeric(optarg, "gid");
24195c007436SBen Gras break;
24205c007436SBen Gras case 'o':
24215c007436SBen Gras dupgid = 1;
24225c007436SBen Gras break;
24235c007436SBen Gras case 'n':
24245c007436SBen Gras memsave(&newname, optarg, strlen(optarg));
24255c007436SBen Gras break;
24265c007436SBen Gras #ifdef EXTENSIONS
24275c007436SBen Gras case 'v':
24285c007436SBen Gras verbose = 1;
24295c007436SBen Gras break;
24305c007436SBen Gras #endif
24315c007436SBen Gras default:
24325c007436SBen Gras usermgmt_usage("groupmod");
24335c007436SBen Gras /* NOTREACHED */
24345c007436SBen Gras }
24355c007436SBen Gras }
24365c007436SBen Gras argc -= optind;
24375c007436SBen Gras argv += optind;
24385c007436SBen Gras if (argc != 1) {
24395c007436SBen Gras usermgmt_usage("groupmod");
24405c007436SBen Gras }
24415c007436SBen Gras checkeuid();
24425c007436SBen Gras if (gid < 0 && newname == NULL) {
24435c007436SBen Gras errx(EXIT_FAILURE, "Nothing to change");
24445c007436SBen Gras }
24455c007436SBen Gras if (dupgid && gid < 0) {
24465c007436SBen Gras errx(EXIT_FAILURE, "Duplicate which gid?");
24475c007436SBen Gras }
24485c007436SBen Gras if (!dupgid && getgrgid((gid_t) gid) != NULL) {
24495c007436SBen Gras errx(EXIT_FAILURE, "Can't modify group: gid %d is a duplicate",
24505c007436SBen Gras gid);
24515c007436SBen Gras }
24525c007436SBen Gras if ((grp = find_group_info(*argv)) == NULL) {
24535c007436SBen Gras errx(EXIT_FAILURE, "Can't find group `%s' to modify", *argv);
24545c007436SBen Gras }
24555c007436SBen Gras if (!is_local(*argv, _PATH_GROUP)) {
24565c007436SBen Gras errx(EXIT_FAILURE, "Group `%s' must be a local group", *argv);
24575c007436SBen Gras }
24585c007436SBen Gras if (newname != NULL && !valid_group(newname)) {
24595c007436SBen Gras warnx("Invalid group name `%s'", newname);
24605c007436SBen Gras }
24615c007436SBen Gras cc = snprintf(buf, sizeof(buf), "%s:%s:%d:",
24625c007436SBen Gras (newname) ? newname : grp->gr_name,
24635c007436SBen Gras grp->gr_passwd,
24645c007436SBen Gras (gid < 0) ? grp->gr_gid : gid);
24655c007436SBen Gras for (cpp = grp->gr_mem ; *cpp && cc < sizeof(buf) ; cpp++) {
24665c007436SBen Gras cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s%s", *cpp,
24675c007436SBen Gras (cpp[1] == NULL) ? "" : ",");
24685c007436SBen Gras }
24695c007436SBen Gras cc += snprintf(&buf[cc], sizeof(buf) - cc, "\n");
24705c007436SBen Gras if (newname != NULL)
24715c007436SBen Gras free(newname);
24725c007436SBen Gras openlog("groupmod", LOG_PID, LOG_USER);
24735c007436SBen Gras if (!modify_gid(*argv, buf))
24745c007436SBen Gras exit(EXIT_FAILURE);
24755c007436SBen Gras
24765c007436SBen Gras return EXIT_SUCCESS;
24775c007436SBen Gras }
24785c007436SBen Gras
24795c007436SBen Gras #ifdef EXTENSIONS
24805c007436SBen Gras /* display user information */
24815c007436SBen Gras int
24825c007436SBen Gras userinfo(int argc, char **argv)
24835c007436SBen Gras {
24845c007436SBen Gras struct passwd *pwp;
24855c007436SBen Gras struct group *grp;
24865c007436SBen Gras char buf[MaxEntryLen];
24875c007436SBen Gras char **cpp;
24885c007436SBen Gras int exists;
24895c007436SBen Gras int cc;
24905c007436SBen Gras int i;
24915c007436SBen Gras
24925c007436SBen Gras exists = 0;
24935c007436SBen Gras buf[0] = '\0';
24945c007436SBen Gras while ((i = getopt(argc, argv, "e")) != -1) {
24955c007436SBen Gras switch(i) {
24965c007436SBen Gras case 'e':
24975c007436SBen Gras exists = 1;
24985c007436SBen Gras break;
24995c007436SBen Gras default:
25005c007436SBen Gras usermgmt_usage("userinfo");
25015c007436SBen Gras /* NOTREACHED */
25025c007436SBen Gras }
25035c007436SBen Gras }
25045c007436SBen Gras argc -= optind;
25055c007436SBen Gras argv += optind;
25065c007436SBen Gras if (argc != 1) {
25075c007436SBen Gras usermgmt_usage("userinfo");
25085c007436SBen Gras }
25095c007436SBen Gras pwp = find_user_info(*argv);
25105c007436SBen Gras if (exists) {
25115c007436SBen Gras exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE);
25125c007436SBen Gras }
25135c007436SBen Gras if (pwp == NULL) {
25145c007436SBen Gras errx(EXIT_FAILURE, "Can't find user `%s'", *argv);
25155c007436SBen Gras }
25165c007436SBen Gras (void)printf("login\t%s\n", pwp->pw_name);
25175c007436SBen Gras (void)printf("passwd\t%s\n", pwp->pw_passwd);
25185c007436SBen Gras (void)printf("uid\t%d\n", pwp->pw_uid);
25195c007436SBen Gras for (cc = 0 ; (grp = getgrent()) != NULL ; ) {
25205c007436SBen Gras for (cpp = grp->gr_mem ; *cpp ; cpp++) {
25215c007436SBen Gras if (strcmp(*cpp, *argv) == 0 &&
25225c007436SBen Gras grp->gr_gid != pwp->pw_gid) {
25235c007436SBen Gras cc += snprintf(&buf[cc], sizeof(buf) - cc,
25245c007436SBen Gras "%s ", grp->gr_name);
25255c007436SBen Gras }
25265c007436SBen Gras }
25275c007436SBen Gras }
25285c007436SBen Gras if ((grp = getgrgid(pwp->pw_gid)) == NULL) {
25295c007436SBen Gras (void)printf("groups\t%d %s\n", pwp->pw_gid, buf);
25305c007436SBen Gras } else {
25315c007436SBen Gras (void)printf("groups\t%s %s\n", grp->gr_name, buf);
25325c007436SBen Gras }
25335c007436SBen Gras (void)printf("change\t%s", pwp->pw_change > 0 ?
25345c007436SBen Gras ctime(&pwp->pw_change) : pwp->pw_change == -1 ?
25355c007436SBen Gras "NEXT LOGIN\n" : "NEVER\n");
25365c007436SBen Gras (void)printf("class\t%s\n", pwp->pw_class);
25375c007436SBen Gras (void)printf("gecos\t%s\n", pwp->pw_gecos);
25385c007436SBen Gras (void)printf("dir\t%s\n", pwp->pw_dir);
25395c007436SBen Gras (void)printf("shell\t%s\n", pwp->pw_shell);
25405c007436SBen Gras (void)printf("expire\t%s", pwp->pw_expire ?
25415c007436SBen Gras ctime(&pwp->pw_expire) : "NEVER\n");
25425c007436SBen Gras return EXIT_SUCCESS;
25435c007436SBen Gras }
25445c007436SBen Gras #endif
25455c007436SBen Gras
25465c007436SBen Gras #ifdef EXTENSIONS
25475c007436SBen Gras /* display user information */
25485c007436SBen Gras int
25495c007436SBen Gras groupinfo(int argc, char **argv)
25505c007436SBen Gras {
25515c007436SBen Gras struct group *grp;
25525c007436SBen Gras char **cpp;
25535c007436SBen Gras int exists;
25545c007436SBen Gras int i;
25555c007436SBen Gras
25565c007436SBen Gras exists = 0;
25575c007436SBen Gras while ((i = getopt(argc, argv, "ev")) != -1) {
25585c007436SBen Gras switch(i) {
25595c007436SBen Gras case 'e':
25605c007436SBen Gras exists = 1;
25615c007436SBen Gras break;
25625c007436SBen Gras case 'v':
25635c007436SBen Gras verbose = 1;
25645c007436SBen Gras break;
25655c007436SBen Gras default:
25665c007436SBen Gras usermgmt_usage("groupinfo");
25675c007436SBen Gras /* NOTREACHED */
25685c007436SBen Gras }
25695c007436SBen Gras }
25705c007436SBen Gras argc -= optind;
25715c007436SBen Gras argv += optind;
25725c007436SBen Gras if (argc != 1) {
25735c007436SBen Gras usermgmt_usage("groupinfo");
25745c007436SBen Gras }
25755c007436SBen Gras grp = find_group_info(*argv);
25765c007436SBen Gras if (exists) {
25775c007436SBen Gras exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE);
25785c007436SBen Gras }
25795c007436SBen Gras if (grp == NULL) {
25805c007436SBen Gras errx(EXIT_FAILURE, "Can't find group `%s'", *argv);
25815c007436SBen Gras }
25825c007436SBen Gras (void)printf("name\t%s\n", grp->gr_name);
25835c007436SBen Gras (void)printf("passwd\t%s\n", grp->gr_passwd);
25845c007436SBen Gras (void)printf("gid\t%d\n", grp->gr_gid);
25855c007436SBen Gras (void)printf("members\t");
25865c007436SBen Gras for (cpp = grp->gr_mem ; *cpp ; cpp++) {
25875c007436SBen Gras (void)printf("%s", *cpp);
25885c007436SBen Gras if (*(cpp + 1)) {
25895c007436SBen Gras (void) printf(", ");
25905c007436SBen Gras }
25915c007436SBen Gras }
25925c007436SBen Gras (void)fputc('\n', stdout);
25935c007436SBen Gras return EXIT_SUCCESS;
25945c007436SBen Gras }
25955c007436SBen Gras #endif
2596