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