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 = fork())) { 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 while (waitpid(child_pid, &stat_loc, WUNTRACED) < 0) { 873 if ((res = errno) == EINTR) 874 continue; 875 syslog(LOG_ERR, "waitpid %d failed - error %d", child_pid, errno); 876 goto done; 877 } 878 879 if (WIFEXITED(stat_loc)) { 880 if (trace > 1) { 881 trace_prt(1, 882 " fork_exec: returns exit status %d\n", 883 WEXITSTATUS(stat_loc)); 884 } 885 886 res = WEXITSTATUS(stat_loc); 887 } else if (WIFSIGNALED(stat_loc)) { 888 syslog(LOG_ERR, "Mount subprocess terminated with %s", 889 strsignal(WTERMSIG(stat_loc))); 890 if (trace > 1) { 891 trace_prt(1, 892 " fork_exec: returns signal status %d\n", 893 WTERMSIG(stat_loc)); 894 } 895 res = EIO; 896 } else if (WIFSTOPPED(stat_loc)) { 897 syslog(LOG_ERR, "Mount subprocess stopped with %s", 898 strsignal(WSTOPSIG(stat_loc))); 899 res = EIO; 900 } else { 901 syslog(LOG_ERR, "Mount subprocess got unknown status 0x%08x", 902 stat_loc); 903 if (trace > 1) 904 trace_prt(1, 905 " fork_exec: returns unknown status\n"); 906 res = EIO; 907 } 908 } 909 910done: 911 if (path_is_allocated) 912 free(path); 913 return (res); 914} 915 916static const struct mntopt mopts_nfs[] = { 917 MOPT_NFS 918}; 919 920int 921do_unmount1(fsid_t mntpnt_fsid, autofs_pathname mntresource, 922 autofs_pathname mntpnt, autofs_component fstype, autofs_opts mntopts) 923{ 924 925 struct mnttab m; 926 int res = 0; 927 mntoptparse_t mp; 928 int flags; 929 int altflags; 930 931 m.mnt_special = mntresource; 932 m.mnt_mountp = mntpnt; 933 m.mnt_fstype = fstype; 934 m.mnt_mntopts = mntopts; 935 /* 936 * Special case for NFS mounts. 937 * Don't want to attempt unmounts from 938 * a dead server. If any member of a 939 * hierarchy belongs to a dead server 940 * give up (try later). 941 */ 942 if (strcmp(fstype, MNTTYPE_NFS) == 0) { 943 struct replica *list; 944 int i, n; 945 long nfs_port; 946 947 /* 948 * See if a port number was specified. If one was 949 * specified that is too large to fit in 16 bits, truncate 950 * the high-order bits (for historical compatibility). Use 951 * zero to indicate "no port specified". 952 */ 953 flags = altflags = 0; 954 getmnt_silent = 1; 955 mp = getmntopts(mntopts, mopts_nfs, &flags, &altflags); 956 if (mp != NULL) { 957 if (altflags & NFS_MNT_PORT) { 958 nfs_port = getmntoptnum(mp, "port"); 959 if (nfs_port != -1) 960 nfs_port &= USHRT_MAX; 961 else { 962 syslog(LOG_ERR, "Couldn't parse port= option in \"%s\": %m", 963 mntopts); 964 nfs_port = 0; /* error */ 965 } 966 } else 967 nfs_port = 0; /* option not present */ 968 freemntopts(mp); 969 } else { 970 syslog(LOG_ERR, "Couldn't parse mount options \"%s\": %m", 971 mntopts); 972 nfs_port = 0; 973 } 974 975 list = parse_replica(mntresource, &n); 976 if (list == NULL) { 977 if (n >= 0) 978 syslog(LOG_ERR, "Memory allocation failed: %m"); 979 res = 1; 980 goto done; 981 } 982 983 for (i = 0; i < n; i++) { 984 if (pingnfs(list[i].host, NULL, 0, nfs_port, 985 list[i].path, NULL) != RPC_SUCCESS) { 986 res = 1; 987 free_replica(list, n); 988 goto done; 989 } 990 } 991 free_replica(list, n); 992 } 993 994 res = unmount_mntpnt(mntpnt_fsid, &m); 995 996done: return (res); 997} 998 999static int 1000unmount_mntpnt(fsid_t mntpnt_fsid, struct mnttab *mnt) 1001{ 1002 int res = 0; 1003 1004 if (umount_by_fsid(&mntpnt_fsid, 0) < 0) 1005 res = errno; 1006 1007 if (trace > 1) 1008 trace_prt(1, " unmount %s %s\n", 1009 mnt->mnt_mountp, res ? "failed" : "OK"); 1010 return (res); 1011} 1012 1013/* 1014 * Remove the autofs specific options 'browse', 'nobrowse' and 1015 * 'restrict' from 'opts'. 1016 */ 1017static void 1018remove_browse_options(char *opts) 1019{ 1020 char *p, *pb; 1021 char buf[MAXOPTSLEN], new[MAXOPTSLEN]; 1022 char *placeholder; 1023 1024 new[0] = '\0'; 1025 CHECK_STRCPY(buf, opts, sizeof buf); 1026 pb = buf; 1027 1028 while ((p = (char *)strtok_r(pb, ",", &placeholder)) != NULL) { 1029 pb = NULL; 1030 if (strcmp(p, "nobrowse") != 0 && 1031 strcmp(p, "browse") != 0 && 1032 strcmp(p, MNTOPT_RESTRICT) != 0) { 1033 if (new[0] != '\0') 1034 (void) strlcat(new, ",", sizeof(new)); 1035 (void) strlcat(new, p, sizeof(new)); 1036 } 1037 } 1038 1039 (void) strlcpy(opts, new, AUTOFS_MAXOPTSLEN); 1040} 1041 1042/* 1043 * A "struct mntopt" table is terminated with an entry with a null 1044 * m_option pointer; therefore, the number of real entries in the 1045 * table is one fewer than the total number of entries. 1046 */ 1047static const struct mntopt mopts_restrict[] = { 1048 RESTRICTED_MNTOPTS 1049}; 1050#define NROPTS ((sizeof (mopts_restrict)/sizeof (mopts_restrict[0])) - 1) 1051 1052static int 1053inherit_options(const char *opts, char **mapentopts) 1054{ 1055 u_int i; 1056 char *new = NULL; 1057 mntoptparse_t mtmap; 1058 int mtmapflags, mtmapaltflags; 1059 mntoptparse_t mtopt; 1060 int mtoptflags, mtoptaltflags; 1061 bool_t addopt; 1062 size_t len; 1063 int error = 0; 1064 1065 len = strlen(*mapentopts); 1066 1067 /* 1068 * Compute the maximum amount of space needed to add all of the 1069 * restricted options. 1070 */ 1071 for (i = 0; i < NROPTS; i++) { 1072 /* 1073 * Count the space for the option name. 1074 */ 1075 len += strlen(mopts_restrict[i].m_option); 1076 1077 /* 1078 * If this is a negative option, and the option should be 1079 * set, the name will be preceded with "no". 1080 */ 1081 if (mopts_restrict[i].m_inverse) 1082 len += 2; 1083 } 1084 1085 /* "," for each new option plus the trailing NUL */ 1086 len += NROPTS + 1; 1087 1088 new = malloc(len); 1089 if (new == 0) 1090 return (-1); 1091 1092 if ((error = CHECK_STRCPY(new, *mapentopts, len))) { 1093 goto DONE; 1094 } 1095 1096 mtmapflags = mtmapaltflags = 0; 1097 getmnt_silent = 1; 1098 mtmap = getmntopts(*mapentopts, mopts_restrict, &mtmapflags, &mtmapaltflags); 1099 if (mtmap == NULL) { 1100 syslog(LOG_ERR, "Couldn't parse mount options \"%s\": %m", 1101 opts); 1102 return (-1); 1103 } 1104 freemntopts(mtmap); 1105 1106 mtoptflags = mtoptaltflags = 0; 1107 getmnt_silent = 1; 1108 mtopt = getmntopts(opts, mopts_restrict, &mtoptflags, &mtoptaltflags); 1109 if (mtopt == NULL) { 1110 syslog(LOG_ERR, "Couldn't parse mount options \"%s\": %m", 1111 opts); 1112 return (-1); 1113 } 1114 freemntopts(mtopt); 1115 1116 for (i = 0; i < NROPTS; i++) { 1117 if (mopts_restrict[i].m_altloc) { 1118 addopt = ((mtoptaltflags & mopts_restrict[i].m_flag) && 1119 !(mtmapaltflags & mopts_restrict[i].m_flag)); 1120 } else { 1121 addopt = ((mtoptflags & mopts_restrict[i].m_flag) && 1122 !(mtmapflags & mopts_restrict[i].m_flag)); 1123 } 1124 if (addopt) { 1125 if (*new != '\0') { 1126 if ((error = CHECK_STRCAT(new, ",", len))) { 1127 goto DONE; 1128 } 1129 } 1130 if (mopts_restrict[i].m_inverse) { 1131 if ((error = CHECK_STRCAT(new, "no", len))) { 1132 goto DONE; 1133 } 1134 } 1135 error = CHECK_STRCAT(new, mopts_restrict[i].m_option, len); 1136 } 1137 } 1138DONE: 1139 free(*mapentopts); 1140 if (error) { 1141 if (new) { 1142 free(new); 1143 } 1144 new = NULL; 1145 } 1146 *mapentopts = new; 1147 1148 return error; 1149} 1150 1151bool_t 1152hasrestrictopt(const char *opts) 1153{ 1154 mntoptparse_t mp; 1155 int flags, altflags; 1156 1157 flags = altflags = 0; 1158 getmnt_silent = 1; 1159 mp = getmntopts(opts, mopts_restrict, &flags, &altflags); 1160 if (mp == NULL) 1161 return (FALSE); 1162 freemntopts(mp); 1163 return ((altflags & AUTOFS_MNT_RESTRICT) != 0); 1164} 1165