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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Vold compatibility for rmvolmgr: emulate old commands as well as 28 * action_filemgr.so to notify legacy apps via /tmp/.removable pipes. 29 * A lot of this code is copied verbatim from vold sources. 30 * 31 * Here's the original description of action_filemgr.so: 32 * 33 * action_filemgr.so - filemgr interface routines for rmmount 34 * 35 * This shared object allows rmmount to communicate with filemgr. 36 * This is done by communicating over a named pipe that filemgr 37 * creates in directory NOTIFY_DIR. The name of the pipe must 38 * begin with NOTIFY_NAME. This source file contains #define 39 * compiler directives set the values of NOTIFY_DIR and NOTIFY_NAME. 40 * 41 * After a partition on a medium has been mounted as a result of 42 * either insertion or remounting of the medium, the action() 43 * method creates a file named with the symbolic name of the 44 * device in which the medium is inserted and the partition name 45 * (e.g. "jaz0-s2") in NOTIFY_DIR. The file consists of one text 46 * line containing a string naming the mount point of the partition, 47 * a string giving the raw device path to the partition, and a 48 * string naming the file system type on the partition. The action() 49 * method then sends a single character ('i' for insertion, 'r' for 50 * remounting) through the named pipe NOTIFY_NAME to tell filemgr to 51 * look for new files in NOTIFY_DIR. 52 * 53 * If a medium containing no mountable partitions is inserted 54 * or remounted in a device, the action() method creates a file 55 * named with the symbolic name of the device in NOTIFY_DIR. 56 * The file consists of one text line containing a string 57 * giving the symbolic name of the device and a string naming 58 * the reason that the medium couldn't be mounted. The action 59 * method then sends either an 'i' or an 'r' through the named 60 * pipe to tell filemgr to look for new files in NOTIFY_DIR. 61 * 62 * When a medium is ejected or unmounted, the action() method 63 * removes the files that were created in NOTIFY_DIR when the medium 64 * was inserted or remounted and sends a single character ('e' for 65 * ejection, 'u' for unmounting) through the named pipe. 66 * 67 * The following environment variables must be set before calling action(): 68 * 69 * VOLUME_ACTION action that occurred (e.g. "insert", "eject") 70 * VOLUME_SYMDEV symbolic name (e.g. "cdrom0", "floppy1") 71 * VOLUME_NAME volume name (e.g. "unnamed_cdrom", "s2") 72 */ 73 74 75 #include <stdio.h> 76 #include <stdlib.h> 77 #include <unistd.h> 78 #include <fcntl.h> 79 #include <string.h> 80 #include <strings.h> 81 #include <dirent.h> 82 #include <signal.h> 83 #include <errno.h> 84 #include <libintl.h> 85 #include <zone.h> 86 #include <pwd.h> 87 #include <sys/types.h> 88 #include <sys/stat.h> 89 #include <sys/dkio.h> 90 #include <sys/cdio.h> 91 #include <sys/vtoc.h> 92 #include <sys/param.h> 93 #include <sys/wait.h> 94 #include <libcontract.h> 95 #include <sys/contract/process.h> 96 #include <sys/ctfs.h> 97 #include <tsol/label.h> 98 99 #include "vold.h" 100 #include "rmm_common.h" 101 102 int rmm_debug = 0; 103 boolean_t rmm_vold_actions_enabled = B_FALSE; 104 boolean_t rmm_vold_mountpoints_enabled = B_FALSE; 105 106 static char *prog_name = NULL; 107 static pid_t prog_pid = 0; 108 static int system_labeled = 0; 109 static uid_t mnt_uid = (uid_t)-1; 110 static gid_t mnt_gid = (gid_t)-1; 111 static zoneid_t mnt_zoneid = -1; 112 static char mnt_zoneroot[MAXPATHLEN]; 113 static char mnt_userdir[MAXPATHLEN]; 114 115 /* 116 * Private attribute types and attributes. 117 */ 118 static const char notify_characters[] = { 119 'e', 120 'i', 121 'r', 122 'u' 123 }; 124 125 static const char *result_strings[] = { 126 "FALSE", 127 "TRUE" 128 }; 129 130 #define NOTIFY_DIR "/tmp/.removable" /* dir where filemgr looks */ 131 #define NOTIFY_NAME "notify" /* named pipe to talk over */ 132 133 static void volrmmount_usage(); 134 static void volcheck_usage(); 135 static int vold_action(struct action_arg *aap); 136 static void vold_update_mountpoints(struct action_arg *aap); 137 static char *not_mountable(struct action_arg *aa); 138 static int create_one_notify_file(char *fstype, 139 char *mount_point, 140 char *notify_file, 141 char *raw_partitionp, 142 char *reason, 143 char *symdev); 144 static int create_notify_files(struct action_arg **aa); 145 static boolean_t notify_clients(action_t action, int do_notify); 146 static void popdir(int fd); 147 static int pushdir(const char *dir); 148 static boolean_t remove_notify_files(struct action_arg **aa); 149 150 /* 151 * should be called once from main() 152 */ 153 /* ARGSUSED */ 154 void 155 vold_init(int argc, char **argv) 156 { 157 system_labeled = is_system_labeled(); 158 } 159 160 /* 161 * Old version of rmmount(8) 162 */ 163 /* ARGSUSED */ 164 int 165 vold_rmmount(int argc, char **argv) 166 { 167 char *volume_action; 168 char *volume_mediatype; 169 char *volume_mount_mode; 170 char *volume_name; 171 char *volume_path; 172 char *volume_pcfs_id; 173 char *volume_symdev; 174 char *volume_zonename; 175 char *volume_user; 176 action_t action; 177 char mountpoint[MAXPATHLEN]; 178 char *zonemountpoint; 179 char *arg_mountpoint = NULL; 180 LibHalContext *hal_ctx; 181 DBusError error; 182 rmm_error_t rmm_error; 183 int ret; 184 185 prog_name = argv[0]; 186 prog_pid = getpid(); 187 188 mnt_zoneroot[0] = '\0'; 189 mnt_userdir[0] = '\0'; 190 191 volume_action = getenv("VOLUME_ACTION"); 192 volume_mediatype = getenv("VOLUME_MEDIATYPE"); 193 volume_mount_mode = getenv("VOLUME_MOUNT_MODE"); 194 volume_name = getenv("VOLUME_NAME"); 195 volume_path = getenv("VOLUME_PATH"); 196 volume_pcfs_id = getenv("VOLUME_PCFS_ID"); 197 volume_symdev = getenv("VOLUME_SYMDEV"); 198 199 if (system_labeled) { 200 volume_zonename = getenv("VOLUME_ZONE_NAME"); 201 volume_user = getenv("VOLUME_USER"); 202 } 203 if (volume_action == NULL) { 204 dbgprintf("%s(%ld): VOLUME_ACTION was null!!\n", 205 prog_name, prog_pid); 206 return (-1); 207 } 208 if (volume_mediatype == NULL) { 209 dbgprintf("%s(%ld): VOLUME_MEDIATYPE was null!!\n", 210 prog_name, prog_pid); 211 return (-1); 212 } 213 if (volume_mount_mode == NULL) { 214 volume_mount_mode = "rw"; 215 } 216 if (volume_name == NULL) { 217 dbgprintf("%s(%ld): VOLUME_NAME was null!!\n", 218 prog_name, prog_pid); 219 return (-1); 220 } 221 if (volume_path == NULL) { 222 dbgprintf("%s(%ld): VOLUME_PATH was null!!\n", 223 prog_name, prog_pid); 224 return (-1); 225 } 226 if (volume_pcfs_id == NULL) { 227 volume_pcfs_id = ""; 228 } 229 if (volume_symdev == NULL) { 230 dbgprintf("%s(%ld): VOLUME_SYMDEV was null!!\n", 231 prog_name, prog_pid); 232 return (-1); 233 } 234 235 if (system_labeled) { 236 if (volume_zonename != NULL && 237 strcmp(volume_zonename, GLOBAL_ZONENAME) != 0) { 238 if ((mnt_zoneid = 239 getzoneidbyname(volume_zonename)) != -1) { 240 if (zone_getattr(mnt_zoneid, ZONE_ATTR_ROOT, 241 mnt_zoneroot, MAXPATHLEN) == -1) { 242 dbgprintf("%s(%ld): NO ZONEPATH!!\n", 243 prog_name, prog_pid); 244 return (-1); 245 } 246 } 247 } else { 248 mnt_zoneid = GLOBAL_ZONEID; 249 mnt_zoneroot[0] = '\0'; 250 } 251 if (volume_user != NULL) { 252 struct passwd *pw; 253 254 if ((pw = getpwnam(volume_user)) == NULL) { 255 dbgprintf("%s(%ld) %s\n", prog_name, prog_pid, 256 ": VOLUME_USER was not a valid user!"); 257 return (-1); 258 } 259 mnt_uid = pw->pw_uid; 260 mnt_gid = pw->pw_gid; 261 262 if (snprintf(mnt_userdir, sizeof (mnt_userdir), 263 "/%s-%s", volume_user, volume_symdev) >= 264 sizeof (mnt_userdir)) 265 return (-1); 266 } else { 267 mnt_uid = 0; 268 mnt_userdir[0] = '\0'; 269 } 270 271 rmm_vold_mountpoints_enabled = B_FALSE; 272 rmm_vold_actions_enabled = B_TRUE; 273 } else { 274 rmm_vold_mountpoints_enabled = B_TRUE; 275 rmm_vold_actions_enabled = B_TRUE; 276 } 277 278 if ((hal_ctx = rmm_hal_init(0, 0, 0, 0, &error, &rmm_error)) == NULL) { 279 rmm_dbus_error_free(&error); 280 281 /* if HAL's not running, must be root */ 282 if (geteuid() != 0) { 283 (void) fprintf(stderr, 284 gettext("%s(%ld) error: must be root to execute\n"), 285 prog_name, prog_pid); 286 return (-1); 287 } 288 } 289 290 if (strcmp(volume_action, "eject") == 0) { 291 action = EJECT; 292 } else if (strcmp(volume_action, "insert") == 0) { 293 action = INSERT; 294 295 if (system_labeled) { 296 /* 297 * create mount point 298 */ 299 if (strlen(mnt_userdir) > 0) { 300 if (snprintf(mountpoint, MAXPATHLEN, 301 "%s/%s%s", mnt_zoneroot, volume_mediatype, 302 mnt_userdir) > MAXPATHLEN) { 303 return (-1); 304 305 } 306 (void) makepath(mountpoint, 0700); 307 (void) chown(mountpoint, mnt_uid, mnt_gid); 308 /* 309 * set the top level directory bits to 0755 310 * so user can access it. 311 */ 312 if (snprintf(mountpoint, MAXPATHLEN, 313 "%s/%s", mnt_zoneroot, 314 volume_mediatype) <= MAXPATHLEN) { 315 (void) chmod(mountpoint, 0755); 316 } 317 } 318 if (snprintf(mountpoint, MAXPATHLEN, 319 "%s/%s%s/%s", mnt_zoneroot, volume_mediatype, 320 mnt_userdir, volume_name) > MAXPATHLEN) { 321 (void) fprintf(stderr, 322 gettext("%s(%ld) error: path too long\n"), 323 prog_name, prog_pid); 324 return (-1); 325 } 326 327 /* make our mountpoint */ 328 (void) makepath(mountpoint, 0755); 329 330 arg_mountpoint = mountpoint; 331 } 332 } else if (strcmp(volume_action, "remount") == 0) { 333 action = REMOUNT; 334 } else if (strcmp(volume_action, "unmount") == 0) { 335 action = UNMOUNT; 336 } 337 338 ret = rmm_action(hal_ctx, volume_symdev, action, 0, 0, 0, 339 arg_mountpoint) ? 0 : 1; 340 341 if (hal_ctx != NULL) { 342 rmm_hal_fini(hal_ctx); 343 } 344 345 return (ret); 346 } 347 348 349 /* 350 * this should be called after rmm_hal_{mount,unmount,eject} 351 */ 352 int 353 vold_postprocess(LibHalContext *hal_ctx, const char *udi, 354 struct action_arg *aap) 355 { 356 int ret = 0; 357 358 /* valid mountpoint required */ 359 if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) { 360 rmm_volume_aa_update_mountpoint(hal_ctx, udi, aap); 361 if ((aap->aa_mountpoint == NULL) || 362 (strlen(aap->aa_mountpoint) == 0)) { 363 return (1); 364 } 365 } 366 367 if (rmm_vold_mountpoints_enabled) { 368 vold_update_mountpoints(aap); 369 } 370 if (rmm_vold_actions_enabled) { 371 ret = vold_action(aap); 372 } 373 374 return (ret); 375 } 376 377 /* 378 * update legacy symlinks 379 * 380 * For cdrom: 381 * 382 * /cdrom/<name> -> original mountpoint 383 * /cdrom/cdrom0 -> ./<name> 384 * /cdrom/cdrom -> cdrom0 (only for cdrom0) 385 * 386 * If it's a slice or partition, /cdrom/<name> becomes a directory: 387 * 388 * /cdrom/<name>/s0 389 * 390 * Same for rmdisk and floppy. 391 * 392 * On labeled system (Trusted Solaris), links are in a user directory. 393 */ 394 static void 395 vold_update_mountpoints(struct action_arg *aap) 396 { 397 boolean_t is_partition; 398 char part_dir[2 * MAXNAMELEN]; 399 char symname_mp[2 * MAXNAMELEN]; 400 char symcontents_mp[MAXNAMELEN]; 401 char symname[2 * MAXNAMELEN]; 402 char symcontents[MAXNAMELEN]; 403 404 is_partition = (aap->aa_partname != NULL); 405 406 if (!system_labeled) { 407 if (!is_partition) { 408 /* /cdrom/<name> -> original mountpoint */ 409 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 410 "%s", aap->aa_mountpoint); 411 (void) snprintf(symname_mp, sizeof (symname_mp), 412 "/%s/%s", aap->aa_media, aap->aa_name); 413 } else { 414 /* /cdrom/<name>/slice -> original mountpoint */ 415 (void) snprintf(part_dir, sizeof (part_dir), 416 "/%s/%s", aap->aa_media, aap->aa_name); 417 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 418 "%s", aap->aa_mountpoint); 419 (void) snprintf(symname_mp, sizeof (symname_mp), 420 "/%s/%s/%s", aap->aa_media, aap->aa_name, 421 aap->aa_partname); 422 423 } 424 /* /cdrom/cdrom0 -> ./<name> */ 425 (void) snprintf(symcontents, sizeof (symcontents), 426 "./%s", aap->aa_name); 427 (void) snprintf(symname, sizeof (symname), 428 "/%s/%s", aap->aa_media, aap->aa_symdev); 429 } else { 430 if (!is_partition) { 431 /* /cdrom/<user>/<name> -> original mountpoint */ 432 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 433 "%s", aap->aa_mountpoint); 434 (void) snprintf(symname_mp, sizeof (symname_mp), 435 "%s/%s/%s", mnt_zoneroot, aap->aa_media, 436 aap->aa_symdev); 437 } else { 438 /* /cdrom/<user>/<name>/slice -> original mountpoint */ 439 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 440 "%s", aap->aa_mountpoint); 441 (void) snprintf(symname_mp, sizeof (symname_mp), 442 "%s/%s/%s", mnt_zoneroot, aap->aa_media, 443 aap->aa_symdev, aap->aa_partname); 444 } 445 446 /* /cdrom/<user>/cdrom0 -> ./<user>/<name> */ 447 (void) snprintf(symcontents, sizeof (symcontents), 448 ".%s/%s", mnt_userdir, aap->aa_name); 449 (void) snprintf(symname, sizeof (symname), "%s/%s/%s", 450 mnt_zoneroot, aap->aa_media, aap->aa_symdev); 451 } 452 453 (void) unlink(symname); 454 (void) unlink(symname_mp); 455 if (is_partition) { 456 (void) rmdir(part_dir); 457 } 458 459 if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) { 460 (void) mkdir(aap->aa_media, 0755); 461 if (is_partition) { 462 (void) mkdir(part_dir, 0755); 463 } 464 (void) symlink(symcontents_mp, symname_mp); 465 (void) symlink(symcontents, symname); 466 } 467 } 468 469 470 static int 471 vold_action(struct action_arg *aap) 472 { 473 action_t action; 474 int result; 475 int do_notify = FALSE; 476 action_t notify_act = EJECT; 477 struct action_arg *aa[2]; 478 struct action_arg a1; 479 480 dbgprintf("%s[%d]: entering action()\n", __FILE__, __LINE__); 481 482 /* 483 * on Trusted Extensions, actions are executed in the user's zone 484 */ 485 if (mnt_zoneid > GLOBAL_ZONEID) { 486 pid_t pid; 487 int status; 488 int ifx; 489 int tmpl_fd; 490 int err = 0; 491 492 tmpl_fd = open64(CTFS_ROOT "/process/template", 493 O_RDWR); 494 if (tmpl_fd == -1) 495 return (1); 496 497 /* 498 * Deliver no events, don't inherit, 499 * and allow it to be orphaned. 500 */ 501 err |= ct_tmpl_set_critical(tmpl_fd, 0); 502 err |= ct_tmpl_set_informative(tmpl_fd, 0); 503 err |= ct_pr_tmpl_set_fatal(tmpl_fd, 504 CT_PR_EV_HWERR); 505 err |= ct_pr_tmpl_set_param(tmpl_fd, 506 CT_PR_PGRPONLY | 507 CT_PR_REGENT); 508 if (err || ct_tmpl_activate(tmpl_fd)) { 509 (void) close(tmpl_fd); 510 return (1); 511 } 512 switch (pid = fork1()) { 513 case 0: 514 (void) ct_tmpl_clear(tmpl_fd); 515 for (ifx = 0; ifx < _NFILE; ifx++) 516 (void) close(ifx); 517 518 if (zone_enter(mnt_zoneid) == -1) 519 _exit(0); 520 521 /* entered zone, proceed to action */ 522 break; 523 case -1: 524 dbgprintf("fork1 failed \n "); 525 return (1); 526 default : 527 (void) ct_tmpl_clear(tmpl_fd); 528 (void) close(tmpl_fd); 529 if (waitpid(pid, &status, 0) < 0) { 530 dbgprintf("%s(%ld): waitpid() " 531 "failed (errno %d) \n", 532 prog_name, prog_pid, errno); 533 return (1); 534 } 535 } 536 } 537 538 /* only support one action at a time XXX */ 539 a1.aa_path = NULL; 540 aa[0] = aap; 541 aa[1] = &a1; 542 543 action = aa[0]->aa_action; 544 545 if (action == CLEAR_MOUNTS) { 546 /* 547 * Remove the notifications files, but don't 548 * notify the client. The "clear_mounts" action 549 * simply clears all existing mounts of a medium's 550 * partitions after a medium has been repartitioned. 551 * Then vold builds a new file system that reflects 552 * the medium's new partition structure and mounts 553 * the new partitions by calling rmmount, and therefore 554 * action(), with the VOLUME_ACTION environment variable 555 * set to "remount". 556 */ 557 result = remove_notify_files(aa); 558 result = TRUE; 559 } else if (action == EJECT) { 560 result = remove_notify_files(aa); 561 if (result == TRUE) { 562 do_notify = TRUE; 563 notify_act = EJECT; 564 } 565 } else if (action = INSERT) { 566 result = create_notify_files(aa); 567 if (result == TRUE) { 568 do_notify = TRUE; 569 notify_act = INSERT; 570 } 571 } else if (action == REMOUNT) { 572 result = create_notify_files(aa); 573 if (result == TRUE) { 574 do_notify = TRUE; 575 notify_act = REMOUNT; 576 } 577 } else if (action == UNMOUNT) { 578 result = remove_notify_files(aa); 579 if (result == TRUE) { 580 do_notify = TRUE; 581 notify_act = UNMOUNT; 582 } 583 } else { 584 dbgprintf("%s[%d]: action(): invalid action: %s\n", 585 __FILE__, __LINE__, action); 586 result = FALSE; 587 } 588 589 if (result == TRUE) { 590 result = notify_clients(notify_act, do_notify); 591 } 592 593 dbgprintf("%s[%d]: leaving action(), result = %s\n", 594 __FILE__, __LINE__, result_strings[result]); 595 596 if (mnt_zoneid > GLOBAL_ZONEID) { 597 /* exit forked local zone process */ 598 _exit(0); 599 } 600 601 if (result == TRUE) { 602 /* 603 * File Manager is running. return 0. 604 * see man page rmmount.conf(5). 605 */ 606 return (0); 607 } else { 608 return (1); 609 } 610 } 611 612 613 /* 614 * Returns NULL if a medium or partition is mountable 615 * and a string stating the reason the medium or partition 616 * can't be mounted if the medium or partition isn't mountable. 617 * 618 * If the volume_name of the medium or partition is one of the 619 * following, the medium or partition isn't mountable. 620 * 621 * unlabeled_<media_type> 622 * unknown_format 623 * password_protected 624 */ 625 /* ARGSUSED */ 626 static char * 627 not_mountable(struct action_arg *aa) 628 { 629 return (NULL); 630 } 631 632 static int 633 create_notify_files(struct action_arg **aa) 634 { 635 int ai; 636 char *fstype; 637 char *mount_point; 638 char notify_file[64]; 639 char *raw_partitionp; 640 char *reason; /* Why the medium wasn't mounted */ 641 int result; 642 char *symdev; 643 644 dbgprintf("%s[%d]: entering create_notify_files()\n", 645 __FILE__, __LINE__); 646 647 ai = 0; 648 result = FALSE; 649 symdev = aa[ai]->aa_symdev; 650 while ((aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) { 651 if (aa[ai]->aa_mountpoint != NULL) { 652 if (aa[ai]->aa_type) { 653 fstype = aa[ai]->aa_type; 654 } else { 655 fstype = "unknown"; 656 } 657 mount_point = aa[ai]->aa_mountpoint; 658 if (aa[ai]->aa_partname != NULL) { 659 /* 660 * Is aa_partname ever NULL? 661 * When time permits, check. 662 * If it is, the action taken 663 * in the else clause could produce 664 * file name conflicts. 665 */ 666 sprintf(notify_file, "%s-%s", symdev, 667 aa[ai]->aa_partname); 668 } else { 669 sprintf(notify_file, "%s-0", symdev); 670 } 671 reason = NULL; 672 } else { 673 /* 674 * The partition isn't mounted. 675 */ 676 fstype = "none"; 677 mount_point = "none"; 678 reason = not_mountable(aa[ai]); 679 if (reason != NULL) { 680 sprintf(notify_file, "%s-0", symdev); 681 } else { 682 /* 683 * Either the partition is a backup slice, or 684 * rmmount tried to mount the partition, but 685 * idenf_fs couldn't identify the file system 686 * type; that can occur when rmmount is 687 * trying to mount all the slices in a Solaris 688 * VTOC, and one or more partitions don't have 689 * file systems in them. 690 */ 691 if (aa[0]->aa_partname != NULL) { 692 /* 693 * Is aa_partname ever NULL? 694 * When time permits, check. 695 * If it is, the action taken 696 * in the else clause could produce 697 * file name conflicts. 698 */ 699 sprintf(notify_file, "%s-%s", symdev, 700 aa[0]->aa_partname); 701 } else { 702 sprintf(notify_file, "%s-0", symdev); 703 } 704 if ((aa[0]->aa_type != NULL) && 705 (strcmp(aa[0]->aa_type, "backup_slice") 706 == 0)) { 707 reason = "backup_slice"; 708 } else { 709 reason = "unformatted_media"; 710 } 711 /* 712 * "unformatted_media" should be 713 * changed to "unformmated_medium" for 714 * grammatical correctness, but 715 * "unformatted_media" is now specified 716 * in the interface to filemgr, so the 717 * change can't be made without the 718 * approval of the CDE group. 719 */ 720 } 721 } 722 raw_partitionp = aa[0]->aa_rawpath; 723 result = create_one_notify_file(fstype, 724 mount_point, 725 notify_file, 726 raw_partitionp, 727 reason, 728 symdev); 729 ai++; 730 } 731 dbgprintf("%s[%d]: leaving create_notify_files(), result = %s\n", 732 __FILE__, __LINE__, result_strings[result]); 733 return (result); 734 } 735 736 static int 737 create_one_notify_file(char *fstype, 738 char *mount_point, 739 char *notify_file, 740 char *raw_partitionp, 741 char *reason, 742 char *symdev) 743 { 744 /* 745 * For a mounted partition, create a notification file 746 * indicating the mount point, the raw device pathname 747 * of the partition, and the partition's file system 748 * type. For an unmounted partition, create a 749 * notification file containing the reason that the 750 * partition wasn't mounted and the raw device pathname 751 * of the partition. 752 * 753 * Create the file as root in a world-writable 754 * directory that resides in a world-writable directory. 755 * 756 * Handle two possible race conditions that could 757 * allow security breaches. 758 */ 759 760 int current_working_dir_fd; 761 int file_descriptor; 762 FILE *filep; 763 int result; 764 765 dbgprintf("%s[%d]:Entering create_one_notify_file()\n", 766 __FILE__, __LINE__); 767 dbgprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype); 768 dbgprintf("\tcreate_one_notify_file(): mount_point = %s\n", 769 mount_point); 770 dbgprintf("\tcreate_one_notify_file(): notify_file = %s\n", 771 notify_file); 772 dbgprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n", 773 raw_partitionp); 774 if (reason != NULL) { 775 dbgprintf("\tcreate_one_notify_file(): reason = %s\n", reason); 776 } else { 777 dbgprintf("\tcreate_one_notify_file(): reason = NULL\n"); 778 } 779 dbgprintf("\tcreate_one_notify_file(): symdev = %s\n", symdev); 780 781 result = TRUE; 782 /* 783 * Handle Race Condition One: 784 * 785 * If NOTIFY_DIR exists, make sure it is not a symlink. 786 * if it is, remove it and try to create it. Check 787 * again to make sure NOTIFY_DIR isn't a symlink. 788 * If it is, remove it and return without creating 789 * a notification file. The condition can only occur if 790 * someone is trying to break into the system by running 791 * a program that repeatedly creates NOTIFY_DIR as a 792 * symlink. If NOTIFY_DIR exists and isn't a symlink, 793 * change the working directory to NOTIFY_DIR. 794 */ 795 current_working_dir_fd = pushdir(NOTIFY_DIR); 796 if (current_working_dir_fd < 0) { 797 (void) makepath(NOTIFY_DIR, 0777); 798 current_working_dir_fd = pushdir(NOTIFY_DIR); 799 if (current_working_dir_fd < 0) { 800 result = FALSE; 801 } 802 } 803 /* 804 * Handle Race Condition Two: 805 * 806 * Create the notification file in NOTIFY_DIR. 807 * Remove any files with the same name that may already be 808 * there, using remove(), as it safely removes directories. 809 * Then open the file O_CREAT|O_EXCL, which doesn't follow 810 * symlinks and requires that the file not exist already, 811 * so the new file actually resides in the current working 812 * directory. Create the file with access mode 644, which 813 * renders it unusable by anyone trying to break into the 814 * system. 815 */ 816 if (result == TRUE) { 817 /* 818 * The current working directory is now NOTIFY_DIR. 819 */ 820 (void) remove(notify_file); 821 file_descriptor = 822 open(notify_file, O_CREAT|O_EXCL|O_WRONLY, 0644); 823 if (file_descriptor < 0) { 824 dbgprintf("%s[%d]: can't create %s/%s; %m\n", 825 __FILE__, __LINE__, NOTIFY_DIR, notify_file); 826 result = FALSE; 827 } else { 828 filep = fdopen(file_descriptor, "w"); 829 if (filep != NULL) { 830 if (reason == NULL) { 831 (void) fprintf(filep, "%s %s %s", 832 mount_point, 833 raw_partitionp, 834 fstype); 835 (void) fclose(filep); 836 dbgprintf("%s[%d]: Just wrote %s %s %s to %s\n", 837 __FILE__, 838 __LINE__, 839 mount_point, 840 raw_partitionp, 841 fstype, 842 notify_file); 843 } else { 844 (void) fprintf(filep, "%s %s", 845 reason, raw_partitionp); 846 (void) fclose(filep); 847 dbgprintf("%s[%d]: Just wrote %s %s to %s\n", 848 __FILE__, 849 __LINE__, 850 reason, 851 raw_partitionp, 852 notify_file); 853 } 854 } else { 855 dbgprintf("%s[%d]: can't write %s/%s; %m\n", 856 __FILE__, __LINE__, 857 NOTIFY_DIR, notify_file); 858 (void) close(file_descriptor); 859 result = FALSE; 860 } 861 } 862 popdir(current_working_dir_fd); 863 } 864 dbgprintf("%s[%d]: leaving create_one_notify_file, result = %s\n", 865 __FILE__, __LINE__, result_strings[result]); 866 return (result); 867 } 868 869 static boolean_t 870 notify_clients(action_t action, int do_notify) 871 { 872 /* 873 * Notify interested applications of changes in the state 874 * of removable media. Interested applications are those 875 * that create a named pipe in NOTIFY_DIR with a name that 876 * begins with "notify". Open the pipe and write a 877 * character through it that indicates the type of state 878 * change = 'e' for ejections, 'i' for insertions, 'r' 879 * for remounts of the file systems on repartitioned media, 880 * and 'u' for unmounts of file systems. 881 */ 882 883 int current_working_dir_fd; 884 DIR *dirp; 885 struct dirent *dir_entryp; 886 size_t len; 887 int fd; 888 char namebuf[MAXPATHLEN]; 889 char notify_character; 890 void (*old_signal_handler)(); 891 int result; 892 struct stat sb; 893 894 dbgprintf("%s[%d]: entering notify_clients()\n", __FILE__, __LINE__); 895 896 result = TRUE; 897 /* 898 * Use relative pathnames after changing the 899 * working directory to the notification directory. 900 * Check to make sure that each "notify" file is a 901 * named pipe to make sure that it hasn't changed 902 * its file type, which could mean that someone is 903 * trying to use "notify" files to break into the 904 * system. 905 */ 906 if ((current_working_dir_fd = pushdir(NOTIFY_DIR)) < 0) { 907 result = FALSE; 908 } 909 if (result == TRUE) { 910 dirp = opendir("."); 911 if (dirp == NULL) { 912 dbgprintf("%s[%d]:opendir failed on '.'; %m\n", 913 __FILE__, __LINE__); 914 popdir(current_working_dir_fd); 915 result = FALSE; 916 } 917 } 918 if (result == TRUE) { 919 /* 920 * Read through the directory and write a notify 921 * character to all files whose names start with "notify". 922 */ 923 result = FALSE; 924 old_signal_handler = signal(SIGPIPE, SIG_IGN); 925 len = strlen(NOTIFY_NAME); 926 while (dir_entryp = readdir(dirp)) { 927 if (strncmp(dir_entryp->d_name, NOTIFY_NAME, len) 928 != 0) { 929 continue; 930 } 931 result = TRUE; 932 if (do_notify != TRUE) { 933 continue; 934 } 935 (void) sprintf(namebuf, "%s/%s", 936 NOTIFY_DIR, dir_entryp->d_name); 937 if ((fd = open(namebuf, O_WRONLY|O_NDELAY)) < 0) { 938 dbgprintf("%s[%d]: open failed for %s; %m\n", 939 __FILE__, __LINE__, namebuf); 940 continue; 941 } 942 /* 943 * Check to be sure that the entry is a named pipe. 944 * That closes a small security hole that could 945 * enable unauthorized access to the system root. 946 */ 947 if ((fstat(fd, &sb) < 0) || (!S_ISFIFO(sb.st_mode))) { 948 dbgprintf("%s[%d]: %s isn't a named pipe\n", 949 __FILE__, __LINE__, namebuf); 950 951 (void) close(fd); 952 continue; 953 } 954 notify_character = notify_characters[action]; 955 if (write(fd, ¬ify_character, 1) < 0) { 956 dbgprintf("%s[%d]: write failed for %s; %m\n", 957 __FILE__, __LINE__, namebuf); 958 (void) close(fd); 959 continue; 960 } 961 (void) close(fd); 962 } 963 (void) closedir(dirp); 964 (void) signal(SIGPIPE, old_signal_handler); 965 popdir(current_working_dir_fd); 966 } 967 dbgprintf("%s[%d]: leaving notify_clients(), result = %s\n", 968 __FILE__, __LINE__, result_strings[result]); 969 return (result); 970 } 971 972 static void 973 popdir(int fd) 974 { 975 /* 976 * Change the current working directory to the directory 977 * specified by fd and close the fd. Exit the program 978 * on failure. 979 */ 980 if (fchdir(fd) < 0) { 981 dbgprintf("%s[%d]: popdir() failed\n", __FILE__, __LINE__); 982 exit(1); 983 } 984 (void) close(fd); 985 } 986 987 static int 988 pushdir(const char *dir) 989 { 990 /* 991 * Change the current working directory to dir and 992 * return a file descriptor for the old working 993 * directory. 994 * 995 * Exception handling: 996 * 997 * If dir doesn't exist, leave the current working 998 * directory the same and return -1. 999 * 1000 * If dir isn't a directory, remove it, leave the 1001 * current working directory the same, and return -1. 1002 * 1003 * If open() fails on the current working directory 1004 * or the chdir operation fails on dir, leave the 1005 * current working directory the same and return -1. 1006 */ 1007 1008 int current_working_dir_fd; 1009 struct stat stat_buf; 1010 1011 if (lstat(dir, &stat_buf) < 0) { 1012 dbgprintf("%s[%d]: push_dir_and_check(): %s does not exist\n", 1013 __FILE__, __LINE__, dir); 1014 return (-1); 1015 } 1016 1017 if (!(S_ISDIR(stat_buf.st_mode))) { 1018 dbgprintf("%s[%d]: push_dir_and_check(): %s not a directory.\n", 1019 __FILE__, __LINE__, dir); 1020 (void) remove(dir); 1021 return (-1); 1022 } 1023 if ((current_working_dir_fd = open(".", O_RDONLY)) < 0) { 1024 dbgprintf("%s[%d]: push_dir_and_check(): can't open %s.\n", 1025 __FILE__, __LINE__, dir); 1026 return (-1); 1027 } 1028 if (chdir(dir) < 0) { 1029 (void) close(current_working_dir_fd); 1030 dbgprintf("%s[%d]: push_dir_and_check(): " 1031 "can't chdir() to %s.\n", __FILE__, __LINE__, dir); 1032 return (-1); 1033 } 1034 return (current_working_dir_fd); 1035 } 1036 1037 static boolean_t 1038 remove_notify_files(struct action_arg **aa) 1039 { 1040 int ai; 1041 int current_working_dir_fd; 1042 char notify_file[64]; 1043 int result; 1044 char *symdev; 1045 1046 dbgprintf("%s[%d]: entering remove_notify_files()\n", 1047 __FILE__, __LINE__); 1048 1049 ai = 0; 1050 result = TRUE; 1051 symdev = aa[ai]->aa_symdev; 1052 while ((result == TRUE) && 1053 (aa[ai] != NULL) && 1054 (aa[ai]->aa_path != NULL)) { 1055 1056 if (not_mountable(aa[ai])) { 1057 sprintf(notify_file, "%s-0", symdev); 1058 } else if (aa[ai]->aa_partname != NULL) { 1059 /* 1060 * Is aa_partname ever NULL? 1061 * When time permits, check. 1062 * If it is, the action taken 1063 * in the else clause could produce 1064 * file name conflicts. 1065 */ 1066 sprintf(notify_file, "%s-%s", 1067 symdev, aa[0]->aa_partname); 1068 } else { 1069 sprintf(notify_file, "%s-0", symdev); 1070 } 1071 1072 current_working_dir_fd = pushdir(NOTIFY_DIR); 1073 if (current_working_dir_fd < 0) { 1074 result = FALSE; 1075 } 1076 if ((result == TRUE) && (remove(notify_file) < 0)) { 1077 dbgprintf("%s[%d]: remove %s/%s; %m\n", 1078 __FILE__, __LINE__, NOTIFY_DIR, notify_file); 1079 result = FALSE; 1080 } 1081 if (current_working_dir_fd != -1) { 1082 popdir(current_working_dir_fd); 1083 } 1084 ai++; 1085 } 1086 dbgprintf("%s[%d]: leaving remove_notify_files(), result = %s\n", 1087 __FILE__, __LINE__, result_strings[result]); 1088 1089 return (result); 1090 } 1091