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 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 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 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