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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 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 = -1; 112 static gid_t mnt_gid = -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 volrmmount_usage(); 136 static void volcheck_usage(); 137 static int vold_action(struct action_arg *aap); 138 static void vold_update_mountpoints(struct action_arg *aap); 139 static char *not_mountable(struct action_arg *aa); 140 static int create_one_notify_file(char *fstype, 141 char *mount_point, 142 char *notify_file, 143 char *raw_partitionp, 144 char *reason, 145 char *symdev); 146 static int create_notify_files(struct action_arg **aa); 147 static boolean_t notify_clients(action_t action, int do_notify); 148 static void popdir(int fd); 149 static int pushdir(const char *dir); 150 static boolean_t remove_notify_files(struct action_arg **aa); 151 152 /* 153 * should be called once from main() 154 */ 155 /* ARGSUSED */ 156 void 157 vold_init(int argc, char **argv) 158 { 159 system_labeled = is_system_labeled(); 160 } 161 162 /* 163 * Old version of rmmount(1M) 164 */ 165 /* ARGSUSED */ 166 int 167 vold_rmmount(int argc, char **argv) 168 { 169 char *volume_action; 170 char *volume_mediatype; 171 char *volume_mount_mode; 172 char *volume_name; 173 char *volume_path; 174 char *volume_pcfs_id; 175 char *volume_symdev; 176 char *volume_zonename; 177 char *volume_user; 178 action_t action; 179 char mountpoint[MAXPATHLEN]; 180 char *zonemountpoint; 181 char *arg_mountpoint = NULL; 182 LibHalContext *hal_ctx; 183 DBusError error; 184 rmm_error_t rmm_error; 185 int ret; 186 187 prog_name = argv[0]; 188 prog_pid = getpid(); 189 190 mnt_zoneroot[0] = '\0'; 191 mnt_userdir[0] = '\0'; 192 193 volume_action = getenv("VOLUME_ACTION"); 194 volume_mediatype = getenv("VOLUME_MEDIATYPE"); 195 volume_mount_mode = getenv("VOLUME_MOUNT_MODE"); 196 volume_name = getenv("VOLUME_NAME"); 197 volume_path = getenv("VOLUME_PATH"); 198 volume_pcfs_id = getenv("VOLUME_PCFS_ID"); 199 volume_symdev = getenv("VOLUME_SYMDEV"); 200 201 if (system_labeled) { 202 volume_zonename = getenv("VOLUME_ZONE_NAME"); 203 volume_user = getenv("VOLUME_USER"); 204 } 205 if (volume_action == NULL) { 206 dprintf("%s(%ld): VOLUME_ACTION was null!!\n", 207 prog_name, prog_pid); 208 return (-1); 209 } 210 if (volume_mediatype == NULL) { 211 dprintf("%s(%ld): VOLUME_MEDIATYPE was null!!\n", 212 prog_name, prog_pid); 213 return (-1); 214 } 215 if (volume_mount_mode == NULL) { 216 volume_mount_mode = "rw"; 217 } 218 if (volume_name == NULL) { 219 dprintf("%s(%ld): VOLUME_NAME was null!!\n", 220 prog_name, prog_pid); 221 return (-1); 222 } 223 if (volume_path == NULL) { 224 dprintf("%s(%ld): VOLUME_PATH was null!!\n", 225 prog_name, prog_pid); 226 return (-1); 227 } 228 if (volume_pcfs_id == NULL) { 229 volume_pcfs_id = ""; 230 } 231 if (volume_symdev == NULL) { 232 dprintf("%s(%ld): VOLUME_SYMDEV was null!!\n", 233 prog_name, prog_pid); 234 return (-1); 235 } 236 237 if (system_labeled) { 238 if (volume_zonename != NULL && 239 strcmp(volume_zonename, GLOBAL_ZONENAME) != 0) { 240 if ((mnt_zoneid = 241 getzoneidbyname(volume_zonename)) != -1) { 242 if (zone_getattr(mnt_zoneid, ZONE_ATTR_ROOT, 243 mnt_zoneroot, MAXPATHLEN) == -1) { 244 dprintf("%s(%ld): NO ZONEPATH!!\n", 245 prog_name, prog_pid); 246 return (-1); 247 } 248 } 249 } else { 250 mnt_zoneid = GLOBAL_ZONEID; 251 mnt_zoneroot[0] = '\0'; 252 } 253 if (volume_user != NULL) { 254 struct passwd *pw; 255 256 if ((pw = getpwnam(volume_user)) == NULL) { 257 dprintf("%s(%ld) %s\n", prog_name, prog_pid, 258 ": VOLUME_USER was not a valid user!"); 259 return (-1); 260 } 261 mnt_uid = pw->pw_uid; 262 mnt_gid = pw->pw_gid; 263 264 if (snprintf(mnt_userdir, sizeof (mnt_userdir), 265 "/%s-%s", volume_user, volume_symdev) >= 266 sizeof (mnt_userdir)) 267 return (-1); 268 } else { 269 mnt_uid = 0; 270 mnt_userdir[0] = '\0'; 271 } 272 273 rmm_vold_mountpoints_enabled = B_FALSE; 274 rmm_vold_actions_enabled = B_TRUE; 275 } else { 276 rmm_vold_mountpoints_enabled = B_TRUE; 277 rmm_vold_actions_enabled = B_TRUE; 278 } 279 280 if ((hal_ctx = rmm_hal_init(0, 0, 0, &error, &rmm_error)) == NULL) { 281 rmm_dbus_error_free(&error); 282 283 /* if HAL's not running, must be root */ 284 if (geteuid() != 0) { 285 (void) fprintf(stderr, 286 gettext("%s(%ld) error: must be root to execute\n"), 287 prog_name, prog_pid); 288 return (-1); 289 } 290 } 291 292 if (strcmp(volume_action, "eject") == 0) { 293 action = EJECT; 294 } else if (strcmp(volume_action, "insert") == 0) { 295 action = INSERT; 296 297 if (system_labeled) { 298 /* 299 * create mount point 300 */ 301 if (strlen(mnt_userdir) > 0) { 302 if (snprintf(mountpoint, MAXPATHLEN, 303 "%s/%s%s", mnt_zoneroot, volume_mediatype, 304 mnt_userdir) > MAXPATHLEN) { 305 return (-1); 306 307 } 308 (void) makepath(mountpoint, 0700); 309 (void) chown(mountpoint, mnt_uid, mnt_gid); 310 /* 311 * set the top level directory bits to 0755 312 * so user can access it. 313 */ 314 if (snprintf(mountpoint, MAXPATHLEN, 315 "%s/%s", mnt_zoneroot, 316 volume_mediatype) <= MAXPATHLEN) { 317 (void) chmod(mountpoint, 0755); 318 } 319 } 320 if (snprintf(mountpoint, MAXPATHLEN, 321 "%s/%s%s/%s", mnt_zoneroot, volume_mediatype, 322 mnt_userdir, volume_name) > MAXPATHLEN) { 323 (void) fprintf(stderr, 324 gettext("%s(%ld) error: path too long\n"), 325 prog_name, prog_pid); 326 return (-1); 327 } 328 329 /* make our mountpoint */ 330 (void) makepath(mountpoint, 0755); 331 332 arg_mountpoint = mountpoint; 333 } 334 } else if (strcmp(volume_action, "remount") == 0) { 335 action = REMOUNT; 336 } else if (strcmp(volume_action, "unmount") == 0) { 337 action = UNMOUNT; 338 } 339 340 ret = rmm_action(hal_ctx, volume_symdev, action, 0, 0, 0, 341 arg_mountpoint) ? 0 : 1; 342 343 if (hal_ctx != NULL) { 344 rmm_hal_fini(hal_ctx); 345 } 346 347 return (ret); 348 } 349 350 351 /* 352 * this should be called after rmm_hal_{mount,unmount,eject} 353 */ 354 int 355 vold_postprocess(LibHalContext *hal_ctx, const char *udi, 356 struct action_arg *aap) 357 { 358 int ret = 0; 359 360 /* valid mountpoint required */ 361 if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) { 362 rmm_volume_aa_update_mountpoint(hal_ctx, udi, aap); 363 if ((aap->aa_mountpoint == NULL) || 364 (strlen(aap->aa_mountpoint) == 0)) { 365 return (1); 366 } 367 } 368 369 if (rmm_vold_mountpoints_enabled) { 370 vold_update_mountpoints(aap); 371 } 372 if (rmm_vold_actions_enabled) { 373 ret = vold_action(aap); 374 } 375 376 return (ret); 377 } 378 379 /* 380 * update legacy symlinks 381 * 382 * For cdrom: 383 * 384 * /cdrom/<name> -> original mountpoint 385 * /cdrom/cdrom0 -> ./<name> 386 * /cdrom/cdrom -> cdrom0 (only for cdrom0) 387 * 388 * If it's a slice or partition, /cdrom/<name> becomes a directory: 389 * 390 * /cdrom/<name>/s0 391 * 392 * Same for rmdisk and floppy. 393 * 394 * On labeled system (Trusted Solaris), links are in a user directory. 395 */ 396 static void 397 vold_update_mountpoints(struct action_arg *aap) 398 { 399 boolean_t is_partition; 400 char part_dir[2 * MAXNAMELEN]; 401 char symname_mp[2 * MAXNAMELEN]; 402 char symcontents_mp[MAXNAMELEN]; 403 char symname[2 * MAXNAMELEN]; 404 char symcontents[MAXNAMELEN]; 405 406 is_partition = (aap->aa_partname != NULL); 407 408 if (!system_labeled) { 409 if (!is_partition) { 410 /* /cdrom/<name> -> original mountpoint */ 411 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 412 "%s", aap->aa_mountpoint); 413 (void) snprintf(symname_mp, sizeof (symname_mp), 414 "/%s/%s", aap->aa_media, aap->aa_name); 415 } else { 416 /* /cdrom/<name>/slice -> original mountpoint */ 417 (void) snprintf(part_dir, sizeof (part_dir), 418 "/%s/%s", aap->aa_media, aap->aa_name); 419 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 420 "%s", aap->aa_mountpoint); 421 (void) snprintf(symname_mp, sizeof (symname_mp), 422 "/%s/%s/%s", aap->aa_media, aap->aa_name, 423 aap->aa_partname); 424 425 } 426 /* /cdrom/cdrom0 -> ./<name> */ 427 (void) snprintf(symcontents, sizeof (symcontents), 428 "./%s", aap->aa_name); 429 (void) snprintf(symname, sizeof (symname), 430 "/%s/%s", aap->aa_media, aap->aa_symdev); 431 } else { 432 if (!is_partition) { 433 /* /cdrom/<user>/<name> -> original mountpoint */ 434 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 435 "%s", aap->aa_mountpoint); 436 (void) snprintf(symname_mp, sizeof (symname_mp), 437 "%s/%s/%s", mnt_zoneroot, aap->aa_media, 438 aap->aa_symdev); 439 } else { 440 /* /cdrom/<user>/<name>/slice -> original mountpoint */ 441 (void) snprintf(symcontents_mp, sizeof (symcontents_mp), 442 "%s", aap->aa_mountpoint); 443 (void) snprintf(symname_mp, sizeof (symname_mp), 444 "%s/%s/%s", mnt_zoneroot, aap->aa_media, 445 aap->aa_symdev, aap->aa_partname); 446 } 447 448 /* /cdrom/<user>/cdrom0 -> ./<user>/<name> */ 449 (void) snprintf(symcontents, sizeof (symcontents), 450 ".%s/%s", mnt_userdir, aap->aa_name); 451 (void) snprintf(symname, sizeof (symname), "%s/%s/%s", 452 mnt_zoneroot, aap->aa_media, aap->aa_symdev); 453 } 454 455 (void) unlink(symname); 456 (void) unlink(symname_mp); 457 if (is_partition) { 458 (void) rmdir(part_dir); 459 } 460 461 if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) { 462 (void) mkdir(aap->aa_media, 0755); 463 if (is_partition) { 464 (void) mkdir(part_dir, 0755); 465 } 466 (void) symlink(symcontents_mp, symname_mp); 467 (void) symlink(symcontents, symname); 468 } 469 } 470 471 472 static int 473 vold_action(struct action_arg *aap) 474 { 475 action_t action; 476 int result; 477 int do_notify = FALSE; 478 action_t notify_act = EJECT; 479 struct action_arg *aa[2]; 480 struct action_arg a1; 481 482 dprintf("%s[%d]: entering action()\n", __FILE__, __LINE__); 483 484 /* 485 * on Trusted Extensions, actions are executed in the user's zone 486 */ 487 if (mnt_zoneid > GLOBAL_ZONEID) { 488 pid_t pid; 489 int status; 490 int ifx; 491 int tmpl_fd; 492 int err = 0; 493 494 tmpl_fd = open64(CTFS_ROOT "/process/template", 495 O_RDWR); 496 if (tmpl_fd == -1) 497 return (1); 498 499 /* 500 * Deliver no events, don't inherit, 501 * and allow it to be orphaned. 502 */ 503 err |= ct_tmpl_set_critical(tmpl_fd, 0); 504 err |= ct_tmpl_set_informative(tmpl_fd, 0); 505 err |= ct_pr_tmpl_set_fatal(tmpl_fd, 506 CT_PR_EV_HWERR); 507 err |= ct_pr_tmpl_set_param(tmpl_fd, 508 CT_PR_PGRPONLY | 509 CT_PR_REGENT); 510 if (err || ct_tmpl_activate(tmpl_fd)) { 511 (void) close(tmpl_fd); 512 return (1); 513 } 514 switch (pid = fork1()) { 515 case 0: 516 (void) ct_tmpl_clear(tmpl_fd); 517 for (ifx = 0; ifx < _NFILE; ifx++) 518 (void) close(ifx); 519 520 if (zone_enter(mnt_zoneid) == -1) 521 _exit(0); 522 523 /* entered zone, proceed to action */ 524 break; 525 case -1: 526 dprintf("fork1 failed \n "); 527 return (1); 528 default : 529 (void) ct_tmpl_clear(tmpl_fd); 530 (void) close(tmpl_fd); 531 if (waitpid(pid, &status, 0) < 0) { 532 dprintf("%s(%ld): waitpid() " 533 "failed (errno %d) \n", 534 prog_name, prog_pid, errno); 535 return (1); 536 } 537 } 538 539 } 540 541 /* only support one action at a time XXX */ 542 a1.aa_path = NULL; 543 aa[0] = aap; 544 aa[1] = &a1; 545 546 action = aa[0]->aa_action; 547 548 if (action == CLEAR_MOUNTS) { 549 /* 550 * Remove the notifications files, but don't 551 * notify the client. The "clear_mounts" action 552 * simply clears all existing mounts of a medium's 553 * partitions after a medium has been repartitioned. 554 * Then vold builds a new file system that reflects 555 * the medium's new partition structure and mounts 556 * the new partitions by calling rmmount, and therefore 557 * action(), with the VOLUME_ACTION environment variable 558 * set to "remount". 559 */ 560 result = remove_notify_files(aa); 561 result = TRUE; 562 } else if (action == EJECT) { 563 result = remove_notify_files(aa); 564 if (result == TRUE) { 565 do_notify = TRUE; 566 notify_act = EJECT; 567 } 568 } else if (action = INSERT) { 569 result = create_notify_files(aa); 570 if (result == TRUE) { 571 do_notify = TRUE; 572 notify_act = INSERT; 573 } 574 } else if (action == REMOUNT) { 575 result = create_notify_files(aa); 576 if (result == TRUE) { 577 do_notify = TRUE; 578 notify_act = REMOUNT; 579 } 580 } else if (action == UNMOUNT) { 581 result = remove_notify_files(aa); 582 if (result == TRUE) { 583 do_notify = TRUE; 584 notify_act = UNMOUNT; 585 } 586 } else { 587 dprintf("%s[%d]: action(): invalid action: %s\n", 588 __FILE__, __LINE__, action); 589 result = FALSE; 590 } 591 592 if (result == TRUE) { 593 result = notify_clients(notify_act, do_notify); 594 } 595 596 dprintf("%s[%d]: leaving action(), result = %s\n", 597 __FILE__, __LINE__, result_strings[result]); 598 599 if (mnt_zoneid > GLOBAL_ZONEID) { 600 /* exit forked local zone process */ 601 _exit(0); 602 } 603 604 if (result == TRUE) { 605 /* 606 * File Manager is running. return 0. 607 * see man page rmmount.conf(4). 608 */ 609 return (0); 610 } else { 611 return (1); 612 } 613 } 614 615 616 /* 617 * Returns NULL if a medium or partition is mountable 618 * and a string stating the reason the medium or partition 619 * can't be mounted if the medium or partition isn't mountable. 620 * 621 * If the volume_name of the medium or partition is one of the 622 * following, the medium or partition isn't mountable. 623 * 624 * unlabeled_<media_type> 625 * unknown_format 626 * password_protected 627 */ 628 /* ARGSUSED */ 629 static char * 630 not_mountable(struct action_arg *aa) 631 { 632 return (NULL); 633 } 634 635 static int 636 create_notify_files(struct action_arg **aa) 637 { 638 int ai; 639 char *fstype; 640 char *mount_point; 641 char notify_file[64]; 642 char *raw_partitionp; 643 char *reason; /* Why the medium wasn't mounted */ 644 int result; 645 char *symdev; 646 647 dprintf("%s[%d]: entering create_notify_files()\n", __FILE__, __LINE__); 648 649 ai = 0; 650 result = FALSE; 651 symdev = aa[ai]->aa_symdev; 652 while ((aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) { 653 if (aa[ai]->aa_mountpoint != NULL) { 654 if (aa[ai]->aa_type) { 655 fstype = aa[ai]->aa_type; 656 } else { 657 fstype = "unknown"; 658 } 659 mount_point = aa[ai]->aa_mountpoint; 660 if (aa[ai]->aa_partname != NULL) { 661 /* 662 * Is aa_partname ever NULL? 663 * When time permits, check. 664 * If it is, the action taken 665 * in the else clause could produce 666 * file name conflicts. 667 */ 668 sprintf(notify_file, "%s-%s", symdev, 669 aa[ai]->aa_partname); 670 } else { 671 sprintf(notify_file, "%s-0", symdev); 672 } 673 reason = NULL; 674 } else { 675 /* 676 * The partition isn't mounted. 677 */ 678 fstype = "none"; 679 mount_point = "none"; 680 reason = not_mountable(aa[ai]); 681 if (reason != NULL) { 682 sprintf(notify_file, "%s-0", symdev); 683 } else { 684 /* 685 * Either the partition is a backup slice, or 686 * rmmount tried to mount the partition, but 687 * idenf_fs couldn't identify the file system 688 * type; that can occur when rmmount is 689 * trying to mount all the slices in a Solaris 690 * VTOC, and one or more partitions don't have 691 * file systems in them. 692 */ 693 if (aa[0]->aa_partname != NULL) { 694 /* 695 * Is aa_partname ever NULL? 696 * When time permits, check. 697 * If it is, the action taken 698 * in the else clause could produce 699 * file name conflicts. 700 */ 701 sprintf(notify_file, "%s-%s", symdev, 702 aa[0]->aa_partname); 703 } else { 704 sprintf(notify_file, "%s-0", symdev); 705 } 706 if ((aa[0]->aa_type != NULL) && 707 (strcmp(aa[0]->aa_type, "backup_slice") 708 == 0)) { 709 reason = "backup_slice"; 710 } else { 711 reason = "unformatted_media"; 712 } 713 /* 714 * "unformatted_media" should be 715 * changed to "unformmated_medium" for 716 * grammatical correctness, but 717 * "unformatted_media" is now specified 718 * in the interface to filemgr, so the 719 * change can't be made without the 720 * approval of the CDE group. 721 */ 722 } 723 } 724 raw_partitionp = aa[0]->aa_rawpath; 725 result = create_one_notify_file(fstype, 726 mount_point, 727 notify_file, 728 raw_partitionp, 729 reason, 730 symdev); 731 ai++; 732 } 733 dprintf("%s[%d]: leaving create_notify_files(), result = %s\n", 734 __FILE__, __LINE__, result_strings[result]); 735 return (result); 736 } 737 738 static int 739 create_one_notify_file(char *fstype, 740 char *mount_point, 741 char *notify_file, 742 char *raw_partitionp, 743 char *reason, 744 char *symdev) 745 { 746 /* 747 * For a mounted partition, create a notification file 748 * indicating the mount point, the raw device pathname 749 * of the partition, and the partition's file system 750 * type. For an unmounted partition, create a 751 * notification file containing the reason that the 752 * partition wasn't mounted and the raw device pathname 753 * of the partition. 754 * 755 * Create the file as root in a world-writable 756 * directory that resides in a world-writable directory. 757 * 758 * Handle two possible race conditions that could 759 * allow security breaches. 760 */ 761 762 int current_working_dir_fd; 763 int file_descriptor; 764 FILE *filep; 765 int result; 766 767 dprintf("%s[%d]:Entering create_one_notify_file()\n", 768 __FILE__, __LINE__); 769 dprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype); 770 dprintf("\tcreate_one_notify_file(): mount_point = %s\n", mount_point); 771 dprintf("\tcreate_one_notify_file(): notify_file = %s\n", notify_file); 772 dprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n", 773 raw_partitionp); 774 if (reason != NULL) { 775 dprintf("\tcreate_one_notify_file(): reason = %s\n", reason); 776 } else { 777 dprintf("\tcreate_one_notify_file(): reason = NULL\n"); 778 } 779 dprintf("\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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%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 dprintf("%s[%d]: push_dir_and_check(): can't chdir() to %s.\n", 1031 __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 dprintf("%s[%d]: entering remove_notify_files()\n", __FILE__, __LINE__); 1047 1048 ai = 0; 1049 result = TRUE; 1050 symdev = aa[ai]->aa_symdev; 1051 while ((result == TRUE) && 1052 (aa[ai] != NULL) && 1053 (aa[ai]->aa_path != NULL)) { 1054 1055 if (not_mountable(aa[ai])) { 1056 sprintf(notify_file, "%s-0", symdev); 1057 } else if (aa[ai]->aa_partname != NULL) { 1058 /* 1059 * Is aa_partname ever NULL? 1060 * When time permits, check. 1061 * If it is, the action taken 1062 * in the else clause could produce 1063 * file name conflicts. 1064 */ 1065 sprintf(notify_file, "%s-%s", 1066 symdev, aa[0]->aa_partname); 1067 } else { 1068 sprintf(notify_file, "%s-0", symdev); 1069 } 1070 1071 current_working_dir_fd = pushdir(NOTIFY_DIR); 1072 if (current_working_dir_fd < 0) { 1073 result = FALSE; 1074 } 1075 if ((result == TRUE) && (remove(notify_file) < 0)) { 1076 dprintf("%s[%d]: remove %s/%s; %m\n", 1077 __FILE__, __LINE__, NOTIFY_DIR, notify_file); 1078 result = FALSE; 1079 } 1080 if (current_working_dir_fd != -1) { 1081 popdir(current_working_dir_fd); 1082 } 1083 ai++; 1084 } 1085 dprintf("%s[%d]: leaving remove_notify_files(), result = %s\n", 1086 __FILE__, __LINE__, result_strings[result]); 1087 1088 return (result); 1089 } 1090