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