On Mon, Feb 27, 2017 at 11:05:55AM +0100, Reyk Floeter wrote:
> Hi,
>
> this is the last diff of the series. It allows users to start or stop
> VMs and to access the console accordingly. In order to make it work,
> VMs have to be pre-configured with the new "owner" option in vm.conf
> or an included file.
>
> 1. Add a "owner user[:group]" in your vm block of vm.conf, eg.
>
> vm "foo" {
> owner :wheel
> }
>
> $ vmctl status
> ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME
> - - 1 1.0G - - :wheel foo
>
> 2. You can now start, stop and attach to it as a matching user:
>
> $ vmctl start foo
> $ vmctl status
> ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME
> 1 82712 1 1.0G 169M ttyp6 reyk:wheel foo
> $ vmctl console foo
> $ vmctl stop foo
>
> It doesn't let you do any template/user-config yet, but this would be
> another step. The tty handling is inspired by sshd, maybe a bit
> tricky, and needs extra pledges in the parent process.
>
> OK?
>
reads ok to me. not sure if you meant 'overriding' or 'overwriting'
in the comment below though (really just a nit):
XXX there could be a mechanism to allow overwriting some options
> Reyk
>
> Add "owner" option to set a user/group ownership for pre-configured VMs
>
> This allows matching users to start or stop VMs that they "own" and to
> access the console accordingly.
>
> diff --git usr.sbin/vmctl/main.c usr.sbin/vmctl/main.c
> index f0007c9..20227ac 100644
> --- usr.sbin/vmctl/main.c
> +++ usr.sbin/vmctl/main.c
> @@ -151,7 +151,7 @@ parse(int argc, char *argv[])
>
> if (!ctl->has_pledge) {
> /* pledge(2) default if command doesn't have its own pledge */
> - if (pledge("stdio rpath exec unix", NULL) == -1)
> + if (pledge("stdio rpath exec unix getpw", NULL) == -1)
> err(1, "pledge");
> }
> if (ctl->main(&res, argc, argv) != 0)
> diff --git usr.sbin/vmctl/vmctl.c usr.sbin/vmctl/vmctl.c
> index ca2da8a..01f2617 100644
> --- usr.sbin/vmctl/vmctl.c
> +++ usr.sbin/vmctl/vmctl.c
> @@ -16,7 +16,7 @@
> * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> */
>
> -#include <sys/types.h>
> +#include <sys/param.h>
> #include <sys/queue.h>
> #include <sys/uio.h>
> #include <sys/stat.h>
> @@ -35,6 +35,8 @@
> #include <string.h>
> #include <unistd.h>
> #include <util.h>
> +#include <pwd.h>
> +#include <grp.h>
>
> #include "vmd.h"
> #include "vmctl.h"
> @@ -369,14 +371,36 @@ print_vm_info(struct vmop_info_result *list, size_t ct)
> char *vcpu_state, *tty;
> char curmem[FMT_SCALED_STRSIZE];
> char maxmem[FMT_SCALED_STRSIZE];
> + char user[16];
> + struct passwd *pw;
> + struct group *gr;
>
> - printf("%5s %5s %5s %7s %7s %7s %s\n", "ID", "PID", "VCPUS",
> - "MAXMEM", "CURMEM", "TTY", "NAME");
> + printf("%5s %5s %5s %7s %7s %7s %12s %s\n", "ID", "PID", "VCPUS",
> + "MAXMEM", "CURMEM", "TTY", "OWNER", "NAME");
>
> for (i = 0; i < ct; i++) {
> vmi = &list[i];
> vir = &vmi->vir_info;
> if (check_info_id(vir->vir_name, vir->vir_id)) {
> + /* get user name */
> + if ((pw = getpwuid(vmi->vir_uid)) == NULL)
> + (void)snprintf(user, sizeof(user),
> + "%d", vmi->vir_uid);
> + else
> + (void)strlcpy(user, pw->pw_name,
> + sizeof(user));
> + /* get group name */
> + if (vmi->vir_gid != -1) {
> + if (vmi->vir_uid == 0)
> + *user = '\0';
> + if ((gr = getgrgid(vmi->vir_gid)) == NULL)
> + (void)snprintf(user, sizeof(user),
> + "%s:%lld", user, vmi->vir_gid);
> + else
> + (void)snprintf(user, sizeof(user),
> + "%s:%s", user, gr->gr_name);
> + }
> +
> (void)strlcpy(curmem, "-", sizeof(curmem));
> (void)strlcpy(maxmem, "-", sizeof(maxmem));
>
> @@ -392,16 +416,16 @@ print_vm_info(struct vmop_info_result *list, size_t ct)
> (void)fmt_scaled(vir->vir_used_size, curmem);
>
> /* running vm */
> - printf("%5u %5u %5zd %7s %7s %7s %s\n",
> + printf("%5u %5u %5zd %7s %7s %7s %12s %s\n",
> vir->vir_id, vir->vir_creator_pid,
> vir->vir_ncpus, maxmem, curmem,
> - tty, vir->vir_name);
> + tty, user, vir->vir_name);
> } else {
> /* disabled vm */
> - printf("%5s %5s %5zd %7s %7s %7s %s\n",
> + printf("%5s %5s %5zd %7s %7s %7s %12s %s\n",
> "-", "-",
> vir->vir_ncpus, maxmem, curmem,
> - "-", vir->vir_name);
> + "-", user, vir->vir_name);
> }
> }
> if (check_info_id(vir->vir_name, vir->vir_id) > 0) {
> diff --git usr.sbin/vmd/config.c usr.sbin/vmd/config.c
> index a16c143..72c151a 100644
> --- usr.sbin/vmd/config.c
> +++ usr.sbin/vmd/config.c
> @@ -120,7 +120,7 @@ config_getreset(struct vmd *env, struct imsg *imsg)
> }
>
> int
> -config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid)
> +config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t
> uid)
> {
> struct vmd_if *vif;
> struct vmop_create_params *vmc = &vm->vm_params;
> @@ -158,6 +158,7 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm,
> uint32_t peerid)
> tapfds[i] = -1;
>
> vm->vm_peerid = peerid;
> + vm->vm_uid = uid;
>
> /* Open external kernel for child */
> if (strlen(vcp->vcp_kernel) &&
> @@ -309,7 +310,7 @@ config_getvm(struct privsep *ps, struct imsg *imsg)
> memcpy(&vmc, imsg->data, sizeof(vmc));
>
> errno = 0;
> - if (vm_register(ps, &vmc, &vm, imsg->hdr.peerid) == -1)
> + if (vm_register(ps, &vmc, &vm, imsg->hdr.peerid, 0) == -1)
> goto fail;
>
> /* If the fd is -1, the kernel will be searched on the disk */
> diff --git usr.sbin/vmd/control.c usr.sbin/vmd/control.c
> index cda7df9..0bdf90e 100644
> --- usr.sbin/vmd/control.c
> +++ usr.sbin/vmd/control.c
> @@ -324,6 +324,8 @@ control_dispatch_imsg(int fd, short event, void *arg)
>
> switch (imsg.hdr.type) {
> case IMSG_VMDOP_GET_INFO_VM_REQUEST:
> + case IMSG_VMDOP_TERMINATE_VM_REQUEST:
> + case IMSG_VMDOP_START_VM_REQUEST:
> break;
> default:
> if (c->peercred.uid != 0) {
> @@ -351,7 +353,6 @@ control_dispatch_imsg(int fd, short event, void *arg)
> case IMSG_CTL_VERBOSE:
> if (IMSG_DATA_SIZE(&imsg) < sizeof(v))
> goto fail;
> -
> memcpy(&v, imsg.data, sizeof(v));
> log_setverbose(v);
>
> @@ -360,9 +361,12 @@ control_dispatch_imsg(int fd, short event, void *arg)
> case IMSG_VMDOP_START_VM_REQUEST:
> if (IMSG_DATA_SIZE(&imsg) < sizeof(vmc))
> goto fail;
> + memcpy(&vmc, imsg.data, sizeof(vmc));
> + vmc.vmc_uid = c->peercred.uid;
> + vmc.vmc_gid = -1;
> +
> if (proc_compose_imsg(ps, PROC_PARENT, -1,
> - imsg.hdr.type, fd, -1,
> - imsg.data, IMSG_DATA_SIZE(&imsg)) == -1) {
> + imsg.hdr.type, fd, -1, &vmc, sizeof(vmc)) == -1) {
> control_close(fd, cs);
> return;
> }
> @@ -370,9 +374,11 @@ control_dispatch_imsg(int fd, short event, void *arg)
> case IMSG_VMDOP_TERMINATE_VM_REQUEST:
> if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
> goto fail;
> + memcpy(&vid, imsg.data, sizeof(vid));
> + vid.vid_uid = c->peercred.uid;
> +
> if (proc_compose_imsg(ps, PROC_PARENT, -1,
> - imsg.hdr.type, fd, -1,
> - imsg.data, IMSG_DATA_SIZE(&imsg)) == -1) {
> + imsg.hdr.type, fd, -1, &vid, sizeof(vid)) == -1) {
> control_close(fd, cs);
> return;
> }
> diff --git usr.sbin/vmd/parse.y usr.sbin/vmd/parse.y
> index 30f002f..062146b 100644
> --- usr.sbin/vmd/parse.y
> +++ usr.sbin/vmd/parse.y
> @@ -44,6 +44,8 @@
> #include <util.h>
> #include <errno.h>
> #include <err.h>
> +#include <pwd.h>
> +#include <grp.h>
>
> #include "proc.h"
> #include "vmd.h"
> @@ -101,6 +103,10 @@ typedef struct {
> uint8_t lladdr[ETHER_ADDR_LEN];
> int64_t number;
> char *string;
> + struct {
> + uid_t uid;
> + int64_t gid;
> + } owner;
> } v;
> int lineno;
> } YYSTYPE;
> @@ -110,7 +116,7 @@ typedef struct {
>
> %token INCLUDE ERROR
> %token ADD DISK DOWN GROUP INTERFACE NIFS PATH SIZE SWITCH UP VMID
> -%token ENABLE DISABLE VM KERNEL LLADDR MEMORY
> +%token ENABLE DISABLE VM KERNEL LLADDR MEMORY OWNER
> %token <v.string> STRING
> %token <v.number> NUMBER
> %type <v.number> disable
> @@ -118,6 +124,7 @@ typedef struct {
> %type <v.lladdr> lladdr
> %type <v.string> string
> %type <v.string> optstring
> +%type <v.owner> owner_id
>
> %%
>
> @@ -260,6 +267,10 @@ vm : VM string {
> yyerror("vm name too long");
> YYERROR;
> }
> +
> + /* set default user/group permissions */
> + vmc.vmc_uid = 0;
> + vmc.vmc_gid = -1;
> } '{' optnl vm_opts_l '}' {
> int ret;
>
> @@ -268,7 +279,8 @@ vm : VM string {
> vcp->vcp_nnics = vcp_nnics;
>
> if (!env->vmd_noaction) {
> - ret = vm_register(&env->vmd_ps, &vmc, &vm, 0);
> + ret = vm_register(&env->vmd_ps, &vmc,
> + &vm, 0, 0);
> if (ret == -1 && errno == EALREADY) {
> log_debug("%s:%d: vm \"%s\""
> " skipped (%s)",
> @@ -398,6 +410,57 @@ vm_opts : disable {
> vcp->vcp_memranges[0].vmr_size = (size_t)res;
> vmc.vmc_flags |= VMOP_CREATE_MEMORY;
> }
> + | OWNER owner_id {
> + vmc.vmc_uid = $2.uid;
> + vmc.vmc_gid = $2.gid;
> + }
> + ;
> +
> +owner_id : /* none */ {
> + $$.uid = 0;
> + $$.gid = -1;
> + }
> + | NUMBER {
> + $$.uid = $1;
> + $$.gid = -1;
> + }
> + | STRING {
> + char *user, *group;
> + struct passwd *pw;
> + struct group *gr;
> +
> + $$.uid = 0;
> + $$.gid = -1;
> +
> + user = $1;
> + if ((group = strchr(user, ':')) != NULL) {
> + if (group == user)
> + user = NULL;
> + *group++ = '\0';
> + }
> +
> + if (user != NULL && *user) {
> + if ((pw = getpwnam(user)) == NULL) {
> + yyerror("failed to get user: %s",
> + user);
> + free($1);
> + YYERROR;
> + }
> + $$.uid = pw->pw_uid;
> + }
> +
> + if (group != NULL && *group) {
> + if ((gr = getgrnam(group)) == NULL) {
> + yyerror("failed to get group: %s",
> + group);
> + free($1);
> + YYERROR;
> + }
> + $$.gid = gr->gr_gid;
> + }
> +
> + free($1);
> + }
> ;
>
> iface_opts_o : '{' optnl iface_opts_l '}'
> @@ -544,6 +607,7 @@ lookup(char *s)
> { "kernel", KERNEL },
> { "lladdr", LLADDR },
> { "memory", MEMORY },
> + { "owner", OWNER },
> { "size", SIZE },
> { "switch", SWITCH },
> { "up", UP },
> diff --git usr.sbin/vmd/vm.conf.5 usr.sbin/vmd/vm.conf.5
> index 9a48a51..c1401ec 100644
> --- usr.sbin/vmd/vm.conf.5
> +++ usr.sbin/vmd/vm.conf.5
> @@ -161,6 +161,12 @@ Kernel to load when booting the VM.
> .It Cm memory Ar bytes
> Memory size of the VM, in bytes, rounded to megabytes.
> The default is 512M.
> +.It Cm owner Ar user Ns Op : Ns Ar group
> +Set the owner of the VM to the specified user or group.
> +The owner will be allowed to start or stop the VM and get the
> +permissions to open the VM's console.
> +.It Cm owner Pf : Ar group
> +Set the owner to the specified group.
> .El
> .Sh SWITCH CONFIGURATION
> A virtual switch allows VMs to communicate with other network interfaces on
> the
> diff --git usr.sbin/vmd/vmd.c usr.sbin/vmd/vmd.c
> index ed678ee..6152c4f 100644
> --- usr.sbin/vmd/vmd.c
> +++ usr.sbin/vmd/vmd.c
> @@ -20,6 +20,7 @@
> #include <sys/queue.h>
> #include <sys/wait.h>
> #include <sys/cdefs.h>
> +#include <sys/stat.h>
> #include <sys/tty.h>
> #include <sys/ioctl.h>
>
> @@ -35,6 +36,8 @@
> #include <syslog.h>
> #include <unistd.h>
> #include <ctype.h>
> +#include <pwd.h>
> +#include <grp.h>
>
> #include "proc.h"
> #include "vmd.h"
> @@ -80,7 +83,7 @@ vmd_dispatch_control(int fd, struct privsep_proc *p, struct
> imsg *imsg)
> case IMSG_VMDOP_START_VM_REQUEST:
> IMSG_SIZE_CHECK(imsg, &vmc);
> memcpy(&vmc, imsg->data, sizeof(vmc));
> - ret = vm_register(ps, &vmc, &vm, 0);
> + ret = vm_register(ps, &vmc, &vm, 0, vmc.vmc_uid);
> if (vmc.vmc_flags == 0) {
> /* start an existing VM with pre-configured options */
> if (!(ret == -1 && errno == EALREADY)) {
> @@ -92,7 +95,7 @@ vmd_dispatch_control(int fd, struct privsep_proc *p, struct
> imsg *imsg)
> cmd = IMSG_VMDOP_START_VM_RESPONSE;
> }
> if (res == 0 &&
> - config_setvm(ps, vm, imsg->hdr.peerid) == -1) {
> + config_setvm(ps, vm, imsg->hdr.peerid, vmc.vmc_uid) == -1) {
> res = errno;
> cmd = IMSG_VMDOP_START_VM_RESPONSE;
> }
> @@ -108,6 +111,12 @@ vmd_dispatch_control(int fd, struct privsep_proc *p,
> struct imsg *imsg)
> break;
> }
> id = vm->vm_params.vmc_params.vcp_id;
> + } else
> + vm = vm_getbyid(id);
> + if (vm_checkperm(vm, vid.vid_uid) != 0) {
> + res = EPERM;
> + cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
> + break;
> }
> memset(&vtp, 0, sizeof(vtp));
> vtp.vtp_vm_id = id;
> @@ -247,7 +256,7 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct
> imsg *imsg)
> } else if (vmr.vmr_result == EAGAIN) {
> /* Stop VM instance but keep the tty open */
> vm_stop(vm, 1);
> - config_setvm(ps, vm, (uint32_t)-1);
> + config_setvm(ps, vm, (uint32_t)-1, 0);
> }
> break;
> case IMSG_VMDOP_GET_INFO_VM_DATA:
> @@ -256,6 +265,9 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct
> imsg *imsg)
> if ((vm = vm_getbyid(vir.vir_info.vir_id)) != NULL) {
> (void)strlcpy(vir.vir_ttyname, vm->vm_ttyname,
> sizeof(vir.vir_ttyname));
> + /* get the user id who started the vm */
> + vir.vir_uid = vm->vm_uid;
> + vir.vir_gid = vm->vm_params.vmc_gid;
> }
> if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type,
> imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) {
> @@ -280,6 +292,9 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct
> imsg *imsg)
>
> vm->vm_params.vmc_params.vcp_memranges[0].vmr_size;
> vir.vir_info.vir_ncpus =
> vm->vm_params.vmc_params.vcp_ncpus;
> + /* get the configured user id for this vm */
> + vir.vir_uid = vm->vm_params.vmc_uid;
> + vir.vir_gid = vm->vm_params.vmc_gid;
> if (proc_compose_imsg(ps, PROC_CONTROL, -1,
> IMSG_VMDOP_GET_INFO_VM_DATA,
> imsg->hdr.peerid, -1, &vir,
> @@ -496,8 +511,11 @@ vmd_configure(void)
> * tty - for openpty.
> * proc - run kill to terminate its children safely.
> * sendfd - for disks, interfaces and other fds.
> + * getpw - lookup user or group id by name.
> + * chown, fattr - change tty ownership
> */
> - if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1)
> + if (pledge("stdio rpath wpath proc tty sendfd getpw"
> + " chown fattr", NULL) == -1)
> fatal("pledge");
>
> if (parse_config(env->vmd_conffile) == -1) {
> @@ -529,7 +547,7 @@ vmd_configure(void)
> vm->vm_params.vmc_params.vcp_name);
> continue;
> }
> - if (config_setvm(&env->vmd_ps, vm, -1) == -1)
> + if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1)
> return (-1);
> }
>
> @@ -595,7 +613,7 @@ vmd_reload(unsigned int reset, const char *filename)
> vm->vm_params.vmc_params.vcp_name);
> continue;
> }
> - if (config_setvm(&env->vmd_ps, vm, -1) == -1)
> + if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1)
> return;
> } else {
> log_debug("%s: not creating vm \"%s\": "
> @@ -712,6 +730,7 @@ vm_stop(struct vmd_vm *vm, int keeptty)
> close(vm->vm_kernel);
> vm->vm_kernel = -1;
> }
> + vm->vm_uid = 0;
> if (!keeptty)
> vm_closetty(vm);
> }
> @@ -729,7 +748,7 @@ vm_remove(struct vmd_vm *vm)
>
> int
> vm_register(struct privsep *ps, struct vmop_create_params *vmc,
> - struct vmd_vm **ret_vm, uint32_t id)
> + struct vmd_vm **ret_vm, uint32_t id, uid_t uid)
> {
> struct vmd_vm *vm = NULL;
> struct vm_create_params *vcp = &vmc->vmc_params;
> @@ -739,11 +758,23 @@ vm_register(struct privsep *ps, struct
> vmop_create_params *vmc,
> *ret_vm = NULL;
>
> if ((vm = vm_getbyname(vcp->vcp_name)) != NULL) {
> + if (vm_checkperm(vm, uid) != 0 || vmc->vmc_flags != 0) {
> + errno = EPERM;
> + goto fail;
> + }
> *ret_vm = vm;
> errno = EALREADY;
> goto fail;
> }
>
> + /*
> + * non-root users can only start existing VMs
> + * XXX there could be a mechanism to allow overwriting some options
> + */
> + if (vm_checkperm(NULL, uid) != 0) {
> + errno = EPERM;
> + goto fail;
> + }
> if (vmc->vmc_flags == 0) {
> errno = ENOENT;
> goto fail;
> @@ -797,9 +828,45 @@ vm_register(struct privsep *ps, struct
> vmop_create_params *vmc,
> }
>
> int
> +vm_checkperm(struct vmd_vm *vm, uid_t uid)
> +{
> + struct group *gr;
> + struct passwd *pw;
> + char **grmem;
> +
> + /* root has no restrictions */
> + if (uid == 0)
> + return (0);
> +
> + if (vm == NULL)
> + return (-1);
> +
> + /* check supplementary groups */
> + if (vm->vm_params.vmc_gid != -1 &&
> + (pw = getpwuid(uid)) != NULL &&
> + (gr = getgrgid(vm->vm_params.vmc_gid)) != NULL) {
> + for (grmem = gr->gr_mem; *grmem; grmem++)
> + if (strcmp(*grmem, pw->pw_name) == 0)
> + return (0);
> + }
> +
> + /* check user */
> + if ((vm->vm_running && vm->vm_uid == uid) ||
> + (!vm->vm_running && vm->vm_params.vmc_uid == uid))
> + return (0);
> +
> + return (-1);
> +}
> +
> +int
> vm_opentty(struct vmd_vm *vm)
> {
> struct ptmget ptm;
> + struct stat st;
> + struct group *gr;
> + uid_t uid;
> + gid_t gid;
> + mode_t mode;
>
> /*
> * Open tty with pre-opened PTM fd
> @@ -812,6 +879,54 @@ vm_opentty(struct vmd_vm *vm)
> if ((vm->vm_ttyname = strdup(ptm.sn)) == NULL)
> goto fail;
>
> + uid = vm->vm_uid;
> + gid = vm->vm_params.vmc_gid;
> +
> + if (vm->vm_params.vmc_gid != -1) {
> + mode = 0660;
> + } else if ((gr = getgrnam("tty")) != NULL) {
> + gid = gr->gr_gid;
> + mode = 0620;
> + } else {
> + mode = 0600;
> + gid = 0;
> + }
> +
> + log_debug("%s: vm %s tty %s uid %d gid %d mode %o",
> + __func__, vm->vm_params.vmc_params.vcp_name,
> + vm->vm_ttyname, uid, gid, mode);
> +
> + /*
> + * Change ownership and mode of the tty as required.
> + * Loosely based on the implementation of sshpty.c
> + */
> + if (stat(vm->vm_ttyname, &st) == -1)
> + goto fail;
> +
> + if (st.st_uid != uid || st.st_gid != gid) {
> + if (chown(vm->vm_ttyname, uid, gid) == -1) {
> + log_warn("chown %s %d %d failed, uid %d",
> + vm->vm_ttyname, uid, gid, getuid());
> +
> + /* Ignore failure on read-only filesystems */
> + if (!((errno == EROFS) &&
> + (st.st_uid == uid || st.st_uid == 0)))
> + goto fail;
> + }
> + }
> +
> + if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
> + if (chmod(vm->vm_ttyname, mode) == -1) {
> + log_warn("chmod %s %o failed, uid %d",
> + vm->vm_ttyname, mode, getuid());
> +
> + /* Ignore failure on read-only filesystems */
> + if (!((errno == EROFS) &&
> + (st.st_uid == uid || st.st_uid == 0)))
> + goto fail;
> + }
> + }
> +
> return (0);
> fail:
> vm_closetty(vm);
> @@ -822,6 +937,11 @@ void
> vm_closetty(struct vmd_vm *vm)
> {
> if (vm->vm_tty != -1) {
> + /* Release and close the tty */
> + if (fchown(vm->vm_tty, 0, 0) == -1)
> + log_warn("chown %s 0 0 failed", vm->vm_ttyname);
> + if (fchmod(vm->vm_tty, 0666) == -1)
> + log_warn("chmod %s 0666 failed", vm->vm_ttyname);
> close(vm->vm_tty);
> vm->vm_tty = -1;
> }
> diff --git usr.sbin/vmd/vmd.h usr.sbin/vmd/vmd.h
> index 26d345c..af31349 100644
> --- usr.sbin/vmd/vmd.h
> +++ usr.sbin/vmd/vmd.h
> @@ -87,11 +87,14 @@ struct vmop_result {
> struct vmop_info_result {
> struct vm_info_result vir_info;
> char vir_ttyname[VM_TTYNAME_MAX];
> + uid_t vir_uid;
> + int64_t vir_gid;
> };
>
> struct vmop_id {
> uint32_t vid_id;
> char vid_name[VMM_MAX_NAME_LEN];
> + uid_t vid_uid;
> };
>
> struct vmop_ifreq {
> @@ -113,6 +116,8 @@ struct vmop_create_params {
> char vmc_ifnames[VMM_MAX_NICS_PER_VM][IF_NAMESIZE];
> char vmc_ifswitch[VMM_MAX_NICS_PER_VM][VM_NAME_MAX];
> char vmc_ifgroup[VMM_MAX_NICS_PER_VM][IF_NAMESIZE];
> + uid_t vmc_uid;
> + int64_t vmc_gid;
> };
>
> struct vmboot_params {
> @@ -166,6 +171,8 @@ struct vmd_vm {
> int vm_from_config;
> struct imsgev vm_iev;
> int vm_shutdown;
> + uid_t vm_uid;
> +
> TAILQ_ENTRY(vmd_vm) vm_entry;
> };
> TAILQ_HEAD(vmlist, vmd_vm);
> @@ -197,7 +204,8 @@ struct vmd_vm *vm_getbypid(pid_t);
> void vm_stop(struct vmd_vm *, int);
> void vm_remove(struct vmd_vm *);
> int vm_register(struct privsep *, struct vmop_create_params *,
> - struct vmd_vm **, uint32_t);
> + struct vmd_vm **, uint32_t, uid_t);
> +int vm_checkperm(struct vmd_vm *, uid_t);
> int vm_opentty(struct vmd_vm *);
> void vm_closetty(struct vmd_vm *);
> void switch_remove(struct vmd_switch *);
> @@ -227,7 +235,7 @@ int config_init(struct vmd *);
> void config_purge(struct vmd *, unsigned int);
> int config_setreset(struct vmd *, unsigned int);
> int config_getreset(struct vmd *, struct imsg *);
> -int config_setvm(struct privsep *, struct vmd_vm *, uint32_t);
> +int config_setvm(struct privsep *, struct vmd_vm *, uint32_t, uid_t);
> int config_getvm(struct privsep *, struct imsg *);
> int config_getdisk(struct privsep *, struct imsg *);
> int config_getif(struct privsep *, struct imsg *);
>