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