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. */ 17 typedef 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 */ 23 struct 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 32 static 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. Return error on different specifications. 49 * TODO: Remove duplicate specifications. Move duplicate check to after sort 50 * to improve performance. 51 */ 52 static int nodups_specs(struct saved_data *data) 53 { 54 int rc = 0; 55 unsigned int ii, jj; 56 struct spec *curr_spec, *spec_arr = data->spec_arr; 57 58 for (ii = 0; ii < data->nspec; ii++) { 59 curr_spec = &spec_arr[ii]; 60 for (jj = ii + 1; jj < data->nspec; jj++) { 61 if (!strcmp(spec_arr[jj].property_key, 62 curr_spec->property_key)) { 63 if (strcmp(spec_arr[jj].lr.ctx_raw, 64 curr_spec->lr.ctx_raw)) { 65 rc = -1; 66 errno = EINVAL; 67 selinux_log 68 (SELINUX_ERROR, 69 "Multiple different specifications for %s (%s and %s).\n", 70 curr_spec->property_key, 71 spec_arr[jj].lr.ctx_raw, 72 curr_spec->lr.ctx_raw); 73 } else { 74 selinux_log 75 (SELINUX_WARNING, 76 "Multiple same specifications for %s.\n", 77 curr_spec->property_key); 78 } 79 } 80 } 81 } 82 return rc; 83 } 84 85 static int process_line(struct selabel_handle *rec, 86 const char *path, char *line_buf, 87 int pass, unsigned lineno) 88 { 89 int items; 90 char *prop = NULL, *context = NULL; 91 struct saved_data *data = (struct saved_data *)rec->data; 92 spec_t *spec_arr = data->spec_arr; 93 unsigned int nspec = data->nspec; 94 const char *errbuf = NULL; 95 96 items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context); 97 if (items < 0) { 98 items = errno; 99 selinux_log(SELINUX_ERROR, 100 "%s: line %u error due to: %s\n", path, 101 lineno, errbuf ?: strerror(errno)); 102 errno = items; 103 return -1; 104 } 105 106 if (items == 0) 107 return items; 108 109 if (items != 2) { 110 selinux_log(SELINUX_ERROR, 111 "%s: line %u is missing fields\n", path, 112 lineno); 113 free(prop); 114 errno = EINVAL; 115 return -1; 116 } 117 118 if (pass == 0) { 119 free(prop); 120 free(context); 121 } else if (pass == 1) { 122 /* On the second pass, process and store the specification in spec. */ 123 spec_arr[nspec].property_key = prop; 124 spec_arr[nspec].lr.ctx_raw = context; 125 126 if (rec->validating) { 127 if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) { 128 selinux_log(SELINUX_ERROR, 129 "%s: line %u has invalid context %s\n", 130 path, lineno, spec_arr[nspec].lr.ctx_raw); 131 errno = EINVAL; 132 return -1; 133 } 134 } 135 136 data->nspec = ++nspec; 137 } 138 139 return 0; 140 } 141 142 static int process_file(struct selabel_handle *rec, const char *path) 143 { 144 struct saved_data *data = (struct saved_data *)rec->data; 145 char line_buf[BUFSIZ]; 146 unsigned int lineno, maxnspec, pass; 147 struct stat sb; 148 FILE *fp; 149 int status = -1; 150 unsigned int nspec; 151 spec_t *spec_arr; 152 153 /* Open the specification file. */ 154 if ((fp = fopen(path, "re")) == NULL) 155 return -1; 156 157 if (fstat(fileno(fp), &sb) < 0) 158 goto finish; 159 160 errno = EINVAL; 161 162 if (!S_ISREG(sb.st_mode)) 163 goto finish; 164 165 /* 166 * Two passes per specification file. First is to get the size. 167 * After the first pass, the spec array is malloced / realloced to 168 * the appropriate size. Second pass is to populate the spec array. 169 */ 170 maxnspec = UINT_MAX / sizeof(spec_t); 171 for (pass = 0; pass < 2; pass++) { 172 nspec = 0; 173 lineno = 0; 174 175 while (fgets(line_buf, sizeof(line_buf) - 1, fp) && 176 nspec < maxnspec) { 177 if (process_line(rec, path, line_buf, pass, ++lineno)) 178 goto finish; 179 nspec++; 180 } 181 182 if (pass == 0) { 183 if (nspec == 0) { 184 status = 0; 185 goto finish; 186 } 187 188 /* grow spec array if required */ 189 spec_arr = realloc(data->spec_arr, 190 (data->nspec + nspec) * sizeof(spec_t)); 191 if (spec_arr == NULL) 192 goto finish; 193 194 memset(&spec_arr[data->nspec], 0, nspec * sizeof(spec_t)); 195 data->spec_arr = spec_arr; 196 maxnspec = nspec; 197 rewind(fp); 198 } 199 } 200 201 status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path); 202 203 finish: 204 fclose(fp); 205 return status; 206 } 207 208 static void closef(struct selabel_handle *rec); 209 210 static int init(struct selabel_handle *rec, const struct selinux_opt *opts, 211 unsigned n) 212 { 213 struct saved_data *data = (struct saved_data *)rec->data; 214 char **paths = NULL; 215 size_t num_paths = 0; 216 int status = -1; 217 size_t i; 218 219 /* Process arguments */ 220 i = n; 221 while (i--) { 222 switch (opts[i].type) { 223 case SELABEL_OPT_PATH: 224 num_paths++; 225 break; 226 } 227 } 228 229 if (!num_paths) 230 return -1; 231 232 paths = calloc(num_paths, sizeof(*paths)); 233 if (!paths) 234 return -1; 235 236 rec->spec_files = paths; 237 rec->spec_files_len = num_paths; 238 239 i = n; 240 while (i--) { 241 switch(opts[i].type) { 242 case SELABEL_OPT_PATH: 243 *paths = strdup(opts[i].value); 244 if (*paths == NULL) 245 goto finish; 246 paths++; 247 } 248 } 249 250 for (i = 0; i < num_paths; i++) { 251 status = process_file(rec, rec->spec_files[i]); 252 if (status) 253 goto finish; 254 } 255 256 /* warn about duplicates after all files have been processed. */ 257 status = nodups_specs(data); 258 if (status) 259 goto finish; 260 261 qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp); 262 263 digest_gen_hash(rec->digest); 264 265 finish: 266 if (status) 267 closef(rec); 268 269 return status; 270 } 271 272 /* 273 * Backend interface routines 274 */ 275 static void closef(struct selabel_handle *rec) 276 { 277 struct saved_data *data = (struct saved_data *)rec->data; 278 struct spec *spec; 279 unsigned int i; 280 281 if (data->spec_arr) { 282 for (i = 0; i < data->nspec; i++) { 283 spec = &data->spec_arr[i]; 284 free(spec->property_key); 285 free(spec->lr.ctx_raw); 286 free(spec->lr.ctx_trans); 287 } 288 289 free(data->spec_arr); 290 } 291 292 free(data); 293 } 294 295 static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec, 296 const char *key, 297 int __attribute__((unused)) type) 298 { 299 struct saved_data *data = (struct saved_data *)rec->data; 300 spec_t *spec_arr = data->spec_arr; 301 unsigned int i; 302 struct selabel_lookup_rec *ret = NULL; 303 304 if (!data->nspec) { 305 errno = ENOENT; 306 goto finish; 307 } 308 309 for (i = 0; i < data->nspec; i++) { 310 if (strncmp(spec_arr[i].property_key, key, 311 strlen(spec_arr[i].property_key)) == 0) { 312 break; 313 } 314 if (strncmp(spec_arr[i].property_key, "*", 1) == 0) 315 break; 316 } 317 318 if (i >= data->nspec) { 319 /* No matching specification. */ 320 errno = ENOENT; 321 goto finish; 322 } 323 324 ret = &spec_arr[i].lr; 325 326 finish: 327 return ret; 328 } 329 330 static struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec, 331 const char *key, int __attribute__((unused)) type) 332 { 333 struct saved_data *data = (struct saved_data *)rec->data; 334 spec_t *spec_arr = data->spec_arr; 335 unsigned int i; 336 struct selabel_lookup_rec *ret = NULL; 337 338 if (!data->nspec) { 339 errno = ENOENT; 340 goto finish; 341 } 342 343 for (i = 0; i < data->nspec; i++) { 344 if (strcmp(spec_arr[i].property_key, key) == 0) 345 break; 346 if (strcmp(spec_arr[i].property_key, "*") == 0) 347 break; 348 } 349 350 if (i >= data->nspec) { 351 /* No matching specification. */ 352 errno = ENOENT; 353 goto finish; 354 } 355 356 ret = &spec_arr[i].lr; 357 358 finish: 359 return ret; 360 } 361 362 static void stats(struct selabel_handle __attribute__((unused)) *rec) 363 { 364 selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n"); 365 } 366 367 int selabel_property_init(struct selabel_handle *rec, 368 const struct selinux_opt *opts, 369 unsigned nopts) 370 { 371 struct saved_data *data; 372 373 data = (struct saved_data *)calloc(1, sizeof(*data)); 374 if (!data) 375 return -1; 376 377 rec->data = data; 378 rec->func_close = &closef; 379 rec->func_stats = &stats; 380 rec->func_lookup = &property_lookup; 381 382 return init(rec, opts, nopts); 383 } 384 385 int selabel_service_init(struct selabel_handle *rec, 386 const struct selinux_opt *opts, unsigned nopts) 387 { 388 struct saved_data *data; 389 390 data = (struct saved_data *)calloc(1, sizeof(*data)); 391 if (!data) 392 return -1; 393 394 rec->data = data; 395 rec->func_close = &closef; 396 rec->func_stats = &stats; 397 rec->func_lookup = &service_lookup; 398 399 return init(rec, opts, nopts); 400 } 401