1/* 2 * Property Service contexts backend for labeling Android 3 * property keys 4 */ 5 6#include <stdarg.h> 7#include <string.h> 8#include <ctype.h> 9#include <errno.h> 10#include <limits.h> 11#include <sys/types.h> 12#include <sys/stat.h> 13#include "callbacks.h" 14#include "label_internal.h" 15 16/* A property security context specification. */ 17typedef struct spec { 18 struct selabel_lookup_rec lr; /* holds contexts for lookup result */ 19 char *property_key; /* property key string */ 20} spec_t; 21 22/* Our stored configuration */ 23struct saved_data { 24 /* 25 * The array of specifications is sorted for longest 26 * prefix match 27 */ 28 spec_t *spec_arr; 29 unsigned int nspec; /* total number of specifications */ 30}; 31 32static int cmp(const void *A, const void *B) 33{ 34 const struct spec *sp1 = A, *sp2 = B; 35 36 if (strncmp(sp1->property_key, "*", 1) == 0) 37 return 1; 38 if (strncmp(sp2->property_key, "*", 1) == 0) 39 return -1; 40 41 size_t L1 = strlen(sp1->property_key); 42 size_t L2 = strlen(sp2->property_key); 43 44 return (L1 < L2) - (L1 > L2); 45} 46 47/* 48 * Warn about duplicate specifications. 49 */ 50static int nodups_specs(struct saved_data *data, const char *path) 51{ 52 int rc = 0; 53 unsigned int ii, jj; 54 struct spec *curr_spec, *spec_arr = data->spec_arr; 55 56 for (ii = 0; ii < data->nspec; ii++) { 57 curr_spec = &spec_arr[ii]; 58 for (jj = ii + 1; jj < data->nspec; jj++) { 59 if (!strcmp(spec_arr[jj].property_key, 60 curr_spec->property_key)) { 61 rc = -1; 62 errno = EINVAL; 63 if (strcmp(spec_arr[jj].lr.ctx_raw, 64 curr_spec->lr.ctx_raw)) { 65 selinux_log 66 (SELINUX_ERROR, 67 "%s: Multiple different specifications for %s (%s and %s).\n", 68 path, curr_spec->property_key, 69 spec_arr[jj].lr.ctx_raw, 70 curr_spec->lr.ctx_raw); 71 } else { 72 selinux_log 73 (SELINUX_ERROR, 74 "%s: Multiple same specifications for %s.\n", 75 path, curr_spec->property_key); 76 } 77 } 78 } 79 } 80 return rc; 81} 82 83static int process_line(struct selabel_handle *rec, 84 const char *path, char *line_buf, 85 int pass, unsigned lineno) 86{ 87 int items; 88 char *prop = NULL, *context = NULL; 89 struct saved_data *data = (struct saved_data *)rec->data; 90 spec_t *spec_arr = data->spec_arr; 91 unsigned int nspec = data->nspec; 92 const char *errbuf = NULL; 93 94 items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context); 95 if (items < 0) { 96 if (errbuf) { 97 selinux_log(SELINUX_ERROR, 98 "%s: line %u error due to: %s\n", path, 99 lineno, errbuf); 100 } else { 101 selinux_log(SELINUX_ERROR, 102 "%s: line %u error due to: %m\n", path, 103 lineno); 104 } 105 return -1; 106 } 107 108 if (items == 0) 109 return items; 110 111 if (items != 2) { 112 selinux_log(SELINUX_ERROR, 113 "%s: line %u is missing fields\n", path, 114 lineno); 115 free(prop); 116 errno = EINVAL; 117 return -1; 118 } 119 120 if (pass == 0) { 121 free(prop); 122 free(context); 123 } else if (pass == 1) { 124 /* On the second pass, process and store the specification in spec. */ 125 spec_arr[nspec].property_key = prop; 126 spec_arr[nspec].lr.ctx_raw = context; 127 128 if (rec->validating) { 129 if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) { 130 selinux_log(SELINUX_ERROR, 131 "%s: line %u has invalid context %s\n", 132 path, lineno, spec_arr[nspec].lr.ctx_raw); 133 errno = EINVAL; 134 return -1; 135 } 136 } 137 } 138 139 data->nspec = ++nspec; 140 return 0; 141} 142 143static int init(struct selabel_handle *rec, const struct selinux_opt *opts, 144 unsigned n) 145{ 146 struct saved_data *data = (struct saved_data *)rec->data; 147 const char *path = NULL; 148 FILE *fp; 149 char line_buf[BUFSIZ]; 150 unsigned int lineno, maxnspec, pass; 151 int status = -1; 152 struct stat sb; 153 154 /* Process arguments */ 155 while (n--) 156 switch (opts[n].type) { 157 case SELABEL_OPT_PATH: 158 path = opts[n].value; 159 break; 160 } 161 162 if (!path) 163 return -1; 164 165 /* Open the specification file. */ 166 if ((fp = fopen(path, "re")) == NULL) 167 return -1; 168 169 if (fstat(fileno(fp), &sb) < 0) 170 goto finish; 171 errno = EINVAL; 172 if (!S_ISREG(sb.st_mode)) 173 goto finish; 174 175 /* 176 * Two passes of the specification file. First is to get the size. 177 * After the first pass, the spec array is malloced to the appropriate 178 * size. Second pass is to populate the spec array and check for 179 * dups. 180 */ 181 maxnspec = UINT_MAX / sizeof(spec_t); 182 for (pass = 0; pass < 2; pass++) { 183 data->nspec = 0; 184 lineno = 0; 185 186 while (fgets(line_buf, sizeof(line_buf) - 1, fp) 187 && data->nspec < maxnspec) { 188 if (process_line(rec, path, line_buf, pass, ++lineno) 189 != 0) 190 goto finish; 191 } 192 193 if (pass == 1) { 194 status = nodups_specs(data, path); 195 196 if (status) 197 goto finish; 198 } 199 200 if (pass == 0) { 201 if (data->nspec == 0) { 202 status = 0; 203 goto finish; 204 } 205 206 if (NULL == (data->spec_arr = 207 calloc(data->nspec, sizeof(spec_t)))) 208 goto finish; 209 210 maxnspec = data->nspec; 211 rewind(fp); 212 } 213 } 214 215 qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp); 216 217 status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path); 218 if (status) 219 goto finish; 220 221 digest_gen_hash(rec->digest); 222 223finish: 224 fclose(fp); 225 return status; 226} 227 228/* 229 * Backend interface routines 230 */ 231static void closef(struct selabel_handle *rec) 232{ 233 struct saved_data *data = (struct saved_data *)rec->data; 234 struct spec *spec; 235 unsigned int i; 236 237 for (i = 0; i < data->nspec; i++) { 238 spec = &data->spec_arr[i]; 239 free(spec->property_key); 240 free(spec->lr.ctx_raw); 241 free(spec->lr.ctx_trans); 242 } 243 244 if (data->spec_arr) 245 free(data->spec_arr); 246 247 free(data); 248} 249 250static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec, 251 const char *key, 252 int __attribute__((unused)) type) 253{ 254 struct saved_data *data = (struct saved_data *)rec->data; 255 spec_t *spec_arr = data->spec_arr; 256 unsigned int i; 257 struct selabel_lookup_rec *ret = NULL; 258 259 if (!data->nspec) { 260 errno = ENOENT; 261 goto finish; 262 } 263 264 for (i = 0; i < data->nspec; i++) { 265 if (strncmp(spec_arr[i].property_key, key, 266 strlen(spec_arr[i].property_key)) == 0) { 267 break; 268 } 269 if (strncmp(spec_arr[i].property_key, "*", 1) == 0) 270 break; 271 } 272 273 if (i >= data->nspec) { 274 /* No matching specification. */ 275 errno = ENOENT; 276 goto finish; 277 } 278 279 ret = &spec_arr[i].lr; 280 281finish: 282 return ret; 283} 284 285static struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec, 286 const char *key, int __attribute__((unused)) type) 287{ 288 struct saved_data *data = (struct saved_data *)rec->data; 289 spec_t *spec_arr = data->spec_arr; 290 unsigned int i; 291 struct selabel_lookup_rec *ret = NULL; 292 293 if (!data->nspec) { 294 errno = ENOENT; 295 goto finish; 296 } 297 298 for (i = 0; i < data->nspec; i++) { 299 if (strcmp(spec_arr[i].property_key, key) == 0) 300 break; 301 if (strcmp(spec_arr[i].property_key, "*") == 0) 302 break; 303 } 304 305 if (i >= data->nspec) { 306 /* No matching specification. */ 307 errno = ENOENT; 308 goto finish; 309 } 310 311 ret = &spec_arr[i].lr; 312 313finish: 314 return ret; 315} 316 317static void stats(struct selabel_handle __attribute__((unused)) *rec) 318{ 319 selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n"); 320} 321 322int selabel_property_init(struct selabel_handle *rec, 323 const struct selinux_opt *opts, 324 unsigned nopts) 325{ 326 struct saved_data *data; 327 328 data = (struct saved_data *)calloc(1, sizeof(*data)); 329 if (!data) 330 return -1; 331 332 rec->data = data; 333 rec->func_close = &closef; 334 rec->func_stats = &stats; 335 rec->func_lookup = &property_lookup; 336 337 return init(rec, opts, nopts); 338} 339 340int selabel_service_init(struct selabel_handle *rec, 341 const struct selinux_opt *opts, unsigned nopts) 342{ 343 struct saved_data *data; 344 345 data = (struct saved_data *)calloc(1, sizeof(*data)); 346 if (!data) 347 return -1; 348 349 rec->data = data; 350 rec->func_close = &closef; 351 rec->func_stats = &stats; 352 rec->func_lookup = &service_lookup; 353 354 return init(rec, opts, nopts); 355} 356