/* * Generalized labeling frontend for userspace object managers. * * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil> */ #include <sys/types.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <selinux/selinux.h> #include "callbacks.h" #include "label_internal.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #ifdef NO_FILE_BACKEND #define CONFIG_FILE_BACKEND(fnptr) NULL #else #define CONFIG_FILE_BACKEND(fnptr) &fnptr #endif #ifdef NO_MEDIA_BACKEND #define CONFIG_MEDIA_BACKEND(fnptr) NULL #else #define CONFIG_MEDIA_BACKEND(fnptr) &fnptr #endif #ifdef NO_X_BACKEND #define CONFIG_X_BACKEND(fnptr) NULL #else #define CONFIG_X_BACKEND(fnptr) &fnptr #endif #ifdef NO_DB_BACKEND #define CONFIG_DB_BACKEND(fnptr) NULL #else #define CONFIG_DB_BACKEND(fnptr) &fnptr #endif #ifdef NO_ANDROID_BACKEND #define CONFIG_ANDROID_BACKEND(fnptr) NULL #else #define CONFIG_ANDROID_BACKEND(fnptr) (&(fnptr)) #endif typedef int (*selabel_initfunc)(struct selabel_handle *rec, const struct selinux_opt *opts, unsigned nopts); static selabel_initfunc initfuncs[] = { CONFIG_FILE_BACKEND(selabel_file_init), CONFIG_MEDIA_BACKEND(selabel_media_init), CONFIG_X_BACKEND(selabel_x_init), CONFIG_DB_BACKEND(selabel_db_init), CONFIG_ANDROID_BACKEND(selabel_property_init), CONFIG_ANDROID_BACKEND(selabel_service_init), }; static inline struct selabel_digest *selabel_is_digest_set (const struct selinux_opt *opts, unsigned n, struct selabel_digest *entry) { struct selabel_digest *digest = NULL; while (n--) { if (opts[n].type == SELABEL_OPT_DIGEST && opts[n].value == (char *)1) { digest = calloc(1, sizeof(*digest)); if (!digest) goto err; digest->digest = calloc(1, DIGEST_SPECFILE_SIZE + 1); if (!digest->digest) goto err; digest->specfile_list = calloc(DIGEST_FILES_MAX, sizeof(char *)); if (!digest->specfile_list) goto err; entry = digest; return entry; } } return NULL; err: if (digest) { free(digest->digest); free(digest->specfile_list); free(digest); } return NULL; } static void selabel_digest_fini(struct selabel_digest *ptr) { int i; free(ptr->digest); free(ptr->hashbuf); if (ptr->specfile_list) { for (i = 0; ptr->specfile_list[i]; i++) free(ptr->specfile_list[i]); free(ptr->specfile_list); } free(ptr); } /* * Validation functions */ static inline int selabel_is_validate_set(const struct selinux_opt *opts, unsigned n) { while (n--) if (opts[n].type == SELABEL_OPT_VALIDATE) return !!opts[n].value; return 0; } int selabel_validate(struct selabel_handle *rec, struct selabel_lookup_rec *contexts) { int rc = 0; if (!rec->validating || contexts->validated) goto out; rc = selinux_validate(&contexts->ctx_raw); if (rc < 0) goto out; contexts->validated = 1; out: return rc; } /* Public API helpers */ static int selabel_fini(struct selabel_handle *rec, struct selabel_lookup_rec *lr, int translating) { char *path = NULL; if (rec->spec_files) path = rec->spec_files[0]; if (compat_validate(rec, lr, path, lr->lineno)) return -1; if (translating && !lr->ctx_trans && selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans)) return -1; return 0; } static struct selabel_lookup_rec * selabel_lookup_common(struct selabel_handle *rec, int translating, const char *key, int type) { struct selabel_lookup_rec *lr; if (key == NULL) { errno = EINVAL; return NULL; } lr = rec->func_lookup(rec, key, type); if (!lr) return NULL; if (selabel_fini(rec, lr, translating)) return NULL; return lr; } static struct selabel_lookup_rec * selabel_lookup_bm_common(struct selabel_handle *rec, int translating, const char *key, int type, const char **aliases) { struct selabel_lookup_rec *lr; if (key == NULL) { errno = EINVAL; return NULL; } lr = rec->func_lookup_best_match(rec, key, aliases, type); if (!lr) return NULL; if (selabel_fini(rec, lr, translating)) return NULL; return lr; } /* * Public API */ struct selabel_handle *selabel_open(unsigned int backend, const struct selinux_opt *opts, unsigned nopts) { struct selabel_handle *rec = NULL; if (backend >= ARRAY_SIZE(initfuncs)) { errno = EINVAL; goto out; } if (!initfuncs[backend]) { errno = ENOTSUP; goto out; } rec = (struct selabel_handle *)malloc(sizeof(*rec)); if (!rec) goto out; memset(rec, 0, sizeof(*rec)); rec->backend = backend; rec->validating = selabel_is_validate_set(opts, nopts); rec->digest = selabel_is_digest_set(opts, nopts, rec->digest); if ((*initfuncs[backend])(rec, opts, nopts)) { selabel_close(rec); rec = NULL; } out: return rec; } int selabel_lookup(struct selabel_handle *rec, char **con, const char *key, int type) { struct selabel_lookup_rec *lr; lr = selabel_lookup_common(rec, 1, key, type); if (!lr) return -1; *con = strdup(lr->ctx_trans); return *con ? 0 : -1; } int selabel_lookup_raw(struct selabel_handle *rec, char **con, const char *key, int type) { struct selabel_lookup_rec *lr; lr = selabel_lookup_common(rec, 0, key, type); if (!lr) return -1; *con = strdup(lr->ctx_raw); return *con ? 0 : -1; } bool selabel_partial_match(struct selabel_handle *rec, const char *key) { if (!rec->func_partial_match) { /* * If the label backend does not support partial matching, * then assume a match is possible. */ return true; } return rec->func_partial_match(rec, key); } bool selabel_hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest) { if (!rec->func_hash_all_partial_matches) { return false; } return rec->func_hash_all_partial_matches(rec, key, digest); } int selabel_lookup_best_match(struct selabel_handle *rec, char **con, const char *key, const char **aliases, int type) { struct selabel_lookup_rec *lr; if (!rec->func_lookup_best_match) { errno = ENOTSUP; return -1; } lr = selabel_lookup_bm_common(rec, 1, key, type, aliases); if (!lr) return -1; *con = strdup(lr->ctx_trans); return *con ? 0 : -1; } int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con, const char *key, const char **aliases, int type) { struct selabel_lookup_rec *lr; if (!rec->func_lookup_best_match) { errno = ENOTSUP; return -1; } lr = selabel_lookup_bm_common(rec, 0, key, type, aliases); if (!lr) return -1; *con = strdup(lr->ctx_raw); return *con ? 0 : -1; } enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1, struct selabel_handle *h2) { if (!h1->func_cmp || h1->func_cmp != h2->func_cmp) return SELABEL_INCOMPARABLE; return h1->func_cmp(h1, h2); } int selabel_digest(struct selabel_handle *rec, unsigned char **digest, size_t *digest_len, char ***specfiles, size_t *num_specfiles) { if (!rec->digest) { errno = EINVAL; return -1; } *digest = rec->digest->digest; *digest_len = DIGEST_SPECFILE_SIZE; *specfiles = rec->digest->specfile_list; *num_specfiles = rec->digest->specfile_cnt; return 0; } void selabel_close(struct selabel_handle *rec) { size_t i; if (rec->spec_files) { for (i = 0; i < rec->spec_files_len; i++) free(rec->spec_files[i]); free(rec->spec_files); } if (rec->digest) selabel_digest_fini(rec->digest); if (rec->func_close) rec->func_close(rec); free(rec); } void selabel_stats(struct selabel_handle *rec) { rec->func_stats(rec); }