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>
47 #include <user_attr.h>
48 #include <libcmdutils.h>
55 * useradd [-u uid [-o] | -g group | -G group [[, group]...]
56 * | -d dir [-m [-z|Z]]
57 * | -s shell | -c comment | -k skel_dir | -b base_dir] ]
58 * [ -A authorization [, authorization ...]]
59 * [ -P profile [, profile ...]]
61 * [ -R role [, role ...]] [-p project [, project ...]] login
62 * useradd -D [ -g group ] [ -b base_dir | -f inactive | -e expire |
63 * -s shell | -k skel_dir ]
64 * [ -A authorization [, authorization ...]]
65 * [ -P profile [, profile ...]] [ -K key=value ]
66 * [ -R role [, role ...]] [-p project [, project ...]] login
68 * This command adds new user logins to the system. Arguments are:
71 * group - an existing group's integer ID or char string name
72 * dir - home directory
73 * shell - a program to be used as a shell
74 * comment - any text string
75 * skel_dir - a skeleton directory
76 * base_dir - a directory
77 * login - a string of printable chars except colon(:)
78 * authorization - One or more comma separated authorizations defined
80 * profile - One or more comma separated execution profiles defined
82 * role - One or more comma-separated role names defined in user_attr(4)
83 * project - One or more comma-separated project names or numbers
87 extern struct userdefs
*getusrdef();
88 extern void dispusrdef();
90 static void cleanup();
92 extern int check_perm(), valid_expire();
93 extern int valid_uid();
94 extern int call_passmgmt(), edit_group(), create_home();
95 extern int edit_project();
96 extern int **valid_lgroup();
97 extern projid_t
**valid_lproject();
98 extern void import_def(struct userdefs
*);
99 extern int get_default_zfs_flags();
101 static uid_t uid
; /* new uid */
102 static char *logname
; /* login name to add */
103 static struct userdefs
*usrdefs
; /* defaults for useradd */
107 static char homedir
[ PATH_MAX
+ 1 ]; /* home directory */
108 static char gidstring
[32]; /* group id string representation */
109 static gid_t gid
; /* gid of new login */
110 static char uidstring
[32]; /* user id string representation */
111 static char *uidstr
= NULL
; /* uid from command line */
112 static char *base_dir
= NULL
; /* base_dir from command line */
113 static char *group
= NULL
; /* group from command line */
114 static char *grps
= NULL
; /* multi groups from command line */
115 static char *dir
= NULL
; /* home dir from command line */
116 static char *shell
= NULL
; /* shell from command line */
117 static char *comment
= NULL
; /* comment from command line */
118 static char *skel_dir
= NULL
; /* skel dir from command line */
119 static long inact
; /* inactive days */
120 static char *inactstr
= NULL
; /* inactive from command line */
121 static char inactstring
[10]; /* inactivity string representation */
122 static char *expirestr
= NULL
; /* expiration date from command line */
123 static char *projects
= NULL
; /* project id's from command line */
125 static char *usertype
= NULL
; /* type of user, either role or normal */
134 static void valid_input(path_opt_t
, const char *);
141 int ch
, ret
, mflag
= 0, oflag
= 0, Dflag
= 0;
142 int zflag
= 0, Zflag
= 0, **gidlist
= NULL
;
143 projid_t
**projlist
= NULL
;
144 char *ptr
; /* loc in a str, may be set by strtol */
146 struct project p_ptr
;
147 char mybuf
[PROJECT_BUFSZ
];
148 struct stat statbuf
; /* status buffer for stat */
151 char **nargv
; /* arguments for execvp of passmgmt */
152 int argindex
; /* argument index into nargv */
153 int zfs_flags
= 0; /* create_home flags */
157 if (geteuid() != 0) {
158 errmsg(M_PERM_DENIED
);
162 opterr
= 0; /* no print errors from getopt */
163 usertype
= getusertype(argv
[0]);
165 change_key(USERATTR_TYPE_KW
, usertype
);
167 while ((ch
= getopt(argc
, argv
,
168 "b:c:Dd:e:f:G:g:k:mzZop:s:u:A:P:R:K:")) != EOF
)
235 change_key(USERATTR_AUTHS_KW
, optarg
);
239 change_key(USERATTR_PROFILES_KW
, optarg
);
243 if (is_role(usertype
)) {
247 change_key(USERATTR_ROLES_KW
, optarg
);
251 change_key(NULL
, optarg
);
256 if (is_role(usertype
))
263 if (((!mflag
) && (zflag
|| Zflag
)) || (zflag
&& Zflag
) ||
264 (mflag
> 1 && (zflag
|| Zflag
))) {
265 if (is_role(usertype
))
273 /* get defaults for adding new users */
274 usrdefs
= getusrdef(usertype
);
280 if (optind
!= argc
) {
281 if (is_role(usertype
))
288 if (uidstr
!= NULL
|| oflag
|| grps
!= NULL
||
289 dir
!= NULL
|| mflag
|| comment
!= NULL
||
290 group
!= NULL
|| projects
!= NULL
||
291 base_dir
!= NULL
|| inactstr
!= NULL
||
292 expirestr
!= NULL
|| shell
!= NULL
||
294 if (is_role(usertype
))
302 dispusrdef(stdout
, (D_ALL
& ~D_RID
), usertype
);
310 if (optind
!= argc
- 1 || (skel_dir
!= NULL
&& !mflag
)) {
311 if (is_role(usertype
))
318 logname
= argv
[optind
];
319 switch (valid_login(logname
, (struct passwd
**)NULL
, &warning
)) {
321 errmsg(M_INVALID
, logname
, "login name");
326 errmsg(M_USED
, logname
);
327 exit(EX_NAME_EXISTS
);
331 errmsg(M_TOO_LONG
, logname
);
337 warningmsg(warning
, logname
);
338 if (uidstr
!= NULL
) {
339 /* convert uidstr to integer */
341 uid
= (uid_t
)strtol(uidstr
, &ptr
, (int)10);
342 if (*ptr
|| errno
== ERANGE
) {
343 errmsg(M_INVALID
, uidstr
, "user id");
347 switch (valid_uid(uid
, NULL
)) {
350 /* override not specified */
351 errmsg(M_UID_USED
, uid
);
356 errmsg(M_RESERVED
, uid
);
359 errmsg(M_TOOBIG
, "uid", uid
);
366 if (findnextuid(DEFRID
+1, MAXUID
, &uid
) != 0) {
367 errmsg(M_INVALID
, "default id", "user id");
373 switch (valid_group(group
, &g_ptr
, &warning
)) {
375 errmsg(M_INVALID
, group
, "group id");
379 errmsg(M_TOOBIG
, "gid", group
);
384 errmsg(M_GRP_NOTUSED
, group
);
385 exit(EX_NAME_NOT_EXIST
);
390 warningmsg(warning
, group
);
393 } else gid
= usrdefs
->defgroup
;
399 else if (!(gidlist
= valid_lgroup(grps
, gid
)))
403 if (projects
!= NULL
) {
405 projects
= (char *)0;
406 else if (! (projlist
= valid_lproject(projects
)))
410 /* if base_dir is provided, check its validity; otherwise default */
411 if (base_dir
!= NULL
)
412 valid_input(BASEDIR
, base_dir
);
414 base_dir
= usrdefs
->defparent
;
417 /* set homedir to home directory made from base_dir */
418 (void) sprintf(homedir
, "%s/%s", base_dir
, logname
);
420 } else if (REL_PATH(dir
)) {
421 errmsg(M_RELPATH
, dir
);
425 (void) strcpy(homedir
, dir
);
428 /* Does home dir. already exist? */
429 if (stat(homedir
, &statbuf
) == 0) {
430 /* directory exists - don't try to create */
433 if (check_perm(statbuf
, uid
, gid
, S_IXOTH
) != 0)
434 errmsg(M_NO_PERM
, logname
, homedir
);
438 * if shell, skel_dir are provided, check their validity.
442 valid_input(SHELL
, shell
);
444 shell
= usrdefs
->defshell
;
446 if (skel_dir
!= NULL
)
447 valid_input(SKELDIR
, skel_dir
);
449 skel_dir
= usrdefs
->defskel
;
451 if (inactstr
!= NULL
) {
452 /* convert inactstr to integer */
453 inact
= strtol(inactstr
, &ptr
, 10);
454 if (*ptr
|| inact
< 0) {
455 errmsg(M_INVALID
, inactstr
, "inactivity period");
458 } else inact
= usrdefs
->definact
;
460 /* expiration string is a date, newer than today */
461 if (expirestr
!= NULL
) {
463 if (valid_expire(expirestr
, (time_t *)0) == INVALID
) {
464 errmsg(M_INVALID
, expirestr
, "expiration date");
467 usrdefs
->defexpire
= expirestr
;
469 /* Unset the expiration date */
470 expirestr
= (char *)0;
472 } else expirestr
= usrdefs
->defexpire
;
476 /* must now call passmgmt */
478 /* set up arguments to passmgmt in nargv array */
479 nargv
= malloc((30 + nkeys
* 2) * sizeof (char *));
481 nargv
[argindex
++] = PASSMGMT
;
482 nargv
[argindex
++] = "-a"; /* add */
484 if (comment
!= NULL
) {
486 nargv
[argindex
++] = "-c";
487 nargv
[argindex
++] = comment
;
490 /* flags for home directory */
491 nargv
[argindex
++] = "-h";
492 nargv
[argindex
++] = homedir
;
495 nargv
[argindex
++] = "-g";
496 (void) sprintf(gidstring
, "%u", gid
);
497 nargv
[argindex
++] = gidstring
;
500 nargv
[argindex
++] = "-s";
501 nargv
[argindex
++] = shell
;
504 nargv
[argindex
++] = "-f";
505 (void) sprintf(inactstring
, "%ld", inact
);
506 nargv
[argindex
++] = inactstring
;
508 /* set expiration date */
509 if (expirestr
!= NULL
) {
510 nargv
[argindex
++] = "-e";
511 nargv
[argindex
++] = expirestr
;
515 nargv
[argindex
++] = "-u";
516 (void) sprintf(uidstring
, "%u", uid
);
517 nargv
[argindex
++] = uidstring
;
519 if (oflag
) nargv
[argindex
++] = "-o";
522 addkey_args(nargv
, &argindex
);
524 /* finally - login name */
525 nargv
[argindex
++] = logname
;
527 /* set the last to null */
528 nargv
[argindex
++] = NULL
;
530 /* now call passmgmt */
533 * If call_passmgmt fails for any reason other than PEX_BADUID, exit
534 * is invoked with an appropriate error message. If PEX_BADUID is
535 * returned, then if the user specified the ID, exit is invoked
536 * with an appropriate error message. Otherwise we try to pick a
537 * different ID and try again. If we run out of IDs, i.e. no more
538 * users can be created, then -1 is returned and we terminate via exit.
539 * If PEX_BUSY is returned we increment a count, since we will stop
540 * trying if PEX_BUSY reaches 3. For PEX_SUCCESS we immediately
541 * terminate the loop.
543 while (busy
< 3 && ret
!= PEX_SUCCESS
) {
544 switch (ret
= call_passmgmt(nargv
)) {
550 case PEX_HOSED_FILES
:
551 errmsg(M_HOSED_FILES
);
552 exit(EX_INCONSISTENT
);
557 /* should NEVER occur that passmgmt usage is wrong */
558 if (is_role(usertype
))
567 * The uid has been taken. If it was specified by a
568 * user, then we must fail. Otherwise, keep trying
569 * to get a good uid until we run out of IDs.
571 if (uidstr
!= NULL
) {
572 errmsg(M_UID_USED
, uid
);
575 if (findnextuid(DEFRID
+1, MAXUID
, &uid
) != 0) {
576 errmsg(M_INVALID
, "default id",
580 (void) sprintf(uidstring
, "%u", uid
);
586 errmsg(M_USED
, logname
);
587 exit(EX_NAME_EXISTS
);
591 errmsg(M_UPDATE
, "created");
597 errmsg(M_UPDATE
, "created");
601 /* add group entry */
602 if ((grps
!= NULL
) && edit_group(logname
, (char *)0, gidlist
, 0)) {
603 errmsg(M_UPDATE
, "created");
608 /* update project database */
609 if ((projects
!= NULL
) &&
610 edit_project(logname
, NULL
, projlist
, 0)) {
611 errmsg(M_UPDATE
, "created");
616 /* create home directory */
618 zfs_flags
= get_default_zfs_flags();
620 if (zflag
|| mflag
> 1)
621 zfs_flags
|= MANAGE_ZFS
;
623 zfs_flags
&= ~MANAGE_ZFS
;
624 ret
= create_home(homedir
, skel_dir
, uid
, gid
, zfs_flags
);
626 if (ret
!= EX_SUCCESS
) {
627 (void) edit_project(logname
, NULL
, (projid_t
**)NULL
,
629 (void) edit_group(logname
, (char *)0, (int **)0, 1);
648 switch (call_passmgmt(nargv
)) {
653 /* should NEVER occur that passmgmt usage is wrong */
654 if (is_role(usertype
))
661 /* uid is used - shouldn't happen but print message anyway */
662 errmsg(M_UID_USED
, uid
);
667 errmsg(M_USED
, logname
);
671 errmsg(M_UPDATE
, "created");
676 /* Check the validity for shell, base_dir and skel_dir */
679 valid_input(path_opt_t opt
, const char *input
)
683 if (REL_PATH(input
)) {
684 errmsg(M_RELPATH
, input
);
687 if (stat(input
, &statbuf
) == -1) {
688 errmsg(M_INVALID
, input
, "path");
692 if (!S_ISREG(statbuf
.st_mode
) ||
693 (statbuf
.st_mode
& 0555) != 0555) {
694 errmsg(M_INVALID
, input
, "shell");
698 if (!S_ISDIR(statbuf
.st_mode
)) {
699 errmsg(M_INVALID
, input
, "directory");