mirror of
https://github.com/SELinuxProject/selinux
synced 2025-01-30 01:12:51 +00:00
7a09af2123
Currently, roletype statements are only added for types when they are declared (not required). This means that in policy like: require { type foo_t; } type bar_t; role staff_r types foo_t, bar_t; only bar_t is associated with staff_r. This patch moves the code that generates roletype statements for types to outside the SCOPE_DECL check so that roletype statements are generated for all types, regardless of the required/declared scope. It further moves the code outside of the type/typeattribute flavor check so that roletype statements are also generated for typeattributes. Reported-by: Sven Vermeulen <sven.vermeulen@siphos.be> Signed-off-by: Steve Lawrence <slawrence@tresys.com> Reviewed-by: Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com> Tested-by: Jason Zaman <jason@perfinion.com>
3971 lines
84 KiB
C
3971 lines
84 KiB
C
/*
|
|
* Copyright (C) 2014 Tresys Technology, LLC
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#include <arpa/inet.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <netinet/in.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sepol/module.h>
|
|
#include <sepol/policydb/conditional.h>
|
|
#include <sepol/policydb/hashtab.h>
|
|
#include <sepol/policydb/polcaps.h>
|
|
#include <sepol/policydb/policydb.h>
|
|
#include <sepol/policydb/services.h>
|
|
#include <sepol/policydb/util.h>
|
|
|
|
#ifdef __GNUC__
|
|
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
|
|
#else
|
|
# define UNUSED(x) UNUSED_ ## x
|
|
#endif
|
|
|
|
char *progname;
|
|
FILE *out_file;
|
|
|
|
#define STACK_SIZE 16
|
|
#define DEFAULT_LEVEL "systemlow"
|
|
#define DEFAULT_OBJECT "object_r"
|
|
#define GEN_REQUIRE_ATTR "cil_gen_require"
|
|
|
|
__attribute__ ((format(printf, 1, 2)))
|
|
static void log_err(const char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
va_start(argptr, fmt);
|
|
if (vfprintf(stderr, fmt, argptr) < 0) {
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
va_end(argptr);
|
|
if (fprintf(stderr, "\n") < 0) {
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void cil_indent(int indent)
|
|
{
|
|
if (fprintf(out_file, "%*s", indent * 4, "") < 0) {
|
|
log_err("Failed to write to output");
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
__attribute__ ((format(printf, 1, 2)))
|
|
static void cil_printf(const char *fmt, ...) {
|
|
va_list argptr;
|
|
va_start(argptr, fmt);
|
|
if (vfprintf(out_file, fmt, argptr) < 0) {
|
|
log_err("Failed to write to output");
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
va_end(argptr);
|
|
}
|
|
|
|
__attribute__ ((format(printf, 2, 3)))
|
|
static void cil_println(int indent, const char *fmt, ...)
|
|
{
|
|
cil_indent(indent);
|
|
va_list argptr;
|
|
va_start(argptr, fmt);
|
|
if (vfprintf(out_file, fmt, argptr) < 0) {
|
|
log_err("Failed to write to output");
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
va_end(argptr);
|
|
if (fprintf(out_file, "\n") < 0) {
|
|
log_err("Failed to write to output");
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
struct map_args {
|
|
struct policydb *pdb;
|
|
struct avrule_block *block;
|
|
struct stack *decl_stack;
|
|
int scope;
|
|
int indent;
|
|
int sym_index;
|
|
};
|
|
|
|
struct stack {
|
|
void **stack;
|
|
int pos;
|
|
int size;
|
|
};
|
|
|
|
struct role_list_node {
|
|
char *role_name;
|
|
role_datum_t *role;
|
|
};
|
|
|
|
struct attr_list_node {
|
|
char *attribute;
|
|
int is_type;
|
|
union {
|
|
struct type_set *ts;
|
|
struct role_set *rs;
|
|
} set;
|
|
};
|
|
|
|
struct list_node {
|
|
void *data;
|
|
struct list_node *next;
|
|
};
|
|
|
|
struct list {
|
|
struct list_node *head;
|
|
};
|
|
|
|
/* A linked list of all roles stored in the pdb
|
|
* which is iterated to determine types associated
|
|
* with each role when printing role_type statements
|
|
*/
|
|
static struct list *role_list;
|
|
|
|
static void list_destroy(struct list **list)
|
|
{
|
|
struct list_node *curr = (*list)->head;
|
|
struct list_node *tmp;
|
|
|
|
while (curr != NULL) {
|
|
tmp = curr->next;
|
|
free(curr);
|
|
curr = tmp;
|
|
}
|
|
|
|
free(*list);
|
|
*list = NULL;
|
|
}
|
|
|
|
static void role_list_destroy(void)
|
|
{
|
|
struct list_node *curr = role_list->head;
|
|
|
|
while (curr != NULL) {
|
|
free(curr->data);
|
|
curr->data = NULL;
|
|
curr = curr->next;
|
|
}
|
|
|
|
list_destroy(&role_list);
|
|
}
|
|
|
|
static void attr_list_destroy(struct list **attr_list)
|
|
{
|
|
if (attr_list == NULL || *attr_list == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct list_node *curr = (*attr_list)->head;
|
|
struct attr_list_node *attr;
|
|
|
|
while (curr != NULL) {
|
|
attr = curr->data;
|
|
if (attr != NULL) {
|
|
free(attr->attribute);
|
|
}
|
|
|
|
free(curr->data);
|
|
curr->data = NULL;
|
|
curr = curr->next;
|
|
}
|
|
|
|
list_destroy(attr_list);
|
|
}
|
|
|
|
static int list_init(struct list **list)
|
|
{
|
|
int rc = -1;
|
|
struct list *l = calloc(1, sizeof(*l));
|
|
if (l == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
*list = l;
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
list_destroy(&l);
|
|
return rc;
|
|
}
|
|
|
|
static int list_prepend(struct list *list, void *data)
|
|
{
|
|
int rc = -1;
|
|
struct list_node *node = calloc(1, sizeof(*node));
|
|
if (node == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
node->data = data;
|
|
node->next = list->head;
|
|
list->head = node;
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int roles_gather_map(char *key, void *data, void *args)
|
|
{
|
|
struct role_list_node *role_node;
|
|
role_datum_t *role = data;
|
|
int rc = -1;
|
|
|
|
role_node = calloc(1, sizeof(*role_node));
|
|
if (role_node == NULL) {
|
|
return rc;
|
|
}
|
|
|
|
role_node->role_name = key;
|
|
role_node->role = role;
|
|
|
|
rc = list_prepend((struct list *)args, role_node);
|
|
return rc;
|
|
}
|
|
|
|
static int role_list_create(hashtab_t roles_tab)
|
|
{
|
|
int rc = -1;
|
|
|
|
rc = list_init(&role_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = hashtab_map(roles_tab, roles_gather_map, role_list);
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
// array of lists, where each list contains all the aliases defined in the scope at index i
|
|
static struct list **typealias_lists;
|
|
static uint32_t typealias_lists_len;
|
|
|
|
static int typealiases_gather_map(char *key, void *data, void *arg)
|
|
{
|
|
int rc = -1;
|
|
struct type_datum *type = data;
|
|
struct policydb *pdb = arg;
|
|
struct scope_datum *scope;
|
|
uint32_t i;
|
|
uint32_t scope_id;
|
|
|
|
if (type->primary != 1) {
|
|
scope = hashtab_search(pdb->scope[SYM_TYPES].table, key);
|
|
if (scope == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < scope->decl_ids_len; i++) {
|
|
scope_id = scope->decl_ids[i];
|
|
if (typealias_lists[scope_id] == NULL) {
|
|
rc = list_init(&typealias_lists[scope_id]);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
list_prepend(typealias_lists[scope_id], key);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static void typealias_list_destroy(void)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < typealias_lists_len; i++) {
|
|
if (typealias_lists[i] != NULL) {
|
|
list_destroy(&typealias_lists[i]);
|
|
}
|
|
}
|
|
typealias_lists_len = 0;
|
|
free(typealias_lists);
|
|
typealias_lists = NULL;
|
|
}
|
|
|
|
static int typealias_list_create(struct policydb *pdb)
|
|
{
|
|
uint32_t max_decl_id = 0;
|
|
struct avrule_decl *decl;
|
|
struct avrule_block *block;
|
|
uint32_t rc = -1;
|
|
|
|
for (block = pdb->global; block != NULL; block = block->next) {
|
|
decl = block->branch_list;
|
|
if (decl->decl_id > max_decl_id) {
|
|
max_decl_id = decl->decl_id;
|
|
}
|
|
}
|
|
|
|
typealias_lists = calloc(max_decl_id + 1, sizeof(*typealias_lists));
|
|
typealias_lists_len = max_decl_id + 1;
|
|
|
|
rc = hashtab_map(pdb->p_types.table, typealiases_gather_map, pdb);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
typealias_list_destroy();
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int stack_destroy(struct stack **stack)
|
|
{
|
|
if (stack == NULL || *stack == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
free((*stack)->stack);
|
|
free(*stack);
|
|
*stack = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stack_init(struct stack **stack)
|
|
{
|
|
int rc = -1;
|
|
struct stack *s = calloc(1, sizeof(*s));
|
|
if (s == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
s->stack = malloc(sizeof(*s->stack) * STACK_SIZE);
|
|
if (s->stack == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
s->pos = -1;
|
|
s->size = STACK_SIZE;
|
|
|
|
*stack = s;
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
stack_destroy(&s);
|
|
return rc;
|
|
}
|
|
|
|
static int stack_push(struct stack *stack, void *ptr)
|
|
{
|
|
int rc = -1;
|
|
void *new_stack;
|
|
|
|
if (stack->pos + 1 == stack->size) {
|
|
new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2));
|
|
if (new_stack == NULL) {
|
|
goto exit;
|
|
}
|
|
stack->stack = new_stack;
|
|
stack->size *= 2;
|
|
}
|
|
|
|
stack->pos++;
|
|
stack->stack[stack->pos] = ptr;
|
|
|
|
rc = 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static void *stack_pop(struct stack *stack)
|
|
{
|
|
if (stack->pos == -1) {
|
|
return NULL;
|
|
}
|
|
|
|
stack->pos--;
|
|
return stack->stack[stack->pos + 1];
|
|
}
|
|
|
|
static void *stack_peek(struct stack *stack)
|
|
{
|
|
if (stack->pos == -1) {
|
|
return NULL;
|
|
}
|
|
|
|
return stack->stack[stack->pos];
|
|
}
|
|
|
|
static int is_id_in_scope_with_start(struct policydb *pdb, struct stack *decl_stack, int start, uint32_t symbol_type, char *id)
|
|
{
|
|
int i;
|
|
uint32_t j;
|
|
struct avrule_decl *decl;
|
|
struct scope_datum *scope;
|
|
|
|
scope = hashtab_search(pdb->scope[symbol_type].table, id);
|
|
if (scope == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = start; i >= 0; i--) {
|
|
decl = decl_stack->stack[i];
|
|
|
|
for (j = 0; j < scope->decl_ids_len; j++) {
|
|
if (scope->decl_ids[j] == decl->decl_id) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int is_id_in_ancestor_scope(struct policydb *pdb, struct stack *decl_stack, char *type, uint32_t symbol_type)
|
|
{
|
|
int start = decl_stack->pos - 1;
|
|
|
|
return is_id_in_scope_with_start(pdb, decl_stack, start, symbol_type, type);
|
|
}
|
|
|
|
static int is_id_in_scope(struct policydb *pdb, struct stack *decl_stack, char *type, uint32_t symbol_type)
|
|
{
|
|
int start = decl_stack->pos;
|
|
|
|
return is_id_in_scope_with_start(pdb, decl_stack, start, symbol_type, type);
|
|
}
|
|
|
|
static int semantic_level_to_cil(struct policydb *pdb, int sens_offset, struct mls_semantic_level *level)
|
|
{
|
|
struct mls_semantic_cat *cat;
|
|
|
|
cil_printf("(%s ", pdb->p_sens_val_to_name[level->sens - sens_offset]);
|
|
|
|
if (level->cat != NULL) {
|
|
cil_printf("(");
|
|
}
|
|
|
|
for (cat = level->cat; cat != NULL; cat = cat->next) {
|
|
if (cat->low == cat->high) {
|
|
cil_printf("%s", pdb->p_cat_val_to_name[cat->low - 1]);
|
|
} else {
|
|
cil_printf("range %s %s", pdb->p_cat_val_to_name[cat->low - 1], pdb->p_cat_val_to_name[cat->high - 1]);
|
|
}
|
|
|
|
if (cat->next != NULL) {
|
|
cil_printf(" ");
|
|
}
|
|
}
|
|
|
|
if (level->cat != NULL) {
|
|
cil_printf(")");
|
|
}
|
|
|
|
cil_printf(")");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int avrule_to_cil(int indent, struct policydb *pdb, uint32_t type, const char *src, const char *tgt, const struct class_perm_node *classperms)
|
|
{
|
|
int rc = -1;
|
|
const char *rule;
|
|
const struct class_perm_node *classperm;
|
|
char *perms;
|
|
|
|
switch (type) {
|
|
case AVRULE_ALLOWED:
|
|
rule = "allow";
|
|
break;
|
|
case AVRULE_AUDITALLOW:
|
|
rule = "auditallow";
|
|
break;
|
|
case AVRULE_AUDITDENY:
|
|
rule = "auditdenty";
|
|
break;
|
|
case AVRULE_DONTAUDIT:
|
|
rule = "dontaudit";
|
|
break;
|
|
case AVRULE_NEVERALLOW:
|
|
rule = "neverallow";
|
|
break;
|
|
case AVRULE_TRANSITION:
|
|
rule = "typetransition";
|
|
break;
|
|
case AVRULE_MEMBER:
|
|
rule = "typemember";
|
|
break;
|
|
case AVRULE_CHANGE:
|
|
rule = "typechange";
|
|
break;
|
|
default:
|
|
log_err("Unknown avrule type: %i", type);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
for (classperm = classperms; classperm != NULL; classperm = classperm->next) {
|
|
if (type & AVRULE_AV) {
|
|
perms = sepol_av_to_string(pdb, classperm->class, classperm->data);
|
|
if (perms == NULL) {
|
|
log_err("Failed to generate permission string");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
cil_println(indent, "(%s %s %s (%s (%s)))",
|
|
rule, src, tgt,
|
|
pdb->p_class_val_to_name[classperm->class - 1],
|
|
perms + 1);
|
|
} else {
|
|
cil_println(indent, "(%s %s %s %s %s)",
|
|
rule, src, tgt,
|
|
pdb->p_class_val_to_name[classperm->class - 1],
|
|
pdb->p_type_val_to_name[classperm->data - 1]);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int num_digits(int n)
|
|
{
|
|
int num = 1;
|
|
while (n >= 10) {
|
|
n /= 10;
|
|
num++;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
static int set_to_cil_attr(struct policydb *pdb, int is_type, char ***names, uint32_t *num_names)
|
|
{
|
|
static unsigned int num_attrs = 0;
|
|
int rc = -1;
|
|
int len, rlen;
|
|
const char *attr_infix;
|
|
char *attr;
|
|
|
|
num_attrs++;
|
|
|
|
if (is_type) {
|
|
attr_infix = "_typeattr_";
|
|
} else {
|
|
attr_infix = "_roleattr_";
|
|
}
|
|
|
|
len = strlen(pdb->name) + strlen(attr_infix) + num_digits(num_attrs) + 1;
|
|
attr = malloc(len);
|
|
if (attr == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
rlen = snprintf(attr, len, "%s%s%i", pdb->name, attr_infix, num_attrs);
|
|
if (rlen < 0 || rlen >= len) {
|
|
log_err("Failed to generate attribute name");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
*names = malloc(sizeof(**names));
|
|
if (*names == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
*names[0] = attr;
|
|
*num_names = 1;
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int cil_print_attr_strs(int indent, struct policydb *pdb, int is_type, struct ebitmap *pos, struct ebitmap *neg, uint32_t flags, char *attr)
|
|
{
|
|
// CIL doesn't support anonymous positive/negative/complemented sets. So
|
|
// instead we create a CIL type/roleattributeset that matches the set. If
|
|
// the set has a negative set, then convert it to is (P & !N), where P is
|
|
// the list of members in the positive set , and N is the list of members
|
|
// in the negative set. Additonally, if the set is complemented, then wrap
|
|
// the whole thing with a negation.
|
|
|
|
int rc = 0;
|
|
struct ebitmap_node *node;
|
|
unsigned int i;
|
|
char *statement;
|
|
int has_positive = pos && (ebitmap_cardinality(pos) > 0);
|
|
int has_negative = neg && (ebitmap_cardinality(neg) > 0);
|
|
char **val_to_name;
|
|
|
|
if (is_type) {
|
|
statement = "type";
|
|
val_to_name = pdb->p_type_val_to_name;
|
|
} else {
|
|
statement = "role";
|
|
val_to_name = pdb->p_role_val_to_name;
|
|
}
|
|
|
|
cil_println(indent, "(%sattribute %s)", statement, attr);
|
|
cil_indent(indent);
|
|
cil_printf("(%sattributeset %s ", statement, attr);
|
|
|
|
if (flags & TYPE_STAR) {
|
|
cil_printf("(all)");
|
|
}
|
|
|
|
if (flags & TYPE_COMP) {
|
|
cil_printf("(not ");
|
|
}
|
|
|
|
if (has_positive && has_negative) {
|
|
cil_printf("(and ");
|
|
}
|
|
|
|
if (has_positive) {
|
|
cil_printf("(");
|
|
ebitmap_for_each_bit(pos, node, i) {
|
|
if (!ebitmap_get_bit(pos, i)) {
|
|
continue;
|
|
}
|
|
cil_printf("%s ", val_to_name[i]);
|
|
}
|
|
cil_printf(") ");
|
|
}
|
|
|
|
if (has_negative) {
|
|
cil_printf("(not (");
|
|
|
|
ebitmap_for_each_bit(neg, node, i) {
|
|
if (!ebitmap_get_bit(neg, i)) {
|
|
continue;
|
|
}
|
|
cil_printf("%s ", val_to_name[i]);
|
|
}
|
|
|
|
cil_printf("))");
|
|
}
|
|
|
|
if (has_positive && has_negative) {
|
|
cil_printf(")");
|
|
}
|
|
|
|
if (flags & TYPE_COMP) {
|
|
cil_printf(")");
|
|
}
|
|
|
|
cil_printf(")\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int ebitmap_to_cil(struct policydb *pdb, struct ebitmap *map, int type)
|
|
{
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
char **val_to_name = pdb->sym_val_to_name[type];
|
|
|
|
ebitmap_for_each_bit(map, node, i) {
|
|
if (!ebitmap_get_bit(map, i)) {
|
|
continue;
|
|
}
|
|
cil_printf("%s ", val_to_name[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ebitmap_to_names(char** vals_to_names, struct ebitmap map, char ***names, uint32_t *num_names)
|
|
{
|
|
int rc = -1;
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
uint32_t num = 0;
|
|
uint32_t max = 8;
|
|
char **name_arr = NULL;
|
|
|
|
name_arr = malloc(sizeof(*name_arr) * max);
|
|
if (name_arr == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
ebitmap_for_each_bit(&map, node, i) {
|
|
if (!ebitmap_get_bit(&map, i)) {
|
|
continue;
|
|
}
|
|
|
|
if (num + 1 == max) {
|
|
max *= 2;
|
|
name_arr = realloc(name_arr, sizeof(*name_arr) * max);
|
|
if (name_arr == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
name_arr[num] = strdup(vals_to_names[i]);
|
|
if (name_arr[num] == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
num++;
|
|
}
|
|
|
|
*names = name_arr;
|
|
*num_names = num;
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
for (i = 0; i < num; i++) {
|
|
free(name_arr[i]);
|
|
}
|
|
free(name_arr);
|
|
return rc;
|
|
}
|
|
|
|
static int cil_add_attr_to_list(struct list *attr_list, char *attribute, int is_type, void *set)
|
|
{
|
|
struct attr_list_node *attr_list_node = NULL;
|
|
int rc = -1;
|
|
|
|
attr_list_node = calloc(1, sizeof(*attr_list_node));
|
|
if (attr_list_node == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
rc = list_prepend(attr_list, attr_list_node);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
attr_list_node->attribute = strdup(attribute);
|
|
if (attr_list_node->attribute == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
attr_list_node->is_type = is_type;
|
|
if (is_type) {
|
|
attr_list_node->set.ts = set;
|
|
} else {
|
|
attr_list_node->set.rs = set;
|
|
}
|
|
|
|
return rc;
|
|
|
|
exit:
|
|
if (attr_list_node != NULL) {
|
|
free(attr_list_node->attribute);
|
|
}
|
|
free(attr_list_node);
|
|
return rc;
|
|
}
|
|
|
|
/* generated_attribute is only set if a new attribute was generated in set_to_cil_attr */
|
|
static int typeset_to_names(struct policydb *pdb, struct type_set *ts, char ***names, uint32_t *num_names, char **generated_attribute)
|
|
{
|
|
int rc = -1;
|
|
if (ebitmap_cardinality(&ts->negset) > 0 || ts->flags != 0) {
|
|
rc = set_to_cil_attr(pdb, 1, names, num_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
*generated_attribute = *names[0];
|
|
} else {
|
|
rc = ebitmap_to_names(pdb->p_type_val_to_name, ts->types, names, num_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
/* generated_attribute is only set if a new attribute was generated in set_to_cil_attr */
|
|
static int roleset_to_names(struct policydb *pdb, struct role_set *rs, char ***names, uint32_t *num_names, char **generated_attribute)
|
|
{
|
|
int rc = -1;
|
|
if (rs->flags != 0) {
|
|
rc = set_to_cil_attr(pdb, 0, names, num_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
*generated_attribute = *names[0];
|
|
} else {
|
|
rc = ebitmap_to_names(pdb->p_role_val_to_name, rs->roles, names, num_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int process_roleset(int indent, struct policydb *pdb, struct role_set *rs, struct list *attr_list, char ***type_names, uint32_t *num_type_names)
|
|
{
|
|
int rc = -1;
|
|
char *generated_attribute = NULL;
|
|
*num_type_names = 0;
|
|
|
|
rc = roleset_to_names(pdb, rs, type_names, num_type_names, &generated_attribute);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
if (generated_attribute == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
if (attr_list == NULL) {
|
|
rc = cil_print_attr_strs(indent, pdb, 0, &rs->roles, NULL, rs->flags, generated_attribute);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
} else {
|
|
rc = cil_add_attr_to_list(attr_list, generated_attribute, 0, rs);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int process_typeset(int indent, struct policydb *pdb, struct type_set *ts, struct list *attr_list, char ***type_names, uint32_t *num_type_names)
|
|
{
|
|
int rc = -1;
|
|
char *generated_attribute = NULL;
|
|
*num_type_names = 0;
|
|
|
|
rc = typeset_to_names(pdb, ts, type_names, num_type_names, &generated_attribute);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
if (generated_attribute == NULL) {
|
|
rc = 0;
|
|
goto exit;
|
|
}
|
|
|
|
if (attr_list == NULL) {
|
|
rc = cil_print_attr_strs(indent, pdb, 1, &ts->types, &ts->negset, ts->flags, generated_attribute);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
} else {
|
|
rc = cil_add_attr_to_list(attr_list, generated_attribute, 1, ts);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static void names_destroy(char ***names, uint32_t *num_names)
|
|
{
|
|
char **arr = *names;
|
|
uint32_t num = *num_names;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
free(arr[i]);
|
|
arr[i] = NULL;
|
|
}
|
|
free(arr);
|
|
|
|
*names = NULL;
|
|
*num_names = 0;
|
|
}
|
|
|
|
static int roletype_role_in_ancestor_to_cil(struct policydb *pdb, struct stack *decl_stack, char *type_name, int indent)
|
|
{
|
|
struct list_node *curr;
|
|
char **tnames = NULL;
|
|
uint32_t num_tnames, i;
|
|
struct role_list_node *role_node = NULL;
|
|
int rc;
|
|
struct type_set *ts;
|
|
|
|
curr = role_list->head;
|
|
for (curr = role_list->head; curr != NULL; curr = curr->next) {
|
|
role_node = curr->data;
|
|
if (!is_id_in_ancestor_scope(pdb, decl_stack, role_node->role_name, SYM_ROLES)) {
|
|
continue;
|
|
}
|
|
|
|
ts = &role_node->role->types;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &tnames, &num_tnames);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
for (i = 0; i < num_tnames; i++) {
|
|
if (!strcmp(type_name, tnames[i])) {
|
|
cil_println(indent, "(roletype %s %s)", role_node->role_name, type_name);
|
|
}
|
|
}
|
|
names_destroy(&tnames, &num_tnames);
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int name_list_to_string(char **names, int num_names, char **string)
|
|
{
|
|
// create a space separated string of the names
|
|
int rc = -1;
|
|
int len = 0;
|
|
int i;
|
|
char *str;
|
|
char *strpos;
|
|
int name_len;
|
|
int rlen;
|
|
|
|
for (i = 0; i < num_names; i++) {
|
|
len += strlen(names[i]);
|
|
}
|
|
|
|
// add spaces + null terminator
|
|
len += (num_names - 1) + 1;
|
|
|
|
str = malloc(len);
|
|
if (str == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
strpos = str;
|
|
|
|
for (i = 0; i < num_names; i++) {
|
|
name_len = strlen(names[i]);
|
|
rlen = snprintf(strpos, len - (strpos - str), "%s", names[i]);
|
|
if (rlen < 0 || rlen >= len) {
|
|
log_err("Failed to generate name list");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
if (i < num_names - 1) {
|
|
strpos[name_len] = ' ';
|
|
}
|
|
strpos += name_len + 1;
|
|
}
|
|
|
|
*string = str;
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int avrule_list_to_cil(int indent, struct policydb *pdb, struct avrule *avrule_list, struct list *attr_list)
|
|
{
|
|
int rc = -1;
|
|
struct avrule *avrule;
|
|
char **snames = NULL;
|
|
char **tnames = NULL;
|
|
uint32_t num_snames;
|
|
uint32_t num_tnames;
|
|
uint32_t s;
|
|
uint32_t t;
|
|
struct type_set *ts;
|
|
|
|
for (avrule = avrule_list; avrule != NULL; avrule = avrule->next) {
|
|
ts = &avrule->stypes;
|
|
rc = process_typeset(indent, pdb, ts, attr_list, &snames, &num_snames);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
ts = &avrule->ttypes;
|
|
rc = process_typeset(indent, pdb, ts, attr_list, &tnames, &num_tnames);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (s = 0; s < num_snames; s++) {
|
|
for (t = 0; t < num_tnames; t++) {
|
|
rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], tnames[t], avrule->perms);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (avrule->flags & RULE_SELF) {
|
|
rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], "self", avrule->perms);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
names_destroy(&snames, &num_snames);
|
|
names_destroy(&tnames, &num_tnames);
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
names_destroy(&snames, &num_snames);
|
|
names_destroy(&tnames, &num_tnames);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int cond_expr_to_cil(int indent, struct policydb *pdb, struct cond_expr *cond_expr, uint32_t flags)
|
|
{
|
|
int rc = -1;
|
|
struct cond_expr *curr;
|
|
struct stack *stack = NULL;
|
|
int len = 0;
|
|
int rlen;
|
|
char *new_val = NULL;
|
|
char *val1 = NULL;
|
|
char *val2 = NULL;
|
|
int num_params;
|
|
const char *op;
|
|
const char *fmt_str;
|
|
const char *type;
|
|
|
|
rc = stack_init(&stack);
|
|
if (rc != 0) {
|
|
log_err("Out of memory");
|
|
goto exit;
|
|
}
|
|
|
|
for (curr = cond_expr; curr != NULL; curr = curr->next) {
|
|
if (curr->expr_type == COND_BOOL) {
|
|
val1 = pdb->p_bool_val_to_name[curr->bool - 1];
|
|
// length of boolean + 2 parens + null terminator
|
|
len = strlen(val1) + 2 + 1;
|
|
new_val = malloc(len);
|
|
if (new_val == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
rlen = snprintf(new_val, len, "(%s)", val1);
|
|
if (rlen < 0 || rlen >= len) {
|
|
log_err("Failed to generate conditional expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
num_params = 0;
|
|
} else {
|
|
switch(curr->expr_type) {
|
|
case COND_NOT: op = "not"; break;
|
|
case COND_OR: op = "or"; break;
|
|
case COND_AND: op = "and"; break;
|
|
case COND_XOR: op = "xor"; break;
|
|
case COND_EQ: op = "eq"; break;
|
|
case COND_NEQ: op = "neq"; break;
|
|
default:
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
num_params = curr->expr_type == COND_NOT ? 1 : 2;
|
|
|
|
if (num_params == 1) {
|
|
val1 = stack_pop(stack);
|
|
val2 = strdup("");
|
|
if (val2 == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
fmt_str = "(%s %s)";
|
|
} else {
|
|
val2 = stack_pop(stack);
|
|
val1 = stack_pop(stack);
|
|
fmt_str = "(%s %s %s)";
|
|
}
|
|
|
|
if (val1 == NULL || val2 == NULL) {
|
|
log_err("Invalid conditional expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
// length = length of parameters +
|
|
// length of operator +
|
|
// 1 space preceeding each parameter +
|
|
// 2 parens around the whole expression
|
|
// + null terminator
|
|
len = strlen(val1) + strlen(val2) + strlen(op) + (num_params * 1) + 2 + 1;
|
|
new_val = malloc(len);
|
|
if (new_val == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
// although we always supply val2 and there isn't always a 2nd
|
|
// value, it should only be used when there are actually two values
|
|
// in the format strings
|
|
rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
|
|
if (rlen < 0 || rlen >= len) {
|
|
log_err("Failed to generate conditional expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
free(val1);
|
|
free(val2);
|
|
val1 = NULL;
|
|
val2 = NULL;
|
|
}
|
|
|
|
rc = stack_push(stack, new_val);
|
|
if (rc != 0) {
|
|
log_err("Out of memory");
|
|
goto exit;
|
|
}
|
|
new_val = NULL;
|
|
}
|
|
|
|
if (flags & COND_NODE_FLAGS_TUNABLE) {
|
|
type = "tunableif";
|
|
} else {
|
|
type = "booleanif";
|
|
}
|
|
|
|
val1 = stack_pop(stack);
|
|
if (val1 == NULL || stack_peek(stack) != NULL) {
|
|
log_err("Invalid conditional expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_println(indent, "(%s %s", type, val1);
|
|
free(val1);
|
|
val1 = NULL;
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
free(new_val);
|
|
free(val1);
|
|
free(val2);
|
|
while ((val1 = stack_pop(stack)) != NULL) {
|
|
free(val1);
|
|
}
|
|
stack_destroy(&stack);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int cil_print_attr_list(int indent, struct policydb *pdb, struct list *attr_list)
|
|
{
|
|
struct list_node *curr;
|
|
struct attr_list_node *attr_list_node;
|
|
int rc = 0;
|
|
struct type_set *ts;
|
|
struct role_set *rs;
|
|
char *generated_attribute;
|
|
|
|
for (curr = attr_list->head; curr != NULL; curr = curr->next) {
|
|
attr_list_node = curr->data;
|
|
generated_attribute = attr_list_node->attribute;
|
|
if (generated_attribute == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (attr_list_node->is_type) {
|
|
ts = attr_list_node->set.ts;
|
|
rc = cil_print_attr_strs(indent, pdb, 1, &ts->types, &ts->negset, ts->flags, generated_attribute);
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
} else {
|
|
rs = attr_list_node->set.rs;
|
|
rc = cil_print_attr_strs(indent, pdb, 0, &rs->roles, NULL, rs->flags, generated_attribute);
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int cond_list_to_cil(int indent, struct policydb *pdb, struct cond_node *cond_list)
|
|
{
|
|
int rc = -1;
|
|
struct cond_node *cond;
|
|
struct list *attr_list;
|
|
|
|
rc = list_init(&attr_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (cond = cond_list; cond != NULL; cond = cond->next) {
|
|
|
|
rc = cond_expr_to_cil(indent, pdb, cond->expr, cond->flags);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
if (cond->avtrue_list != NULL) {
|
|
cil_println(indent + 1, "(true");
|
|
rc = avrule_list_to_cil(indent + 2, pdb, cond->avtrue_list, attr_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
cil_println(indent + 1, ")");
|
|
}
|
|
|
|
if (cond->avfalse_list != NULL) {
|
|
cil_println(indent + 1, "(false");
|
|
rc = avrule_list_to_cil(indent + 2, pdb, cond->avfalse_list, attr_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
cil_println(indent + 1, ")");
|
|
}
|
|
|
|
cil_println(indent, ")");
|
|
}
|
|
|
|
rc = cil_print_attr_list(indent, pdb, attr_list);
|
|
|
|
exit:
|
|
attr_list_destroy(&attr_list);
|
|
return rc;
|
|
}
|
|
|
|
static int role_trans_to_cil(int indent, struct policydb *pdb, struct role_trans_rule *rules)
|
|
{
|
|
int rc = -1;
|
|
struct role_trans_rule *rule;
|
|
char **role_names = NULL;
|
|
uint32_t num_role_names = 0;
|
|
char **type_names = NULL;
|
|
uint32_t num_type_names = 0;
|
|
uint32_t type;
|
|
uint32_t role;
|
|
uint32_t i;
|
|
struct ebitmap_node *node;
|
|
struct type_set *ts;
|
|
struct role_set *rs;
|
|
|
|
|
|
for (rule = rules; rule != NULL; rule = rule->next) {
|
|
rs = &rule->roles;
|
|
rc = process_roleset(indent, pdb, rs, NULL, &role_names, &num_role_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
ts = &rule->types;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &type_names, &num_type_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (role = 0; role < num_role_names; role++) {
|
|
for (type = 0; type < num_type_names; type++) {
|
|
ebitmap_for_each_bit(&rule->classes, node, i) {
|
|
if (!ebitmap_get_bit(&rule->classes, i)) {
|
|
continue;
|
|
}
|
|
cil_println(indent, "(roletransition %s %s %s %s)", role_names[role],
|
|
type_names[type],
|
|
pdb->p_class_val_to_name[i],
|
|
pdb->p_role_val_to_name[rule->new_role - 1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
names_destroy(&role_names, &num_role_names);
|
|
names_destroy(&type_names, &num_type_names);
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
names_destroy(&role_names, &num_role_names);
|
|
names_destroy(&type_names, &num_type_names);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int role_allows_to_cil(int indent, struct policydb *pdb, struct role_allow_rule *rules)
|
|
{
|
|
int rc = -1;
|
|
struct role_allow_rule *rule;
|
|
char **roles = NULL;
|
|
uint32_t num_roles = 0;
|
|
char **new_roles = NULL;
|
|
uint32_t num_new_roles = 0;
|
|
uint32_t i;
|
|
uint32_t j;
|
|
struct role_set *rs;
|
|
|
|
for (rule = rules; rule != NULL; rule = rule->next) {
|
|
rs = &rule->roles;
|
|
rc = process_roleset(indent, pdb, rs, NULL, &roles, &num_roles);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rs = &rule->new_roles;
|
|
rc = process_roleset(indent, pdb, rs, NULL, &new_roles, &num_new_roles);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (i = 0; i < num_roles; i++) {
|
|
for (j = 0; j < num_new_roles; j++) {
|
|
cil_println(indent, "(roleallow %s %s)", roles[i], new_roles[j]);
|
|
}
|
|
}
|
|
|
|
names_destroy(&roles, &num_roles);
|
|
names_destroy(&new_roles, &num_new_roles);
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
names_destroy(&roles, &num_roles);
|
|
names_destroy(&new_roles, &num_new_roles);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int range_trans_to_cil(int indent, struct policydb *pdb, struct range_trans_rule *rules)
|
|
{
|
|
int rc = -1;
|
|
struct range_trans_rule *rule;
|
|
char **stypes = NULL;
|
|
uint32_t num_stypes = 0;
|
|
char **ttypes = NULL;
|
|
uint32_t num_ttypes = 0;
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
uint32_t stype;
|
|
uint32_t ttype;
|
|
struct type_set *ts;
|
|
|
|
if (!pdb->mls) {
|
|
return 0;
|
|
}
|
|
|
|
for (rule = rules; rule != NULL; rule = rule->next) {
|
|
ts = &rule->stypes;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &stypes, &num_stypes);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
ts = &rule->ttypes;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &ttypes, &num_ttypes);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (stype = 0; stype < num_stypes; stype++) {
|
|
for (ttype = 0; ttype < num_ttypes; ttype++) {
|
|
ebitmap_for_each_bit(&rule->tclasses, node, i) {
|
|
if (!ebitmap_get_bit(&rule->tclasses, i)) {
|
|
continue;
|
|
}
|
|
|
|
cil_indent(indent);
|
|
cil_printf("(rangetransition %s %s %s ", stypes[stype], ttypes[ttype], pdb->p_class_val_to_name[i]);
|
|
|
|
cil_printf("(");
|
|
|
|
rc = semantic_level_to_cil(pdb, 1, &rule->trange.level[0]);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf(" ");
|
|
|
|
rc = semantic_level_to_cil(pdb, 1, &rule->trange.level[1]);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf("))\n");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
names_destroy(&stypes, &num_stypes);
|
|
names_destroy(&ttypes, &num_ttypes);
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
names_destroy(&stypes, &num_stypes);
|
|
names_destroy(&ttypes, &num_ttypes);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filename_trans_rule *rules)
|
|
{
|
|
int rc = -1;
|
|
char **stypes = NULL;
|
|
uint32_t num_stypes = 0;
|
|
char **ttypes = NULL;
|
|
uint32_t num_ttypes = 0;
|
|
uint32_t stype;
|
|
uint32_t ttype;
|
|
struct type_set *ts;
|
|
|
|
struct filename_trans_rule *rule;
|
|
|
|
for (rule = rules; rule != NULL; rule = rule->next) {
|
|
ts = &rule->stypes;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &stypes, &num_stypes);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
ts = &rule->ttypes;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &ttypes, &num_ttypes);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (stype = 0; stype < num_stypes; stype++) {
|
|
for (ttype = 0; ttype < num_ttypes; ttype++) {
|
|
cil_println(indent, "(typetransition %s %s %s \"%s\" %s)", stypes[stype],
|
|
ttypes[ttype],
|
|
pdb->p_class_val_to_name[rule->tclass - 1],
|
|
rule->name,
|
|
pdb->p_type_val_to_name[rule->otype - 1]);
|
|
}
|
|
}
|
|
|
|
names_destroy(&stypes, &num_stypes);
|
|
names_destroy(&ttypes, &num_ttypes);
|
|
}
|
|
|
|
rc = 0;
|
|
exit:
|
|
names_destroy(&stypes, &num_stypes);
|
|
names_destroy(&ttypes, &num_ttypes);
|
|
|
|
return rc;
|
|
}
|
|
|
|
struct class_perm_datum {
|
|
char *name;
|
|
uint32_t val;
|
|
};
|
|
|
|
struct class_perm_array {
|
|
struct class_perm_datum *perms;
|
|
uint32_t count;
|
|
};
|
|
|
|
static int class_perm_to_array(char *key, void *data, void *args)
|
|
{
|
|
struct class_perm_array *arr = args;
|
|
struct perm_datum *datum = data;
|
|
arr->perms[arr->count].name = key;
|
|
arr->perms[arr->count].val = datum->s.value;
|
|
arr->count++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int class_perm_cmp(const void *a, const void *b)
|
|
{
|
|
const struct class_perm_datum *aa = a;
|
|
const struct class_perm_datum *bb = b;
|
|
|
|
return aa->val - bb->val;
|
|
}
|
|
|
|
static int common_to_cil(char *key, void *data, void *UNUSED(arg))
|
|
{
|
|
int rc = -1;
|
|
struct common_datum *common = data;
|
|
struct class_perm_array arr;
|
|
uint32_t i;
|
|
|
|
arr.count = 0;
|
|
arr.perms = calloc(common->permissions.nprim, sizeof(*arr.perms));
|
|
rc = hashtab_map(common->permissions.table, class_perm_to_array, &arr);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
qsort(arr.perms, arr.count, sizeof(*arr.perms), class_perm_cmp);
|
|
|
|
cil_printf("(common %s (", key);
|
|
for (i = 0; i < arr.count; i++) {
|
|
cil_printf("%s ", arr.perms[i].name);
|
|
}
|
|
cil_printf("))\n");
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
free(arr.perms);
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int constraint_expr_to_string(int indent, struct policydb *pdb, struct constraint_expr *exprs, char **expr_string)
|
|
{
|
|
int rc = -1;
|
|
struct constraint_expr *expr;
|
|
struct stack *stack = NULL;
|
|
int len = 0;
|
|
int rlen;
|
|
char *new_val = NULL;
|
|
char *val1 = NULL;
|
|
char *val2 = NULL;
|
|
uint32_t num_params;
|
|
const char *op;
|
|
const char *fmt_str;
|
|
const char *attr1;
|
|
const char *attr2;
|
|
char *names;
|
|
char **name_list = NULL;
|
|
uint32_t num_names = 0;
|
|
struct type_set *ts;
|
|
|
|
rc = stack_init(&stack);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (expr = exprs; expr != NULL; expr = expr->next) {
|
|
if (expr->expr_type == CEXPR_ATTR || expr->expr_type == CEXPR_NAMES) {
|
|
switch (expr->op) {
|
|
case CEXPR_EQ: op = "eq"; break;
|
|
case CEXPR_NEQ: op = "neq"; break;
|
|
case CEXPR_DOM: op = "dom"; break;
|
|
case CEXPR_DOMBY: op = "domby"; break;
|
|
case CEXPR_INCOMP: op = "incomp"; break;
|
|
default:
|
|
log_err("Unknown constraint operator type: %i", expr->op);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
switch (expr->attr) {
|
|
case CEXPR_USER: attr1 = "u1"; attr2 = "u2"; break;
|
|
case CEXPR_USER | CEXPR_TARGET: attr1 = "u2"; attr2 = ""; break;
|
|
case CEXPR_USER | CEXPR_XTARGET: attr1 = "u3"; attr2 = ""; break;
|
|
case CEXPR_ROLE: attr1 = "r1"; attr2 = "r2"; break;
|
|
case CEXPR_ROLE | CEXPR_TARGET: attr1 = "r2"; attr2 = ""; break;
|
|
case CEXPR_ROLE | CEXPR_XTARGET: attr1 = "r3"; attr2 = ""; break;
|
|
case CEXPR_TYPE: attr1 = "t1"; attr2 = ""; break;
|
|
case CEXPR_TYPE | CEXPR_TARGET: attr1 = "t2"; attr2 = ""; break;
|
|
case CEXPR_TYPE | CEXPR_XTARGET: attr1 = "t3"; attr2 = ""; break;
|
|
case CEXPR_L1L2: attr1 = "l1"; attr2 = "l2"; break;
|
|
case CEXPR_L1H2: attr1 = "l1"; attr2 = "h2"; break;
|
|
case CEXPR_H1L2: attr1 = "h1"; attr2 = "l2"; break;
|
|
case CEXPR_H1H2: attr1 = "h1"; attr2 = "h2"; break;
|
|
case CEXPR_L1H1: attr1 = "l1"; attr2 = "h1"; break;
|
|
case CEXPR_L2H2: attr1 = "l2"; attr2 = "h2"; break;
|
|
default:
|
|
log_err("Unknown expression attribute type: %i", expr->attr);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
if (expr->expr_type == CEXPR_ATTR) {
|
|
// length of values/attrs + 2 separating spaces + 2 parens + null terminator
|
|
len = strlen(op) + strlen(attr1) + strlen(attr2) + 2 + 2 + 1;
|
|
new_val = malloc(len);
|
|
if (new_val == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
rlen = snprintf(new_val, len, "(%s %s %s)", op, attr1, attr2);
|
|
if (rlen < 0 || rlen >= len) {
|
|
log_err("Failed to generate constraint expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
} else {
|
|
if (expr->attr & CEXPR_TYPE) {
|
|
ts = expr->type_names;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &name_list, &num_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
} else if (expr->attr & CEXPR_USER) {
|
|
rc = ebitmap_to_names(pdb->p_user_val_to_name, expr->names, &name_list, &num_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
} else if (expr->attr & CEXPR_ROLE) {
|
|
rc = ebitmap_to_names(pdb->p_role_val_to_name, expr->names, &name_list, &num_names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
rc = name_list_to_string(name_list, num_names, &names);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
// length of values/oper + 2 spaces + 2 parens + null terminator
|
|
len = strlen(op) + strlen(attr1) + strlen(names) + 2 + 2 + 1;
|
|
new_val = malloc(len);
|
|
if (new_val == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
rlen = snprintf(new_val, len, "(%s %s %s)", op, attr1, names);
|
|
if (rlen < 0 || rlen >= len) {
|
|
log_err("Failed to generate constraint expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
names_destroy(&name_list, &num_names);
|
|
free(names);
|
|
}
|
|
|
|
num_params = 0;
|
|
} else {
|
|
switch (expr->expr_type) {
|
|
case CEXPR_NOT: op = "not"; break;
|
|
case CEXPR_AND: op = "and"; break;
|
|
case CEXPR_OR: op = "or"; break;
|
|
default:
|
|
log_err("Unknown constraint expression type: %i", expr->expr_type);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
num_params = expr->expr_type == CEXPR_NOT ? 1 : 2;
|
|
|
|
if (num_params == 1) {
|
|
val1 = stack_pop(stack);
|
|
val2 = strdup("");
|
|
if (val2 == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
fmt_str = "(%s %s)";
|
|
} else {
|
|
val2 = stack_pop(stack);
|
|
val1 = stack_pop(stack);
|
|
fmt_str = "(%s %s %s)";
|
|
}
|
|
|
|
if (val1 == NULL || val2 == NULL) {
|
|
log_err("Invalid constraint expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
// length = length of parameters +
|
|
// length of operator +
|
|
// 1 space preceeding each parameter +
|
|
// 2 parens around the whole expression
|
|
// + null terminator
|
|
len = strlen(val1) + strlen(val2) + strlen(op) + (num_params * 1) + 2 + 1;
|
|
new_val = malloc(len);
|
|
if (new_val == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
// although we always supply val2 and there isn't always a 2nd
|
|
// value, it should only be used when there are actually two values
|
|
// in the format strings
|
|
rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
|
|
if (rlen < 0 || rlen >= len) {
|
|
log_err("Failed to generate constraint expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
free(val1);
|
|
free(val2);
|
|
val1 = NULL;
|
|
val2 = NULL;
|
|
}
|
|
|
|
rc = stack_push(stack, new_val);
|
|
if (rc != 0) {
|
|
log_err("Out of memory");
|
|
goto exit;
|
|
}
|
|
|
|
new_val = NULL;
|
|
}
|
|
|
|
new_val = stack_pop(stack);
|
|
if (new_val == NULL || stack_peek(stack) != NULL) {
|
|
log_err("Invalid constraint expression");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
*expr_string = new_val;
|
|
new_val = NULL;
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
names_destroy(&name_list, &num_names);
|
|
|
|
free(new_val);
|
|
free(val1);
|
|
free(val2);
|
|
while ((val1 = stack_pop(stack)) != NULL) {
|
|
free(val1);
|
|
}
|
|
stack_destroy(&stack);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int constraints_to_cil(int indent, struct policydb *pdb, char *classkey, struct class_datum *class, struct constraint_node *constraints, int is_constraint)
|
|
{
|
|
int rc = -1;
|
|
struct constraint_node *node;
|
|
char *expr = NULL;
|
|
const char *mls;
|
|
char *perms;
|
|
|
|
mls = pdb->mls ? "mls" : "";
|
|
|
|
for (node = constraints; node != NULL; node = node->next) {
|
|
|
|
rc = constraint_expr_to_string(indent, pdb, node->expr, &expr);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
if (is_constraint) {
|
|
perms = sepol_av_to_string(pdb, class->s.value, node->permissions);
|
|
cil_println(indent, "(%sconstrain (%s (%s)) %s)", mls, classkey, perms + 1, expr);
|
|
} else {
|
|
cil_println(indent, "(%svalidatetrans %s %s)", mls, classkey, expr);
|
|
}
|
|
|
|
free(expr);
|
|
expr = NULL;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
free(expr);
|
|
return rc;
|
|
}
|
|
|
|
static int class_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
|
|
{
|
|
int rc = -1;
|
|
struct class_datum *class = datum;
|
|
const char *dflt;
|
|
struct class_perm_array arr;
|
|
uint32_t i;
|
|
|
|
if (scope == SCOPE_REQ) {
|
|
return 0;
|
|
}
|
|
|
|
arr.count = 0;
|
|
arr.perms = calloc(class->permissions.nprim, sizeof(*arr.perms));
|
|
rc = hashtab_map(class->permissions.table, class_perm_to_array, &arr);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
qsort(arr.perms, arr.count, sizeof(*arr.perms), class_perm_cmp);
|
|
|
|
cil_indent(indent);
|
|
cil_printf("(class %s (", key);
|
|
for (i = 0; i < arr.count; i++) {
|
|
cil_printf("%s ", arr.perms[i].name);
|
|
}
|
|
cil_printf("))\n");
|
|
|
|
if (class->comkey != NULL) {
|
|
cil_println(indent, "(classcommon %s %s)", key, class->comkey);
|
|
}
|
|
|
|
if (class->default_user != 0) {
|
|
switch (class->default_user) {
|
|
case DEFAULT_SOURCE: dflt = "source"; break;
|
|
case DEFAULT_TARGET: dflt = "target"; break;
|
|
default:
|
|
log_err("Unknown default user value: %i", class->default_user);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
cil_println(indent, "(defaultuser %s %s)", key, dflt);
|
|
}
|
|
|
|
if (class->default_role != 0) {
|
|
switch (class->default_role) {
|
|
case DEFAULT_SOURCE: dflt = "source"; break;
|
|
case DEFAULT_TARGET: dflt = "target"; break;
|
|
default:
|
|
log_err("Unknown default role value: %i", class->default_role);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
cil_println(indent, "(defaultrole %s %s)", key, dflt);
|
|
}
|
|
|
|
if (class->default_type != 0) {
|
|
switch (class->default_type) {
|
|
case DEFAULT_SOURCE: dflt = "source"; break;
|
|
case DEFAULT_TARGET: dflt = "target"; break;
|
|
default:
|
|
log_err("Unknown default type value: %i", class->default_type);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
cil_println(indent, "(defaulttype %s %s)", key, dflt);
|
|
}
|
|
|
|
if (class->default_range != 0) {
|
|
switch (class->default_range) {
|
|
case DEFAULT_SOURCE_LOW: dflt = "source low"; break;
|
|
case DEFAULT_SOURCE_HIGH: dflt = "source high"; break;
|
|
case DEFAULT_SOURCE_LOW_HIGH: dflt = "source low-high"; break;
|
|
case DEFAULT_TARGET_LOW: dflt = "target low"; break;
|
|
case DEFAULT_TARGET_HIGH: dflt = "target high"; break;
|
|
case DEFAULT_TARGET_LOW_HIGH: dflt = "target low-high"; break;
|
|
default:
|
|
log_err("Unknown default range value: %i", class->default_range);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
cil_println(indent, "(defaultrange %s %s)", key, dflt);
|
|
|
|
}
|
|
|
|
if (class->constraints != NULL) {
|
|
rc = constraints_to_cil(indent, pdb, key, class, class->constraints, 1);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (class->validatetrans != NULL) {
|
|
rc = constraints_to_cil(indent, pdb, key, class, class->validatetrans, 0);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
free(arr.perms);
|
|
return rc;
|
|
}
|
|
|
|
static int class_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
|
|
{
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
|
|
if (ebitmap_cardinality(&order) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
cil_indent(indent);
|
|
cil_printf("(classorder (");
|
|
|
|
ebitmap_for_each_bit(&order, node, i) {
|
|
if (!ebitmap_get_bit(&order, i)) {
|
|
continue;
|
|
}
|
|
cil_printf("%s ", pdb->sym_val_to_name[SYM_CLASSES][i]);
|
|
}
|
|
|
|
cil_printf("))\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int role_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack, char *key, void *datum, int scope)
|
|
{
|
|
int rc = -1;
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
char **types = NULL;
|
|
uint32_t num_types = 0;
|
|
struct role_datum *role = datum;
|
|
struct type_set *ts;
|
|
|
|
if (scope == SCOPE_REQ) {
|
|
// if a role/roleattr is in the REQ scope, then it could cause an
|
|
// optional block to fail, even if it is never used. However in CIL,
|
|
// symbols must be used in order to cause an optional block to fail. So
|
|
// for symbols in the REQ scope, add them to a roleattribute as a way
|
|
// to 'use' them in the optional without affecting the resulting policy.
|
|
cil_println(indent, "(roleattributeset " GEN_REQUIRE_ATTR " %s)", key);
|
|
}
|
|
|
|
switch (role->flavor) {
|
|
case ROLE_ROLE:
|
|
if (scope == SCOPE_DECL) {
|
|
// Only declare certain roles if we are reading a base module.
|
|
// These roles are defined in the base module and sometimes in
|
|
// other non-base modules. If we generated the roles regardless of
|
|
// the policy type, it would result in duplicate declarations,
|
|
// which isn't allowed in CIL. Patches have been made to refpolicy
|
|
// to remove these duplicate role declarations, but we need to be
|
|
// backwards compatable and support older policies. Since we know
|
|
// these roles are always declared in base, only print them when we
|
|
// see them in the base module. If the declarations appear in a
|
|
// non-base module, ignore their declarations.
|
|
//
|
|
// Note that this is a hack, and if a policy author does not define
|
|
// one of these roles in base, the declaration will not appeaer in
|
|
// the resulting policy, likely resulting in a compilation error in
|
|
// CIL.
|
|
int is_base_role = (!strcmp(key, "user_r") ||
|
|
!strcmp(key, "staff_r") ||
|
|
!strcmp(key, "sysadm_r") ||
|
|
!strcmp(key, "system_r") ||
|
|
!strcmp(key, "unconfined_r"));
|
|
if ((is_base_role && pdb->policy_type == SEPOL_POLICY_BASE) || !is_base_role) {
|
|
cil_println(indent, "(role %s)", key);
|
|
}
|
|
}
|
|
|
|
if (ebitmap_cardinality(&role->dominates) > 1) {
|
|
log_err("Warning: role 'dominance' statement unsupported in CIL. Dropping from output.");
|
|
}
|
|
|
|
ts = &role->types;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &types, &num_types);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (i = 0; i < num_types; i++) {
|
|
if (is_id_in_scope(pdb, decl_stack, types[i], SYM_TYPES)) {
|
|
cil_println(indent, "(roletype %s %s)", key, types[i]);
|
|
}
|
|
}
|
|
|
|
if (role->bounds > 0) {
|
|
cil_println(indent, "(rolebounds %s %s)", key, pdb->p_role_val_to_name[role->bounds - 1]);
|
|
}
|
|
break;
|
|
|
|
case ROLE_ATTRIB:
|
|
if (scope == SCOPE_DECL) {
|
|
cil_println(indent, "(roleattribute %s)", key);
|
|
}
|
|
|
|
if (ebitmap_cardinality(&role->roles) > 0) {
|
|
cil_indent(indent);
|
|
cil_printf("(roleattributeset %s (", key);
|
|
ebitmap_for_each_bit(&role->roles, node, i) {
|
|
if (!ebitmap_get_bit(&role->roles, i)) {
|
|
continue;
|
|
}
|
|
cil_printf("%s ", pdb->p_role_val_to_name[i]);
|
|
}
|
|
cil_printf("))\n");
|
|
}
|
|
|
|
ts = &role->types;
|
|
rc = process_typeset(indent, pdb, ts, NULL, &types, &num_types);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
|
|
for (i = 0; i < num_types; i++) {
|
|
cil_println(indent, "(roletype %s %s)", key, types[i]);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
log_err("Unknown role type: %i", role->flavor);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
rc = 0;
|
|
exit:
|
|
names_destroy(&types, &num_types);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int type_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack, char *key, void *datum, int scope)
|
|
{
|
|
int rc = -1;
|
|
struct type_datum *type = datum;
|
|
|
|
if (scope == SCOPE_REQ) {
|
|
// if a type/typeattr is in the REQ scope, then it could cause an
|
|
// optional block to fail, even if it is never used. However in CIL,
|
|
// symbols must be used in order to cause an optional block to fail. So
|
|
// for symbols in the REQ scope, add them to a typeattribute as a way
|
|
// to 'use' them in the optional without affecting the resulting policy.
|
|
cil_println(indent, "(typeattributeset " GEN_REQUIRE_ATTR " %s)", key);
|
|
}
|
|
|
|
rc = roletype_role_in_ancestor_to_cil(pdb, decl_stack, key, indent);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
switch(type->flavor) {
|
|
case TYPE_TYPE:
|
|
if (scope == SCOPE_DECL) {
|
|
cil_println(indent, "(type %s)", key);
|
|
// object_r is implicit in checkmodule, but not with CIL,
|
|
// create it as part of base
|
|
cil_println(indent, "(roletype " DEFAULT_OBJECT " %s)", key);
|
|
}
|
|
|
|
if (type->flags & TYPE_FLAGS_PERMISSIVE) {
|
|
cil_println(indent, "(typepermissive %s)", key);
|
|
}
|
|
|
|
if (type->bounds > 0) {
|
|
cil_println(indent, "(typebounds %s %s)", pdb->p_type_val_to_name[type->bounds - 1], key);
|
|
}
|
|
break;
|
|
case TYPE_ATTRIB:
|
|
if (scope == SCOPE_DECL) {
|
|
cil_println(indent, "(typeattribute %s)", key);
|
|
}
|
|
|
|
if (ebitmap_cardinality(&type->types) > 0) {
|
|
cil_indent(indent);
|
|
cil_printf("(typeattributeset %s (", key);
|
|
ebitmap_to_cil(pdb, &type->types, SYM_TYPES);
|
|
cil_printf("))\n");
|
|
}
|
|
break;
|
|
default:
|
|
log_err("Unknown flavor (%i) of type %s", type->flavor, key);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int user_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
|
|
{
|
|
struct user_datum *user = datum;
|
|
struct ebitmap roles = user->roles.roles;
|
|
struct mls_semantic_level level = user->dfltlevel;
|
|
struct mls_semantic_range range = user->range;
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
int sens_offset = 1;
|
|
|
|
if (scope == SCOPE_DECL) {
|
|
cil_println(indent, "(user %s)", key);
|
|
// object_r is implicit in checkmodule, but not with CIL, create it
|
|
// as part of base
|
|
cil_println(indent, "(userrole %s " DEFAULT_OBJECT ")", key);
|
|
}
|
|
|
|
ebitmap_for_each_bit(&roles, node, i) {
|
|
if (!ebitmap_get_bit(&roles, i)) {
|
|
continue;
|
|
}
|
|
cil_println(indent, "(userrole %s %s)", key, pdb->p_role_val_to_name[i]);
|
|
}
|
|
|
|
if (block->flags & AVRULE_OPTIONAL) {
|
|
// sensitivites in user statements in optionals do not have the
|
|
// standard -1 offest
|
|
sens_offset = 0;
|
|
}
|
|
|
|
cil_indent(indent);
|
|
cil_printf("(userlevel %s ", key);
|
|
if (pdb->mls) {
|
|
semantic_level_to_cil(pdb, sens_offset, &level);
|
|
} else {
|
|
cil_printf(DEFAULT_LEVEL);
|
|
}
|
|
cil_printf(")\n");
|
|
|
|
cil_indent(indent);
|
|
cil_printf("(userrange %s (", key);
|
|
if (pdb->mls) {
|
|
semantic_level_to_cil(pdb, sens_offset, &range.level[0]);
|
|
cil_printf(" ");
|
|
semantic_level_to_cil(pdb, sens_offset, &range.level[1]);
|
|
} else {
|
|
cil_printf(DEFAULT_LEVEL " " DEFAULT_LEVEL);
|
|
}
|
|
cil_printf("))\n");
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int boolean_to_cil(int indent, struct policydb *UNUSED(pdb), struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
|
|
{
|
|
struct cond_bool_datum *boolean = datum;
|
|
const char *type;
|
|
|
|
if (scope == SCOPE_DECL) {
|
|
if (boolean->flags & COND_BOOL_FLAGS_TUNABLE) {
|
|
type = "tunable";
|
|
} else {
|
|
type = "boolean";
|
|
}
|
|
|
|
cil_println(indent, "(%s %s %s)", type, key, boolean->state ? "true" : "false");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sens_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
|
|
{
|
|
struct level_datum *level = datum;
|
|
|
|
if (scope == SCOPE_DECL) {
|
|
if (!level->isalias) {
|
|
cil_println(indent, "(sensitivity %s)", key);
|
|
} else {
|
|
cil_println(indent, "(sensitivityalias %s)", key);
|
|
cil_println(indent, "(sensitivityaliasactual %s %s)", key, pdb->p_sens_val_to_name[level->level->sens - 1]);
|
|
}
|
|
}
|
|
|
|
if (ebitmap_cardinality(&level->level->cat) > 0) {
|
|
cil_indent(indent);
|
|
cil_printf("(sensitivitycategory %s (", key);
|
|
ebitmap_to_cil(pdb, &level->level->cat, SYM_CATS);
|
|
cil_printf("))\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sens_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
|
|
{
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
|
|
if (ebitmap_cardinality(&order) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
cil_indent(indent);
|
|
cil_printf("(sensitivityorder (");
|
|
|
|
ebitmap_for_each_bit(&order, node, i) {
|
|
if (!ebitmap_get_bit(&order, i)) {
|
|
continue;
|
|
}
|
|
cil_printf("%s ", pdb->p_sens_val_to_name[i]);
|
|
}
|
|
|
|
cil_printf("))\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cat_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
|
|
{
|
|
struct cat_datum *cat = datum;
|
|
|
|
if (scope == SCOPE_REQ) {
|
|
return 0;
|
|
}
|
|
|
|
if (!cat->isalias) {
|
|
cil_println(indent, "(category %s)", key);
|
|
} else {
|
|
cil_println(indent, "(categoryalias %s)", key);
|
|
cil_println(indent, "(categoryaliasactual %s %s)", key, pdb->p_cat_val_to_name[cat->s.value - 1]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cat_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
|
|
{
|
|
int rc = -1;
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
|
|
if (ebitmap_cardinality(&order) == 0) {
|
|
rc = 0;
|
|
goto exit;
|
|
}
|
|
|
|
cil_indent(indent);
|
|
cil_printf("(categoryorder (");
|
|
|
|
ebitmap_for_each_bit(&order, node, i) {
|
|
if (!ebitmap_get_bit(&order, i)) {
|
|
continue;
|
|
}
|
|
cil_printf("%s ", pdb->p_cat_val_to_name[i]);
|
|
}
|
|
|
|
cil_printf("))\n");
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int polcaps_to_cil(struct policydb *pdb)
|
|
{
|
|
int rc = -1;
|
|
struct ebitmap *map;
|
|
struct ebitmap_node *node;
|
|
uint32_t i;
|
|
const char *name;
|
|
|
|
map = &pdb->policycaps;
|
|
|
|
ebitmap_for_each_bit(map, node, i) {
|
|
if (!ebitmap_get_bit(map, i)) {
|
|
continue;
|
|
}
|
|
name = sepol_polcap_getname(i);
|
|
if (name == NULL) {
|
|
log_err("Unknown policy capability id: %i", i);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_println(0, "(policycap %s)", name);
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int level_to_cil(struct policydb *pdb, struct mls_level *level)
|
|
{
|
|
struct ebitmap *map = &level->cat;
|
|
|
|
cil_printf("(%s", pdb->p_sens_val_to_name[level->sens - 1]);
|
|
|
|
if (ebitmap_cardinality(map) > 0) {
|
|
cil_printf("(");
|
|
ebitmap_to_cil(pdb, map, SYM_CATS);
|
|
cil_printf(")");
|
|
}
|
|
|
|
cil_printf(")");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int context_to_cil(struct policydb *pdb, struct context_struct *con)
|
|
{
|
|
cil_printf("(%s %s %s (",
|
|
pdb->p_user_val_to_name[con->user - 1],
|
|
pdb->p_role_val_to_name[con->role - 1],
|
|
pdb->p_type_val_to_name[con->type - 1]);
|
|
|
|
if (pdb->mls) {
|
|
level_to_cil(pdb, &con->range.level[0]);
|
|
cil_printf(" ");
|
|
level_to_cil(pdb, &con->range.level[1]);
|
|
} else {
|
|
cil_printf(DEFAULT_LEVEL);
|
|
cil_printf(" ");
|
|
cil_printf(DEFAULT_LEVEL);
|
|
}
|
|
|
|
cil_printf("))");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ocontext_isid_to_cil(struct policydb *pdb, const char **sid_to_string, struct ocontext *isids)
|
|
{
|
|
int rc = -1;
|
|
|
|
struct ocontext *isid;
|
|
|
|
struct sid_item {
|
|
const char *sid_key;
|
|
struct sid_item *next;
|
|
};
|
|
|
|
struct sid_item *head = NULL;
|
|
struct sid_item *item = NULL;
|
|
|
|
for (isid = isids; isid != NULL; isid = isid->next) {
|
|
cil_println(0, "(sid %s)", sid_to_string[isid->sid[0]]);
|
|
cil_printf("(sidcontext %s ", sid_to_string[isid->sid[0]]);
|
|
context_to_cil(pdb, &isid->context[0]);
|
|
cil_printf(")\n");
|
|
|
|
// get the sid names in the correct order (reverse from the isids
|
|
// ocontext) for sidorder statement
|
|
item = malloc(sizeof(*item));
|
|
if (item == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
item->sid_key = sid_to_string[isid->sid[0]];
|
|
item->next = head;
|
|
head = item;
|
|
}
|
|
|
|
if (head != NULL) {
|
|
cil_printf("(sidorder (");
|
|
for (item = head; item != NULL; item = item->next) {
|
|
cil_printf("%s ", item->sid_key);
|
|
}
|
|
cil_printf("))\n");
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
while(head) {
|
|
item = head;
|
|
head = item->next;
|
|
free(item);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int ocontext_selinux_isid_to_cil(struct policydb *pdb, struct ocontext *isids)
|
|
{
|
|
int rc = -1;
|
|
|
|
// initial sid names aren't actually stored in the pp files, need to a have
|
|
// a mapping, taken from the linux kernel
|
|
static const char *selinux_sid_to_string[] = {
|
|
"null",
|
|
"kernel",
|
|
"security",
|
|
"unlabeled",
|
|
"fs",
|
|
"file",
|
|
"file_labels",
|
|
"init",
|
|
"any_socket",
|
|
"port",
|
|
"netif",
|
|
"netmsg",
|
|
"node",
|
|
"igmp_packet",
|
|
"icmp_socket",
|
|
"tcp_socket",
|
|
"sysctl_modprobe",
|
|
"sysctl",
|
|
"sysctl_fs",
|
|
"sysctl_kernel",
|
|
"sysctl_net",
|
|
"sysctl_net_unix",
|
|
"sysctl_vm",
|
|
"sysctl_dev",
|
|
"kmod",
|
|
"policy",
|
|
"scmp_packet",
|
|
"devnull",
|
|
NULL
|
|
};
|
|
|
|
rc = ocontext_isid_to_cil(pdb, selinux_sid_to_string, isids);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int ocontext_selinux_fs_to_cil(struct policydb *UNUSED(pdb), struct ocontext *fss)
|
|
{
|
|
if (fss != NULL) {
|
|
log_err("Warning: 'fscon' statement unsupported in CIL. Dropping from output.");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ocontext_selinux_port_to_cil(struct policydb *pdb, struct ocontext *portcons)
|
|
{
|
|
int rc = -1;
|
|
struct ocontext *portcon;
|
|
const char *protocol;
|
|
uint16_t high;
|
|
uint16_t low;
|
|
|
|
for (portcon = portcons; portcon != NULL; portcon = portcon->next) {
|
|
|
|
switch (portcon->u.port.protocol) {
|
|
case IPPROTO_TCP: protocol = "tcp"; break;
|
|
case IPPROTO_UDP: protocol = "udp"; break;
|
|
default:
|
|
log_err("Unknown portcon protocol: %i", portcon->u.port.protocol);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
low = portcon->u.port.low_port;
|
|
high = portcon->u.port.high_port;
|
|
|
|
if (low == high) {
|
|
cil_printf("(portcon %s %i ", protocol, low);
|
|
} else {
|
|
cil_printf("(portcon %s (%i %i) ", protocol, low, high);
|
|
}
|
|
|
|
context_to_cil(pdb, &portcon->context[0]);
|
|
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int ocontext_selinux_netif_to_cil(struct policydb *pdb, struct ocontext *netifs)
|
|
{
|
|
struct ocontext *netif;
|
|
|
|
for (netif = netifs; netif != NULL; netif = netif->next) {
|
|
cil_printf("(netifcon %s ", netif->u.name);
|
|
context_to_cil(pdb, &netif->context[0]);
|
|
|
|
cil_printf(" ");
|
|
context_to_cil(pdb, &netif->context[1]);
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ocontext_selinux_node_to_cil(struct policydb *pdb, struct ocontext *nodes)
|
|
{
|
|
int rc = -1;
|
|
struct ocontext *node;
|
|
char addr[INET_ADDRSTRLEN];
|
|
char mask[INET_ADDRSTRLEN];
|
|
|
|
for (node = nodes; node != NULL; node = node->next) {
|
|
if (inet_ntop(AF_INET, &node->u.node.addr, addr, INET_ADDRSTRLEN) == NULL) {
|
|
log_err("Nodecon address is invalid: %s", strerror(errno));
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
if (inet_ntop(AF_INET, &node->u.node.mask, mask, INET_ADDRSTRLEN) == NULL) {
|
|
log_err("Nodecon mask is invalid: %s", strerror(errno));
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf("(nodecon %s %s ", addr, mask);
|
|
|
|
context_to_cil(pdb, &node->context[0]);
|
|
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int ocontext_selinux_node6_to_cil(struct policydb *pdb, struct ocontext *nodes)
|
|
{
|
|
int rc = -1;
|
|
struct ocontext *node;
|
|
char addr[INET6_ADDRSTRLEN];
|
|
char mask[INET6_ADDRSTRLEN];
|
|
|
|
for (node = nodes; node != NULL; node = node->next) {
|
|
if (inet_ntop(AF_INET6, &node->u.node6.addr, addr, INET6_ADDRSTRLEN) == NULL) {
|
|
log_err("Nodecon address is invalid: %s", strerror(errno));
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
if (inet_ntop(AF_INET6, &node->u.node6.mask, mask, INET6_ADDRSTRLEN) == NULL) {
|
|
log_err("Nodecon mask is invalid: %s", strerror(errno));
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf("(nodecon %s %s ", addr, mask);
|
|
|
|
context_to_cil(pdb, &node->context[0]);
|
|
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int ocontext_selinux_fsuse_to_cil(struct policydb *pdb, struct ocontext *fsuses)
|
|
{
|
|
int rc = -1;
|
|
struct ocontext *fsuse;
|
|
const char *behavior;
|
|
|
|
|
|
for (fsuse = fsuses; fsuse != NULL; fsuse = fsuse->next) {
|
|
switch (fsuse->v.behavior) {
|
|
case SECURITY_FS_USE_XATTR: behavior = "xattr"; break;
|
|
case SECURITY_FS_USE_TRANS: behavior = "trans"; break;
|
|
case SECURITY_FS_USE_TASK: behavior = "task"; break;
|
|
default:
|
|
log_err("Unknown fsuse behavior: %i", fsuse->v.behavior);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf("(fsuse %s %s ", behavior, fsuse->u.name);
|
|
|
|
context_to_cil(pdb, &fsuse->context[0]);
|
|
|
|
cil_printf(")\n");
|
|
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int ocontext_xen_isid_to_cil(struct policydb *pdb, struct ocontext *isids)
|
|
{
|
|
int rc = -1;
|
|
|
|
// initial sid names aren't actually stored in the pp files, need to a have
|
|
// a mapping, taken from the xen kernel
|
|
static const char *xen_sid_to_string[] = {
|
|
"null",
|
|
"xen",
|
|
"dom0",
|
|
"domio",
|
|
"domxen",
|
|
"unlabeled",
|
|
"security",
|
|
"ioport",
|
|
"iomem",
|
|
"irq",
|
|
"device",
|
|
NULL,
|
|
};
|
|
|
|
rc = ocontext_isid_to_cil(pdb, xen_sid_to_string, isids);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int ocontext_xen_pirq_to_cil(struct policydb *pdb, struct ocontext *pirqs)
|
|
{
|
|
struct ocontext *pirq;
|
|
|
|
for (pirq = pirqs; pirq != NULL; pirq = pirq->next) {
|
|
cil_printf("(pirqcon %i ", pirq->u.pirq);
|
|
context_to_cil(pdb, &pirq->context[0]);
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ocontext_xen_ioport_to_cil(struct policydb *pdb, struct ocontext *ioports)
|
|
{
|
|
struct ocontext *ioport;
|
|
uint32_t low;
|
|
uint32_t high;
|
|
|
|
for (ioport = ioports; ioport != NULL; ioport = ioport->next) {
|
|
low = ioport->u.ioport.low_ioport;
|
|
high = ioport->u.ioport.high_ioport;
|
|
|
|
if (low == high) {
|
|
cil_printf("(ioportcon %i ", low);
|
|
} else {
|
|
cil_printf("(ioportcon (%i %i) ", low, high);
|
|
}
|
|
|
|
context_to_cil(pdb, &ioport->context[0]);
|
|
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ocontext_xen_iomem_to_cil(struct policydb *pdb, struct ocontext *iomems)
|
|
{
|
|
struct ocontext *iomem;
|
|
uint32_t low;
|
|
uint32_t high;
|
|
|
|
for (iomem = iomems; iomem != NULL; iomem = iomem->next) {
|
|
low = iomem->u.iomem.low_iomem;
|
|
high = iomem->u.iomem.high_iomem;
|
|
|
|
if (low == high) {
|
|
cil_printf("(iomemcon %#lX ", (unsigned long)low);
|
|
} else {
|
|
cil_printf("(iomemcon (%#lX %#lX) ", (unsigned long)low, (unsigned long)high);
|
|
}
|
|
|
|
context_to_cil(pdb, &iomem->context[0]);
|
|
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ocontext_xen_pcidevice_to_cil(struct policydb *pdb, struct ocontext *pcids)
|
|
{
|
|
struct ocontext *pcid;
|
|
|
|
for (pcid = pcids; pcid != NULL; pcid = pcid->next) {
|
|
cil_printf("(pcidevicecon %#lx ", (unsigned long)pcid->u.device);
|
|
context_to_cil(pdb, &pcid->context[0]);
|
|
cil_printf(")\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ocontexts_to_cil(struct policydb *pdb)
|
|
{
|
|
int rc = -1;
|
|
int ocon;
|
|
|
|
static int (**ocon_funcs)(struct policydb *pdb, struct ocontext *ocon);
|
|
static int (*ocon_selinux_funcs[OCON_NUM])(struct policydb *pdb, struct ocontext *ocon) = {
|
|
ocontext_selinux_isid_to_cil,
|
|
ocontext_selinux_fs_to_cil,
|
|
ocontext_selinux_port_to_cil,
|
|
ocontext_selinux_netif_to_cil,
|
|
ocontext_selinux_node_to_cil,
|
|
ocontext_selinux_fsuse_to_cil,
|
|
ocontext_selinux_node6_to_cil,
|
|
};
|
|
static int (*ocon_xen_funcs[OCON_NUM])(struct policydb *pdb, struct ocontext *ocon) = {
|
|
ocontext_xen_isid_to_cil,
|
|
ocontext_xen_pirq_to_cil,
|
|
ocontext_xen_ioport_to_cil,
|
|
ocontext_xen_iomem_to_cil,
|
|
ocontext_xen_pcidevice_to_cil,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
switch (pdb->target_platform) {
|
|
case SEPOL_TARGET_SELINUX:
|
|
ocon_funcs = ocon_selinux_funcs;
|
|
break;
|
|
case SEPOL_TARGET_XEN:
|
|
ocon_funcs = ocon_xen_funcs;
|
|
break;
|
|
default:
|
|
log_err("Unknown target platform: %i", pdb->target_platform);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
for (ocon = 0; ocon < OCON_NUM; ocon++) {
|
|
if (ocon_funcs[ocon] != NULL) {
|
|
rc = ocon_funcs[ocon](pdb, pdb->ocontexts[ocon]);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int genfscon_to_cil(struct policydb *pdb)
|
|
{
|
|
struct genfs *genfs;
|
|
struct ocontext *ocon;
|
|
|
|
for (genfs = pdb->genfs; genfs != NULL; genfs = genfs->next) {
|
|
for (ocon = genfs->head; ocon != NULL; ocon = ocon->next) {
|
|
cil_printf("(genfscon %s %s ", genfs->fstype, ocon->u.name);
|
|
context_to_cil(pdb, &ocon->context[0]);
|
|
cil_printf(")\n");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int level_string_to_cil(char *levelstr)
|
|
{
|
|
int rc = -1;
|
|
char *sens = NULL;
|
|
char *cats = NULL;
|
|
int matched;
|
|
char *saveptr = NULL;
|
|
char *token = NULL;
|
|
char *ranged = NULL;
|
|
|
|
matched = sscanf(levelstr, "%m[^:]:%ms", &sens, &cats);
|
|
if (matched < 1 || matched > 2) {
|
|
log_err("Invalid level: %s", levelstr);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf("(%s", sens);
|
|
|
|
if (matched == 2) {
|
|
cil_printf("(");
|
|
token = strtok_r(cats, ",", &saveptr);
|
|
while (token != NULL) {
|
|
ranged = strchr(token, '.');
|
|
if (ranged == NULL) {
|
|
cil_printf("%s ", token);
|
|
} else {
|
|
*ranged = '\0';
|
|
cil_printf("(range %s %s) ", token, ranged + 1);
|
|
}
|
|
token = strtok_r(NULL, ",", &saveptr);
|
|
}
|
|
cil_printf(")");
|
|
}
|
|
|
|
cil_printf(")");
|
|
|
|
rc = 0;
|
|
exit:
|
|
free(sens);
|
|
free(cats);
|
|
return rc;
|
|
}
|
|
|
|
static int level_range_string_to_cil(char *levelrangestr)
|
|
{
|
|
char *ranged = NULL;
|
|
char *low;
|
|
char *high;
|
|
|
|
ranged = strchr(levelrangestr, '-');
|
|
if (ranged == NULL) {
|
|
low = high = levelrangestr;
|
|
} else {
|
|
*ranged = '\0';
|
|
low = levelrangestr;
|
|
high = ranged + 1;
|
|
}
|
|
|
|
level_string_to_cil(low);
|
|
cil_printf(" ");
|
|
level_string_to_cil(high);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int context_string_to_cil(char *contextstr)
|
|
{
|
|
int rc = -1;
|
|
int matched;
|
|
char *user = NULL;
|
|
char *role = NULL;
|
|
char *type = NULL;
|
|
char *level = NULL;
|
|
|
|
matched = sscanf(contextstr, "%m[^:]:%m[^:]:%m[^:]:%ms", &user, &role, &type, &level);
|
|
if (matched < 3 || matched > 4) {
|
|
log_err("Invalid context: %s", contextstr);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf("(%s %s %s (", user, role, type);
|
|
|
|
if (matched == 3) {
|
|
cil_printf(DEFAULT_LEVEL);
|
|
cil_printf(" ");
|
|
cil_printf(DEFAULT_LEVEL);
|
|
} else {
|
|
level_range_string_to_cil(level);
|
|
}
|
|
|
|
cil_printf("))");
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
free(user);
|
|
free(role);
|
|
free(type);
|
|
free(level);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int seusers_to_cil(struct sepol_module_package *mod_pkg)
|
|
{
|
|
int rc = -1;
|
|
FILE *fp = NULL;
|
|
char *seusers = sepol_module_package_get_seusers(mod_pkg);
|
|
size_t seusers_len = sepol_module_package_get_seusers_len(mod_pkg);
|
|
size_t len = 0;
|
|
char *line = NULL;
|
|
ssize_t line_len = 0;
|
|
char *buf = NULL;
|
|
|
|
char *user = NULL;
|
|
char *seuser = NULL;
|
|
char *level = NULL;
|
|
int matched;
|
|
|
|
if (seusers_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
fp = fmemopen(seusers, seusers_len, "r");
|
|
|
|
while ((line_len = getline(&line, &len, fp)) != -1) {
|
|
buf = line;
|
|
buf[line_len - 1] = '\0';
|
|
while (*buf && isspace(buf[0])) {
|
|
buf++;
|
|
}
|
|
if (buf[0] == '#' || buf[0] == '\0') {
|
|
continue;
|
|
}
|
|
|
|
matched = sscanf(buf, "%m[^:]:%m[^:]:%ms", &user, &seuser, &level);
|
|
|
|
if (matched < 2 || matched > 3) {
|
|
log_err("Invalid seuser line: %s", line);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
if (!strcmp(user, "__default__")) {
|
|
cil_printf("(selinuxuserdefault %s (", seuser);
|
|
} else {
|
|
cil_printf("(selinuxuser %s %s (", user, seuser);
|
|
}
|
|
|
|
switch (matched) {
|
|
case 2:
|
|
cil_printf("systemlow systemlow");
|
|
break;
|
|
case 3:
|
|
level_range_string_to_cil(level);
|
|
break;
|
|
}
|
|
|
|
cil_printf("))\n");
|
|
|
|
free(user);
|
|
free(seuser);
|
|
free(level);
|
|
user = seuser = level = NULL;
|
|
}
|
|
if (ferror(fp)) {
|
|
cil_printf("Failed to read seusers\n");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
if (fp != NULL) {
|
|
fclose(fp);
|
|
}
|
|
free(line);
|
|
free(user);
|
|
free(seuser);
|
|
free(level);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int netfilter_contexts_to_cil(struct sepol_module_package *mod_pkg)
|
|
{
|
|
size_t netcons_len = sepol_module_package_get_netfilter_contexts_len(mod_pkg);
|
|
|
|
if (netcons_len > 0) {
|
|
log_err("Warning: netfilter_contexts are unsupported in CIL. Dropping from output.");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int user_extra_to_cil(struct sepol_module_package *mod_pkg)
|
|
{
|
|
int rc = -1;
|
|
char *userx = sepol_module_package_get_user_extra(mod_pkg);
|
|
size_t userx_len = sepol_module_package_get_user_extra_len(mod_pkg);
|
|
FILE *fp = NULL;
|
|
size_t len = 0;
|
|
char *line = NULL;
|
|
ssize_t line_len = 0;
|
|
int matched;
|
|
char *user = NULL;
|
|
char *prefix = NULL;
|
|
|
|
if (userx_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
fp = fmemopen(userx, userx_len, "r");
|
|
|
|
while ((line_len = getline(&line, &len, fp)) != -1) {
|
|
line[line_len - 1] = '\0';
|
|
|
|
matched = sscanf(line, "user %ms prefix %m[^;];", &user, &prefix);
|
|
if (matched != 2) {
|
|
rc = -1;
|
|
log_err("Invalid file context line: %s", line);
|
|
goto exit;
|
|
}
|
|
|
|
cil_println(0, "(userprefix %s %s)", user, prefix);
|
|
free(user);
|
|
free(prefix);
|
|
user = prefix = NULL;
|
|
}
|
|
|
|
if (ferror(fp)) {
|
|
cil_printf("Failed to read user_extra\n");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
rc = 0;
|
|
exit:
|
|
if (fp != NULL) {
|
|
fclose(fp);
|
|
}
|
|
free(line);
|
|
free(user);
|
|
free(prefix);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int file_contexts_to_cil(struct sepol_module_package *mod_pkg)
|
|
{
|
|
int rc = -1;
|
|
char *fc = sepol_module_package_get_file_contexts(mod_pkg);
|
|
size_t fc_len = sepol_module_package_get_file_contexts_len(mod_pkg);
|
|
FILE *fp = NULL;
|
|
size_t len = 0;
|
|
char *line = NULL;
|
|
char *buf = NULL;
|
|
ssize_t line_len = 0;
|
|
int matched;
|
|
char *regex = NULL;
|
|
char *mode = NULL;
|
|
char *context = NULL;
|
|
const char *cilmode;
|
|
|
|
if (fc_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
fp = fmemopen(fc, fc_len, "r");
|
|
while ((line_len = getline(&line, &len, fp)) != -1) {
|
|
buf = line;
|
|
if (buf[line_len - 1] == '\n') {
|
|
buf[line_len - 1] = '\0';
|
|
}
|
|
while (*buf && isspace(buf[0])) {
|
|
buf++;
|
|
}
|
|
if (buf[0] == '#' || buf[0] == '\0') {
|
|
continue;
|
|
}
|
|
|
|
matched = sscanf(buf, "%ms %ms %ms", ®ex, &mode, &context);
|
|
if (matched < 2 || matched > 3) {
|
|
rc = -1;
|
|
log_err("Invalid file context line: %s", line);
|
|
goto exit;
|
|
}
|
|
|
|
if (matched == 2) {
|
|
context = mode;
|
|
mode = NULL;
|
|
}
|
|
|
|
if (mode == NULL) {
|
|
cilmode = "any";
|
|
} else if (!strcmp(mode, "--")) {
|
|
cilmode = "file";
|
|
} else if (!strcmp(mode, "-d")) {
|
|
cilmode = "dir";
|
|
} else if (!strcmp(mode, "-c")) {
|
|
cilmode = "char";
|
|
} else if (!strcmp(mode, "-b")) {
|
|
cilmode = "block";
|
|
} else if (!strcmp(mode, "-s")) {
|
|
cilmode = "socket";
|
|
} else if (!strcmp(mode, "-p")) {
|
|
cilmode = "pipe";
|
|
} else if (!strcmp(mode, "-l")) {
|
|
cilmode = "symlink";
|
|
} else {
|
|
rc = -1;
|
|
log_err("Invalid mode in file context line: %s", line);
|
|
goto exit;
|
|
}
|
|
|
|
cil_printf("(filecon \"%s\" %s ", regex, cilmode);
|
|
|
|
if (!strcmp(context, "<<none>>")) {
|
|
cil_printf("()");
|
|
} else {
|
|
context_string_to_cil(context);
|
|
}
|
|
|
|
cil_printf(")\n");
|
|
|
|
free(regex);
|
|
free(mode);
|
|
free(context);
|
|
regex = mode = context = NULL;
|
|
}
|
|
|
|
if (ferror(fp)) {
|
|
cil_printf("Failed to read user_extra\n");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
rc = 0;
|
|
exit:
|
|
free(line);
|
|
free(regex);
|
|
free(mode);
|
|
free(context);
|
|
if (fp != NULL) {
|
|
fclose(fp);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int (*func_to_cil[SYM_NUM])(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack, char *key, void *datum, int scope) = {
|
|
NULL, // commons, only stored in the global symtab, handled elsewhere
|
|
class_to_cil,
|
|
role_to_cil,
|
|
type_to_cil,
|
|
user_to_cil,
|
|
boolean_to_cil,
|
|
sens_to_cil,
|
|
cat_to_cil
|
|
};
|
|
|
|
static int typealiases_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack)
|
|
{
|
|
struct type_datum *alias_datum;
|
|
char *alias_name;
|
|
struct list_node *curr;
|
|
struct avrule_decl *decl = stack_peek(decl_stack);
|
|
struct list *alias_list = typealias_lists[decl->decl_id];
|
|
int rc = -1;
|
|
|
|
if (alias_list == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
for (curr = alias_list->head; curr != NULL; curr = curr->next) {
|
|
alias_name = curr->data;
|
|
alias_datum = hashtab_search(pdb->p_types.table, alias_name);
|
|
if (alias_datum == NULL) {
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_println(indent, "(typealias %s)", alias_name);
|
|
cil_println(indent, "(typealiasactual %s %s)", alias_name, pdb->p_type_val_to_name[alias_datum->s.value - 1]);
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int declared_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
|
|
{
|
|
int rc = -1;
|
|
struct ebitmap map;
|
|
struct ebitmap_node *node;
|
|
unsigned int i;
|
|
char * key;
|
|
struct scope_datum *scope;
|
|
int sym;
|
|
void *datum;
|
|
struct avrule_decl *decl = stack_peek(decl_stack);
|
|
|
|
for (sym = 0; sym < SYM_NUM; sym++) {
|
|
if (func_to_cil[sym] == NULL) {
|
|
continue;
|
|
}
|
|
|
|
map = decl->declared.scope[sym];
|
|
ebitmap_for_each_bit(&map, node, i) {
|
|
if (!ebitmap_get_bit(&map, i)) {
|
|
continue;
|
|
}
|
|
key = pdb->sym_val_to_name[sym][i];
|
|
datum = hashtab_search(pdb->symtab[sym].table, key);
|
|
if (datum == NULL) {
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
scope = hashtab_search(pdb->scope[sym].table, key);
|
|
if (scope == NULL) {
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
rc = func_to_cil[sym](indent, pdb, block, decl_stack, key, datum, scope->scope);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (sym == SYM_CATS) {
|
|
rc = cat_order_to_cil(indent, pdb, map);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (sym == SYM_LEVELS) {
|
|
rc = sens_order_to_cil(indent, pdb, map);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (sym == SYM_CLASSES) {
|
|
rc = class_order_to_cil(indent, pdb, map);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int required_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
|
|
{
|
|
int rc = -1;
|
|
struct ebitmap map;
|
|
struct ebitmap_node *node;
|
|
unsigned int i;
|
|
unsigned int j;
|
|
char * key;
|
|
int sym;
|
|
void *datum;
|
|
struct avrule_decl *decl = stack_peek(decl_stack);
|
|
struct scope_datum *scope_datum;
|
|
|
|
for (sym = 0; sym < SYM_NUM; sym++) {
|
|
if (func_to_cil[sym] == NULL) {
|
|
continue;
|
|
}
|
|
|
|
map = decl->required.scope[sym];
|
|
ebitmap_for_each_bit(&map, node, i) {
|
|
if (!ebitmap_get_bit(&map, i)) {
|
|
continue;
|
|
}
|
|
key = pdb->sym_val_to_name[sym][i];
|
|
|
|
scope_datum = hashtab_search(pdb->scope[sym].table, key);
|
|
for (j = 0; j < scope_datum->decl_ids_len; j++) {
|
|
if (scope_datum->decl_ids[j] == decl->decl_id) {
|
|
break;
|
|
}
|
|
}
|
|
if (j >= scope_datum->decl_ids_len) {
|
|
// Symbols required in the global scope are also in the
|
|
// required scope ebitmap of all avrule decls (i.e. required
|
|
// in all optionals). So we need to look at the scopes of each
|
|
// symbol in this avrule_decl to determine if it actually is
|
|
// required in this decl, or if it's just required in the
|
|
// global scope. If we got here, then this symbol is not
|
|
// actually required in this scope, so skip it.
|
|
continue;
|
|
}
|
|
|
|
datum = hashtab_search(pdb->symtab[sym].table, key);
|
|
if (datum == NULL) {
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
rc = func_to_cil[sym](indent, pdb, block, decl_stack, key, datum, SCOPE_REQ);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int additive_scopes_to_cil_map(char *key, void *data, void *arg)
|
|
{
|
|
int rc = -1;
|
|
struct map_args *args = arg;
|
|
|
|
rc = func_to_cil[args->sym_index](args->indent, args->pdb, args->block, args->decl_stack, key, data, SCOPE_REQ);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int additive_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
|
|
{
|
|
int rc = -1;
|
|
struct map_args args;
|
|
args.pdb = pdb;
|
|
args.block = block;
|
|
args.decl_stack = decl_stack;
|
|
args.indent = indent;
|
|
struct avrule_decl *decl = stack_peek(decl_stack);
|
|
|
|
for (args.sym_index = 0; args.sym_index < SYM_NUM; args.sym_index++) {
|
|
rc = hashtab_map(decl->symtab[args.sym_index].table, additive_scopes_to_cil_map, &args);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int is_scope_superset(struct scope_index *sup, struct scope_index *sub)
|
|
{
|
|
// returns 1 if sup is a superset of sub, returns 0 otherwise
|
|
|
|
int rc = 0;
|
|
|
|
uint32_t i;
|
|
struct ebitmap sup_map;
|
|
struct ebitmap sub_map;
|
|
struct ebitmap res;
|
|
|
|
ebitmap_init(&res);
|
|
|
|
for (i = 0; i < SYM_NUM; i++) {
|
|
sup_map = sup->scope[i];
|
|
sub_map = sub->scope[i];
|
|
|
|
ebitmap_and(&res, &sup_map, &sub_map);
|
|
if (!ebitmap_cmp(&res, &sub_map)) {
|
|
goto exit;
|
|
}
|
|
ebitmap_destroy(&res);
|
|
}
|
|
|
|
if (sup->class_perms_len < sub->class_perms_len) {
|
|
goto exit;
|
|
}
|
|
|
|
for (i = 0; i < sub->class_perms_len; i++) {
|
|
sup_map = sup->class_perms_map[i];
|
|
sub_map = sub->class_perms_map[i];
|
|
|
|
ebitmap_and(&res, &sup_map, &sub_map);
|
|
if (!ebitmap_cmp(&res, &sub_map)) {
|
|
goto exit;
|
|
}
|
|
ebitmap_destroy(&res);
|
|
}
|
|
|
|
rc = 1;
|
|
|
|
exit:
|
|
|
|
ebitmap_destroy(&res);
|
|
return rc;
|
|
}
|
|
|
|
static int blocks_to_cil(struct policydb *pdb)
|
|
{
|
|
int rc = -1;
|
|
struct avrule_block *block;
|
|
struct avrule_decl *decl;
|
|
struct avrule_decl *decl_tmp;
|
|
int indent = 0;
|
|
struct stack *stack;
|
|
struct list *attr_list;
|
|
|
|
rc = stack_init(&stack);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
for (block = pdb->global; block != NULL; block = block->next) {
|
|
rc = list_init(&attr_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
decl = block->branch_list;
|
|
if (decl == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (decl->next != NULL) {
|
|
log_err("Warning: 'else' blocks in optional statements are unsupported in CIL. Dropping from output.");
|
|
}
|
|
|
|
if (block->flags & AVRULE_OPTIONAL) {
|
|
while (stack->pos > 0) {
|
|
decl_tmp = stack_peek(stack);
|
|
if (is_scope_superset(&decl->required, &decl_tmp->required)) {
|
|
break;
|
|
}
|
|
|
|
stack_pop(stack);
|
|
indent--;
|
|
cil_println(indent, ")");
|
|
}
|
|
|
|
cil_println(indent, "(optional %s_optional_%i", pdb->name, decl->decl_id);
|
|
indent++;
|
|
}
|
|
|
|
stack_push(stack, decl);
|
|
|
|
if (stack->pos == 0) {
|
|
// type aliases and commons are only stored in the global symtab.
|
|
// However, to get scoping correct, we assume they are in the
|
|
// global block
|
|
struct map_args args;
|
|
args.pdb = pdb;
|
|
args.block = block;
|
|
args.decl_stack = stack;
|
|
args.indent = 0;
|
|
args.scope = SCOPE_DECL;
|
|
|
|
rc = hashtab_map(pdb->p_commons.table, common_to_cil, &args);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
rc = typealiases_to_cil(indent, pdb, block, stack);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = declared_scopes_to_cil(indent, pdb, block, stack);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = required_scopes_to_cil(indent, pdb, block, stack);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = additive_scopes_to_cil(indent, pdb, block, stack);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = avrule_list_to_cil(indent, pdb, decl->avrules, attr_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = role_trans_to_cil(indent, pdb, decl->role_tr_rules);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = role_allows_to_cil(indent, pdb, decl->role_allow_rules);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = range_trans_to_cil(indent, pdb, decl->range_tr_rules);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = filename_trans_to_cil(indent, pdb, decl->filename_trans_rules);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = cond_list_to_cil(indent, pdb, decl->cond_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = cil_print_attr_list(indent, pdb, attr_list);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
attr_list_destroy(&attr_list);
|
|
}
|
|
|
|
while (indent > 0) {
|
|
indent--;
|
|
cil_println(indent, ")");
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
stack_destroy(&stack);
|
|
attr_list_destroy(&attr_list);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int handle_unknown_to_cil(struct policydb *pdb)
|
|
{
|
|
int rc = -1;
|
|
const char *hu;
|
|
|
|
switch (pdb->handle_unknown) {
|
|
case SEPOL_DENY_UNKNOWN:
|
|
hu = "deny";
|
|
break;
|
|
case SEPOL_REJECT_UNKNOWN:
|
|
hu = "reject";
|
|
break;
|
|
case SEPOL_ALLOW_UNKNOWN:
|
|
hu = "allow";
|
|
break;
|
|
default:
|
|
log_err("Unknown value for handle-unknown: %i", pdb->handle_unknown);
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
cil_println(0, "(handleunknown %s)", hu);
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int generate_mls(struct policydb *pdb)
|
|
{
|
|
const char *mls_str = pdb->mls ? "true" : "false";
|
|
cil_println(0, "(mls %s)", mls_str);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_default_level(void)
|
|
{
|
|
cil_println(0, "(sensitivity s0)");
|
|
cil_println(0, "(sensitivityorder (s0))");
|
|
cil_println(0, "(level " DEFAULT_LEVEL " (s0))");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_default_object(void)
|
|
{
|
|
cil_println(0, "(role " DEFAULT_OBJECT ")");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_gen_require_attribute(void)
|
|
{
|
|
cil_println(0, "(typeattribute " GEN_REQUIRE_ATTR ")");
|
|
cil_println(0, "(roleattribute " GEN_REQUIRE_ATTR ")");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fix_module_name(struct policydb *pdb)
|
|
{
|
|
char *letter;
|
|
int rc = -1;
|
|
|
|
// The base module doesn't have its name set, but we use that for some
|
|
// autogenerated names, like optionals and attributes, to prevent naming
|
|
// collisions. However, they sometimes need to be fixed up.
|
|
|
|
// the base module isn't given a name, so just call it "base"
|
|
if (pdb->policy_type == POLICY_BASE) {
|
|
pdb->name = strdup("base");
|
|
if (pdb->name == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// CIL is more restrictive in module names than checkmodule. Convert bad
|
|
// characters to underscores
|
|
for (letter = pdb->name; *letter != '\0'; letter++) {
|
|
if (isalnum(*letter)) {
|
|
continue;
|
|
}
|
|
|
|
*letter = '_';
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static int module_package_to_cil(struct sepol_module_package *mod_pkg)
|
|
{
|
|
int rc = -1;
|
|
struct sepol_policydb *pdb;
|
|
|
|
pdb = sepol_module_package_get_policy(mod_pkg);
|
|
if (pdb == NULL) {
|
|
log_err("Failed to get policydb");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
if (pdb->p.policy_type != SEPOL_POLICY_BASE &&
|
|
pdb->p.policy_type != SEPOL_POLICY_MOD) {
|
|
log_err("Policy pakcage is not a base or module");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
rc = fix_module_name(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
if (pdb->p.policy_type == SEPOL_POLICY_BASE && !pdb->p.mls) {
|
|
// If this is a base non-mls policy, we need to define a default level
|
|
// range that can be used for contexts by other non-mls modules, since
|
|
// CIL requires that all contexts have a range, even if they are
|
|
// ignored as in non-mls policies
|
|
rc = generate_default_level();
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (pdb->p.policy_type == SEPOL_POLICY_BASE) {
|
|
// object_r is implicit in checkmodule, but not with CIL, create it
|
|
// as part of base
|
|
rc = generate_default_object();
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
// default attribute to be used to mimic gen_require in CIL
|
|
rc = generate_gen_require_attribute();
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
// handle_unknown is used from only the base module
|
|
rc = handle_unknown_to_cil(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
// mls is used from only the base module
|
|
rc = generate_mls(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
rc = role_list_create(pdb->p.p_roles.table);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = typealias_list_create(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = polcaps_to_cil(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = ocontexts_to_cil(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = genfscon_to_cil(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = seusers_to_cil(mod_pkg);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = netfilter_contexts_to_cil(mod_pkg);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = user_extra_to_cil(mod_pkg);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = file_contexts_to_cil(mod_pkg);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
// now print everything that is scoped
|
|
rc = blocks_to_cil(&pdb->p);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
role_list_destroy();
|
|
typealias_list_destroy();
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
|
|
{
|
|
int rc = -1;
|
|
char *d = NULL;
|
|
size_t d_len = 0;
|
|
size_t read_len = 0;
|
|
size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files
|
|
|
|
d = malloc(max_len);
|
|
if (d == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
while ((read_len = fread(d + d_len, 1, max_len - d_len, fp)) > 0) {
|
|
d_len += read_len;
|
|
if (d_len == max_len) {
|
|
max_len *= 2;
|
|
d = realloc(d, max_len);
|
|
if (d == NULL) {
|
|
log_err("Out of memory");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ferror(fp) != 0) {
|
|
log_err("Failed to read pp file");
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
*data = d;
|
|
*data_len = d_len;
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
free(d);
|
|
return rc;
|
|
}
|
|
|
|
static int ppfile_to_module_package(FILE *fp, struct sepol_module_package **mod_pkg)
|
|
{
|
|
int rc = -1;
|
|
FILE *f = NULL;
|
|
struct sepol_policy_file *pf = NULL;
|
|
struct sepol_module_package *pkg = NULL;
|
|
char *data = NULL;
|
|
size_t data_len;
|
|
int fd;
|
|
struct stat sb;
|
|
|
|
rc = sepol_policy_file_create(&pf);
|
|
if (rc != 0) {
|
|
log_err("Failed to create policy file");
|
|
goto exit;
|
|
}
|
|
|
|
fd = fileno(fp);
|
|
if (fstat(fd, &sb) == -1) {
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
if (S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode)) {
|
|
// libsepol fails when trying to read a policy package from a pipe or a
|
|
// socket due its use of lseek. In this case, read the data into a
|
|
// buffer and provide that to libsepol
|
|
rc = fp_to_buffer(fp, &data, &data_len);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
sepol_policy_file_set_mem(pf, data, data_len);
|
|
} else {
|
|
sepol_policy_file_set_fp(pf, fp);
|
|
}
|
|
|
|
rc = sepol_module_package_create(&pkg);
|
|
if (rc != 0) {
|
|
log_err("Failed to create module package");
|
|
goto exit;
|
|
}
|
|
|
|
rc = sepol_module_package_read(pkg, pf, 0);
|
|
if (rc != 0) {
|
|
log_err("Failed to read policy package");
|
|
goto exit;
|
|
}
|
|
|
|
*mod_pkg = pkg;
|
|
|
|
exit:
|
|
free(data);
|
|
|
|
sepol_policy_file_free(pf);
|
|
if (f != NULL) {
|
|
fclose(f);
|
|
}
|
|
|
|
if (rc != 0) {
|
|
sepol_module_package_free(pkg);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void usage(int err)
|
|
{
|
|
fprintf(stderr, "Usage: %s [OPTIONS] [IN_FILE [OUT_FILE]]\n", progname);
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Read an SELinux policy package (.pp) and output the equivilent CIL.\n");
|
|
fprintf(stderr, "If IN_FILE is not provided or is -, read SELinux policy package from\n");
|
|
fprintf(stderr, "standard input. If OUT_FILE is not provided or is -, output CIL to\n");
|
|
fprintf(stderr, "standard output.\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Options:\n");
|
|
fprintf(stderr, " -h, --help print this message and exit\n");
|
|
exit(err);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int rc = -1;
|
|
int opt;
|
|
static struct option long_opts[] = {
|
|
{ "help", 0, NULL, 'h' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
struct sepol_module_package *mod_pkg = NULL;
|
|
FILE *in = NULL;
|
|
FILE *out = NULL;
|
|
int outfd = -1;
|
|
|
|
// ignore sigpipe so we can check the return code of write, and potentially
|
|
// return a more helpful error message
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
progname = basename(argv[0]);
|
|
|
|
while ((opt = getopt_long(argc, argv, "h", long_opts, NULL)) != -1) {
|
|
switch (opt) {
|
|
case 'h':
|
|
usage(0);
|
|
case '?':
|
|
default:
|
|
usage(1);
|
|
}
|
|
}
|
|
|
|
if (argc >= optind + 1 && strcmp(argv[1], "-") != 0) {
|
|
in = fopen(argv[1], "rb");
|
|
if (in == NULL) {
|
|
log_err("Failed to open %s: %s", argv[1], strerror(errno));
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
} else {
|
|
in = stdin;
|
|
}
|
|
|
|
if (argc >= optind + 2 && strcmp(argv[2], "-") != 0) {
|
|
out = fopen(argv[2], "w");
|
|
if (out == NULL) {
|
|
log_err("Failed to open %s: %s", argv[2], strerror(errno));
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
} else {
|
|
out = stdout;
|
|
}
|
|
out_file = out;
|
|
|
|
if (argc >= optind + 3) {
|
|
log_err("Too many arguments");
|
|
usage(1);
|
|
}
|
|
|
|
rc = ppfile_to_module_package(in, &mod_pkg);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
fclose(in);
|
|
in = NULL;
|
|
|
|
rc = module_package_to_cil(mod_pkg);
|
|
if (rc != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
if (in != NULL) {
|
|
fclose(in);
|
|
}
|
|
if (out != NULL) {
|
|
fclose(out);
|
|
}
|
|
if (outfd != -1) {
|
|
close(outfd);
|
|
if (rc != 0) {
|
|
unlink(argv[2]);
|
|
}
|
|
}
|
|
sepol_module_package_free(mod_pkg);
|
|
|
|
return rc;
|
|
}
|