#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;
}