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 * Copyright (c) 2013 RackTop Systems. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <sys/param.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <ctype.h> 41 #include <limits.h> 42 #include <string.h> 43 #include <userdefs.h> 44 #include <user_attr.h> 45 #include <nss_dbdefs.h> 46 #include <errno.h> 47 #include <project.h> 48 #include "users.h" 49 #include "messages.h" 50 #include "funcs.h" 51 52 /* 53 * usermod [-u uid [-o] | -g group | -G group [[,group]...] 54 * | -d dir [-m [-z|Z]] 55 * | -s shell | -c comment | -l new_logname] 56 * | -f inactive | -e expire ] 57 * [ -A authorization [, authorization ...]] 58 * [ -P profile [, profile ...]] 59 * [ -R role [, role ...]] 60 * [ -K key=value ] 61 * [ -p project [, project]] login 62 * 63 * This command adds new user logins to the system. Arguments are: 64 * 65 * uid - an integer less than MAXUID 66 * group - an existing group's integer ID or char string name 67 * dir - a directory 68 * shell - a program to be used as a shell 69 * comment - any text string 70 * skel_dir - a directory 71 * base_dir - a directory 72 * rid - an integer less than 2**16 (USHORT) 73 * login - a string of printable chars except colon (:) 74 * inactive - number of days a login maybe inactive before it is locked 75 * expire - date when a login is no longer valid 76 * authorization - One or more comma separated authorizations defined 77 * in auth_attr(5). 78 * profile - One or more comma separated execution profiles defined 79 * in prof_attr(5) 80 * role - One or more comma-separated role names defined in user_attr(5) 81 * key=value - One or more -K options each specifying a valid user_attr(5) 82 * attribute. 83 * 84 */ 85 86 extern int **valid_lgroup(), isbusy(), get_default_zfs_flags(); 87 extern int valid_uid(), check_perm(), create_home(), move_dir(); 88 extern int valid_expire(), edit_group(), call_passmgmt(); 89 extern projid_t **valid_lproject(); 90 91 static uid_t uid; /* new uid */ 92 static gid_t gid; /* gid of new login */ 93 static char *new_logname = NULL; /* new login name with -l option */ 94 static char *uidstr = NULL; /* uid from command line */ 95 static char *group = NULL; /* group from command line */ 96 static char *grps = NULL; /* multi groups from command line */ 97 static char *dir = NULL; /* home dir from command line */ 98 static char *shell = NULL; /* shell from command line */ 99 static char *comment = NULL; /* comment from command line */ 100 static char *logname = NULL; /* login name to add */ 101 static char *inactstr = NULL; /* inactive from command line */ 102 static char *expire = NULL; /* expiration date from command line */ 103 static char *projects = NULL; /* project ids from command line */ 104 static char *usertype; 105 106 char *cmdname; 107 static char gidstring[32], uidstring[32]; 108 char inactstring[10]; 109 110 char * 111 strcpmalloc(str) 112 char *str; 113 { 114 if (str == NULL) 115 return (NULL); 116 117 return (strdup(str)); 118 } 119 struct passwd * 120 passwd_cpmalloc(opw) 121 struct passwd *opw; 122 { 123 struct passwd *npw; 124 125 if (opw == NULL) 126 return (NULL); 127 128 129 npw = malloc(sizeof (struct passwd)); 130 131 npw->pw_name = strcpmalloc(opw->pw_name); 132 npw->pw_passwd = strcpmalloc(opw->pw_passwd); 133 npw->pw_uid = opw->pw_uid; 134 npw->pw_gid = opw->pw_gid; 135 npw->pw_age = strcpmalloc(opw->pw_age); 136 npw->pw_comment = strcpmalloc(opw->pw_comment); 137 npw->pw_gecos = strcpmalloc(opw->pw_gecos); 138 npw->pw_dir = strcpmalloc(opw->pw_dir); 139 npw->pw_shell = strcpmalloc(opw->pw_shell); 140 141 return (npw); 142 } 143 144 int 145 main(argc, argv) 146 int argc; 147 char **argv; 148 { 149 int ch, ret = EX_SUCCESS, call_pass = 0, oflag = 0, zfs_flags = 0; 150 int tries, mflag = 0, inact, **gidlist, flag = 0, zflag = 0, Zflag = 0; 151 boolean_t fail_if_busy = B_FALSE; 152 char *ptr; 153 struct passwd *pstruct; /* password struct for login */ 154 struct passwd *pw; 155 struct group *g_ptr; /* validated group from -g */ 156 struct stat statbuf; /* status buffer for stat */ 157 #ifndef att 158 FILE *pwf; /* fille ptr for opened passwd file */ 159 #endif 160 int warning; 161 projid_t **projlist; 162 char **nargv; /* arguments for execvp of passmgmt */ 163 int argindex; /* argument index into nargv */ 164 userattr_t *ua; 165 char *val; 166 int isrole; /* current account is role */ 167 168 cmdname = argv[0]; 169 170 if (geteuid() != 0) { 171 errmsg(M_PERM_DENIED); 172 exit(EX_NO_PERM); 173 } 174 175 opterr = 0; /* no print errors from getopt */ 176 /* get user type based on the program name */ 177 usertype = getusertype(argv[0]); 178 179 while ((ch = getopt(argc, argv, 180 "c:d:e:f:G:g:l:mzZop:s:u:A:P:R:K:")) != EOF) 181 switch (ch) { 182 case 'c': 183 comment = optarg; 184 flag++; 185 break; 186 case 'd': 187 dir = optarg; 188 fail_if_busy = B_TRUE; 189 flag++; 190 break; 191 case 'e': 192 expire = optarg; 193 flag++; 194 break; 195 case 'f': 196 inactstr = optarg; 197 flag++; 198 break; 199 case 'G': 200 grps = optarg; 201 flag++; 202 break; 203 case 'g': 204 group = optarg; 205 fail_if_busy = B_TRUE; 206 flag++; 207 break; 208 case 'l': 209 new_logname = optarg; 210 fail_if_busy = B_TRUE; 211 flag++; 212 break; 213 case 'm': 214 mflag++; 215 flag++; 216 fail_if_busy = B_TRUE; 217 break; 218 case 'o': 219 oflag++; 220 flag++; 221 fail_if_busy = B_TRUE; 222 break; 223 case 'p': 224 projects = optarg; 225 flag++; 226 break; 227 case 's': 228 shell = optarg; 229 flag++; 230 break; 231 case 'u': 232 uidstr = optarg; 233 flag++; 234 fail_if_busy = B_TRUE; 235 break; 236 case 'Z': 237 Zflag++; 238 break; 239 case 'z': 240 zflag++; 241 break; 242 case 'A': 243 change_key(USERATTR_AUTHS_KW, optarg); 244 flag++; 245 break; 246 case 'P': 247 change_key(USERATTR_PROFILES_KW, optarg); 248 flag++; 249 break; 250 case 'R': 251 change_key(USERATTR_ROLES_KW, optarg); 252 flag++; 253 break; 254 case 'K': 255 change_key(NULL, optarg); 256 flag++; 257 break; 258 default: 259 case '?': 260 if (is_role(usertype)) 261 errmsg(M_MRUSAGE); 262 else 263 errmsg(M_MUSAGE); 264 exit(EX_SYNTAX); 265 } 266 267 if (((!mflag) && (zflag || Zflag)) || (zflag && Zflag) || 268 (mflag > 1 && (zflag || Zflag))) { 269 if (is_role(usertype)) 270 errmsg(M_ARUSAGE); 271 else 272 errmsg(M_AUSAGE); 273 exit(EX_SYNTAX); 274 } 275 276 277 if (optind != argc - 1 || flag == 0) { 278 if (is_role(usertype)) 279 errmsg(M_MRUSAGE); 280 else 281 errmsg(M_MUSAGE); 282 exit(EX_SYNTAX); 283 } 284 285 if ((!uidstr && oflag) || (mflag && !dir)) { 286 if (is_role(usertype)) 287 errmsg(M_MRUSAGE); 288 else 289 errmsg(M_MUSAGE); 290 exit(EX_SYNTAX); 291 } 292 293 logname = argv[optind]; 294 295 /* Determine whether the account is a role or not */ 296 if ((ua = getusernam(logname)) == NULL || 297 (val = kva_match(ua->attr, USERATTR_TYPE_KW)) == NULL || 298 strcmp(val, USERATTR_TYPE_NONADMIN_KW) != 0) 299 isrole = 0; 300 else 301 isrole = 1; 302 303 /* Verify that rolemod is used for roles and usermod for users */ 304 if (isrole != is_role(usertype)) { 305 if (isrole) 306 errmsg(M_ISROLE); 307 else 308 errmsg(M_ISUSER); 309 exit(EX_SYNTAX); 310 } 311 312 /* Set the usertype key; defaults to the commandline */ 313 usertype = getsetdefval(USERATTR_TYPE_KW, usertype); 314 315 if (is_role(usertype)) { 316 /* Roles can't have roles */ 317 if (getsetdefval(USERATTR_ROLES_KW, NULL) != NULL) { 318 errmsg(M_MRUSAGE); 319 exit(EX_SYNTAX); 320 } 321 /* If it was an ordinary user, delete its roles */ 322 if (!isrole) 323 change_key(USERATTR_ROLES_KW, ""); 324 } 325 326 #ifdef att 327 pw = getpwnam(logname); 328 #else 329 /* 330 * Do this with fgetpwent to make sure we are only looking on local 331 * system (since passmgmt only works on local system). 332 */ 333 if ((pwf = fopen("/etc/passwd", "r")) == NULL) { 334 errmsg(M_OOPS, "open", "/etc/passwd"); 335 exit(EX_FAILURE); 336 } 337 while ((pw = fgetpwent(pwf)) != NULL) 338 if (strcmp(pw->pw_name, logname) == 0) 339 break; 340 341 fclose(pwf); 342 #endif 343 344 if (pw == NULL) { 345 char pwdb[NSS_BUFLEN_PASSWD]; 346 struct passwd pwd; 347 348 if (getpwnam_r(logname, &pwd, pwdb, sizeof (pwdb)) == NULL) { 349 /* This user does not exist. */ 350 errmsg(M_EXIST, logname); 351 exit(EX_NAME_NOT_EXIST); 352 } else { 353 /* This user exists in non-local name service. */ 354 errmsg(M_NONLOCAL, logname); 355 exit(EX_NOT_LOCAL); 356 } 357 } 358 359 pstruct = passwd_cpmalloc(pw); 360 361 /* 362 * We can't modify a logged in user if any of the following 363 * are being changed: 364 * uid (-u & -o), group (-g), home dir (-m), loginname (-l). 365 * If none of those are specified it is okay to go ahead 366 * some types of changes only take effect on next login, some 367 * like authorisations and profiles take effect instantly. 368 * One might think that -K type=role should require that the 369 * user not be logged in, however this would make it very 370 * difficult to make the root account a role using this command. 371 */ 372 if (isbusy(logname)) { 373 if (fail_if_busy) { 374 errmsg(M_BUSY, logname, "change"); 375 exit(EX_BUSY); 376 } 377 warningmsg(WARN_LOGGED_IN, logname); 378 } 379 380 if (new_logname && strcmp(new_logname, logname)) { 381 switch (valid_login(new_logname, (struct passwd **)NULL, 382 &warning)) { 383 case INVALID: 384 errmsg(M_INVALID, new_logname, "login name"); 385 exit(EX_BADARG); 386 /*NOTREACHED*/ 387 388 case NOTUNIQUE: 389 errmsg(M_USED, new_logname); 390 exit(EX_NAME_EXISTS); 391 /*NOTREACHED*/ 392 393 case LONGNAME: 394 errmsg(M_TOO_LONG, new_logname); 395 exit(EX_BADARG); 396 /*NOTREACHED*/ 397 398 default: 399 call_pass = 1; 400 break; 401 } 402 if (warning) 403 warningmsg(warning, logname); 404 } 405 406 if (uidstr) { 407 /* convert uidstr to integer */ 408 errno = 0; 409 uid = (uid_t)strtol(uidstr, &ptr, (int)10); 410 if (*ptr || errno == ERANGE) { 411 errmsg(M_INVALID, uidstr, "user id"); 412 exit(EX_BADARG); 413 } 414 415 if (uid != pstruct->pw_uid) { 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 call_pass = 1; 434 435 } else { 436 /* uid's the same, so don't change anything */ 437 uidstr = NULL; 438 oflag = 0; 439 } 440 441 } else uid = pstruct->pw_uid; 442 443 if (group) { 444 switch (valid_group(group, &g_ptr, &warning)) { 445 case INVALID: 446 errmsg(M_INVALID, group, "group id"); 447 exit(EX_BADARG); 448 /*NOTREACHED*/ 449 case TOOBIG: 450 errmsg(M_TOOBIG, "gid", group); 451 exit(EX_BADARG); 452 /*NOTREACHED*/ 453 case UNIQUE: 454 errmsg(M_GRP_NOTUSED, group); 455 exit(EX_NAME_NOT_EXIST); 456 /*NOTREACHED*/ 457 case RESERVED: 458 gid = (gid_t)strtol(group, &ptr, (int)10); 459 errmsg(M_RESERVED_GID, gid); 460 break; 461 } 462 if (warning) 463 warningmsg(warning, group); 464 465 if (g_ptr != NULL) 466 gid = g_ptr->gr_gid; 467 else 468 gid = pstruct->pw_gid; 469 470 /* call passmgmt if gid is different, else ignore group */ 471 if (gid != pstruct->pw_gid) 472 call_pass = 1; 473 else group = NULL; 474 475 } else gid = pstruct->pw_gid; 476 477 if (grps && *grps) { 478 if (!(gidlist = valid_lgroup(grps, gid))) 479 exit(EX_BADARG); 480 } else 481 gidlist = (int **)0; 482 483 if (projects && *projects) { 484 if (! (projlist = valid_lproject(projects))) 485 exit(EX_BADARG); 486 } else 487 projlist = (projid_t **)0; 488 489 if (dir) { 490 if (REL_PATH(dir)) { 491 errmsg(M_RELPATH, dir); 492 exit(EX_BADARG); 493 } 494 if (strcmp(pstruct->pw_dir, dir) == 0) { 495 /* home directory is the same so ignore dflag & mflag */ 496 dir = NULL; 497 mflag = 0; 498 } else call_pass = 1; 499 } 500 501 if (mflag) { 502 if (stat(dir, &statbuf) == 0) { 503 /* Home directory exists */ 504 if (check_perm(statbuf, pstruct->pw_uid, 505 pstruct->pw_gid, S_IWOTH|S_IXOTH) != 0) { 506 errmsg(M_NO_PERM, logname, dir); 507 exit(EX_NO_PERM); 508 } 509 510 } else { 511 zfs_flags = get_default_zfs_flags(); 512 if (zflag || mflag > 1) 513 zfs_flags |= MANAGE_ZFS; 514 else if (Zflag) 515 zfs_flags &= ~MANAGE_ZFS; 516 ret = create_home(dir, NULL, uid, gid, zfs_flags); 517 } 518 519 if (ret == EX_SUCCESS) 520 ret = move_dir(pstruct->pw_dir, dir, 521 logname, zfs_flags); 522 523 if (ret != EX_SUCCESS) 524 exit(ret); 525 } 526 527 if (shell) { 528 if (REL_PATH(shell)) { 529 errmsg(M_RELPATH, shell); 530 exit(EX_BADARG); 531 } 532 if (strcmp(pstruct->pw_shell, shell) == 0) { 533 /* ignore s option if shell is not different */ 534 shell = NULL; 535 } else { 536 if (stat(shell, &statbuf) < 0 || 537 (statbuf.st_mode & S_IFMT) != S_IFREG || 538 (statbuf.st_mode & 0555) != 0555) { 539 540 errmsg(M_INVALID, shell, "shell"); 541 exit(EX_BADARG); 542 } 543 544 call_pass = 1; 545 } 546 } 547 548 if (comment) { 549 /* ignore comment if comment is not changed */ 550 if (strcmp(pstruct->pw_comment, comment)) 551 call_pass = 1; 552 else 553 comment = NULL; 554 } 555 556 /* inactive string is a positive integer */ 557 if (inactstr) { 558 /* convert inactstr to integer */ 559 inact = (int)strtol(inactstr, &ptr, 10); 560 if (*ptr || inact < 0) { 561 errmsg(M_INVALID, inactstr, "inactivity period"); 562 exit(EX_BADARG); 563 } 564 call_pass = 1; 565 } 566 567 /* expiration string is a date, newer than today */ 568 if (expire) { 569 if (*expire && 570 valid_expire(expire, (time_t *)0) == INVALID) { 571 errmsg(M_INVALID, expire, "expiration date"); 572 exit(EX_BADARG); 573 } 574 call_pass = 1; 575 } 576 577 if (nkeys > 0) 578 call_pass = 1; 579 580 /* that's it for validations - now do the work */ 581 582 if (grps) { 583 /* redefine login's supplentary group memberships */ 584 ret = edit_group(logname, new_logname, gidlist, 1); 585 if (ret != EX_SUCCESS) { 586 errmsg(M_UPDATE, "modified"); 587 exit(ret); 588 } 589 } 590 if (projects) { 591 ret = edit_project(logname, (char *)NULL, projlist, 0); 592 if (ret != EX_SUCCESS) { 593 errmsg(M_UPDATE, "modified"); 594 exit(ret); 595 } 596 } 597 598 599 if (!call_pass) exit(ret); 600 601 /* only get to here if need to call passmgmt */ 602 /* set up arguments to passmgmt in nargv array */ 603 nargv = malloc((30 + nkeys * 2) * sizeof (char *)); 604 605 argindex = 0; 606 nargv[argindex++] = PASSMGMT; 607 nargv[argindex++] = "-m"; /* modify */ 608 609 if (comment) { /* comment */ 610 nargv[argindex++] = "-c"; 611 nargv[argindex++] = comment; 612 } 613 614 if (dir) { 615 /* flags for home directory */ 616 nargv[argindex++] = "-h"; 617 nargv[argindex++] = dir; 618 } 619 620 if (group) { 621 /* set gid flag */ 622 nargv[argindex++] = "-g"; 623 (void) sprintf(gidstring, "%u", gid); 624 nargv[argindex++] = gidstring; 625 } 626 627 if (shell) { /* shell */ 628 nargv[argindex++] = "-s"; 629 nargv[argindex++] = shell; 630 } 631 632 if (inactstr) { 633 nargv[argindex++] = "-f"; 634 nargv[argindex++] = inactstr; 635 } 636 637 if (expire) { 638 nargv[argindex++] = "-e"; 639 nargv[argindex++] = expire; 640 } 641 642 if (uidstr) { /* set uid flag */ 643 nargv[argindex++] = "-u"; 644 (void) sprintf(uidstring, "%u", uid); 645 nargv[argindex++] = uidstring; 646 } 647 648 if (oflag) nargv[argindex++] = "-o"; 649 650 if (new_logname) { /* redefine login name */ 651 nargv[argindex++] = "-l"; 652 nargv[argindex++] = new_logname; 653 } 654 655 if (nkeys > 0) 656 addkey_args(nargv, &argindex); 657 658 /* finally - login name */ 659 nargv[argindex++] = logname; 660 661 /* set the last to null */ 662 nargv[argindex++] = NULL; 663 664 /* now call passmgmt */ 665 ret = PEX_FAILED; 666 for (tries = 3; ret != PEX_SUCCESS && tries--; ) { 667 switch (ret = call_passmgmt(nargv)) { 668 case PEX_SUCCESS: 669 case PEX_BUSY: 670 break; 671 672 case PEX_HOSED_FILES: 673 errmsg(M_HOSED_FILES); 674 exit(EX_INCONSISTENT); 675 break; 676 677 case PEX_SYNTAX: 678 case PEX_BADARG: 679 /* should NEVER occur that passmgmt usage is wrong */ 680 if (is_role(usertype)) 681 errmsg(M_MRUSAGE); 682 else 683 errmsg(M_MUSAGE); 684 exit(EX_SYNTAX); 685 break; 686 687 case PEX_BADUID: 688 /* uid in use - shouldn't happen print message anyway */ 689 errmsg(M_UID_USED, uid); 690 exit(EX_ID_EXISTS); 691 break; 692 693 case PEX_BADNAME: 694 /* invalid loname */ 695 errmsg(M_USED, logname); 696 exit(EX_NAME_EXISTS); 697 break; 698 699 default: 700 errmsg(M_UPDATE, "modified"); 701 exit(ret); 702 break; 703 } 704 } 705 if (tries == 0) { 706 errmsg(M_UPDATE, "modified"); 707 } 708 709 exit(ret); 710 /*NOTREACHED*/ 711 } 712