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

Reply via email to