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