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 "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 705 if (explicit_mount_point_given) { 706 mount_point_not_available (mount_dir); 707 } 708 709 i++; 710 } 711 } 712 713 dbus_error_init (&error); 714 allowed_options = libhal_device_get_property_strlist (hal_ctx, udi, "volume.mount.valid_options", &error); 715 if (dbus_error_is_set (&error)) { 716 unknown_error ("Cannot get volume.mount.valid_options"); 717 dbus_error_free (&error); 718 } 719 720 #ifdef DEBUG 721 for (i = 0; given_options[i] != NULL; i++) 722 printf ("given_options[%d] = '%s'\n", i, given_options[i]); 723 for (i = 0; allowed_options[i] != NULL; i++) 724 printf ("allowed_options[%d] = '%s'\n", i, allowed_options[i]); 725 #endif 726 727 wants_to_change_uid = FALSE; 728 729 /* check mount options */ 730 for (i = 0; given_options[i] != NULL; i++) { 731 char *given = given_options[i]; 732 733 for (j = 0; allowed_options[j] != NULL; j++) { 734 char *allow = allowed_options[j]; 735 int allow_len = strlen (allow); 736 737 if (strcmp (given, allow) == 0) { 738 goto option_ok; 739 } 740 741 if ((allow[allow_len - 1] == '=') && 742 (strncmp (given, allow, allow_len) == 0) && 743 (int) strlen (given) > allow_len) { 744 745 /* option matched allowed ending in '=', e.g. 746 * given == "umask=foobar" and allowed == "umask=" 747 */ 748 if (strcmp (allow, "uid=") == 0) { 749 uid_t uid; 750 char *endp; 751 /* check for uid=, it requires special handling */ 752 uid = (uid_t) strtol (given + allow_len, &endp, 10); 753 if (*endp != '\0') { 754 printf ("'%s' is not a number?\n", given); 755 unknown_error ("option uid is malformed"); 756 } 757 #ifdef DEBUG 758 printf ("%s with uid %d\n", allow, uid); 759 #endif 760 wants_to_change_uid = TRUE; 761 762 goto option_ok; 763 } else { 764 765 goto option_ok; 766 } 767 } 768 } 769 770 /* apparently option was not ok */ 771 invalid_mount_option (given, invoked_by_uid); 772 773 option_ok: 774 ; 775 } 776 777 /* Check privilege */ 778 pol_is_fixed = TRUE; 779 if (libhal_drive_is_hotpluggable (drive) || libhal_drive_uses_removable_media (drive)) 780 pol_is_fixed = FALSE; 781 782 pol_change_uid = FALSE; 783 /* don't consider uid= on non-pollable drives for the purpose of policy 784 * (since these drives normally use vfat) 785 */ 786 if (volume != NULL) { 787 /* don't consider uid= on vfat, iso9660, udf change-uid for the purpose of policy 788 * (since these doesn't contain uid/gid bits) 789 */ 790 if (strcmp (libhal_volume_get_fstype (volume), "vfat") != 0 && 791 strcmp (libhal_volume_get_fstype (volume), "iso9660") != 0 && 792 strcmp (libhal_volume_get_fstype (volume), "udf") != 0) { 793 pol_change_uid = wants_to_change_uid; 794 } 795 } 796 797 if (pol_is_fixed) { 798 if (pol_change_uid) { 799 privilege = "hal-storage-fixed-mount-all-options"; 800 } else { 801 privilege = "hal-storage-fixed-mount"; 802 } 803 } else { 804 if (pol_change_uid) { 805 privilege = "hal-storage-removable-mount-all-options"; 806 } else { 807 privilege = "hal-storage-removable-mount"; 808 } 809 } 810 811 #ifdef DEBUG 812 printf ("using privilege %s for uid %s, system_bus_connection %s\n", privilege, invoked_by_uid, 813 invoked_by_syscon_name); 814 #endif 815 816 #ifdef HAVE_POLKIT 817 if (libpolkit_is_uid_allowed_for_privilege (pol_ctx, 818 invoked_by_syscon_name, 819 invoked_by_uid, 820 privilege, 821 udi, 822 &allowed_by_privilege, 823 &is_temporary_privilege, 824 NULL) != LIBPOLKIT_RESULT_OK) { 825 printf ("cannot lookup privilege\n"); 826 unknown_error ("Cannot lookup privilege from PolicyKit"); 827 } 828 829 if (!allowed_by_privilege) { 830 printf ("caller don't possess privilege\n"); 831 permission_denied_privilege (privilege, invoked_by_uid); 832 } 833 #endif 834 835 #ifdef DEBUG 836 printf ("passed privilege\n"); 837 #endif 838 839 if (!is_remount) { 840 /* create directory */ 841 #ifdef sun 842 if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS) && 843 (g_mkdir (mount_dir, 0755) != 0)) { 844 #else 845 if (g_mkdir (mount_dir, 0700) != 0) { 846 #endif 847 printf ("Cannot create '%s'\n", mount_dir); 848 unknown_error ("Cannot create mount directory"); 849 } 850 851 #ifdef __FreeBSD__ 852 calling_uid = (uid_t) strtol (invoked_by_uid, (char **) NULL, 10); 853 pw = getpwuid (calling_uid); 854 if (pw != NULL) { 855 calling_gid = pw->pw_gid; 856 } else { 857 calling_gid = 0; 858 } 859 if (chown (mount_dir, calling_uid, calling_gid) != 0) { 860 printf ("Cannot chown '%s' to uid: %d, gid: %d\n", mount_dir, 861 calling_uid, calling_gid); 862 g_rmdir (mount_dir); 863 unknown_error (); 864 } 865 #endif 866 } 867 868 char *mount_option_commasep = NULL; 869 char *mount_do_fstype = "auto"; 870 871 /* construct arguments to mount */ 872 na = 0; 873 args[na++] = MOUNT; 874 if (strlen (mount_fstype) > 0) { 875 mount_do_fstype = (char *) map_fstype (mount_fstype); 876 } else if (volume == NULL) { 877 /* non-pollable drive; force auto */ 878 mount_do_fstype = "auto"; 879 } else if (libhal_volume_get_fstype (volume) != NULL && strlen (libhal_volume_get_fstype (volume)) > 0) { 880 mount_do_fstype = (char *) map_fstype (libhal_volume_get_fstype (volume)); 881 } 882 args[na++] = MOUNT_TYPE_OPT; 883 args[na++] = mount_do_fstype; 884 885 args[na++] = "-o"; 886 mount_option_str = g_string_new (MOUNT_OPTIONS); 887 for (i = 0; given_options[i] != NULL; i++) { 888 g_string_append (mount_option_str, ","); 889 g_string_append (mount_option_str, given_options[i]); 890 } 891 #ifdef sun 892 if (append_ro) { 893 g_string_append (mount_option_str, ",ro"); 894 } 895 #endif 896 mount_option_commasep = g_string_free (mount_option_str, FALSE); /* leak! */ 897 args[na++] = mount_option_commasep; 898 args[na++] = (char *) device; 899 args[na++] = mount_dir; 900 args[na++] = NULL; 901 902 /* TODO FIXME XXX HACK: OK, so we should rewrite the options in /media/.hal-mtab .. 903 * but it doesn't really matter much at this point */ 904 if (!is_remount) { 905 FILE *hal_mtab; 906 char *mount_dir_escaped; 907 FILE *hal_mtab_orig; 908 int hal_mtab_orig_len; 909 int num_read; 910 char *hal_mtab_buf; 911 char *hal_mtab_buf_old; 912 913 /* Maintain a list in /media/.hal-mtab with entries of the following format 914 * 915 * <device_file>\t<uid>\t<session-id>\t<fstype>\t<options_sep_by_comma>\t<mount point>\n 916 * 917 * where session-id currently is unused and thus set to 0. 918 * 919 * Example: 920 * 921 * /dev/sda2 500 0 hfsplus noexec,nosuid,nodev /media/Macintosh HD 922 * /dev/sda4 500 0 ntfs noexec,nosuid,nodev,umask=222 /media/Windows 923 * /dev/sdb1 500 0 vfat noexec,nosuid,nodev,shortname=winnt,uid=500 /media/davidz 924 */ 925 926 927 if (g_file_test ("/media/.hal-mtab", G_FILE_TEST_EXISTS)) { 928 hal_mtab_orig = fopen ("/media/.hal-mtab", "r"); 929 if (hal_mtab_orig == NULL) { 930 unknown_error ("Cannot open /media/.hal-mtab"); 931 } 932 if (fseek (hal_mtab_orig, 0L, SEEK_END) != 0) { 933 unknown_error ("Cannot seek to end of /media/.hal-mtab"); 934 } 935 hal_mtab_orig_len = ftell (hal_mtab_orig); 936 if (hal_mtab_orig_len < 0) { 937 unknown_error ("Cannot determine size of /media/.hal-mtab"); 938 } 939 rewind (hal_mtab_orig); 940 hal_mtab_buf = g_new0 (char, hal_mtab_orig_len + 1); 941 num_read = fread (hal_mtab_buf, 1, hal_mtab_orig_len, hal_mtab_orig); 942 if (num_read != hal_mtab_orig_len) { 943 unknown_error ("Cannot read from /media/.hal-mtab"); 944 } 945 fclose (hal_mtab_orig); 946 } else { 947 hal_mtab_buf = g_strdup (""); 948 } 949 950 mount_dir_escaped = g_strescape (mount_dir, NULL); 951 #ifdef DEBUG 952 printf ("%d: XYA creating /media/.hal-mtab~\n", getpid ()); 953 #endif 954 hal_mtab = fopen ("/media/.hal-mtab~", "w"); 955 if (hal_mtab == NULL) { 956 unknown_error ("Cannot create /media/.hal-mtab~"); 957 } 958 hal_mtab_buf_old = hal_mtab_buf; 959 hal_mtab_buf = g_strdup_printf ("%s%s\t%s\t0\t%s\t%s\t%s\n", 960 hal_mtab_buf_old, 961 device, invoked_by_uid, mount_do_fstype, 962 mount_option_commasep, mount_dir_escaped); 963 g_free (hal_mtab_buf_old); 964 if (hal_mtab_buf_old == NULL) { 965 unknown_error ("Out of memory appending to /media/.hal-mtab~"); 966 } 967 if (fwrite (hal_mtab_buf, 1, strlen (hal_mtab_buf), hal_mtab) != strlen (hal_mtab_buf)) { 968 unknown_error ("Cannot write to /media/.hal-mtab~"); 969 } 970 fclose (hal_mtab); 971 g_free (hal_mtab_buf); 972 g_free (mount_dir_escaped); 973 #ifdef DEBUG 974 printf ("%d: XYA closing /media/.hal-mtab~\n", getpid ()); 975 #endif 976 } /* !is_remount */ 977 978 /* now try to mount */ 979 if (!g_spawn_sync ("/", 980 args, 981 NULL, 982 0, 983 NULL, 984 NULL, 985 &sout, 986 &serr, 987 &exit_status, 988 &err)) { 989 printf ("Cannot execute %s\n", MOUNT); 990 g_rmdir (mount_dir); 991 unlink ("/media/.hal-mtab~"); 992 unknown_error ("Cannot spawn " MOUNT); 993 } 994 995 996 if (exit_status != 0) { 997 char errstr[] = "mount: unknown filesystem type"; 998 999 printf ("%s error %d, stdout='%s', stderr='%s'\n", MOUNT, exit_status, sout, serr); 1000 1001 if (!is_remount) { 1002 g_rmdir (mount_dir); 1003 unlink ("/media/.hal-mtab~"); 1004 } 1005 1006 if (strncmp (errstr, serr, sizeof (errstr) - 1) == 0) { 1007 unknown_filesystem (strlen (mount_fstype) > 0 ? 1008 mount_fstype : 1009 (volume != NULL ? libhal_volume_get_fstype (volume) : "") ); 1010 } else { 1011 int n; 1012 for (n = 0; serr[n] != '\0'; n++) { 1013 if (serr[n] == '\n') { 1014 serr[n] = ' '; 1015 } 1016 } 1017 unknown_error (serr); 1018 } 1019 } 1020 1021 if (!is_remount) { 1022 if (rename ("/media/.hal-mtab~", "/media/.hal-mtab") != 0) { 1023 printf ("rename(2) failed, errno=%d -> '%s'\n", errno, strerror (errno)); 1024 unlink ("/media/.hal-mtab~"); 1025 #ifdef DEBUG 1026 printf ("%d: XYA failed renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ()); 1027 #endif 1028 unknown_error ("Cannot rename /media/.hal-mtab~ to /media/.hal-mtab"); 1029 } 1030 #ifdef DEBUG 1031 printf ("%d: XYA done renaming /media/.hal-mtab~ to /media/.hal-mtab\n", getpid ()); 1032 #endif 1033 } 1034 1035 openlog ("hald", 0, LOG_DAEMON); 1036 if (is_remount) { 1037 syslog (LOG_INFO, "remounted %s at '%s' on behalf of uid %s", device, mount_dir, invoked_by_uid); 1038 } else { 1039 syslog (LOG_INFO, "mounted %s on behalf of uid %s", device, invoked_by_uid); 1040 } 1041 closelog (); 1042 1043 #ifdef sun 1044 if ((adt_data = get_audit_export_data (system_bus, 1045 invoked_by_syscon_name, &adt_data_size)) != NULL) { 1046 audit_volume (adt_data, ADT_attach, 1047 WEXITSTATUS(exit_status), auth_from_privilege(privilege), 1048 mount_dir, device, mount_option_commasep); 1049 free (adt_data); 1050 } 1051 #endif 1052 1053 g_free (sout); 1054 g_free (serr); 1055 g_free (mount_dir); 1056 libhal_free_string_array (allowed_options); 1057 g_strfreev (given_options); 1058 } 1059 1060 1061 int 1062 main (int argc, char *argv[]) 1063 { 1064 char *udi; 1065 char *device; 1066 LibHalVolume *volume; 1067 DBusError error; 1068 LibHalContext *hal_ctx = NULL; 1069 DBusConnection *system_bus = NULL; 1070 #ifdef HAVE_POLKIT 1071 LibPolKitContext *pol_ctx = NULL; 1072 #endif 1073 char *invoked_by_uid; 1074 char *invoked_by_syscon_name; 1075 1076 if (!lock_hal_mtab ()) { 1077 unknown_error ("Cannot obtain lock on /media/.hal-mtab"); 1078 } 1079 1080 device = getenv ("HAL_PROP_BLOCK_DEVICE"); 1081 if (device == NULL) 1082 usage (); 1083 1084 udi = getenv ("HAL_PROP_INFO_UDI"); 1085 if (udi == NULL) 1086 usage (); 1087 1088 invoked_by_uid = getenv ("HAL_METHOD_INVOKED_BY_UID"); 1089 1090 invoked_by_syscon_name = getenv ("HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME"); 1091 1092 dbus_error_init (&error); 1093 if ((hal_ctx = libhal_ctx_init_direct (&error)) == NULL) { 1094 printf ("Cannot connect to hald\n"); 1095 LIBHAL_FREE_DBUS_ERROR (&error); 1096 usage (); 1097 } 1098 1099 dbus_error_init (&error); 1100 system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error); 1101 if (system_bus == NULL) { 1102 printf ("Cannot connect to the system bus\n"); 1103 LIBHAL_FREE_DBUS_ERROR (&error); 1104 usage (); 1105 } 1106 #ifdef HAVE_POLKIT 1107 pol_ctx = libpolkit_new_context (system_bus); 1108 if (pol_ctx == NULL) { 1109 printf ("Cannot get libpolkit context\n"); 1110 unknown_error ("Cannot get libpolkit context"); 1111 } 1112 #endif 1113 1114 volume = libhal_volume_from_udi (hal_ctx, udi); 1115 if (volume == NULL) { 1116 LibHalDrive *drive; 1117 1118 drive = libhal_drive_from_udi (hal_ctx, udi); 1119 if (drive == NULL) { 1120 usage (); 1121 } else { 1122 handle_mount (hal_ctx, 1123 #ifdef HAVE_POLKIT 1124 pol_ctx, 1125 #endif 1126 udi, NULL, drive, device, invoked_by_uid, 1127 invoked_by_syscon_name, system_bus); 1128 } 1129 1130 } else { 1131 const char *drive_udi; 1132 LibHalDrive *drive; 1133 1134 drive_udi = libhal_volume_get_storage_device_udi (volume); 1135 1136 if (drive_udi == NULL) 1137 unknown_error ("Cannot get drive_udi from volume"); 1138 drive = libhal_drive_from_udi (hal_ctx, drive_udi); 1139 if (drive == NULL) 1140 unknown_error ("Cannot get drive from hal"); 1141 1142 handle_mount (hal_ctx, 1143 #ifdef HAVE_POLKIT 1144 pol_ctx, 1145 #endif 1146 udi, volume, drive, device, invoked_by_uid, 1147 invoked_by_syscon_name, system_bus); 1148 1149 } 1150 1151 unlock_hal_mtab (); 1152 1153 return 0; 1154 } 1155