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