8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / oamuser / user / useradd.c
blobde4f97e9530fb5c1fad37300eab8ca8ce5003f84
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 <errno.h>
45 #include <project.h>
46 #include <unistd.h>
47 #include <user_attr.h>
48 #include <libcmdutils.h>
49 #include "users.h"
50 #include "messages.h"
51 #include "userdisp.h"
52 #include "funcs.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 ...]]
60 * [ -K key=value ]
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:
70 * uid - an integer
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
79 * in auth_attr(4).
80 * profile - One or more comma separated execution profiles defined
81 * in prof_attr(4)
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 */
106 char *cmdname;
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 */
128 typedef enum {
129 BASEDIR = 0,
130 SKELDIR,
131 SHELL
132 } path_opt_t;
135 static void valid_input(path_opt_t, const char *);
138 main(argc, argv)
139 int argc;
140 char *argv[];
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 */
146 struct group *g_ptr;
147 struct project p_ptr;
148 char mybuf[PROJECT_BUFSZ];
149 struct stat statbuf; /* status buffer for stat */
150 int warning;
151 int busy = 0;
152 char **nargv; /* arguments for execvp of passmgmt */
153 int argindex; /* argument index into nargv */
154 int zfs_flags = 0; /* create_home flags */
156 cmdname = argv[0];
158 if (geteuid() != 0) {
159 errmsg(M_PERM_DENIED);
160 exit(EX_NO_PERM);
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)
170 switch (ch) {
171 case 'b':
172 base_dir = optarg;
173 break;
175 case 'c':
176 comment = optarg;
177 break;
179 case 'D':
180 Dflag++;
181 break;
183 case 'd':
184 dir = optarg;
185 break;
187 case 'e':
188 expirestr = optarg;
189 break;
191 case 'f':
192 inactstr = optarg;
193 break;
195 case 'G':
196 grps = optarg;
197 break;
199 case 'g':
200 group = optarg;
201 break;
203 case 'k':
204 skel_dir = optarg;
205 break;
207 case 'm':
208 mflag++;
209 break;
211 case 'o':
212 oflag++;
213 break;
215 case 'p':
216 projects = optarg;
217 break;
219 case 's':
220 shell = optarg;
221 break;
223 case 'u':
224 uidstr = optarg;
225 break;
227 case 'Z':
228 Zflag++;
229 break;
231 case 'z':
232 zflag++;
233 break;
235 case 'A':
236 change_key(USERATTR_AUTHS_KW, optarg);
237 break;
239 case 'P':
240 change_key(USERATTR_PROFILES_KW, optarg);
241 break;
243 case 'R':
244 if (is_role(usertype)) {
245 errmsg(M_ARUSAGE);
246 exit(EX_SYNTAX);
248 change_key(USERATTR_ROLES_KW, optarg);
249 break;
251 case 'K':
252 change_key(NULL, optarg);
253 break;
255 default:
256 case '?':
257 if (is_role(usertype))
258 errmsg(M_ARUSAGE);
259 else
260 errmsg(M_AUSAGE);
261 exit(EX_SYNTAX);
264 if (((!mflag) && (zflag || Zflag)) || (zflag && Zflag) ||
265 (mflag > 1 && (zflag || Zflag))) {
266 if (is_role(usertype))
267 errmsg(M_ARUSAGE);
268 else
269 errmsg(M_AUSAGE);
270 exit(EX_SYNTAX);
274 /* get defaults for adding new users */
275 usrdefs = getusrdef(usertype);
277 if (Dflag) {
278 /* DISPLAY mode */
280 /* check syntax */
281 if (optind != argc) {
282 if (is_role(usertype))
283 errmsg(M_ARUSAGE);
284 else
285 errmsg(M_AUSAGE);
286 exit(EX_SYNTAX);
289 if (uidstr != NULL || oflag || grps != NULL ||
290 dir != NULL || mflag || comment != NULL) {
291 if (is_role(usertype))
292 errmsg(M_ARUSAGE);
293 else
294 errmsg(M_AUSAGE);
295 exit(EX_SYNTAX);
298 /* Group must be an existing group */
299 if (group != NULL) {
300 switch (valid_group(group, &g_ptr, &warning)) {
301 case INVALID:
302 errmsg(M_INVALID, group, "group id");
303 exit(EX_BADARG);
304 /*NOTREACHED*/
305 case TOOBIG:
306 errmsg(M_TOOBIG, "gid", group);
307 exit(EX_BADARG);
308 /*NOTREACHED*/
309 case RESERVED:
310 case UNIQUE:
311 errmsg(M_GRP_NOTUSED, group);
312 exit(EX_NAME_NOT_EXIST);
314 if (warning)
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)) {
326 case INVALID:
327 errmsg(M_INVALID, projects, "project id");
328 exit(EX_BADARG);
329 /*NOTREACHED*/
330 case TOOBIG:
331 errmsg(M_TOOBIG, "projid", projects);
332 exit(EX_BADARG);
333 /*NOTREACHED*/
334 case UNIQUE:
335 errmsg(M_PROJ_NOTUSED, projects);
336 exit(EX_NAME_NOT_EXIST);
338 if (warning)
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");
358 exit(EX_BADARG);
361 usrdefs->definact = inact;
364 /* expiration string is a date, newer than today */
365 if (expirestr != NULL) {
366 if (*expirestr) {
367 if (valid_expire(expirestr, (time_t *)0)
368 == INVALID) {
369 errmsg(M_INVALID, expirestr,
370 "expiration date");
371 exit(EX_BADARG);
373 usrdefs->defexpire = expirestr;
374 } else
375 /* Unset the expiration date */
376 usrdefs->defexpire = "";
379 if (shell != NULL) {
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;
387 update_def(usrdefs);
389 /* change defaults for useradd */
390 if (putusrdef(usrdefs, usertype) < 0) {
391 errmsg(M_UPDATE, "created");
392 exit(EX_UPDATE);
395 /* Now, display */
396 dispusrdef(stdout, (D_ALL & ~D_RID), usertype);
397 exit(EX_SUCCESS);
401 /* ADD mode */
403 /* check syntax */
404 if (optind != argc - 1 || (skel_dir != NULL && !mflag)) {
405 if (is_role(usertype))
406 errmsg(M_ARUSAGE);
407 else
408 errmsg(M_AUSAGE);
409 exit(EX_SYNTAX);
412 logname = argv[optind];
413 switch (valid_login(logname, (struct passwd **)NULL, &warning)) {
414 case INVALID:
415 errmsg(M_INVALID, logname, "login name");
416 exit(EX_BADARG);
417 /*NOTREACHED*/
419 case NOTUNIQUE:
420 errmsg(M_USED, logname);
421 exit(EX_NAME_EXISTS);
422 /*NOTREACHED*/
424 case LONGNAME:
425 errmsg(M_TOO_LONG, logname);
426 exit(EX_BADARG);
427 /*NOTREACHED*/
430 if (warning)
431 warningmsg(warning, logname);
432 if (uidstr != NULL) {
433 /* convert uidstr to integer */
434 errno = 0;
435 uid = (uid_t)strtol(uidstr, &ptr, (int)10);
436 if (*ptr || errno == ERANGE) {
437 errmsg(M_INVALID, uidstr, "user id");
438 exit(EX_BADARG);
441 switch (valid_uid(uid, NULL)) {
442 case NOTUNIQUE:
443 if (!oflag) {
444 /* override not specified */
445 errmsg(M_UID_USED, uid);
446 exit(EX_ID_EXISTS);
448 break;
449 case RESERVED:
450 errmsg(M_RESERVED, uid);
451 break;
452 case TOOBIG:
453 errmsg(M_TOOBIG, "uid", uid);
454 exit(EX_BADARG);
455 break;
458 } else {
460 if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
461 errmsg(M_INVALID, "default id", "user id");
462 exit(EX_ID_EXISTS);
466 if (group != NULL) {
467 switch (valid_group(group, &g_ptr, &warning)) {
468 case INVALID:
469 errmsg(M_INVALID, group, "group id");
470 exit(EX_BADARG);
471 /*NOTREACHED*/
472 case TOOBIG:
473 errmsg(M_TOOBIG, "gid", group);
474 exit(EX_BADARG);
475 /*NOTREACHED*/
476 case RESERVED:
477 case UNIQUE:
478 errmsg(M_GRP_NOTUSED, group);
479 exit(EX_NAME_NOT_EXIST);
480 /*NOTREACHED*/
483 if (warning)
484 warningmsg(warning, group);
485 gid = g_ptr->gr_gid;
487 } else gid = usrdefs->defgroup;
489 if (grps != NULL) {
490 if (!*grps)
491 /* ignore -G "" */
492 grps = (char *)0;
493 else if (!(gidlist = valid_lgroup(grps, gid)))
494 exit(EX_BADARG);
497 if (projects != NULL) {
498 if (! *projects)
499 projects = (char *)0;
500 else if (! (projlist = valid_lproject(projects)))
501 exit(EX_BADARG);
504 /* if base_dir is provided, check its validity; otherwise default */
505 if (base_dir != NULL)
506 valid_input(BASEDIR, base_dir);
507 else
508 base_dir = usrdefs->defparent;
510 if (dir == NULL) {
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);
516 exit(EX_BADARG);
518 } else
519 (void) strcpy(homedir, dir);
521 if (mflag) {
522 /* Does home dir. already exist? */
523 if (stat(homedir, &statbuf) == 0) {
524 /* directory exists - don't try to create */
525 mflag = 0;
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.
533 * Otherwise default.
535 if (shell != NULL)
536 valid_input(SHELL, shell);
537 else
538 shell = usrdefs->defshell;
540 if (skel_dir != NULL)
541 valid_input(SKELDIR, skel_dir);
542 else
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");
550 exit(EX_BADARG);
552 } else inact = usrdefs->definact;
554 /* expiration string is a date, newer than today */
555 if (expirestr != NULL) {
556 if (*expirestr) {
557 if (valid_expire(expirestr, (time_t *)0) == INVALID) {
558 errmsg(M_INVALID, expirestr, "expiration date");
559 exit(EX_BADARG);
561 usrdefs->defexpire = expirestr;
562 } else
563 /* Unset the expiration date */
564 expirestr = (char *)0;
566 } else expirestr = usrdefs->defexpire;
568 import_def(usrdefs);
570 /* must now call passmgmt */
572 /* set up arguments to passmgmt in nargv array */
573 nargv = malloc((30 + nkeys * 2) * sizeof (char *));
574 argindex = 0;
575 nargv[argindex++] = PASSMGMT;
576 nargv[argindex++] = "-a"; /* add */
578 if (comment != NULL) {
579 /* comment */
580 nargv[argindex++] = "-c";
581 nargv[argindex++] = comment;
584 /* flags for home directory */
585 nargv[argindex++] = "-h";
586 nargv[argindex++] = homedir;
588 /* set gid flag */
589 nargv[argindex++] = "-g";
590 (void) sprintf(gidstring, "%u", gid);
591 nargv[argindex++] = gidstring;
593 /* shell */
594 nargv[argindex++] = "-s";
595 nargv[argindex++] = shell;
597 /* set inactive */
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;
608 /* set uid flag */
609 nargv[argindex++] = "-u";
610 (void) sprintf(uidstring, "%u", uid);
611 nargv[argindex++] = uidstring;
613 if (oflag) nargv[argindex++] = "-o";
615 if (nkeys > 1)
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 */
625 ret = PEX_FAILED;
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)) {
639 case PEX_SUCCESS:
640 break;
641 case PEX_BUSY:
642 busy++;
643 break;
644 case PEX_HOSED_FILES:
645 errmsg(M_HOSED_FILES);
646 exit(EX_INCONSISTENT);
647 break;
649 case PEX_SYNTAX:
650 case PEX_BADARG:
651 /* should NEVER occur that passmgmt usage is wrong */
652 if (is_role(usertype))
653 errmsg(M_ARUSAGE);
654 else
655 errmsg(M_AUSAGE);
656 exit(EX_SYNTAX);
657 break;
659 case PEX_BADUID:
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);
667 exit(EX_ID_EXISTS);
668 } else {
669 if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
670 errmsg(M_INVALID, "default id",
671 "user id");
672 exit(EX_ID_EXISTS);
674 (void) sprintf(uidstring, "%u", uid);
676 break;
678 case PEX_BADNAME:
679 /* invalid loname */
680 errmsg(M_USED, logname);
681 exit(EX_NAME_EXISTS);
682 break;
684 default:
685 errmsg(M_UPDATE, "created");
686 exit(ret);
687 break;
690 if (busy == 3) {
691 errmsg(M_UPDATE, "created");
692 exit(ret);
695 /* add group entry */
696 if ((grps != NULL) && edit_group(logname, (char *)0, gidlist, 0)) {
697 errmsg(M_UPDATE, "created");
698 cleanup(logname);
699 exit(EX_UPDATE);
702 /* update project database */
703 if ((projects != NULL) &&
704 edit_project(logname, (char *)NULL, projlist, 0)) {
705 errmsg(M_UPDATE, "created");
706 cleanup(logname);
707 exit(EX_UPDATE);
710 /* create home directory */
711 if (mflag) {
712 zfs_flags = get_default_zfs_flags();
714 if (zflag || mflag > 1)
715 zfs_flags |= MANAGE_ZFS;
716 else if (Zflag)
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);
724 cleanup(logname);
725 exit(EX_HOMEDIR);
728 return (ret);
731 static void
732 cleanup(logname)
733 char *logname;
735 char *nargv[4];
737 nargv[0] = PASSMGMT;
738 nargv[1] = "-d";
739 nargv[2] = logname;
740 nargv[3] = NULL;
742 switch (call_passmgmt(nargv)) {
743 case PEX_SUCCESS:
744 break;
746 case PEX_SYNTAX:
747 /* should NEVER occur that passmgmt usage is wrong */
748 if (is_role(usertype))
749 errmsg(M_ARUSAGE);
750 else
751 errmsg(M_AUSAGE);
752 break;
754 case PEX_BADUID:
755 /* uid is used - shouldn't happen but print message anyway */
756 errmsg(M_UID_USED, uid);
757 break;
759 case PEX_BADNAME:
760 /* invalid loname */
761 errmsg(M_USED, logname);
762 break;
764 default:
765 errmsg(M_UPDATE, "created");
766 break;
770 /* Check the validity for shell, base_dir and skel_dir */
772 void
773 valid_input(path_opt_t opt, const char *input)
775 struct stat statbuf;
777 if (REL_PATH(input)) {
778 errmsg(M_RELPATH, input);
779 exit(EX_BADARG);
781 if (stat(input, &statbuf) == -1) {
782 errmsg(M_INVALID, input, "path");
783 exit(EX_BADARG);
785 if (opt == SHELL) {
786 if (!S_ISREG(statbuf.st_mode) ||
787 (statbuf.st_mode & 0555) != 0555) {
788 errmsg(M_INVALID, input, "shell");
789 exit(EX_BADARG);
791 } else {
792 if (!S_ISDIR(statbuf.st_mode)) {
793 errmsg(M_INVALID, input, "directory");
794 exit(EX_BADARG);