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