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