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