xref: /onnv-gate/usr/src/cmd/oamuser/user/usermod.c (revision 7729:ee9d712b45da)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54321Scasper  * Common Development and Distribution License (the "License").
64321Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*7729SJohn.Sonnenschein@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate 
300Sstevel@tonic-gate 
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <sys/stat.h>
330Sstevel@tonic-gate #include <sys/param.h>
340Sstevel@tonic-gate #include <stdio.h>
350Sstevel@tonic-gate #include <stdlib.h>
360Sstevel@tonic-gate #include <ctype.h>
370Sstevel@tonic-gate #include <limits.h>
380Sstevel@tonic-gate #include <string.h>
390Sstevel@tonic-gate #include <userdefs.h>
400Sstevel@tonic-gate #include <user_attr.h>
410Sstevel@tonic-gate #include <nss_dbdefs.h>
420Sstevel@tonic-gate #include <errno.h>
430Sstevel@tonic-gate #include <project.h>
440Sstevel@tonic-gate #include "users.h"
450Sstevel@tonic-gate #include "messages.h"
460Sstevel@tonic-gate #include "funcs.h"
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate  *  usermod [-u uid [-o] | -g group | -G group [[,group]...] | -d dir [-m]
500Sstevel@tonic-gate  *		| -s shell | -c comment | -l new_logname]
510Sstevel@tonic-gate  *		| -f inactive | -e expire ]
520Sstevel@tonic-gate  *		[ -A authorization [, authorization ...]]
530Sstevel@tonic-gate  *		[ -P profile [, profile ...]]
540Sstevel@tonic-gate  *		[ -R role [, role ...]]
550Sstevel@tonic-gate  *		[ -K key=value ]
560Sstevel@tonic-gate  *		[ -p project [, project]] login
570Sstevel@tonic-gate  *
580Sstevel@tonic-gate  *	This command adds new user logins to the system.  Arguments are:
590Sstevel@tonic-gate  *
600Sstevel@tonic-gate  *	uid - an integer less than MAXUID
610Sstevel@tonic-gate  *	group - an existing group's integer ID or char string name
620Sstevel@tonic-gate  *	dir - a directory
630Sstevel@tonic-gate  *	shell - a program to be used as a shell
640Sstevel@tonic-gate  *	comment - any text string
650Sstevel@tonic-gate  *	skel_dir - a directory
660Sstevel@tonic-gate  *	base_dir - a directory
670Sstevel@tonic-gate  *	rid - an integer less than 2**16 (USHORT)
680Sstevel@tonic-gate  *	login - a string of printable chars except colon (:)
690Sstevel@tonic-gate  *	inactive - number of days a login maybe inactive before it is locked
700Sstevel@tonic-gate  *	expire - date when a login is no longer valid
710Sstevel@tonic-gate  *	authorization - One or more comma separated authorizations defined
720Sstevel@tonic-gate  *			in auth_attr(4).
730Sstevel@tonic-gate  *	profile - One or more comma separated execution profiles defined
740Sstevel@tonic-gate  *		  in prof_attr(4)
750Sstevel@tonic-gate  *	role - One or more comma-separated role names defined in user_attr(4)
760Sstevel@tonic-gate  *	key=value - One or more -K options each specifying a valid user_attr(4)
770Sstevel@tonic-gate  *		attribute.
780Sstevel@tonic-gate  *
790Sstevel@tonic-gate  */
800Sstevel@tonic-gate 
810Sstevel@tonic-gate extern int **valid_lgroup(), isbusy();
820Sstevel@tonic-gate extern int valid_uid(), check_perm(), create_home(), move_dir();
830Sstevel@tonic-gate extern int valid_expire(), edit_group(), call_passmgmt();
840Sstevel@tonic-gate extern projid_t **valid_lproject();
850Sstevel@tonic-gate 
860Sstevel@tonic-gate static uid_t uid;		/* new uid */
870Sstevel@tonic-gate static gid_t gid;			/* gid of new login */
880Sstevel@tonic-gate static char *new_logname = NULL;	/* new login name with -l option */
890Sstevel@tonic-gate static char *uidstr = NULL;		/* uid from command line */
900Sstevel@tonic-gate static char *group = NULL;		/* group from command line */
910Sstevel@tonic-gate static char *grps = NULL;		/* multi groups from command line */
920Sstevel@tonic-gate static char *dir = NULL;		/* home dir from command line */
930Sstevel@tonic-gate static char *shell = NULL;		/* shell from command line */
940Sstevel@tonic-gate static char *comment = NULL;		/* comment from command line */
950Sstevel@tonic-gate static char *logname = NULL;		/* login name to add */
960Sstevel@tonic-gate static char *inactstr = NULL;		/* inactive from command line */
970Sstevel@tonic-gate static char *expire = NULL;		/* expiration date from command line */
980Sstevel@tonic-gate static char *projects = NULL;		/* project ids from command line */
990Sstevel@tonic-gate static char *usertype;
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate char *cmdname;
1020Sstevel@tonic-gate static char gidstring[32], uidstring[32];
1030Sstevel@tonic-gate char inactstring[10];
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate char *
strcpmalloc(str)1060Sstevel@tonic-gate strcpmalloc(str)
1070Sstevel@tonic-gate char *str;
1080Sstevel@tonic-gate {
1090Sstevel@tonic-gate 	if (str == NULL)
1100Sstevel@tonic-gate 		return (NULL);
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	return (strdup(str));
1130Sstevel@tonic-gate }
1140Sstevel@tonic-gate struct passwd *
passwd_cpmalloc(opw)1150Sstevel@tonic-gate passwd_cpmalloc(opw)
1160Sstevel@tonic-gate struct passwd *opw;
1170Sstevel@tonic-gate {
1180Sstevel@tonic-gate 	struct passwd *npw;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 	if (opw == NULL)
1210Sstevel@tonic-gate 		return (NULL);
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 	npw = malloc(sizeof (struct passwd));
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	npw->pw_name = strcpmalloc(opw->pw_name);
1270Sstevel@tonic-gate 	npw->pw_passwd = strcpmalloc(opw->pw_passwd);
1280Sstevel@tonic-gate 	npw->pw_uid = opw->pw_uid;
1290Sstevel@tonic-gate 	npw->pw_gid = opw->pw_gid;
1300Sstevel@tonic-gate 	npw->pw_age = strcpmalloc(opw->pw_age);
1310Sstevel@tonic-gate 	npw->pw_comment = strcpmalloc(opw->pw_comment);
1320Sstevel@tonic-gate 	npw->pw_gecos  = strcpmalloc(opw->pw_gecos);
1330Sstevel@tonic-gate 	npw->pw_dir = strcpmalloc(opw->pw_dir);
1340Sstevel@tonic-gate 	npw->pw_shell = strcpmalloc(opw->pw_shell);
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	return (npw);
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate int
main(argc,argv)1400Sstevel@tonic-gate main(argc, argv)
1410Sstevel@tonic-gate int argc;
1420Sstevel@tonic-gate char **argv;
1430Sstevel@tonic-gate {
1440Sstevel@tonic-gate 	int ch, ret = EX_SUCCESS, call_pass = 0, oflag = 0;
1450Sstevel@tonic-gate 	int tries, mflag = 0, inact, **gidlist, flag = 0;
1460Sstevel@tonic-gate 	boolean_t fail_if_busy = B_FALSE;
1470Sstevel@tonic-gate 	char *ptr;
1480Sstevel@tonic-gate 	struct passwd *pstruct;		/* password struct for login */
1490Sstevel@tonic-gate 	struct passwd *pw;
1500Sstevel@tonic-gate 	struct group *g_ptr;	/* validated group from -g */
1510Sstevel@tonic-gate 	struct stat statbuf;		/* status buffer for stat */
1520Sstevel@tonic-gate #ifndef att
1530Sstevel@tonic-gate 	FILE *pwf;		/* fille ptr for opened passwd file */
1540Sstevel@tonic-gate #endif
1550Sstevel@tonic-gate 	int warning;
1560Sstevel@tonic-gate 	projid_t **projlist;
1570Sstevel@tonic-gate 	char **nargv;			/* arguments for execvp of passmgmt */
1580Sstevel@tonic-gate 	int argindex;			/* argument index into nargv */
1590Sstevel@tonic-gate 	userattr_t *ua;
1600Sstevel@tonic-gate 	char *val;
1610Sstevel@tonic-gate 	int isrole;			/* current account is role */
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	cmdname = argv[0];
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	if (geteuid() != 0) {
1660Sstevel@tonic-gate 		errmsg(M_PERM_DENIED);
1670Sstevel@tonic-gate 		exit(EX_NO_PERM);
1680Sstevel@tonic-gate 	}
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	opterr = 0;			/* no print errors from getopt */
1710Sstevel@tonic-gate 	/* get user type based on the program name */
1720Sstevel@tonic-gate 	usertype = getusertype(argv[0]);
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	while ((ch = getopt(argc, argv,
1750Sstevel@tonic-gate 				"c:d:e:f:G:g:l:mop:s:u:A:P:R:K:")) != EOF)
1760Sstevel@tonic-gate 		switch (ch) {
1770Sstevel@tonic-gate 		case 'c':
1780Sstevel@tonic-gate 			comment = optarg;
1790Sstevel@tonic-gate 			flag++;
1800Sstevel@tonic-gate 			break;
1810Sstevel@tonic-gate 		case 'd':
1820Sstevel@tonic-gate 			dir = optarg;
1830Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
1840Sstevel@tonic-gate 			flag++;
1850Sstevel@tonic-gate 			break;
1860Sstevel@tonic-gate 		case 'e':
1870Sstevel@tonic-gate 			expire = optarg;
1880Sstevel@tonic-gate 			flag++;
1890Sstevel@tonic-gate 			break;
1900Sstevel@tonic-gate 		case 'f':
1910Sstevel@tonic-gate 			inactstr = optarg;
1920Sstevel@tonic-gate 			flag++;
1930Sstevel@tonic-gate 			break;
1940Sstevel@tonic-gate 		case 'G':
1950Sstevel@tonic-gate 			grps = optarg;
1960Sstevel@tonic-gate 			flag++;
1970Sstevel@tonic-gate 			break;
1980Sstevel@tonic-gate 		case 'g':
1990Sstevel@tonic-gate 			group = optarg;
2000Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
2010Sstevel@tonic-gate 			flag++;
2020Sstevel@tonic-gate 			break;
2030Sstevel@tonic-gate 		case 'l':
2040Sstevel@tonic-gate 			new_logname = optarg;
2050Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
2060Sstevel@tonic-gate 			flag++;
2070Sstevel@tonic-gate 			break;
2080Sstevel@tonic-gate 		case 'm':
2090Sstevel@tonic-gate 			mflag++;
2100Sstevel@tonic-gate 			flag++;
2110Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
2120Sstevel@tonic-gate 			break;
2130Sstevel@tonic-gate 		case 'o':
2140Sstevel@tonic-gate 			oflag++;
2150Sstevel@tonic-gate 			flag++;
2160Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
2170Sstevel@tonic-gate 			break;
2180Sstevel@tonic-gate 		case 'p':
2190Sstevel@tonic-gate 			projects = optarg;
2200Sstevel@tonic-gate 			flag++;
2210Sstevel@tonic-gate 			break;
2220Sstevel@tonic-gate 		case 's':
2230Sstevel@tonic-gate 			shell = optarg;
2240Sstevel@tonic-gate 			flag++;
2250Sstevel@tonic-gate 			break;
2260Sstevel@tonic-gate 		case 'u':
2270Sstevel@tonic-gate 			uidstr = optarg;
2280Sstevel@tonic-gate 			flag++;
2290Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
2300Sstevel@tonic-gate 			break;
2310Sstevel@tonic-gate 		case 'A':
2320Sstevel@tonic-gate 			change_key(USERATTR_AUTHS_KW, optarg);
2330Sstevel@tonic-gate 			flag++;
2340Sstevel@tonic-gate 			break;
2350Sstevel@tonic-gate 		case 'P':
2360Sstevel@tonic-gate 			change_key(USERATTR_PROFILES_KW, optarg);
2370Sstevel@tonic-gate 			flag++;
2380Sstevel@tonic-gate 			break;
2390Sstevel@tonic-gate 		case 'R':
2400Sstevel@tonic-gate 			change_key(USERATTR_ROLES_KW, optarg);
2410Sstevel@tonic-gate 			flag++;
2420Sstevel@tonic-gate 			break;
2430Sstevel@tonic-gate 		case 'K':
2440Sstevel@tonic-gate 			change_key(NULL, optarg);
2450Sstevel@tonic-gate 			flag++;
2460Sstevel@tonic-gate 			break;
2470Sstevel@tonic-gate 		default:
2480Sstevel@tonic-gate 		case '?':
2490Sstevel@tonic-gate 			if (is_role(usertype))
2500Sstevel@tonic-gate 				errmsg(M_MRUSAGE);
2510Sstevel@tonic-gate 			else
2520Sstevel@tonic-gate 				errmsg(M_MUSAGE);
2530Sstevel@tonic-gate 			exit(EX_SYNTAX);
2540Sstevel@tonic-gate 		}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	if (optind != argc - 1 || flag == 0) {
2570Sstevel@tonic-gate 		if (is_role(usertype))
2580Sstevel@tonic-gate 			errmsg(M_MRUSAGE);
2590Sstevel@tonic-gate 		else
2600Sstevel@tonic-gate 			errmsg(M_MUSAGE);
2610Sstevel@tonic-gate 		exit(EX_SYNTAX);
2620Sstevel@tonic-gate 	}
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	if ((!uidstr && oflag) || (mflag && !dir)) {
2650Sstevel@tonic-gate 		if (is_role(usertype))
2660Sstevel@tonic-gate 			errmsg(M_MRUSAGE);
2670Sstevel@tonic-gate 		else
2680Sstevel@tonic-gate 			errmsg(M_MUSAGE);
2690Sstevel@tonic-gate 		exit(EX_SYNTAX);
2700Sstevel@tonic-gate 	}
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	logname = argv[optind];
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/* Determine whether the account is a role or not */
2750Sstevel@tonic-gate 	if ((ua = getusernam(logname)) == NULL ||
2760Sstevel@tonic-gate 	    (val = kva_match(ua->attr, USERATTR_TYPE_KW)) == NULL ||
2770Sstevel@tonic-gate 	    strcmp(val, USERATTR_TYPE_NONADMIN_KW) != 0)
2780Sstevel@tonic-gate 		isrole = 0;
2790Sstevel@tonic-gate 	else
2800Sstevel@tonic-gate 		isrole = 1;
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	/* Verify that rolemod is used for roles and usermod for users */
2830Sstevel@tonic-gate 	if (isrole != is_role(usertype)) {
2840Sstevel@tonic-gate 		if (isrole)
2850Sstevel@tonic-gate 			errmsg(M_ISROLE);
2860Sstevel@tonic-gate 		else
2870Sstevel@tonic-gate 			errmsg(M_ISUSER);
2880Sstevel@tonic-gate 		exit(EX_SYNTAX);
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	/* Set the usertype key; defaults to the commandline  */
2920Sstevel@tonic-gate 	usertype = getsetdefval(USERATTR_TYPE_KW, usertype);
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	if (is_role(usertype)) {
2950Sstevel@tonic-gate 		/* Roles can't have roles */
2960Sstevel@tonic-gate 		if (getsetdefval(USERATTR_ROLES_KW, NULL) != NULL) {
2970Sstevel@tonic-gate 			errmsg(M_MRUSAGE);
2980Sstevel@tonic-gate 			exit(EX_SYNTAX);
2990Sstevel@tonic-gate 		}
3000Sstevel@tonic-gate 		/* If it was an ordinary user, delete its roles */
3010Sstevel@tonic-gate 		if (!isrole)
3020Sstevel@tonic-gate 			change_key(USERATTR_ROLES_KW, "");
3030Sstevel@tonic-gate 	}
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate #ifdef att
3060Sstevel@tonic-gate 	pw = getpwnam(logname);
3070Sstevel@tonic-gate #else
3080Sstevel@tonic-gate 	/*
3090Sstevel@tonic-gate 	 * Do this with fgetpwent to make sure we are only looking on local
3100Sstevel@tonic-gate 	 * system (since passmgmt only works on local system).
3110Sstevel@tonic-gate 	 */
3120Sstevel@tonic-gate 	if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
3130Sstevel@tonic-gate 		errmsg(M_OOPS, "open", "/etc/passwd");
3140Sstevel@tonic-gate 		exit(EX_FAILURE);
3150Sstevel@tonic-gate 	}
3160Sstevel@tonic-gate 	while ((pw = fgetpwent(pwf)) != NULL)
3170Sstevel@tonic-gate 		if (strcmp(pw->pw_name, logname) == 0)
3180Sstevel@tonic-gate 			break;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	fclose(pwf);
3210Sstevel@tonic-gate #endif
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	if (pw == NULL) {
3240Sstevel@tonic-gate 		char		pwdb[NSS_BUFLEN_PASSWD];
3250Sstevel@tonic-gate 		struct passwd	pwd;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 		if (getpwnam_r(logname, &pwd, pwdb, sizeof (pwdb)) == NULL) {
3280Sstevel@tonic-gate 			/* This user does not exist. */
3290Sstevel@tonic-gate 			errmsg(M_EXIST, logname);
3300Sstevel@tonic-gate 			exit(EX_NAME_NOT_EXIST);
3310Sstevel@tonic-gate 		} else {
3320Sstevel@tonic-gate 			/* This user exists in non-local name service. */
3330Sstevel@tonic-gate 			errmsg(M_NONLOCAL, logname);
3340Sstevel@tonic-gate 			exit(EX_NOT_LOCAL);
3350Sstevel@tonic-gate 		}
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	pstruct = passwd_cpmalloc(pw);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	/*
3410Sstevel@tonic-gate 	 * We can't modify a logged in user if any of the following
3420Sstevel@tonic-gate 	 * are being changed:
3430Sstevel@tonic-gate 	 * uid (-u & -o), group (-g), home dir (-m), loginname (-l).
3440Sstevel@tonic-gate 	 * If none of those are specified it is okay to go ahead
3450Sstevel@tonic-gate 	 * some types of changes only take effect on next login, some
3460Sstevel@tonic-gate 	 * like authorisations and profiles take effect instantly.
3470Sstevel@tonic-gate 	 * One might think that -K type=role should require that the
3480Sstevel@tonic-gate 	 * user not be logged in, however this would make it very
3490Sstevel@tonic-gate 	 * difficult to make the root account a role using this command.
3500Sstevel@tonic-gate 	 */
3510Sstevel@tonic-gate 	if (isbusy(logname)) {
3520Sstevel@tonic-gate 		if (fail_if_busy) {
3530Sstevel@tonic-gate 			errmsg(M_BUSY, logname, "change");
3540Sstevel@tonic-gate 			exit(EX_BUSY);
3550Sstevel@tonic-gate 		}
3560Sstevel@tonic-gate 		warningmsg(WARN_LOGGED_IN, logname);
3570Sstevel@tonic-gate 	}
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	if (new_logname && strcmp(new_logname, logname)) {
3600Sstevel@tonic-gate 		switch (valid_login(new_logname, (struct passwd **)NULL,
3610Sstevel@tonic-gate 			&warning)) {
3620Sstevel@tonic-gate 		case INVALID:
3630Sstevel@tonic-gate 			errmsg(M_INVALID, new_logname, "login name");
3640Sstevel@tonic-gate 			exit(EX_BADARG);
3650Sstevel@tonic-gate 			/*NOTREACHED*/
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 		case NOTUNIQUE:
3680Sstevel@tonic-gate 			errmsg(M_USED, new_logname);
3690Sstevel@tonic-gate 			exit(EX_NAME_EXISTS);
3700Sstevel@tonic-gate 			/*NOTREACHED*/
3710Sstevel@tonic-gate 		default:
3720Sstevel@tonic-gate 			call_pass = 1;
3730Sstevel@tonic-gate 			break;
3740Sstevel@tonic-gate 		}
3750Sstevel@tonic-gate 		if (warning)
3760Sstevel@tonic-gate 			warningmsg(warning, logname);
3770Sstevel@tonic-gate 	}
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	if (uidstr) {
3800Sstevel@tonic-gate 		/* convert uidstr to integer */
3810Sstevel@tonic-gate 		errno = 0;
3820Sstevel@tonic-gate 		uid = (uid_t)strtol(uidstr, &ptr, (int)10);
3830Sstevel@tonic-gate 		if (*ptr || errno == ERANGE) {
3840Sstevel@tonic-gate 			errmsg(M_INVALID, uidstr, "user id");
3850Sstevel@tonic-gate 			exit(EX_BADARG);
3860Sstevel@tonic-gate 		}
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 		if (uid != pstruct->pw_uid) {
3890Sstevel@tonic-gate 			switch (valid_uid(uid, NULL)) {
3900Sstevel@tonic-gate 			case NOTUNIQUE:
3910Sstevel@tonic-gate 				if (!oflag) {
3920Sstevel@tonic-gate 					/* override not specified */
3930Sstevel@tonic-gate 					errmsg(M_UID_USED, uid);
3940Sstevel@tonic-gate 					exit(EX_ID_EXISTS);
3950Sstevel@tonic-gate 				}
3960Sstevel@tonic-gate 				break;
3970Sstevel@tonic-gate 			case RESERVED:
3980Sstevel@tonic-gate 				errmsg(M_RESERVED, uid);
3990Sstevel@tonic-gate 				break;
4000Sstevel@tonic-gate 			case TOOBIG:
4010Sstevel@tonic-gate 				errmsg(M_TOOBIG, "uid", uid);
4020Sstevel@tonic-gate 				exit(EX_BADARG);
4030Sstevel@tonic-gate 				break;
4040Sstevel@tonic-gate 			}
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 			call_pass = 1;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 		} else {
4090Sstevel@tonic-gate 			/* uid's the same, so don't change anything */
4100Sstevel@tonic-gate 			uidstr = NULL;
4110Sstevel@tonic-gate 			oflag = 0;
4120Sstevel@tonic-gate 		}
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	} else uid = pstruct->pw_uid;
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	if (group) {
4170Sstevel@tonic-gate 		switch (valid_group(group, &g_ptr, &warning)) {
4180Sstevel@tonic-gate 		case INVALID:
4190Sstevel@tonic-gate 			errmsg(M_INVALID, group, "group id");
4200Sstevel@tonic-gate 			exit(EX_BADARG);
4210Sstevel@tonic-gate 			/*NOTREACHED*/
4220Sstevel@tonic-gate 		case TOOBIG:
4230Sstevel@tonic-gate 			errmsg(M_TOOBIG, "gid", group);
4240Sstevel@tonic-gate 			exit(EX_BADARG);
4250Sstevel@tonic-gate 			/*NOTREACHED*/
4260Sstevel@tonic-gate 		case UNIQUE:
4270Sstevel@tonic-gate 			errmsg(M_GRP_NOTUSED, group);
4280Sstevel@tonic-gate 			exit(EX_NAME_NOT_EXIST);
4290Sstevel@tonic-gate 			/*NOTREACHED*/
4300Sstevel@tonic-gate 		case RESERVED:
4310Sstevel@tonic-gate 			gid = (gid_t)strtol(group, &ptr, (int)10);
4320Sstevel@tonic-gate 			errmsg(M_RESERVED_GID, gid);
4330Sstevel@tonic-gate 			break;
4340Sstevel@tonic-gate 		}
4350Sstevel@tonic-gate 		if (warning)
4360Sstevel@tonic-gate 			warningmsg(warning, group);
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 		if (g_ptr != NULL)
4390Sstevel@tonic-gate 			gid = g_ptr->gr_gid;
4400Sstevel@tonic-gate 		else
4410Sstevel@tonic-gate 			gid = pstruct->pw_gid;
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 		/* call passmgmt if gid is different, else ignore group */
4440Sstevel@tonic-gate 		if (gid != pstruct->pw_gid)
4450Sstevel@tonic-gate 			call_pass = 1;
4460Sstevel@tonic-gate 		else group = NULL;
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	} else gid = pstruct->pw_gid;
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	if (grps && *grps) {
4510Sstevel@tonic-gate 		if (!(gidlist = valid_lgroup(grps, gid)))
4520Sstevel@tonic-gate 			exit(EX_BADARG);
4530Sstevel@tonic-gate 	} else
4540Sstevel@tonic-gate 		gidlist = (int **)0;
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	if (projects && *projects) {
4570Sstevel@tonic-gate 		if (! (projlist = valid_lproject(projects)))
4580Sstevel@tonic-gate 			exit(EX_BADARG);
4590Sstevel@tonic-gate 	} else
4600Sstevel@tonic-gate 		projlist = (projid_t **)0;
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	if (dir) {
4630Sstevel@tonic-gate 		if (REL_PATH(dir)) {
4640Sstevel@tonic-gate 			errmsg(M_RELPATH, dir);
4650Sstevel@tonic-gate 			exit(EX_BADARG);
4660Sstevel@tonic-gate 		}
4670Sstevel@tonic-gate 		if (strcmp(pstruct->pw_dir, dir) == 0) {
4680Sstevel@tonic-gate 			/* home directory is the same so ignore dflag & mflag */
4690Sstevel@tonic-gate 			dir = NULL;
4700Sstevel@tonic-gate 			mflag = 0;
4710Sstevel@tonic-gate 		} else call_pass = 1;
4720Sstevel@tonic-gate 	}
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	if (mflag) {
4750Sstevel@tonic-gate 		if (stat(dir, &statbuf) == 0) {
4760Sstevel@tonic-gate 			/* Home directory exists */
4770Sstevel@tonic-gate 			if (check_perm(statbuf, pstruct->pw_uid,
4780Sstevel@tonic-gate 			    pstruct->pw_gid, S_IWOTH|S_IXOTH) != 0) {
4790Sstevel@tonic-gate 				errmsg(M_NO_PERM, logname, dir);
4800Sstevel@tonic-gate 				exit(EX_NO_PERM);
4810Sstevel@tonic-gate 			}
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 		} else ret = create_home(dir, NULL, uid, gid);
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 		if (ret == EX_SUCCESS)
4860Sstevel@tonic-gate 			ret = move_dir(pstruct->pw_dir, dir, logname);
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 		if (ret != EX_SUCCESS)
4890Sstevel@tonic-gate 			exit(ret);
4900Sstevel@tonic-gate 	}
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	if (shell) {
4930Sstevel@tonic-gate 		if (REL_PATH(shell)) {
4940Sstevel@tonic-gate 			errmsg(M_RELPATH, shell);
4950Sstevel@tonic-gate 			exit(EX_BADARG);
4960Sstevel@tonic-gate 		}
4970Sstevel@tonic-gate 		if (strcmp(pstruct->pw_shell, shell) == 0) {
4980Sstevel@tonic-gate 			/* ignore s option if shell is not different */
4990Sstevel@tonic-gate 			shell = NULL;
5000Sstevel@tonic-gate 		} else {
5010Sstevel@tonic-gate 			if (stat(shell, &statbuf) < 0 ||
5020Sstevel@tonic-gate 			    (statbuf.st_mode & S_IFMT) != S_IFREG ||
5030Sstevel@tonic-gate 			    (statbuf.st_mode & 0555) != 0555) {
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 				errmsg(M_INVALID, shell, "shell");
5060Sstevel@tonic-gate 				exit(EX_BADARG);
5070Sstevel@tonic-gate 			}
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 			call_pass = 1;
5100Sstevel@tonic-gate 		}
5110Sstevel@tonic-gate 	}
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	if (comment)
5140Sstevel@tonic-gate 		/* ignore comment if comment is not changed */
5150Sstevel@tonic-gate 		if (strcmp(pstruct->pw_comment, comment))
5160Sstevel@tonic-gate 			call_pass = 1;
5170Sstevel@tonic-gate 		else
5180Sstevel@tonic-gate 			comment = NULL;
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	/* inactive string is a positive integer */
5210Sstevel@tonic-gate 	if (inactstr) {
5220Sstevel@tonic-gate 		/* convert inactstr to integer */
5230Sstevel@tonic-gate 		inact = (int)strtol(inactstr, &ptr, 10);
5240Sstevel@tonic-gate 		if (*ptr || inact < 0) {
5250Sstevel@tonic-gate 			errmsg(M_INVALID, inactstr, "inactivity period");
5260Sstevel@tonic-gate 			exit(EX_BADARG);
5270Sstevel@tonic-gate 		}
5280Sstevel@tonic-gate 		call_pass = 1;
5290Sstevel@tonic-gate 	}
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	/* expiration string is a date, newer than today */
5320Sstevel@tonic-gate 	if (expire) {
5330Sstevel@tonic-gate 		if (*expire &&
5340Sstevel@tonic-gate 		    valid_expire(expire, (time_t *)0) == INVALID) {
5350Sstevel@tonic-gate 			errmsg(M_INVALID, expire, "expiration date");
5360Sstevel@tonic-gate 			exit(EX_BADARG);
5370Sstevel@tonic-gate 		}
5380Sstevel@tonic-gate 		call_pass = 1;
5390Sstevel@tonic-gate 	}
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	if (nkeys > 0)
5420Sstevel@tonic-gate 		call_pass = 1;
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	/* that's it for validations - now do the work */
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	if (grps) {
5470Sstevel@tonic-gate 		/* redefine login's supplentary group memberships */
5480Sstevel@tonic-gate 		ret = edit_group(logname, new_logname, gidlist, 1);
5490Sstevel@tonic-gate 		if (ret != EX_SUCCESS) {
5500Sstevel@tonic-gate 			errmsg(M_UPDATE, "modified");
5510Sstevel@tonic-gate 			exit(ret);
5520Sstevel@tonic-gate 		}
5530Sstevel@tonic-gate 	}
5540Sstevel@tonic-gate 	if (projects) {
5550Sstevel@tonic-gate 		ret = edit_project(logname, (char *)NULL, projlist, 0);
5560Sstevel@tonic-gate 		if (ret != EX_SUCCESS) {
5570Sstevel@tonic-gate 			errmsg(M_UPDATE, "modified");
5580Sstevel@tonic-gate 			exit(ret);
5590Sstevel@tonic-gate 		}
5600Sstevel@tonic-gate 	}
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	if (!call_pass) exit(ret);
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	/* only get to here if need to call passmgmt */
5660Sstevel@tonic-gate 	/* set up arguments to  passmgmt in nargv array */
5670Sstevel@tonic-gate 	nargv = malloc((30 + nkeys * 2) * sizeof (char *));
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	argindex = 0;
570*7729SJohn.Sonnenschein@Sun.COM 	nargv[argindex++] = PASSMGMT;
5710Sstevel@tonic-gate 	nargv[argindex++] = "-m";	/* modify */
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	if (comment) {	/* comment */
5740Sstevel@tonic-gate 		nargv[argindex++] = "-c";
5750Sstevel@tonic-gate 		nargv[argindex++] = comment;
5760Sstevel@tonic-gate 	}
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	if (dir) {
5790Sstevel@tonic-gate 		/* flags for home directory */
5800Sstevel@tonic-gate 		nargv[argindex++] = "-h";
5810Sstevel@tonic-gate 		nargv[argindex++] = dir;
5820Sstevel@tonic-gate 	}
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	if (group) {
5850Sstevel@tonic-gate 		/* set gid flag */
5860Sstevel@tonic-gate 		nargv[argindex++] = "-g";
5874321Scasper 		(void) sprintf(gidstring, "%u", gid);
5880Sstevel@tonic-gate 		nargv[argindex++] = gidstring;
5890Sstevel@tonic-gate 	}
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	if (shell) { 	/* shell */
5920Sstevel@tonic-gate 		nargv[argindex++] = "-s";
5930Sstevel@tonic-gate 		nargv[argindex++] = shell;
5940Sstevel@tonic-gate 	}
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 	if (inactstr) {
5970Sstevel@tonic-gate 		nargv[argindex++] = "-f";
5980Sstevel@tonic-gate 		nargv[argindex++] = inactstr;
5990Sstevel@tonic-gate 	}
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 	if (expire) {
6020Sstevel@tonic-gate 		nargv[argindex++] = "-e";
6030Sstevel@tonic-gate 		nargv[argindex++] = expire;
6040Sstevel@tonic-gate 	}
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 	if (uidstr) {	/* set uid flag */
6070Sstevel@tonic-gate 		nargv[argindex++] = "-u";
6084321Scasper 		(void) sprintf(uidstring, "%u", uid);
6090Sstevel@tonic-gate 		nargv[argindex++] = uidstring;
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	if (oflag) nargv[argindex++] = "-o";
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	if (new_logname) {	/* redefine login name */
6150Sstevel@tonic-gate 		nargv[argindex++] = "-l";
6160Sstevel@tonic-gate 		nargv[argindex++] = new_logname;
6170Sstevel@tonic-gate 	}
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	if (nkeys > 0)
6200Sstevel@tonic-gate 		addkey_args(nargv, &argindex);
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	/* finally - login name */
6230Sstevel@tonic-gate 	nargv[argindex++] = logname;
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	/* set the last to null */
6260Sstevel@tonic-gate 	nargv[argindex++] = NULL;
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	/* now call passmgmt */
6290Sstevel@tonic-gate 	ret = PEX_FAILED;
6300Sstevel@tonic-gate 	for (tries = 3; ret != PEX_SUCCESS && tries--; ) {
6310Sstevel@tonic-gate 		switch (ret = call_passmgmt(nargv)) {
6320Sstevel@tonic-gate 		case PEX_SUCCESS:
6330Sstevel@tonic-gate 		case PEX_BUSY:
6340Sstevel@tonic-gate 			break;
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 		case PEX_HOSED_FILES:
6370Sstevel@tonic-gate 			errmsg(M_HOSED_FILES);
6380Sstevel@tonic-gate 			exit(EX_INCONSISTENT);
6390Sstevel@tonic-gate 			break;
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 		case PEX_SYNTAX:
6420Sstevel@tonic-gate 		case PEX_BADARG:
6430Sstevel@tonic-gate 			/* should NEVER occur that passmgmt usage is wrong */
6440Sstevel@tonic-gate 			if (is_role(usertype))
6450Sstevel@tonic-gate 				errmsg(M_MRUSAGE);
6460Sstevel@tonic-gate 			else
6470Sstevel@tonic-gate 				errmsg(M_MUSAGE);
6480Sstevel@tonic-gate 			exit(EX_SYNTAX);
6490Sstevel@tonic-gate 			break;
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 		case PEX_BADUID:
6520Sstevel@tonic-gate 			/* uid in use - shouldn't happen print message anyway */
6530Sstevel@tonic-gate 			errmsg(M_UID_USED, uid);
6540Sstevel@tonic-gate 			exit(EX_ID_EXISTS);
6550Sstevel@tonic-gate 			break;
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 		case PEX_BADNAME:
6580Sstevel@tonic-gate 			/* invalid loname */
6590Sstevel@tonic-gate 			errmsg(M_USED, logname);
6600Sstevel@tonic-gate 			exit(EX_NAME_EXISTS);
6610Sstevel@tonic-gate 			break;
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 		default:
6640Sstevel@tonic-gate 			errmsg(M_UPDATE, "modified");
6650Sstevel@tonic-gate 			exit(ret);
6660Sstevel@tonic-gate 			break;
6670Sstevel@tonic-gate 		}
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 	if (tries == 0) {
6700Sstevel@tonic-gate 		errmsg(M_UPDATE, "modified");
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	exit(ret);
6740Sstevel@tonic-gate 	/*NOTREACHED*/
6750Sstevel@tonic-gate }
676