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_FILE_BACKEND
21 #define CONFIG_FILE_BACKEND(fnptr) NULL
22 #else
23 #define CONFIG_FILE_BACKEND(fnptr) &fnptr
24 #endif
25
26 #ifdef NO_MEDIA_BACKEND
27 #define CONFIG_MEDIA_BACKEND(fnptr) NULL
28 #else
29 #define CONFIG_MEDIA_BACKEND(fnptr) &fnptr
30 #endif
31
32 #ifdef NO_X_BACKEND
33 #define CONFIG_X_BACKEND(fnptr) NULL
34 #else
35 #define CONFIG_X_BACKEND(fnptr) &fnptr
36 #endif
37
38 #ifdef NO_DB_BACKEND
39 #define CONFIG_DB_BACKEND(fnptr) NULL
40 #else
41 #define CONFIG_DB_BACKEND(fnptr) &fnptr
42 #endif
43
44 #ifdef NO_ANDROID_BACKEND
45 #define CONFIG_ANDROID_BACKEND(fnptr) NULL
46 #else
47 #define CONFIG_ANDROID_BACKEND(fnptr) (&(fnptr))
48 #endif
49
50 typedef int (*selabel_initfunc)(struct selabel_handle *rec,
51 const struct selinux_opt *opts,
52 unsigned nopts);
53
54 static selabel_initfunc initfuncs[] = {
55 CONFIG_FILE_BACKEND(selabel_file_init),
56 CONFIG_MEDIA_BACKEND(selabel_media_init),
57 CONFIG_X_BACKEND(selabel_x_init),
58 CONFIG_DB_BACKEND(selabel_db_init),
59 CONFIG_ANDROID_BACKEND(selabel_property_init),
60 CONFIG_ANDROID_BACKEND(selabel_service_init),
61 };
62
selabel_subs_fini(struct selabel_sub * ptr)63 static void selabel_subs_fini(struct selabel_sub *ptr)
64 {
65 struct selabel_sub *next;
66
67 while (ptr) {
68 next = ptr->next;
69 free(ptr->src);
70 free(ptr->dst);
71 free(ptr);
72 ptr = next;
73 }
74 }
75
selabel_sub(struct selabel_sub * ptr,const char * src)76 static char *selabel_sub(struct selabel_sub *ptr, const char *src)
77 {
78 char *dst = NULL;
79 int len;
80
81 while (ptr) {
82 if (strncmp(src, ptr->src, ptr->slen) == 0 ) {
83 if (src[ptr->slen] == '/' ||
84 src[ptr->slen] == 0) {
85 if ((src[ptr->slen] == '/') &&
86 (strcmp(ptr->dst, "/") == 0))
87 len = ptr->slen + 1;
88 else
89 len = ptr->slen;
90 if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0)
91 return NULL;
92 return dst;
93 }
94 }
95 ptr = ptr->next;
96 }
97 return NULL;
98 }
99
selabel_subs_init(const char * path,struct selabel_sub * list,struct selabel_digest * digest)100 struct selabel_sub *selabel_subs_init(const char *path,
101 struct selabel_sub *list,
102 struct selabel_digest *digest)
103 {
104 char buf[1024];
105 FILE *cfg = fopen(path, "re");
106 struct selabel_sub *sub = NULL;
107 struct stat sb;
108
109 if (!cfg)
110 return list;
111
112 if (fstat(fileno(cfg), &sb) < 0)
113 return list;
114
115 while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) {
116 char *ptr = NULL;
117 char *src = buf;
118 char *dst = NULL;
119
120 while (*src && isspace(*src))
121 src++;
122 if (src[0] == '#') continue;
123 ptr = src;
124 while (*ptr && ! isspace(*ptr))
125 ptr++;
126 *ptr++ = '\0';
127 if (! *src) continue;
128
129 dst = ptr;
130 while (*dst && isspace(*dst))
131 dst++;
132 ptr=dst;
133 while (*ptr && ! isspace(*ptr))
134 ptr++;
135 *ptr='\0';
136 if (! *dst)
137 continue;
138
139 sub = malloc(sizeof(*sub));
140 if (! sub)
141 goto err;
142 memset(sub, 0, sizeof(*sub));
143
144 sub->src=strdup(src);
145 if (! sub->src)
146 goto err;
147
148 sub->dst=strdup(dst);
149 if (! sub->dst)
150 goto err;
151
152 sub->slen = strlen(src);
153 sub->next = list;
154 list = sub;
155 }
156
157 if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0)
158 goto err;
159
160 out:
161 fclose(cfg);
162 return list;
163 err:
164 if (sub)
165 free(sub->src);
166 free(sub);
167 goto out;
168 }
169
selabel_is_digest_set(const struct selinux_opt * opts,unsigned n,struct selabel_digest * entry)170 static inline struct selabel_digest *selabel_is_digest_set
171 (const struct selinux_opt *opts,
172 unsigned n,
173 struct selabel_digest *entry)
174 {
175 struct selabel_digest *digest = NULL;
176
177 while (n--) {
178 if (opts[n].type == SELABEL_OPT_DIGEST &&
179 opts[n].value == (char *)1) {
180 digest = calloc(1, sizeof(*digest));
181 if (!digest)
182 goto err;
183
184 digest->digest = calloc(1, DIGEST_SPECFILE_SIZE + 1);
185 if (!digest->digest)
186 goto err;
187
188 digest->specfile_list = calloc(DIGEST_FILES_MAX,
189 sizeof(char *));
190 if (!digest->specfile_list)
191 goto err;
192
193 entry = digest;
194 return entry;
195 }
196 }
197 return NULL;
198
199 err:
200 if (digest) {
201 free(digest->digest);
202 free(digest->specfile_list);
203 free(digest);
204 }
205 return NULL;
206 }
207
selabel_digest_fini(struct selabel_digest * ptr)208 static void selabel_digest_fini(struct selabel_digest *ptr)
209 {
210 int i;
211
212 free(ptr->digest);
213 free(ptr->hashbuf);
214
215 if (ptr->specfile_list) {
216 for (i = 0; ptr->specfile_list[i]; i++)
217 free(ptr->specfile_list[i]);
218 free(ptr->specfile_list);
219 }
220 free(ptr);
221 }
222
223 /*
224 * Validation functions
225 */
226
selabel_is_validate_set(const struct selinux_opt * opts,unsigned n)227 static inline int selabel_is_validate_set(const struct selinux_opt *opts,
228 unsigned n)
229 {
230 while (n--)
231 if (opts[n].type == SELABEL_OPT_VALIDATE)
232 return !!opts[n].value;
233
234 return 0;
235 }
236
selabel_validate(struct selabel_handle * rec,struct selabel_lookup_rec * contexts)237 int selabel_validate(struct selabel_handle *rec,
238 struct selabel_lookup_rec *contexts)
239 {
240 int rc = 0;
241
242 if (!rec->validating || contexts->validated)
243 goto out;
244
245 rc = selinux_validate(&contexts->ctx_raw);
246 if (rc < 0)
247 goto out;
248
249 contexts->validated = 1;
250 out:
251 return rc;
252 }
253
254 /* Public API helpers */
selabel_sub_key(struct selabel_handle * rec,const char * key)255 static char *selabel_sub_key(struct selabel_handle *rec, const char *key)
256 {
257 char *ptr = NULL;
258 char *dptr = NULL;
259
260 ptr = selabel_sub(rec->subs, key);
261 if (ptr) {
262 dptr = selabel_sub(rec->dist_subs, ptr);
263 if (dptr) {
264 free(ptr);
265 ptr = dptr;
266 }
267 } else {
268 ptr = selabel_sub(rec->dist_subs, key);
269 }
270 if (ptr)
271 return ptr;
272
273 return NULL;
274 }
275
selabel_fini(struct selabel_handle * rec,struct selabel_lookup_rec * lr,int translating)276 static int selabel_fini(struct selabel_handle *rec,
277 struct selabel_lookup_rec *lr,
278 int translating)
279 {
280 char *path = NULL;
281
282 if (rec->spec_files)
283 path = rec->spec_files[0];
284 if (compat_validate(rec, lr, path, 0))
285 return -1;
286
287 if (translating && !lr->ctx_trans &&
288 selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans))
289 return -1;
290
291 return 0;
292 }
293
294 static struct selabel_lookup_rec *
selabel_lookup_common(struct selabel_handle * rec,int translating,const char * key,int type)295 selabel_lookup_common(struct selabel_handle *rec, int translating,
296 const char *key, int type)
297 {
298 struct selabel_lookup_rec *lr;
299 char *ptr = NULL;
300
301 if (key == NULL) {
302 errno = EINVAL;
303 return NULL;
304 }
305
306 ptr = selabel_sub_key(rec, key);
307 if (ptr) {
308 lr = rec->func_lookup(rec, ptr, type);
309 free(ptr);
310 } else {
311 lr = rec->func_lookup(rec, key, type);
312 }
313 if (!lr)
314 return NULL;
315
316 if (selabel_fini(rec, lr, translating))
317 return NULL;
318
319 return lr;
320 }
321
322 static struct selabel_lookup_rec *
selabel_lookup_bm_common(struct selabel_handle * rec,int translating,const char * key,int type,const char ** aliases)323 selabel_lookup_bm_common(struct selabel_handle *rec, int translating,
324 const char *key, int type, const char **aliases)
325 {
326 struct selabel_lookup_rec *lr;
327 char *ptr = NULL;
328
329 if (key == NULL) {
330 errno = EINVAL;
331 return NULL;
332 }
333
334 ptr = selabel_sub_key(rec, key);
335 if (ptr) {
336 lr = rec->func_lookup_best_match(rec, ptr, aliases, type);
337 free(ptr);
338 } else {
339 lr = rec->func_lookup_best_match(rec, key, aliases, type);
340 }
341 if (!lr)
342 return NULL;
343
344 if (selabel_fini(rec, lr, translating))
345 return NULL;
346
347 return lr;
348 }
349
350 /*
351 * Public API
352 */
353
selabel_open(unsigned int backend,const struct selinux_opt * opts,unsigned nopts)354 struct selabel_handle *selabel_open(unsigned int backend,
355 const struct selinux_opt *opts,
356 unsigned nopts)
357 {
358 struct selabel_handle *rec = NULL;
359
360 if (backend >= ARRAY_SIZE(initfuncs)) {
361 errno = EINVAL;
362 goto out;
363 }
364
365 if (!initfuncs[backend]) {
366 errno = ENOTSUP;
367 goto out;
368 }
369
370 rec = (struct selabel_handle *)malloc(sizeof(*rec));
371 if (!rec)
372 goto out;
373
374 memset(rec, 0, sizeof(*rec));
375 rec->backend = backend;
376 rec->validating = selabel_is_validate_set(opts, nopts);
377
378 rec->subs = NULL;
379 rec->dist_subs = NULL;
380 rec->digest = selabel_is_digest_set(opts, nopts, rec->digest);
381
382 if ((*initfuncs[backend])(rec, opts, nopts)) {
383 selabel_close(rec);
384 rec = NULL;
385 }
386 out:
387 return rec;
388 }
389
selabel_lookup(struct selabel_handle * rec,char ** con,const char * key,int type)390 int selabel_lookup(struct selabel_handle *rec, char **con,
391 const char *key, int type)
392 {
393 struct selabel_lookup_rec *lr;
394
395 lr = selabel_lookup_common(rec, 1, key, type);
396 if (!lr)
397 return -1;
398
399 *con = strdup(lr->ctx_trans);
400 return *con ? 0 : -1;
401 }
402
selabel_lookup_raw(struct selabel_handle * rec,char ** con,const char * key,int type)403 int selabel_lookup_raw(struct selabel_handle *rec, char **con,
404 const char *key, int type)
405 {
406 struct selabel_lookup_rec *lr;
407
408 lr = selabel_lookup_common(rec, 0, key, type);
409 if (!lr)
410 return -1;
411
412 *con = strdup(lr->ctx_raw);
413 return *con ? 0 : -1;
414 }
415
selabel_partial_match(struct selabel_handle * rec,const char * key)416 bool selabel_partial_match(struct selabel_handle *rec, const char *key)
417 {
418 char *ptr;
419 bool ret;
420
421 if (!rec->func_partial_match) {
422 /*
423 * If the label backend does not support partial matching,
424 * then assume a match is possible.
425 */
426 return true;
427 }
428
429 ptr = selabel_sub_key(rec, key);
430 if (ptr) {
431 ret = rec->func_partial_match(rec, ptr);
432 free(ptr);
433 } else {
434 ret = rec->func_partial_match(rec, key);
435 }
436
437 return ret;
438 }
439
selabel_lookup_best_match(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)440 int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
441 const char *key, const char **aliases, int type)
442 {
443 struct selabel_lookup_rec *lr;
444
445 if (!rec->func_lookup_best_match) {
446 errno = ENOTSUP;
447 return -1;
448 }
449
450 lr = selabel_lookup_bm_common(rec, 1, key, type, aliases);
451 if (!lr)
452 return -1;
453
454 *con = strdup(lr->ctx_trans);
455 return *con ? 0 : -1;
456 }
457
selabel_lookup_best_match_raw(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)458 int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con,
459 const char *key, const char **aliases, int type)
460 {
461 struct selabel_lookup_rec *lr;
462
463 if (!rec->func_lookup_best_match) {
464 errno = ENOTSUP;
465 return -1;
466 }
467
468 lr = selabel_lookup_bm_common(rec, 0, key, type, aliases);
469 if (!lr)
470 return -1;
471
472 *con = strdup(lr->ctx_raw);
473 return *con ? 0 : -1;
474 }
475
selabel_cmp(struct selabel_handle * h1,struct selabel_handle * h2)476 enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1,
477 struct selabel_handle *h2)
478 {
479 if (!h1->func_cmp || h1->func_cmp != h2->func_cmp)
480 return SELABEL_INCOMPARABLE;
481
482 return h1->func_cmp(h1, h2);
483 }
484
selabel_digest(struct selabel_handle * rec,unsigned char ** digest,size_t * digest_len,char *** specfiles,size_t * num_specfiles)485 int selabel_digest(struct selabel_handle *rec,
486 unsigned char **digest, size_t *digest_len,
487 char ***specfiles, size_t *num_specfiles)
488 {
489 if (!rec->digest) {
490 errno = EINVAL;
491 return -1;
492 }
493
494 *digest = rec->digest->digest;
495 *digest_len = DIGEST_SPECFILE_SIZE;
496 *specfiles = rec->digest->specfile_list;
497 *num_specfiles = rec->digest->specfile_cnt;
498 return 0;
499 }
500
selabel_close(struct selabel_handle * rec)501 void selabel_close(struct selabel_handle *rec)
502 {
503 size_t i;
504 selabel_subs_fini(rec->subs);
505 selabel_subs_fini(rec->dist_subs);
506 if (rec->spec_files) {
507 for (i = 0; i < rec->spec_files_len; i++)
508 free(rec->spec_files[i]);
509 free(rec->spec_files);
510 }
511 if (rec->digest)
512 selabel_digest_fini(rec->digest);
513 if (rec->func_close)
514 rec->func_close(rec);
515 free(rec);
516 }
517
selabel_stats(struct selabel_handle * rec)518 void selabel_stats(struct selabel_handle *rec)
519 {
520 rec->func_stats(rec);
521 }
522