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