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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 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 "users.h" 47 #include "messages.h" 48 #include "userdisp.h" 49 #include "funcs.h" 50 51 /* 52 * useradd [-u uid [-o] | -g group | -G group [[, group]...] | -d dir [-m] 53 * | -s shell | -c comment | -k skel_dir] ] 54 * [ -A authorization [, authorization ...]] 55 * [ -P profile [, profile ...]] 56 * [ -K key=value ] 57 * [ -R role [, role ...]] [-p project [, project ...]] login 58 * useradd -D [ -g group ] [ -b base_dir | -f inactive | -e expire ] 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 int 124 main(argc, argv) 125 int argc; 126 char *argv[]; 127 { 128 int ch, ret, mflag = 0, oflag = 0, Dflag = 0, **gidlist; 129 projid_t **projlist; 130 char *ptr; /* loc in a str, may be set by strtol */ 131 struct group *g_ptr; 132 struct project p_ptr; 133 char mybuf[PROJECT_BUFSZ]; 134 struct stat statbuf; /* status buffer for stat */ 135 int warning; 136 int busy = 0; 137 char **nargv; /* arguments for execvp of passmgmt */ 138 int argindex; /* argument index into nargv */ 139 140 cmdname = argv[0]; 141 142 if (geteuid() != 0) { 143 errmsg(M_PERM_DENIED); 144 exit(EX_NO_PERM); 145 } 146 147 opterr = 0; /* no print errors from getopt */ 148 usertype = getusertype(argv[0]); 149 150 change_key(USERATTR_TYPE_KW, usertype); 151 152 while ((ch = getopt(argc, argv, 153 "b:c:Dd:e:f:G:g:k:mop:s:u:A:P:R:K:")) != EOF) 154 switch (ch) { 155 case 'b': 156 base_dir = optarg; 157 break; 158 159 case 'c': 160 comment = optarg; 161 break; 162 163 case 'D': 164 Dflag++; 165 break; 166 167 case 'd': 168 dir = optarg; 169 break; 170 171 case 'e': 172 expirestr = optarg; 173 break; 174 175 case 'f': 176 inactstr = optarg; 177 break; 178 179 case 'G': 180 grps = optarg; 181 break; 182 183 case 'g': 184 group = optarg; 185 break; 186 187 case 'k': 188 skel_dir = optarg; 189 break; 190 191 case 'm': 192 mflag++; 193 break; 194 195 case 'o': 196 oflag++; 197 break; 198 199 case 'p': 200 projects = optarg; 201 break; 202 203 case 's': 204 shell = optarg; 205 break; 206 207 case 'u': 208 uidstr = optarg; 209 break; 210 211 case 'A': 212 change_key(USERATTR_AUTHS_KW, optarg); 213 break; 214 215 case 'P': 216 change_key(USERATTR_PROFILES_KW, optarg); 217 break; 218 219 case 'R': 220 if (is_role(usertype)) { 221 errmsg(M_ARUSAGE); 222 exit(EX_SYNTAX); 223 } 224 change_key(USERATTR_ROLES_KW, optarg); 225 break; 226 227 case 'K': 228 change_key(NULL, optarg); 229 break; 230 231 default: 232 case '?': 233 if (is_role(usertype)) 234 errmsg(M_ARUSAGE); 235 else 236 errmsg(M_AUSAGE); 237 exit(EX_SYNTAX); 238 } 239 240 /* get defaults for adding new users */ 241 usrdefs = getusrdef(usertype); 242 243 if (Dflag) { 244 /* DISPLAY mode */ 245 246 /* check syntax */ 247 if (optind != argc) { 248 if (is_role(usertype)) 249 errmsg(M_ARUSAGE); 250 else 251 errmsg(M_AUSAGE); 252 exit(EX_SYNTAX); 253 } 254 255 if (uidstr || oflag || grps || dir || mflag || 256 shell || comment || skel_dir) { 257 if (is_role(usertype)) 258 errmsg(M_ARUSAGE); 259 else 260 errmsg(M_AUSAGE); 261 exit(EX_SYNTAX); 262 } 263 264 /* Group must be an existing group */ 265 if (group) { 266 switch (valid_group(group, &g_ptr, &warning)) { 267 case INVALID: 268 errmsg(M_INVALID, group, "group id"); 269 exit(EX_BADARG); 270 /*NOTREACHED*/ 271 case TOOBIG: 272 errmsg(M_TOOBIG, "gid", group); 273 exit(EX_BADARG); 274 /*NOTREACHED*/ 275 case RESERVED: 276 case UNIQUE: 277 errmsg(M_GRP_NOTUSED, group); 278 exit(EX_NAME_NOT_EXIST); 279 } 280 if (warning) 281 warningmsg(warning, group); 282 283 usrdefs->defgroup = g_ptr->gr_gid; 284 usrdefs->defgname = g_ptr->gr_name; 285 286 } 287 288 /* project must be an existing project */ 289 if (projects) { 290 switch (valid_project(projects, &p_ptr, mybuf, 291 sizeof (mybuf), &warning)) { 292 case INVALID: 293 errmsg(M_INVALID, projects, "project id"); 294 exit(EX_BADARG); 295 /*NOTREACHED*/ 296 case TOOBIG: 297 errmsg(M_TOOBIG, "projid", projects); 298 exit(EX_BADARG); 299 /*NOTREACHED*/ 300 case UNIQUE: 301 errmsg(M_PROJ_NOTUSED, projects); 302 exit(EX_NAME_NOT_EXIST); 303 } 304 if (warning) 305 warningmsg(warning, projects); 306 307 usrdefs->defproj = p_ptr.pj_projid; 308 usrdefs->defprojname = p_ptr.pj_name; 309 } 310 311 /* base_dir must be an existing directory */ 312 if (base_dir) { 313 if (REL_PATH(base_dir)) { 314 errmsg(M_RELPATH, base_dir); 315 exit(EX_BADARG); 316 } 317 if (stat(base_dir, &statbuf) < 0 && 318 (statbuf.st_mode & S_IFMT) != S_IFDIR) { 319 errmsg(M_INVALID, base_dir, "base directory"); 320 exit(EX_BADARG); 321 } 322 323 usrdefs->defparent = base_dir; 324 } 325 326 /* inactivity period is an integer */ 327 if (inactstr) { 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) { 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 update_def(usrdefs); 355 356 /* change defaults for useradd */ 357 if (putusrdef(usrdefs, usertype) < 0) { 358 errmsg(M_UPDATE, "created"); 359 exit(EX_UPDATE); 360 } 361 362 /* Now, display */ 363 dispusrdef(stdout, (D_ALL & ~D_RID), usertype); 364 exit(EX_SUCCESS); 365 366 } 367 368 /* ADD mode */ 369 370 /* check syntax */ 371 if (optind != argc - 1 || base_dir || (skel_dir && !mflag)) { 372 if (is_role(usertype)) 373 errmsg(M_ARUSAGE); 374 else 375 errmsg(M_AUSAGE); 376 exit(EX_SYNTAX); 377 } 378 379 logname = argv[optind]; 380 switch (valid_login(logname, (struct passwd **)NULL, &warning)) { 381 case INVALID: 382 errmsg(M_INVALID, logname, "login name"); 383 exit(EX_BADARG); 384 /*NOTREACHED*/ 385 386 case NOTUNIQUE: 387 errmsg(M_USED, logname); 388 exit(EX_NAME_EXISTS); 389 /*NOTREACHED*/ 390 } 391 392 if (warning) 393 warningmsg(warning, logname); 394 if (uidstr) { 395 /* convert uidstr to integer */ 396 errno = 0; 397 uid = (uid_t)strtol(uidstr, &ptr, (int)10); 398 if (*ptr || errno == ERANGE) { 399 errmsg(M_INVALID, uidstr, "user id"); 400 exit(EX_BADARG); 401 } 402 403 switch (valid_uid(uid, NULL)) { 404 case NOTUNIQUE: 405 if (!oflag) { 406 /* override not specified */ 407 errmsg(M_UID_USED, uid); 408 exit(EX_ID_EXISTS); 409 } 410 break; 411 case RESERVED: 412 errmsg(M_RESERVED, uid); 413 break; 414 case TOOBIG: 415 errmsg(M_TOOBIG, "uid", uid); 416 exit(EX_BADARG); 417 break; 418 } 419 420 } else { 421 422 if ((uid = findnextuid()) < 0) { 423 errmsg(M_INVALID, "default id", "user id"); 424 exit(EX_ID_EXISTS); 425 } 426 } 427 428 if (group) { 429 switch (valid_group(group, &g_ptr, &warning)) { 430 case INVALID: 431 errmsg(M_INVALID, group, "group id"); 432 exit(EX_BADARG); 433 /*NOTREACHED*/ 434 case TOOBIG: 435 errmsg(M_TOOBIG, "gid", group); 436 exit(EX_BADARG); 437 /*NOTREACHED*/ 438 case RESERVED: 439 case UNIQUE: 440 errmsg(M_GRP_NOTUSED, group); 441 exit(EX_NAME_NOT_EXIST); 442 /*NOTREACHED*/ 443 } 444 445 if (warning) 446 warningmsg(warning, group); 447 gid = g_ptr->gr_gid; 448 449 } else gid = usrdefs->defgroup; 450 451 if (grps) { 452 if (!*grps) 453 /* ignore -G "" */ 454 grps = (char *)0; 455 else if (!(gidlist = valid_lgroup(grps, gid))) 456 exit(EX_BADARG); 457 } 458 459 if (projects) { 460 if (! *projects) 461 projects = (char *)0; 462 else if (! (projlist = valid_lproject(projects))) 463 exit(EX_BADARG); 464 } 465 466 if (!dir) { 467 /* set homedir to home directory made from base_dir */ 468 (void) sprintf(homedir, "%s/%s", usrdefs->defparent, logname); 469 470 } else if (REL_PATH(dir)) { 471 errmsg(M_RELPATH, dir); 472 exit(EX_BADARG); 473 474 } else 475 (void) strcpy(homedir, dir); 476 477 if (mflag) { 478 /* Does home dir. already exist? */ 479 if (stat(homedir, &statbuf) == 0) { 480 /* directory exists - don't try to create */ 481 mflag = 0; 482 483 if (check_perm(statbuf, uid, gid, S_IXOTH) != 0) 484 errmsg(M_NO_PERM, logname, homedir); 485 } 486 } 487 488 if (shell) { 489 if (REL_PATH(shell)) { 490 errmsg(M_RELPATH, shell); 491 exit(EX_BADARG); 492 } 493 /* check that shell is an executable file */ 494 if (stat(shell, &statbuf) < 0 || 495 (statbuf.st_mode & S_IFMT) != S_IFREG || 496 (statbuf.st_mode & 0555) != 0555) { 497 498 errmsg(M_INVALID, shell, "shell"); 499 exit(EX_BADARG); 500 } 501 } else shell = usrdefs->defshell; 502 503 if (skel_dir) { 504 if (REL_PATH(skel_dir)) { 505 errmsg(M_RELPATH, skel_dir); 506 exit(EX_BADARG); 507 } 508 if (stat(skel_dir, &statbuf) < 0 && 509 (statbuf.st_mode & S_IFMT) != S_IFDIR) { 510 511 errmsg(M_INVALID, skel_dir, "directory"); 512 exit(EX_BADARG); 513 } 514 } else skel_dir = usrdefs->defskel; 515 516 if (inactstr) { 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) { 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) { 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, "%ld", 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) { 575 nargv[argindex++] = "-e"; 576 nargv[argindex++] = expirestr; 577 } 578 579 /* set uid flag */ 580 nargv[argindex++] = "-u"; 581 (void) sprintf(uidstring, "%ld", 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 ((uid = findnextuid()) < 0) { 641 errmsg(M_INVALID, "default id", 642 "user id"); 643 exit(EX_ID_EXISTS); 644 } 645 (void) sprintf(uidstring, "%ld", 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 && 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 && 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