On Mon, Feb 27, 2017 at 12:21:19PM -0800, Mike Larkin wrote:
> 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
>
Probably "overriding" in this case.
Reyk
> > 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 *);
> >
--