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