/*
* Changes:
+ * Mar 02, 2009: Extended isolation policies (Jorrit N. Herder)
* Jul 22, 2005: Created (Jorrit N. Herder)
*/
#include <minix/ds.h>
#include <minix/endpoint.h>
#include <minix/vm.h>
-#include <minix/rs.h>
#include <lib.h>
#include <timers.h> /* For priv.h */
struct rproc *rproc_ptr[NR_PROCS]; /* mapping for fast access */
/* Prototypes for internal functions that do the hard work. */
+FORWARD _PROTOTYPE( int caller_is_root, (endpoint_t endpoint) );
+FORWARD _PROTOTYPE( int caller_can_control, (endpoint_t endpoint,
+ char *label) );
+FORWARD _PROTOTYPE( int copy_label, (endpoint_t src_e,
+ struct rss_label *src_label, char *dst_label, size_t dst_len) );
FORWARD _PROTOTYPE( int start_service, (struct rproc *rp, int flags,
endpoint_t *ep) );
FORWARD _PROTOTYPE( int stop_service, (struct rproc *rp,int how) );
extern int rs_verbose;
/*===========================================================================*
- * do_up *
+ * caller_is_root *
+ *===========================================================================*/
+PRIVATE int caller_is_root(endpoint)
+endpoint_t endpoint; /* caller endpoint */
+{
+ uid_t euid;
+
+ /* Check if caller has root user ID. */
+ euid = getnuid(endpoint);
+ if (rs_verbose && euid != 0)
+ {
+ printf("RS: got unauthorized request from endpoint %d\n", endpoint);
+ }
+
+ return euid == 0;
+}
+
+/*===========================================================================*
+ * caller_can_control *
+ *===========================================================================*/
+PRIVATE int caller_can_control(endpoint, label)
+endpoint_t endpoint;
+char *label;
+{
+ int control_allowed = 0;
+ register struct rproc *rp;
+ int c;
+ char *progname;
+
+ /* Find name of binary for given label. */
+ for (rp = BEG_RPROC_ADDR; rp < END_RPROC_ADDR; rp++) {
+ if (strcmp(rp->r_label, label) == 0) {
+ break;
+ }
+ }
+ if (rp == END_RPROC_ADDR) return 0;
+ progname = strrchr(rp->r_argv[0], '/');
+ if (progname != NULL)
+ progname++;
+ else
+ progname = rp->r_argv[0];
+
+ /* Check if label is listed in caller's isolation policy. */
+ for (rp = BEG_RPROC_ADDR; rp < END_RPROC_ADDR; rp++) {
+ if (rp->r_proc_nr_e == endpoint) {
+ break;
+ }
+ }
+ if (rp == END_RPROC_ADDR) return 0;
+ if (rp->r_nr_control > 0) {
+ for (c = 0; c < rp->r_nr_control; c++) {
+ if (strcmp(rp->r_control[c], progname) == 0)
+ control_allowed = 1;
+ }
+ }
+
+ if (rs_verbose) {
+ printf("RS: allowing %u control over %s via policy: %s\n",
+ endpoint, label, control_allowed ? "yes" : "no");
+ }
+ return control_allowed;
+}
+
+/*===========================================================================*
+ * copy_label *
+ *===========================================================================*/
+PRIVATE int copy_label(src_e, src_label, dst_label, dst_len)
+endpoint_t src_e;
+struct rss_label *src_label;
+char *dst_label;
+size_t dst_len;
+{
+ int s, len;
+
+ len = MIN(dst_len-1, src_label->l_len);
+
+ s = sys_datacopy(src_e, (vir_bytes) src_label->l_addr,
+ SELF, (vir_bytes) dst_label, len);
+ if (s != OK) return s;
+
+ dst_label[len] = 0;
+
+ if (rs_verbose)
+ printf("RS: do_start: using label (custom) '%s'\n", dst_label);
+ return OK;
+}
+
+/*===========================================================================*
+ * do_up *
*===========================================================================*/
PUBLIC int do_up(m_ptr, do_copy, flags)
message *m_ptr; /* request message pointer */
int r;
endpoint_t ep; /* new endpoint no. */
+ /* This call requires special privileges. */
+ if (!caller_is_root(m_ptr->m_source)) return(EPERM);
+
/* See if there is a free entry in the table with system processes. */
for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
rp = &rproc[slot_nr]; /* get pointer to slot */
/*===========================================================================*
- * do_start *
+ * do_start *
*===========================================================================*/
PUBLIC int do_start(m_ptr)
message *m_ptr; /* request message pointer */
struct rproc *tmp_rp;
struct rs_start rs_start;
- /* Get the request structure */
- s= sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR,
- SELF, (vir_bytes) &rs_start, sizeof(rs_start));
- if (s != OK) return(s);
+ /* This call requires special privileges. */
+ if (!caller_is_root(m_ptr->m_source)) return(EPERM);
/* See if there is a free entry in the table with system processes. */
for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
return ENOMEM;
}
+ /* Ok, there is space. Get the request structure. */
+ s= sys_datacopy(m_ptr->m_source, (vir_bytes) m_ptr->RS_CMD_ADDR,
+ SELF, (vir_bytes) &rs_start, sizeof(rs_start));
+ if (s != OK) return(s);
+
/* Obtain command name and parameters. This is a space-separated string
* that looks like "/sbin/service arg1 arg2 ...". Arguments are optional.
*/
rp->r_argv[arg_count] = NULL; /* end with NULL pointer */
rp->r_argc = arg_count;
- if(rs_start.rss_label) {
- int len;
+ if(rs_start.rss_label.l_len > 0) {
/* RS_START caller has supplied a custom label for this driver. */
- len = MIN(sizeof(rp->r_label)-1, rs_start.rss_labellen);
- s=sys_datacopy(m_ptr->m_source, (vir_bytes) rs_start.rss_label,
- SELF, (vir_bytes) rp->r_label, len);
+ int s = copy_label(m_ptr->m_source, &rs_start.rss_label,
+ rp->r_label, sizeof(rp->r_label));
if(s != OK)
return s;
- rp->r_label[len] = '\0';
if(rs_verbose)
printf("RS: do_start: using label (custom) '%s'\n", rp->r_label);
} else {
rp->r_argv[0], rp->r_label);
}
+ if(rs_start.rss_nr_control > 0) {
+ int i, s;
+ if (rs_start.rss_nr_control > RSS_NR_CONTROL)
+ {
+ printf("RS: do_start: too many control labels\n");
+ return EINVAL;
+ }
+ for (i=0; i<rs_start.rss_nr_control; i++) {
+ s = copy_label(m_ptr->m_source, &rs_start.rss_control[i],
+ rp->r_control[i], sizeof(rp->r_control[i]));
+ if(s != OK)
+ return s;
+ }
+ rp->r_nr_control = rs_start.rss_nr_control;
+
+ if (rs_verbose) {
+ printf("RS: do_start: control labels:");
+ for (i=0; i<rp->r_nr_control; i++)
+ printf(" %s", rp->r_control[i]);
+ printf("\n");
+ }
+ }
+
/* Check for duplicates */
for (slot_nr = 0; slot_nr < NR_SYS_PROCS; slot_nr++) {
tmp_rp = &rproc[slot_nr]; /* get pointer to slot */
#endif
}
- if (rs_start.rss_nr_pci_id > MAX_NR_PCI_ID)
+ if (rs_start.rss_nr_pci_id > RSS_NR_PCI_ID)
{
printf("RS: do_start: too many PCI device IDs\n");
return EINVAL;
printf("RS: do_start: PCI %04x/%04x\n",
rp->r_pci_id[i].vid, rp->r_pci_id[i].did);
}
- if (rs_start.rss_nr_pci_class > MAX_NR_PCI_CLASS)
+ if (rs_start.rss_nr_pci_class > RSS_NR_PCI_CLASS)
{
printf("RS: do_start: too many PCI class IDs\n");
return EINVAL;
int s, proc;
char label[MAX_LABEL_LEN];
+ /* This call requires special privileges. */
+ if (!caller_is_root(m_ptr->m_source)) return(EPERM);
+
len= m_ptr->RS_CMD_LEN;
if (len >= sizeof(label))
return EINVAL; /* Too long */
if (s != OK) return(s);
label[len]= '\0';
+ /* This call requires special privileges. */
+ if (! (caller_can_control(m_ptr->m_source, label) ||
+ caller_is_root(m_ptr->m_source))) {
+ return(EPERM);
+ }
+
for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
if ((rp->r_flags & RS_IN_USE) && strcmp(rp->r_label, label) == 0) {
if(rs_verbose) printf("RS: restarting '%s' (%d)\n", label, rp->r_pid);
if (s != OK) return(s);
label[len]= '\0';
+ /* This call requires special privileges. */
+ if (! (caller_can_control(m_ptr->m_source, label) ||
+ caller_is_root(m_ptr->m_source))) {
+ return(EPERM);
+ }
+
for (rp=BEG_RPROC_ADDR; rp<END_RPROC_ADDR; rp++) {
if (rp->r_flags & RS_IN_USE && strcmp(rp->r_label, label) == 0) {
#if VERBOSE
*===========================================================================*/
PUBLIC int do_shutdown(message *m_ptr)
{
+ /* This call requires special privileges. */
+ if (m_ptr != NULL && !caller_is_root(m_ptr->m_source)) return(EPERM);
+
/* Set flag so that RS server knows services shouldn't be restarted. */
shutting_down = TRUE;
return(OK);
case 0: /* child process */
/* Try to execute the binary that has an absolute path. If this fails,
- * e.g., because the root file system cannot be read, try to strip of
+ * e.g., because the root file system cannot be read, try to strip off
* the path, and see if the command is in RS' current working dir.
*/
nice(rp->r_nice); /* Nice before setuid, to allow negative
{
execve(rp->r_argv[0], rp->r_argv, &null_env); /* POSIX execute */
file_only = strrchr(rp->r_argv[0], '/') + 1;
- execve(file_only, rp->r_argv, &null_env); /* POSIX execute */
+ execve(file_only, rp->r_argv, &null_env); /* POSIX execute */
}
printf("RS: exec failed for %s: %d\n", rp->r_argv[0], errno);
slot_nr= rp-rproc;
rp->r_check_tm = 0; /* not checked yet */
getuptime(&rp->r_alive_tm); /* currently alive */
rp->r_stop_tm = 0; /* not exiting yet */
+ rp->r_backoff = 0; /* not to be restarted */
rproc_ptr[child_proc_nr_n] = rp; /* mapping for fast access */
/* If any of the calls below fail, the RS_EXITING flag is set. This implies
size_t len;
int s;
+ /* This call requires special privileges. */
+ if (!caller_is_root(m_ptr->m_source)) return(EPERM);
+
switch(m_ptr->m1_i1) {
case SI_PROC_TAB:
src_addr = (vir_bytes) rproc;
src_bits_per_word= 8*sizeof(rp->r_call_mask[0]);
dst_bits_per_word= 8*sizeof(privp->s_k_call_mask[0]);
- for (src_word= 0; src_word < MAX_NR_SYSTEM; src_word++)
+ for (src_word= 0; src_word < RSS_NR_SYSTEM; src_word++)
{
for (src_bit= 0; src_bit < src_bits_per_word; src_bit++)
{
#define KW_SYSTEM "system"
#define KW_IPC "ipc"
#define KW_VM "vm"
+#define KW_CONTROL "control"
FORWARD void do_driver(config_t *cpe, config_t *config);
{
if (!(cp->flags & CFG_SUBLIST))
{
- fatal("do_class: expected list at %s:%d\n",
+ fatal("do_class: expected list at %s:%d",
cp->file, cp->line);
}
cp1= cp->list;
if ((cp1->flags & CFG_STRING) ||
(cp1->flags & CFG_SUBLIST))
{
- fatal("do_class: expected word at %s:%d\n",
+ fatal("do_class: expected word at %s:%d",
cp1->file, cp1->line);
}
/* At this place we expect the word 'driver' */
if (strcmp(cp1->word, KW_DRIVER) != 0)
- fatal("do_class: exected word '%S' at %s:%d\n",
+ fatal("do_class: exected word '%S' at %s:%d",
KW_DRIVER, cp1->file, cp1->line);
cp1= cp1->next;
if ((cp1->flags & CFG_STRING) ||
(cp1->flags & CFG_SUBLIST))
{
- fatal("do_class: expected word at %s:%d\n",
+ fatal("do_class: expected word at %s:%d",
cp1->file, cp1->line);
}
if (cp == NULL)
{
fatal(
- "do_class: no entry found for class '%s' at %s:%d\n",
+ "do_class: no entry found for class '%s' at %s:%d",
cpe->word, cpe->file, cpe->line);
}
do_driver(cp1->next, config);
if (call_nr < KERNEL_CALL)
{
fatal(
- "do_system: bad call number %d in system tab for '%s'\n",
+ "do_system: bad call number %d in system tab for '%s'",
call_nr, system_tab[i].label);
}
call_nr -= KERNEL_CALL;
if (word >= RSS_NR_SYSTEM)
{
fatal(
- "do_system: RSS_NR_SYSTEM is too small (%d needed)\n",
+ "do_system: RSS_NR_SYSTEM is too small (%d needed)",
word+1);
}
rs_start.rss_system[word] |= mask;
}
}
+PRIVATE void do_control(config_t *cpe)
+{
+ int nr_control = 0;
+
+ /* Process a list of 'control' labels. */
+ for (; cpe; cpe= cpe->next)
+ {
+ if (cpe->flags & CFG_SUBLIST)
+ {
+ fatal("do_control: unexpected sublist at %s:%d",
+ cpe->file, cpe->line);
+ }
+ if (cpe->flags & CFG_STRING)
+ {
+ fatal("do_control: unexpected string at %s:%d",
+ cpe->file, cpe->line);
+ }
+ if (nr_control >= RSS_NR_CONTROL)
+ {
+ fatal(
+ "do_control: RSS_NR_CONTROL is too small (%d needed)",
+ nr_control+1);
+ }
+
+ rs_start.rss_control[nr_control].l_addr = cpe->word;
+ rs_start.rss_control[nr_control].l_len = strlen(cpe->word);
+ rs_start.rss_nr_control = ++nr_control;
+ }
+}
+
PRIVATE void do_driver(config_t *cpe, config_t *config)
{
config_t *cp;
*/
if (!(cpe->flags & CFG_SUBLIST))
{
- fatal("do_driver: expected list at %s:%d\n",
+ fatal("do_driver: expected list at %s:%d",
cpe->file, cpe->line);
}
if (cpe->next != NULL)
{
cpe= cpe->next;
- fatal("do_driver: expected end of list at %s:%d\n",
+ fatal("do_driver: expected end of list at %s:%d",
cpe->file, cpe->line);
}
cpe= cpe->list;
{
if (!(cp->flags & CFG_SUBLIST))
{
- fatal("do_driver: expected list at %s:%d\n",
+ fatal("do_driver: expected list at %s:%d",
cp->file, cp->line);
}
cpe= cp->list;
if ((cpe->flags & CFG_STRING) || (cpe->flags & CFG_SUBLIST))
{
- fatal("do_driver: expected word at %s:%d\n",
+ fatal("do_driver: expected word at %s:%d",
cpe->file, cpe->line);
}
do_vm(cpe->next);
continue;
}
-
+ if (strcmp(cpe->word, KW_CONTROL) == 0)
+ {
+ do_control(cpe->next);
+ continue;
+ }
}
}
{
if (!(cp->flags & CFG_SUBLIST))
{
- fatal("do_config: expected list at %s:%d\n",
+ fatal("do_config: expected list at %s:%d",
cp->file, cp->line);
}
cpe= cp->list;
if ((cpe->flags & CFG_STRING) || (cpe->flags & CFG_SUBLIST))
{
- fatal("do_config: expected word at %s:%d\n",
+ fatal("do_config: expected word at %s:%d",
cpe->file, cpe->line);
}
/* At this place we expect the word 'driver' */
if (strcmp(cpe->word, KW_DRIVER) != 0)
- fatal("do_config: exected word '%S' at %s:%d\n",
+ fatal("do_config: exected word '%S' at %s:%d",
KW_DRIVER, cpe->file, cpe->line);
cpe= cpe->next;
if ((cpe->flags & CFG_STRING) || (cpe->flags & CFG_SUBLIST))
{
- fatal("do_config: expected word at %s:%d\n",
+ fatal("do_config: expected word at %s:%d",
cpe->file, cpe->line);
}
{
fprintf(stderr, "service: driver '%s' not found in config\n",
label);
- return;
+ exit(1);
}
cpe= cpe->next;
rs_start.rss_period= req_period;
rs_start.rss_script= req_script;
if(req_label) {
- rs_start.rss_label = req_label;
- rs_start.rss_labellen = strlen(req_label);
+ rs_start.rss_label.l_addr = req_label;
+ rs_start.rss_label.l_len = strlen(req_label);
} else {
- rs_start.rss_label = progname;
- rs_start.rss_labellen = strlen(progname);
+ rs_start.rss_label.l_addr = progname;
+ rs_start.rss_label.l_len = strlen(progname);
}
if (req_script)
rs_start.rss_scriptlen= strlen(req_script);