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