1 /*************************************************************************** 2 * CVSID: $Id$ 3 * 4 * hal-storage-mount.c : Mount wrapper 5 * 6 * Copyright (C) 2006 David Zeuthen, <david@fubar.dk> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 **************************************************************************/ 23 24 25 #ifdef HAVE_CONFIG_H 26 # include <config.h> 27 #endif 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <glib.h> 33 #include <glib/gstdio.h> 34 #ifdef __FreeBSD__ 35 #include <fstab.h> 36 #include <sys/param.h> 37 #include <sys/ucred.h> 38 #include <sys/mount.h> 39 #include <limits.h> 40 #include <pwd.h> 41 #elif sun 42 #include <sys/mnttab.h> 43 #include <sys/vfstab.h> 44 #include <sys/wait.h> 45 #else 46 #include <mntent.h> 47 #endif 48 #include <sys/types.h> 49 #include <unistd.h> 50 #include <errno.h> 51 #include <syslog.h> 52 53 #include <libhal.h> 54 #include <libhal-storage.h> 55 #ifdef HAVE_POLKIT 56 #include <libpolkit.h> 57 #endif 58 59 #include "hal-storage-shared.h" 60 61 #ifdef __FreeBSD__ 62 #define MOUNT "/sbin/mount" 63 #define MOUNT_OPTIONS "noexec,nosuid" 64 #define MOUNT_TYPE_OPT "-t" 65 #elif sun 66 #define MOUNT "/sbin/mount" 67 #define MOUNT_OPTIONS "noexec,nosuid" 68 #define MOUNT_TYPE_OPT "-F" 69 #else 70 #define MOUNT "/bin/mount" 71 #define MOUNT_OPTIONS "noexec,nosuid,nodev" 72 #define MOUNT_TYPE_OPT "-t" 73 #endif 74 75 static void 76 usage (void) 77 { 78 fprintf (stderr, "This program should only be started by hald.\n"); 79 exit (1); 80 } 81 82 static void 83 permission_denied_volume_ignore (const char *device) 84 { 85 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n"); 86 fprintf (stderr, "Device has %s volume.ignore set to TRUE. Refusing to mount.\n", device); 87 exit (1); 88 } 89 90 static void 91 permission_denied_etc_fstab (const char *device) 92 { 93 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n"); 94 fprintf (stderr, "Device %s is listed in /etc/fstab. Refusing to mount.\n", device); 95 exit (1); 96 } 97 98 static void 99 already_mounted (const char *device) 100 { 101 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.AlreadyMounted\n"); 102 fprintf (stderr, "Device %s is already mounted.\n", device); 103 exit (1); 104 } 105 106 static void 107 invalid_mount_option (const char *option, const char *uid) 108 { 109 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.InvalidMountOption\n"); 110 fprintf (stderr, "The option '%s' is not allowed for uid=%s\n", option, uid); 111 exit (1); 112 } 113 114 static void 115 unknown_filesystem (const char *filesystem) 116 { 117 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.UnknownFilesystemType\n"); 118 fprintf (stderr, "Unknown file system '%s'\n", filesystem); 119 exit (1); 120 } 121 122 static void 123 invalid_mount_point (const char *mount_point) 124 { 125 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.InvalidMountpoint\n"); 126 fprintf (stderr, "The mount point '%s' is invalid\n", mount_point); 127 exit (1); 128 } 129 130 static void 131 mount_point_not_available (const char *mount_point) 132 { 133 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.MountPointNotAvailable\n"); 134 fprintf (stderr, "The mount point '%s' is already occupied\n", mount_point); 135 exit (1); 136 } 137 138 139 static void 140 cannot_remount (const char *device) 141 { 142 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.CannotRemount\n"); 143 fprintf (stderr, "%s not mounted already\n", device); 144 exit (1); 145 } 146 147 #ifdef HAVE_POLKIT 148 static void 149 permission_denied_privilege (const char *privilege, const char *uid) 150 { 151 fprintf (stderr, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy\n"); 152 fprintf (stderr, "%s refused uid %s\n", privilege, uid); 153 exit (1); 154 } 155 #endif 156 157 158 /* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */ 159 static void 160 canonicalize_filename (gchar *filename) 161 { 162 gchar *p, *q; 163 gboolean last_was_slash = FALSE; 164 165 p = filename; 166 q = filename; 167 168 while (*p) 169 { 170 if (*p == G_DIR_SEPARATOR) 171 { 172 if (!last_was_slash) 173 *q++ = G_DIR_SEPARATOR; 174 175 last_was_slash = TRUE; 176 } 177 else 178 { 179 if (last_was_slash && *p == '.') 180 { 181 if (*(p + 1) == G_DIR_SEPARATOR || 182 *(p + 1) == '\0') 183 { 184 if (*(p + 1) == '\0') 185 break; 186 187 p += 1; 188 } 189 else if (*(p + 1) == '.' && 190 (*(p + 2) == G_DIR_SEPARATOR || 191 *(p + 2) == '\0')) 192 { 193 if (q > filename + 1) 194 { 195 q--; 196 while (q > filename + 1 && 197 *(q - 1) != G_DIR_SEPARATOR) 198 q--; 199 } 200 201 if (*(p + 2) == '\0') 202 break; 203 204 p += 2; 205 } 206 else 207 { 208 *q++ = *p; 209 last_was_slash = FALSE; 210 } 211 } 212 else 213 { 214 *q++ = *p; 215 last_was_slash = FALSE; 216 } 217 } 218 219 p++; 220 } 221 222 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR) 223 q--; 224 225 *q = '\0'; 226 } 227 228 static char * 229 resolve_symlink (const char *file) 230 { 231 GError *error; 232 char *dir; 233 char *link; 234 char *f; 235 char *f1; 236 237 f = g_strdup (file); 238 239 while (g_file_test (f, G_FILE_TEST_IS_SYMLINK)) { 240 link = g_file_read_link (f, &error); 241 if (link == NULL) { 242 g_warning ("Cannot resolve symlink %s: %s", f, error->message); 243 g_error_free (error); 244 g_free (f); 245 f = NULL; 246 goto out; 247 } 248 249 dir = g_path_get_dirname (f); 250 f1 = g_strdup_printf ("%s/%s", dir, link); 251 g_free (dir); 252 g_free (link); 253 g_free (f); 254 f = f1; 255 } 256 257 out: 258 if (f != NULL) 259 canonicalize_filename (f); 260 return f; 261 } 262 263 static LibHalVolume * 264 volume_findby (LibHalContext *hal_ctx, const char *property, const char *value) 265 { 266 int i; 267 char **hal_udis; 268 int num_hal_udis; 269 LibHalVolume *result = NULL; 270 char *found_udi = NULL; 271 DBusError error; 272 273 dbus_error_init (&error); 274 if ((hal_udis = libhal_manager_find_device_string_match (hal_ctx, property, 275 value, &num_hal_udis, &error)) == NULL) { 276 LIBHAL_FREE_DBUS_ERROR (&error); 277 goto out; 278 } 279 for (i = 0; i < num_hal_udis; i++) { 280 char *udi; 281 udi = hal_udis[i]; 282 if (libhal_device_query_capability (hal_ctx, udi, "volume", &error)) { 283 found_udi = strdup (udi); 284 break; 285 } 286 } 287 288 libhal_free_string_array (hal_udis); 289 290 if (found_udi != NULL) 291 result = libhal_volume_from_udi (hal_ctx, found_udi); 292 293 free (found_udi); 294 out: 295 return result; 296 } 297 298 static void 299 bailout_if_in_fstab (LibHalContext *hal_ctx, const char *device, const char *label, const char *uuid) 300 { 301 gpointer handle; 302 char *entry; 303 char *_mount_point; 304 305 printf (" label '%s' uuid '%s'\n", label ? label : "" , uuid ? uuid : ""); 306 307 /* check if /etc/fstab mentions this device... (with symlinks etc) */ 308 if (! fstab_open (&handle)) { 309 printf ("cannot open /etc/fstab\n"); 310 unknown_error ("Cannot open /etc/fstab"); 311 } 312 while ((entry = fstab_next (handle, &_mount_point)) != NULL) { 313 char *resolved; 314 315 #ifdef DEBUG 316 printf ("Looking at /etc/fstab entry '%s'\n", entry); 317 #endif 318 if (label != NULL && g_str_has_prefix (entry, "LABEL=")) { 319 if (strcmp (entry + 6, label) == 0) { 320 gboolean skip_fstab_entry; 321 322 skip_fstab_entry = FALSE; 323 324 /* (heck, we also do the stuff below in gnome-mount) */ 325 326 /* OK, so what's if someone attaches an external disk with the label '/' and 327 * /etc/fstab has 328 * 329 * LABEL=/ / ext3 defaults 1 1 330 * 331 * in /etc/fstab as most Red Hat systems do? Bugger, this is a very common use 332 * case; suppose that you take the disk from your Fedora server and attaches it 333 * to your laptop. Bingo, you now have two disks with the label '/'. One must 334 * seriously wonder if using things like LABEL=/ for / is a good idea; just 335 * what happens if you boot in this configuration? (answer: the initrd gets 336 * it wrong most of the time.. sigh) 337 * 338 * To work around this, check if the listed entry in /etc/fstab is already mounted, 339 * if it is, then check if it's the same device_file as the given one... 340 */ 341 342 /* see if a volume is mounted at this mount point */ 343 if (_mount_point != NULL) { 344 LibHalVolume *mounted_vol; 345 346 mounted_vol = volume_findby (hal_ctx, "volume.mount_point", _mount_point); 347 if (mounted_vol != NULL) { 348 const char *mounted_vol_device_file; 349 350 mounted_vol_device_file = libhal_volume_get_device_file (mounted_vol); 351 /* no need to resolve symlinks, hal uses the canonical device file */ 352 if (mounted_vol_device_file != NULL && 353 strcmp (mounted_vol_device_file, device) !=0) { 354 #ifdef DEBUG 355 printf ("Wanting to mount %s that has label %s, but /etc/fstab says LABEL=%s is to be mounted at mount point '%s'. However %s (that also has label %s), is already mounted at said mount point. So, skipping said /etc/fstab entry.\n", 356 device, label, label, _mount_point, mounted_vol_device_file, _mount_point); 357 #endif 358 skip_fstab_entry = TRUE; 359 } 360 libhal_volume_free (mounted_vol); 361 } 362 } 363 364 if (!skip_fstab_entry) { 365 printf ("%s found in /etc/fstab. Not mounting.\n", entry); 366 permission_denied_etc_fstab (device); 367 } 368 } 369 } else if (uuid != NULL && g_str_has_prefix (entry, "UUID=")) { 370 if (strcmp (entry + 5, uuid) == 0) { 371 printf ("%s found in /etc/fstab. Not mounting.\n", entry); 372 permission_denied_etc_fstab (device); 373 } 374 } else { 375 376 resolved = resolve_symlink (entry); 377 #ifdef DEBUG 378 printf ("/etc/fstab: device %s -> %s \n", entry, resolved); 379 #endif 380 if (strcmp (device, resolved) == 0) { 381 printf ("%s (-> %s) found in /etc/fstab. Not mounting.\n", entry, resolved); 382 permission_denied_etc_fstab (device); 383 } 384 385 g_free (resolved); 386 } 387 } 388 fstab_close (handle); 389 } 390 391 static gboolean 392 device_is_mounted (const char *device, char **mount_point) 393 { 394 gpointer handle; 395 char *entry; 396 gboolean ret; 397 398 ret = FALSE; 399 400 /* check if /proc/mounts mentions this device... (with symlinks etc) */ 401 if (! mtab_open (&handle)) { 402 printf ("cannot open mount list\n"); 403 unknown_error ("Cannot open /etc/mtab or equivalent"); 404 } 405 while (((entry = mtab_next (handle, mount_point)) != NULL) && (ret == FALSE)) { 406 char *resolved; 407 408 resolved = resolve_symlink (entry); 409 #ifdef DEBUG 410 printf ("/proc/mounts: device %s -> %s \n", entry, resolved); 411 #endif 412 if (strcmp (device, resolved) == 0) { 413 printf ("%s (-> %s) found in mount list. Not mounting.\n", entry, resolved); 414 ret = TRUE; 415 } 416 417 g_free (resolved); 418 } 419 mtab_close (handle); 420 return ret; 421 } 422 423 /* maps volume_id fs types to the appropriate -t mount option */ 424 static const char * 425 map_fstype (const char *fstype) 426 { 427 #ifdef __FreeBSD__ 428 if (! strcmp (fstype, "iso9660")) 429 return "cd9660"; 430 else if (! strcmp (fstype, "ext2")) 431 return "ext2fs"; 432 else if (! strcmp (fstype, "vfat")) 433 return "msdosfs"; 434 #elif sun 435 if (! strcmp (fstype, "iso9660")) 436 return "hsfs"; 437 else if (! strcmp (fstype, "vfat")) 438 return "pcfs"; 439 #endif 440 441 return fstype; 442 } 443 444 static void 445 handle_mount (LibHalContext *hal_ctx, 446 #ifdef HAVE_POLKIT 447 LibPolKitContext *pol_ctx, 448 #endif 449 const char *udi, 450 LibHalVolume *volume, LibHalDrive *drive, const char *device, 451 const char *invoked_by_uid, const char *invoked_by_syscon_name, 452 DBusConnection *system_bus) 453 { 454 int i, j; 455 DBusError error; 456 char mount_point[256]; 457 char mount_fstype[256]; 458 char mount_options[1024]; 459 char **allowed_options; 460 char **given_options; 461 gboolean wants_to_change_uid; 462 char *mount_dir; 463 GError *err = NULL; 464 char *sout = NULL; 465 char *serr = NULL; 466 int exit_status; 467 char *args[10]; 468 int na; 469 GString *mount_option_str; 470 gboolean pol_is_fixed; 471 gboolean pol_change_uid; 472 char *privilege; 473 gboolean is_remount; 474 #ifdef HAVE_POLKIT 475 gboolean allowed_by_privilege; 476 gboolean is_temporary_privilege; 477 #endif 478 gboolean explicit_mount_point_given; 479 const char *end; 480 #ifdef __FreeBSD__ 481 struct passwd *pw; 482 uid_t calling_uid; 483 gid_t calling_gid; 484 #endif 485 const char *label; 486 const char *uuid; 487 const char *model; 488 const char *drive_type; 489 #ifdef sun 490 adt_export_data_t *adt_data; 491 size_t adt_data_size; 492 gboolean append_ro = FALSE; 493 gboolean is_abs_path = FALSE; 494 uid_t calling_uid; 495 496 calling_uid = atol (invoked_by_uid); 497 #endif 498 499 #ifdef DEBUG 500 printf ("device = %s\n", device); 501 printf ("invoked by uid = %s\n", invoked_by_uid); 502 printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name); 503 #endif 504 505 if (volume != NULL) { 506 dbus_error_init (&error); 507 if (libhal_device_get_property_bool (hal_ctx, udi, "volume.ignore", &error) || 508 dbus_error_is_set (&error)) { 509 if (dbus_error_is_set (&error)) { 510 LIBHAL_FREE_DBUS_ERROR (&error); 511 } 512 /* 513 * When device allocation is enabled (bsmconv or TX), we 514 * set volume.ignore on all volumes, but still want 515 * Mount() to succeed when called from the euid=0 516 * device allocation program. 517 */ 518 if (atol (invoked_by_uid) != 0) { 519 permission_denied_volume_ignore (device); 520 } 521 } 522 523 label = libhal_volume_get_label (volume); 524 uuid = libhal_volume_get_uuid (volume); 525 } else { 526 label = NULL; 527 uuid = NULL; 528 } 529 530 bailout_if_in_fstab (hal_ctx, device, label, uuid); 531 532 /* TODO: sanity check that what hal exports is correct (cf. Martin Pitt's email) */ 533 534 /* read from stdin */ 535 if (strlen (fgets (mount_point, sizeof (mount_point), stdin)) > 0) 536 mount_point [strlen (mount_point) - 1] = '\0'; 537 if (strlen (fgets (mount_fstype, sizeof (mount_fstype), stdin)) > 0) 538 mount_fstype [strlen (mount_fstype) - 1] = '\0'; 539 if (strlen (fgets (mount_options, sizeof (mount_options), stdin)) > 0) 540 mount_options [strlen (mount_options) - 1] = '\0'; 541 /* validate that input from stdin is UTF-8 */ 542 if (!g_utf8_validate (mount_point, -1, &end)) 543 unknown_error ("Error validating mount_point as UTF-8"); 544 if (!g_utf8_validate (mount_fstype, -1, &end)) 545 unknown_error ("Error validating mount_fstype as UTF-8"); 546 if (!g_utf8_validate (mount_options, -1, &end)) 547 unknown_error ("Error validating mount_options as UTF-8"); 548 549 #ifdef sun 550 if (calling_uid != 0) { 551 #endif 552 for (i = 0; mount_point[i] != '\0'; i++) { 553 if (mount_point[i] == '\n' || 554 mount_point[i] == G_DIR_SEPARATOR) { 555 unknown_error ("mount_point cannot contain the following characters: newline, G_DIR_SEPARATOR (usually /)"); 556 } 557 } 558 #ifdef sun 559 } 560 is_abs_path = (mount_point[0] == G_DIR_SEPARATOR); 561 #endif 562 563 #ifdef DEBUG 564 printf ("mount_point = '%s'\n", mount_point); 565 printf ("mount_fstype = '%s'\n", mount_fstype); 566 printf ("mount_options = '%s'\n", mount_options); 567 #endif 568 569 /* delete any trailing whitespace options from splitting the string */ 570 given_options = g_strsplit (mount_options, "\t", 0); 571 for (i = g_strv_length (given_options) - 1; i >= 0; --i) { 572 if (strlen (given_options[i]) > 0) 573 break; 574 given_options[i] = NULL; 575 } 576 577 #ifdef sun 578 /* for read-only media append 'ro' option if not already */ 579 append_ro = libhal_device_get_property_bool (hal_ctx, libhal_drive_get_udi(drive), 580 "storage.removable.solaris.read_only", NULL); 581 582 if (append_ro) { 583 for (i = 0; i < (int) g_strv_length (given_options); i++) { 584 if (strcmp (given_options[i], "ro") == 0) { 585 append_ro = FALSE; 586 } 587 } 588 } 589 #endif /* sun */ 590 591 /* is option 'remount' included? */ 592 is_remount = FALSE; 593 for (i = 0; i < (int) g_strv_length (given_options); i++) { 594 if (strcmp (given_options[i], "remount") == 0) { 595 is_remount = TRUE; 596 } 597 } 598 599 mount_dir = NULL; 600 if (is_remount) { 601 if (volume != NULL) { 602 if (!libhal_volume_is_mounted (volume)) { 603 cannot_remount (device); 604 } 605 mount_dir = g_strdup (libhal_volume_get_mount_point (volume)); 606 } else { 607 if (!device_is_mounted (device, &mount_dir)) { 608 cannot_remount (device); 609 } 610 } 611 612 if (mount_dir == NULL) { 613 unknown_error ("Cannot get mount_dir for remount even though volume is mounted!"); 614 } 615 616 } else { 617 if (volume != NULL) { 618 if (libhal_volume_is_mounted (volume)) { 619 already_mounted (device); 620 } 621 } else { 622 if (device_is_mounted (device, NULL)) { 623 already_mounted (device); 624 } 625 } 626 } 627 628 if (!is_remount) { 629 /* figure out mount point if no mount point is given... */ 630 explicit_mount_point_given = FALSE; 631 if (strlen (mount_point) == 0) { 632 char *p; 633 const char *label; 634 635 if (volume != NULL) 636 label = libhal_volume_get_label (volume); 637 else 638 label = NULL; 639 640 model = libhal_drive_get_model (drive); 641 drive_type = libhal_drive_get_type_textual (drive); 642 643 if (label != NULL) { 644 /* best - use label */ 645 g_strlcpy (mount_point, label, sizeof (mount_point)); 646 647 } else if ((model != NULL) && (strlen (model) > 0)) { 648 g_strlcpy (mount_point, model, sizeof (mount_point)); 649 } else if ((drive_type != NULL) && (strlen (drive_type) > 0)) { 650 g_strlcpy (mount_point, drive_type, sizeof (mount_point)); 651 } else { 652 /* fallback - use "disk" */ 653 g_snprintf (mount_point, sizeof (mount_point), "disk"); 654 } 655 656 /* sanitize computed mount point name, e.g. replace invalid chars with '-' */ 657 p = mount_point; 658 while (TRUE) { 659 p = g_utf8_strchr (mount_point, -1, G_DIR_SEPARATOR); 660 if (p == NULL) 661 break; 662 *p = '-'; 663 }; 664 665 } else { 666 explicit_mount_point_given = TRUE; 667 } 668 669 /* check mount point name - only forbid separators */ 670 #ifdef sun 671 if (calling_uid != 0) { 672 #endif 673 if (g_utf8_strchr (mount_point, -1, G_DIR_SEPARATOR) != NULL) { 674 printf ("'%s' is an invalid mount point\n", mount_point); 675 invalid_mount_point (mount_point); 676 } 677 #ifdef sun 678 } 679 #endif 680 681 /* check if mount point is available - append number to mount point */ 682 i = 0; 683 mount_dir = NULL; 684 while (TRUE) { 685 g_free (mount_dir); 686 #ifdef sun 687 if (is_abs_path) 688 mount_dir = g_strdup (mount_point); 689 else 690 #endif 691 if (i == 0) 692 mount_dir = g_strdup_printf ("/media/%s", mount_point); 693 else 694 mount_dir = g_strdup_printf ("/media/%s-%d", mount_point, i); 695 696 #ifdef DEBUG 697 printf ("trying dir %s\n", mount_dir); 698 #endif 699 700 /* XXX should test for being a mount point */ 701 if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS)) { 702 break; 703 } 704 #ifdef sun 705 if (calling_uid == 0) { 706 break; 707 } 708 #endif 709 if (explicit_mount_point_given) { 710 mount_point_not_available (mount_dir); 711 } 712 713 i++; 714 } 715 } 716 717 dbus_error_init (&error); 718 allowed_options = libhal_device_get_property_strlist (hal_ctx, udi, "volume.mount.valid_options", &error); 719 if (dbus_error_is_set (&error)) { 720 unknown_error ("Cannot get volume.mount.valid_options"); 721 dbus_error_free (&error); 722 } 723 724 #ifdef DEBUG 725 for (i = 0; given_options[i] != NULL; i++) 726 printf ("given_options[%d] = '%s'\n", i, given_options[i]); 727 for (i = 0; allowed_options[i] != NULL; i++) 728 printf ("allowed_options[%d] = '%s'\n", i, allowed_options[i]); 729 #endif 730 731 wants_to_change_uid = FALSE; 732 733 /* check mount options */ 734 for (i = 0; given_options[i] != NULL; i++) { 735 char *given = given_options[i]; 736 737 for (j = 0; allowed_options[j] != NULL; j++) { 738 char *allow = allowed_options[j]; 739 int allow_len = strlen (allow); 740 741 if (strcmp (given, allow) == 0) { 742 goto option_ok; 743 } 744 745 if ((allow[allow_len - 1] == '=') && 746 (strncmp (given, allow, allow_len) == 0) && 747 (int) strlen (given) > allow_len) { 748 749 /* option matched allowed ending in '=', e.g. 750 * given == "umask=foobar" and allowed == "umask=" 751 */ 752 if (strcmp (allow, "uid=") == 0) { 753 uid_t uid; 754 char *endp; 755 /* check for uid=, it requires special handling */ 756 uid = (uid_t) strtol (given + allow_len, &endp, 10); 757 if (*endp != '\0') { 758 printf ("'%s' is not a number?\n", given); 759 unknown_error ("option uid is malformed"); 760 } 761 #ifdef DEBUG 762 printf ("%s with uid %d\n", allow, uid); 763 #endif 764 wants_to_change_uid = TRUE; 765 766 goto option_ok; 767 } else { 768 769 goto option_ok; 770 } 771 } 772 } 773 774 /* apparently option was not ok */ 775 invalid_mount_option (given, invoked_by_uid); 776 777 option_ok: 778 ; 779 } 780 781 /* Check privilege */ 782 pol_is_fixed = TRUE; 783 if (libhal_drive_is_hotpluggable (drive) || libhal_drive_uses_removable_media (drive)) 784 pol_is_fixed = FALSE; 785 786 pol_change_uid = FALSE; 787 /* don't consider uid= on non-pollable drives for the purpose of policy 788 * (since these drives normally use vfat) 789 */ 790 if (volume != NULL) { 791 /* don't consider uid= on vfat, iso9660, udf change-uid for the purpose of policy 792 * (since these doesn't contain uid/gid bits) 793 */ 794 if (strcmp (libhal_volume_get_fstype (volume), "vfat") != 0 && 795 strcmp (libhal_volume_get_fstype (volume), "iso9660") != 0 && 796 strcmp (libhal_volume_get_fstype (volume), "udf") != 0) { 797 pol_change_uid = wants_to_change_uid; 798 } 799 } 800 801 if (pol_is_fixed) { 802 if (pol_change_uid) { 803 privilege = "hal-storage-fixed-mount-all-options"; 804 } else { 805 privilege = "hal-storage-fixed-mount"; 806 } 807 } else { 808 if (pol_change_uid) { 809 privilege = "hal-storage-removable-mount-all-options"; 810 } else { 811 privilege = "hal-storage-removable-mount"; 812 } 813 } 814 815 #ifdef DEBUG 816 printf ("using privilege %s for uid %s, system_bus_connection %s\n", privilege, invoked_by_uid, 817 invoked_by_syscon_name); 818 #endif 819 820 #ifdef HAVE_POLKIT 821 if (libpolkit_is_uid_allowed_for_privilege (pol_ctx, 822 invoked_by_syscon_name, 823 invoked_by_uid, 824 privilege, 825 udi, 826 &allowed_by_privilege, 827 &is_temporary_privilege, 828 NULL) != LIBPOLKIT_RESULT_OK) { 829 printf ("cannot lookup privilege\n"); 830 unknown_error ("Cannot lookup privilege from PolicyKit"); 831 } 832 833 if (!allowed_by_privilege) { 834 printf ("caller don't possess privilege\n"); 835 permission_denied_privilege (privilege, invoked_by_uid); 836 } 837 #endif 838 839 #ifdef DEBUG 840 printf ("passed privilege\n"); 841 #endif 842 843 if (!is_remount) { 844 /* create directory */ 845 #ifdef sun 846 if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS)) 847 #endif 848 if (g_mkdir (mount_dir, 0700) != 0) { 849 printf ("Cannot create '%s'\n", mount_dir); 850 unknown_error ("Cannot create mount directory"); 851 } 852 853 #ifdef __FreeBSD__ 854 calling_uid = (uid_t) strtol (invoked_by_uid, (char **) NULL, 10); 855 pw = getpwuid (calling_uid); 856 if (pw != NULL) { 857 calling_gid = pw->pw_gid; 858 } else { 859 calling_gid = 0; 860 } 861 if (chown (mount_dir, calling_uid, calling_gid) != 0) { 862 printf ("Cannot chown '%s' to uid: %d, gid: %d\n", mount_dir, 863 calling_uid, calling_gid); 864 g_rmdir (mount_dir); 865 unknown_error (); 866 } 867 #endif 868 } 869 870 char *mount_option_commasep = NULL; 871 char *mount_do_fstype = "auto"; 872 873 /* construct arguments to mount */ 874 na = 0; 875 args[na++] = MOUNT; 876 if (strlen (mount_fstype) > 0) { 877 mount_do_fstype = (char *) map_fstype (mount_fstype); 878 } else if (volume == NULL) { 879 /* non-pollable drive; force auto */ 880 mount_do_fstype = "auto"; 881 } else if (libhal_volume_get_fstype (volume) != NULL && strlen (libhal_volume_get_fstype (volume)) > 0) { 882 mount_do_fstype = (char *) map_fstype (libhal_volume_get_fstype (volume)); 883 } 884 args[na++] = MOUNT_TYPE_OPT; 885 args[na++] = mount_do_fstype; 886 887 args[na++] = "-o"; 888 mount_option_str = g_string_new (MOUNT_OPTIONS); 889 for (i = 0; given_options[i] != NULL; i++) { 890 g_string_append (mount_option_str, ","); 891 g_string_append (mount_option_str, given_options[i]); 892 } 893 #ifdef sun 894 if (append_ro) { 895 g_string_append (mount_option_str, ",ro"); 896 } 897 #endif 898 mount_option_commasep = g_string_free (mount_option_str, FALSE); /* leak! */ 899 args[na++] = mount_option_commasep; 900 args[na++] = (char *) device; 901 args[na++] = mount_dir; 902 args[na++] = NULL; 903 904 /* TODO FIXME XXX HACK: OK, so we should rewrite the options in /media/.hal-mtab .. 905 * but it doesn't really matter much at this point */ 906 if (!is_remount) { 907 FILE *hal_mtab; 908 char *mount_dir_escaped; 909 FILE *hal_mtab_orig; 910 int hal_mtab_orig_len; 911 int num_read; 912 char *hal_mtab_buf; 913 char *hal_mtab_buf_old; 914 915 /* Maintain a list in /media/.hal-mtab with entries of the following format 916 * 917 * <device_file>\t<uid>\t<session-id>\t<fstype>\t<options_sep_by_comma>\t<mount point>\n 918 * 919 * where session-id currently is unused and thus set to 0. 920 * 921 * Example: 922 * 923 * /dev/sda2 500 0 hfsplus noexec,nosuid,nodev /media/Macintosh HD 924 * /dev/sda4 500 0 ntfs noexec,nosuid,nodev,umask=222 /media/Windows 925 * /dev/sdb1 500 0 vfat noexec,nosuid,nodev,shortname=winnt,uid=500 /media/davidz 926 */ 927 928 929 if (g_file_test ("/media/.hal-mtab", G_FILE_TEST_EXISTS)) { 930 hal_mtab_orig = fopen ("/media/.hal-mtab", "r"); 931 if (hal_mtab_orig == NULL) { 932 unknown_error ("Cannot open /media/.hal-mtab"); 933 } 934 if (fseek (hal_mtab_orig, 0L, SEEK_END) != 0) { 935 unknown_error ("Cannot seek to end of /media/.hal-mtab"); 936 } 937 hal_mtab_orig_len = ftell (hal_mtab_orig); 938 if (hal_mtab_orig_len < 0) { 939 unknown_error ("Cannot determine size of /media/.hal-mtab"); 940 } 941 rewind (hal_mtab_orig); 942 hal_mtab_buf = g_new0 (char, hal_mtab_orig_len + 1); 943 num_read = fread (hal_mtab_buf, 1, hal_mtab_orig_len, hal_mtab_orig); 944 if (num_read != hal_mtab_orig_len) { 945 unknown_error ("Cannot read from /media/.hal-mtab"); 946 } 947 fclose (hal_mtab_orig); 948 } else { 949 hal_mtab_buf = g_strdup (""); 950 } 951 952 mount_dir_escaped = g_strescape (mount_dir, NULL); 953 #ifdef DEBUG 954 printf ("%d: XYA creating /media/.hal-mtab~\n", getpid ()); 955 #endif 956 hal_mtab = fopen ("/media/.hal-mtab~", "w"); 957 if (hal_mtab == NULL) { 958 unknown_error ("Cannot create /media/.hal-mtab~"); 959 } 960 hal_mtab_buf_old = hal_mtab_buf; 961 hal_mtab_buf = g_strdup_printf ("%s%s\t%s\t0\t%s\t%s\t%s\n", 962 hal_mtab_buf_old, 963 device, invoked_by_uid, mount_do_fstype, 964 mount_option_commasep, mount_dir_escaped); 965 g_free (hal_mtab_buf_old); 966 if (hal_mtab_buf_old == NULL) { 967 unknown_error ("Out of memory appending to /media/.hal-mtab~"); 968 } 969 if (fwrite (hal_mtab_buf, 1, strlen (hal_mtab_buf), hal_mtab) != strlen (hal_mtab_buf)) { 970 unknown_error ("Cannot write to /media/.hal-mtab~"); 971 } 972 fclose (hal_mtab); 973 g_free (hal_mtab_buf); 974 g_free (mount_dir_escaped); 975 #ifdef DEBUG 976 printf ("%d: XYA closing /media/.hal-mtab~\n", getpid ()); 977 #endif 978 } /* !is_remount */ 979 980 /* now try to mount */ 981 if (!g_spawn_sync ("/", 982 args, 983 NULL, 984 0, 985 NULL, 986 NULL, 987 &sout, 988 &serr, 989 &exit_status, 990 &err)) { 991 printf ("Cannot execute %s\n", MOUNT); 992 g_rmdir (mount_dir); 993 unlink ("/media/.hal-mtab~"); 994 unknown_error ("Cannot spawn " MOUNT); 995 } 996 997 998 if (exit_status != 0) { 999 char errstr[] = "mount: unknown filesystem type"; 1000 1001 printf ("%s error %d, stdout='%s', stderr='%s'\n", MOUNT, exit_status, sout, serr); 1002 1003 if (!is_remount) { 1004 g_rmdir (mount_dir); 1005 unlink ("/media/.hal-mtab~"); 1006 } 1007 1008 if (strncmp (errstr, serr, sizeof (errstr) - 1) == 0) { 1009 unknown_filesystem (strlen (mount_fstype) > 0 ? 1010 mount_fstype : 1011 (volume != NULL ? libhal_volume_get_fstype (volume) : "") ); 1012 } else { 1013 int n; 1014 for (n = 0; serr[n] != '\0'; n++) { 1015 if (serr[n] == '\n') { 1016 serr[n] = ' '; 1017 } 1018 } 1019 unknown_error (serr); 1020 } 1021 } 1022 1023 if (!is_remount) { 1024 if (rename ("/media/.hal-mtab~", "/media/.hal-mtab") != 0) { 1025 printf ("rename(2) failed, errno=%d -> '%s'\n", errno, strerror (errno)); 1026 unlink ("/media/.hal-mtab~"); 1027 #ifdef DEBUG 1028 printf ("%d: XYA failed renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ()); 1029 #endif 1030 unknown_error ("Cannot rename /media/.hal-mtab~ to /media/.hal-mtab"); 1031 } 1032 #ifdef DEBUG 1033 printf ("%d: XYA done renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ()); 1034 #endif 1035 } 1036 1037 openlog ("hald", 0, LOG_DAEMON); 1038 if (is_remount) { 1039 syslog (LOG_INFO, "remounted %s at '%s' on behalf of uid %s", device, mount_dir, invoked_by_uid); 1040 } else { 1041 syslog (LOG_INFO, "mounted %s on behalf of uid %s", device, invoked_by_uid); 1042 } 1043 closelog (); 1044 1045 #ifdef sun 1046 if ((adt_data = get_audit_export_data (system_bus, 1047 invoked_by_syscon_name, &adt_data_size)) != NULL) { 1048 audit_volume (adt_data, ADT_attach, 1049 WEXITSTATUS(exit_status), auth_from_privilege(privilege), 1050 mount_dir, device, mount_option_commasep); 1051 free (adt_data); 1052 } 1053 #endif 1054 1055 g_free (sout); 1056 g_free (serr); 1057 g_free (mount_dir); 1058 libhal_free_string_array (allowed_options); 1059 g_strfreev (given_options); 1060 } 1061 1062 1063 int 1064 main (int argc, char *argv[]) 1065 { 1066 char *udi; 1067 char *device; 1068 LibHalVolume *volume; 1069 DBusError error; 1070 LibHalContext *hal_ctx = NULL; 1071 DBusConnection *system_bus = NULL; 1072 #ifdef HAVE_POLKIT 1073 LibPolKitContext *pol_ctx = NULL; 1074 #endif 1075 char *invoked_by_uid; 1076 char *invoked_by_syscon_name; 1077 1078 if (!lock_hal_mtab ()) { 1079 unknown_error ("Cannot obtain lock on /media/.hal-mtab"); 1080 } 1081 1082 device = getenv ("HAL_PROP_BLOCK_DEVICE"); 1083 if (device == NULL) 1084 usage (); 1085 1086 udi = getenv ("HAL_PROP_INFO_UDI"); 1087 if (udi == NULL) 1088 usage (); 1089 1090 invoked_by_uid = getenv ("HAL_METHOD_INVOKED_BY_UID"); 1091 1092 invoked_by_syscon_name = getenv ("HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME"); 1093 1094 dbus_error_init (&error); 1095 if ((hal_ctx = libhal_ctx_init_direct (&error)) == NULL) { 1096 printf ("Cannot connect to hald\n"); 1097 LIBHAL_FREE_DBUS_ERROR (&error); 1098 usage (); 1099 } 1100 1101 dbus_error_init (&error); 1102 system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error); 1103 if (system_bus == NULL) { 1104 printf ("Cannot connect to the system bus\n"); 1105 LIBHAL_FREE_DBUS_ERROR (&error); 1106 usage (); 1107 } 1108 #ifdef HAVE_POLKIT 1109 pol_ctx = libpolkit_new_context (system_bus); 1110 if (pol_ctx == NULL) { 1111 printf ("Cannot get libpolkit context\n"); 1112 unknown_error ("Cannot get libpolkit context"); 1113 } 1114 #endif 1115 1116 volume = libhal_volume_from_udi (hal_ctx, udi); 1117 if (volume == NULL) { 1118 LibHalDrive *drive; 1119 1120 drive = libhal_drive_from_udi (hal_ctx, udi); 1121 if (drive == NULL) { 1122 usage (); 1123 } else { 1124 handle_mount (hal_ctx, 1125 #ifdef HAVE_POLKIT 1126 pol_ctx, 1127 #endif 1128 udi, NULL, drive, device, invoked_by_uid, 1129 invoked_by_syscon_name, system_bus); 1130 } 1131 1132 } else { 1133 const char *drive_udi; 1134 LibHalDrive *drive; 1135 1136 drive_udi = libhal_volume_get_storage_device_udi (volume); 1137 1138 if (drive_udi == NULL) 1139 unknown_error ("Cannot get drive_udi from volume"); 1140 drive = libhal_drive_from_udi (hal_ctx, drive_udi); 1141 if (drive == NULL) 1142 unknown_error ("Cannot get drive from hal"); 1143 1144 handle_mount (hal_ctx, 1145 #ifdef HAVE_POLKIT 1146 pol_ctx, 1147 #endif 1148 udi, volume, drive, device, invoked_by_uid, 1149 invoked_by_syscon_name, system_bus); 1150 1151 } 1152 1153 unlock_hal_mtab (); 1154 1155 return 0; 1156 } 1157