Home | History | Annotate | Download | only in src
      1 /*
      2  * Generalized labeling frontend for userspace object managers.
      3  *
      4  * Author : Eamon Walsh <ewalsh (at) 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 typedef int (*selabel_initfunc)(struct selabel_handle *rec,
     21 				const struct selinux_opt *opts,
     22 				unsigned nopts);
     23 
     24 static 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 
     32 static 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 
     45 static 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 
     69 struct 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 
    129 out:
    130 	fclose(cfg);
    131 	return list;
    132 err:
    133 	if (sub)
    134 		free(sub->src);
    135 	free(sub);
    136 	goto out;
    137 }
    138 
    139 static 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 
    168 err:
    169 	free(digest->digest);
    170 	free(digest->specfile_list);
    171 	free(digest);
    172 	return NULL;
    173 }
    174 
    175 static 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 
    194 static 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 
    204 int 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;
    217 out:
    218 	return rc;
    219 }
    220 
    221 /* Public API helpers */
    222 static 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 
    243 static 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 
    257 static struct selabel_lookup_rec *
    258 selabel_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 
    285 static struct selabel_lookup_rec *
    286 selabel_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 
    317 struct 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 
    346 out:
    347 	return rec;
    348 }
    349 
    350 int 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 
    363 int 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 
    376 bool 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 
    400 int 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 
    418 int 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 
    436 enum 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 
    445 int 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 
    461 void 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 
    472 void selabel_stats(struct selabel_handle *rec)
    473 {
    474 	rec->func_stats(rec);
    475 }
    476