Mercurial > illumos > illumos-gate
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