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]
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>
37 #include <sys/param.h>
44 #include <user_attr.h>
45 #include <nss_dbdefs.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 ...]]
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
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
78 * profile - One or more comma separated execution profiles defined
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)
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();
91 static uid_t uid
; /* new uid */
92 static gid_t gid
; /* gid of new login */
93 static char *new_logname
= NULL
; /* new login name with -l option */
94 static char *uidstr
= NULL
; /* uid from command line */
95 static char *group
= NULL
; /* group from command line */
96 static char *grps
= NULL
; /* multi groups from command line */
97 static char *dir
= NULL
; /* home dir from command line */
98 static char *shell
= NULL
; /* shell from command line */
99 static char *comment
= NULL
; /* comment from command line */
100 static char *logname
= NULL
; /* login name to add */
101 static char *inactstr
= NULL
; /* inactive from command line */
102 static char *expire
= NULL
; /* expiration date from command line */
103 static char *projects
= NULL
; /* project ids from command line */
104 static char *usertype
;
107 static char gidstring
[32], uidstring
[32];
108 char inactstring
[10];
117 return (strdup(str
));
129 npw
= malloc(sizeof (struct passwd
));
131 npw
->pw_name
= strcpmalloc(opw
->pw_name
);
132 npw
->pw_passwd
= strcpmalloc(opw
->pw_passwd
);
133 npw
->pw_uid
= opw
->pw_uid
;
134 npw
->pw_gid
= opw
->pw_gid
;
135 npw
->pw_age
= strcpmalloc(opw
->pw_age
);
136 npw
->pw_comment
= strcpmalloc(opw
->pw_comment
);
137 npw
->pw_gecos
= strcpmalloc(opw
->pw_gecos
);
138 npw
->pw_dir
= strcpmalloc(opw
->pw_dir
);
139 npw
->pw_shell
= strcpmalloc(opw
->pw_shell
);
149 int ch
, ret
= EX_SUCCESS
, call_pass
= 0, oflag
= 0, zfs_flags
= 0;
150 int tries
, mflag
= 0, inact
, **gidlist
, flag
= 0, zflag
= 0, Zflag
= 0;
151 boolean_t fail_if_busy
= B_FALSE
;
153 struct passwd
*pstruct
; /* password struct for login */
155 struct group
*g_ptr
; /* validated group from -g */
156 struct stat statbuf
; /* status buffer for stat */
158 FILE *pwf
; /* fille ptr for opened passwd file */
162 char **nargv
; /* arguments for execvp of passmgmt */
163 int argindex
; /* argument index into nargv */
166 int isrole
; /* current account is role */
170 if (geteuid() != 0) {
171 errmsg(M_PERM_DENIED
);
175 opterr
= 0; /* no print errors from getopt */
176 /* get user type based on the program name */
177 usertype
= getusertype(argv
[0]);
179 while ((ch
= getopt(argc
, argv
,
180 "c:d:e:f:G:g:l:mzZop:s:u:A:P:R:K:")) != EOF
)
188 fail_if_busy
= B_TRUE
;
205 fail_if_busy
= B_TRUE
;
209 new_logname
= optarg
;
210 fail_if_busy
= B_TRUE
;
216 fail_if_busy
= B_TRUE
;
221 fail_if_busy
= B_TRUE
;
234 fail_if_busy
= B_TRUE
;
243 change_key(USERATTR_AUTHS_KW
, optarg
);
247 change_key(USERATTR_PROFILES_KW
, optarg
);
251 change_key(USERATTR_ROLES_KW
, optarg
);
255 change_key(NULL
, optarg
);
260 if (is_role(usertype
))
267 if (((!mflag
) && (zflag
|| Zflag
)) || (zflag
&& Zflag
) ||
268 (mflag
> 1 && (zflag
|| Zflag
))) {
269 if (is_role(usertype
))
277 if (optind
!= argc
- 1 || flag
== 0) {
278 if (is_role(usertype
))
285 if ((!uidstr
&& oflag
) || (mflag
&& !dir
)) {
286 if (is_role(usertype
))
293 logname
= argv
[optind
];
295 /* Determine whether the account is a role or not */
296 if ((ua
= getusernam(logname
)) == NULL
||
297 (val
= kva_match(ua
->attr
, USERATTR_TYPE_KW
)) == NULL
||
298 strcmp(val
, USERATTR_TYPE_NONADMIN_KW
) != 0)
303 /* Verify that rolemod is used for roles and usermod for users */
304 if (isrole
!= is_role(usertype
)) {
312 /* Set the usertype key; defaults to the commandline */
313 usertype
= getsetdefval(USERATTR_TYPE_KW
, usertype
);
315 if (is_role(usertype
)) {
316 /* Roles can't have roles */
317 if (getsetdefval(USERATTR_ROLES_KW
, NULL
) != NULL
) {
321 /* If it was an ordinary user, delete its roles */
323 change_key(USERATTR_ROLES_KW
, "");
327 pw
= getpwnam(logname
);
330 * Do this with fgetpwent to make sure we are only looking on local
331 * system (since passmgmt only works on local system).
333 if ((pwf
= fopen("/etc/passwd", "r")) == NULL
) {
334 errmsg(M_OOPS
, "open", "/etc/passwd");
337 while ((pw
= fgetpwent(pwf
)) != NULL
)
338 if (strcmp(pw
->pw_name
, logname
) == 0)
345 char pwdb
[NSS_BUFLEN_PASSWD
];
348 if (getpwnam_r(logname
, &pwd
, pwdb
, sizeof (pwdb
)) == NULL
) {
349 /* This user does not exist. */
350 errmsg(M_EXIST
, logname
);
351 exit(EX_NAME_NOT_EXIST
);
353 /* This user exists in non-local name service. */
354 errmsg(M_NONLOCAL
, logname
);
359 pstruct
= passwd_cpmalloc(pw
);
362 * We can't modify a logged in user if any of the following
364 * uid (-u & -o), group (-g), home dir (-m), loginname (-l).
365 * If none of those are specified it is okay to go ahead
366 * some types of changes only take effect on next login, some
367 * like authorisations and profiles take effect instantly.
368 * One might think that -K type=role should require that the
369 * user not be logged in, however this would make it very
370 * difficult to make the root account a role using this command.
372 if (isbusy(logname
)) {
374 errmsg(M_BUSY
, logname
, "change");
377 warningmsg(WARN_LOGGED_IN
, logname
);
380 if (new_logname
&& strcmp(new_logname
, logname
)) {
381 switch (valid_login(new_logname
, (struct passwd
**)NULL
,
384 errmsg(M_INVALID
, new_logname
, "login name");
389 errmsg(M_USED
, new_logname
);
390 exit(EX_NAME_EXISTS
);
394 errmsg(M_TOO_LONG
, new_logname
);
403 warningmsg(warning
, logname
);
407 /* convert uidstr to integer */
409 uid
= (uid_t
)strtol(uidstr
, &ptr
, (int)10);
410 if (*ptr
|| errno
== ERANGE
) {
411 errmsg(M_INVALID
, uidstr
, "user id");
415 if (uid
!= pstruct
->pw_uid
) {
416 switch (valid_uid(uid
, NULL
)) {
419 /* override not specified */
420 errmsg(M_UID_USED
, uid
);
425 errmsg(M_RESERVED
, uid
);
428 errmsg(M_TOOBIG
, "uid", uid
);
436 /* uid's the same, so don't change anything */
441 } else uid
= pstruct
->pw_uid
;
444 switch (valid_group(group
, &g_ptr
, &warning
)) {
446 errmsg(M_INVALID
, group
, "group id");
450 errmsg(M_TOOBIG
, "gid", group
);
454 errmsg(M_GRP_NOTUSED
, group
);
455 exit(EX_NAME_NOT_EXIST
);
458 gid
= (gid_t
)strtol(group
, &ptr
, (int)10);
459 errmsg(M_RESERVED_GID
, gid
);
463 warningmsg(warning
, group
);
468 gid
= pstruct
->pw_gid
;
470 /* call passmgmt if gid is different, else ignore group */
471 if (gid
!= pstruct
->pw_gid
)
475 } else gid
= pstruct
->pw_gid
;
478 if (!(gidlist
= valid_lgroup(grps
, gid
)))
483 if (projects
&& *projects
) {
484 if (! (projlist
= valid_lproject(projects
)))
487 projlist
= (projid_t
**)0;
491 errmsg(M_RELPATH
, dir
);
494 if (strcmp(pstruct
->pw_dir
, dir
) == 0) {
495 /* home directory is the same so ignore dflag & mflag */
498 } else call_pass
= 1;
502 if (stat(dir
, &statbuf
) == 0) {
503 /* Home directory exists */
504 if (check_perm(statbuf
, pstruct
->pw_uid
,
505 pstruct
->pw_gid
, S_IWOTH
|S_IXOTH
) != 0) {
506 errmsg(M_NO_PERM
, logname
, dir
);
511 zfs_flags
= get_default_zfs_flags();
512 if (zflag
|| mflag
> 1)
513 zfs_flags
|= MANAGE_ZFS
;
515 zfs_flags
&= ~MANAGE_ZFS
;
516 ret
= create_home(dir
, NULL
, uid
, gid
, zfs_flags
);
519 if (ret
== EX_SUCCESS
)
520 ret
= move_dir(pstruct
->pw_dir
, dir
,
523 if (ret
!= EX_SUCCESS
)
528 if (REL_PATH(shell
)) {
529 errmsg(M_RELPATH
, shell
);
532 if (strcmp(pstruct
->pw_shell
, shell
) == 0) {
533 /* ignore s option if shell is not different */
536 if (stat(shell
, &statbuf
) < 0 ||
537 (statbuf
.st_mode
& S_IFMT
) != S_IFREG
||
538 (statbuf
.st_mode
& 0555) != 0555) {
540 errmsg(M_INVALID
, shell
, "shell");
549 /* ignore comment if comment is not changed */
550 if (strcmp(pstruct
->pw_comment
, comment
))
556 /* inactive string is a positive integer */
558 /* convert inactstr to integer */
559 inact
= (int)strtol(inactstr
, &ptr
, 10);
560 if (*ptr
|| inact
< 0) {
561 errmsg(M_INVALID
, inactstr
, "inactivity period");
567 /* expiration string is a date, newer than today */
570 valid_expire(expire
, (time_t *)0) == INVALID
) {
571 errmsg(M_INVALID
, expire
, "expiration date");
580 /* that's it for validations - now do the work */
583 /* redefine login's supplentary group memberships */
584 ret
= edit_group(logname
, new_logname
, gidlist
, 1);
585 if (ret
!= EX_SUCCESS
) {
586 errmsg(M_UPDATE
, "modified");
591 ret
= edit_project(logname
, (char *)NULL
, projlist
, 0);
592 if (ret
!= EX_SUCCESS
) {
593 errmsg(M_UPDATE
, "modified");
599 if (!call_pass
) exit(ret
);
601 /* only get to here if need to call passmgmt */
602 /* set up arguments to passmgmt in nargv array */
603 nargv
= malloc((30 + nkeys
* 2) * sizeof (char *));
606 nargv
[argindex
++] = PASSMGMT
;
607 nargv
[argindex
++] = "-m"; /* modify */
609 if (comment
) { /* comment */
610 nargv
[argindex
++] = "-c";
611 nargv
[argindex
++] = comment
;
615 /* flags for home directory */
616 nargv
[argindex
++] = "-h";
617 nargv
[argindex
++] = dir
;
622 nargv
[argindex
++] = "-g";
623 (void) sprintf(gidstring
, "%u", gid
);
624 nargv
[argindex
++] = gidstring
;
627 if (shell
) { /* shell */
628 nargv
[argindex
++] = "-s";
629 nargv
[argindex
++] = shell
;
633 nargv
[argindex
++] = "-f";
634 nargv
[argindex
++] = inactstr
;
638 nargv
[argindex
++] = "-e";
639 nargv
[argindex
++] = expire
;
642 if (uidstr
) { /* set uid flag */
643 nargv
[argindex
++] = "-u";
644 (void) sprintf(uidstring
, "%u", uid
);
645 nargv
[argindex
++] = uidstring
;
648 if (oflag
) nargv
[argindex
++] = "-o";
650 if (new_logname
) { /* redefine login name */
651 nargv
[argindex
++] = "-l";
652 nargv
[argindex
++] = new_logname
;
656 addkey_args(nargv
, &argindex
);
658 /* finally - login name */
659 nargv
[argindex
++] = logname
;
661 /* set the last to null */
662 nargv
[argindex
++] = NULL
;
664 /* now call passmgmt */
666 for (tries
= 3; ret
!= PEX_SUCCESS
&& tries
--; ) {
667 switch (ret
= call_passmgmt(nargv
)) {
672 case PEX_HOSED_FILES
:
673 errmsg(M_HOSED_FILES
);
674 exit(EX_INCONSISTENT
);
679 /* should NEVER occur that passmgmt usage is wrong */
680 if (is_role(usertype
))
688 /* uid in use - shouldn't happen print message anyway */
689 errmsg(M_UID_USED
, uid
);
695 errmsg(M_USED
, logname
);
696 exit(EX_NAME_EXISTS
);
700 errmsg(M_UPDATE
, "modified");
706 errmsg(M_UPDATE
, "modified");