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