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