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 <selinux/selinux.h>
14#include "callbacks.h"
15#include "label_internal.h"
16
17#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
18
19typedef int (*selabel_initfunc)(struct selabel_handle *rec,
20				struct selinux_opt *opts, unsigned nopts);
21
22static selabel_initfunc initfuncs[] = {
23	&selabel_file_init,
24	&selabel_media_init,
25	&selabel_x_init,
26	&selabel_db_init,
27	&selabel_property_init,
28};
29
30static void selabel_subs_fini(struct selabel_sub *ptr)
31{
32	struct selabel_sub *next;
33
34	while (ptr) {
35		next = ptr->next;
36		free(ptr->src);
37		free(ptr->dst);
38		free(ptr);
39		ptr = next;
40	}
41}
42
43static char *selabel_sub(struct selabel_sub *ptr, const char *src)
44{
45	char *dst = NULL;
46	int len;
47
48	while (ptr) {
49		if (strncmp(src, ptr->src, ptr->slen) == 0 ) {
50			if (src[ptr->slen] == '/' ||
51			    src[ptr->slen] == 0) {
52				if ((src[ptr->slen] == '/') &&
53				    (strcmp(ptr->dst, "/") == 0))
54					len = ptr->slen + 1;
55				else
56					len = ptr->slen;
57				if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0)
58					return NULL;
59				return dst;
60			}
61		}
62		ptr = ptr->next;
63	}
64	return NULL;
65}
66
67struct selabel_sub *selabel_subs_init(const char *path, struct selabel_sub *list)
68{
69	char buf[1024];
70	FILE *cfg = fopen(path, "r");
71	struct selabel_sub *sub;
72
73	if (!cfg)
74		return list;
75
76	while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) {
77		char *ptr = NULL;
78		char *src = buf;
79		char *dst = NULL;
80
81		while (*src && isspace(*src))
82			src++;
83		if (src[0] == '#') continue;
84		ptr = src;
85		while (*ptr && ! isspace(*ptr))
86			ptr++;
87		*ptr++ = '\0';
88		if (! *src) continue;
89
90		dst = ptr;
91		while (*dst && isspace(*dst))
92			dst++;
93		ptr=dst;
94		while (*ptr && ! isspace(*ptr))
95			ptr++;
96		*ptr='\0';
97		if (! *dst)
98			continue;
99
100		sub = malloc(sizeof(*sub));
101		if (! sub)
102			goto err;
103		memset(sub, 0, sizeof(*sub));
104
105		sub->src=strdup(src);
106		if (! sub->src)
107			goto err;
108
109		sub->dst=strdup(dst);
110		if (! sub->dst)
111			goto err;
112
113		sub->slen = strlen(src);
114		sub->next = list;
115		list = sub;
116	}
117out:
118	fclose(cfg);
119	return list;
120err:
121	if (sub)
122		free(sub->src);
123	free(sub);
124	goto out;
125}
126
127/*
128 * Validation functions
129 */
130
131static inline int selabel_is_validate_set(struct selinux_opt *opts, unsigned n)
132{
133	while (n--)
134		if (opts[n].type == SELABEL_OPT_VALIDATE)
135			return !!opts[n].value;
136
137	return 0;
138}
139
140int selabel_validate(struct selabel_handle *rec,
141		     struct selabel_lookup_rec *contexts)
142{
143	int rc = 0;
144
145	if (!rec->validating || contexts->validated)
146		goto out;
147
148	rc = selinux_validate(&contexts->ctx_raw);
149	if (rc < 0)
150		goto out;
151
152	contexts->validated = 1;
153out:
154	return rc;
155}
156
157/*
158 * Public API
159 */
160
161struct selabel_handle *selabel_open(unsigned int backend,
162				    struct selinux_opt *opts, unsigned nopts)
163{
164	struct selabel_handle *rec = NULL;
165
166	if (backend >= ARRAY_SIZE(initfuncs)) {
167		errno = EINVAL;
168		goto out;
169	}
170
171	rec = (struct selabel_handle *)malloc(sizeof(*rec));
172	if (!rec)
173		goto out;
174
175	memset(rec, 0, sizeof(*rec));
176	rec->backend = backend;
177	rec->validating = selabel_is_validate_set(opts, nopts);
178
179	rec->subs = NULL;
180	rec->dist_subs = NULL;
181
182	if ((*initfuncs[backend])(rec, opts, nopts)) {
183		free(rec);
184		rec = NULL;
185	}
186
187out:
188	return rec;
189}
190
191static struct selabel_lookup_rec *
192selabel_lookup_common(struct selabel_handle *rec, int translating,
193		      const char *key, int type)
194{
195	struct selabel_lookup_rec *lr;
196	char *ptr = NULL;
197	char *dptr = NULL;
198
199	if (key == NULL) {
200		errno = EINVAL;
201		return NULL;
202	}
203
204	ptr = selabel_sub(rec->subs, key);
205	if (ptr) {
206		dptr = selabel_sub(rec->dist_subs, ptr);
207		if (dptr) {
208			free(ptr);
209			ptr = dptr;
210		}
211	} else {
212		ptr = selabel_sub(rec->dist_subs, key);
213	}
214	if (ptr) {
215		lr = rec->func_lookup(rec, ptr, type);
216		free(ptr);
217	} else {
218		lr = rec->func_lookup(rec, key, type);
219	}
220	if (!lr)
221		return NULL;
222
223	if (compat_validate(rec, lr, rec->spec_file, 0))
224		return NULL;
225
226	if (translating && !lr->ctx_trans &&
227	    selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans))
228		return NULL;
229
230	return lr;
231}
232
233int selabel_lookup(struct selabel_handle *rec, char **con,
234		   const char *key, int type)
235{
236	struct selabel_lookup_rec *lr;
237
238	lr = selabel_lookup_common(rec, 1, key, type);
239	if (!lr)
240		return -1;
241
242	*con = strdup(lr->ctx_trans);
243	return *con ? 0 : -1;
244}
245
246int selabel_lookup_raw(struct selabel_handle *rec, char **con,
247		       const char *key, int type)
248{
249	struct selabel_lookup_rec *lr;
250
251	lr = selabel_lookup_common(rec, 0, key, type);
252	if (!lr)
253		return -1;
254
255	*con = strdup(lr->ctx_raw);
256	return *con ? 0 : -1;
257}
258
259void selabel_close(struct selabel_handle *rec)
260{
261	selabel_subs_fini(rec->subs);
262	selabel_subs_fini(rec->dist_subs);
263	rec->func_close(rec);
264	free(rec->spec_file);
265	free(rec);
266}
267
268void selabel_stats(struct selabel_handle *rec)
269{
270	rec->func_stats(rec);
271}
272