changeset 10972:807794d41b3a

PSARC 2009/348 Security Labels for ZFS 6795907 Add label attribute to ZFS datasets
author Ric Aleshire <Ric.Aleshire@Sun.COM>
date Thu, 05 Nov 2009 16:04:34 -0800
parents 2663784ac9bb
children 6969e719525a
files usr/src/cmd/zoneadmd/vplat.c usr/src/common/tsol/ltos.c usr/src/common/tsol/stol.c usr/src/common/zfs/zfs_prop.c usr/src/lib/libtsol/Makefile.com usr/src/lib/libtsol/common/label.h usr/src/lib/libtsol/common/ltos.c usr/src/lib/libtsol/common/mapfile-vers usr/src/lib/libtsol/common/stol.c usr/src/lib/libzfs/Makefile.com usr/src/lib/libzfs/common/libzfs_dataset.c usr/src/uts/common/Makefile.files usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h usr/src/uts/common/fs/zfs/zfs_ioctl.c usr/src/uts/common/fs/zfs/zfs_vfsops.c usr/src/uts/common/sys/fs/zfs.h usr/src/uts/common/sys/tsol/label.h
diffstat 17 files changed, 1310 insertions(+), 721 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/zoneadmd/vplat.c	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/cmd/zoneadmd/vplat.c	Thu Nov 05 16:04:34 2009 -0800
@@ -3141,6 +3141,134 @@
 }
 
 /*
+ * Return true if the path is its own zfs file system.  We determine this
+ * by stat-ing the path to see if it is zfs and stat-ing the parent to see
+ * if it is a different fs.
+ */
+boolean_t
+is_zonepath_zfs(char *zonepath)
+{
+	int res;
+	char *path;
+	char *parent;
+	struct statvfs64 buf1, buf2;
+
+	if (statvfs64(zonepath, &buf1) != 0)
+		return (B_FALSE);
+
+	if (strcmp(buf1.f_basetype, "zfs") != 0)
+		return (B_FALSE);
+
+	if ((path = strdup(zonepath)) == NULL)
+		return (B_FALSE);
+
+	parent = dirname(path);
+	res = statvfs64(parent, &buf2);
+	free(path);
+
+	if (res != 0)
+		return (B_FALSE);
+
+	if (buf1.f_fsid == buf2.f_fsid)
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+/*
+ * Verify the MAC label in the root dataset for the zone.
+ * If the label exists, it must match the label configured for the zone.
+ * Otherwise if there's no label on the dataset, create one here.
+ */
+
+static int
+validate_rootds_label(zlog_t *zlogp, char *rootpath, m_label_t *zone_sl)
+{
+	int		error = -1;
+	zfs_handle_t	*zhp;
+	libzfs_handle_t	*hdl;
+	m_label_t	ds_sl;
+	char		zonepath[MAXPATHLEN];
+	char		ds_hexsl[MAXNAMELEN];
+
+	if (!is_system_labeled())
+		return (0);
+
+	if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) {
+		zerror(zlogp, B_TRUE, "unable to determine zone path");
+		return (-1);
+	}
+
+	if (!is_zonepath_zfs(zonepath))
+		return (0);
+
+	if ((hdl = libzfs_init()) == NULL) {
+		zerror(zlogp, B_FALSE, "opening ZFS library");
+		return (-1);
+	}
+
+	if ((zhp = zfs_path_to_zhandle(hdl, rootpath,
+	    ZFS_TYPE_FILESYSTEM)) == NULL) {
+		zerror(zlogp, B_FALSE, "cannot open ZFS dataset for path '%s'",
+		    rootpath);
+		libzfs_fini(hdl);
+		return (-1);
+	}
+
+	/* Get the mlslabel property if it exists. */
+	if ((zfs_prop_get(zhp, ZFS_PROP_MLSLABEL, ds_hexsl, MAXNAMELEN,
+	    NULL, NULL, 0, B_TRUE) != 0) ||
+	    (strcmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) == 0)) {
+		char		*str2 = NULL;
+
+		/*
+		 * No label on the dataset (or default only); create one.
+		 * (Only do this automatic labeling for the labeled brand.)
+		 */
+		if (strcmp(brand_name, LABELED_BRAND_NAME) != 0) {
+			error = 0;
+			goto out;
+		}
+
+		error = l_to_str_internal(zone_sl, &str2);
+		if (error)
+			goto out;
+		if (str2 == NULL) {
+			error = -1;
+			goto out;
+		}
+		if ((error = zfs_prop_set(zhp,
+		    zfs_prop_to_name(ZFS_PROP_MLSLABEL), str2)) != 0) {
+			zerror(zlogp, B_FALSE, "cannot set 'mlslabel' "
+			    "property for root dataset at '%s'\n", rootpath);
+		}
+		free(str2);
+		goto out;
+	}
+
+	/* Convert the retrieved dataset label to binary form. */
+	error = hexstr_to_label(ds_hexsl, &ds_sl);
+	if (error) {
+		zerror(zlogp, B_FALSE, "invalid 'mlslabel' "
+		    "property on root dataset at '%s'\n", rootpath);
+		goto out;			/* exit with error */
+	}
+
+	/*
+	 * Perform a MAC check by comparing the zone label with the
+	 * dataset label.
+	 */
+	error = (!blequal(zone_sl, &ds_sl));
+	if (error)
+		zerror(zlogp, B_FALSE, "Rootpath dataset has mismatched label");
+out:
+	zfs_close(zhp);
+	libzfs_fini(hdl);
+
+	return (error);
+}
+
+/*
  * Mount lower level home directories into/from current zone
  * Share exported directories specified in dfstab for zone
  */
@@ -4013,6 +4141,8 @@
 		} else {
 			goto error;
 		}
+		if (validate_rootds_label(zlogp, rootpath, zlabel) != 0)
+			goto error;
 	}
 
 	kzone = zone_name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/common/tsol/ltos.c	Thu Nov 05 16:04:34 2009 -0800
@@ -0,0 +1,335 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#if !defined(_KERNEL)
+#include <errno.h>
+#endif /* !defined(_KERNEL) */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/tsol/label_macro.h>
+
+#include <sys/tsol/label.h>
+
+#if !defined(_KERNEL)
+#include "clnt.h"
+#include "labeld.h"
+#endif /* !defined(_KERNEL) */
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#include <sys/sunddi.h>
+#else
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#endif
+
+
+
+static _mac_label_impl_t low;
+static _mac_label_impl_t high;
+static int	inited = 0;
+
+#if defined(_KERNEL)
+#define	malloc(l)	kmem_alloc(l, KM_NOSLEEP)
+#define	freeit(a, l)		kmem_free(a, l)
+#else /* defined(_KERNEL) */
+#define	freeit(a, l)		free(a)
+#endif /* defined(_KERNEL) */
+
+/* 0x + Classification + '-' + ll + '-' + Compartments + end of string */
+#define	_HEX_SIZE 2+(sizeof (Classification_t)*2)+4+\
+	(sizeof (Compartments_t)*2)+1
+
+/* 0x + Classification + '-' + ll + '-' + end of string */
+#define	_MIN_HEX (2 + (sizeof (Classification_t)*2) + 4 + 1)
+
+static char digits[] = "0123456789abcdef";
+
+#define	HEX(h, i, l, s) \
+	for (; i < s; /* */) {\
+	h[i++] = digits[(unsigned int)(*l >> 4)];\
+	h[i++] = digits[(unsigned int)(*l++&0xF)]; }
+
+static int
+__hex(char **s, const m_label_t *l)
+{
+	char	*hex;
+	int	i = 0;
+	uchar_t *hl;
+	int	hex_len;
+	uchar_t *len;
+
+	hl = (uchar_t  *)&(((_mac_label_impl_t *)l)->_c_len);
+	len = hl;
+
+	if (*len == 0) {
+		/* old binary label */
+		hex_len = _HEX_SIZE;
+	} else {
+		hex_len = _MIN_HEX + (*len * sizeof (uint32_t) * 2);
+	}
+
+	if ((hex = malloc(hex_len)) == NULL) {
+		return (-1);
+	}
+
+	/* header */
+
+	hex[i++] = '0';
+	hex[i++] = 'x';
+
+	/* classification */
+
+	hl++;		/* start at classification */
+	HEX(hex, i, hl, 6);
+
+	/* Add compartments length */
+	hex[i++] = '-';
+	HEX(hex, i, len, 9);
+	hex[i++] = '-';
+
+	/* compartments */
+	HEX(hex, i, hl, hex_len-1);
+	hex[i] = '\0';
+
+	/* truncate trailing zeros */
+
+	while (hex[i-1] == '0' && hex[i-2] == '0') {
+		i -= 2;
+	}
+	hex[i] = '\0';
+
+	if ((*s = strdup(hex)) == NULL) {
+		freeit(hex, hex_len);
+		return (-1);
+	}
+
+	freeit(hex, hex_len);
+	return (0);
+
+}
+
+int
+l_to_str_internal(const m_label_t *l, char **s)
+{
+	if (inited == 0) {
+		inited = 1;
+		_BSLLOW(&low);
+		_BSLHIGH(&high);
+	}
+
+	if (!(_MTYPE(l, SUN_MAC_ID) || _MTYPE(l, SUN_UCLR_ID))) {
+#if !defined(_KERNEL)
+		errno = EINVAL;
+#endif /* !defined(_KERNEL) */
+		*s = NULL;
+		return (-1);
+	}
+	if (_MEQUAL(&low, (_mac_label_impl_t *)l)) {
+		if ((*s = strdup(ADMIN_LOW)) == NULL) {
+			return (-1);
+		}
+		return (0);
+	}
+	if (_MEQUAL(&high, (_mac_label_impl_t *)l)) {
+		if ((*s = strdup(ADMIN_HIGH)) == NULL) {
+			return (-1);
+		}
+		return (0);
+	}
+
+	return (__hex(s, l));
+}
+
+#if !defined(_KERNEL)
+/*
+ * label_to_str -- convert a label to the requested type of string.
+ *
+ *	Entry	l = label to convert;
+ *		t = type of conversion;
+ *		f = flags for conversion type;
+ *
+ *	Exit	*s = allocated converted string;
+ *		     Caller must call free() to free.
+ *
+ *	Returns	0, success.
+ *		-1, error, errno set; *s = NULL.
+ *
+ *	Calls	labeld
+ */
+
+int
+label_to_str(const m_label_t *l, char **s, const m_label_str_t t, uint_t f)
+{
+	labeld_data_t	call;
+	labeld_data_t	*callp = &call;
+	size_t	bufsize = sizeof (labeld_data_t);
+	size_t	datasize;
+	int	err;
+	int	string_start = 0;
+
+	if (inited == 0) {
+		inited = 1;
+		_BSLLOW(&low);
+		_BSLHIGH(&high);
+	}
+
+#define	lscall callp->param.acall.cargs.ls_arg
+#define	lsret callp->param.aret.rvals.ls_ret
+	switch (t) {
+	case M_LABEL:
+		call.callop = LTOS;
+		lscall.label = *l;
+		lscall.flags = f;
+		datasize = CALL_SIZE(ls_call_t, 0);
+		if ((err = __call_labeld(&callp, &bufsize, &datasize)) ==
+		    SUCCESS) {
+			if (callp->reterr != 0) {
+				errno = EINVAL;
+				*s = NULL;
+				return (-1);
+			}
+			*s = strdup(lsret.buf);
+			if (callp != &call) {
+				/* release returned buffer */
+				(void) munmap((void *)callp, bufsize);
+			}
+			if (*s == NULL) {
+				return (-1);
+			}
+			return (0);
+		}
+		switch (err) {
+		case NOSERVER:
+			/* server not present */
+			/* special case admin_low and admin_high */
+
+			if (_MEQUAL(&low, (_mac_label_impl_t *)l)) {
+				if ((*s = strdup(ADMIN_LOW)) == NULL) {
+					return (-1);
+				}
+				return (0);
+			} else if (_MEQUAL(&high, (_mac_label_impl_t *)l)) {
+				if ((*s = strdup(ADMIN_HIGH)) == NULL) {
+					return (-1);
+				}
+				return (0);
+			}
+			errno = ENOTSUP;
+			break;
+		default:
+			errno = EINVAL;
+			break;
+		}
+		*s = NULL;
+		return (-1);
+#undef	lscall
+#undef	lsret
+
+	case M_INTERNAL: {
+		return (l_to_str_internal(l, s));
+	}
+
+#define	ccall callp->param.acall.cargs.color_arg
+#define	cret callp->param.aret.rvals.color_ret
+	case M_COLOR:
+		datasize = CALL_SIZE(color_call_t, 0);
+		call.callop = BLTOCOLOR;
+		ccall.label = *l;
+
+		if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
+			if (callp->reterr != 0) {
+				errno = EINVAL;
+				*s = NULL;
+				return (-1);
+			}
+			*s = strdup(cret.color);
+			if (callp != &call) {
+				/* release returned buffer */
+				(void) munmap((void *)callp, bufsize);
+			}
+			if (*s == NULL) {
+				return (-1);
+			}
+			return (0);
+		} else {
+			errno = ENOTSUP;
+			*s = NULL;
+			return (-1);
+		}
+#undef	ccall
+#undef	cret
+
+#define	prcall	callp->param.acall.cargs.pr_arg
+#define	prret	callp->param.aret.rvals.pr_ret
+	case PRINTER_TOP_BOTTOM:
+		call.callop = PR_TOP;
+		break;
+	case PRINTER_LABEL:
+		call.callop = PR_LABEL;
+		break;
+	case PRINTER_CAVEATS:
+		call.callop = PR_CAVEATS;
+		string_start = 1;	/* compensate for leading space */
+		break;
+	case PRINTER_CHANNELS:
+		call.callop = PR_CHANNELS;
+		string_start = 1;	/* compensate for leading space */
+		break;
+	default:
+		errno = EINVAL;
+		*s = NULL;
+		return (-1);
+	}
+	/* do the common printer calls */
+	datasize = CALL_SIZE(pr_call_t, 0);
+	prcall.label = *l;
+	prcall.flags = f;
+	if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
+		if (callp->reterr != 0) {
+			errno = EINVAL;
+			*s = NULL;
+			return (-1);
+		}
+		*s = strdup(&prret.buf[string_start]);
+		if (callp != &call) {
+			/* release returned buffer */
+			(void) munmap((void *)callp, bufsize);
+		}
+		if (*s == NULL) {
+			return (-1);
+		}
+		return (0);
+	} else {
+		errno = ENOTSUP;
+		*s = NULL;
+		return (-1);
+	}
+#undef	prcall
+#undef	prret
+}
+#endif /* !defined(_KERNEL) */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/common/tsol/stol.c	Thu Nov 05 16:04:34 2009 -0800
@@ -0,0 +1,448 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#if !defined(_KERNEL)
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <strings.h>
+#else /* !defined(_KERNEL) */
+#include <sys/systm.h>
+#endif /* !defined(_KERNEL) */
+
+#include <sys/mman.h>
+#include <sys/tsol/label_macro.h>
+
+#include <sys/tsol/label.h>
+
+#if !defined(_KERNEL)
+#include "clnt.h"
+#include "labeld.h"
+#else /* !defined(_KERNEL) */
+#include <util/strtolctype.h>
+
+#define	L_DEFAULT	0x0
+#define	L_NO_CORRECTION	0x2
+#endif /* !defined(_KERNEL) */
+
+#define	IS_LOW(s) \
+	((strncasecmp(s, ADMIN_LOW, (sizeof (ADMIN_LOW) - 1)) == 0) && \
+	(s[sizeof (ADMIN_LOW) - 1] == '\0'))
+#define	IS_HIGH(s) \
+	((strncasecmp(s, ADMIN_HIGH, (sizeof (ADMIN_HIGH) - 1)) == 0) && \
+	(s[sizeof (ADMIN_HIGH) - 1] == '\0'))
+#define	IS_HEX(f, s) \
+	(((((f) == L_NO_CORRECTION)) || ((f) == L_DEFAULT)) && \
+	(((s)[0] == '0') && (((s)[1] == 'x') || ((s)[1] == 'X'))))
+
+static boolean_t
+unhex(const char **h, uchar_t *l, int len)
+{
+	const char	*hx = *h;
+	char	ch;
+	uchar_t	byte;
+
+	for (; len--; ) {
+		ch = *hx++;
+		if (!isxdigit(ch))
+			return (B_FALSE);
+		if (isdigit(ch))
+			byte = ch - '0';
+		else
+			byte = ch - (isupper(ch) ? 'A' - 10 : 'a' - 10);
+		byte <<= 4;
+		ch = *hx++;
+		if (!isxdigit(ch))
+			return (B_FALSE);
+		if (isdigit(ch))
+			byte |= ch - '0';
+		else
+			byte |= ch - (isupper(ch) ? 'A' - 10 : 'a' - 10);
+		*l++ = byte;
+	}
+	*h = hx;
+	return (B_TRUE);
+}
+
+/*
+ * Formats accepted:
+ * 0x + 4 class + 64 comps + end of string
+ * 0x + 4 class + '-' + ll + '-' + comps + end of string
+ * ll = number of words to fill out the entire comps field
+ *      presumes trailing zero for comps
+ *
+ * So in the case of 256 comps (i.e., 8 compartment words):
+ * 0x0006-08-7ff3f
+ * 0x + Classification + Compartments + end of string
+ * 0[xX]hhh...
+ */
+
+static int
+htol(const char *s, m_label_t *l)
+{
+	const char	*h = &s[2];	/* skip 0[xX] */
+	uchar_t *lp = (uchar_t *)&(((_mac_label_impl_t *)l)->_lclass);
+	size_t	len = sizeof (_mac_label_impl_t) - 4;
+	int	bytes;
+
+	/* unpack 16 bit signed classification */
+	if (!unhex(&h, lp, 2) || (LCLASS(l) < 0)) {
+		return (-1);
+	}
+	lp = (uchar_t *)&(((_mac_label_impl_t *)l)->_comps);
+	if (h[0] == '-' && h[3] == '-') {
+		uchar_t size;
+
+		/* length specified of internal text label */
+		h++;	/* skip '-' */
+		if (!unhex(&h, &size, 1)) {
+			return (-1);
+		}
+		/* convert size from words to bytes */
+		if ((size * sizeof (uint32_t)) > len) {
+			/*
+			 * internal label greater than will fit in current
+			 * binary.
+			 */
+			return (-1);
+		}
+		bzero(lp, len);
+		h++;	/* skip '-' */
+	}
+	bytes = strlen(h)/2;
+	if ((bytes > len) ||
+	    (bytes*2 != strlen(h)) ||
+	    !unhex(&h, lp, bytes)) {
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * hexstr_to_label -- parse a string representing a hex label into a
+ *			binary label.  Only admin high/low and hex are
+ *			accepted.
+ *
+ *	Returns	 0, success.
+ *		-1, failure
+ */
+int
+hexstr_to_label(const char *s, m_label_t *l)
+{
+	uint_t	f = L_DEFAULT;
+
+	/* translate hex, admin_low and admin_high */
+	if (IS_LOW(s)) {
+		_LOW_LABEL(l, SUN_MAC_ID);
+		return (0);
+	} else if (IS_HIGH(s)) {
+		_HIGH_LABEL(l, SUN_MAC_ID);
+		return (0);
+	} else if (IS_HEX(f, s)) {
+		_LOW_LABEL(l, SUN_MAC_ID);
+		if (htol(s, l) == 0)
+			return (0);
+	}
+
+	return (-1);
+}
+
+#if !defined(_KERNEL)
+static int
+convert_id(m_label_type_t t)
+{
+	switch (t) {
+	case MAC_LABEL:
+		return (SUN_MAC_ID);
+	case USER_CLEAR:
+		return (SUN_UCLR_ID);
+	default:
+		return (-1);
+	}
+}
+
+/*
+ * str_to_label -- parse a string into the requested label type.
+ *
+ *	Entry	s = string to parse.
+ *		l = label to create or modify.
+ *		t = label type (MAC_LABEL, USER_CLEAR).
+ *		f = flags
+ *			L_DEFAULT,
+ *			L_MODIFY_EXISTING, use the existing label as a basis for
+ *				the parse string.
+ *			L_NO_CORRECTION, s must be correct and full by the
+ *				label_encoding rules.
+ *			L_CHECK_AR, for non-hex s, MAC_LABEL, check the l_e AR
+ *
+ *	Exit	l = parsed label value.
+ *		e = index into string of error.
+ *		  = M_BAD_STRING (-3 L_BAD_LABEL) or could be zero,
+ *		    indicates entire string,
+ *	        e = M_BAD_LABEL (-2 L_BAD_CLASSIFICATION), problems with l
+ *		e = M_OUTSIDE_AR (-4 unrelated to L_BAD_* return values)
+ *
+ *	Returns	 0, success.
+ *		-1, failure
+ *			errno = ENOTSUP, the underlying label mechanism
+ *				does not support label parsing.
+ *				ENOMEM, unable to allocate memory for l.
+ *				EINVAL, invalid argument, l != NULL or
+ *				invalid label type for the underlying
+ *				label mechanism.
+ */
+#define	_M_GOOD_LABEL	-1	/* gfi L_GOOD_LABEL */
+int
+str_to_label(const char *str, m_label_t **l, const m_label_type_t t, uint_t f,
+    int *e)
+{
+	char		*s = strdup(str);
+	char		*st = s;
+	char		*p;
+	labeld_data_t	call;
+	labeld_data_t	*callp = &call;
+	size_t		bufsize = sizeof (labeld_data_t);
+	size_t		datasize;
+	int		err = M_BAD_LABEL;
+	int		id = convert_id(t);
+	boolean_t	new = B_FALSE;
+	uint_t		lf = (f & ~L_CHECK_AR);	/* because L_DEFAULT == 0 */
+
+	if (st == NULL) {
+		errno = ENOMEM;
+		return (-1);
+	}
+	if (*l == NULL) {
+		if ((*l = m_label_alloc(t)) == NULL) {
+			free(st);
+			return (-1);
+		}
+		if (id == -1) {
+			goto badlabel;
+		}
+		_LOW_LABEL(*l, id);
+		new = B_TRUE;
+	} else if (_MTYPE(*l, SUN_INVALID_ID) &&
+	    ((lf == L_NO_CORRECTION) || (lf == L_DEFAULT))) {
+		_LOW_LABEL(*l, id);
+		new = B_TRUE;
+	} else if (!(_MTYPE(*l, SUN_MAC_ID) || _MTYPE(*l, SUN_CLR_ID))) {
+		goto badlabel;
+	}
+
+	if (new == B_FALSE && id == -1) {
+		goto badlabel;
+	}
+
+	/* get to the beginning of the string to parse */
+	while (isspace(*s)) {
+		s++;
+	}
+
+	/* accept a leading '[' and trailing ']' for old times sake */
+	if (*s == '[') {
+		*s = ' ';
+		s++;
+		while (isspace(*s)) {
+			s++;
+		}
+	}
+	p = s;
+	while (*p != '\0' && *p != ']') {
+		p++;
+	}
+
+	/* strip trailing spaces */
+	while (p != s && isspace(*(p-1))) {
+		--p;
+	}
+	*p = '\0';	/* end of string */
+
+	/* translate hex, admin_low and admin_high */
+	id = _MGETTYPE(*l);
+	if (IS_LOW(s)) {
+		_LOW_LABEL(*l, id);
+		goto goodlabel;
+	} else if (IS_HIGH(s)) {
+		_HIGH_LABEL(*l, id);
+		goto goodlabel;
+	} else if (IS_HEX(lf, s)) {
+		if (htol(s, *l) != 0) {
+			/* whole string in error */
+			err = 0;
+			goto badlabel;
+		}
+		goto goodlabel;
+	}
+#define	slcall callp->param.acall.cargs.sl_arg
+#define	slret callp->param.aret.rvals.sl_ret
+	/* now try label server */
+
+	datasize = CALL_SIZE_STR(sl_call_t, strlen(st) + 1);
+	if (datasize > bufsize) {
+		if ((callp = malloc(datasize)) == NULL) {
+			free(st);
+			return (-1);
+		}
+		bufsize = datasize;
+	}
+	callp->callop = STOL;
+	slcall.label = **l;
+	slcall.flags = f;
+	if (new)
+		slcall.flags |= L_NEW_LABEL;
+	(void) strcpy(slcall.string, st);
+	/*
+	 * callp->reterr = L_GOOD_LABEL (-1) == OK;
+	 *		   L_BAD_CLASSIFICATION (-2) == bad input
+	 *			classification: class
+	 *		   L_BAD_LABEL (-3) == either string or input label bad
+	 *		   M_OUTSIDE_AR (-4) == resultant MAC_LABEL is out
+	 *			l_e accreditation range
+	 *		   O'E == offset in string 0 == entire string.
+	 */
+	if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
+
+		err = callp->reterr;
+		if (callp != &call) {
+			/* free allocated buffer */
+			free(callp);
+		}
+		switch (err) {
+		case _M_GOOD_LABEL:	/* L_GOOD_LABEL */
+			**l = slret.label;
+			goto goodlabel;
+		case M_BAD_LABEL:	/* L_BAD_CLASSIFICATION */
+		case M_BAD_STRING:	/* L_BAD_LABEL */
+		default:
+			goto badlabel;
+		}
+	}
+	switch (callp->reterr) {
+	case NOSERVER:
+		errno = ENOTSUP;
+		break;
+	default:
+		errno = EINVAL;
+		break;
+	}
+	free(st);
+	return (-1);
+
+badlabel:
+	errno = EINVAL;
+	free(st);
+	if (e != NULL)
+		*e = err;
+	return (-1);
+
+goodlabel:
+	free(st);
+	return (0);
+}
+#undef	slcall
+#undef	slret
+
+/*
+ * m_label_alloc -- allocate a label structure
+ *
+ *	Entry	t = label type (MAC_LABEL, USER_CLEAR).
+ *
+ *	Exit	If error, NULL, errno set to ENOMEM
+ *		Otherwise, pointer to m_label_t memory
+ */
+
+/* ARGUSED */
+m_label_t *
+m_label_alloc(const m_label_type_t t)
+{
+	m_label_t *l;
+
+	switch (t) {
+	case MAC_LABEL:
+	case USER_CLEAR:
+		if ((l = malloc(sizeof (_mac_label_impl_t))) == NULL) {
+			return (NULL);
+		}
+		_MSETTYPE(l, SUN_INVALID_ID);
+		break;
+	default:
+		errno = EINVAL;
+		return (NULL);
+	}
+	return (l);
+}
+
+/*
+ * m_label_dup -- make a duplicate copy of the given label.
+ *
+ *	Entry	l = label to duplicate.
+ *
+ *	Exit	d = duplicate copy of l.
+ *
+ *	Returns	 0, success
+ *		-1, if error.
+ *			errno = ENOTSUP, the underlying label mechanism
+ *				does not support label duplication.
+ *				ENOMEM, unable to allocate memory for d.
+ *				EINVAL, invalid argument, l == NULL or
+ *				invalid label type for the underlying
+ *				label mechanism.
+ */
+
+int
+m_label_dup(m_label_t **d, const m_label_t *l)
+{
+	if (d == NULL || *d != NULL) {
+		errno = EINVAL;
+		return (-1);
+	}
+	if ((*d = malloc(sizeof (_mac_label_impl_t))) == NULL) {
+		errno = ENOMEM;
+		return (-1);
+	}
+
+	(void) memcpy(*d, l, sizeof (_mac_label_impl_t));
+	return (0);
+}
+
+/*
+ * m_label_free -- free label structure
+ *
+ *	Entry	l = label to free.
+ *
+ *	Exit	memory freed.
+ *
+ */
+
+void
+m_label_free(m_label_t *l)
+{
+	if (l)
+		free(l);
+}
+#endif /* !defined(_KERNEL) */
--- a/usr/src/common/zfs/zfs_prop.c	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/common/zfs/zfs_prop.c	Thu Nov 05 16:04:34 2009 -0800
@@ -293,6 +293,8 @@
 	    ZFS_TYPE_DATASET, "filesystem | volume | snapshot", "TYPE");
 	register_string(ZFS_PROP_SHARESMB, "sharesmb", "off", PROP_INHERIT,
 	    ZFS_TYPE_FILESYSTEM, "on | off | sharemgr(1M) options", "SHARESMB");
+	register_string(ZFS_PROP_MLSLABEL, "mlslabel", ZFS_MLSLABEL_DEFAULT,
+	    PROP_INHERIT, ZFS_TYPE_DATASET, "<sensitivity label>", "MLSLABEL");
 
 	/* readonly number properties */
 	register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY,
@@ -369,6 +371,11 @@
 zfs_prop_delegatable(zfs_prop_t prop)
 {
 	zprop_desc_t *pd = &zfs_prop_table[prop];
+
+	/* The mlslabel property is never delegatable. */
+	if (prop == ZFS_PROP_MLSLABEL)
+		return (B_FALSE);
+
 	return (pd->pd_attr != PROP_READONLY);
 }
 
--- a/usr/src/lib/libtsol/Makefile.com	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/lib/libtsol/Makefile.com	Thu Nov 05 16:04:34 2009 -0800
@@ -19,18 +19,23 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
 LIBRARY =	libtsol.a
 VERS =		.2
 
-OBJECTS = \
-	blabel.o btohex.o btos.o call_labeld.o \
+COMMONOBJS = \
+	blabel.o ltos.o stol.o
+
+NONCOMMONOBJS = \
+	btohex.o btos.o call_labeld.o \
 	getlabel.o getplabel.o hextob.o \
-	ltos.o misc.o getpathbylabel.o private.o privlib.o \
-	setflabel.o stob.o stol.o zone.o
+	misc.o getpathbylabel.o private.o privlib.o \
+	setflabel.o stob.o zone.o \
+
+OBJECTS = $(NONCOMMONOBJS) $(COMMONOBJS)
 
 include ../../Makefile.lib
 
@@ -43,8 +48,9 @@
 SRCDIR =	../common
 $(LINTLIB):=	SRCS = $(SRCDIR)/$(LINTSRC)
 
-NONCOMMON =	$(OBJECTS:blabel.o=)
-lint:=		SRCS = $(NONCOMMON:%.o=$(SRCDIR)/%.c) $(COMMONDIR)/blabel.c
+lint:=		SRCS = \
+	$(NONCOMMONOBJS:%.o=$(SRCDIR)/%.c) \
+	$(COMMONOBJS:%.o=$(COMMONDIR)/%.c)
 
 COMMONDIR=	$(SRC)/common/tsol
 
--- a/usr/src/lib/libtsol/common/label.h	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/lib/libtsol/common/label.h	Thu Nov 05 16:04:34 2009 -0800
@@ -167,11 +167,6 @@
 extern int	bslvalid(const m_label_t *);
 extern int	bclearvalid(const m_label_t *);
 
-/* Manifest human readable label names */
-
-#define	ADMIN_LOW	"ADMIN_LOW"
-#define	ADMIN_HIGH	"ADMIN_HIGH"
-
 /* DIA label conversion and parsing */
 
 /* Conversion types */
@@ -193,6 +188,7 @@
 
 extern int label_to_str(const m_label_t *, char **, const m_label_str_t,
     uint_t);
+extern int l_to_str_internal(const m_label_t *, char **);
 
 /* Parsing types */
 typedef enum _m_label_type {
@@ -216,6 +212,7 @@
 
 extern int str_to_label(const char *, m_label_t **, const m_label_type_t,
     uint_t, int *);
+extern int hexstr_to_label(const char *, m_label_t *);
 
 extern m_label_t *m_label_alloc(const m_label_type_t);
 
--- a/usr/src/lib/libtsol/common/ltos.c	Thu Nov 05 15:54:24 2009 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,302 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/tsol/label_macro.h>
-
-#include <tsol/label.h>
-
-#include "clnt.h"
-#include "labeld.h"
-
-static _mac_label_impl_t low;
-static _mac_label_impl_t high;
-static int	inited = 0;
-
-/* 0x + Classification + '-' + ll + '-' + Compartments + end of string */
-#define	_HEX_SIZE 2+(sizeof (Classification_t)*2)+4+\
-	(sizeof (Compartments_t)*2)+1
-
-/* 0x + Classification + '-' + ll + '-' + end of string */
-#define	_MIN_HEX (2 + (sizeof (Classification_t)*2) + 4 + 1)
-
-static char digits[] = "0123456789abcdef";
-
-#define	HEX(h, i, l, s) \
-	for (; i < s; /* */) {\
-	h[i++] = digits[(unsigned int)(*l >> 4)];\
-	h[i++] = digits[(unsigned int)(*l++&0xF)]; }
-
-static int
-__hex(char **s, const m_label_t *l)
-{
-	char	*hex;
-	int	i = 0;
-	uchar_t *hl;
-	int	hex_len;
-	uchar_t *len;
-
-	hl = (uchar_t  *)&(((_mac_label_impl_t *)l)->_c_len);
-	len = hl;
-
-	if (*len == 0) {
-		/* old binary label */
-		hex_len = _HEX_SIZE;
-	} else {
-		hex_len = _MIN_HEX + (*len * sizeof (uint32_t) * 2);
-	}
-
-	if ((hex = malloc(hex_len)) == NULL) {
-		return (-1);
-	}
-
-	/* header */
-
-	hex[i++] = '0';
-	hex[i++] = 'x';
-
-	/* classification */
-
-	hl++;		/* start at classification */
-	HEX(hex, i, hl, 6);
-
-	/* Add compartments length */
-	hex[i++] = '-';
-	HEX(hex, i, len, 9);
-	hex[i++] = '-';
-
-	/* compartments */
-	HEX(hex, i, hl, hex_len-1);
-	hex[i] = '\0';
-
-	/* truncate trailing zeros */
-
-	while (hex[i-1] == '0' && hex[i-2] == '0') {
-		i -= 2;
-	}
-	hex[i] = '\0';
-
-	if ((*s = strdup(hex)) == NULL) {
-		free(hex);
-		return (-1);
-	}
-
-	free(hex);
-	return (0);
-
-}
-
-/*
- * label_to_str -- convert a label to the requested type of string.
- *
- *	Entry	l = label to convert;
- *		t = type of conversion;
- *		f = flags for conversion type;
- *
- *	Exit	*s = allocated converted string;
- *		     Caller must call free() to free.
- *
- *	Returns	0, success.
- *		-1, error, errno set; *s = NULL.
- *
- *	Calls	labeld
- */
-
-int
-label_to_str(const m_label_t *l, char **s, const m_label_str_t t, uint_t f)
-{
-	labeld_data_t	call;
-	labeld_data_t	*callp = &call;
-	size_t	bufsize = sizeof (labeld_data_t);
-	size_t	datasize;
-	int	err;
-	int	string_start = 0;
-
-	if (inited == 0) {
-		inited = 1;
-		_BSLLOW(&low);
-		_BSLHIGH(&high);
-	}
-
-#define	lscall callp->param.acall.cargs.ls_arg
-#define	lsret callp->param.aret.rvals.ls_ret
-	switch (t) {
-	case M_LABEL:
-		call.callop = LTOS;
-		lscall.label = *l;
-		lscall.flags = f;
-		datasize = CALL_SIZE(ls_call_t, 0);
-		if ((err = __call_labeld(&callp, &bufsize, &datasize)) ==
-		    SUCCESS) {
-			if (callp->reterr != 0) {
-				errno = EINVAL;
-				*s = NULL;
-				return (-1);
-			}
-			*s = strdup(lsret.buf);
-			if (callp != &call) {
-				/* release returned buffer */
-				(void) munmap((void *)callp, bufsize);
-			}
-			if (*s == NULL) {
-				return (-1);
-			}
-			return (0);
-		}
-		switch (err) {
-		case NOSERVER:
-			/* server not present */
-			/* special case admin_low and admin_high */
-
-			if (_MEQUAL(&low, (_mac_label_impl_t *)l)) {
-				if ((*s = strdup(ADMIN_LOW)) == NULL) {
-					return (-1);
-				}
-				return (0);
-			} else if (_MEQUAL(&high, (_mac_label_impl_t *)l)) {
-				if ((*s = strdup(ADMIN_HIGH)) == NULL) {
-					return (-1);
-				}
-				return (0);
-			}
-			errno = ENOTSUP;
-			break;
-		default:
-			errno = EINVAL;
-			break;
-		}
-		*s = NULL;
-		return (-1);
-#undef	lscall
-#undef	lsret
-
-	case M_INTERNAL: {
-		if (!(_MTYPE(l, SUN_MAC_ID) ||
-		    _MTYPE(l, SUN_UCLR_ID))) {
-			errno = EINVAL;
-			*s = NULL;
-			return (-1);
-		}
-		if (_MEQUAL(&low, (_mac_label_impl_t *)l)) {
-			if ((*s = strdup(ADMIN_LOW)) == NULL) {
-				return (-1);
-			}
-			return (0);
-		}
-		if (_MEQUAL(&high, (_mac_label_impl_t *)l)) {
-			if ((*s = strdup(ADMIN_HIGH)) == NULL) {
-				return (-1);
-			}
-			return (0);
-		}
-
-		return (__hex(s, l));
-	}
-
-#define	ccall callp->param.acall.cargs.color_arg
-#define	cret callp->param.aret.rvals.color_ret
-	case M_COLOR:
-		datasize = CALL_SIZE(color_call_t, 0);
-		call.callop = BLTOCOLOR;
-		ccall.label = *l;
-
-		if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
-			if (callp->reterr != 0) {
-				errno = EINVAL;
-				*s = NULL;
-				return (-1);
-			}
-			*s = strdup(cret.color);
-			if (callp != &call) {
-				/* release returned buffer */
-				(void) munmap((void *)callp, bufsize);
-			}
-			if (*s == NULL) {
-				return (-1);
-			}
-			return (0);
-		} else {
-			errno = ENOTSUP;
-			*s = NULL;
-			return (-1);
-		}
-#undef	ccall
-#undef	cret
-
-#define	prcall	callp->param.acall.cargs.pr_arg
-#define	prret	callp->param.aret.rvals.pr_ret
-	case PRINTER_TOP_BOTTOM:
-		call.callop = PR_TOP;
-		break;
-	case PRINTER_LABEL:
-		call.callop = PR_LABEL;
-		break;
-	case PRINTER_CAVEATS:
-		call.callop = PR_CAVEATS;
-		string_start = 1;	/* compensate for leading space */
-		break;
-	case PRINTER_CHANNELS:
-		call.callop = PR_CHANNELS;
-		string_start = 1;	/* compensate for leading space */
-		break;
-	default:
-		errno = EINVAL;
-		*s = NULL;
-		return (-1);
-	}
-	/* do the common printer calls */
-	datasize = CALL_SIZE(pr_call_t, 0);
-	prcall.label = *l;
-	prcall.flags = f;
-	if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
-		if (callp->reterr != 0) {
-			errno = EINVAL;
-			*s = NULL;
-			return (-1);
-		}
-		*s = strdup(&prret.buf[string_start]);
-		if (callp != &call) {
-			/* release returned buffer */
-			(void) munmap((void *)callp, bufsize);
-		}
-		if (*s == NULL) {
-			return (-1);
-		}
-		return (0);
-	} else {
-		errno = ENOTSUP;
-		*s = NULL;
-		return (-1);
-	}
-#undef	prcall
-#undef	prret
-}
--- a/usr/src/lib/libtsol/common/mapfile-vers	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/lib/libtsol/common/mapfile-vers	Thu Nov 05 16:04:34 2009 -0800
@@ -95,8 +95,10 @@
 	getpathbylabel;
 	h_alloc;
 	h_free;
+	hexstr_to_label;
 	htobclear;
 	htobsl;
+	l_to_str_internal;
 	labelfields;
 	labelinfo;
 	labelvers;
--- a/usr/src/lib/libtsol/common/stol.c	Thu Nov 05 15:54:24 2009 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,406 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-
-#include <sys/mman.h>
-#include <sys/tsol/label_macro.h>
-
-#include <tsol/label.h>
-
-#include "clnt.h"
-#include "labeld.h"
-
-#define	IS_LOW(s) \
-	((strncasecmp(s, ADMIN_LOW, (sizeof (ADMIN_LOW) - 1)) == 0) && \
-	(s[sizeof (ADMIN_LOW) - 1] == '\0'))
-#define	IS_HIGH(s) \
-	((strncasecmp(s, ADMIN_HIGH, (sizeof (ADMIN_HIGH) - 1)) == 0) && \
-	(s[sizeof (ADMIN_HIGH) - 1] == '\0'))
-#define	IS_HEX(f, s) \
-	(((((f) == L_NO_CORRECTION)) || ((f) == L_DEFAULT)) && \
-	(((s)[0] == '0') && (((s)[1] == 'x') || ((s)[1] == 'X'))))
-
-static boolean_t
-unhex(char **h, uchar_t *l, int len)
-{
-	char	*hx = *h;
-	char	ch;
-	uchar_t	byte;
-
-	for (; len--; ) {
-		ch = *hx++;
-		if (!isxdigit(ch))
-			return (B_FALSE);
-		if (isdigit(ch))
-			byte = ch - '0';
-		else
-			byte = ch - (isupper(ch) ? 'A' - 10 : 'a' - 10);
-		byte <<= 4;
-		ch = *hx++;
-		if (!isxdigit(ch))
-			return (B_FALSE);
-		if (isdigit(ch))
-			byte |= ch - '0';
-		else
-			byte |= ch - (isupper(ch) ? 'A' - 10 : 'a' - 10);
-		*l++ = byte;
-	}
-	*h = hx;
-	return (B_TRUE);
-}
-
-/*
- * Formats accepted:
- * 0x + 4 class + 64 comps + end of string
- * 0x + 4 class + '-' + ll + '-' + comps + end of string
- * ll = number of words to fill out the entire comps field
- *      presumes trailing zero for comps
- *
- * So in the case of 256 comps (i.e., 8 compartment words):
- * 0x0006-08-7ff3f
- * 0x + Classification + Compartments + end of string
- * 0[xX]hhh...
- */
-
-static int
-htol(char *s, m_label_t *l)
-{
-	char	*h = &s[2];	/* skip 0[xX] */
-	uchar_t *lp = (uchar_t *)&(((_mac_label_impl_t *)l)->_lclass);
-	size_t	len = sizeof (_mac_label_impl_t) - 4;
-	int	bytes;
-
-	/* unpack 16 bit signed classification */
-	if (!unhex(&h, lp, 2) || (LCLASS(l) < 0)) {
-		return (-1);
-	}
-	lp = (uchar_t *)&(((_mac_label_impl_t *)l)->_comps);
-	if (h[0] == '-' && h[3] == '-') {
-		uchar_t size;
-
-		/* length specified of internal text label */
-		h++;	/* skip '-' */
-		if (!unhex(&h, &size, 1)) {
-			return (-1);
-		}
-		/* convert size from words to bytes */
-		if ((size * sizeof (uint32_t)) > len) {
-			/*
-			 * internal label greater than will fit in current
-			 * binary.
-			 */
-			return (-1);
-		}
-		bzero(lp, len);
-		h++;	/* skip '-' */
-	}
-	bytes = strlen(h)/2;
-	if ((bytes > len) ||
-	    (bytes*2 != strlen(h)) ||
-	    !unhex(&h, lp, bytes)) {
-		return (-1);
-	}
-	return (0);
-}
-
-static int
-convert_id(m_label_type_t t)
-{
-	switch (t) {
-	case MAC_LABEL:
-		return (SUN_MAC_ID);
-	case USER_CLEAR:
-		return (SUN_UCLR_ID);
-	default:
-		return (-1);
-	}
-}
-
-/*
- * str_to_label -- parse a string into the requested label type.
- *
- *	Entry	s = string to parse.
- *		l = label to create or modify.
- *		t = label type (MAC_LABEL, USER_CLEAR).
- *		f = flags
- *			L_DEFAULT,
- *			L_MODIFY_EXISTING, use the existing label as a basis for
- *				the parse string.
- *			L_NO_CORRECTION, s must be correct and full by the
- *				label_encoding rules.
- *			L_CHECK_AR, for non-hex s, MAC_LABEL, check the l_e AR
- *
- *	Exit	l = parsed label value.
- *		e = index into string of error.
- *		  = M_BAD_STRING (-3 L_BAD_LABEL) or could be zero,
- *		    indicates entire string,
- *	        e = M_BAD_LABEL (-2 L_BAD_CLASSIFICATION), problems with l
- *		e = M_OUTSIDE_AR (-4 unrelated to L_BAD_* return values)
- *
- *	Returns	 0, success.
- *		-1, failure
- *			errno = ENOTSUP, the underlying label mechanism
- *				does not support label parsing.
- *				ENOMEM, unable to allocate memory for l.
- *				EINVAL, invalid argument, l != NULL or
- *				invalid label type for the underlying
- *				label mechanism.
- */
-#define	_M_GOOD_LABEL	-1	/* gfi L_GOOD_LABEL */
-int
-str_to_label(const char *str, m_label_t **l, const m_label_type_t t, uint_t f,
-    int *e)
-{
-	char		*s = strdup(str);
-	char		*st = s;
-	char		*p;
-	labeld_data_t	call;
-	labeld_data_t	*callp = &call;
-	size_t		bufsize = sizeof (labeld_data_t);
-	size_t		datasize;
-	int		err = M_BAD_LABEL;
-	int		id = convert_id(t);
-	boolean_t	new = B_FALSE;
-	uint_t		lf = (f & ~L_CHECK_AR);	/* because L_DEFAULT == 0 */
-
-	if (st == NULL) {
-		errno = ENOMEM;
-		return (-1);
-	}
-	if (*l == NULL) {
-		if ((*l = m_label_alloc(t)) == NULL) {
-			free(st);
-			return (-1);
-		}
-		if (id == -1) {
-			goto badlabel;
-		}
-		_LOW_LABEL(*l, id);
-		new = B_TRUE;
-	} else if (_MTYPE(*l, SUN_INVALID_ID) &&
-	    ((lf == L_NO_CORRECTION) || (lf == L_DEFAULT))) {
-		_LOW_LABEL(*l, id);
-		new = B_TRUE;
-	} else if (!(_MTYPE(*l, SUN_MAC_ID) || _MTYPE(*l, SUN_CLR_ID))) {
-		goto badlabel;
-	}
-
-	if (new == B_FALSE && id == -1) {
-		goto badlabel;
-	}
-
-	/* get to the beginning of the string to parse */
-	while (isspace(*s)) {
-		s++;
-	}
-
-	/* accept a leading '[' and trailing ']' for old times sake */
-	if (*s == '[') {
-		*s = ' ';
-		s++;
-		while (isspace(*s)) {
-			s++;
-		}
-	}
-	p = s;
-	while (*p != '\0' && *p != ']') {
-		p++;
-	}
-
-	/* strip trailing spaces */
-	while (p != s && isspace(*(p-1))) {
-		--p;
-	}
-	*p = '\0';	/* end of string */
-
-	/* translate hex, admin_low and admin_high */
-	id = _MGETTYPE(*l);
-	if (IS_LOW(s)) {
-		_LOW_LABEL(*l, id);
-		goto goodlabel;
-	} else if (IS_HIGH(s)) {
-		_HIGH_LABEL(*l, id);
-		goto goodlabel;
-	} else if (IS_HEX(lf, s)) {
-		if (htol(s, *l) != 0) {
-			/* whole string in error */
-			err = 0;
-			goto badlabel;
-		}
-		goto goodlabel;
-	}
-#define	slcall callp->param.acall.cargs.sl_arg
-#define	slret callp->param.aret.rvals.sl_ret
-	/* now try label server */
-
-	datasize = CALL_SIZE_STR(sl_call_t, strlen(st) + 1);
-	if (datasize > bufsize) {
-		if ((callp = malloc(datasize)) == NULL) {
-			free(st);
-			return (-1);
-		}
-		bufsize = datasize;
-	}
-	callp->callop = STOL;
-	slcall.label = **l;
-	slcall.flags = f;
-	if (new)
-		slcall.flags |= L_NEW_LABEL;
-	(void) strcpy(slcall.string, st);
-	/*
-	 * callp->reterr = L_GOOD_LABEL (-1) == OK;
-	 *		   L_BAD_CLASSIFICATION (-2) == bad input
-	 *			classification: class
-	 *		   L_BAD_LABEL (-3) == either string or input label bad
-	 *		   M_OUTSIDE_AR (-4) == resultant MAC_LABEL is out
-	 *			l_e accreditation range
-	 *		   O'E == offset in string 0 == entire string.
-	 */
-	if (__call_labeld(&callp, &bufsize, &datasize) == SUCCESS) {
-
-		err = callp->reterr;
-		if (callp != &call) {
-			/* free allocated buffer */
-			free(callp);
-		}
-		switch (err) {
-		case _M_GOOD_LABEL:	/* L_GOOD_LABEL */
-			**l = slret.label;
-			goto goodlabel;
-		case M_BAD_LABEL:	/* L_BAD_CLASSIFICATION */
-		case M_BAD_STRING:	/* L_BAD_LABEL */
-		default:
-			goto badlabel;
-		}
-	}
-	switch (callp->reterr) {
-	case NOSERVER:
-		errno = ENOTSUP;
-		break;
-	default:
-		errno = EINVAL;
-		break;
-	}
-	free(st);
-	return (-1);
-
-badlabel:
-	errno = EINVAL;
-	free(st);
-	if (e != NULL)
-		*e = err;
-	return (-1);
-
-goodlabel:
-	free(st);
-	return (0);
-}
-#undef	slcall
-#undef	slret
-
-/*
- * m_label_alloc -- allocate a label structure
- *
- *	Entry	t = label type (MAC_LABEL, USER_CLEAR).
- *
- *	Exit	If error, NULL, errno set to ENOMEM
- *		Otherwise, pointer to m_label_t memory
- */
-
-/* ARGUSED */
-m_label_t *
-m_label_alloc(const m_label_type_t t)
-{
-	m_label_t *l;
-
-	switch (t) {
-	case MAC_LABEL:
-	case USER_CLEAR:
-		if ((l = malloc(sizeof (_mac_label_impl_t))) == NULL) {
-			return (NULL);
-		}
-		_MSETTYPE(l, SUN_INVALID_ID);
-		break;
-	default:
-		errno = EINVAL;
-		return (NULL);
-	}
-	return (l);
-}
-
-/*
- * m_label_dup -- make a duplicate copy of the given label.
- *
- *	Entry	l = label to duplicate.
- *
- *	Exit	d = duplicate copy of l.
- *
- *	Returns	 0, success
- *		-1, if error.
- *			errno = ENOTSUP, the underlying label mechanism
- *				does not support label duplication.
- *				ENOMEM, unable to allocate memory for d.
- *				EINVAL, invalid argument, l == NULL or
- *				invalid label type for the underlying
- *				label mechanism.
- */
-
-int
-m_label_dup(m_label_t **d, const m_label_t *l)
-{
-	if (d == NULL || *d != NULL) {
-		errno = EINVAL;
-		return (-1);
-	}
-	if ((*d = malloc(sizeof (_mac_label_impl_t))) == NULL) {
-		errno = ENOMEM;
-		return (-1);
-	}
-
-	(void) memcpy(*d, l, sizeof (_mac_label_impl_t));
-	return (0);
-}
-
-/*
- * m_label_free -- free label structure
- *
- *	Entry	l = label to free.
- *
- *	Exit	memory freed.
- *
- */
-
-void
-m_label_free(m_label_t *l)
-{
-	if (l)
-		free(l);
-}
--- a/usr/src/lib/libzfs/Makefile.com	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/lib/libzfs/Makefile.com	Thu Nov 05 16:04:34 2009 -0800
@@ -66,7 +66,7 @@
 C99MODE=	-xc99=%all
 C99LMODE=	-Xc99=%all
 LDLIBS +=	-lc -lm -ldevid -lgen -lnvpair -luutil -lavl -lefi \
-	-lidmap
+	-lidmap -lsec -ltsol
 CPPFLAGS +=	$(INCS) -D_REENTRANT
 
 SRCS=	$(OBJS_COMMON:%.o=$(SRCDIR)/%.c)	\
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c	Thu Nov 05 16:04:34 2009 -0800
@@ -911,6 +911,60 @@
 
 			break;
 
+		case ZFS_PROP_MLSLABEL:
+		{
+			/*
+			 * Verify the mlslabel string and convert to
+			 * internal hex label string.
+			 */
+
+			m_label_t *new_sl;
+			char *hex = NULL;	/* internal label string */
+
+			/* Default value is already OK. */
+			if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
+				break;
+
+			/* Verify the label can be converted to binary form */
+			if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) ||
+			    (str_to_label(strval, &new_sl, MAC_LABEL,
+			    L_NO_CORRECTION, NULL) == -1)) {
+				goto badlabel;
+			}
+
+			/* Now translate to hex internal label string */
+			if (label_to_str(new_sl, &hex, M_INTERNAL,
+			    DEF_NAMES) != 0) {
+				if (hex)
+					free(hex);
+				goto badlabel;
+			}
+			m_label_free(new_sl);
+
+			/* If string is already in internal form, we're done. */
+			if (strcmp(strval, hex) == 0) {
+				free(hex);
+				break;
+			}
+
+			/* Replace the label string with the internal form. */
+			nvlist_remove(ret, zfs_prop_to_name(prop),
+			    DATA_TYPE_STRING);
+			verify(nvlist_add_string(ret, zfs_prop_to_name(prop),
+			    hex) == 0);
+			free(hex);
+
+			break;
+
+badlabel:
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "invalid mlslabel '%s'"), strval);
+			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+			m_label_free(new_sl);	/* OK if null */
+			goto error;
+
+		}
+
 		case ZFS_PROP_MOUNTPOINT:
 		{
 			namecheck_err_t why;
@@ -1866,6 +1920,44 @@
 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
 		break;
 
+	case ZFS_PROP_MLSLABEL:
+		{
+			m_label_t *new_sl = NULL;
+			char *ascii = NULL;	/* human readable label */
+
+			(void) strlcpy(propbuf,
+			    getprop_string(zhp, prop, &source), proplen);
+
+			if (literal || (strcasecmp(propbuf,
+			    ZFS_MLSLABEL_DEFAULT) == 0))
+				break;
+
+			/*
+			 * Try to translate the internal hex string to
+			 * human-readable output.  If there are any
+			 * problems just use the hex string.
+			 */
+
+			if (str_to_label(propbuf, &new_sl, MAC_LABEL,
+			    L_NO_CORRECTION, NULL) == -1) {
+				m_label_free(new_sl);
+				break;
+			}
+
+			if (label_to_str(new_sl, &ascii, M_LABEL,
+			    DEF_NAMES) != 0) {
+				if (ascii)
+					free(ascii);
+				m_label_free(new_sl);
+				break;
+			}
+			m_label_free(new_sl);
+
+			(void) strlcpy(propbuf, ascii, proplen);
+			free(ascii);
+		}
+		break;
+
 	default:
 		switch (zfs_prop_get_type(prop)) {
 		case PROP_TYPE_NUMBER:
--- a/usr/src/uts/common/Makefile.files	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/uts/common/Makefile.files	Thu Nov 05 16:04:34 2009 -0800
@@ -210,6 +210,7 @@
 		logsubr.o	\
 		lookup.o	\
 		lseek.o		\
+		ltos.o		\
 		lwp.o		\
 		lwp_create.o	\
 		lwp_info.o	\
@@ -313,6 +314,7 @@
 		stat.o		\
 		statfs.o	\
 		statvfs.o	\
+		stol.o		\
 		str_conf.o	\
 		strcalls.o	\
 		stream.o	\
--- a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h	Thu Nov 05 16:04:34 2009 -0800
@@ -145,6 +145,7 @@
 extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers);
 extern int zfsvfs_create(const char *name, zfsvfs_t **zvp);
 extern void zfsvfs_free(zfsvfs_t *zfsvfs);
+extern int zfs_check_global_label(const char *dsname, const char *hexsl);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Thu Nov 05 16:04:34 2009 -0800
@@ -36,11 +36,13 @@
 #include <sys/cmn_err.h>
 #include <sys/stat.h>
 #include <sys/zfs_ioctl.h>
+#include <sys/zfs_vfsops.h>
 #include <sys/zfs_znode.h>
 #include <sys/zap.h>
 #include <sys/spa.h>
 #include <sys/spa_impl.h>
 #include <sys/vdev.h>
+#include <sys/priv_impl.h>
 #include <sys/dmu.h>
 #include <sys/dsl_dir.h>
 #include <sys/dsl_dataset.h>
@@ -100,6 +102,8 @@
 	ZFS_DELEG_PERM_GROUPQUOTA,
 };
 
+static char *setsl_tag = "setsl_tag";
+
 static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
 static void clear_props(char *dataset, nvlist_t *props, nvlist_t *newprops);
 static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
@@ -326,6 +330,104 @@
 	return (error);
 }
 
+/*
+ * Policy for setting the security label property.
+ *
+ * Returns 0 for success, non-zero for access and other errors.
+ *
+ * If the objset is non-NULL upon return, the caller is responsible
+ * for dis-owning it, using the tag: setsl_tag.
+ *
+ */
+static int
+zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr,
+    objset_t **osp)
+{
+	char		ds_hexsl[MAXNAMELEN];
+	bslabel_t	ds_sl, new_sl;
+	boolean_t	new_default = FALSE;
+	uint64_t	zoned;
+	int		needed_priv = -1;
+	int		error;
+
+	/* First get the existing dataset label. */
+	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
+	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
+	if (error)
+		return (EPERM);
+
+	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
+		new_default = TRUE;
+
+	/* The label must be translatable */
+	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
+		return (EINVAL);
+
+	/*
+	 * In a non-global zone, disallow attempts to set a label that
+	 * doesn't match that of the zone; otherwise no other checks
+	 * are needed.
+	 */
+	if (!INGLOBALZONE(curproc)) {
+		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
+			return (EPERM);
+		return (0);
+	}
+
+	/*
+	 * For global-zone datasets (i.e., those whose zoned property is
+	 * "off", verify that the specified new label is valid for the
+	 * global zone.
+	 */
+	if (dsl_prop_get_integer(name,
+	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
+		return (EPERM);
+	if (!zoned) {
+		if (zfs_check_global_label(name, strval) != 0)
+			return (EPERM);
+	}
+
+	/*
+	 * If the existing dataset label is nondefault, check if the
+	 * dataset is mounted (label cannot be changed while mounted).
+	 * Get the zfsvfs; if there isn't one, then the dataset isn't
+	 * mounted (or isn't a dataset, doesn't exist, ...).
+	 */
+	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
+		ASSERT(osp != NULL);
+		/*
+		 * Try to own the dataset; abort if there is any error,
+		 * (e.g., already mounted, in use, or other error).
+		 */
+		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
+		    setsl_tag, osp);
+		if (error)
+			return (EPERM);
+
+		if (new_default) {
+			needed_priv = PRIV_FILE_DOWNGRADE_SL;
+			goto out_check;
+		}
+
+		if (hexstr_to_label(strval, &new_sl) != 0)
+			return (EPERM);
+
+		if (blstrictdom(&ds_sl, &new_sl))
+			needed_priv = PRIV_FILE_DOWNGRADE_SL;
+		else if (blstrictdom(&new_sl, &ds_sl))
+			needed_priv = PRIV_FILE_UPGRADE_SL;
+	} else {
+		/* dataset currently has a default label */
+		if (!new_default)
+			needed_priv = PRIV_FILE_UPGRADE_SL;
+	}
+
+out_check:
+	if (needed_priv != -1)
+		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
+	return (0);
+}
+
 static int
 zfs_secpolicy_setprop(const char *name, zfs_prop_t prop, cred_t *cr)
 {
@@ -357,6 +459,11 @@
 				return (EPERM);
 		}
 		break;
+
+	case ZFS_PROP_MLSLABEL:
+		if (!is_system_labeled())
+			return (EPERM);
+		break;
 	}
 
 	return (zfs_secpolicy_write_perms(name, zfs_prop_to_name(prop), cr));
@@ -1811,6 +1918,28 @@
 			break;
 		}
 
+		case ZFS_PROP_MLSLABEL:
+		{
+			objset_t *os = NULL;
+
+			if ((error = nvpair_value_string(elem, &strval)) != 0)
+				goto out;
+			if ((error = zfs_set_slabel_policy(name, strval,
+			    CRED(), &os)) != 0) {
+				/* error; first release the dataset if needed */
+				if (os)
+					dmu_objset_disown(os, setsl_tag);
+				goto out;
+			}
+
+			error = nvlist_add_nvpair(genericnvl, elem);
+			if (os)
+				dmu_objset_disown(os, setsl_tag);
+			if (error != 0)
+				goto out;
+			break;
+		}
+
 		default:
 			if (nvpair_type(elem) == DATA_TYPE_STRING) {
 				if (zfs_prop_get_type(prop) !=
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Thu Nov 05 16:04:34 2009 -0800
@@ -1247,6 +1247,140 @@
 	return (error);
 }
 
+/*
+ * zfs_check_global_label:
+ *	Check that the hex label string is appropriate for the dataset
+ *	being mounted into the global_zone proper.
+ *
+ *	Return an error if the hex label string is not default or
+ *	admin_low/admin_high.  For admin_low labels, the corresponding
+ *	dataset must be readonly.
+ */
+int
+zfs_check_global_label(const char *dsname, const char *hexsl)
+{
+	if (strcasecmp(hexsl, ZFS_MLSLABEL_DEFAULT) == 0)
+		return (0);
+	if (strcasecmp(hexsl, ADMIN_HIGH) == 0)
+		return (0);
+	if (strcasecmp(hexsl, ADMIN_LOW) == 0) {
+		/* must be readonly */
+		uint64_t rdonly;
+
+		if (dsl_prop_get_integer(dsname,
+		    zfs_prop_to_name(ZFS_PROP_READONLY), &rdonly, NULL))
+			return (EACCES);
+		return (rdonly ? 0 : EACCES);
+	}
+	return (EACCES);
+}
+
+/*
+ * zfs_mount_label_policy:
+ *	Determine whether the mount is allowed according to MAC check.
+ *	by comparing (where appropriate) label of the dataset against
+ *	the label of the zone being mounted into.  If the dataset has
+ *	no label, create one.
+ *
+ *	Returns:
+ *		 0 :	access allowed
+ *		>0 :	error code, such as EACCES
+ */
+static int
+zfs_mount_label_policy(vfs_t *vfsp, char *osname)
+{
+	int		error, retv;
+	zone_t		*mntzone = NULL;
+	ts_label_t	*mnt_tsl;
+	bslabel_t	*mnt_sl;
+	bslabel_t	ds_sl;
+	char		ds_hexsl[MAXNAMELEN];
+	char		*str2 = NULL;
+
+	retv = EACCES;				/* assume the worst */
+
+	/*
+	 * Start by getting the dataset label if it exists.
+	 */
+	error = dsl_prop_get(osname, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
+	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
+	if (error)
+		return (EACCES);
+
+	/*
+	 * If labeling is NOT enabled, then disallow the mount of datasets
+	 * which have a non-default label already.  No other label checks
+	 * are needed.
+	 */
+	if (!is_system_labeled()) {
+		if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) == 0)
+			return (0);
+		return (EACCES);
+	}
+
+	/*
+	 * Get the label of the mountpoint.  If mounting into the global
+	 * zone (i.e. mountpoint is not within an active zone and the
+	 * zoned property is off), the label must be default or
+	 * admin_low/admin_high only; no other checks are needed.
+	 */
+	mntzone = zone_find_by_any_path(refstr_value(vfsp->vfs_mntpt), B_FALSE);
+	if (mntzone->zone_id == GLOBAL_ZONEID) {
+		uint64_t zoned;
+
+		zone_rele(mntzone);
+
+		if (dsl_prop_get_integer(osname,
+		    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
+			return (EACCES);
+		if (!zoned)
+			return (zfs_check_global_label(osname, ds_hexsl));
+		else
+			/*
+			 * This is the case of a zone dataset being mounted
+			 * initially, before the zone has been fully created;
+			 * allow this mount into global zone.
+			 */
+			return (0);
+	}
+
+	mnt_tsl = mntzone->zone_slabel;
+	ASSERT(mnt_tsl != NULL);
+	label_hold(mnt_tsl);
+	mnt_sl = label2bslabel(mnt_tsl);
+
+	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) == 0) {
+		/*
+		 * The dataset doesn't have a real label, so fabricate one.
+		 */
+		char *str = NULL;
+
+		if (l_to_str_internal(mnt_sl, &str) == 0 &&
+		    dsl_prop_set(osname, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
+		    1, strlen(str) + 1, str) == 0)
+			retv = 0;
+		if (str != NULL)
+			kmem_free(str, strlen(str) + 1);
+	} else if (hexstr_to_label(ds_hexsl, &ds_sl) == 0) {
+		/*
+		 * Now compare labels to complete the MAC check.  If the
+		 * labels are equal then allow access.  If the mountpoint
+		 * label dominates the dataset label, allow readonly access.
+		 * Otherwise, access is denied.
+		 */
+		if (blequal(mnt_sl, &ds_sl))
+			retv = 0;
+		else if (bldominates(mnt_sl, &ds_sl)) {
+			vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
+			retv = 0;
+		}
+	}
+
+	label_rele(mnt_tsl);
+	zone_rele(mntzone);
+	return (retv);
+}
+
 static int
 zfs_mountroot(vfs_t *vfsp, enum whymountroot why)
 {
@@ -1436,6 +1570,10 @@
 		goto out;
 	}
 
+	error = zfs_mount_label_policy(vfsp, osname);
+	if (error)
+		goto out;
+
 	/*
 	 * When doing a remount, we simply refresh our temporary properties
 	 * according to those options set in the current VFS options.
--- a/usr/src/uts/common/sys/fs/zfs.h	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/uts/common/sys/fs/zfs.h	Thu Nov 05 16:04:34 2009 -0800
@@ -120,6 +120,7 @@
 	ZFS_PROP_UNIQUE,		/* not exposed to the user */
 	ZFS_PROP_OBJSETID,		/* not exposed to the user */
 	ZFS_PROP_DEDUP,
+	ZFS_PROP_MLSLABEL,
 	ZFS_NUM_PROPS
 } zfs_prop_t;
 
@@ -244,6 +245,8 @@
 #define	ZFS_DELEG_PERM_GID	"gid"
 #define	ZFS_DELEG_PERM_GROUPS	"groups"
 
+#define	ZFS_MLSLABEL_DEFAULT	"none"
+
 #define	ZFS_SMB_ACL_SRC		"src"
 #define	ZFS_SMB_ACL_TARGET	"target"
 
--- a/usr/src/uts/common/sys/tsol/label.h	Thu Nov 05 15:54:24 2009 -0800
+++ b/usr/src/uts/common/sys/tsol/label.h	Thu Nov 05 16:04:34 2009 -0800
@@ -43,6 +43,10 @@
 #define	EQUALITY_CHECK	0
 #define	DOMINANCE_CHECK	1
 
+/* Manifest human readable label names */
+#define	ADMIN_LOW	"ADMIN_LOW"
+#define	ADMIN_HIGH	"ADMIN_HIGH"
+
 /* Binary Label Structure Definitions */
 
 typedef	struct _mac_label_impl	m_label_t;
@@ -145,6 +149,9 @@
 extern int		_blinrange(const m_label_t *, const brange_t *);
 extern int		blinlset(const m_label_t *, const blset_t);
 
+extern int		l_to_str_internal(const m_label_t *, char **);
+extern int		hexstr_to_label(const char *, m_label_t *);
+
 /*
  * The use of '!!' here prevents users from referencing this function-like
  * macro as though it were an l-value, and in normal use is optimized away