diff --git a/libraries/libapparmor/include/sys/apparmor.h b/libraries/libapparmor/include/sys/apparmor.h index d70eff947..e8c09f482 100644 --- a/libraries/libapparmor/include/sys/apparmor.h +++ b/libraries/libapparmor/include/sys/apparmor.h @@ -157,6 +157,8 @@ extern int aa_features_write_to_file(aa_features *features, int dirfd, const char *path); extern bool aa_features_is_equal(aa_features *features1, aa_features *features2); +extern int aa_features_check(int dirfd, const char *path, + aa_features *features); extern bool aa_features_supports(aa_features *features, const char *str); extern char *aa_features_id(aa_features *features); extern char *aa_features_value(aa_features *features, const char *str, size_t *len); diff --git a/libraries/libapparmor/src/features.c b/libraries/libapparmor/src/features.c index 23297885b..9ee5204cf 100644 --- a/libraries/libapparmor/src/features.c +++ b/libraries/libapparmor/src/features.c @@ -35,6 +35,7 @@ #include "PMurHash.h" #define FEATURES_FILE "/sys/kernel/security/apparmor/features" +#define CACHE_FEATURES_FILE ".features" #define HASH_SIZE (8 + 1) /* 32 bits binary to hex + NUL terminator */ #define STRING_SIZE 8192 @@ -656,6 +657,44 @@ bool aa_features_is_equal(aa_features *features1, aa_features *features2) strcmp(features1->string, features2->string) == 0; } +/** + * aa_features_check - check if features from a directory matches an aa_features object + * @dirfd: a directory file descriptory or AT_FDCWD (see openat(2)) + * @path: the path containing the features + * @features: features to be matched against + * + * Returns: 0 on success, -1 on failure. errno is set to EEXIST when there's not a match + */ +int aa_features_check(int dirfd, const char *path, + aa_features *features) +{ + aa_features *local_features = NULL; + autofree char *name = NULL; + bool rc; + int len; + + len = asprintf(&name, "%s/%s", path, CACHE_FEATURES_FILE); + if (len == -1) { + errno = ENOMEM; + return -1; + } + + /* verify that path dir .features matches */ + if (aa_features_new(&local_features, dirfd, name)) { + PDEBUG("could not setup new features object for dirfd '%d' '%s'\n", dirfd, name); + return -1; + } + + rc = aa_features_is_equal(local_features, features); + aa_features_unref(local_features); + if (!rc) { + errno = EEXIST; + return -1; + } + + return 0; +} + static const char *features_lookup(aa_features *features, const char *str) { const char *features_string = features->string; diff --git a/libraries/libapparmor/src/libapparmor.map b/libraries/libapparmor/src/libapparmor.map index 41e541ac2..e9e1563d3 100644 --- a/libraries/libapparmor/src/libapparmor.map +++ b/libraries/libapparmor/src/libapparmor.map @@ -124,6 +124,13 @@ APPARMOR_3.0 { *; } APPARMOR_2.13.1; +APPARMOR_3.1 { + global: + aa_features_check; + local: + *; +} APPARMOR_3.0; + PRIVATE { global: _aa_is_blacklisted; diff --git a/libraries/libapparmor/src/policy_cache.c b/libraries/libapparmor/src/policy_cache.c index 1f3478746..2b6d75aee 100644 --- a/libraries/libapparmor/src/policy_cache.c +++ b/libraries/libapparmor/src/policy_cache.c @@ -145,36 +145,6 @@ repeat: return path; } -static int cache_check_features(int dirfd, const char *cache_name, - aa_features *features) -{ - aa_features *local_features = NULL; - autofree char *name = NULL; - bool rc; - int len; - - len = asprintf(&name, "%s/%s", cache_name, CACHE_FEATURES_FILE); - if (len == -1) { - errno = ENOMEM; - return -1; - } - - /* verify that cache dir .features matches */ - if (aa_features_new(&local_features, dirfd, name)) { - PDEBUG("could not setup new features object for dirfd '%d' '%s'\n", dirfd, name); - return -1; - } - - rc = aa_features_is_equal(local_features, features); - aa_features_unref(local_features); - if (!rc) { - errno = EEXIST; - return -1; - } - - return 0; -} - static int create_cache(aa_policy_cache *policy_cache, aa_features *features) { if (aa_policy_cache_remove(policy_cache->dirfd[0], ".")) @@ -192,8 +162,8 @@ static int create_cache(aa_policy_cache *policy_cache, aa_features *features) static int init_cache_features(aa_policy_cache *policy_cache, aa_features *kernel_features, bool create) { - if (cache_check_features(policy_cache->dirfd[0], ".", - kernel_features)) { + if (aa_features_check(policy_cache->dirfd[0], ".", + kernel_features)) { /* EEXIST must come before ENOENT for short circuit eval */ if (!create || errno == EEXIST || errno != ENOENT) return -1; @@ -229,13 +199,13 @@ static int cache_miss_cb(int dirfd, const struct dirent *ent, void *arg) errno = ENOMEM; return -1; } - if (!cache_check_features(dirfd, cache_name, data->features) || errno == ENOENT) { + if (!aa_features_check(dirfd, cache_name, data->features) || errno == ENOENT) { /* found cache dir matching pattern */ data->cache_name = cache_name; /* return 1 to stop iteration and signal dir found */ return 1; } else if (errno != EEXIST) { - PDEBUG("cache_check_features() failed for dirfd '%d' '%s'\n", dirfd, cache_name); + PDEBUG("aa_features_check() failed for dirfd '%d' '%s'\n", dirfd, cache_name); free(cache_name); return -1; } @@ -271,12 +241,12 @@ static int cache_dir_from_path_and_features(char **cache_path, if (len == -1) return -1; - if (!cache_check_features(dirfd, cache_dir, features) || errno == ENOENT) { + if (!aa_features_check(dirfd, cache_dir, features) || errno == ENOENT) { PDEBUG("cache_dir_from_path_and_features() found '%s'\n", cache_dir); *cache_path = cache_dir; return 0; } else if (errno != EEXIST) { - PDEBUG("cache_check_features() failed for dirfd '%d' %s\n", dirfd, cache_dir); + PDEBUG("aa_features_check() failed for dirfd '%d' %s\n", dirfd, cache_dir); free(cache_dir); return -1; }