1/* 2 * Generalized labeling frontend for userspace object managers. 3 * 4 * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil> 5 */ 6 7#include <sys/types.h> 8#include <ctype.h> 9#include <errno.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <sys/stat.h> 14#include <selinux/selinux.h> 15#include "callbacks.h" 16#include "label_internal.h" 17 18#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 19 20#ifdef NO_MEDIA_BACKEND 21#define CONFIG_MEDIA_BACKEND(fnptr) NULL 22#else 23#define CONFIG_MEDIA_BACKEND(fnptr) &fnptr 24#endif 25 26#ifdef NO_X_BACKEND 27#define CONFIG_X_BACKEND(fnptr) NULL 28#else 29#define CONFIG_X_BACKEND(fnptr) &fnptr 30#endif 31 32#ifdef NO_DB_BACKEND 33#define CONFIG_DB_BACKEND(fnptr) NULL 34#else 35#define CONFIG_DB_BACKEND(fnptr) &fnptr 36#endif 37 38#ifdef NO_ANDROID_BACKEND 39#define CONFIG_ANDROID_BACKEND(fnptr) NULL 40#else 41#define CONFIG_ANDROID_BACKEND(fnptr) (&(fnptr)) 42#endif 43 44typedef int (*selabel_initfunc)(struct selabel_handle *rec, 45 const struct selinux_opt *opts, 46 unsigned nopts); 47 48static selabel_initfunc initfuncs[] = { 49 &selabel_file_init, 50 CONFIG_MEDIA_BACKEND(selabel_media_init), 51 CONFIG_X_BACKEND(selabel_x_init), 52 CONFIG_DB_BACKEND(selabel_db_init), 53 CONFIG_ANDROID_BACKEND(selabel_property_init), 54 CONFIG_ANDROID_BACKEND(selabel_service_init), 55}; 56 57static void selabel_subs_fini(struct selabel_sub *ptr) 58{ 59 struct selabel_sub *next; 60 61 while (ptr) { 62 next = ptr->next; 63 free(ptr->src); 64 free(ptr->dst); 65 free(ptr); 66 ptr = next; 67 } 68} 69 70static char *selabel_sub(struct selabel_sub *ptr, const char *src) 71{ 72 char *dst = NULL; 73 int len; 74 75 while (ptr) { 76 if (strncmp(src, ptr->src, ptr->slen) == 0 ) { 77 if (src[ptr->slen] == '/' || 78 src[ptr->slen] == 0) { 79 if ((src[ptr->slen] == '/') && 80 (strcmp(ptr->dst, "/") == 0)) 81 len = ptr->slen + 1; 82 else 83 len = ptr->slen; 84 if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0) 85 return NULL; 86 return dst; 87 } 88 } 89 ptr = ptr->next; 90 } 91 return NULL; 92} 93 94struct selabel_sub *selabel_subs_init(const char *path, 95 struct selabel_sub *list, 96 struct selabel_digest *digest) 97{ 98 char buf[1024]; 99 FILE *cfg = fopen(path, "r"); 100 struct selabel_sub *sub = NULL; 101 struct stat sb; 102 103 if (!cfg) 104 return list; 105 106 if (fstat(fileno(cfg), &sb) < 0) 107 return list; 108 109 while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) { 110 char *ptr = NULL; 111 char *src = buf; 112 char *dst = NULL; 113 114 while (*src && isspace(*src)) 115 src++; 116 if (src[0] == '#') continue; 117 ptr = src; 118 while (*ptr && ! isspace(*ptr)) 119 ptr++; 120 *ptr++ = '\0'; 121 if (! *src) continue; 122 123 dst = ptr; 124 while (*dst && isspace(*dst)) 125 dst++; 126 ptr=dst; 127 while (*ptr && ! isspace(*ptr)) 128 ptr++; 129 *ptr='\0'; 130 if (! *dst) 131 continue; 132 133 sub = malloc(sizeof(*sub)); 134 if (! sub) 135 goto err; 136 memset(sub, 0, sizeof(*sub)); 137 138 sub->src=strdup(src); 139 if (! sub->src) 140 goto err; 141 142 sub->dst=strdup(dst); 143 if (! sub->dst) 144 goto err; 145 146 sub->slen = strlen(src); 147 sub->next = list; 148 list = sub; 149 } 150 151 if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0) 152 goto err; 153 154out: 155 fclose(cfg); 156 return list; 157err: 158 if (sub) 159 free(sub->src); 160 free(sub); 161 goto out; 162} 163 164static inline struct selabel_digest *selabel_is_digest_set 165 (const struct selinux_opt *opts, 166 unsigned n, 167 struct selabel_digest *entry) 168{ 169 struct selabel_digest *digest = NULL; 170 171 while (n--) { 172 if (opts[n].type == SELABEL_OPT_DIGEST && 173 opts[n].value == (char *)1) { 174 digest = calloc(1, sizeof(*digest)); 175 if (!digest) 176 goto err; 177 178 digest->digest = calloc(1, DIGEST_SPECFILE_SIZE + 1); 179 if (!digest->digest) 180 goto err; 181 182 digest->specfile_list = calloc(DIGEST_FILES_MAX, 183 sizeof(char *)); 184 if (!digest->specfile_list) 185 goto err; 186 187 entry = digest; 188 return entry; 189 } 190 } 191 return NULL; 192 193err: 194 free(digest->digest); 195 free(digest->specfile_list); 196 free(digest); 197 return NULL; 198} 199 200static void selabel_digest_fini(struct selabel_digest *ptr) 201{ 202 int i; 203 204 free(ptr->digest); 205 free(ptr->hashbuf); 206 207 if (ptr->specfile_list) { 208 for (i = 0; ptr->specfile_list[i]; i++) 209 free(ptr->specfile_list[i]); 210 free(ptr->specfile_list); 211 } 212 free(ptr); 213} 214 215/* 216 * Validation functions 217 */ 218 219static inline int selabel_is_validate_set(const struct selinux_opt *opts, 220 unsigned n) 221{ 222 while (n--) 223 if (opts[n].type == SELABEL_OPT_VALIDATE) 224 return !!opts[n].value; 225 226 return 0; 227} 228 229int selabel_validate(struct selabel_handle *rec, 230 struct selabel_lookup_rec *contexts) 231{ 232 int rc = 0; 233 234 if (!rec->validating || contexts->validated) 235 goto out; 236 237 rc = selinux_validate(&contexts->ctx_raw); 238 if (rc < 0) 239 goto out; 240 241 contexts->validated = 1; 242out: 243 return rc; 244} 245 246/* Public API helpers */ 247static char *selabel_sub_key(struct selabel_handle *rec, const char *key) 248{ 249 char *ptr = NULL; 250 char *dptr = NULL; 251 252 ptr = selabel_sub(rec->subs, key); 253 if (ptr) { 254 dptr = selabel_sub(rec->dist_subs, ptr); 255 if (dptr) { 256 free(ptr); 257 ptr = dptr; 258 } 259 } else { 260 ptr = selabel_sub(rec->dist_subs, key); 261 } 262 if (ptr) 263 return ptr; 264 265 return NULL; 266} 267 268static int selabel_fini(struct selabel_handle *rec, 269 struct selabel_lookup_rec *lr, 270 int translating) 271{ 272 char *path = NULL; 273 274 if (rec->spec_files) 275 path = rec->spec_files[0]; 276 if (compat_validate(rec, lr, path, 0)) 277 return -1; 278 279 if (translating && !lr->ctx_trans && 280 selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans)) 281 return -1; 282 283 return 0; 284} 285 286static struct selabel_lookup_rec * 287selabel_lookup_common(struct selabel_handle *rec, int translating, 288 const char *key, int type) 289{ 290 struct selabel_lookup_rec *lr; 291 char *ptr = NULL; 292 293 if (key == NULL) { 294 errno = EINVAL; 295 return NULL; 296 } 297 298 ptr = selabel_sub_key(rec, key); 299 if (ptr) { 300 lr = rec->func_lookup(rec, ptr, type); 301 free(ptr); 302 } else { 303 lr = rec->func_lookup(rec, key, type); 304 } 305 if (!lr) 306 return NULL; 307 308 if (selabel_fini(rec, lr, translating)) 309 return NULL; 310 311 return lr; 312} 313 314static struct selabel_lookup_rec * 315selabel_lookup_bm_common(struct selabel_handle *rec, int translating, 316 const char *key, int type, const char **aliases) 317{ 318 struct selabel_lookup_rec *lr; 319 char *ptr = NULL; 320 321 if (key == NULL) { 322 errno = EINVAL; 323 return NULL; 324 } 325 326 ptr = selabel_sub_key(rec, key); 327 if (ptr) { 328 lr = rec->func_lookup_best_match(rec, ptr, aliases, type); 329 free(ptr); 330 } else { 331 lr = rec->func_lookup_best_match(rec, key, aliases, type); 332 } 333 if (!lr) 334 return NULL; 335 336 if (selabel_fini(rec, lr, translating)) 337 return NULL; 338 339 return lr; 340} 341 342/* 343 * Public API 344 */ 345 346struct selabel_handle *selabel_open(unsigned int backend, 347 const struct selinux_opt *opts, 348 unsigned nopts) 349{ 350 struct selabel_handle *rec = NULL; 351 352 if (backend >= ARRAY_SIZE(initfuncs)) { 353 errno = EINVAL; 354 goto out; 355 } 356 357 if (!initfuncs[backend]) { 358 errno = ENOTSUP; 359 goto out; 360 } 361 362 rec = (struct selabel_handle *)malloc(sizeof(*rec)); 363 if (!rec) 364 goto out; 365 366 memset(rec, 0, sizeof(*rec)); 367 rec->backend = backend; 368 rec->validating = selabel_is_validate_set(opts, nopts); 369 370 rec->subs = NULL; 371 rec->dist_subs = NULL; 372 rec->digest = selabel_is_digest_set(opts, nopts, rec->digest); 373 374 if ((*initfuncs[backend])(rec, opts, nopts)) { 375 selabel_close(rec); 376 rec = NULL; 377 } 378out: 379 return rec; 380} 381 382int selabel_lookup(struct selabel_handle *rec, char **con, 383 const char *key, int type) 384{ 385 struct selabel_lookup_rec *lr; 386 387 lr = selabel_lookup_common(rec, 1, key, type); 388 if (!lr) 389 return -1; 390 391 *con = strdup(lr->ctx_trans); 392 return *con ? 0 : -1; 393} 394 395int selabel_lookup_raw(struct selabel_handle *rec, char **con, 396 const char *key, int type) 397{ 398 struct selabel_lookup_rec *lr; 399 400 lr = selabel_lookup_common(rec, 0, key, type); 401 if (!lr) 402 return -1; 403 404 *con = strdup(lr->ctx_raw); 405 return *con ? 0 : -1; 406} 407 408bool selabel_partial_match(struct selabel_handle *rec, const char *key) 409{ 410 char *ptr; 411 bool ret; 412 413 if (!rec->func_partial_match) { 414 /* 415 * If the label backend does not support partial matching, 416 * then assume a match is possible. 417 */ 418 return true; 419 } 420 421 ptr = selabel_sub_key(rec, key); 422 if (ptr) { 423 ret = rec->func_partial_match(rec, ptr); 424 free(ptr); 425 } else { 426 ret = rec->func_partial_match(rec, key); 427 } 428 429 return ret; 430} 431 432int selabel_lookup_best_match(struct selabel_handle *rec, char **con, 433 const char *key, const char **aliases, int type) 434{ 435 struct selabel_lookup_rec *lr; 436 437 if (!rec->func_lookup_best_match) { 438 errno = ENOTSUP; 439 return -1; 440 } 441 442 lr = selabel_lookup_bm_common(rec, 1, key, type, aliases); 443 if (!lr) 444 return -1; 445 446 *con = strdup(lr->ctx_trans); 447 return *con ? 0 : -1; 448} 449 450int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con, 451 const char *key, const char **aliases, int type) 452{ 453 struct selabel_lookup_rec *lr; 454 455 if (!rec->func_lookup_best_match) { 456 errno = ENOTSUP; 457 return -1; 458 } 459 460 lr = selabel_lookup_bm_common(rec, 0, key, type, aliases); 461 if (!lr) 462 return -1; 463 464 *con = strdup(lr->ctx_raw); 465 return *con ? 0 : -1; 466} 467 468enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1, 469 struct selabel_handle *h2) 470{ 471 if (!h1->func_cmp || h1->func_cmp != h2->func_cmp) 472 return SELABEL_INCOMPARABLE; 473 474 return h1->func_cmp(h1, h2); 475} 476 477int selabel_digest(struct selabel_handle *rec, 478 unsigned char **digest, size_t *digest_len, 479 char ***specfiles, size_t *num_specfiles) 480{ 481 if (!rec->digest) { 482 errno = EINVAL; 483 return -1; 484 } 485 486 *digest = rec->digest->digest; 487 *digest_len = DIGEST_SPECFILE_SIZE; 488 *specfiles = rec->digest->specfile_list; 489 *num_specfiles = rec->digest->specfile_cnt; 490 return 0; 491} 492 493void selabel_close(struct selabel_handle *rec) 494{ 495 size_t i; 496 selabel_subs_fini(rec->subs); 497 selabel_subs_fini(rec->dist_subs); 498 if (rec->spec_files) { 499 for (i = 0; i < rec->spec_files_len; i++) 500 free(rec->spec_files[i]); 501 free(rec->spec_files); 502 } 503 if (rec->digest) 504 selabel_digest_fini(rec->digest); 505 if (rec->func_close) 506 rec->func_close(rec); 507 free(rec); 508} 509 510void selabel_stats(struct selabel_handle *rec) 511{ 512 rec->func_stats(rec); 513} 514