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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <auth_attr.h> 30 #include <auth_list.h> 31 #include <dirent.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <libintl.h> 35 #include <locale.h> 36 #include <pwd.h> 37 #include <signal.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include <bsm/devices.h> 44 #include <bsm/audit_uevents.h> 45 46 #include <sys/acl.h> 47 #include <sys/file.h> 48 #include <sys/procfs.h> 49 #include <sys/param.h> 50 #include <sys/resource.h> 51 #include <sys/stat.h> 52 #include <sys/time.h> 53 #include <sys/types.h> 54 #include <sys/wait.h> 55 56 #include "allocate.h" 57 58 #ifdef DEBUG 59 #define dprintf(s, a) (void) fprintf(stderr, s, a) 60 #define dperror(s) perror(s) 61 #else /* !DEBUG */ 62 #define dprintf(s, a) 63 #define dperror(s) 64 #endif /* DEBUG */ 65 66 #define EXIT(number) { \ 67 if (optflg & FORCE) \ 68 error = number; \ 69 else \ 70 return (number); \ 71 } 72 73 #define DEV_ALLOCATED(sbuf) ((sbuf).st_uid != ALLOC_UID || \ 74 ((sbuf).st_mode & ~S_IFMT) == ALLOC_MODE) 75 76 #define DEVICE_AUTH_SEPARATOR "," 77 #define PROCFS "/proc/" 78 79 extern void audit_allocate_list(char *); 80 extern void audit_allocate_device(char *); 81 82 extern char *newenv[]; 83 84 /* 85 * Checks if the specified user has any of the authorizations in the 86 * list of authorizations 87 */ 88 89 static int 90 is_authorized(char *auth_list, uid_t uid) 91 { 92 char *auth; 93 struct passwd *pw; 94 95 pw = getpwuid(uid); 96 if (pw == NULL) { 97 dprintf("Can't get user info for uid=%d\n", (int)uid); 98 return (0); 99 } 100 101 auth = strtok(auth_list, DEVICE_AUTH_SEPARATOR); 102 while (auth != NULL) { 103 if (chkauthattr(auth, pw->pw_name)) 104 return (1); 105 auth = strtok(NULL, DEVICE_AUTH_SEPARATOR); 106 } 107 return (0); 108 } 109 110 static int 111 check_devs(char *list) 112 { 113 char *file; 114 115 file = strtok(list, " "); 116 while (file != NULL) { 117 118 if (access(file, F_OK) == -1) { 119 dprintf("Unable to access file %s\n", file); 120 return (-1); 121 } 122 file = strtok(NULL, " "); 123 } 124 return (0); 125 } 126 127 static void 128 print_dev(devmap_t *dev_list) 129 { 130 char *file; 131 132 (void) printf(gettext("device: %s "), dev_list->dmap_devname); 133 (void) printf(gettext("type: %s "), dev_list->dmap_devtype); 134 (void) printf(gettext("files: ")); 135 136 file = strtok(dev_list->dmap_devlist, " "); 137 while (file != NULL) { 138 (void) printf("%s ", file); 139 file = strtok(NULL, " "); 140 } 141 (void) printf("\n"); 142 } 143 144 static int 145 list_device(int optflg, uid_t uid, char *device) 146 { 147 devalloc_t *dev_ent; 148 devmap_t *dev_list; 149 char file_name[MAXPATHLEN]; 150 struct stat stat_buf; 151 char *list; 152 int bytes_formated; 153 154 if ((dev_ent = getdanam(device)) == NULL) { 155 if ((dev_list = getdmapdev(device)) == NULL) { 156 dprintf("Unable to find %s in the allocate database\n", 157 device); 158 return (NODMAPENT); 159 } else if ((dev_ent = getdanam(dev_list->dmap_devname)) == 160 NULL) { 161 dprintf("Unable to find %s in the allocate database\n", 162 device); 163 return (NODAENT); 164 } 165 } else if ((dev_list = getdmapnam(device)) == NULL) { 166 dprintf("Unable to find %s in the allocate database\n", device); 167 return (NODMAPENT); 168 } 169 170 bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR, 171 dev_ent->da_devname); 172 if (bytes_formated <= 0) { 173 return (DEVNAME_ERR); 174 } else if (bytes_formated >= MAXPATHLEN) { 175 dprintf("device name %s is too long.\n", dev_ent->da_devname); 176 return (DEVNAME_TOOLONG); 177 } 178 179 if (stat(file_name, &stat_buf)) { 180 dprintf("Unable to stat %s\n", file_name); 181 dperror("Error:"); 182 return (DACACC); 183 } 184 185 if ((optflg & FREE) && DEV_ALLOCATED(stat_buf)) 186 return (ALLOC); 187 188 if ((optflg & LIST) && DEV_ALLOCATED(stat_buf) && 189 (stat_buf.st_uid != uid)) 190 return (ALLOC_OTHER); 191 192 if ((optflg & CURRENT) && (stat_buf.st_uid != uid)) 193 return (NALLOC); 194 195 if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE) 196 return (ALLOCERR); 197 198 if ((list = strdup(dev_list->dmap_devlist)) == NULL) 199 return (SYSERROR); 200 201 if (check_devs(list) == -1) { 202 free(list); 203 return (DSPMISS); 204 } 205 206 print_dev(dev_list); 207 208 free(list); 209 return (0); 210 } 211 212 int 213 list_devices(int optflg, uid_t uid, char *device) 214 { 215 DIR * dev_dir; 216 struct dirent *dac_file; 217 int error = 0, ret_code = 1; 218 219 if (optflg & USERID) { 220 if (!is_authorized(DEVICE_REVOKE_AUTH, getuid())) 221 return (NOTAUTH); 222 } 223 setdaent(); 224 225 if (device) { 226 return (list_device(optflg, uid, device)); 227 } 228 229 if ((dev_dir = opendir(DAC_DIR)) == NULL) { 230 231 dperror("Can't open DAC_DIR"); 232 return (DACACC); 233 } 234 235 while ((dac_file = readdir(dev_dir)) != NULL) { 236 if ((strcmp(dac_file->d_name, ".") == 0) || 237 (strcmp(dac_file->d_name, "..") == 0)) { 238 continue; 239 } else { 240 error = list_device(optflg, uid, dac_file->d_name); 241 ret_code = ret_code ? error : ret_code; 242 } 243 } 244 (void) closedir(dev_dir); 245 enddaent(); 246 return (ret_code); 247 } 248 249 /* 250 * Set the DAC characteristics of the file. 251 * This uses a fancy chmod() by setting a minimal ACL which sets the mode 252 * and discards any existing ACL. 253 */ 254 255 static int 256 newdac(char *file, uid_t owner, gid_t group, o_mode_t mode) 257 { 258 int err = 0; 259 260 do { 261 if (chown(file, owner, group) == -1) { 262 dperror("newdac, unable to chown"); 263 err = CHOWN_PERR; 264 } 265 } while (fdetach(file) == 0); 266 267 err = acl_strip(file, owner, group, (mode_t)mode); 268 269 if (err != 0) { 270 dperror("newdac, unable to setacl"); 271 err = SETACL_PERR; 272 } 273 274 return (err); 275 } 276 277 static int 278 lock_dev(char *file) 279 { 280 int fd; 281 282 dprintf("locking %s\n", file); 283 if ((fd = open(file, O_RDWR)) == -1) { 284 dperror("lock_dev, cannot open DAC file"); 285 return (DACACC); 286 } 287 288 if (lockf(fd, F_TLOCK, 0) == -1) { 289 dperror("lock_dev, cannot set lock"); 290 return (DACLCK); 291 } 292 293 return (0); 294 } 295 296 static int 297 mk_alloc(char *list, uid_t uid) 298 { 299 char *file; 300 int err; 301 302 file = strtok(list, " "); 303 while (file != NULL) { 304 305 dprintf("Allocating %s\n", file); 306 if ((err = newdac(file, uid, getgid(), ALLOC_MODE)) != 0) { 307 (void) newdac(file, ALLOC_UID, ALLOC_GID, 308 ALLOC_ERR_MODE); 309 return (err); 310 } 311 312 file = strtok(NULL, " "); 313 } 314 return (0); 315 } 316 317 /* 318 * mk_revoke() is used instead of system("/usr/sbin/fuser -k file") 319 * because "/usr/sbin/fuser -k file" kills all processes 320 * working with the file, even "vold" (bug #4095152). 321 */ 322 static int 323 mk_revoke(int optflg, char *file) 324 { 325 char buf[MAXPATHLEN]; 326 int r = 0, p[2], fp, lock; 327 FILE *ptr; 328 prpsinfo_t info; 329 pid_t pid, c_pid; 330 331 (void) strcpy(buf, PROCFS); 332 333 /* 334 * vfork() and execle() just to make the same output 335 * as before fixing of bug #4095152. 336 * The problem is that the "fuser" command prints 337 * one part of output into stderr and another into stdout, 338 * but user sees them mixed. Of course, better to change "fuser" 339 * or to intercept and not to print its output. 340 */ 341 if (!(optflg & SILENT)) { 342 c_pid = vfork(); 343 if (c_pid == -1) 344 return (-1); 345 if (c_pid == 0) { 346 dprintf("first exec fuser %s\n", file); 347 (void) execle("/usr/sbin/fuser", "fuser", file, NULL, 348 newenv); 349 dperror("first exec fuser"); 350 _exit(1); 351 } 352 353 (void) waitpid(c_pid, &lock, 0); 354 dprintf("exit status %x\n", lock); 355 if (WEXITSTATUS(lock) != 0) 356 return (-1); 357 } 358 dprintf("first continuing c_pid=%d\n", c_pid); 359 360 if (pipe(p)) { 361 dperror("pipe"); 362 return (-1); 363 } 364 365 /* vfork() and execle() to catch output and to process it */ 366 c_pid = vfork(); 367 if (c_pid == -1) { 368 dperror("second vfork"); 369 return (-1); 370 } 371 dprintf("second continuing c_pid=%d\n", c_pid); 372 373 if (c_pid == 0) { 374 (void) close(p[0]); 375 (void) close(1); 376 (void) fcntl(p[1], F_DUPFD, 1); 377 (void) close(p[1]); 378 (void) close(2); 379 dprintf("second exec fuser %s\n", file); 380 (void) execle("/usr/sbin/fuser", "fuser", file, NULL, newenv); 381 dperror("second exec fuser"); 382 _exit(1); 383 } 384 385 (void) close(p[1]); 386 if ((ptr = fdopen(p[0], "r")) != NULL) { 387 while (!feof(ptr)) { 388 if (fscanf(ptr, "%d", &pid) > 0) { 389 (void) sprintf(buf + strlen(PROCFS), "%d", pid); 390 if ((fp = open(buf, O_RDONLY)) == -1) { 391 dperror(buf); 392 continue; 393 } 394 if (ioctl(fp, PIOCPSINFO, (char *)&info) 395 == -1) { 396 dprintf("%d psinfo failed", pid); 397 dperror(""); 398 (void) close(fp); 399 continue; 400 } 401 (void) close(fp); 402 if (strcmp(info.pr_fname, "vold") == NULL) { 403 dprintf("%d matched vold name\n", pid); 404 continue; 405 } 406 dprintf("killing %s", info.pr_fname); 407 dprintf("(%d)\n", pid); 408 if ((r = kill(pid, SIGKILL)) == -1) { 409 dprintf("kill %d", pid); 410 dperror(""); 411 break; 412 } 413 } 414 } 415 dprintf("eof reached %x\n", ptr); 416 } else { 417 dperror("fdopen(p[0])"); 418 r = -1; 419 } 420 421 (void) fclose(ptr); 422 return (r); 423 } 424 425 static int 426 mk_unalloc(int optflg, char *list) 427 { 428 char *file; 429 int error = 0; 430 int child, status; 431 432 audit_allocate_list(list); 433 434 child = vfork(); 435 switch (child) { 436 case -1: 437 return (-1); 438 case 0: 439 (void) setuid(0); 440 file = strtok(list, " "); 441 while (file != NULL) { 442 dprintf("Deallocating %s\n", file); 443 if (mk_revoke(optflg, file) < 0) { 444 dprintf("mk_unalloc: unable to revoke %s\n", 445 file); 446 dperror(""); 447 error = CNTFRC; 448 break; 449 } 450 error = newdac(file, ALLOC_UID, ALLOC_GID, 451 DEALLOC_MODE); 452 file = strtok(NULL, " "); 453 } 454 exit(error); 455 default: 456 while (wait(&status) != child); 457 if (WIFEXITED(status)) { 458 return (WEXITSTATUS(status)); 459 } 460 return (-1); 461 } 462 } 463 464 static int 465 exec_clean(int optflg, char *name, char *path) 466 { 467 char *mode, *cmd; 468 int status; 469 int c; 470 471 if ((optflg & (FORCE_ALL | SILENT)) == (FORCE_ALL | SILENT)) 472 mode = "-I"; 473 else if (optflg & FORCE_ALL) 474 mode = "-i"; 475 else if (optflg & FORCE) 476 mode = "-f"; 477 else 478 mode = "-s"; 479 if ((cmd = strrchr(path, '/')) == NULL) 480 cmd = path; 481 else 482 cmd++; /* skip leading '/' */ 483 484 c = vfork(); 485 switch (c) { 486 case -1: 487 return (-1); 488 case 0: 489 (void) setuid(0); 490 dprintf("clean script: %s, ", path); 491 dprintf("cmd=%s, ", cmd); 492 dprintf("mode=%s, ", mode); 493 dprintf("name=%s\n", name); 494 (void) execle(path, cmd, mode, name, NULL, newenv); 495 dprintf("Unable to execute clean up script %s\n", path); 496 dperror(""); 497 exit(CNTDEXEC); 498 default: 499 while (wait(&status) != c); 500 if (WIFEXITED(status)) 501 return (WEXITSTATUS(status)); 502 dprintf("exit status %d\n", status); 503 return (-1); 504 } 505 } 506 507 static int 508 deallocate_dev(int optflg, devalloc_t *dev_ent, uid_t uid) 509 { 510 devmap_t *dev_list; 511 char file_name[MAXPATHLEN]; 512 struct stat stat_buf; 513 char *list; 514 int error = 0, err; 515 int bytes_formated; 516 517 bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR, 518 dev_ent->da_devname); 519 if (bytes_formated <= 0) { 520 return (DEVNAME_ERR); 521 } else if (bytes_formated >= MAXPATHLEN) { 522 dprintf("device name %s is too long.\n", dev_ent->da_devname); 523 return (DEVNAME_TOOLONG); 524 } 525 526 audit_allocate_device(file_name); 527 528 if (stat(file_name, &stat_buf)) { 529 dprintf("Unable to stat %s\n", file_name); 530 dperror("Error:"); 531 return (DACACC); 532 } 533 534 if (!(optflg & FORCE) && stat_buf.st_uid != uid && 535 DEV_ALLOCATED(stat_buf)) { 536 return (NALLOCU); 537 } 538 539 if (!(optflg & FORCE_ALL) && !DEV_ALLOCATED(stat_buf)) { 540 if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE) { 541 if (!(optflg & FORCE)) 542 return (ALLOCERR); 543 } else 544 return (NALLOC); 545 } 546 547 /* All checks passed, time to lock and deallocate */ 548 if ((error = lock_dev(file_name)) != 0) 549 return (error); 550 551 if ((err = newdac(file_name, ALLOC_UID, ALLOC_GID, DEALLOC_MODE)) 552 != 0) { 553 (void) newdac(file_name, ALLOC_UID, ALLOC_GID, ALLOC_ERR_MODE); 554 EXIT(err); 555 } 556 557 if ((dev_list = getdmapnam(dev_ent->da_devname)) == NULL) { 558 dprintf("Unable to find %s in the device map database\n", 559 dev_ent->da_devname); 560 EXIT(NODMAPENT); 561 } else { 562 if ((list = strdup(dev_list->dmap_devlist)) == NULL) { 563 EXIT(SYSERROR) 564 } else { 565 if (mk_unalloc(optflg, list) != 0) { 566 (void) newdac(file_name, ALLOC_UID, ALLOC_GID, 567 ALLOC_ERR_MODE); 568 free(list); 569 list = NULL; 570 EXIT(DEVLST); 571 } 572 } 573 } 574 575 if (list != NULL) 576 free(list); 577 if (exec_clean(optflg, dev_ent->da_devname, dev_ent->da_devexec)) 578 EXIT(CLEAN_ERR); 579 return (error); 580 } 581 582 static int 583 allocate_dev(int optflg, uid_t uid, devalloc_t *dev_ent) 584 { 585 devmap_t *dev_list; 586 char file_name[MAXPATHLEN]; 587 struct stat stat_buf; 588 char *list; 589 int error = 0; 590 int bytes_formated; 591 592 bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR, 593 dev_ent->da_devname); 594 if (bytes_formated <= 0) { 595 return (DEVNAME_ERR); 596 } else if (bytes_formated >= MAXPATHLEN) { 597 dprintf("device name %s is too long.\n", dev_ent->da_devname); 598 return (DEVNAME_TOOLONG); 599 } 600 601 audit_allocate_device(file_name); 602 603 if (stat(file_name, &stat_buf)) { 604 dprintf("Unable to stat %s\n", file_name); 605 dperror("Error:"); 606 return (DACACC); 607 } 608 609 if (DEV_ALLOCATED(stat_buf)) { 610 if (optflg & FORCE) { 611 if (deallocate_dev(FORCE, dev_ent, uid)) { 612 dprintf("Couldn't force deallocate device %s\n", 613 dev_ent->da_devname); 614 return (CNTFRC); 615 } 616 } else if (stat_buf.st_uid == uid) { 617 return (ALLOC); 618 } else 619 return (ALLOC_OTHER); 620 } 621 if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE) 622 return (ALLOCERR); 623 624 if (strcmp(dev_ent->da_devauth, "*") == 0) { 625 dprintf("Device %s is not allocatable\n", dev_ent->da_devname); 626 return (AUTHERR); 627 } 628 629 if (strcmp(dev_ent->da_devauth, "@")) { 630 if (!is_authorized(dev_ent->da_devauth, uid)) { 631 dprintf("User %d is unauthorized to allocate\n", 632 (int)uid); 633 return (IMPORT_ERR); 634 } 635 } 636 637 if ((dev_list = getdmapnam(dev_ent->da_devname)) == NULL) { 638 dprintf("Unable to find %s in device map database\n", 639 dev_ent->da_devname); 640 return (NODMAPENT); 641 } 642 643 if ((list = strdup(dev_list->dmap_devlist)) == NULL) 644 return (SYSERROR); 645 646 if (check_devs(list) == -1) { 647 free(list); 648 return (DSPMISS); 649 } 650 651 /* All checks passed, time to lock and allocate */ 652 if ((error = lock_dev(file_name)) != 0) { 653 free(list); 654 return (error); 655 } 656 657 if ((error = newdac(file_name, uid, getgid(), ALLOC_MODE)) != 0) { 658 (void) newdac(file_name, ALLOC_UID, ALLOC_GID, ALLOC_ERR_MODE); 659 free(list); 660 return (error); 661 } 662 663 /* refresh list from check_devs overwritting it */ 664 (void) strcpy(list, dev_list->dmap_devlist); 665 audit_allocate_list(list); 666 667 if (mk_alloc(list, uid) != 0) { 668 /* refresh list from mk_alloc overwritting it */ 669 (void) strcpy(list, dev_list->dmap_devlist); 670 (void) mk_unalloc(optflg, list); 671 free(list); 672 return (DEVLST); 673 } 674 675 free(list); 676 return (0); 677 } 678 679 int 680 allocate(int optflg, uid_t uid, char *device) 681 { 682 devalloc_t *dev_ent; 683 devmap_t *dev_list; 684 685 if (((optflg & FORCE) || uid != getuid()) && 686 !is_authorized(DEVICE_REVOKE_AUTH, getuid())) 687 return (NOTAUTH); 688 689 setdaent(); 690 setdmapent(); 691 692 if (!(optflg & TYPE)) { 693 if ((dev_ent = getdanam(device)) == NULL) { 694 if ((dev_list = getdmapdev(device)) == NULL) 695 return (NODMAPENT); 696 else if ((dev_ent = getdanam(dev_list->dmap_devname)) 697 == NULL) 698 return (NODAENT); 699 } 700 return (allocate_dev(optflg, uid, dev_ent)); 701 } 702 703 while ((dev_ent = getdatype(device)) != NULL) { 704 dprintf("trying to allocate %s\n", dev_ent->da_devname); 705 if (!allocate_dev(optflg, uid, dev_ent)) { 706 return (0); 707 } 708 } 709 enddaent(); 710 return (NO_DEVICE); 711 } 712 713 int 714 deallocate(int optflg, uid_t uid, char *device) 715 { 716 DIR *dev_dir; 717 struct dirent *dac_file; 718 devalloc_t *dev_ent; 719 devmap_t *dev_list; 720 int error = NODAENT; 721 722 if (optflg & (FORCE | FORCE_ALL) && 723 !is_authorized(DEVICE_REVOKE_AUTH, getuid())) 724 return (NOTAUTH); 725 if (optflg & FORCE_ALL) 726 optflg |= FORCE; 727 728 setdaent(); 729 setdmapent(); 730 731 if (!(optflg & FORCE_ALL)) { 732 if ((dev_ent = getdanam(device)) == NULL) { 733 if ((dev_list = getdmapdev(device)) == NULL) 734 return (NODMAPENT); 735 else if ((dev_ent = getdanam(dev_list->dmap_devname)) 736 == NULL) 737 return (NODAENT); 738 } 739 740 return (deallocate_dev(optflg, dev_ent, uid)); 741 } 742 743 if ((dev_dir = opendir(DAC_DIR)) == NULL) { 744 dperror("Can't open DAC_DIR"); 745 return (DACACC); 746 } 747 748 while ((dac_file = readdir(dev_dir)) != NULL) { 749 if ((strcmp(dac_file->d_name, ".") == 0) || 750 (strcmp(dac_file->d_name, "..") == 0)) { 751 continue; 752 } else { 753 if ((dev_ent = getdanam(dac_file->d_name)) == NULL) { 754 continue; 755 } 756 error = deallocate_dev(optflg, dev_ent, uid); 757 } 758 } 759 (void) closedir(dev_dir); 760 enddaent(); 761 return (error); 762 } 763