1 /* 2 * Media contexts backend for X contexts 3 * 4 * Author : Eamon Walsh <ewalsh (at) 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. */ 23 typedef 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 30 struct saved_data { 31 unsigned int nspec; 32 spec_t *spec_arr; 33 }; 34 35 static 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 97 static int init(struct selabel_handle *rec, 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, "r")) == NULL) 121 return -1; 122 __fsetlocking(fp, FSETLOCKING_BYCALLER); 123 124 if (fstat(fileno(fp), &sb) < 0) 125 return -1; 126 if (!S_ISREG(sb.st_mode)) { 127 errno = EINVAL; 128 return -1; 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 lineno = 0; 150 151 if (pass == 0) { 152 if (data->nspec == 0) { 153 status = 0; 154 goto finish; 155 } 156 data->spec_arr = malloc(sizeof(spec_t)*data->nspec); 157 if (data->spec_arr == NULL) 158 goto finish; 159 memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec); 160 maxnspec = data->nspec; 161 rewind(fp); 162 } 163 } 164 free(line_buf); 165 166 status = 0; 167 finish: 168 fclose(fp); 169 return status; 170 } 171 172 /* 173 * Backend interface routines 174 */ 175 static void close(struct selabel_handle *rec) 176 { 177 struct saved_data *data = (struct saved_data *)rec->data; 178 struct spec *spec, *spec_arr = data->spec_arr; 179 unsigned int i; 180 181 for (i = 0; i < data->nspec; i++) { 182 spec = &spec_arr[i]; 183 free(spec->key); 184 free(spec->lr.ctx_raw); 185 free(spec->lr.ctx_trans); 186 } 187 188 if (spec_arr) 189 free(spec_arr); 190 191 memset(data, 0, sizeof(*data)); 192 } 193 194 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 195 const char *key, int type) 196 { 197 struct saved_data *data = (struct saved_data *)rec->data; 198 spec_t *spec_arr = data->spec_arr; 199 unsigned int i; 200 201 for (i = 0; i < data->nspec; i++) { 202 if (spec_arr[i].type != type) 203 continue; 204 if (!fnmatch(spec_arr[i].key, key, 0)) 205 break; 206 } 207 208 if (i >= data->nspec) { 209 /* No matching specification. */ 210 errno = ENOENT; 211 return NULL; 212 } 213 214 spec_arr[i].matches++; 215 return &spec_arr[i].lr; 216 } 217 218 static void stats(struct selabel_handle *rec) 219 { 220 struct saved_data *data = (struct saved_data *)rec->data; 221 unsigned int i, total = 0; 222 223 for (i = 0; i < data->nspec; i++) 224 total += data->spec_arr[i].matches; 225 226 selinux_log(SELINUX_INFO, "%u entries, %u matches made\n", 227 data->nspec, total); 228 } 229 230 int selabel_x_init(struct selabel_handle *rec, struct selinux_opt *opts, 231 unsigned nopts) 232 { 233 struct saved_data *data; 234 235 data = (struct saved_data *)malloc(sizeof(*data)); 236 if (!data) 237 return -1; 238 memset(data, 0, sizeof(*data)); 239 240 rec->data = data; 241 rec->func_close = &close; 242 rec->func_lookup = &lookup; 243 rec->func_stats = &stats; 244 245 return init(rec, opts, nopts); 246 } 247