1/* 2 * Media contexts backend for X contexts 3 * 4 * Author : Eamon Walsh <ewalsh@tycho.nsa.gov> 5 */ 6 7#include <sys/stat.h> 8#include <string.h> 9#include <stdio.h> 10#include <stdio_ext.h> 11#include <ctype.h> 12#include <errno.h> 13#include <limits.h> 14#include <fnmatch.h> 15#include "callbacks.h" 16#include "label_internal.h" 17 18/* 19 * Internals 20 */ 21 22/* A context specification. */ 23typedef struct spec { 24 struct selabel_lookup_rec lr; /* holds contexts for lookup result */ 25 char *key; /* key string */ 26 int type; /* type of record (prop, ext, client) */ 27 int matches; /* number of matches made during operation */ 28} spec_t; 29 30struct saved_data { 31 unsigned int nspec; 32 spec_t *spec_arr; 33}; 34 35static int process_line(const char *path, char *line_buf, int pass, 36 unsigned lineno, struct selabel_handle *rec) 37{ 38 struct saved_data *data = (struct saved_data *)rec->data; 39 int items; 40 char *buf_p; 41 char *type, *key, *context; 42 43 buf_p = line_buf; 44 while (isspace(*buf_p)) 45 buf_p++; 46 /* Skip comment lines and empty lines. */ 47 if (*buf_p == '#' || *buf_p == 0) 48 return 0; 49 items = sscanf(line_buf, "%ms %ms %ms ", &type, &key, &context); 50 if (items < 3) { 51 selinux_log(SELINUX_WARNING, 52 "%s: line %u is missing fields, skipping\n", path, 53 lineno); 54 if (items > 0) 55 free(type); 56 if (items > 1) 57 free(key); 58 return 0; 59 } 60 61 if (pass == 1) { 62 /* Convert the type string to a mode format */ 63 if (!strcmp(type, "property")) 64 data->spec_arr[data->nspec].type = SELABEL_X_PROP; 65 else if (!strcmp(type, "extension")) 66 data->spec_arr[data->nspec].type = SELABEL_X_EXT; 67 else if (!strcmp(type, "client")) 68 data->spec_arr[data->nspec].type = SELABEL_X_CLIENT; 69 else if (!strcmp(type, "event")) 70 data->spec_arr[data->nspec].type = SELABEL_X_EVENT; 71 else if (!strcmp(type, "selection")) 72 data->spec_arr[data->nspec].type = SELABEL_X_SELN; 73 else if (!strcmp(type, "poly_property")) 74 data->spec_arr[data->nspec].type = SELABEL_X_POLYPROP; 75 else if (!strcmp(type, "poly_selection")) 76 data->spec_arr[data->nspec].type = SELABEL_X_POLYSELN; 77 else { 78 selinux_log(SELINUX_WARNING, 79 "%s: line %u has invalid object type %s\n", 80 path, lineno, type); 81 return 0; 82 } 83 data->spec_arr[data->nspec].key = key; 84 data->spec_arr[data->nspec].lr.ctx_raw = context; 85 free(type); 86 } 87 88 data->nspec++; 89 if (pass == 0) { 90 free(type); 91 free(key); 92 free(context); 93 } 94 return 0; 95} 96 97static int init(struct selabel_handle *rec, const struct selinux_opt *opts, 98 unsigned n) 99{ 100 FILE *fp; 101 struct saved_data *data = (struct saved_data *)rec->data; 102 const char *path = NULL; 103 char *line_buf = NULL; 104 size_t line_len = 0; 105 int status = -1; 106 unsigned int lineno, pass, maxnspec; 107 struct stat sb; 108 109 /* Process arguments */ 110 while (n--) 111 switch(opts[n].type) { 112 case SELABEL_OPT_PATH: 113 path = opts[n].value; 114 break; 115 } 116 117 /* Open the specification file. */ 118 if (!path) 119 path = selinux_x_context_path(); 120 if ((fp = fopen(path, "re")) == NULL) 121 return -1; 122 __fsetlocking(fp, FSETLOCKING_BYCALLER); 123 124 if (fstat(fileno(fp), &sb) < 0) 125 goto finish; 126 if (!S_ISREG(sb.st_mode)) { 127 errno = EINVAL; 128 goto finish; 129 } 130 rec->spec_file = strdup(path); 131 132 /* 133 * Perform two passes over the specification file. 134 * The first pass counts the number of specifications and 135 * performs simple validation of the input. At the end 136 * of the first pass, the spec array is allocated. 137 * The second pass performs detailed validation of the input 138 * and fills in the spec array. 139 */ 140 maxnspec = UINT_MAX / sizeof(spec_t); 141 for (pass = 0; pass < 2; pass++) { 142 lineno = 0; 143 data->nspec = 0; 144 while (getline(&line_buf, &line_len, fp) > 0 && 145 data->nspec < maxnspec) { 146 if (process_line(path, line_buf, pass, ++lineno, rec)) 147 goto finish; 148 } 149 150 if (pass == 0) { 151 if (data->nspec == 0) { 152 status = 0; 153 goto finish; 154 } 155 data->spec_arr = malloc(sizeof(spec_t)*data->nspec); 156 if (data->spec_arr == NULL) 157 goto finish; 158 memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec); 159 maxnspec = data->nspec; 160 rewind(fp); 161 } 162 } 163 free(line_buf); 164 165 status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path); 166 if (status) 167 goto finish; 168 169 digest_gen_hash(rec->digest); 170 171finish: 172 fclose(fp); 173 return status; 174} 175 176/* 177 * Backend interface routines 178 */ 179static void close(struct selabel_handle *rec) 180{ 181 struct saved_data *data = (struct saved_data *)rec->data; 182 struct spec *spec, *spec_arr = data->spec_arr; 183 unsigned int i; 184 185 for (i = 0; i < data->nspec; i++) { 186 spec = &spec_arr[i]; 187 free(spec->key); 188 free(spec->lr.ctx_raw); 189 free(spec->lr.ctx_trans); 190 } 191 192 if (spec_arr) 193 free(spec_arr); 194 195 free(data); 196} 197 198static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 199 const char *key, int type) 200{ 201 struct saved_data *data = (struct saved_data *)rec->data; 202 spec_t *spec_arr = data->spec_arr; 203 unsigned int i; 204 205 for (i = 0; i < data->nspec; i++) { 206 if (spec_arr[i].type != type) 207 continue; 208 if (!fnmatch(spec_arr[i].key, key, 0)) 209 break; 210 } 211 212 if (i >= data->nspec) { 213 /* No matching specification. */ 214 errno = ENOENT; 215 return NULL; 216 } 217 218 spec_arr[i].matches++; 219 return &spec_arr[i].lr; 220} 221 222static void stats(struct selabel_handle *rec) 223{ 224 struct saved_data *data = (struct saved_data *)rec->data; 225 unsigned int i, total = 0; 226 227 for (i = 0; i < data->nspec; i++) 228 total += data->spec_arr[i].matches; 229 230 selinux_log(SELINUX_INFO, "%u entries, %u matches made\n", 231 data->nspec, total); 232} 233 234int selabel_x_init(struct selabel_handle *rec, const struct selinux_opt *opts, 235 unsigned nopts) 236{ 237 struct saved_data *data; 238 239 data = (struct saved_data *)malloc(sizeof(*data)); 240 if (!data) 241 return -1; 242 memset(data, 0, sizeof(*data)); 243 244 rec->data = data; 245 rec->func_close = &close; 246 rec->func_lookup = &lookup; 247 rec->func_stats = &stats; 248 249 return init(rec, opts, nopts); 250} 251