#include <stdlib.h>
#include "cil_flavor.h"
#include "cil_internal.h"
#include "cil_log.h"
#include "cil_tree.h"
struct cil_args_write {
FILE *cil_out;
struct cil_db *db;
};
static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren);
static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren);
static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args);
static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args);
static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args);
static int __cil_strlist_concat(struct cil_list *str_list, char **out_str, int paren) {
size_t len = paren ? 3 : 1;
size_t num_elems = 0;
char *p = NULL;
struct cil_list_item *curr;
/* get buffer size */
cil_list_for_each(curr, str_list) {
len += strlen((char *)curr->data);
num_elems++;
}
if (num_elems != 0) {
/* add spaces between elements */
len += num_elems - 1;
}
*out_str = cil_malloc(len);
p = *out_str;
if (paren)
*p++ = '(';
cil_list_for_each(curr, str_list) {
size_t src_len = strlen((char *)curr->data);
memcpy(p, curr->data, src_len);
p += src_len;
if (curr->next != NULL)
*p++ = ' ';
}
if (paren)
*p++ = ')';
*p++ = '\0';
return SEPOL_OK;
}
static int __cil_unfill_expr_helper(struct cil_list_item *curr,
struct cil_list_item **next, char **out_str, int paren) {
int rc = SEPOL_ERR;
char *str = NULL;
char *operand1 = NULL;
char *operand2 = NULL;
switch(curr->flavor) {
case CIL_LIST:
rc = cil_unfill_expr((struct cil_list *)curr->data, &str, paren);
if (rc != SEPOL_OK)
goto exit;
*out_str = str;
*next = curr->next;
break;
case CIL_STRING:
str = strdup((char *)curr->data);
if (!str) {
cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
rc = SEPOL_ERR;
goto exit;
}
*out_str = str;
*next = curr->next;
break;
case CIL_DATUM:
str = strdup(((struct cil_symtab_datum *)curr->data)->name);
if (!str) {
cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
rc = SEPOL_ERR;
goto exit;
}
*out_str = str;
*next = curr->next;
break;
case CIL_OP: {
char *op_str = NULL;
size_t len = 0;
enum cil_flavor op_flavor = (enum cil_flavor)curr->data;
switch (op_flavor) {
case CIL_AND:
op_str = CIL_KEY_AND;
break;
case CIL_OR:
op_str = CIL_KEY_OR;
break;
case CIL_NOT:
op_str = CIL_KEY_NOT;
break;
case CIL_ALL:
op_str = CIL_KEY_ALL;
break;
case CIL_EQ:
op_str = CIL_KEY_EQ;
break;
case CIL_NEQ:
op_str = CIL_KEY_NEQ;
break;
case CIL_RANGE:
op_str = CIL_KEY_RANGE;
break;
case CIL_XOR:
op_str = CIL_KEY_XOR;
break;
case CIL_CONS_DOM:
op_str = CIL_KEY_CONS_DOM;
break;
case CIL_CONS_DOMBY:
op_str = CIL_KEY_CONS_DOMBY;
break;
case CIL_CONS_INCOMP:
op_str = CIL_KEY_CONS_INCOMP;
break;
default:
cil_log(CIL_ERR, "Unknown operator in expression: %d\n", op_flavor);
goto exit;
break;
}
/* all operands take two args except for 'all' and 'not', which take
* one and two, respectively */
len = strlen(op_str) + 3;
if (op_flavor == CIL_ALL) {
*out_str = cil_malloc(len);
sprintf(*out_str, "(%s)", op_str);
*next = curr->next;
} else if (op_flavor == CIL_NOT) {
rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
if (rc != SEPOL_OK)
goto exit;
len += strlen(operand1) + 1;
*out_str = cil_malloc(len);
sprintf(*out_str, "(%s %s)", op_str, operand1);
// *next already set by recursive call
} else {
rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
if (rc != SEPOL_OK)
goto exit;
len += strlen(operand1) + 1;
// *next contains operand2, but keep track of next after that
rc = __cil_unfill_expr_helper(*next, next, &operand2, paren);
if (rc != SEPOL_OK)
goto exit;
len += strlen(operand2) + 1;
*out_str = cil_malloc(len);
sprintf(*out_str, "(%s %s %s)", op_str, operand1, operand2);
// *next already set by recursive call
}
}
break;
case CIL_CONS_OPERAND: {
enum cil_flavor operand_flavor = (enum cil_flavor)curr->data;
char *operand_str = NULL;
switch (operand_flavor) {
case CIL_CONS_U1:
operand_str = CIL_KEY_CONS_U1;
break;
case CIL_CONS_U2:
operand_str = CIL_KEY_CONS_U2;
break;
case CIL_CONS_U3:
operand_str = CIL_KEY_CONS_U3;
break;
case CIL_CONS_T1:
operand_str = CIL_KEY_CONS_T1;
break;
case CIL_CONS_T2:
operand_str = CIL_KEY_CONS_T2;
break;
case CIL_CONS_T3:
operand_str = CIL_KEY_CONS_T3;
break;
case CIL_CONS_R1:
operand_str = CIL_KEY_CONS_R1;
break;
case CIL_CONS_R2:
operand_str = CIL_KEY_CONS_R2;
break;
case CIL_CONS_R3:
operand_str = CIL_KEY_CONS_R3;
break;
case CIL_CONS_L1:
operand_str = CIL_KEY_CONS_L1;
break;
case CIL_CONS_L2:
operand_str = CIL_KEY_CONS_L2;
break;
case CIL_CONS_H1:
operand_str = CIL_KEY_CONS_H1;
break;
case CIL_CONS_H2:
operand_str = CIL_KEY_CONS_H2;
break;
default:
cil_log(CIL_ERR, "Unknown operand in expression\n");
goto exit;
break;
}
str = strdup(operand_str);
if (!str) {
cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
rc = SEPOL_ERR;
goto exit;
}
*out_str = str;
*next = curr->next;
}
break;
default:
cil_log(CIL_ERR, "Unknown flavor in expression\n");
goto exit;
break;
}
rc = SEPOL_OK;
exit:
free(operand1);
free(operand2);
return rc;
}
static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren) {
int rc = SEPOL_ERR;
/* reuse cil_list to keep track of strings */
struct cil_list *str_list = NULL;
struct cil_list_item *curr = NULL;
cil_list_init(&str_list, CIL_NONE);
/* iterate through cil_list, grabbing elements as needed */
curr = expr_str->head;
while(curr != NULL) {
char *str = NULL;
struct cil_list_item *next = NULL;
rc = __cil_unfill_expr_helper(curr, &next, &str, paren);
if (rc != SEPOL_OK)
goto exit;
cil_list_append(str_list, CIL_STRING, (void *) str);
str = NULL;
curr = next;
}
rc = __cil_strlist_concat(str_list, out_str, paren);
if (rc != SEPOL_OK)
goto exit;
rc = SEPOL_OK;
exit:
cil_list_for_each(curr, str_list) {
free(curr->data);
}
cil_list_destroy(&str_list, 0);
return rc;
}
static int cil_unfill_cats(struct cil_cats *cats, char **out_str) {
return cil_unfill_expr(cats->str_expr, out_str, 0);
}
static int cil_unfill_level(struct cil_level *lvl, char **out_str) {
int rc = SEPOL_ERR;
size_t len = 0;
char *sens, *cats = NULL;
sens = lvl->sens_str;
len = strlen(sens) + 3; // '()\0'
if (lvl->cats != NULL) {
rc = cil_unfill_cats(lvl->cats, &cats);
if (rc != SEPOL_OK)
goto exit;
len += strlen(cats) + 1;
}
*out_str = cil_malloc(len);
if (cats == NULL) {
if (sprintf(*out_str, "(%s)", sens) < 0) {
cil_log(CIL_ERR, "Error unpacking and writing level\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
if (sprintf(*out_str, "(%s %s)", sens, cats) < 0) {
cil_log(CIL_ERR, "Error unpacking and writing level\n");
rc = SEPOL_ERR;
goto exit;
}
}
rc = SEPOL_OK;
exit:
free(cats);
return rc;
}
static int cil_unfill_levelrange(struct cil_levelrange *lvlrnge, char **out_str) {
int rc = SEPOL_ERR;
size_t len = 0;
char *low = NULL, *high = NULL;
if (lvlrnge->low_str != NULL) {
low = strdup(lvlrnge->low_str);
if (low == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_level(lvlrnge->low, &low);
if (rc != SEPOL_OK)
goto exit;
}
if (lvlrnge->high_str != NULL) {
high = strdup(lvlrnge->high_str);
if (high == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_level(lvlrnge->high, &high);
if (rc != SEPOL_OK)
goto exit;
}
len = strlen(low) + strlen(high) + 4;
*out_str = cil_malloc(len);
if (sprintf(*out_str, "(%s %s)", low, high) < 0) {
cil_log(CIL_ERR, "Error unpacking and writing levelrange\n");
rc = SEPOL_ERR;
goto exit;
}
rc = SEPOL_OK;
exit:
free(low);
free(high);
return rc;
}
static int cil_unfill_context(struct cil_context *context, char **out_str) {
int rc = SEPOL_ERR;
size_t len = 0;
char *user_str, *role_str, *type_str;
char *range_str = NULL;
user_str = context->user_str;
role_str = context->role_str;
type_str = context->type_str;
if (context->range_str != NULL) {
range_str = strdup(context->range_str);
if (range_str == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy range string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_levelrange(context->range, &range_str);
if (rc != SEPOL_OK)
goto exit;
}
len = strlen(user_str) + strlen(role_str) + strlen(type_str)
+ strlen(range_str) + 6;
*out_str = cil_malloc(len);
if (sprintf(*out_str, "(%s %s %s %s)", user_str, role_str, type_str, range_str) < 0) {
cil_log(CIL_ERR, "Error unpacking and writing context\n");
rc = SEPOL_ERR;
goto exit;
}
rc = SEPOL_OK;
exit:
free(range_str);
return rc;
}
static int cil_unfill_permx(struct cil_permissionx *permx, char **out_str) {
size_t len = 3;
int rc = SEPOL_ERR;
char *kind, *obj;
char *expr = NULL;
switch (permx->kind) {
case CIL_PERMX_KIND_IOCTL:
kind = CIL_KEY_IOCTL;
break;
default:
cil_log(CIL_ERR, "Unknown permissionx kind: %d\n", permx->kind);
rc = SEPOL_ERR;
goto exit;
break;
}
obj = permx->obj_str;
rc = cil_unfill_expr(permx->expr_str, &expr, 1);
if (rc != SEPOL_OK)
goto exit;
len += strlen(kind) + strlen(obj) + strlen(expr) + 2;
*out_str = cil_malloc(len);
if (sprintf(*out_str, "(%s %s %s)", kind, obj, expr) < 0) {
cil_log(CIL_ERR, "Error writing xperm\n");
rc = SEPOL_ERR;
goto exit;
}
rc = SEPOL_OK;
exit:
free(expr);
return rc;
}
#define cil_write_unsupported(flavor) _cil_write_unsupported(flavor, __LINE__)
static int _cil_write_unsupported(const char *flavor, int line) {
cil_log(CIL_ERR,
"flavor \"%s\" is not supported, look in file \"%s\""
" on line %d to add support.\n", flavor, __FILE__, line);
return SEPOL_ENOTSUP;
}
static int cil_write_policycap(struct cil_tree_node *node, FILE *cil_out) {
struct cil_policycap *polcap = (struct cil_policycap *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_POLICYCAP, polcap->datum.name);
return SEPOL_OK;
}
static int cil_write_perm(struct cil_tree_node *node, FILE *cil_out) {
struct cil_perm *perm = (struct cil_perm *)node->data;
fprintf(cil_out, "%s", perm->datum.name);
if (node->next != NULL)
fprintf(cil_out, " ");
return SEPOL_OK;
}
static int cil_write_class(struct cil_tree_node *node, uint32_t *finished,
struct cil_args_write *extra_args) {
int rc = SEPOL_ERR;
FILE *cil_out = extra_args->cil_out;
struct cil_symtab_datum *datum = (struct cil_symtab_datum *)node->data;
char *class_type = (node->flavor == CIL_CLASS) ? CIL_KEY_CLASS : CIL_KEY_COMMON;
/* print preamble */
fprintf(cil_out, "(%s %s ", class_type, datum->name);
if (node->cl_head == NULL) {
/* no associated perms in this part of tree */
fprintf(cil_out, "()");
} else {
/* visit subtree (perms) */
rc = cil_tree_walk(node, __cil_write_node_helper,
__cil_write_first_child_helper,
__cil_write_last_child_helper,
extra_args);
if (rc != SEPOL_OK)
goto exit;
}
/* postamble (trailing paren) */
fprintf(cil_out, ")\n");
*finished = CIL_TREE_SKIP_HEAD;
rc = SEPOL_OK;
exit:
return rc;
}
static int cil_write_classorder(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *ord_str = NULL;
struct cil_classorder *classord = (struct cil_classorder *)node->data;
/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
rc = cil_unfill_expr(classord->class_list_str, &ord_str, 1);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_CLASSORDER, ord_str);
rc = SEPOL_OK;
exit:
free(ord_str);
return rc;
}
static int cil_write_classcommon(struct cil_tree_node *node, FILE *cil_out) {
struct cil_classcommon *classcommon = (struct cil_classcommon *)node->data;
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_CLASSCOMMON, classcommon->class_str,
classcommon->common_str);
return SEPOL_OK;
}
static int cil_write_sid(struct cil_tree_node *node, FILE *cil_out) {
struct cil_sid *sid = (struct cil_sid *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_SID, sid->datum.name);
return SEPOL_OK;
}
static int cil_write_sidcontext(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *sid;
char *ctx_str = NULL;
struct cil_sidcontext *sidcon = (struct cil_sidcontext *)node->data;
sid = sidcon->sid_str;
if (sidcon->context_str != NULL) {
ctx_str = strdup(sidcon->context_str);
if (ctx_str == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_context(sidcon->context, &ctx_str);
if (rc != SEPOL_OK)
goto exit;
}
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_SIDCONTEXT, sid, ctx_str);
rc = SEPOL_OK;
exit:
free(ctx_str);
return rc;
}
static int cil_write_sidorder(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *ord_str = NULL;
struct cil_sidorder *sidord = (struct cil_sidorder *)node->data;
/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
rc = cil_unfill_expr(sidord->sid_list_str, &ord_str, 1);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_SIDORDER, ord_str);
rc = SEPOL_OK;
exit:
free(ord_str);
return rc;
}
static int cil_write_user(struct cil_tree_node *node, FILE *cil_out) {
struct cil_user *user = (struct cil_user *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_USER, user->datum.name);
return SEPOL_OK;
}
static int cil_write_userrole(struct cil_tree_node *node, FILE *cil_out) {
struct cil_userrole *userrole = (struct cil_userrole *)node->data;
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERROLE, userrole->user_str,
userrole->role_str);
return SEPOL_OK;
}
static int cil_write_userlevel(struct cil_tree_node *node, FILE *cil_out) {
struct cil_userlevel *usrlvl = (struct cil_userlevel *)node->data;
int rc = SEPOL_ERR;
char *usr;
char *lvl = NULL;
usr = usrlvl->user_str;
if (usrlvl->level_str != NULL) {
lvl = strdup(usrlvl->level_str);
if (lvl == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_level(usrlvl->level, &lvl);
if (rc != SEPOL_OK)
goto exit;
}
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERLEVEL, usr, lvl);
rc = SEPOL_OK;
exit:
free(lvl);
return rc;
}
static int cil_write_userrange(struct cil_tree_node *node, FILE *cil_out) {
struct cil_userrange *usrrng = (struct cil_userrange *)node->data;
int rc = SEPOL_ERR;
char *usr;
char *range = NULL;
usr = usrrng->user_str;
if (usrrng->range_str != NULL) {
range = strdup(usrrng->range_str);
if (range == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy levelrange string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_levelrange(usrrng->range, &range);
if (rc != SEPOL_OK)
goto exit;
}
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERRANGE, usr, range);
rc = SEPOL_OK;
exit:
free(range);
return rc;
}
static int cil_write_role(struct cil_tree_node *node, FILE *cil_out) {
struct cil_role *role = (struct cil_role *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLE, role->datum.name);
return SEPOL_OK;
}
static int cil_write_roletype(struct cil_tree_node *node, FILE *cil_out) {
struct cil_roletype *roletype = (struct cil_roletype *)node->data;
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_ROLETYPE, roletype->role_str, roletype->type_str);
return SEPOL_OK;
}
static int cil_write_roleattribute(struct cil_tree_node *node, FILE *cil_out) {
struct cil_roleattribute *roleattr = (struct cil_roleattribute *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLEATTRIBUTE, roleattr->datum.name);
return SEPOL_OK;
}
static int cil_write_type(struct cil_tree_node *node, FILE *cil_out) {
struct cil_type *type = (struct cil_type *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPE, type->datum.name);
return SEPOL_OK;
}
static int cil_write_typepermissive(struct cil_tree_node *node, FILE *cil_out) {
struct cil_typepermissive *type = (struct cil_typepermissive *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEPERMISSIVE, type->type_str);
return SEPOL_OK;
}
static int cil_write_typeattribute(struct cil_tree_node *node, FILE *cil_out) {
struct cil_typeattribute *typeattr = (struct cil_typeattribute *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEATTRIBUTE, typeattr->datum.name);
return SEPOL_OK;
}
static int cil_write_typeattributeset(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *typeattr;
char *set_str = NULL;
struct cil_typeattributeset *typeattrset = (struct cil_typeattributeset *)node->data;
typeattr = typeattrset->attr_str;
rc = cil_unfill_expr(typeattrset->str_expr, &set_str, 1);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_TYPEATTRIBUTESET, typeattr, set_str);
rc = SEPOL_OK;
exit:
free(set_str);
return rc;
}
static int cil_write_expandtypeattribute(struct cil_tree_node *node, FILE *cil_out)
{
int rc = SEPOL_ERR;
char *attr_strs = NULL;
struct cil_expandtypeattribute *expandattr = (struct cil_expandtypeattribute *)node->data;
rc = cil_unfill_expr(expandattr->attr_strs, &attr_strs, 1);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_EXPANDTYPEATTRIBUTE, attr_strs,
expandattr->expand ? CIL_KEY_CONDTRUE : CIL_KEY_CONDFALSE);
rc = SEPOL_OK;
exit:
free(attr_strs);
return rc;
}
static int cil_write_alias(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *type;
struct cil_alias *alias = (struct cil_alias *)node->data;
switch (node->flavor) {
case CIL_TYPEALIAS:
type = CIL_KEY_TYPEALIAS;
break;
case CIL_SENSALIAS:
type = CIL_KEY_SENSALIAS;
break;
case CIL_CATALIAS:
type = CIL_KEY_CATALIAS;
break;
default:
cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
rc = SEPOL_ERR;
goto exit;
break;
}
fprintf(cil_out, "(%s %s)\n", type, alias->datum.name);
rc = SEPOL_OK;
exit:
return rc;
}
static int cil_write_aliasactual(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *type, *alias, *actual;
struct cil_aliasactual *aliasact = (struct cil_aliasactual *)node->data;
switch (node->flavor) {
case CIL_TYPEALIASACTUAL:
type = CIL_KEY_TYPEALIASACTUAL;
break;
case CIL_SENSALIASACTUAL:
type = CIL_KEY_SENSALIASACTUAL;
break;
case CIL_CATALIASACTUAL:
type = CIL_KEY_CATALIASACTUAL;
break;
default:
cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
rc = SEPOL_ERR;
goto exit;
break;
}
alias = aliasact->alias_str;
actual = aliasact->actual_str;
fprintf(cil_out, "(%s %s %s)\n", type, alias, actual);
rc = SEPOL_OK;
exit:
return rc;
}
static int cil_write_nametypetransition(struct cil_tree_node *node, FILE *cil_out) {
char *src, *tgt, *obj, *res, *name;
struct cil_nametypetransition *ntrans = (struct cil_nametypetransition *)node->data;
src = ntrans->src_str;
tgt = ntrans->tgt_str;
obj = ntrans->obj_str;
res = ntrans->result_str;
name = ntrans->name_str;
fprintf(cil_out, "(%s %s %s %s \"%s\" %s)\n", CIL_KEY_TYPETRANSITION,
src, tgt, obj, name, res);
return SEPOL_OK;
}
static int cil_write_avrule_x(struct cil_avrule *avrule, FILE *cil_out) {
int rc = SEPOL_ERR;
char *rulekind, *src, *tgt;
char *xperms = NULL;
switch (avrule->rule_kind) {
case CIL_AVRULE_ALLOWED:
rulekind = CIL_KEY_ALLOWX;
break;
case CIL_AVRULE_AUDITALLOW:
rulekind = CIL_KEY_AUDITALLOWX;
break;
case CIL_AVRULE_DONTAUDIT:
rulekind = CIL_KEY_DONTAUDITX;
break;
case CIL_AVRULE_NEVERALLOW:
rulekind = CIL_KEY_NEVERALLOWX;
break;
default:
cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
rc = SEPOL_ERR;
goto exit;
break;
}
src = avrule->src_str;
tgt = avrule->tgt_str;
if (avrule->perms.x.permx_str != NULL) {
xperms = strdup(avrule->perms.x.permx_str);
if (xperms == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy xperms string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_permx(avrule->perms.x.permx, &xperms);
if (rc != SEPOL_OK)
goto exit;
}
fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, xperms);
rc = SEPOL_OK;
exit:
free(xperms);
return rc;
}
static int cil_write_avrule_orig(struct cil_avrule *avrule, FILE *cil_out) {
int rc = SEPOL_ERR;
char *rulekind, *src, *tgt;
char *classperms = NULL;
switch (avrule->rule_kind) {
case CIL_AVRULE_ALLOWED:
rulekind = CIL_KEY_ALLOW;
break;
case CIL_AVRULE_AUDITALLOW:
rulekind = CIL_KEY_AUDITALLOW;
break;
case CIL_AVRULE_DONTAUDIT:
rulekind = CIL_KEY_DONTAUDIT;
break;
case CIL_AVRULE_NEVERALLOW:
rulekind = CIL_KEY_NEVERALLOW;
break;
default:
cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
rc = SEPOL_ERR;
goto exit;
break;
}
src = avrule->src_str;
tgt = avrule->tgt_str;
rc = cil_unfill_classperms_list(avrule->perms.classperms, &classperms, 0);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, classperms);
rc = SEPOL_OK;
exit:
free(classperms);
return rc;
}
static int cil_write_avrule(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
struct cil_avrule *avrule = (struct cil_avrule *)node->data;
if (avrule->is_extended)
rc = cil_write_avrule_x(avrule, cil_out);
else
rc = cil_write_avrule_orig(avrule, cil_out);
return rc;
}
static int cil_write_type_rule(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *type, *src, *tgt, *obj, *res;
struct cil_type_rule *typerule = (struct cil_type_rule *)node->data;
switch (typerule->rule_kind) {
case CIL_TYPE_TRANSITION:
type = CIL_KEY_TYPETRANSITION;
break;
case CIL_TYPE_MEMBER:
type = CIL_KEY_TYPEMEMBER;
break;
case CIL_TYPE_CHANGE:
type = CIL_KEY_TYPECHANGE;
break;
default:
cil_log(CIL_ERR, "Unknown TYPERULE type: %d\n", typerule->rule_kind);
rc = SEPOL_ERR;
goto exit;
break;
}
src = typerule->src_str;
tgt = typerule->tgt_str;
obj = typerule->obj_str;
res = typerule->result_str;
fprintf(cil_out, "(%s %s %s %s %s)\n", type, src, tgt, obj, res);
rc = SEPOL_OK;
exit:
return rc;
}
static int cil_write_sens(struct cil_tree_node *node, FILE *cil_out) {
struct cil_sens *sens = (struct cil_sens *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITY, sens->datum.name);
return SEPOL_OK;
}
static int cil_write_cat(struct cil_tree_node *node, FILE *cil_out) {
struct cil_cat *cat = (struct cil_cat *)node->data;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATEGORY, cat->datum.name);
return SEPOL_OK;
}
static int cil_write_senscat(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *sens;
char *cats = NULL;
struct cil_senscat *senscat = (struct cil_senscat *)node->data;
sens = senscat->sens_str;
rc = cil_unfill_cats(senscat->cats, &cats);
if (rc != SEPOL_OK)
goto exit;
/* TODO: deal with extra/missing parens */
fprintf(cil_out, "(%s %s (%s))\n", CIL_KEY_SENSCAT, sens, cats);
rc = SEPOL_OK;
exit:
free(cats);
return rc;
}
static int cil_write_catorder(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *ord_str = NULL;
struct cil_catorder *catord = (struct cil_catorder *)node->data;
/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
rc = cil_unfill_expr(catord->cat_list_str, &ord_str, 1);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATORDER, ord_str);
rc = SEPOL_OK;
exit:
free(ord_str);
return rc;
}
static int cil_write_sensorder(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *ord_str = NULL;
struct cil_sensorder *sensord = (struct cil_sensorder *)node->data;
/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
rc = cil_unfill_expr(sensord->sens_list_str, &ord_str, 1);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITYORDER, ord_str);
rc = SEPOL_OK;
exit:
free(ord_str);
return rc;
}
static int cil_write_genfscon(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
char *ctx_str = NULL;
struct cil_genfscon *genfscon = (struct cil_genfscon *)node->data;
if (genfscon->context_str != NULL) {
ctx_str = strdup(genfscon->context_str);
if (ctx_str == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_context(genfscon->context, &ctx_str);
if (rc != SEPOL_OK)
goto exit;
}
fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_GENFSCON, genfscon->fs_str,
genfscon->path_str, ctx_str);
rc = SEPOL_OK;
exit:
free(ctx_str);
return rc;
}
static int cil_unfill_classperms(struct cil_list_item *curr, char **out_str) {
int rc = SEPOL_ERR;
size_t len = 3;
char *class_str;
char *perms_str = NULL;
struct cil_classperms *cp = (struct cil_classperms *)curr->data;
class_str = cp->class_str;
len += strlen(class_str) + 1;
/* fill_perms just calls gen_expr */
rc = cil_unfill_expr(cp->perm_strs, &perms_str, 1);
if (rc != SEPOL_OK)
goto exit;
len += strlen(perms_str);
*out_str = cil_malloc(len);
sprintf(*out_str, "(%s %s)", class_str, perms_str);
rc = SEPOL_OK;
exit:
free(perms_str);
return rc;
}
static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren) {
int rc = SEPOL_ERR;
struct cil_list_item *curr;
char *str = NULL;
/* reuse cil_list to keep track of strings */
struct cil_list *str_list = NULL;
cil_list_init(&str_list, CIL_NONE);
cil_list_for_each(curr, classperms) {
switch (curr->flavor) {
case CIL_CLASSPERMS_SET:
str = strdup(((struct cil_classperms_set *)curr->data)->set_str);
if (str == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy classpermset.\n");
rc = SEPOL_ERR;
goto exit;
}
break;
case CIL_CLASSPERMS:
rc = cil_unfill_classperms(curr, &str);
if (rc != SEPOL_OK)
goto exit;
break;
default:
cil_log(CIL_ERR, "Unrecognized classperms flavor\n.");
goto exit;
}
cil_list_append(str_list, CIL_STRING, (void *) str);
str = NULL;
}
rc = __cil_strlist_concat(str_list, out_str, paren);
if (rc != SEPOL_OK)
goto exit;
rc = SEPOL_OK;
exit:
cil_list_for_each(curr, str_list) {
free(curr->data);
}
cil_list_destroy(&str_list, 0);
return rc;
}
static int cil_write_fsuse(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
struct cil_fsuse *fsuse = (struct cil_fsuse *)node->data;
char *type, *fsname;
char *ctx_str = NULL;
switch(fsuse->type) {
case CIL_FSUSE_XATTR:
type = CIL_KEY_XATTR;
break;
case CIL_FSUSE_TASK:
type = CIL_KEY_TASK;
break;
case CIL_FSUSE_TRANS:
type = CIL_KEY_TRANS;
break;
default:
cil_log(CIL_ERR, "Unrecognized fsuse type\n");
rc = SEPOL_ERR;
goto exit;
break;
}
fsname = fsuse->fs_str;
if (fsuse->context_str != NULL) {
ctx_str = strdup(fsuse->context_str);
if (ctx_str == NULL) {
cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
rc = SEPOL_ERR;
goto exit;
}
} else {
rc = cil_unfill_context(fsuse->context, &ctx_str);
if (rc != SEPOL_OK)
goto exit;
}
fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_FSUSE, type, fsname, ctx_str);
exit:
free(ctx_str);
return rc;
}
static int cil_write_constrain(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_ERR;
struct cil_constrain *cons = (struct cil_constrain *)node->data;
char *flav;
char *classperms = NULL;
char *expr = NULL;
flav = (node->flavor == CIL_CONSTRAIN) ? CIL_KEY_CONSTRAIN : CIL_KEY_MLSCONSTRAIN;
rc = cil_unfill_classperms_list(cons->classperms, &classperms, 0);
if (rc != SEPOL_OK)
goto exit;
rc = cil_unfill_expr(cons->str_expr, &expr, 0);
if (rc != SEPOL_OK)
goto exit;
fprintf(cil_out, "(%s %s %s)\n", flav, classperms, expr);
exit:
free(classperms);
free(expr);
return rc;
}
static int cil_write_handleunknown(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_OK;
struct cil_handleunknown *handunknown = (struct cil_handleunknown *)node->data;
char *val = NULL;
switch (handunknown->handle_unknown) {
case SEPOL_ALLOW_UNKNOWN:
val = CIL_KEY_HANDLEUNKNOWN_ALLOW;
break;
case SEPOL_DENY_UNKNOWN:
val = CIL_KEY_HANDLEUNKNOWN_DENY;
break;
case SEPOL_REJECT_UNKNOWN:
val = CIL_KEY_HANDLEUNKNOWN_REJECT;
break;
default:
cil_log(CIL_ERR, "Unknown handleunknown value: %d.\n",
handunknown->handle_unknown);
rc = SEPOL_ERR;
goto exit;
break;
}
fprintf(cil_out, "(%s %s)\n", CIL_KEY_HANDLEUNKNOWN, val);
exit:
return rc;
}
static int cil_write_mls(struct cil_tree_node *node, FILE *cil_out) {
int rc = SEPOL_OK;
struct cil_mls *mls = (struct cil_mls *)node->data;
char *val = NULL;
switch (mls->value) {
case CIL_TRUE:
val = CIL_KEY_CONDTRUE;
break;
case CIL_FALSE:
val = CIL_KEY_CONDFALSE;
break;
default:
cil_log(CIL_ERR, "Unknown mls value: %d.\n", mls->value);
rc = SEPOL_ERR;
goto exit;
break;
}
fprintf(cil_out, "(%s %s)\n", CIL_KEY_MLS, val);
exit:
return rc;
}
static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args)
{
int rc = SEPOL_ERR;
struct cil_args_write *args = (struct cil_args_write *) extra_args;
FILE *cil_out = NULL;
if (node == NULL || extra_args == NULL) {
goto exit;
}
cil_out = args->cil_out;
if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO)
fprintf(cil_out,"(");
rc = SEPOL_OK;
exit:
return rc;
}
static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
{
int rc = SEPOL_OK;
struct cil_args_write *args = NULL;
FILE *cil_out = NULL;
if (node == NULL || extra_args == NULL) {
goto exit;
}
args = extra_args;
cil_out = args->cil_out;
switch (node->flavor) {
case CIL_BLOCK:
rc = cil_write_unsupported("CIL_BLOCK");
break;
case CIL_BLOCKABSTRACT:
rc = cil_write_unsupported("CIL_BLOCKABSTRACT");
break;
case CIL_BLOCKINHERIT:
rc = cil_write_unsupported("CIL_BLOCKINHERIT");
break;
case CIL_IN:
rc = cil_write_unsupported("CIL_IN");
break;
case CIL_POLICYCAP:
cil_write_policycap(node, cil_out);
break;
case CIL_PERM:
rc = cil_write_perm(node, cil_out);
break;
case CIL_MAP_PERM:
rc = cil_write_unsupported("CIL_MAP_PERM");
break;
case CIL_CLASSMAPPING:
rc = cil_write_unsupported("CIL_CLASSMAPPING");
break;
case CIL_CLASS:
rc = cil_write_class(node, finished, extra_args);
break;
case CIL_COMMON:
rc = cil_write_class(node, finished, extra_args);
break;
case CIL_MAP_CLASS:
rc = cil_write_unsupported("CIL_MAP_CLASS");
break;
case CIL_CLASSORDER:
rc = cil_write_classorder(node, cil_out);
break;
case CIL_CLASSPERMISSION:
rc = cil_write_unsupported("CIL_CLASSPERMISSION");
break;
case CIL_CLASSPERMISSIONSET:
rc = cil_write_unsupported("CIL_CLASSPERMISSIONSET");
break;
case CIL_CLASSCOMMON:
rc = cil_write_classcommon(node, cil_out);
break;
case CIL_SID:
rc = cil_write_sid(node, cil_out);
break;
case CIL_SIDCONTEXT:
rc = cil_write_sidcontext(node, cil_out);
break;
case CIL_SIDORDER:
rc = cil_write_sidorder(node, cil_out);
break;
case CIL_USER:
rc = cil_write_user(node, cil_out);
break;
case CIL_USERATTRIBUTE:
rc = cil_write_unsupported("CIL_USERATTRIBUTE");
break;
case CIL_USERATTRIBUTESET:
rc = cil_write_unsupported("CIL_USERATTRIBUTESET");
break;
case CIL_USERROLE:
rc = cil_write_userrole(node, cil_out);
break;
case CIL_USERLEVEL:
rc = cil_write_userlevel(node, cil_out);
break;
case CIL_USERRANGE:
rc = cil_write_userrange(node, cil_out);
break;
case CIL_USERBOUNDS:
rc = cil_write_unsupported("CIL_USERBOUNDS");
break;
case CIL_USERPREFIX:
rc = cil_write_unsupported("CIL_USERPREFIX");
break;
case CIL_ROLE:
rc = cil_write_role(node, cil_out);
break;
case CIL_ROLETYPE:
rc = cil_write_roletype(node, cil_out);
break;
case CIL_ROLEBOUNDS:
rc = cil_write_unsupported("CIL_ROLEBOUNDS");
break;
case CIL_ROLEATTRIBUTE:
cil_write_roleattribute(node, cil_out);
break;
case CIL_ROLEATTRIBUTESET:
rc = cil_write_unsupported("CIL_ROLEATTRIBUTESET");
break;
case CIL_ROLEALLOW:
rc = cil_write_unsupported("CIL_ROLEALLOW");
break;
case CIL_TYPE:
rc = cil_write_type(node, cil_out);
break;
case CIL_TYPEBOUNDS:
rc = cil_write_unsupported("CIL_TYPEBOUNDS");
break;
case CIL_TYPEPERMISSIVE:
rc = cil_write_typepermissive(node, cil_out);
break;
case CIL_TYPEATTRIBUTE:
rc = cil_write_typeattribute(node, cil_out);
break;
case CIL_TYPEATTRIBUTESET:
rc = cil_write_typeattributeset(node, cil_out);
break;
case CIL_EXPANDTYPEATTRIBUTE:
rc = cil_write_expandtypeattribute(node, cil_out);
break;
case CIL_TYPEALIAS:
rc = cil_write_alias(node, cil_out);
break;
case CIL_TYPEALIASACTUAL:
rc = cil_write_aliasactual(node, cil_out);
break;
case CIL_ROLETRANSITION:
rc = cil_write_unsupported("CIL_ROLETRANSITION");
break;
case CIL_NAMETYPETRANSITION:
rc = cil_write_nametypetransition(node, cil_out);
break;
case CIL_RANGETRANSITION:
rc = cil_write_unsupported("CIL_RANGETRANSITION");
break;
case CIL_TUNABLE:
rc = cil_write_unsupported("CIL_TUNABLE");
break;
case CIL_BOOL:
rc = cil_write_unsupported("CIL_BOOL");
break;
case CIL_AVRULE:
case CIL_AVRULEX:
rc = cil_write_avrule(node, cil_out);
break;
case CIL_PERMISSIONX:
rc = cil_write_unsupported("CIL_PERMISSIONX");
break;
case CIL_TYPE_RULE:
cil_write_type_rule(node, cil_out);
break;
case CIL_SENS:
rc = cil_write_sens(node, cil_out);
break;
case CIL_SENSALIAS:
rc = cil_write_alias(node, cil_out);
break;
case CIL_SENSALIASACTUAL:
rc = cil_write_aliasactual(node, cil_out);
break;
case CIL_CAT:
rc = cil_write_cat(node, cil_out);
break;
case CIL_CATALIAS:
rc = cil_write_alias(node, cil_out);
break;
case CIL_CATALIASACTUAL:
rc = cil_write_aliasactual(node, cil_out);
break;
case CIL_CATSET:
rc = cil_write_unsupported("CIL_CATSET");
break;
case CIL_SENSCAT:
rc = cil_write_senscat(node, cil_out);
break;
case CIL_CATORDER:
rc = cil_write_catorder(node, cil_out);
break;
case CIL_SENSITIVITYORDER:
rc = cil_write_sensorder(node, cil_out);
break;
case CIL_LEVEL:
rc = cil_write_unsupported("CIL_LEVEL");
break;
case CIL_LEVELRANGE:
rc = cil_write_unsupported("CIL_LEVELRANGE");
break;
case CIL_CONTEXT:
rc = cil_write_unsupported("CIL_CONTEXT");
break;
case CIL_NETIFCON:
rc = cil_write_unsupported("CIL_NETIFCON");
break;
case CIL_GENFSCON:
rc = cil_write_genfscon(node, cil_out);
break;
case CIL_FILECON:
rc = cil_write_unsupported("CIL_FILECON");
break;
case CIL_NODECON:
rc = cil_write_unsupported("CIL_NODECON");
break;
case CIL_PORTCON:
rc = cil_write_unsupported("CIL_PORTCON");
break;
case CIL_PIRQCON:
rc = cil_write_unsupported("CIL_PIRQCON");
break;
case CIL_IOMEMCON:
rc = cil_write_unsupported("CIL_IOMEMCON");
break;
case CIL_IOPORTCON:
rc = cil_write_unsupported("CIL_IOPORTCON");
break;
case CIL_PCIDEVICECON:
rc = cil_write_unsupported("CIL_PCIDEVICECON");
break;
case CIL_DEVICETREECON:
rc = cil_write_unsupported("CIL_DEVICETREECON");
break;
case CIL_FSUSE:
rc = cil_write_fsuse(node, cil_out);
break;
case CIL_CONSTRAIN:
rc = cil_write_unsupported("CIL_CONSTRAIN");
break;
case CIL_MLSCONSTRAIN:
rc = cil_write_constrain(node, cil_out);
break;
case CIL_VALIDATETRANS:
rc = cil_write_unsupported("CIL_VALIDATETRANS");
break;
case CIL_MLSVALIDATETRANS:
rc = cil_write_unsupported("CIL_MLSVALIDATETRANS");
break;
case CIL_CALL:
rc = cil_write_unsupported("CIL_CALL");
break;
case CIL_MACRO:
rc = cil_write_unsupported("CIL_MACRO");
break;
case CIL_NODE:
rc = cil_write_unsupported("CIL_NODE");
break;
case CIL_OPTIONAL:
rc = cil_write_unsupported("CIL_OPTIONAL");
break;
case CIL_IPADDR:
rc = cil_write_unsupported("CIL_IPADDR");
break;
case CIL_CONDBLOCK:
rc = cil_write_unsupported("CIL_CONDBLOCK");
break;
case CIL_BOOLEANIF:
rc = cil_write_unsupported("CIL_BOOLEANIF");
break;
case CIL_TUNABLEIF:
rc = cil_write_unsupported("CIL_TUNABLEIF");
break;
case CIL_DEFAULTUSER:
rc = cil_write_unsupported("CIL_DEFAULTUSER");
break;
case CIL_DEFAULTROLE:
rc = cil_write_unsupported("CIL_DEFAULTROLE");
break;
case CIL_DEFAULTTYPE:
rc = cil_write_unsupported("CIL_DEFAULTTYPE");
break;
case CIL_DEFAULTRANGE:
rc = cil_write_unsupported("CIL_DEFAULTRANGE");
break;
case CIL_SELINUXUSER:
rc = cil_write_unsupported("CIL_SELINUXUSER");
break;
case CIL_SELINUXUSERDEFAULT:
rc = cil_write_unsupported("CIL_SELINUXUSERDEFAULT");
break;
case CIL_HANDLEUNKNOWN:
rc = cil_write_handleunknown(node, cil_out);
break;
case CIL_MLS:
rc = cil_write_mls(node, cil_out);
break;
case CIL_SRC_INFO:
break;
case CIL_NONE:
// TODO: add proper removal support
*finished = CIL_TREE_SKIP_HEAD;
break;
default:
cil_log(CIL_ERR, "Unknown AST flavor: %d.\n", node->flavor);
rc = SEPOL_ERR;
goto exit;
break;
}
exit:
return rc;
}
static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args)
{
int rc = SEPOL_ERR;
struct cil_args_write *args = NULL;
FILE *cil_out = NULL;
if (node == NULL || extra_args == NULL) {
goto exit;
}
args = extra_args;
cil_out = args->cil_out;
if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO) {
fprintf(cil_out,")");
}
rc = SEPOL_OK;
exit:
return rc;
}
/* main exported function */
int cil_write_ast(struct cil_db *db, const char* path) {
int rc = SEPOL_ERR;
struct cil_args_write extra_args;
FILE *cil_out = NULL;
cil_out = fopen(path, "we");
if (cil_out == NULL) {
cil_log(CIL_ERR, "Failure opening output file for writing AST\n");
rc = SEPOL_ERR;
goto exit;
}
extra_args.cil_out = cil_out;
extra_args.db = db;
rc = cil_tree_walk(db->ast->root, __cil_write_node_helper,
__cil_write_first_child_helper,
__cil_write_last_child_helper,
&extra_args);
if (rc != SEPOL_OK) {
cil_log(CIL_INFO, "cil_tree_walk failed, rc: %d\n", rc);
goto exit;
}
exit:
fclose(cil_out);
cil_out = NULL;
return rc;
}