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(1M) 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 dprintf("%s(%ld): VOLUME_ACTION was null!!\n", 205 prog_name, prog_pid); 206 return (-1); 207 } 208 if (volume_mediatype == NULL) { 209 dprintf("%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 dprintf("%s(%ld): VOLUME_NAME was null!!\n", 218 prog_name, prog_pid); 219 return (-1); 220 } 221 if (volume_path == NULL) { 222 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("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 dprintf("%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 dprintf("%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 dprintf("%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(4). 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 dprintf("%s[%d]: entering create_notify_files()\n", __FILE__, __LINE__); 645 646 ai = 0; 647 result = FALSE; 648 symdev = aa[ai]->aa_symdev; 649 while ((aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) { 650 if (aa[ai]->aa_mountpoint != NULL) { 651 if (aa[ai]->aa_type) { 652 fstype = aa[ai]->aa_type; 653 } else { 654 fstype = "unknown"; 655 } 656 mount_point = aa[ai]->aa_mountpoint; 657 if (aa[ai]->aa_partname != NULL) { 658 /* 659 * Is aa_partname ever NULL? 660 * When time permits, check. 661 * If it is, the action taken 662 * in the else clause could produce 663 * file name conflicts. 664 */ 665 sprintf(notify_file, "%s-%s", symdev, 666 aa[ai]->aa_partname); 667 } else { 668 sprintf(notify_file, "%s-0", symdev); 669 } 670 reason = NULL; 671 } else { 672 /* 673 * The partition isn't mounted. 674 */ 675 fstype = "none"; 676 mount_point = "none"; 677 reason = not_mountable(aa[ai]); 678 if (reason != NULL) { 679 sprintf(notify_file, "%s-0", symdev); 680 } else { 681 /* 682 * Either the partition is a backup slice, or 683 * rmmount tried to mount the partition, but 684 * idenf_fs couldn't identify the file system 685 * type; that can occur when rmmount is 686 * trying to mount all the slices in a Solaris 687 * VTOC, and one or more partitions don't have 688 * file systems in them. 689 */ 690 if (aa[0]->aa_partname != NULL) { 691 /* 692 * Is aa_partname ever NULL? 693 * When time permits, check. 694 * If it is, the action taken 695 * in the else clause could produce 696 * file name conflicts. 697 */ 698 sprintf(notify_file, "%s-%s", symdev, 699 aa[0]->aa_partname); 700 } else { 701 sprintf(notify_file, "%s-0", symdev); 702 } 703 if ((aa[0]->aa_type != NULL) && 704 (strcmp(aa[0]->aa_type, "backup_slice") 705 == 0)) { 706 reason = "backup_slice"; 707 } else { 708 reason = "unformatted_media"; 709 } 710 /* 711 * "unformatted_media" should be 712 * changed to "unformmated_medium" for 713 * grammatical correctness, but 714 * "unformatted_media" is now specified 715 * in the interface to filemgr, so the 716 * change can't be made without the 717 * approval of the CDE group. 718 */ 719 } 720 } 721 raw_partitionp = aa[0]->aa_rawpath; 722 result = create_one_notify_file(fstype, 723 mount_point, 724 notify_file, 725 raw_partitionp, 726 reason, 727 symdev); 728 ai++; 729 } 730 dprintf("%s[%d]: leaving create_notify_files(), result = %s\n", 731 __FILE__, __LINE__, result_strings[result]); 732 return (result); 733 } 734 735 static int 736 create_one_notify_file(char *fstype, 737 char *mount_point, 738 char *notify_file, 739 char *raw_partitionp, 740 char *reason, 741 char *symdev) 742 { 743 /* 744 * For a mounted partition, create a notification file 745 * indicating the mount point, the raw device pathname 746 * of the partition, and the partition's file system 747 * type. For an unmounted partition, create a 748 * notification file containing the reason that the 749 * partition wasn't mounted and the raw device pathname 750 * of the partition. 751 * 752 * Create the file as root in a world-writable 753 * directory that resides in a world-writable directory. 754 * 755 * Handle two possible race conditions that could 756 * allow security breaches. 757 */ 758 759 int current_working_dir_fd; 760 int file_descriptor; 761 FILE *filep; 762 int result; 763 764 dprintf("%s[%d]:Entering create_one_notify_file()\n", 765 __FILE__, __LINE__); 766 dprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype); 767 dprintf("\tcreate_one_notify_file(): mount_point = %s\n", mount_point); 768 dprintf("\tcreate_one_notify_file(): notify_file = %s\n", notify_file); 769 dprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n", 770 raw_partitionp); 771 if (reason != NULL) { 772 dprintf("\tcreate_one_notify_file(): reason = %s\n", reason); 773 } else { 774 dprintf("\tcreate_one_notify_file(): reason = NULL\n"); 775 } 776 dprintf("\tcreate_one_notify_file(): symdev = %s\n", symdev); 777 778 result = TRUE; 779 /* 780 * Handle Race Condition One: 781 * 782 * If NOTIFY_DIR exists, make sure it is not a symlink. 783 * if it is, remove it and try to create it. Check 784 * again to make sure NOTIFY_DIR isn't a symlink. 785 * If it is, remove it and return without creating 786 * a notification file. The condition can only occur if 787 * someone is trying to break into the system by running 788 * a program that repeatedly creates NOTIFY_DIR as a 789 * symlink. If NOTIFY_DIR exists and isn't a symlink, 790 * change the working directory to NOTIFY_DIR. 791 */ 792 current_working_dir_fd = pushdir(NOTIFY_DIR); 793 if (current_working_dir_fd < 0) { 794 (void) makepath(NOTIFY_DIR, 0777); 795 current_working_dir_fd = pushdir(NOTIFY_DIR); 796 if (current_working_dir_fd < 0) { 797 result = FALSE; 798 } 799 } 800 /* 801 * Handle Race Condition Two: 802 * 803 * Create the notification file in NOTIFY_DIR. 804 * Remove any files with the same name that may already be 805 * there, using remove(), as it safely removes directories. 806 * Then open the file O_CREAT|O_EXCL, which doesn't follow 807 * symlinks and requires that the file not exist already, 808 * so the new file actually resides in the current working 809 * directory. Create the file with access mode 644, which 810 * renders it unusable by anyone trying to break into the 811 * system. 812 */ 813 if (result == TRUE) { 814 /* 815 * The current working directory is now NOTIFY_DIR. 816 */ 817 (void) remove(notify_file); 818 file_descriptor = 819 open(notify_file, O_CREAT|O_EXCL|O_WRONLY, 0644); 820 if (file_descriptor < 0) { 821 dprintf("%s[%d]: can't create %s/%s; %m\n", 822 __FILE__, __LINE__, NOTIFY_DIR, notify_file); 823 result = FALSE; 824 } else { 825 filep = fdopen(file_descriptor, "w"); 826 if (filep != NULL) { 827 if (reason == NULL) { 828 (void) fprintf(filep, "%s %s %s", 829 mount_point, 830 raw_partitionp, 831 fstype); 832 (void) fclose(filep); 833 dprintf("%s[%d]: Just wrote %s %s %s to %s\n", 834 __FILE__, 835 __LINE__, 836 mount_point, 837 raw_partitionp, 838 fstype, 839 notify_file); 840 } else { 841 (void) fprintf(filep, "%s %s", 842 reason, raw_partitionp); 843 (void) fclose(filep); 844 dprintf("%s[%d]: Just wrote %s %s to %s\n", 845 __FILE__, 846 __LINE__, 847 reason, 848 raw_partitionp, 849 notify_file); 850 } 851 } else { 852 dprintf("%s[%d]: can't write %s/%s; %m\n", 853 __FILE__, __LINE__, 854 NOTIFY_DIR, notify_file); 855 (void) close(file_descriptor); 856 result = FALSE; 857 } 858 } 859 popdir(current_working_dir_fd); 860 } 861 dprintf("%s[%d]: leaving create_one_notify_file, result = %s\n", 862 __FILE__, __LINE__, result_strings[result]); 863 return (result); 864 } 865 866 static boolean_t 867 notify_clients(action_t action, int do_notify) 868 { 869 /* 870 * Notify interested applications of changes in the state 871 * of removable media. Interested applications are those 872 * that create a named pipe in NOTIFY_DIR with a name that 873 * begins with "notify". Open the pipe and write a 874 * character through it that indicates the type of state 875 * change = 'e' for ejections, 'i' for insertions, 'r' 876 * for remounts of the file systems on repartitioned media, 877 * and 'u' for unmounts of file systems. 878 */ 879 880 int current_working_dir_fd; 881 DIR *dirp; 882 struct dirent *dir_entryp; 883 size_t len; 884 int fd; 885 char namebuf[MAXPATHLEN]; 886 char notify_character; 887 void (*old_signal_handler)(); 888 int result; 889 struct stat sb; 890 891 dprintf("%s[%d]: entering notify_clients()\n", __FILE__, __LINE__); 892 893 result = TRUE; 894 /* 895 * Use relative pathnames after changing the 896 * working directory to the notification directory. 897 * Check to make sure that each "notify" file is a 898 * named pipe to make sure that it hasn't changed 899 * its file type, which could mean that someone is 900 * trying to use "notify" files to break into the 901 * system. 902 */ 903 if ((current_working_dir_fd = pushdir(NOTIFY_DIR)) < 0) { 904 result = FALSE; 905 } 906 if (result == TRUE) { 907 dirp = opendir("."); 908 if (dirp == NULL) { 909 dprintf("%s[%d]:opendir failed on '.'; %m\n", 910 __FILE__, __LINE__); 911 popdir(current_working_dir_fd); 912 result = FALSE; 913 } 914 } 915 if (result == TRUE) { 916 /* 917 * Read through the directory and write a notify 918 * character to all files whose names start with "notify". 919 */ 920 result = FALSE; 921 old_signal_handler = signal(SIGPIPE, SIG_IGN); 922 len = strlen(NOTIFY_NAME); 923 while (dir_entryp = readdir(dirp)) { 924 if (strncmp(dir_entryp->d_name, NOTIFY_NAME, len) 925 != 0) { 926 continue; 927 } 928 result = TRUE; 929 if (do_notify != TRUE) { 930 continue; 931 } 932 (void) sprintf(namebuf, "%s/%s", 933 NOTIFY_DIR, dir_entryp->d_name); 934 if ((fd = open(namebuf, O_WRONLY|O_NDELAY)) < 0) { 935 dprintf("%s[%d]: open failed for %s; %m\n", 936 __FILE__, __LINE__, namebuf); 937 continue; 938 } 939 /* 940 * Check to be sure that the entry is a named pipe. 941 * That closes a small security hole that could 942 * enable unauthorized access to the system root. 943 */ 944 if ((fstat(fd, &sb) < 0) || (!S_ISFIFO(sb.st_mode))) { 945 dprintf("%s[%d]: %s isn't a named pipe\n", 946 __FILE__, __LINE__, namebuf); 947 948 (void) close(fd); 949 continue; 950 } 951 notify_character = notify_characters[action]; 952 if (write(fd, ¬ify_character, 1) < 0) { 953 dprintf("%s[%d]: write failed for %s; %m\n", 954 __FILE__, __LINE__, namebuf); 955 (void) close(fd); 956 continue; 957 } 958 (void) close(fd); 959 } 960 (void) closedir(dirp); 961 (void) signal(SIGPIPE, old_signal_handler); 962 popdir(current_working_dir_fd); 963 } 964 dprintf("%s[%d]: leaving notify_clients(), result = %s\n", 965 __FILE__, __LINE__, result_strings[result]); 966 return (result); 967 } 968 969 static void 970 popdir(int fd) 971 { 972 /* 973 * Change the current working directory to the directory 974 * specified by fd and close the fd. Exit the program 975 * on failure. 976 */ 977 if (fchdir(fd) < 0) { 978 dprintf("%s[%d]: popdir() failed\n", __FILE__, __LINE__); 979 exit(1); 980 } 981 (void) close(fd); 982 } 983 984 static int 985 pushdir(const char *dir) 986 { 987 /* 988 * Change the current working directory to dir and 989 * return a file descriptor for the old working 990 * directory. 991 * 992 * Exception handling: 993 * 994 * If dir doesn't exist, leave the current working 995 * directory the same and return -1. 996 * 997 * If dir isn't a directory, remove it, leave the 998 * current working directory the same, and return -1. 999 * 1000 * If open() fails on the current working directory 1001 * or the chdir operation fails on dir, leave the 1002 * current working directory the same and return -1. 1003 */ 1004 1005 int current_working_dir_fd; 1006 struct stat stat_buf; 1007 1008 if (lstat(dir, &stat_buf) < 0) { 1009 dprintf("%s[%d]: push_dir_and_check(): %s does not exist\n", 1010 __FILE__, __LINE__, dir); 1011 return (-1); 1012 } 1013 1014 if (!(S_ISDIR(stat_buf.st_mode))) { 1015 dprintf("%s[%d]: push_dir_and_check(): %s not a directory.\n", 1016 __FILE__, __LINE__, dir); 1017 (void) remove(dir); 1018 return (-1); 1019 } 1020 if ((current_working_dir_fd = open(".", O_RDONLY)) < 0) { 1021 dprintf("%s[%d]: push_dir_and_check(): can't open %s.\n", 1022 __FILE__, __LINE__, dir); 1023 return (-1); 1024 } 1025 if (chdir(dir) < 0) { 1026 (void) close(current_working_dir_fd); 1027 dprintf("%s[%d]: push_dir_and_check(): can't chdir() to %s.\n", 1028 __FILE__, __LINE__, dir); 1029 return (-1); 1030 } 1031 return (current_working_dir_fd); 1032 } 1033 1034 static boolean_t 1035 remove_notify_files(struct action_arg **aa) 1036 { 1037 int ai; 1038 int current_working_dir_fd; 1039 char notify_file[64]; 1040 int result; 1041 char *symdev; 1042 1043 dprintf("%s[%d]: entering remove_notify_files()\n", __FILE__, __LINE__); 1044 1045 ai = 0; 1046 result = TRUE; 1047 symdev = aa[ai]->aa_symdev; 1048 while ((result == TRUE) && 1049 (aa[ai] != NULL) && 1050 (aa[ai]->aa_path != NULL)) { 1051 1052 if (not_mountable(aa[ai])) { 1053 sprintf(notify_file, "%s-0", symdev); 1054 } else if (aa[ai]->aa_partname != NULL) { 1055 /* 1056 * Is aa_partname ever NULL? 1057 * When time permits, check. 1058 * If it is, the action taken 1059 * in the else clause could produce 1060 * file name conflicts. 1061 */ 1062 sprintf(notify_file, "%s-%s", 1063 symdev, aa[0]->aa_partname); 1064 } else { 1065 sprintf(notify_file, "%s-0", symdev); 1066 } 1067 1068 current_working_dir_fd = pushdir(NOTIFY_DIR); 1069 if (current_working_dir_fd < 0) { 1070 result = FALSE; 1071 } 1072 if ((result == TRUE) && (remove(notify_file) < 0)) { 1073 dprintf("%s[%d]: remove %s/%s; %m\n", 1074 __FILE__, __LINE__, NOTIFY_DIR, notify_file); 1075 result = FALSE; 1076 } 1077 if (current_working_dir_fd != -1) { 1078 popdir(current_working_dir_fd); 1079 } 1080 ai++; 1081 } 1082 dprintf("%s[%d]: leaving remove_notify_files(), result = %s\n", 1083 __FILE__, __LINE__, result_strings[result]); 1084 1085 return (result); 1086 }