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();
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
;
108 static char gidstring
[32], uidstring
[32];
109 char inactstring
[10];
118 return (strdup(str
));
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
);
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
;
154 struct passwd
*pstruct
; /* password struct for login */
156 struct group
*g_ptr
; /* validated group from -g */
157 struct stat statbuf
; /* status buffer for stat */
159 FILE *pwf
; /* fille ptr for opened passwd file */
163 char **nargv
; /* arguments for execvp of passmgmt */
164 int argindex
; /* argument index into nargv */
167 int isrole
; /* current account is role */
171 if (geteuid() != 0) {
172 errmsg(M_PERM_DENIED
);
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
)
189 fail_if_busy
= B_TRUE
;
206 fail_if_busy
= B_TRUE
;
210 new_logname
= optarg
;
211 fail_if_busy
= B_TRUE
;
217 fail_if_busy
= B_TRUE
;
222 fail_if_busy
= B_TRUE
;
235 fail_if_busy
= B_TRUE
;
244 change_key(USERATTR_AUTHS_KW
, optarg
);
248 change_key(USERATTR_PROFILES_KW
, optarg
);
252 change_key(USERATTR_ROLES_KW
, optarg
);
256 change_key(NULL
, optarg
);
261 if (is_role(usertype
))
268 if (((!mflag
) && (zflag
|| Zflag
)) || (zflag
&& Zflag
) ||
269 (mflag
> 1 && (zflag
|| Zflag
))) {
270 if (is_role(usertype
))
278 if (optind
!= argc
- 1 || flag
== 0) {
279 if (is_role(usertype
))
286 if ((!uidstr
&& oflag
) || (mflag
&& !dir
)) {
287 if (is_role(usertype
))
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)
304 /* Verify that rolemod is used for roles and usermod for users */
305 if (isrole
!= is_role(usertype
)) {
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
) {
322 /* If it was an ordinary user, delete its roles */
324 change_key(USERATTR_ROLES_KW
, "");
328 pw
= getpwnam(logname
);
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");
338 while ((pw
= fgetpwent(pwf
)) != NULL
)
339 if (strcmp(pw
->pw_name
, logname
) == 0)
346 char pwdb
[NSS_BUFLEN_PASSWD
];
348 struct passwd
*result
;
350 getpwnam_r(logname
, &pwd
, pwdb
, sizeof (pwdb
), &result
);
352 /* This user does not exist. */
353 errmsg(M_EXIST
, logname
);
354 exit(EX_NAME_NOT_EXIST
);
356 /* This user exists in non-local name service. */
357 errmsg(M_NONLOCAL
, logname
);
362 pstruct
= passwd_cpmalloc(pw
);
365 * We can't modify a logged in user if any of the following
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
)) {
377 errmsg(M_BUSY
, logname
, "change");
380 warningmsg(WARN_LOGGED_IN
, logname
);
383 if (new_logname
&& strcmp(new_logname
, logname
)) {
384 switch (valid_login(new_logname
, (struct passwd
**)NULL
,
387 errmsg(M_INVALID
, new_logname
, "login name");
392 errmsg(M_USED
, new_logname
);
393 exit(EX_NAME_EXISTS
);
397 errmsg(M_TOO_LONG
, new_logname
);
406 warningmsg(warning
, logname
);
410 /* convert uidstr to integer */
412 uid
= (uid_t
)strtol(uidstr
, &ptr
, (int)10);
413 if (*ptr
|| errno
== ERANGE
) {
414 errmsg(M_INVALID
, uidstr
, "user id");
418 if (uid
!= pstruct
->pw_uid
) {
419 switch (valid_uid(uid
, NULL
)) {
422 /* override not specified */
423 errmsg(M_UID_USED
, uid
);
428 errmsg(M_RESERVED
, uid
);
431 errmsg(M_TOOBIG
, "uid", uid
);
439 /* uid's the same, so don't change anything */
444 } else uid
= pstruct
->pw_uid
;
447 switch (valid_group(group
, &g_ptr
, &warning
)) {
449 errmsg(M_INVALID
, group
, "group id");
453 errmsg(M_TOOBIG
, "gid", group
);
457 errmsg(M_GRP_NOTUSED
, group
);
458 exit(EX_NAME_NOT_EXIST
);
461 gid
= (gid_t
)strtol(group
, &ptr
, (int)10);
462 errmsg(M_RESERVED_GID
, gid
);
466 warningmsg(warning
, group
);
471 gid
= pstruct
->pw_gid
;
473 /* call passmgmt if gid is different, else ignore group */
474 if (gid
!= pstruct
->pw_gid
)
478 } else gid
= pstruct
->pw_gid
;
481 if (!(gidlist
= valid_lgroup(grps
, gid
)))
486 if (projects
&& *projects
) {
487 if (! (projlist
= valid_lproject(projects
)))
490 projlist
= (projid_t
**)0;
494 errmsg(M_RELPATH
, dir
);
497 if (strcmp(pstruct
->pw_dir
, dir
) == 0) {
498 /* home directory is the same so ignore dflag & mflag */
501 } else call_pass
= 1;
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
);
514 zfs_flags
= get_default_zfs_flags();
515 if (zflag
|| mflag
> 1)
516 zfs_flags
|= MANAGE_ZFS
;
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
,
526 if (ret
!= EX_SUCCESS
)
531 if (REL_PATH(shell
)) {
532 errmsg(M_RELPATH
, shell
);
535 if (strcmp(pstruct
->pw_shell
, shell
) == 0) {
536 /* ignore s option if shell is not different */
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");
552 /* ignore comment if comment is not changed */
553 if (strcmp(pstruct
->pw_comment
, comment
))
559 /* inactive string is a positive integer */
561 /* convert inactstr to integer */
562 inact
= (int)strtol(inactstr
, &ptr
, 10);
563 if (*ptr
|| inact
< 0) {
564 errmsg(M_INVALID
, inactstr
, "inactivity period");
570 /* expiration string is a date, newer than today */
573 valid_expire(expire
, (time_t *)0) == INVALID
) {
574 errmsg(M_INVALID
, expire
, "expiration date");
583 /* that's it for validations - now do the work */
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");
594 ret
= edit_project(logname
, NULL
, projlist
, 0);
595 if (ret
!= EX_SUCCESS
) {
596 errmsg(M_UPDATE
, "modified");
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 *));
609 nargv
[argindex
++] = PASSMGMT
;
610 nargv
[argindex
++] = "-m"; /* modify */
612 if (comment
) { /* comment */
613 nargv
[argindex
++] = "-c";
614 nargv
[argindex
++] = comment
;
618 /* flags for home directory */
619 nargv
[argindex
++] = "-h";
620 nargv
[argindex
++] = dir
;
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
;
636 nargv
[argindex
++] = "-f";
637 nargv
[argindex
++] = inactstr
;
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
;
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 */
669 for (tries
= 3; ret
!= PEX_SUCCESS
&& tries
--; ) {
670 switch (ret
= call_passmgmt(nargv
)) {
675 case PEX_HOSED_FILES
:
676 errmsg(M_HOSED_FILES
);
677 exit(EX_INCONSISTENT
);
682 /* should NEVER occur that passmgmt usage is wrong */
683 if (is_role(usertype
))
691 /* uid in use - shouldn't happen print message anyway */
692 errmsg(M_UID_USED
, uid
);
698 errmsg(M_USED
, logname
);
699 exit(EX_NAME_EXISTS
);
703 errmsg(M_UPDATE
, "modified");
709 errmsg(M_UPDATE
, "modified");