78d707cd26
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
120 lines
2.5 KiB
C
120 lines
2.5 KiB
C
|
|
/* 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;
|
|
}
|
|
}
|