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