| /* |
| * Generalized labeling frontend for userspace object managers. |
| * |
| * Author : Eamon Walsh <[email protected]> |
| */ |
| |
| #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_exact_match_init),//service init |
| CONFIG_ANDROID_BACKEND(selabel_exact_match_init),//keyStore key 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_get_digests_all_partial_matches(struct selabel_handle *rec, |
| const char *key, |
| uint8_t **calculated_digest, |
| uint8_t **xattr_digest, |
| size_t *digest_len) |
| { |
| if (!rec->func_get_digests_all_partial_matches) |
| return false; |
| |
| return rec->func_get_digests_all_partial_matches(rec, key, |
| calculated_digest, |
| xattr_digest, |
| digest_len); |
| } |
| |
| 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); |
| } |