PSARC 2006/124 Configurable Privileges for Zones
authorcomay
Sun, 19 Mar 2006 14:21:20 -0800
changeset 1645 5c204cdba7d2
parent 1644 f2b7985b5922
child 1646 b4e43ae19fff
PSARC 2006/124 Configurable Privileges for Zones 4966416 RFE: zone privileges should be configurable 5008923 zoneadm halt message is a little illogical when run from the target zone 5049028 Makefiles that hacked around libpool errors now need cleanup 6345758 zonecfg rctl help doesn't mention 'remove' 6397853 zone console doesn't report failures to boot 6399963 get_zone_pool() isn't consistent with its return values
usr/src/cmd/zoneadm/Makefile
usr/src/cmd/zoneadm/zoneadm.c
usr/src/cmd/zoneadmd/Makefile
usr/src/cmd/zoneadmd/vplat.c
usr/src/cmd/zoneadmd/zcons.c
usr/src/cmd/zoneadmd/zoneadmd.c
usr/src/cmd/zoneadmd/zoneadmd.h
usr/src/cmd/zonecfg/zonecfg.c
usr/src/cmd/zonecfg/zonecfg.h
usr/src/cmd/zonecfg/zonecfg_grammar.y
usr/src/cmd/zonecfg/zonecfg_lex.l
usr/src/head/libzonecfg.h
usr/src/lib/libc/port/llib-lc
usr/src/lib/libzonecfg/common/libzonecfg.c
usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
usr/src/lib/libzonecfg/spec/libzonecfg.spec
--- a/usr/src/cmd/zoneadm/Makefile	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zoneadm/Makefile	Sun Mar 19 14:21:20 2006 -0800
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# 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.
@@ -19,8 +18,9 @@
 #
 # CDDL HEADER END
 #
+
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -40,8 +40,6 @@
 
 LDLIBS += -lzonecfg -lsocket -lgen -lpool -lbsm -lzfs
 
-lint := LINTFLAGS += -ux
-
 .KEEP_STATE:
 
 all: $(PROG)
--- a/usr/src/cmd/zoneadm/zoneadm.c	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zoneadm/zoneadm.c	Sun Mar 19 14:21:20 2006 -0800
@@ -1549,8 +1549,20 @@
 	FILE *fp;
 
 	if (getzoneid() != GLOBAL_ZONEID) {
-		zerror(gettext("must be in the global zone to %s a zone."),
-		    cmd_to_str(cmd_num));
+		switch (cmd_num) {
+		case CMD_HALT:
+			zerror(gettext("use %s to %s this zone."), "halt(1M)",
+			    cmd_to_str(cmd_num));
+			break;
+		case CMD_REBOOT:
+			zerror(gettext("use %s to %s this zone."),
+			    "reboot(1M)", cmd_to_str(cmd_num));
+			break;
+		default:
+			zerror(gettext("must be in the global zone to %s a "
+			    "zone."), cmd_to_str(cmd_num));
+			break;
+		}
 		return (Z_ERR);
 	}
 
@@ -1970,15 +1982,15 @@
 
 	if ((zhp = zfs_open(fstab->zone_fs_special, ZFS_TYPE_ANY)) == NULL) {
 		(void) fprintf(stderr, gettext("could not verify fs %s: "
-			"could not access zfs dataset '%s'\n"),
-			fstab->zone_fs_dir, fstab->zone_fs_special);
+		    "could not access zfs dataset '%s'\n"),
+		    fstab->zone_fs_dir, fstab->zone_fs_special);
 		return (Z_ERR);
 	}
 
 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
 		(void) fprintf(stderr, gettext("cannot verify fs %s: "
-			"'%s' is not a filesystem\n"),
-			fstab->zone_fs_dir, fstab->zone_fs_special);
+		    "'%s' is not a filesystem\n"),
+		    fstab->zone_fs_dir, fstab->zone_fs_special);
 		zfs_close(zhp);
 		return (Z_ERR);
 	}
@@ -1986,8 +1998,8 @@
 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
 	    NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) {
 		(void) fprintf(stderr, gettext("could not verify fs %s: "
-			"zfs '%s' mountpoint is not \"legacy\"\n"),
-			fstab->zone_fs_dir, fstab->zone_fs_special);
+		    "zfs '%s' mountpoint is not \"legacy\"\n"),
+		    fstab->zone_fs_dir, fstab->zone_fs_special);
 		zfs_close(zhp);
 		return (Z_ERR);
 	}
@@ -2264,6 +2276,44 @@
 }
 
 static int
+verify_limitpriv(zone_dochandle_t handle)
+{
+	char *privname = NULL;
+	int err;
+	priv_set_t *privs;
+
+	if ((privs = priv_allocset()) == NULL) {
+		zperror(gettext("failed to allocate privilege set"), B_FALSE);
+		return (Z_NOMEM);
+	}
+	err = zonecfg_get_privset(handle, privs, &privname);
+	switch (err) {
+	case Z_OK:
+		break;
+	case Z_PRIV_PROHIBITED:
+		(void) fprintf(stderr, gettext("privilege \"%s\" is not "
+		    "permitted within the zone's privilege set\n"), privname);
+		break;
+	case Z_PRIV_REQUIRED:
+		(void) fprintf(stderr, gettext("required privilege \"%s\" is "
+		    "missing from the zone's privilege set\n"), privname);
+		break;
+	case Z_PRIV_UNKNOWN:
+		(void) fprintf(stderr, gettext("unknown privilege \"%s\" "
+		    "specified in the zone's privilege set\n"), privname);
+		break;
+	default:
+		zperror(
+		    gettext("failed to determine the zone's privilege set"),
+		    B_TRUE);
+		break;
+	}
+	free(privname);
+	priv_freeset(privs);
+	return (err);
+}
+
+static int
 verify_details(int cmd_num)
 {
 	zone_dochandle_t handle;
@@ -2381,6 +2431,17 @@
 		return_code = Z_ERR;
 	if (!in_alt_root && verify_datasets(handle) != Z_OK)
 		return_code = Z_ERR;
+
+	/*
+	 * As the "mount" command is used for patching/upgrading of zones
+	 * or other maintenance processes, the zone's privilege set is not
+	 * checked in this case.  Instead, the default, safe set of
+	 * privileges will be used when this zone is created in the
+	 * kernel.
+	 */
+	if (!in_alt_root && cmd_num != CMD_MOUNT &&
+	    verify_limitpriv(handle) != Z_OK)
+		return_code = Z_ERR;
 	zonecfg_fini_handle(handle);
 	if (return_code == Z_ERR)
 		(void) fprintf(stderr,
@@ -4074,7 +4135,7 @@
 	err = zonecfg_find_mounts(rootpath, NULL, NULL);
 	if (err) {
 		zerror(gettext("These file-systems are mounted on "
-			"subdirectories of %s.\n"), rootpath);
+		    "subdirectories of %s.\n"), rootpath);
 		(void) zonecfg_find_mounts(rootpath, zfm_print, NULL);
 		return (Z_ERR);
 	}
--- a/usr/src/cmd/zoneadmd/Makefile	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zoneadmd/Makefile	Sun Mar 19 14:21:20 2006 -0800
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# 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.
@@ -19,11 +18,13 @@
 #
 # CDDL HEADER END
 #
+
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
+#
 
 PROG= zoneadmd
 
@@ -37,7 +38,6 @@
 POFILES= $(OBJS:%.o=%.po)
 
 CFLAGS += $(CCVERBOSE)
-LINTFLAGS += -ux
 LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair -lpool \
 	-lgen -lbsm -lcontract -lzfs
 XGETFLAGS += -a -x zoneadmd.xcl
--- a/usr/src/cmd/zoneadmd/vplat.c	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zoneadmd/vplat.c	Sun Mar 19 14:21:20 2006 -0800
@@ -18,6 +18,7 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
@@ -1336,8 +1337,7 @@
 	}
 
 	if ((handle = zonecfg_init_handle()) == NULL) {
-		zerror(zlogp, B_TRUE,
-		    "could not get zone configuration handle");
+		zerror(zlogp, B_TRUE, "getting zone configuration handle");
 		goto bad;
 	}
 	if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK ||
@@ -2255,7 +2255,7 @@
 	if (status == 0 || status == -1)
 		return (status);
 	zerror(zlogp, B_FALSE, "%s call (%s %s %s) unexpectedly returned %d",
-		    DEVFSADM, DEVFSADM_PATH, arg, zone_name, status);
+	    DEVFSADM, DEVFSADM_PATH, arg, zone_name, status);
 	return (-1);
 }
 
@@ -2275,6 +2275,58 @@
 }
 
 static int
+get_privset(zlog_t *zlogp, priv_set_t *privs, boolean_t mount_cmd)
+{
+	int error = -1;
+	zone_dochandle_t handle;
+	char *privname = NULL;
+
+	if (mount_cmd) {
+		if (zonecfg_default_privset(privs) == Z_OK)
+			return (0);
+		zerror(zlogp, B_FALSE,
+		    "failed to determine the zone's default privilege set");
+		return (-1);
+	}
+
+	if ((handle = zonecfg_init_handle()) == NULL) {
+		zerror(zlogp, B_TRUE, "getting zone configuration handle");
+		return (-1);
+	}
+	if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) {
+		zerror(zlogp, B_FALSE, "invalid configuration");
+		zonecfg_fini_handle(handle);
+		return (-1);
+	}
+
+	switch (zonecfg_get_privset(handle, privs, &privname)) {
+	case Z_OK:
+		error = 0;
+		break;
+	case Z_PRIV_PROHIBITED:
+		zerror(zlogp, B_FALSE, "privilege \"%s\" is not permitted "
+		    "within the zone's privilege set", privname);
+		break;
+	case Z_PRIV_REQUIRED:
+		zerror(zlogp, B_FALSE, "required privilege \"%s\" is missing "
+		    "from the zone's privilege set", privname);
+		break;
+	case Z_PRIV_UNKNOWN:
+		zerror(zlogp, B_FALSE, "unknown privilege \"%s\" specified "
+		    "in the zone's privilege set", privname);
+		break;
+	default:
+		zerror(zlogp, B_FALSE, "failed to determine the zone's "
+		    "privilege set");
+		break;
+	}
+
+	free(privname);
+	zonecfg_fini_handle(handle);
+	return (error);
+}
+
+static int
 get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep)
 {
 	nvlist_t *nvl = NULL;
@@ -2364,13 +2416,13 @@
 				goto out;
 			}
 			if (nvlist_add_uint64(nvlv[i], "privilege",
-				    rctlblk_get_privilege(rctlblk)) != 0) {
+			    rctlblk_get_privilege(rctlblk)) != 0) {
 				zerror(zlogp, B_FALSE, "%s failed",
 				    "nvlist_add_uint64");
 				goto out;
 			}
 			if (nvlist_add_uint64(nvlv[i], "limit",
-				    rctlblk_get_value(rctlblk)) != 0) {
+			    rctlblk_get_value(rctlblk)) != 0) {
 				zerror(zlogp, B_FALSE, "%s failed",
 				    "nvlist_add_uint64");
 				goto out;
@@ -2435,12 +2487,13 @@
 
 	if ((handle = zonecfg_init_handle()) == NULL) {
 		zerror(zlogp, B_TRUE, "getting zone configuration handle");
-		return (-1);
+		return (Z_NOMEM);
 	}
-	if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) {
+	error = zonecfg_get_snapshot_handle(zone_name, handle);
+	if (error != Z_OK) {
 		zerror(zlogp, B_FALSE, "invalid configuration");
 		zonecfg_fini_handle(handle);
-		return (-1);
+		return (error);
 	}
 	error = zonecfg_get_pool(handle, poolbuf, bufsz);
 	zonecfg_fini_handle(handle);
@@ -2776,14 +2829,14 @@
 		return (-1);
 	}
 	priv_emptyset(privs);
-	if (zonecfg_get_privset(privs) != Z_OK) {
-		zerror(zlogp, B_TRUE, "Failed to initialize privileges");
+	if (get_privset(zlogp, privs, mount_cmd) != 0)
 		goto error;
-	}
+
 	if (!mount_cmd && get_rctls(zlogp, &rctlbuf, &rctlbufsz) != 0) {
 		zerror(zlogp, B_FALSE, "Unable to get list of rctls");
 		goto error;
 	}
+
 	if (get_datasets(zlogp, &zfsbuf, &zfsbufsz) != 0) {
 		zerror(zlogp, B_FALSE, "Unable to get list of ZFS datasets");
 		goto error;
--- a/usr/src/cmd/zoneadmd/zcons.c	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zoneadmd/zcons.c	Sun Mar 19 14:21:20 2006 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -19,8 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -720,6 +720,9 @@
 	case Z_EVT_ZONE_UNINSTALLING:
 		str = "Zone is being uninstalled.  Disconnecting...";
 		break;
+	case Z_EVT_ZONE_BOOTFAILED:
+		str = "Zone boot failed";
+		break;
 	default:
 		return;
 	}
--- a/usr/src/cmd/zoneadmd/zoneadmd.c	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zoneadmd/zoneadmd.c	Sun Mar 19 14:21:20 2006 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -19,8 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -681,6 +681,7 @@
 			if (rval != 0) {
 				bringup_failure_recovery = B_TRUE;
 				(void) zone_halt(zlogp, B_FALSE);
+				eventstream_write(Z_EVT_ZONE_BOOTFAILED);
 			}
 			break;
 		case Z_HALT:
@@ -716,8 +717,11 @@
 			if (kernelcall)	/* Invalid; can't happen */
 				abort();
 			rval = zone_ready(zlogp, B_TRUE);
-			if (rval == 0)
+			if (rval == 0) {
+				eventstream_write(Z_EVT_ZONE_READIED);
 				rval = zone_mount_early(zlogp, zone_id);
+			}
+
 			/*
 			 * Ordinarily, /dev/fd would be mounted inside the zone
 			 * by svc:/system/filesystem/usr:default, but since
@@ -756,6 +760,7 @@
 			if (rval != 0) {
 				bringup_failure_recovery = B_TRUE;
 				(void) zone_halt(zlogp, B_FALSE);
+				eventstream_write(Z_EVT_ZONE_BOOTFAILED);
 			}
 			break;
 		case Z_HALT:
@@ -785,8 +790,10 @@
 			if (kernelcall)	/* Invalid; can't happen */
 				abort();
 			rval = zone_halt(zlogp, B_TRUE);
-			if (rval == 0)
+			if (rval == 0) {
+				eventstream_write(Z_EVT_ZONE_HALTED);
 				(void) sema_post(&scratch_sem);
+			}
 			break;
 		default:
 			if (kernelcall)	/* Invalid; can't happen */
@@ -808,6 +815,8 @@
 				break;
 			if ((rval = zone_ready(zlogp, B_FALSE)) == 0)
 				eventstream_write(Z_EVT_ZONE_READIED);
+			else
+				eventstream_write(Z_EVT_ZONE_HALTED);
 			break;
 		case Z_BOOT:
 			/*
@@ -826,13 +835,19 @@
 			break;
 		case Z_REBOOT:
 			eventstream_write(Z_EVT_ZONE_REBOOTING);
-			if ((rval = zone_halt(zlogp, B_FALSE)) != 0)
+			if ((rval = zone_halt(zlogp, B_FALSE)) != 0) {
+				eventstream_write(Z_EVT_ZONE_BOOTFAILED);
+				break;
+			}
+			if ((rval = zone_ready(zlogp, B_FALSE)) != 0) {
+				eventstream_write(Z_EVT_ZONE_BOOTFAILED);
 				break;
-			if ((rval = zone_ready(zlogp, B_FALSE)) == 0) {
-				rval = zone_bootup(zlogp, "");
-				audit_put_record(zlogp, uc, rval, "reboot");
-				if (rval != 0)
-					(void) zone_halt(zlogp, B_FALSE);
+			}
+			rval = zone_bootup(zlogp, "");
+			audit_put_record(zlogp, uc, rval, "reboot");
+			if (rval != 0) {
+				(void) zone_halt(zlogp, B_FALSE);
+				eventstream_write(Z_EVT_ZONE_BOOTFAILED);
 			}
 			break;
 		case Z_NOTE_UNINSTALLING:
--- a/usr/src/cmd/zoneadmd/zoneadmd.h	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zoneadmd/zoneadmd.h	Sun Mar 19 14:21:20 2006 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -19,8 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -84,7 +84,8 @@
 	Z_EVT_ZONE_REBOOTING,
 	Z_EVT_ZONE_HALTED,
 	Z_EVT_ZONE_READIED,
-	Z_EVT_ZONE_UNINSTALLING
+	Z_EVT_ZONE_UNINSTALLING,
+	Z_EVT_ZONE_BOOTFAILED
 } zone_evt_t;
 
 extern int eventstream_init();
--- a/usr/src/cmd/zonecfg/zonecfg.c	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zonecfg/zonecfg.c	Sun Mar 19 14:21:20 2006 -0800
@@ -111,7 +111,7 @@
 #define	SHELP_HELP	"help [commands] [syntax] [usage] [<command-name>]"
 #define	SHELP_INFO	"info [<resource-type> [property-name=property-value]*]"
 #define	SHELP_REMOVE	"remove <resource-type> { <property-name>=<property-" \
-	"value> }\n\t(global scope)\nremove <property-name>=<property-value>" \
+	"value> }\n\t(global scope)\nremove <property-name> <property-value>" \
 	"\n\t(resource scope)"
 #define	SHELP_REVERT	"revert [-F]"
 #define	SHELP_SELECT	"select <resource-type> { <property-name>=" \
@@ -154,6 +154,7 @@
 	"rctl",
 	"attr",
 	"dataset",
+	"limitpriv",
 	NULL
 };
 
@@ -177,10 +178,11 @@
 	"limit",
 	"action",
 	"raw",
+	"limitpriv",
 	NULL
 };
 
-/* These *must* match the order of the PT_ define's from zonecfg.h */
+/* These *must* match the order of the PROP_VAL_ define's from zonecfg.h */
 static char *prop_val_types[] = {
 	"simple",
 	"complex",
@@ -240,6 +242,7 @@
 	"set zonepath=",
 	"set autoboot=",
 	"set pool=",
+	"set limitpriv=",
 	NULL
 };
 
@@ -250,6 +253,7 @@
 	"exit",
 	"help",
 	"info",
+	"remove options ",
 	"set dir=",
 	"set raw=",
 	"set special=",
@@ -307,6 +311,7 @@
 	"exit",
 	"help",
 	"info",
+	"remove value ",
 	"set name=",
 	NULL
 };
@@ -722,6 +727,9 @@
 			(void) fprintf(fp, "\t%s %s %s\n", cmd_to_str(CMD_ADD),
 			    pt_to_str(PT_OPTIONS),
 			    gettext("<file-system options>"));
+			(void) fprintf(fp, "\t%s %s %s\n",
+			    cmd_to_str(CMD_REMOVE), pt_to_str(PT_OPTIONS),
+			    gettext("<file-system options>"));
 			(void) fprintf(fp, gettext("Consult the file-system "
 			    "specific manual page, such as mount_ufs(1M), "
 			    "for\ndetails about file-system options.  Note "
@@ -771,6 +779,11 @@
 			    pt_to_str(PT_PRIV), gettext("<priv-value>"),
 			    pt_to_str(PT_LIMIT), gettext("<number>"),
 			    pt_to_str(PT_ACTION), gettext("<action-value>"));
+			(void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n",
+			    cmd_to_str(CMD_REMOVE), pt_to_str(PT_VALUE),
+			    pt_to_str(PT_PRIV), gettext("<priv-value>"),
+			    pt_to_str(PT_LIMIT), gettext("<number>"),
+			    pt_to_str(PT_ACTION), gettext("<action-value>"));
 			(void) fprintf(fp, "%s\n\t%s := privileged\n"
 			    "\t%s := none | deny\n", gettext("Where"),
 			    gettext("<priv-value>"), gettext("<action-value>"));
@@ -883,6 +896,8 @@
 		    pt_to_str(PT_AUTOBOOT));
 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
 		    pt_to_str(PT_POOL));
+		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
+		    pt_to_str(PT_LIMITPRIV));
 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s\n", rt_to_str(RT_FS),
 		    pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL),
 		    pt_to_str(PT_RAW), pt_to_str(PT_TYPE),
@@ -1295,6 +1310,7 @@
 	struct zone_rctlvaltab *valptr;
 	int err, arg;
 	char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
+	char *limitpriv;
 	FILE *of;
 	boolean_t autoboot;
 	bool need_to_close = FALSE;
@@ -1354,6 +1370,13 @@
 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
 		    pt_to_str(PT_POOL), pool);
 
+	if (zonecfg_get_limitpriv(handle, &limitpriv) == Z_OK &&
+	    strlen(limitpriv) > 0) {
+		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
+		    pt_to_str(PT_LIMITPRIV), limitpriv);
+		free(limitpriv);
+	}
+
 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
 		zone_perror(zone, err, FALSE);
 		goto done;
@@ -2651,6 +2674,8 @@
 			res_type = RT_AUTOBOOT;
 		} else if (prop_type == PT_POOL) {
 			res_type = RT_POOL;
+		} else if (prop_type == PT_LIMITPRIV) {
+			res_type = RT_LIMITPRIV;
 		} else {
 			zerr(gettext("Cannot set a resource-specific property "
 			    "from the global scope."));
@@ -2753,6 +2778,12 @@
 		else
 			need_to_commit = TRUE;
 		return;
+	case RT_LIMITPRIV:
+		if ((err = zonecfg_set_limitpriv(handle, prop_id)) != Z_OK)
+			zone_perror(zone, err, TRUE);
+		else
+			need_to_commit = TRUE;
+		return;
 	case RT_FS:
 		switch (prop_type) {
 		case PT_DIR:
@@ -3007,6 +3038,21 @@
 }
 
 static void
+info_limitpriv(zone_dochandle_t handle, FILE *fp)
+{
+	char *limitpriv;
+	int err;
+
+	if ((err = zonecfg_get_limitpriv(handle, &limitpriv)) == Z_OK) {
+		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_LIMITPRIV),
+		    limitpriv);
+		free(limitpriv);
+	} else {
+		zone_perror(zone, err, TRUE);
+	}
+}
+
+static void
 output_fs(FILE *fp, struct zone_fstab *fstab)
 {
 	zone_fsopt_t *this;
@@ -3376,6 +3422,7 @@
 		info_zonepath(handle, fp);
 		info_autoboot(handle, fp);
 		info_pool(handle, fp);
+		info_limitpriv(handle, fp);
 		info_ipd(handle, fp, cmd);
 		info_fs(handle, fp, cmd);
 		info_net(handle, fp, cmd);
@@ -3396,6 +3443,9 @@
 	case RT_POOL:
 		info_pool(handle, fp);
 		break;
+	case RT_LIMITPRIV:
+		info_limitpriv(handle, fp);
+		break;
 	case RT_FS:
 		info_fs(handle, fp, cmd);
 		break;
--- a/usr/src/cmd/zonecfg/zonecfg.h	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zonecfg/zonecfg.h	Sun Mar 19 14:21:20 2006 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -19,8 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -80,9 +80,10 @@
 #define	RT_RCTL		9
 #define	RT_ATTR		10
 #define	RT_DATASET	11
+#define	RT_LIMITPRIV	12	/* really a property, but for info ... */
 
 #define	RT_MIN		RT_UNKNOWN
-#define	RT_MAX		RT_DATASET
+#define	RT_MAX		RT_LIMITPRIV
 
 /* property types: increment PT_MAX when expanding this list */
 #define	PT_UNKNOWN	0
@@ -103,9 +104,10 @@
 #define	PT_LIMIT	15
 #define	PT_ACTION	16
 #define	PT_RAW		17
+#define	PT_LIMITPRIV	18
 
 #define	PT_MIN		PT_UNKNOWN
-#define	PT_MAX		PT_RAW
+#define	PT_MAX		PT_LIMITPRIV
 
 #define	MAX_EQ_PROP_PAIRS	3
 
--- a/usr/src/cmd/zonecfg/zonecfg_grammar.y	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y	Sun Mar 19 14:21:20 2006 -0800
@@ -3,9 +3,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -22,7 +21,7 @@
  */
 
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -61,14 +60,14 @@
 %token COMMIT REVERT EXIT SEMICOLON TOKEN ZONENAME ZONEPATH AUTOBOOT POOL NET
 %token FS IPD ATTR DEVICE RCTL SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL
 %token NAME MATCH PRIV LIMIT ACTION VALUE EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET
-%token OPEN_PAREN CLOSE_PAREN COMMA DATASET
+%token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV
 
 %type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET
     property_value OPEN_PAREN CLOSE_PAREN COMMA simple_prop_val
 %type <complex> complex_piece complex_prop_val
 %type <ival> resource_type NET FS IPD DEVICE RCTL ATTR
 %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME
-    MATCH ZONENAME ZONEPATH AUTOBOOT POOL VALUE PRIV LIMIT ACTION
+    MATCH ZONENAME ZONEPATH AUTOBOOT POOL LIMITPRIV VALUE PRIV LIMIT ACTION
 %type <cmd> command
 %type <cmd> add_command ADD
 %type <cmd> cancel_command CANCEL
@@ -438,6 +437,15 @@
 		$$->cmd_res_type = RT_POOL;
 		$$->cmd_prop_nv_pairs = 0;
 	}
+	|	INFO LIMITPRIV
+	{
+		if (($$ = alloc_cmd()) == NULL)
+			YYERROR;
+		cmd = $$;
+		$$->cmd_handler = &info_func;
+		$$->cmd_res_type = RT_LIMITPRIV;
+		$$->cmd_prop_nv_pairs = 0;
+	}
 	|	INFO resource_type property_name EQUAL property_value
 	{
 		if (($$ = alloc_cmd()) == NULL)
@@ -679,6 +687,7 @@
 	| ZONEPATH	{ $$ = PT_ZONEPATH; }
 	| AUTOBOOT	{ $$ = PT_AUTOBOOT; }
 	| POOL		{ $$ = PT_POOL; }
+	| LIMITPRIV	{ $$ = PT_LIMITPRIV; }
 	| ADDRESS	{ $$ = PT_ADDRESS; }
 	| PHYSICAL	{ $$ = PT_PHYSICAL; }
 	| NAME		{ $$ = PT_NAME; }
--- a/usr/src/cmd/zonecfg/zonecfg_lex.l	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/cmd/zonecfg/zonecfg_lex.l	Sun Mar 19 14:21:20 2006 -0800
@@ -3,9 +3,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -19,8 +18,10 @@
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
- *
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -170,6 +171,9 @@
 <TSTATE>pool	{ return POOL; }
 <CSTATE>pool	{ return POOL; }
 
+<TSTATE>limitpriv	{ return LIMITPRIV; }
+<CSTATE>limitpriv	{ return LIMITPRIV; }
+
 <TSTATE>type	{ return TYPE; }
 <CSTATE>type	{ return TYPE; }
 
--- a/usr/src/head/libzonecfg.h	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/head/libzonecfg.h	Sun Mar 19 14:21:20 2006 -0800
@@ -18,6 +18,7 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
@@ -84,6 +85,9 @@
 #define	Z_RESOLVED_PATH		34	/* resolved path mismatch */
 #define	Z_IPV6_ADDR_PREFIX_LEN	35	/* IPv6 address prefix length needed */
 #define	Z_BOGUS_ADDRESS		36	/* not IPv[4|6] address or host name */
+#define	Z_PRIV_PROHIBITED	37	/* specified privilege is prohibited */
+#define	Z_PRIV_REQUIRED		38	/* required privilege is missing */
+#define	Z_PRIV_UNKNOWN		39	/* specified privilege is unknown */
 
 /*
  * Warning: these are shared with the admin/install consolidation.
@@ -352,7 +356,11 @@
 /*
  * Privilege-related functions.
  */
-extern	int	zonecfg_get_privset(priv_set_t *);
+extern	int	zonecfg_default_privset(priv_set_t *);
+extern	int	zonecfg_get_privset(zone_dochandle_t, priv_set_t *,
+    char **);
+extern	int	zonecfg_get_limitpriv(zone_dochandle_t, char **);
+extern	int	zonecfg_set_limitpriv(zone_dochandle_t, char *);
 
 /*
  * Higher-level routines.
--- a/usr/src/lib/libc/port/llib-lc	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/lib/libc/port/llib-lc	Sun Mar 19 14:21:20 2006 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -19,8 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -1737,3 +1737,6 @@
 
 /* private interface for use only by java */
 volatile sc_shared_t *volatile *_thr_schedctl(void);
+
+/* private interface to unmount all autofs mounts */
+int _autofssys(enum autofssys_op, void *);
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c	Sun Mar 19 14:21:20 2006 -0800
@@ -18,6 +18,7 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
@@ -90,6 +91,7 @@
 #define	DTD_ATTR_AUTOBOOT	(const xmlChar *) "autoboot"
 #define	DTD_ATTR_DIR		(const xmlChar *) "directory"
 #define	DTD_ATTR_LIMIT		(const xmlChar *) "limit"
+#define	DTD_ATTR_LIMITPRIV	(const xmlChar *) "limitpriv"
 #define	DTD_ATTR_MATCH		(const xmlChar *) "match"
 #define	DTD_ATTR_NAME		(const xmlChar *) "name"
 #define	DTD_ATTR_PHYSICAL	(const xmlChar *) "physical"
@@ -381,24 +383,59 @@
 }
 
 static int
+fetchprop(xmlNodePtr cur, const xmlChar *propname, char *dst, size_t dstsize)
+{
+	xmlChar *property;
+	size_t srcsize;
+
+	if ((property = xmlGetProp(cur, propname)) == NULL)
+		return (Z_BAD_PROPERTY);
+	srcsize = strlcpy(dst, (char *)property, dstsize);
+	xmlFree(property);
+	if (srcsize >= dstsize)
+		return (Z_TOO_BIG);
+	return (Z_OK);
+}
+
+static int
+fetch_alloc_prop(xmlNodePtr cur, const xmlChar *propname, char **dst)
+{
+	xmlChar *property;
+
+	if ((property = xmlGetProp(cur, propname)) == NULL)
+		return (Z_BAD_PROPERTY);
+	if ((*dst = strdup((char *)property)) == NULL) {
+		xmlFree(property);
+		return (Z_NOMEM);
+	}
+	xmlFree(property);
+	return (Z_OK);
+}
+
+static int
 getrootattr(zone_dochandle_t handle, const xmlChar *propname,
     char *propval, size_t propsize)
 {
 	xmlNodePtr root;
-	xmlChar *property;
-	size_t srcsize;
 	int err;
 
 	if ((err = getroot(handle, &root)) != 0)
 		return (err);
 
-	if ((property = xmlGetProp(root, propname)) == NULL)
-		return (Z_BAD_PROPERTY);
-	srcsize = strlcpy(propval, (char *)property, propsize);
-	xmlFree(property);
-	if (srcsize >= propsize)
-		return (Z_TOO_BIG);
-	return (Z_OK);
+	return (fetchprop(root, propname, propval, propsize));
+}
+
+static int
+get_alloc_rootattr(zone_dochandle_t handle, const xmlChar *propname,
+    char **propval)
+{
+	xmlNodePtr root;
+	int err;
+
+	if ((err = getroot(handle, &root)) != 0)
+		return (err);
+
+	return (fetch_alloc_prop(root, propname, propval));
 }
 
 static int
@@ -771,6 +808,18 @@
 	return (setrootattr(handle, DTD_ATTR_POOL, pool));
 }
 
+int
+zonecfg_get_limitpriv(zone_dochandle_t handle, char **limitpriv)
+{
+	return (get_alloc_rootattr(handle, DTD_ATTR_LIMITPRIV, limitpriv));
+}
+
+int
+zonecfg_set_limitpriv(zone_dochandle_t handle, char *limitprivsize)
+{
+	return (setrootattr(handle, DTD_ATTR_LIMITPRIV, limitprivsize));
+}
+
 /*
  * /etc/zones/index caches a vital piece of information which is also
  * in the <zonename>.xml file: the path to the zone.  This is for performance,
@@ -1500,36 +1549,6 @@
 	return (Z_OK);
 }
 
-static int
-fetchprop(xmlNodePtr cur, const xmlChar *propname, char *dst, size_t dstsize)
-{
-	xmlChar *property;
-	size_t srcsize;
-
-	if ((property = xmlGetProp(cur, propname)) == NULL)
-		return (Z_BAD_PROPERTY);
-	srcsize = strlcpy(dst, (char *)property, dstsize);
-	xmlFree(property);
-	if (srcsize >= dstsize)
-		return (Z_TOO_BIG);
-	return (Z_OK);
-}
-
-static int
-fetch_alloc_prop(xmlNodePtr cur, const xmlChar *propname, char **dst)
-{
-	xmlChar *property;
-
-	if ((property = xmlGetProp(cur, propname)) == NULL)
-		return (Z_BAD_PROPERTY);
-	if ((*dst = strdup((char *)property)) == NULL) {
-		xmlFree(property);
-		return (Z_NOMEM);
-	}
-	xmlFree(property);
-	return (Z_OK);
-}
-
 int
 zonecfg_lookup_filesystem(
 	zone_dochandle_t handle,
@@ -2981,6 +3000,15 @@
 	case Z_BOGUS_ADDRESS:
 		return (dgettext(TEXT_DOMAIN,
 		    "Neither an IPv4 nor an IPv6 address nor a host name"));
+	case Z_PRIV_PROHIBITED:
+		return (dgettext(TEXT_DOMAIN,
+		    "Specified privilege is prohibited"));
+	case Z_PRIV_REQUIRED:
+		return (dgettext(TEXT_DOMAIN,
+		    "Required privilege is missing"));
+	case Z_PRIV_UNKNOWN:
+		return (dgettext(TEXT_DOMAIN,
+		    "Specified privilege is unknown"));
 	default:
 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
 	}
@@ -3340,8 +3368,44 @@
 	return (zonecfg_endent(handle));
 }
 
-/* This will ultimately be configurable. */
-static const char *priv_list[] = {
+/*
+ * The privileges available on the system and described in privileges(5)
+ * fall into four categories with respect to non-global zones; those that
+ * are required in order for a non-global zone to boot, those which are in
+ * the default set of privileges available to non-global zones, those
+ * privileges which should not be allowed to be given to non-global zones
+ * and all other privileges, which are optional and potentially useful for
+ * processes executing inside a non-global zone.
+ *
+ * When privileges are added to the system, a determination needs to be
+ * made as to which category the privilege belongs to.  Ideally,
+ * privileges should be fine-grained enough and the mechanisms they cover
+ * virtualized enough so that they can be made available to non-global
+ * zones.
+ */
+
+/*
+ * Set of privileges required in order to get a zone booted and init(1M)
+ * started.  These cannot be removed from the zone's privilege set.
+ */
+static const char *required_priv_list[] = {
+	PRIV_PROC_EXEC,
+	PRIV_PROC_FORK,
+	PRIV_SYS_MOUNT,
+	NULL
+};
+
+/*
+ * Default set of privileges considered safe for all non-global zones.
+ * These privileges are "safe" in the sense that a privileged process in
+ * the zone cannot affect processes in other non-global zones on the
+ * system or in the global zone.  Privileges which are considered by
+ * default, "unsafe", include ones which affect a global resource, such as
+ * the system clock or physical memory.
+ */
+static const char *default_priv_list[] = {
+	PRIV_CONTRACT_EVENT,
+	PRIV_CONTRACT_OBSERVER,
 	PRIV_FILE_CHOWN,
 	PRIV_FILE_CHOWN_SELF,
 	PRIV_FILE_DAC_EXECUTE,
@@ -3366,24 +3430,54 @@
 	PRIV_SYS_MOUNT,
 	PRIV_SYS_NFS,
 	PRIV_SYS_RESOURCE,
-	PRIV_CONTRACT_EVENT,
-	PRIV_CONTRACT_OBSERVER,
 	NULL
 };
 
+/*
+ * Set of privileges not currently permitted within a non-global zone.
+ * Some of these privileges are overly broad and cover more than one
+ * mechanism in the system.  In other cases, there has not been sufficient
+ * virtualization in the parts of the system the privilege covers to allow
+ * its use within a non-global zone.
+ */
+static const char *prohibited_priv_list[] = {
+	PRIV_DTRACE_KERNEL,
+	PRIV_PROC_ZONE,
+	PRIV_SYS_CONFIG,
+	PRIV_SYS_DEVICES,
+	PRIV_SYS_LINKDIR,
+	PRIV_SYS_NET_CONFIG,
+	PRIV_SYS_RES_CONFIG,
+	PRIV_SYS_SUSER_COMPAT,
+	NULL
+};
+
+/*
+ * Define some of the tokens that priv_str_to_set(3C) recognizes.  Since
+ * the privilege string separator can be any character, although it is
+ * usually a comma character, define these here as well in the event that
+ * they change or are augmented in the future.
+ */
+#define	BASIC_TOKEN		"basic"
+#define	DEFAULT_TOKEN		"default"
+#define	ZONE_TOKEN		"zone"
+#define	TOKEN_PRIV_CHAR		','
+#define	TOKEN_PRIV_STR		","
+
 int
-zonecfg_get_privset(priv_set_t *privs)
+zonecfg_default_privset(priv_set_t *privs)
 {
 	const char **strp;
-	priv_set_t *basic = priv_str_to_set("basic", ",", NULL);
-
+	priv_set_t *basic;
+
+	basic = priv_str_to_set(BASIC_TOKEN, TOKEN_PRIV_STR, NULL);
 	if (basic == NULL)
-		return (Z_INVAL);
+		return (errno == ENOMEM ? Z_NOMEM : Z_INVAL);
 
 	priv_union(basic, privs);
 	priv_freeset(basic);
 
-	for (strp = priv_list; *strp != NULL; strp++) {
+	for (strp = default_priv_list; *strp != NULL; strp++) {
 		if (priv_addset(privs, *strp) != 0) {
 			return (Z_INVAL);
 		}
@@ -3391,6 +3485,180 @@
 	return (Z_OK);
 }
 
+void
+append_priv_token(char *priv, char *str, size_t strlen)
+{
+	if (*str != '\0')
+		(void) strlcat(str, TOKEN_PRIV_STR, strlen);
+	(void) strlcat(str, priv, strlen);
+}
+
+/*
+ * Verify that the supplied string is a valid privilege limit set for a
+ * non-global zone.  This string must not only be acceptable to
+ * priv_str_to_set(3C) which parses it, but it also must resolve to a
+ * privilege set that includes certain required privileges and lacks
+ * certain prohibited privileges.
+ */
+static int
+verify_privset(char *privbuf, priv_set_t *privs, char **privname,
+    boolean_t add_default)
+{
+	char *cp;
+	char *lasts;
+	size_t len;
+	priv_set_t *mergeset;
+	const char **strp;
+	char *tmp;
+	const char *token;
+
+	/*
+	 * The verification of the privilege string occurs in several
+	 * phases.  In the first phase, the supplied string is scanned for
+	 * the ZONE_TOKEN token which is not support as part of the
+	 * "limitpriv" property.
+	 *
+	 * Duplicate the supplied privilege string since strtok_r(3C)
+	 * tokenizes its input by null-terminating the tokens.
+	 */
+	if ((tmp = strdup(privbuf)) == NULL)
+		return (Z_NOMEM);
+	for (cp = strtok_r(tmp, TOKEN_PRIV_STR, &lasts); cp != NULL;
+	    cp = strtok_r(NULL, TOKEN_PRIV_STR, &lasts)) {
+		if (strcmp(cp, ZONE_TOKEN) == 0) {
+			free(tmp);
+			if ((*privname = strdup(ZONE_TOKEN)) == NULL)
+				return (Z_NOMEM);
+			else
+				return (Z_PRIV_UNKNOWN);
+		}
+	}
+	free(tmp);
+
+	if (add_default) {
+		/*
+		 * If DEFAULT_TOKEN was specified, a string needs to be
+		 * built containing the privileges from the default, safe
+		 * set along with those of the "limitpriv" property.
+		 */
+		len = strlen(privbuf) + sizeof (BASIC_TOKEN) + 2;
+		for (strp = default_priv_list; *strp != NULL; strp++)
+			len += strlen(*strp) + 1;
+		tmp = alloca(len);
+		*tmp = '\0';
+
+		append_priv_token(BASIC_TOKEN, tmp, len);
+		for (strp = default_priv_list; *strp != NULL; strp++)
+			append_priv_token((char *)*strp, tmp, len);
+		(void) strlcat(tmp, TOKEN_PRIV_STR, len);
+		(void) strlcat(tmp, privbuf, len);
+	} else {
+		tmp = privbuf;
+	}
+
+
+	/*
+	 * In the next phase, attempt to convert the merged privilege
+	 * string into a privilege set.  In the case of an error, either
+	 * there was a memory allocation failure or there was an invalid
+	 * privilege token in the string.  In either case, return an
+	 * appropriate error code but in the event of an invalid token,
+	 * allocate a string containing its name and return that back to
+	 * the caller.
+	 */
+	mergeset = priv_str_to_set(tmp, TOKEN_PRIV_STR, &token);
+	if (mergeset == NULL) {
+		if (token == NULL)
+			return (Z_NOMEM);
+		if ((cp = strchr(token, TOKEN_PRIV_CHAR)) != NULL)
+			*cp = '\0';
+		if ((*privname = strdup(token)) == NULL)
+			return (Z_NOMEM);
+		else
+			return (Z_PRIV_UNKNOWN);
+	}
+
+	/*
+	 * Next, verify that none of the prohibited zone privileges are
+	 * present in the merged privilege set.
+	 */
+	for (strp = prohibited_priv_list; *strp != NULL; strp++) {
+		if (priv_ismember(mergeset, *strp)) {
+			priv_freeset(mergeset);
+			if ((*privname = strdup(*strp)) == NULL)
+				return (Z_NOMEM);
+			else
+				return (Z_PRIV_PROHIBITED);
+		}
+	}
+
+	/*
+	 * Finally, verify that all of the required zone privileges are
+	 * present in the merged privilege set.
+	 */
+	for (strp = required_priv_list; *strp != NULL; strp++) {
+		if (!priv_ismember(mergeset, *strp)) {
+			priv_freeset(mergeset);
+			if ((*privname = strdup(*strp)) == NULL)
+				return (Z_NOMEM);
+			else
+				return (Z_PRIV_REQUIRED);
+		}
+	}
+
+	priv_copyset(mergeset, privs);
+	priv_freeset(mergeset);
+	return (Z_OK);
+}
+
+/*
+ * Fill in the supplied privilege set with either the default, safe set of
+ * privileges suitable for a non-global zone, or one based on the
+ * "limitpriv" property in the zone's configuration.
+ *
+ * In the event of an invalid privilege specification in the
+ * configuration, a string is allocated and returned containing the
+ * "privilege" causing the issue.  It is the caller's responsibility to
+ * free this memory when it is done with it.
+ */
+int
+zonecfg_get_privset(zone_dochandle_t handle, priv_set_t *privs,
+    char **privname)
+{
+	char *cp;
+	int err;
+	int limitlen;
+	char *limitpriv = NULL;
+
+	/*
+	 * Attempt to lookup the "limitpriv" property.  If it does not
+	 * exist or matches the string DEFAULT_TOKEN exactly, then the
+	 * default, safe privilege set is returned.
+	 */
+	err = zonecfg_get_limitpriv(handle, &limitpriv);
+	if (err != Z_OK)
+		return (err);
+	limitlen = strlen(limitpriv);
+	if (limitlen == 0 || strcmp(limitpriv, DEFAULT_TOKEN) == 0) {
+		free(limitpriv);
+		return (zonecfg_default_privset(privs));
+	}
+
+	/*
+	 * Check if the string DEFAULT_TOKEN is the first token in a list
+	 * of privileges.
+	 */
+	cp = strchr(limitpriv, TOKEN_PRIV_CHAR);
+	if (cp != NULL &&
+	    strncmp(limitpriv, DEFAULT_TOKEN, cp - limitpriv) == 0)
+		err = verify_privset(cp + 1, privs, privname, B_TRUE);
+	else
+		err = verify_privset(limitpriv, privs, privname, B_FALSE);
+
+	free(limitpriv);
+	return (err);
+}
+
 int
 zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
 {
@@ -3920,7 +4188,7 @@
 is_ping(sysevent_t *ev)
 {
 	if (strcmp(sysevent_get_subclass_name(ev),
-		ZONE_EVENT_PING_SUBCLASS) == 0) {
+	    ZONE_EVENT_PING_SUBCLASS) == 0) {
 		return (B_TRUE);
 	} else {
 		return (B_FALSE);
@@ -3967,11 +4235,11 @@
 
 		if ((nvlist_lookup_string(l, ZONE_CB_NAME, &zonename) == 0) &&
 		    (nvlist_lookup_string(l, ZONE_CB_NEWSTATE, &newstate)
-			== 0) &&
+		    == 0) &&
 		    (nvlist_lookup_string(l, ZONE_CB_OLDSTATE, &oldstate)
-			== 0) &&
+		    == 0) &&
 		    (nvlist_lookup_uint64(l, ZONE_CB_TIMESTAMP,
-			    (uint64_t *)&when) == 0) &&
+		    (uint64_t *)&when) == 0) &&
 		    (nvlist_lookup_int32(l, ZONE_CB_ZONEID, &zid) == 0)) {
 			ret = zevtchan->zn_callback(zonename, zid, newstate,
 			    oldstate, when, zevtchan->zn_private);
@@ -4144,7 +4412,7 @@
 	}
 
 	if (sysevent_evc_bind(ZONE_EVENT_CHANNEL, &(zevtchan->zn_eventchan),
-		0) != 0)
+	    0) != 0)
 		goto out2;
 
 	do {
@@ -4624,7 +4892,8 @@
 			char	*nlp;
 
 			for (j = 0; j < 4 &&
-			    strtok_r(NULL, " ", &lastp) != NULL; j++);
+			    strtok_r(NULL, " ", &lastp) != NULL; j++)
+				;
 			/*
 			 * If there are < 4 fields this entry is corrupt,
 			 * just skip it.
--- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Sun Mar 19 14:21:20 2006 -0800
@@ -23,7 +23,7 @@
  Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  Use is subject to license terms.
 
-    ident	"%Z%%M%	%I%	%E% SMI"
+ ident	"%Z%%M%	%I%	%E% SMI"
 -->
 
 <!--Element Definitions-->
@@ -57,7 +57,7 @@
 			limit		CDATA #REQUIRED
 			action		CDATA #REQUIRED>
 
-<!ELEMENT rctl          (rctl-value)*>
+<!ELEMENT rctl		(rctl-value)*>
 
 <!ATTLIST rctl		name		CDATA #REQUIRED>
 
@@ -101,4 +101,5 @@
 			zonepath	CDATA #REQUIRED
 			autoboot	(true | false) #REQUIRED
 			pool		CDATA ""
+			limitpriv	CDATA ""
 			version		NMTOKEN #FIXED '1'>
--- a/usr/src/lib/libzonecfg/spec/libzonecfg.spec	Sun Mar 19 00:05:12 2006 -0800
+++ b/usr/src/lib/libzonecfg/spec/libzonecfg.spec	Sun Mar 19 14:21:20 2006 -0800
@@ -1,6 +1,3 @@
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
 #
 # CDDL HEADER START
 #
@@ -21,9 +18,15 @@
 #
 # CDDL HEADER END
 #
+
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
 # ident	"%Z%%M%	%I%	%E% SMI"
 #
 # lib/libzonecfg/spec/libzonecfg.spec
+#
 
 function	zonecfg_set_root
 include		<libzonecfg.h>
@@ -140,6 +143,18 @@
 version		SUNWprivate_1.1
 end		
 
+function	zonecfg_get_limitpriv
+include		<libzonecfg.h>
+declaration	int zonecfg_get_limitpriv(zone_dochandle_t, char **)
+version		SUNWprivate_1.1
+end		
+
+function	zonecfg_set_limitpriv
+include		<libzonecfg.h>
+declaration	int zonecfg_set_limitpriv(zone_dochandle_t, char *)
+version		SUNWprivate_1.1
+end		
+
 function	zonecfg_add_fs_option
 include		<libzonecfg.h>
 declaration	int zonecfg_add_fs_option(struct zone_fstab *, char *)
@@ -626,9 +641,16 @@
 version		SUNWprivate_1.1
 end		
 
+function	zonecfg_default_privset
+include		<libzonecfg.h>
+declaration	int zonecfg_default_privset(struct priv_set *)
+version		SUNWprivate_1.1
+end
+
 function	zonecfg_get_privset
 include		<libzonecfg.h>
-declaration	int zonecfg_get_privset(struct priv_set *)
+declaration	int zonecfg_get_privset(zone_dochandle_t, priv_set_t *, \
+		    char **)
 version		SUNWprivate_1.1
 end