minix/servers/vm/rs.c
David van Moolenbroek 78d707cd26 VM: support for shared call mask ACLs
The VM server now manages its call masks such that all user processes
share the same call mask. As a result, an update for the call mask of
any user process will apply to all user processes. This is similar to
the privilege infrastructure employed by the kernel, and may serve as
a template for similar fine-grained restrictions in other servers.

Concretely, this patch fixes the problem of "service edit init" not
applying the given VM call mask to user processes started from RC
scripts during system startup.

In addition, this patch makes RS set a proper VM call mask for each
recovery script it spawns.

Change-Id: I520a30d85a0d3f3502d2b158293a2258825358cf
2013-08-08 23:22:58 +02:00

214 lines
4.7 KiB
C

#define _SYSTEM 1
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/config.h>
#include <minix/const.h>
#include <minix/ds.h>
#include <minix/endpoint.h>
#include <minix/keymap.h>
#include <minix/minlib.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/safecopies.h>
#include <minix/bitmap.h>
#include <errno.h>
#include <string.h>
#include <env.h>
#include <stdio.h>
#include <assert.h>
#include "glo.h"
#include "proto.h"
#include "util.h"
#include "region.h"
/*===========================================================================*
* do_rs_set_priv *
*===========================================================================*/
int do_rs_set_priv(message *m)
{
int r, n, nr;
struct vmproc *vmp;
bitchunk_t call_mask[VM_CALL_MASK_SIZE], *call_mask_p;
nr = m->VM_RS_NR;
if ((r = vm_isokendpt(nr, &n)) != OK) {
printf("do_rs_set_priv: bad endpoint %d\n", nr);
return EINVAL;
}
vmp = &vmproc[n];
if (m->VM_RS_BUF) {
r = sys_datacopy(m->m_source, (vir_bytes) m->VM_RS_BUF, SELF,
(vir_bytes) call_mask, sizeof(call_mask));
if (r != OK)
return r;
call_mask_p = call_mask;
} else {
if (m->VM_RS_SYS) {
printf("VM: do_rs_set_priv: sys procs don't share!\n");
return EINVAL;
}
call_mask_p = NULL;
}
acl_set(vmp, call_mask_p, m->VM_RS_SYS);
return OK;
}
/*===========================================================================*
* do_rs_update *
*===========================================================================*/
int do_rs_update(message *m_ptr)
{
endpoint_t src_e, dst_e, reply_e;
int src_p, dst_p;
struct vmproc *src_vmp, *dst_vmp;
int r;
src_e = m_ptr->VM_RS_SRC_ENDPT;
dst_e = m_ptr->VM_RS_DST_ENDPT;
/* Lookup slots for source and destination process. */
if(vm_isokendpt(src_e, &src_p) != OK) {
printf("do_rs_update: bad src endpoint %d\n", src_e);
return EINVAL;
}
src_vmp = &vmproc[src_p];
if(vm_isokendpt(dst_e, &dst_p) != OK) {
printf("do_rs_update: bad dst endpoint %d\n", dst_e);
return EINVAL;
}
dst_vmp = &vmproc[dst_p];
/* Let the kernel do the update first. */
r = sys_update(src_e, dst_e);
if(r != OK) {
return r;
}
/* Do the update in VM now. */
r = swap_proc_slot(src_vmp, dst_vmp);
if(r != OK) {
return r;
}
r = swap_proc_dyn_data(src_vmp, dst_vmp);
if(r != OK) {
return r;
}
pt_bind(&src_vmp->vm_pt, src_vmp);
pt_bind(&dst_vmp->vm_pt, dst_vmp);
/* Reply, update-aware. */
reply_e = m_ptr->m_source;
if(reply_e == src_e) reply_e = dst_e;
else if(reply_e == dst_e) reply_e = src_e;
m_ptr->m_type = OK;
r = send(reply_e, m_ptr);
if(r != OK) {
panic("send() error");
}
return SUSPEND;
}
/*===========================================================================*
* rs_memctl_make_vm_instance *
*===========================================================================*/
static int rs_memctl_make_vm_instance(struct vmproc *new_vm_vmp)
{
int r;
u32_t flags;
int verify;
struct vmproc *this_vm_vmp;
this_vm_vmp = &vmproc[VM_PROC_NR];
/* Pin memory for the new VM instance. */
r = map_pin_memory(new_vm_vmp);
if(r != OK) {
return r;
}
/* Preallocate page tables for the entire address space for both
* VM and the new VM instance.
*/
flags = 0;
verify = FALSE;
r = pt_ptalloc_in_range(&this_vm_vmp->vm_pt, 0, 0, flags, verify);
if(r != OK) {
return r;
}
r = pt_ptalloc_in_range(&new_vm_vmp->vm_pt, 0, 0, flags, verify);
if(r != OK) {
return r;
}
/* Let the new VM instance map VM's page tables and its own. */
r = pt_ptmap(this_vm_vmp, new_vm_vmp);
if(r != OK) {
return r;
}
r = pt_ptmap(new_vm_vmp, new_vm_vmp);
if(r != OK) {
return r;
}
return OK;
}
/*===========================================================================*
* do_rs_memctl *
*===========================================================================*/
int do_rs_memctl(message *m_ptr)
{
endpoint_t ep;
int req, r, proc_nr;
struct vmproc *vmp;
ep = m_ptr->VM_RS_CTL_ENDPT;
req = m_ptr->VM_RS_CTL_REQ;
/* Lookup endpoint. */
if ((r = vm_isokendpt(ep, &proc_nr)) != OK) {
printf("do_rs_memctl: bad endpoint %d\n", ep);
return EINVAL;
}
vmp = &vmproc[proc_nr];
/* Process request. */
switch(req)
{
case VM_RS_MEM_PIN:
/* Do not perform VM_RS_MEM_PIN yet - it costs the full
* size of the RS stack (64MB by default) in memory,
* and it's needed for functionality that isn't complete /
* merged in current Minix (surviving VM crashes).
*/
#if 0
r = map_pin_memory(vmp);
return r;
#else
return OK;
#endif
case VM_RS_MEM_MAKE_VM:
r = rs_memctl_make_vm_instance(vmp);
return r;
default:
printf("do_rs_memctl: bad request %d\n", req);
return EINVAL;
}
}