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