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