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 
44 typedef int (*selabel_initfunc)(struct selabel_handle *rec,
45 				const struct selinux_opt *opts,
46 				unsigned nopts);
47 
48 static 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 
selabel_subs_fini(struct selabel_sub * ptr)57 static 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 
selabel_sub(struct selabel_sub * ptr,const char * src)70 static 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 
selabel_subs_init(const char * path,struct selabel_sub * list,struct selabel_digest * digest)94 struct 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 
154 out:
155 	fclose(cfg);
156 	return list;
157 err:
158 	if (sub)
159 		free(sub->src);
160 	free(sub);
161 	goto out;
162 }
163 
selabel_is_digest_set(const struct selinux_opt * opts,unsigned n,struct selabel_digest * entry)164 static 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 
193 err:
194 	free(digest->digest);
195 	free(digest->specfile_list);
196 	free(digest);
197 	return NULL;
198 }
199 
selabel_digest_fini(struct selabel_digest * ptr)200 static 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 
selabel_is_validate_set(const struct selinux_opt * opts,unsigned n)219 static 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 
selabel_validate(struct selabel_handle * rec,struct selabel_lookup_rec * contexts)229 int 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;
242 out:
243 	return rc;
244 }
245 
246 /* Public API helpers */
selabel_sub_key(struct selabel_handle * rec,const char * key)247 static 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 
selabel_fini(struct selabel_handle * rec,struct selabel_lookup_rec * lr,int translating)268 static 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 
286 static struct selabel_lookup_rec *
selabel_lookup_common(struct selabel_handle * rec,int translating,const char * key,int type)287 selabel_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 
314 static struct selabel_lookup_rec *
selabel_lookup_bm_common(struct selabel_handle * rec,int translating,const char * key,int type,const char ** aliases)315 selabel_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 
selabel_open(unsigned int backend,const struct selinux_opt * opts,unsigned nopts)346 struct 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 	}
378 out:
379 	return rec;
380 }
381 
selabel_lookup(struct selabel_handle * rec,char ** con,const char * key,int type)382 int 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 
selabel_lookup_raw(struct selabel_handle * rec,char ** con,const char * key,int type)395 int 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 
selabel_partial_match(struct selabel_handle * rec,const char * key)408 bool 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 
selabel_lookup_best_match(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)432 int 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 
selabel_lookup_best_match_raw(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)450 int 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 
selabel_cmp(struct selabel_handle * h1,struct selabel_handle * h2)468 enum 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 
selabel_digest(struct selabel_handle * rec,unsigned char ** digest,size_t * digest_len,char *** specfiles,size_t * num_specfiles)477 int 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 
selabel_close(struct selabel_handle * rec)493 void 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 
selabel_stats(struct selabel_handle * rec)510 void selabel_stats(struct selabel_handle *rec)
511 {
512 	rec->func_stats(rec);
513 }
514