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 putusrdef(), 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 update_def(struct userdefs
*);
99 extern void import_def(struct userdefs
*);
100 extern int get_default_zfs_flags();
102 static uid_t uid
; /* new uid */
103 static char *logname
; /* login name to add */
104 static struct userdefs
*usrdefs
; /* defaults for useradd */
108 static char homedir
[ PATH_MAX
+ 1 ]; /* home directory */
109 static char gidstring
[32]; /* group id string representation */
110 static gid_t gid
; /* gid of new login */
111 static char uidstring
[32]; /* user id string representation */
112 static char *uidstr
= NULL
; /* uid from command line */
113 static char *base_dir
= NULL
; /* base_dir from command line */
114 static char *group
= NULL
; /* group from command line */
115 static char *grps
= NULL
; /* multi groups from command line */
116 static char *dir
= NULL
; /* home dir from command line */
117 static char *shell
= NULL
; /* shell from command line */
118 static char *comment
= NULL
; /* comment from command line */
119 static char *skel_dir
= NULL
; /* skel dir from command line */
120 static long inact
; /* inactive days */
121 static char *inactstr
= NULL
; /* inactive from command line */
122 static char inactstring
[10]; /* inactivity string representation */
123 static char *expirestr
= NULL
; /* expiration date from command line */
124 static char *projects
= NULL
; /* project id's from command line */
126 static char *usertype
= NULL
; /* type of user, either role or normal */
135 static void valid_input(path_opt_t
, const char *);
142 int ch
, ret
, mflag
= 0, oflag
= 0, Dflag
= 0;
143 int zflag
= 0, Zflag
= 0, **gidlist
= NULL
;
144 projid_t
**projlist
= NULL
;
145 char *ptr
; /* loc in a str, may be set by strtol */
147 struct project p_ptr
;
148 char mybuf
[PROJECT_BUFSZ
];
149 struct stat statbuf
; /* status buffer for stat */
152 char **nargv
; /* arguments for execvp of passmgmt */
153 int argindex
; /* argument index into nargv */
154 int zfs_flags
= 0; /* create_home flags */
158 if (geteuid() != 0) {
159 errmsg(M_PERM_DENIED
);
163 opterr
= 0; /* no print errors from getopt */
164 usertype
= getusertype(argv
[0]);
166 change_key(USERATTR_TYPE_KW
, usertype
);
168 while ((ch
= getopt(argc
, argv
,
169 "b:c:Dd:e:f:G:g:k:mzZop:s:u:A:P:R:K:")) != EOF
)
236 change_key(USERATTR_AUTHS_KW
, optarg
);
240 change_key(USERATTR_PROFILES_KW
, optarg
);
244 if (is_role(usertype
)) {
248 change_key(USERATTR_ROLES_KW
, optarg
);
252 change_key(NULL
, optarg
);
257 if (is_role(usertype
))
264 if (((!mflag
) && (zflag
|| Zflag
)) || (zflag
&& Zflag
) ||
265 (mflag
> 1 && (zflag
|| Zflag
))) {
266 if (is_role(usertype
))
274 /* get defaults for adding new users */
275 usrdefs
= getusrdef(usertype
);
281 if (optind
!= argc
) {
282 if (is_role(usertype
))
289 if (uidstr
!= NULL
|| oflag
|| grps
!= NULL
||
290 dir
!= NULL
|| mflag
|| comment
!= NULL
) {
291 if (is_role(usertype
))
298 /* Group must be an existing group */
300 switch (valid_group(group
, &g_ptr
, &warning
)) {
302 errmsg(M_INVALID
, group
, "group id");
306 errmsg(M_TOOBIG
, "gid", group
);
311 errmsg(M_GRP_NOTUSED
, group
);
312 exit(EX_NAME_NOT_EXIST
);
315 warningmsg(warning
, group
);
317 usrdefs
->defgroup
= g_ptr
->gr_gid
;
318 usrdefs
->defgname
= g_ptr
->gr_name
;
322 /* project must be an existing project */
323 if (projects
!= NULL
) {
324 switch (valid_project(projects
, &p_ptr
, mybuf
,
325 sizeof (mybuf
), &warning
)) {
327 errmsg(M_INVALID
, projects
, "project id");
331 errmsg(M_TOOBIG
, "projid", projects
);
335 errmsg(M_PROJ_NOTUSED
, projects
);
336 exit(EX_NAME_NOT_EXIST
);
339 warningmsg(warning
, projects
);
341 usrdefs
->defproj
= p_ptr
.pj_projid
;
342 usrdefs
->defprojname
= p_ptr
.pj_name
;
345 /* base_dir must be an existing directory */
346 if (base_dir
!= NULL
) {
347 valid_input(BASEDIR
, base_dir
);
348 usrdefs
->defparent
= base_dir
;
351 /* inactivity period is an integer */
352 if (inactstr
!= NULL
) {
353 /* convert inactstr to integer */
354 inact
= strtol(inactstr
, &ptr
, 10);
355 if (*ptr
|| inact
< 0) {
356 errmsg(M_INVALID
, inactstr
,
357 "inactivity period");
361 usrdefs
->definact
= inact
;
364 /* expiration string is a date, newer than today */
365 if (expirestr
!= NULL
) {
367 if (valid_expire(expirestr
, (time_t *)0)
369 errmsg(M_INVALID
, expirestr
,
373 usrdefs
->defexpire
= expirestr
;
375 /* Unset the expiration date */
376 usrdefs
->defexpire
= "";
380 valid_input(SHELL
, shell
);
381 usrdefs
->defshell
= shell
;
383 if (skel_dir
!= NULL
) {
384 valid_input(SKELDIR
, skel_dir
);
385 usrdefs
->defskel
= skel_dir
;
389 /* change defaults for useradd */
390 if (putusrdef(usrdefs
, usertype
) < 0) {
391 errmsg(M_UPDATE
, "created");
396 dispusrdef(stdout
, (D_ALL
& ~D_RID
), usertype
);
404 if (optind
!= argc
- 1 || (skel_dir
!= NULL
&& !mflag
)) {
405 if (is_role(usertype
))
412 logname
= argv
[optind
];
413 switch (valid_login(logname
, (struct passwd
**)NULL
, &warning
)) {
415 errmsg(M_INVALID
, logname
, "login name");
420 errmsg(M_USED
, logname
);
421 exit(EX_NAME_EXISTS
);
425 errmsg(M_TOO_LONG
, logname
);
431 warningmsg(warning
, logname
);
432 if (uidstr
!= NULL
) {
433 /* convert uidstr to integer */
435 uid
= (uid_t
)strtol(uidstr
, &ptr
, (int)10);
436 if (*ptr
|| errno
== ERANGE
) {
437 errmsg(M_INVALID
, uidstr
, "user id");
441 switch (valid_uid(uid
, NULL
)) {
444 /* override not specified */
445 errmsg(M_UID_USED
, uid
);
450 errmsg(M_RESERVED
, uid
);
453 errmsg(M_TOOBIG
, "uid", uid
);
460 if (findnextuid(DEFRID
+1, MAXUID
, &uid
) != 0) {
461 errmsg(M_INVALID
, "default id", "user id");
467 switch (valid_group(group
, &g_ptr
, &warning
)) {
469 errmsg(M_INVALID
, group
, "group id");
473 errmsg(M_TOOBIG
, "gid", group
);
478 errmsg(M_GRP_NOTUSED
, group
);
479 exit(EX_NAME_NOT_EXIST
);
484 warningmsg(warning
, group
);
487 } else gid
= usrdefs
->defgroup
;
493 else if (!(gidlist
= valid_lgroup(grps
, gid
)))
497 if (projects
!= NULL
) {
499 projects
= (char *)0;
500 else if (! (projlist
= valid_lproject(projects
)))
504 /* if base_dir is provided, check its validity; otherwise default */
505 if (base_dir
!= NULL
)
506 valid_input(BASEDIR
, base_dir
);
508 base_dir
= usrdefs
->defparent
;
511 /* set homedir to home directory made from base_dir */
512 (void) sprintf(homedir
, "%s/%s", base_dir
, logname
);
514 } else if (REL_PATH(dir
)) {
515 errmsg(M_RELPATH
, dir
);
519 (void) strcpy(homedir
, dir
);
522 /* Does home dir. already exist? */
523 if (stat(homedir
, &statbuf
) == 0) {
524 /* directory exists - don't try to create */
527 if (check_perm(statbuf
, uid
, gid
, S_IXOTH
) != 0)
528 errmsg(M_NO_PERM
, logname
, homedir
);
532 * if shell, skel_dir are provided, check their validity.
536 valid_input(SHELL
, shell
);
538 shell
= usrdefs
->defshell
;
540 if (skel_dir
!= NULL
)
541 valid_input(SKELDIR
, skel_dir
);
543 skel_dir
= usrdefs
->defskel
;
545 if (inactstr
!= NULL
) {
546 /* convert inactstr to integer */
547 inact
= strtol(inactstr
, &ptr
, 10);
548 if (*ptr
|| inact
< 0) {
549 errmsg(M_INVALID
, inactstr
, "inactivity period");
552 } else inact
= usrdefs
->definact
;
554 /* expiration string is a date, newer than today */
555 if (expirestr
!= NULL
) {
557 if (valid_expire(expirestr
, (time_t *)0) == INVALID
) {
558 errmsg(M_INVALID
, expirestr
, "expiration date");
561 usrdefs
->defexpire
= expirestr
;
563 /* Unset the expiration date */
564 expirestr
= (char *)0;
566 } else expirestr
= usrdefs
->defexpire
;
570 /* must now call passmgmt */
572 /* set up arguments to passmgmt in nargv array */
573 nargv
= malloc((30 + nkeys
* 2) * sizeof (char *));
575 nargv
[argindex
++] = PASSMGMT
;
576 nargv
[argindex
++] = "-a"; /* add */
578 if (comment
!= NULL
) {
580 nargv
[argindex
++] = "-c";
581 nargv
[argindex
++] = comment
;
584 /* flags for home directory */
585 nargv
[argindex
++] = "-h";
586 nargv
[argindex
++] = homedir
;
589 nargv
[argindex
++] = "-g";
590 (void) sprintf(gidstring
, "%u", gid
);
591 nargv
[argindex
++] = gidstring
;
594 nargv
[argindex
++] = "-s";
595 nargv
[argindex
++] = shell
;
598 nargv
[argindex
++] = "-f";
599 (void) sprintf(inactstring
, "%ld", inact
);
600 nargv
[argindex
++] = inactstring
;
602 /* set expiration date */
603 if (expirestr
!= NULL
) {
604 nargv
[argindex
++] = "-e";
605 nargv
[argindex
++] = expirestr
;
609 nargv
[argindex
++] = "-u";
610 (void) sprintf(uidstring
, "%u", uid
);
611 nargv
[argindex
++] = uidstring
;
613 if (oflag
) nargv
[argindex
++] = "-o";
616 addkey_args(nargv
, &argindex
);
618 /* finally - login name */
619 nargv
[argindex
++] = logname
;
621 /* set the last to null */
622 nargv
[argindex
++] = NULL
;
624 /* now call passmgmt */
627 * If call_passmgmt fails for any reason other than PEX_BADUID, exit
628 * is invoked with an appropriate error message. If PEX_BADUID is
629 * returned, then if the user specified the ID, exit is invoked
630 * with an appropriate error message. Otherwise we try to pick a
631 * different ID and try again. If we run out of IDs, i.e. no more
632 * users can be created, then -1 is returned and we terminate via exit.
633 * If PEX_BUSY is returned we increment a count, since we will stop
634 * trying if PEX_BUSY reaches 3. For PEX_SUCCESS we immediately
635 * terminate the loop.
637 while (busy
< 3 && ret
!= PEX_SUCCESS
) {
638 switch (ret
= call_passmgmt(nargv
)) {
644 case PEX_HOSED_FILES
:
645 errmsg(M_HOSED_FILES
);
646 exit(EX_INCONSISTENT
);
651 /* should NEVER occur that passmgmt usage is wrong */
652 if (is_role(usertype
))
661 * The uid has been taken. If it was specified by a
662 * user, then we must fail. Otherwise, keep trying
663 * to get a good uid until we run out of IDs.
665 if (uidstr
!= NULL
) {
666 errmsg(M_UID_USED
, uid
);
669 if (findnextuid(DEFRID
+1, MAXUID
, &uid
) != 0) {
670 errmsg(M_INVALID
, "default id",
674 (void) sprintf(uidstring
, "%u", uid
);
680 errmsg(M_USED
, logname
);
681 exit(EX_NAME_EXISTS
);
685 errmsg(M_UPDATE
, "created");
691 errmsg(M_UPDATE
, "created");
695 /* add group entry */
696 if ((grps
!= NULL
) && edit_group(logname
, (char *)0, gidlist
, 0)) {
697 errmsg(M_UPDATE
, "created");
702 /* update project database */
703 if ((projects
!= NULL
) &&
704 edit_project(logname
, (char *)NULL
, projlist
, 0)) {
705 errmsg(M_UPDATE
, "created");
710 /* create home directory */
712 zfs_flags
= get_default_zfs_flags();
714 if (zflag
|| mflag
> 1)
715 zfs_flags
|= MANAGE_ZFS
;
717 zfs_flags
&= ~MANAGE_ZFS
;
718 ret
= create_home(homedir
, skel_dir
, uid
, gid
, zfs_flags
);
720 if (ret
!= EX_SUCCESS
) {
721 (void) edit_project(logname
, (char *)NULL
, (projid_t
**)NULL
,
723 (void) edit_group(logname
, (char *)0, (int **)0, 1);
742 switch (call_passmgmt(nargv
)) {
747 /* should NEVER occur that passmgmt usage is wrong */
748 if (is_role(usertype
))
755 /* uid is used - shouldn't happen but print message anyway */
756 errmsg(M_UID_USED
, uid
);
761 errmsg(M_USED
, logname
);
765 errmsg(M_UPDATE
, "created");
770 /* Check the validity for shell, base_dir and skel_dir */
773 valid_input(path_opt_t opt
, const char *input
)
777 if (REL_PATH(input
)) {
778 errmsg(M_RELPATH
, input
);
781 if (stat(input
, &statbuf
) == -1) {
782 errmsg(M_INVALID
, input
, "path");
786 if (!S_ISREG(statbuf
.st_mode
) ||
787 (statbuf
.st_mode
& 0555) != 0555) {
788 errmsg(M_INVALID
, input
, "shell");
792 if (!S_ISDIR(statbuf
.st_mode
)) {
793 errmsg(M_INVALID
, input
, "directory");