summaryrefslogtreecommitdiffstats
path: root/libqpol/src/policy.c
diff options
context:
space:
mode:
Diffstat (limited to 'libqpol/src/policy.c')
-rw-r--r--libqpol/src/policy.c1563
1 files changed, 1563 insertions, 0 deletions
diff --git a/libqpol/src/policy.c b/libqpol/src/policy.c
new file mode 100644
index 0000000..7180556
--- /dev/null
+++ b/libqpol/src/policy.c
@@ -0,0 +1,1563 @@
+/**
+ * @file
+ * Defines the public interface the QPol policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2006-2008 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 <config.h>
+
+#include "qpol_internal.h"
+#include <assert.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <asm/types.h>
+
+#include <sepol/debug.h>
+#include <sepol/handle.h>
+#include <sepol/policydb/flask_types.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/conditional.h>
+#include <sepol/policydb.h>
+#include <sepol/module.h>
+#include <sepol/policydb/module.h>
+#include <sepol/policydb/avrule_block.h>
+
+#include <stdbool.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/policy_extend.h>
+#include "expand.h"
+#include "queue.h"
+#include "iterator_internal.h"
+
+/* redefine input so we can read from a string */
+/* borrowed from O'Reilly lex and yacc pg 157 */
+char *qpol_src_originalinput;
+char *qpol_src_input;
+char *qpol_src_inputptr; /* current position in qpol_src_input */
+char *qpol_src_inputlim; /* end of data */
+
+extern void init_scanner(void);
+extern int yyparse(void);
+extern void init_parser(int, int);
+extern queue_t id_queue;
+extern unsigned int policydb_errors;
+extern unsigned long policydb_lineno;
+extern char source_file[];
+extern policydb_t *policydbp;
+extern int mlspol;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(x) (x)
+#define le16_to_cpu(x) (x)
+#define cpu_to_le32(x) (x)
+#define le32_to_cpu(x) (x)
+#define cpu_to_le64(x) (x)
+#define le64_to_cpu(x) (x)
+#else
+#define cpu_to_le16(x) bswap_16(x)
+#define le16_to_cpu(x) bswap_16(x)
+#define cpu_to_le32(x) bswap_32(x)
+#define le32_to_cpu(x) bswap_32(x)
+#define cpu_to_le64(x) bswap_64(x)
+#define le64_to_cpu(x) bswap_64(x)
+#endif
+
+/* buffer for reading from file */
+typedef struct fbuf
+{
+ char *buf;
+ size_t sz;
+ int err;
+} qpol_fbuf_t;
+
+static void qpol_handle_route_to_callback(void *varg
+ __attribute__ ((unused)), const qpol_policy_t * p, int level, const char *fmt,
+ va_list va_args)
+{
+ if (!p || !(p->fn)) {
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ p->fn(p->varg, p, level, fmt, va_args);
+}
+
+static void sepol_handle_route_to_callback(void *varg, sepol_handle_t * sh, const char *fmt, ...)
+{
+ va_list ap;
+ qpol_policy_t *p = varg;
+
+ if (!sh) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ va_start(ap, fmt);
+ qpol_handle_route_to_callback(NULL, p, sepol_msg_get_level(sh), fmt, ap);
+ va_end(ap);
+}
+
+void qpol_handle_msg(const qpol_policy_t * p, int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!p) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ va_start(ap, fmt);
+ /* explicit cast here to remove const for sepol handle */
+ qpol_handle_route_to_callback((void *)p->varg, p, level, fmt, ap);
+ va_end(ap);
+}
+
+static void qpol_handle_default_callback(void *varg __attribute__ ((unused)), const qpol_policy_t * p
+ __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ switch (level) {
+ case QPOL_MSG_INFO:
+ {
+ /* by default ignore info messages */
+ return;
+ }
+ case QPOL_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case QPOL_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+static int read_source_policy(qpol_policy_t * qpolicy, char *progname, int options)
+{
+ int load_rules = 1;
+ if (options & QPOL_POLICY_OPTION_NO_RULES)
+ load_rules = 0;
+ if ((id_queue = queue_create()) == NULL) {
+ ERR(qpolicy, "%s", strerror(ENOMEM));
+ return -1;
+ }
+
+ policydbp = &qpolicy->p->p;
+ mlspol = policydbp->mls;
+
+ INFO(qpolicy, "%s", "Parsing policy. (Step 1 of 5)");
+ init_scanner();
+ init_parser(1, load_rules);
+ errno = 0;
+ if (yyparse() || policydb_errors) {
+ ERR(qpolicy, "%s: error(s) encountered while parsing configuration\n", progname);
+ queue_destroy(id_queue);
+ id_queue = NULL;
+// errno = EIO;
+ return -1;
+ }
+ /* rewind the pointer */
+ qpol_src_inputptr = qpol_src_originalinput;
+ init_parser(2, load_rules);
+ source_file[0] = '\0';
+ if (yyparse() || policydb_errors) {
+ ERR(qpolicy, "%s: error(s) encountered while parsing configuration\n", progname);
+ queue_destroy(id_queue);
+ id_queue = NULL;
+// errno = EIO;
+ return -1;
+ }
+ queue_destroy(id_queue);
+ id_queue = NULL;
+ if (policydb_errors) {
+// errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+static int qpol_init_fbuf(qpol_fbuf_t ** fb)
+{
+ if (fb == NULL)
+ return -1;
+ *fb = (qpol_fbuf_t *) malloc(sizeof(qpol_fbuf_t));
+ if (*fb == NULL)
+ return -1;
+ (*fb)->buf = NULL;
+ (*fb)->sz = 0;
+ (*fb)->err = 0;
+ return 0;
+}
+
+static void qpol_free_fbuf(qpol_fbuf_t ** fb)
+{
+ if (*fb == NULL)
+ return;
+ if ((*fb)->sz > 0 && (*fb)->buf != NULL)
+ free((*fb)->buf);
+ free(*fb);
+ return;
+}
+
+static void *qpol_read_fbuf(qpol_fbuf_t * fb, size_t bytes, FILE * fp)
+{
+ size_t sz;
+
+ assert(fb != NULL && fp != NULL);
+ assert(!(fb->sz > 0 && fb->buf == NULL));
+
+ if (fb->sz == 0) {
+ fb->buf = (char *)malloc(bytes + 1);
+ fb->sz = bytes + 1;
+ } else if (bytes + 1 > fb->sz) {
+ fb->buf = (char *)realloc(fb->buf, bytes + 1);
+ fb->sz = bytes + 1;
+ }
+
+ if (fb->buf == NULL) {
+ fb->err = -1;
+ return NULL;
+ }
+
+ sz = fread(fb->buf, bytes, 1, fp);
+ if (sz != 1) {
+ fb->err = -3;
+ return NULL;
+ }
+ fb->err = 0;
+ return fb->buf;
+}
+
+int qpol_binpol_version(FILE * fp)
+{
+ __u32 *buf;
+ int rt, len;
+ qpol_fbuf_t *fb;
+
+ if (fp == NULL)
+ return -1;
+
+ if (qpol_init_fbuf(&fb) != 0)
+ return -1;
+
+ /* magic # and sz of policy string */
+ buf = qpol_read_fbuf(fb, sizeof(__u32) * 2, fp);
+ if (buf == NULL) {
+ rt = fb->err;
+ goto err_return;
+ }
+ buf[0] = le32_to_cpu(buf[0]);
+ if (buf[0] != SELINUX_MAGIC) {
+ rt = -2;
+ goto err_return;
+ }
+
+ len = le32_to_cpu(buf[1]);
+ if (len < 0) {
+ rt = -3;
+ goto err_return;
+ }
+ /* skip over the policy string */
+ if (fseek(fp, sizeof(char) * len, SEEK_CUR) != 0) {
+ rt = -3;
+ goto err_return;
+ }
+
+ /* Read the version, config, and table sizes. */
+ buf = qpol_read_fbuf(fb, sizeof(__u32) * 1, fp);
+ if (buf == NULL) {
+ rt = fb->err;
+ goto err_return;
+ }
+ buf[0] = le32_to_cpu(buf[0]);
+
+ rt = buf[0];
+ err_return:
+ rewind(fp);
+ qpol_free_fbuf(&fb);
+ return rt;
+}
+
+int qpol_is_file_binpol(FILE * fp)
+{
+ int rt;
+ size_t sz;
+ __u32 ubuf;
+
+ sz = fread(&ubuf, sizeof(__u32), 1, fp);
+ if (sz != 1)
+ rt = 0;
+
+ ubuf = le32_to_cpu(ubuf);
+ if (ubuf == SELINUX_MAGIC)
+ rt = 1;
+ else
+ rt = 0;
+ rewind(fp);
+ return rt;
+}
+
+int qpol_is_data_mod_pkg(char * data)
+{
+ size_t sz;
+ __u32 ubuf;
+
+ memcpy(&ubuf, data, sizeof(__u32));
+
+ ubuf = le32_to_cpu(ubuf);
+ if (ubuf == SEPOL_MODULE_PACKAGE_MAGIC)
+ return 1;
+
+ return 0;
+}
+
+int qpol_is_file_mod_pkg(FILE * fp)
+{
+ size_t sz;
+ __u32 ubuf;
+ int rt;
+
+ sz = fread(&ubuf, sizeof(__u32), 1, fp);
+
+ if (sz != 1)
+ rt = 0; /* problem reading file */
+
+ ubuf = le32_to_cpu(ubuf);
+ if (ubuf == SEPOL_MODULE_PACKAGE_MAGIC)
+ rt = 1;
+ else
+ rt = 0;
+ rewind(fp);
+ return rt;
+}
+
+static int infer_policy_version(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_fs_use_t *fsuse = NULL;
+ qpol_range_trans_t *rangetrans = NULL;
+ uint32_t behavior = 0;
+ size_t nvtrans = 0, fsusexattr = 0;
+ const char *obj_name = NULL;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ if (db->policyvers) {
+ /* version already set */
+ return STATUS_SUCCESS;
+ }
+
+ /* check fs_use for xattr and psid */
+ qpol_policy_get_fs_use_iter(policy, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&fsuse);
+ qpol_fs_use_get_behavior(policy, fsuse, &behavior);
+ /* not possible to have xattr and psid in same policy */
+ if (behavior == QPOL_FS_USE_XATTR) {
+ fsusexattr = 1;
+ break;
+ } else if (behavior == QPOL_FS_USE_PSID) {
+ qpol_iterator_destroy(&iter);
+ db->policyvers = 12;
+ return STATUS_SUCCESS;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+#if defined(HAVE_SEPOL_PERMISSIVE_TYPES) || defined(HAVE_SEPOL_POLICYCAPS)
+ ebitmap_node_t *node = NULL;
+ unsigned int i = 0;
+#endif
+
+ /* 23 : there exists at least one type that is permissive */
+#ifdef HAVE_SEPOL_PERMISSIVE_TYPES
+ ebitmap_for_each_bit(&db->permissive_map, node, i) {
+ if (ebitmap_get_bit(&db->permissive_map, i)) {
+ db->policyvers = 23;
+ return STATUS_SUCCESS;
+ }
+ }
+#endif
+
+ /* 22 : there exists at least one policy capability */
+#ifdef HAVE_SEPOL_POLICYCAPS
+ ebitmap_for_each_bit(&db->policycaps, node, i) {
+ if (ebitmap_get_bit(&db->policycaps, i)) {
+ db->policyvers = 22;
+ return STATUS_SUCCESS;
+ }
+ }
+#endif
+
+ /* 21 : object classes other than process for range_transitions */
+ qpol_policy_get_range_trans_iter(policy, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&rangetrans);
+ qpol_range_trans_get_target_class(policy, rangetrans, &obj_class);
+ qpol_class_get_name(policy, obj_class, &obj_name);
+ if (strcmp(obj_name, "process")) {
+ db->policyvers = 21;
+ qpol_iterator_destroy(&iter);
+ return STATUS_SUCCESS;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ /* 19 & 20 : mls and validatetrans statements added */
+ qpol_policy_get_validatetrans_iter(policy, &iter);
+ qpol_iterator_get_size(iter, &nvtrans);
+ qpol_iterator_destroy(&iter);
+ if (db->mls || nvtrans) {
+ db->policyvers = 19;
+ }
+
+ /* 18 : the netlink_audit_socket class added */
+ else if (hashtab_search(db->p_classes.table, (const hashtab_key_t)"netlink_audit_socket")) {
+ db->policyvers = 18;
+ }
+
+ /* 17 : IPv6 nodecon statements added */
+ else if (db->ocontexts[OCON_NODE6]) {
+ db->policyvers = 17;
+ }
+
+ /* 16 : conditional policy added */
+ else if (db->p_bool_val_to_name && db->p_bool_val_to_name[0]) {
+ db->policyvers = 16;
+
+ }
+ /* 15 */
+ else if (fsusexattr) {
+ db->policyvers = 15;
+ }
+
+ /* 12 */
+ else {
+ db->policyvers = 12;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/** State tracking struct used in the functions check_disabled, remove_symbol, and prune_disabled_symbols to handle disabled symbols */
+struct symbol_pruning_state
+{
+ qpol_policy_t *p; /**< The policy */
+ int symbol_type; /**< The current symbol type being processed */
+};
+
+/** Apply callback for hashtab_map_remove_on_error.
+ * This function tests whether a symbol referenced by the policy is declared or only ever required.
+ * Symbols without a declaration are disabled and must be removed.
+ * @param key Symbol key to check.
+ * @param datum Symbol datum to check.
+ * @param args State object (of type struct symbol_pruning_state)
+ * @return 0 if symbol is enabled, 1 if not enabled.
+ */
+static int check_disabled(hashtab_key_t key, hashtab_datum_t datum, void *args)
+{
+ struct symbol_pruning_state *s = args;
+ if (!is_id_enabled((char *)key, &(s->p->p->p), s->symbol_type))
+ return 1;
+ return 0;
+}
+
+/** Remove callback for hashtab_map_remove_on_error.
+ * Frees all memory associated with a disabled symbol that has been removed from the symbol table.
+ * @param key Symbol key to remove
+ * @param datum Symbol datum to remove
+ * @param args State object (of type struct symbol_pruning_state)
+ * @post All memory associated with the symbol is freed.
+ */
+static void remove_symbol(hashtab_key_t key, hashtab_datum_t datum, void *args)
+{
+ struct symbol_pruning_state *s = args;
+ switch (s->symbol_type) {
+ case SYM_ROLES:
+ {
+ role_datum_destroy((role_datum_t *) datum);
+ break;
+ }
+ case SYM_TYPES:
+ {
+ type_datum_destroy((type_datum_t *) datum);
+ break;
+ }
+ case SYM_USERS:
+ {
+ user_datum_destroy((user_datum_t *) datum);
+ break;
+ }
+ case SYM_BOOLS:
+ {
+ /* no-op */
+ break;
+ }
+ case SYM_LEVELS:
+ {
+ level_datum_destroy((level_datum_t *) datum);
+ break;
+ }
+ case SYM_CATS:
+ {
+ cat_datum_destroy((cat_datum_t *) datum);
+ break;
+ }
+ default:
+ return; /* invalid type of datum to free; do nothing */
+ }
+ free(key);
+ free(datum);
+}
+
+/** Remove symbols that are only required but never declared from the policy.
+ * Removes each disabled symbol freeing all memory associated with it.
+ * @param policy The policy from which disabled symbols should be removed.
+ * @return always 0.
+ * @note Since hashtab_map_remove_on_error does not return any error status,
+ * it is impossible to tell if it has failed; if it fails, the policy will
+ * be in an inconsistent state.
+ */
+static int prune_disabled_symbols(qpol_policy_t * policy)
+{
+ if (policy->type == QPOL_POLICY_KERNEL_BINARY)
+ return 0; /* checkpolicy already prunes disabled symbols */
+ struct symbol_pruning_state state;
+ state.p = policy;
+ for (state.symbol_type = SYM_ROLES; state.symbol_type < SYM_NUM; state.symbol_type++) {
+ hashtab_map_remove_on_error(policy->p->p.symtab[state.symbol_type].table, check_disabled, remove_symbol, &state);
+ }
+ return 0;
+}
+
+/** For all symbols that are multiply defined (such as attributes, roles, and users),
+ * union the relevant sets of types and roles from each declaration.
+ * @param policy The policy containig the symbols to union.
+ * @return 0 on success, non-zero on error; if the call fails,
+ * errno will be set, and the policy should be considered invalid.
+ */
+static int union_multiply_declared_symbols(qpol_policy_t * policy) {
+ /* general structure of this function:
+ walk role and user symbol tables for each role/user/attribute
+ get datum from symtab, get key from array
+ look up symbol in scope table
+ foreach decl_id in scope entry
+ union types/roles bitmap with datum's copy
+ */
+ qpol_iterator_t * iter = NULL;
+ int error = 0;
+ if (qpol_policy_get_type_iter(policy, &iter)) {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ type_datum_t *attr;
+ if (qpol_iterator_get_item(iter, (void**)&attr)) {
+ error = errno;
+ goto err;
+ }
+ unsigned char isattr = 0;
+ if (qpol_type_get_isattr(policy, attr, &isattr)) {
+ error = errno;
+ goto err;
+ }
+ if (!isattr)
+ continue;
+ const char *name;
+ if (qpol_type_get_name(policy, (qpol_type_t*)attr, &name)) {
+ error = errno;
+ goto err;
+ }
+ policydb_t *db = &policy->p->p;
+ avrule_block_t *blk = db->global;
+ for (; blk; blk = blk->next) {
+ avrule_decl_t *decl = blk->enabled;
+ if (!decl)
+ continue; /* disabled */
+ type_datum_t *internal_datum = hashtab_search(decl->symtab[SYM_TYPES].table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ continue; /* not declared here */
+ }
+ if (ebitmap_union(&attr->types, &internal_datum->types))
+ {
+ error = errno;
+ ERR(policy, "could not merge declarations for attribute %s", name);
+ goto err;
+ }
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ /* repeat for roles */
+ if (qpol_policy_get_role_iter(policy, &iter)) {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ role_datum_t *role;
+ if (qpol_iterator_get_item(iter, (void**)&role)) {
+ error = errno;
+ goto err;
+ }
+ const char *name;
+ if (qpol_role_get_name(policy, (qpol_role_t*)role, &name)) {
+ error = errno;
+ goto err;
+ }
+ policydb_t *db = &policy->p->p;
+ scope_datum_t* scope_datum = hashtab_search(db->scope[SYM_ROLES].table, (const hashtab_key_t)name);
+ if (scope_datum == NULL) {
+ ERR(policy, "could not find scope datum for role %s", name);
+ error = ENOENT;
+ goto err;
+ }
+ for (uint32_t i = 0; i < scope_datum->decl_ids_len; i++)
+ {
+ if (db->decl_val_to_struct[scope_datum->decl_ids[i] - 1]->enabled == 0)
+ continue; /* block is disabled */
+ role_datum_t *internal_datum = hashtab_search(db->decl_val_to_struct[scope_datum->decl_ids[i] - 1]->symtab[SYM_ROLES].table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ continue; /* not declared here */
+ }
+ if (ebitmap_union(&role->types.types, &internal_datum->types.types) || ebitmap_union(&role->dominates, &internal_datum->dominates))
+ {
+ error = errno;
+ ERR(policy, "could not merge declarations for role %s", name);
+ goto err;
+ }
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ /* repeat for users */
+ if (qpol_policy_get_user_iter(policy, &iter)) {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ user_datum_t *user;
+ if (qpol_iterator_get_item(iter, (void**)&user)) {
+ error = errno;
+ goto err;
+ }
+ const char *name;
+ if (qpol_user_get_name(policy, (qpol_user_t*)user, &name)) {
+ error = errno;
+ goto err;
+ }
+ policydb_t *db = &policy->p->p;
+ scope_datum_t* scope_datum = hashtab_search(db->scope[SYM_USERS].table, (const hashtab_key_t)name);
+ if (scope_datum == NULL) {
+ ERR(policy, "could not find scope datum for user %s", name);
+ error = ENOENT;
+ goto err;
+ }
+ for (uint32_t i = 0; i < scope_datum->decl_ids_len; i++)
+ {
+ if (db->decl_val_to_struct[scope_datum->decl_ids[i] - 1]->enabled == 0)
+ continue; /* block is disabled */
+ user_datum_t *internal_datum = hashtab_search(db->decl_val_to_struct[scope_datum->decl_ids[i] -1 ]->symtab[SYM_USERS].table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ continue; /* not declared here */
+ }
+ if (ebitmap_union(&user->roles.roles, &internal_datum->roles.roles))
+ {
+ error = errno;
+ ERR(policy, "could not merge declarations for user %s", name);
+ goto err;
+ }
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ return 0;
+err:
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return 1;
+}
+
+/* forward declarations see policy_extend.c */
+struct qpol_extended_image;
+extern void qpol_extended_image_destroy(struct qpol_extended_image **ext);
+
+#if LINK_SHARED == 1
+__asm__(".symver qpol_policy_open_from_file_old,qpol_policy_open_from_file@");
+__asm__(".symver qpol_policy_open_from_file_opt,qpol_policy_open_from_file@@VERS_1.3");
+__asm__(".symver qpol_policy_open_from_memory_old,qpol_policy_open_from_memory@");
+__asm__(".symver qpol_policy_open_from_memory_opt,qpol_policy_open_from_memory@VERS_1.3");
+__asm__(".symver qpol_policy_rebuild_old,qpol_policy_rebuild@");
+__asm__(".symver qpol_policy_rebuild_opt,qpol_policy_rebuild@@VERS_1.3");
+#endif
+
+/**
+ * @brief Internal version of qpol_policy_rebuild() version 1.3
+ *
+ * Implementation of the exported function qpol_policy_rebuild()
+ * for version 1.3; this symbol name is not exported.
+ * @see qpol_policy_rebuild()
+ */
+int qpol_policy_rebuild_opt(qpol_policy_t * policy, const int options)
+{
+ sepol_policydb_t *old_p = NULL;
+ sepol_policydb_t **modules = NULL;
+ qpol_module_t *base = NULL;
+ size_t num_modules = 0, i;
+ int error = 0, old_options;
+
+ if (!policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ /* if kernel binary do nothing */
+ if (policy->type == QPOL_POLICY_KERNEL_BINARY)
+ return STATUS_SUCCESS;
+
+ /* if options are the same and the modules were not modified, do nothing */
+ if (options == policy->options && policy->modified == 0)
+ return STATUS_SUCCESS;
+
+ /* cache old policy in case of failure */
+ old_p = policy->p;
+ policy->p = NULL;
+ struct qpol_extended_image *ext = policy->ext;
+ policy->ext = NULL;
+ old_options = policy->options;
+ policy->options = options;
+
+ /* QPOL_POLICY_OPTION_NO_RULES implies QPOL_POLICY_OPTION_NO_NEVERALLOWS */
+ if (policy->options & QPOL_POLICY_OPTION_NO_RULES)
+ policy->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+
+ if (policy->type == QPOL_POLICY_MODULE_BINARY) {
+ /* allocate enough space for all modules then fill with list of enabled ones only */
+ if (!(modules = calloc(policy->num_modules, sizeof(sepol_policydb_t *)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ /* first module is base and cannot be disabled */
+ for (i = 1; i < policy->num_modules; i++) {
+ if ((policy->modules[i])->enabled) {
+ modules[num_modules++] = (policy->modules[i])->p;
+ }
+ }
+ /* have to reopen the base since link alters it */
+ if (qpol_module_create_from_file((policy->modules[0])->path, &base)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ /* take the policy from base and use as new base into which to link */
+ policy->p = base->p;
+ base->p = NULL;
+ qpol_module_destroy(&base);
+ if (sepol_link_modules(policy->sh, policy->p, modules, num_modules, 0)) {
+ error = EIO;
+ goto err;
+ }
+ free(modules);
+ } else {
+ /* repeat open process as if qpol_policy_open_from_memory() */
+ if (sepol_policydb_create(&(policy->p))) {
+ error = errno;
+ goto err;
+ }
+
+ qpol_src_input = policy->file_data;
+ qpol_src_inputptr = qpol_src_input;
+ qpol_src_inputlim = qpol_src_inputptr + policy->file_data_sz - 1;
+ qpol_src_originalinput = qpol_src_input;
+
+ /* read in source */
+ policy->p->p.policy_type = POLICY_BASE;
+ if (read_source_policy(policy, "parse", policy->options) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ /* link the source */
+ INFO(policy, "%s", "Linking source policy. (Step 2 of 5)");
+ if (sepol_link_modules(policy->sh, policy->p, NULL, 0, 0)) {
+ error = EIO;
+ goto err;
+ }
+ avtab_destroy(&(policy->p->p.te_avtab));
+ avtab_destroy(&(policy->p->p.te_cond_avtab));
+ avtab_init(&(policy->p->p.te_avtab));
+ avtab_init(&(policy->p->p.te_cond_avtab));
+ }
+
+ if (prune_disabled_symbols(policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (union_multiply_declared_symbols(policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (qpol_expand_module(policy, !(policy->options & (QPOL_POLICY_OPTION_NO_NEVERALLOWS)))) {
+ error = errno;
+ goto err;
+ }
+
+ if (infer_policy_version(policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (policy_extend(policy)) {
+ error = errno;
+ goto err;
+ }
+ qpol_extended_image_destroy(&ext);
+
+ sepol_policydb_free(old_p);
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(modules);
+
+ policy->p = old_p;
+ policy->ext = ext;
+ policy->options = old_options;
+ errno = error;
+ return STATUS_ERR;
+}
+
+#if LINK_SHARED == 0
+int qpol_policy_rebuild(qpol_policy_t * policy, int options)
+{
+ return qpol_policy_rebuild_opt(policy, options);
+}
+#endif
+
+/**
+ * @brief Internal version of qpol_policy_rebuild() version 1.2 or earlier
+ * @deprecated use the 1.3 version.
+ * @see qpol_policy_rebuild()
+ */
+int qpol_policy_rebuild_old(qpol_policy_t * policy)
+{
+ if (!policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ /* fail if not a modular policy */
+ if (policy->type != QPOL_POLICY_MODULE_BINARY) {
+ ERR(policy, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return STATUS_ERR;
+ }
+
+ if (!policy->modified)
+ return STATUS_SUCCESS;
+
+ return qpol_policy_rebuild_opt(policy, policy->options);
+}
+
+/**
+ * @brief Internal version of qpol_policy_open_from_file() version 1.3
+ *
+ * Implementation of the exported function qpol_policy_open_from_file()
+ * for version 1.3; this symbol name is not exported.
+ * @see qpol_policy_open_from_file()
+ */
+int qpol_policy_open_from_file_opt(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg, const int options)
+{
+ int error = 0, retv = -1;
+ FILE *infile = NULL;
+ sepol_policy_file_t *pfile = NULL;
+ qpol_module_t *mod = NULL;
+ int fd = 0;
+ struct stat sb;
+
+ if (policy != NULL)
+ *policy = NULL;
+
+ if (path == NULL || policy == NULL) {
+ /* handle passed as NULL here as it has yet to be created */
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ errno = 0;
+ if (!(*policy = calloc(1, sizeof(qpol_policy_t)))) {
+ error = errno;
+ ERR(NULL, "%s", strerror(error));
+ goto err;
+ }
+ (*policy)->options = options;
+
+ /* QPOL_POLICY_OPTION_NO_RULES implies QPOL_POLICY_OPTION_NO_NEVERALLOWS */
+ if ((*policy)->options & QPOL_POLICY_OPTION_NO_RULES)
+ (*policy)->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+
+ (*policy)->sh = sepol_handle_create();
+ if ((*policy)->sh == NULL) {
+ error = errno;
+ ERR(*policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (fn) {
+ (*policy)->fn = fn;
+ (*policy)->varg = varg;
+ } else {
+ (*policy)->fn = qpol_handle_default_callback;
+ }
+ sepol_msg_set_callback((*policy)->sh, sepol_handle_route_to_callback, (*policy));
+
+ if (sepol_policydb_create(&((*policy)->p))) {
+ error = errno;
+ goto err;
+ }
+
+ if (sepol_policy_file_create(&pfile)) {
+ error = errno;
+ goto err;
+ }
+
+ infile = fopen(path, "rb");
+ if (infile == NULL) {
+ error = errno;
+ goto err;
+ }
+
+ sepol_policy_file_set_handle(pfile, (*policy)->sh);
+
+ errno=0;
+ if (qpol_is_file_binpol(infile)) {
+ (*policy)->type = retv = QPOL_POLICY_KERNEL_BINARY;
+ sepol_policy_file_set_fp(pfile, infile);
+ if (sepol_policydb_read((*policy)->p, pfile)) {
+// error = EIO;
+ goto err;
+ }
+ /* By definition, binary policy cannot have neverallow rules and all other rules are always loaded. */
+ (*policy)->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+ (*policy)->options &= ~(QPOL_POLICY_OPTION_NO_RULES);
+ if (policy_extend(*policy)) {
+ error = errno;
+ goto err;
+ }
+ } else if (qpol_module_create_from_file(path, &mod) == STATUS_SUCCESS) {
+ (*policy)->type = retv = QPOL_POLICY_MODULE_BINARY;
+
+ if (qpol_policy_append_module(*policy, mod)) {
+ error = errno;
+ goto err;
+ }
+ /* *policy now owns mod */
+ mod = NULL;
+ if (qpol_policy_rebuild_opt(*policy, options)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ (*policy)->type = retv = QPOL_POLICY_KERNEL_SOURCE;
+ fd = fileno(infile);
+ if (fd < 0) {
+ error = errno;
+ goto err;
+ }
+ if (fstat(fd, &sb) < 0) {
+ error = errno;
+ ERR(*policy, "Can't stat '%s': %s\n", path, strerror(errno));
+ goto err;
+ }
+ qpol_src_input = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (qpol_src_input == MAP_FAILED) {
+ error = errno;
+ ERR(*policy, "Can't map '%s': %s\n", path, strerror(errno));
+
+ goto err;
+ }
+ qpol_src_inputptr = qpol_src_input;
+ qpol_src_inputlim = &qpol_src_inputptr[sb.st_size - 1];
+ qpol_src_originalinput = qpol_src_input;
+
+ /* store mmaped version for rebuild() */
+ (*policy)->file_data = qpol_src_originalinput;
+ (*policy)->file_data_sz = sb.st_size;
+ (*policy)->file_data_type = QPOL_POLICY_FILE_DATA_TYPE_MMAP;
+
+ (*policy)->p->p.policy_type = POLICY_BASE;
+ if (read_source_policy(*policy, "libqpol", (*policy)->options) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ /* link the source */
+ INFO(*policy, "%s", "Linking source policy. (Step 2 of 5)");
+ if (sepol_link_modules((*policy)->sh, (*policy)->p, NULL, 0, 0)) {
+ error = EIO;
+ goto err;
+ }
+ avtab_destroy(&((*policy)->p->p.te_avtab));
+ avtab_destroy(&((*policy)->p->p.te_cond_avtab));
+ avtab_init(&((*policy)->p->p.te_avtab));
+ avtab_init(&((*policy)->p->p.te_cond_avtab));
+
+ if (prune_disabled_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (union_multiply_declared_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ /* expand */
+ if (qpol_expand_module(*policy, !(options & (QPOL_POLICY_OPTION_NO_NEVERALLOWS)))) {
+ error = errno;
+ goto err;
+ }
+
+ if (infer_policy_version(*policy)) {
+ error = errno;
+ goto err;
+ }
+ if (policy_extend(*policy)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ fclose(infile);
+ sepol_policy_file_free(pfile);
+ return retv;
+
+ err:
+ qpol_policy_destroy(policy);
+ qpol_module_destroy(&mod);
+ sepol_policy_file_free(pfile);
+ if (infile)
+ fclose(infile);
+ errno = error;
+ return -1;
+}
+
+#if LINK_SHARED == 0
+int qpol_policy_open_from_file(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg, const int options)
+{
+ return qpol_policy_open_from_file_opt(path, policy, fn, varg, options);
+}
+#endif
+
+int qpol_policy_open_from_file_no_rules(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg)
+{
+ return qpol_policy_open_from_file_opt(path, policy, fn, varg, QPOL_POLICY_OPTION_NO_RULES);
+}
+
+/**
+ * @brief Internal version of qpol_policy_open_from_memory() version 1.3
+ *
+ * Implementation of the exported function qpol_policy_open_from_memory()
+ * for version 1.3; this symbol name is not exported.
+ * @see qpol_policy_open_from_memory()
+ */
+int qpol_policy_open_from_memory_opt(qpol_policy_t ** policy, const char *filedata, size_t size, qpol_callback_fn_t fn, void *varg,
+ const int options)
+{
+ int error = 0;
+ if (policy == NULL || filedata == NULL)
+ return -1;
+ *policy = NULL;
+
+ if (!(*policy = calloc(1, sizeof(qpol_policy_t)))) {
+ error = errno;
+ goto err;
+ }
+ (*policy)->options = options;
+
+ /* QPOL_POLICY_OPTION_NO_RULES implies QPOL_POLICY_OPTION_NO_NEVERALLOWS */
+ if ((*policy)->options & QPOL_POLICY_OPTION_NO_RULES)
+ (*policy)->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+
+ (*policy)->sh = sepol_handle_create();
+ if ((*policy)->sh == NULL) {
+ error = errno;
+ ERR(*policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ sepol_msg_set_callback((*policy)->sh, sepol_handle_route_to_callback, (*policy));
+ if (fn) {
+ (*policy)->fn = fn;
+ (*policy)->varg = varg;
+ } else {
+ (*policy)->fn = qpol_handle_default_callback;
+ }
+
+ if (sepol_policydb_create(&((*policy)->p))) {
+ error = errno;
+ goto err;
+ }
+
+ qpol_src_input = (char *)filedata;
+ qpol_src_inputptr = qpol_src_input;
+ qpol_src_inputlim = qpol_src_inputptr + size - 1;
+ qpol_src_originalinput = qpol_src_input;
+
+ /* store filedata for rebuild() */
+ if (!((*policy)->file_data = malloc(size))) {
+ error = errno;
+ goto err;
+ }
+ memcpy((*policy)->file_data, filedata, size);
+ (*policy)->file_data_sz = size;
+ (*policy)->file_data_type = QPOL_POLICY_FILE_DATA_TYPE_MEM;
+
+ /* read in source */
+ (*policy)->p->p.policy_type = POLICY_BASE;
+ if (read_source_policy(*policy, "parse", (*policy)->options) < 0)
+ exit(1);
+
+ /* link the source */
+ INFO(*policy, "%s", "Linking source policy. (Step 2 of 5)");
+ if (sepol_link_modules((*policy)->sh, (*policy)->p, NULL, 0, 0)) {
+ error = EIO;
+ goto err;
+ }
+ avtab_destroy(&((*policy)->p->p.te_avtab));
+ avtab_destroy(&((*policy)->p->p.te_cond_avtab));
+ avtab_init(&((*policy)->p->p.te_avtab));
+ avtab_init(&((*policy)->p->p.te_cond_avtab));
+
+ if (prune_disabled_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (union_multiply_declared_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ /* expand */
+ if (qpol_expand_module(*policy, !(options & (QPOL_POLICY_OPTION_NO_NEVERALLOWS)))) {
+ error = errno;
+ goto err;
+ }
+
+ return 0;
+ err:
+ qpol_policy_destroy(policy);
+ errno = error;
+ return -1;
+
+}
+
+#if LINK_SHARED == 0
+int qpol_policy_open_from_memory(qpol_policy_t ** policy, const char *filedata, size_t size, qpol_callback_fn_t fn, void *varg,
+ const int options)
+{
+ return qpol_policy_open_from_memory_opt(policy, filedata, size, fn, varg, options);
+}
+#endif
+
+/**
+ * @brief Internal version of qpol_policy_open_from_file() version 1.2 or earlier
+ * @deprecated use the 1.3 version.
+ * @see qpol_policy_open_from_file()
+ */
+int qpol_policy_open_from_file_old(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg)
+{
+ return qpol_policy_open_from_file(path, policy, fn, varg, 0);
+}
+
+/**
+ * @brief Internal version of qpol_policy_open_from_memory() version 1.2 or earlier
+ * @deprecated use the 1.3 version.
+ * @see qpol_policy_open_from_memory()
+ */
+int qpol_policy_open_from_memory_old(qpol_policy_t ** policy, const char *filedata, size_t size, qpol_callback_fn_t fn, void *varg)
+{
+ return qpol_policy_open_from_memory_opt(policy, filedata, size, fn, varg, 0);
+}
+
+void qpol_policy_destroy(qpol_policy_t ** policy)
+{
+ if (policy != NULL && *policy != NULL) {
+ sepol_policydb_free((*policy)->p);
+ sepol_handle_destroy((*policy)->sh);
+ qpol_extended_image_destroy(&((*policy)->ext));
+ if ((*policy)->modules) {
+ size_t i = 0;
+ for (i = 0; i < (*policy)->num_modules; i++) {
+ qpol_module_destroy(&((*policy)->modules[i]));
+ }
+ free((*policy)->modules);
+ }
+ if ((*policy)->file_data_type == QPOL_POLICY_FILE_DATA_TYPE_MEM) {
+ free((*policy)->file_data);
+ } else if ((*policy)->file_data_type == QPOL_POLICY_FILE_DATA_TYPE_MMAP) {
+ munmap((*policy)->file_data, (*policy)->file_data_sz);
+ }
+ free(*policy);
+ *policy = NULL;
+ }
+}
+
+int qpol_policy_reevaluate_conds(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ cond_node_t *cond = NULL;
+ cond_av_list_t *list_ptr = NULL;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ for (cond = db->cond_list; cond; cond = cond->next) {
+ /* evaluate cond */
+ cond->cur_state = cond_evaluate_expr(db, cond->expr);
+ if (cond->cur_state < 0) {
+ ERR(policy, "Error evaluating conditional: %s", strerror(EILSEQ));
+ errno = EILSEQ;
+ return STATUS_ERR;
+ }
+
+ /* walk true list */
+ for (list_ptr = cond->true_list; list_ptr; list_ptr = list_ptr->next) {
+ /* field not used (except by write),
+ * now storing list and enabled flags */
+ if (cond->cur_state)
+ list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
+ else
+ list_ptr->node->merged &= ~(QPOL_COND_RULE_ENABLED);
+ }
+
+ /* walk false list */
+ for (list_ptr = cond->false_list; list_ptr; list_ptr = list_ptr->next) {
+ /* field not used (except by write),
+ * now storing list and enabled flags */
+ if (!cond->cur_state)
+ list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
+ else
+ list_ptr->node->merged &= ~(QPOL_COND_RULE_ENABLED);
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_append_module(qpol_policy_t * policy, qpol_module_t * module)
+{
+ qpol_module_t **tmp = NULL;
+ int error = 0;
+
+ if (!policy || !module) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(tmp = realloc(policy->modules, (1 + policy->num_modules) * sizeof(qpol_module_t *)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ policy->modules = tmp;
+ policy->modules[policy->num_modules] = module;
+ policy->num_modules++;
+ policy->modified = 1;
+ module->parent = policy;
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct mod_state
+{
+ qpol_module_t **list;
+ size_t cur;
+ size_t end;
+} mod_state_t;
+
+static int mod_state_end(const qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 1;
+ }
+
+ return (ms->cur >= ms->end);
+}
+
+static void *mod_state_get_cur(const qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return ms->list[ms->cur];
+}
+
+static int mod_state_next(qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ ms->cur++;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t mod_state_size(const qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ return ms->end;
+}
+
+int qpol_policy_get_module_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ mod_state_t *ms = NULL;
+ int error = 0;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(ms = calloc(1, sizeof(mod_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_create(policy, (void *)ms, mod_state_get_cur, mod_state_next, mod_state_end, mod_state_size, free, iter)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ free(ms);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ ms->end = policy->num_modules;
+ ms->list = policy->modules;
+
+ return STATUS_SUCCESS;
+}
+
+static int is_mls_policy(const qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ if (db->mls != 0)
+ return 1;
+ else
+ return 0;
+}
+
+int qpol_policy_is_mls_enabled(qpol_policy_t * policy)
+{
+ return is_mls_policy(policy);
+}
+
+int qpol_policy_get_policy_version(const qpol_policy_t * policy, unsigned int *version)
+{
+ policydb_t *db;
+
+ if (version != NULL)
+ *version = 0;
+
+ if (policy == NULL || version == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ *version = db->policyvers;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_type(const qpol_policy_t * policy, int *type)
+{
+ if (!policy || !type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *type = policy->type;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_has_capability(const qpol_policy_t * policy, qpol_capability_e cap)
+{
+ unsigned int version = 0;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+
+ qpol_policy_get_policy_version(policy, &version);
+
+ switch (cap) {
+ case QPOL_CAP_ATTRIB_NAMES:
+ {
+ if ((policy->type == QPOL_POLICY_KERNEL_SOURCE || policy->type == QPOL_POLICY_MODULE_BINARY) || (version >= 24))
+ return 1;
+ break;
+ }
+ case QPOL_CAP_SYN_RULES:
+ {
+ if (policy->type == QPOL_POLICY_KERNEL_SOURCE || policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_LINE_NUMBERS:
+ {
+ if (policy->type == QPOL_POLICY_KERNEL_SOURCE)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_CONDITIONALS:
+ {
+ if (version >= 16 || policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_MLS:
+ {
+ return is_mls_policy(policy);
+ }
+ case QPOL_CAP_MODULES:
+ {
+ if (policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_POLCAPS:
+ {
+ if (version >= 22 && policy->type != QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ if (version >= 7 && policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_RULES_LOADED:
+ {
+ if (!(policy->options & QPOL_POLICY_OPTION_NO_RULES))
+ return 1;
+ break;
+ }
+ case QPOL_CAP_SOURCE:
+ {
+ if (policy->type == QPOL_POLICY_KERNEL_SOURCE)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_NEVERALLOW:
+ {
+ if (!(policy->options & QPOL_POLICY_OPTION_NO_NEVERALLOWS) && policy->type != QPOL_POLICY_KERNEL_BINARY)
+ return 1;
+ break;
+ }
+ default:
+ {
+ ERR(policy, "%s", "Unknown capability");
+ errno = EDOM;
+ break;
+ }
+ }
+ return 0;
+}