usr/src/cmd/allocate/allocate3.c
changeset 1676 37f4a3e2bd99
parent 1269 f33c74eed274
child 2621 4ea88858d952
--- a/usr/src/cmd/allocate/allocate3.c	Thu Mar 23 19:49:27 2006 -0800
+++ b/usr/src/cmd/allocate/allocate3.c	Fri Mar 24 12:29: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,6 +18,7 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
@@ -38,11 +38,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <unistd.h>
-
 #include <bsm/devices.h>
-#include <bsm/audit_uevents.h>
-
+#include <sys/acl.h>
+#include <tsol/label.h>
+#include <syslog.h>
+#include <limits.h>
+#include <user_attr.h>
+#include <secdb.h>
+#include <sys/mkdev.h>
 #include <sys/acl.h>
 #include <sys/file.h>
 #include <sys/procfs.h>
@@ -52,198 +57,510 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-
+#include <utime.h>
+#include <libgen.h>
+#include <zone.h>
+#include <nss_dbdefs.h>
+#include <bsm/devalloc.h>
 #include "allocate.h"
 
-#ifdef	DEBUG
+extern void print_error(int, char *);
+
+#if	defined(DEBUG) || defined(lint)
 #define	dprintf(s, a) (void) fprintf(stderr, s, a)
 #define	dperror(s) perror(s)
 #else	/* !DEBUG */
-#define	dprintf(s, a)
-#define	dperror(s)
+#define	dprintf(s, a)	0
+#define	dperror(s)	0
 #endif	/* DEBUG */
 
-#define	EXIT(number) { \
-	if (optflg & FORCE) \
-		error = number; \
-	else \
-		return (number); \
-}
+#define	DEV_ERRORED(sbuf)	(((sbuf).st_mode & ~S_IFMT) == ALLOC_ERR_MODE)
+#define	DEV_INVALID(sbuf)	(((sbuf).st_mode & ~S_IFMT) == ALLOC_INVALID)
+#define	DEV_ALLOCATED(sbuf)	((sbuf).st_uid != ALLOC_UID || \
+			!(((sbuf).st_mode & ~S_IFMT) == DEALLOC_MODE || \
+			DEV_ERRORED(sbuf) || DEV_INVALID(sbuf)))
 
-#define	DEV_ALLOCATED(sbuf)	((sbuf).st_uid != ALLOC_UID || \
-				((sbuf).st_mode & ~S_IFMT) == ALLOC_MODE)
+#define	ALLOC_CLEAN		"-A"
+#define	DEALLOC_CLEAN		"-D"
+#define	DAC_DIR			"/etc/security/dev"
+#define	DEVICE_AUTH_SEPARATOR	","
+#define	LOCALDEVICE		"/dev/console"
+#define	PROCFS			"/proc/"
+#define	SFF_NO_ERROR		0x1
 
-#define	DEVICE_AUTH_SEPARATOR	","
-#define	PROCFS	"/proc/"
+#define	ALLOC_BY_NONE		-1
+#define	CHECK_DRANGE		1
+#define	CHECK_URANGE		2
+#define	CHECK_ZLABEL		3
 
 extern void audit_allocate_list(char *);
 extern void audit_allocate_device(char *);
 
+extern int	system_labeled;
 extern char	*newenv[];
 
+struct state_file {
+	int	sf_flags;
+	char	sf_path[MAXPATHLEN];
+};
+
+struct file_info {
+	struct stat	fi_stat;
+	char		*fi_message;
+};
+
+struct zone_path {
+	int	count;
+	char	**path;
+};
+
+struct devnames {
+	char **dnames;
+};
+
+static int _dev_file_name(struct state_file *, devmap_t *);
+static int lock_dev(char *);
+static int _check_label(devalloc_t *, char *, uid_t, int);
+static int create_znode(char *, struct zone_path *, devmap_t *);
+static int remove_znode(char *, devmap_t *);
+static int update_device(char **, char *, int);
+
 /*
- * Checks if the specified user has any of the authorizations in the
- * list of authorizations
+ * checks if the invoking user is local to the device
  */
-
-static int
-is_authorized(char *auth_list, uid_t uid)
+/*ARGSUSED*/
+int
+_is_local(uid_t uid)
 {
-	char	*auth;
-	struct passwd *pw;
+	struct stat	statbuf;
 
-	pw = getpwuid(uid);
-	if (pw == NULL) {
-		dprintf("Can't get user info for uid=%d\n", (int)uid);
-		return (0);
-	}
+	if (stat(LOCALDEVICE, &statbuf) == 0 &&
+	    statbuf.st_uid == uid)
+		return (1);
 
-	auth = strtok(auth_list, DEVICE_AUTH_SEPARATOR);
-	while (auth != NULL) {
-		if (chkauthattr(auth, pw->pw_name))
-			return (1);
-		auth = strtok(NULL, DEVICE_AUTH_SEPARATOR);
-	}
 	return (0);
 }
 
-static int
-check_devs(char *list)
+/*
+ * Checks if the user with the specified uid has the specified authorization
+ */
+int
+_is_authorized(char *auths, uid_t uid)
 {
-	char	*file;
+	char		*dcp, *authlist, *lasts;
+	char		pw_buf[NSS_BUFLEN_PASSWD];
+	struct passwd	pw_ent;
+
+	/*
+	 * first, the easy cases
+	 */
+	if (strcmp(auths, "@") == 0)
+		return (1);
+	if (strcmp(auths, "*") == 0)
+		return (ALLOC_BY_NONE);
+	if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL)
+		return (0);
+	if (strpbrk(auths, DEVICE_AUTH_SEPARATOR) == NULL)
+		return (chkauthattr(auths, pw_ent.pw_name));
+	authlist = strdup(auths);
+	if (authlist == NULL)
+		return (0);
+	for (dcp = authlist;
+	    (dcp = strtok_r(dcp, DEVICE_AUTH_SEPARATOR, &lasts)) != NULL;
+	    dcp = NULL) {
+		if (chkauthattr(dcp, pw_ent.pw_name))
+			break;
+	}
+	free(authlist);
+
+	return (dcp != NULL);
+}
+
+/*
+ * Checks if the specified user has authorization for the device
+ */
+int
+_is_dev_authorized(devalloc_t *da, uid_t uid)
+{
+	int	ares;
+	char	*auth_list, *dcp, *subauth = NULL;
 
-	file = strtok(list, " ");
-	while (file != NULL) {
+	auth_list = da->da_devauth;
+	if (auth_list == NULL)
+		return (0);
+	dcp = strpbrk(auth_list, KV_TOKEN_DELIMIT);
+	if (dcp == NULL)
+		return (_is_authorized(auth_list, uid));
+	if (_is_local(uid)) {
+		/* the local authorization is before the separator */
+		ares = dcp - auth_list;
+		subauth = malloc(ares + 1);
+		if (subauth == NULL)
+			return (0);
+		(void) strlcpy(subauth, auth_list, (ares + 1));
+		auth_list = subauth;
+	} else
+		auth_list = dcp + 1;
+	ares = _is_authorized(auth_list, uid);
+	if (subauth != NULL)
+		free(subauth);
+
+	return (ares);
+}
+
+int
+check_devs(devmap_t *dm)
+{
+	int	status = 0;
+	char	**file;
 
-		if (access(file, F_OK) == -1) {
-			dprintf("Unable to access file %s\n", file);
-			return (-1);
+	if (dm->dmap_devarray == NULL)
+		return (NODMAPERR);
+	for (file = dm->dmap_devarray; *file != NULL; file++) {
+		if ((status = access(*file, F_OK)) == -1) {
+			dprintf("Unable to access file %s\n", *file);
+			break;
 		}
-		file = strtok(NULL, " ");
 	}
+
+	return (status);
+}
+
+int
+print_da_defs(da_defs_t *da_defs)
+{
+	char	optbuf[BUFSIZ];
+	char	*p = NULL;
+
+	if (da_defs->devopts == NULL) {
+		dprintf("No default attributes for %s\n", da_defs->devtype);
+		return (DEFATTRSERR);
+	}
+	(void) printf("dev_type=%s\n", da_defs->devtype);
+	if (_kva2str(da_defs->devopts, optbuf, sizeof (optbuf), KV_ASSIGN,
+	    KV_TOKEN_DELIMIT) == 0) {
+		if (p = rindex(optbuf, ':'))
+			*p = '\0';
+		(void) printf("\t%s\n", optbuf);
+	}
+
 	return (0);
 }
 
-static void
-print_dev(devmap_t *dev_list)
+void
+print_dev_attrs(int optflag, devalloc_t *da, devmap_t *dm,
+    struct file_info *fip)
 {
-	char	*file;
+	char	*p = NULL;
+	char	optbuf[BUFSIZ];
 
-	(void) printf(gettext("device: %s "), dev_list->dmap_devname);
-	(void) printf(gettext("type: %s "), dev_list->dmap_devtype);
-	(void) printf(gettext("files: "));
+	(void) printf("device=%s%s", dm->dmap_devname, KV_DELIMITER);
+	(void) printf("type=%s%s", dm->dmap_devtype, KV_DELIMITER);
+	(void) printf("auths=%s%s",
+	    (da->da_devauth ? da->da_devauth : ""), KV_DELIMITER);
+	(void) printf("clean=%s%s",
+	    (da->da_devexec ? da->da_devexec : ""), KV_DELIMITER);
+	if (da->da_devopts != NULL) {
+		if (_kva2str(da->da_devopts, optbuf, sizeof (optbuf),
+		    KV_ASSIGN, KV_TOKEN_DELIMIT) == 0) {
+			if (p = rindex(optbuf, ':'))
+				*p = '\0';
+			(void) printf("%s", optbuf);
+		}
+	}
+	(void) printf("%s", KV_DELIMITER);
+	if (optflag & WINDOWING) {
+		if (DEV_INVALID(fip->fi_stat))
+			(void) printf("owner=/INVALID:%s%s", fip->fi_message,
+			    KV_DELIMITER);
+		else if (DEV_ERRORED(fip->fi_stat))
+			(void) printf("owner=/ERROR%s", KV_DELIMITER);
+		else if (!DEV_ALLOCATED(fip->fi_stat))
+			(void) printf("owner=/FREE%s", KV_DELIMITER);
+		else
+			(void) printf("owner=%ld%s", fip->fi_stat.st_uid,
+			    KV_DELIMITER);
+	}
+	(void) printf("files=%s", dm->dmap_devlist);
+	(void) printf("\n");
+}
 
-	file = strtok(dev_list->dmap_devlist, " ");
-	while (file != NULL) {
-		(void) printf("%s ", file);
-		file = strtok(NULL, " ");
+void
+print_dev(devmap_t *dm)
+{
+	char	**file;
+
+	(void) printf(gettext("device: %s "), dm->dmap_devname);
+	(void) printf(gettext("type: %s "), dm->dmap_devtype);
+	(void) printf(gettext("files:"));
+	file = dm->dmap_devarray;
+	if (file != NULL) {
+		for (; *file != NULL; file++)
+			(void) printf(" %s", *file);
 	}
 	(void) printf("\n");
 }
 
-static int
-list_device(int optflg, uid_t uid, char *device)
+/* ARGSUSED */
+int
+_list_device(int optflag, uid_t uid, devalloc_t *da, char *zonename)
 {
-	devalloc_t *dev_ent;
-	devmap_t *dev_list;
-	char	file_name[MAXPATHLEN];
-	struct	stat stat_buf;
-	char	*list;
-	int	bytes_formated;
-
-	if ((dev_ent = getdanam(device)) == NULL) {
-		if ((dev_list = getdmapdev(device)) == NULL) {
-			dprintf("Unable to find %s in the allocate database\n",
-			    device);
-			return (NODMAPENT);
-		} else if ((dev_ent = getdanam(dev_list->dmap_devname)) ==
-		    NULL) {
-			dprintf("Unable to find %s in the allocate database\n",
-			    device);
-			return (NODAENT);
-		}
-	} else if ((dev_list = getdmapnam(device)) == NULL) {
-		dprintf("Unable to find %s in the allocate database\n", device);
-		return (NODMAPENT);
-	}
+	int			bytes = 0;
+	int			error = 0;
+	int			is_authorized = 0;
+	char			*fname = NULL;
+	char			file_name[MAXPATHLEN];
+	devmap_t		*dm;
+	struct file_info	fi;
+	struct state_file	sf;
 
-	bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
-	    dev_ent->da_devname);
-	if (bytes_formated <= 0) {
-		return (DEVNAME_ERR);
-	} else if (bytes_formated >= MAXPATHLEN) {
-		dprintf("device name %s is too long.\n", dev_ent->da_devname);
-		return (DEVNAME_TOOLONG);
+	setdmapent();
+	if ((dm = getdmapnam(da->da_devname)) == NULL) {
+		enddmapent();
+		dprintf("Unable to find %s in the maps database\n",
+		    da->da_devname);
+		return (NODMAPERR);
+	}
+	enddmapent();
+	if (system_labeled) {
+		if ((error = _dev_file_name(&sf, dm)) != 0) {
+			freedmapent(dm);
+			dprintf("Unable to find %s device files\n",
+			    da->da_devname);
+			error = NODMAPERR;
+			goto out;
+		}
+		fname = sf.sf_path;
+	} else {
+		bytes = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
+		    da->da_devname);
+		if (bytes <= 0) {
+			error = DEVNAMEERR;
+			goto out;
+		} else if (bytes >= MAXPATHLEN) {
+			dprintf("device name %s is too long.\n",
+			    da->da_devname);
+			error = DEVLONGERR;
+			goto out;
+		}
+		fname = file_name;
+	}
+	if (stat(fname, &fi.fi_stat) != 0) {
+		dprintf("Unable to stat %s\n", fname);
+		dperror("Error:");
+		error = DACACCERR;
+		goto out;
 	}
-
-	if (stat(file_name, &stat_buf)) {
-		dprintf("Unable to stat %s\n", file_name);
-		dperror("Error:");
-		return (DACACC);
+	if (optflag & USERID)
+		is_authorized = 1;
+	else
+		is_authorized = _is_dev_authorized(da, uid);
+	if (optflag & LISTFREE) {	/* list_devices -n */
+		/*
+		 * list all free devices
+		 */
+		if (DEV_ALLOCATED(fi.fi_stat)) {
+				error = PREALLOCERR;
+				goto out;
+		}
+		if (system_labeled) {
+			/*
+			 * for this free device, check if -
+			 * 1. user has authorization to allocate
+			 * 2. the zone label is within the label range of the
+			 *    device
+			 */
+			if (is_authorized == ALLOC_BY_NONE) {
+				error = DAUTHERR;
+				goto out;
+			} else if (is_authorized == 0) {
+				error = UAUTHERR;
+				goto out;
+			}
+			if (_check_label(da, zonename, uid,
+			    CHECK_DRANGE) != 0) {
+				error = LABELRNGERR;
+				goto out;
+			}
+		}
+	} else if (optflag & LISTALLOC) {	/*  list_devices -u */
+		/*
+		 * list all allocated devices
+		 */
+		if (!DEV_ALLOCATED(fi.fi_stat)) {
+			error = DEVNALLOCERR;
+			goto out;
+		}
+		if (fi.fi_stat.st_uid != uid) {
+			error = DEVSTATEERR;
+			goto out;
+		}
+		if (system_labeled) {
+			/*
+			 * check if the zone label equals the label at which
+			 * the device is allocated.
+			 */
+			if (_check_label(da, zonename, uid,
+			    CHECK_ZLABEL) != 0) {
+				error = LABELRNGERR;
+				goto out;
+			}
+		}
+	} else if (optflag & LISTALL) {		/* list_devices -l */
+		/*
+		 * list all devices - free and allocated - available
+		 */
+		if (DEV_ALLOCATED(fi.fi_stat)) {
+			if (optflag & WINDOWING &&
+			    (is_authorized == ALLOC_BY_NONE)) {
+				/*
+				 * don't complain if we're here for the GUI.
+				 */
+				error = 0;
+			} else if (fi.fi_stat.st_uid != uid) {
+				if (!(optflag & WINDOWING)) {
+					error = ALLOCUERR;
+					goto out;
+				}
+			}
+			if (system_labeled && !(optflag & WINDOWING)) {
+				/*
+				 * if we're not displaying in the GUI,
+				 * check if the zone label equals the label
+				 * at which the device is allocated.
+				 */
+				if (_check_label(da, zonename, uid,
+				    CHECK_ZLABEL) != 0) {
+					error = LABELRNGERR;
+					goto out;
+				}
+			}
+		} else if (system_labeled && !(optflag & WINDOWING)) {
+			/*
+			 * if we're not displaying in the GUI,
+			 * for this free device, check if -
+			 * 1. user has authorization to allocate
+			 * 2. the zone label is within the label range of the
+			 *    device
+			 */
+			if (is_authorized == ALLOC_BY_NONE) {
+				error = DAUTHERR;
+				goto out;
+			} else if (is_authorized == 0) {
+				error = UAUTHERR;
+				goto out;
+			}
+			if (_check_label(da, zonename, uid,
+			    CHECK_DRANGE) != 0) {
+				error = LABELRNGERR;
+				goto out;
+			}
+		}
 	}
-
-	if ((optflg & FREE) && DEV_ALLOCATED(stat_buf))
-		return (ALLOC);
+	if (system_labeled && DEV_ERRORED(fi.fi_stat) && !(optflag & LISTALL)) {
+		error = DEVSTATEERR;
+		goto out;
+	}
+	if (check_devs(dm) == -1) {
+		error = DSPMISSERR;
+		goto out;
+	}
+	if (optflag & LISTATTRS)
+		print_dev_attrs(optflag, da, dm, &fi);
+	else
+		print_dev(dm);
 
-	if ((optflg & LIST) && DEV_ALLOCATED(stat_buf) &&
-	    (stat_buf.st_uid != uid))
-		return (ALLOC_OTHER);
-
-	if ((optflg & CURRENT) && (stat_buf.st_uid != uid))
-		return (NALLOC);
-
-	if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE)
-		return (ALLOCERR);
+	error = 0;
 
-	if ((list = strdup(dev_list->dmap_devlist)) == NULL)
-		return (SYSERROR);
-
-	if (check_devs(list) == -1) {
-		free(list);
-		return (DSPMISS);
-	}
-
-	print_dev(dev_list);
-
-	free(list);
-	return (0);
+out:
+	freedmapent(dm);
+	return (error);
 }
 
+/* ARGSUSED */
 int
-list_devices(int optflg, uid_t uid, char *device)
+list_devices(int optflag, uid_t uid, char *device, char *zonename)
 {
-	DIR   * dev_dir;
-	struct dirent *dac_file;
-	int	error = 0, ret_code = 1;
+	int		error = 0;
+	da_defs_t	*da_defs;
+	devalloc_t	*da;
 
-	if (optflg & USERID) {
-		if (!is_authorized(DEVICE_REVOKE_AUTH, getuid()))
-			return (NOTAUTH);
+	if (system_labeled && optflag & WINDOWING && !(optflag & LISTATTRS)) {
+		/*
+		 * Private interface for GUI.
+		 */
+		(void) lock_dev(NULL);
+		(void) puts(DA_DB_LOCK);
+		return (0);
+	}
+	if (optflag & USERID) {
+		/*
+		 * we need device.revoke to list someone else's devices
+		 */
+		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
+			return (UAUTHERR);
+	}
+	if (system_labeled) {
+		if (!(optflag & USERID) &&
+		    !_is_authorized(DEFAULT_DEV_ALLOC_AUTH, uid))
+			/*
+			 * we need device.allocate to list our devices
+			 */
+			return (UAUTHERR);
+		if (optflag & LISTDEFS) {
+			/*
+			 * list default attrs from devalloc_defaults
+			 */
+			setdadefent();
+			if (device) {
+				/*
+				 * list default attrs for this device type
+				 */
+				da_defs = getdadeftype(device);
+				if (da_defs == NULL) {
+					enddadefent();
+					dprintf("No default attributes for "
+					    "%s\n", device);
+					return (DEFATTRSERR);
+				}
+				error = print_da_defs(da_defs);
+				freedadefent(da_defs);
+			} else {
+				/*
+				 * list everything in devalloc_defaults
+				 */
+				while ((da_defs = getdadefent()) != NULL) {
+					(void) print_da_defs(da_defs);
+					freedadefent(da_defs);
+				}
+			}
+			enddadefent();
+			return (error);
+		}
 	}
 	setdaent();
-
 	if (device) {
-		return (list_device(optflg, uid, device));
-	}
-
-	if ((dev_dir = opendir(DAC_DIR)) == NULL) {
-
-		dperror("Can't open DAC_DIR");
-		return (DACACC);
-	}
-
-	while ((dac_file = readdir(dev_dir)) != NULL) {
-		if ((strcmp(dac_file->d_name, ".") == 0) ||
-		    (strcmp(dac_file->d_name, "..") == 0)) {
-			continue;
-		} else {
-			error = list_device(optflg, uid, dac_file->d_name);
-			ret_code = ret_code ? error : ret_code;
+		/*
+		 * list this device
+		 */
+		if ((da = getdanam(device)) == NULL) {
+			enddaent();
+			return (NODAERR);
+		}
+		error = _list_device(optflag, uid, da, zonename);
+		freedaent(da);
+	} else {
+		/*
+		 * list all devices
+		 */
+		while ((da = getdaent()) != NULL) {
+			(void) _list_device(optflag, uid, da, zonename);
+			freedaent(da);
 		}
 	}
-	(void) closedir(dev_dir);
 	enddaent();
-	return (ret_code);
+
+	return (error);
 }
 
 /*
@@ -251,24 +568,41 @@
  * This uses a fancy chmod() by setting a minimal ACL which sets the mode
  * and discards any existing ACL.
  */
-
-static int
-newdac(char *file, uid_t owner, gid_t group, o_mode_t mode)
+int
+_newdac(char *file, uid_t owner, gid_t group, o_mode_t mode)
 {
-	int		err = 0;
+	int	err = 0;
 
-	do {
+	if (mode == ALLOC_MODE) {
 		if (chown(file, owner, group) == -1) {
-			dperror("newdac, unable to chown");
-			err = CHOWN_PERR;
+			dperror("newdac: unable to chown");
+			err = CHOWNERR;
+		}
+	} else do {
+		if (chown(file, owner, group) == -1) {
+			dperror("newdac: unable to chown");
+			err = CHOWNERR;
 		}
 	} while (fdetach(file) == 0);
 
-	err = acl_strip(file, owner, group, (mode_t)mode);
+	if (err)
+		return (err);
+
+	if (strncmp(file, "/dev/", strlen("/dev/")) != 0) {
+		/*
+		 * This could be a SunRay device that is in /tmp.
+		 */
+		if (chmod(file, mode) == -1) {
+			dperror("newdac: unable to chmod");
+			err = SETACLERR;
+		}
+	} else {
+		err = acl_strip(file, owner, group, (mode_t)mode);
+	}
 
 	if (err != 0) {
-		dperror("newdac, unable to setacl");
-		err = SETACL_PERR;
+		dperror("newdac: unable to setacl");
+		err = SETACLERR;
 	}
 
 	return (err);
@@ -277,41 +611,60 @@
 static int
 lock_dev(char *file)
 {
-	int	fd;
+	int	lockfd = -1;
 
+	if ((file == NULL) || system_labeled)
+		file = DA_DEV_LOCK;
 	dprintf("locking %s\n", file);
-	if ((fd = open(file, O_RDWR)) == -1) {
-		dperror("lock_dev, cannot open DAC file");
-		return (DACACC);
+	if ((lockfd = open(file, O_RDWR | O_CREAT, 0600)) == -1) {
+		dperror("lock_dev: cannot open lock file");
+		return (DEVLKERR);
 	}
-
-	if (lockf(fd, F_TLOCK, 0) == -1) {
-		dperror("lock_dev, cannot set lock");
-		return (DACLCK);
+	if (lockf(lockfd, F_TLOCK, 0) == -1) {
+		dperror("lock_dev: cannot set lock");
+		return (DEVLKERR);
 	}
 
 	return (0);
 }
 
-static int
-mk_alloc(char *list, uid_t uid)
+int
+mk_alloc(devmap_t *list, uid_t uid, struct zone_path *zpath)
 {
-	char	*file;
-	int	err;
-
-	file = strtok(list, " ");
-	while (file != NULL) {
+	int	i;
+	int	error = 0;
+	char	**file;
+	gid_t	gid = getgid();
+	mode_t	mode = ALLOC_MODE;
 
-		dprintf("Allocating %s\n", file);
-		if ((err = newdac(file, uid, getgid(), ALLOC_MODE)) != 0) {
-			(void) newdac(file, ALLOC_UID, ALLOC_GID,
+	file = list->dmap_devarray;
+	if (file == NULL)
+		return (NODMAPERR);
+	for (; *file != NULL; file++) {
+		dprintf("Allocating %s\n", *file);
+		if ((error = _newdac(*file, uid, gid, mode)) != 0) {
+			(void) _newdac(*file, ALLOC_ERRID, ALLOC_GID,
 			    ALLOC_ERR_MODE);
-			return (err);
+			break;
 		}
+	}
+	if (system_labeled && zpath->count && (error == 0)) {
+		/*
+		 * mark as allocated any new device nodes that we
+		 * created in local zone
+		 */
+		for (i = 0; i < zpath->count; i++) {
+			dprintf("Allocating %s\n", zpath->path[i]);
+			if ((error = _newdac(zpath->path[i], uid, gid,
+			    mode)) != 0) {
+				(void) _newdac(zpath->path[i], ALLOC_ERRID,
+				    ALLOC_GID, ALLOC_ERR_MODE);
+				break;
+			}
+		}
+	}
 
-		file = strtok(NULL, " ");
-	}
-	return (0);
+	return (error);
 }
 
 /*
@@ -319,33 +672,32 @@
  * because "/usr/sbin/fuser -k file" kills all processes
  * working with the file, even "vold" (bug #4095152).
  */
-static int
-mk_revoke(int optflg, char *file)
+int
+mk_revoke(int optflag, char *file)
 {
-	char buf[MAXPATHLEN];
-	int r = 0, p[2], fp, lock;
-	FILE *ptr;
-	prpsinfo_t info;
-	pid_t pid, c_pid;
+	int		r = 0, p[2], fp, lock;
+	int		fuserpid;
+	char		buf[MAXPATHLEN];
+	FILE		*ptr;
+	pid_t		c_pid;
+	prpsinfo_t	info;
 
 	(void) strcpy(buf, PROCFS);
-
 	/*
-	 * vfork() and execle() just to make the same output
+	 * vfork() and execl() just to make the same output
 	 * as before fixing of bug #4095152.
 	 * The problem is that the "fuser" command prints
 	 * one part of output into stderr and another into stdout,
 	 * but user sees them mixed. Of course, better to change "fuser"
 	 * or to intercept and not to print its output.
 	 */
-	if (!(optflg & SILENT)) {
+	if (!(optflag & SILENT)) {
 		c_pid = vfork();
 		if (c_pid == -1)
 			return (-1);
 		if (c_pid == 0) {
 			dprintf("first exec fuser %s\n", file);
-			(void) execle("/usr/sbin/fuser", "fuser", file, NULL,
-			    newenv);
+			(void) execl("/usr/sbin/fuser", "fuser", file, NULL);
 			dperror("first exec fuser");
 			_exit(1);
 		}
@@ -355,21 +707,18 @@
 		if (WEXITSTATUS(lock) != 0)
 			return (-1);
 	}
-	dprintf("first continuing c_pid=%d\n", c_pid);
-
+	dprintf("first continuing c_pid=%d\n", (int)c_pid);
 	if (pipe(p)) {
 		dperror("pipe");
 		return (-1);
 	}
-
-	/* vfork() and execle() to catch output and to process it */
+	/* vfork() and execl() to catch output and to process it */
 	c_pid = vfork();
 	if (c_pid == -1) {
 		dperror("second vfork");
 		return (-1);
 	}
-	dprintf("second continuing c_pid=%d\n", c_pid);
-
+	dprintf("second continuing c_pid=%d\n", (int)c_pid);
 	if (c_pid == 0) {
 		(void) close(p[0]);
 		(void) close(1);
@@ -377,392 +726,1026 @@
 		(void) close(p[1]);
 		(void) close(2);
 		dprintf("second exec fuser %s\n", file);
-		(void) execle("/usr/sbin/fuser", "fuser", file, NULL, newenv);
+		(void) execl("/usr/sbin/fuser", "fuser", file, NULL);
 		dperror("second exec fuser");
 		_exit(1);
 	}
-
 	(void) close(p[1]);
 	if ((ptr = fdopen(p[0], "r")) != NULL) {
 		while (!feof(ptr)) {
-			if (fscanf(ptr, "%d", &pid) > 0) {
-				(void) sprintf(buf + strlen(PROCFS), "%d", pid);
+			if (fscanf(ptr, "%d", &fuserpid) > 0) {
+				(void) sprintf(buf + strlen(PROCFS), "%d",
+				    fuserpid);
 				if ((fp = open(buf, O_RDONLY)) == -1) {
 					dperror(buf);
 					continue;
 				}
-				if (ioctl(fp, PIOCPSINFO, (char *)&info)
-				    == -1) {
-					dprintf("%d psinfo failed", pid);
+				if (ioctl(fp, PIOCPSINFO,
+				    (char *)&info) == -1) {
+					dprintf("%d psinfo failed", fuserpid);
 					dperror("");
 					(void) close(fp);
 					continue;
 				}
 				(void) close(fp);
 				if (strcmp(info.pr_fname, "vold") == NULL) {
-					dprintf("%d matched vold name\n", pid);
+					dprintf("%d matched vold name\n",
+					    fuserpid);
 					continue;
 				}
 				dprintf("killing %s", info.pr_fname);
-				dprintf("(%d)\n", pid);
-				if ((r = kill(pid, SIGKILL)) == -1) {
-					dprintf("kill %d", pid);
+				dprintf("(%d)\n", fuserpid);
+				if ((r =
+				    kill((pid_t)fuserpid, SIGKILL)) == -1) {
+					dprintf("kill %d", fuserpid);
 					dperror("");
 					break;
 				}
 			}
 		}
-		dprintf("eof reached %x\n", ptr);
 	} else {
-		dperror("fdopen(p[0])");
+		dperror("fdopen(p[0], r)");
 		r = -1;
 	}
+	(void) fclose(ptr);
 
-	(void) fclose(ptr);
 	return (r);
 }
 
-static int
-mk_unalloc(int optflg, char *list)
+int
+mk_unalloc(int optflag, devmap_t *list)
 {
-	char	*file;
 	int	error = 0;
-	int child, status;
-
-	audit_allocate_list(list);
+	int	status;
+	char	**file;
 
-	child = vfork();
-	switch (child) {
-	case -1:
-		return (-1);
-	case 0:
-		(void) setuid(0);
-		file = strtok(list, " ");
-		while (file != NULL) {
-			dprintf("Deallocating %s\n", file);
-			if (mk_revoke(optflg, file) < 0) {
-				dprintf("mk_unalloc: unable to revoke %s\n",
-				    file);
-				dperror("");
-				error = CNTFRC;
-				break;
-			}
-			error = newdac(file, ALLOC_UID, ALLOC_GID,
-			    DEALLOC_MODE);
-			file = strtok(NULL, " ");
+	audit_allocate_list(list->dmap_devlist);
+	file = list->dmap_devarray;
+	if (file == NULL)
+		return (NODMAPERR);
+	for (; *file != NULL; file++) {
+		dprintf("Deallocating %s\n", *file);
+		if (mk_revoke(optflag, *file) < 0) {
+			dprintf("mk_unalloc: unable to revoke %s\n", *file);
+			dperror("");
+			error = CNTFRCERR;
 		}
-		exit(error);
-	default:
-		while (wait(&status) != child);
-		if (WIFEXITED(status)) {
-			return (WEXITSTATUS(status));
-		}
-		return (-1);
+		status = _newdac(*file, ALLOC_UID, ALLOC_GID, DEALLOC_MODE);
+		if (error == 0)
+			error = status;
+
 	}
+
+	return (error);
 }
 
-static int
-exec_clean(int optflg, char *name, char *path)
+int
+mk_error(devmap_t *list)
+{
+	int	status = 0;
+	char	**file;
+
+	audit_allocate_list(list->dmap_devlist);
+	file = list->dmap_devarray;
+	if (file == NULL)
+		return (NODMAPERR);
+	for (; *file != NULL; file++) {
+		dprintf("Putting %s in error state\n", *file);
+		status = _newdac(*file, ALLOC_ERRID, ALLOC_GID, ALLOC_ERR_MODE);
+	}
+
+	return (status);
+}
+
+int
+exec_clean(int optflag, char *devname, char *path, uid_t uid, char *zonename,
+    char *clean_arg)
 {
-	char	*mode, *cmd;
-	int	status;
-	int	c;
+	int		c;
+	int		status = 0, exit_status;
+	char		*mode, *cmd, *wdwcmd, *zoneroot;
+	char		*devzone = zonename;
+	char		wdwpath[PATH_MAX];
+	char		zonepath[MAXPATHLEN];
+	char		title[100];
+	char		pw_buf[NSS_BUFLEN_PASSWD];
+	struct passwd	pw_ent;
 
-	if ((optflg & (FORCE_ALL | SILENT)) == (FORCE_ALL | SILENT))
+	zonepath[0] = '\0';
+	if (system_labeled) {
+		if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
+			if (strcmp(clean_arg, ALLOC_CLEAN) == 0) {
+				return (-1);
+			} else if (optflag & FORCE) {
+				(void) strcpy(zonepath, "/");
+				devzone = GLOBAL_ZONENAME;
+			} else {
+				dprintf("unable to get label for %s zone\n",
+				    zonename);
+				return (-1);
+			}
+		} else {
+			(void) strcpy(zonepath, zoneroot);
+			free(zoneroot);
+		}
+	}
+	if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL)
+		return (-1);
+	if (optflag & FORCE_ALL)
 		mode = "-I";
-	else if (optflg & FORCE_ALL)
-		mode = "-i";
-	else if (optflg & FORCE)
+	else if (optflag & FORCE)
 		mode = "-f";
 	else
 		mode = "-s";
+	if (path == NULL)
+		return (0);
 	if ((cmd = strrchr(path, '/')) == NULL)
 		cmd = path;
 	else
 		cmd++;	/* skip leading '/' */
-
 	c = vfork();
 	switch (c) {
 	case -1:
 		return (-1);
 	case 0:
 		(void) setuid(0);
+		if (system_labeled && (optflag & WINDOWING)) {
+			/* First try .windowing version of script */
+			(void) strncpy(wdwpath, path, PATH_MAX);
+			(void) strncat(wdwpath, ".windowing", PATH_MAX);
+			if ((wdwcmd = strrchr(wdwpath, '/')) == NULL)
+				wdwcmd = wdwpath;
+			(void) execl(wdwpath, wdwcmd, mode, devname, clean_arg,
+			    pw_ent.pw_name, devzone, zonepath, NULL);
+			/* If that failed, run regular version via dtterm */
+			(void) snprintf(title, sizeof (title),
+			    "Device %s for %s",
+			    strcmp(clean_arg, ALLOC_CLEAN) == 0 ?
+			    "allocation" : "deallocation", devname);
+			(void) execl("/usr/dt/bin/dtterm", "dtterm",
+			    "-title", title, "-geometry", "x10+100+400",
+			    "-e", "/etc/security/lib/wdwwrapper",
+			    path, mode, devname, clean_arg, pw_ent.pw_name,
+			    devzone, zonepath, NULL);
+			/*
+			 * And if that failed, continue on to try
+			 * running regular version directly.
+			 */
+		}
 		dprintf("clean script: %s, ", path);
 		dprintf("cmd=%s, ", cmd);
 		dprintf("mode=%s, ", mode);
-		dprintf("name=%s\n", name);
-		(void) execle(path, cmd, mode, name, NULL, newenv);
+		if (system_labeled) {
+			dprintf("devname=%s ", devname);
+			dprintf("zonename=%s ", devzone);
+			dprintf("zonepath=%s ", zonepath);
+			dprintf("username=%s\n", pw_ent.pw_name);
+			(void) execl(path, cmd, mode, devname, clean_arg,
+			    pw_ent.pw_name, devzone, zonepath, NULL);
+		} else {
+			dprintf("devname=%s\n", devname);
+			(void) execle(path, cmd, mode, devname, NULL, newenv);
+		}
 		dprintf("Unable to execute clean up script %s\n", path);
 		dperror("");
-		exit(CNTDEXEC);
+		exit(CNTDEXECERR);
 	default:
-		while (wait(&status) != c);
-		if (WIFEXITED(status))
-			return (WEXITSTATUS(status));
-		dprintf("exit status %d\n", status);
+		(void) waitpid(c, &status, 0);
+		dprintf("Child %d", c);
+		if (WIFEXITED(status)) {
+			exit_status = WEXITSTATUS(status);
+			dprintf(" exited, status: %d\n", exit_status);
+			return (exit_status);
+		} else if (WIFSIGNALED(status)) {
+			dprintf(" killed, signal %d\n", WTERMSIG(status));
+		} else {
+			dprintf(": exit status %d\n", status);
+		}
 		return (-1);
 	}
 }
 
-static int
-deallocate_dev(int optflg, devalloc_t *dev_ent, uid_t uid)
+int
+_deallocate_dev(int optflag, devalloc_t *da, devmap_t *dm_in, uid_t uid,
+    char *zonename)
 {
-	devmap_t *dev_list;
-	char	file_name[MAXPATHLEN];
-	struct stat stat_buf;
-	char	*list;
-	int	error = 0, err;
-	int	bytes_formated;
+	int			bytes = 0;
+	int			error = 0;
+	int			is_authorized = 0;
+	uid_t			nuid;
+	char			*fname = NULL;
+	char			file_name[MAXPATHLEN];
+	char			*devzone = NULL;
+	devmap_t		*dm = NULL, *dm_new = NULL;
+	struct stat		stat_buf;
+	struct state_file	sf;
 
-	bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
-	    dev_ent->da_devname);
-	if (bytes_formated <= 0) {
-		return (DEVNAME_ERR);
-	} else if (bytes_formated >= MAXPATHLEN) {
-		dprintf("device name %s is too long.\n", dev_ent->da_devname);
-		return (DEVNAME_TOOLONG);
+	if (dm_in == NULL) {
+		setdmapent();
+		if ((dm_new = getdmapnam(da->da_devname)) == NULL) {
+			enddmapent();
+			dprintf("Unable to find %s in device map database\n",
+			    da->da_devname);
+			return (NODMAPERR);
+		}
+		enddmapent();
+		dm = dm_new;
+	} else {
+		dm = dm_in;
+	}
+	if (system_labeled) {
+		if (_dev_file_name(&sf, dm) != 0) {
+			if (dm_new)
+				freedmapent(dm_new);
+			dprintf("Unable to find %s device files\n",
+			    da->da_devname);
+			error = NODMAPERR;
+			goto out;
+		}
+		fname = sf.sf_path;
+	} else {
+		bytes = snprintf(file_name,  MAXPATHLEN, "%s/%s", DAC_DIR,
+		    da->da_devname);
+		if (bytes <= 0) {
+			error = DEVNAMEERR;
+			goto out;
+		} else if (bytes >= MAXPATHLEN) {
+			dprintf("device name %s is too long.\n",
+			    da->da_devname);
+			error = DEVLONGERR;
+			goto out;
+		}
+		fname = file_name;
 	}
 
-	audit_allocate_device(file_name);
+	audit_allocate_device(fname);
 
-	if (stat(file_name, &stat_buf)) {
-		dprintf("Unable to stat %s\n", file_name);
-		dperror("Error:");
-		return (DACACC);
+	if (stat(fname, &stat_buf) != 0) {
+		dprintf("Unable to stat %s\n", fname);
+		error = DACACCERR;
+		goto out;
+	}
+	is_authorized = _is_dev_authorized(da, uid);
+	if (!(optflag & (FORCE | FORCE_ALL)) && !is_authorized) {
+		dprintf("User %d is unauthorized to deallocate\n", (int)uid);
+		error = UAUTHERR;
+		goto out;
+	}
+	if (system_labeled) {
+		/*
+		 * unless we're here to deallocate by force, check if the
+		 * label at which the device is currently allocated is
+		 * within the user label range.
+		 */
+		if (!(optflag & FORCE) &&
+		    _check_label(da, zonename, uid, CHECK_URANGE) != 0) {
+			error = LABELRNGERR;
+			goto out;
+		}
+	}
+	if (!(optflag & FORCE) && stat_buf.st_uid != uid &&
+	    DEV_ALLOCATED(stat_buf)) {
+		error = ALLOCUERR;
+		goto out;
+	}
+	if (!DEV_ALLOCATED(stat_buf)) {
+		if (DEV_ERRORED(stat_buf)) {
+			if (!(optflag & FORCE)) {
+				error = DEVSTATEERR;
+				goto out;
+			}
+		} else {
+			error = DEVNALLOCERR;
+			goto out;
+		}
 	}
-
-	if (!(optflg & FORCE) && stat_buf.st_uid != uid &&
-	    DEV_ALLOCATED(stat_buf)) {
-		return (NALLOCU);
+	/* All checks passed, time to lock and deallocate */
+	if ((error = lock_dev(fname)) != 0)
+		goto out;
+	if (system_labeled) {
+		devzone = kva_match(da->da_devopts, DAOPT_ZONE);
+		if (devzone && (strcmp(devzone, GLOBAL_ZONENAME) != 0)) {
+			if ((remove_znode(devzone, dm) != 0) &&
+			    !(optflag & FORCE)) {
+				error = ZONEERR;
+				goto out;
+			}
+		}
+	}
+	if ((error = mk_unalloc(optflag, dm)) != 0) {
+		if (!(optflag & FORCE))
+			goto out;
+	}
+	if (system_labeled == 0) {
+		if ((error = _newdac(fname, ALLOC_UID, ALLOC_GID,
+		    DEALLOC_MODE)) != 0) {
+			(void) _newdac(file_name, ALLOC_UID, ALLOC_GID,
+			    ALLOC_ERR_MODE);
+			goto out;
+		}
+	}
+	/*
+	 * if we are deallocating device owned by someone else,
+	 * pass the owner's uid to the cleaning script.
+	 */
+	nuid = (stat_buf.st_uid == uid) ? uid : stat_buf.st_uid;
+	error = exec_clean(optflag, da->da_devname, da->da_devexec, nuid,
+	    devzone, DEALLOC_CLEAN);
+	if (error != 0) {
+		if (!(optflag & (FORCE | FORCE_ALL))) {
+			error = CLEANERR;
+			(void) mk_error(dm);
+		} else {
+			error = 0;
+		}
 	}
 
-	if (!(optflg & FORCE_ALL) && !DEV_ALLOCATED(stat_buf)) {
-		if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE) {
-			if (!(optflg & FORCE))
-				return (ALLOCERR);
-		} else
-			return (NALLOC);
+out:
+	if (dm_new)
+		freedmapent(dm_new);
+	return (error);
+}
+
+int
+_allocate_dev(int optflag, uid_t uid, devalloc_t *da, char *zonename)
+{
+	int			i;
+	int			bytes = 0;
+	int			error = 0;
+	int			is_authorized = 0;
+	int			dealloc_optflag = 0;
+	char			*fname = NULL;
+	char			file_name[MAXPATHLEN];
+	devmap_t 		*dm;
+	struct stat 		stat_buf;
+	struct state_file	sf;
+	struct zone_path	zpath;
+
+	zpath.count = 0;
+	zpath.path = NULL;
+	setdmapent();
+	if ((dm = getdmapnam(da->da_devname)) == NULL) {
+		enddmapent();
+		dprintf("Unable to find %s in device map database\n",
+		    da->da_devname);
+		return (NODMAPERR);
+	}
+	enddmapent();
+	if (system_labeled) {
+		if (_dev_file_name(&sf, dm) != 0) {
+			freedmapent(dm);
+			dprintf("Unable to find %s device files\n",
+			    da->da_devname);
+			error = NODMAPERR;
+			goto out;
+		}
+		fname = sf.sf_path;
+	} else {
+		bytes = snprintf(file_name,  MAXPATHLEN, "%s/%s", DAC_DIR,
+		    da->da_devname);
+		if (bytes <= 0) {
+			error = DEVNAMEERR;
+			goto out;
+		} else if (bytes >= MAXPATHLEN) {
+			dprintf("device name %s is too long.\n",
+			    da->da_devname);
+			error = DEVLONGERR;
+			goto out;
+		}
+		fname = file_name;
 	}
 
-	/* All checks passed, time to lock and deallocate */
-	if ((error = lock_dev(file_name)) != 0)
-		return (error);
-
-	if ((err = newdac(file_name, ALLOC_UID, ALLOC_GID, DEALLOC_MODE))
-	    != 0) {
-		(void) newdac(file_name, ALLOC_UID, ALLOC_GID, ALLOC_ERR_MODE);
-		EXIT(err);
-	}
+	(void) audit_allocate_device(fname);
 
-	if ((dev_list = getdmapnam(dev_ent->da_devname)) == NULL) {
-		dprintf("Unable to find %s in the device map database\n",
-		    dev_ent->da_devname);
-		EXIT(NODMAPENT);
-	} else {
-		if ((list = strdup(dev_list->dmap_devlist)) == NULL) {
-			EXIT(SYSERROR)
+	if (stat(fname, &stat_buf) != 0) {
+		dprintf("Unable to stat %s\n", fname);
+		dperror("Error:");
+		error = DACACCERR;
+		goto out;
+	}
+	if (DEV_ERRORED(stat_buf)) {
+		error = DEVSTATEERR;
+		goto out;
+	}
+	is_authorized = _is_dev_authorized(da, uid);
+	if (is_authorized == ALLOC_BY_NONE) {
+		dprintf("Device %s is not allocatable\n", da->da_devname);
+		error = UAUTHERR;
+		goto out;
+	} else if (!is_authorized && !(optflag & USERNAME)) {
+		dprintf("User %d is unauthorized to allocate\n", (int)uid);
+		error = UAUTHERR;
+		goto out;
+	}
+	if (system_labeled) {
+		/*
+		 * check if label of the zone to which the device is being
+		 * allocated is within the device label range.
+		 */
+		if (_check_label(da, zonename, uid, CHECK_DRANGE) != 0) {
+			error = LABELRNGERR;
+			goto out;
+		}
+	}
+	if (check_devs(dm) == -1) {
+		error = DSPMISSERR;
+		goto out;
+	}
+	if (DEV_ALLOCATED(stat_buf)) {
+		if (optflag & FORCE) {
+			if (optflag & SILENT)
+				dealloc_optflag = FORCE|SILENT;
+			else
+				dealloc_optflag = FORCE;
+			if (_deallocate_dev(dealloc_optflag, da, dm, uid,
+			    zonename)) {
+				dprintf("Couldn't force deallocate device %s\n",
+				    da->da_devname);
+				error = CNTFRCERR;
+				goto out;
+			}
+		} else if (stat_buf.st_uid == uid) {
+			error = PREALLOCERR;
+			goto out;
 		} else {
-			if (mk_unalloc(optflg, list) != 0) {
-				(void) newdac(file_name, ALLOC_UID, ALLOC_GID,
-				    ALLOC_ERR_MODE);
-				free(list);
-				list = NULL;
-				EXIT(DEVLST);
+			error = ALLOCUERR;
+			goto out;
+		}
+	}
+	/* All checks passed, time to lock and allocate */
+	if ((error = lock_dev(fname)) != 0)
+		goto out;
+	if (system_labeled) {
+		/*
+		 * Run the cleaning program; it also mounts allocated
+		 * device if required.
+		 */
+		error = exec_clean(optflag, da->da_devname, da->da_devexec, uid,
+		    zonename, ALLOC_CLEAN);
+		if ((error != 0) && (error != CLEAN_MOUNT)) {
+			error = CLEANERR;
+			(void) mk_error(dm);
+			goto out;
+		}
+		/*
+		 * If not mounted, create zonelinks, if this is not the
+		 * global zone.
+		 */
+		if ((strcmp(zonename, GLOBAL_ZONENAME) != 0) &&
+		    (error != CLEAN_MOUNT)) {
+			if (create_znode(zonename, &zpath, dm) != 0) {
+				error = ZONEERR;
+				goto out;
 			}
 		}
 	}
 
-	if (list != NULL)
-		free(list);
-	if (exec_clean(optflg, dev_ent->da_devname, dev_ent->da_devexec))
-		EXIT(CLEAN_ERR);
+	(void) audit_allocate_list(dm->dmap_devlist);
+
+	if ((error = mk_alloc(dm, uid, &zpath)) != 0) {
+		(void) mk_unalloc(optflag, dm);
+		goto out;
+	}
+
+	if (system_labeled == 0) {
+		if ((error = _newdac(file_name, uid, getgid(),
+		    ALLOC_MODE)) != 0) {
+			(void) _newdac(file_name, ALLOC_UID, ALLOC_GID,
+			    ALLOC_ERR_MODE);
+			goto out;
+		}
+	}
+	error = 0;
+out:
+	if (zpath.count) {
+		for (i = 0; i < zpath.count; i++)
+			free(zpath.path[i]);
+		free(zpath.path);
+	}
+	freedmapent(dm);
+	return (error);
+}
+
+void
+_store_devnames(int *count, struct devnames *dnms, char *zonename,
+    devalloc_t *da, int flag)
+{
+	int i;
+
+	dnms->dnames = (char **)realloc(dnms->dnames,
+	    (*count + 1) * sizeof (char *));
+	if (da) {
+		dnms->dnames[*count] = strdup(da->da_devname);
+		(*count)++;
+	} else {
+		dnms->dnames[*count] = NULL;
+		if (flag == DA_ADD_ZONE)
+			(void) update_device(dnms->dnames, zonename,
+			    DA_ADD_ZONE);
+		else if (flag == DA_REMOVE_ZONE)
+			(void) update_device(dnms->dnames, NULL,
+			    DA_REMOVE_ZONE);
+		for (i = 0; i < *count; i++)
+			free(dnms->dnames[i]);
+		free(dnms->dnames);
+	}
+}
+
+int
+allocate(int optflag, uid_t uid, char *device, char *zonename)
+{
+	int		count = 0;
+	int		error = 0;
+	devalloc_t	*da;
+	struct devnames	dnms;
+
+	if (optflag & (FORCE | USERID | USERNAME)) {
+		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
+			return (UAUTHERR);
+	}
+	dnms.dnames = NULL;
+	setdaent();
+	if (optflag & TYPE) {
+		/*
+		 * allocate devices of this type
+		 */
+		while ((da = getdatype(device)) != NULL) {
+			if (system_labeled &&
+			    da_check_logindevperm(da->da_devname)) {
+				freedaent(da);
+				continue;
+			}
+			dprintf("trying to allocate %s\n", da->da_devname);
+			error = _allocate_dev(optflag, uid, da, zonename);
+			if (system_labeled && (error == 0)) {
+				/*
+				 * we need to record in device_allocate the
+				 * label (zone name) at which this device is
+				 * being allocated. store this device entry.
+				 */
+				_store_devnames(&count, &dnms, zonename, da, 0);
+			}
+			freedaent(da);
+			error = 0;
+		}
+	} else {
+		/*
+		 * allocate this device
+		 */
+		if ((da = getdanam(device)) == NULL) {
+			enddaent();
+			return (NODAERR);
+		}
+		if (system_labeled && da_check_logindevperm(device)) {
+			freedaent(da);
+			return (LOGINDEVPERMERR);
+		}
+		dprintf("trying to allocate %s\n", da->da_devname);
+		error = _allocate_dev(optflag, uid, da, zonename);
+		/*
+		 * we need to record in device_allocate the label (zone name)
+		 * at which this device is being allocated. store this device
+		 * entry.
+		 */
+		if (system_labeled && (error == 0))
+			_store_devnames(&count, &dnms, zonename, da, 0);
+		freedaent(da);
+	}
+	enddaent();
+	/*
+	 * add to device_allocate labels (zone names) for the devices we
+	 * allocated.
+	 */
+	if (dnms.dnames)
+		_store_devnames(&count, &dnms, zonename, NULL, DA_ADD_ZONE);
+
+	return (error);
+}
+
+/* ARGSUSED */
+int
+deallocate(int optflag, uid_t uid, char *device, char *zonename)
+{
+	int		count = 0;
+	int		error = 0;
+	devalloc_t	*da;
+	struct devnames dnms;
+
+	if (optflag & (FORCE | FORCE_ALL)) {
+		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
+		return (UAUTHERR);
+	}
+	if (optflag & FORCE_ALL)
+		optflag |= FORCE;
+	dnms.dnames = NULL;
+	setdaent();
+	if (optflag & FORCE_ALL) {
+		/*
+		 * deallocate all devices
+		 */
+		while ((da = getdaent()) != NULL) {
+			if (system_labeled &&
+			    da_check_logindevperm(da->da_devname)) {
+				freedaent(da);
+				continue;
+			}
+			dprintf("trying to deallocate %s\n", da->da_devname);
+			error = _deallocate_dev(optflag, da, NULL, uid,
+			    zonename);
+			if (system_labeled && (error == 0)) {
+				/*
+				 * we need to remove this device's allocation
+				 * label (zone name) from device_allocate.
+				 * store this device name.
+				 */
+				_store_devnames(&count, &dnms, zonename, da, 0);
+			}
+			freedaent(da);
+			error = 0;
+		}
+	} else if (system_labeled && optflag & TYPE) {
+		/*
+		 * deallocate all devices of this type
+		 */
+		while ((da = getdatype(device)) != NULL) {
+			if (da_check_logindevperm(da->da_devname)) {
+				freedaent(da);
+				continue;
+			}
+			dprintf("trying to deallocate %s\n", da->da_devname);
+			error = _deallocate_dev(optflag, da, NULL, uid,
+			    zonename);
+			if (error == 0) {
+				/*
+				 * we need to remove this device's allocation
+				 * label (zone name) from device_allocate.
+				 * store this device name.
+				 */
+				_store_devnames(&count, &dnms, zonename, da, 0);
+			}
+			freedaent(da);
+			error = 0;
+		}
+	} else if (!(optflag & TYPE)) {
+		/*
+		 * deallocate this device
+		 */
+		if ((da = getdanam(device)) == NULL) {
+			enddaent();
+			return (NODAERR);
+		}
+		if (system_labeled && da_check_logindevperm(da->da_devname)) {
+			freedaent(da);
+			return (LOGINDEVPERMERR);
+		}
+		dprintf("trying to deallocate %s\n", da->da_devname);
+		error = _deallocate_dev(optflag, da, NULL, uid, zonename);
+		if (system_labeled && (error == 0)) {
+			/*
+			 * we need to remove this device's allocation label
+			 * (zone name) from device_allocate. store this
+			 * device name.
+			 */
+			_store_devnames(&count, &dnms, zonename, da, 0);
+		}
+		freedaent(da);
+	}
+	enddaent();
+	/*
+	 * remove from device_allocate labels (zone names) for the devices we
+	 * deallocated.
+	 */
+	if (dnms.dnames)
+		_store_devnames(&count, &dnms, zonename, NULL, DA_REMOVE_ZONE);
+
 	return (error);
 }
 
 static int
-allocate_dev(int optflg, uid_t uid, devalloc_t *dev_ent)
+_dev_file_name(struct state_file *sfp, devmap_t *dm)
 {
-	devmap_t *dev_list;
-	char	file_name[MAXPATHLEN];
-	struct stat stat_buf;
-	char	*list;
-	int	error = 0;
-	int	bytes_formated;
-	int	deallocate_optflg = 0;
-
-	bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
-	    dev_ent->da_devname);
-	if (bytes_formated <= 0) {
-		return (DEVNAME_ERR);
-	} else if (bytes_formated >= MAXPATHLEN) {
-		dprintf("device name %s is too long.\n", dev_ent->da_devname);
-		return (DEVNAME_TOOLONG);
-	}
-
-	audit_allocate_device(file_name);
-
-	if (stat(file_name, &stat_buf)) {
-		dprintf("Unable to stat %s\n", file_name);
-		dperror("Error:");
-		return (DACACC);
+	sfp->sf_flags = 0;
+	/* if devlist is generated, never leave device in error state */
+	if (dm->dmap_devlist[0] == '`')
+		sfp->sf_flags |= SFF_NO_ERROR;
+	if (dm->dmap_devarray == NULL ||
+	    dm->dmap_devarray[0] == NULL)
+		return (NODMAPERR);
+	(void) strncpy(sfp->sf_path, dm->dmap_devarray[0],
+	    sizeof (sfp->sf_path));
+	sfp->sf_path[sizeof (sfp->sf_path) - 1] = '\0';
+	if (sfp->sf_path[0] == '\0') {
+		dprintf("dev_file_name: no device list for %s\n",
+		    dm->dmap_devname);
+		return (NODMAPERR);
 	}
 
-	if (DEV_ALLOCATED(stat_buf)) {
-		if (optflg & FORCE) {
-			if (optflg & SILENT)
-				deallocate_optflg = FORCE|SILENT;
-			else
-				deallocate_optflg = FORCE;
+	return (0);
+}
+
+/*
+ * _check_label -
+ *	checks the device label range against zone label, which is also
+ *	user's current label.
+ *	returns 0 if in range, -1 for all other conditions.
+ *
+ */
+
+static int
+_check_label(devalloc_t *da, char *zonename, uid_t uid, int flag)
+{
+	int		err;
+	int		in_range = 0;
+	char		*alloczone, *lstr;
+	char		pw_buf[NSS_BUFLEN_PASSWD];
+	blrange_t	*range;
+	m_label_t	*zlabel;
+	struct passwd	pw_ent;
+
+	if ((da == NULL) || (zonename == NULL))
+		return (-1);
+
+	if ((zlabel = getzonelabelbyname(zonename)) == NULL) {
+		dprintf("unable to get label for %s zone\n", zonename);
+		return (-1);
+	}
+	if (flag == CHECK_DRANGE) {
+		blrange_t	drange;
 
-			if (deallocate_dev(deallocate_optflg, dev_ent, uid)) {
-				dprintf("Couldn't force deallocate device %s\n",
-				    dev_ent->da_devname);
-				return (CNTFRC);
-			}
-		} else if (stat_buf.st_uid == uid) {
-			return (ALLOC);
-		} else
-			return (ALLOC_OTHER);
-	}
-	if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE)
-		return (ALLOCERR);
+		drange.lower_bound = blabel_alloc();
+		lstr = kva_match(da->da_devopts, DAOPT_MINLABEL);
+		if (lstr == NULL) {
+			bsllow(drange.lower_bound);
+		} else if (stobsl(lstr, drange.lower_bound, NO_CORRECTION,
+		    &err) == 0) {
+			dprintf("bad min_label for device %s\n",
+			    da->da_devname);
+			free(zlabel);
+			blabel_free(drange.lower_bound);
+			return (-1);
+		}
+		drange.upper_bound = blabel_alloc();
+		lstr = kva_match(da->da_devopts, DAOPT_MAXLABEL);
+		if (lstr == NULL) {
+			bslhigh(drange.upper_bound);
+		} else if (stobsl(lstr, drange.upper_bound, NO_CORRECTION,
+		    &err) == 0) {
+			dprintf("bad max_label for device %s\n",
+			    da->da_devname);
+			free(zlabel);
+			blabel_free(drange.lower_bound);
+			blabel_free(drange.upper_bound);
+			return (-1);
+		}
+		if (blinrange(zlabel, &drange) == 0) {
+			char	*zlbl = NULL, *min = NULL, *max = NULL;
 
-	if (strcmp(dev_ent->da_devauth, "*") == 0) {
-		dprintf("Device %s is not allocatable\n", dev_ent->da_devname);
-		return (AUTHERR);
-	}
-
-	if (strcmp(dev_ent->da_devauth, "@")) {
-		if (!is_authorized(dev_ent->da_devauth, uid)) {
-			dprintf("User %d is unauthorized to allocate\n",
+			(void) bsltos(zlabel, &zlbl, 0, 0);
+			(void) bsltos(drange.lower_bound, &min, 0, 0);
+			(void) bsltos(drange.upper_bound, &max, 0, 0);
+			dprintf("%s zone label ", zonename);
+			dprintf("%s outside device label range: ", zlbl);
+			dprintf("min - %s, ", min);
+			dprintf("max - %s\n", max);
+			free(zlabel);
+			blabel_free(drange.lower_bound);
+			blabel_free(drange.upper_bound);
+			return (-1);
+		}
+	} else if (flag == CHECK_URANGE) {
+		if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) {
+			dprintf("Unable to get passwd entry for userid %d\n",
+			    (int)uid);
+			free(zlabel);
+			return (-1);
+		}
+		if ((range = getuserrange(pw_ent.pw_name)) == NULL) {
+			dprintf("Unable to get label range for userid %d\n",
 			    (int)uid);
-			return (IMPORT_ERR);
+			free(zlabel);
+			return (-1);
+		}
+		in_range = blinrange(zlabel, range);
+		free(zlabel);
+		blabel_free(range->lower_bound);
+		blabel_free(range->upper_bound);
+		free(range);
+		if (in_range == 0) {
+			dprintf("%s device label ", da->da_devname);
+			dprintf("out of user %d label range\n", (int)uid);
+			return (-1);
+		}
+	} else if (flag == CHECK_ZLABEL) {
+		alloczone = kva_match(da->da_devopts, DAOPT_ZONE);
+		if (alloczone == NULL) {
+			free(zlabel);
+			return (-1);
+		}
+		if (strcmp(zonename, alloczone) != 0) {
+			dprintf("%s zone is different than ", zonename);
+			dprintf("%s zone to which the device ", alloczone);
+			dprintf("%s is allocated\n", da->da_devname);
+			free(zlabel);
+			return (-1);
 		}
 	}
-
-	if ((dev_list = getdmapnam(dev_ent->da_devname)) == NULL) {
-		dprintf("Unable to find %s in device map database\n",
-		    dev_ent->da_devname);
-		return (NODMAPENT);
-	}
-
-	if ((list = strdup(dev_list->dmap_devlist)) == NULL)
-		return (SYSERROR);
-
-	if (check_devs(list) == -1) {
-		free(list);
-		return (DSPMISS);
-	}
+	free(zlabel);
 
-	/* All checks passed, time to lock and allocate */
-	if ((error = lock_dev(file_name)) != 0) {
-		free(list);
-		return (error);
-	}
-
-	if ((error = newdac(file_name, uid, getgid(), ALLOC_MODE)) != 0) {
-		(void) newdac(file_name, ALLOC_UID, ALLOC_GID, ALLOC_ERR_MODE);
-		free(list);
-		return (error);
-	}
-
-	/* refresh list from check_devs overwritting it */
-	(void) strcpy(list, dev_list->dmap_devlist);
-	audit_allocate_list(list);
-
-	if (mk_alloc(list, uid) != 0) {
-		/* refresh list from mk_alloc overwritting it */
-		(void) strcpy(list, dev_list->dmap_devlist);
-		(void) mk_unalloc(optflg, list);
-		free(list);
-		return (DEVLST);
-	}
-
-	free(list);
 	return (0);
 }
 
 int
-allocate(int optflg, uid_t uid, char *device)
+create_znode(char *zonename, struct zone_path *zpath, devmap_t *list)
 {
-	devalloc_t	*dev_ent;
-	devmap_t	*dev_list;
+	int		i;
+	int		size;
+	int		len = 0;
+	int		fcount = 0;
+	char		*p, *tmpfile, *zoneroot;
+	char		**file;
+	char		zonepath[MAXPATHLEN];
+	struct stat	statb;
 
-	if (((optflg & FORCE) || uid != getuid()) &&
-	    !is_authorized(DEVICE_REVOKE_AUTH, getuid()))
-		return (NOTAUTH);
+	file = list->dmap_devarray;
+	if (file == NULL)
+		return (NODMAPERR);
+	if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
+		dprintf("unable to get label for %s zone\n", zonename);
+		return (1);
+	}
+	(void) strcpy(zonepath, zoneroot);
+	free(zoneroot);
+	if ((p = strstr(zonepath, "/root")) == NULL)
+		return (1);
+	*p = '\0';
+	len = strlen(zonepath);
+	size = sizeof (zonepath);
+	for (; *file != NULL; file++) {
+		if (stat(*file, &statb) == -1) {
+			dprintf("Couldn't stat the file %s\n", *file);
+			return (1);
+		}
+		/*
+		 * First time initialization
+		 */
+		tmpfile = strdup(*file);
+
+		/*
+		 * Most devices have pathnames starting in /dev
+		 * but SunRay devices do not. In SRRS 3.1 they use /tmp.
+		 *
+		 * If the device pathname is not in /dev then create
+		 * a symbolic link to it and put the device in /dev
+		 */
+		if (strncmp(tmpfile, "/dev/", strlen("/dev/")) != 0) {
+			char	*linkdir;
+			char	srclinkdir[MAXPATHLEN];
+			char	dstlinkdir[MAXPATHLEN];
 
-	setdaent();
-	setdmapent();
-
-	if (!(optflg & TYPE)) {
-		if ((dev_ent = getdanam(device)) == NULL) {
-			if ((dev_list = getdmapdev(device)) == NULL)
-				return (NODMAPENT);
-			else if ((dev_ent = getdanam(dev_list->dmap_devname))
-			    == NULL)
-				return (NODAENT);
+			linkdir = strchr(tmpfile + 1, '/');
+			p = strchr(linkdir + 1, '/');
+			*p = '\0';
+			(void) strcpy(dstlinkdir, "/dev");
+			(void) strncat(dstlinkdir, linkdir, MAXPATHLEN);
+			(void) snprintf(srclinkdir, MAXPATHLEN, "%s/root%s",
+				zonepath, tmpfile);
+			(void) symlink(dstlinkdir, srclinkdir);
+			*p = '/';
+			(void) strncat(dstlinkdir, p, MAXPATHLEN);
+			free(tmpfile);
+			tmpfile = strdup(dstlinkdir);
+		}
+		if ((p = rindex(tmpfile, '/')) == NULL) {
+			dprintf("bad path in create_znode for %s\n",
+			    tmpfile);
+			return (1);
+		}
+		*p = '\0';
+		(void) strcat(zonepath, tmpfile);
+		*p = '/';
+		if ((mkdirp(zonepath, 0755) != 0) && (errno != EEXIST)) {
+			dprintf("Unable to create directory %s\n", zonepath);
+			return (1);
 		}
-		return (allocate_dev(optflg, uid, dev_ent));
+		zonepath[len] = '\0';
+		if (strlcat(zonepath, tmpfile, size) >= size) {
+			dprintf("Buffer overflow in create_znode for %s\n",
+			    *file);
+			free(tmpfile);
+			return (1);
+		}
+		free(tmpfile);
+		fcount++;
+		if ((zpath->path = (char **)realloc(zpath->path,
+		    (fcount * sizeof (char *)))) == NULL)
+			return (1);
+		zpath->path[zpath->count] = strdup(zonepath);
+		zpath->count = fcount;
+		if (mknod(zonepath, statb.st_mode, statb.st_rdev) == -1) {
+			switch (errno) {
+			case EEXIST:
+				break;
+			default:
+				dprintf("mknod error: %s\n",
+				    strerror(errno));
+				for (i = 0; i <= fcount; i++)
+					free(zpath->path[i]);
+				free(zpath->path);
+				return (1);
+			}
+		}
+		zonepath[len] = '\0';
 	}
 
-	while ((dev_ent = getdatype(device)) != NULL) {
-		dprintf("trying to allocate %s\n", dev_ent->da_devname);
-		if (!allocate_dev(optflg, uid, dev_ent)) {
-			return (0);
-		}
-	}
-	enddaent();
-	return (NO_DEVICE);
+	return (0);
 }
 
 int
-deallocate(int optflg, uid_t uid, char *device)
+remove_znode(char *zonename, devmap_t *dm)
 {
-	DIR	*dev_dir;
-	struct dirent	*dac_file;
-	devalloc_t	*dev_ent;
-	devmap_t	*dev_list;
-	int	error = NODAENT;
+	int		len = 0;
+	char		*zoneroot;
+	char		**file;
+	char		zonepath[MAXPATHLEN];
 
-	if (optflg & (FORCE | FORCE_ALL) &&
-	    !is_authorized(DEVICE_REVOKE_AUTH, getuid()))
-		return (NOTAUTH);
-	if (optflg & FORCE_ALL)
-		optflg |= FORCE;
+	file = dm->dmap_devarray;
+	if (file == NULL)
+		return (NODMAPERR);
+	if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
+		(void) snprintf(zonepath, MAXPATHLEN, "/zone/%s", zonename);
+	} else {
+		char *p;
 
-	setdaent();
-	setdmapent();
+		if ((p = strstr(zoneroot, "/root")) == NULL)
+			return (1);
+		*p = '\0';
+		(void)  strcpy(zonepath, zoneroot);
+		free(zoneroot);
+	}
+	/*
+	 * To support SunRay we will just deal with the
+	 * file in /dev, not the symlinks.
+	 */
+	(void) strncat(zonepath, "/dev", MAXPATHLEN);
+	len = strlen(zonepath);
+	for (; *file != NULL; file++) {
+		char *devrelpath;
 
-	if (!(optflg & FORCE_ALL)) {
-		if ((dev_ent = getdanam(device)) == NULL) {
-			if ((dev_list = getdmapdev(device)) == NULL)
-				return (NODMAPENT);
-			else if ((dev_ent = getdanam(dev_list->dmap_devname))
-			    == NULL)
-				return (NODAENT);
+		/*
+		 * remove device node from zone.
+		 *
+		 * SunRay devices don't start with /dev
+		 * so skip over first directory to make
+		 * sure it is /dev. SunRay devices in zones
+		 * will have a symlink into /dev but
+		 * we don't ever delete it.
+		 */
+		devrelpath = strchr(*file + 1, '/');
+
+		if (strlcat(zonepath, devrelpath, MAXPATHLEN) >= MAXPATHLEN) {
+			dprintf("Buffer overflow in remove_znode for %s\n",
+			    *file);
+			return (1);
 		}
-
-		return (deallocate_dev(optflg, dev_ent, uid));
+		errno = 0;
+		if ((unlink(zonepath) == -1) && (errno != ENOENT)) {
+			perror(zonepath);
+			return (1);
+		}
+		zonepath[len] = '\0';
 	}
 
-	if ((dev_dir = opendir(DAC_DIR)) == NULL) {
-		dperror("Can't open DAC_DIR");
-		return (DACACC);
-	}
+	return (0);
+}
+
+int
+update_device(char **devnames, char *zonename, int flag)
+{
+	int		len, rc;
+	char		*optstr = NULL;
+	da_args		dargs;
+	devinfo_t	devinfo;
 
-	while ((dac_file = readdir(dev_dir)) != NULL) {
-		if ((strcmp(dac_file->d_name, ".") == 0) ||
-		    (strcmp(dac_file->d_name, "..") == 0)) {
-			continue;
-		} else {
-			if ((dev_ent = getdanam(dac_file->d_name)) == NULL) {
-				continue;
-			}
-			error = deallocate_dev(optflg, dev_ent, uid);
-		}
+	dargs.optflag = flag;
+	dargs.optflag |= DA_UPDATE|DA_ALLOC_ONLY;
+	dargs.rootdir = NULL;
+	dargs.devnames = devnames;
+	devinfo.devname = devinfo.devtype = devinfo.devauths = devinfo.devexec =
+	    devinfo.devlist = NULL;
+	if (dargs.optflag & DA_ADD_ZONE) {
+		len = strlen(DAOPT_ZONE) + strlen(zonename) + 3;
+		if ((optstr = (char *)malloc(len)) == NULL)
+			return (-1);
+		(void) snprintf(optstr, len, "%s%s%s", DAOPT_ZONE, KV_ASSIGN,
+		    zonename);
+		devinfo.devopts = optstr;
 	}
-	(void) closedir(dev_dir);
-	enddaent();
-	return (error);
+	dargs.devinfo = &devinfo;
+
+	rc = da_update_device(&dargs);
+
+	if (optstr)
+		free(optstr);
+
+	return (rc);
 }