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