setools/libqpol/policy.c
Richard Haines dd29dc9c43 setools-V4: libqpol policy V30 updates (xen/xperm statements)
Updated libqpol services to use the latest checkpolicy 2.4 source
files to support Xen and extended permissions (allowxperm etc.).

TODO: Add support for querying the xperm values.

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
2016-03-21 10:56:37 -04:00

1714 lines
41 KiB
C

/**
* @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 <sys/stat.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;
extern int xenpol;
#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;
__attribute__ ((format(printf, 4, 0)))
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);
}
__attribute__ ((format(printf, 3, 4)))
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);
}
__attribute__ ((format(printf, 3, 4)))
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);
}
__attribute__ ((format(printf, 4, 0)))
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, const 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;
xenpol = policydbp->target_platform;
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 = EINVAL;
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 = EINVAL;
return -1;
}
queue_destroy(id_queue);
id_queue = NULL;
if (policydb_errors) {
errno = EINVAL;
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)
{
__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);
/* Check each version change from 30 to 24 */
/* If these are available set version 30 */
#if defined(HAVE_SEPOL_XPERM_IOCTL) || defined(HAVE_SEPOL_XEN_DEVICETREE)
db->policyvers = 30;
return STATUS_SUCCESS;
#endif
/* If this is available then set version 29 */
#ifdef HAVE_SEPOL_CONSTRAINT_NAMES
db->policyvers = 29;
return STATUS_SUCCESS;
#endif
/*
* These will remove the rules from policy_define.c if libsepol
* does not have the support listed in policydb.h. The earlier code
* checked for at least one rule before enabling - this patch does not
* as if in policydb.h then must be capable of being built.
*/
#ifdef HAVE_SEPOL_DEFAULT_TYPE
db->policyvers = 28;
return STATUS_SUCCESS;
#endif
#ifdef HAVE_SEPOL_NEW_OBJECT_DEFAULTS
db->policyvers = 27;
return STATUS_SUCCESS;
#endif
/* This seems to be in place already ??
#ifdef HAVE_SEPOL_ROLETRANS
db->policyvers = 26;
return STATUS_SUCCESS;
#endif */
#ifdef HAVE_SEPOL_FILENAME_TRANS
db->policyvers = 25;
return STATUS_SUCCESS;
#endif
#ifdef HAVE_SEPOL_BOUNDARY
db->policyvers = 24;
return STATUS_SUCCESS;
#endif
#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, (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, (qpol_type_t*)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, (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;
uint32_t i;
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, (hashtab_key_t)name);
if (scope_datum == NULL) {
ERR(policy, "could not find scope datum for role %s", name);
error = ENOENT;
goto err;
}
for (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, (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;
uint32_t i;
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, (hashtab_key_t)name);
if (scope_datum == NULL) {
ERR(policy, "could not find scope datum for user %s", name);
error = ENOENT;
goto err;
}
for (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, (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_policy_handle_unknown(const qpol_policy_t * policy, unsigned int *handle_unknown)
{
policydb_t *db;
if (handle_unknown != NULL)
*handle_unknown = 0;
if (policy == NULL || handle_unknown == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return STATUS_ERR;
}
db = &policy->p->p;
*handle_unknown = db->handle_unknown;
return STATUS_SUCCESS;
}
int qpol_policy_get_target_platform(const qpol_policy_t *policy,
int *target_platform)
{
policydb_t *db;
if (target_platform != NULL)
*target_platform = 0;
if (policy == NULL || target_platform == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return STATUS_ERR;
}
db = &policy->p->p;
*target_platform = db->target_platform;
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_BOUNDS:
{
if (version >= 24 && policy->type != QPOL_POLICY_MODULE_BINARY)
return 1;
if (version >= 9 && policy->type == QPOL_POLICY_MODULE_BINARY)
return 1;
break;
}
case QPOL_CAP_PERMISSIVE:
{
if (version >= 23 && policy->type != QPOL_POLICY_MODULE_BINARY)
return 1;
if (version >= 8 && policy->type == QPOL_POLICY_MODULE_BINARY)
return 1;
break;
}
case QPOL_CAP_FILENAME_TRANS:
{
if (version >= 25 && policy->type != QPOL_POLICY_MODULE_BINARY)
return 1;
if (version >= 11 && policy->type == QPOL_POLICY_MODULE_BINARY)
return 1;
break;
}
case QPOL_CAP_ROLETRANS:
{
if (version >= 26 && policy->type != QPOL_POLICY_MODULE_BINARY)
return 1;
if (version >= 12 && policy->type == QPOL_POLICY_MODULE_BINARY)
return 1;
break;
}
/* This indicates the user, role and range - types were ate 28/16 */
case QPOL_CAP_DEFAULT_OBJECTS:
{
if (version >= 27 && policy->type != QPOL_POLICY_MODULE_BINARY)
return 1;
if (version >= 15 && policy->type == QPOL_POLICY_MODULE_BINARY)
return 1;
break;
}
case QPOL_CAP_DEFAULT_TYPE:
{
if (version >= 28 && policy->type != QPOL_POLICY_MODULE_BINARY)
return 1;
if (version >= 16 && policy->type == QPOL_POLICY_MODULE_BINARY)
return 1;
break;
}
case QPOL_CAP_XPERM_IOCTL:
{
if (version >= 30 && policy->type != QPOL_POLICY_MODULE_BINARY)
return 1;
if (version >= 17 && 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;
}