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