383 lines
10 KiB
C
383 lines
10 KiB
C
/*-
|
|
* Copyright (c) 2001 Networks Associates Technology, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This software was developed for the FreeBSD Project by NAI Labs, the
|
|
* Security Research Division of Network Associates, Inc. under
|
|
* DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
|
|
* CHATS research program.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $Id$
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/namei.h>
|
|
|
|
#include "kernel_interface.h"
|
|
#include "kernel_plm.h"
|
|
#include "lomacfs.h"
|
|
#include "policy_plm.h"
|
|
|
|
MALLOC_DEFINE(M_LOMACPLM, "LOMAC_PLM", "LOMAC PLM nodes and strings");
|
|
char *strsep(register char **stringp, register const char *delim);
|
|
|
|
/*
|
|
* Get next token from string *stringp, where tokens are possibly-empty
|
|
* strings separated by characters from delim.
|
|
*
|
|
* Writes NULs into the string at *stringp to end tokens.
|
|
* delim need not remain constant from call to call.
|
|
* On return, *stringp points past the last NUL written (if there might
|
|
* be further tokens), or is NULL (if there are definitely no more tokens).
|
|
*
|
|
* If *stringp is NULL, strsep returns NULL.
|
|
*/
|
|
char *
|
|
strsep(stringp, delim)
|
|
register char **stringp;
|
|
register const char *delim;
|
|
{
|
|
register char *s;
|
|
register const char *spanp;
|
|
register int c, sc;
|
|
char *tok;
|
|
|
|
if ((s = *stringp) == NULL)
|
|
return (NULL);
|
|
for (tok = s;;) {
|
|
c = *s++;
|
|
spanp = delim;
|
|
do {
|
|
if ((sc = *spanp++) == c) {
|
|
if (c == 0)
|
|
s = NULL;
|
|
else
|
|
s[-1] = 0;
|
|
*stringp = s;
|
|
return (tok);
|
|
}
|
|
} while (sc != 0);
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
struct lomac_node_entry lomac_node_entry_root = {
|
|
SLIST_HEAD_INITIALIZER(lomac_node_entry),
|
|
{ NULL },
|
|
LN_HIGHEST_LEVEL | LN_INHERIT_HIGH,
|
|
"/"
|
|
};
|
|
|
|
static struct lomac_node_entry *
|
|
lomac_plm_subtree_find_cnp(struct lomac_node_entry *root,
|
|
struct componentname *cnp) {
|
|
char *nameptr = cnp->cn_nameptr;
|
|
struct lomac_node_entry *lne;
|
|
int len = cnp->cn_namelen;
|
|
|
|
SLIST_FOREACH(lne, &root->ln_children, ln_chain)
|
|
if (strlen(lne->ln_name) == len &&
|
|
bcmp(lne->ln_name, nameptr, len) == 0)
|
|
break;
|
|
|
|
return (lne);
|
|
}
|
|
|
|
static struct lomac_node_entry *
|
|
lomac_plm_subtree_find(struct lomac_node_entry *root, const char *name) {
|
|
struct lomac_node_entry *lne;
|
|
|
|
SLIST_FOREACH(lne, &root->ln_children, ln_chain)
|
|
if (strcmp(name, lne->ln_name) == 0)
|
|
break;
|
|
|
|
return (lne);
|
|
}
|
|
|
|
|
|
/*
|
|
* This is called from inside getnewvnode() before the vnode is in use.
|
|
*/
|
|
void
|
|
lomac_plm_init_lomacfs_vnode(struct vnode *dvp, struct vnode *vp,
|
|
struct componentname *cnp, lattr_t *subjlattr) {
|
|
struct lomac_node *ln = VTOLOMAC(vp);
|
|
struct lomac_node_entry *mlne = NULL;
|
|
|
|
/*
|
|
* Only "/" has no parent, so inherit directly from our PLM root.
|
|
*/
|
|
if (dvp == NULL) {
|
|
ln->ln_flags = lomac_node_entry_root.ln_flags;
|
|
ln->ln_entry = ln->ln_underpolicy = &lomac_node_entry_root;
|
|
} else {
|
|
struct lomac_node *dln = VTOLOMAC(dvp);
|
|
struct lomac_node_entry *dlne = dln->ln_entry;
|
|
int fixup_inherit = 0;
|
|
|
|
/*
|
|
* If we have no directory-specific entry, we inherit
|
|
* directly from the lomac_node's previously-inherited
|
|
* flags implicitly, otherwise we inherit explicitly
|
|
* from the corresponding lomac_node_entry.
|
|
*/
|
|
if (dlne == NULL) {
|
|
ln->ln_flags = dln->ln_flags & LN_INHERIT_MASK;
|
|
fixup_inherit = 1;
|
|
ln->ln_underpolicy = dln->ln_underpolicy;
|
|
ln->ln_entry = NULL;
|
|
} else if ((mlne = lomac_plm_subtree_find_cnp(dlne, cnp)) ==
|
|
NULL) {
|
|
ln->ln_flags = dlne->ln_flags & LN_INHERIT_MASK;
|
|
fixup_inherit = 2;
|
|
ln->ln_underpolicy = dlne;
|
|
ln->ln_entry = NULL;
|
|
} else {
|
|
ln->ln_entry = ln->ln_underpolicy = mlne;
|
|
}
|
|
if (fixup_inherit) {
|
|
switch (ln->ln_flags) {
|
|
case LN_INHERIT_LOW:
|
|
ln->ln_flags |= LN_LOWEST_LEVEL;
|
|
break;
|
|
case LN_INHERIT_SUBJ:
|
|
if (subjlattr->level == LOMAC_HIGHEST_LEVEL)
|
|
ln->ln_flags |= LN_HIGHEST_LEVEL;
|
|
else {
|
|
ln->ln_flags &= ~LN_INHERIT_MASK;
|
|
ln->ln_flags |= LN_INHERIT_LOW |
|
|
LN_LOWEST_LEVEL;
|
|
}
|
|
break;
|
|
case LN_INHERIT_HIGH:
|
|
ln->ln_flags |= LN_HIGHEST_LEVEL;
|
|
break;
|
|
}
|
|
if (fixup_inherit == 2)
|
|
ln->ln_flags |=
|
|
(dlne->ln_flags & LN_CHILD_ATTR_MASK) >>
|
|
LN_CHILD_ATTR_SHIFT;
|
|
} else {
|
|
/* this is the only case where mlne != NULL */
|
|
ln->ln_flags &= ~(LN_INHERIT_MASK | LN_ATTR_MASK);
|
|
ln->ln_flags |= mlne->ln_flags &
|
|
(LN_INHERIT_MASK | LN_ATTR_MASK);
|
|
if ((mlne->ln_flags & LN_LEVEL_MASK) ==
|
|
LN_SUBJ_LEVEL) {
|
|
if (subjlattr->level == LOMAC_HIGHEST_LEVEL)
|
|
ln->ln_flags |= LN_HIGHEST_LEVEL;
|
|
else
|
|
ln->ln_flags |= LN_LOWEST_LEVEL;
|
|
} else
|
|
ln->ln_flags |= mlne->ln_flags & LN_LEVEL_MASK;
|
|
}
|
|
}
|
|
|
|
KASSERT(ln->ln_flags & LN_LEVEL_MASK, ("lomac_node has no level"));
|
|
KASSERT(ln->ln_flags & LN_INHERIT_MASK, ("lomac_node has no inherit"));
|
|
#ifdef INVARIANTS
|
|
if (mlne != NULL) {
|
|
KASSERT(mlne->ln_flags & LN_LEVEL_MASK,
|
|
("lomac_node_entry has no level"));
|
|
KASSERT(mlne->ln_flags & LN_INHERIT_MASK,
|
|
("lomac_node_entry has no inherit"));
|
|
}
|
|
#endif /* INVARIANTS */
|
|
}
|
|
|
|
static struct lomac_node_entry *
|
|
lomac_plm_subtree_new(struct lomac_node_entry *plne, char *name) {
|
|
struct lomac_node_entry *lne;
|
|
static struct lomac_node_entry_head head_init =
|
|
SLIST_HEAD_INITIALIZER(lomac_node_entry);
|
|
|
|
lne = malloc(sizeof(*lne), M_LOMACPLM, M_WAITOK);
|
|
bcopy(&head_init, &lne->ln_children, sizeof(head_init));
|
|
lne->ln_name = name;
|
|
lne->ln_flags = plne->ln_flags & LN_INHERIT_MASK;
|
|
switch (lne->ln_flags) {
|
|
case LN_INHERIT_LOW:
|
|
lne->ln_flags |= LN_LOWEST_LEVEL;
|
|
break;
|
|
case LN_INHERIT_HIGH:
|
|
lne->ln_flags |= LN_HIGHEST_LEVEL;
|
|
break;
|
|
case LN_INHERIT_SUBJ:
|
|
lne->ln_flags |= LN_SUBJ_LEVEL;
|
|
break;
|
|
}
|
|
SLIST_INSERT_HEAD(&plne->ln_children, lne, ln_chain);
|
|
return (lne);
|
|
}
|
|
|
|
static void
|
|
lomac_plm_subtree_free(struct lomac_node_entry *lneself) {
|
|
struct lomac_node_entry_head *head = &lneself->ln_children;
|
|
struct lomac_node_entry *lne;
|
|
|
|
while (!SLIST_EMPTY(head)) {
|
|
lne = SLIST_FIRST(head);
|
|
SLIST_REMOVE_HEAD(head, ln_chain);
|
|
lomac_plm_subtree_free(lne);
|
|
}
|
|
free(lneself, M_LOMACPLM);
|
|
}
|
|
|
|
struct string_list {
|
|
SLIST_ENTRY(string_list) entries;
|
|
char string[1];
|
|
};
|
|
static SLIST_HEAD(, string_list) string_list_head =
|
|
SLIST_HEAD_INITIALIZER(string_list);
|
|
|
|
static char *
|
|
string_list_new(const char *s) {
|
|
struct string_list *sl;
|
|
|
|
sl = malloc(sizeof(*sl) + strlen(s), M_LOMACPLM, M_WAITOK);
|
|
strcpy(sl->string, s);
|
|
SLIST_INSERT_HEAD(&string_list_head, sl, entries);
|
|
|
|
return (sl->string);
|
|
}
|
|
|
|
static void
|
|
lomac_plm_uninitialize(void) {
|
|
struct lomac_node_entry_head *head = &lomac_node_entry_root.ln_children;
|
|
struct lomac_node_entry *lne;
|
|
struct string_list *sl;
|
|
|
|
while (!SLIST_EMPTY(head)) {
|
|
lne = SLIST_FIRST(head);
|
|
SLIST_REMOVE_HEAD(head, ln_chain);
|
|
lomac_plm_subtree_free(lne);
|
|
}
|
|
while (!SLIST_EMPTY(&string_list_head)) {
|
|
sl = SLIST_FIRST(&string_list_head);
|
|
SLIST_REMOVE_HEAD(&string_list_head, entries);
|
|
free(sl, M_LOMACPLM);
|
|
}
|
|
}
|
|
|
|
static int
|
|
lomac_plm_initialize(void) {
|
|
struct lomac_node_entry *plne, *lne;
|
|
plm_rule_t *pr;
|
|
|
|
for (pr = plm; pr->path != NULL; pr++) {
|
|
char *path;
|
|
char *comp;
|
|
int depth;
|
|
|
|
if (*pr->path == '\0') {
|
|
printf("lomac_plm: invalid path \"%s\"\n", pr->path);
|
|
return (EINVAL);
|
|
}
|
|
path = string_list_new(pr->path);
|
|
lne = &lomac_node_entry_root;
|
|
depth = 0;
|
|
for (;; depth++) {
|
|
plne = lne;
|
|
comp = strsep(&path, "/");
|
|
if (comp == NULL)
|
|
break;
|
|
if (depth == 0) { /* special case: beginning / */
|
|
if (*comp == '\0')
|
|
continue;
|
|
else {
|
|
printf("lomac_plm: not absolute path "
|
|
"\"%s\"\n", pr->path);
|
|
return (EINVAL);
|
|
}
|
|
} else if (depth == 1) { /* special case: "/" */
|
|
if (*comp == '\0' && strsep(&path, "/") == NULL)
|
|
break;
|
|
}
|
|
if (*comp == '\0' ||
|
|
strcmp(comp, ".") == 0 ||
|
|
strcmp(comp, "..") == 0) {
|
|
printf("lomac_plm: empty path component in "
|
|
"\"%s\"\n", pr->path);
|
|
return (EINVAL);
|
|
}
|
|
lne = lomac_plm_subtree_find(plne, comp);
|
|
if (lne == NULL) {
|
|
lne = lomac_plm_subtree_new(plne, comp);
|
|
lne->ln_path = plne->ln_path;
|
|
}
|
|
}
|
|
lne->ln_path = pr->path;
|
|
if (pr->flags == PLM_NOFLAGS)
|
|
lne->ln_flags &= ~LN_LEVEL_MASK;
|
|
else
|
|
lne->ln_flags &= ~LN_INHERIT_MASK;
|
|
lne->ln_flags |=
|
|
plm_levelflags_to_node_flags[pr->level][pr->flags];
|
|
if (pr->flags == PLM_NOFLAGS)
|
|
lne->ln_flags |= pr->attr;
|
|
else
|
|
lne->ln_flags |= (pr->attr & LN_ATTR_MASK)
|
|
<< LN_CHILD_ATTR_SHIFT;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int lomac_plm_initialized = 0;
|
|
|
|
static int
|
|
lomac_plm_modevent(module_t module, int event, void *unused) {
|
|
int error = 0;
|
|
|
|
switch ((enum modeventtype)event) {
|
|
case MOD_LOAD:
|
|
error = lomac_plm_initialize();
|
|
if (error == 0)
|
|
lomac_plm_initialized = 1;
|
|
break;
|
|
case MOD_UNLOAD:
|
|
lomac_plm_uninitialize();
|
|
case MOD_SHUTDOWN:
|
|
break;
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
static moduledata_t lomac_plm_moduledata = {
|
|
"lomac_plm",
|
|
&lomac_plm_modevent,
|
|
NULL
|
|
};
|
|
DECLARE_MODULE(lomac_plm, lomac_plm_moduledata, SI_SUB_VFS, SI_ORDER_ANY);
|
|
MODULE_VERSION(lomac_plm, 1);
|