#include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <stdio.h> #include <stdio_ext.h> #include <ctype.h> #include <alloca.h> #include <fnmatch.h> #include <syslog.h> #include <selinux/selinux.h> #include <selinux/context.h> #include "mcstrans.h" /* Define data structures */ typedef struct secolor { uint32_t fg; uint32_t bg; } secolor_t; typedef struct semnemonic { char *name; uint32_t color; struct semnemonic *next; } semnemonic_t; typedef struct setab { char *pattern; secolor_t color; struct setab *next; } setab_t; #define COLOR_USER 0 #define COLOR_ROLE 1 #define COLOR_TYPE 2 #define COLOR_RANGE 3 #define N_COLOR 4 #define AUX_RULE_COLOR "color" static const char *rules[] = { "user", "role", "type", "range" }; static setab_t *clist[N_COLOR]; static setab_t *cend[N_COLOR]; static semnemonic_t *mnemonics; static security_context_t my_context; void finish_context_colors(void) { setab_t *cur, *next; semnemonic_t *ptr; unsigned i; for (i = 0; i < N_COLOR; i++) { cur = clist[i]; while(cur) { next = cur->next; free(cur->pattern); free(cur); cur = next; } clist[i] = cend[i] = NULL; } ptr = mnemonics; while (ptr) { mnemonics = ptr->next; free(ptr->name); free(ptr); ptr = mnemonics; } mnemonics = NULL; freecon(my_context); my_context = NULL; } static int check_dominance(const char *pattern, const char *raw) { security_context_t ctx; context_t con; struct av_decision avd; int rc = -1; context_t my_tmp; const char *raw_range; security_class_t context_class = string_to_security_class("context"); access_vector_t context_contains_perm = string_to_av_perm(context_class, "contains"); con = context_new(raw); if (!con) return -1; raw_range = context_range_get(con); my_tmp = context_new(my_context); if (!my_tmp) { context_free(con); return -1; } ctx = NULL; if (context_range_set(my_tmp, pattern)) goto out; ctx = strdup(context_str(my_tmp)); if (!ctx) goto out; if (context_range_set(my_tmp, raw_range)) goto out; raw = context_str(my_tmp); if (!raw) goto out; rc = security_compute_av_raw(ctx, (security_context_t)raw, context_class, context_contains_perm, &avd); if (rc) goto out; rc = (context_contains_perm & avd.allowed) != context_contains_perm; out: free(ctx); context_free(my_tmp); context_free(con); return rc; } static const secolor_t *find_color(int idx, const char *component, const char *raw) { setab_t *ptr = clist[idx]; if (idx == COLOR_RANGE) { if (!raw) { return NULL; } } else if (!component) { return NULL; } while (ptr) { if (fnmatch(ptr->pattern, component, 0) == 0) { if (idx == COLOR_RANGE) { if (check_dominance(ptr->pattern, raw) == 0) return &ptr->color; } else return &ptr->color; } ptr = ptr->next; } return NULL; } static int add_secolor(int idx, char *pattern, uint32_t fg, uint32_t bg) { setab_t *cptr; cptr = calloc(1, sizeof(setab_t)); if (!cptr) return -1; cptr->pattern = strdup(pattern); if (!cptr->pattern) { free(cptr); return -1; } cptr->color.fg = fg & 0xffffff; cptr->color.bg = bg & 0xffffff; if (cend[idx]) { cend[idx]->next = cptr; cend[idx] = cptr; } else { clist[idx] = cptr; cend[idx] = cptr; } return 0; } static int find_mnemonic(const char *name, uint32_t *retval) { semnemonic_t *ptr; if (*name == '#') return sscanf(name, "#%x", retval) == 1 ? 0 : -1; ptr = mnemonics; while (ptr) { if (!strcmp(ptr->name, name)) { *retval = ptr->color; return 0; } ptr = ptr->next; } return -1; } static int add_mnemonic(const char *name, uint32_t color) { semnemonic_t *ptr = malloc(sizeof(semnemonic_t)); if (!ptr) return -1; ptr->color = color; ptr->name = strdup(name); if (!ptr->name) { free(ptr); return -1; } ptr->next = mnemonics; mnemonics = ptr; return 0; } /* Process line from color file. May modify the data pointed to by the buffer paremeter */ static int process_color(char *buffer, int line) { char rule[10], pat[256], f[256], b[256]; uint32_t i, fg, bg; int ret; while(isspace(*buffer)) buffer++; if(buffer[0] == '#' || buffer[0] == '\0') return 0; ret = sscanf(buffer, "%8s %255s = %255s %255s", rule, pat, f, b); if (ret == 4) { if (find_mnemonic(f, &fg) == 0 && find_mnemonic(b, &bg) == 0) for (i = 0; i < N_COLOR; i++) if (!strcmp(rule, rules[i])) return add_secolor(i, pat, fg, bg); } else if (ret == 3) { if (!strcmp(rule, AUX_RULE_COLOR)) { if (sscanf(f, "#%x", &fg) == 1) return add_mnemonic(pat, fg); } } syslog(LOG_WARNING, "Line %d of secolors file is invalid.", line); return 0; } /* Read in color file. */ int init_colors(void) { FILE *cfg = NULL; size_t size = 0; char *buffer = NULL; int line = 0; getcon(&my_context); cfg = fopen(selinux_colors_path(), "r"); if (!cfg) return 1; __fsetlocking(cfg, FSETLOCKING_BYCALLER); while (getline(&buffer, &size, cfg) > 0) { if( process_color(buffer, ++line) < 0 ) break; } free(buffer); fclose(cfg); return 0; } static const unsigned precedence[N_COLOR][N_COLOR - 1] = { { COLOR_ROLE, COLOR_TYPE, COLOR_RANGE }, { COLOR_USER, COLOR_TYPE, COLOR_RANGE }, { COLOR_USER, COLOR_ROLE, COLOR_RANGE }, { COLOR_USER, COLOR_ROLE, COLOR_TYPE }, }; static const secolor_t default_color = { 0x000000, 0xffffff }; static int parse_components(context_t con, char **components) { components[COLOR_USER] = (char *)context_user_get(con); components[COLOR_ROLE] = (char *)context_role_get(con); components[COLOR_TYPE] = (char *)context_type_get(con); components[COLOR_RANGE] = (char *)context_range_get(con); return 0; } /* Look up colors. */ int raw_color(const security_context_t raw, char **color_str) { #define CHARS_PER_COLOR 16 context_t con; uint32_t i, j, mask = 0; const secolor_t *items[N_COLOR]; char *result, *components[N_COLOR]; char buf[CHARS_PER_COLOR + 1]; size_t result_size = (N_COLOR * CHARS_PER_COLOR) + 1; int rc = -1; if (!color_str || *color_str) { return -1; } /* parse context and allocate memory */ con = context_new(raw); if (!con) return -1; if (parse_components(con, components) < 0) goto out; result = malloc(result_size); if (!result) goto out; result[0] = '\0'; /* find colors for which we have a match */ for (i = 0; i < N_COLOR; i++) { items[i] = find_color(i, components[i], raw); if (items[i]) mask |= (1 << i); } if (mask == 0) { items[0] = &default_color; mask = 1; } /* propagate colors according to the precedence rules */ for (i = 0; i < N_COLOR; i++) if (!(mask & (1 << i))) for (j = 0; j < N_COLOR - 1; j++) if (mask & (1 << precedence[i][j])) { items[i] = items[precedence[i][j]]; break; } /* print results into a big long string */ for (i = 0; i < N_COLOR; i++) { snprintf(buf, sizeof(buf), "#%06x #%06x ", items[i]->fg, items[i]->bg); strncat(result, buf, result_size-1); } *color_str = result; rc = 0; out: context_free(con); return rc; }