]> Zhao Yanbai Git Server - minix.git/commitdiff
VM: support for shared call mask ACLs 17/717/3
authorDavid van Moolenbroek <david@minix3.org>
Wed, 7 Aug 2013 20:03:47 +0000 (22:03 +0200)
committerGerrit Code Review <gerrit@gerrit>
Thu, 8 Aug 2013 21:22:58 +0000 (23:22 +0200)
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

16 files changed:
include/minix/com.h
include/minix/rs.h
include/minix/vm.h
lib/libminlib/vm_set_priv.c
servers/rs/manager.c
servers/rs/request.c
servers/vfs/dmap.c
servers/vm/Makefile
servers/vm/acl.c [new file with mode: 0644]
servers/vm/exit.c
servers/vm/fork.c
servers/vm/main.c
servers/vm/proto.h
servers/vm/rs.c
servers/vm/utility.c
servers/vm/vmproc.h

index df1e7fc34004be58d2a2edab6c0dd0fa24b25c7b..a6589ca78d21c5de0421c5f498e37d3865b4b055 100644 (file)
 #define VM_RS_SET_PRIV         (VM_RQ_BASE+37)
 #      define VM_RS_NR                 m2_i1
 #      define VM_RS_BUF                m2_l1
+#      define VM_RS_SYS                m2_i2
 
 #define VM_QUERY_EXIT          (VM_RQ_BASE+38)
 #      define VM_QUERY_RET_PT  m2_i1
index 71f9868b8f5dc4c83facac1b6814eb76fe8e5ffe..f48d552e7f54afb5fe1088447e6b3e7e3cc25ee3 100644 (file)
@@ -119,6 +119,11 @@ struct rprocpub {
   int devman_id;
 };
 
+/* Return whether the given boot process is a user process, as opposed to a
+ * system process. Only usable by core services during SEF initialization.
+ */
+#define IS_RPUB_BOOT_USR(rpub) ((rpub)->endpoint == INIT_PROC_NR)
+
 int minix_rs_lookup(const char *name, endpoint_t *value);
 
 #endif
index 914c01925f1d6e639588aa65c5c411a78bc6a27e..a4388ddb394e0fde0ba481fe18a45d43436a00d6 100644 (file)
@@ -17,7 +17,7 @@ void *vm_map_phys(endpoint_t who, void *physaddr, size_t len);
 int vm_unmap_phys(endpoint_t who, void *vaddr, size_t len);
 
 int vm_notify_sig(endpoint_t ep, endpoint_t ipc_ep);
-int vm_set_priv(int procnr, void *buf);
+int vm_set_priv(endpoint_t ep, void *buf, int sys_proc);
 int vm_update(endpoint_t src_e, endpoint_t dst_e);
 int vm_memctl(endpoint_t ep, int req);
 int vm_query_exit(endpoint_t *endpt);
index ea286526a2a089c35f6aec357496c9bb099fd433..f7d2ed0265fc1a9784759df003d212ea6034a9d0 100644 (file)
@@ -1,11 +1,12 @@
 #include <lib.h>
 #include <unistd.h>
 
-int vm_set_priv(int nr, void *buf)
+int vm_set_priv(endpoint_t ep, void *buf, int sys_proc)
 {
        message m;
-       m.VM_RS_NR = nr;
+       m.VM_RS_NR = ep;
        m.VM_RS_BUF = (long) buf;
+       m.VM_RS_SYS = sys_proc;
        return _syscall(VM_PROC_NR, VM_RS_SET_PRIV, &m);
 }
 
index 89bf45ba722369ffb0bfa9e65494711fffa9d3af..7926d2b673d84449276bb858f541facbf3f5de37 100644 (file)
@@ -563,7 +563,7 @@ struct rproc *rp;
   }
 
   /* Tell VM about allowed calls. */
-  if ((s = vm_set_priv(rpub->endpoint, &rpub->vm_call_mask[0])) != OK) {
+  if ((s = vm_set_priv(rpub->endpoint, &rpub->vm_call_mask[0], TRUE)) != OK) {
       printf("RS: vm_set_priv failed: %d\n", s);
       cleanup_service(rp);
       return s;
@@ -1139,6 +1139,11 @@ static int run_script(struct rproc *rp)
                        != OK) {
                        return kill_service(rp,"can't set script privileges",r);
                }
+               /* Set the script's privileges on other servers. */
+               vm_set_priv(endpoint, NULL, FALSE);
+               if ((r = vm_set_priv(endpoint, NULL, FALSE)) != OK) {
+                       return kill_service(rp,"can't set script VM privs",r);
+               }
                /* Allow the script to run. */
                if ((r = sys_privctl(endpoint, SYS_PRIV_ALLOW, NULL)) != OK) {
                        return kill_service(rp,"can't let the script run",r);
index 5096071deead36ea7a08cb7688f404811bdcd862..ac49c82675550a9630c5d55b23cd3de07e0a3bef 100755 (executable)
@@ -291,7 +291,8 @@ int do_edit(message *m_ptr)
   }
 
   /* Update VM calls. */
-  if ((r = vm_set_priv(rpub->endpoint, &rpub->vm_call_mask[0])) != OK) {
+  if ((r = vm_set_priv(rpub->endpoint, &rpub->vm_call_mask[0],
+    !!(rp->r_priv.s_flags & SYS_PROC))) != OK) {
       printf("RS: do_edit: failed: %d\n", r);
       return r;
   }
index 3afe7eeb0116432fe8be6a29d478c5e1fbf0c5b6..7f0b513bef6b675f27c3d327fc01296c02fe5f90 100644 (file)
@@ -242,6 +242,8 @@ int map_service(struct rprocpub *rpub)
   struct dmap *fdp, *sdp;
   struct fproc *rfp;
 
+  if (IS_RPUB_BOOT_USR(rpub)) return(OK);
+
   /* Process is a service */
   if (isokendpt(rpub->endpoint, &slot) != OK) {
        printf("VFS: can't map service with unknown endpoint %d\n",
index 85bfd5629ebfa7453395f489397f1017e648094f..0e09e38e8a4bbc2f36e325d30fb66b1547436414 100644 (file)
@@ -6,7 +6,7 @@ SRCS=   main.c alloc.c utility.c exit.c fork.c break.c \
        mmap.c slaballoc.c region.c pagefaults.c \
        rs.c queryexit.c pb.c regionavl.c \
        mem_anon.c mem_directphys.c mem_anon_contig.c mem_shared.c      \
-       mem_cache.c cache.c vfs.c mem_file.c fdref.c
+       mem_cache.c cache.c vfs.c mem_file.c fdref.c acl.c
 
 .if ${MACHINE_ARCH} == "earm"
 LDFLAGS+= -T ${.CURDIR}/arch/${MACHINE_ARCH}/vm.lds
diff --git a/servers/vm/acl.c b/servers/vm/acl.c
new file mode 100644 (file)
index 0000000..a31b22c
--- /dev/null
@@ -0,0 +1,120 @@
+
+/* Call mask ACL management. */
+
+#include <minix/drivers.h>
+
+#include "proto.h"
+#include "glo.h"
+#include "util.h"
+
+#define NO_ACL         -1
+#define USER_ACL        0
+#define FIRST_SYS_ACL   1
+
+static bitchunk_t acl_mask[NR_SYS_PROCS][VM_CALL_MASK_SIZE];
+static bitchunk_t acl_inuse[BITMAP_CHUNKS(NR_SYS_PROCS)];
+
+/*
+ * Initialize ACL data structures.
+ */
+void
+acl_init(void)
+{
+       int i;
+
+       for (i = 0; i < ELEMENTS(vmproc); i++)
+               vmproc[i].vm_acl = NO_ACL;
+
+       memset(acl_mask, 0, sizeof(acl_mask));
+       memset(acl_inuse, 0, sizeof(acl_inuse));
+}
+
+/*
+ * Check whether a process is allowed to make a certain (zero-based) call.
+ * Return OK or an error.
+ */
+int
+acl_check(struct vmproc *vmp, int call)
+{
+       /* If the process has no ACL, all calls are allowed.. for now. */
+       if (vmp->vm_acl == NO_ACL) {
+               printf("VM: calling process %u has no ACL!\n",
+                   vmp->vm_endpoint);
+
+               return OK;
+       }
+
+       /* See if the call is allowed. */
+       if (!GET_BIT(acl_mask[vmp->vm_acl], call))
+               return EPERM;
+
+       return OK;
+}
+
+/*
+ * Assign a call mask to a process.  User processes share the first ACL entry.
+ * System processes are assigned to any of the other slots.  For user
+ * processes, no call mask need to be provided: it will simply be inherited in
+ * that case.
+ */
+void
+acl_set(struct vmproc *vmp, bitchunk_t *mask, int sys_proc)
+{
+       int i;
+
+       acl_clear(vmp);
+
+       if (sys_proc) {
+               for (i = FIRST_SYS_ACL; i < NR_SYS_PROCS; i++)
+                       if (!GET_BIT(acl_inuse, i))
+                               break;
+
+               /*
+                * This should never happen.  If it does, then different user
+                * processes have been assigned call masks separately.  It is
+                * RS's responsibility to prevent that.
+                */
+               if (i == NR_SYS_PROCS) {
+                       printf("VM: no ACL entries available!\n");
+                       return;
+               }
+       } else
+               i = USER_ACL;
+
+       if (!GET_BIT(acl_inuse, i) && mask == NULL)
+               printf("VM: WARNING: inheriting uninitialized ACL mask\n");
+
+       SET_BIT(acl_inuse, i);
+       vmp->vm_acl = i;
+
+       if (mask != NULL)
+               memcpy(&acl_mask[vmp->vm_acl], mask, sizeof(acl_mask[0]));
+}
+
+/*
+ * A process has forked.  User processes inherit their parent's ACL by default,
+ * although they may be turned into system processes later.  System processes
+ * do not inherit an ACL, and will have to be assigned one before getting to
+ * run.
+ */
+void
+acl_fork(struct vmproc *vmp)
+{
+       if (vmp->vm_acl != USER_ACL)
+               vmp->vm_acl = NO_ACL;
+}
+
+/*
+ * A process has exited.  Decrease the reference count on its ACL entry, and
+ * mark the process as having no ACL.
+ */
+void
+acl_clear(struct vmproc *vmp)
+{
+       if (vmp->vm_acl != NO_ACL) {
+               if (vmp->vm_acl != USER_ACL)
+                       UNSET_BIT(acl_inuse, vmp->vm_acl);
+
+               vmp->vm_acl = NO_ACL;
+       }
+}
index 11cfcc94ba8313aaed7cd3a506120c01f2fad2a1..cc9a7953894c010bb17b9ddf5b30a6383ac1d0ce 100644 (file)
@@ -47,6 +47,7 @@ void free_proc(struct vmproc *vmp)
 void clear_proc(struct vmproc *vmp)
 {
        region_init(&vmp->vm_regions_avl);
+       acl_clear(vmp);
        vmp->vm_flags = 0;              /* Clear INUSE, so slot is free. */
 #if VMSTATS
        vmp->vm_bytecopies = 0;
index 877ec7f7bd70828b8f738d853d4bcb060f616a18..5b9b319c4f56f5c2f2f05f6bc0e0946eaccd6441 100644 (file)
@@ -84,8 +84,8 @@ int do_fork(message *msg)
   /* Only inherit these flags. */
   vmc->vm_flags &= VMF_INUSE;
 
-  /* inherit the priv call bitmaps */
-  memcpy(&vmc->vm_call_mask, &vmp->vm_call_mask, sizeof(vmc->vm_call_mask));
+  /* Deal with ACLs. */
+  acl_fork(vmc);
 
   /* Tell kernel about the (now successful) FORK. */
   if((r=sys_fork(vmp->vm_endpoint, childproc,
index 1d9f0c723ebb35f1a754b13dac9d6eb63004730e..ce784a4f7c52d531b31fb4a7de6a856f17f0be52 100644 (file)
@@ -57,7 +57,6 @@ struct {
                        ((c) - VM_RQ_BASE) : -1)
 
 static int map_service(struct rprocpub *rpub);
-static int vm_acl_ok(endpoint_t caller, int call);
 static int do_rs_init(message *m);
 
 /* SEF functions and variables. */
@@ -131,7 +130,7 @@ int main(void)
        } else if(c < 0 || !vm_calls[c].vmc_func) {
                /* out of range or missing callnr */
        } else {
-               if (vm_acl_ok(who_e, c) != OK) {
+               if (acl_check(&vmproc[caller_slot], c) != OK) {
                        printf("VM: unauthorized %s by %d\n",
                                        vm_calls[c].vmc_name, who_e);
                } else {
@@ -339,6 +338,9 @@ void init_vm(void)
                vmproc[i].vm_slot = i;
        }
 
+       /* Initialize ACL data structures. */
+       acl_init();
+
        /* region management initialization. */
        map_region_init();
 
@@ -492,27 +494,7 @@ struct rprocpub *rpub;
        }
 
        /* Copy the call mask. */
-       memcpy(&vmproc[proc_nr].vm_call_mask, &rpub->vm_call_mask,
-               sizeof(vmproc[proc_nr].vm_call_mask));
+       acl_set(&vmproc[proc_nr], rpub->vm_call_mask, !IS_RPUB_BOOT_USR(rpub));
 
        return(OK);
 }
-
-/*===========================================================================*
- *                             vm_acl_ok                                    *
- *===========================================================================*/
-static int vm_acl_ok(endpoint_t caller, int call)
-{
-       int n, r;
-
-       if ((r = vm_isokendpt(caller, &n)) != OK)
-               panic("VM: from strange source: %d", caller);
-
-       /* See if the call is allowed. */
-       if (!GET_BIT(vmproc[n].vm_call_mask, call)) {
-               return EPERM;
-       }
-
-       return OK;
-}
-
index 5c98aa223e3709ea08a6b71b7c1357a640f659b5..44e0724cae383f4d623850d957c660504e57d414 100644 (file)
@@ -16,6 +16,13 @@ struct phys_region;
 #include "pt.h"
 #include "vm.h"
 
+/* acl.c */
+void acl_init(void);
+int acl_check(struct vmproc *vmp, int call);
+void acl_set(struct vmproc *vmp, bitchunk_t *mask, int sys_proc);
+void acl_fork(struct vmproc *vmp);
+void acl_clear(struct vmproc *vmp);
+
 /* alloc.c */
 void *reservedqueue_new(int, int, int, int);
 int reservedqueue_alloc(void *, phys_bytes *, void **);
index e932f6b14a6acc487f296bcb338108111138b0f2..b3be72785d995c16b468417a8e2bb73fb30acade 100644 (file)
@@ -34,6 +34,7 @@ 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;
 
@@ -45,13 +46,21 @@ int do_rs_set_priv(message *m)
        vmp = &vmproc[n];
 
        if (m->VM_RS_BUF) {
-               r = sys_datacopy(m->m_source, (vir_bytes) m->VM_RS_BUF,
-                                SELF, (vir_bytes) vmp->vm_call_mask,
-                                sizeof(vmp->vm_call_mask));
+               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;
 }
 
index 9c7640f892aac8600c61cf219c25b3373d700037..1b77a2cdb50002d3df2cecdd71d434f40dcb80e3 100644 (file)
@@ -348,6 +348,6 @@ int do_getrusage(message *m)
        r_usage.ru_minflt = vmp->vm_minor_page_fault;
        r_usage.ru_majflt = vmp->vm_major_page_fault;
 
-       return sys_datacopy(SELF, &r_usage, m->m_source,
+       return sys_datacopy(SELF, (vir_bytes) &r_usage, m->m_source,
                (vir_bytes) m->RU_RUSAGE_ADDR, (vir_bytes) sizeof(r_usage));
 }
index f8c6e430a2f5ede30f80e1f4461dfa0c09700297..a174714a40fa8223c04744bc56d5ee5413830f70 100644 (file)
@@ -20,7 +20,7 @@ struct vmproc {
        /* Regions in virtual address space. */
        region_avl vm_regions_avl;
        vir_bytes  vm_region_top;       /* highest vaddr last inserted */
-       bitchunk_t vm_call_mask[VM_CALL_MASK_SIZE];
+       int vm_acl;
        int vm_slot;            /* process table slot */
 #if VMSTATS
        int vm_bytecopies;