1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2013 Gary Mills
23 *
24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
30
31 /*
32 * Copyright (c) 2013 RackTop Systems.
33 */
34
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"
53
54 /*
55 * useradd [-u uid [-o] | -g group | -G group [[, group]...] | -d dir [-m]
56 * | -s shell | -c comment | -k skel_dir | -b base_dir] ]
57 * [ -A authorization [, authorization ...]]
58 * [ -P profile [, profile ...]]
59 * [ -K key=value ]
60 * [ -R role [, role ...]] [-p project [, project ...]] login
61 * useradd -D [ -g group ] [ -b base_dir | -f inactive | -e expire |
62 * -s shell | -k skel_dir ]
63 * [ -A authorization [, authorization ...]]
64 * [ -P profile [, profile ...]] [ -K key=value ]
65 * [ -R role [, role ...]] [-p project [, project ...]] login
66 *
67 * This command adds new user logins to the system. Arguments are:
68 *
69 * uid - an integer
70 * group - an existing group's integer ID or char string name
71 * dir - home directory
72 * shell - a program to be used as a shell
73 * comment - any text string
74 * skel_dir - a skeleton directory
75 * base_dir - a directory
76 * login - a string of printable chars except colon(:)
77 * authorization - One or more comma separated authorizations defined
78 * in auth_attr(4).
79 * profile - One or more comma separated execution profiles defined
80 * in prof_attr(4)
81 * role - One or more comma-separated role names defined in user_attr(4)
82 * project - One or more comma-separated project names or numbers
83 *
84 */
85
86 extern struct userdefs *getusrdef();
87 extern void dispusrdef();
88
89 static void cleanup();
90
91 extern int check_perm(), valid_expire();
92 extern int putusrdef(), valid_uid();
93 extern int call_passmgmt(), edit_group(), create_home();
94 extern int edit_project();
95 extern int **valid_lgroup();
96 extern projid_t **valid_lproject();
97 extern void update_def(struct userdefs *);
98 extern void import_def(struct userdefs *);
99
100 static uid_t uid; /* new uid */
101 static char *logname; /* login name to add */
102 static struct userdefs *usrdefs; /* defaults for useradd */
103
104 char *cmdname;
105
106 static char homedir[ PATH_MAX + 1 ]; /* home directory */
107 static char gidstring[32]; /* group id string representation */
108 static gid_t gid; /* gid of new login */
109 static char uidstring[32]; /* user id string representation */
110 static char *uidstr = NULL; /* uid from command line */
111 static char *base_dir = NULL; /* base_dir from command line */
112 static char *group = NULL; /* group from command line */
113 static char *grps = NULL; /* multi groups from command line */
114 static char *dir = NULL; /* home dir from command line */
115 static char *shell = NULL; /* shell from command line */
116 static char *comment = NULL; /* comment from command line */
117 static char *skel_dir = NULL; /* skel dir from command line */
118 static long inact; /* inactive days */
119 static char *inactstr = NULL; /* inactive from command line */
120 static char inactstring[10]; /* inactivity string representation */
121 static char *expirestr = NULL; /* expiration date from command line */
122 static char *projects = NULL; /* project id's from command line */
123
124 static char *usertype = NULL; /* type of user, either role or normal */
125
126 typedef enum {
127 BASEDIR = 0,
128 SKELDIR,
129 SHELL
130 } path_opt_t;
131
132
133 static void valid_input(path_opt_t, const char *);
134
135 int
main(argc,argv)136 main(argc, argv)
137 int argc;
138 char *argv[];
139 {
140 int ch, ret, mflag = 0, oflag = 0, Dflag = 0, **gidlist = NULL;
141 projid_t **projlist = NULL;
142 char *ptr; /* loc in a str, may be set by strtol */
143 struct group *g_ptr;
144 struct project p_ptr;
145 char mybuf[PROJECT_BUFSZ];
146 struct stat statbuf; /* status buffer for stat */
147 int warning;
148 int busy = 0;
149 char **nargv; /* arguments for execvp of passmgmt */
150 int argindex; /* argument index into nargv */
151
152 cmdname = argv[0];
153
154 if (geteuid() != 0) {
155 errmsg(M_PERM_DENIED);
156 exit(EX_NO_PERM);
157 }
158
159 opterr = 0; /* no print errors from getopt */
160 usertype = getusertype(argv[0]);
161
162 change_key(USERATTR_TYPE_KW, usertype);
163
164 while ((ch = getopt(argc, argv,
165 "b:c:Dd:e:f:G:g:k:mop:s:u:A:P:R:K:")) != EOF)
166 switch (ch) {
167 case 'b':
168 base_dir = optarg;
169 break;
170
171 case 'c':
172 comment = optarg;
173 break;
174
175 case 'D':
176 Dflag++;
177 break;
178
179 case 'd':
180 dir = optarg;
181 break;
182
183 case 'e':
184 expirestr = optarg;
185 break;
186
187 case 'f':
188 inactstr = optarg;
189 break;
190
191 case 'G':
192 grps = optarg;
193 break;
194
195 case 'g':
196 group = optarg;
197 break;
198
199 case 'k':
200 skel_dir = optarg;
201 break;
202
203 case 'm':
204 mflag++;
205 break;
206
207 case 'o':
208 oflag++;
209 break;
210
211 case 'p':
212 projects = optarg;
213 break;
214
215 case 's':
216 shell = optarg;
217 break;
218
219 case 'u':
220 uidstr = optarg;
221 break;
222
223 case 'A':
224 change_key(USERATTR_AUTHS_KW, optarg);
225 break;
226
227 case 'P':
228 change_key(USERATTR_PROFILES_KW, optarg);
229 break;
230
231 case 'R':
232 if (is_role(usertype)) {
233 errmsg(M_ARUSAGE);
234 exit(EX_SYNTAX);
235 }
236 change_key(USERATTR_ROLES_KW, optarg);
237 break;
238
239 case 'K':
240 change_key(NULL, optarg);
241 break;
242
243 default:
244 case '?':
245 if (is_role(usertype))
246 errmsg(M_ARUSAGE);
247 else
248 errmsg(M_AUSAGE);
249 exit(EX_SYNTAX);
250 }
251
252 /* get defaults for adding new users */
253 usrdefs = getusrdef(usertype);
254
255 if (Dflag) {
256 /* DISPLAY mode */
257
258 /* check syntax */
259 if (optind != argc) {
260 if (is_role(usertype))
261 errmsg(M_ARUSAGE);
262 else
263 errmsg(M_AUSAGE);
264 exit(EX_SYNTAX);
265 }
266
267 if (uidstr != NULL || oflag || grps != NULL ||
268 dir != NULL || mflag || comment != NULL) {
269 if (is_role(usertype))
270 errmsg(M_ARUSAGE);
271 else
272 errmsg(M_AUSAGE);
273 exit(EX_SYNTAX);
274 }
275
276 /* Group must be an existing group */
277 if (group != NULL) {
278 switch (valid_group(group, &g_ptr, &warning)) {
279 case INVALID:
280 errmsg(M_INVALID, group, "group id");
281 exit(EX_BADARG);
282 /*NOTREACHED*/
283 case TOOBIG:
284 errmsg(M_TOOBIG, "gid", group);
285 exit(EX_BADARG);
286 /*NOTREACHED*/
287 case RESERVED:
288 case UNIQUE:
289 errmsg(M_GRP_NOTUSED, group);
290 exit(EX_NAME_NOT_EXIST);
291 }
292 if (warning)
293 warningmsg(warning, group);
294
295 usrdefs->defgroup = g_ptr->gr_gid;
296 usrdefs->defgname = g_ptr->gr_name;
297
298 }
299
300 /* project must be an existing project */
301 if (projects != NULL) {
302 switch (valid_project(projects, &p_ptr, mybuf,
303 sizeof (mybuf), &warning)) {
304 case INVALID:
305 errmsg(M_INVALID, projects, "project id");
306 exit(EX_BADARG);
307 /*NOTREACHED*/
308 case TOOBIG:
309 errmsg(M_TOOBIG, "projid", projects);
310 exit(EX_BADARG);
311 /*NOTREACHED*/
312 case UNIQUE:
313 errmsg(M_PROJ_NOTUSED, projects);
314 exit(EX_NAME_NOT_EXIST);
315 }
316 if (warning)
317 warningmsg(warning, projects);
318
319 usrdefs->defproj = p_ptr.pj_projid;
320 usrdefs->defprojname = p_ptr.pj_name;
321 }
322
323 /* base_dir must be an existing directory */
324 if (base_dir != NULL) {
325 valid_input(BASEDIR, base_dir);
326 usrdefs->defparent = base_dir;
327 }
328
329 /* inactivity period is an integer */
330 if (inactstr != NULL) {
331 /* convert inactstr to integer */
332 inact = strtol(inactstr, &ptr, 10);
333 if (*ptr || inact < 0) {
334 errmsg(M_INVALID, inactstr,
335 "inactivity period");
336 exit(EX_BADARG);
337 }
338
339 usrdefs->definact = inact;
340 }
341
342 /* expiration string is a date, newer than today */
343 if (expirestr != NULL) {
344 if (*expirestr) {
345 if (valid_expire(expirestr, (time_t *)0)
346 == INVALID) {
347 errmsg(M_INVALID, expirestr,
348 "expiration date");
349 exit(EX_BADARG);
350 }
351 usrdefs->defexpire = expirestr;
352 } else
353 /* Unset the expiration date */
354 usrdefs->defexpire = "";
355 }
356
357 if (shell != NULL) {
358 valid_input(SHELL, shell);
359 usrdefs->defshell = shell;
360 }
361 if (skel_dir != NULL) {
362 valid_input(SKELDIR, skel_dir);
363 usrdefs->defskel = skel_dir;
364 }
365 update_def(usrdefs);
366
367 /* change defaults for useradd */
368 if (putusrdef(usrdefs, usertype) < 0) {
369 errmsg(M_UPDATE, "created");
370 exit(EX_UPDATE);
371 }
372
373 /* Now, display */
374 dispusrdef(stdout, (D_ALL & ~D_RID), usertype);
375 exit(EX_SUCCESS);
376
377 }
378
379 /* ADD mode */
380
381 /* check syntax */
382 if (optind != argc - 1 || (skel_dir != NULL && !mflag)) {
383 if (is_role(usertype))
384 errmsg(M_ARUSAGE);
385 else
386 errmsg(M_AUSAGE);
387 exit(EX_SYNTAX);
388 }
389
390 logname = argv[optind];
391 switch (valid_login(logname, (struct passwd **)NULL, &warning)) {
392 case INVALID:
393 errmsg(M_INVALID, logname, "login name");
394 exit(EX_BADARG);
395 /*NOTREACHED*/
396
397 case NOTUNIQUE:
398 errmsg(M_USED, logname);
399 exit(EX_NAME_EXISTS);
400 /*NOTREACHED*/
401
402 case LONGNAME:
403 errmsg(M_TOO_LONG, logname);
404 exit(EX_BADARG);
405 /*NOTREACHED*/
406 }
407
408 if (warning)
409 warningmsg(warning, logname);
410 if (uidstr != NULL) {
411 /* convert uidstr to integer */
412 errno = 0;
413 uid = (uid_t)strtol(uidstr, &ptr, (int)10);
414 if (*ptr || errno == ERANGE) {
415 errmsg(M_INVALID, uidstr, "user id");
416 exit(EX_BADARG);
417 }
418
419 switch (valid_uid(uid, NULL)) {
420 case NOTUNIQUE:
421 if (!oflag) {
422 /* override not specified */
423 errmsg(M_UID_USED, uid);
424 exit(EX_ID_EXISTS);
425 }
426 break;
427 case RESERVED:
428 errmsg(M_RESERVED, uid);
429 break;
430 case TOOBIG:
431 errmsg(M_TOOBIG, "uid", uid);
432 exit(EX_BADARG);
433 break;
434 }
435
436 } else {
437
438 if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
439 errmsg(M_INVALID, "default id", "user id");
440 exit(EX_ID_EXISTS);
441 }
442 }
443
444 if (group != NULL) {
445 switch (valid_group(group, &g_ptr, &warning)) {
446 case INVALID:
447 errmsg(M_INVALID, group, "group id");
448 exit(EX_BADARG);
449 /*NOTREACHED*/
450 case TOOBIG:
451 errmsg(M_TOOBIG, "gid", group);
452 exit(EX_BADARG);
453 /*NOTREACHED*/
454 case RESERVED:
455 case UNIQUE:
456 errmsg(M_GRP_NOTUSED, group);
457 exit(EX_NAME_NOT_EXIST);
458 /*NOTREACHED*/
459 }
460
461 if (warning)
462 warningmsg(warning, group);
463 gid = g_ptr->gr_gid;
464
465 } else gid = usrdefs->defgroup;
466
467 if (grps != NULL) {
468 if (!*grps)
469 /* ignore -G "" */
470 grps = (char *)0;
471 else if (!(gidlist = valid_lgroup(grps, gid)))
472 exit(EX_BADARG);
473 }
474
475 if (projects != NULL) {
476 if (! *projects)
477 projects = (char *)0;
478 else if (! (projlist = valid_lproject(projects)))
479 exit(EX_BADARG);
480 }
481
482 /* if base_dir is provided, check its validity; otherwise default */
483 if (base_dir != NULL)
484 valid_input(BASEDIR, base_dir);
485 else
486 base_dir = usrdefs->defparent;
487
488 if (dir == NULL) {
489 /* set homedir to home directory made from base_dir */
490 (void) sprintf(homedir, "%s/%s", base_dir, logname);
491
492 } else if (REL_PATH(dir)) {
493 errmsg(M_RELPATH, dir);
494 exit(EX_BADARG);
495
496 } else
497 (void) strcpy(homedir, dir);
498
499 if (mflag) {
500 /* Does home dir. already exist? */
501 if (stat(homedir, &statbuf) == 0) {
502 /* directory exists - don't try to create */
503 mflag = 0;
504
505 if (check_perm(statbuf, uid, gid, S_IXOTH) != 0)
506 errmsg(M_NO_PERM, logname, homedir);
507 }
508 }
509 /*
510 * if shell, skel_dir are provided, check their validity.
511 * Otherwise default.
512 */
513 if (shell != NULL)
514 valid_input(SHELL, shell);
515 else
516 shell = usrdefs->defshell;
517
518 if (skel_dir != NULL)
519 valid_input(SKELDIR, skel_dir);
520 else
521 skel_dir = usrdefs->defskel;
522
523 if (inactstr != NULL) {
524 /* convert inactstr to integer */
525 inact = strtol(inactstr, &ptr, 10);
526 if (*ptr || inact < 0) {
527 errmsg(M_INVALID, inactstr, "inactivity period");
528 exit(EX_BADARG);
529 }
530 } else inact = usrdefs->definact;
531
532 /* expiration string is a date, newer than today */
533 if (expirestr != NULL) {
534 if (*expirestr) {
535 if (valid_expire(expirestr, (time_t *)0) == INVALID) {
536 errmsg(M_INVALID, expirestr, "expiration date");
537 exit(EX_BADARG);
538 }
539 usrdefs->defexpire = expirestr;
540 } else
541 /* Unset the expiration date */
542 expirestr = (char *)0;
543
544 } else expirestr = usrdefs->defexpire;
545
546 import_def(usrdefs);
547
548 /* must now call passmgmt */
549
550 /* set up arguments to passmgmt in nargv array */
551 nargv = malloc((30 + nkeys * 2) * sizeof (char *));
552 argindex = 0;
553 nargv[argindex++] = PASSMGMT;
554 nargv[argindex++] = "-a"; /* add */
555
556 if (comment != NULL) {
557 /* comment */
558 nargv[argindex++] = "-c";
559 nargv[argindex++] = comment;
560 }
561
562 /* flags for home directory */
563 nargv[argindex++] = "-h";
564 nargv[argindex++] = homedir;
565
566 /* set gid flag */
567 nargv[argindex++] = "-g";
568 (void) sprintf(gidstring, "%u", gid);
569 nargv[argindex++] = gidstring;
570
571 /* shell */
572 nargv[argindex++] = "-s";
573 nargv[argindex++] = shell;
574
575 /* set inactive */
576 nargv[argindex++] = "-f";
577 (void) sprintf(inactstring, "%ld", inact);
578 nargv[argindex++] = inactstring;
579
580 /* set expiration date */
581 if (expirestr != NULL) {
582 nargv[argindex++] = "-e";
583 nargv[argindex++] = expirestr;
584 }
585
586 /* set uid flag */
587 nargv[argindex++] = "-u";
588 (void) sprintf(uidstring, "%u", uid);
589 nargv[argindex++] = uidstring;
590
591 if (oflag) nargv[argindex++] = "-o";
592
593 if (nkeys > 1)
594 addkey_args(nargv, &argindex);
595
596 /* finally - login name */
597 nargv[argindex++] = logname;
598
599 /* set the last to null */
600 nargv[argindex++] = NULL;
601
602 /* now call passmgmt */
603 ret = PEX_FAILED;
604 /*
605 * If call_passmgmt fails for any reason other than PEX_BADUID, exit
606 * is invoked with an appropriate error message. If PEX_BADUID is
607 * returned, then if the user specified the ID, exit is invoked
608 * with an appropriate error message. Otherwise we try to pick a
609 * different ID and try again. If we run out of IDs, i.e. no more
610 * users can be created, then -1 is returned and we terminate via exit.
611 * If PEX_BUSY is returned we increment a count, since we will stop
612 * trying if PEX_BUSY reaches 3. For PEX_SUCCESS we immediately
613 * terminate the loop.
614 */
615 while (busy < 3 && ret != PEX_SUCCESS) {
616 switch (ret = call_passmgmt(nargv)) {
617 case PEX_SUCCESS:
618 break;
619 case PEX_BUSY:
620 busy++;
621 break;
622 case PEX_HOSED_FILES:
623 errmsg(M_HOSED_FILES);
624 exit(EX_INCONSISTENT);
625 break;
626
627 case PEX_SYNTAX:
628 case PEX_BADARG:
629 /* should NEVER occur that passmgmt usage is wrong */
630 if (is_role(usertype))
631 errmsg(M_ARUSAGE);
632 else
633 errmsg(M_AUSAGE);
634 exit(EX_SYNTAX);
635 break;
636
637 case PEX_BADUID:
638 /*
639 * The uid has been taken. If it was specified by a
640 * user, then we must fail. Otherwise, keep trying
641 * to get a good uid until we run out of IDs.
642 */
643 if (uidstr != NULL) {
644 errmsg(M_UID_USED, uid);
645 exit(EX_ID_EXISTS);
646 } else {
647 if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
648 errmsg(M_INVALID, "default id",
649 "user id");
650 exit(EX_ID_EXISTS);
651 }
652 (void) sprintf(uidstring, "%u", uid);
653 }
654 break;
655
656 case PEX_BADNAME:
657 /* invalid loname */
658 errmsg(M_USED, logname);
659 exit(EX_NAME_EXISTS);
660 break;
661
662 default:
663 errmsg(M_UPDATE, "created");
664 exit(ret);
665 break;
666 }
667 }
668 if (busy == 3) {
669 errmsg(M_UPDATE, "created");
670 exit(ret);
671 }
672
673 /* add group entry */
674 if ((grps != NULL) && edit_group(logname, (char *)0, gidlist, 0)) {
675 errmsg(M_UPDATE, "created");
676 cleanup(logname);
677 exit(EX_UPDATE);
678 }
679
680 /* update project database */
681 if ((projects != NULL) &&
682 edit_project(logname, (char *)NULL, projlist, 0)) {
683 errmsg(M_UPDATE, "created");
684 cleanup(logname);
685 exit(EX_UPDATE);
686 }
687
688 /* create home directory */
689 if (mflag &&
690 (create_home(homedir, skel_dir, uid, gid) != EX_SUCCESS)) {
691 (void) edit_group(logname, (char *)0, (int **)0, 1);
692 cleanup(logname);
693 exit(EX_HOMEDIR);
694 }
695
696 return (ret);
697 }
698
699 static void
cleanup(logname)700 cleanup(logname)
701 char *logname;
702 {
703 char *nargv[4];
704
705 nargv[0] = PASSMGMT;
706 nargv[1] = "-d";
707 nargv[2] = logname;
708 nargv[3] = NULL;
709
710 switch (call_passmgmt(nargv)) {
711 case PEX_SUCCESS:
712 break;
713
714 case PEX_SYNTAX:
715 /* should NEVER occur that passmgmt usage is wrong */
716 if (is_role(usertype))
717 errmsg(M_ARUSAGE);
718 else
719 errmsg(M_AUSAGE);
720 break;
721
722 case PEX_BADUID:
723 /* uid is used - shouldn't happen but print message anyway */
724 errmsg(M_UID_USED, uid);
725 break;
726
727 case PEX_BADNAME:
728 /* invalid loname */
729 errmsg(M_USED, logname);
730 break;
731
732 default:
733 errmsg(M_UPDATE, "created");
734 break;
735 }
736 }
737
738 /* Check the validity for shell, base_dir and skel_dir */
739
740 void
valid_input(path_opt_t opt,const char * input)741 valid_input(path_opt_t opt, const char *input)
742 {
743 struct stat statbuf;
744
745 if (REL_PATH(input)) {
746 errmsg(M_RELPATH, input);
747 exit(EX_BADARG);
748 }
749 if (stat(input, &statbuf) == -1) {
750 errmsg(M_INVALID, input, "path");
751 exit(EX_BADARG);
752 }
753 if (opt == SHELL) {
754 if (!S_ISREG(statbuf.st_mode) ||
755 (statbuf.st_mode & 0555) != 0555) {
756 errmsg(M_INVALID, input, "shell");
757 exit(EX_BADARG);
758 }
759 } else {
760 if (!S_ISDIR(statbuf.st_mode)) {
761 errmsg(M_INVALID, input, "directory");
762 exit(EX_BADARG);
763 }
764 }
765 }
766