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]...] 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 67 * 68 * This command adds new user logins to the system. Arguments are: 69 * 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 84 * 85 */ 86 87 extern struct userdefs *getusrdef(); 88 extern void dispusrdef(); 89 90 static void cleanup(); 91 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(); 101 102 static uid_t uid; /* new uid */ 103 static char *logname; /* login name to add */ 104 static struct userdefs *usrdefs; /* defaults for useradd */ 105 106 char *cmdname; 107 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 */ 125 126 static char *usertype = NULL; /* type of user, either role or normal */ 127 128 typedef enum { 129 BASEDIR = 0, 130 SKELDIR, 131 SHELL 132 } path_opt_t; 133 134 135 static void valid_input(path_opt_t, const char *); 136 137 int 138 main(argc, argv) 139 int argc; 140 char *argv[]; 141 { 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 */ 155 156 cmdname = argv[0]; 157 158 if (geteuid() != 0) { 159 errmsg(M_PERM_DENIED); 160 exit(EX_NO_PERM); 161 } 162 163 opterr = 0; /* no print errors from getopt */ 164 usertype = getusertype(argv[0]); 165 166 change_key(USERATTR_TYPE_KW, usertype); 167 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; 174 175 case 'c': 176 comment = optarg; 177 break; 178 179 case 'D': 180 Dflag++; 181 break; 182 183 case 'd': 184 dir = optarg; 185 break; 186 187 case 'e': 188 expirestr = optarg; 189 break; 190 191 case 'f': 192 inactstr = optarg; 193 break; 194 195 case 'G': 196 grps = optarg; 197 break; 198 199 case 'g': 200 group = optarg; 201 break; 202 203 case 'k': 204 skel_dir = optarg; 205 break; 206 207 case 'm': 208 mflag++; 209 break; 210 211 case 'o': 212 oflag++; 213 break; 214 215 case 'p': 216 projects = optarg; 217 break; 218 219 case 's': 220 shell = optarg; 221 break; 222 223 case 'u': 224 uidstr = optarg; 225 break; 226 227 case 'Z': 228 Zflag++; 229 break; 230 231 case 'z': 232 zflag++; 233 break; 234 235 case 'A': 236 change_key(USERATTR_AUTHS_KW, optarg); 237 break; 238 239 case 'P': 240 change_key(USERATTR_PROFILES_KW, optarg); 241 break; 242 243 case 'R': 244 if (is_role(usertype)) { 245 errmsg(M_ARUSAGE); 246 exit(EX_SYNTAX); 247 } 248 change_key(USERATTR_ROLES_KW, optarg); 249 break; 250 251 case 'K': 252 change_key(NULL, optarg); 253 break; 254 255 default: 256 case '?': 257 if (is_role(usertype)) 258 errmsg(M_ARUSAGE); 259 else 260 errmsg(M_AUSAGE); 261 exit(EX_SYNTAX); 262 } 263 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); 271 } 272 273 274 /* get defaults for adding new users */ 275 usrdefs = getusrdef(usertype); 276 277 if (Dflag) { 278 /* DISPLAY mode */ 279 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); 287 } 288 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); 296 } 297 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); 313 } 314 if (warning) 315 warningmsg(warning, group); 316 317 usrdefs->defgroup = g_ptr->gr_gid; 318 usrdefs->defgname = g_ptr->gr_name; 319 320 } 321 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); 337 } 338 if (warning) 339 warningmsg(warning, projects); 340 341 usrdefs->defproj = p_ptr.pj_projid; 342 usrdefs->defprojname = p_ptr.pj_name; 343 } 344 345 /* base_dir must be an existing directory */ 346 if (base_dir != NULL) { 347 valid_input(BASEDIR, base_dir); 348 usrdefs->defparent = base_dir; 349 } 350 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); 359 } 360 361 usrdefs->definact = inact; 362 } 363 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); 372 } 373 usrdefs->defexpire = expirestr; 374 } else 375 /* Unset the expiration date */ 376 usrdefs->defexpire = ""; 377 } 378 379 if (shell != NULL) { 380 valid_input(SHELL, shell); 381 usrdefs->defshell = shell; 382 } 383 if (skel_dir != NULL) { 384 valid_input(SKELDIR, skel_dir); 385 usrdefs->defskel = skel_dir; 386 } 387 update_def(usrdefs); 388 389 /* change defaults for useradd */ 390 if (putusrdef(usrdefs, usertype) < 0) { 391 errmsg(M_UPDATE, "created"); 392 exit(EX_UPDATE); 393 } 394 395 /* Now, display */ 396 dispusrdef(stdout, (D_ALL & ~D_RID), usertype); 397 exit(EX_SUCCESS); 398 399 } 400 401 /* ADD mode */ 402 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); 410 } 411 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*/ 418 419 case NOTUNIQUE: 420 errmsg(M_USED, logname); 421 exit(EX_NAME_EXISTS); 422 /*NOTREACHED*/ 423 424 case LONGNAME: 425 errmsg(M_TOO_LONG, logname); 426 exit(EX_BADARG); 427 /*NOTREACHED*/ 428 } 429 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); 439 } 440 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); 447 } 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; 456 } 457 458 } else { 459 460 if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) { 461 errmsg(M_INVALID, "default id", "user id"); 462 exit(EX_ID_EXISTS); 463 } 464 } 465 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*/ 481 } 482 483 if (warning) 484 warningmsg(warning, group); 485 gid = g_ptr->gr_gid; 486 487 } else gid = usrdefs->defgroup; 488 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); 495 } 496 497 if (projects != NULL) { 498 if (! *projects) 499 projects = (char *)0; 500 else if (! (projlist = valid_lproject(projects))) 501 exit(EX_BADARG); 502 } 503 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; 509 510 if (dir == NULL) { 511 /* set homedir to home directory made from base_dir */ 512 (void) sprintf(homedir, "%s/%s", base_dir, logname); 513 514 } else if (REL_PATH(dir)) { 515 errmsg(M_RELPATH, dir); 516 exit(EX_BADARG); 517 518 } else 519 (void) strcpy(homedir, dir); 520 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; 526 527 if (check_perm(statbuf, uid, gid, S_IXOTH) != 0) 528 errmsg(M_NO_PERM, logname, homedir); 529 } 530 } 531 /* 532 * if shell, skel_dir are provided, check their validity. 533 * Otherwise default. 534 */ 535 if (shell != NULL) 536 valid_input(SHELL, shell); 537 else 538 shell = usrdefs->defshell; 539 540 if (skel_dir != NULL) 541 valid_input(SKELDIR, skel_dir); 542 else 543 skel_dir = usrdefs->defskel; 544 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); 551 } 552 } else inact = usrdefs->definact; 553 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); 560 } 561 usrdefs->defexpire = expirestr; 562 } else 563 /* Unset the expiration date */ 564 expirestr = (char *)0; 565 566 } else expirestr = usrdefs->defexpire; 567 568 import_def(usrdefs); 569 570 /* must now call passmgmt */ 571 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 */ 577 578 if (comment != NULL) { 579 /* comment */ 580 nargv[argindex++] = "-c"; 581 nargv[argindex++] = comment; 582 } 583 584 /* flags for home directory */ 585 nargv[argindex++] = "-h"; 586 nargv[argindex++] = homedir; 587 588 /* set gid flag */ 589 nargv[argindex++] = "-g"; 590 (void) sprintf(gidstring, "%u", gid); 591 nargv[argindex++] = gidstring; 592 593 /* shell */ 594 nargv[argindex++] = "-s"; 595 nargv[argindex++] = shell; 596 597 /* set inactive */ 598 nargv[argindex++] = "-f"; 599 (void) sprintf(inactstring, "%ld", inact); 600 nargv[argindex++] = inactstring; 601 602 /* set expiration date */ 603 if (expirestr != NULL) { 604 nargv[argindex++] = "-e"; 605 nargv[argindex++] = expirestr; 606 } 607 608 /* set uid flag */ 609 nargv[argindex++] = "-u"; 610 (void) sprintf(uidstring, "%u", uid); 611 nargv[argindex++] = uidstring; 612 613 if (oflag) nargv[argindex++] = "-o"; 614 615 if (nkeys > 1) 616 addkey_args(nargv, &argindex); 617 618 /* finally - login name */ 619 nargv[argindex++] = logname; 620 621 /* set the last to null */ 622 nargv[argindex++] = NULL; 623 624 /* now call passmgmt */ 625 ret = PEX_FAILED; 626 /* 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. 636 */ 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; 648 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; 658 659 case PEX_BADUID: 660 /* 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. 664 */ 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); 673 } 674 (void) sprintf(uidstring, "%u", uid); 675 } 676 break; 677 678 case PEX_BADNAME: 679 /* invalid loname */ 680 errmsg(M_USED, logname); 681 exit(EX_NAME_EXISTS); 682 break; 683 684 default: 685 errmsg(M_UPDATE, "created"); 686 exit(ret); 687 break; 688 } 689 } 690 if (busy == 3) { 691 errmsg(M_UPDATE, "created"); 692 exit(ret); 693 } 694 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); 700 } 701 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); 708 } 709 710 /* create home directory */ 711 if (mflag) { 712 zfs_flags = get_default_zfs_flags(); 713 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); 719 } 720 if (ret != EX_SUCCESS) { 721 (void) edit_project(logname, (char *)NULL, (projid_t **)NULL, 722 0); 723 (void) edit_group(logname, (char *)0, (int **)0, 1); 724 cleanup(logname); 725 exit(EX_HOMEDIR); 726 } 727 728 return (ret); 729 } 730 731 static void 732 cleanup(logname) 733 char *logname; 734 { 735 char *nargv[4]; 736 737 nargv[0] = PASSMGMT; 738 nargv[1] = "-d"; 739 nargv[2] = logname; 740 nargv[3] = NULL; 741 742 switch (call_passmgmt(nargv)) { 743 case PEX_SUCCESS: 744 break; 745 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; 753 754 case PEX_BADUID: 755 /* uid is used - shouldn't happen but print message anyway */ 756 errmsg(M_UID_USED, uid); 757 break; 758 759 case PEX_BADNAME: 760 /* invalid loname */ 761 errmsg(M_USED, logname); 762 break; 763 764 default: 765 errmsg(M_UPDATE, "created"); 766 break; 767 } 768 } 769 770 /* Check the validity for shell, base_dir and skel_dir */ 771 772 void 773 valid_input(path_opt_t opt, const char *input) 774 { 775 struct stat statbuf; 776 777 if (REL_PATH(input)) { 778 errmsg(M_RELPATH, input); 779 exit(EX_BADARG); 780 } 781 if (stat(input, &statbuf) == -1) { 782 errmsg(M_INVALID, input, "path"); 783 exit(EX_BADARG); 784 } 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); 790 } 791 } else { 792 if (!S_ISDIR(statbuf.st_mode)) { 793 errmsg(M_INVALID, input, "directory"); 794 exit(EX_BADARG); 795 } 796 } 797 } 798