summaryrefslogtreecommitdiffstats
path: root/libapol/tests/constrain-tests.c
diff options
context:
space:
mode:
Diffstat (limited to 'libapol/tests/constrain-tests.c')
-rw-r--r--libapol/tests/constrain-tests.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/libapol/tests/constrain-tests.c b/libapol/tests/constrain-tests.c
new file mode 100644
index 0000000..3133b5d
--- /dev/null
+++ b/libapol/tests/constrain-tests.c
@@ -0,0 +1,523 @@
+/**
+ * @file
+ *
+ * Test the information flow analysis code.
+ *
+ *
+ * Copyright (C) 2010 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/perm-map.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <stdbool.h>
+#include <string.h>
+#include <apol/constraint-query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/constraint.h>
+#include <libqpol/src/queue.h>
+
+#define CONSTR_SOURCE TEST_POLICIES "/setools-3.3/apol/constrain_test_policy.conf"
+#define CONSTR_BINARY TEST_POLICIES "/setools-3.3/apol/constrain_test_policy.21"
+// Glob won't work, but this gives the idea of where we are trying to go
+#define CONSTR_MODULAR TEST_POLICIES "/setools-3.1/modules/*.pp"
+
+//#define DEBUGTRACE 1
+
+
+/* General concepts: The constraints are stored in the policy by class,
+ * that is, the list of classes stored in the policy has attached to it
+ * whatever constraints affect that class.
+ * The "policy_iter" iterator is a structure which contains a pointer to the
+ * list of classes from the loaded policy, and another pointer to the list of
+ * constraints associated with the current class. This latter pointer is
+ * traversed to its end, at which point the class pointer is updated, and the
+ * new class' list of constraints is put in its place. The switch from one
+ * class to the next is done behind the scenes by the iterator. Thus each time
+ * a new item is retrieved from policy_iter, it needs to have all info (class,
+ * permissions, expression) extracted from it.
+ *
+ * The input file must be a known file. The class and permissions are used as
+ * a key by this test routine to determine what the expected expression will
+ * be. Thus, if the input file is modified, this test becomes invalid. The file
+ * (defined above) resides in the 'testing-policies' repository.
+ *
+ * The statements validatetrans and mlsvalidatetrans, although similar to
+ * constrain and mlsconstrain, are not considered here.
+ *
+ */
+
+// Define data for expected policy. This is a hack, but what I could think of
+// on short notice.
+
+// Similar to struct constraint_expr from sepol/policydb/constraint.h
+// but want char * list of names, not internal representations.
+typedef struct local_expr {
+ uint32_t expr_type;
+ uint32_t attr;
+ uint32_t op;
+ size_t name_count;
+ char **namelist;
+} local_expr_t;
+
+typedef struct constrain_test_list {
+ char **class;
+ char **permissions; // Must end with NULL
+ int test_found;
+ int expr_count;
+ local_expr_t **expr_list;
+} constrain_test_list_t;
+
+// TODO Clean up memory leaks -- all iterators need to be destroyed, check other stuff
+
+
+char *class0 = "file";
+char *perm0[] = { "create", "relabelto", NULL };
+local_expr_t expr00 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_EQ, 0, NULL };
+local_expr_t *expr0[] = { &expr00, NULL };
+
+char *class1 = "lnk_file";
+char *perm1[10] = { "create", "relabelto", NULL };
+local_expr_t expr10 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_NEQ, 0, NULL };
+local_expr_t *expr1[] = { &expr10, NULL };
+
+// This test (test 2) is not expected to be matched
+char *class2 = "fifo_file";
+char *perm2[] = { "create", "relabelto", NULL };
+local_expr_t expr20 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_DOM, 0, NULL };
+local_expr_t *expr2[] = { &expr20, NULL };
+
+char *class3 = "node";
+char *perm3[] = { "udp_send", NULL };
+local_expr_t expr30 = { CEXPR_ATTR, CEXPR_L1L2, CEXPR_DOM, 0, NULL };
+local_expr_t expr31 = { CEXPR_ATTR, CEXPR_L1H2, CEXPR_DOMBY, 0, NULL };
+local_expr_t expr32 = { CEXPR_AND, 0, 0, 0, NULL };
+local_expr_t *expr3[] = { &expr30, &expr31, &expr32, NULL };
+
+char *class4 = "netif";
+char *perm4[] = { "tcp_send", NULL };
+local_expr_t expr40 = { CEXPR_ATTR, CEXPR_L1L2, CEXPR_DOM, 0, NULL };
+local_expr_t expr41 = { CEXPR_ATTR, CEXPR_L1H2, CEXPR_DOMBY, 0, NULL };
+local_expr_t expr42 = { CEXPR_OR, 0, 0, 0, NULL };
+local_expr_t *expr4[] = { &expr40, &expr41, &expr42, NULL };
+
+char *class5 = "dir";
+char *perm5[] = { "read", NULL };
+char *name50[] = { "sysadm_t", "secadm_t", NULL };
+local_expr_t expr50 = { CEXPR_NAMES, CEXPR_TYPE, CEXPR_EQ, 2, name50 };
+local_expr_t *expr5[] = { &expr50, NULL };
+
+constrain_test_list_t test_list[] = {
+ { &class0, perm0, 0, 1, expr0 },
+ { &class1, perm1, 0, 1, expr1 },
+ { &class2, perm2, 0, 1, expr2 },
+ { &class3, perm3, 0, 3, expr3 },
+ { &class4, perm4, 0, 3, expr4 },
+ { &class5, perm5, 0, 3, expr5 }
+};
+
+typedef struct compare_perm_str {
+ int list_length;
+ int list_found;
+ int q_elements_compared;
+ char **list;
+} compare_perm_str_t;
+
+typedef struct compare_expr_str {
+ int list_length;
+ int list_found;
+ local_expr_t **list;
+} compare_expr_str_t;
+
+
+static apol_policy_t *ps = NULL; // Source policy
+static apol_policy_t *pb = NULL; // Binary policy
+static apol_policy_t *pm = NULL; // Modular policy
+
+
+// Prototypes if needed
+static int compare_item_to_list(void *e, void *v);
+
+
+
+
+static int doprintstr (queue_element_t e, void *p)
+{
+ char *s = (char *)e;
+ // Second arg is not used
+
+ printf ("%s ", s);
+ return 0;
+}
+
+static int compare_expr_list(qpol_policy_t *q, qpol_iterator_t *expr_iter, int expr_count, local_expr_t **le)
+{
+ const qpol_constraint_expr_node_t *expr;
+ int sym_type;
+ int op;
+ int expr_type;
+ int i;
+ int err;
+
+ for (i=0; qpol_iterator_end(expr_iter) == 0; i++, qpol_iterator_next(expr_iter))
+ {
+ expr_type = op = sym_type = 0;
+ if (i >= expr_count) // Hit the end of the list
+ return 1; // Not the right list
+
+ err = qpol_iterator_get_item(expr_iter, (void **)&expr);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_constraint_expr_node_get_sym_type(q, expr, &sym_type);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_constraint_expr_node_get_op(q, expr, &op);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_constraint_expr_node_get_expr_type(q, expr, &expr_type);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+#ifdef DEBUGTRACE
+ printf ("Expr compare: Policy:attr:%d, op:%d, expr_type:%d\n", sym_type, op, expr_type);
+ printf ("Expr compare: Test:attr:%d, op:%d, expr_type:%d\n", le[i]->attr, le[i]->op, le[i]->expr_type);
+#endif
+ if (sym_type != le[i]->attr)
+ {
+ return 1;
+ }
+ if (op != le[i]->op)
+ {
+ return 1;
+ }
+ if (expr_type != le[i]->expr_type)
+ {
+ return 1;
+ }
+
+ if (expr_type == CEXPR_NAMES) // Need compare name lists
+ {
+ qpol_iterator_t *names_iter=NULL;
+ size_t name_size=0;
+ compare_perm_str_t x;
+
+#ifdef DEBUGTRACE
+ printf ("Found CEXPR_NAMES expression\n");
+#endif
+ x.list_length = le[i]->name_count;
+ x.list = le[i]->namelist;
+ x.list_found = 0;
+ x.q_elements_compared = 0;
+
+ err = qpol_constraint_expr_node_get_names_iter (q, expr, &names_iter);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_iterator_get_size(names_iter, &name_size);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+ CU_ASSERT_TRUE_FATAL(name_size > 0);
+
+ if (name_size != x.list_length) // Want exact match,
+ {
+ qpol_iterator_destroy(&names_iter);
+ return 1;
+ }
+
+ for (; qpol_iterator_end(names_iter) == 0; qpol_iterator_next(names_iter))
+ {
+ char *lname = NULL;
+
+ err = qpol_iterator_get_item (names_iter, (void **)&lname);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ compare_item_to_list (lname, &x);
+ free (lname);
+ }
+
+#ifdef DEBUGTRACE
+ printf ("name list length=%d, list_found=%d, q_elements_compared=%d\n", x.list_length, x.list_found, x.q_elements_compared);
+#endif
+ if ((x.list_length != x.list_found) || (x.list_length != x.q_elements_compared))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int compare_item_to_list(void *e, void *v)
+{
+ char *pe = (char *)e;
+ compare_perm_str_t *x = (compare_perm_str_t *)v;
+ char **permlist = x->list;
+ char *perm;
+
+ CU_ASSERT_PTR_NOT_NULL(permlist);
+ CU_ASSERT_PTR_NOT_NULL(pe);
+
+ while ((perm=*permlist++) != NULL)
+ {
+#ifdef DEBUGTRACE
+ printf ("pe = %s\n", pe);
+ printf ("perm = %s\n", perm);
+#endif
+ if (strcmp(pe, perm) == 0)
+ x->list_found++;
+ }
+ x->q_elements_compared++;
+ return 0;
+}
+
+static int compare_perm_list(queue_t perm_q, char **permissions)
+{
+ compare_perm_str_t x;
+
+ x.list_length = 0;
+ x.list_found = 0;
+ x.q_elements_compared = 0;
+ x.list = permissions;
+
+ while (*permissions++ != NULL)
+ x.list_length++;
+
+#ifdef DEBUGTRACE
+ printf ("list_length = %d\n", x.list_length);
+#endif
+ if (queue_map(perm_q, compare_item_to_list, &x) != 0)
+ return 1;
+
+#ifdef DEBUGTRACE
+ printf ("list length=%d, list_found=%d, q_elements_compared=%d\n", x.list_length, x.list_found, x.q_elements_compared);
+#endif
+ if ((x.list_length != x.list_found) || (x.list_length != x.q_elements_compared))
+ return 1;
+
+ return 0;
+}
+
+static void constrain_test(apol_policy_t *ap)
+{
+ int i;
+ int err=0;
+ const char *class_name = NULL;
+ const char *constrain_type = "?constrain";
+ char *perm_list = "No Perms Extracted";
+ const qpol_constraint_expr_node_t *expr = NULL;
+ qpol_iterator_t *policy_iter = NULL; // Iterates over all constraints in a policy
+ qpol_iterator_t *perm_iter = NULL; // Iterates over permissions in a constraint
+ qpol_iterator_t *expr_iter = NULL; // Iterates over expression in a constraint
+ qpol_policy_t *q = apol_policy_get_qpol(ap);
+ qpol_constraint_t *constraint = NULL;
+ const qpol_class_t *class;
+ size_t n_constraints = 0;
+ size_t counted_constraints = 0;
+ size_t tests_not_found = 0;
+ int test_count = sizeof(test_list) / sizeof(constrain_test_list_t);
+ int tests_matched = 0;
+ int constrains_matched = 0;
+
+ queue_t perm_q; // holds list of permissions, in case more than one
+
+ err = qpol_policy_get_constraint_iter(q, &policy_iter);
+ if (err != 0)
+ {
+ CU_FAIL("Policy iterator not accessible");
+ goto cleanup;
+ }
+ err = qpol_iterator_get_size(policy_iter, &n_constraints);
+ if (err != 0)
+ {
+ CU_FAIL("Policy size computation failed");
+ goto cleanup;
+ }
+
+ CU_ASSERT_EQUAL(n_constraints, 7); // Count of constraints split among all classes
+
+ counted_constraints=0;
+ for (i=0; i<test_count; i++)
+ {
+ test_list[i].test_found = 0;
+ }
+
+ // Iterate through constraints
+ for (; qpol_iterator_end(policy_iter) == 0; qpol_iterator_next(policy_iter))
+ {
+ counted_constraints++;
+ /* The qpol_constraint_t that is returned below consists of
+ * struct qpol_constraint <<<from constraint_query.c
+ * {
+ * const qpol_class_t *obj_class;
+ * constraint_node_t *constr;
+ * };
+ * the qpol_class_t is a pseudonym for class_datum_t from policydb.h
+ * constraint_node_t is defined in sepol/policydb/constraint.h
+ */
+ err = qpol_iterator_get_item(policy_iter, (void **)&constraint);
+ CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen
+
+ err = qpol_constraint_get_class(q, constraint, &class);
+ CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen
+ err = qpol_class_get_name(q, class, &class_name);
+ CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen
+
+#ifdef DEBUGTRACE
+ printf ("Found class %s\n", class_name);
+#endif
+ // get permission(s)
+ err = qpol_constraint_get_perm_iter (q, constraint, &perm_iter);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ perm_q = queue_create();
+ for (; qpol_iterator_end(perm_iter) == 0; qpol_iterator_next(perm_iter))
+ {
+ err = qpol_iterator_get_item(perm_iter, (void **)&perm_list);
+ CU_ASSERT_EQUAL_FATAL(err,0)
+
+ err = queue_insert (perm_q, perm_list);
+ CU_ASSERT_EQUAL_FATAL(err,0)
+ }
+#ifdef DEBUGTRACE
+ printf ("perms: ");
+ queue_map(perm_q, doprintstr, NULL);
+ printf ("\n");
+#endif
+
+ // get RPN expressions
+ err = qpol_constraint_get_expr_iter (q, constraint, &expr_iter);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ // At this point, the class, permission list, and expression list (in
+ // the iterator) have been identified. Based on expected class/permission
+ // combinations, find one which matches, and note that it was found.
+ // If not found, count that too.
+ for (i=0; i<test_count; i++)
+ {
+ if (strcmp(*(test_list[i].class), class_name) == 0)
+ {
+ if (compare_perm_list(perm_q, test_list[i].permissions) == 0)
+ {
+ if (compare_expr_list(q, expr_iter, test_list[i].expr_count, test_list[i].expr_list) == 0)
+ {
+ test_list[i].test_found = 1;
+ constrains_matched++;
+ break;
+ }
+#ifdef DEBUGTRACE
+ else
+ {
+ printf ("Mismatch comparing expression list\n");
+ }
+#endif
+ }
+#ifdef DEBUGTRACE
+ else
+ {
+ printf ("Mismatch comparing permission list\n");
+ }
+#endif
+ }
+#ifdef DEBUGTRACE
+ else
+ {
+ printf ("Mismatch comparing classes %s,%s\n", *(test_list[i].class),class_name);
+ }
+#endif
+ }
+ queue_destroy(perm_q);
+ }
+ for (i=0; i<test_count; i++)
+ {
+ if (test_list[i].test_found == 0)
+ {
+ CU_ASSERT_EQUAL(i, 2);
+ }
+ else
+ tests_matched++;
+ }
+#ifdef DEBUGTRACE
+ printf ("tests_matched: %d, constrains_matched: %d, counted_constraints: %d, n_constraints: %d\n", tests_matched, constrains_matched, counted_constraints, n_constraints);
+#endif
+ CU_ASSERT_EQUAL(tests_matched, 5);
+ CU_ASSERT_EQUAL(constrains_matched, 5);
+ CU_ASSERT_EQUAL(counted_constraints, 7);
+ CU_ASSERT_EQUAL(n_constraints, 7);
+
+ CU_PASS();
+
+cleanup:
+ return;
+ // close and destroy iterators/policy pointers
+}
+
+static void constrain_source(void)
+{
+ constrain_test(ps);
+}
+
+static void constrain_binary(void)
+{
+ constrain_test(pb);
+// CU_PASS("Not yet implemented")
+}
+
+
+static void constrain_modular(void)
+{
+ CU_PASS("Not yet implemented")
+}
+
+CU_TestInfo constrain_tests[] = {
+ {"constrain from source policy", constrain_source},
+ {"constrain from binary policy", constrain_binary},
+// {"constrain from modular policy", constrain_modular},
+ CU_TEST_INFO_NULL
+};
+
+int constrain_init()
+{
+ // Probably should move this to individual tests, just fstat policy to see if it is there!
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, CONSTR_SOURCE, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((ps = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, CONSTR_BINARY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((pb = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ return 0;
+}
+
+int constrain_cleanup()
+{
+ apol_policy_destroy(&ps);
+ return 0;
+}