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

-- 

Reply via email to