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