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, &notify_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 }