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
44 typedef int (*selabel_initfunc)(struct selabel_handle *rec,
45 const struct selinux_opt *opts,
46 unsigned nopts);
47
48 static 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
selabel_subs_fini(struct selabel_sub * ptr)57 static 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
selabel_sub(struct selabel_sub * ptr,const char * src)70 static 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
selabel_subs_init(const char * path,struct selabel_sub * list,struct selabel_digest * digest)94 struct 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
154 out:
155 fclose(cfg);
156 return list;
157 err:
158 if (sub)
159 free(sub->src);
160 free(sub);
161 goto out;
162 }
163
selabel_is_digest_set(const struct selinux_opt * opts,unsigned n,struct selabel_digest * entry)164 static 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
193 err:
194 free(digest->digest);
195 free(digest->specfile_list);
196 free(digest);
197 return NULL;
198 }
199
selabel_digest_fini(struct selabel_digest * ptr)200 static 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
selabel_is_validate_set(const struct selinux_opt * opts,unsigned n)219 static 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
selabel_validate(struct selabel_handle * rec,struct selabel_lookup_rec * contexts)229 int 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;
242 out:
243 return rc;
244 }
245
246 /* Public API helpers */
selabel_sub_key(struct selabel_handle * rec,const char * key)247 static 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
selabel_fini(struct selabel_handle * rec,struct selabel_lookup_rec * lr,int translating)268 static 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
286 static struct selabel_lookup_rec *
selabel_lookup_common(struct selabel_handle * rec,int translating,const char * key,int type)287 selabel_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
314 static struct selabel_lookup_rec *
selabel_lookup_bm_common(struct selabel_handle * rec,int translating,const char * key,int type,const char ** aliases)315 selabel_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
selabel_open(unsigned int backend,const struct selinux_opt * opts,unsigned nopts)346 struct 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 }
378 out:
379 return rec;
380 }
381
selabel_lookup(struct selabel_handle * rec,char ** con,const char * key,int type)382 int 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
selabel_lookup_raw(struct selabel_handle * rec,char ** con,const char * key,int type)395 int 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
selabel_partial_match(struct selabel_handle * rec,const char * key)408 bool 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
selabel_lookup_best_match(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)432 int 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
selabel_lookup_best_match_raw(struct selabel_handle * rec,char ** con,const char * key,const char ** aliases,int type)450 int 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
selabel_cmp(struct selabel_handle * h1,struct selabel_handle * h2)468 enum 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
selabel_digest(struct selabel_handle * rec,unsigned char ** digest,size_t * digest_len,char *** specfiles,size_t * num_specfiles)477 int 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
selabel_close(struct selabel_handle * rec)493 void 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
selabel_stats(struct selabel_handle * rec)510 void selabel_stats(struct selabel_handle *rec)
511 {
512 rec->func_stats(rec);
513 }
514