dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / oamuser / user / useradd.c
blobe0c6987af48d00e5d966b63d655e1bea918a537b
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 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 */
105 char *cmdname;
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 */
127 typedef enum {
128 BASEDIR = 0,
129 SKELDIR,
130 SHELL
131 } path_opt_t;
134 static void valid_input(path_opt_t, const char *);
137 main(argc, argv)
138 int argc;
139 char *argv[];
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 */
145 struct group *g_ptr;
146 struct project p_ptr;
147 char mybuf[PROJECT_BUFSZ];
148 struct stat statbuf; /* status buffer for stat */
149 int warning;
150 int busy = 0;
151 char **nargv; /* arguments for execvp of passmgmt */
152 int argindex; /* argument index into nargv */
153 int zfs_flags = 0; /* create_home flags */
155 cmdname = argv[0];
157 if (geteuid() != 0) {
158 errmsg(M_PERM_DENIED);
159 exit(EX_NO_PERM);
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)
169 switch (ch) {
170 case 'b':
171 base_dir = optarg;
172 break;
174 case 'c':
175 comment = optarg;
176 break;
178 case 'D':
179 Dflag++;
180 break;
182 case 'd':
183 dir = optarg;
184 break;
186 case 'e':
187 expirestr = optarg;
188 break;
190 case 'f':
191 inactstr = optarg;
192 break;
194 case 'G':
195 grps = optarg;
196 break;
198 case 'g':
199 group = optarg;
200 break;
202 case 'k':
203 skel_dir = optarg;
204 break;
206 case 'm':
207 mflag++;
208 break;
210 case 'o':
211 oflag++;
212 break;
214 case 'p':
215 projects = optarg;
216 break;
218 case 's':
219 shell = optarg;
220 break;
222 case 'u':
223 uidstr = optarg;
224 break;
226 case 'Z':
227 Zflag++;
228 break;
230 case 'z':
231 zflag++;
232 break;
234 case 'A':
235 change_key(USERATTR_AUTHS_KW, optarg);
236 break;
238 case 'P':
239 change_key(USERATTR_PROFILES_KW, optarg);
240 break;
242 case 'R':
243 if (is_role(usertype)) {
244 errmsg(M_ARUSAGE);
245 exit(EX_SYNTAX);
247 change_key(USERATTR_ROLES_KW, optarg);
248 break;
250 case 'K':
251 change_key(NULL, optarg);
252 break;
254 default:
255 case '?':
256 if (is_role(usertype))
257 errmsg(M_ARUSAGE);
258 else
259 errmsg(M_AUSAGE);
260 exit(EX_SYNTAX);
263 if (((!mflag) && (zflag || Zflag)) || (zflag && Zflag) ||
264 (mflag > 1 && (zflag || Zflag))) {
265 if (is_role(usertype))
266 errmsg(M_ARUSAGE);
267 else
268 errmsg(M_AUSAGE);
269 exit(EX_SYNTAX);
273 /* get defaults for adding new users */
274 usrdefs = getusrdef(usertype);
276 if (Dflag) {
277 /* DISPLAY mode */
279 /* check syntax */
280 if (optind != argc) {
281 if (is_role(usertype))
282 errmsg(M_ARUSAGE);
283 else
284 errmsg(M_AUSAGE);
285 exit(EX_SYNTAX);
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 ||
293 skel_dir != NULL) {
294 if (is_role(usertype))
295 errmsg(M_ARUSAGE);
296 else
297 errmsg(M_AUSAGE);
298 exit(EX_SYNTAX);
301 /* Now, display */
302 dispusrdef(stdout, (D_ALL & ~D_RID), usertype);
303 exit(EX_SUCCESS);
307 /* ADD mode */
309 /* check syntax */
310 if (optind != argc - 1 || (skel_dir != NULL && !mflag)) {
311 if (is_role(usertype))
312 errmsg(M_ARUSAGE);
313 else
314 errmsg(M_AUSAGE);
315 exit(EX_SYNTAX);
318 logname = argv[optind];
319 switch (valid_login(logname, (struct passwd **)NULL, &warning)) {
320 case INVALID:
321 errmsg(M_INVALID, logname, "login name");
322 exit(EX_BADARG);
323 /*NOTREACHED*/
325 case NOTUNIQUE:
326 errmsg(M_USED, logname);
327 exit(EX_NAME_EXISTS);
328 /*NOTREACHED*/
330 case LONGNAME:
331 errmsg(M_TOO_LONG, logname);
332 exit(EX_BADARG);
333 /*NOTREACHED*/
336 if (warning)
337 warningmsg(warning, logname);
338 if (uidstr != NULL) {
339 /* convert uidstr to integer */
340 errno = 0;
341 uid = (uid_t)strtol(uidstr, &ptr, (int)10);
342 if (*ptr || errno == ERANGE) {
343 errmsg(M_INVALID, uidstr, "user id");
344 exit(EX_BADARG);
347 switch (valid_uid(uid, NULL)) {
348 case NOTUNIQUE:
349 if (!oflag) {
350 /* override not specified */
351 errmsg(M_UID_USED, uid);
352 exit(EX_ID_EXISTS);
354 break;
355 case RESERVED:
356 errmsg(M_RESERVED, uid);
357 break;
358 case TOOBIG:
359 errmsg(M_TOOBIG, "uid", uid);
360 exit(EX_BADARG);
361 break;
364 } else {
366 if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
367 errmsg(M_INVALID, "default id", "user id");
368 exit(EX_ID_EXISTS);
372 if (group != NULL) {
373 switch (valid_group(group, &g_ptr, &warning)) {
374 case INVALID:
375 errmsg(M_INVALID, group, "group id");
376 exit(EX_BADARG);
377 /*NOTREACHED*/
378 case TOOBIG:
379 errmsg(M_TOOBIG, "gid", group);
380 exit(EX_BADARG);
381 /*NOTREACHED*/
382 case RESERVED:
383 case UNIQUE:
384 errmsg(M_GRP_NOTUSED, group);
385 exit(EX_NAME_NOT_EXIST);
386 /*NOTREACHED*/
389 if (warning)
390 warningmsg(warning, group);
391 gid = g_ptr->gr_gid;
393 } else gid = usrdefs->defgroup;
395 if (grps != NULL) {
396 if (!*grps)
397 /* ignore -G "" */
398 grps = (char *)0;
399 else if (!(gidlist = valid_lgroup(grps, gid)))
400 exit(EX_BADARG);
403 if (projects != NULL) {
404 if (! *projects)
405 projects = (char *)0;
406 else if (! (projlist = valid_lproject(projects)))
407 exit(EX_BADARG);
410 /* if base_dir is provided, check its validity; otherwise default */
411 if (base_dir != NULL)
412 valid_input(BASEDIR, base_dir);
413 else
414 base_dir = usrdefs->defparent;
416 if (dir == NULL) {
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);
422 exit(EX_BADARG);
424 } else
425 (void) strcpy(homedir, dir);
427 if (mflag) {
428 /* Does home dir. already exist? */
429 if (stat(homedir, &statbuf) == 0) {
430 /* directory exists - don't try to create */
431 mflag = 0;
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.
439 * Otherwise default.
441 if (shell != NULL)
442 valid_input(SHELL, shell);
443 else
444 shell = usrdefs->defshell;
446 if (skel_dir != NULL)
447 valid_input(SKELDIR, skel_dir);
448 else
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");
456 exit(EX_BADARG);
458 } else inact = usrdefs->definact;
460 /* expiration string is a date, newer than today */
461 if (expirestr != NULL) {
462 if (*expirestr) {
463 if (valid_expire(expirestr, (time_t *)0) == INVALID) {
464 errmsg(M_INVALID, expirestr, "expiration date");
465 exit(EX_BADARG);
467 usrdefs->defexpire = expirestr;
468 } else
469 /* Unset the expiration date */
470 expirestr = (char *)0;
472 } else expirestr = usrdefs->defexpire;
474 import_def(usrdefs);
476 /* must now call passmgmt */
478 /* set up arguments to passmgmt in nargv array */
479 nargv = malloc((30 + nkeys * 2) * sizeof (char *));
480 argindex = 0;
481 nargv[argindex++] = PASSMGMT;
482 nargv[argindex++] = "-a"; /* add */
484 if (comment != NULL) {
485 /* comment */
486 nargv[argindex++] = "-c";
487 nargv[argindex++] = comment;
490 /* flags for home directory */
491 nargv[argindex++] = "-h";
492 nargv[argindex++] = homedir;
494 /* set gid flag */
495 nargv[argindex++] = "-g";
496 (void) sprintf(gidstring, "%u", gid);
497 nargv[argindex++] = gidstring;
499 /* shell */
500 nargv[argindex++] = "-s";
501 nargv[argindex++] = shell;
503 /* set inactive */
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;
514 /* set uid flag */
515 nargv[argindex++] = "-u";
516 (void) sprintf(uidstring, "%u", uid);
517 nargv[argindex++] = uidstring;
519 if (oflag) nargv[argindex++] = "-o";
521 if (nkeys > 1)
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 */
531 ret = PEX_FAILED;
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)) {
545 case PEX_SUCCESS:
546 break;
547 case PEX_BUSY:
548 busy++;
549 break;
550 case PEX_HOSED_FILES:
551 errmsg(M_HOSED_FILES);
552 exit(EX_INCONSISTENT);
553 break;
555 case PEX_SYNTAX:
556 case PEX_BADARG:
557 /* should NEVER occur that passmgmt usage is wrong */
558 if (is_role(usertype))
559 errmsg(M_ARUSAGE);
560 else
561 errmsg(M_AUSAGE);
562 exit(EX_SYNTAX);
563 break;
565 case PEX_BADUID:
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);
573 exit(EX_ID_EXISTS);
574 } else {
575 if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
576 errmsg(M_INVALID, "default id",
577 "user id");
578 exit(EX_ID_EXISTS);
580 (void) sprintf(uidstring, "%u", uid);
582 break;
584 case PEX_BADNAME:
585 /* invalid loname */
586 errmsg(M_USED, logname);
587 exit(EX_NAME_EXISTS);
588 break;
590 default:
591 errmsg(M_UPDATE, "created");
592 exit(ret);
593 break;
596 if (busy == 3) {
597 errmsg(M_UPDATE, "created");
598 exit(ret);
601 /* add group entry */
602 if ((grps != NULL) && edit_group(logname, (char *)0, gidlist, 0)) {
603 errmsg(M_UPDATE, "created");
604 cleanup(logname);
605 exit(EX_UPDATE);
608 /* update project database */
609 if ((projects != NULL) &&
610 edit_project(logname, NULL, projlist, 0)) {
611 errmsg(M_UPDATE, "created");
612 cleanup(logname);
613 exit(EX_UPDATE);
616 /* create home directory */
617 if (mflag) {
618 zfs_flags = get_default_zfs_flags();
620 if (zflag || mflag > 1)
621 zfs_flags |= MANAGE_ZFS;
622 else if (Zflag)
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);
630 cleanup(logname);
631 exit(EX_HOMEDIR);
634 return (ret);
637 static void
638 cleanup(logname)
639 char *logname;
641 char *nargv[4];
643 nargv[0] = PASSMGMT;
644 nargv[1] = "-d";
645 nargv[2] = logname;
646 nargv[3] = NULL;
648 switch (call_passmgmt(nargv)) {
649 case PEX_SUCCESS:
650 break;
652 case PEX_SYNTAX:
653 /* should NEVER occur that passmgmt usage is wrong */
654 if (is_role(usertype))
655 errmsg(M_ARUSAGE);
656 else
657 errmsg(M_AUSAGE);
658 break;
660 case PEX_BADUID:
661 /* uid is used - shouldn't happen but print message anyway */
662 errmsg(M_UID_USED, uid);
663 break;
665 case PEX_BADNAME:
666 /* invalid loname */
667 errmsg(M_USED, logname);
668 break;
670 default:
671 errmsg(M_UPDATE, "created");
672 break;
676 /* Check the validity for shell, base_dir and skel_dir */
678 void
679 valid_input(path_opt_t opt, const char *input)
681 struct stat statbuf;
683 if (REL_PATH(input)) {
684 errmsg(M_RELPATH, input);
685 exit(EX_BADARG);
687 if (stat(input, &statbuf) == -1) {
688 errmsg(M_INVALID, input, "path");
689 exit(EX_BADARG);
691 if (opt == SHELL) {
692 if (!S_ISREG(statbuf.st_mode) ||
693 (statbuf.st_mode & 0555) != 0555) {
694 errmsg(M_INVALID, input, "shell");
695 exit(EX_BADARG);
697 } else {
698 if (!S_ISDIR(statbuf.st_mode)) {
699 errmsg(M_INVALID, input, "directory");
700 exit(EX_BADARG);