xref: /minix3/usr.sbin/user/user.c (revision 0a4059ee41df617a65d07c6cada400711de4540c)
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