|
| 1 | +/* SPDX-License-Identifier: LGPL-2.1+ */ |
| 2 | + |
| 3 | +#include <errno.h> |
| 4 | +#if HAVE_APPARMOR |
| 5 | +# include <sys/apparmor.h> |
| 6 | +#endif |
| 7 | +#include <unistd.h> |
| 8 | + |
| 9 | +#include "apparmor-setup.h" |
| 10 | +#include "apparmor-util.h" |
| 11 | +#include "fd-util.h" |
| 12 | +#include "fileio.h" |
| 13 | +#include "log.h" |
| 14 | +#include "macro.h" |
| 15 | +#include "string-util.h" |
| 16 | +#include "strv.h" |
| 17 | + |
| 18 | +#if HAVE_APPARMOR |
| 19 | +DEFINE_TRIVIAL_CLEANUP_FUNC(aa_policy_cache *, aa_policy_cache_unref); |
| 20 | +DEFINE_TRIVIAL_CLEANUP_FUNC(aa_features *, aa_features_unref); |
| 21 | +#endif |
| 22 | + |
| 23 | +int mac_apparmor_setup(void) { |
| 24 | +#if HAVE_APPARMOR |
| 25 | + int r; |
| 26 | + _cleanup_(aa_policy_cache_unrefp) aa_policy_cache *policy_cache = NULL; |
| 27 | + _cleanup_(aa_features_unrefp) aa_features *features = NULL; |
| 28 | + const char *current_file; |
| 29 | + _cleanup_free_ char *current_profile = NULL, *cache_dir_path = NULL; |
| 30 | + |
| 31 | + if (!mac_apparmor_use()) { |
| 32 | + log_debug("AppArmor either not supported by the kernel or disabled."); |
| 33 | + return 0; |
| 34 | + } |
| 35 | + |
| 36 | + /* To enable LSM stacking a patch to the kernel is proposed to create a |
| 37 | + * per-LSM subdirectory to distinguish between the LSMs. Therefore, we |
| 38 | + * read the file from the LSM specific directory first and only if that |
| 39 | + * fails the one from the generic directory. |
| 40 | + */ |
| 41 | + FOREACH_STRING(current_file, "/proc/self/attr/apparmor/current", "/proc/self/attr/current") { |
| 42 | + r = read_one_line_file(current_file, ¤t_profile); |
| 43 | + if (r == -ENOENT) |
| 44 | + continue; |
| 45 | + else if (r < 0) |
| 46 | + log_warning_errno(r, "Failed to read current AppArmor profile from file %s, ignoring: %m", current_file); |
| 47 | + else |
| 48 | + break; |
| 49 | + } |
| 50 | + if (!current_profile) { |
| 51 | + log_warning("Failed to get the current AppArmor profile of systemd from /proc/self/attr/apparmor/current or /proc/self/attr/current, ignoring."); |
| 52 | + return 0; |
| 53 | + } |
| 54 | + if (!streq(current_profile, "unconfined")) { |
| 55 | + log_debug("We are already confined in an AppArmor profile."); |
| 56 | + return 0; |
| 57 | + } |
| 58 | + |
| 59 | + r = aa_features_new_from_kernel(&features); |
| 60 | + if (r < 0) { |
| 61 | + log_warning_errno(errno, "Failed to get the AppArmor feature set from the kernel, ignoring: %m"); |
| 62 | + return 0; |
| 63 | + } |
| 64 | + cache_dir_path = aa_policy_cache_dir_path_preview(features, AT_FDCWD, "/etc/apparmor/earlypolicy"); |
| 65 | + if (!cache_dir_path) { |
| 66 | + log_debug_errno(errno, "Failed to get the path of the early AppArmor policy cache directory."); |
| 67 | + return 0; |
| 68 | + } |
| 69 | + |
| 70 | + /* aa_policy_cache_new will internally use the same path as aa_policy_cache_dir_path_preview has returned. */ |
| 71 | + r = aa_policy_cache_new(&policy_cache, features, AT_FDCWD, "/etc/apparmor/earlypolicy", 0); |
| 72 | + if (r < 0) { |
| 73 | + if (errno == ENOENT) { |
| 74 | + log_debug_errno(errno, "The early AppArmor policy cache directory %s does not exist.", cache_dir_path); |
| 75 | + return 0; |
| 76 | + } |
| 77 | + log_warning_errno(errno, "Failed to create a new AppArmor policy cache, ignoring: %m"); |
| 78 | + return 0; |
| 79 | + } |
| 80 | + r = aa_policy_cache_replace_all(policy_cache, NULL); |
| 81 | + if (r < 0) { |
| 82 | + log_warning_errno(errno, "Failed to load the profiles from the early AppArmor policy cache directory %s, ignoring: %m", cache_dir_path); |
| 83 | + return 0; |
| 84 | + } |
| 85 | + |
| 86 | + log_info("Successfully loaded all binary profiles from AppArmor early policy cache at %s.", cache_dir_path); |
| 87 | + |
| 88 | + r = aa_change_profile("systemd"); |
| 89 | + if (r < 0) { |
| 90 | + if (errno == ENOENT) |
| 91 | + log_debug_errno(errno, "Failed to change to AppArmor profile 'systemd'. Please ensure that one of the binary profile files in policy cache directory %s contains a profile with that name.", cache_dir_path); |
| 92 | + else |
| 93 | + log_error_errno(errno, "Failed to change to AppArmor profile 'systemd': %m"); |
| 94 | + return 0; |
| 95 | + } |
| 96 | + |
| 97 | + log_info("Changed to AppArmor profile systemd."); |
| 98 | +#endif |
| 99 | + return 0; |
| 100 | +} |
0 commit comments