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 2007 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 #pragma ident "%Z%%M% %I% %E% SMI" 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 400 if (warning) 401 warningmsg(warning, logname); 402 if (uidstr != NULL) { 403 /* convert uidstr to integer */ 404 errno = 0; 405 uid = (uid_t)strtol(uidstr, &ptr, (int)10); 406 if (*ptr || errno == ERANGE) { 407 errmsg(M_INVALID, uidstr, "user id"); 408 exit(EX_BADARG); 409 } 410 411 switch (valid_uid(uid, NULL)) { 412 case NOTUNIQUE: 413 if (!oflag) { 414 /* override not specified */ 415 errmsg(M_UID_USED, uid); 416 exit(EX_ID_EXISTS); 417 } 418 break; 419 case RESERVED: 420 errmsg(M_RESERVED, uid); 421 break; 422 case TOOBIG: 423 errmsg(M_TOOBIG, "uid", uid); 424 exit(EX_BADARG); 425 break; 426 } 427 428 } else { 429 430 if ((uid = findnextuid()) < 0) { 431 errmsg(M_INVALID, "default id", "user id"); 432 exit(EX_ID_EXISTS); 433 } 434 } 435 436 if (group != NULL) { 437 switch (valid_group(group, &g_ptr, &warning)) { 438 case INVALID: 439 errmsg(M_INVALID, group, "group id"); 440 exit(EX_BADARG); 441 /*NOTREACHED*/ 442 case TOOBIG: 443 errmsg(M_TOOBIG, "gid", group); 444 exit(EX_BADARG); 445 /*NOTREACHED*/ 446 case RESERVED: 447 case UNIQUE: 448 errmsg(M_GRP_NOTUSED, group); 449 exit(EX_NAME_NOT_EXIST); 450 /*NOTREACHED*/ 451 } 452 453 if (warning) 454 warningmsg(warning, group); 455 gid = g_ptr->gr_gid; 456 457 } else gid = usrdefs->defgroup; 458 459 if (grps != NULL) { 460 if (!*grps) 461 /* ignore -G "" */ 462 grps = (char *)0; 463 else if (!(gidlist = valid_lgroup(grps, gid))) 464 exit(EX_BADARG); 465 } 466 467 if (projects != NULL) { 468 if (! *projects) 469 projects = (char *)0; 470 else if (! (projlist = valid_lproject(projects))) 471 exit(EX_BADARG); 472 } 473 474 /* if base_dir is provided, check its validity; otherwise default */ 475 if (base_dir != NULL) 476 valid_input(BASEDIR, base_dir); 477 else 478 base_dir = usrdefs->defparent; 479 480 if (dir == NULL) { 481 /* set homedir to home directory made from base_dir */ 482 (void) sprintf(homedir, "%s/%s", base_dir, logname); 483 484 } else if (REL_PATH(dir)) { 485 errmsg(M_RELPATH, dir); 486 exit(EX_BADARG); 487 488 } else 489 (void) strcpy(homedir, dir); 490 491 if (mflag) { 492 /* Does home dir. already exist? */ 493 if (stat(homedir, &statbuf) == 0) { 494 /* directory exists - don't try to create */ 495 mflag = 0; 496 497 if (check_perm(statbuf, uid, gid, S_IXOTH) != 0) 498 errmsg(M_NO_PERM, logname, homedir); 499 } 500 } 501 /* 502 * if shell, skel_dir are provided, check their validity. 503 * Otherwise default. 504 */ 505 if (shell != NULL) 506 valid_input(SHELL, shell); 507 else 508 shell = usrdefs->defshell; 509 510 if (skel_dir != NULL) 511 valid_input(SKELDIR, skel_dir); 512 else 513 skel_dir = usrdefs->defskel; 514 515 if (inactstr != NULL) { 516 /* convert inactstr to integer */ 517 inact = strtol(inactstr, &ptr, 10); 518 if (*ptr || inact < 0) { 519 errmsg(M_INVALID, inactstr, "inactivity period"); 520 exit(EX_BADARG); 521 } 522 } else inact = usrdefs->definact; 523 524 /* expiration string is a date, newer than today */ 525 if (expirestr != NULL) { 526 if (*expirestr) { 527 if (valid_expire(expirestr, (time_t *)0) == INVALID) { 528 errmsg(M_INVALID, expirestr, "expiration date"); 529 exit(EX_BADARG); 530 } 531 usrdefs->defexpire = expirestr; 532 } else 533 /* Unset the expiration date */ 534 expirestr = (char *)0; 535 536 } else expirestr = usrdefs->defexpire; 537 538 import_def(usrdefs); 539 540 /* must now call passmgmt */ 541 542 /* set up arguments to passmgmt in nargv array */ 543 nargv = malloc((30 + nkeys * 2) * sizeof (char *)); 544 argindex = 0; 545 nargv[argindex++] = "passmgmt"; 546 nargv[argindex++] = "-a"; /* add */ 547 548 if (comment != NULL) { 549 /* comment */ 550 nargv[argindex++] = "-c"; 551 nargv[argindex++] = comment; 552 } 553 554 /* flags for home directory */ 555 nargv[argindex++] = "-h"; 556 nargv[argindex++] = homedir; 557 558 /* set gid flag */ 559 nargv[argindex++] = "-g"; 560 (void) sprintf(gidstring, "%u", gid); 561 nargv[argindex++] = gidstring; 562 563 /* shell */ 564 nargv[argindex++] = "-s"; 565 nargv[argindex++] = shell; 566 567 /* set inactive */ 568 nargv[argindex++] = "-f"; 569 (void) sprintf(inactstring, "%ld", inact); 570 nargv[argindex++] = inactstring; 571 572 /* set expiration date */ 573 if (expirestr != NULL) { 574 nargv[argindex++] = "-e"; 575 nargv[argindex++] = expirestr; 576 } 577 578 /* set uid flag */ 579 nargv[argindex++] = "-u"; 580 (void) sprintf(uidstring, "%u", uid); 581 nargv[argindex++] = uidstring; 582 583 if (oflag) nargv[argindex++] = "-o"; 584 585 if (nkeys > 1) 586 addkey_args(nargv, &argindex); 587 588 /* finally - login name */ 589 nargv[argindex++] = logname; 590 591 /* set the last to null */ 592 nargv[argindex++] = NULL; 593 594 /* now call passmgmt */ 595 ret = PEX_FAILED; 596 /* 597 * If call_passmgmt fails for any reason other than PEX_BADUID, exit 598 * is invoked with an appropriate error message. If PEX_BADUID is 599 * returned, then if the user specified the ID, exit is invoked 600 * with an appropriate error message. Otherwise we try to pick a 601 * different ID and try again. If we run out of IDs, i.e. no more 602 * users can be created, then -1 is returned and we terminate via exit. 603 * If PEX_BUSY is returned we increment a count, since we will stop 604 * trying if PEX_BUSY reaches 3. For PEX_SUCCESS we immediately 605 * terminate the loop. 606 */ 607 while (busy < 3 && ret != PEX_SUCCESS) { 608 switch (ret = call_passmgmt(nargv)) { 609 case PEX_SUCCESS: 610 break; 611 case PEX_BUSY: 612 busy++; 613 break; 614 case PEX_HOSED_FILES: 615 errmsg(M_HOSED_FILES); 616 exit(EX_INCONSISTENT); 617 break; 618 619 case PEX_SYNTAX: 620 case PEX_BADARG: 621 /* should NEVER occur that passmgmt usage is wrong */ 622 if (is_role(usertype)) 623 errmsg(M_ARUSAGE); 624 else 625 errmsg(M_AUSAGE); 626 exit(EX_SYNTAX); 627 break; 628 629 case PEX_BADUID: 630 /* 631 * The uid has been taken. If it was specified by a 632 * user, then we must fail. Otherwise, keep trying 633 * to get a good uid until we run out of IDs. 634 */ 635 if (uidstr != NULL) { 636 errmsg(M_UID_USED, uid); 637 exit(EX_ID_EXISTS); 638 } else { 639 if ((uid = findnextuid()) < 0) { 640 errmsg(M_INVALID, "default id", 641 "user id"); 642 exit(EX_ID_EXISTS); 643 } 644 (void) sprintf(uidstring, "%u", uid); 645 } 646 break; 647 648 case PEX_BADNAME: 649 /* invalid loname */ 650 errmsg(M_USED, logname); 651 exit(EX_NAME_EXISTS); 652 break; 653 654 default: 655 errmsg(M_UPDATE, "created"); 656 exit(ret); 657 break; 658 } 659 } 660 if (busy == 3) { 661 errmsg(M_UPDATE, "created"); 662 exit(ret); 663 } 664 665 /* add group entry */ 666 if ((grps != NULL) && edit_group(logname, (char *)0, gidlist, 0)) { 667 errmsg(M_UPDATE, "created"); 668 cleanup(logname); 669 exit(EX_UPDATE); 670 } 671 672 /* update project database */ 673 if ((projects != NULL) && 674 edit_project(logname, (char *)NULL, projlist, 0)) { 675 errmsg(M_UPDATE, "created"); 676 cleanup(logname); 677 exit(EX_UPDATE); 678 } 679 680 /* create home directory */ 681 if (mflag && 682 (create_home(homedir, skel_dir, uid, gid) != EX_SUCCESS)) { 683 (void) edit_group(logname, (char *)0, (int **)0, 1); 684 cleanup(logname); 685 exit(EX_HOMEDIR); 686 } 687 688 return (ret); 689 } 690 691 static void 692 cleanup(logname) 693 char *logname; 694 { 695 char *nargv[4]; 696 697 nargv[0] = "passmgmt"; 698 nargv[1] = "-d"; 699 nargv[2] = logname; 700 nargv[3] = NULL; 701 702 switch (call_passmgmt(nargv)) { 703 case PEX_SUCCESS: 704 break; 705 706 case PEX_SYNTAX: 707 /* should NEVER occur that passmgmt usage is wrong */ 708 if (is_role(usertype)) 709 errmsg(M_ARUSAGE); 710 else 711 errmsg(M_AUSAGE); 712 break; 713 714 case PEX_BADUID: 715 /* uid is used - shouldn't happen but print message anyway */ 716 errmsg(M_UID_USED, uid); 717 break; 718 719 case PEX_BADNAME: 720 /* invalid loname */ 721 errmsg(M_USED, logname); 722 break; 723 724 default: 725 errmsg(M_UPDATE, "created"); 726 break; 727 } 728 } 729 730 /* Check the validity for shell, base_dir and skel_dir */ 731 732 void 733 valid_input(path_opt_t opt, const char *input) 734 { 735 struct stat statbuf; 736 737 if (REL_PATH(input)) { 738 errmsg(M_RELPATH, input); 739 exit(EX_BADARG); 740 } 741 if (stat(input, &statbuf) == -1) { 742 errmsg(M_INVALID, input, "path"); 743 exit(EX_BADARG); 744 } 745 if (opt == SHELL) { 746 if (!S_ISREG(statbuf.st_mode) || 747 (statbuf.st_mode & 0555) != 0555) { 748 errmsg(M_INVALID, input, "shell"); 749 exit(EX_BADARG); 750 } 751 } else { 752 if (!S_ISDIR(statbuf.st_mode)) { 753 errmsg(M_INVALID, input, "directory"); 754 exit(EX_BADARG); 755 } 756 } 757 } 758