blob: aafc9dee39edf33e36ffd100e67eab07236b1d48 [file] [log] [blame]
/*
* Copyright 2011 Tresys Technology, LLC. All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``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 TRESYS TECHNOLOGY, LLC 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.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of Tresys Technology, LLC.
*/
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sepol/policydb/conditional.h>
#include "cil_internal.h"
#include "cil_flavor.h"
#include "cil_log.h"
#include "cil_tree.h"
#include "cil_list.h"
#include "cil_parser.h"
#include "cil_strpool.h"
__attribute__((noreturn)) __attribute__((format (printf, 1, 2))) void cil_tree_error(const char* msg, ...)
{
va_list ap;
va_start(ap, msg);
cil_vlog(CIL_ERR, msg, ap);
va_end(ap);
exit(1);
}
struct cil_tree_node *cil_tree_get_next_path(struct cil_tree_node *node, char **info_kind, uint32_t *hll_line, char **path)
{
int rc;
if (!node) {
goto exit;
}
node = node->parent;
while (node) {
if (node->flavor == CIL_NODE && node->data == NULL) {
if (node->cl_head && node->cl_head->data == CIL_KEY_SRC_INFO) {
if (!node->cl_head->next || !node->cl_head->next->next || !node->cl_head->next->next->next) {
goto exit;
}
/* Parse Tree */
*info_kind = node->cl_head->next->data;
rc = cil_string_to_uint32(node->cl_head->next->next->data, hll_line, 10);
if (rc != SEPOL_OK) {
goto exit;
}
*path = node->cl_head->next->next->next->data;
return node;
}
node = node->parent;
} else if (node->flavor == CIL_SRC_INFO) {
/* AST */
struct cil_src_info *info = node->data;
*info_kind = info->kind;
*hll_line = info->hll_line;
*path = info->path;
return node;
} else {
if (node->flavor == CIL_CALL) {
struct cil_call *call = node->data;
node = NODE(call->macro);
} else if (node->flavor == CIL_BLOCKINHERIT) {
struct cil_blockinherit *inherit = node->data;
node = NODE(inherit->block);
} else {
node = node->parent;
}
}
}
exit:
*info_kind = NULL;
*hll_line = 0;
*path = NULL;
return NULL;
}
char *cil_tree_get_cil_path(struct cil_tree_node *node)
{
char *info_kind;
uint32_t hll_line;
char *path;
while (node) {
node = cil_tree_get_next_path(node, &info_kind, &hll_line, &path);
if (node && info_kind == CIL_KEY_SRC_CIL) {
return path;
}
}
return NULL;
}
__attribute__((format (printf, 3, 4))) void cil_tree_log(struct cil_tree_node *node, enum cil_log_level lvl, const char* msg, ...)
{
va_list ap;
va_start(ap, msg);
cil_vlog(lvl, msg, ap);
va_end(ap);
if (node) {
char *path = NULL;
uint32_t hll_offset = node->hll_offset;
path = cil_tree_get_cil_path(node);
if (path != NULL) {
cil_log(lvl, " at %s:%u", path, node->line);
}
while (node) {
do {
char *info_kind;
uint32_t hll_line;
node = cil_tree_get_next_path(node, &info_kind, &hll_line, &path);
if (!node || info_kind == CIL_KEY_SRC_CIL) {
break;
}
if (info_kind == CIL_KEY_SRC_HLL_LMS) {
hll_line += hll_offset - node->hll_offset - 1;
}
cil_log(lvl," from %s:%u", path, hll_line);
} while (1);
}
}
cil_log(lvl,"\n");
}
int cil_tree_subtree_has_decl(struct cil_tree_node *node)
{
while (node) {
if (node->flavor >= CIL_MIN_DECLARATIVE) {
return CIL_TRUE;
}
if (node->cl_head != NULL) {
if (cil_tree_subtree_has_decl(node->cl_head))
return CIL_TRUE;
}
node = node->next;
}
return CIL_FALSE;
}
int cil_tree_init(struct cil_tree **tree)
{
struct cil_tree *new_tree = cil_malloc(sizeof(*new_tree));
cil_tree_node_init(&new_tree->root);
*tree = new_tree;
return SEPOL_OK;
}
void cil_tree_destroy(struct cil_tree **tree)
{
if (tree == NULL || *tree == NULL) {
return;
}
cil_tree_subtree_destroy((*tree)->root);
free(*tree);
*tree = NULL;
}
void cil_tree_subtree_destroy(struct cil_tree_node *node)
{
cil_tree_children_destroy(node);
cil_tree_node_destroy(&node);
}
void cil_tree_children_destroy(struct cil_tree_node *node)
{
struct cil_tree_node *curr, *next;
if (!node) {
return;
}
curr = node->cl_head;
while (curr) {
next = curr->next;
cil_tree_children_destroy(curr);
cil_tree_node_destroy(&curr);
curr = next;
}
node->cl_head = NULL;
node->cl_tail = NULL;
}
void cil_tree_node_init(struct cil_tree_node **node)
{
struct cil_tree_node *new_node = cil_malloc(sizeof(*new_node));
new_node->cl_head = NULL;
new_node->cl_tail = NULL;
new_node->parent = NULL;
new_node->data = NULL;
new_node->next = NULL;
new_node->flavor = CIL_ROOT;
new_node->line = 0;
new_node->hll_offset = 0;
*node = new_node;
}
void cil_tree_node_destroy(struct cil_tree_node **node)
{
struct cil_symtab_datum *datum;
if (node == NULL || *node == NULL) {
return;
}
if ((*node)->flavor >= CIL_MIN_DECLARATIVE) {
datum = (*node)->data;
cil_symtab_datum_remove_node(datum, *node);
if (datum->nodes == NULL) {
cil_destroy_data(&(*node)->data, (*node)->flavor);
}
} else {
cil_destroy_data(&(*node)->data, (*node)->flavor);
}
free(*node);
*node = NULL;
}
/* Perform depth-first walk of the tree
Parameters:
start_node: root node to start walking from
process_node: function to call when visiting a node
Takes parameters:
node: node being visited
finished: boolean indicating to the tree walker that it should move on from this branch
extra_args: additional data
first_child: Function to call before entering list of children
Takes parameters:
node: node of first child
extra args: additional data
last_child: Function to call when finished with the last child of a node's children
extra_args: any additional data to be passed to the helper functions
*/
int cil_tree_walk_core(struct cil_tree_node *node,
int (*process_node)(struct cil_tree_node *node, uint32_t *finished, void *extra_args),
int (*first_child)(struct cil_tree_node *node, void *extra_args),
int (*last_child)(struct cil_tree_node *node, void *extra_args),
void *extra_args)
{
int rc = SEPOL_ERR;
while (node) {
uint32_t finished = CIL_TREE_SKIP_NOTHING;
if (process_node != NULL) {
rc = (*process_node)(node, &finished, extra_args);
if (rc != SEPOL_OK) {
cil_tree_log(node, CIL_INFO, "Problem");
return rc;
}
}
if (finished & CIL_TREE_SKIP_NEXT) {
return SEPOL_OK;
}
if (node->cl_head != NULL && !(finished & CIL_TREE_SKIP_HEAD)) {
rc = cil_tree_walk(node, process_node, first_child, last_child, extra_args);
if (rc != SEPOL_OK) {
return rc;
}
}
node = node->next;
}
return SEPOL_OK;
}
int cil_tree_walk(struct cil_tree_node *node,
int (*process_node)(struct cil_tree_node *node, uint32_t *finished, void *extra_args),
int (*first_child)(struct cil_tree_node *node, void *extra_args),
int (*last_child)(struct cil_tree_node *node, void *extra_args),
void *extra_args)
{
int rc = SEPOL_ERR;
if (!node || !node->cl_head) {
return SEPOL_OK;
}
if (first_child != NULL) {
rc = (*first_child)(node->cl_head, extra_args);
if (rc != SEPOL_OK) {
cil_tree_log(node, CIL_INFO, "Problem");
return rc;
}
}
rc = cil_tree_walk_core(node->cl_head, process_node, first_child, last_child, extra_args);
if (rc != SEPOL_OK) {
return rc;
}
if (last_child != NULL) {
rc = (*last_child)(node->cl_tail, extra_args);
if (rc != SEPOL_OK) {
cil_tree_log(node, CIL_INFO, "Problem");
return rc;
}
}
return SEPOL_OK;
}