| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <auth_attr.h> |
| #include <auth_list.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <libintl.h> |
| #include <locale.h> |
| #include <pwd.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <unistd.h> |
| #include <bsm/devices.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> |
| #include <sys/param.h> |
| #include <sys/resource.h> |
| #include <sys/stat.h> |
| #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" |
| |
| 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) 0 |
| #define dperror(s) 0 |
| #endif /* DEBUG */ |
| |
| #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 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 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 invoking user is local to the device |
| */ |
| /*ARGSUSED*/ |
| int |
| _is_local(uid_t uid) |
| { |
| struct stat statbuf; |
| |
| if (stat(LOCALDEVICE, &statbuf) == 0 && |
| statbuf.st_uid == uid) |
| return (1); |
| |
| return (0); |
| } |
| |
| /* |
| * Checks if the user with the specified uid has the specified authorization |
| */ |
| int |
| _is_authorized(char *auths, uid_t uid) |
| { |
| 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; |
| |
| 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 (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; |
| } |
| } |
| |
| 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); |
| } |
| |
| void |
| print_dev_attrs(int optflag, devalloc_t *da, devmap_t *dm, |
| struct file_info *fip) |
| { |
| char *p = NULL; |
| char optbuf[BUFSIZ]; |
| |
| (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"); |
| } |
| |
| 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"); |
| } |
| |
| /* ARGSUSED */ |
| int |
| _list_device(int optflag, uid_t uid, devalloc_t *da, char *zonename) |
| { |
| 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; |
| |
| 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 (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 (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); |
| |
| error = 0; |
| |
| out: |
| freedmapent(dm); |
| return (error); |
| } |
| |
| /* ARGSUSED */ |
| int |
| list_devices(int optflag, uid_t uid, char *device, char *zonename) |
| { |
| int error = 0; |
| da_defs_t *da_defs; |
| devalloc_t *da; |
| |
| 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) { |
| /* |
| * 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); |
| } |
| } |
| enddaent(); |
| |
| return (error); |
| } |
| |
| /* |
| * Set the DAC characteristics of the file. |
| * This uses a fancy chmod() by setting a minimal ACL which sets the mode |
| * and discards any existing ACL. |
| */ |
| int |
| _newdac(char *file, uid_t owner, gid_t group, o_mode_t mode) |
| { |
| int err = 0; |
| |
| if (mode == ALLOC_MODE) { |
| if (chown(file, owner, group) == -1) { |
| 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); |
| |
| 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 = SETACLERR; |
| } |
| |
| return (err); |
| } |
| |
| static int |
| lock_dev(char *file) |
| { |
| int lockfd = -1; |
| |
| if ((file == NULL) || system_labeled) |
| file = DA_DEV_LOCK; |
| dprintf("locking %s\n", file); |
| if ((lockfd = open(file, O_RDWR | O_CREAT, 0600)) == -1) { |
| dperror("lock_dev: cannot open lock file"); |
| return (DEVLKERR); |
| } |
| if (lockf(lockfd, F_TLOCK, 0) == -1) { |
| dperror("lock_dev: cannot set lock"); |
| return (DEVLKERR); |
| } |
| |
| return (0); |
| } |
| |
| int |
| mk_alloc(devmap_t *list, uid_t uid, struct zone_path *zpath) |
| { |
| int i; |
| int error = 0; |
| char **file; |
| gid_t gid = getgid(); |
| mode_t mode = ALLOC_MODE; |
| |
| 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); |
| 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; |
| } |
| } |
| } |
| |
| return (error); |
| } |
| |
| /* |
| * mk_revoke() is used instead of system("/usr/sbin/fuser -k file") |
| * because "/usr/sbin/fuser -k file" kills all processes |
| * working with the file, even "vold" (bug #4095152). |
| */ |
| int |
| mk_revoke(int optflag, char *file) |
| { |
| 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 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 (!(optflag & SILENT)) { |
| c_pid = vfork(); |
| if (c_pid == -1) |
| return (-1); |
| if (c_pid == 0) { |
| dprintf("first exec fuser %s\n", file); |
| (void) execl("/usr/sbin/fuser", "fuser", file, NULL); |
| dperror("first exec fuser"); |
| _exit(1); |
| } |
| |
| (void) waitpid(c_pid, &lock, 0); |
| dprintf("exit status %x\n", lock); |
| if (WEXITSTATUS(lock) != 0) |
| return (-1); |
| } |
| dprintf("first continuing c_pid=%d\n", (int)c_pid); |
| if (pipe(p)) { |
| dperror("pipe"); |
| return (-1); |
| } |
| /* 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", (int)c_pid); |
| if (c_pid == 0) { |
| (void) close(p[0]); |
| (void) close(1); |
| (void) fcntl(p[1], F_DUPFD, 1); |
| (void) close(p[1]); |
| (void) close(2); |
| dprintf("second exec fuser %s\n", file); |
| (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", &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", fuserpid); |
| dperror(""); |
| (void) close(fp); |
| continue; |
| } |
| (void) close(fp); |
| if (strcmp(info.pr_fname, "vold") == NULL) { |
| dprintf("%d matched vold name\n", |
| fuserpid); |
| continue; |
| } |
| dprintf("killing %s", info.pr_fname); |
| dprintf("(%d)\n", fuserpid); |
| if ((r = |
| kill((pid_t)fuserpid, SIGKILL)) == -1) { |
| dprintf("kill %d", fuserpid); |
| dperror(""); |
| break; |
| } |
| } |
| } |
| } else { |
| dperror("fdopen(p[0], r)"); |
| r = -1; |
| } |
| (void) fclose(ptr); |
| |
| return (r); |
| } |
| |
| int |
| mk_unalloc(int optflag, devmap_t *list) |
| { |
| int error = 0; |
| int status; |
| char **file; |
| |
| 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; |
| } |
| status = _newdac(*file, ALLOC_UID, ALLOC_GID, DEALLOC_MODE); |
| if (error == 0) |
| error = status; |
| |
| } |
| |
| return (error); |
| } |
| |
| 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) |
| { |
| 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; |
| |
| 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 (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); |
| 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(CNTDEXECERR); |
| default: |
| (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); |
| } |
| } |
| |
| int |
| _deallocate_dev(int optflag, devalloc_t *da, devmap_t *dm_in, uid_t uid, |
| char *zonename) |
| { |
| 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; |
| |
| 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(fname); |
| |
| 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; |
| } |
| } |
| /* 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; |
| } |
| } |
| |
| 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; |
| } |
| |
| (void) audit_allocate_device(fname); |
| |
| 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 { |
| 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; |
| } |
| } |
| } |
| |
| (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 |
| _dev_file_name(struct state_file *sfp, devmap_t *dm) |
| { |
| 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); |
| } |
| |
| 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; |
| |
| 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; |
| |
| (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); |
| 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); |
| } |
| } |
| free(zlabel); |
| |
| return (0); |
| } |
| |
| int |
| create_znode(char *zonename, struct zone_path *zpath, devmap_t *list) |
| { |
| int i; |
| int size; |
| int len = 0; |
| int fcount = 0; |
| char *p, *tmpfile, *zoneroot; |
| char **file; |
| char zonepath[MAXPATHLEN]; |
| struct stat statb; |
| |
| 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]; |
| |
| 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); |
| } |
| 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'; |
| } |
| |
| return (0); |
| } |
| |
| int |
| remove_znode(char *zonename, devmap_t *dm) |
| { |
| int len = 0; |
| char *zoneroot; |
| char **file; |
| char zonepath[MAXPATHLEN]; |
| |
| file = dm->dmap_devarray; |
| if (file == NULL) |
| return (NODMAPERR); |
| if ((zoneroot = getzonerootbyname(zonename)) == NULL) { |
| (void) snprintf(zonepath, MAXPATHLEN, "/zone/%s", zonename); |
| } else { |
| char *p; |
| |
| 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; |
| |
| /* |
| * 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); |
| } |
| errno = 0; |
| if ((unlink(zonepath) == -1) && (errno != ENOENT)) { |
| perror(zonepath); |
| return (1); |
| } |
| zonepath[len] = '\0'; |
| } |
| |
| return (0); |
| } |
| |
| int |
| update_device(char **devnames, char *zonename, int flag) |
| { |
| int len, rc; |
| char *optstr = NULL; |
| da_args dargs; |
| devinfo_t devinfo; |
| |
| 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; |
| } |
| dargs.devinfo = &devinfo; |
| |
| rc = da_update_device(&dargs); |
| |
| if (optstr) |
| free(optstr); |
| |
| return (rc); |
| } |