• 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 typedef int (*selabel_initfunc)(struct selabel_handle *rec,
21 				const struct selinux_opt *opts,
22 				unsigned nopts);
23 
24 static 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 
selabel_subs_fini(struct selabel_sub * ptr)32 static 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 
selabel_sub(struct selabel_sub * ptr,const char * src)45 static 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 
selabel_subs_init(const char * path,struct selabel_sub * list,struct selabel_digest * digest)69 struct 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 
129 out:
130 	fclose(cfg);
131 	return list;
132 err:
133 	if (sub)
134 		free(sub->src);
135 	free(sub);
136 	goto out;
137 }
138 
selabel_is_digest_set(const struct selinux_opt * opts,unsigned n,struct selabel_digest * entry)139 static 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 
168 err:
169 	free(digest->digest);
170 	free(digest->specfile_list);
171 	free(digest);
172 	return NULL;
173 }
174 
selabel_digest_fini(struct selabel_digest * ptr)175 static 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 
selabel_is_validate_set(const struct selinux_opt * opts,unsigned n)194 static 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 
selabel_validate(struct selabel_handle * rec,struct selabel_lookup_rec * contexts)204 int 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;
217 out:
218 	return rc;
219 }
220 
221 /* Public API helpers */
selabel_sub_key(struct selabel_handle * rec,const char * key)222 static 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 
selabel_fini(struct selabel_handle * rec,struct selabel_lookup_rec * lr,int translating)243 static 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 
257 static struct selabel_lookup_rec *
selabel_lookup_common(struct selabel_handle * rec,int translating,const char * key,int type)258 selabel_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 
285 static struct selabel_lookup_rec *
selabel_lookup_bm_common(struct selabel_handle * rec,int translating,const char * key,int type,const char ** aliases)286 selabel_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 
selabel_open(unsigned int backend,const struct selinux_opt * opts,unsigned nopts)317 struct 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 
346 out:
347 	return rec;
348 }
349 
selabel_lookup(struct selabel_handle * rec,char ** con,const char * key,int type)350 int 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 
selabel_lookup_raw(struct selabel_handle * rec,char ** con,const char * key,int type)363 int 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 
selabel_partial_match(struct selabel_handle * rec,const char * key)376 bool 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 
selabel_lookup_best_match(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)400 int 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 
selabel_lookup_best_match_raw(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)418 int 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 
selabel_cmp(struct selabel_handle * h1,struct selabel_handle * h2)436 enum 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 
selabel_digest(struct selabel_handle * rec,unsigned char ** digest,size_t * digest_len,char *** specfiles,size_t * num_specfiles)445 int 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 
selabel_close(struct selabel_handle * rec)461 void 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 
selabel_stats(struct selabel_handle * rec)472 void selabel_stats(struct selabel_handle *rec)
473 {
474 	rec->func_stats(rec);
475 }
476