| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <arpa/inet.h> |
| #include <netinet/in.h> |
| #ifndef IPPROTO_DCCP |
| #define IPPROTO_DCCP 33 |
| #endif |
| #ifndef IPPROTO_SCTP |
| #define IPPROTO_SCTP 132 |
| #endif |
| |
| #include <sepol/policydb/ebitmap.h> |
| #include <sepol/policydb/hashtab.h> |
| #include <sepol/policydb/symtab.h> |
| |
| #include "private.h" |
| #include "kernel_to_common.h" |
| |
| |
| void sepol_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); |
| } |
| } |
| |
| void sepol_indent(FILE *out, int indent) |
| { |
| if (fprintf(out, "%*s", indent * 4, "") < 0) { |
| sepol_log_err("Failed to write to output"); |
| } |
| } |
| |
| void sepol_printf(FILE *out, const char *fmt, ...) |
| { |
| va_list argptr; |
| va_start(argptr, fmt); |
| if (vfprintf(out, fmt, argptr) < 0) { |
| sepol_log_err("Failed to write to output"); |
| } |
| va_end(argptr); |
| } |
| |
| __attribute__ ((format(printf, 1, 0))) |
| static char *create_str_helper(const char *fmt, int num, va_list vargs) |
| { |
| va_list vargs2; |
| char *str = NULL; |
| char *s; |
| size_t len, s_len; |
| int i, rc; |
| |
| va_copy(vargs2, vargs); |
| |
| len = strlen(fmt) + 1; /* +1 for '\0' */ |
| |
| for (i=0; i<num; i++) { |
| s = va_arg(vargs, char *); |
| s_len = strlen(s); |
| len += s_len > 1 ? s_len - 2 : 0; /* -2 for each %s in fmt */ |
| } |
| |
| str = malloc(len); |
| if (!str) { |
| sepol_log_err("Out of memory"); |
| goto exit; |
| } |
| |
| rc = vsnprintf(str, len, fmt, vargs2); |
| if (rc < 0 || rc >= (int)len) { |
| goto exit; |
| } |
| |
| va_end(vargs2); |
| |
| return str; |
| |
| exit: |
| free(str); |
| va_end(vargs2); |
| return NULL; |
| } |
| |
| char *create_str(const char *fmt, int num, ...) |
| { |
| char *str = NULL; |
| va_list vargs; |
| |
| va_start(vargs, num); |
| str = create_str_helper(fmt, num, vargs); |
| va_end(vargs); |
| |
| return str; |
| } |
| |
| int strs_init(struct strs **strs, size_t size) |
| { |
| struct strs *new; |
| |
| if (size == 0) { |
| size = 1; |
| } |
| |
| *strs = NULL; |
| |
| new = malloc(sizeof(struct strs)); |
| if (!new) { |
| sepol_log_err("Out of memory"); |
| return -1; |
| } |
| |
| new->list = calloc(size, sizeof(char *)); |
| if (!new->list) { |
| sepol_log_err("Out of memory"); |
| free(new); |
| return -1; |
| } |
| |
| new->num = 0; |
| new->size = size; |
| |
| *strs = new; |
| |
| return 0; |
| } |
| |
| void strs_destroy(struct strs **strs) |
| { |
| if (!strs || !*strs) { |
| return; |
| } |
| |
| free((*strs)->list); |
| (*strs)->list = NULL; |
| (*strs)->num = 0; |
| (*strs)->size = 0; |
| free(*strs); |
| *strs = NULL; |
| } |
| |
| void strs_free_all(struct strs *strs) |
| { |
| if (!strs) { |
| return; |
| } |
| |
| while (strs->num > 0) { |
| strs->num--; |
| free(strs->list[strs->num]); |
| } |
| } |
| |
| int strs_add(struct strs *strs, char *s) |
| { |
| if (strs->num + 1 > strs->size) { |
| char **new; |
| size_t i = strs->size; |
| strs->size *= 2; |
| new = reallocarray(strs->list, strs->size, sizeof(char *)); |
| if (!new) { |
| sepol_log_err("Out of memory"); |
| return -1; |
| } |
| strs->list = new; |
| memset(&strs->list[i], 0, sizeof(char *)*(strs->size-i)); |
| } |
| |
| strs->list[strs->num] = s; |
| strs->num++; |
| |
| return 0; |
| } |
| |
| int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...) |
| { |
| char *str; |
| va_list vargs; |
| int rc; |
| |
| va_start(vargs, num); |
| str = create_str_helper(fmt, num, vargs); |
| va_end(vargs); |
| |
| if (!str) { |
| rc = -1; |
| goto exit; |
| } |
| |
| rc = strs_add(strs, str); |
| if (rc != 0) { |
| free(str); |
| goto exit; |
| } |
| |
| return 0; |
| |
| exit: |
| return rc; |
| } |
| |
| char *strs_remove_last(struct strs *strs) |
| { |
| if (strs->num == 0) { |
| return NULL; |
| } |
| strs->num--; |
| return strs->list[strs->num]; |
| } |
| |
| int strs_add_at_index(struct strs *strs, char *s, size_t index) |
| { |
| if (index >= strs->size) { |
| char **new; |
| size_t i = strs->size; |
| while (index >= strs->size) { |
| strs->size *= 2; |
| } |
| new = reallocarray(strs->list, strs->size, sizeof(char *)); |
| if (!new) { |
| sepol_log_err("Out of memory"); |
| return -1; |
| } |
| strs->list = new; |
| memset(&strs->list[i], 0, sizeof(char *)*(strs->size - i)); |
| } |
| |
| strs->list[index] = s; |
| if (index >= strs->num) { |
| strs->num = index+1; |
| } |
| |
| return 0; |
| } |
| |
| char *strs_read_at_index(struct strs *strs, size_t index) |
| { |
| if (index >= strs->num) { |
| return NULL; |
| } |
| |
| return strs->list[index]; |
| } |
| |
| static int strs_cmp(const void *a, const void *b) |
| { |
| char *const *aa = a; |
| char *const *bb = b; |
| return strcmp(*aa,*bb); |
| } |
| |
| void strs_sort(struct strs *strs) |
| { |
| if (strs->num == 0) { |
| return; |
| } |
| qsort(strs->list, strs->num, sizeof(char *), strs_cmp); |
| } |
| |
| unsigned strs_num_items(const struct strs *strs) |
| { |
| return strs->num; |
| } |
| |
| size_t strs_len_items(const struct strs *strs) |
| { |
| unsigned i; |
| size_t len = 0; |
| |
| for (i=0; i<strs->num; i++) { |
| if (!strs->list[i]) continue; |
| len += strlen(strs->list[i]); |
| } |
| |
| return len; |
| } |
| |
| char *strs_to_str(const struct strs *strs) |
| { |
| char *str = NULL; |
| size_t len = 0; |
| char *p; |
| unsigned i; |
| int rc; |
| |
| if (strs->num == 0) { |
| goto exit; |
| } |
| |
| /* strs->num added because either ' ' or '\0' follows each item */ |
| len = strs_len_items(strs) + strs->num; |
| str = malloc(len); |
| if (!str) { |
| sepol_log_err("Out of memory"); |
| goto exit; |
| } |
| |
| p = str; |
| for (i=0; i<strs->num; i++) { |
| if (!strs->list[i]) continue; |
| len = strlen(strs->list[i]); |
| rc = snprintf(p, len+1, "%s", strs->list[i]); |
| if (rc < 0 || rc > (int)len) { |
| free(str); |
| str = NULL; |
| goto exit; |
| } |
| p += len; |
| if (i < strs->num - 1) { |
| *p++ = ' '; |
| } |
| } |
| |
| *p = '\0'; |
| |
| exit: |
| return str; |
| } |
| |
| void strs_write_each(const struct strs *strs, FILE *out) |
| { |
| unsigned i; |
| |
| for (i=0; i<strs->num; i++) { |
| if (!strs->list[i]) { |
| continue; |
| } |
| sepol_printf(out, "%s\n",strs->list[i]); |
| } |
| } |
| |
| void strs_write_each_indented(const struct strs *strs, FILE *out, int indent) |
| { |
| unsigned i; |
| |
| for (i=0; i<strs->num; i++) { |
| if (!strs->list[i]) { |
| continue; |
| } |
| sepol_indent(out, indent); |
| sepol_printf(out, "%s\n",strs->list[i]); |
| } |
| } |
| |
| int hashtab_ordered_to_strs(char *key, void *data, void *args) |
| { |
| struct strs *strs = (struct strs *)args; |
| symtab_datum_t *datum = data; |
| |
| return strs_add_at_index(strs, key, datum->value-1); |
| } |
| |
| int ebitmap_to_strs(const struct ebitmap *map, struct strs *strs, char **val_to_name) |
| { |
| struct ebitmap_node *node; |
| uint32_t i; |
| int rc; |
| |
| ebitmap_for_each_positive_bit(map, node, i) { |
| if (!val_to_name[i]) |
| continue; |
| |
| rc = strs_add(strs, val_to_name[i]); |
| if (rc != 0) { |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| char *ebitmap_to_str(const struct ebitmap *map, char **val_to_name, int sort) |
| { |
| struct strs *strs; |
| char *str = NULL; |
| int rc; |
| |
| rc = strs_init(&strs, 32); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = ebitmap_to_strs(map, strs, val_to_name); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| if (sort) { |
| strs_sort(strs); |
| } |
| |
| str = strs_to_str(strs); |
| |
| exit: |
| strs_destroy(&strs); |
| |
| return str; |
| } |
| |
| int strs_stack_init(struct strs **stack) |
| { |
| return strs_init(stack, STACK_SIZE); |
| } |
| |
| void strs_stack_destroy(struct strs **stack) |
| { |
| return strs_destroy(stack); |
| } |
| |
| int strs_stack_push(struct strs *stack, char *s) |
| { |
| return strs_add(stack, s); |
| } |
| |
| char *strs_stack_pop(struct strs *stack) |
| { |
| return strs_remove_last(stack); |
| } |
| |
| int strs_stack_empty(const struct strs *stack) |
| { |
| return strs_num_items(stack) == 0; |
| } |
| |
| static int compare_ranges(uint64_t l1, uint64_t h1, uint64_t l2, uint64_t h2) |
| { |
| uint64_t d1, d2; |
| |
| d1 = h1-l1; |
| d2 = h2-l2; |
| |
| if (d1 < d2) { |
| return -1; |
| } else if (d1 > d2) { |
| return 1; |
| } else { |
| if (l1 < l2) { |
| return -1; |
| } else if (l1 > l2) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int fsuse_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| if ((*aa)->v.behavior != (*bb)->v.behavior) { |
| if ((*aa)->v.behavior < (*bb)->v.behavior) { |
| return -1; |
| } else { |
| return 1; |
| } |
| } |
| |
| return strcmp((*aa)->u.name, (*bb)->u.name); |
| } |
| |
| static int portcon_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| int rc; |
| |
| rc = compare_ranges((*aa)->u.port.low_port, (*aa)->u.port.high_port, |
| (*bb)->u.port.low_port, (*bb)->u.port.high_port); |
| if (rc == 0) { |
| if ((*aa)->u.port.protocol < (*bb)->u.port.protocol) { |
| rc = -1; |
| } else if ((*aa)->u.port.protocol > (*bb)->u.port.protocol) { |
| rc = 1; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int netif_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| return strcmp((*aa)->u.name, (*bb)->u.name); |
| } |
| |
| static int node_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| int rc; |
| |
| rc = memcmp(&(*aa)->u.node.mask, &(*bb)->u.node.mask, sizeof((*aa)->u.node.mask)); |
| if (rc > 0) { |
| return -1; |
| } else if (rc < 0) { |
| return 1; |
| } |
| |
| return memcmp(&(*aa)->u.node.addr, &(*bb)->u.node.addr, sizeof((*aa)->u.node.addr)); |
| } |
| |
| static int node6_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| int rc; |
| |
| rc = memcmp(&(*aa)->u.node6.mask, &(*bb)->u.node6.mask, sizeof((*aa)->u.node6.mask)); |
| if (rc > 0) { |
| return -1; |
| } else if (rc < 0) { |
| return 1; |
| } |
| |
| return memcmp(&(*aa)->u.node6.addr, &(*bb)->u.node6.addr, sizeof((*aa)->u.node6.addr)); |
| } |
| |
| static int ibpkey_data_cmp(const void *a, const void *b) |
| { |
| int rc; |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| rc = (*aa)->u.ibpkey.subnet_prefix - (*bb)->u.ibpkey.subnet_prefix; |
| if (rc) |
| return rc; |
| |
| return compare_ranges((*aa)->u.ibpkey.low_pkey, (*aa)->u.ibpkey.high_pkey, |
| (*bb)->u.ibpkey.low_pkey, (*bb)->u.ibpkey.high_pkey); |
| } |
| |
| static int ibendport_data_cmp(const void *a, const void *b) |
| { |
| int rc; |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| rc = strcmp((*aa)->u.ibendport.dev_name, (*bb)->u.ibendport.dev_name); |
| if (rc) |
| return rc; |
| |
| return (*aa)->u.ibendport.port - (*bb)->u.ibendport.port; |
| } |
| |
| static int pirq_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| if ((*aa)->u.pirq < (*bb)->u.pirq) { |
| return -1; |
| } else if ((*aa)->u.pirq > (*bb)->u.pirq) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int ioport_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| return compare_ranges((*aa)->u.ioport.low_ioport, (*aa)->u.ioport.high_ioport, |
| (*bb)->u.ioport.low_ioport, (*bb)->u.ioport.high_ioport); |
| } |
| |
| static int iomem_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| return compare_ranges((*aa)->u.iomem.low_iomem, (*aa)->u.iomem.high_iomem, |
| (*bb)->u.iomem.low_iomem, (*bb)->u.iomem.high_iomem); |
| } |
| |
| static int pcid_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| if ((*aa)->u.device < (*bb)->u.device) { |
| return -1; |
| } else if ((*aa)->u.device > (*bb)->u.device) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int dtree_data_cmp(const void *a, const void *b) |
| { |
| struct ocontext *const *aa = a; |
| struct ocontext *const *bb = b; |
| |
| return strcmp((*aa)->u.name, (*bb)->u.name); |
| } |
| |
| static int sort_ocontext_data(struct ocontext **ocons, int (*cmp)(const void *, const void *)) |
| { |
| struct ocontext *ocon; |
| struct ocontext **data; |
| unsigned i, num; |
| |
| num = 0; |
| for (ocon = *ocons; ocon != NULL; ocon = ocon->next) { |
| num++; |
| } |
| |
| if (num == 0) { |
| return 0; |
| } |
| |
| data = calloc(sizeof(*data), num); |
| if (!data) { |
| sepol_log_err("Out of memory\n"); |
| return -1; |
| } |
| |
| i = 0; |
| for (ocon = *ocons; ocon != NULL; ocon = ocon->next) { |
| data[i] = ocon; |
| i++; |
| } |
| |
| qsort(data, num, sizeof(*data), cmp); |
| |
| *ocons = data[0]; |
| for (i=1; i < num; i++) { |
| data[i-1]->next = data[i]; |
| } |
| data[num-1]->next = NULL; |
| |
| free(data); |
| |
| return 0; |
| } |
| |
| int sort_ocontexts(struct policydb *pdb) |
| { |
| int rc = 0; |
| |
| if (pdb->target_platform == SEPOL_TARGET_SELINUX) { |
| rc = sort_ocontext_data(&pdb->ocontexts[5], fsuse_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[2], portcon_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[3], netif_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[4], node_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[6], node6_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[OCON_IBPKEY], ibpkey_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[OCON_IBENDPORT], ibendport_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| } else if (pdb->target_platform == SEPOL_TARGET_XEN) { |
| rc = sort_ocontext_data(&pdb->ocontexts[1], pirq_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[2], ioport_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[3], iomem_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[4], pcid_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| |
| rc = sort_ocontext_data(&pdb->ocontexts[5], dtree_data_cmp); |
| if (rc != 0) { |
| goto exit; |
| } |
| } |
| |
| exit: |
| if (rc != 0) { |
| sepol_log_err("Error sorting ocontexts\n"); |
| } |
| |
| return rc; |
| } |