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 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <errno.h> 30 #include <locale.h> 31 #include <pwd.h> 32 #include <unistd.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <ctype.h> 37 #include <fcntl.h> 38 #include <nss_dbdefs.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/wait.h> 42 #include <tsol/label.h> 43 #include <zone.h> 44 #include <bsm/devalloc.h> 45 #include "allocate.h" 46 47 #if !defined(TEXT_DOMAIN) 48 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 49 #endif 50 51 #define ALLOC "allocate" 52 #define DEALLOC "deallocate" 53 #define LIST "list_devices" 54 55 extern void audit_allocate_argv(int, int, char *[]); 56 extern int audit_allocate_record(int); 57 58 int system_labeled = 0; 59 static int windowing = 0; 60 static int wdwmsg(char *name, char *msg); 61 62 static void 63 usage(int func) 64 { 65 if (system_labeled) { 66 char *use[6]; 67 68 use[0] = gettext("allocate [-s] [-w] [-U uname] [-z zonename] " 69 "[-F] device|-g dev-type"); 70 use[1] = gettext("deallocate [-s] [-w] [-z zonename] " 71 "[-F] device|-c dev-class|-g dev-type"); 72 use[2] = gettext("deallocate [-s] [-w] [-z zonename] -I"); 73 use[3] = gettext("list_devices [-s] [-U uid] [-z zonename] " 74 "[-a [-w]] -l|-n|-u [device]"); 75 use[4] = gettext("list_devices [-s] [-U uid] [-z zonename] " 76 "[-a [-w]] [-l|-n|-u] -c dev-class"); 77 use[5] = gettext("list_devices [-s] -d [dev-type]"); 78 79 switch (func) { 80 case 0: 81 (void) fprintf(stderr, "%s\n", use[0]); 82 break; 83 case 1: 84 (void) fprintf(stderr, "%s\n%s\n", 85 use[1], use[2]); 86 break; 87 case 2: 88 (void) fprintf(stderr, "%s\n%s\n%s\n", 89 use[3], use[4], use[5]); 90 break; 91 default: 92 (void) fprintf(stderr, 93 "%s\n%s\n%s\n%s\n%s\n%s\n", 94 use[0], use[1], use[2], use[3], use[4], 95 use[5]); 96 } 97 } else { 98 char *use[5]; 99 100 use[0] = gettext("allocate " 101 "[-s] [-U uname] [-F] device|-g dev-type"); 102 use[1] = gettext("deallocate [-s] [-F] device|-c dev-class"); 103 use[2] = gettext("deallocate [-s] -I"); 104 use[3] = gettext("list_devices " 105 "[-s] [-U uid] -l|-n|-u [device]"); 106 use[4] = gettext("list_devices " 107 "[-s] [-U uid] [-l|-n|-u] -c dev-class"); 108 109 switch (func) { 110 case 0: 111 (void) fprintf(stderr, "%s\n", use[0]); 112 break; 113 case 1: 114 (void) fprintf(stderr, "%s\n%s\n", 115 use[1], use[2]); 116 break; 117 case 2: 118 (void) fprintf(stderr, "%s\n%s\n", 119 use[3], use[4]); 120 break; 121 default: 122 (void) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 123 use[0], use[1], use[2], use[3], use[4]); 124 } 125 } 126 exit(1); 127 } 128 129 void 130 print_error(int error, char *name) 131 { 132 char *msg; 133 char msgbuf[200]; 134 135 switch (error) { 136 case ALLOCUERR: 137 msg = gettext("Specified device is allocated to another user."); 138 break; 139 case CHOWNERR: 140 msg = gettext("Failed to chown."); 141 break; 142 case CLEANERR: 143 msg = gettext("Unable to clean up device."); 144 break; 145 case CNTDEXECERR: 146 msg = gettext( 147 "Can't exec device-clean program for specified device."); 148 break; 149 case CNTFRCERR: 150 msg = gettext("Can't force deallocate specified device."); 151 break; 152 case DACACCERR: 153 msg = gettext( 154 "Can't access DAC file for the device specified."); 155 break; 156 case DAOFFERR: 157 msg = gettext( 158 "Device allocation feature is not activated " 159 "on this system."); 160 break; 161 case DAUTHERR: 162 msg = gettext("Device not allocatable."); 163 break; 164 case DEFATTRSERR: 165 msg = gettext("No default attributes for specified " 166 "device type."); 167 break; 168 case DEVLKERR: 169 msg = gettext("Concurrent operations for specified device, " 170 "try later."); 171 break; 172 case DEVLONGERR: 173 msg = gettext("Device name is too long."); 174 break; 175 case DEVNALLOCERR: 176 msg = gettext("Device not allocated."); 177 break; 178 case DEVNAMEERR: 179 msg = gettext("Device name error."); 180 break; 181 case DEVSTATEERR: 182 msg = gettext("Device specified is in allocate error state."); 183 break; 184 case DEVZONEERR: 185 msg = gettext("Can't find name of the zone to which " 186 "device is allocated."); 187 break; 188 case DSPMISSERR: 189 msg = gettext( 190 "Device special file(s) missing for specified device."); 191 break; 192 case LABELRNGERR: 193 msg = gettext( 194 "Operation inconsistent with device's label range."); 195 break; 196 case LOGINDEVPERMERR: 197 msg = gettext("Device controlled by logindevperm(4)"); 198 break; 199 case NODAERR: 200 msg = gettext("No entry for specified device."); 201 break; 202 case NODMAPERR: 203 msg = gettext("No entry for specified device."); 204 break; 205 case PREALLOCERR: 206 msg = gettext("Device already allocated."); 207 break; 208 case SETACLERR: 209 msg = gettext("Failed to set ACL."); 210 break; 211 case UAUTHERR: 212 msg = gettext( 213 "User lacks authorization required for this operation."); 214 break; 215 case ZONEERR: 216 msg = gettext("Failed to configure device in zone."); 217 break; 218 default: 219 msg = gettext("Unknown error code."); 220 break; 221 } 222 223 if (windowing) { 224 (void) snprintf(msgbuf, sizeof (msgbuf), "%s: %s\n", name, msg); 225 (void) wdwmsg(name, msgbuf); 226 } else { 227 (void) fprintf(stderr, "%s: %s\n", name, msg); 228 (void) fflush(stderr); 229 } 230 } 231 232 char *newenv[] = {"PATH=/usr/bin:/usr/sbin", 233 NULL, /* for LC_ALL */ 234 NULL, /* for LC_COLLATE */ 235 NULL, /* for LC_CTYPE */ 236 NULL, /* for LC_MESSAGES */ 237 NULL, /* for LC_NUMERIC */ 238 NULL, /* for LC_TIME */ 239 NULL, /* for LANG */ 240 NULL 241 }; 242 243 static char * 244 getenvent(char *name, char *env[]) 245 { 246 for (; *env != NULL; env++) { 247 if (strncmp(*env, name, strlen(name)) == 0) 248 return (*env); 249 } 250 return (NULL); 251 } 252 253 int 254 main(int argc, char *argv[], char *envp[]) 255 { 256 char *name, *env; 257 int func = -1, optflg = 0, error = 0, c; 258 zoneid_t zoneid; 259 uid_t uid; 260 char *uname = NULL, *device = NULL, *zonename = NULL; 261 char *zname; 262 char pw_buf[NSS_BUFLEN_PASSWD]; 263 struct passwd pw_ent; 264 int env_num = 1; /* PATH= is 0 entry */ 265 #ifdef DEBUG 266 struct stat statbuf; 267 #endif 268 269 (void) setlocale(LC_ALL, ""); 270 (void) textdomain(TEXT_DOMAIN); 271 272 system_labeled = is_system_labeled(); 273 274 /* test hook: see also mkdevalloc.c and devfsadm.c */ 275 if (!system_labeled) { 276 system_labeled = is_system_labeled_debug(&statbuf); 277 if (system_labeled) { 278 fprintf(stderr, "/ALLOCATE_FORCE_LABEL is set,\n" 279 "forcing system label on for testing...\n"); 280 } 281 } 282 283 /* 284 * get all enviroment variables 285 * which affect on internationalization. 286 */ 287 env = getenvent("LC_ALL=", envp); 288 if (env != NULL) 289 newenv[env_num++] = env; 290 env = getenvent("LC_COLLATE=", envp); 291 if (env != NULL) 292 newenv[env_num++] = env; 293 env = getenvent("LC_CTYPE=", envp); 294 if (env != NULL) 295 newenv[env_num++] = env; 296 env = getenvent("LC_MESSAGES=", envp); 297 if (env != NULL) 298 newenv[env_num++] = env; 299 env = getenvent("LC_NUMERIC=", envp); 300 if (env != NULL) 301 newenv[env_num++] = env; 302 env = getenvent("LC_TIME=", envp); 303 if (env != NULL) 304 newenv[env_num++] = env; 305 env = getenvent("LANG=", envp); 306 if (env != NULL) 307 newenv[env_num] = env; 308 309 if ((name = strrchr(argv[0], '/')) == NULL) 310 name = argv[0]; 311 else 312 name++; 313 314 if (strcmp(name, ALLOC) == 0) 315 func = 0; 316 else if (strcmp(name, DEALLOC) == 0) 317 func = 1; 318 else if (strcmp(name, LIST) == 0) 319 func = 2; 320 else 321 usage(-1); 322 323 audit_allocate_argv(func, argc, argv); 324 325 if (system_labeled) { 326 /* 327 * allocate, deallocate, list_devices run in 328 * global zone only. 329 */ 330 zoneid = getzoneid(); 331 if (zoneid != GLOBAL_ZONEID) 332 exit(GLOBALERR); 333 zname = GLOBAL_ZONENAME; 334 /* 335 * check if device allocation is activated. 336 */ 337 if (da_is_on() == 0) { 338 (void) fprintf(stderr, "%s%s", 339 gettext("Turn device allocation on"), 340 gettext(" to use this feature.\n")); 341 exit(DAOFFERR); 342 } 343 } 344 345 if (func == 0) { /* allocate */ 346 while ((c = getopt(argc, argv, "g:swz:FU:")) != -1) { 347 switch (c) { 348 case 'g': 349 optflg |= TYPE; 350 device = optarg; 351 break; 352 case 's': 353 optflg |= SILENT; 354 break; 355 case 'w': 356 if (system_labeled) { 357 optflg |= WINDOWING; 358 windowing = 1; 359 } else { 360 usage(func); 361 } 362 break; 363 case 'z': 364 if (system_labeled) { 365 optflg |= ZONENAME; 366 zonename = optarg; 367 } else { 368 usage(func); 369 } 370 break; 371 case 'F': 372 optflg |= FORCE; 373 break; 374 case 'U': 375 optflg |= USERNAME; 376 uname = optarg; 377 break; 378 case '?': 379 default : 380 usage(func); 381 } 382 } 383 384 /* 385 * allocate(1) must be supplied with one device argument 386 */ 387 if (device && ((argc - optind) >= 1)) 388 usage(func); 389 if (device == NULL) { 390 if ((argc - optind) != 1) 391 usage(func); 392 device = argv[optind]; 393 } 394 } 395 396 else if (func == 1) { /* deallocate */ 397 while ((c = getopt(argc, argv, "c:g:swz:FI")) != -1) { 398 switch (c) { 399 case 'c': 400 if (optflg & (TYPE | FORCE_ALL)) 401 usage(func); 402 optflg |= CLASS; 403 device = optarg; 404 break; 405 case 'g': 406 if (system_labeled) { 407 if (optflg & (CLASS | FORCE_ALL)) 408 usage(func); 409 optflg |= TYPE; 410 device = optarg; 411 } else { 412 usage(func); 413 } 414 break; 415 case 's': 416 optflg |= SILENT; 417 break; 418 case 'w': 419 if (system_labeled) { 420 optflg |= WINDOWING; 421 windowing = 1; 422 } else { 423 usage(func); 424 } 425 break; 426 case 'z': 427 if (system_labeled) { 428 optflg |= ZONENAME; 429 zonename = optarg; 430 } else { 431 usage(func); 432 } 433 break; 434 case 'F': 435 if (optflg & FORCE_ALL) 436 usage(func); 437 optflg |= FORCE; 438 break; 439 case 'I': 440 if (optflg & (CLASS | TYPE | FORCE)) 441 usage(func); 442 optflg |= FORCE_ALL; 443 break; 444 case '?': 445 default : 446 usage(func); 447 } 448 } 449 450 /* 451 * deallocate(1) must be supplied with one device 452 * argument unless the '-I' argument is supplied 453 */ 454 if (device || (optflg & FORCE_ALL)) { 455 if ((argc - optind) >= 1) 456 usage(func); 457 } else if (device == NULL) { 458 if ((argc - optind) != 1) 459 usage(func); 460 device = argv[optind]; 461 } 462 } 463 464 else if (func == 2) { /* list_devices */ 465 while ((c = getopt(argc, argv, "ac:dlnsuwz:U:")) != -1) { 466 switch (c) { 467 case 'a': 468 if (system_labeled) { 469 /* 470 * list auths, cleaning programs, 471 * labels. 472 */ 473 if (optflg & LISTDEFS) 474 usage(func); 475 optflg |= LISTATTRS; 476 } else { 477 usage(func); 478 } 479 break; 480 case 'c': 481 optflg |= CLASS; 482 device = optarg; 483 break; 484 case 'd': 485 if (system_labeled) { 486 /* 487 * List devalloc_defaults 488 * This cannot used with anything other 489 * than -s. 490 */ 491 if (optflg & (LISTATTRS | CLASS | 492 LISTALL | LISTFREE | LISTALLOC | 493 WINDOWING | ZONENAME | USERID)) 494 usage(func); 495 optflg |= LISTDEFS; 496 } else { 497 usage(func); 498 } 499 break; 500 case 'l': 501 if (optflg & (LISTFREE | LISTALLOC | LISTDEFS)) 502 usage(func); 503 optflg |= LISTALL; 504 break; 505 case 'n': 506 if (optflg & (LISTALL | LISTALLOC | LISTDEFS)) 507 usage(func); 508 optflg |= LISTFREE; 509 break; 510 case 's': 511 optflg |= SILENT; 512 break; 513 case 'u': 514 if (optflg & (LISTALL | LISTFREE | LISTDEFS)) 515 usage(func); 516 optflg |= LISTALLOC; 517 break; 518 case 'w': 519 if (system_labeled) { 520 if (optflg & LISTDEFS) 521 usage(func); 522 optflg |= WINDOWING; 523 } else { 524 usage(func); 525 } 526 break; 527 case 'z': 528 if (system_labeled) { 529 if (optflg & LISTDEFS) 530 usage(func); 531 optflg |= ZONENAME; 532 zonename = optarg; 533 } else { 534 usage(func); 535 } 536 break; 537 case 'U': 538 if (optflg & LISTDEFS) 539 usage(func); 540 optflg |= USERID; 541 uid = atoi(optarg); 542 break; 543 case '?': 544 default : 545 usage(func); 546 } 547 } 548 549 if (system_labeled) { 550 if (!(optflg & (LISTALL | LISTFREE | LISTALLOC | 551 LISTDEFS | WINDOWING))) { 552 if (!(optflg & CLASS)) 553 usage(func); 554 } 555 } else if (!(optflg & (LISTALL | LISTFREE | LISTALLOC))) { 556 if (!(optflg & CLASS)) 557 usage(func); 558 } 559 560 /* 561 * list_devices(1) takes an optional device argument. 562 */ 563 if (device && ((argc - optind) >= 1)) 564 usage(func); 565 if (device == NULL) { 566 if ((argc - optind) == 1) 567 device = argv[optind]; 568 else if ((argc - optind) > 1) 569 usage(func); 570 } 571 } 572 573 if (optflg & USERNAME) { 574 if (getpwnam_r(uname, &pw_ent, pw_buf, sizeof (pw_buf)) == 575 NULL) { 576 (void) fprintf(stderr, 577 gettext("Invalid user name -- %s -- \n"), uname); 578 exit(1); 579 } 580 uid = pw_ent.pw_uid; 581 } else if (optflg & USERID) { 582 if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) { 583 (void) fprintf(stderr, 584 gettext("Invalid user ID -- %d -- \n"), uid); 585 exit(1); 586 } 587 uid = pw_ent.pw_uid; 588 } else { 589 /* 590 * caller's uid is the default if no user specified. 591 */ 592 uid = getuid(); 593 } 594 595 /* 596 * global zone is the default if no zonename specified. 597 */ 598 if (zonename == NULL) { 599 zonename = zname; 600 } else { 601 if (zone_get_id(zonename, &zoneid) != 0) { 602 (void) fprintf(stderr, 603 gettext("Invalid zone name -- %s -- \n"), zonename); 604 exit(1); 605 } 606 } 607 608 if (func == 0) 609 error = allocate(optflg, uid, device, zonename); 610 else if (func == 1) 611 error = deallocate(optflg, uid, device, zonename); 612 else if (func == 2) 613 error = list_devices(optflg, uid, device, zonename); 614 615 (void) audit_allocate_record(error); 616 617 if (error) { 618 if (!(optflg & SILENT)) 619 print_error(error, name); 620 exit(error); 621 } 622 623 return (0); 624 } 625 626 /* 627 * Display error message via /etc/security/lib/wdwmsg script 628 */ 629 static int 630 wdwmsg(char *name, char *msg) 631 { 632 pid_t child_pid; 633 pid_t wait_pid; 634 int child_status; 635 636 /* Fork a child */ 637 switch (child_pid = fork()) { 638 case -1: /* FAILURE */ 639 return (-1); 640 break; 641 642 case 0: /* CHILD */ 643 (void) execl("/etc/security/lib/wdwmsg", "wdwmsg", msg, 644 name, "OK", NULL); 645 /* If exec failed, send message to stderr */ 646 (void) fprintf(stderr, "%s", msg); 647 return (-1); 648 649 default: /* PARENT */ 650 /* Wait for child to exit */ 651 wait_pid = waitpid(child_pid, &child_status, 0); 652 if ((wait_pid < 0) && (errno == ECHILD)) 653 return (0); 654 if ((wait_pid < 0) || (wait_pid != child_pid)) 655 return (-1); 656 if (WIFEXITED(child_status)) 657 return (WEXITSTATUS(child_status)); 658 if (WIFSIGNALED(child_status)) 659 return (WTERMSIG(child_status)); 660 return (0); 661 } 662 } 663