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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * autod_mount.c 24 * 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29/* 30 * Portions Copyright 2007-2012 Apple Inc. 31 */ 32 33#pragma ident "@(#)autod_mount.c 1.71 05/06/08 SMI" 34 35#include <stdio.h> 36#include <ctype.h> 37#include <string.h> 38#include <syslog.h> 39#include <sys/types.h> 40#include <sys/stat.h> 41#include <sys/param.h> 42#include <errno.h> 43#include <mntopts.h> 44#include <stdlib.h> 45#include <unistd.h> 46#include <sys/wait.h> 47#include <assert.h> 48#include <fcntl.h> 49#include <bsm/audit.h> 50 51#include "automount.h" 52#include "nfs.h" 53#include "automountd.h" 54#include "auto_mntopts.h" 55#include "replica.h" 56#include "sysctl_fsid.h" 57#include "umount_by_fsid.h" 58 59static void free_action_list(action_list *); 60static int unmount_mntpnt(fsid_t, struct mnttab *); 61static int fork_exec(char *, char **, uid_t, au_asid_t); 62static void remove_browse_options(char *); 63static int inherit_options(const char *, char **); 64 65#define ROUND_UP(a, b) ((((a) + (b) - 1)/(b)) * (b)) 66 67static uint32_t 68countstring(char *string) 69{ 70 uint32_t stringlen; 71 72 if (string != NULL) 73 stringlen = (uint32_t)strlen(string); 74 else 75 stringlen = 0; 76 return ((uint32_t)sizeof (uint32_t) + stringlen); 77} 78 79static uint8_t * 80putstring(uint8_t *outbuf, char *string) 81{ 82 uint32_t stringlen; 83 84 if (string != NULL) { 85 stringlen = (uint32_t)strlen(string); 86 memcpy(outbuf, &stringlen, sizeof (uint32_t)); 87 outbuf += sizeof (uint32_t); 88 memcpy(outbuf, string, stringlen); 89 outbuf += stringlen; 90 } else { 91 stringlen = 0xFFFFFFFF; 92 memcpy(outbuf, &stringlen, sizeof (uint32_t)); 93 outbuf += sizeof (uint32_t); 94 } 95 return (outbuf); 96} 97 98static uint8_t * 99putint(uint8_t *outbuf, int val) 100{ 101 memcpy(outbuf, &val, sizeof (int)); 102 outbuf += sizeof (int); 103 return (outbuf); 104} 105 106static uint8_t * 107putuint32(uint8_t *outbuf, uint32_t val) 108{ 109 memcpy(outbuf, &val, sizeof (uint32_t)); 110 outbuf += sizeof (uint32_t); 111 return (outbuf); 112} 113 114int 115do_mount1(const autofs_pathname mapname, const char *key, 116 const autofs_pathname subdir, const autofs_opts mapopts, 117 const autofs_pathname path, boolean_t isdirect, boolean_t issubtrigger, 118 fsid_t mntpnt_fsid, uid_t sendereuid, au_asid_t asid, fsid_t *fsidp, 119 uint32_t *retflags, byte_buffer *actions, 120 mach_msg_type_number_t *actionsCnt) 121{ 122 struct mapent *me, *mapents; 123 char mntpnt[MAXPATHLEN]; 124 char spec_mntpnt[MAXPATHLEN]; 125 int err; 126 char *private; /* fs specific data. eg prevhost in case of nfs */ 127 ssize_t len; 128 action_list *alp, *alphead, *prev, *tmp; 129 char root[MAXPATHLEN]; 130 char next_subdir[MAXPATHLEN]; 131 bool_t mount_access = TRUE; 132 bool_t isrestricted = hasrestrictopt(mapopts); 133 kern_return_t ret; 134 size_t bufsize; 135 vm_address_t buffer_vm_address; 136 uint8_t *outbuf; 137 138retry: 139 mapents = parse_entry(key, mapname, mapopts, subdir, isdirect, 140 NULL, isrestricted, mount_access, &err); 141 if (mapents == NULL) { 142 /* Return the error parse_entry handed back. */ 143 return (err); 144 } 145 146 if (trace > 1) { 147 struct mapfs *mfs; 148 trace_prt(1, " do_mount1:\n"); 149 for (me = mapents; me; me = me->map_next) { 150 trace_prt(1, " (%s,%s)\t%s%s%s -%s\n", 151 me->map_fstype ? me->map_fstype : "", 152 me->map_mounter ? me->map_mounter : "", 153 path ? path : "", 154 me->map_root ? me->map_root : "", 155 me->map_mntpnt ? me->map_mntpnt : "", 156 me->map_mntopts ? me->map_mntopts : ""); 157 158 for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) 159 trace_prt(1, "\t\t%s:%s\tpenalty=%d\n", 160 mfs->mfs_host ? mfs->mfs_host: "", 161 mfs->mfs_dir ? mfs->mfs_dir : "", 162 mfs->mfs_penalty); 163 } 164 } 165 166 alphead = NULL; 167 168 /* 169 * Each mapent in the list describes a mount to be done. 170 * Normally there's just a single entry, though in the 171 * case of /net mounts there may be many entries, that 172 * must be mounted as a hierarchy. For each mount the 173 * automountd must make sure the required mountpoint 174 * exists and invoke the appropriate mount command for 175 * the fstype. 176 */ 177 private = ""; 178 for (me = mapents; me && !err; me = me->map_next) { 179 /* 180 * For subtrigger mounts, path is the mount path of 181 * the subtrigger, which is the path atop which we 182 * mount the remote object, so there's no subdirectory 183 * underneath the root of the mount. subdir is 184 * relative to the root of the top-level autofs mount, 185 * not relative to the root of the subtrigger mount, 186 * so we don't include it in the path. (subdir is 187 * used to look in the map entry for mapents at or 188 * below that directory, as the topmost of those 189 * tells us what to mount, and the entries below 190 * it tell us what triggers need to be planted.) 191 */ 192 if (isdirect) { 193 len = snprintf(mntpnt, sizeof (mntpnt), "%s%s%s", 194 path, issubtrigger ? "" : subdir, me->map_mntpnt); 195 } else { 196 len = snprintf(mntpnt, sizeof (mntpnt), "%s%s%s%s", 197 path, mapents->map_root, 198 issubtrigger ? "" : subdir, me->map_mntpnt); 199 } 200 201 if (len < 0) { 202 free_mapent(mapents); 203 return (EINVAL); 204 } 205 if ((size_t)len >= sizeof (mntpnt)) { 206 free_mapent(mapents); 207 return (ENAMETOOLONG); 208 } 209 /* 210 * remove trailing /'s from mountpoint to avoid problems 211 * stating a directory with two or more trailing slashes. 212 * This will let us mount directories from machines 213 * which export with two or more slashes (apollo for instance). 214 */ 215 len -= 1; 216 while (mntpnt[len] == '/') 217 mntpnt[len--] = '\0'; 218 219 (void) strlcpy(spec_mntpnt, mntpnt, sizeof(spec_mntpnt)); 220 221 if (isrestricted && 222 inherit_options(mapopts, &me->map_mntopts) != 0) { 223 syslog(LOG_ERR, "malloc of options failed"); 224 free_mapent(mapents); 225 return (ENOMEM); 226 } 227 228 if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) { 229 remove_browse_options(me->map_mntopts); 230 err = 231 mount_nfs(me, spec_mntpnt, private, isdirect, 232 mntpnt_fsid, asid, fsidp, retflags); 233 /* 234 * We must retry if we don't have access to the 235 * root file system and there are other 236 * following mapents. The reason we can't 237 * continue because the rest of the mapent list 238 * depends on whether mount_access is TRUE or FALSE. 239 */ 240 if (err == EACCES && me->map_next != NULL) { 241 /* 242 * don't expect mount_access to be 243 * FALSE here, but we do a check 244 * anyway. 245 */ 246 if (mount_access == TRUE) { 247 mount_access = FALSE; 248 free_mapent(mapents); 249 goto retry; 250 } 251 } 252 } else if (strcmp(me->map_fstype, MNTTYPE_AUTOFS) == 0) { 253 if (isdirect) { 254 len = strlcpy(root, path, sizeof (root)); 255 } else { 256 len = snprintf(root, sizeof (root), "%s/%s", 257 path, key); 258 } 259 if (len < 0) { 260 free_mapent(mapents); 261 return (EINVAL); 262 } 263 if ((size_t)len >= sizeof (root)) { 264 free_mapent(mapents); 265 return (ENAMETOOLONG); 266 } 267 268 /* 269 * get the next subdir 270 */ 271 len = snprintf(next_subdir, sizeof (next_subdir), 272 "%s%s", subdir, me->map_mntpnt); 273 274 if (trace > 2) 275 trace_prt(1, " root=%s\t next_subdir=%s\n", root, next_subdir); 276 if (len < 0) { 277 err = EINVAL; 278 } else if ((size_t)len < sizeof (next_subdir)) { 279 err = mount_autofs(mapname, me, spec_mntpnt, 280 mntpnt_fsid, &alp, root, 281 next_subdir, key, fsidp, retflags); 282 } else { 283 err = ENAMETOOLONG; 284 } 285 if (alp != NULL) { 286 /* 287 * We were given an action list entry to 288 * append to the action list; do so. 289 */ 290 if (alphead == NULL) 291 alphead = alp; 292 else { 293 for (tmp = alphead; tmp != NULL; 294 tmp = tmp->next) 295 prev = tmp; 296 prev->next = alp; 297 } 298 } 299#ifdef HAVE_LOFS 300 } else if (strcmp(me->map_fstype, MNTTYPE_LOFS) == 0) { 301 remove_browse_options(me->map_mntopts); 302 err = loopbackmount(me->map_fs->mfs_dir, spec_mntpnt, 303 me->map_mntopts); 304#endif 305 } else { 306 remove_browse_options(me->map_mntopts); 307 err = mount_generic(me->map_fs->mfs_dir, 308 me->map_fstype, me->map_mntopts, 0, 309 spec_mntpnt, isdirect, FALSE, 310 mntpnt_fsid, sendereuid, asid, 311 fsidp, retflags); 312 } 313 } 314 if (mapents) 315 free_mapent(mapents); 316 317 if (!err) { 318 /* 319 * Serialize the action list and supply it to the 320 * caller. 321 */ 322 bufsize = 0; 323 for (tmp = alphead; tmp != NULL; tmp = tmp->next) { 324 bufsize += countstring(tmp->mounta.dir); 325 bufsize += countstring(tmp->mounta.opts); 326 bufsize += countstring(tmp->mounta.path); 327 bufsize += countstring(tmp->mounta.map); 328 bufsize += countstring(tmp->mounta.subdir); 329 bufsize += countstring(tmp->mounta.trig_mntpnt); 330 bufsize += sizeof (int); /* tmp->mounta.flags */ 331 bufsize += sizeof (int); /* tmp->mounta.mntflags */ 332 bufsize += sizeof (uint32_t); /* tmp->mounta.isdirect */ 333 bufsize += sizeof (uint32_t); /* tmp->mounta.needs_subtrigger */ 334 bufsize += countstring(tmp->mounta.key); 335 } 336 if (bufsize != 0) { 337 ret = vm_allocate(current_task(), &buffer_vm_address, 338 bufsize, VM_FLAGS_ANYWHERE); 339 if (ret != KERN_SUCCESS) { 340 syslog(LOG_ERR, "memory allocation error: %s", 341 mach_error_string(ret)); 342 free_action_list(alphead); 343 return (ENOMEM); 344 } 345 outbuf = (uint8_t *)buffer_vm_address; 346 *actions = outbuf; 347 for (tmp = alphead; tmp != NULL; tmp = tmp->next) { 348 outbuf = putstring(outbuf, tmp->mounta.dir); 349 outbuf = putstring(outbuf, tmp->mounta.opts); 350 outbuf = putstring(outbuf, tmp->mounta.path); 351 outbuf = putstring(outbuf, tmp->mounta.map); 352 outbuf = putstring(outbuf, tmp->mounta.subdir); 353 outbuf = putstring(outbuf, tmp->mounta.trig_mntpnt); 354 outbuf = putint(outbuf, tmp->mounta.flags); 355 outbuf = putint(outbuf, tmp->mounta.mntflags); 356 outbuf = putuint32(outbuf, tmp->mounta.isdirect); 357 outbuf = putuint32(outbuf, tmp->mounta.needs_subtrigger); 358 outbuf = putstring(outbuf, tmp->mounta.key); 359 } 360 free_action_list(alphead); 361 } 362 *actionsCnt = (mach_msg_type_number_t)bufsize; 363 } 364 return (err); 365} 366 367static void 368free_action_list(action_list *alp) 369{ 370 action_list *p, *next = NULL; 371 372 for (p = alp; p != NULL; p = next) { 373 free_action_list_fields(p); 374 next = p->next; 375 free(p); 376 } 377} 378 379#define ARGV_MAX 23 380#define VFS_PATH "/sbin" 381#define MOUNT_PATH "/sbin/mount" 382#define MOUNT_URL_PATH "/usr/libexec/mount_url" 383 384struct attr_buffer { 385 uint32_t length; 386 fsid_t fsid; 387 uint32_t mountflags; 388 vol_capabilities_attr_t capabilities; 389}; 390 391/* 392 * TRUE if two fsids are equal. 393 */ 394#define FSIDS_EQUAL(fsid1, fsid2) \ 395 ((fsid1).val[0] == (fsid2).val[0] && \ 396 (fsid1).val[1] == (fsid2).val[1]) 397 398int 399mount_generic(char *special, char *fstype, char *opts, int nfsvers, 400 char *mntpnt, boolean_t isdirect, boolean_t usenetauth, fsid_t mntpnt_fsid, 401 uid_t sendereuid, au_asid_t asid, fsid_t *fsidp, 402 uint32_t *retflags) 403{ 404 struct stat stbuf; 405 int i, res; 406 char *newargv[ARGV_MAX]; 407 static struct mntopt mopts_soft[] = { 408 { "soft", 0, 1, 1 }, 409 { NULL, 0, 0, 0 } 410 }; 411 mntoptparse_t mp; 412 int flags, altflags; 413 char *opts_copy; 414 size_t mapped_opts_buflen; 415 char *mapped_opts = NULL; 416 char *p; 417 char *optp; 418 419 if (trace > 1) { 420 trace_prt(1, " mount: %s %s %s %s\n", special, mntpnt, fstype, opts); 421 } 422 423 /* 424 * XXX - if we do a stat() on the mount point of a direct 425 * mount, that'll trigger the mount, so do that only for 426 * an indirect mount. 427 * 428 * XXX - why bother doing it at all? Won't the program 429 * we run just fail if it doesn't exist? 430 */ 431 if (!isdirect && stat(mntpnt, &stbuf) < 0) { 432 syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt); 433 return (ENOENT); 434 } 435 436 i = 1; 437 438#if 0 439 /* 440 * Use "quiet" option to suppress warnings about unsupported 441 * mount options. 442 */ 443 newargv[i++] = "-q"; 444#endif 445 446 /* 447 * If this should go through the NetAuth agent, say so. 448 */ 449 if (usenetauth) 450 newargv[i++] = "-n"; 451 452 /* 453 * Flag it as not to show up as a "mounted volume" in the 454 * Finder/File Manager sense; we put this first so that 455 * it can be overridden if somebody wants it to show up. 456 */ 457 newargv[i++] = "-o"; 458 newargv[i++] = "nobrowse"; 459 460 if (strcmp(fstype, "nfs") == 0) { 461 /* 462 * Is "soft" set? 463 */ 464 flags = altflags = 0; 465 getmnt_silent = 1; 466 mp = getmntopts(opts, mopts_soft, &flags, &altflags); 467 if (mp == NULL) { 468 syslog(LOG_ERR, "Couldn't parse mount options \"%s\": %m", 469 opts); 470 return (ENOENT); 471 } 472 freemntopts(mp); 473 if (!altflags) { 474 /* 475 * The only bit in altflags is the bit for "soft", 476 * so if nothing is set, it's a hard mount. 477 * 478 * Therefore, we don't want to preemptively unmount 479 * it on a sleep or network change, because that 480 * unmount might hang forever, causing a more severe 481 * hang than if we just let it stay mounted and hang 482 * in a regular NFS operation, as, in the latter case, 483 * you can ^C out of it from the command line, and 484 * should get a "server not responding" dialog from 485 * the GUI, which, if you click the unmount button, 486 * will trigger a forced unmount, which will cause 487 * the hanging operations to time out and won't 488 * itself block. 489 */ 490 *retflags |= MOUNT_RETF_DONTPREUNMOUNT; 491 } 492 493 /* 494 * Add a "-t nfs" option, as we'll be running "mount" 495 * rather than "mount_nfs". 496 */ 497 newargv[i++] = "-t"; 498 newargv[i++] = "nfs"; 499 500 /* 501 * Turn down mount_nfs's aggressiveness about 502 * trying to mount. 503 */ 504 newargv[i++] = "-o"; 505 newargv[i++] = "retrycnt=0"; 506 507 /* 508 * Specify the NFS version, if a version was given. 509 */ 510 switch (nfsvers) { 511 512 case NFS_VER4: 513 newargv[i++] = "-o"; 514 newargv[i++] = "vers=4"; 515 break; 516 517 case NFS_VER3: 518 newargv[i++] = "-o"; 519 newargv[i++] = "vers=3"; 520 break; 521 522 case NFS_VER2: 523 newargv[i++] = "-o"; 524 newargv[i++] = "vers=2"; 525 break; 526 } 527 } 528 if (automountd_defopts != NULL) { 529 /* 530 * Add the default mount options. 531 * The options for this particular entry come later, 532 * so they can override the defaults. 533 */ 534 newargv[i++] = "-o"; 535 newargv[i++] = automountd_defopts; 536 } 537 if (opts && *opts) { 538 /* 539 * Map "findervol" to "browse", and "nofindervol" 540 * to "nobrowse". In autofs, we use "findervol" 541 * and "nofindervol" to control whether a mount 542 * should show up as a "mounted volume" in the 543 * Finder/File Manager sense, to avoid collisions 544 * with the autofs "browse"/"nobrowse" options. 545 * 546 * Remove any automounter-specific options that the 547 * mount command may warn about such as "hidefromfinder" 548 * or any commonly-encountered Solaris options. 549 * 550 * Scan a copy of the options, as strsep() will 551 * modify what it's passed. 552 */ 553 opts_copy = strdup(opts); 554 if (opts_copy == NULL) { 555 syslog(LOG_ERR, "Can't mount \"%s\" - out of memory", 556 special); 557 return (ENOMEM); 558 } 559 mapped_opts_buflen = strlen(opts_copy) + 1; 560 mapped_opts = malloc(mapped_opts_buflen); 561 *mapped_opts = '\0'; 562 opts = opts_copy; 563 while ((p = strsep(&opts, ",")) != NULL) { 564 565 /* 566 * Edit out any automounter specific options 567 * or commonly encountered but unsupported options 568 * that the mount command might warn about. 569 */ 570 if (strcmp(p, MNTOPT_HIDEFROMFINDER) == 0 || 571 strcmp(p, "grpid") == 0) 572 continue; 573 if (automountd_nosuid && strcmp(p, "suid") == 0) 574 continue; 575 /* 576 * Now handle mappings 577 */ 578 if (strcmp(p, "findervol") == 0) 579 optp = "browse"; 580 else if (strcmp(p, "nofindervol") == 0) 581 optp = "nobrowse"; 582 else 583 optp = p; 584 585 /* 586 * "findervol" is longer than "browse", and 587 * the target string is long enough for the 588 * options before we map "findervol" to 589 * "browse", so we know the options will fit. 590 */ 591 if (mapped_opts[0] != '\0') { 592 /* 593 * We already have mount options; add a 594 * comma before this one. 595 */ 596 strlcat(mapped_opts, ",", mapped_opts_buflen); 597 } 598 strlcat(mapped_opts, optp, mapped_opts_buflen); 599 } 600 free(opts_copy); 601 newargv[i++] = "-o"; 602 newargv[i++] = mapped_opts; 603 } 604 605 /* 606 * Make sure we flag it as automounted; we put this last so 607 * that it can't be overridden (the automounter is mounting 608 * it, so it is by definition automounted). 609 */ 610 newargv[i++] = "-o"; 611 newargv[i++] = "automounted"; 612 613 /* 614 * If forcing "nosuid" then append it too 615 */ 616 if (automountd_nosuid) { 617 newargv[i++] = "-o"; 618 newargv[i++] = "nosuid"; 619 } 620 621 /* 622 * XXX - not all our mount commands support "--" as an 623 * end-of-flags indication, so we just reject attempts 624 * to mount anything that begins with "-". 625 */ 626 if (special[0] == '-') { 627 syslog(LOG_ERR, 628 "Can't mount \"%s\", as its name begins with \"-\"\n", 629 special); 630 if (mapped_opts != NULL) 631 free(mapped_opts); 632 return (ENOENT); 633 } 634 newargv[i++] = special; 635 newargv[i++] = mntpnt; 636 newargv[i] = NULL; 637 638 res = fork_exec(fstype, newargv, sendereuid, asid); 639 if (res == 0) { 640 res = get_triggered_mount_info(mntpnt, mntpnt_fsid, 641 fsidp, retflags); 642 643 if (trace > 1) { 644 if (stat(mntpnt, &stbuf) == 0) { 645 trace_prt(1, " mount of %s dev=%x rdev=%x OK\n", 646 mntpnt, stbuf.st_dev, stbuf.st_rdev); 647 } else { 648 trace_prt(1, " failed to stat %s\n", mntpnt); 649 } 650 } 651 } 652 if (mapped_opts != NULL) 653 free(mapped_opts); 654 return (res); 655} 656 657int 658get_triggered_mount_info(const char *mntpnt, fsid_t mntpnt_fsid, 659 fsid_t *fsidp, uint32_t *retflags) 660{ 661 struct attrlist attrs; 662 struct attr_buffer attrbuf; 663 664 memset(&attrs, 0, sizeof (attrs)); 665 attrs.bitmapcount = ATTR_BIT_MAP_COUNT; 666 attrs.commonattr = ATTR_CMN_FSID; 667 attrs.volattr = ATTR_VOL_INFO|ATTR_VOL_MOUNTFLAGS|ATTR_VOL_CAPABILITIES; 668 if (getattrlist(mntpnt, &attrs, &attrbuf, sizeof (attrbuf), 0) == -1) { 669 /* Failed. */ 670 return errno; 671 } 672 if (FSIDS_EQUAL(mntpnt_fsid, attrbuf.fsid)) { 673 /* 674 * OK, the file system there has the same fsid 675 * as the file system containing the mount 676 * point, so there's nothing mounted there. 677 * This presumably means somebody unmounted it 678 * out from under us; return EAGAIN to force 679 * the mount to be re-triggered. 680 */ 681 return EAGAIN; 682 } 683 684 /* 685 * Get the FSID of what's presumably the 686 * newly-mounted file system. 687 */ 688 *fsidp = attrbuf.fsid; 689 690 /* 691 * If this is a VolFS file system, we don't want to unmount it 692 * ourselves, as, if somebody wanted to reopen a file from it, 693 * using /.vol, that would fail, as the /.vol path would use the 694 * fsid the file system had then, and if we unmount it, that fsid 695 * would no longer be valid and the /.vol lookup would fail - even 696 * though this was a triggered mount; the /.vol lookup would not 697 * trigger a remount. 698 * 699 * If either the VOL_CAP_FMT_PATH_FROM_ID capability is present 700 * or the MNT_DOVOLFS flag is set, it's a VolFS file system. 701 */ 702 if ((attrbuf.capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_PATH_FROM_ID) && 703 (attrbuf.capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_PATH_FROM_ID)) 704 *retflags |= MOUNT_RETF_DONTUNMOUNT; 705 else if (attrbuf.mountflags & MNT_DOVOLFS) 706 *retflags |= MOUNT_RETF_DONTUNMOUNT; 707 return 0; 708} 709 710/* 711 * Given an audit session id, Try and join that session 712 * Return 0 on success else -1 713 */ 714int 715join_session(au_asid_t asid) 716{ 717 int err; 718 au_asid_t asid2; 719 mach_port_name_t session_port; 720 721 err = audit_session_port(asid, &session_port); 722 if (err) { 723 syslog(LOG_ERR, "Could not get audit session port %d for %m", asid); 724 return (-1); 725 } 726 asid2 = audit_session_join(session_port); 727 (void) mach_port_deallocate(current_task(), session_port); 728 729 if (asid2 < 0) { 730 syslog(LOG_ERR, "Could not join audit session %d", asid); 731 return (-1); 732 } 733 734 if (asid2 != asid) { 735 syslog(LOG_ERR, "Joined session %d but wound up in session %d", asid, asid2); 736 return (-1); 737 } 738 return (0); 739} 740#define CFENVFORMATSTRING "0x%X:0:0" 741 742static int 743fork_exec(char *fstype, char **newargv, uid_t sendereuid, au_asid_t asid) 744{ 745 char *path; 746 volatile int path_is_allocated; 747 struct stat stbuf; 748 int i; 749 char CFUserTextEncodingEnvSetting[sizeof(CFENVFORMATSTRING) + 20]; /* Extra bytes for expansion of %X uid field */ 750 int child_pid; 751 int stat_loc; 752 int res; 753 754 /* 755 * Build the full path name of the fstype dependent command. 756 * If fstype is "url", however, we run our mount_url helper, and, 757 * if it's "nfs", we just run "mount" with a "-t nfs" option, so 758 * that mount options in the form of flags (i.e., "-s" instead of 759 * "soft") work. 760 */ 761 if (strcmp(fstype, "url") == 0) { 762 path = MOUNT_URL_PATH; 763 path_is_allocated = 0; 764 } else if (strcmp(fstype, "nfs") == 0) { 765 path = MOUNT_PATH; 766 path_is_allocated = 0; 767 } else { 768 if (strcmp(fstype, "smb") == 0) 769 fstype = "smbfs"; /* mount_smbfs, not mount_smb */ 770 if (asprintf(&path, "%s/mount_%s", VFS_PATH, fstype) == -1) { 771 res = errno; 772 syslog(LOG_ERR, "Can't construct pathname of mount program: %m"); 773 return (res); 774 } 775 path_is_allocated = 1; 776 } 777 778 if (stat(path, &stbuf) != 0) { 779 res = errno; 780 syslog(LOG_ERR, "Can't stat mount program %s: %m", path); 781 goto done; 782 } 783 784 if (trace > 1) { 785 char *bufp; 786 int c = 0; 787 788 for (i = 1; newargv[i]; i++) // sum arg length + space 789 c += strlen(newargv[i]) + 1; 790 bufp = malloc(c + 1); 791 if (bufp) { 792 char *p = bufp; 793 for (i = 1; newargv[i]; i++) { 794 c -= snprintf(p, c, "%s ", newargv[i]); 795 p += strlen(newargv[i]) + 1; 796 } 797 trace_prt(1, " fork_exec: %s %s\n", path, bufp); 798 free(bufp); 799 } 800 } 801 802 newargv[0] = path; 803 804 switch ((child_pid = fork1())) { 805 case -1: 806 /* 807 * Fork failure. Log an error, and quit. 808 */ 809 res = errno; 810 syslog(LOG_ERR, "Cannot fork: %m"); 811 goto done; 812 case 0: 813 /* 814 * Child. 815 * 816 * We need to join the right audit session 817 */ 818 if (join_session(asid)) 819 _exit(EPERM); 820 821 /* 822 * We leave most of our environment as it is; we assume 823 * that launchd has made the right thing happen for us, 824 * and that this is also the right thing for the processes 825 * we run. 826 * 827 * One change we make is that we do the mount as the user 828 * who triggered the mount, so that if it's a file system 829 * such as AFP or SMB where a mount has a single session 830 * and user identity associated with it, the right user 831 * identity gets associated with it, and if it's a file 832 * system such as NFS that doesn't, but that might require 833 * user credentials, the UID matches the credentials that 834 * the GSSD it talks to has. 835 */ 836 if (setuid(sendereuid) == -1) { 837 res = errno; 838 syslog(LOG_ERR, "Can't set mount subprocess UID: %s", 839 strerror(res)); 840 _exit(res); 841 } 842 843 /* 844 * Create a new environment with a definition of 845 * __CF_USER_TEXT_ENCODING to work around CF's interest 846 * in the user's home directory. We're a child of the 847 * automountd process, so those references won't deadlock 848 * us by blocking waiting for the very mount that we're 849 * trying to do, but it still means that CF makes a 850 * pointless attempt to find the user's home directory. 851 * The program we run to do the mount, if linked with 852 * CF - as mount_url is - will do that. 853 * 854 * Make sure we include the UID since CF will check for 855 * this when deciding whether to look in the home directory. 856 */ 857 snprintf(CFUserTextEncodingEnvSetting, 858 sizeof(CFUserTextEncodingEnvSetting), CFENVFORMATSTRING, 859 getuid()); 860 setenv("__CF_USER_TEXT_ENCODING", CFUserTextEncodingEnvSetting, 861 1); 862 (void) execv(path, newargv); 863 res = errno; 864 syslog(LOG_ERR, "exec %s: %m", path); 865 _exit(res); 866 default: 867 /* 868 * Parent. 869 * 870 * Now wait for the child to finish. 871 */ 872 (void) waitpid(child_pid, &stat_loc, WUNTRACED); 873 874 if (WIFEXITED(stat_loc)) { 875 if (trace > 1) { 876 trace_prt(1, 877 " fork_exec: returns exit status %d\n", 878 WEXITSTATUS(stat_loc)); 879 } 880 881 res = WEXITSTATUS(stat_loc); 882 } else if (WIFSIGNALED(stat_loc)) { 883 syslog(LOG_ERR, "Mount subprocess terminated with %s", 884 strsignal(WTERMSIG(stat_loc))); 885 if (trace > 1) { 886 trace_prt(1, 887 " fork_exec: returns signal status %d\n", 888 WTERMSIG(stat_loc)); 889 } 890 res = EIO; 891 } else if (WIFSTOPPED(stat_loc)) { 892 syslog(LOG_ERR, "Mount subprocess stopped with %s", 893 strsignal(WSTOPSIG(stat_loc))); 894 res = EIO; 895 } else { 896 syslog(LOG_ERR, "Mount subprocess got unknown status 0x%08x", 897 stat_loc); 898 if (trace > 1) 899 trace_prt(1, 900 " fork_exec: returns unknown status\n"); 901 res = EIO; 902 } 903 } 904 905done: 906 if (path_is_allocated) 907 free(path); 908 return (res); 909} 910 911static const struct mntopt mopts_nfs[] = { 912 MOPT_NFS 913}; 914 915int 916do_unmount1(fsid_t mntpnt_fsid, autofs_pathname mntresource, 917 autofs_pathname mntpnt, autofs_component fstype, autofs_opts mntopts) 918{ 919 920 struct mnttab m; 921 int res = 0; 922 mntoptparse_t mp; 923 int flags; 924 int altflags; 925 926 m.mnt_special = mntresource; 927 m.mnt_mountp = mntpnt; 928 m.mnt_fstype = fstype; 929 m.mnt_mntopts = mntopts; 930 /* 931 * Special case for NFS mounts. 932 * Don't want to attempt unmounts from 933 * a dead server. If any member of a 934 * hierarchy belongs to a dead server 935 * give up (try later). 936 */ 937 if (strcmp(fstype, MNTTYPE_NFS) == 0) { 938 struct replica *list; 939 int i, n; 940 long nfs_port; 941 942 /* 943 * See if a port number was specified. If one was 944 * specified that is too large to fit in 16 bits, truncate 945 * the high-order bits (for historical compatibility). Use 946 * zero to indicate "no port specified". 947 */ 948 flags = altflags = 0; 949 getmnt_silent = 1; 950 mp = getmntopts(mntopts, mopts_nfs, &flags, &altflags); 951 if (mp != NULL) { 952 if (altflags & NFS_MNT_PORT) { 953 nfs_port = getmntoptnum(mp, "port"); 954 if (nfs_port != -1) 955 nfs_port &= USHRT_MAX; 956 else { 957 syslog(LOG_ERR, "Couldn't parse port= option in \"%s\": %m", 958 mntopts); 959 nfs_port = 0; /* error */ 960 } 961 } else 962 nfs_port = 0; /* option not present */ 963 freemntopts(mp); 964 } else { 965 syslog(LOG_ERR, "Couldn't parse mount options \"%s\": %m", 966 mntopts); 967 nfs_port = 0; 968 } 969 970 list = parse_replica(mntresource, &n); 971 if (list == NULL) { 972 if (n >= 0) 973 syslog(LOG_ERR, "Memory allocation failed: %m"); 974 res = 1; 975 goto done; 976 } 977 978 for (i = 0; i < n; i++) { 979 if (pingnfs(list[i].host, NULL, 0, nfs_port, 980 list[i].path, NULL) != RPC_SUCCESS) { 981 res = 1; 982 free_replica(list, n); 983 goto done; 984 } 985 } 986 free_replica(list, n); 987 } 988 989 res = unmount_mntpnt(mntpnt_fsid, &m); 990 991done: return (res); 992} 993 994static int 995unmount_mntpnt(fsid_t mntpnt_fsid, struct mnttab *mnt) 996{ 997 int res = 0; 998 999 if (umount_by_fsid(&mntpnt_fsid, 0) < 0) 1000 res = errno; 1001 1002 if (trace > 1) 1003 trace_prt(1, " unmount %s %s\n", 1004 mnt->mnt_mountp, res ? "failed" : "OK"); 1005 return (res); 1006} 1007 1008/* 1009 * Remove the autofs specific options 'browse', 'nobrowse' and 1010 * 'restrict' from 'opts'. 1011 */ 1012static void 1013remove_browse_options(char *opts) 1014{ 1015 char *p, *pb; 1016 char buf[MAXOPTSLEN], new[MAXOPTSLEN]; 1017 char *placeholder; 1018 1019 new[0] = '\0'; 1020 CHECK_STRCPY(buf, opts, sizeof buf); 1021 pb = buf; 1022 1023 while ((p = (char *)strtok_r(pb, ",", &placeholder)) != NULL) { 1024 pb = NULL; 1025 if (strcmp(p, "nobrowse") != 0 && 1026 strcmp(p, "browse") != 0 && 1027 strcmp(p, MNTOPT_RESTRICT) != 0) { 1028 if (new[0] != '\0') 1029 (void) strlcat(new, ",", sizeof(new)); 1030 (void) strlcat(new, p, sizeof(new)); 1031 } 1032 } 1033 1034 (void) strlcpy(opts, new, AUTOFS_MAXOPTSLEN); 1035} 1036 1037/* 1038 * A "struct mntopt" table is terminated with an entry with a null 1039 * m_option pointer; therefore, the number of real entries in the 1040 * table is one fewer than the total number of entries. 1041 */ 1042static const struct mntopt mopts_restrict[] = { 1043 RESTRICTED_MNTOPTS 1044}; 1045#define NROPTS ((sizeof (mopts_restrict)/sizeof (mopts_restrict[0])) - 1) 1046 1047static int 1048inherit_options(const char *opts, char **mapentopts) 1049{ 1050 u_int i; 1051 char *new; 1052 mntoptparse_t mtmap; 1053 int mtmapflags, mtmapaltflags; 1054 mntoptparse_t mtopt; 1055 int mtoptflags, mtoptaltflags; 1056 bool_t addopt; 1057 size_t len; 1058 1059 len = strlen(*mapentopts); 1060 1061 /* 1062 * Compute the maximum amount of space needed to add all of the 1063 * restricted options. 1064 */ 1065 for (i = 0; i < NROPTS; i++) { 1066 /* 1067 * Count the space for the option name. 1068 */ 1069 len += strlen(mopts_restrict[i].m_option); 1070 1071 /* 1072 * If this is a negative option, and the option should be 1073 * set, the name will be preceded with "no". 1074 */ 1075 if (mopts_restrict[i].m_inverse) 1076 len += 2; 1077 } 1078 1079 /* "," for each new option plus the trailing NUL */ 1080 len += NROPTS + 1; 1081 1082 new = malloc(len); 1083 if (new == 0) 1084 return (-1); 1085 1086 CHECK_STRCPY(new, *mapentopts, len); 1087 1088 mtmapflags = mtmapaltflags = 0; 1089 getmnt_silent = 1; 1090 mtmap = getmntopts(*mapentopts, mopts_restrict, &mtmapflags, &mtmapaltflags); 1091 if (mtmap == NULL) { 1092 syslog(LOG_ERR, "Couldn't parse mount options \"%s\": %m", 1093 opts); 1094 return (-1); 1095 } 1096 freemntopts(mtmap); 1097 1098 mtoptflags = mtoptaltflags = 0; 1099 getmnt_silent = 1; 1100 mtopt = getmntopts(opts, mopts_restrict, &mtoptflags, &mtoptaltflags); 1101 if (mtopt == NULL) { 1102 syslog(LOG_ERR, "Couldn't parse mount options \"%s\": %m", 1103 opts); 1104 return (-1); 1105 } 1106 freemntopts(mtopt); 1107 1108 for (i = 0; i < NROPTS; i++) { 1109 if (mopts_restrict[i].m_altloc) { 1110 addopt = ((mtoptaltflags & mopts_restrict[i].m_flag) && 1111 !(mtmapaltflags & mopts_restrict[i].m_flag)); 1112 } else { 1113 addopt = ((mtoptflags & mopts_restrict[i].m_flag) && 1114 !(mtmapflags & mopts_restrict[i].m_flag)); 1115 } 1116 if (addopt) { 1117 if (*new != '\0') 1118 CHECK_STRCAT(new, ",", len); 1119 if (mopts_restrict[i].m_inverse) 1120 CHECK_STRCAT(new, "no", len); 1121 CHECK_STRCAT(new, mopts_restrict[i].m_option, len); 1122 } 1123 } 1124 free(*mapentopts); 1125 *mapentopts = new; 1126 return (0); 1127} 1128 1129bool_t 1130hasrestrictopt(const char *opts) 1131{ 1132 mntoptparse_t mp; 1133 int flags, altflags; 1134 1135 flags = altflags = 0; 1136 getmnt_silent = 1; 1137 mp = getmntopts(opts, mopts_restrict, &flags, &altflags); 1138 if (mp == NULL) 1139 return (FALSE); 1140 freemntopts(mp); 1141 return ((altflags & AUTOFS_MNT_RESTRICT) != 0); 1142} 1143