xref: /onnv-gate/usr/src/uts/common/os/tlabel.c (revision 1676:37f4a3e2bd99)
1*1676Sjpk /*
2*1676Sjpk  * CDDL HEADER START
3*1676Sjpk  *
4*1676Sjpk  * The contents of this file are subject to the terms of the
5*1676Sjpk  * Common Development and Distribution License (the "License").
6*1676Sjpk  * You may not use this file except in compliance with the License.
7*1676Sjpk  *
8*1676Sjpk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1676Sjpk  * or http://www.opensolaris.org/os/licensing.
10*1676Sjpk  * See the License for the specific language governing permissions
11*1676Sjpk  * and limitations under the License.
12*1676Sjpk  *
13*1676Sjpk  * When distributing Covered Code, include this CDDL HEADER in each
14*1676Sjpk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1676Sjpk  * If applicable, add the following below this CDDL HEADER, with the
16*1676Sjpk  * fields enclosed by brackets "[]" replaced with your own identifying
17*1676Sjpk  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1676Sjpk  *
19*1676Sjpk  * CDDL HEADER END
20*1676Sjpk  */
21*1676Sjpk /*
22*1676Sjpk  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*1676Sjpk  * Use is subject to license terms.
24*1676Sjpk  */
25*1676Sjpk 
26*1676Sjpk #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*1676Sjpk 
28*1676Sjpk #include <sys/types.h>
29*1676Sjpk #include <sys/param.h>
30*1676Sjpk #include <sys/cmn_err.h>
31*1676Sjpk #include <sys/systm.h>
32*1676Sjpk #include <sys/cred.h>
33*1676Sjpk #include <sys/modctl.h>
34*1676Sjpk #include <sys/vfs.h>
35*1676Sjpk #include <sys/vnode.h>
36*1676Sjpk #include <sys/tiuser.h>
37*1676Sjpk #include <sys/kmem.h>
38*1676Sjpk #include <sys/pathname.h>
39*1676Sjpk #include <sys/zone.h>
40*1676Sjpk #include <sys/tsol/label.h>
41*1676Sjpk #include <sys/tsol/tnet.h>
42*1676Sjpk #include <sys/fs/lofs_node.h>
43*1676Sjpk #include <inet/ip6.h>
44*1676Sjpk #include <rpc/auth.h>
45*1676Sjpk #include <rpc/clnt.h>
46*1676Sjpk #include <nfs/nfs.h>
47*1676Sjpk #include <nfs/nfs4.h>
48*1676Sjpk #include <nfs/nfs_clnt.h>
49*1676Sjpk #include <sys/dnlc.h>
50*1676Sjpk 
51*1676Sjpk 
52*1676Sjpk int sys_labeling = -1;			/* initially unset */
53*1676Sjpk 
54*1676Sjpk static kmem_cache_t *tslabel_cache;
55*1676Sjpk ts_label_t *l_admin_low;
56*1676Sjpk ts_label_t *l_admin_high;
57*1676Sjpk 
58*1676Sjpk uint32_t default_doi = DEFAULT_DOI;
59*1676Sjpk 
60*1676Sjpk /*
61*1676Sjpk  * Initialize labels infrastructure.
62*1676Sjpk  * This is called during startup() time (before vfs_mntroot) by thread_init().
63*1676Sjpk  * It has to be called early so that the is_system_labeled() function returns
64*1676Sjpk  * the right value when called by the networking code on a diskless boot.
65*1676Sjpk  */
66*1676Sjpk void
67*1676Sjpk label_init(void)
68*1676Sjpk {
69*1676Sjpk 	bslabel_t label;
70*1676Sjpk 
71*1676Sjpk 	/*
72*1676Sjpk 	 * Use the value of "label_services" within the edition module.
73*1676Sjpk 	 * If for some reason label_services is not found, this will
74*1676Sjpk 	 * result in the appropriate default -- "off."
75*1676Sjpk 	 */
76*1676Sjpk 	if (modgetsymvalue("label_services", B_FALSE) != 0)
77*1676Sjpk 		sys_labeling = 1;
78*1676Sjpk 	else
79*1676Sjpk 		sys_labeling = 0;
80*1676Sjpk 
81*1676Sjpk 	tslabel_cache = kmem_cache_create("tslabel_cache", sizeof (ts_label_t),
82*1676Sjpk 	    0, NULL, NULL, NULL, NULL, NULL, 0);
83*1676Sjpk 	bsllow(&label);
84*1676Sjpk 	l_admin_low = labelalloc(&label, default_doi, KM_SLEEP);
85*1676Sjpk 	bslhigh(&label);
86*1676Sjpk 	l_admin_high = labelalloc(&label, default_doi, KM_SLEEP);
87*1676Sjpk }
88*1676Sjpk 
89*1676Sjpk /*
90*1676Sjpk  * Allocate new ts_label_t.
91*1676Sjpk  */
92*1676Sjpk ts_label_t *
93*1676Sjpk labelalloc(const bslabel_t *val, uint32_t doi, int flag)
94*1676Sjpk {
95*1676Sjpk 	ts_label_t *lab = kmem_cache_alloc(tslabel_cache, flag);
96*1676Sjpk 
97*1676Sjpk 	if (lab != NULL) {
98*1676Sjpk 		lab->tsl_ref = 1;
99*1676Sjpk 		lab->tsl_doi = doi;
100*1676Sjpk 		if (val == NULL)
101*1676Sjpk 			bzero(&lab->tsl_label, sizeof (bslabel_t));
102*1676Sjpk 		else
103*1676Sjpk 			bcopy(val, &lab->tsl_label,  sizeof (bslabel_t));
104*1676Sjpk 	}
105*1676Sjpk 	return (lab);
106*1676Sjpk }
107*1676Sjpk 
108*1676Sjpk /*
109*1676Sjpk  * Put a hold on a label structure.
110*1676Sjpk  */
111*1676Sjpk void
112*1676Sjpk label_hold(ts_label_t *lab)
113*1676Sjpk {
114*1676Sjpk 	atomic_add_32(&lab->tsl_ref, 1);
115*1676Sjpk }
116*1676Sjpk 
117*1676Sjpk /*
118*1676Sjpk  * Release previous hold on a label structure.  Free it if refcnt == 0.
119*1676Sjpk  */
120*1676Sjpk void
121*1676Sjpk label_rele(ts_label_t *lab)
122*1676Sjpk {
123*1676Sjpk 	if (atomic_add_32_nv(&lab->tsl_ref, -1) == 0)
124*1676Sjpk 		kmem_cache_free(tslabel_cache, lab);
125*1676Sjpk }
126*1676Sjpk 
127*1676Sjpk bslabel_t *
128*1676Sjpk label2bslabel(ts_label_t *lab)
129*1676Sjpk {
130*1676Sjpk 	return (&lab->tsl_label);
131*1676Sjpk }
132*1676Sjpk 
133*1676Sjpk 
134*1676Sjpk uint32_t
135*1676Sjpk label2doi(ts_label_t *lab)
136*1676Sjpk {
137*1676Sjpk 	return (lab->tsl_doi);
138*1676Sjpk }
139*1676Sjpk 
140*1676Sjpk /*
141*1676Sjpk  * Compare labels. Return 1 if equal, 0 otherwise.
142*1676Sjpk  */
143*1676Sjpk boolean_t
144*1676Sjpk label_equal(const ts_label_t *l1, const ts_label_t *l2)
145*1676Sjpk {
146*1676Sjpk 	return ((l1->tsl_doi == l2->tsl_doi) &&
147*1676Sjpk 	    blequal(&l1->tsl_label, &l2->tsl_label));
148*1676Sjpk }
149*1676Sjpk 
150*1676Sjpk /*
151*1676Sjpk  * There's no protocol today to obtain the label from the server.
152*1676Sjpk  * So we rely on conventions: zones, zone names, and zone paths
153*1676Sjpk  * must match across TX servers and their TX clients.  Now use
154*1676Sjpk  * the exported name to find the equivalent local zone and its
155*1676Sjpk  * label.  Caller is responsible for doing a label_rele of the
156*1676Sjpk  * returned ts_label.
157*1676Sjpk  */
158*1676Sjpk ts_label_t *
159*1676Sjpk getflabel_cipso(vfs_t *vfsp)
160*1676Sjpk {
161*1676Sjpk 	zone_t	*reszone;
162*1676Sjpk 	zone_t	*new_reszone;
163*1676Sjpk 	char	*nfspath, *respath;
164*1676Sjpk 	refstr_t	*resource_ref;
165*1676Sjpk 	boolean_t	treat_abs = B_FALSE;
166*1676Sjpk 
167*1676Sjpk 	if (vfsp->vfs_resource == NULL)
168*1676Sjpk 		return (NULL);			/* error */
169*1676Sjpk 	resource_ref = vfs_getresource(vfsp);
170*1676Sjpk 
171*1676Sjpk 	nfspath = (char *)refstr_value(resource_ref);
172*1676Sjpk 	respath = strchr(nfspath, ':');		/* skip server name */
173*1676Sjpk 	if (respath)
174*1676Sjpk 		respath++;			/* skip over ":" */
175*1676Sjpk 	if (*respath != '/') {
176*1676Sjpk 		/* treat path as absolute but it doesn't have leading '/' */
177*1676Sjpk 		treat_abs = B_TRUE;
178*1676Sjpk 	}
179*1676Sjpk 
180*1676Sjpk 	reszone = zone_find_by_any_path(respath, treat_abs);
181*1676Sjpk 	if (reszone == global_zone) {
182*1676Sjpk 		refstr_rele(resource_ref);
183*1676Sjpk 		label_hold(l_admin_low);
184*1676Sjpk 		zone_rele(reszone);
185*1676Sjpk 		return (l_admin_low);
186*1676Sjpk 	}
187*1676Sjpk 
188*1676Sjpk 	/*
189*1676Sjpk 	 * Skip over zonepath (not including "root"), e.g. /zone/internal
190*1676Sjpk 	 */
191*1676Sjpk 	respath += reszone->zone_rootpathlen - 7;
192*1676Sjpk 	if (treat_abs)
193*1676Sjpk 		respath--;			/* no leading '/' to skip */
194*1676Sjpk 	if (strncmp(respath, "/root/", 6) == 0) {
195*1676Sjpk 		/* Check if we now have something like "/zone/public/" */
196*1676Sjpk 
197*1676Sjpk 		respath += 5;			/* skip "/root" first */
198*1676Sjpk 		new_reszone = zone_find_by_any_path(respath, B_FALSE);
199*1676Sjpk 		if (new_reszone != global_zone) {
200*1676Sjpk 			zone_rele(reszone);
201*1676Sjpk 			reszone = new_reszone;
202*1676Sjpk 		} else {
203*1676Sjpk 			zone_rele(new_reszone);
204*1676Sjpk 		}
205*1676Sjpk 	}
206*1676Sjpk 
207*1676Sjpk 	refstr_rele(resource_ref);
208*1676Sjpk 	label_hold(reszone->zone_slabel);
209*1676Sjpk 	zone_rele(reszone);
210*1676Sjpk 
211*1676Sjpk 	return (reszone->zone_slabel);
212*1676Sjpk }
213*1676Sjpk 
214*1676Sjpk static ts_label_t *
215*1676Sjpk getflabel_nfs(vfs_t *vfsp)
216*1676Sjpk {
217*1676Sjpk 	bslabel_t	*server_sl;
218*1676Sjpk 	ts_label_t	*srv_label;
219*1676Sjpk 	tsol_tpc_t	*tp;
220*1676Sjpk 	int		addr_type;
221*1676Sjpk 	void		*ipaddr;
222*1676Sjpk 	struct servinfo *svp;
223*1676Sjpk 	struct netbuf	*addr;
224*1676Sjpk 	struct knetconfig *knconf;
225*1676Sjpk 	mntinfo_t	*mi;
226*1676Sjpk 
227*1676Sjpk 	mi = VFTOMI(vfsp);
228*1676Sjpk 	svp = mi->mi_curr_serv;
229*1676Sjpk 	addr = &svp->sv_addr;
230*1676Sjpk 	knconf = svp->sv_knconf;
231*1676Sjpk 
232*1676Sjpk 	if (strcmp(knconf->knc_protofmly, NC_INET) == 0) {
233*1676Sjpk 		addr_type = IPV4_VERSION;
234*1676Sjpk 		/* LINTED: following cast to ipaddr is OK */
235*1676Sjpk 		ipaddr = &((struct sockaddr_in *)addr->buf)->sin_addr;
236*1676Sjpk 	} else if (strcmp(knconf->knc_protofmly, NC_INET6) == 0) {
237*1676Sjpk 		addr_type = IPV6_VERSION;
238*1676Sjpk 		/* LINTED: following cast to ipaddr is OK */
239*1676Sjpk 		ipaddr = &((struct sockaddr_in6 *)addr->buf)->sin6_addr;
240*1676Sjpk 	} else {
241*1676Sjpk 		goto errout;
242*1676Sjpk 	}
243*1676Sjpk 
244*1676Sjpk 	tp = find_tpc(ipaddr, addr_type, B_FALSE);
245*1676Sjpk 	if (tp == NULL)
246*1676Sjpk 		goto errout;
247*1676Sjpk 
248*1676Sjpk 	if (tp->tpc_tp.host_type == SUN_CIPSO) {
249*1676Sjpk 		TPC_RELE(tp);
250*1676Sjpk 		return (getflabel_cipso(vfsp));
251*1676Sjpk 	}
252*1676Sjpk 
253*1676Sjpk 	if (tp->tpc_tp.host_type != UNLABELED)
254*1676Sjpk 		goto errout;
255*1676Sjpk 
256*1676Sjpk 	server_sl = &tp->tpc_tp.tp_def_label;
257*1676Sjpk 	srv_label = labelalloc(server_sl, default_doi, KM_SLEEP);
258*1676Sjpk 
259*1676Sjpk 	TPC_RELE(tp);
260*1676Sjpk 
261*1676Sjpk 	return (srv_label);
262*1676Sjpk 
263*1676Sjpk errout:
264*1676Sjpk 	return (NULL);
265*1676Sjpk }
266*1676Sjpk 
267*1676Sjpk /*
268*1676Sjpk  * getflabel -
269*1676Sjpk  *
270*1676Sjpk  * Return pointer to the ts_label associated with the specified file,
271*1676Sjpk  * or returns NULL if error occurs.  Caller is responsible for doing
272*1676Sjpk  * a label_rele of the ts_label.
273*1676Sjpk  */
274*1676Sjpk ts_label_t *
275*1676Sjpk getflabel(vnode_t *vp)
276*1676Sjpk {
277*1676Sjpk 	vfs_t		*vfsp, *rvfsp, *nvfs;
278*1676Sjpk 	vnode_t		*rvp, *rvp2;
279*1676Sjpk 	zone_t		*zone;
280*1676Sjpk 	ts_label_t	*zl;
281*1676Sjpk 	boolean_t	vfs_is_held = B_FALSE;
282*1676Sjpk 	refstr_t	*resource_ref = NULL;
283*1676Sjpk 	char		*resource = NULL;
284*1676Sjpk 	char		vpath[MAXPATHLEN];
285*1676Sjpk 
286*1676Sjpk 	ASSERT(vp);
287*1676Sjpk 	vfsp = rvfsp = nvfs = vp->v_vfsp;
288*1676Sjpk 	if (vfsp == NULL)
289*1676Sjpk 		return (NULL);
290*1676Sjpk 
291*1676Sjpk 	rvp = rvp2 = vp;
292*1676Sjpk 
293*1676Sjpk 	/*
294*1676Sjpk 	 * Get rid of all but the last loopback vfs, since the last such mount
295*1676Sjpk 	 * has the correct resource to use (except for nfs case, handled later).
296*1676Sjpk 	 */
297*1676Sjpk 	while (strcmp(vfssw[nvfs->vfs_fstype].vsw_name, "lofs") == 0) {
298*1676Sjpk 		rvp = rvp2;
299*1676Sjpk 		rvfsp = nvfs;
300*1676Sjpk 		if ((rvp2 = realvp(rvp)) == NULL)
301*1676Sjpk 			break;
302*1676Sjpk 		if (((nvfs = rvp2->v_vfsp) == NULL) || (nvfs == rvfsp))
303*1676Sjpk 			break;
304*1676Sjpk 	}
305*1676Sjpk 
306*1676Sjpk 	/*
307*1676Sjpk 	 * rvp/rvfsp now represent the preliminary vnode/vfs we may use.  Now
308*1676Sjpk 	 * check if the next vfs is nfs; if so, then it has the correct info
309*1676Sjpk 	 * to use.  And finally, for some cases on loop-back mounts there will
310*1676Sjpk 	 * be no resource; for these, use the underlying vfs also.
311*1676Sjpk 	 */
312*1676Sjpk 	if (strcmp(vfssw[rvfsp->vfs_fstype].vsw_name, "lofs") == 0) {
313*1676Sjpk 		if (((rvp2 = realvp(rvp)) != NULL) &&
314*1676Sjpk 		    ((nvfs = rvp2->v_vfsp) != NULL) &&
315*1676Sjpk 		    ((strcmp(vfssw[nvfs->vfs_fstype].vsw_name, "nfs") == 0)) ||
316*1676Sjpk 		    (rvfsp->vfs_resource == NULL)) {
317*1676Sjpk 			rvp = rvp2;
318*1676Sjpk 			rvfsp = nvfs;
319*1676Sjpk 		}
320*1676Sjpk 	}
321*1676Sjpk 
322*1676Sjpk 	/* rvp/rvfsp now represent the real vnode/vfs we will be using */
323*1676Sjpk 
324*1676Sjpk 	/* Go elsewhere to handle all nfs files. */
325*1676Sjpk 	if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "nfs", 3) == 0)
326*1676Sjpk 		return (getflabel_nfs(rvfsp));
327*1676Sjpk 
328*1676Sjpk 	/*
329*1676Sjpk 	 * Fast path, for objects in a labeled zone: everything except
330*1676Sjpk 	 * for lofs/nfs will be just the label of that zone.
331*1676Sjpk 	 */
332*1676Sjpk 	if ((rvfsp->vfs_zone != NULL) && (rvfsp->vfs_zone != global_zone)) {
333*1676Sjpk 		if ((strcmp(vfssw[rvfsp->vfs_fstype].vsw_name,
334*1676Sjpk 		    "lofs") != 0)) {
335*1676Sjpk 			zone = rvfsp->vfs_zone;
336*1676Sjpk 			zone_hold(zone);
337*1676Sjpk 			goto zone_out;		/* return this label */
338*1676Sjpk 		}
339*1676Sjpk 	}
340*1676Sjpk 
341*1676Sjpk 	if (rvfsp->vfs_resource) {
342*1676Sjpk 		resource_ref = vfs_getresource(rvfsp);
343*1676Sjpk 		resource = (char *)refstr_value(resource_ref);
344*1676Sjpk 	}
345*1676Sjpk 
346*1676Sjpk 	/*
347*1676Sjpk 	 * Sanity check - resource may be weird for some cases, like devices.
348*1676Sjpk 	 * In this case, the label must be "local", so just use the mount point.
349*1676Sjpk 	 */
350*1676Sjpk 	if ((resource == NULL) || (*resource != '/')) {
351*1676Sjpk 		if (resource_ref)
352*1676Sjpk 			refstr_rele(resource_ref);
353*1676Sjpk 		if (rvfsp->vfs_mntpt) {
354*1676Sjpk 			resource_ref = vfs_getmntpoint(rvfsp);
355*1676Sjpk 			resource = (char *)refstr_value(resource_ref);
356*1676Sjpk 		}
357*1676Sjpk 		if ((resource == NULL) || (*resource != '/')) {
358*1676Sjpk 			zone = curproc->p_zone;
359*1676Sjpk 			zone_hold(zone);
360*1676Sjpk 			goto zone_out;
361*1676Sjpk 		}
362*1676Sjpk 	}
363*1676Sjpk 
364*1676Sjpk 	VFS_HOLD(vfsp);
365*1676Sjpk 	vfs_is_held = B_TRUE;
366*1676Sjpk 
367*1676Sjpk 	zone = zone_find_by_any_path(resource, B_FALSE);
368*1676Sjpk 
369*1676Sjpk 	/*
370*1676Sjpk 	 * If the vfs source zone is properly set to a non-global zone, or
371*1676Sjpk 	 * any zone if the mount is R/W, then use the label of that zone.
372*1676Sjpk 	 */
373*1676Sjpk 	if ((zone != global_zone) || ((vfsp->vfs_flag & VFS_RDONLY) != 0))
374*1676Sjpk 		goto zone_out;		/* return this label */
375*1676Sjpk 
376*1676Sjpk 	/*
377*1676Sjpk 	 * Otherwise, if we're not in the global zone, use the label of
378*1676Sjpk 	 * our zone.
379*1676Sjpk 	 */
380*1676Sjpk 	if ((zone = curproc->p_zone) != global_zone) {
381*1676Sjpk 		zone_hold(zone);
382*1676Sjpk 		goto zone_out;		/* return this label */
383*1676Sjpk 	}
384*1676Sjpk 
385*1676Sjpk 	/*
386*1676Sjpk 	 * We're in the global zone and the mount is R/W ... so the file
387*1676Sjpk 	 * may actually be in the global zone -- or in the root of any zone.
388*1676Sjpk 	 * Always build our own path for the file, to be sure it's simplified
389*1676Sjpk 	 * (i.e., no ".", "..", "//", and so on).
390*1676Sjpk 	 */
391*1676Sjpk 	if (vnodetopath(NULL, vp, vpath, sizeof (vpath), CRED()) != 0) {
392*1676Sjpk 		if (vfs_is_held)
393*1676Sjpk 			VFS_RELE(vfsp);
394*1676Sjpk 		if (resource_ref)
395*1676Sjpk 			refstr_rele(resource_ref);
396*1676Sjpk 		zone_rele(zone);
397*1676Sjpk 		return (NULL);
398*1676Sjpk 	}
399*1676Sjpk 
400*1676Sjpk 	zone_rele(zone);
401*1676Sjpk 	zone = zone_find_by_any_path(vpath, B_FALSE);
402*1676Sjpk 
403*1676Sjpk zone_out:
404*1676Sjpk 	if ((curproc->p_zone == global_zone) && (zone == global_zone)) {
405*1676Sjpk 		vfs_t		*nvfs;
406*1676Sjpk 		boolean_t	exported = B_FALSE;
407*1676Sjpk 		refstr_t	*mntpt_ref;
408*1676Sjpk 		char		*mntpt;
409*1676Sjpk 
410*1676Sjpk 		/*
411*1676Sjpk 		 * File is in the global zone - check whether it's admin_high.
412*1676Sjpk 		 * If it's in a filesys that was exported from the global zone,
413*1676Sjpk 		 * it's admin_low by definition.  Otherwise, if it's in a
414*1676Sjpk 		 * filesys that's NOT exported to any zone, it's admin_high.
415*1676Sjpk 		 *
416*1676Sjpk 		 * And for these files if there wasn't a valid mount resource,
417*1676Sjpk 		 * the file must be admin_high (not exported, probably a global
418*1676Sjpk 		 * zone device).
419*1676Sjpk 		 */
420*1676Sjpk 		if (!vfs_is_held)
421*1676Sjpk 			goto out_high;
422*1676Sjpk 
423*1676Sjpk 		mntpt_ref = vfs_getmntpoint(vfsp);
424*1676Sjpk 		mntpt = (char *)refstr_value(mntpt_ref);
425*1676Sjpk 
426*1676Sjpk 		if ((mntpt != NULL) && (*mntpt == '/')) {
427*1676Sjpk 			zone_t	*to_zone;
428*1676Sjpk 
429*1676Sjpk 			to_zone = zone_find_by_any_path(mntpt, B_FALSE);
430*1676Sjpk 			zone_rele(to_zone);
431*1676Sjpk 			if (to_zone != global_zone) {
432*1676Sjpk 				/* force admin_low */
433*1676Sjpk 				exported = B_TRUE;
434*1676Sjpk 			}
435*1676Sjpk 		}
436*1676Sjpk 		if (mntpt_ref)
437*1676Sjpk 			refstr_rele(mntpt_ref);
438*1676Sjpk 
439*1676Sjpk 		if (!exported) {
440*1676Sjpk 			size_t	plen = strlen(vpath);
441*1676Sjpk 
442*1676Sjpk 			vfs_list_read_lock();
443*1676Sjpk 			nvfs = vfsp->vfs_next;
444*1676Sjpk 			while (nvfs != vfsp) {
445*1676Sjpk 				const char	*rstr;
446*1676Sjpk 				size_t		rlen = 0;
447*1676Sjpk 
448*1676Sjpk 				rstr = refstr_value(nvfs->vfs_resource);
449*1676Sjpk 				if (rstr != NULL)
450*1676Sjpk 					rlen = strlen(rstr);
451*1676Sjpk 
452*1676Sjpk 				/*
453*1676Sjpk 				 * Check for a match: does this vfs correspond
454*1676Sjpk 				 * to our global zone file path?  I.e., check
455*1676Sjpk 				 * if the resource string of this vfs is a
456*1676Sjpk 				 * prefix of our path.
457*1676Sjpk 				 */
458*1676Sjpk 				if ((rlen > 0) && (rlen <= plen) &&
459*1676Sjpk 				    (strncmp(rstr, vpath, rlen) == 0) &&
460*1676Sjpk 				    (vpath[rlen] == '/' ||
461*1676Sjpk 				    vpath[rlen] == '\0')) {
462*1676Sjpk 					/* force admin_low */
463*1676Sjpk 					exported = B_TRUE;
464*1676Sjpk 					break;
465*1676Sjpk 				}
466*1676Sjpk 				nvfs = nvfs->vfs_next;
467*1676Sjpk 			}
468*1676Sjpk 			vfs_list_unlock();
469*1676Sjpk 		}
470*1676Sjpk 
471*1676Sjpk 		if (!exported)
472*1676Sjpk 			goto out_high;
473*1676Sjpk 	}
474*1676Sjpk 
475*1676Sjpk 	if (vfs_is_held)
476*1676Sjpk 		VFS_RELE(vfsp);
477*1676Sjpk 
478*1676Sjpk 	if (resource_ref)
479*1676Sjpk 		refstr_rele(resource_ref);
480*1676Sjpk 
481*1676Sjpk 	/*
482*1676Sjpk 	 * Now that we have the "home" zone for the file, return the slabel
483*1676Sjpk 	 * of that zone.
484*1676Sjpk 	 */
485*1676Sjpk 	zl = zone->zone_slabel;
486*1676Sjpk 	label_hold(zl);
487*1676Sjpk 	zone_rele(zone);
488*1676Sjpk 	return (zl);
489*1676Sjpk 
490*1676Sjpk out_high:
491*1676Sjpk 	if (vfs_is_held)
492*1676Sjpk 		VFS_RELE(vfsp);
493*1676Sjpk 	if (resource_ref)
494*1676Sjpk 		refstr_rele(resource_ref);
495*1676Sjpk 
496*1676Sjpk 	label_hold(l_admin_high);
497*1676Sjpk 	zone_rele(zone);
498*1676Sjpk 	return (l_admin_high);
499*1676Sjpk }
500*1676Sjpk 
501*1676Sjpk static int
502*1676Sjpk cgetlabel(bslabel_t *label_p, vnode_t *vp)
503*1676Sjpk {
504*1676Sjpk 	ts_label_t	*tsl;
505*1676Sjpk 	int		error = 0;
506*1676Sjpk 
507*1676Sjpk 	if ((tsl = getflabel(vp)) == NULL)
508*1676Sjpk 		return (EIO);
509*1676Sjpk 
510*1676Sjpk 	if (copyout((caddr_t)label2bslabel(tsl), (caddr_t)label_p,
511*1676Sjpk 	    sizeof (*(label_p))) != 0)
512*1676Sjpk 		error = EFAULT;
513*1676Sjpk 
514*1676Sjpk 	label_rele(tsl);
515*1676Sjpk 	return (error);
516*1676Sjpk }
517*1676Sjpk 
518*1676Sjpk /*
519*1676Sjpk  * fgetlabel(2TSOL) - get file label
520*1676Sjpk  * getlabel(2TSOL) - get file label
521*1676Sjpk  */
522*1676Sjpk int
523*1676Sjpk getlabel(const char *path, bslabel_t *label_p)
524*1676Sjpk {
525*1676Sjpk 	struct		vnode	*vp;
526*1676Sjpk 	char		*spath;
527*1676Sjpk 	int		error;
528*1676Sjpk 
529*1676Sjpk 	/* Sanity check arguments */
530*1676Sjpk 	if (path == NULL)
531*1676Sjpk 		return (set_errno(EINVAL));
532*1676Sjpk 
533*1676Sjpk 	spath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
534*1676Sjpk 	if ((error = copyinstr(path, spath, MAXPATHLEN, NULL)) != 0) {
535*1676Sjpk 		kmem_free(spath, MAXPATHLEN);
536*1676Sjpk 		return (set_errno(error));
537*1676Sjpk 	}
538*1676Sjpk 
539*1676Sjpk 	if (error = lookupname(spath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) {
540*1676Sjpk 		kmem_free(spath, MAXPATHLEN);
541*1676Sjpk 		return (set_errno(error));
542*1676Sjpk 	}
543*1676Sjpk 	kmem_free(spath, MAXPATHLEN);
544*1676Sjpk 
545*1676Sjpk 	error = cgetlabel(label_p, vp);
546*1676Sjpk 
547*1676Sjpk 	VN_RELE(vp);
548*1676Sjpk 	if (error != 0)
549*1676Sjpk 		return (set_errno(error));
550*1676Sjpk 	else
551*1676Sjpk 		return (0);
552*1676Sjpk }
553*1676Sjpk 
554*1676Sjpk int
555*1676Sjpk fgetlabel(int fd, bslabel_t *label_p)
556*1676Sjpk {
557*1676Sjpk 	file_t		*fp;
558*1676Sjpk 	int		error;
559*1676Sjpk 
560*1676Sjpk 	if ((fp = getf(fd)) == NULL)
561*1676Sjpk 		return (set_errno(EBADF));
562*1676Sjpk 
563*1676Sjpk 	error = cgetlabel(label_p, fp->f_vnode);
564*1676Sjpk 	releasef(fd);
565*1676Sjpk 
566*1676Sjpk 	if (error != 0)
567*1676Sjpk 		return (set_errno(error));
568*1676Sjpk 	else
569*1676Sjpk 		return (0);
570*1676Sjpk }
571*1676Sjpk 
572*1676Sjpk /*
573*1676Sjpk  * Used by NFSv4 to query label of a pathname
574*1676Sjpk  * component during lookup/access ops.
575*1676Sjpk  */
576*1676Sjpk ts_label_t *
577*1676Sjpk nfs4_getflabel(vnode_t *vp)
578*1676Sjpk {
579*1676Sjpk 	zone_t *zone;
580*1676Sjpk 	ts_label_t *zone_label;
581*1676Sjpk 	char path[MAXNAMELEN];
582*1676Sjpk 	vnode_t *pvp, *tvp;
583*1676Sjpk 
584*1676Sjpk 	mutex_enter(&vp->v_lock);
585*1676Sjpk 	/*
586*1676Sjpk 	 * mount traverse has been done by caller
587*1676Sjpk 	 * before calling this routine.
588*1676Sjpk 	 */
589*1676Sjpk 	ASSERT(!vn_ismntpt(vp));
590*1676Sjpk 	if (vp->v_path != NULL) {
591*1676Sjpk 		zone = zone_find_by_any_path(vp->v_path, B_FALSE);
592*1676Sjpk 		mutex_exit(&vp->v_lock);
593*1676Sjpk 	} else {
594*1676Sjpk 		/*
595*1676Sjpk 		 * v_path not cached. Since we rely on path
596*1676Sjpk 		 * of an obj to get its label, we need to get
597*1676Sjpk 		 * path corresponding to the parent vnode.
598*1676Sjpk 		 */
599*1676Sjpk 		tvp = vp;
600*1676Sjpk 		do {
601*1676Sjpk 			mutex_exit(&tvp->v_lock);
602*1676Sjpk 			if ((pvp = dnlc_reverse_lookup(tvp, path,
603*1676Sjpk 			    sizeof (path))) == NULL)
604*1676Sjpk 				return (NULL);
605*1676Sjpk 			mutex_enter(&pvp->v_lock);
606*1676Sjpk 			tvp = pvp;
607*1676Sjpk 		} while (pvp->v_path == NULL);
608*1676Sjpk 		zone = zone_find_by_any_path(pvp->v_path, B_FALSE);
609*1676Sjpk 		mutex_exit(&pvp->v_lock);
610*1676Sjpk 	}
611*1676Sjpk 	/*
612*1676Sjpk 	 * Caller has verified that the file is either
613*1676Sjpk 	 * exported or visible. So if the path falls in
614*1676Sjpk 	 * global zone, admin_low is returned; otherwise
615*1676Sjpk 	 * the zone's label is returned.
616*1676Sjpk 	 */
617*1676Sjpk 	zone_label = zone->zone_slabel;
618*1676Sjpk 	label_hold(zone_label);
619*1676Sjpk 	zone_rele(zone);
620*1676Sjpk 	return (zone_label);
621*1676Sjpk }
622