/**************************************************************** * secpol_xml2bin.c * * Copyright (C) 2005 IBM Corporation * * Author: Reiner Sailer * * Maintained: * Reiner Sailer * Ray Valdez * * 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, version 2 of the * License. * * sHype policy translation tool. This tool takes an XML * policy specification as input and produces a binary * policy file that can be loaded into Xen through the * ACM operations (xensec_tool loadpolicy) interface or at * boot time (grub module parameter) * * indent -i4 -kr -nut */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "secpol_xml2bin.h" #define DEBUG 0 #define NULL_LABEL_NAME "__NULL_LABEL__" #define ROUND8(x) ((x + 7) & ~7) /* primary / secondary policy component setting */ enum policycomponent { CHWALL, STE, NULLPOLICY } primary = NULLPOLICY, secondary = NULLPOLICY; /* general list element for ste and chwall type queues */ struct type_entry { TAILQ_ENTRY(type_entry) entries; char *name; /* name of type from xml file */ type_t mapping; /* type mapping into 16bit */ }; TAILQ_HEAD(tailhead, type_entry) ste_head, chwall_head; /* general list element for all label queues */ enum label_type { VM, RES, ANY }; struct ssid_entry { TAILQ_ENTRY(ssid_entry) entries; char *name; /* label name */ enum label_type type; /* type: VM / RESOURCE LABEL */ u_int32_t num; /* ssid or referenced ssid */ int is_ref; /* if this entry references earlier ssid number */ unsigned char *row; /* index of types (if not a reference) */ }; TAILQ_HEAD(tailhead_ssid, ssid_entry) ste_ssid_head, chwall_ssid_head, conflictsets_head; struct ssid_entry *current_chwall_ssid_p = NULL; struct ssid_entry *current_ste_ssid_p = NULL; struct ssid_entry *current_conflictset_p = NULL; /* which label to assign to dom0 during boot */ char *bootstrap_label; u_int32_t max_ste_ssids = 0; u_int32_t max_chwall_ssids = 0; u_int32_t max_chwall_labels = 0; u_int32_t max_ste_labels = 0; u_int32_t max_conflictsets = 0; char *current_ssid_name; /* store name until structure is allocated */ char *current_conflictset_name; /* store name until structure is allocated */ /* dynamic list of type mappings for STE */ u_int32_t max_ste_types = 0; /* dynamic list of type mappings for CHWALL */ u_int32_t max_chwall_types = 0; /* dynamic list of conflict sets */ int max_conflict_set = 0; /* which policies are defined */ int have_ste = 0; int have_chwall = 0; /* input/output file names */ char *policy_filename = NULL, *binary_filename = NULL, *mapping_filename = NULL, *schema_filename = NULL; char *policy_reference_name = NULL; char *policy_version_string = NULL; void walk_labels(xmlNode * start, xmlDocPtr doc, unsigned long state); void usage(char *prg) { printf( "Usage: %s [OPTIONS] POLICYNAME\n" "POLICYNAME is the directory name within the policy directory\n" "that contains the policy files. The default policy directory\n" "is '%s' (see the '-d' option below to change it)\n" "The policy files contained in the POLICYNAME directory must be named:\n" "\tPOLICYNAME-security_policy.xml\n" "\tPOLICYNAME-security_label_template.xml\n\n" "OPTIONS:\n" "\t-d POLICYDIR\n" "\t\tUse POLICYDIR as the policy directory. This directory must \n" "\t\tcontain the policy schema file 'security_policy.xsd'\n", prg, POLICY_DIR); exit(EXIT_FAILURE); } /***************** policy-related parsing *********************/ char *type_by_mapping(struct tailhead *head, u_int32_t mapping) { struct type_entry *np; for (np = head->tqh_first; np != NULL; np = np->entries.tqe_next) if (np->mapping == mapping) return np->name; return NULL; } struct type_entry *lookup(struct tailhead *head, char *name) { struct type_entry *np; for (np = head->tqh_first; np != NULL; np = np->entries.tqe_next) if (!(strcmp(np->name, name))) return np; return NULL; } /* enforces single-entry lists */ int add_entry(struct tailhead *head, char *name, type_t mapping) { struct type_entry *e; if (lookup(head, name)) { printf("Error: Type >%s< defined more than once.\n", name); return -EFAULT; /* already in the list */ } if (!(e = malloc(sizeof(struct type_entry)))) return -ENOMEM; e->name = name; e->mapping = mapping; TAILQ_INSERT_TAIL(head, e, entries); return 0; } int totoken(char *tok) { int i; for (i = 0; token[i] != NULL; i++) if (!strcmp(token[i], tok)) return i; return -EFAULT; } /* conflictsets use the same data structure as ssids; since * they are similar in structure (set of types) */ int init_next_conflictset(void) { struct ssid_entry *conflictset = malloc(sizeof(struct ssid_entry)); if (!conflictset) return -ENOMEM; conflictset->name = current_conflictset_name; conflictset->num = max_conflictsets++; conflictset->is_ref = 0; /* n/a for conflictsets */ /** * row: allocate one byte per type; * [i] != 0 --> mapped type >i< is part of the conflictset */ conflictset->row = malloc(max_chwall_types); if (!conflictset->row) return -ENOMEM; memset(conflictset->row, 0, max_chwall_types); TAILQ_INSERT_TAIL(&conflictsets_head, conflictset, entries); current_conflictset_p = conflictset; return 0; } int register_type(xmlNode * cur_node, xmlDocPtr doc, unsigned long state) { xmlChar *text; struct type_entry *e; text = xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!text) { printf("Error reading type name!\n"); return -EFAULT; } switch (state) { case XML2BIN_stetype_S: if (add_entry(&ste_head, (char *) text, max_ste_types)) { xmlFree(text); return -EFAULT; } max_ste_types++; break; case XML2BIN_chwalltype_S: if (add_entry(&chwall_head, (char *) text, max_chwall_types)) { xmlFree(text); return -EFAULT; } max_chwall_types++; break; case XML2BIN_conflictsettype_S: /* a) search the type in the chwall_type list */ e = lookup(&chwall_head, (char *) text); if (e == NULL) { printf("CS type >%s< not a CHWALL type.\n", text); xmlFree(text); return -EFAULT; } /* b) add type entry to the current cs set */ if (current_conflictset_p->row[e->mapping]) { printf ("ERROR: Double entry of type >%s< in conflict set %d.\n", text, current_conflictset_p->num); xmlFree(text); return -EFAULT; } current_conflictset_p->row[e->mapping] = 1; break; default: printf("Incorrect type environment (state = %lx, text = %s).\n", state, text); xmlFree(text); return -EFAULT; } return 0; } void set_component_type(xmlNode * cur_node, enum policycomponent pc) { xmlChar *order; if ((order = xmlGetProp(cur_node, (xmlChar *) PRIMARY_COMPONENT_ATTR_NAME))) { if (strcmp((char *) order, PRIMARY_COMPONENT)) { printf("ERROR: Illegal attribut value >order=%s<.\n", (char *) order); xmlFree(order); exit(EXIT_FAILURE); } if (primary != NULLPOLICY) { printf("ERROR: Primary Policy Component set twice!\n"); exit(EXIT_FAILURE); } primary = pc; xmlFree(order); } } void walk_policy(xmlNode * start, xmlDocPtr doc, unsigned long state) { xmlNode *cur_node = NULL; int code; for (cur_node = start; cur_node; cur_node = cur_node->next) { if ((code = totoken((char *) cur_node->name)) < 0) { printf("Unknown token: >%s<. Aborting.\n", cur_node->name); exit(EXIT_FAILURE); } switch (code) { /* adjust state to new state */ case XML2BIN_SECPOL: case XML2BIN_STETYPES: case XML2BIN_CHWALLTYPES: case XML2BIN_CONFLICTSETS: case XML2BIN_POLICYHEADER: case XML2BIN_FROMPOLICY: walk_policy(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_POLICYNAME: /* get policy reference name .... */ if (state != XML2BIN_PN_S && state != XML2BIN_PN_frompolicy_S) { printf("ERROR: >Url< >%s< out of context.\n", (char *) xmlNodeListGetString(doc, cur_node-> xmlChildrenNode, 1)); exit(EXIT_FAILURE); } if (state == XML2BIN_PN_S) { policy_reference_name = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!policy_reference_name) { printf("ERROR: empty >policy reference name (Url)Url< >%s< out of context.\n", (char *) xmlNodeListGetString(doc, cur_node-> xmlChildrenNode, 1)); exit(EXIT_FAILURE); } if (state == XML2BIN_PN_S) { policy_version_string = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!policy_version_string) { printf("ERROR: empty >policy version string children, doc, state | (1 << code)); break; case XML2BIN_CHWALL: if (WRITTEN_AGAINST_ACM_CHWALL_VERSION != ACM_CHWALL_VERSION) { printf ("ERROR: This program was written against another CHWALL version.\n"); exit(EXIT_FAILURE); } have_chwall = 1; set_component_type(cur_node, CHWALL); walk_policy(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_CSTYPE: current_conflictset_name = (char *) xmlGetProp(cur_node, (xmlChar *) "name"); if (!current_conflictset_name) current_conflictset_name = ""; if (init_next_conflictset()) { printf ("ERROR: creating new conflictset structure failed.\n"); exit(EXIT_FAILURE); } walk_policy(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_TYPE: if (register_type(cur_node, doc, state)) exit(EXIT_FAILURE); /* type leaf */ break; case XML2BIN_LABELTEMPLATE: /* handle in second pass */ case XML2BIN_TEXT: case XML2BIN_COMMENT: case XML2BIN_DATE: case XML2BIN_REFERENCE: case XML2BIN_NSURL: /* for future use: where to find global label / type name mappings */ case XML2BIN_URL: /* for future use: where to find policy */ /* leaf - nothing to do */ break; default: printf("Unkonwn token Error (%d) in Policy\n", code); exit(EXIT_FAILURE); } } return; } void init_type_mapping(void) { printf("Creating ssid mappings ...\n"); /* initialize the ste and chwall type lists */ TAILQ_INIT(&ste_head); TAILQ_INIT(&chwall_head); TAILQ_INIT(&conflictsets_head); } void post_type_mapping(void) { struct type_entry *te; struct ssid_entry *se; int i; /* determine primary/secondary policy component orders */ if ((primary == NULLPOLICY) && have_chwall) primary = CHWALL; /* default if not set */ else if ((primary == NULLPOLICY) && have_ste) primary = STE; switch (primary) { case CHWALL: if (have_ste) secondary = STE; /* else default = NULLPOLICY */ break; case STE: if (have_chwall) secondary = CHWALL; /* else default = NULLPOLICY */ break; default: /* NULL/NULL policy */ break; } if (!DEBUG) return; /* print queues */ if (have_ste) { printf("STE-Type queue (%s):\n", (primary == STE) ? "PRIMARY" : "SECONDARY"); for (te = ste_head.tqh_first; te != NULL; te = te->entries.tqe_next) printf("name=%22s, map=%x\n", te->name, te->mapping); } if (have_chwall) { printf("CHWALL-Type queue (%s):\n", (primary == CHWALL) ? "PRIMARY" : "SECONDARY"); for (te = chwall_head.tqh_first; te != NULL; te = te->entries.tqe_next) printf("name=%s, map=%x\n", te->name, te->mapping); printf("Conflictset queue (max=%d):\n", max_conflictsets); for (se = conflictsets_head.tqh_first; se != NULL; se = se->entries.tqe_next) { printf("conflictset name >%s<\n", se->name ? se->name : "NONAME"); for (i = 0; i < max_chwall_types; i++) if (se->row[i]) printf("#%x ", i); printf("\n"); } } } /***************** template-related parsing *********************/ /* add default ssid at head of ssid queues */ int init_ssid_queues(void) { struct ssid_entry *default_ssid_chwall, *default_ssid_ste; default_ssid_chwall = malloc(sizeof(struct ssid_entry)); default_ssid_ste = malloc(sizeof(struct ssid_entry)); if ((!default_ssid_chwall) || (!default_ssid_ste)) return -ENOMEM; /* default chwall ssid */ default_ssid_chwall->name = NULL_LABEL_NAME; default_ssid_chwall->num = max_chwall_ssids++; default_ssid_chwall->is_ref = 0; default_ssid_chwall->type = ANY; default_ssid_chwall->row = malloc(max_chwall_types); if (!default_ssid_chwall->row) return -ENOMEM; memset(default_ssid_chwall->row, 0, max_chwall_types); TAILQ_INSERT_TAIL(&chwall_ssid_head, default_ssid_chwall, entries); current_chwall_ssid_p = default_ssid_chwall; max_chwall_labels++; /* default ste ssid */ default_ssid_ste->name = NULL_LABEL_NAME; default_ssid_ste->num = max_ste_ssids++; default_ssid_ste->is_ref = 0; default_ssid_ste->type = ANY; default_ssid_ste->row = malloc(max_ste_types); if (!default_ssid_ste->row) return -ENOMEM; memset(default_ssid_ste->row, 0, max_ste_types); TAILQ_INSERT_TAIL(&ste_ssid_head, default_ssid_ste, entries); current_ste_ssid_p = default_ssid_ste; max_ste_labels++; return 0; } int init_next_chwall_ssid(unsigned long state) { struct ssid_entry *ssid = malloc(sizeof(struct ssid_entry)); if (!ssid) return -ENOMEM; ssid->name = current_ssid_name; ssid->num = max_chwall_ssids++; ssid->is_ref = 0; if (state & (1 << XML2BIN_VM)) ssid->type = VM; else ssid->type = RES; /** * row: allocate one byte per type; * [i] != 0 --> mapped type >i< is part of the ssid */ ssid->row = malloc(max_chwall_types); if (!ssid->row) return -ENOMEM; memset(ssid->row, 0, max_chwall_types); TAILQ_INSERT_TAIL(&chwall_ssid_head, ssid, entries); current_chwall_ssid_p = ssid; max_chwall_labels++; return 0; } int init_next_ste_ssid(unsigned long state) { struct ssid_entry *ssid = malloc(sizeof(struct ssid_entry)); if (!ssid) return -ENOMEM; ssid->name = current_ssid_name; ssid->num = max_ste_ssids++; ssid->is_ref = 0; if (state & (1 << XML2BIN_VM)) ssid->type = VM; else ssid->type = RES; /** * row: allocate one byte per type; * [i] != 0 --> mapped type >i< is part of the ssid */ ssid->row = malloc(max_ste_types); if (!ssid->row) return -ENOMEM; memset(ssid->row, 0, max_ste_types); TAILQ_INSERT_TAIL(&ste_ssid_head, ssid, entries); current_ste_ssid_p = ssid; max_ste_labels++; return 0; } /* adds a type to the current ssid */ int add_type(xmlNode * cur_node, xmlDocPtr doc, unsigned long state) { xmlChar *text; struct type_entry *e; text = xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!text) { printf("Error reading type name!\n"); return -EFAULT; } /* same for all: 1. lookup type mapping, 2. mark type in ssid */ switch (state) { case XML2BIN_VM_STE_S: case XML2BIN_RES_STE_S: /* lookup the type mapping and include the type mapping into the array */ if (!(e = lookup(&ste_head, (char *) text))) { printf("ERROR: unknown VM STE type >%s<.\n", text); exit(EXIT_FAILURE); } if (current_ste_ssid_p->row[e->mapping]) printf("Warning: double entry of VM STE type >%s<.\n", text); current_ste_ssid_p->row[e->mapping] = 1; break; case XML2BIN_VM_CHWALL_S: /* lookup the type mapping and include the type mapping into the array */ if (!(e = lookup(&chwall_head, (char *) text))) { printf("ERROR: unknown VM CHWALL type >%s<.\n", text); exit(EXIT_FAILURE); } if (current_chwall_ssid_p->row[e->mapping]) printf("Warning: double entry of VM CHWALL type >%s<.\n", text); current_chwall_ssid_p->row[e->mapping] = 1; break; default: printf("Incorrect type environment (state = %lx, text = %s).\n", state, text); xmlFree(text); return -EFAULT; } return 0; } void set_bootstrap_label(xmlNode * cur_node) { xmlChar *order; if ((order = xmlGetProp(cur_node, (xmlChar *) BOOTSTRAP_LABEL_ATTR_NAME))) bootstrap_label = (char *) order; else { printf("ERROR: No bootstrap label defined!\n"); exit(EXIT_FAILURE); } } void walk_labels(xmlNode * start, xmlDocPtr doc, unsigned long state) { xmlNode *cur_node = NULL; int code; for (cur_node = start; cur_node; cur_node = cur_node->next) { if ((code = totoken((char *) cur_node->name)) < 0) { printf("Unkonwn token: >%s<. Aborting.\n", cur_node->name); exit(EXIT_FAILURE); } switch (code) { /* adjust state to new state */ case XML2BIN_SUBJECTS: set_bootstrap_label(cur_node); /* fall through */ case XML2BIN_SECPOL: case XML2BIN_LABELTEMPLATE: case XML2BIN_VM: case XML2BIN_RES: case XML2BIN_OBJECTS: walk_labels(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_STETYPES: /* create new ssid entry to use and point current to it */ if (init_next_ste_ssid(state)) { printf("ERROR: creating new ste ssid structure failed.\n"); exit(EXIT_FAILURE); } walk_labels(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_CHWALLTYPES: /* create new ssid entry to use and point current to it */ if (init_next_chwall_ssid(state)) { printf ("ERROR: creating new chwall ssid structure failed.\n"); exit(EXIT_FAILURE); } walk_labels(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_TYPE: /* add type to current ssid */ if (add_type(cur_node, doc, state)) exit(EXIT_FAILURE); break; case XML2BIN_NAME: if ((state == XML2BIN_VM_S) || (state == XML2BIN_RES_S)) { current_ssid_name = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!current_ssid_name) { printf("ERROR: empty >vm/res namename< >%s< out of context (state = 0x%lx.\n", (char *) xmlNodeListGetString(doc, cur_node-> xmlChildrenNode, 1), state); exit(EXIT_FAILURE); } break; case XML2BIN_TEXT: case XML2BIN_COMMENT: case XML2BIN_POLICYHEADER: case XML2BIN_STE: case XML2BIN_CHWALL: break; default: printf("Unkonwn token Error (%d) in Label Template\n", code); exit(EXIT_FAILURE); } } return; } /* * will go away as soon as we have non-static bootstrap ssidref for dom0 */ void fixup_bootstrap_label(struct tailhead_ssid *head, u_int32_t max_types, u_int32_t * max_ssids) { struct ssid_entry *np; int i; /* should not happen if xml / xsd checks work */ if (!bootstrap_label) { printf("ERROR: No bootstrap label defined.\n"); exit(EXIT_FAILURE); } /* search bootstrap_label */ for (np = head->tqh_first; np != NULL; np = np->entries.tqe_next) { if (!strcmp(np->name, bootstrap_label)) { break; } } if (!np) { /* bootstrap label not found */ printf("ERROR: Bootstrap label >%s< not found.\n", bootstrap_label); exit(EXIT_FAILURE); } /* move this entry ahead in the list right after the default entry so it * receives ssidref 1/1 */ TAILQ_REMOVE(head, np, entries); TAILQ_INSERT_AFTER(head, head->tqh_first, np, entries); /* renumber the ssids (we could also just switch places with 1st element) */ for (np = head->tqh_first, i = 0; np != NULL; np = np->entries.tqe_next, i++) np->num = i; } void init_label_mapping(void) { printf("Creating label mappings ...\n"); /* initialize the ste and chwall type lists */ TAILQ_INIT(&chwall_ssid_head); TAILQ_INIT(&ste_ssid_head); /* init with default ssids */ if (init_ssid_queues()) { printf("ERROR adding default ssids.\n"); exit(EXIT_FAILURE); } } void post_label_mapping(void) { struct ssid_entry *np; int i; /* * now sort bootstrap label to the head of the list * (for now), dom0 assumes its label in the first * defined ssidref (1/1). 0/0 is the default non-Label */ if (have_chwall) fixup_bootstrap_label(&chwall_ssid_head, max_chwall_types, &max_chwall_ssids); if (have_ste) fixup_bootstrap_label(&ste_ssid_head, max_ste_types, &max_ste_ssids); if (!DEBUG) return; /* print queues */ if (have_chwall) { printf("CHWALL SSID queue (max ssidrefs=%d):\n", max_chwall_ssids); np = NULL; for (np = chwall_ssid_head.tqh_first; np != NULL; np = np->entries.tqe_next) { printf("SSID #%02u (Label=%s)\n", np->num, np->name); if (np->is_ref) printf("REFERENCE"); else for (i = 0; i < max_chwall_types; i++) if (np->row[i]) printf("#%02d ", i); printf("\n\n"); } } if (have_ste) { printf("STE SSID queue (max ssidrefs=%d):\n", max_ste_ssids); np = NULL; for (np = ste_ssid_head.tqh_first; np != NULL; np = np->entries.tqe_next) { printf("SSID #%02u (Label=%s)\n", np->num, np->name); if (np->is_ref) printf("REFERENCE"); else for (i = 0; i < max_ste_types; i++) if (np->row[i]) printf("#%02d ", i); printf("\n\n"); } } } void create_mappings(xmlDocPtr doc) { xmlNode *doc_root_node = xmlDocGetRootElement(doc); /* walk the XML policy tree and fill in types and labels */ init_type_mapping(); walk_policy(doc_root_node, doc, XML2BIN_NULL); /* first pass: types */ post_type_mapping(); init_label_mapping(); walk_labels(doc_root_node, doc, XML2BIN_NULL); /* second pass: labels */ post_label_mapping(); } /***************** writing the binary policy *********************/ /* * the mapping file is ascii-based since it will likely be used from * within scripts (using awk, grep, etc.); * * We print from high-level to low-level information so that with one * pass, any symbol can be resolved (e.g. Label -> types) */ int write_mapping(char *filename) { struct ssid_entry *e; struct type_entry *t; int i; FILE *file; if ((file = fopen(filename, "w")) == NULL) return -EIO; fprintf(file, "POLICYREFERENCENAME %s\n", policy_reference_name); fprintf(file, "MAGIC %08x\n", ACM_MAGIC); fprintf(file, "POLICY FILE %s\n", policy_filename); fprintf(file, "BINARY FILE %s\n", binary_filename); if (have_chwall) { fprintf(file, "MAX-CHWALL-TYPES %08x\n", max_chwall_types); fprintf(file, "MAX-CHWALL-SSIDS %08x\n", max_chwall_ssids); fprintf(file, "MAX-CHWALL-LABELS %08x\n", max_chwall_labels); } if (have_ste) { fprintf(file, "MAX-STE-TYPES %08x\n", max_ste_types); fprintf(file, "MAX-STE-SSIDS %08x\n", max_ste_ssids); fprintf(file, "MAX-STE-LABELS %08x\n", max_ste_labels); } fprintf(file, "\n"); /* primary / secondary order for combined ssid synthesis/analysis * if no primary is named, then chwall is primary */ switch (primary) { case CHWALL: fprintf(file, "PRIMARY CHWALL\n"); break; case STE: fprintf(file, "PRIMARY STE\n"); break; default: fprintf(file, "PRIMARY NULL\n"); break; } switch (secondary) { case CHWALL: fprintf(file, "SECONDARY CHWALL\n"); break; case STE: fprintf(file, "SECONDARY STE\n"); break; default: fprintf(file, "SECONDARY NULL\n"); break; } fprintf(file, "\n"); /* first labels to ssid mappings */ if (have_chwall) { for (e = chwall_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { fprintf(file, "LABEL->SSID %s CHWALL %-25s %8x\n", (e->type == VM) ? "VM " : ((e->type == RES) ? "RES" : "ANY"), e->name, e->num); } fprintf(file, "\n"); } if (have_ste) { for (e = ste_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { fprintf(file, "LABEL->SSID %s STE %-25s %8x\n", (e->type == VM) ? "VM " : ((e->type == RES) ? "RES" : "ANY"), e->name, e->num); } fprintf(file, "\n"); } /* second ssid to type mappings */ if (have_chwall) { for (e = chwall_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { if (e->is_ref) continue; fprintf(file, "SSID->TYPE CHWALL %08x", e->num); for (i = 0; i < max_chwall_types; i++) if (e->row[i]) fprintf(file, " %s", type_by_mapping(&chwall_head, i)); fprintf(file, "\n"); } fprintf(file, "\n"); } if (have_ste) { for (e = ste_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { if (e->is_ref) continue; fprintf(file, "SSID->TYPE STE %08x", e->num); for (i = 0; i < max_ste_types; i++) if (e->row[i]) fprintf(file, " %s", type_by_mapping(&ste_head, i)); fprintf(file, "\n"); } fprintf(file, "\n"); } /* third type mappings */ if (have_chwall) { for (t = chwall_head.tqh_first; t != NULL; t = t->entries.tqe_next) { fprintf(file, "TYPE CHWALL %-25s %8x\n", t->name, t->mapping); } fprintf(file, "\n"); } if (have_ste) { for (t = ste_head.tqh_first; t != NULL; t = t->entries.tqe_next) { fprintf(file, "TYPE STE %-25s %8x\n", t->name, t->mapping); } fprintf(file, "\n"); } fclose(file); return 0; } unsigned char *write_policy_reference_binary(u_int32_t * len_pr) { unsigned char *buf, *ptr; struct acm_policy_reference_buffer *pr_header; u_int32_t len; u_int32_t name_len; if (policy_reference_name == NULL) { printf("ERROR: No policy reference name found.\n"); exit(EXIT_FAILURE); } name_len = strlen(policy_reference_name) + 1; /* strend '\0' */ len = sizeof(struct acm_policy_reference_buffer) + name_len; len = (len + 7) & ~7; /* Alignment. */ buf = malloc(len); ptr = buf; if (!buf) { printf ("ERROR: out of memory allocating label reference buffer.\n"); exit(EXIT_FAILURE); } memset (buf, 0, len); pr_header = (struct acm_policy_reference_buffer *) buf; pr_header->len = htonl(name_len); ptr += sizeof(struct acm_policy_reference_buffer); strcpy((char *) ptr, policy_reference_name); (*len_pr) = len; return buf; } unsigned char *write_chwall_binary(u_int32_t * len_chwall) { unsigned char *buf, *ptr; struct acm_chwall_policy_buffer *chwall_header; u_int32_t len; struct ssid_entry *e; int i; if (!have_chwall) return NULL; len = sizeof(struct acm_chwall_policy_buffer) + sizeof(type_t) * max_chwall_types * max_chwall_ssids + sizeof(type_t) * max_chwall_types * max_conflictsets; buf = malloc(len); ptr = buf; if (!buf) { printf("ERROR: out of memory allocating chwall buffer.\n"); exit(EXIT_FAILURE); } /* chwall has 3 parts : header, types, conflictsets */ chwall_header = (struct acm_chwall_policy_buffer *) buf; chwall_header->chwall_max_types = htonl(max_chwall_types); chwall_header->chwall_max_ssidrefs = htonl(max_chwall_ssids); chwall_header->policy_code = htonl(ACM_CHINESE_WALL_POLICY); chwall_header->policy_version = htonl(ACM_CHWALL_VERSION); chwall_header->chwall_ssid_offset = htonl(sizeof(struct acm_chwall_policy_buffer)); chwall_header->chwall_max_conflictsets = htonl(max_conflictsets); chwall_header->chwall_conflict_sets_offset = htonl(ntohl(chwall_header->chwall_ssid_offset) + sizeof(domaintype_t) * max_chwall_ssids * max_chwall_types); chwall_header->chwall_running_types_offset = 0; chwall_header->chwall_conflict_aggregate_offset = 0; ptr += sizeof(struct acm_chwall_policy_buffer); /* types */ for (e = chwall_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { if (e->is_ref) continue; for (i = 0; i < max_chwall_types; i++) ((type_t *) ptr)[i] = htons((type_t) e->row[i]); ptr += sizeof(type_t) * max_chwall_types; } /* conflictsets */ for (e = conflictsets_head.tqh_first; e != NULL; e = e->entries.tqe_next) { for (i = 0; i < max_chwall_types; i++) ((type_t *) ptr)[i] = htons((type_t) e->row[i]); ptr += sizeof(type_t) * max_chwall_types; } if ((ptr - buf) != len) { printf("ERROR: wrong lengths in %s.\n", __func__); exit(EXIT_FAILURE); } (*len_chwall) = len; return buf; } unsigned char *write_ste_binary(u_int32_t * len_ste) { unsigned char *buf, *ptr; struct acm_ste_policy_buffer *ste_header; struct ssid_entry *e; u_int32_t len; int i; if (!have_ste) return NULL; len = sizeof(struct acm_ste_policy_buffer) + sizeof(type_t) * max_ste_types * max_ste_ssids; buf = malloc(len); ptr = buf; if (!buf) { printf("ERROR: out of memory allocating chwall buffer.\n"); exit(EXIT_FAILURE); } /* fill buffer */ ste_header = (struct acm_ste_policy_buffer *) buf; ste_header->policy_version = htonl(ACM_STE_VERSION); ste_header->policy_code = htonl(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY); ste_header->ste_max_types = htonl(max_ste_types); ste_header->ste_max_ssidrefs = htonl(max_ste_ssids); ste_header->ste_ssid_offset = htonl(sizeof(struct acm_ste_policy_buffer)); ptr += sizeof(struct acm_ste_policy_buffer); /* types */ for (e = ste_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { if (e->is_ref) continue; for (i = 0; i < max_ste_types; i++) ((type_t *) ptr)[i] = htons((type_t) e->row[i]); ptr += sizeof(type_t) * max_ste_types; } if ((ptr - buf) != len) { printf("ERROR: wrong lengths in %s.\n", __func__); exit(EXIT_FAILURE); } (*len_ste) = len; return buf; /* for now */ } static ssize_t write_padded(int fd, const void *buf, size_t count) { int rc; static const char padding[7] = {0,0,0,0,0,0,0}; unsigned int len = ROUND8(count) - count; rc = write(fd, buf, count); if (rc == count && len > 0) { write(fd, padding, len); } return rc; } int write_binary(char *filename) { struct acm_policy_buffer header; unsigned char *ste_buffer = NULL, *chwall_buffer = NULL, *policy_reference_buffer = NULL; u_int32_t len; int fd, ret = 0; uint32_t major = 0, minor = 0; u_int32_t len_ste = 0, len_chwall = 0, len_pr = 0; /* length of policy components */ if (policy_version_string) sscanf(policy_version_string,"%d.%d", &major, &minor); /* open binary file */ if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) <= 0) { ret = -EIO; goto out1; } policy_reference_buffer = write_policy_reference_binary(&len_pr); ste_buffer = write_ste_binary(&len_ste); chwall_buffer = write_chwall_binary(&len_chwall); /* determine primary component (default chwall) */ header.policy_version = htonl(ACM_POLICY_VERSION); header.magic = htonl(ACM_MAGIC); header.xml_pol_version.major = htonl(major); header.xml_pol_version.minor = htonl(minor); len = ROUND8(sizeof(struct acm_policy_buffer)); if (have_chwall) len += ROUND8(len_chwall); if (have_ste) len += ROUND8(len_ste); len += ROUND8(len_pr); /* policy reference is mandatory */ header.len = htonl(len); header.policy_reference_offset = htonl(ROUND8(sizeof(struct acm_policy_buffer))); header.primary_buffer_offset = htonl(ROUND8(sizeof(struct acm_policy_buffer)) + ROUND8(len_pr)); if (primary == CHWALL) { header.primary_policy_code = htonl(ACM_CHINESE_WALL_POLICY); header.secondary_buffer_offset = htonl(ROUND8(sizeof(struct acm_policy_buffer)) + ROUND8(len_pr) + ROUND8(len_chwall)); } else if (primary == STE) { header.primary_policy_code = htonl(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY); header.secondary_buffer_offset = htonl(ROUND8(sizeof(struct acm_policy_buffer)) + ROUND8(len_pr) + ROUND8(len_ste)); } else { /* null policy */ header.primary_policy_code = htonl(ACM_NULL_POLICY); header.secondary_buffer_offset = header.primary_buffer_offset; } if (secondary == CHWALL) header.secondary_policy_code = htonl(ACM_CHINESE_WALL_POLICY); else if (secondary == STE) header.secondary_policy_code = htonl(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY); else header.secondary_policy_code = htonl(ACM_NULL_POLICY); if (write_padded(fd, (void *) &header, sizeof(struct acm_policy_buffer)) != sizeof(struct acm_policy_buffer)) { ret = -EIO; goto out1; } /* write label reference name */ if (write_padded(fd, policy_reference_buffer, len_pr) != len_pr) { ret = -EIO; goto out1; } /* write primary policy component */ if (primary == CHWALL) { if (write_padded(fd, chwall_buffer, len_chwall) != len_chwall) { ret = -EIO; goto out1; } } else if (primary == STE) { if (write_padded(fd, ste_buffer, len_ste) != len_ste) { ret = -EIO; goto out1; } } else; /* NULL POLICY has no policy data */ /* write secondary policy component */ if (secondary == CHWALL) { if (write_padded(fd, chwall_buffer, len_chwall) != len_chwall) { ret = -EIO; goto out1; } } else if (secondary == STE) { if (write_padded(fd, ste_buffer, len_ste) != len_ste) { ret = -EIO; goto out1; } } else; /* NULL POLICY has no policy data */ out1: /* cleanup */ if (policy_reference_buffer) free(policy_reference_buffer); if (chwall_buffer) free(chwall_buffer); if (ste_buffer) free(ste_buffer); close(fd); return ret; } int is_valid(xmlDocPtr doc) { int err = 0; xmlSchemaPtr schema_ctxt = NULL; xmlSchemaParserCtxtPtr schemaparser_ctxt = NULL; xmlSchemaValidCtxtPtr schemavalid_ctxt = NULL; schemaparser_ctxt = xmlSchemaNewParserCtxt(schema_filename); schema_ctxt = xmlSchemaParse(schemaparser_ctxt); schemavalid_ctxt = xmlSchemaNewValidCtxt(schema_ctxt); #ifdef VALIDATE_SCHEMA /* only tested to be available from libxml2-2.6.20 upwards */ if ((err = xmlSchemaIsValid(schemavalid_ctxt)) != 1) { printf("ERROR: Invalid schema file %s (err=%d)\n", schema_filename, err); err = -EIO; goto out; } else printf("XML Schema %s valid.\n", schema_filename); #endif if ((err = xmlSchemaValidateDoc(schemavalid_ctxt, doc))) { err = -EIO; goto out; } out: xmlSchemaFreeValidCtxt(schemavalid_ctxt); xmlSchemaFreeParserCtxt(schemaparser_ctxt); xmlSchemaFree(schema_ctxt); return (err != 0) ? 0 : 1; } int main(int argc, char **argv) { xmlDocPtr policydoc = NULL; int err = EXIT_FAILURE; char *file_prefix; int prefix_len; int opt_char; char *policy_dir = POLICY_DIR; if (ACM_POLICY_VERSION != WRITTEN_AGAINST_ACM_POLICY_VERSION) { printf ("ERROR: This program was written against an older ACM version.\n"); printf("ERROR: ACM_POLICY_VERSION=%d, WRITTEN AGAINST= %d.\n", ACM_POLICY_VERSION, WRITTEN_AGAINST_ACM_POLICY_VERSION); exit(EXIT_FAILURE); } while ((opt_char = getopt(argc, argv, "d:")) != -1) { switch (opt_char) { case 'd': policy_dir = malloc(strlen(optarg) + 2); /* null terminator and possibly "/" */ if (!policy_dir) { printf("ERROR allocating directory name memory.\n"); exit(EXIT_FAILURE); } strcpy(policy_dir, optarg); if (policy_dir[strlen(policy_dir) - 1] != '/') strcat(policy_dir, "/"); break; default: usage(basename(argv[0])); } } if ((argc - optind) != 1) usage(basename(argv[0])); printf("arg=%s\n", argv[optind]); prefix_len = strlen(policy_dir) + strlen(argv[optind]) + 1 /* null terminator */ ; file_prefix = malloc(prefix_len); policy_filename = malloc(prefix_len + strlen(POLICY_EXTENSION)); binary_filename = malloc(prefix_len + strlen(BINARY_EXTENSION)); mapping_filename = malloc(prefix_len + strlen(MAPPING_EXTENSION)); schema_filename = malloc(strlen(policy_dir) + strlen(SCHEMA_FILENAME) + 1); if (!file_prefix || !policy_filename || !binary_filename || !mapping_filename || !schema_filename) { printf("ERROR allocating file name memory.\n"); goto out2; } /* create input/output filenames out of prefix */ strcpy(file_prefix, policy_dir); strcat(file_prefix, argv[optind]); strcpy(policy_filename, file_prefix); strcpy(binary_filename, file_prefix); strcpy(mapping_filename, file_prefix); strcat(policy_filename, POLICY_EXTENSION); strcat(binary_filename, BINARY_EXTENSION); strcat(mapping_filename, MAPPING_EXTENSION); strcpy(schema_filename, policy_dir); strcat(schema_filename, SCHEMA_FILENAME); policydoc = xmlParseFile(policy_filename); if (policydoc == NULL) { printf("Error: could not parse file %s.\n", argv[optind]); goto out; } printf("Validating policy file %s...\n", policy_filename); if (!is_valid(policydoc)) { printf("ERROR: Failed schema-validation for file %s (err=%d)\n", policy_filename, err); goto out; } /* create mappings */ create_mappings(policydoc); /* write label mapping file */ if (write_mapping(mapping_filename)) { printf("ERROR: writing mapping file %s.\n", mapping_filename); goto out; } /* write binary file */ if (write_binary(binary_filename)) { printf("ERROR: writing binary file %s.\n", binary_filename); goto out; } err = EXIT_SUCCESS; /* write stats */ if (have_chwall) { printf("Max chwall labels: %u\n", max_chwall_labels); printf("Max chwall-types: %u\n", max_chwall_types); printf("Max chwall-ssids: %u\n", max_chwall_ssids); } if (have_ste) { printf("Max ste labels: %u\n", max_ste_labels); printf("Max ste-types: %u\n", max_ste_types); printf("Max ste-ssids: %u\n", max_ste_ssids); } /* cleanup */ out: xmlFreeDoc(policydoc); out2: xmlCleanupParser(); return err; } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */