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