--- 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);
}