diff options
Diffstat (limited to 'sys/security/lomac/kernel_plm.c')
-rw-r--r-- | sys/security/lomac/kernel_plm.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/sys/security/lomac/kernel_plm.c b/sys/security/lomac/kernel_plm.c new file mode 100644 index 000000000000..138670444a7e --- /dev/null +++ b/sys/security/lomac/kernel_plm.c @@ -0,0 +1,382 @@ +/*- + * Copyright (c) 2001 Networks Associates Technologies, 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); |