xref: /onnv-gate/usr/src/cmd/oamuser/user/usermod.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include <sys/types.h>
35*0Sstevel@tonic-gate #include <sys/stat.h>
36*0Sstevel@tonic-gate #include <sys/param.h>
37*0Sstevel@tonic-gate #include <stdio.h>
38*0Sstevel@tonic-gate #include <stdlib.h>
39*0Sstevel@tonic-gate #include <ctype.h>
40*0Sstevel@tonic-gate #include <limits.h>
41*0Sstevel@tonic-gate #include <string.h>
42*0Sstevel@tonic-gate #include <userdefs.h>
43*0Sstevel@tonic-gate #include <user_attr.h>
44*0Sstevel@tonic-gate #include <nss_dbdefs.h>
45*0Sstevel@tonic-gate #include <errno.h>
46*0Sstevel@tonic-gate #include <project.h>
47*0Sstevel@tonic-gate #include "users.h"
48*0Sstevel@tonic-gate #include "messages.h"
49*0Sstevel@tonic-gate #include "funcs.h"
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate /*
52*0Sstevel@tonic-gate  *  usermod [-u uid [-o] | -g group | -G group [[,group]...] | -d dir [-m]
53*0Sstevel@tonic-gate  *		| -s shell | -c comment | -l new_logname]
54*0Sstevel@tonic-gate  *		| -f inactive | -e expire ]
55*0Sstevel@tonic-gate  *		[ -A authorization [, authorization ...]]
56*0Sstevel@tonic-gate  *		[ -P profile [, profile ...]]
57*0Sstevel@tonic-gate  *		[ -R role [, role ...]]
58*0Sstevel@tonic-gate  *		[ -K key=value ]
59*0Sstevel@tonic-gate  *		[ -p project [, project]] login
60*0Sstevel@tonic-gate  *
61*0Sstevel@tonic-gate  *	This command adds new user logins to the system.  Arguments are:
62*0Sstevel@tonic-gate  *
63*0Sstevel@tonic-gate  *	uid - an integer less than MAXUID
64*0Sstevel@tonic-gate  *	group - an existing group's integer ID or char string name
65*0Sstevel@tonic-gate  *	dir - a directory
66*0Sstevel@tonic-gate  *	shell - a program to be used as a shell
67*0Sstevel@tonic-gate  *	comment - any text string
68*0Sstevel@tonic-gate  *	skel_dir - a directory
69*0Sstevel@tonic-gate  *	base_dir - a directory
70*0Sstevel@tonic-gate  *	rid - an integer less than 2**16 (USHORT)
71*0Sstevel@tonic-gate  *	login - a string of printable chars except colon (:)
72*0Sstevel@tonic-gate  *	inactive - number of days a login maybe inactive before it is locked
73*0Sstevel@tonic-gate  *	expire - date when a login is no longer valid
74*0Sstevel@tonic-gate  *	authorization - One or more comma separated authorizations defined
75*0Sstevel@tonic-gate  *			in auth_attr(4).
76*0Sstevel@tonic-gate  *	profile - One or more comma separated execution profiles defined
77*0Sstevel@tonic-gate  *		  in prof_attr(4)
78*0Sstevel@tonic-gate  *	role - One or more comma-separated role names defined in user_attr(4)
79*0Sstevel@tonic-gate  *	key=value - One or more -K options each specifying a valid user_attr(4)
80*0Sstevel@tonic-gate  *		attribute.
81*0Sstevel@tonic-gate  *
82*0Sstevel@tonic-gate  */
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate extern int **valid_lgroup(), isbusy();
85*0Sstevel@tonic-gate extern int valid_uid(), check_perm(), create_home(), move_dir();
86*0Sstevel@tonic-gate extern int valid_expire(), edit_group(), call_passmgmt();
87*0Sstevel@tonic-gate extern projid_t **valid_lproject();
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate static uid_t uid;		/* new uid */
90*0Sstevel@tonic-gate static gid_t gid;			/* gid of new login */
91*0Sstevel@tonic-gate static char *new_logname = NULL;	/* new login name with -l option */
92*0Sstevel@tonic-gate static char *uidstr = NULL;		/* uid from command line */
93*0Sstevel@tonic-gate static char *group = NULL;		/* group from command line */
94*0Sstevel@tonic-gate static char *grps = NULL;		/* multi groups from command line */
95*0Sstevel@tonic-gate static char *dir = NULL;		/* home dir from command line */
96*0Sstevel@tonic-gate static char *shell = NULL;		/* shell from command line */
97*0Sstevel@tonic-gate static char *comment = NULL;		/* comment from command line */
98*0Sstevel@tonic-gate static char *logname = NULL;		/* login name to add */
99*0Sstevel@tonic-gate static char *inactstr = NULL;		/* inactive from command line */
100*0Sstevel@tonic-gate static char *expire = NULL;		/* expiration date from command line */
101*0Sstevel@tonic-gate static char *projects = NULL;		/* project ids from command line */
102*0Sstevel@tonic-gate static char *usertype;
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate char *cmdname;
105*0Sstevel@tonic-gate static char gidstring[32], uidstring[32];
106*0Sstevel@tonic-gate char inactstring[10];
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate char *
109*0Sstevel@tonic-gate strcpmalloc(str)
110*0Sstevel@tonic-gate char *str;
111*0Sstevel@tonic-gate {
112*0Sstevel@tonic-gate 	if (str == NULL)
113*0Sstevel@tonic-gate 		return (NULL);
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 	return (strdup(str));
116*0Sstevel@tonic-gate }
117*0Sstevel@tonic-gate struct passwd *
118*0Sstevel@tonic-gate passwd_cpmalloc(opw)
119*0Sstevel@tonic-gate struct passwd *opw;
120*0Sstevel@tonic-gate {
121*0Sstevel@tonic-gate 	struct passwd *npw;
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate 	if (opw == NULL)
124*0Sstevel@tonic-gate 		return (NULL);
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate 	npw = malloc(sizeof (struct passwd));
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 	npw->pw_name = strcpmalloc(opw->pw_name);
130*0Sstevel@tonic-gate 	npw->pw_passwd = strcpmalloc(opw->pw_passwd);
131*0Sstevel@tonic-gate 	npw->pw_uid = opw->pw_uid;
132*0Sstevel@tonic-gate 	npw->pw_gid = opw->pw_gid;
133*0Sstevel@tonic-gate 	npw->pw_age = strcpmalloc(opw->pw_age);
134*0Sstevel@tonic-gate 	npw->pw_comment = strcpmalloc(opw->pw_comment);
135*0Sstevel@tonic-gate 	npw->pw_gecos  = strcpmalloc(opw->pw_gecos);
136*0Sstevel@tonic-gate 	npw->pw_dir = strcpmalloc(opw->pw_dir);
137*0Sstevel@tonic-gate 	npw->pw_shell = strcpmalloc(opw->pw_shell);
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	return (npw);
140*0Sstevel@tonic-gate }
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate int
143*0Sstevel@tonic-gate main(argc, argv)
144*0Sstevel@tonic-gate int argc;
145*0Sstevel@tonic-gate char **argv;
146*0Sstevel@tonic-gate {
147*0Sstevel@tonic-gate 	int ch, ret = EX_SUCCESS, call_pass = 0, oflag = 0;
148*0Sstevel@tonic-gate 	int tries, mflag = 0, inact, **gidlist, flag = 0;
149*0Sstevel@tonic-gate 	boolean_t fail_if_busy = B_FALSE;
150*0Sstevel@tonic-gate 	char *ptr;
151*0Sstevel@tonic-gate 	struct passwd *pstruct;		/* password struct for login */
152*0Sstevel@tonic-gate 	struct passwd *pw;
153*0Sstevel@tonic-gate 	struct group *g_ptr;	/* validated group from -g */
154*0Sstevel@tonic-gate 	struct stat statbuf;		/* status buffer for stat */
155*0Sstevel@tonic-gate #ifndef att
156*0Sstevel@tonic-gate 	FILE *pwf;		/* fille ptr for opened passwd file */
157*0Sstevel@tonic-gate #endif
158*0Sstevel@tonic-gate 	int warning;
159*0Sstevel@tonic-gate 	projid_t **projlist;
160*0Sstevel@tonic-gate 	char **nargv;			/* arguments for execvp of passmgmt */
161*0Sstevel@tonic-gate 	int argindex;			/* argument index into nargv */
162*0Sstevel@tonic-gate 	userattr_t *ua;
163*0Sstevel@tonic-gate 	char *val;
164*0Sstevel@tonic-gate 	int isrole;			/* current account is role */
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	cmdname = argv[0];
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	if (geteuid() != 0) {
169*0Sstevel@tonic-gate 		errmsg(M_PERM_DENIED);
170*0Sstevel@tonic-gate 		exit(EX_NO_PERM);
171*0Sstevel@tonic-gate 	}
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 	opterr = 0;			/* no print errors from getopt */
174*0Sstevel@tonic-gate 	/* get user type based on the program name */
175*0Sstevel@tonic-gate 	usertype = getusertype(argv[0]);
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate 	while ((ch = getopt(argc, argv,
178*0Sstevel@tonic-gate 				"c:d:e:f:G:g:l:mop:s:u:A:P:R:K:")) != EOF)
179*0Sstevel@tonic-gate 		switch (ch) {
180*0Sstevel@tonic-gate 		case 'c':
181*0Sstevel@tonic-gate 			comment = optarg;
182*0Sstevel@tonic-gate 			flag++;
183*0Sstevel@tonic-gate 			break;
184*0Sstevel@tonic-gate 		case 'd':
185*0Sstevel@tonic-gate 			dir = optarg;
186*0Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
187*0Sstevel@tonic-gate 			flag++;
188*0Sstevel@tonic-gate 			break;
189*0Sstevel@tonic-gate 		case 'e':
190*0Sstevel@tonic-gate 			expire = optarg;
191*0Sstevel@tonic-gate 			flag++;
192*0Sstevel@tonic-gate 			break;
193*0Sstevel@tonic-gate 		case 'f':
194*0Sstevel@tonic-gate 			inactstr = optarg;
195*0Sstevel@tonic-gate 			flag++;
196*0Sstevel@tonic-gate 			break;
197*0Sstevel@tonic-gate 		case 'G':
198*0Sstevel@tonic-gate 			grps = optarg;
199*0Sstevel@tonic-gate 			flag++;
200*0Sstevel@tonic-gate 			break;
201*0Sstevel@tonic-gate 		case 'g':
202*0Sstevel@tonic-gate 			group = optarg;
203*0Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
204*0Sstevel@tonic-gate 			flag++;
205*0Sstevel@tonic-gate 			break;
206*0Sstevel@tonic-gate 		case 'l':
207*0Sstevel@tonic-gate 			new_logname = optarg;
208*0Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
209*0Sstevel@tonic-gate 			flag++;
210*0Sstevel@tonic-gate 			break;
211*0Sstevel@tonic-gate 		case 'm':
212*0Sstevel@tonic-gate 			mflag++;
213*0Sstevel@tonic-gate 			flag++;
214*0Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
215*0Sstevel@tonic-gate 			break;
216*0Sstevel@tonic-gate 		case 'o':
217*0Sstevel@tonic-gate 			oflag++;
218*0Sstevel@tonic-gate 			flag++;
219*0Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
220*0Sstevel@tonic-gate 			break;
221*0Sstevel@tonic-gate 		case 'p':
222*0Sstevel@tonic-gate 			projects = optarg;
223*0Sstevel@tonic-gate 			flag++;
224*0Sstevel@tonic-gate 			break;
225*0Sstevel@tonic-gate 		case 's':
226*0Sstevel@tonic-gate 			shell = optarg;
227*0Sstevel@tonic-gate 			flag++;
228*0Sstevel@tonic-gate 			break;
229*0Sstevel@tonic-gate 		case 'u':
230*0Sstevel@tonic-gate 			uidstr = optarg;
231*0Sstevel@tonic-gate 			flag++;
232*0Sstevel@tonic-gate 			fail_if_busy = B_TRUE;
233*0Sstevel@tonic-gate 			break;
234*0Sstevel@tonic-gate 		case 'A':
235*0Sstevel@tonic-gate 			change_key(USERATTR_AUTHS_KW, optarg);
236*0Sstevel@tonic-gate 			flag++;
237*0Sstevel@tonic-gate 			break;
238*0Sstevel@tonic-gate 		case 'P':
239*0Sstevel@tonic-gate 			change_key(USERATTR_PROFILES_KW, optarg);
240*0Sstevel@tonic-gate 			flag++;
241*0Sstevel@tonic-gate 			break;
242*0Sstevel@tonic-gate 		case 'R':
243*0Sstevel@tonic-gate 			change_key(USERATTR_ROLES_KW, optarg);
244*0Sstevel@tonic-gate 			flag++;
245*0Sstevel@tonic-gate 			break;
246*0Sstevel@tonic-gate 		case 'K':
247*0Sstevel@tonic-gate 			change_key(NULL, optarg);
248*0Sstevel@tonic-gate 			flag++;
249*0Sstevel@tonic-gate 			break;
250*0Sstevel@tonic-gate 		default:
251*0Sstevel@tonic-gate 		case '?':
252*0Sstevel@tonic-gate 			if (is_role(usertype))
253*0Sstevel@tonic-gate 				errmsg(M_MRUSAGE);
254*0Sstevel@tonic-gate 			else
255*0Sstevel@tonic-gate 				errmsg(M_MUSAGE);
256*0Sstevel@tonic-gate 			exit(EX_SYNTAX);
257*0Sstevel@tonic-gate 		}
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	if (optind != argc - 1 || flag == 0) {
260*0Sstevel@tonic-gate 		if (is_role(usertype))
261*0Sstevel@tonic-gate 			errmsg(M_MRUSAGE);
262*0Sstevel@tonic-gate 		else
263*0Sstevel@tonic-gate 			errmsg(M_MUSAGE);
264*0Sstevel@tonic-gate 		exit(EX_SYNTAX);
265*0Sstevel@tonic-gate 	}
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	if ((!uidstr && oflag) || (mflag && !dir)) {
268*0Sstevel@tonic-gate 		if (is_role(usertype))
269*0Sstevel@tonic-gate 			errmsg(M_MRUSAGE);
270*0Sstevel@tonic-gate 		else
271*0Sstevel@tonic-gate 			errmsg(M_MUSAGE);
272*0Sstevel@tonic-gate 		exit(EX_SYNTAX);
273*0Sstevel@tonic-gate 	}
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate 	logname = argv[optind];
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 	/* Determine whether the account is a role or not */
278*0Sstevel@tonic-gate 	if ((ua = getusernam(logname)) == NULL ||
279*0Sstevel@tonic-gate 	    (val = kva_match(ua->attr, USERATTR_TYPE_KW)) == NULL ||
280*0Sstevel@tonic-gate 	    strcmp(val, USERATTR_TYPE_NONADMIN_KW) != 0)
281*0Sstevel@tonic-gate 		isrole = 0;
282*0Sstevel@tonic-gate 	else
283*0Sstevel@tonic-gate 		isrole = 1;
284*0Sstevel@tonic-gate 
285*0Sstevel@tonic-gate 	/* Verify that rolemod is used for roles and usermod for users */
286*0Sstevel@tonic-gate 	if (isrole != is_role(usertype)) {
287*0Sstevel@tonic-gate 		if (isrole)
288*0Sstevel@tonic-gate 			errmsg(M_ISROLE);
289*0Sstevel@tonic-gate 		else
290*0Sstevel@tonic-gate 			errmsg(M_ISUSER);
291*0Sstevel@tonic-gate 		exit(EX_SYNTAX);
292*0Sstevel@tonic-gate 	}
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	/* Set the usertype key; defaults to the commandline  */
295*0Sstevel@tonic-gate 	usertype = getsetdefval(USERATTR_TYPE_KW, usertype);
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate 	if (is_role(usertype)) {
298*0Sstevel@tonic-gate 		/* Roles can't have roles */
299*0Sstevel@tonic-gate 		if (getsetdefval(USERATTR_ROLES_KW, NULL) != NULL) {
300*0Sstevel@tonic-gate 			errmsg(M_MRUSAGE);
301*0Sstevel@tonic-gate 			exit(EX_SYNTAX);
302*0Sstevel@tonic-gate 		}
303*0Sstevel@tonic-gate 		/* If it was an ordinary user, delete its roles */
304*0Sstevel@tonic-gate 		if (!isrole)
305*0Sstevel@tonic-gate 			change_key(USERATTR_ROLES_KW, "");
306*0Sstevel@tonic-gate 	}
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate #ifdef att
309*0Sstevel@tonic-gate 	pw = getpwnam(logname);
310*0Sstevel@tonic-gate #else
311*0Sstevel@tonic-gate 	/*
312*0Sstevel@tonic-gate 	 * Do this with fgetpwent to make sure we are only looking on local
313*0Sstevel@tonic-gate 	 * system (since passmgmt only works on local system).
314*0Sstevel@tonic-gate 	 */
315*0Sstevel@tonic-gate 	if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
316*0Sstevel@tonic-gate 		errmsg(M_OOPS, "open", "/etc/passwd");
317*0Sstevel@tonic-gate 		exit(EX_FAILURE);
318*0Sstevel@tonic-gate 	}
319*0Sstevel@tonic-gate 	while ((pw = fgetpwent(pwf)) != NULL)
320*0Sstevel@tonic-gate 		if (strcmp(pw->pw_name, logname) == 0)
321*0Sstevel@tonic-gate 			break;
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 	fclose(pwf);
324*0Sstevel@tonic-gate #endif
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate 	if (pw == NULL) {
327*0Sstevel@tonic-gate 		char		pwdb[NSS_BUFLEN_PASSWD];
328*0Sstevel@tonic-gate 		struct passwd	pwd;
329*0Sstevel@tonic-gate 
330*0Sstevel@tonic-gate 		if (getpwnam_r(logname, &pwd, pwdb, sizeof (pwdb)) == NULL) {
331*0Sstevel@tonic-gate 			/* This user does not exist. */
332*0Sstevel@tonic-gate 			errmsg(M_EXIST, logname);
333*0Sstevel@tonic-gate 			exit(EX_NAME_NOT_EXIST);
334*0Sstevel@tonic-gate 		} else {
335*0Sstevel@tonic-gate 			/* This user exists in non-local name service. */
336*0Sstevel@tonic-gate 			errmsg(M_NONLOCAL, logname);
337*0Sstevel@tonic-gate 			exit(EX_NOT_LOCAL);
338*0Sstevel@tonic-gate 		}
339*0Sstevel@tonic-gate 	}
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 	pstruct = passwd_cpmalloc(pw);
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 	/*
344*0Sstevel@tonic-gate 	 * We can't modify a logged in user if any of the following
345*0Sstevel@tonic-gate 	 * are being changed:
346*0Sstevel@tonic-gate 	 * uid (-u & -o), group (-g), home dir (-m), loginname (-l).
347*0Sstevel@tonic-gate 	 * If none of those are specified it is okay to go ahead
348*0Sstevel@tonic-gate 	 * some types of changes only take effect on next login, some
349*0Sstevel@tonic-gate 	 * like authorisations and profiles take effect instantly.
350*0Sstevel@tonic-gate 	 * One might think that -K type=role should require that the
351*0Sstevel@tonic-gate 	 * user not be logged in, however this would make it very
352*0Sstevel@tonic-gate 	 * difficult to make the root account a role using this command.
353*0Sstevel@tonic-gate 	 */
354*0Sstevel@tonic-gate 	if (isbusy(logname)) {
355*0Sstevel@tonic-gate 		if (fail_if_busy) {
356*0Sstevel@tonic-gate 			errmsg(M_BUSY, logname, "change");
357*0Sstevel@tonic-gate 			exit(EX_BUSY);
358*0Sstevel@tonic-gate 		}
359*0Sstevel@tonic-gate 		warningmsg(WARN_LOGGED_IN, logname);
360*0Sstevel@tonic-gate 	}
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate 	if (new_logname && strcmp(new_logname, logname)) {
363*0Sstevel@tonic-gate 		switch (valid_login(new_logname, (struct passwd **)NULL,
364*0Sstevel@tonic-gate 			&warning)) {
365*0Sstevel@tonic-gate 		case INVALID:
366*0Sstevel@tonic-gate 			errmsg(M_INVALID, new_logname, "login name");
367*0Sstevel@tonic-gate 			exit(EX_BADARG);
368*0Sstevel@tonic-gate 			/*NOTREACHED*/
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 		case NOTUNIQUE:
371*0Sstevel@tonic-gate 			errmsg(M_USED, new_logname);
372*0Sstevel@tonic-gate 			exit(EX_NAME_EXISTS);
373*0Sstevel@tonic-gate 			/*NOTREACHED*/
374*0Sstevel@tonic-gate 		default:
375*0Sstevel@tonic-gate 			call_pass = 1;
376*0Sstevel@tonic-gate 			break;
377*0Sstevel@tonic-gate 		}
378*0Sstevel@tonic-gate 		if (warning)
379*0Sstevel@tonic-gate 			warningmsg(warning, logname);
380*0Sstevel@tonic-gate 	}
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 	if (uidstr) {
383*0Sstevel@tonic-gate 		/* convert uidstr to integer */
384*0Sstevel@tonic-gate 		errno = 0;
385*0Sstevel@tonic-gate 		uid = (uid_t)strtol(uidstr, &ptr, (int)10);
386*0Sstevel@tonic-gate 		if (*ptr || errno == ERANGE) {
387*0Sstevel@tonic-gate 			errmsg(M_INVALID, uidstr, "user id");
388*0Sstevel@tonic-gate 			exit(EX_BADARG);
389*0Sstevel@tonic-gate 		}
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 		if (uid != pstruct->pw_uid) {
392*0Sstevel@tonic-gate 			switch (valid_uid(uid, NULL)) {
393*0Sstevel@tonic-gate 			case NOTUNIQUE:
394*0Sstevel@tonic-gate 				if (!oflag) {
395*0Sstevel@tonic-gate 					/* override not specified */
396*0Sstevel@tonic-gate 					errmsg(M_UID_USED, uid);
397*0Sstevel@tonic-gate 					exit(EX_ID_EXISTS);
398*0Sstevel@tonic-gate 				}
399*0Sstevel@tonic-gate 				break;
400*0Sstevel@tonic-gate 			case RESERVED:
401*0Sstevel@tonic-gate 				errmsg(M_RESERVED, uid);
402*0Sstevel@tonic-gate 				break;
403*0Sstevel@tonic-gate 			case TOOBIG:
404*0Sstevel@tonic-gate 				errmsg(M_TOOBIG, "uid", uid);
405*0Sstevel@tonic-gate 				exit(EX_BADARG);
406*0Sstevel@tonic-gate 				break;
407*0Sstevel@tonic-gate 			}
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 			call_pass = 1;
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 		} else {
412*0Sstevel@tonic-gate 			/* uid's the same, so don't change anything */
413*0Sstevel@tonic-gate 			uidstr = NULL;
414*0Sstevel@tonic-gate 			oflag = 0;
415*0Sstevel@tonic-gate 		}
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 	} else uid = pstruct->pw_uid;
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate 	if (group) {
420*0Sstevel@tonic-gate 		switch (valid_group(group, &g_ptr, &warning)) {
421*0Sstevel@tonic-gate 		case INVALID:
422*0Sstevel@tonic-gate 			errmsg(M_INVALID, group, "group id");
423*0Sstevel@tonic-gate 			exit(EX_BADARG);
424*0Sstevel@tonic-gate 			/*NOTREACHED*/
425*0Sstevel@tonic-gate 		case TOOBIG:
426*0Sstevel@tonic-gate 			errmsg(M_TOOBIG, "gid", group);
427*0Sstevel@tonic-gate 			exit(EX_BADARG);
428*0Sstevel@tonic-gate 			/*NOTREACHED*/
429*0Sstevel@tonic-gate 		case UNIQUE:
430*0Sstevel@tonic-gate 			errmsg(M_GRP_NOTUSED, group);
431*0Sstevel@tonic-gate 			exit(EX_NAME_NOT_EXIST);
432*0Sstevel@tonic-gate 			/*NOTREACHED*/
433*0Sstevel@tonic-gate 		case RESERVED:
434*0Sstevel@tonic-gate 			gid = (gid_t)strtol(group, &ptr, (int)10);
435*0Sstevel@tonic-gate 			errmsg(M_RESERVED_GID, gid);
436*0Sstevel@tonic-gate 			break;
437*0Sstevel@tonic-gate 		}
438*0Sstevel@tonic-gate 		if (warning)
439*0Sstevel@tonic-gate 			warningmsg(warning, group);
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate 		if (g_ptr != NULL)
442*0Sstevel@tonic-gate 			gid = g_ptr->gr_gid;
443*0Sstevel@tonic-gate 		else
444*0Sstevel@tonic-gate 			gid = pstruct->pw_gid;
445*0Sstevel@tonic-gate 
446*0Sstevel@tonic-gate 		/* call passmgmt if gid is different, else ignore group */
447*0Sstevel@tonic-gate 		if (gid != pstruct->pw_gid)
448*0Sstevel@tonic-gate 			call_pass = 1;
449*0Sstevel@tonic-gate 		else group = NULL;
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	} else gid = pstruct->pw_gid;
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	if (grps && *grps) {
454*0Sstevel@tonic-gate 		if (!(gidlist = valid_lgroup(grps, gid)))
455*0Sstevel@tonic-gate 			exit(EX_BADARG);
456*0Sstevel@tonic-gate 	} else
457*0Sstevel@tonic-gate 		gidlist = (int **)0;
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate 	if (projects && *projects) {
460*0Sstevel@tonic-gate 		if (! (projlist = valid_lproject(projects)))
461*0Sstevel@tonic-gate 			exit(EX_BADARG);
462*0Sstevel@tonic-gate 	} else
463*0Sstevel@tonic-gate 		projlist = (projid_t **)0;
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	if (dir) {
466*0Sstevel@tonic-gate 		if (REL_PATH(dir)) {
467*0Sstevel@tonic-gate 			errmsg(M_RELPATH, dir);
468*0Sstevel@tonic-gate 			exit(EX_BADARG);
469*0Sstevel@tonic-gate 		}
470*0Sstevel@tonic-gate 		if (strcmp(pstruct->pw_dir, dir) == 0) {
471*0Sstevel@tonic-gate 			/* home directory is the same so ignore dflag & mflag */
472*0Sstevel@tonic-gate 			dir = NULL;
473*0Sstevel@tonic-gate 			mflag = 0;
474*0Sstevel@tonic-gate 		} else call_pass = 1;
475*0Sstevel@tonic-gate 	}
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 	if (mflag) {
478*0Sstevel@tonic-gate 		if (stat(dir, &statbuf) == 0) {
479*0Sstevel@tonic-gate 			/* Home directory exists */
480*0Sstevel@tonic-gate 			if (check_perm(statbuf, pstruct->pw_uid,
481*0Sstevel@tonic-gate 			    pstruct->pw_gid, S_IWOTH|S_IXOTH) != 0) {
482*0Sstevel@tonic-gate 				errmsg(M_NO_PERM, logname, dir);
483*0Sstevel@tonic-gate 				exit(EX_NO_PERM);
484*0Sstevel@tonic-gate 			}
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 		} else ret = create_home(dir, NULL, uid, gid);
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate 		if (ret == EX_SUCCESS)
489*0Sstevel@tonic-gate 			ret = move_dir(pstruct->pw_dir, dir, logname);
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate 		if (ret != EX_SUCCESS)
492*0Sstevel@tonic-gate 			exit(ret);
493*0Sstevel@tonic-gate 	}
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 	if (shell) {
496*0Sstevel@tonic-gate 		if (REL_PATH(shell)) {
497*0Sstevel@tonic-gate 			errmsg(M_RELPATH, shell);
498*0Sstevel@tonic-gate 			exit(EX_BADARG);
499*0Sstevel@tonic-gate 		}
500*0Sstevel@tonic-gate 		if (strcmp(pstruct->pw_shell, shell) == 0) {
501*0Sstevel@tonic-gate 			/* ignore s option if shell is not different */
502*0Sstevel@tonic-gate 			shell = NULL;
503*0Sstevel@tonic-gate 		} else {
504*0Sstevel@tonic-gate 			if (stat(shell, &statbuf) < 0 ||
505*0Sstevel@tonic-gate 			    (statbuf.st_mode & S_IFMT) != S_IFREG ||
506*0Sstevel@tonic-gate 			    (statbuf.st_mode & 0555) != 0555) {
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 				errmsg(M_INVALID, shell, "shell");
509*0Sstevel@tonic-gate 				exit(EX_BADARG);
510*0Sstevel@tonic-gate 			}
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 			call_pass = 1;
513*0Sstevel@tonic-gate 		}
514*0Sstevel@tonic-gate 	}
515*0Sstevel@tonic-gate 
516*0Sstevel@tonic-gate 	if (comment)
517*0Sstevel@tonic-gate 		/* ignore comment if comment is not changed */
518*0Sstevel@tonic-gate 		if (strcmp(pstruct->pw_comment, comment))
519*0Sstevel@tonic-gate 			call_pass = 1;
520*0Sstevel@tonic-gate 		else
521*0Sstevel@tonic-gate 			comment = NULL;
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 	/* inactive string is a positive integer */
524*0Sstevel@tonic-gate 	if (inactstr) {
525*0Sstevel@tonic-gate 		/* convert inactstr to integer */
526*0Sstevel@tonic-gate 		inact = (int)strtol(inactstr, &ptr, 10);
527*0Sstevel@tonic-gate 		if (*ptr || inact < 0) {
528*0Sstevel@tonic-gate 			errmsg(M_INVALID, inactstr, "inactivity period");
529*0Sstevel@tonic-gate 			exit(EX_BADARG);
530*0Sstevel@tonic-gate 		}
531*0Sstevel@tonic-gate 		call_pass = 1;
532*0Sstevel@tonic-gate 	}
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 	/* expiration string is a date, newer than today */
535*0Sstevel@tonic-gate 	if (expire) {
536*0Sstevel@tonic-gate 		if (*expire &&
537*0Sstevel@tonic-gate 		    valid_expire(expire, (time_t *)0) == INVALID) {
538*0Sstevel@tonic-gate 			errmsg(M_INVALID, expire, "expiration date");
539*0Sstevel@tonic-gate 			exit(EX_BADARG);
540*0Sstevel@tonic-gate 		}
541*0Sstevel@tonic-gate 		call_pass = 1;
542*0Sstevel@tonic-gate 	}
543*0Sstevel@tonic-gate 
544*0Sstevel@tonic-gate 	if (nkeys > 0)
545*0Sstevel@tonic-gate 		call_pass = 1;
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 	/* that's it for validations - now do the work */
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate 	if (grps) {
550*0Sstevel@tonic-gate 		/* redefine login's supplentary group memberships */
551*0Sstevel@tonic-gate 		ret = edit_group(logname, new_logname, gidlist, 1);
552*0Sstevel@tonic-gate 		if (ret != EX_SUCCESS) {
553*0Sstevel@tonic-gate 			errmsg(M_UPDATE, "modified");
554*0Sstevel@tonic-gate 			exit(ret);
555*0Sstevel@tonic-gate 		}
556*0Sstevel@tonic-gate 	}
557*0Sstevel@tonic-gate 	if (projects) {
558*0Sstevel@tonic-gate 		ret = edit_project(logname, (char *)NULL, projlist, 0);
559*0Sstevel@tonic-gate 		if (ret != EX_SUCCESS) {
560*0Sstevel@tonic-gate 			errmsg(M_UPDATE, "modified");
561*0Sstevel@tonic-gate 			exit(ret);
562*0Sstevel@tonic-gate 		}
563*0Sstevel@tonic-gate 	}
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate 	if (!call_pass) exit(ret);
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 	/* only get to here if need to call passmgmt */
569*0Sstevel@tonic-gate 	/* set up arguments to  passmgmt in nargv array */
570*0Sstevel@tonic-gate 	nargv = malloc((30 + nkeys * 2) * sizeof (char *));
571*0Sstevel@tonic-gate 
572*0Sstevel@tonic-gate 	argindex = 0;
573*0Sstevel@tonic-gate 	nargv[argindex++] = "passmgmt";
574*0Sstevel@tonic-gate 	nargv[argindex++] = "-m";	/* modify */
575*0Sstevel@tonic-gate 
576*0Sstevel@tonic-gate 	if (comment) {	/* comment */
577*0Sstevel@tonic-gate 		nargv[argindex++] = "-c";
578*0Sstevel@tonic-gate 		nargv[argindex++] = comment;
579*0Sstevel@tonic-gate 	}
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 	if (dir) {
582*0Sstevel@tonic-gate 		/* flags for home directory */
583*0Sstevel@tonic-gate 		nargv[argindex++] = "-h";
584*0Sstevel@tonic-gate 		nargv[argindex++] = dir;
585*0Sstevel@tonic-gate 	}
586*0Sstevel@tonic-gate 
587*0Sstevel@tonic-gate 	if (group) {
588*0Sstevel@tonic-gate 		/* set gid flag */
589*0Sstevel@tonic-gate 		nargv[argindex++] = "-g";
590*0Sstevel@tonic-gate 		(void) sprintf(gidstring, "%ld", gid);
591*0Sstevel@tonic-gate 		nargv[argindex++] = gidstring;
592*0Sstevel@tonic-gate 	}
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate 	if (shell) { 	/* shell */
595*0Sstevel@tonic-gate 		nargv[argindex++] = "-s";
596*0Sstevel@tonic-gate 		nargv[argindex++] = shell;
597*0Sstevel@tonic-gate 	}
598*0Sstevel@tonic-gate 
599*0Sstevel@tonic-gate 	if (inactstr) {
600*0Sstevel@tonic-gate 		nargv[argindex++] = "-f";
601*0Sstevel@tonic-gate 		nargv[argindex++] = inactstr;
602*0Sstevel@tonic-gate 	}
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	if (expire) {
605*0Sstevel@tonic-gate 		nargv[argindex++] = "-e";
606*0Sstevel@tonic-gate 		nargv[argindex++] = expire;
607*0Sstevel@tonic-gate 	}
608*0Sstevel@tonic-gate 
609*0Sstevel@tonic-gate 	if (uidstr) {	/* set uid flag */
610*0Sstevel@tonic-gate 		nargv[argindex++] = "-u";
611*0Sstevel@tonic-gate 		(void) sprintf(uidstring, "%ld", uid);
612*0Sstevel@tonic-gate 		nargv[argindex++] = uidstring;
613*0Sstevel@tonic-gate 	}
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	if (oflag) nargv[argindex++] = "-o";
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 	if (new_logname) {	/* redefine login name */
618*0Sstevel@tonic-gate 		nargv[argindex++] = "-l";
619*0Sstevel@tonic-gate 		nargv[argindex++] = new_logname;
620*0Sstevel@tonic-gate 	}
621*0Sstevel@tonic-gate 
622*0Sstevel@tonic-gate 	if (nkeys > 0)
623*0Sstevel@tonic-gate 		addkey_args(nargv, &argindex);
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 	/* finally - login name */
626*0Sstevel@tonic-gate 	nargv[argindex++] = logname;
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate 	/* set the last to null */
629*0Sstevel@tonic-gate 	nargv[argindex++] = NULL;
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	/* now call passmgmt */
632*0Sstevel@tonic-gate 	ret = PEX_FAILED;
633*0Sstevel@tonic-gate 	for (tries = 3; ret != PEX_SUCCESS && tries--; ) {
634*0Sstevel@tonic-gate 		switch (ret = call_passmgmt(nargv)) {
635*0Sstevel@tonic-gate 		case PEX_SUCCESS:
636*0Sstevel@tonic-gate 		case PEX_BUSY:
637*0Sstevel@tonic-gate 			break;
638*0Sstevel@tonic-gate 
639*0Sstevel@tonic-gate 		case PEX_HOSED_FILES:
640*0Sstevel@tonic-gate 			errmsg(M_HOSED_FILES);
641*0Sstevel@tonic-gate 			exit(EX_INCONSISTENT);
642*0Sstevel@tonic-gate 			break;
643*0Sstevel@tonic-gate 
644*0Sstevel@tonic-gate 		case PEX_SYNTAX:
645*0Sstevel@tonic-gate 		case PEX_BADARG:
646*0Sstevel@tonic-gate 			/* should NEVER occur that passmgmt usage is wrong */
647*0Sstevel@tonic-gate 			if (is_role(usertype))
648*0Sstevel@tonic-gate 				errmsg(M_MRUSAGE);
649*0Sstevel@tonic-gate 			else
650*0Sstevel@tonic-gate 				errmsg(M_MUSAGE);
651*0Sstevel@tonic-gate 			exit(EX_SYNTAX);
652*0Sstevel@tonic-gate 			break;
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 		case PEX_BADUID:
655*0Sstevel@tonic-gate 			/* uid in use - shouldn't happen print message anyway */
656*0Sstevel@tonic-gate 			errmsg(M_UID_USED, uid);
657*0Sstevel@tonic-gate 			exit(EX_ID_EXISTS);
658*0Sstevel@tonic-gate 			break;
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 		case PEX_BADNAME:
661*0Sstevel@tonic-gate 			/* invalid loname */
662*0Sstevel@tonic-gate 			errmsg(M_USED, logname);
663*0Sstevel@tonic-gate 			exit(EX_NAME_EXISTS);
664*0Sstevel@tonic-gate 			break;
665*0Sstevel@tonic-gate 
666*0Sstevel@tonic-gate 		default:
667*0Sstevel@tonic-gate 			errmsg(M_UPDATE, "modified");
668*0Sstevel@tonic-gate 			exit(ret);
669*0Sstevel@tonic-gate 			break;
670*0Sstevel@tonic-gate 		}
671*0Sstevel@tonic-gate 	}
672*0Sstevel@tonic-gate 	if (tries == 0) {
673*0Sstevel@tonic-gate 		errmsg(M_UPDATE, "modified");
674*0Sstevel@tonic-gate 	}
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate 	exit(ret);
677*0Sstevel@tonic-gate 	/*NOTREACHED*/
678*0Sstevel@tonic-gate }
679