From 47008e5161fa097ce9b848dee194b43262b743a5 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 16:13:25 -0700 Subject: LSM: Introduce LSM_FLAG_LEGACY_MAJOR This adds a flag for the current "major" LSMs to distinguish them when we have a universal method for ordering all LSMs. It's called "legacy" since the distinction of "major" will go away in the blob-sharing world. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler Reviewed-by: John Johansen --- security/apparmor/lsm.c | 1 + security/selinux/hooks.c | 1 + security/smack/smack_lsm.c | 1 + security/tomoyo/tomoyo.c | 1 + 4 files changed, 4 insertions(+) (limited to 'security') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 2c010874329f..e49c50e0d5ab 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1729,5 +1729,6 @@ alloc_out: DEFINE_LSM(apparmor) = { .name = "apparmor", + .flags = LSM_FLAG_LEGACY_MAJOR, .init = apparmor_init, }; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f0e36c3492ba..41908d2d6149 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6999,6 +6999,7 @@ void selinux_complete_init(void) all processes and objects when they are created. */ DEFINE_LSM(selinux) = { .name = "selinux", + .flags = LSM_FLAG_LEGACY_MAJOR, .init = selinux_init, }; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 430d4f35e55c..d72d215d7fde 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4812,5 +4812,6 @@ static __init int smack_init(void) */ DEFINE_LSM(smack) = { .name = "smack", + .flags = LSM_FLAG_LEGACY_MAJOR, .init = smack_init, }; diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 1b5b5097efd7..09f7af130d3a 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -552,5 +552,6 @@ static int __init tomoyo_init(void) DEFINE_LSM(tomoyo) = { .name = "tomoyo", + .flags = LSM_FLAG_LEGACY_MAJOR, .init = tomoyo_init, }; -- cgit v1.2.3-59-g8ed1b From 657d910b52a38c5e0d753c2a5448c6ae26ec85d0 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 16:16:55 -0700 Subject: LSM: Provide separate ordered initialization This provides a place for ordered LSMs to be initialized, separate from the "major" LSMs. This is mainly a copy/paste from major_lsm_init() to ordered_lsm_init(), but it will change drastically in later patches. What is not obvious in the patch is that this change moves the integrity LSM from major_lsm_init() into ordered_lsm_init(), since it is not marked with the LSM_FLAG_LEGACY_MAJOR. As it is the only LSM in the "ordered" list, there is no reordering yet created. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler Reviewed-by: John Johansen --- security/security.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'security') diff --git a/security/security.c b/security/security.c index f1b8d2587639..6bc591f77b1a 100644 --- a/security/security.c +++ b/security/security.c @@ -52,12 +52,30 @@ static __initdata bool debug; pr_info(__VA_ARGS__); \ } while (0) +static void __init ordered_lsm_init(void) +{ + struct lsm_info *lsm; + int ret; + + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) != 0) + continue; + + init_debug("initializing %s\n", lsm->name); + ret = lsm->init(); + WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); + } +} + static void __init major_lsm_init(void) { struct lsm_info *lsm; int ret; for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0) + continue; + init_debug("initializing %s\n", lsm->name); ret = lsm->init(); WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); @@ -87,6 +105,9 @@ int __init security_init(void) yama_add_hooks(); loadpin_add_hooks(); + /* Load LSMs in specified order. */ + ordered_lsm_init(); + /* * Load all the remaining security modules. */ -- cgit v1.2.3-59-g8ed1b From c5459b829b716dafd226ad270f25c9a3050f7586 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 13 Sep 2018 22:28:48 -0700 Subject: LSM: Plumb visibility into optional "enabled" state In preparation for lifting the "is this LSM enabled?" logic out of the individual LSMs, pass in any special enabled state tracking (as needed for SELinux, AppArmor, and LoadPin). This should be an "int" to include handling any future cases where "enabled" is exposed via sysctl which has no "bool" type. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler Reviewed-by: John Johansen --- include/linux/lsm_hooks.h | 1 + security/apparmor/lsm.c | 5 +++-- security/selinux/hooks.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 318d93f918c3..7bbe5e287161 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2047,6 +2047,7 @@ extern void security_add_hooks(struct security_hook_list *hooks, int count, struct lsm_info { const char *name; /* Required. */ unsigned long flags; /* Optional: flags describing LSM */ + int *enabled; /* Optional: NULL means enabled. */ int (*init)(void); /* Required. */ }; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e49c50e0d5ab..a4652ff622cf 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1333,8 +1333,8 @@ bool aa_g_paranoid_load = true; module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO); /* Boot time disable flag */ -static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; -module_param_named(enabled, apparmor_enabled, bool, S_IRUGO); +static int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; +module_param_named(enabled, apparmor_enabled, int, 0444); static int __init apparmor_enabled_setup(char *str) { @@ -1730,5 +1730,6 @@ alloc_out: DEFINE_LSM(apparmor) = { .name = "apparmor", .flags = LSM_FLAG_LEGACY_MAJOR, + .enabled = &apparmor_enabled, .init = apparmor_init, }; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 41908d2d6149..f847514d6f03 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -7000,6 +7000,7 @@ void selinux_complete_init(void) DEFINE_LSM(selinux) = { .name = "selinux", .flags = LSM_FLAG_LEGACY_MAJOR, + .enabled = &selinux_enabled, .init = selinux_init, }; -- cgit v1.2.3-59-g8ed1b From f4941d75b9cba5e1fae1aebe0139dcca0703a294 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 13 Sep 2018 23:17:50 -0700 Subject: LSM: Lift LSM selection out of individual LSMs As a prerequisite to adjusting LSM selection logic in the future, this moves the selection logic up out of the individual major LSMs, making their init functions only run when actually enabled. This considers all LSMs enabled by default unless they specified an external "enable" variable. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler Reviewed-by: John Johansen --- include/linux/lsm_hooks.h | 1 - security/apparmor/lsm.c | 6 --- security/security.c | 102 +++++++++++++++++++++++++++++++-------------- security/selinux/hooks.c | 10 ----- security/smack/smack_lsm.c | 3 -- security/tomoyo/tomoyo.c | 2 - 6 files changed, 71 insertions(+), 53 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 7bbe5e287161..be1581d18e3e 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2088,7 +2088,6 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, #define __lsm_ro_after_init __ro_after_init #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ -extern int __init security_module_enable(const char *module); extern void __init capability_add_hooks(void); #ifdef CONFIG_SECURITY_YAMA extern void __init yama_add_hooks(void); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index a4652ff622cf..dfc5fbf8ba82 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1663,12 +1663,6 @@ static int __init apparmor_init(void) { int error; - if (!apparmor_enabled || !security_module_enable("apparmor")) { - aa_info_message("AppArmor disabled by boot time parameter"); - apparmor_enabled = false; - return 0; - } - aa_secids_init(); error = aa_setup_dfa_engine(); diff --git a/security/security.c b/security/security.c index 6bc591f77b1a..c900d7a1441a 100644 --- a/security/security.c +++ b/security/security.c @@ -52,33 +52,96 @@ static __initdata bool debug; pr_info(__VA_ARGS__); \ } while (0) +static bool __init is_enabled(struct lsm_info *lsm) +{ + if (!lsm->enabled || *lsm->enabled) + return true; + + return false; +} + +/* Mark an LSM's enabled flag. */ +static int lsm_enabled_true __initdata = 1; +static int lsm_enabled_false __initdata = 0; +static void __init set_enabled(struct lsm_info *lsm, bool enabled) +{ + /* + * When an LSM hasn't configured an enable variable, we can use + * a hard-coded location for storing the default enabled state. + */ + if (!lsm->enabled) { + if (enabled) + lsm->enabled = &lsm_enabled_true; + else + lsm->enabled = &lsm_enabled_false; + } else if (lsm->enabled == &lsm_enabled_true) { + if (!enabled) + lsm->enabled = &lsm_enabled_false; + } else if (lsm->enabled == &lsm_enabled_false) { + if (enabled) + lsm->enabled = &lsm_enabled_true; + } else { + *lsm->enabled = enabled; + } +} + +/* Is an LSM allowed to be initialized? */ +static bool __init lsm_allowed(struct lsm_info *lsm) +{ + /* Skip if the LSM is disabled. */ + if (!is_enabled(lsm)) + return false; + + /* Skip major-specific checks if not a major LSM. */ + if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0) + return true; + + /* Disabled if this LSM isn't the chosen one. */ + if (strcmp(lsm->name, chosen_lsm) != 0) + return false; + + return true; +} + +/* Check if LSM should be initialized. */ +static void __init maybe_initialize_lsm(struct lsm_info *lsm) +{ + int enabled = lsm_allowed(lsm); + + /* Record enablement (to handle any following exclusive LSMs). */ + set_enabled(lsm, enabled); + + /* If selected, initialize the LSM. */ + if (enabled) { + int ret; + + init_debug("initializing %s\n", lsm->name); + ret = lsm->init(); + WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); + } +} + static void __init ordered_lsm_init(void) { struct lsm_info *lsm; - int ret; for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) != 0) continue; - init_debug("initializing %s\n", lsm->name); - ret = lsm->init(); - WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); + maybe_initialize_lsm(lsm); } } static void __init major_lsm_init(void) { struct lsm_info *lsm; - int ret; for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0) continue; - init_debug("initializing %s\n", lsm->name); - ret = lsm->init(); - WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); + maybe_initialize_lsm(lsm); } } @@ -168,29 +231,6 @@ static int lsm_append(char *new, char **result) return 0; } -/** - * security_module_enable - Load given security module on boot ? - * @module: the name of the module - * - * Each LSM must pass this method before registering its own operations - * to avoid security registration races. This method may also be used - * to check if your LSM is currently loaded during kernel initialization. - * - * Returns: - * - * true if: - * - * - The passed LSM is the one chosen by user at boot time, - * - or the passed LSM is configured as the default and the user did not - * choose an alternate LSM at boot time. - * - * Otherwise, return false. - */ -int __init security_module_enable(const char *module) -{ - return !strcmp(module, chosen_lsm); -} - /** * security_add_hooks - Add a modules hooks to the hook lists. * @hooks: the hooks to add diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f847514d6f03..0f8ae2fbd14a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6928,16 +6928,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { static __init int selinux_init(void) { - if (!security_module_enable("selinux")) { - selinux_enabled = 0; - return 0; - } - - if (!selinux_enabled) { - pr_info("SELinux: Disabled at boot.\n"); - return 0; - } - pr_info("SELinux: Initializing.\n"); memset(&selinux_state, 0, sizeof(selinux_state)); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index d72d215d7fde..580e9d6e5680 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4762,9 +4762,6 @@ static __init int smack_init(void) struct cred *cred; struct task_smack *tsp; - if (!security_module_enable("smack")) - return 0; - smack_inode_cache = KMEM_CACHE(inode_smack, 0); if (!smack_inode_cache) return -ENOMEM; diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 09f7af130d3a..a46f6bc1e97c 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -540,8 +540,6 @@ static int __init tomoyo_init(void) { struct cred *cred = (struct cred *) current_cred(); - if (!security_module_enable("tomoyo")) - return 0; /* register ourselves with the security framework */ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); printk(KERN_INFO "TOMOYO Linux initialized\n"); -- cgit v1.2.3-59-g8ed1b From 2d4d51198c730adbbc5e071b18c84e5d0d2d65df Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 16:58:31 -0700 Subject: LSM: Build ordered list of LSMs to initialize This constructs an ordered list of LSMs to initialize, using a hard-coded list of only "integrity": minor LSMs continue to have direct hook calls, and major LSMs continue to initialize separately. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler --- security/security.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index c900d7a1441a..3a277fbf6023 100644 --- a/security/security.c +++ b/security/security.c @@ -37,6 +37,9 @@ /* Maximum number of letters for an LSM name string */ #define SECURITY_NAME_MAX 10 +/* How many LSMs were built into the kernel? */ +#define LSM_COUNT (__end_lsm_info - __start_lsm_info) + struct security_hook_heads security_hook_heads __lsm_ro_after_init; static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); @@ -45,6 +48,9 @@ char *lsm_names; static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_DEFAULT_SECURITY; +/* Ordered list of LSMs to initialize. */ +static __initdata struct lsm_info **ordered_lsms; + static __initdata bool debug; #define init_debug(...) \ do { \ @@ -85,6 +91,34 @@ static void __init set_enabled(struct lsm_info *lsm, bool enabled) } } +/* Is an LSM already listed in the ordered LSMs list? */ +static bool __init exists_ordered_lsm(struct lsm_info *lsm) +{ + struct lsm_info **check; + + for (check = ordered_lsms; *check; check++) + if (*check == lsm) + return true; + + return false; +} + +/* Append an LSM to the list of ordered LSMs to initialize. */ +static int last_lsm __initdata; +static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) +{ + /* Ignore duplicate selections. */ + if (exists_ordered_lsm(lsm)) + return; + + if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\n", from)) + return; + + ordered_lsms[last_lsm++] = lsm; + init_debug("%s ordering: %s (%sabled)\n", from, lsm->name, + is_enabled(lsm) ? "en" : "dis"); +} + /* Is an LSM allowed to be initialized? */ static bool __init lsm_allowed(struct lsm_info *lsm) { @@ -121,18 +155,32 @@ static void __init maybe_initialize_lsm(struct lsm_info *lsm) } } -static void __init ordered_lsm_init(void) +/* Populate ordered LSMs list from single LSM name. */ +static void __init ordered_lsm_parse(const char *order, const char *origin) { struct lsm_info *lsm; for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { - if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) != 0) - continue; - - maybe_initialize_lsm(lsm); + if (strcmp(lsm->name, order) == 0) + append_ordered_lsm(lsm, origin); } } +static void __init ordered_lsm_init(void) +{ + struct lsm_info **lsm; + + ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms), + GFP_KERNEL); + + ordered_lsm_parse("integrity", "builtin"); + + for (lsm = ordered_lsms; *lsm; lsm++) + maybe_initialize_lsm(*lsm); + + kfree(ordered_lsms); +} + static void __init major_lsm_init(void) { struct lsm_info *lsm; -- cgit v1.2.3-59-g8ed1b From 13e735c0e953246bd531d342bb86acb5b1bf664a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 9 Oct 2018 14:27:46 -0700 Subject: LSM: Introduce CONFIG_LSM This provides a way to declare LSM initialization order via the new CONFIG_LSM. Currently only non-major LSMs are recognized. This will be expanded in future patches. Signed-off-by: Kees Cook --- security/Kconfig | 9 +++++++++ security/security.c | 27 ++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) (limited to 'security') diff --git a/security/Kconfig b/security/Kconfig index e4fe2f3c2c65..7f21190cb677 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -276,5 +276,14 @@ config DEFAULT_SECURITY default "apparmor" if DEFAULT_SECURITY_APPARMOR default "" if DEFAULT_SECURITY_DAC +config LSM + string "Ordered list of enabled LSMs" + default "integrity" + help + A comma-separated list of LSMs, in initialization order. + Any LSMs left off this list will be ignored. + + If unsure, leave this as the default. + endmenu diff --git a/security/security.c b/security/security.c index 3a277fbf6023..11a42cd313c5 100644 --- a/security/security.c +++ b/security/security.c @@ -48,6 +48,8 @@ char *lsm_names; static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_DEFAULT_SECURITY; +static __initconst const char * const builtin_lsm_order = CONFIG_LSM; + /* Ordered list of LSMs to initialize. */ static __initdata struct lsm_info **ordered_lsms; @@ -155,15 +157,30 @@ static void __init maybe_initialize_lsm(struct lsm_info *lsm) } } -/* Populate ordered LSMs list from single LSM name. */ +/* Populate ordered LSMs list from comma-separated LSM name list. */ static void __init ordered_lsm_parse(const char *order, const char *origin) { struct lsm_info *lsm; + char *sep, *name, *next; + + sep = kstrdup(order, GFP_KERNEL); + next = sep; + /* Walk the list, looking for matching LSMs. */ + while ((name = strsep(&next, ",")) != NULL) { + bool found = false; + + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0 && + strcmp(lsm->name, name) == 0) { + append_ordered_lsm(lsm, origin); + found = true; + } + } - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { - if (strcmp(lsm->name, order) == 0) - append_ordered_lsm(lsm, origin); + if (!found) + init_debug("%s ignored: %s\n", origin, name); } + kfree(sep); } static void __init ordered_lsm_init(void) @@ -173,7 +190,7 @@ static void __init ordered_lsm_init(void) ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms), GFP_KERNEL); - ordered_lsm_parse("integrity", "builtin"); + ordered_lsm_parse(builtin_lsm_order, "builtin"); for (lsm = ordered_lsms; *lsm; lsm++) maybe_initialize_lsm(*lsm); -- cgit v1.2.3-59-g8ed1b From 79f7865d844c7703e3dc0e2f5b9ed2f3a4f412e5 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 17:30:09 -0700 Subject: LSM: Introduce "lsm=" for boottime LSM selection Provide a way to explicitly choose LSM initialization order via the new "lsm=" comma-separated list of LSMs. Signed-off-by: Kees Cook --- Documentation/admin-guide/kernel-parameters.txt | 4 ++++ security/Kconfig | 3 ++- security/security.c | 14 +++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b799bcf67d7b..e59e1471d4db 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2319,6 +2319,10 @@ lsm.debug [SECURITY] Enable LSM initialization debugging output. + lsm=lsm1,...,lsmN + [SECURITY] Choose order of LSM initialization. This + overrides CONFIG_LSM. + machvec= [IA-64] Force the use of a particular machine-vector (machvec) in a generic kernel. Example: machvec=hpzx1_swiotlb diff --git a/security/Kconfig b/security/Kconfig index 7f21190cb677..cedf69e8a22c 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -281,7 +281,8 @@ config LSM default "integrity" help A comma-separated list of LSMs, in initialization order. - Any LSMs left off this list will be ignored. + Any LSMs left off this list will be ignored. This can be + controlled at boot with the "lsm=" parameter. If unsure, leave this as the default. diff --git a/security/security.c b/security/security.c index 11a42cd313c5..2e1f48e8a6f2 100644 --- a/security/security.c +++ b/security/security.c @@ -47,6 +47,7 @@ char *lsm_names; /* Boot-time LSM user choice */ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_DEFAULT_SECURITY; +static __initdata const char *chosen_lsm_order; static __initconst const char * const builtin_lsm_order = CONFIG_LSM; @@ -190,7 +191,10 @@ static void __init ordered_lsm_init(void) ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms), GFP_KERNEL); - ordered_lsm_parse(builtin_lsm_order, "builtin"); + if (chosen_lsm_order) + ordered_lsm_parse(chosen_lsm_order, "cmdline"); + else + ordered_lsm_parse(builtin_lsm_order, "builtin"); for (lsm = ordered_lsms; *lsm; lsm++) maybe_initialize_lsm(*lsm); @@ -252,6 +256,14 @@ static int __init choose_lsm(char *str) } __setup("security=", choose_lsm); +/* Explicitly choose LSM initialization order. */ +static int __init choose_lsm_order(char *str) +{ + chosen_lsm_order = str; + return 1; +} +__setup("lsm=", choose_lsm_order); + /* Enable LSM order debugging. */ static int __init enable_debug(char *str) { -- cgit v1.2.3-59-g8ed1b From a8027fb0d188599ccdb2096f49f708bae04d86c4 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 9 Oct 2018 14:42:57 -0700 Subject: LSM: Tie enabling logic to presence in ordered list Until now, any LSM without an enable storage variable was considered enabled. This inverts the logic and sets defaults to true only if the LSM gets added to the ordered initialization list. (And an exception continues for the major LSMs until they are integrated into the ordered initialization in a later patch.) Signed-off-by: Kees Cook --- include/linux/lsm_hooks.h | 2 +- security/security.c | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index be1581d18e3e..e28a3aa639e8 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2047,7 +2047,7 @@ extern void security_add_hooks(struct security_hook_list *hooks, int count, struct lsm_info { const char *name; /* Required. */ unsigned long flags; /* Optional: flags describing LSM */ - int *enabled; /* Optional: NULL means enabled. */ + int *enabled; /* Optional: controlled by CONFIG_LSM */ int (*init)(void); /* Required. */ }; diff --git a/security/security.c b/security/security.c index 2e1f48e8a6f2..b6d3456978a4 100644 --- a/security/security.c +++ b/security/security.c @@ -63,10 +63,10 @@ static __initdata bool debug; static bool __init is_enabled(struct lsm_info *lsm) { - if (!lsm->enabled || *lsm->enabled) - return true; + if (!lsm->enabled) + return false; - return false; + return *lsm->enabled; } /* Mark an LSM's enabled flag. */ @@ -117,7 +117,11 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from) if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\n", from)) return; + /* Enable this LSM, if it is not already set. */ + if (!lsm->enabled) + lsm->enabled = &lsm_enabled_true; ordered_lsms[last_lsm++] = lsm; + init_debug("%s ordering: %s (%sabled)\n", from, lsm->name, is_enabled(lsm) ? "en" : "dis"); } @@ -210,6 +214,10 @@ static void __init major_lsm_init(void) if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0) continue; + /* Enable this LSM, if it is not already set. */ + if (!lsm->enabled) + lsm->enabled = &lsm_enabled_true; + maybe_initialize_lsm(lsm); } } -- cgit v1.2.3-59-g8ed1b From 5ef4e41918b2dffffa445d8d3a45f3dc257920dc Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 13:11:41 -0700 Subject: LSM: Prepare for reorganizing "security=" logic This moves the string handling for "security=" boot parameter into a stored pointer instead of a string duplicate. This will allow easier handling of the string when switching logic to use the coming enable/disable infrastructure. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler Reviewed-by: John Johansen --- security/security.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index b6d3456978a4..1e1f34285e96 100644 --- a/security/security.c +++ b/security/security.c @@ -34,9 +34,6 @@ #define MAX_LSM_EVM_XATTR 2 -/* Maximum number of letters for an LSM name string */ -#define SECURITY_NAME_MAX 10 - /* How many LSMs were built into the kernel? */ #define LSM_COUNT (__end_lsm_info - __start_lsm_info) @@ -45,9 +42,8 @@ static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); char *lsm_names; /* Boot-time LSM user choice */ -static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = - CONFIG_DEFAULT_SECURITY; static __initdata const char *chosen_lsm_order; +static __initdata const char *chosen_major_lsm; static __initconst const char * const builtin_lsm_order = CONFIG_LSM; @@ -138,7 +134,7 @@ static bool __init lsm_allowed(struct lsm_info *lsm) return true; /* Disabled if this LSM isn't the chosen one. */ - if (strcmp(lsm->name, chosen_lsm) != 0) + if (strcmp(lsm->name, chosen_major_lsm) != 0) return false; return true; @@ -168,6 +164,9 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) struct lsm_info *lsm; char *sep, *name, *next; + if (!chosen_major_lsm) + chosen_major_lsm = CONFIG_DEFAULT_SECURITY; + sep = kstrdup(order, GFP_KERNEL); next = sep; /* Walk the list, looking for matching LSMs. */ @@ -257,12 +256,12 @@ int __init security_init(void) } /* Save user chosen LSM */ -static int __init choose_lsm(char *str) +static int __init choose_major_lsm(char *str) { - strncpy(chosen_lsm, str, SECURITY_NAME_MAX); + chosen_major_lsm = str; return 1; } -__setup("security=", choose_lsm); +__setup("security=", choose_major_lsm); /* Explicitly choose LSM initialization order. */ static int __init choose_lsm_order(char *str) -- cgit v1.2.3-59-g8ed1b From 7e611486d905f435faf80969deed68a615019e6b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 13:32:15 -0700 Subject: LSM: Refactor "security=" in terms of enable/disable For what are marked as the Legacy Major LSMs, make them effectively exclusive when selected on the "security=" boot parameter, to handle the future case of when a previously major LSMs become non-exclusive (e.g. when TOMOYO starts blob-sharing). Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler --- security/security.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index 1e1f34285e96..88de6b073246 100644 --- a/security/security.c +++ b/security/security.c @@ -129,14 +129,6 @@ static bool __init lsm_allowed(struct lsm_info *lsm) if (!is_enabled(lsm)) return false; - /* Skip major-specific checks if not a major LSM. */ - if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0) - return true; - - /* Disabled if this LSM isn't the chosen one. */ - if (strcmp(lsm->name, chosen_major_lsm) != 0) - return false; - return true; } @@ -164,8 +156,28 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) struct lsm_info *lsm; char *sep, *name, *next; + /* Process "security=", if given. */ if (!chosen_major_lsm) chosen_major_lsm = CONFIG_DEFAULT_SECURITY; + if (chosen_major_lsm) { + struct lsm_info *major; + + /* + * To match the original "security=" behavior, this + * explicitly does NOT fallback to another Legacy Major + * if the selected one was separately disabled: disable + * all non-matching Legacy Major LSMs. + */ + for (major = __start_lsm_info; major < __end_lsm_info; + major++) { + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) && + strcmp(major->name, chosen_major_lsm) != 0) { + set_enabled(major, false); + init_debug("security=%s disabled: %s\n", + chosen_major_lsm, major->name); + } + } + } sep = kstrdup(order, GFP_KERNEL); next = sep; -- cgit v1.2.3-59-g8ed1b From 14bd99c821f7ace0e8110a1bfdfaa27e1788e20f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 19:57:06 -0700 Subject: LSM: Separate idea of "major" LSM from "exclusive" LSM In order to both support old "security=" Legacy Major LSM selection, and handling real exclusivity, this creates LSM_FLAG_EXCLUSIVE and updates the selection logic to handle them. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler --- include/linux/lsm_hooks.h | 1 + security/apparmor/lsm.c | 2 +- security/security.c | 12 ++++++++++++ security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- security/tomoyo/tomoyo.c | 2 +- 6 files changed, 17 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index e28a3aa639e8..c3843b33da9e 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2043,6 +2043,7 @@ extern void security_add_hooks(struct security_hook_list *hooks, int count, char *lsm); #define LSM_FLAG_LEGACY_MAJOR BIT(0) +#define LSM_FLAG_EXCLUSIVE BIT(1) struct lsm_info { const char *name; /* Required. */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index dfc5fbf8ba82..149a3e16b5da 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1723,7 +1723,7 @@ alloc_out: DEFINE_LSM(apparmor) = { .name = "apparmor", - .flags = LSM_FLAG_LEGACY_MAJOR, + .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, .enabled = &apparmor_enabled, .init = apparmor_init, }; diff --git a/security/security.c b/security/security.c index 88de6b073246..a8dd7defe30a 100644 --- a/security/security.c +++ b/security/security.c @@ -49,6 +49,7 @@ static __initconst const char * const builtin_lsm_order = CONFIG_LSM; /* Ordered list of LSMs to initialize. */ static __initdata struct lsm_info **ordered_lsms; +static __initdata struct lsm_info *exclusive; static __initdata bool debug; #define init_debug(...) \ @@ -129,6 +130,12 @@ static bool __init lsm_allowed(struct lsm_info *lsm) if (!is_enabled(lsm)) return false; + /* Not allowed if another exclusive LSM already initialized. */ + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) { + init_debug("exclusive disabled: %s\n", lsm->name); + return false; + } + return true; } @@ -144,6 +151,11 @@ static void __init maybe_initialize_lsm(struct lsm_info *lsm) if (enabled) { int ret; + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { + exclusive = lsm; + init_debug("exclusive chosen: %s\n", lsm->name); + } + init_debug("initializing %s\n", lsm->name); ret = lsm->init(); WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0f8ae2fbd14a..49865f119b16 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6989,7 +6989,7 @@ void selinux_complete_init(void) all processes and objects when they are created. */ DEFINE_LSM(selinux) = { .name = "selinux", - .flags = LSM_FLAG_LEGACY_MAJOR, + .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, .enabled = &selinux_enabled, .init = selinux_init, }; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 580e9d6e5680..780733341d02 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4809,6 +4809,6 @@ static __init int smack_init(void) */ DEFINE_LSM(smack) = { .name = "smack", - .flags = LSM_FLAG_LEGACY_MAJOR, + .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, .init = smack_init, }; diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index a46f6bc1e97c..daff7d7897ad 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -550,6 +550,6 @@ static int __init tomoyo_init(void) DEFINE_LSM(tomoyo) = { .name = "tomoyo", - .flags = LSM_FLAG_LEGACY_MAJOR, + .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, .init = tomoyo_init, }; -- cgit v1.2.3-59-g8ed1b From 0102fb83f90050b86ce37aec810ea17bb4448e0c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 1 Oct 2018 17:08:57 -0700 Subject: apparmor: Remove SECURITY_APPARMOR_BOOTPARAM_VALUE In preparation for removing CONFIG_DEFAULT_SECURITY, this removes the soon-to-be redundant SECURITY_APPARMOR_BOOTPARAM_VALUE. Since explicit ordering via CONFIG_LSM or "lsm=" will define whether an LSM is enabled or not, this CONFIG will become effectively ignored, so remove it. However, in order to stay backward-compatible with "security=apparmor", the enable variable defaults to true. Signed-off-by: Kees Cook --- security/apparmor/Kconfig | 16 ---------------- security/apparmor/lsm.c | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) (limited to 'security') diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index b6b68a7750ce..3de21f46c82a 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -14,22 +14,6 @@ config SECURITY_APPARMOR If you are unsure how to answer this question, answer N. -config SECURITY_APPARMOR_BOOTPARAM_VALUE - int "AppArmor boot parameter default value" - depends on SECURITY_APPARMOR - range 0 1 - default 1 - help - This option sets the default value for the kernel parameter - 'apparmor', which allows AppArmor to be enabled or disabled - at boot. If this option is set to 0 (zero), the AppArmor - kernel parameter will default to 0, disabling AppArmor at - boot. If this option is set to 1 (one), the AppArmor - kernel parameter will default to 1, enabling AppArmor at - boot. - - If you are unsure how to answer this question, answer 1. - config SECURITY_APPARMOR_HASH bool "Enable introspection of sha1 hashes for loaded profiles" depends on SECURITY_APPARMOR diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 149a3e16b5da..cda345767cfc 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1333,7 +1333,7 @@ bool aa_g_paranoid_load = true; module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO); /* Boot time disable flag */ -static int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; +static int apparmor_enabled __lsm_ro_after_init = 1; module_param_named(enabled, apparmor_enabled, int, 0444); static int __init apparmor_enabled_setup(char *str) -- cgit v1.2.3-59-g8ed1b From be6ec88f41ba94746f830ba38cc4d08dd5ddbb08 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 1 Oct 2018 17:08:57 -0700 Subject: selinux: Remove SECURITY_SELINUX_BOOTPARAM_VALUE In preparation for removing CONFIG_DEFAULT_SECURITY, this removes the soon-to-be redundant SECURITY_SELINUX_BOOTPARAM_VALUE. Since explicit ordering via CONFIG_LSM or "lsm=" will define whether an LSM is enabled or not, this CONFIG will become effectively ignored, so remove it. However, in order to stay backward-compatible with "security=selinux", the enable variable defaults to true. Signed-off-by: Kees Cook --- security/selinux/Kconfig | 15 --------------- security/selinux/hooks.c | 5 +---- 2 files changed, 1 insertion(+), 19 deletions(-) (limited to 'security') diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index 8af7a690eb40..55f032f1fc2d 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -22,21 +22,6 @@ config SECURITY_SELINUX_BOOTPARAM If you are unsure how to answer this question, answer N. -config SECURITY_SELINUX_BOOTPARAM_VALUE - int "NSA SELinux boot parameter default value" - depends on SECURITY_SELINUX_BOOTPARAM - range 0 1 - default 1 - help - This option sets the default value for the kernel parameter - 'selinux', which allows SELinux to be disabled at boot. If this - option is set to 0 (zero), the SELinux kernel parameter will - default to 0, disabling SELinux at bootup. If this option is - set to 1 (one), the SELinux kernel parameter will default to 1, - enabling SELinux at bootup. - - If you are unsure how to answer this question, answer 1. - config SECURITY_SELINUX_DISABLE bool "NSA SELinux runtime disable" depends on SECURITY_SELINUX diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 49865f119b16..c5d9fbbb5e5b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -121,9 +121,8 @@ __setup("enforcing=", enforcing_setup); #define selinux_enforcing_boot 1 #endif +int selinux_enabled __lsm_ro_after_init = 1; #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM -int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE; - static int __init selinux_enabled_setup(char *str) { unsigned long enabled; @@ -132,8 +131,6 @@ static int __init selinux_enabled_setup(char *str) return 1; } __setup("selinux=", selinux_enabled_setup); -#else -int selinux_enabled = 1; #endif static unsigned int selinux_checkreqprot_boot = -- cgit v1.2.3-59-g8ed1b From c91d8106b370593b4d3dcc0b06282bf39478ae13 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Mon, 19 Nov 2018 18:04:32 -0800 Subject: LSM: Add all exclusive LSMs to ordered initialization This removes CONFIG_DEFAULT_SECURITY in favor of the explicit ordering offered by CONFIG_LSM and adds all the exclusive LSMs to the ordered LSM initialization. The old meaning of CONFIG_DEFAULT_SECURITY is now captured by which exclusive LSM is listed first in the LSM order. All LSMs not added to the ordered list are explicitly disabled. Signed-off-by: Kees Cook Signed-off-by: Casey Schaufler --- security/security.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index a8dd7defe30a..2d08a5357bf4 100644 --- a/security/security.c +++ b/security/security.c @@ -169,8 +169,6 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) char *sep, *name, *next; /* Process "security=", if given. */ - if (!chosen_major_lsm) - chosen_major_lsm = CONFIG_DEFAULT_SECURITY; if (chosen_major_lsm) { struct lsm_info *major; @@ -198,8 +196,7 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) bool found = false; for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { - if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0 && - strcmp(lsm->name, name) == 0) { + if (strcmp(lsm->name, name) == 0) { append_ordered_lsm(lsm, origin); found = true; } @@ -208,6 +205,25 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) if (!found) init_debug("%s ignored: %s\n", origin, name); } + + /* Process "security=", if given. */ + if (chosen_major_lsm) { + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + if (exists_ordered_lsm(lsm)) + continue; + if (strcmp(lsm->name, chosen_major_lsm) == 0) + append_ordered_lsm(lsm, "security="); + } + } + + /* Disable all LSMs not in the ordered list. */ + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + if (exists_ordered_lsm(lsm)) + continue; + set_enabled(lsm, false); + init_debug("%s disabled: %s\n", origin, lsm->name); + } + kfree(sep); } @@ -229,22 +245,6 @@ static void __init ordered_lsm_init(void) kfree(ordered_lsms); } -static void __init major_lsm_init(void) -{ - struct lsm_info *lsm; - - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { - if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0) - continue; - - /* Enable this LSM, if it is not already set. */ - if (!lsm->enabled) - lsm->enabled = &lsm_enabled_true; - - maybe_initialize_lsm(lsm); - } -} - /** * security_init - initializes the security framework * @@ -271,11 +271,6 @@ int __init security_init(void) /* Load LSMs in specified order. */ ordered_lsm_init(); - /* - * Load all the remaining security modules. - */ - major_lsm_init(); - return 0; } -- cgit v1.2.3-59-g8ed1b From d8e9bbd4fa7f654bd877a312fc4104c6e5e5c6ca Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 10 Oct 2018 15:45:22 -0700 Subject: LSM: Split LSM preparation from initialization Since we already have to do a pass through the LSMs to figure out if exclusive LSMs should be disabled after the first one is seen as enabled, this splits the logic up a bit more cleanly. Now we do a full "prepare" pass through the LSMs (which also allows for later use by the blob-sharing code), before starting the LSM initialization pass. Signed-off-by: Kees Cook --- security/security.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index 2d08a5357bf4..46c5b0fa515e 100644 --- a/security/security.c +++ b/security/security.c @@ -139,22 +139,28 @@ static bool __init lsm_allowed(struct lsm_info *lsm) return true; } -/* Check if LSM should be initialized. */ -static void __init maybe_initialize_lsm(struct lsm_info *lsm) +/* Prepare LSM for initialization. */ +static void __init prepare_lsm(struct lsm_info *lsm) { int enabled = lsm_allowed(lsm); /* Record enablement (to handle any following exclusive LSMs). */ set_enabled(lsm, enabled); - /* If selected, initialize the LSM. */ + /* If enabled, do pre-initialization work. */ if (enabled) { - int ret; - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) { exclusive = lsm; init_debug("exclusive chosen: %s\n", lsm->name); } + } +} + +/* Initialize a given LSM, if it is enabled. */ +static void __init initialize_lsm(struct lsm_info *lsm) +{ + if (is_enabled(lsm)) { + int ret; init_debug("initializing %s\n", lsm->name); ret = lsm->init(); @@ -240,7 +246,10 @@ static void __init ordered_lsm_init(void) ordered_lsm_parse(builtin_lsm_order, "builtin"); for (lsm = ordered_lsms; *lsm; lsm++) - maybe_initialize_lsm(*lsm); + prepare_lsm(*lsm); + + for (lsm = ordered_lsms; *lsm; lsm++) + initialize_lsm(*lsm); kfree(ordered_lsms); } -- cgit v1.2.3-59-g8ed1b From 70b62c25665f636c9f6c700b26af7df296b0887e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 14 Sep 2018 15:26:37 -0700 Subject: LoadPin: Initialize as ordered LSM This converts LoadPin from being a direct "minor" LSM into an ordered LSM. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler --- include/linux/lsm_hooks.h | 5 ----- security/Kconfig | 39 +-------------------------------------- security/loadpin/loadpin.c | 8 +++++++- security/security.c | 1 - 4 files changed, 8 insertions(+), 45 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index c3843b33da9e..fb1a653ccfcb 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2095,10 +2095,5 @@ extern void __init yama_add_hooks(void); #else static inline void __init yama_add_hooks(void) { } #endif -#ifdef CONFIG_SECURITY_LOADPIN -void __init loadpin_add_hooks(void); -#else -static inline void loadpin_add_hooks(void) { }; -#endif #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/Kconfig b/security/Kconfig index cedf69e8a22c..2cd737ba7660 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -239,46 +239,9 @@ source "security/yama/Kconfig" source "security/integrity/Kconfig" -choice - prompt "Default security module" - default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX - default DEFAULT_SECURITY_SMACK if SECURITY_SMACK - default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO - default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR - default DEFAULT_SECURITY_DAC - - help - Select the security module that will be used by default if the - kernel parameter security= is not specified. - - config DEFAULT_SECURITY_SELINUX - bool "SELinux" if SECURITY_SELINUX=y - - config DEFAULT_SECURITY_SMACK - bool "Simplified Mandatory Access Control" if SECURITY_SMACK=y - - config DEFAULT_SECURITY_TOMOYO - bool "TOMOYO" if SECURITY_TOMOYO=y - - config DEFAULT_SECURITY_APPARMOR - bool "AppArmor" if SECURITY_APPARMOR=y - - config DEFAULT_SECURITY_DAC - bool "Unix Discretionary Access Controls" - -endchoice - -config DEFAULT_SECURITY - string - default "selinux" if DEFAULT_SECURITY_SELINUX - default "smack" if DEFAULT_SECURITY_SMACK - default "tomoyo" if DEFAULT_SECURITY_TOMOYO - default "apparmor" if DEFAULT_SECURITY_APPARMOR - default "" if DEFAULT_SECURITY_DAC - config LSM string "Ordered list of enabled LSMs" - default "integrity" + default "loadpin,integrity,selinux,smack,tomoyo,apparmor" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list will be ignored. This can be diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index 48f39631b370..055fb0a64169 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -187,13 +187,19 @@ static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(kernel_load_data, loadpin_load_data), }; -void __init loadpin_add_hooks(void) +static int __init loadpin_init(void) { pr_info("ready to pin (currently %senforcing)\n", enforce ? "" : "not "); security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin"); + return 0; } +DEFINE_LSM(loadpin) = { + .name = "loadpin", + .init = loadpin_init, +}; + /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ module_param(enforce, int, 0); MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning"); diff --git a/security/security.c b/security/security.c index 46c5b0fa515e..b8d75f5a948d 100644 --- a/security/security.c +++ b/security/security.c @@ -275,7 +275,6 @@ int __init security_init(void) */ capability_add_hooks(); yama_add_hooks(); - loadpin_add_hooks(); /* Load LSMs in specified order. */ ordered_lsm_init(); -- cgit v1.2.3-59-g8ed1b From d6aed64b74b73b64278c059eacd59d87167aa968 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 14 Sep 2018 15:37:20 -0700 Subject: Yama: Initialize as ordered LSM This converts Yama from being a direct "minor" LSM into an ordered LSM. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler --- include/linux/lsm_hooks.h | 5 ----- security/Kconfig | 2 +- security/security.c | 1 - security/yama/yama_lsm.c | 8 +++++++- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index fb1a653ccfcb..2849e9b2c01d 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2090,10 +2090,5 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ extern void __init capability_add_hooks(void); -#ifdef CONFIG_SECURITY_YAMA -extern void __init yama_add_hooks(void); -#else -static inline void __init yama_add_hooks(void) { } -#endif #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/Kconfig b/security/Kconfig index 2cd737ba7660..78dc12b7eeb3 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -241,7 +241,7 @@ source "security/integrity/Kconfig" config LSM string "Ordered list of enabled LSMs" - default "loadpin,integrity,selinux,smack,tomoyo,apparmor" + default "yama,loadpin,integrity,selinux,smack,tomoyo,apparmor" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list will be ignored. This can be diff --git a/security/security.c b/security/security.c index b8d75f5a948d..35f93b7c585b 100644 --- a/security/security.c +++ b/security/security.c @@ -274,7 +274,6 @@ int __init security_init(void) * Load minor LSMs, with the capability module always first. */ capability_add_hooks(); - yama_add_hooks(); /* Load LSMs in specified order. */ ordered_lsm_init(); diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index ffda91a4a1aa..eb1da1303d2e 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -477,9 +477,15 @@ static void __init yama_init_sysctl(void) static inline void yama_init_sysctl(void) { } #endif /* CONFIG_SYSCTL */ -void __init yama_add_hooks(void) +static int __init yama_init(void) { pr_info("Yama: becoming mindful.\n"); security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama"); yama_init_sysctl(); + return 0; } + +DEFINE_LSM(yama) = { + .name = "yama", + .init = yama_init, +}; -- cgit v1.2.3-59-g8ed1b From e2bc445b66cad25b0627391df8138a83d0e48f97 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 19 Sep 2018 17:48:21 -0700 Subject: LSM: Introduce enum lsm_order In preparation for distinguishing the "capability" LSM from other LSMs, it must be ordered first. This introduces LSM_ORDER_MUTABLE for the general LSMs and LSM_ORDER_FIRST for capability. In the future LSM_ORDER_LAST for could be added for anything that must run last (e.g. Landlock may use this). Signed-off-by: Kees Cook --- include/linux/lsm_hooks.h | 6 ++++++ security/security.c | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 2849e9b2c01d..27d4db9588bb 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2045,8 +2045,14 @@ extern void security_add_hooks(struct security_hook_list *hooks, int count, #define LSM_FLAG_LEGACY_MAJOR BIT(0) #define LSM_FLAG_EXCLUSIVE BIT(1) +enum lsm_order { + LSM_ORDER_FIRST = -1, /* This is only for capabilities. */ + LSM_ORDER_MUTABLE = 0, +}; + struct lsm_info { const char *name; /* Required. */ + enum lsm_order order; /* Optional: default is LSM_ORDER_MUTABLE */ unsigned long flags; /* Optional: flags describing LSM */ int *enabled; /* Optional: controlled by CONFIG_LSM */ int (*init)(void); /* Required. */ diff --git a/security/security.c b/security/security.c index 35f93b7c585b..8b673bb2a0dd 100644 --- a/security/security.c +++ b/security/security.c @@ -174,6 +174,12 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) struct lsm_info *lsm; char *sep, *name, *next; + /* LSM_ORDER_FIRST is always first. */ + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { + if (lsm->order == LSM_ORDER_FIRST) + append_ordered_lsm(lsm, "first"); + } + /* Process "security=", if given. */ if (chosen_major_lsm) { struct lsm_info *major; @@ -202,7 +208,8 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) bool found = false; for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) { - if (strcmp(lsm->name, name) == 0) { + if (lsm->order == LSM_ORDER_MUTABLE && + strcmp(lsm->name, name) == 0) { append_ordered_lsm(lsm, origin); found = true; } -- cgit v1.2.3-59-g8ed1b From d117a154e6128abac5409d3f173584e7b25981a2 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 14 Sep 2018 15:40:45 -0700 Subject: capability: Initialize as LSM_ORDER_FIRST This converts capabilities to use the new LSM_ORDER_FIRST position. Signed-off-by: Kees Cook Reviewed-by: Casey Schaufler --- include/linux/lsm_hooks.h | 2 -- security/commoncap.c | 9 ++++++++- security/security.c | 5 ----- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 27d4db9588bb..0c908c091a03 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2095,6 +2095,4 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, #define __lsm_ro_after_init __ro_after_init #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ -extern void __init capability_add_hooks(void); - #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/commoncap.c b/security/commoncap.c index 232db019f051..52e04136bfa8 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1362,10 +1362,17 @@ struct security_hook_list capability_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory), }; -void __init capability_add_hooks(void) +static int __init capability_init(void) { security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), "capability"); + return 0; } +DEFINE_LSM(capability) = { + .name = "capability", + .order = LSM_ORDER_FIRST, + .init = capability_init, +}; + #endif /* CONFIG_SECURITY */ diff --git a/security/security.c b/security/security.c index 8b673bb2a0dd..9411f659454b 100644 --- a/security/security.c +++ b/security/security.c @@ -277,11 +277,6 @@ int __init security_init(void) i++) INIT_HLIST_HEAD(&list[i]); - /* - * Load minor LSMs, with the capability module always first. - */ - capability_add_hooks(); - /* Load LSMs in specified order. */ ordered_lsm_init(); -- cgit v1.2.3-59-g8ed1b From 6d9c939dbe4d0bcea09cd4b410f624cde1acb678 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:16:59 -0700 Subject: procfs: add smack subdir to attrs Back in 2007 I made what turned out to be a rather serious mistake in the implementation of the Smack security module. The SELinux module used an interface in /proc to manipulate the security context on processes. Rather than use a similar interface, I used the same interface. The AppArmor team did likewise. Now /proc/.../attr/current will tell you the security "context" of the process, but it will be different depending on the security module you're using. This patch provides a subdirectory in /proc/.../attr for Smack. Smack user space can use the "current" file in this subdirectory and never have to worry about getting SELinux attributes by mistake. Programs that use the old interface will continue to work (or fail, as the case may be) as before. The proposed S.A.R.A security module is dependent on the mechanism to create its own attr subdirectory. The original implementation is by Kees Cook. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- Documentation/admin-guide/LSM/index.rst | 13 +++++-- fs/proc/base.c | 64 ++++++++++++++++++++++++++++----- fs/proc/internal.h | 1 + include/linux/security.h | 15 +++++--- security/security.c | 24 ++++++++++--- 5 files changed, 96 insertions(+), 21 deletions(-) (limited to 'security') diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst index c980dfe9abf1..9842e21afd4a 100644 --- a/Documentation/admin-guide/LSM/index.rst +++ b/Documentation/admin-guide/LSM/index.rst @@ -17,9 +17,8 @@ MAC extensions, other extensions can be built using the LSM to provide specific changes to system operation when these tweaks are not available in the core functionality of Linux itself. -Without a specific LSM built into the kernel, the default LSM will be the -Linux capabilities system. Most LSMs choose to extend the capabilities -system, building their checks on top of the defined capability hooks. +The Linux capabilities modules will always be included. This may be +followed by any number of "minor" modules and at most one "major" module. For more details on capabilities, see ``capabilities(7)`` in the Linux man-pages project. @@ -30,6 +29,14 @@ order in which checks are made. The capability module will always be first, followed by any "minor" modules (e.g. Yama) and then the one "major" module (e.g. SELinux) if there is one configured. +Process attributes associated with "major" security modules should +be accessed and maintained using the special files in ``/proc/.../attr``. +A security module may maintain a module specific subdirectory there, +named after the module. ``/proc/.../attr/smack`` is provided by the Smack +security module and contains all its special files. The files directly +in ``/proc/.../attr`` remain as legacy interfaces for modules that provide +subdirectories. + .. toctree:: :maxdepth: 1 diff --git a/fs/proc/base.c b/fs/proc/base.c index 633a63462573..c9d775fd24ef 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -140,9 +140,13 @@ struct pid_entry { #define REG(NAME, MODE, fops) \ NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {}) #define ONE(NAME, MODE, show) \ - NOD(NAME, (S_IFREG|(MODE)), \ + NOD(NAME, (S_IFREG|(MODE)), \ NULL, &proc_single_file_operations, \ { .proc_show = show } ) +#define ATTR(LSM, NAME, MODE) \ + NOD(NAME, (S_IFREG|(MODE)), \ + NULL, &proc_pid_attr_operations, \ + { .lsm = LSM }) /* * Count the number of hardlinks for the pid_entry table, excluding the . @@ -2525,7 +2529,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, if (!task) return -ESRCH; - length = security_getprocattr(task, + length = security_getprocattr(task, PROC_I(inode)->op.lsm, (char*)file->f_path.dentry->d_name.name, &p); put_task_struct(task); @@ -2574,7 +2578,9 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, if (rv < 0) goto out_free; - rv = security_setprocattr(file->f_path.dentry->d_name.name, page, count); + rv = security_setprocattr(PROC_I(inode)->op.lsm, + file->f_path.dentry->d_name.name, page, + count); mutex_unlock(¤t->signal->cred_guard_mutex); out_free: kfree(page); @@ -2588,13 +2594,53 @@ static const struct file_operations proc_pid_attr_operations = { .llseek = generic_file_llseek, }; +#define LSM_DIR_OPS(LSM) \ +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \ + struct dir_context *ctx) \ +{ \ + return proc_pident_readdir(filp, ctx, \ + LSM##_attr_dir_stuff, \ + ARRAY_SIZE(LSM##_attr_dir_stuff)); \ +} \ +\ +static const struct file_operations proc_##LSM##_attr_dir_ops = { \ + .read = generic_read_dir, \ + .iterate = proc_##LSM##_attr_dir_iterate, \ + .llseek = default_llseek, \ +}; \ +\ +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \ + struct dentry *dentry, unsigned int flags) \ +{ \ + return proc_pident_lookup(dir, dentry, \ + LSM##_attr_dir_stuff, \ + ARRAY_SIZE(LSM##_attr_dir_stuff)); \ +} \ +\ +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \ + .lookup = proc_##LSM##_attr_dir_lookup, \ + .getattr = pid_getattr, \ + .setattr = proc_setattr, \ +} + +#ifdef CONFIG_SECURITY_SMACK +static const struct pid_entry smack_attr_dir_stuff[] = { + ATTR("smack", "current", 0666), +}; +LSM_DIR_OPS(smack); +#endif + static const struct pid_entry attr_dir_stuff[] = { - REG("current", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("prev", S_IRUGO, proc_pid_attr_operations), - REG("exec", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("fscreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("keycreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), + ATTR(NULL, "current", 0666), + ATTR(NULL, "prev", 0444), + ATTR(NULL, "exec", 0666), + ATTR(NULL, "fscreate", 0666), + ATTR(NULL, "keycreate", 0666), + ATTR(NULL, "sockcreate", 0666), +#ifdef CONFIG_SECURITY_SMACK + DIR("smack", 0555, + proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops), +#endif }; static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 5185d7f6a51e..d4f9989063d0 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -81,6 +81,7 @@ union proc_op { int (*proc_show)(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task); + const char *lsm; }; struct proc_inode { diff --git a/include/linux/security.h b/include/linux/security.h index dbfb5a66babb..b2c5333ed4b5 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -366,8 +366,10 @@ int security_sem_semctl(struct kern_ipc_perm *sma, int cmd); int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops, unsigned nsops, int alter); void security_d_instantiate(struct dentry *dentry, struct inode *inode); -int security_getprocattr(struct task_struct *p, char *name, char **value); -int security_setprocattr(const char *name, void *value, size_t size); +int security_getprocattr(struct task_struct *p, const char *lsm, char *name, + char **value); +int security_setprocattr(const char *lsm, const char *name, void *value, + size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); int security_ismaclabel(const char *name); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); @@ -1112,15 +1114,18 @@ static inline int security_sem_semop(struct kern_ipc_perm *sma, return 0; } -static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode) +static inline void security_d_instantiate(struct dentry *dentry, + struct inode *inode) { } -static inline int security_getprocattr(struct task_struct *p, char *name, char **value) +static inline int security_getprocattr(struct task_struct *p, const char *lsm, + char *name, char **value) { return -EINVAL; } -static inline int security_setprocattr(char *name, void *value, size_t size) +static inline int security_setprocattr(const char *lsm, char *name, + void *value, size_t size) { return -EINVAL; } diff --git a/security/security.c b/security/security.c index 9411f659454b..60b39db95c2f 100644 --- a/security/security.c +++ b/security/security.c @@ -1485,14 +1485,30 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) } EXPORT_SYMBOL(security_d_instantiate); -int security_getprocattr(struct task_struct *p, char *name, char **value) +int security_getprocattr(struct task_struct *p, const char *lsm, char *name, + char **value) { - return call_int_hook(getprocattr, -EINVAL, p, name, value); + struct security_hook_list *hp; + + hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + return hp->hook.getprocattr(p, name, value); + } + return -EINVAL; } -int security_setprocattr(const char *name, void *value, size_t size) +int security_setprocattr(const char *lsm, const char *name, void *value, + size_t size) { - return call_int_hook(setprocattr, -EINVAL, name, value, size); + struct security_hook_list *hp; + + hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + return hp->hook.setprocattr(name, value, size); + } + return -EINVAL; } int security_netlink_send(struct sock *sk, struct sk_buff *skb) -- cgit v1.2.3-59-g8ed1b From b17103a8b8ae9c9ecc5e1e6501b1478ee2dc6fe4 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 9 Nov 2018 16:12:56 -0800 Subject: Smack: Abstract use of cred security blob Don't use the cred->security pointer directly. Provide a helper function that provides the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- security/smack/smack.h | 17 ++++++++++--- security/smack/smack_access.c | 4 +-- security/smack/smack_lsm.c | 57 +++++++++++++++++++++---------------------- security/smack/smackfs.c | 18 +++++++------- 4 files changed, 53 insertions(+), 43 deletions(-) (limited to 'security') diff --git a/security/smack/smack.h b/security/smack/smack.h index f7db791fb566..01a922856eba 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -356,6 +356,11 @@ extern struct list_head smack_onlycap_list; #define SMACK_HASH_SLOTS 16 extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; +static inline struct task_smack *smack_cred(const struct cred *cred) +{ + return cred->security; +} + /* * Is the directory transmuting? */ @@ -382,13 +387,19 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp) return tsp->smk_task; } -static inline struct smack_known *smk_of_task_struct(const struct task_struct *t) +static inline struct smack_known *smk_of_task_struct( + const struct task_struct *t) { struct smack_known *skp; + const struct cred *cred; rcu_read_lock(); - skp = smk_of_task(__task_cred(t)->security); + + cred = __task_cred(t); + skp = smk_of_task(smack_cred(cred)); + rcu_read_unlock(); + return skp; } @@ -405,7 +416,7 @@ static inline struct smack_known *smk_of_forked(const struct task_smack *tsp) */ static inline struct smack_known *smk_of_current(void) { - return smk_of_task(current_security()); + return smk_of_task(smack_cred(current_cred())); } /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 9a4c0ad46518..489d49a20b47 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -275,7 +275,7 @@ out_audit: int smk_curacc(struct smack_known *obj_known, u32 mode, struct smk_audit_info *a) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_tskacc(tsp, obj_known, mode, a); } @@ -635,7 +635,7 @@ DEFINE_MUTEX(smack_onlycap_lock); */ bool smack_privileged_cred(int cap, const struct cred *cred) { - struct task_smack *tsp = cred->security; + struct task_smack *tsp = smack_cred(cred); struct smack_known *skp = tsp->smk_task; struct smack_known_list_elem *sklep; int rc; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 780733341d02..9a050ca17296 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -139,7 +139,7 @@ static int smk_bu_note(char *note, struct smack_known *sskp, static int smk_bu_current(char *note, struct smack_known *oskp, int mode, int rc) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); char acc[SMK_NUM_ACCESS_TYPE + 1]; if (rc <= 0) @@ -160,7 +160,7 @@ static int smk_bu_current(char *note, struct smack_known *oskp, #ifdef CONFIG_SECURITY_SMACK_BRINGUP static int smk_bu_task(struct task_struct *otp, int mode, int rc) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); struct smack_known *smk_task = smk_of_task_struct(otp); char acc[SMK_NUM_ACCESS_TYPE + 1]; @@ -182,7 +182,7 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc) #ifdef CONFIG_SECURITY_SMACK_BRINGUP static int smk_bu_inode(struct inode *inode, int mode, int rc) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); struct inode_smack *isp = inode->i_security; char acc[SMK_NUM_ACCESS_TYPE + 1]; @@ -212,7 +212,7 @@ static int smk_bu_inode(struct inode *inode, int mode, int rc) #ifdef CONFIG_SECURITY_SMACK_BRINGUP static int smk_bu_file(struct file *file, int mode, int rc) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); struct smack_known *sskp = tsp->smk_task; struct inode *inode = file_inode(file); struct inode_smack *isp = inode->i_security; @@ -242,7 +242,7 @@ static int smk_bu_file(struct file *file, int mode, int rc) static int smk_bu_credfile(const struct cred *cred, struct file *file, int mode, int rc) { - struct task_smack *tsp = cred->security; + struct task_smack *tsp = smack_cred(cred); struct smack_known *sskp = tsp->smk_task; struct inode *inode = file_inode(file); struct inode_smack *isp = inode->i_security; @@ -448,7 +448,7 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, rcu_read_lock(); tracercred = __task_cred(tracer); - tsp = tracercred->security; + tsp = smack_cred(tracercred); tracer_known = smk_of_task(tsp); if ((mode & PTRACE_MODE_ATTACH) && @@ -515,7 +515,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp) int rc; struct smack_known *skp; - skp = smk_of_task(current_security()); + skp = smk_of_task(smack_cred(current_cred())); rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); return rc; @@ -831,7 +831,7 @@ static int smack_sb_statfs(struct dentry *dentry) static int smack_bprm_set_creds(struct linux_binprm *bprm) { struct inode *inode = file_inode(bprm->file); - struct task_smack *bsp = bprm->cred->security; + struct task_smack *bsp = smack_cred(bprm->cred); struct inode_smack *isp; struct superblock_smack *sbsp; int rc; @@ -1662,7 +1662,7 @@ static int smack_mmap_file(struct file *file, return -EACCES; mkp = isp->smk_mmap; - tsp = current_security(); + tsp = smack_cred(current_cred()); skp = smk_of_current(); rc = 0; @@ -1758,7 +1758,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { struct smack_known *skp; - struct smack_known *tkp = smk_of_task(tsk->cred->security); + struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred)); const struct cred *tcred; struct file *file; int rc; @@ -1811,7 +1811,7 @@ static int smack_file_receive(struct file *file) if (inode->i_sb->s_magic == SOCKFS_MAGIC) { sock = SOCKET_I(inode); ssp = sock->sk->sk_security; - tsp = current_security(); + tsp = smack_cred(current_cred()); /* * If the receiving process can't write to the * passed socket or if the passed socket can't @@ -1853,7 +1853,7 @@ static int smack_file_receive(struct file *file) */ static int smack_file_open(struct file *file) { - struct task_smack *tsp = file->f_cred->security; + struct task_smack *tsp = smack_cred(file->f_cred); struct inode *inode = file_inode(file); struct smk_audit_info ad; int rc; @@ -1900,7 +1900,7 @@ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp) */ static void smack_cred_free(struct cred *cred) { - struct task_smack *tsp = cred->security; + struct task_smack *tsp = smack_cred(cred); struct smack_rule *rp; struct list_head *l; struct list_head *n; @@ -1930,7 +1930,7 @@ static void smack_cred_free(struct cred *cred) static int smack_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { - struct task_smack *old_tsp = old->security; + struct task_smack *old_tsp = smack_cred(old); struct task_smack *new_tsp; int rc; @@ -1961,15 +1961,14 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, */ static void smack_cred_transfer(struct cred *new, const struct cred *old) { - struct task_smack *old_tsp = old->security; - struct task_smack *new_tsp = new->security; + struct task_smack *old_tsp = smack_cred(old); + struct task_smack *new_tsp = smack_cred(new); new_tsp->smk_task = old_tsp->smk_task; new_tsp->smk_forked = old_tsp->smk_task; mutex_init(&new_tsp->smk_rules_lock); INIT_LIST_HEAD(&new_tsp->smk_rules); - /* cbs copy rule list */ } @@ -1980,12 +1979,12 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old) * * Sets the secid to contain a u32 version of the smack label. */ -static void smack_cred_getsecid(const struct cred *c, u32 *secid) +static void smack_cred_getsecid(const struct cred *cred, u32 *secid) { struct smack_known *skp; rcu_read_lock(); - skp = smk_of_task(c->security); + skp = smk_of_task(smack_cred(cred)); *secid = skp->smk_secid; rcu_read_unlock(); } @@ -1999,7 +1998,7 @@ static void smack_cred_getsecid(const struct cred *c, u32 *secid) */ static int smack_kernel_act_as(struct cred *new, u32 secid) { - struct task_smack *new_tsp = new->security; + struct task_smack *new_tsp = smack_cred(new); new_tsp->smk_task = smack_from_secid(secid); return 0; @@ -2017,7 +2016,7 @@ static int smack_kernel_create_files_as(struct cred *new, struct inode *inode) { struct inode_smack *isp = inode->i_security; - struct task_smack *tsp = new->security; + struct task_smack *tsp = smack_cred(new); tsp->smk_forked = isp->smk_inode; tsp->smk_task = tsp->smk_forked; @@ -2201,7 +2200,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info, * specific behavior. This is not clean. For one thing * we can't take privilege into account. */ - skp = smk_of_task(cred->security); + skp = smk_of_task(smack_cred(cred)); rc = smk_access(skp, tkp, MAY_DELIVER, &ad); rc = smk_bu_note("USB signal", skp, tkp, MAY_DELIVER, rc); return rc; @@ -3528,7 +3527,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) */ static int smack_setprocattr(const char *name, void *value, size_t size) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); struct cred *new; struct smack_known *skp; struct smack_known_list_elem *sklep; @@ -3569,7 +3568,7 @@ static int smack_setprocattr(const char *name, void *value, size_t size) if (new == NULL) return -ENOMEM; - tsp = new->security; + tsp = smack_cred(new); tsp->smk_task = skp; /* * process can change its label only once @@ -4214,7 +4213,7 @@ static void smack_inet_csk_clone(struct sock *sk, static int smack_key_alloc(struct key *key, const struct cred *cred, unsigned long flags) { - struct smack_known *skp = smk_of_task(cred->security); + struct smack_known *skp = smk_of_task(smack_cred(cred)); key->security = skp; return 0; @@ -4245,7 +4244,7 @@ static int smack_key_permission(key_ref_t key_ref, { struct key *keyp; struct smk_audit_info ad; - struct smack_known *tkp = smk_of_task(cred->security); + struct smack_known *tkp = smk_of_task(smack_cred(cred)); int request = 0; int rc; @@ -4520,7 +4519,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new) return -ENOMEM; } - tsp = new_creds->security; + tsp = smack_cred(new_creds); /* * Get label from overlay inode and set it in create_sid @@ -4548,8 +4547,8 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, const struct cred *old, struct cred *new) { - struct task_smack *otsp = old->security; - struct task_smack *ntsp = new->security; + struct task_smack *otsp = smack_cred(old); + struct task_smack *ntsp = smack_cred(new); struct inode_smack *isp; int may; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 06b517075ec0..faf2ea3968b3 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -2208,14 +2208,14 @@ static const struct file_operations smk_logging_ops = { static void *load_self_seq_start(struct seq_file *s, loff_t *pos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_seq_start(s, pos, &tsp->smk_rules); } static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_seq_next(s, v, pos, &tsp->smk_rules); } @@ -2262,7 +2262,7 @@ static int smk_open_load_self(struct inode *inode, struct file *file) static ssize_t smk_write_load_self(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules, &tsp->smk_rules_lock, SMK_FIXED24_FMT); @@ -2414,14 +2414,14 @@ static const struct file_operations smk_load2_ops = { static void *load_self2_seq_start(struct seq_file *s, loff_t *pos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_seq_start(s, pos, &tsp->smk_rules); } static void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_seq_next(s, v, pos, &tsp->smk_rules); } @@ -2467,7 +2467,7 @@ static int smk_open_load_self2(struct inode *inode, struct file *file) static ssize_t smk_write_load_self2(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules, &tsp->smk_rules_lock, SMK_LONG_FMT); @@ -2681,14 +2681,14 @@ static const struct file_operations smk_syslog_ops = { static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_seq_start(s, pos, &tsp->smk_relabel); } static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); return smk_seq_next(s, v, pos, &tsp->smk_relabel); } @@ -2736,7 +2736,7 @@ static int smk_open_relabel_self(struct inode *inode, struct file *file) static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct task_smack *tsp = current_security(); + struct task_smack *tsp = smack_cred(current_cred()); char *data; int rc; LIST_HEAD(list_tmp); -- cgit v1.2.3-59-g8ed1b From 0c6cfa622cf57109607f3416b286b6b55561a2ea Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:17:16 -0700 Subject: SELinux: Abstract use of cred security blob Don't use the cred->security pointer directly. Provide a helper function that provides the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- security/selinux/hooks.c | 54 +++++++++++++++++++-------------------- security/selinux/include/objsec.h | 5 ++++ security/selinux/xfrm.c | 4 +-- 3 files changed, 34 insertions(+), 29 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c5d9fbbb5e5b..beec1de5c2da 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -226,7 +226,7 @@ static inline u32 cred_sid(const struct cred *cred) { const struct task_security_struct *tsec; - tsec = cred->security; + tsec = selinux_cred(cred); return tsec->sid; } @@ -498,7 +498,7 @@ static int may_context_mount_sb_relabel(u32 sid, struct superblock_security_struct *sbsec, const struct cred *cred) { - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = selinux_cred(cred); int rc; rc = avc_has_perm(&selinux_state, @@ -517,7 +517,7 @@ static int may_context_mount_inode_relabel(u32 sid, struct superblock_security_struct *sbsec, const struct cred *cred) { - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = selinux_cred(cred); int rc; rc = avc_has_perm(&selinux_state, tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, @@ -1803,7 +1803,7 @@ static int may_create(struct inode *dir, struct dentry *dentry, u16 tclass) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 sid, newsid; @@ -1825,7 +1825,7 @@ static int may_create(struct inode *dir, if (rc) return rc; - rc = selinux_determine_inode_label(current_security(), dir, + rc = selinux_determine_inode_label(selinux_cred(current_cred()), dir, &dentry->d_name, tclass, &newsid); if (rc) return rc; @@ -2332,8 +2332,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) if (bprm->called_set_creds) return 0; - old_tsec = current_security(); - new_tsec = bprm->cred->security; + old_tsec = selinux_cred(current_cred()); + new_tsec = selinux_cred(bprm->cred); isec = inode_security(inode); /* Default to the current task SID. */ @@ -2497,7 +2497,7 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm) struct rlimit *rlim, *initrlim; int rc, i; - new_tsec = bprm->cred->security; + new_tsec = selinux_cred(bprm->cred); if (new_tsec->sid == new_tsec->osid) return; @@ -2540,7 +2540,7 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm) */ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); struct itimerval itimer; u32 osid, sid; int rc, i; @@ -2777,7 +2777,7 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, u32 newsid; int rc; - rc = selinux_determine_inode_label(current_security(), + rc = selinux_determine_inode_label(selinux_cred(current_cred()), d_inode(dentry->d_parent), name, inode_mode_to_security_class(mode), &newsid); @@ -2797,14 +2797,14 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode, int rc; struct task_security_struct *tsec; - rc = selinux_determine_inode_label(old->security, + rc = selinux_determine_inode_label(selinux_cred(old), d_inode(dentry->d_parent), name, inode_mode_to_security_class(mode), &newsid); if (rc) return rc; - tsec = new->security; + tsec = selinux_cred(new); tsec->create_sid = newsid; return 0; } @@ -2814,7 +2814,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, const char **name, void **value, size_t *len) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); struct superblock_security_struct *sbsec; u32 newsid, clen; int rc; @@ -2824,7 +2824,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, newsid = tsec->create_sid; - rc = selinux_determine_inode_label(current_security(), + rc = selinux_determine_inode_label(selinux_cred(current_cred()), dir, qstr, inode_mode_to_security_class(inode->i_mode), &newsid); @@ -3286,7 +3286,7 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new) return -ENOMEM; } - tsec = new_creds->security; + tsec = selinux_cred(new_creds); /* Get label from overlay inode and set it in create_sid */ selinux_inode_getsecid(d_inode(src), &sid); tsec->create_sid = sid; @@ -3706,7 +3706,7 @@ static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp) */ static void selinux_cred_free(struct cred *cred) { - struct task_security_struct *tsec = cred->security; + struct task_security_struct *tsec = selinux_cred(cred); /* * cred->security == NULL if security_cred_alloc_blank() or @@ -3726,7 +3726,7 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old, const struct task_security_struct *old_tsec; struct task_security_struct *tsec; - old_tsec = old->security; + old_tsec = selinux_cred(old); tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp); if (!tsec) @@ -3741,8 +3741,8 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old, */ static void selinux_cred_transfer(struct cred *new, const struct cred *old) { - const struct task_security_struct *old_tsec = old->security; - struct task_security_struct *tsec = new->security; + const struct task_security_struct *old_tsec = selinux_cred(old); + struct task_security_struct *tsec = selinux_cred(new); *tsec = *old_tsec; } @@ -3758,7 +3758,7 @@ static void selinux_cred_getsecid(const struct cred *c, u32 *secid) */ static int selinux_kernel_act_as(struct cred *new, u32 secid) { - struct task_security_struct *tsec = new->security; + struct task_security_struct *tsec = selinux_cred(new); u32 sid = current_sid(); int ret; @@ -3783,7 +3783,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid) static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) { struct inode_security_struct *isec = inode_security(inode); - struct task_security_struct *tsec = new->security; + struct task_security_struct *tsec = selinux_cred(new); u32 sid = current_sid(); int ret; @@ -4332,7 +4332,7 @@ static int sock_has_perm(struct sock *sk, u32 perms) static int selinux_socket_create(int family, int type, int protocol, int kern) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); u32 newsid; u16 secclass; int rc; @@ -4352,7 +4352,7 @@ static int selinux_socket_create(int family, int type, static int selinux_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); struct sk_security_struct *sksec; u16 sclass = socket_type_to_security_class(family, type, protocol); @@ -5233,7 +5233,7 @@ static int selinux_secmark_relabel_packet(u32 sid) const struct task_security_struct *__tsec; u32 tsid; - __tsec = current_security(); + __tsec = selinux_cred(current_cred()); tsid = __tsec->sid; return avc_has_perm(&selinux_state, @@ -6170,7 +6170,7 @@ static int selinux_getprocattr(struct task_struct *p, unsigned len; rcu_read_lock(); - __tsec = __task_cred(p)->security; + __tsec = selinux_cred(__task_cred(p)); if (current != p) { error = avc_has_perm(&selinux_state, @@ -6293,7 +6293,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) operation. See selinux_bprm_set_creds for the execve checks and may_create for the file creation checks. The operation will then fail if the context is not permitted. */ - tsec = new->security; + tsec = selinux_cred(new); if (!strcmp(name, "exec")) { tsec->exec_sid = sid; } else if (!strcmp(name, "fscreate")) { @@ -6422,7 +6422,7 @@ static int selinux_key_alloc(struct key *k, const struct cred *cred, if (!ksec) return -ENOMEM; - tsec = cred->security; + tsec = selinux_cred(cred); if (tsec->keycreate_sid) ksec->sid = tsec->keycreate_sid; else diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index cc5e26b0161b..734b6833bdff 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -158,4 +158,9 @@ struct bpf_security_struct { u32 sid; /*SID of bpf obj creater*/ }; +static inline struct task_security_struct *selinux_cred(const struct cred *cred) +{ + return cred->security; +} + #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index bd7d18bdb147..7c57cb7e4146 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -79,7 +79,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, gfp_t gfp) { int rc; - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); struct xfrm_sec_ctx *ctx = NULL; u32 str_len; @@ -138,7 +138,7 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) */ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); if (!ctx) return 0; -- cgit v1.2.3-59-g8ed1b From 98c88651365767c72ec6dc672072423bc19a39aa Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:17:25 -0700 Subject: SELinux: Remove cred security blob poisoning The SELinux specific credential poisioning only makes sense if SELinux is managing the credentials. As the intent of this patch set is to move the blob management out of the modules and into the infrastructure, the SELinux specific code has to go. The poisioning could be introduced into the infrastructure at some later date. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- kernel/cred.c | 13 ------------- security/selinux/hooks.c | 6 ------ 2 files changed, 19 deletions(-) (limited to 'security') diff --git a/kernel/cred.c b/kernel/cred.c index 21f4a97085b4..45d77284aed0 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -760,19 +760,6 @@ bool creds_are_invalid(const struct cred *cred) { if (cred->magic != CRED_MAGIC) return true; -#ifdef CONFIG_SECURITY_SELINUX - /* - * cred->security == NULL if security_cred_alloc_blank() or - * security_prepare_creds() returned an error. - */ - if (selinux_is_enabled() && cred->security) { - if ((unsigned long) cred->security < PAGE_SIZE) - return true; - if ((*(u32 *)cred->security & 0xffffff00) == - (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8)) - return true; - } -#endif return false; } EXPORT_SYMBOL(creds_are_invalid); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index beec1de5c2da..ad227177550b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3708,12 +3708,6 @@ static void selinux_cred_free(struct cred *cred) { struct task_security_struct *tsec = selinux_cred(cred); - /* - * cred->security == NULL if security_cred_alloc_blank() or - * security_prepare_creds() returned an error. - */ - BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE); - cred->security = (void *) 0x7UL; kfree(tsec); } -- cgit v1.2.3-59-g8ed1b From 3d252529480c68bfd6a6774652df7c8968b28e41 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:17:34 -0700 Subject: SELinux: Remove unused selinux_is_enabled There are no longer users of selinux_is_enabled(). Remove it. As selinux_is_enabled() is the only reason for include/linux/selinux.h remove that as well. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- include/linux/cred.h | 1 - include/linux/selinux.h | 35 ----------------------------------- security/selinux/Makefile | 2 +- security/selinux/exports.c | 23 ----------------------- security/selinux/hooks.c | 1 - security/selinux/include/audit.h | 3 --- security/selinux/ss/services.c | 1 - 7 files changed, 1 insertion(+), 65 deletions(-) delete mode 100644 include/linux/selinux.h delete mode 100644 security/selinux/exports.c (limited to 'security') diff --git a/include/linux/cred.h b/include/linux/cred.h index 4907c9df86b3..ddd45bb74887 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/selinux.h b/include/linux/selinux.h deleted file mode 100644 index 44f459612690..000000000000 --- a/include/linux/selinux.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SELinux services exported to the rest of the kernel. - * - * Author: James Morris - * - * Copyright (C) 2005 Red Hat, Inc., James Morris - * Copyright (C) 2006 Trusted Computer Solutions, Inc. - * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - */ -#ifndef _LINUX_SELINUX_H -#define _LINUX_SELINUX_H - -struct selinux_audit_rule; -struct audit_context; -struct kern_ipc_perm; - -#ifdef CONFIG_SECURITY_SELINUX - -/** - * selinux_is_enabled - is SELinux enabled? - */ -bool selinux_is_enabled(void); -#else - -static inline bool selinux_is_enabled(void) -{ - return false; -} -#endif /* CONFIG_SECURITY_SELINUX */ - -#endif /* _LINUX_SELINUX_H */ diff --git a/security/selinux/Makefile b/security/selinux/Makefile index c7161f8792b2..ccf950409384 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ - netnode.o netport.o ibpkey.o exports.o \ + netnode.o netport.o ibpkey.o \ ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o diff --git a/security/selinux/exports.c b/security/selinux/exports.c deleted file mode 100644 index e75dd94e2d2b..000000000000 --- a/security/selinux/exports.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SELinux services exported to the rest of the kernel. - * - * Author: James Morris - * - * Copyright (C) 2005 Red Hat, Inc., James Morris - * Copyright (C) 2006 Trusted Computer Solutions, Inc. - * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - */ -#include -#include - -#include "security.h" - -bool selinux_is_enabled(void) -{ - return selinux_enabled; -} -EXPORT_SYMBOL_GPL(selinux_is_enabled); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ad227177550b..169cf5b3334b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -79,7 +79,6 @@ #include #include #include -#include #include #include #include diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h index 1bdf973433cc..36e1d44c0209 100644 --- a/security/selinux/include/audit.h +++ b/security/selinux/include/audit.h @@ -1,9 +1,6 @@ /* * SELinux support for the Audit LSM hooks * - * Most of below header was moved from include/linux/selinux.h which - * is released under below copyrights: - * * Author: James Morris * * Copyright (C) 2005 Red Hat, Inc., James Morris diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index dd44126c8d14..d6e7b4856d93 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 69b5a44a95bb86f3ad8a50bf2e354057ec450082 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:17:59 -0700 Subject: AppArmor: Abstract use of cred security blob Don't use the cred->security pointer directly. Provide a helper function that provides the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- security/apparmor/domain.c | 2 +- security/apparmor/include/cred.h | 16 +++++++++++++++- security/apparmor/lsm.c | 10 +++++----- security/apparmor/task.c | 6 +++--- 4 files changed, 24 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 08c88de0ffda..726910bba84b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -975,7 +975,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) } aa_put_label(cred_label(bprm->cred)); /* transfer reference, released when cred is freed */ - cred_label(bprm->cred) = new; + set_cred_label(bprm->cred, new); done: aa_put_label(label); diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h index 265ae6641a06..a757370f2a0c 100644 --- a/security/apparmor/include/cred.h +++ b/security/apparmor/include/cred.h @@ -23,8 +23,22 @@ #include "policy_ns.h" #include "task.h" -#define cred_label(X) ((X)->security) +static inline struct aa_label *cred_label(const struct cred *cred) +{ + struct aa_label **blob = cred->security; + + AA_BUG(!blob); + return *blob; +} +static inline void set_cred_label(const struct cred *cred, + struct aa_label *label) +{ + struct aa_label **blob = cred->security; + + AA_BUG(!blob); + *blob = label; +} /** * aa_cred_raw_label - obtain cred's label diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index cda345767cfc..8c2cb4b1a6c3 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -60,7 +60,7 @@ DEFINE_PER_CPU(struct aa_buffers, aa_buffers); static void apparmor_cred_free(struct cred *cred) { aa_put_label(cred_label(cred)); - cred_label(cred) = NULL; + set_cred_label(cred, NULL); } /* @@ -68,7 +68,7 @@ static void apparmor_cred_free(struct cred *cred) */ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) { - cred_label(cred) = NULL; + set_cred_label(cred, NULL); return 0; } @@ -78,7 +78,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) static int apparmor_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { - cred_label(new) = aa_get_newest_label(cred_label(old)); + set_cred_label(new, aa_get_newest_label(cred_label(old))); return 0; } @@ -87,7 +87,7 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, */ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) { - cred_label(new) = aa_get_newest_label(cred_label(old)); + set_cred_label(new, aa_get_newest_label(cred_label(old))); } static void apparmor_task_free(struct task_struct *task) @@ -1485,7 +1485,7 @@ static int __init set_init_ctx(void) if (!ctx) return -ENOMEM; - cred_label(cred) = aa_get_label(ns_unconfined(root_ns)); + set_cred_label(cred, aa_get_label(ns_unconfined(root_ns))); task_ctx(current) = ctx; return 0; diff --git a/security/apparmor/task.c b/security/apparmor/task.c index c6b78a14da91..4551110f0496 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -81,7 +81,7 @@ int aa_replace_current_label(struct aa_label *label) */ aa_get_label(label); aa_put_label(cred_label(new)); - cred_label(new) = label; + set_cred_label(new, label); commit_creds(new); return 0; @@ -138,7 +138,7 @@ int aa_set_current_hat(struct aa_label *label, u64 token) return -EACCES; } - cred_label(new) = aa_get_newest_label(label); + set_cred_label(new, aa_get_newest_label(label)); /* clear exec on switching context */ aa_put_label(ctx->onexec); ctx->onexec = NULL; @@ -172,7 +172,7 @@ int aa_restore_previous_label(u64 token) return -ENOMEM; aa_put_label(cred_label(new)); - cred_label(new) = aa_get_newest_label(ctx->previous); + set_cred_label(new, aa_get_newest_label(ctx->previous)); AA_BUG(!cred_label(new)); /* clear exec && prev information when restoring to previous context */ aa_clear_task_ctx_trans(ctx); -- cgit v1.2.3-59-g8ed1b From 43fc460907dc56a3450654efc6ba1dfbcd4594eb Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:18:07 -0700 Subject: TOMOYO: Abstract use of cred security blob Don't use the cred->security pointer directly. Provide helper functions that provide the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- security/tomoyo/common.h | 21 +++++++++++++++++++-- security/tomoyo/domain.c | 4 +++- security/tomoyo/securityfs_if.c | 15 +++++++++++---- security/tomoyo/tomoyo.c | 40 +++++++++++++++++++++++++++++++--------- 4 files changed, 64 insertions(+), 16 deletions(-) (limited to 'security') diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 539bcdd30bb8..41898613d93b 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -1062,6 +1063,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, /********** External variable definitions. **********/ extern bool tomoyo_policy_loaded; +extern int tomoyo_enabled; extern const char * const tomoyo_condition_keyword [TOMOYO_MAX_CONDITION_KEYWORD]; extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS]; @@ -1196,6 +1198,17 @@ static inline void tomoyo_put_group(struct tomoyo_group *group) atomic_dec(&group->head.users); } +/** + * tomoyo_cred - Get a pointer to the tomoyo cred security blob + * @cred - the relevant cred + * + * Returns pointer to the tomoyo cred blob. + */ +static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred) +{ + return (struct tomoyo_domain_info **)&cred->security; +} + /** * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread. * @@ -1203,7 +1216,9 @@ static inline void tomoyo_put_group(struct tomoyo_group *group) */ static inline struct tomoyo_domain_info *tomoyo_domain(void) { - return current_cred()->security; + struct tomoyo_domain_info **blob = tomoyo_cred(current_cred()); + + return *blob; } /** @@ -1216,7 +1231,9 @@ static inline struct tomoyo_domain_info *tomoyo_domain(void) static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct *task) { - return task_cred_xxx(task, security); + struct tomoyo_domain_info **blob = tomoyo_cred(get_task_cred(task)); + + return *blob; } /** diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index f6758dad981f..b7469fdbff01 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -678,6 +678,7 @@ out: */ int tomoyo_find_next_domain(struct linux_binprm *bprm) { + struct tomoyo_domain_info **blob; struct tomoyo_domain_info *old_domain = tomoyo_domain(); struct tomoyo_domain_info *domain = NULL; const char *original_name = bprm->filename; @@ -843,7 +844,8 @@ force_jump_domain: domain = old_domain; /* Update reference count on "struct tomoyo_domain_info". */ atomic_inc(&domain->users); - bprm->cred->security = domain; + blob = tomoyo_cred(bprm->cred); + *blob = domain; kfree(exename.name); if (!retval) { ee->r.domain = domain; diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 1d3d7e7a1f05..768dff9608b1 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -71,9 +71,12 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, if (!cred) { error = -ENOMEM; } else { - struct tomoyo_domain_info *old_domain = - cred->security; - cred->security = new_domain; + struct tomoyo_domain_info **blob; + struct tomoyo_domain_info *old_domain; + + blob = tomoyo_cred(cred); + old_domain = *blob; + *blob = new_domain; atomic_inc(&new_domain->users); atomic_dec(&old_domain->users); commit_creds(cred); @@ -234,10 +237,14 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode, */ static int __init tomoyo_initerface_init(void) { + struct tomoyo_domain_info *domain; struct dentry *tomoyo_dir; + if (!tomoyo_enabled) + return 0; + domain = tomoyo_domain(); /* Don't create securityfs entries unless registered. */ - if (current_cred()->security != &tomoyo_kernel_domain) + if (domain != &tomoyo_kernel_domain) return 0; tomoyo_dir = securityfs_create_dir("tomoyo", NULL); diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index daff7d7897ad..15864307925d 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -18,7 +18,9 @@ */ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp) { - new->security = NULL; + struct tomoyo_domain_info **blob = tomoyo_cred(new); + + *blob = NULL; return 0; } @@ -34,8 +36,13 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp) static int tomoyo_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { - struct tomoyo_domain_info *domain = old->security; - new->security = domain; + struct tomoyo_domain_info **old_blob = tomoyo_cred(old); + struct tomoyo_domain_info **new_blob = tomoyo_cred(new); + struct tomoyo_domain_info *domain; + + domain = *old_blob; + *new_blob = domain; + if (domain) atomic_inc(&domain->users); return 0; @@ -59,7 +66,9 @@ static void tomoyo_cred_transfer(struct cred *new, const struct cred *old) */ static void tomoyo_cred_free(struct cred *cred) { - struct tomoyo_domain_info *domain = cred->security; + struct tomoyo_domain_info **blob = tomoyo_cred(cred); + struct tomoyo_domain_info *domain = *blob; + if (domain) atomic_dec(&domain->users); } @@ -73,6 +82,9 @@ static void tomoyo_cred_free(struct cred *cred) */ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) { + struct tomoyo_domain_info **blob; + struct tomoyo_domain_info *domain; + /* * Do only if this function is called for the first time of an execve * operation. @@ -93,13 +105,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) * stored inside "bprm->cred->security" will be acquired later inside * tomoyo_find_next_domain(). */ - atomic_dec(&((struct tomoyo_domain_info *) - bprm->cred->security)->users); + blob = tomoyo_cred(bprm->cred); + domain = *blob; + atomic_dec(&domain->users); /* * Tell tomoyo_bprm_check_security() is called for the first time of an * execve operation. */ - bprm->cred->security = NULL; + *blob = NULL; return 0; } @@ -112,8 +125,11 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) */ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) { - struct tomoyo_domain_info *domain = bprm->cred->security; + struct tomoyo_domain_info **blob; + struct tomoyo_domain_info *domain; + blob = tomoyo_cred(bprm->cred); + domain = *blob; /* * Execute permission is checked against pathname passed to do_execve() * using current domain. @@ -531,6 +547,8 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = { /* Lock for GC. */ DEFINE_SRCU(tomoyo_ss); +int tomoyo_enabled __lsm_ro_after_init = 1; + /** * tomoyo_init - Register TOMOYO Linux as a LSM module. * @@ -539,17 +557,21 @@ DEFINE_SRCU(tomoyo_ss); static int __init tomoyo_init(void) { struct cred *cred = (struct cred *) current_cred(); + struct tomoyo_domain_info **blob; /* register ourselves with the security framework */ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); printk(KERN_INFO "TOMOYO Linux initialized\n"); - cred->security = &tomoyo_kernel_domain; + blob = tomoyo_cred(cred); + *blob = &tomoyo_kernel_domain; tomoyo_mm_init(); + return 0; } DEFINE_LSM(tomoyo) = { .name = "tomoyo", + .enabled = &tomoyo_enabled, .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, .init = tomoyo_init, }; -- cgit v1.2.3-59-g8ed1b From bbd3662a834813730912a58efb44dd6df6d952e6 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Mon, 12 Nov 2018 09:30:56 -0800 Subject: Infrastructure management of the cred security blob Move management of the cred security blob out of the security modules and into the security infrastructre. Instead of allocating and freeing space the security modules tell the infrastructure how much space they require. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- include/linux/lsm_hooks.h | 12 ++++++ security/apparmor/include/cred.h | 4 +- security/apparmor/include/lib.h | 4 ++ security/apparmor/lsm.c | 9 ++++ security/security.c | 89 ++++++++++++++++++++++++++++++++++++++- security/selinux/hooks.c | 51 +++++----------------- security/selinux/include/objsec.h | 4 +- security/smack/smack.h | 3 +- security/smack/smack_lsm.c | 79 +++++++++++----------------------- security/tomoyo/common.h | 3 +- security/tomoyo/tomoyo.c | 6 +++ 11 files changed, 162 insertions(+), 102 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 0c908c091a03..dd33666567bc 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2027,6 +2027,13 @@ struct security_hook_list { char *lsm; } __randomize_layout; +/* + * Security blob size or offset data. + */ +struct lsm_blob_sizes { + int lbs_cred; +}; + /* * Initializing a security_hook_list structure takes * up a lot of space in a source file. This macro takes @@ -2056,6 +2063,7 @@ struct lsm_info { unsigned long flags; /* Optional: flags describing LSM */ int *enabled; /* Optional: controlled by CONFIG_LSM */ int (*init)(void); /* Required. */ + struct lsm_blob_sizes *blobs; /* Optional: for blob sharing. */ }; extern struct lsm_info __start_lsm_info[], __end_lsm_info[]; @@ -2095,4 +2103,8 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, #define __lsm_ro_after_init __ro_after_init #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ +#ifdef CONFIG_SECURITY +void __init lsm_early_cred(struct cred *cred); +#endif + #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h index a757370f2a0c..b9504a05fddc 100644 --- a/security/apparmor/include/cred.h +++ b/security/apparmor/include/cred.h @@ -25,7 +25,7 @@ static inline struct aa_label *cred_label(const struct cred *cred) { - struct aa_label **blob = cred->security; + struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred; AA_BUG(!blob); return *blob; @@ -34,7 +34,7 @@ static inline struct aa_label *cred_label(const struct cred *cred) static inline void set_cred_label(const struct cred *cred, struct aa_label *label) { - struct aa_label **blob = cred->security; + struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred; AA_BUG(!blob); *blob = label; diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 6505e1ad9e23..bbe9b384d71d 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -16,6 +16,7 @@ #include #include +#include #include "match.h" @@ -55,6 +56,9 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, size_t *ns_len); void aa_info_message(const char *str); +/* Security blob offsets */ +extern struct lsm_blob_sizes apparmor_blob_sizes; + /** * aa_strneq - compare null terminated @str to a non null terminated substring * @str: a null terminated string diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8c2cb4b1a6c3..d5e4a384f205 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1151,6 +1151,13 @@ static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb, } #endif +/* + * The cred blob is a pointer to, not an instance of, an aa_task_ctx. + */ +struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = { + .lbs_cred = sizeof(struct aa_task_ctx *), +}; + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), @@ -1485,6 +1492,7 @@ static int __init set_init_ctx(void) if (!ctx) return -ENOMEM; + lsm_early_cred(cred); set_cred_label(cred, aa_get_label(ns_unconfined(root_ns))); task_ctx(current) = ctx; @@ -1725,5 +1733,6 @@ DEFINE_LSM(apparmor) = { .name = "apparmor", .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, .enabled = &apparmor_enabled, + .blobs = &apparmor_blob_sizes, .init = apparmor_init, }; diff --git a/security/security.c b/security/security.c index 60b39db95c2f..09be8ce007a2 100644 --- a/security/security.c +++ b/security/security.c @@ -41,6 +41,8 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init; static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); char *lsm_names; +static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init; + /* Boot-time LSM user choice */ static __initdata const char *chosen_lsm_order; static __initdata const char *chosen_major_lsm; @@ -139,6 +141,25 @@ static bool __init lsm_allowed(struct lsm_info *lsm) return true; } +static void __init lsm_set_blob_size(int *need, int *lbs) +{ + int offset; + + if (*need > 0) { + offset = *lbs; + *lbs += *need; + *need = offset; + } +} + +static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) +{ + if (!needed) + return; + + lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); +} + /* Prepare LSM for initialization. */ static void __init prepare_lsm(struct lsm_info *lsm) { @@ -153,6 +174,8 @@ static void __init prepare_lsm(struct lsm_info *lsm) exclusive = lsm; init_debug("exclusive chosen: %s\n", lsm->name); } + + lsm_set_blob_sizes(lsm->blobs); } } @@ -255,6 +278,8 @@ static void __init ordered_lsm_init(void) for (lsm = ordered_lsms; *lsm; lsm++) prepare_lsm(*lsm); + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); + for (lsm = ordered_lsms; *lsm; lsm++) initialize_lsm(*lsm); @@ -382,6 +407,47 @@ int unregister_lsm_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_lsm_notifier); +/** + * lsm_cred_alloc - allocate a composite cred blob + * @cred: the cred that needs a blob + * @gfp: allocation type + * + * Allocate the cred blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +static int lsm_cred_alloc(struct cred *cred, gfp_t gfp) +{ + if (blob_sizes.lbs_cred == 0) { + cred->security = NULL; + return 0; + } + + cred->security = kzalloc(blob_sizes.lbs_cred, gfp); + if (cred->security == NULL) + return -ENOMEM; + return 0; +} + +/** + * lsm_early_cred - during initialization allocate a composite cred blob + * @cred: the cred that needs a blob + * + * Allocate the cred blob for all the modules if it's not already there + */ +void __init lsm_early_cred(struct cred *cred) +{ + int rc; + + if (cred == NULL) + panic("%s: NULL cred.\n", __func__); + if (cred->security != NULL) + return; + rc = lsm_cred_alloc(cred, GFP_KERNEL); + if (rc) + panic("%s: Early cred alloc failed.\n", __func__); +} + /* * Hook list operation macros. * @@ -1195,17 +1261,36 @@ void security_task_free(struct task_struct *task) int security_cred_alloc_blank(struct cred *cred, gfp_t gfp) { - return call_int_hook(cred_alloc_blank, 0, cred, gfp); + int rc = lsm_cred_alloc(cred, gfp); + + if (rc) + return rc; + + rc = call_int_hook(cred_alloc_blank, 0, cred, gfp); + if (rc) + security_cred_free(cred); + return rc; } void security_cred_free(struct cred *cred) { call_void_hook(cred_free, cred); + + kfree(cred->security); + cred->security = NULL; } int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp) { - return call_int_hook(cred_prepare, 0, new, old, gfp); + int rc = lsm_cred_alloc(new, gfp); + + if (rc) + return rc; + + rc = call_int_hook(cred_prepare, 0, new, old, gfp); + if (rc) + security_cred_free(new); + return rc; } void security_transfer_creds(struct cred *new, const struct cred *old) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 169cf5b3334b..239b13b442e7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -210,12 +210,9 @@ static void cred_init_security(void) struct cred *cred = (struct cred *) current->real_cred; struct task_security_struct *tsec; - tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL); - if (!tsec) - panic("SELinux: Failed to initialize initial task.\n"); - + lsm_early_cred(cred); + tsec = selinux_cred(cred); tsec->osid = tsec->sid = SECINITSID_KERNEL; - cred->security = tsec; } /* @@ -3685,47 +3682,16 @@ static int selinux_task_alloc(struct task_struct *task, sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); } -/* - * allocate the SELinux part of blank credentials - */ -static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp) -{ - struct task_security_struct *tsec; - - tsec = kzalloc(sizeof(struct task_security_struct), gfp); - if (!tsec) - return -ENOMEM; - - cred->security = tsec; - return 0; -} - -/* - * detach and free the LSM part of a set of credentials - */ -static void selinux_cred_free(struct cred *cred) -{ - struct task_security_struct *tsec = selinux_cred(cred); - - kfree(tsec); -} - /* * prepare a new set of credentials for modification */ static int selinux_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { - const struct task_security_struct *old_tsec; - struct task_security_struct *tsec; - - old_tsec = selinux_cred(old); - - tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp); - if (!tsec) - return -ENOMEM; + const struct task_security_struct *old_tsec = selinux_cred(old); + struct task_security_struct *tsec = selinux_cred(new); - new->security = tsec; + *tsec = *old_tsec; return 0; } @@ -6678,6 +6644,10 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux) } #endif +struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { + .lbs_cred = sizeof(struct task_security_struct), +}; + static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr), LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction), @@ -6761,8 +6731,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(file_open, selinux_file_open), LSM_HOOK_INIT(task_alloc, selinux_task_alloc), - LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank), - LSM_HOOK_INIT(cred_free, selinux_cred_free), LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare), LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer), LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid), @@ -6981,6 +6949,7 @@ DEFINE_LSM(selinux) = { .name = "selinux", .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, .enabled = &selinux_enabled, + .blobs = &selinux_blob_sizes, .init = selinux_init, }; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 734b6833bdff..c2974b031d05 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "flask.h" #include "avc.h" @@ -158,9 +159,10 @@ struct bpf_security_struct { u32 sid; /*SID of bpf obj creater*/ }; +extern struct lsm_blob_sizes selinux_blob_sizes; static inline struct task_security_struct *selinux_cred(const struct cred *cred) { - return cred->security; + return cred->security + selinux_blob_sizes.lbs_cred; } #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/smack/smack.h b/security/smack/smack.h index 01a922856eba..b27eb252e953 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -336,6 +336,7 @@ extern struct smack_known *smack_syslog_label; extern struct smack_known *smack_unconfined; #endif extern int smack_ptrace_rule; +extern struct lsm_blob_sizes smack_blob_sizes; extern struct smack_known smack_known_floor; extern struct smack_known smack_known_hat; @@ -358,7 +359,7 @@ extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; static inline struct task_smack *smack_cred(const struct cred *cred) { - return cred->security; + return cred->security + smack_blob_sizes.lbs_cred; } /* diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 9a050ca17296..bad27a8e1631 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -326,29 +326,20 @@ static struct inode_smack *new_inode_smack(struct smack_known *skp) } /** - * new_task_smack - allocate a task security blob + * init_task_smack - initialize a task security blob + * @tsp: blob to initialize * @task: a pointer to the Smack label for the running task * @forked: a pointer to the Smack label for the forked task - * @gfp: type of the memory for the allocation * - * Returns the new blob or NULL if there's no memory available */ -static struct task_smack *new_task_smack(struct smack_known *task, - struct smack_known *forked, gfp_t gfp) +static void init_task_smack(struct task_smack *tsp, struct smack_known *task, + struct smack_known *forked) { - struct task_smack *tsp; - - tsp = kzalloc(sizeof(struct task_smack), gfp); - if (tsp == NULL) - return NULL; - tsp->smk_task = task; tsp->smk_forked = forked; INIT_LIST_HEAD(&tsp->smk_rules); INIT_LIST_HEAD(&tsp->smk_relabel); mutex_init(&tsp->smk_rules_lock); - - return tsp; } /** @@ -1881,14 +1872,7 @@ static int smack_file_open(struct file *file) */ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp) { - struct task_smack *tsp; - - tsp = new_task_smack(NULL, NULL, gfp); - if (tsp == NULL) - return -ENOMEM; - - cred->security = tsp; - + init_task_smack(smack_cred(cred), NULL, NULL); return 0; } @@ -1905,10 +1889,6 @@ static void smack_cred_free(struct cred *cred) struct list_head *l; struct list_head *n; - if (tsp == NULL) - return; - cred->security = NULL; - smk_destroy_label_list(&tsp->smk_relabel); list_for_each_safe(l, n, &tsp->smk_rules) { @@ -1916,7 +1896,6 @@ static void smack_cred_free(struct cred *cred) list_del(&rp->list); kfree(rp); } - kfree(tsp); } /** @@ -1931,14 +1910,10 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { struct task_smack *old_tsp = smack_cred(old); - struct task_smack *new_tsp; + struct task_smack *new_tsp = smack_cred(new); int rc; - new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp); - if (new_tsp == NULL) - return -ENOMEM; - - new->security = new_tsp; + init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task); rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp); if (rc != 0) @@ -1946,10 +1921,7 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel, gfp); - if (rc != 0) - return rc; - - return 0; + return rc; } /** @@ -4581,6 +4553,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, return 0; } +struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { + .lbs_cred = sizeof(struct task_smack), +}; + static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme), @@ -4758,20 +4734,25 @@ static __init void init_smack_known_list(void) */ static __init int smack_init(void) { - struct cred *cred; + struct cred *cred = (struct cred *) current->cred; struct task_smack *tsp; smack_inode_cache = KMEM_CACHE(inode_smack, 0); if (!smack_inode_cache) return -ENOMEM; - tsp = new_task_smack(&smack_known_floor, &smack_known_floor, - GFP_KERNEL); - if (tsp == NULL) { - kmem_cache_destroy(smack_inode_cache); - return -ENOMEM; - } + lsm_early_cred(cred); + /* + * Set the security state for the initial task. + */ + tsp = smack_cred(cred); + init_task_smack(tsp, &smack_known_floor, &smack_known_floor); + + /* + * Register with LSM + */ + security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack"); smack_enabled = 1; pr_info("Smack: Initializing.\n"); @@ -4785,20 +4766,9 @@ static __init int smack_init(void) pr_info("Smack: IPv6 Netfilter enabled.\n"); #endif - /* - * Set the security state for the initial task. - */ - cred = (struct cred *) current->cred; - cred->security = tsp; - /* initialize the smack_known_list */ init_smack_known_list(); - /* - * Register with LSM - */ - security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack"); - return 0; } @@ -4809,5 +4779,6 @@ static __init int smack_init(void) DEFINE_LSM(smack) = { .name = "smack", .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, + .blobs = &smack_blob_sizes, .init = smack_init, }; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 41898613d93b..4fc17294a12d 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -1087,6 +1087,7 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain; extern struct tomoyo_policy_namespace tomoyo_kernel_namespace; extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT]; extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT]; +extern struct lsm_blob_sizes tomoyo_blob_sizes; /********** Inlined functions. **********/ @@ -1206,7 +1207,7 @@ static inline void tomoyo_put_group(struct tomoyo_group *group) */ static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred) { - return (struct tomoyo_domain_info **)&cred->security; + return cred->security + tomoyo_blob_sizes.lbs_cred; } /** diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 15864307925d..9094cf41a247 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -509,6 +509,10 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg, return tomoyo_socket_sendmsg_permission(sock, msg, size); } +struct lsm_blob_sizes tomoyo_blob_sizes __lsm_ro_after_init = { + .lbs_cred = sizeof(struct tomoyo_domain_info *), +}; + /* * tomoyo_security_ops is a "struct security_operations" which is used for * registering TOMOYO. @@ -562,6 +566,7 @@ static int __init tomoyo_init(void) /* register ourselves with the security framework */ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); printk(KERN_INFO "TOMOYO Linux initialized\n"); + lsm_early_cred(cred); blob = tomoyo_cred(cred); *blob = &tomoyo_kernel_domain; tomoyo_mm_init(); @@ -573,5 +578,6 @@ DEFINE_LSM(tomoyo) = { .name = "tomoyo", .enabled = &tomoyo_enabled, .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, + .blobs = &tomoyo_blob_sizes, .init = tomoyo_init, }; -- cgit v1.2.3-59-g8ed1b From bb6c6b02ccb7d76f628c5dc6abe13f1115637cfd Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:22:32 -0700 Subject: SELinux: Abstract use of file security blob Don't use the file->f_security pointer directly. Provide a helper function that provides the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- security/selinux/hooks.c | 18 +++++++++--------- security/selinux/include/objsec.h | 5 +++++ 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 239b13b442e7..620be0367c0b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -394,7 +394,7 @@ static int file_alloc_security(struct file *file) static void file_free_security(struct file *file) { - struct file_security_struct *fsec = file->f_security; + struct file_security_struct *fsec = selinux_file(file); file->f_security = NULL; kmem_cache_free(file_security_cache, fsec); } @@ -1733,7 +1733,7 @@ static int file_has_perm(const struct cred *cred, struct file *file, u32 av) { - struct file_security_struct *fsec = file->f_security; + struct file_security_struct *fsec = selinux_file(file); struct inode *inode = file_inode(file); struct common_audit_data ad; u32 sid = cred_sid(cred); @@ -2077,7 +2077,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, struct file *file) { u32 sid = task_sid(to); - struct file_security_struct *fsec = file->f_security; + struct file_security_struct *fsec = selinux_file(file); struct dentry *dentry = file->f_path.dentry; struct inode_security_struct *isec; struct common_audit_data ad; @@ -3323,7 +3323,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask) static int selinux_file_permission(struct file *file, int mask) { struct inode *inode = file_inode(file); - struct file_security_struct *fsec = file->f_security; + struct file_security_struct *fsec = selinux_file(file); struct inode_security_struct *isec; u32 sid = current_sid(); @@ -3358,7 +3358,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, u32 requested, u16 cmd) { struct common_audit_data ad; - struct file_security_struct *fsec = file->f_security; + struct file_security_struct *fsec = selinux_file(file); struct inode *inode = file_inode(file); struct inode_security_struct *isec; struct lsm_ioctlop_audit ioctl; @@ -3610,7 +3610,7 @@ static void selinux_file_set_fowner(struct file *file) { struct file_security_struct *fsec; - fsec = file->f_security; + fsec = selinux_file(file); fsec->fown_sid = current_sid(); } @@ -3625,7 +3625,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, /* struct fown_struct is never outside the context of a struct file */ file = container_of(fown, struct file, f_owner); - fsec = file->f_security; + fsec = selinux_file(file); if (!signum) perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */ @@ -3649,7 +3649,7 @@ static int selinux_file_open(struct file *file) struct file_security_struct *fsec; struct inode_security_struct *isec; - fsec = file->f_security; + fsec = selinux_file(file); isec = inode_security(file_inode(file)); /* * Save inode label and policy sequence number @@ -3788,7 +3788,7 @@ static int selinux_kernel_module_from_file(struct file *file) ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; - fsec = file->f_security; + fsec = selinux_file(file); if (sid != fsec->sid) { rc = avc_has_perm(&selinux_state, sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index c2974b031d05..e0ac2992e059 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -165,4 +165,9 @@ static inline struct task_security_struct *selinux_cred(const struct cred *cred) return cred->security + selinux_blob_sizes.lbs_cred; } +static inline struct file_security_struct *selinux_file(const struct file *file) +{ + return file->f_security; +} + #endif /* _SELINUX_OBJSEC_H_ */ -- cgit v1.2.3-59-g8ed1b From f28952ac900822a189fc383a5b73631e72c69356 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Mon, 12 Nov 2018 09:38:53 -0800 Subject: Smack: Abstract use of file security blob Don't use the file->f_security pointer directly. Provide a helper function that provides the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- security/smack/smack.h | 5 +++++ security/smack/smack_lsm.c | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/smack/smack.h b/security/smack/smack.h index b27eb252e953..50854969a391 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -362,6 +362,11 @@ static inline struct task_smack *smack_cred(const struct cred *cred) return cred->security + smack_blob_sizes.lbs_cred; } +static inline struct smack_known **smack_file(const struct file *file) +{ + return (struct smack_known **)&file->f_security; +} + /* * Is the directory transmuting? */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index bad27a8e1631..8f72641f94ab 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1489,9 +1489,9 @@ static void smack_inode_getsecid(struct inode *inode, u32 *secid) */ static int smack_file_alloc_security(struct file *file) { - struct smack_known *skp = smk_of_current(); + struct smack_known **blob = smack_file(file); - file->f_security = skp; + *blob = smk_of_current(); return 0; } @@ -1731,7 +1731,9 @@ static int smack_mmap_file(struct file *file, */ static void smack_file_set_fowner(struct file *file) { - file->f_security = smk_of_current(); + struct smack_known **blob = smack_file(file); + + *blob = smk_of_current(); } /** @@ -1748,6 +1750,7 @@ static void smack_file_set_fowner(struct file *file) static int smack_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { + struct smack_known **blob; struct smack_known *skp; struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred)); const struct cred *tcred; @@ -1761,7 +1764,8 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, file = container_of(fown, struct file, f_owner); /* we don't log here as rc can be overriden */ - skp = file->f_security; + blob = smack_file(file); + skp = *blob; rc = smk_access(skp, tkp, MAY_DELIVER, NULL); rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, rc); -- cgit v1.2.3-59-g8ed1b From 33bf60cabcc7687b194a689b068b65e9ecd556be Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Mon, 12 Nov 2018 12:02:49 -0800 Subject: LSM: Infrastructure management of the file security Move management of the file->f_security blob out of the individual security modules and into the infrastructure. The modules no longer allocate or free the data, instead they tell the infrastructure how much space they require. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- include/linux/lsm_hooks.h | 1 + security/apparmor/include/file.h | 5 +++- security/apparmor/lsm.c | 19 +++++++------- security/security.c | 54 ++++++++++++++++++++++++++++++++++++--- security/selinux/hooks.c | 25 ++---------------- security/selinux/include/objsec.h | 2 +- security/smack/smack.h | 3 ++- security/smack/smack_lsm.c | 14 +--------- 8 files changed, 72 insertions(+), 51 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index dd33666567bc..e8cef019b645 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2032,6 +2032,7 @@ struct security_hook_list { */ struct lsm_blob_sizes { int lbs_cred; + int lbs_file; }; /* diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 4c2c8ac8842f..8be09208cf7c 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -32,7 +32,10 @@ struct path; AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ AA_EXEC_MMAP | AA_MAY_LINK) -#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security) +static inline struct aa_file_ctx *file_ctx(struct file *file) +{ + return file->f_security + apparmor_blob_sizes.lbs_file; +} /* struct aa_file_ctx - the AppArmor context the file was opened in * @lock: lock to update the ctx diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index d5e4a384f205..6821187b06ad 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -434,21 +434,21 @@ static int apparmor_file_open(struct file *file) static int apparmor_file_alloc_security(struct file *file) { - int error = 0; - - /* freed by apparmor_file_free_security */ + struct aa_file_ctx *ctx = file_ctx(file); struct aa_label *label = begin_current_label_crit_section(); - file->f_security = aa_alloc_file_ctx(label, GFP_KERNEL); - if (!file_ctx(file)) - error = -ENOMEM; - end_current_label_crit_section(label); - return error; + spin_lock_init(&ctx->lock); + rcu_assign_pointer(ctx->label, aa_get_label(label)); + end_current_label_crit_section(label); + return 0; } static void apparmor_file_free_security(struct file *file) { - aa_free_file_ctx(file_ctx(file)); + struct aa_file_ctx *ctx = file_ctx(file); + + if (ctx) + aa_put_label(rcu_access_pointer(ctx->label)); } static int common_file_perm(const char *op, struct file *file, u32 mask) @@ -1156,6 +1156,7 @@ static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb, */ struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct aa_task_ctx *), + .lbs_file = sizeof(struct aa_file_ctx), }; static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { diff --git a/security/security.c b/security/security.c index 09be8ce007a2..f32d7d2075c6 100644 --- a/security/security.c +++ b/security/security.c @@ -40,6 +40,8 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init; static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); +static struct kmem_cache *lsm_file_cache; + char *lsm_names; static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init; @@ -158,6 +160,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) return; lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); + lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); } /* Prepare LSM for initialization. */ @@ -279,6 +282,15 @@ static void __init ordered_lsm_init(void) prepare_lsm(*lsm); init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); + init_debug("file blob size = %d\n", blob_sizes.lbs_file); + + /* + * Create any kmem_caches needed for blobs + */ + if (blob_sizes.lbs_file) + lsm_file_cache = kmem_cache_create("lsm_file_cache", + blob_sizes.lbs_file, 0, + SLAB_PANIC, NULL); for (lsm = ordered_lsms; *lsm; lsm++) initialize_lsm(*lsm); @@ -448,6 +460,27 @@ void __init lsm_early_cred(struct cred *cred) panic("%s: Early cred alloc failed.\n", __func__); } +/** + * lsm_file_alloc - allocate a composite file blob + * @file: the file that needs a blob + * + * Allocate the file blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +static int lsm_file_alloc(struct file *file) +{ + if (!lsm_file_cache) { + file->f_security = NULL; + return 0; + } + + file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL); + if (file->f_security == NULL) + return -ENOMEM; + return 0; +} + /* * Hook list operation macros. * @@ -1144,12 +1177,27 @@ int security_file_permission(struct file *file, int mask) int security_file_alloc(struct file *file) { - return call_int_hook(file_alloc_security, 0, file); + int rc = lsm_file_alloc(file); + + if (rc) + return rc; + rc = call_int_hook(file_alloc_security, 0, file); + if (unlikely(rc)) + security_file_free(file); + return rc; } void security_file_free(struct file *file) { + void *blob; + call_void_hook(file_free_security, file); + + blob = file->f_security; + if (blob) { + file->f_security = NULL; + kmem_cache_free(lsm_file_cache, blob); + } } int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -1267,7 +1315,7 @@ int security_cred_alloc_blank(struct cred *cred, gfp_t gfp) return rc; rc = call_int_hook(cred_alloc_blank, 0, cred, gfp); - if (rc) + if (unlikely(rc)) security_cred_free(cred); return rc; } @@ -1288,7 +1336,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp) return rc; rc = call_int_hook(cred_prepare, 0, new, old, gfp); - if (rc) + if (unlikely(rc)) security_cred_free(new); return rc; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 620be0367c0b..632813821da6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -146,7 +146,6 @@ static int __init checkreqprot_setup(char *str) __setup("checkreqprot=", checkreqprot_setup); static struct kmem_cache *sel_inode_cache; -static struct kmem_cache *file_security_cache; /** * selinux_secmark_enabled - Check to see if SECMARK is currently enabled @@ -378,27 +377,15 @@ static void inode_free_security(struct inode *inode) static int file_alloc_security(struct file *file) { - struct file_security_struct *fsec; + struct file_security_struct *fsec = selinux_file(file); u32 sid = current_sid(); - fsec = kmem_cache_zalloc(file_security_cache, GFP_KERNEL); - if (!fsec) - return -ENOMEM; - fsec->sid = sid; fsec->fown_sid = sid; - file->f_security = fsec; return 0; } -static void file_free_security(struct file *file) -{ - struct file_security_struct *fsec = selinux_file(file); - file->f_security = NULL; - kmem_cache_free(file_security_cache, fsec); -} - static int superblock_alloc_security(struct super_block *sb) { struct superblock_security_struct *sbsec; @@ -3345,11 +3332,6 @@ static int selinux_file_alloc_security(struct file *file) return file_alloc_security(file); } -static void selinux_file_free_security(struct file *file) -{ - file_free_security(file); -} - /* * Check whether a task has the ioctl permission and cmd * operation to an inode. @@ -6646,6 +6628,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux) struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_security_struct), + .lbs_file = sizeof(struct file_security_struct), }; static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { @@ -6717,7 +6700,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), - LSM_HOOK_INIT(file_free_security, selinux_file_free_security), LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), LSM_HOOK_INIT(mmap_file, selinux_mmap_file), LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), @@ -6902,9 +6884,6 @@ static __init int selinux_init(void) sel_inode_cache = kmem_cache_create("selinux_inode_security", sizeof(struct inode_security_struct), 0, SLAB_PANIC, NULL); - file_security_cache = kmem_cache_create("selinux_file_security", - sizeof(struct file_security_struct), - 0, SLAB_PANIC, NULL); avc_init(); avtab_cache_init(); diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index e0ac2992e059..96374dbf4ace 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -167,7 +167,7 @@ static inline struct task_security_struct *selinux_cred(const struct cred *cred) static inline struct file_security_struct *selinux_file(const struct file *file) { - return file->f_security; + return file->f_security + selinux_blob_sizes.lbs_file; } #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/smack/smack.h b/security/smack/smack.h index 50854969a391..2007d38d0e46 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -364,7 +364,8 @@ static inline struct task_smack *smack_cred(const struct cred *cred) static inline struct smack_known **smack_file(const struct file *file) { - return (struct smack_known **)&file->f_security; + return (struct smack_known **)(file->f_security + + smack_blob_sizes.lbs_file); } /* diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8f72641f94ab..7c76668ea3a6 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1495,18 +1495,6 @@ static int smack_file_alloc_security(struct file *file) return 0; } -/** - * smack_file_free_security - clear a file security blob - * @file: the object - * - * The security blob for a file is a pointer to the master - * label list, so no memory is freed. - */ -static void smack_file_free_security(struct file *file) -{ - file->f_security = NULL; -} - /** * smack_file_ioctl - Smack check on ioctls * @file: the object @@ -4559,6 +4547,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_smack), + .lbs_file = sizeof(struct smack_known *), }; static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { @@ -4595,7 +4584,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid), LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security), - LSM_HOOK_INIT(file_free_security, smack_file_free_security), LSM_HOOK_INIT(file_ioctl, smack_file_ioctl), LSM_HOOK_INIT(file_lock, smack_file_lock), LSM_HOOK_INIT(file_fcntl, smack_file_fcntl), -- cgit v1.2.3-59-g8ed1b From 80788c229116b28cc914d73c142f74bdee28ab57 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:19:11 -0700 Subject: SELinux: Abstract use of inode security blob Don't use the inode->i_security pointer directly. Provide a helper function that provides the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- security/selinux/hooks.c | 26 +++++++++++++------------- security/selinux/include/objsec.h | 6 ++++++ security/selinux/selinuxfs.c | 4 ++-- 3 files changed, 21 insertions(+), 15 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 632813821da6..2d691e8dfbbf 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -273,7 +273,7 @@ static int __inode_security_revalidate(struct inode *inode, struct dentry *dentry, bool may_sleep) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); might_sleep_if(may_sleep); @@ -294,7 +294,7 @@ static int __inode_security_revalidate(struct inode *inode, static struct inode_security_struct *inode_security_novalidate(struct inode *inode) { - return inode->i_security; + return selinux_inode(inode); } static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu) @@ -304,7 +304,7 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo error = __inode_security_revalidate(inode, NULL, !rcu); if (error) return ERR_PTR(error); - return inode->i_security; + return selinux_inode(inode); } /* @@ -313,14 +313,14 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo static struct inode_security_struct *inode_security(struct inode *inode) { __inode_security_revalidate(inode, NULL, true); - return inode->i_security; + return selinux_inode(inode); } static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); - return inode->i_security; + return selinux_inode(inode); } /* @@ -331,7 +331,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr struct inode *inode = d_backing_inode(dentry); __inode_security_revalidate(inode, dentry, true); - return inode->i_security; + return selinux_inode(inode); } static void inode_free_rcu(struct rcu_head *head) @@ -344,7 +344,7 @@ static void inode_free_rcu(struct rcu_head *head) static void inode_free_security(struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); struct superblock_security_struct *sbsec = inode->i_sb->s_security; /* @@ -1354,7 +1354,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry, static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) { struct superblock_security_struct *sbsec = NULL; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); u32 task_sid, sid = 0; u16 sclass; struct dentry *dentry; @@ -1654,7 +1654,7 @@ static int inode_has_perm(const struct cred *cred, return 0; sid = cred_sid(cred); - isec = inode->i_security; + isec = selinux_inode(inode); return avc_has_perm(&selinux_state, sid, isec->sid, isec->sclass, perms, adp); @@ -2816,7 +2816,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, /* Possibly defer initialization to selinux_complete_init. */ if (sbsec->flags & SE_SBINITIALIZED) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = LABEL_INITIALIZED; @@ -2916,7 +2916,7 @@ static noinline int audit_inode_permission(struct inode *inode, unsigned flags) { struct common_audit_data ad; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); int rc; ad.type = LSM_AUDIT_DATA_INODE; @@ -3936,7 +3936,7 @@ static int selinux_task_kill(struct task_struct *p, struct kernel_siginfo *info, static void selinux_task_to_inode(struct task_struct *p, struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); u32 sid = task_sid(p); spin_lock(&isec->lock); @@ -6318,7 +6318,7 @@ static void selinux_release_secctx(char *secdata, u32 seclen) static void selinux_inode_invalidate_secctx(struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); spin_lock(&isec->lock); isec->initialized = LABEL_INVALID; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 96374dbf4ace..26b4ff6b4d81 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -170,4 +170,10 @@ static inline struct file_security_struct *selinux_file(const struct file *file) return file->f_security + selinux_blob_sizes.lbs_file; } +static inline struct inode_security_struct *selinux_inode( + const struct inode *inode) +{ + return inode->i_security; +} + #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index f3a5a138a096..145ee62f205a 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1378,7 +1378,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi) goto out; } - isec = (struct inode_security_struct *)inode->i_security; + isec = selinux_inode(inode); ret = security_genfs_sid(fsi->state, "selinuxfs", page, SECCLASS_FILE, &sid); if (ret) { @@ -1953,7 +1953,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) } inode->i_ino = ++fsi->last_ino; - isec = (struct inode_security_struct *)inode->i_security; + isec = selinux_inode(inode); isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; isec->initialized = LABEL_INITIALIZED; -- cgit v1.2.3-59-g8ed1b From fb4021b6fb5818df1228a35b7e2645038d01bb9f Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Mon, 12 Nov 2018 12:43:01 -0800 Subject: Smack: Abstract use of inode security blob Don't use the inode->i_security pointer directly. Provide a helper function that provides the security blob pointer. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- security/smack/smack.h | 9 +++++++-- security/smack/smack_lsm.c | 32 ++++++++++++++++---------------- 2 files changed, 23 insertions(+), 18 deletions(-) (limited to 'security') diff --git a/security/smack/smack.h b/security/smack/smack.h index 2007d38d0e46..436231dfae33 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -368,12 +368,17 @@ static inline struct smack_known **smack_file(const struct file *file) smack_blob_sizes.lbs_file); } +static inline struct inode_smack *smack_inode(const struct inode *inode) +{ + return inode->i_security; +} + /* * Is the directory transmuting? */ static inline int smk_inode_transmutable(const struct inode *isp) { - struct inode_smack *sip = isp->i_security; + struct inode_smack *sip = smack_inode(isp); return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0; } @@ -382,7 +387,7 @@ static inline int smk_inode_transmutable(const struct inode *isp) */ static inline struct smack_known *smk_of_inode(const struct inode *isp) { - struct inode_smack *sip = isp->i_security; + struct inode_smack *sip = smack_inode(isp); return sip->smk_inode; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 7c76668ea3a6..ddffda39d107 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -183,7 +183,7 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc) static int smk_bu_inode(struct inode *inode, int mode, int rc) { struct task_smack *tsp = smack_cred(current_cred()); - struct inode_smack *isp = inode->i_security; + struct inode_smack *isp = smack_inode(inode); char acc[SMK_NUM_ACCESS_TYPE + 1]; if (isp->smk_flags & SMK_INODE_IMPURE) @@ -215,7 +215,7 @@ static int smk_bu_file(struct file *file, int mode, int rc) struct task_smack *tsp = smack_cred(current_cred()); struct smack_known *sskp = tsp->smk_task; struct inode *inode = file_inode(file); - struct inode_smack *isp = inode->i_security; + struct inode_smack *isp = smack_inode(inode); char acc[SMK_NUM_ACCESS_TYPE + 1]; if (isp->smk_flags & SMK_INODE_IMPURE) @@ -245,7 +245,7 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file, struct task_smack *tsp = smack_cred(cred); struct smack_known *sskp = tsp->smk_task; struct inode *inode = file_inode(file); - struct inode_smack *isp = inode->i_security; + struct inode_smack *isp = smack_inode(inode); char acc[SMK_NUM_ACCESS_TYPE + 1]; if (isp->smk_flags & SMK_INODE_IMPURE) @@ -773,7 +773,7 @@ static int smack_set_mnt_opts(struct super_block *sb, /* * Initialize the root inode. */ - isp = inode->i_security; + isp = smack_inode(inode); if (isp == NULL) { isp = new_inode_smack(sp->smk_root); if (isp == NULL) @@ -830,7 +830,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) if (bprm->called_set_creds) return 0; - isp = inode->i_security; + isp = smack_inode(inode); if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) return 0; @@ -910,7 +910,7 @@ static void smack_inode_free_rcu(struct rcu_head *head) */ static void smack_inode_free_security(struct inode *inode) { - struct inode_smack *issp = inode->i_security; + struct inode_smack *issp = smack_inode(inode); /* * The inode may still be referenced in a path walk and @@ -938,7 +938,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, void **value, size_t *len) { - struct inode_smack *issp = inode->i_security; + struct inode_smack *issp = smack_inode(inode); struct smack_known *skp = smk_of_current(); struct smack_known *isp = smk_of_inode(inode); struct smack_known *dsp = smk_of_inode(dir); @@ -1276,7 +1276,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct smack_known *skp; - struct inode_smack *isp = d_backing_inode(dentry)->i_security; + struct inode_smack *isp = smack_inode(d_backing_inode(dentry)); if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { isp->smk_flags |= SMK_INODE_TRANSMUTE; @@ -1357,7 +1357,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) if (rc != 0) return rc; - isp = d_backing_inode(dentry)->i_security; + isp = smack_inode(d_backing_inode(dentry)); /* * Don't do anything special for these. * XATTR_NAME_SMACKIPIN @@ -1632,7 +1632,7 @@ static int smack_mmap_file(struct file *file, if (unlikely(IS_PRIVATE(file_inode(file)))) return 0; - isp = file_inode(file)->i_security; + isp = smack_inode(file_inode(file)); if (isp->smk_mmap == NULL) return 0; sbsp = file_inode(file)->i_sb->s_security; @@ -1979,7 +1979,7 @@ static int smack_kernel_act_as(struct cred *new, u32 secid) static int smack_kernel_create_files_as(struct cred *new, struct inode *inode) { - struct inode_smack *isp = inode->i_security; + struct inode_smack *isp = smack_inode(inode); struct task_smack *tsp = smack_cred(new); tsp->smk_forked = isp->smk_inode; @@ -2179,7 +2179,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info, */ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) { - struct inode_smack *isp = inode->i_security; + struct inode_smack *isp = smack_inode(inode); struct smack_known *skp = smk_of_task_struct(p); isp->smk_inode = skp; @@ -2642,7 +2642,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct smack_known *skp; - struct inode_smack *nsp = inode->i_security; + struct inode_smack *nsp = smack_inode(inode); struct socket_smack *ssp; struct socket *sock; int rc = 0; @@ -3250,7 +3250,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) if (inode == NULL) return; - isp = inode->i_security; + isp = smack_inode(inode); mutex_lock(&isp->smk_lock); /* @@ -4488,7 +4488,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new) /* * Get label from overlay inode and set it in create_sid */ - isp = d_inode(dentry->d_parent)->i_security; + isp = smack_inode(d_inode(dentry->d_parent)); skp = isp->smk_inode; tsp->smk_task = skp; *new = new_creds; @@ -4525,7 +4525,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, /* * the attribute of the containing directory */ - isp = d_inode(dentry->d_parent)->i_security; + isp = smack_inode(d_inode(dentry->d_parent)); if (isp->smk_flags & SMK_INODE_TRANSMUTE) { rcu_read_lock(); -- cgit v1.2.3-59-g8ed1b From afb1cbe37440c7f38b9cf46fc331cc9dfd5cce21 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:19:29 -0700 Subject: LSM: Infrastructure management of the inode security Move management of the inode->i_security blob out of the individual security modules and into the security infrastructure. Instead of allocating the blobs from within the modules the modules tell the infrastructure how much space is required, and the space is allocated there. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- include/linux/lsm_hooks.h | 3 ++ security/security.c | 64 +++++++++++++++++++++++++++++++-- security/selinux/hooks.c | 37 ++++--------------- security/selinux/include/objsec.h | 9 +++-- security/smack/smack.h | 2 +- security/smack/smack_lsm.c | 76 +++++++++------------------------------ 6 files changed, 93 insertions(+), 98 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index e8cef019b645..1c798e842de2 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2033,6 +2033,7 @@ struct security_hook_list { struct lsm_blob_sizes { int lbs_cred; int lbs_file; + int lbs_inode; }; /* @@ -2104,6 +2105,8 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, #define __lsm_ro_after_init __ro_after_init #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ +extern int lsm_inode_alloc(struct inode *inode); + #ifdef CONFIG_SECURITY void __init lsm_early_cred(struct cred *cred); #endif diff --git a/security/security.c b/security/security.c index f32d7d2075c6..4989fb65e662 100644 --- a/security/security.c +++ b/security/security.c @@ -41,6 +41,7 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init; static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); static struct kmem_cache *lsm_file_cache; +static struct kmem_cache *lsm_inode_cache; char *lsm_names; static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init; @@ -161,6 +162,13 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); + /* + * The inode blob gets an rcu_head in addition to + * what the modules might need. + */ + if (needed->lbs_inode && blob_sizes.lbs_inode == 0) + blob_sizes.lbs_inode = sizeof(struct rcu_head); + lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode); } /* Prepare LSM for initialization. */ @@ -283,6 +291,7 @@ static void __init ordered_lsm_init(void) init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); init_debug("file blob size = %d\n", blob_sizes.lbs_file); + init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); /* * Create any kmem_caches needed for blobs @@ -291,6 +300,10 @@ static void __init ordered_lsm_init(void) lsm_file_cache = kmem_cache_create("lsm_file_cache", blob_sizes.lbs_file, 0, SLAB_PANIC, NULL); + if (blob_sizes.lbs_inode) + lsm_inode_cache = kmem_cache_create("lsm_inode_cache", + blob_sizes.lbs_inode, 0, + SLAB_PANIC, NULL); for (lsm = ordered_lsms; *lsm; lsm++) initialize_lsm(*lsm); @@ -481,6 +494,27 @@ static int lsm_file_alloc(struct file *file) return 0; } +/** + * lsm_inode_alloc - allocate a composite inode blob + * @inode: the inode that needs a blob + * + * Allocate the inode blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +int lsm_inode_alloc(struct inode *inode) +{ + if (!lsm_inode_cache) { + inode->i_security = NULL; + return 0; + } + + inode->i_security = kmem_cache_zalloc(lsm_inode_cache, GFP_NOFS); + if (inode->i_security == NULL) + return -ENOMEM; + return 0; +} + /* * Hook list operation macros. * @@ -740,14 +774,40 @@ EXPORT_SYMBOL(security_add_mnt_opt); int security_inode_alloc(struct inode *inode) { - inode->i_security = NULL; - return call_int_hook(inode_alloc_security, 0, inode); + int rc = lsm_inode_alloc(inode); + + if (unlikely(rc)) + return rc; + rc = call_int_hook(inode_alloc_security, 0, inode); + if (unlikely(rc)) + security_inode_free(inode); + return rc; +} + +static void inode_free_by_rcu(struct rcu_head *head) +{ + /* + * The rcu head is at the start of the inode blob + */ + kmem_cache_free(lsm_inode_cache, head); } void security_inode_free(struct inode *inode) { integrity_inode_free(inode); call_void_hook(inode_free_security, inode); + /* + * The inode may still be referenced in a path walk and + * a call to security_inode_permission() can be made + * after inode_free_security() is called. Ideally, the VFS + * wouldn't do this, but fixing that is a much harder + * job. For now, simply free the i_security via RCU, and + * leave the current inode->i_security pointer intact. + * The inode will be freed after the RCU grace period too. + */ + if (inode->i_security) + call_rcu((struct rcu_head *)inode->i_security, + inode_free_by_rcu); } int security_dentry_init_security(struct dentry *dentry, int mode, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2d691e8dfbbf..23da46cd6e37 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -145,8 +145,6 @@ static int __init checkreqprot_setup(char *str) } __setup("checkreqprot=", checkreqprot_setup); -static struct kmem_cache *sel_inode_cache; - /** * selinux_secmark_enabled - Check to see if SECMARK is currently enabled * @@ -242,13 +240,9 @@ static inline u32 task_sid(const struct task_struct *task) static int inode_alloc_security(struct inode *inode) { - struct inode_security_struct *isec; + struct inode_security_struct *isec = selinux_inode(inode); u32 sid = current_sid(); - isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS); - if (!isec) - return -ENOMEM; - spin_lock_init(&isec->lock); INIT_LIST_HEAD(&isec->list); isec->inode = inode; @@ -256,7 +250,6 @@ static int inode_alloc_security(struct inode *inode) isec->sclass = SECCLASS_FILE; isec->task_sid = sid; isec->initialized = LABEL_INVALID; - inode->i_security = isec; return 0; } @@ -334,19 +327,14 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr return selinux_inode(inode); } -static void inode_free_rcu(struct rcu_head *head) -{ - struct inode_security_struct *isec; - - isec = container_of(head, struct inode_security_struct, rcu); - kmem_cache_free(sel_inode_cache, isec); -} - static void inode_free_security(struct inode *inode) { struct inode_security_struct *isec = selinux_inode(inode); - struct superblock_security_struct *sbsec = inode->i_sb->s_security; + struct superblock_security_struct *sbsec; + if (!isec) + return; + sbsec = inode->i_sb->s_security; /* * As not all inode security structures are in a list, we check for * empty list outside of the lock to make sure that we won't waste @@ -362,17 +350,6 @@ static void inode_free_security(struct inode *inode) list_del_init(&isec->list); spin_unlock(&sbsec->isec_lock); } - - /* - * The inode may still be referenced in a path walk and - * a call to selinux_inode_permission() can be made - * after inode_free_security() is called. Ideally, the VFS - * wouldn't do this, but fixing that is a much harder - * job. For now, simply free the i_security via RCU, and - * leave the current inode->i_security pointer intact. - * The inode will be freed after the RCU grace period too. - */ - call_rcu(&isec->rcu, inode_free_rcu); } static int file_alloc_security(struct file *file) @@ -6629,6 +6606,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux) struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_security_struct), .lbs_file = sizeof(struct file_security_struct), + .lbs_inode = sizeof(struct inode_security_struct), }; static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { @@ -6881,9 +6859,6 @@ static __init int selinux_init(void) default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC); - sel_inode_cache = kmem_cache_create("selinux_inode_security", - sizeof(struct inode_security_struct), - 0, SLAB_PANIC, NULL); avc_init(); avtab_cache_init(); diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 26b4ff6b4d81..562fad58c56b 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -57,10 +57,7 @@ enum label_initialized { struct inode_security_struct { struct inode *inode; /* back pointer to inode object */ - union { - struct list_head list; /* list of inode_security_struct */ - struct rcu_head rcu; /* for freeing the inode_security_struct */ - }; + struct list_head list; /* list of inode_security_struct */ u32 task_sid; /* SID of creating task */ u32 sid; /* SID of this object */ u16 sclass; /* security class of this object */ @@ -173,7 +170,9 @@ static inline struct file_security_struct *selinux_file(const struct file *file) static inline struct inode_security_struct *selinux_inode( const struct inode *inode) { - return inode->i_security; + if (unlikely(!inode->i_security)) + return NULL; + return inode->i_security + selinux_blob_sizes.lbs_inode; } #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/smack/smack.h b/security/smack/smack.h index 436231dfae33..bf0abc35ca1c 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -370,7 +370,7 @@ static inline struct smack_known **smack_file(const struct file *file) static inline struct inode_smack *smack_inode(const struct inode *inode) { - return inode->i_security; + return inode->i_security + smack_blob_sizes.lbs_inode; } /* diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ddffda39d107..804897c82810 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -305,24 +305,18 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip, } /** - * new_inode_smack - allocate an inode security blob + * init_inode_smack - initialize an inode security blob + * @isp: the blob to initialize * @skp: a pointer to the Smack label entry to use in the blob * - * Returns the new blob or NULL if there's no memory available */ -static struct inode_smack *new_inode_smack(struct smack_known *skp) +static void init_inode_smack(struct inode *inode, struct smack_known *skp) { - struct inode_smack *isp; - - isp = kmem_cache_zalloc(smack_inode_cache, GFP_NOFS); - if (isp == NULL) - return NULL; + struct inode_smack *isp = smack_inode(inode); isp->smk_inode = skp; isp->smk_flags = 0; mutex_init(&isp->smk_lock); - - return isp; } /** @@ -709,6 +703,13 @@ static int smack_set_mnt_opts(struct super_block *sb, if (sp->smk_flags & SMK_SB_INITIALIZED) return 0; + if (inode->i_security == NULL) { + int rc = lsm_inode_alloc(inode); + + if (rc) + return rc; + } + if (!smack_privileged(CAP_MAC_ADMIN)) { /* * Unprivileged mounts don't get to specify Smack values. @@ -773,17 +774,12 @@ static int smack_set_mnt_opts(struct super_block *sb, /* * Initialize the root inode. */ - isp = smack_inode(inode); - if (isp == NULL) { - isp = new_inode_smack(sp->smk_root); - if (isp == NULL) - return -ENOMEM; - inode->i_security = isp; - } else - isp->smk_inode = sp->smk_root; + init_inode_smack(inode, sp->smk_root); - if (transmute) + if (transmute) { + isp = smack_inode(inode); isp->smk_flags |= SMK_INODE_TRANSMUTE; + } return 0; } @@ -881,48 +877,10 @@ static int smack_inode_alloc_security(struct inode *inode) { struct smack_known *skp = smk_of_current(); - inode->i_security = new_inode_smack(skp); - if (inode->i_security == NULL) - return -ENOMEM; + init_inode_smack(inode, skp); return 0; } -/** - * smack_inode_free_rcu - Free inode_smack blob from cache - * @head: the rcu_head for getting inode_smack pointer - * - * Call back function called from call_rcu() to free - * the i_security blob pointer in inode - */ -static void smack_inode_free_rcu(struct rcu_head *head) -{ - struct inode_smack *issp; - - issp = container_of(head, struct inode_smack, smk_rcu); - kmem_cache_free(smack_inode_cache, issp); -} - -/** - * smack_inode_free_security - free an inode blob using call_rcu() - * @inode: the inode with a blob - * - * Clears the blob pointer in inode using RCU - */ -static void smack_inode_free_security(struct inode *inode) -{ - struct inode_smack *issp = smack_inode(inode); - - /* - * The inode may still be referenced in a path walk and - * a call to smack_inode_permission() can be made - * after smack_inode_free_security() is called. - * To avoid race condition free the i_security via RCU - * and leave the current inode->i_security pointer intact. - * The inode will be freed after the RCU grace period too. - */ - call_rcu(&issp->smk_rcu, smack_inode_free_rcu); -} - /** * smack_inode_init_security - copy out the smack from an inode * @inode: the newly created inode @@ -4548,6 +4506,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_smack), .lbs_file = sizeof(struct smack_known *), + .lbs_inode = sizeof(struct inode_smack), }; static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { @@ -4565,7 +4524,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds), LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security), - LSM_HOOK_INIT(inode_free_security, smack_inode_free_security), LSM_HOOK_INIT(inode_init_security, smack_inode_init_security), LSM_HOOK_INIT(inode_link, smack_inode_link), LSM_HOOK_INIT(inode_unlink, smack_inode_unlink), -- cgit v1.2.3-59-g8ed1b From f4ad8f2c40769b3cc9497ba0883bbaf823f7752f Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:19:37 -0700 Subject: LSM: Infrastructure management of the task security Move management of the task_struct->security blob out of the individual security modules and into the security infrastructure. Instead of allocating the blobs from within the modules the modules tell the infrastructure how much space is required, and the space is allocated there. The only user of this blob is AppArmor. The AppArmor use is abstracted to avoid future conflict. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- include/linux/lsm_hooks.h | 2 ++ security/apparmor/include/task.h | 18 +++----------- security/apparmor/lsm.c | 15 +++-------- security/security.c | 54 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 27 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 1c798e842de2..9b39fefa88c4 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2034,6 +2034,7 @@ struct lsm_blob_sizes { int lbs_cred; int lbs_file; int lbs_inode; + int lbs_task; }; /* @@ -2109,6 +2110,7 @@ extern int lsm_inode_alloc(struct inode *inode); #ifdef CONFIG_SECURITY void __init lsm_early_cred(struct cred *cred); +void __init lsm_early_task(struct task_struct *task); #endif #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h index 55edaa1d83f8..039c1e60887a 100644 --- a/security/apparmor/include/task.h +++ b/security/apparmor/include/task.h @@ -14,7 +14,10 @@ #ifndef __AA_TASK_H #define __AA_TASK_H -#define task_ctx(X) ((X)->security) +static inline struct aa_task_ctx *task_ctx(struct task_struct *task) +{ + return task->security; +} /* * struct aa_task_ctx - information for current task label change @@ -36,17 +39,6 @@ int aa_set_current_hat(struct aa_label *label, u64 token); int aa_restore_previous_label(u64 cookie); struct aa_label *aa_get_task_label(struct task_struct *task); -/** - * aa_alloc_task_ctx - allocate a new task_ctx - * @flags: gfp flags for allocation - * - * Returns: allocated buffer or NULL on failure - */ -static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags) -{ - return kzalloc(sizeof(struct aa_task_ctx), flags); -} - /** * aa_free_task_ctx - free a task_ctx * @ctx: task_ctx to free (MAYBE NULL) @@ -57,8 +49,6 @@ static inline void aa_free_task_ctx(struct aa_task_ctx *ctx) aa_put_label(ctx->nnp); aa_put_label(ctx->previous); aa_put_label(ctx->onexec); - - kzfree(ctx); } } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 6821187b06ad..60ef71268ccf 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -94,19 +94,14 @@ static void apparmor_task_free(struct task_struct *task) { aa_free_task_ctx(task_ctx(task)); - task_ctx(task) = NULL; } static int apparmor_task_alloc(struct task_struct *task, unsigned long clone_flags) { - struct aa_task_ctx *new = aa_alloc_task_ctx(GFP_KERNEL); - - if (!new) - return -ENOMEM; + struct aa_task_ctx *new = task_ctx(task); aa_dup_task_ctx(new, task_ctx(current)); - task_ctx(task) = new; return 0; } @@ -1157,6 +1152,7 @@ static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct aa_task_ctx *), .lbs_file = sizeof(struct aa_file_ctx), + .lbs_task = sizeof(struct aa_task_ctx), }; static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { @@ -1487,15 +1483,10 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; - struct aa_task_ctx *ctx; - - ctx = aa_alloc_task_ctx(GFP_KERNEL); - if (!ctx) - return -ENOMEM; lsm_early_cred(cred); + lsm_early_task(current); set_cred_label(cred, aa_get_label(ns_unconfined(root_ns))); - task_ctx(current) = ctx; return 0; } diff --git a/security/security.c b/security/security.c index 4989fb65e662..e59a1e1514ee 100644 --- a/security/security.c +++ b/security/security.c @@ -169,6 +169,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) if (needed->lbs_inode && blob_sizes.lbs_inode == 0) blob_sizes.lbs_inode = sizeof(struct rcu_head); lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode); + lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task); } /* Prepare LSM for initialization. */ @@ -292,6 +293,7 @@ static void __init ordered_lsm_init(void) init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); init_debug("file blob size = %d\n", blob_sizes.lbs_file); init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); + init_debug("task blob size = %d\n", blob_sizes.lbs_task); /* * Create any kmem_caches needed for blobs @@ -515,6 +517,46 @@ int lsm_inode_alloc(struct inode *inode) return 0; } +/** + * lsm_task_alloc - allocate a composite task blob + * @task: the task that needs a blob + * + * Allocate the task blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +int lsm_task_alloc(struct task_struct *task) +{ + if (blob_sizes.lbs_task == 0) { + task->security = NULL; + return 0; + } + + task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL); + if (task->security == NULL) + return -ENOMEM; + return 0; +} + +/** + * lsm_early_task - during initialization allocate a composite task blob + * @task: the task that needs a blob + * + * Allocate the task blob for all the modules if it's not already there + */ +void __init lsm_early_task(struct task_struct *task) +{ + int rc; + + if (task == NULL) + panic("%s: task cred.\n", __func__); + if (task->security != NULL) + return; + rc = lsm_task_alloc(task); + if (rc) + panic("%s: Early task alloc failed.\n", __func__); +} + /* * Hook list operation macros. * @@ -1359,12 +1401,22 @@ int security_file_open(struct file *file) int security_task_alloc(struct task_struct *task, unsigned long clone_flags) { - return call_int_hook(task_alloc, 0, task, clone_flags); + int rc = lsm_task_alloc(task); + + if (rc) + return rc; + rc = call_int_hook(task_alloc, 0, task, clone_flags); + if (unlikely(rc)) + security_task_free(task); + return rc; } void security_task_free(struct task_struct *task) { call_void_hook(task_free, task); + + kfree(task->security); + task->security = NULL; } int security_cred_alloc_blank(struct cred *cred, gfp_t gfp) -- cgit v1.2.3-59-g8ed1b From 7c6538280ae99eaebd589a5c029e1ed1c063909c Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:19:45 -0700 Subject: SELinux: Abstract use of ipc security blobs Don't use the ipc->security pointer directly. Don't use the msg_msg->security pointer directly. Provide helper functions that provides the security blob pointers. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- security/selinux/hooks.c | 18 +++++++++--------- security/selinux/include/objsec.h | 13 +++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 23da46cd6e37..4b64ad31326f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5678,7 +5678,7 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, struct common_audit_data ad; u32 sid = current_sid(); - isec = ipc_perms->security; + isec = selinux_ipc(ipc_perms); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = ipc_perms->key; @@ -5735,7 +5735,7 @@ static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg) struct common_audit_data ad; u32 sid = current_sid(); - isec = msq->security; + isec = selinux_ipc(msq); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; @@ -5784,8 +5784,8 @@ static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *m u32 sid = current_sid(); int rc; - isec = msq->security; - msec = msg->security; + isec = selinux_ipc(msq); + msec = selinux_msg_msg(msg); /* * First time through, need to assign label to the message @@ -5832,8 +5832,8 @@ static int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *m u32 sid = task_sid(target); int rc; - isec = msq->security; - msec = msg->security; + isec = selinux_ipc(msq); + msec = selinux_msg_msg(msg); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; @@ -5886,7 +5886,7 @@ static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg) struct common_audit_data ad; u32 sid = current_sid(); - isec = shp->security; + isec = selinux_ipc(shp); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->key; @@ -5983,7 +5983,7 @@ static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg) struct common_audit_data ad; u32 sid = current_sid(); - isec = sma->security; + isec = selinux_ipc(sma); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->key; @@ -6069,7 +6069,7 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) { - struct ipc_security_struct *isec = ipcp->security; + struct ipc_security_struct *isec = selinux_ipc(ipcp); *secid = isec->sid; } diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 562fad58c56b..539cacf4a572 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "flask.h" #include "avc.h" @@ -175,4 +176,16 @@ static inline struct inode_security_struct *selinux_inode( return inode->i_security + selinux_blob_sizes.lbs_inode; } +static inline struct msg_security_struct *selinux_msg_msg( + const struct msg_msg *msg_msg) +{ + return msg_msg->security; +} + +static inline struct ipc_security_struct *selinux_ipc( + const struct kern_ipc_perm *ipc) +{ + return ipc->security; +} + #endif /* _SELINUX_OBJSEC_H_ */ -- cgit v1.2.3-59-g8ed1b From 019bcca4626a9ed119e1d9ebfadb9fdbdcf9b35b Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Fri, 21 Sep 2018 17:19:54 -0700 Subject: Smack: Abstract use of ipc security blobs Don't use the ipc->security pointer directly. Don't use the msg_msg->security pointer directly. Provide helper functions that provides the security blob pointers. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook Signed-off-by: Kees Cook --- security/smack/smack.h | 11 +++++++++++ security/smack/smack_lsm.c | 14 +++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'security') diff --git a/security/smack/smack.h b/security/smack/smack.h index bf0abc35ca1c..0adddbeecc62 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -24,6 +24,7 @@ #include #include #include +#include /* * Use IPv6 port labeling if IPv6 is enabled and secmarks @@ -373,6 +374,16 @@ static inline struct inode_smack *smack_inode(const struct inode *inode) return inode->i_security + smack_blob_sizes.lbs_inode; } +static inline struct smack_known **smack_msg_msg(const struct msg_msg *msg) +{ + return (struct smack_known **)&msg->security; +} + +static inline struct smack_known **smack_ipc(const struct kern_ipc_perm *ipc) +{ + return (struct smack_known **)&ipc->security; +} + /* * Is the directory transmuting? */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 804897c82810..154521b6843b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2834,7 +2834,9 @@ static void smack_msg_msg_free_security(struct msg_msg *msg) */ static struct smack_known *smack_of_ipc(struct kern_ipc_perm *isp) { - return (struct smack_known *)isp->security; + struct smack_known **blob = smack_ipc(isp); + + return *blob; } /** @@ -2845,9 +2847,9 @@ static struct smack_known *smack_of_ipc(struct kern_ipc_perm *isp) */ static int smack_ipc_alloc_security(struct kern_ipc_perm *isp) { - struct smack_known *skp = smk_of_current(); + struct smack_known **blob = smack_ipc(isp); - isp->security = skp; + *blob = smk_of_current(); return 0; } @@ -3159,7 +3161,8 @@ static int smack_msg_queue_msgrcv(struct kern_ipc_perm *isp, struct msg_msg *msg */ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) { - struct smack_known *iskp = ipp->security; + struct smack_known **blob = smack_ipc(ipp); + struct smack_known *iskp = *blob; int may = smack_flags_to_may(flag); struct smk_audit_info ad; int rc; @@ -3180,7 +3183,8 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) */ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) { - struct smack_known *iskp = ipp->security; + struct smack_known **blob = smack_ipc(ipp); + struct smack_known *iskp = *blob; *secid = iskp->smk_secid; } -- cgit v1.2.3-59-g8ed1b From ecd5f82e05ddd9b06c258167ec7467ac79741d77 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Tue, 20 Nov 2018 11:55:02 -0800 Subject: LSM: Infrastructure management of the ipc security blob Move management of the kern_ipc_perm->security and msg_msg->security blobs out of the individual security modules and into the security infrastructure. Instead of allocating the blobs from within the modules the modules tell the infrastructure how much space is required, and the space is allocated there. Signed-off-by: Casey Schaufler Reviewed-by: Kees Cook [kees: adjusted for ordered init series] Signed-off-by: Kees Cook --- include/linux/lsm_hooks.h | 2 + security/security.c | 91 ++++++++++++++++++++++++++++++++++-- security/selinux/hooks.c | 98 ++++++--------------------------------- security/selinux/include/objsec.h | 4 +- security/smack/smack.h | 4 +- security/smack/smack_lsm.c | 32 ++----------- 6 files changed, 110 insertions(+), 121 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 9b39fefa88c4..40511a8a5ae6 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2034,6 +2034,8 @@ struct lsm_blob_sizes { int lbs_cred; int lbs_file; int lbs_inode; + int lbs_ipc; + int lbs_msg_msg; int lbs_task; }; diff --git a/security/security.c b/security/security.c index e59a1e1514ee..953fc3ea18a9 100644 --- a/security/security.c +++ b/security/security.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define MAX_LSM_EVM_XATTR 2 @@ -169,6 +170,8 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) if (needed->lbs_inode && blob_sizes.lbs_inode == 0) blob_sizes.lbs_inode = sizeof(struct rcu_head); lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode); + lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc); + lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg); lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task); } @@ -293,6 +296,8 @@ static void __init ordered_lsm_init(void) init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); init_debug("file blob size = %d\n", blob_sizes.lbs_file); init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); + init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); + init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); init_debug("task blob size = %d\n", blob_sizes.lbs_task); /* @@ -538,6 +543,48 @@ int lsm_task_alloc(struct task_struct *task) return 0; } +/** + * lsm_ipc_alloc - allocate a composite ipc blob + * @kip: the ipc that needs a blob + * + * Allocate the ipc blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +int lsm_ipc_alloc(struct kern_ipc_perm *kip) +{ + if (blob_sizes.lbs_ipc == 0) { + kip->security = NULL; + return 0; + } + + kip->security = kzalloc(blob_sizes.lbs_ipc, GFP_KERNEL); + if (kip->security == NULL) + return -ENOMEM; + return 0; +} + +/** + * lsm_msg_msg_alloc - allocate a composite msg_msg blob + * @mp: the msg_msg that needs a blob + * + * Allocate the ipc blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +int lsm_msg_msg_alloc(struct msg_msg *mp) +{ + if (blob_sizes.lbs_msg_msg == 0) { + mp->security = NULL; + return 0; + } + + mp->security = kzalloc(blob_sizes.lbs_msg_msg, GFP_KERNEL); + if (mp->security == NULL) + return -ENOMEM; + return 0; +} + /** * lsm_early_task - during initialization allocate a composite task blob * @task: the task that needs a blob @@ -1631,22 +1678,40 @@ void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) int security_msg_msg_alloc(struct msg_msg *msg) { - return call_int_hook(msg_msg_alloc_security, 0, msg); + int rc = lsm_msg_msg_alloc(msg); + + if (unlikely(rc)) + return rc; + rc = call_int_hook(msg_msg_alloc_security, 0, msg); + if (unlikely(rc)) + security_msg_msg_free(msg); + return rc; } void security_msg_msg_free(struct msg_msg *msg) { call_void_hook(msg_msg_free_security, msg); + kfree(msg->security); + msg->security = NULL; } int security_msg_queue_alloc(struct kern_ipc_perm *msq) { - return call_int_hook(msg_queue_alloc_security, 0, msq); + int rc = lsm_ipc_alloc(msq); + + if (unlikely(rc)) + return rc; + rc = call_int_hook(msg_queue_alloc_security, 0, msq); + if (unlikely(rc)) + security_msg_queue_free(msq); + return rc; } void security_msg_queue_free(struct kern_ipc_perm *msq) { call_void_hook(msg_queue_free_security, msq); + kfree(msq->security); + msq->security = NULL; } int security_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg) @@ -1673,12 +1738,21 @@ int security_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *msg, int security_shm_alloc(struct kern_ipc_perm *shp) { - return call_int_hook(shm_alloc_security, 0, shp); + int rc = lsm_ipc_alloc(shp); + + if (unlikely(rc)) + return rc; + rc = call_int_hook(shm_alloc_security, 0, shp); + if (unlikely(rc)) + security_shm_free(shp); + return rc; } void security_shm_free(struct kern_ipc_perm *shp) { call_void_hook(shm_free_security, shp); + kfree(shp->security); + shp->security = NULL; } int security_shm_associate(struct kern_ipc_perm *shp, int shmflg) @@ -1698,12 +1772,21 @@ int security_shm_shmat(struct kern_ipc_perm *shp, char __user *shmaddr, int shmf int security_sem_alloc(struct kern_ipc_perm *sma) { - return call_int_hook(sem_alloc_security, 0, sma); + int rc = lsm_ipc_alloc(sma); + + if (unlikely(rc)) + return rc; + rc = call_int_hook(sem_alloc_security, 0, sma); + if (unlikely(rc)) + security_sem_free(sma); + return rc; } void security_sem_free(struct kern_ipc_perm *sma) { call_void_hook(sem_free_security, sma); + kfree(sma->security); + sma->security = NULL; } int security_sem_associate(struct kern_ipc_perm *sma, int semflg) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4b64ad31326f..d98e1d8d18f6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5626,51 +5626,22 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) return selinux_nlmsg_perm(sk, skb); } -static int ipc_alloc_security(struct kern_ipc_perm *perm, - u16 sclass) +static void ipc_init_security(struct ipc_security_struct *isec, u16 sclass) { - struct ipc_security_struct *isec; - - isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); - if (!isec) - return -ENOMEM; - isec->sclass = sclass; isec->sid = current_sid(); - perm->security = isec; - - return 0; -} - -static void ipc_free_security(struct kern_ipc_perm *perm) -{ - struct ipc_security_struct *isec = perm->security; - perm->security = NULL; - kfree(isec); } static int msg_msg_alloc_security(struct msg_msg *msg) { struct msg_security_struct *msec; - msec = kzalloc(sizeof(struct msg_security_struct), GFP_KERNEL); - if (!msec) - return -ENOMEM; - + msec = selinux_msg_msg(msg); msec->sid = SECINITSID_UNLABELED; - msg->security = msec; return 0; } -static void msg_msg_free_security(struct msg_msg *msg) -{ - struct msg_security_struct *msec = msg->security; - - msg->security = NULL; - kfree(msec); -} - static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, u32 perms) { @@ -5692,11 +5663,6 @@ static int selinux_msg_msg_alloc_security(struct msg_msg *msg) return msg_msg_alloc_security(msg); } -static void selinux_msg_msg_free_security(struct msg_msg *msg) -{ - msg_msg_free_security(msg); -} - /* message queue security operations */ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) { @@ -5705,11 +5671,8 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) u32 sid = current_sid(); int rc; - rc = ipc_alloc_security(msq, SECCLASS_MSGQ); - if (rc) - return rc; - - isec = msq->security; + isec = selinux_ipc(msq); + ipc_init_security(isec, SECCLASS_MSGQ); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = msq->key; @@ -5717,16 +5680,7 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) rc = avc_has_perm(&selinux_state, sid, isec->sid, SECCLASS_MSGQ, MSGQ__CREATE, &ad); - if (rc) { - ipc_free_security(msq); - return rc; - } - return 0; -} - -static void selinux_msg_queue_free_security(struct kern_ipc_perm *msq) -{ - ipc_free_security(msq); + return rc; } static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg) @@ -5856,11 +5810,8 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp) u32 sid = current_sid(); int rc; - rc = ipc_alloc_security(shp, SECCLASS_SHM); - if (rc) - return rc; - - isec = shp->security; + isec = selinux_ipc(shp); + ipc_init_security(isec, SECCLASS_SHM); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = shp->key; @@ -5868,16 +5819,7 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp) rc = avc_has_perm(&selinux_state, sid, isec->sid, SECCLASS_SHM, SHM__CREATE, &ad); - if (rc) { - ipc_free_security(shp); - return rc; - } - return 0; -} - -static void selinux_shm_free_security(struct kern_ipc_perm *shp) -{ - ipc_free_security(shp); + return rc; } static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg) @@ -5953,11 +5895,8 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma) u32 sid = current_sid(); int rc; - rc = ipc_alloc_security(sma, SECCLASS_SEM); - if (rc) - return rc; - - isec = sma->security; + isec = selinux_ipc(sma); + ipc_init_security(isec, SECCLASS_SEM); ad.type = LSM_AUDIT_DATA_IPC; ad.u.ipc_id = sma->key; @@ -5965,16 +5904,7 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma) rc = avc_has_perm(&selinux_state, sid, isec->sid, SECCLASS_SEM, SEM__CREATE, &ad); - if (rc) { - ipc_free_security(sma); - return rc; - } - return 0; -} - -static void selinux_sem_free_security(struct kern_ipc_perm *sma) -{ - ipc_free_security(sma); + return rc; } static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg) @@ -6607,6 +6537,8 @@ struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_security_struct), .lbs_file = sizeof(struct file_security_struct), .lbs_inode = sizeof(struct inode_security_struct), + .lbs_ipc = sizeof(struct ipc_security_struct), + .lbs_msg_msg = sizeof(struct msg_security_struct), }; static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { @@ -6718,24 +6650,20 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid), LSM_HOOK_INIT(msg_msg_alloc_security, selinux_msg_msg_alloc_security), - LSM_HOOK_INIT(msg_msg_free_security, selinux_msg_msg_free_security), LSM_HOOK_INIT(msg_queue_alloc_security, selinux_msg_queue_alloc_security), - LSM_HOOK_INIT(msg_queue_free_security, selinux_msg_queue_free_security), LSM_HOOK_INIT(msg_queue_associate, selinux_msg_queue_associate), LSM_HOOK_INIT(msg_queue_msgctl, selinux_msg_queue_msgctl), LSM_HOOK_INIT(msg_queue_msgsnd, selinux_msg_queue_msgsnd), LSM_HOOK_INIT(msg_queue_msgrcv, selinux_msg_queue_msgrcv), LSM_HOOK_INIT(shm_alloc_security, selinux_shm_alloc_security), - LSM_HOOK_INIT(shm_free_security, selinux_shm_free_security), LSM_HOOK_INIT(shm_associate, selinux_shm_associate), LSM_HOOK_INIT(shm_shmctl, selinux_shm_shmctl), LSM_HOOK_INIT(shm_shmat, selinux_shm_shmat), LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security), - LSM_HOOK_INIT(sem_free_security, selinux_sem_free_security), LSM_HOOK_INIT(sem_associate, selinux_sem_associate), LSM_HOOK_INIT(sem_semctl, selinux_sem_semctl), LSM_HOOK_INIT(sem_semop, selinux_sem_semop), diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 539cacf4a572..231262d8eac9 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -179,13 +179,13 @@ static inline struct inode_security_struct *selinux_inode( static inline struct msg_security_struct *selinux_msg_msg( const struct msg_msg *msg_msg) { - return msg_msg->security; + return msg_msg->security + selinux_blob_sizes.lbs_msg_msg; } static inline struct ipc_security_struct *selinux_ipc( const struct kern_ipc_perm *ipc) { - return ipc->security; + return ipc->security + selinux_blob_sizes.lbs_ipc; } #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/smack/smack.h b/security/smack/smack.h index 0adddbeecc62..9c7c95a5c497 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -376,12 +376,12 @@ static inline struct inode_smack *smack_inode(const struct inode *inode) static inline struct smack_known **smack_msg_msg(const struct msg_msg *msg) { - return (struct smack_known **)&msg->security; + return msg->security + smack_blob_sizes.lbs_msg_msg; } static inline struct smack_known **smack_ipc(const struct kern_ipc_perm *ipc) { - return (struct smack_known **)&ipc->security; + return ipc->security + smack_blob_sizes.lbs_ipc; } /* diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 154521b6843b..0b848b1f6366 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2809,23 +2809,12 @@ static int smack_flags_to_may(int flags) */ static int smack_msg_msg_alloc_security(struct msg_msg *msg) { - struct smack_known *skp = smk_of_current(); + struct smack_known **blob = smack_msg_msg(msg); - msg->security = skp; + *blob = smk_of_current(); return 0; } -/** - * smack_msg_msg_free_security - Clear the security blob for msg_msg - * @msg: the object - * - * Clears the blob pointer - */ -static void smack_msg_msg_free_security(struct msg_msg *msg) -{ - msg->security = NULL; -} - /** * smack_of_ipc - the smack pointer for the ipc * @isp: the object @@ -2853,17 +2842,6 @@ static int smack_ipc_alloc_security(struct kern_ipc_perm *isp) return 0; } -/** - * smack_ipc_free_security - Clear the security blob for ipc - * @isp: the object - * - * Clears the blob pointer - */ -static void smack_ipc_free_security(struct kern_ipc_perm *isp) -{ - isp->security = NULL; -} - /** * smk_curacc_shm : check if current has access on shm * @isp : the object @@ -4511,6 +4489,8 @@ struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_smack), .lbs_file = sizeof(struct smack_known *), .lbs_inode = sizeof(struct inode_smack), + .lbs_ipc = sizeof(struct smack_known *), + .lbs_msg_msg = sizeof(struct smack_known *), }; static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { @@ -4581,23 +4561,19 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ipc_getsecid, smack_ipc_getsecid), LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security), - LSM_HOOK_INIT(msg_msg_free_security, smack_msg_msg_free_security), LSM_HOOK_INIT(msg_queue_alloc_security, smack_ipc_alloc_security), - LSM_HOOK_INIT(msg_queue_free_security, smack_ipc_free_security), LSM_HOOK_INIT(msg_queue_associate, smack_msg_queue_associate), LSM_HOOK_INIT(msg_queue_msgctl, smack_msg_queue_msgctl), LSM_HOOK_INIT(msg_queue_msgsnd, smack_msg_queue_msgsnd), LSM_HOOK_INIT(msg_queue_msgrcv, smack_msg_queue_msgrcv), LSM_HOOK_INIT(shm_alloc_security, smack_ipc_alloc_security), - LSM_HOOK_INIT(shm_free_security, smack_ipc_free_security), LSM_HOOK_INIT(shm_associate, smack_shm_associate), LSM_HOOK_INIT(shm_shmctl, smack_shm_shmctl), LSM_HOOK_INIT(shm_shmat, smack_shm_shmat), LSM_HOOK_INIT(sem_alloc_security, smack_ipc_alloc_security), - LSM_HOOK_INIT(sem_free_security, smack_ipc_free_security), LSM_HOOK_INIT(sem_associate, smack_sem_associate), LSM_HOOK_INIT(sem_semctl, smack_sem_semctl), LSM_HOOK_INIT(sem_semop, smack_sem_semop), -- cgit v1.2.3-59-g8ed1b From a5e2fe7ede1268d2f80fe49ca1f717d0e3750995 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 12 Oct 2018 12:07:39 -0700 Subject: TOMOYO: Update LSM flags to no longer be exclusive With blob sharing in place, TOMOYO is no longer an exclusive LSM, so it can operate separately now. Mark it as such. Signed-off-by: Kees Cook --- security/tomoyo/tomoyo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 9094cf41a247..066c0daf0efc 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -577,7 +577,7 @@ static int __init tomoyo_init(void) DEFINE_LSM(tomoyo) = { .name = "tomoyo", .enabled = &tomoyo_enabled, - .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, + .flags = LSM_FLAG_LEGACY_MAJOR, .blobs = &tomoyo_blob_sizes, .init = tomoyo_init, }; -- cgit v1.2.3-59-g8ed1b From c1a85a00ea66cb6f0bd0f14e47c28c2b0999799f Mon Sep 17 00:00:00 2001 From: Micah Morton Date: Mon, 7 Jan 2019 16:10:53 -0800 Subject: LSM: generalize flag passing to security_capable This patch provides a general mechanism for passing flags to the security_capable LSM hook. It replaces the specific 'audit' flag that is used to tell security_capable whether it should log an audit message for the given capability check. The reason for generalizing this flag passing is so we can add an additional flag that signifies whether security_capable is being called by a setid syscall (which is needed by the proposed SafeSetID LSM). Signed-off-by: Micah Morton Reviewed-by: Kees Cook Signed-off-by: James Morris --- include/linux/lsm_hooks.h | 8 +++++--- include/linux/security.h | 28 ++++++++++++++-------------- kernel/capability.c | 22 +++++++++++++--------- kernel/seccomp.c | 4 ++-- security/apparmor/capability.c | 14 +++++++------- security/apparmor/include/capability.h | 2 +- security/apparmor/ipc.c | 3 ++- security/apparmor/lsm.c | 4 ++-- security/apparmor/resource.c | 2 +- security/commoncap.c | 17 +++++++++-------- security/security.c | 14 +++++--------- security/selinux/hooks.c | 18 +++++++++--------- security/smack/smack_access.c | 2 +- 13 files changed, 71 insertions(+), 67 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 40511a8a5ae6..195707210975 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1270,7 +1270,7 @@ * @cred contains the credentials to use. * @ns contains the user namespace we want the capability in * @cap contains the capability . - * @audit contains whether to write an audit message or not + * @opts contains options for the capable check * Return 0 if the capability is granted for @tsk. * @syslog: * Check permission before accessing the kernel message ring or changing @@ -1446,8 +1446,10 @@ union security_list_options { const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); - int (*capable)(const struct cred *cred, struct user_namespace *ns, - int cap, int audit); + int (*capable)(const struct cred *cred, + struct user_namespace *ns, + int cap, + unsigned int opts); int (*quotactl)(int cmds, int type, int id, struct super_block *sb); int (*quota_on)(struct dentry *dentry); int (*syslog)(int type); diff --git a/include/linux/security.h b/include/linux/security.h index b2c5333ed4b5..13537a49ae97 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -54,9 +54,12 @@ struct xattr; struct xfrm_sec_ctx; struct mm_struct; +/* Default (no) options for the capable function */ +#define CAP_OPT_NONE 0x0 /* If capable should audit the security request */ -#define SECURITY_CAP_NOAUDIT 0 -#define SECURITY_CAP_AUDIT 1 +#define CAP_OPT_NOAUDIT BIT(1) +/* If capable is being called by a setid function */ +#define CAP_OPT_INSETID BIT(2) /* LSM Agnostic defines for sb_set_mnt_opts */ #define SECURITY_LSM_NATIVE_LABELS 1 @@ -72,7 +75,7 @@ enum lsm_event { /* These functions are in security/commoncap.c */ extern int cap_capable(const struct cred *cred, struct user_namespace *ns, - int cap, int audit); + int cap, unsigned int opts); extern int cap_settime(const struct timespec64 *ts, const struct timezone *tz); extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode); extern int cap_ptrace_traceme(struct task_struct *parent); @@ -207,10 +210,10 @@ int security_capset(struct cred *new, const struct cred *old, const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable(const struct cred *cred, struct user_namespace *ns, - int cap); -int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns, - int cap); +int security_capable(const struct cred *cred, + struct user_namespace *ns, + int cap, + unsigned int opts); int security_quotactl(int cmds, int type, int id, struct super_block *sb); int security_quota_on(struct dentry *dentry); int security_syslog(int type); @@ -464,14 +467,11 @@ static inline int security_capset(struct cred *new, } static inline int security_capable(const struct cred *cred, - struct user_namespace *ns, int cap) + struct user_namespace *ns, + int cap, + unsigned int opts) { - return cap_capable(cred, ns, cap, SECURITY_CAP_AUDIT); -} - -static inline int security_capable_noaudit(const struct cred *cred, - struct user_namespace *ns, int cap) { - return cap_capable(cred, ns, cap, SECURITY_CAP_NOAUDIT); + return cap_capable(cred, ns, cap, opts); } static inline int security_quotactl(int cmds, int type, int id, diff --git a/kernel/capability.c b/kernel/capability.c index 1e1c0236f55b..7718d7dcadc7 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -299,7 +299,7 @@ bool has_ns_capability(struct task_struct *t, int ret; rcu_read_lock(); - ret = security_capable(__task_cred(t), ns, cap); + ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NONE); rcu_read_unlock(); return (ret == 0); @@ -340,7 +340,7 @@ bool has_ns_capability_noaudit(struct task_struct *t, int ret; rcu_read_lock(); - ret = security_capable_noaudit(__task_cred(t), ns, cap); + ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NOAUDIT); rcu_read_unlock(); return (ret == 0); @@ -363,7 +363,9 @@ bool has_capability_noaudit(struct task_struct *t, int cap) return has_ns_capability_noaudit(t, &init_user_ns, cap); } -static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) +static bool ns_capable_common(struct user_namespace *ns, + int cap, + unsigned int opts) { int capable; @@ -372,8 +374,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) BUG(); } - capable = audit ? security_capable(current_cred(), ns, cap) : - security_capable_noaudit(current_cred(), ns, cap); + capable = security_capable(current_cred(), ns, cap, opts); if (capable == 0) { current->flags |= PF_SUPERPRIV; return true; @@ -394,7 +395,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) */ bool ns_capable(struct user_namespace *ns, int cap) { - return ns_capable_common(ns, cap, true); + return ns_capable_common(ns, cap, CAP_OPT_NONE); } EXPORT_SYMBOL(ns_capable); @@ -412,7 +413,7 @@ EXPORT_SYMBOL(ns_capable); */ bool ns_capable_noaudit(struct user_namespace *ns, int cap) { - return ns_capable_common(ns, cap, false); + return ns_capable_common(ns, cap, CAP_OPT_NOAUDIT); } EXPORT_SYMBOL(ns_capable_noaudit); @@ -448,10 +449,11 @@ EXPORT_SYMBOL(capable); bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap) { + if (WARN_ON_ONCE(!cap_valid(cap))) return false; - if (security_capable(file->f_cred, ns, cap) == 0) + if (security_capable(file->f_cred, ns, cap, CAP_OPT_NONE) == 0) return true; return false; @@ -500,10 +502,12 @@ bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns) { int ret = 0; /* An absent tracer adds no restrictions */ const struct cred *cred; + rcu_read_lock(); cred = rcu_dereference(tsk->ptracer_cred); if (cred) - ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE); + ret = security_capable(cred, ns, CAP_SYS_PTRACE, + CAP_OPT_NOAUDIT); rcu_read_unlock(); return (ret == 0); } diff --git a/kernel/seccomp.c b/kernel/seccomp.c index d7f538847b84..38a77800def6 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -443,8 +443,8 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) * behavior of privileged children. */ if (!task_no_new_privs(current) && - security_capable_noaudit(current_cred(), current_user_ns(), - CAP_SYS_ADMIN) != 0) + security_capable(current_cred(), current_user_ns(), + CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) != 0) return ERR_PTR(-EACCES); /* Allocate a new seccomp_filter */ diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 253ef6e9d445..752f73980e30 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -110,13 +110,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile, * profile_capable - test if profile allows use of capability @cap * @profile: profile being enforced (NOT NULL, NOT unconfined) * @cap: capability to test if allowed - * @audit: whether an audit record should be generated + * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated * @sa: audit data (MAY BE NULL indicating no auditing) * * Returns: 0 if allowed else -EPERM */ -static int profile_capable(struct aa_profile *profile, int cap, int audit, - struct common_audit_data *sa) +static int profile_capable(struct aa_profile *profile, int cap, + unsigned int opts, struct common_audit_data *sa) { int error; @@ -126,7 +126,7 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit, else error = -EPERM; - if (audit == SECURITY_CAP_NOAUDIT) { + if (opts & CAP_OPT_NOAUDIT) { if (!COMPLAIN_MODE(profile)) return error; /* audit the cap request in complain mode but note that it @@ -142,13 +142,13 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit, * aa_capable - test permission to use capability * @label: label being tested for capability (NOT NULL) * @cap: capability to be tested - * @audit: whether an audit record should be generated + * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated * * Look up capability in profile capability set. * * Returns: 0 on success, or else an error code. */ -int aa_capable(struct aa_label *label, int cap, int audit) +int aa_capable(struct aa_label *label, int cap, unsigned int opts) { struct aa_profile *profile; int error = 0; @@ -156,7 +156,7 @@ int aa_capable(struct aa_label *label, int cap, int audit) sa.u.cap = cap; error = fn_for_each_confined(label, profile, - profile_capable(profile, cap, audit, &sa)); + profile_capable(profile, cap, opts, &sa)); return error; } diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index e0304e2aeb7f..1b3663b6ab12 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h @@ -40,7 +40,7 @@ struct aa_caps { extern struct aa_sfs_entry aa_sfs_entry_caps[]; -int aa_capable(struct aa_label *label, int cap, int audit); +int aa_capable(struct aa_label *label, int cap, unsigned int opts); static inline void aa_free_cap_rules(struct aa_caps *caps) { diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 527ea1557120..aacd1e95cb59 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -107,7 +107,8 @@ static int profile_tracer_perm(struct aa_profile *tracer, aad(sa)->label = &tracer->label; aad(sa)->peer = tracee; aad(sa)->request = 0; - aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1); + aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, + CAP_OPT_NONE); return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 60ef71268ccf..b6c395e2acd0 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -172,14 +172,14 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, } static int apparmor_capable(const struct cred *cred, struct user_namespace *ns, - int cap, int audit) + int cap, unsigned int opts) { struct aa_label *label; int error = 0; label = aa_get_newest_cred_label(cred); if (!unconfined(label)) - error = aa_capable(label, cap, audit); + error = aa_capable(label, cap, opts); aa_put_label(label); return error; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 95fd26d09757..552ed09cb47e 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -124,7 +124,7 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task, */ if (label != peer && - aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT) != 0) + aa_capable(label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT) != 0) error = fn_for_each(label, profile, audit_resource(profile, resource, new_rlim->rlim_max, peer, diff --git a/security/commoncap.c b/security/commoncap.c index 52e04136bfa8..188eaf59f82f 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -68,7 +68,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) * kernel's capable() and has_capability() returns 1 for this case. */ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, - int cap, int audit) + int cap, unsigned int opts) { struct user_namespace *ns = targ_ns; @@ -222,12 +222,11 @@ int cap_capget(struct task_struct *target, kernel_cap_t *effective, */ static inline int cap_inh_is_capped(void) { - /* they are so limited unless the current task has the CAP_SETPCAP * capability */ if (cap_capable(current_cred(), current_cred()->user_ns, - CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) + CAP_SETPCAP, CAP_OPT_NONE) == 0) return 0; return 1; } @@ -1208,8 +1207,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, || ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ || (cap_capable(current_cred(), - current_cred()->user_ns, CAP_SETPCAP, - SECURITY_CAP_AUDIT) != 0) /*[4]*/ + current_cred()->user_ns, + CAP_SETPCAP, + CAP_OPT_NONE) != 0) /*[4]*/ /* * [1] no changing of bits that are locked * [2] no unlocking of locks @@ -1304,9 +1304,10 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) { int cap_sys_admin = 0; - if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN, - SECURITY_CAP_NOAUDIT) == 0) + if (cap_capable(current_cred(), &init_user_ns, + CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0) cap_sys_admin = 1; + return cap_sys_admin; } @@ -1325,7 +1326,7 @@ int cap_mmap_addr(unsigned long addr) if (addr < dac_mmap_min_addr) { ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO, - SECURITY_CAP_AUDIT); + CAP_OPT_NONE); /* set PF_SUPERPRIV if it turns out we allow the low mmap */ if (ret == 0) current->flags |= PF_SUPERPRIV; diff --git a/security/security.c b/security/security.c index 953fc3ea18a9..a618e22df5c6 100644 --- a/security/security.c +++ b/security/security.c @@ -689,16 +689,12 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable(const struct cred *cred, struct user_namespace *ns, - int cap) +int security_capable(const struct cred *cred, + struct user_namespace *ns, + int cap, + unsigned int opts) { - return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_AUDIT); -} - -int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns, - int cap) -{ - return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_NOAUDIT); + return call_int_hook(capable, 0, cred, ns, cap, opts); } int security_quotactl(int cmds, int type, int id, struct super_block *sb) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d98e1d8d18f6..b2ee49f938f1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1578,7 +1578,7 @@ static inline u32 signal_to_av(int sig) /* Check whether a task is allowed to use a capability. */ static int cred_has_capability(const struct cred *cred, - int cap, int audit, bool initns) + int cap, unsigned int opts, bool initns) { struct common_audit_data ad; struct av_decision avd; @@ -1605,7 +1605,7 @@ static int cred_has_capability(const struct cred *cred, rc = avc_has_perm_noaudit(&selinux_state, sid, sid, sclass, av, 0, &avd); - if (audit == SECURITY_CAP_AUDIT) { + if (!(opts & CAP_OPT_NOAUDIT)) { int rc2 = avc_audit(&selinux_state, sid, sid, sclass, av, &avd, rc, &ad, 0); if (rc2) @@ -2125,9 +2125,9 @@ static int selinux_capset(struct cred *new, const struct cred *old, */ static int selinux_capable(const struct cred *cred, struct user_namespace *ns, - int cap, int audit) + int cap, unsigned int opts) { - return cred_has_capability(cred, cap, audit, ns == &init_user_ns); + return cred_has_capability(cred, cap, opts, ns == &init_user_ns); } static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) @@ -2201,7 +2201,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) int rc, cap_sys_admin = 0; rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN, - SECURITY_CAP_NOAUDIT, true); + CAP_OPT_NOAUDIT, true); if (rc == 0) cap_sys_admin = 1; @@ -2988,11 +2988,11 @@ static int selinux_inode_getattr(const struct path *path) static bool has_cap_mac_admin(bool audit) { const struct cred *cred = current_cred(); - int cap_audit = audit ? SECURITY_CAP_AUDIT : SECURITY_CAP_NOAUDIT; + unsigned int opts = audit ? CAP_OPT_NONE : CAP_OPT_NOAUDIT; - if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, cap_audit)) + if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, opts)) return false; - if (cred_has_capability(cred, CAP_MAC_ADMIN, cap_audit, true)) + if (cred_has_capability(cred, CAP_MAC_ADMIN, opts, true)) return false; return true; } @@ -3387,7 +3387,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, case KDSKBENT: case KDSKBSENT: error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG, - SECURITY_CAP_AUDIT, true); + CAP_OPT_NONE, true); break; /* default case assumes that the command will go diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 489d49a20b47..fe2ce3a65822 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -640,7 +640,7 @@ bool smack_privileged_cred(int cap, const struct cred *cred) struct smack_known_list_elem *sklep; int rc; - rc = cap_capable(cred, &init_user_ns, cap, SECURITY_CAP_AUDIT); + rc = cap_capable(cred, &init_user_ns, cap, CAP_OPT_NONE); if (rc) return false; -- cgit v1.2.3-59-g8ed1b From 1cfb2a512e74e577bb0ed7c8d76df90a41a83f6a Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 18 Jan 2019 19:15:59 +0900 Subject: LSM: Make lsm_early_cred() and lsm_early_task() local functions. Since current->cred == current->real_cred when ordered_lsm_init() is called, and lsm_early_cred()/lsm_early_task() need to be called between the amount of required bytes is determined and module specific initialization function is called, we can move these calls from individual modules to ordered_lsm_init(). Signed-off-by: Tetsuo Handa Acked-by: Casey Schaufler Signed-off-by: James Morris --- include/linux/lsm_hooks.h | 5 ----- security/apparmor/lsm.c | 2 -- security/security.c | 27 +++++++++++---------------- security/selinux/hooks.c | 1 - security/smack/smack_lsm.c | 2 -- security/tomoyo/tomoyo.c | 1 - 6 files changed, 11 insertions(+), 27 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 195707210975..22fc786d723a 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -2112,9 +2112,4 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, extern int lsm_inode_alloc(struct inode *inode); -#ifdef CONFIG_SECURITY -void __init lsm_early_cred(struct cred *cred); -void __init lsm_early_task(struct task_struct *task); -#endif - #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b6c395e2acd0..bb5a02d2439f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1484,8 +1484,6 @@ static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; - lsm_early_cred(cred); - lsm_early_task(current); set_cred_label(cred, aa_get_label(ns_unconfined(root_ns))); return 0; diff --git a/security/security.c b/security/security.c index a618e22df5c6..992b612c819a 100644 --- a/security/security.c +++ b/security/security.c @@ -278,6 +278,9 @@ static void __init ordered_lsm_parse(const char *order, const char *origin) kfree(sep); } +static void __init lsm_early_cred(struct cred *cred); +static void __init lsm_early_task(struct task_struct *task); + static void __init ordered_lsm_init(void) { struct lsm_info **lsm; @@ -312,6 +315,8 @@ static void __init ordered_lsm_init(void) blob_sizes.lbs_inode, 0, SLAB_PANIC, NULL); + lsm_early_cred((struct cred *) current->cred); + lsm_early_task(current); for (lsm = ordered_lsms; *lsm; lsm++) initialize_lsm(*lsm); @@ -465,17 +470,12 @@ static int lsm_cred_alloc(struct cred *cred, gfp_t gfp) * lsm_early_cred - during initialization allocate a composite cred blob * @cred: the cred that needs a blob * - * Allocate the cred blob for all the modules if it's not already there + * Allocate the cred blob for all the modules */ -void __init lsm_early_cred(struct cred *cred) +static void __init lsm_early_cred(struct cred *cred) { - int rc; + int rc = lsm_cred_alloc(cred, GFP_KERNEL); - if (cred == NULL) - panic("%s: NULL cred.\n", __func__); - if (cred->security != NULL) - return; - rc = lsm_cred_alloc(cred, GFP_KERNEL); if (rc) panic("%s: Early cred alloc failed.\n", __func__); } @@ -589,17 +589,12 @@ int lsm_msg_msg_alloc(struct msg_msg *mp) * lsm_early_task - during initialization allocate a composite task blob * @task: the task that needs a blob * - * Allocate the task blob for all the modules if it's not already there + * Allocate the task blob for all the modules */ -void __init lsm_early_task(struct task_struct *task) +static void __init lsm_early_task(struct task_struct *task) { - int rc; + int rc = lsm_task_alloc(task); - if (task == NULL) - panic("%s: task cred.\n", __func__); - if (task->security != NULL) - return; - rc = lsm_task_alloc(task); if (rc) panic("%s: Early task alloc failed.\n", __func__); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b2ee49f938f1..5d92167dbe05 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -207,7 +207,6 @@ static void cred_init_security(void) struct cred *cred = (struct cred *) current->real_cred; struct task_security_struct *tsec; - lsm_early_cred(cred); tsec = selinux_cred(cred); tsec->osid = tsec->sid = SECINITSID_KERNEL; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0b848b1f6366..79d6d2a6a0bc 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4671,8 +4671,6 @@ static __init int smack_init(void) if (!smack_inode_cache) return -ENOMEM; - lsm_early_cred(cred); - /* * Set the security state for the initial task. */ diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 066c0daf0efc..2b3eee06004b 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -566,7 +566,6 @@ static int __init tomoyo_init(void) /* register ourselves with the security framework */ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); printk(KERN_INFO "TOMOYO Linux initialized\n"); - lsm_early_cred(cred); blob = tomoyo_cred(cred); *blob = &tomoyo_kernel_domain; tomoyo_mm_init(); -- cgit v1.2.3-59-g8ed1b From 3e8c73671244af16f1a6042f1c10d13b75cd1156 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 16 Jan 2019 05:44:32 +0000 Subject: LSM: Make some functions static Fixes the following sparse warnings: security/security.c:533:5: warning: symbol 'lsm_task_alloc' was not declared. Should it be static? security/security.c:554:5: warning: symbol 'lsm_ipc_alloc' was not declared. Should it be static? security/security.c:575:5: warning: symbol 'lsm_msg_msg_alloc' was not declared. Should it be static? Fixes: f4ad8f2c4076 ("LSM: Infrastructure management of the task security") Signed-off-by: Wei Yongjun Acked-by: Casey Schaufler Signed-off-by: James Morris --- security/security.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index 992b612c819a..3664fb9d5cf7 100644 --- a/security/security.c +++ b/security/security.c @@ -530,7 +530,7 @@ int lsm_inode_alloc(struct inode *inode) * * Returns 0, or -ENOMEM if memory can't be allocated. */ -int lsm_task_alloc(struct task_struct *task) +static int lsm_task_alloc(struct task_struct *task) { if (blob_sizes.lbs_task == 0) { task->security = NULL; @@ -551,7 +551,7 @@ int lsm_task_alloc(struct task_struct *task) * * Returns 0, or -ENOMEM if memory can't be allocated. */ -int lsm_ipc_alloc(struct kern_ipc_perm *kip) +static int lsm_ipc_alloc(struct kern_ipc_perm *kip) { if (blob_sizes.lbs_ipc == 0) { kip->security = NULL; @@ -572,7 +572,7 @@ int lsm_ipc_alloc(struct kern_ipc_perm *kip) * * Returns 0, or -ENOMEM if memory can't be allocated. */ -int lsm_msg_msg_alloc(struct msg_msg *mp) +static int lsm_msg_msg_alloc(struct msg_msg *mp) { if (blob_sizes.lbs_msg_msg == 0) { mp->security = NULL; -- cgit v1.2.3-59-g8ed1b From 6c2976b06f6813768d3e61aeeb2b3fb04597a25f Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 21 Jan 2019 10:25:10 +0900 Subject: apparmor: Adjust offset when accessing task blob. AppArmor will no longer be the only user of task blob after TOMOYO started using task blob. Signed-off-by: Tetsuo Handa Fixes: f4ad8f2c4076 ("LSM: Infrastructure management of the task security") Acked-by: Kees Cook Signed-off-by: James Morris --- security/apparmor/include/task.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h index 039c1e60887a..311e652324e3 100644 --- a/security/apparmor/include/task.h +++ b/security/apparmor/include/task.h @@ -16,7 +16,7 @@ static inline struct aa_task_ctx *task_ctx(struct task_struct *task) { - return task->security; + return task->security + apparmor_blob_sizes.lbs_task; } /* -- cgit v1.2.3-59-g8ed1b From 5b73262a5c5bd23b8250af1e1c7361b11b5c4b91 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 14 Jan 2019 21:17:23 +0100 Subject: security: keys: annotate implicit fall through There is a plan to build the kernel with -Wimplicit-fallthrough and this place in the code produced a warning (W=1). This commit remove the following warning: security/keys/keyring.c:248:10: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Mathieu Malaterre Signed-off-by: James Morris --- security/keys/keyring.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security') diff --git a/security/keys/keyring.c b/security/keys/keyring.c index eadebb92986a..b1ff07d9ce37 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -246,6 +246,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8)); n--; offset = 1; + /* fall through */ default: offset += sizeof(chunk) - 1; offset += (level - 3) * sizeof(chunk); -- cgit v1.2.3-59-g8ed1b From 0f949bcc7b4ff2ae4fea461e49034f41cdf0c103 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 14 Jan 2019 21:17:24 +0100 Subject: security: keys: annotate implicit fall throughs There is a plan to build the kernel with -Wimplicit-fallthrough and these places in the code produced warnings (W=1). Fix them up. This commit remove the following warning: security/keys/process_keys.c:380:7: warning: this statement may fall through [-Wimplicit-fallthrough=] security/keys/process_keys.c:404:7: warning: this statement may fall through [-Wimplicit-fallthrough=] security/keys/process_keys.c:424:7: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Mathieu Malaterre Signed-off-by: James Morris --- security/keys/process_keys.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'security') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 02c77e928f68..3308d738734c 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -379,6 +379,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) case -EAGAIN: /* no key */ if (ret) break; + /* fall through */ case -ENOKEY: /* negative key */ ret = key_ref; break; @@ -403,6 +404,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) case -EAGAIN: /* no key */ if (ret) break; + /* fall through */ case -ENOKEY: /* negative key */ ret = key_ref; break; @@ -423,6 +425,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) case -EAGAIN: /* no key */ if (ret) break; + /* fall through */ case -ENOKEY: /* negative key */ ret = key_ref; break; -- cgit v1.2.3-59-g8ed1b From 23711df7f4a268dce2adba379ad77ded8220d097 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 14 Jan 2019 21:17:25 +0100 Subject: security: keys: annotate implicit fall throughs There is a plan to build the kernel with -Wimplicit-fallthrough and these places in the code produced warnings (W=1). Fix them up. This commit remove the following warnings: security/keys/request_key.c:293:7: warning: this statement may fall through [-Wimplicit-fallthrough=] security/keys/request_key.c:298:7: warning: this statement may fall through [-Wimplicit-fallthrough=] security/keys/request_key.c:307:7: warning: this statement may fall through [-Wimplicit-fallthrough=] Signed-off-by: Mathieu Malaterre Signed-off-by: James Morris --- security/keys/request_key.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'security') diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 301f0e300dbd..af8816ffb6ce 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -287,16 +287,19 @@ static int construct_get_dest_keyring(struct key **_dest_keyring) } } + /* fall through */ case KEY_REQKEY_DEFL_THREAD_KEYRING: dest_keyring = key_get(cred->thread_keyring); if (dest_keyring) break; + /* fall through */ case KEY_REQKEY_DEFL_PROCESS_KEYRING: dest_keyring = key_get(cred->process_keyring); if (dest_keyring) break; + /* fall through */ case KEY_REQKEY_DEFL_SESSION_KEYRING: rcu_read_lock(); dest_keyring = key_get( @@ -306,6 +309,7 @@ static int construct_get_dest_keyring(struct key **_dest_keyring) if (dest_keyring) break; + /* fall through */ case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: dest_keyring = key_get(cred->user->session_keyring); -- cgit v1.2.3-59-g8ed1b From 8c6cb983cd52d78ab4e4c0191c73a11dcb60b866 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 19 Jan 2019 23:11:40 +0900 Subject: tomoyo: Swicth from cred->security to task_struct->security. TOMOYO security module is designed to use "struct task_struct"->security in order to allow per "struct task_struct" tracking without being disturbed by unable to update "struct cred"->security due to override mechanism. Now that infrastructure-managed security blob is ready, this patch updates TOMOYO to use "struct task_struct"->security. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 4 +- security/tomoyo/common.h | 45 ++++-------- security/tomoyo/domain.c | 11 +-- security/tomoyo/securityfs_if.c | 21 ++---- security/tomoyo/tomoyo.c | 149 +++++++++++++++++++++------------------- 5 files changed, 108 insertions(+), 122 deletions(-) (limited to 'security') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index c598aa00d5e3..6936def78002 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -986,7 +986,7 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, else p = find_task_by_vpid(pid); if (p) - domain = tomoyo_real_domain(p); + domain = tomoyo_task(p)->domain_info; rcu_read_unlock(); } else if (!strncmp(data, "domain=", 7)) { if (tomoyo_domain_def(data + 7)) @@ -1668,7 +1668,7 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head) else p = find_task_by_vpid(pid); if (p) - domain = tomoyo_real_domain(p); + domain = tomoyo_task(p)->domain_info; rcu_read_unlock(); if (!domain) return; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 4fc17294a12d..cfd075c92583 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -686,7 +686,7 @@ struct tomoyo_domain_info { u8 group; /* Group number to use. */ bool is_deleted; /* Delete flag. */ bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS]; - atomic_t users; /* Number of referring credentials. */ + atomic_t users; /* Number of referring tasks. */ }; /* @@ -913,6 +913,12 @@ struct tomoyo_policy_namespace { const char *name; }; +/* Structure for "struct task_struct"->security. */ +struct tomoyo_task { + struct tomoyo_domain_info *domain_info; + struct tomoyo_domain_info *old_domain_info; +}; + /********** Function prototypes. **********/ bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, @@ -1021,6 +1027,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param); struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, const bool transit); +struct tomoyo_domain_info *tomoyo_domain(void); struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, const u8 idx); @@ -1200,41 +1207,15 @@ static inline void tomoyo_put_group(struct tomoyo_group *group) } /** - * tomoyo_cred - Get a pointer to the tomoyo cred security blob - * @cred - the relevant cred - * - * Returns pointer to the tomoyo cred blob. - */ -static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred) -{ - return cred->security + tomoyo_blob_sizes.lbs_cred; -} - -/** - * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread. + * tomoyo_task - Get "struct tomoyo_task" for specified thread. * - * Returns pointer to "struct tomoyo_domain_info" for current thread. - */ -static inline struct tomoyo_domain_info *tomoyo_domain(void) -{ - struct tomoyo_domain_info **blob = tomoyo_cred(current_cred()); - - return *blob; -} - -/** - * tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread. + * @task - Pointer to "struct task_struct". * - * @task: Pointer to "struct task_struct". - * - * Returns pointer to "struct tomoyo_security" for specified thread. + * Returns pointer to "struct tomoyo_task" for specified thread. */ -static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct - *task) +static inline struct tomoyo_task *tomoyo_task(struct task_struct *task) { - struct tomoyo_domain_info **blob = tomoyo_cred(get_task_cred(task)); - - return *blob; + return task->security + tomoyo_blob_sizes.lbs_task; } /** diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index b7469fdbff01..39abf3ae6168 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -678,7 +678,6 @@ out: */ int tomoyo_find_next_domain(struct linux_binprm *bprm) { - struct tomoyo_domain_info **blob; struct tomoyo_domain_info *old_domain = tomoyo_domain(); struct tomoyo_domain_info *domain = NULL; const char *original_name = bprm->filename; @@ -843,9 +842,13 @@ force_jump_domain: if (!domain) domain = old_domain; /* Update reference count on "struct tomoyo_domain_info". */ - atomic_inc(&domain->users); - blob = tomoyo_cred(bprm->cred); - *blob = domain; + { + struct tomoyo_task *s = tomoyo_task(current); + + s->old_domain_info = s->domain_info; + s->domain_info = domain; + atomic_inc(&domain->users); + } kfree(exename.name); if (!retval) { ee->r.domain = domain; diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 768dff9608b1..338872fa3d06 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -67,21 +67,14 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, if (!new_domain) { error = -ENOENT; } else { - struct cred *cred = prepare_creds(); - if (!cred) { - error = -ENOMEM; - } else { - struct tomoyo_domain_info **blob; - struct tomoyo_domain_info *old_domain; + struct tomoyo_task *s = tomoyo_task(current); + struct tomoyo_domain_info *old_domain = + s->domain_info; - blob = tomoyo_cred(cred); - old_domain = *blob; - *blob = new_domain; - atomic_inc(&new_domain->users); - atomic_dec(&old_domain->users); - commit_creds(cred); - error = 0; - } + s->domain_info = new_domain; + atomic_inc(&new_domain->users); + atomic_dec(&old_domain->users); + error = 0; } } tomoyo_read_unlock(idx); diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 2b3eee06004b..c2ab6575e68e 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -9,19 +9,19 @@ #include "common.h" /** - * tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank(). + * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread. * - * @new: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0. + * Returns pointer to "struct tomoyo_domain_info" for current thread. */ -static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp) +struct tomoyo_domain_info *tomoyo_domain(void) { - struct tomoyo_domain_info **blob = tomoyo_cred(new); + struct tomoyo_task *s = tomoyo_task(current); - *blob = NULL; - return 0; + if (s->old_domain_info && !current->in_execve) { + atomic_dec(&s->old_domain_info->users); + s->old_domain_info = NULL; + } + return s->domain_info; } /** @@ -36,85 +36,56 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp) static int tomoyo_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { - struct tomoyo_domain_info **old_blob = tomoyo_cred(old); - struct tomoyo_domain_info **new_blob = tomoyo_cred(new); - struct tomoyo_domain_info *domain; - - domain = *old_blob; - *new_blob = domain; + /* Restore old_domain_info saved by previous execve() request. */ + struct tomoyo_task *s = tomoyo_task(current); - if (domain) - atomic_inc(&domain->users); + if (s->old_domain_info && !current->in_execve) { + atomic_dec(&s->domain_info->users); + s->domain_info = s->old_domain_info; + s->old_domain_info = NULL; + } return 0; } /** - * tomoyo_cred_transfer - Target for security_transfer_creds(). + * tomoyo_bprm_committed_creds - Target for security_bprm_committed_creds(). * - * @new: Pointer to "struct cred". - * @old: Pointer to "struct cred". - */ -static void tomoyo_cred_transfer(struct cred *new, const struct cred *old) -{ - tomoyo_cred_prepare(new, old, 0); -} - -/** - * tomoyo_cred_free - Target for security_cred_free(). - * - * @cred: Pointer to "struct cred". + * @bprm: Pointer to "struct linux_binprm". */ -static void tomoyo_cred_free(struct cred *cred) +static void tomoyo_bprm_committed_creds(struct linux_binprm *bprm) { - struct tomoyo_domain_info **blob = tomoyo_cred(cred); - struct tomoyo_domain_info *domain = *blob; + /* Clear old_domain_info saved by execve() request. */ + struct tomoyo_task *s = tomoyo_task(current); - if (domain) - atomic_dec(&domain->users); + atomic_dec(&s->old_domain_info->users); + s->old_domain_info = NULL; } +#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER /** * tomoyo_bprm_set_creds - Target for security_bprm_set_creds(). * * @bprm: Pointer to "struct linux_binprm". * - * Returns 0 on success, negative value otherwise. + * Returns 0. */ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) { - struct tomoyo_domain_info **blob; - struct tomoyo_domain_info *domain; - /* * Do only if this function is called for the first time of an execve * operation. */ if (bprm->called_set_creds) return 0; -#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER /* * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested * for the first time. */ if (!tomoyo_policy_loaded) tomoyo_load_policy(bprm->filename); -#endif - /* - * Release reference to "struct tomoyo_domain_info" stored inside - * "bprm->cred->security". New reference to "struct tomoyo_domain_info" - * stored inside "bprm->cred->security" will be acquired later inside - * tomoyo_find_next_domain(). - */ - blob = tomoyo_cred(bprm->cred); - domain = *blob; - atomic_dec(&domain->users); - /* - * Tell tomoyo_bprm_check_security() is called for the first time of an - * execve operation. - */ - *blob = NULL; return 0; } +#endif /** * tomoyo_bprm_check_security - Target for security_bprm_check(). @@ -125,16 +96,13 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) */ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) { - struct tomoyo_domain_info **blob; - struct tomoyo_domain_info *domain; + struct tomoyo_task *s = tomoyo_task(current); - blob = tomoyo_cred(bprm->cred); - domain = *blob; /* * Execute permission is checked against pathname passed to do_execve() * using current domain. */ - if (!domain) { + if (!s->old_domain_info) { const int idx = tomoyo_read_lock(); const int err = tomoyo_find_next_domain(bprm); tomoyo_read_unlock(idx); @@ -143,8 +111,8 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) /* * Read permission is checked against interpreters using next domain. */ - return tomoyo_check_open_permission(domain, &bprm->file->f_path, - O_RDONLY); + return tomoyo_check_open_permission(s->domain_info, + &bprm->file->f_path, O_RDONLY); } /** @@ -510,19 +478,60 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg, } struct lsm_blob_sizes tomoyo_blob_sizes __lsm_ro_after_init = { - .lbs_cred = sizeof(struct tomoyo_domain_info *), + .lbs_task = sizeof(struct tomoyo_task), }; +/** + * tomoyo_task_alloc - Target for security_task_alloc(). + * + * @task: Pointer to "struct task_struct". + * @flags: clone() flags. + * + * Returns 0. + */ +static int tomoyo_task_alloc(struct task_struct *task, + unsigned long clone_flags) +{ + struct tomoyo_task *old = tomoyo_task(current); + struct tomoyo_task *new = tomoyo_task(task); + + new->domain_info = old->domain_info; + atomic_inc(&new->domain_info->users); + new->old_domain_info = NULL; + return 0; +} + +/** + * tomoyo_task_free - Target for security_task_free(). + * + * @task: Pointer to "struct task_struct". + */ +static void tomoyo_task_free(struct task_struct *task) +{ + struct tomoyo_task *s = tomoyo_task(task); + + if (s->domain_info) { + atomic_dec(&s->domain_info->users); + s->domain_info = NULL; + } + if (s->old_domain_info) { + atomic_dec(&s->old_domain_info->users); + s->old_domain_info = NULL; + } +} + /* * tomoyo_security_ops is a "struct security_operations" which is used for * registering TOMOYO. */ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = { - LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank), LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare), - LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer), - LSM_HOOK_INIT(cred_free, tomoyo_cred_free), + LSM_HOOK_INIT(bprm_committed_creds, tomoyo_bprm_committed_creds), + LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc), + LSM_HOOK_INIT(task_free, tomoyo_task_free), +#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER LSM_HOOK_INIT(bprm_set_creds, tomoyo_bprm_set_creds), +#endif LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security), LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl), LSM_HOOK_INIT(file_open, tomoyo_file_open), @@ -560,14 +569,14 @@ int tomoyo_enabled __lsm_ro_after_init = 1; */ static int __init tomoyo_init(void) { - struct cred *cred = (struct cred *) current_cred(); - struct tomoyo_domain_info **blob; + struct tomoyo_task *s = tomoyo_task(current); /* register ourselves with the security framework */ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); printk(KERN_INFO "TOMOYO Linux initialized\n"); - blob = tomoyo_cred(cred); - *blob = &tomoyo_kernel_domain; + s->domain_info = &tomoyo_kernel_domain; + atomic_inc(&tomoyo_kernel_domain.users); + s->old_domain_info = NULL; tomoyo_mm_init(); return 0; -- cgit v1.2.3-59-g8ed1b From cdcf6723add57a0ffb37cfde1ca54a00f5715b71 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 24 Jan 2019 18:37:35 +0900 Subject: tomoyo: Coding style fix. Follow many of recommendations by scripts/checkpatch.pl, and follow "lift switch variables out of switches" by Kees Cook. This patch makes no functional change. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/audit.c | 31 +++++---- security/tomoyo/common.c | 138 ++++++++++++++++++++++++++++------------ security/tomoyo/common.h | 13 ++-- security/tomoyo/condition.c | 59 ++++++++++++----- security/tomoyo/domain.c | 50 ++++++++++----- security/tomoyo/file.c | 20 ++++++ security/tomoyo/gc.c | 19 ++++++ security/tomoyo/group.c | 5 ++ security/tomoyo/load_policy.c | 8 +-- security/tomoyo/memory.c | 9 ++- security/tomoyo/mount.c | 2 + security/tomoyo/realpath.c | 18 +++++- security/tomoyo/securityfs_if.c | 4 ++ security/tomoyo/tomoyo.c | 15 ++++- security/tomoyo/util.c | 23 +++++-- 15 files changed, 309 insertions(+), 105 deletions(-) (limited to 'security') diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 479b03a7a17e..3c96e8402e94 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -32,6 +32,7 @@ static char *tomoyo_print_bprm(struct linux_binprm *bprm, int argv_count = bprm->argc; int envp_count = bprm->envc; bool truncated = false; + if (!buffer) return NULL; len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ "); @@ -49,6 +50,7 @@ static char *tomoyo_print_bprm(struct linux_binprm *bprm, while (offset < PAGE_SIZE) { const char *kaddr = dump->data; const unsigned char c = kaddr[offset++]; + if (cp == last_start) *cp++ = '"'; if (cp >= buffer + tomoyo_buffer_len - 32) { @@ -154,19 +156,18 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); int pos; u8 i; + if (!buffer) return NULL; tomoyo_convert_time(ktime_get_real_seconds(), &stamp); pos = snprintf(buffer, tomoyo_buffer_len - 1, - "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s " - "granted=%s (global-pid=%u) task={ pid=%u ppid=%u " - "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u " - "fsuid=%u fsgid=%u }", stamp.year, stamp.month, - stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile, - tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid, - tomoyo_sys_getpid(), tomoyo_sys_getppid(), + "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s granted=%s (global-pid=%u) task={ pid=%u ppid=%u uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", + stamp.year, stamp.month, stamp.day, stamp.hour, + stamp.min, stamp.sec, r->profile, tomoyo_mode[r->mode], + tomoyo_yesno(r->granted), gpid, tomoyo_sys_getpid(), + tomoyo_sys_getppid(), from_kuid(&init_user_ns, current_uid()), from_kgid(&init_user_ns, current_gid()), from_kuid(&init_user_ns, current_euid()), @@ -185,6 +186,7 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) struct tomoyo_mini_stat *stat; unsigned int dev; umode_t mode; + if (!obj->stat_valid[i]) continue; stat = &obj->stat[i]; @@ -193,8 +195,8 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) if (i & 1) { pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, - " path%u.parent={ uid=%u gid=%u " - "ino=%lu perm=0%o }", (i >> 1) + 1, + " path%u.parent={ uid=%u gid=%u ino=%lu perm=0%o }", + (i >> 1) + 1, from_kuid(&init_user_ns, stat->uid), from_kgid(&init_user_ns, stat->gid), (unsigned long)stat->ino, @@ -202,8 +204,8 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) continue; } pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, - " path%u={ uid=%u gid=%u ino=%lu major=%u" - " minor=%u perm=0%o type=%s", (i >> 1) + 1, + " path%u={ uid=%u gid=%u ino=%lu major=%u minor=%u perm=0%o type=%s", + (i >> 1) + 1, from_kuid(&init_user_ns, stat->uid), from_kgid(&init_user_ns, stat->gid), (unsigned long)stat->ino, @@ -249,6 +251,7 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, const char *symlink = NULL; int pos; const char *domainname = r->domain->domainname->name; + header = tomoyo_print_header(r); if (!header) return NULL; @@ -256,6 +259,7 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, len += strlen(domainname) + strlen(header) + 10; if (r->ee) { struct file *file = r->ee->bprm->file; + realpath = tomoyo_realpath_from_path(&file->f_path); bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump); if (!realpath || !bprm_info) @@ -275,6 +279,7 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, pos = snprintf(buf, len, "%s", header); if (realpath) { struct linux_binprm *bprm = r->ee->bprm; + pos += snprintf(buf + pos, len - pos, " exec={ realpath=\"%s\" argc=%d envc=%d %s }", realpath, bprm->argc, bprm->envc, bprm_info); @@ -328,6 +333,7 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, const u8 category = tomoyo_index2category[index] + TOMOYO_MAX_MAC_INDEX; struct tomoyo_profile *p; + if (!tomoyo_policy_loaded) return false; p = tomoyo_profile(ns, profile); @@ -362,6 +368,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, char *buf; struct tomoyo_log *entry; bool quota_exceeded = false; + if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->matched_acl, r->granted)) goto out; @@ -413,6 +420,7 @@ void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) { va_list args; int len; + va_start(args, fmt); len = vsnprintf((char *) &len, 1, fmt, args) + 1; va_end(args); @@ -431,6 +439,7 @@ void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) void tomoyo_read_log(struct tomoyo_io_buffer *head) { struct tomoyo_log *ptr = NULL; + if (head->r.w_pos) return; kfree(head->read_buf); diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 6936def78002..0f8079b65e23 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -197,6 +197,7 @@ static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) { va_list args; const int pos = strlen(buffer); + va_start(args, fmt); vsnprintf(buffer + pos, len - pos - 1, fmt, args); va_end(args); @@ -214,6 +215,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head) while (head->r.w_pos) { const char *w = head->r.w[0]; size_t len = strlen(w); + if (len) { if (len > head->read_user_buf_avail) len = head->read_user_buf_avail; @@ -279,6 +281,7 @@ static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, size_t len; size_t pos = head->r.avail; int size = head->readbuf_size - pos; + if (size <= 0) return; va_start(args, fmt); @@ -344,6 +347,7 @@ static bool tomoyo_namespace_enabled; void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) { unsigned int idx; + for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++) INIT_LIST_HEAD(&ns->acl_group[idx]); for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) @@ -433,6 +437,7 @@ static void tomoyo_print_number_union_nospace u8 min_type = ptr->value_type[0]; const u8 max_type = ptr->value_type[1]; char buffer[128]; + buffer[0] = '\0'; for (i = 0; i < 2; i++) { switch (min_type) { @@ -487,6 +492,7 @@ static struct tomoyo_profile *tomoyo_assign_profile { struct tomoyo_profile *ptr; struct tomoyo_profile *entry; + if (profile >= TOMOYO_MAX_PROFILES) return NULL; ptr = ns->profile_ptr[profile]; @@ -530,6 +536,7 @@ struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, { static struct tomoyo_profile tomoyo_null_profile; struct tomoyo_profile *ptr = ns->profile_ptr[profile]; + if (!ptr) ptr = &tomoyo_null_profile; return ptr; @@ -546,6 +553,7 @@ struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, static s8 tomoyo_find_yesno(const char *string, const char *find) { const char *cp = strstr(string, find); + if (cp) { cp += strlen(find); if (!strncmp(cp, "=yes", 4)) @@ -569,6 +577,7 @@ static void tomoyo_set_uint(unsigned int *i, const char *string, const char *find) { const char *cp = strstr(string, find); + if (cp) sscanf(cp + strlen(find), "=%u", i); } @@ -587,6 +596,7 @@ static int tomoyo_set_mode(char *name, const char *value, { u8 i; u8 config; + if (!strcmp(name, "CONFIG")) { i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; config = profile->default_config; @@ -595,10 +605,12 @@ static int tomoyo_set_mode(char *name, const char *value, for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { int len = 0; + if (i < TOMOYO_MAX_MAC_INDEX) { const u8 c = tomoyo_index2category[i]; const char *category = tomoyo_category_keywords[c]; + len = strlen(category); if (strncmp(name, category, len) || name[len++] != ':' || name[len++] != ':') @@ -618,6 +630,7 @@ static int tomoyo_set_mode(char *name, const char *value, config = TOMOYO_CONFIG_USE_DEFAULT; } else { u8 mode; + for (mode = 0; mode < 4; mode++) if (strstr(value, tomoyo_mode[mode])) /* @@ -664,6 +677,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) unsigned int i; char *cp; struct tomoyo_profile *profile; + if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version) == 1) return 0; @@ -683,6 +697,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) const struct tomoyo_path_info *new_comment = tomoyo_get_name(cp); const struct tomoyo_path_info *old_comment; + if (!new_comment) return -ENOMEM; spin_lock(&lock); @@ -732,6 +747,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) struct tomoyo_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), namespace_list); const struct tomoyo_profile *profile; + if (head->r.eof) return; next: @@ -760,6 +776,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) u8 i; const struct tomoyo_path_info *comment = profile->comment; + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-COMMENT=", index); tomoyo_set_string(head, comment ? comment->name : ""); @@ -788,6 +805,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) + TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) { const u8 i = head->r.bit; const u8 config = profile->config[i]; + if (config == TOMOYO_CONFIG_USE_DEFAULT) continue; tomoyo_print_namespace(head); @@ -847,10 +865,10 @@ static int tomoyo_update_manager_entry(const char *manager, struct tomoyo_acl_param param = { /* .ns = &tomoyo_kernel_namespace, */ .is_delete = is_delete, - .list = &tomoyo_kernel_namespace. - policy_list[TOMOYO_ID_MANAGER], + .list = &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], }; int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_correct_domain(manager) && !tomoyo_correct_word(manager)) return -EINVAL; @@ -894,10 +912,10 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head) { if (head->r.eof) return; - list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace. - policy_list[TOMOYO_ID_MANAGER]) { + list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER]) { struct tomoyo_manager *ptr = list_entry(head->r.acl, typeof(*ptr), head.list); + if (ptr->head.is_deleted) continue; if (!tomoyo_flush(head)) @@ -933,8 +951,7 @@ static bool tomoyo_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. - policy_list[TOMOYO_ID_MANAGER], head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list) { if (!ptr->head.is_deleted && (!tomoyo_pathcmp(domainname, ptr->manager) || !strcmp(exe, ptr->manager->name))) { @@ -945,9 +962,10 @@ static bool tomoyo_manager(void) if (!found) { /* Reduce error messages. */ static pid_t last_pid; const pid_t pid = current->pid; + if (last_pid != pid) { - printk(KERN_WARNING "%s ( %s ) is not permitted to " - "update policies.\n", domainname->name, exe); + pr_warn("%s ( %s ) is not permitted to update policies.\n", + domainname->name, exe); last_pid = pid; } } @@ -974,12 +992,14 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, unsigned int pid; struct tomoyo_domain_info *domain = NULL; bool global_pid = false; + if (strncmp(data, "select ", 7)) return false; data += 7; if (sscanf(data, "pid=%u", &pid) == 1 || (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; + rcu_read_lock(); if (global_pid) p = find_task_by_pid_ns(pid, &init_pid_ns); @@ -1020,10 +1040,11 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, * Returns true if @a == @b, false otherwise. */ static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, - const struct tomoyo_acl_info *b) + const struct tomoyo_acl_info *b) { const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head); + return p1->domainname == p2->domainname; } @@ -1039,11 +1060,13 @@ static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, static int tomoyo_write_task(struct tomoyo_acl_param *param) { int error = -EINVAL; + if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) { struct tomoyo_task_acl e = { .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL, .domainname = tomoyo_get_domainname(param), }; + if (e.domainname) error = tomoyo_update_domain(&e.head, sizeof(e), param, tomoyo_same_task_acl, @@ -1110,7 +1133,7 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, }; static const struct { const char *keyword; - int (*write) (struct tomoyo_acl_param *); + int (*write)(struct tomoyo_acl_param *param); } tomoyo_callback[5] = { { "file ", tomoyo_write_file }, { "network inet ", tomoyo_write_inet_network }, @@ -1152,8 +1175,10 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) const bool is_delete = head->w.is_delete; bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); unsigned int profile; + if (*data == '<') { int ret = 0; + domain = NULL; if (is_delete) ret = tomoyo_delete_domain(data); @@ -1181,6 +1206,7 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) } for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) { const char *cp = tomoyo_dif[profile]; + if (strncmp(data, cp, strlen(cp) - 1)) continue; domain->flags[profile] = !is_delete; @@ -1225,9 +1251,11 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, const struct tomoyo_envp *envp = (typeof(envp)) (argv + cond->argc); u16 skip; + for (skip = 0; skip < head->r.cond_index; skip++) { const u8 left = condp->left; const u8 right = condp->right; + condp++; switch (left) { case TOMOYO_ARGV_ENTRY: @@ -1253,6 +1281,7 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, const u8 match = condp->equals; const u8 left = condp->left; const u8 right = condp->right; + if (!tomoyo_flush(head)) return false; condp++; @@ -1262,8 +1291,7 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, case TOMOYO_ARGV_ENTRY: tomoyo_io_printf(head, "exec.argv[%lu]%s=\"", - argv->index, argv-> - is_not ? "!" : ""); + argv->index, argv->is_not ? "!" : ""); tomoyo_set_string(head, argv->value->name); tomoyo_set_string(head, "\""); @@ -1274,12 +1302,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, "exec.envp[\""); tomoyo_set_string(head, envp->name->name); - tomoyo_io_printf(head, "\"]%s=", envp-> - is_not ? "!" : ""); + tomoyo_io_printf(head, "\"]%s=", envp->is_not ? "!" : ""); if (envp->value) { tomoyo_set_string(head, "\""); - tomoyo_set_string(head, envp-> - value->name); + tomoyo_set_string(head, envp->value->name); tomoyo_set_string(head, "\""); } else { tomoyo_set_string(head, @@ -1375,6 +1401,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_path_acl *ptr = container_of(acl, typeof(*ptr), head); const u16 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1395,6 +1422,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) { struct tomoyo_task_acl *ptr = container_of(acl, typeof(*ptr), head); + tomoyo_set_group(head, "task "); tomoyo_set_string(head, "manual_domain_transition "); tomoyo_set_string(head, ptr->domainname->name); @@ -1404,6 +1432,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_path2_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1424,6 +1453,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_path_number_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1444,6 +1474,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_mkdev_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1490,6 +1521,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, ->name); } else { char buf[128]; + tomoyo_print_ip(buf, sizeof(buf), &ptr->address); tomoyo_io_printf(head, "%s", buf); } @@ -1519,6 +1551,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { struct tomoyo_mount_acl *ptr = container_of(acl, typeof(*ptr), head); + tomoyo_set_group(head, "file mount"); tomoyo_print_name_union(head, &ptr->dev_name); tomoyo_print_name_union(head, &ptr->dir_name); @@ -1562,6 +1595,7 @@ static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, list_for_each_cookie(head->r.acl, list) { struct tomoyo_acl_info *ptr = list_entry(head->r.acl, typeof(*ptr), list); + if (!tomoyo_print_entry(head, ptr)) return false; } @@ -1583,8 +1617,9 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { struct tomoyo_domain_info *domain = list_entry(head->r.domain, typeof(*domain), list); + u8 i; + switch (head->r.step) { - u8 i; case 0: if (domain->is_deleted && !head->r.print_this_domain_only) @@ -1711,6 +1746,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) .data = head->write_buf, }; u8 i; + if (tomoyo_str_starts(¶m.data, "aggregator ")) return tomoyo_write_aggregator(¶m); for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) @@ -1722,6 +1758,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) if (tomoyo_str_starts(¶m.data, "acl_group ")) { unsigned int group; char *data; + group = simple_strtoul(param.data, &data, 10); if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ') return tomoyo_write_domain2 @@ -1746,12 +1783,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) struct tomoyo_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), namespace_list); struct list_head *list = &ns->group_list[idx]; + list_for_each_cookie(head->r.group, list) { struct tomoyo_group *group = list_entry(head->r.group, typeof(*group), head.list); + list_for_each_cookie(head->r.acl, &group->member_list) { struct tomoyo_acl_head *ptr = list_entry(head->r.acl, typeof(*ptr), list); + if (ptr->is_deleted) continue; if (!tomoyo_flush(head)) @@ -1771,10 +1811,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) head)->number); } else if (idx == TOMOYO_ADDRESS_GROUP) { char buffer[128]; - struct tomoyo_address_group *member = container_of(ptr, typeof(*member), head); + tomoyo_print_ip(buffer, sizeof(buffer), &member->address); tomoyo_io_printf(head, " %s", buffer); @@ -1802,6 +1842,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) struct tomoyo_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), namespace_list); struct list_head *list = &ns->policy_list[idx]; + list_for_each_cookie(head->r.acl, list) { struct tomoyo_acl_head *acl = container_of(head->r.acl, typeof(*acl), list); @@ -1814,6 +1855,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { struct tomoyo_transition_control *ptr = container_of(acl, typeof(*ptr), head); + tomoyo_print_namespace(head); tomoyo_set_string(head, tomoyo_transition_type [ptr->type]); @@ -1829,6 +1871,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { struct tomoyo_aggregator *ptr = container_of(acl, typeof(*ptr), head); + tomoyo_print_namespace(head); tomoyo_set_string(head, "aggregator "); tomoyo_set_string(head, @@ -1858,6 +1901,7 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head) { struct tomoyo_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), namespace_list); + if (head->r.eof) return; while (head->r.step < TOMOYO_MAX_POLICY && @@ -1921,6 +1965,7 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); static int tomoyo_truncate(char *str) { char *start = str; + while (*(unsigned char *) str > (unsigned char) ' ') str++; *str = '\0'; @@ -1943,6 +1988,7 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) char *symlink = NULL; char *cp = strchr(header, '\n'); int len; + if (!cp) return; cp = strchr(cp + 1, '\n'); @@ -2002,6 +2048,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) static unsigned int tomoyo_serial; struct tomoyo_query entry = { }; bool quota_exceeded = false; + va_start(args, fmt); len = vsnprintf((char *) &len, 1, fmt, args) + 1; va_end(args); @@ -2063,8 +2110,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) (tomoyo_answer_wait, entry.answer || !atomic_read(&tomoyo_query_observers), HZ)) break; - else - entry.timer++; + entry.timer++; } spin_lock(&tomoyo_query_list_lock); list_del(&entry.list); @@ -2100,6 +2146,7 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid { struct tomoyo_query *ptr; struct tomoyo_domain_info *domain = NULL; + spin_lock(&tomoyo_query_list_lock); list_for_each_entry(ptr, &tomoyo_query_list, list) { if (ptr->serial != serial) @@ -2142,15 +2189,15 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) unsigned int pos = 0; size_t len = 0; char *buf; + if (head->r.w_pos) return; - if (head->read_buf) { - kfree(head->read_buf); - head->read_buf = NULL; - } + kfree(head->read_buf); + head->read_buf = NULL; spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (pos++ != head->r.query_index) continue; len = ptr->query_len; @@ -2168,6 +2215,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (pos++ != head->r.query_index) continue; /* @@ -2202,9 +2250,11 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) struct list_head *tmp; unsigned int serial; unsigned int answer; + spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + ptr->timer = 0; } spin_unlock(&tomoyo_query_list_lock); @@ -2213,6 +2263,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (ptr->serial != serial) continue; ptr->answer = answer; @@ -2287,6 +2338,7 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head) { u8 i; unsigned int total = 0; + if (head->r.eof) return; for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) { @@ -2295,9 +2347,9 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head) tomoyo_stat_updated[i]); if (tomoyo_stat_modified[i]) { struct tomoyo_time stamp; + tomoyo_convert_time(tomoyo_stat_modified[i], &stamp); - tomoyo_io_printf(head, " (Last: %04u/%02u/%02u " - "%02u:%02u:%02u)", + tomoyo_io_printf(head, " (Last: %04u/%02u/%02u %02u:%02u:%02u)", stamp.year, stamp.month, stamp.day, stamp.hour, stamp.min, stamp.sec); } @@ -2305,6 +2357,7 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head) } for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) { unsigned int used = tomoyo_memory_used[i]; + total += used; tomoyo_io_printf(head, "Memory used by %-22s %10u", tomoyo_memory_headers[i], used); @@ -2329,6 +2382,7 @@ static int tomoyo_write_stat(struct tomoyo_io_buffer *head) { char *data = head->write_buf; u8 i; + if (tomoyo_str_starts(&data, "Memory used by ")) for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) if (tomoyo_str_starts(&data, tomoyo_memory_headers[i])) @@ -2457,6 +2511,7 @@ int tomoyo_open_control(const u8 type, struct file *file) __poll_t tomoyo_poll_control(struct file *file, poll_table *wait) { struct tomoyo_io_buffer *head = file->private_data; + if (head->poll) return head->poll(file, wait) | EPOLLOUT | EPOLLWRNORM; return EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM; @@ -2472,6 +2527,7 @@ __poll_t tomoyo_poll_control(struct file *file, poll_table *wait) static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head) { struct list_head *ns; + if (head->type != TOMOYO_EXCEPTIONPOLICY && head->type != TOMOYO_PROFILE) return; @@ -2517,7 +2573,7 @@ ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, int idx; if (!head->read) - return -ENOSYS; + return -EINVAL; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; head->read_user_buf = buffer; @@ -2557,6 +2613,7 @@ static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) head->type == TOMOYO_PROFILE) { if (*line == '<') { char *cp = strchr(line, ' '); + if (cp) { *cp++ = '\0'; head->w.ns = tomoyo_assign_namespace(line); @@ -2589,8 +2646,9 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, size_t avail_len = buffer_len; char *cp0 = head->write_buf; int idx; + if (!head->write) - return -ENOSYS; + return -EINVAL; if (!access_ok(buffer, buffer_len)) return -EFAULT; if (mutex_lock_interruptible(&head->io_sem)) @@ -2600,9 +2658,11 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, /* Read a line and dispatch it to the policy handler. */ while (avail_len > 0) { char c; + if (head->w.avail >= head->writebuf_size - 1) { const int len = head->writebuf_size * 2; char *cp = kzalloc(len, GFP_NOFS); + if (!cp) { error = -ENOMEM; break; @@ -2701,30 +2761,27 @@ void tomoyo_check_profile(void) { struct tomoyo_domain_info *domain; const int idx = tomoyo_read_lock(); + tomoyo_policy_loaded = true; - printk(KERN_INFO "TOMOYO: 2.5.0\n"); + pr_info("TOMOYO: 2.5.0\n"); list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { const u8 profile = domain->profile; const struct tomoyo_policy_namespace *ns = domain->ns; + if (ns->profile_version != 20110903) - printk(KERN_ERR - "Profile version %u is not supported.\n", + pr_err("Profile version %u is not supported.\n", ns->profile_version); else if (!ns->profile_ptr[profile]) - printk(KERN_ERR - "Profile %u (used by '%s') is not defined.\n", + pr_err("Profile %u (used by '%s') is not defined.\n", profile, domain->domainname->name); else continue; - printk(KERN_ERR - "Userland tools for TOMOYO 2.5 must be installed and " - "policy must be initialized.\n"); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ " - "for more information.\n"); + pr_err("Userland tools for TOMOYO 2.5 must be installed and policy must be initialized.\n"); + pr_err("Please see http://tomoyo.sourceforge.jp/2.5/ for more information.\n"); panic("STOP!"); } tomoyo_read_unlock(idx); - printk(KERN_INFO "Mandatory Access Control activated.\n"); + pr_info("Mandatory Access Control activated.\n"); } /** @@ -2743,9 +2800,11 @@ void __init tomoyo_load_builtin_policy(void) #include "builtin-policy.h" u8 i; const int idx = tomoyo_read_lock(); + for (i = 0; i < 5; i++) { struct tomoyo_io_buffer head = { }; char *start = ""; + switch (i) { case 0: start = tomoyo_builtin_profile; @@ -2775,6 +2834,7 @@ void __init tomoyo_load_builtin_policy(void) } while (1) { char *end = strchr(start, '\n'); + if (!end) break; *end = '\0'; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index cfd075c92583..f2c458ab9942 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -10,6 +10,8 @@ #ifndef _SECURITY_TOMOYO_COMMON_H #define _SECURITY_TOMOYO_COMMON_H +#define pr_fmt(fmt) fmt + #include #include #include @@ -788,9 +790,9 @@ struct tomoyo_acl_param { * interfaces. */ struct tomoyo_io_buffer { - void (*read) (struct tomoyo_io_buffer *); - int (*write) (struct tomoyo_io_buffer *); - __poll_t (*poll) (struct file *file, poll_table *wait); + void (*read)(struct tomoyo_io_buffer *head); + int (*write)(struct tomoyo_io_buffer *head); + __poll_t (*poll)(struct file *file, poll_table *wait); /* Exclusive lock for this structure. */ struct mutex io_sem; char __user *read_user_buf; @@ -1042,8 +1044,8 @@ void *tomoyo_commit_ok(void *data, const unsigned int size); void __init tomoyo_load_builtin_policy(void); void __init tomoyo_mm_init(void); void tomoyo_check_acl(struct tomoyo_request_info *r, - bool (*check_entry) (struct tomoyo_request_info *, - const struct tomoyo_acl_info *)); + bool (*check_entry)(struct tomoyo_request_info *, + const struct tomoyo_acl_info *)); void tomoyo_check_profile(void); void tomoyo_convert_time(time64_t time, struct tomoyo_time *stamp); void tomoyo_del_condition(struct list_head *element); @@ -1131,6 +1133,7 @@ static inline void tomoyo_read_unlock(int idx) static inline pid_t tomoyo_sys_getppid(void) { pid_t pid; + rcu_read_lock(); pid = task_tgid_vnr(rcu_dereference(current->real_parent)); rcu_read_unlock(); diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 8d0e1b9c9c57..8f6d57c15df6 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -28,9 +28,11 @@ static bool tomoyo_argv(const unsigned int index, const char *arg_ptr, { int i; struct tomoyo_path_info arg; + arg.name = arg_ptr; for (i = 0; i < argc; argv++, checked++, i++) { bool result; + if (index != argv->index) continue; *checked = 1; @@ -62,12 +64,14 @@ static bool tomoyo_envp(const char *env_name, const char *env_value, int i; struct tomoyo_path_info name; struct tomoyo_path_info value; + name.name = env_name; tomoyo_fill_path_info(&name); value.name = env_value; tomoyo_fill_path_info(&value); for (i = 0; i < envc; envp++, checked++, i++) { bool result; + if (!tomoyo_path_matches_pattern(&name, envp->name)) continue; *checked = 1; @@ -113,6 +117,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee, bool result = true; u8 local_checked[32]; u8 *checked; + if (argc + envc <= sizeof(local_checked)) { checked = local_checked; memset(local_checked, 0, sizeof(local_checked)); @@ -131,6 +136,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee, /* Read. */ const char *kaddr = dump->data; const unsigned char c = kaddr[offset++]; + if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) { if (c == '\\') { arg_ptr[arg_len++] = '\\'; @@ -160,6 +166,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee, argv_count--; } else if (envp_count) { char *cp = strchr(arg_ptr, '='); + if (cp) { *cp = '\0'; if (!tomoyo_envp(arg_ptr, cp + 1, @@ -182,6 +189,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee, out: if (result) { int i; + /* Check not-yet-checked entries. */ for (i = 0; i < argc; i++) { if (checked[i]) @@ -229,6 +237,7 @@ static bool tomoyo_scan_exec_realpath(struct file *file, { bool result; struct tomoyo_path_info exe; + if (!file) return false; exe.name = tomoyo_realpath_from_path(&file->f_path); @@ -250,6 +259,7 @@ static bool tomoyo_scan_exec_realpath(struct file *file, static const struct tomoyo_path_info *tomoyo_get_dqword(char *start) { char *cp = start + strlen(start) - 1; + if (cp == start || *start++ != '"' || *cp != '"') return NULL; *cp = '\0'; @@ -270,6 +280,7 @@ static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param, struct tomoyo_name_union *ptr) { char *filename = param->data; + if (*filename == '@') return tomoyo_parse_name_union(param, ptr); ptr->filename = tomoyo_get_dqword(filename); @@ -310,6 +321,7 @@ static bool tomoyo_parse_envp(char *left, char *right, const struct tomoyo_path_info *name; const struct tomoyo_path_info *value; char *cp = left + strlen(left) - 1; + if (*cp-- != ']' || *cp != '"') goto out; *cp = '\0'; @@ -364,6 +376,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, static u8 tomoyo_condition_type(const char *word) { u8 i; + for (i = 0; i < TOMOYO_MAX_CONDITION_KEYWORD; i++) { if (!strcmp(word, tomoyo_condition_keyword[i])) break; @@ -395,6 +408,7 @@ static struct tomoyo_condition *tomoyo_commit_condition { struct tomoyo_condition *ptr; bool found = false; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) { dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); ptr = NULL; @@ -442,12 +456,14 @@ static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param, { char * const pos = param->data; bool flag; + if (*pos == '<') { e->transit = tomoyo_get_domainname(param); goto done; } { char *cp = strchr(pos, ' '); + if (cp) *cp = '\0'; flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") || @@ -489,6 +505,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) tomoyo_get_transit_preference(param, &e); char * const end_of_string = start_of_string + strlen(start_of_string); char *pos; + rerun: pos = start_of_string; while (1) { @@ -498,6 +515,7 @@ rerun: char *cp; char *right_word; bool is_not; + if (!*left_word) break; /* @@ -622,8 +640,8 @@ rerun: } store_value: if (!condp) { - dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " - "match=%u\n", __LINE__, left, right, !is_not); + dprintk(KERN_WARNING "%u: dry_run left=%u right=%u match=%u\n", + __LINE__, left, right, !is_not); continue; } condp->left = left; @@ -660,6 +678,7 @@ store_value: envp = (struct tomoyo_envp *) (argv + e.argc); { bool flag = false; + for (pos = start_of_string; pos < end_of_string; pos++) { if (*pos) continue; @@ -698,6 +717,7 @@ void tomoyo_get_attributes(struct tomoyo_obj_info *obj) for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) { struct inode *inode; + switch (i) { case TOMOYO_PATH1: dentry = obj->path1.dentry; @@ -718,6 +738,7 @@ void tomoyo_get_attributes(struct tomoyo_obj_info *obj) inode = d_backing_inode(dentry); if (inode) { struct tomoyo_mini_stat *stat = &obj->stat[i]; + stat->uid = inode->i_uid; stat->gid = inode->i_gid; stat->ino = inode->i_ino; @@ -726,8 +747,7 @@ void tomoyo_get_attributes(struct tomoyo_obj_info *obj) stat->rdev = inode->i_rdev; obj->stat_valid[i] = true; } - if (i & 1) /* i == TOMOYO_PATH1_PARENT || - i == TOMOYO_PATH2_PARENT */ + if (i & 1) /* TOMOYO_PATH1_PARENT or TOMOYO_PATH2_PARENT */ dput(dentry); } } @@ -758,6 +778,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r, u16 argc; u16 envc; struct linux_binprm *bprm = NULL; + if (!cond) return true; condc = cond->condc; @@ -780,6 +801,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r, const u8 right = condp->right; bool is_bitop[2] = { false, false }; u8 j; + condp++; /* Check argv[] and envp[] later. */ if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY) @@ -787,10 +809,11 @@ bool tomoyo_condition(struct tomoyo_request_info *r, /* Check string expressions. */ if (right == TOMOYO_NAME_UNION) { const struct tomoyo_name_union *ptr = names_p++; + struct tomoyo_path_info *symlink; + struct tomoyo_execve *ee; + struct file *file; + switch (left) { - struct tomoyo_path_info *symlink; - struct tomoyo_execve *ee; - struct file *file; case TOMOYO_SYMLINK_TARGET: symlink = obj ? obj->symlink_target : NULL; if (!symlink || @@ -812,6 +835,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r, for (j = 0; j < 2; j++) { const u8 index = j ? right : left; unsigned long value = 0; + switch (index) { case TOMOYO_TASK_UID: value = from_kuid(&init_user_ns, current_uid()); @@ -874,31 +898,31 @@ bool tomoyo_condition(struct tomoyo_request_info *r, value = S_ISVTX; break; case TOMOYO_MODE_OWNER_READ: - value = S_IRUSR; + value = 0400; break; case TOMOYO_MODE_OWNER_WRITE: - value = S_IWUSR; + value = 0200; break; case TOMOYO_MODE_OWNER_EXECUTE: - value = S_IXUSR; + value = 0100; break; case TOMOYO_MODE_GROUP_READ: - value = S_IRGRP; + value = 0040; break; case TOMOYO_MODE_GROUP_WRITE: - value = S_IWGRP; + value = 0020; break; case TOMOYO_MODE_GROUP_EXECUTE: - value = S_IXGRP; + value = 0010; break; case TOMOYO_MODE_OTHERS_READ: - value = S_IROTH; + value = 0004; break; case TOMOYO_MODE_OTHERS_WRITE: - value = S_IWOTH; + value = 0002; break; case TOMOYO_MODE_OTHERS_EXECUTE: - value = S_IXOTH; + value = 0001; break; case TOMOYO_EXEC_ARGC: if (!bprm) @@ -923,6 +947,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r, { u8 stat_index; struct tomoyo_mini_stat *stat; + switch (index) { case TOMOYO_PATH1_UID: case TOMOYO_PATH1_GID: @@ -1036,12 +1061,14 @@ bool tomoyo_condition(struct tomoyo_request_info *r, if (left == TOMOYO_NUMBER_UNION) { /* Fetch values now. */ const struct tomoyo_number_union *ptr = numbers_p++; + min_v[0] = ptr->values[0]; max_v[0] = ptr->values[1]; } if (right == TOMOYO_NUMBER_UNION) { /* Fetch values now. */ const struct tomoyo_number_union *ptr = numbers_p++; + if (ptr->group) { if (tomoyo_number_matches_group(min_v[0], max_v[0], diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 39abf3ae6168..bf832b301412 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -30,10 +30,10 @@ struct tomoyo_domain_info tomoyo_kernel_domain; */ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, struct tomoyo_acl_param *param, - bool (*check_duplicate) (const struct tomoyo_acl_head - *, - const struct tomoyo_acl_head - *)) + bool (*check_duplicate)(const struct tomoyo_acl_head + *, + const struct tomoyo_acl_head + *)) { int error = param->is_delete ? -ENOENT : -ENOMEM; struct tomoyo_acl_head *entry; @@ -90,13 +90,13 @@ static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a, */ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, struct tomoyo_acl_param *param, - bool (*check_duplicate) (const struct tomoyo_acl_info - *, - const struct tomoyo_acl_info - *), - bool (*merge_duplicate) (struct tomoyo_acl_info *, - struct tomoyo_acl_info *, - const bool)) + bool (*check_duplicate)(const struct tomoyo_acl_info + *, + const struct tomoyo_acl_info + *), + bool (*merge_duplicate)(struct tomoyo_acl_info *, + struct tomoyo_acl_info *, + const bool)) { const bool is_delete = param->is_delete; int error = is_delete ? -ENOENT : -ENOMEM; @@ -157,8 +157,8 @@ out: * Caller holds tomoyo_read_lock(). */ void tomoyo_check_acl(struct tomoyo_request_info *r, - bool (*check_entry) (struct tomoyo_request_info *, - const struct tomoyo_acl_info *)) + bool (*check_entry)(struct tomoyo_request_info *, + const struct tomoyo_acl_info *)) { const struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; @@ -198,6 +198,7 @@ LIST_HEAD(tomoyo_domain_list); static const char *tomoyo_last_word(const char *name) { const char *cp = strrchr(name, ' '); + if (cp) return cp + 1; return name; @@ -220,6 +221,7 @@ static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, const struct tomoyo_transition_control *p2 = container_of(b, typeof(*p2), head); + return p1->type == p2->type && p1->is_last_name == p2->is_last_name && p1->domainname == p2->domainname && p1->program == p2->program; @@ -240,6 +242,7 @@ int tomoyo_write_transition_control(struct tomoyo_acl_param *param, int error = param->is_delete ? -ENOENT : -ENOMEM; char *program = param->data; char *domainname = strstr(program, " from "); + if (domainname) { *domainname = '\0'; domainname += 6; @@ -293,6 +296,7 @@ static inline bool tomoyo_scan_transition const enum tomoyo_transition_type type) { const struct tomoyo_transition_control *ptr; + list_for_each_entry_rcu(ptr, list, head.list) { if (ptr->head.is_deleted || ptr->type != type) continue; @@ -338,9 +342,11 @@ static enum tomoyo_transition_type tomoyo_transition_type { const char *last_name = tomoyo_last_word(domainname->name); enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET; + while (type < TOMOYO_MAX_TRANSITION_TYPE) { const struct list_head * const list = &ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; + if (!tomoyo_scan_transition(list, domainname, program, last_name, type)) { type++; @@ -375,6 +381,7 @@ static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, head); const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head); + return p1->original_name == p2->original_name && p1->aggregated_name == p2->aggregated_name; } @@ -394,6 +401,7 @@ int tomoyo_write_aggregator(struct tomoyo_acl_param *param) int error = param->is_delete ? -ENOENT : -ENOMEM; const char *original_name = tomoyo_read_token(param); const char *aggregated_name = tomoyo_read_token(param); + if (!tomoyo_correct_word(original_name) || !tomoyo_correct_path(aggregated_name)) return -EINVAL; @@ -426,6 +434,7 @@ static struct tomoyo_policy_namespace *tomoyo_find_namespace (const char *name, const unsigned int len) { struct tomoyo_policy_namespace *ns; + list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { if (strncmp(name, ns->name, len) || (name[len] && name[len] != ' ')) @@ -451,6 +460,7 @@ struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname) struct tomoyo_policy_namespace *entry; const char *cp = domainname; unsigned int len = 0; + while (*cp && *cp++ != ' ') len++; ptr = tomoyo_find_namespace(domainname, len); @@ -466,6 +476,7 @@ struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname) ptr = tomoyo_find_namespace(domainname, len); if (!ptr && tomoyo_memory_ok(entry)) { char *name = (char *) (entry + 1); + ptr = entry; memmove(name, domainname, len); name[len] = '\0'; @@ -490,6 +501,7 @@ static bool tomoyo_namespace_jump(const char *domainname) { const char *namespace = tomoyo_current_namespace()->name; const int len = strlen(namespace); + return strncmp(domainname, namespace, len) || (domainname[len] && domainname[len] != ' '); } @@ -510,6 +522,7 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, struct tomoyo_domain_info e = { }; struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname); bool created = false; + if (entry) { if (transit) { /* @@ -546,6 +559,7 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, */ if (transit) { const struct tomoyo_domain_info *domain = tomoyo_domain(); + e.profile = domain->profile; e.group = domain->group; } @@ -569,6 +583,7 @@ out: if (entry && transit) { if (created) { struct tomoyo_request_info r; + tomoyo_init_request_info(&r, entry, TOMOYO_MAC_FILE_EXECUTE); r.granted = false; @@ -712,6 +727,7 @@ retry: struct tomoyo_aggregator *ptr; struct list_head *list = &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; + /* Check 'aggregator' directive. */ candidate = &exename; list_for_each_entry_rcu(ptr, list, head.list) { @@ -747,6 +763,7 @@ retry: */ if (ee->transition) { const char *domainname = ee->transition->name; + reject_on_transition_failure = true; if (!strcmp(domainname, "keep")) goto force_keep_domain; @@ -758,6 +775,7 @@ retry: goto force_initialize_domain; if (!strcmp(domainname, "parent")) { char *cp; + strncpy(ee->tmp, old_domain->domainname->name, TOMOYO_EXEC_TMPSIZE - 1); cp = strrchr(ee->tmp, ' '); @@ -822,8 +840,7 @@ force_jump_domain: if (domain) retval = 0; else if (reject_on_transition_failure) { - printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", - ee->tmp); + pr_warn("ERROR: Domain '%s' not ready.\n", ee->tmp); retval = -ENOMEM; } else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING) retval = -ENOMEM; @@ -834,8 +851,7 @@ force_jump_domain: ee->r.granted = false; tomoyo_write_log(&ee->r, "%s", tomoyo_dif [TOMOYO_DIF_TRANSITION_FAILED]); - printk(KERN_WARNING - "ERROR: Domain '%s' not defined.\n", ee->tmp); + pr_warn("ERROR: Domain '%s' not defined.\n", ee->tmp); } } out: diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 2a374b4da8f5..86f7d1b90212 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -214,6 +214,7 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) const u8 type = r->param.path_number.operation; u8 radix; char buffer[64]; + switch (type) { case TOMOYO_TYPE_CREATE: case TOMOYO_TYPE_MKDIR: @@ -253,6 +254,7 @@ static bool tomoyo_check_path_acl(struct tomoyo_request_info *r, { const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl), head); + if (acl->perm & (1 << r->param.path.operation)) { r->param.path.matched_path = tomoyo_compare_name_union(r->param.path.filename, @@ -275,6 +277,7 @@ static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r, { const struct tomoyo_path_number_acl *acl = container_of(ptr, typeof(*acl), head); + return (acl->perm & (1 << r->param.path_number.operation)) && tomoyo_compare_number_union(r->param.path_number.number, &acl->number) && @@ -295,6 +298,7 @@ static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r, { const struct tomoyo_path2_acl *acl = container_of(ptr, typeof(*acl), head); + return (acl->perm & (1 << r->param.path2.operation)) && tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1) && tomoyo_compare_name_union(r->param.path2.filename2, @@ -314,6 +318,7 @@ static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, { const struct tomoyo_mkdev_acl *acl = container_of(ptr, typeof(*acl), head); + return (acl->perm & (1 << r->param.mkdev.operation)) && tomoyo_compare_number_union(r->param.mkdev.mode, &acl->mode) && @@ -338,6 +343,7 @@ static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, { const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_same_name_union(&p1->name, &p2->name); } @@ -358,6 +364,7 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, ->perm; u16 perm = *a_perm; const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm; + if (is_delete) perm &= ~b_perm; else @@ -384,6 +391,7 @@ static int tomoyo_update_path_acl(const u16 perm, .perm = perm }; int error; + if (!tomoyo_parse_name_union(param, &e.name)) error = -EINVAL; else @@ -407,6 +415,7 @@ static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a, { const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_same_name_union(&p1->name, &p2->name) && tomoyo_same_number_union(&p1->mode, &p2->mode) && tomoyo_same_number_union(&p1->major, &p2->major) && @@ -431,6 +440,7 @@ static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, u8 perm = *a_perm; const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head) ->perm; + if (is_delete) perm &= ~b_perm; else @@ -457,6 +467,7 @@ static int tomoyo_update_mkdev_acl(const u8 perm, .perm = perm }; int error; + if (!tomoyo_parse_name_union(param, &e.name) || !tomoyo_parse_number_union(param, &e.mode) || !tomoyo_parse_number_union(param, &e.major) || @@ -486,6 +497,7 @@ static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a, { const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_same_name_union(&p1->name1, &p2->name1) && tomoyo_same_name_union(&p1->name2, &p2->name2); } @@ -507,6 +519,7 @@ static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, ->perm; u8 perm = *a_perm; const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm; + if (is_delete) perm &= ~b_perm; else @@ -533,6 +546,7 @@ static int tomoyo_update_path2_acl(const u8 perm, .perm = perm }; int error; + if (!tomoyo_parse_name_union(param, &e.name1) || !tomoyo_parse_name_union(param, &e.name2)) error = -EINVAL; @@ -621,6 +635,7 @@ static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, head); const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_same_name_union(&p1->name, &p2->name) && tomoyo_same_number_union(&p1->number, &p2->number); } @@ -643,6 +658,7 @@ static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, u8 perm = *a_perm; const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head) ->perm; + if (is_delete) perm &= ~b_perm; else @@ -667,6 +683,7 @@ static int tomoyo_update_path_number_acl(const u8 perm, .perm = perm }; int error; + if (!tomoyo_parse_name_union(param, &e.name) || !tomoyo_parse_number_union(param, &e.number)) error = -EINVAL; @@ -947,6 +964,7 @@ static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, { const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && @@ -966,6 +984,7 @@ static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param) { struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; int error; + if (!tomoyo_parse_name_union(param, &e.dev_name) || !tomoyo_parse_name_union(param, &e.dir_name) || !tomoyo_parse_name_union(param, &e.fs_type) || @@ -995,6 +1014,7 @@ int tomoyo_write_file(struct tomoyo_acl_param *param) u16 perm = 0; u8 type; const char *operation = tomoyo_read_token(param); + for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) if (tomoyo_permstr(operation, tomoyo_path_keyword[type])) perm |= 1 << type; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index e22bea811c57..9537832fca18 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -77,11 +77,13 @@ static bool tomoyo_name_used_by_io_buffer(const char *string) spin_lock(&tomoyo_io_buffer_list_lock); list_for_each_entry(head, &tomoyo_io_buffer_list, list) { int i; + head->users++; spin_unlock(&tomoyo_io_buffer_list_lock); mutex_lock(&head->io_sem); for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { const char *w = head->r.w[i]; + if (w < string || w > string + size) continue; in_use = true; @@ -108,6 +110,7 @@ static inline void tomoyo_del_transition_control(struct list_head *element) { struct tomoyo_transition_control *ptr = container_of(element, typeof(*ptr), head.list); + tomoyo_put_name(ptr->domainname); tomoyo_put_name(ptr->program); } @@ -123,6 +126,7 @@ static inline void tomoyo_del_aggregator(struct list_head *element) { struct tomoyo_aggregator *ptr = container_of(element, typeof(*ptr), head.list); + tomoyo_put_name(ptr->original_name); tomoyo_put_name(ptr->aggregated_name); } @@ -138,6 +142,7 @@ static inline void tomoyo_del_manager(struct list_head *element) { struct tomoyo_manager *ptr = container_of(element, typeof(*ptr), head.list); + tomoyo_put_name(ptr->manager); } @@ -152,6 +157,7 @@ static void tomoyo_del_acl(struct list_head *element) { struct tomoyo_acl_info *acl = container_of(element, typeof(*acl), list); + tomoyo_put_condition(acl->cond); switch (acl->type) { case TOMOYO_TYPE_PATH_ACL: @@ -226,6 +232,7 @@ static void tomoyo_del_acl(struct list_head *element) { struct tomoyo_task_acl *entry = container_of(acl, typeof(*entry), head); + tomoyo_put_name(entry->domainname); } break; @@ -247,6 +254,7 @@ static inline void tomoyo_del_domain(struct list_head *element) container_of(element, typeof(*domain), list); struct tomoyo_acl_info *acl; struct tomoyo_acl_info *tmp; + /* * Since this domain is referenced from neither * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete @@ -286,6 +294,7 @@ void tomoyo_del_condition(struct list_head *element) = (const struct tomoyo_argv *) (names_p + names_count); const struct tomoyo_envp *envp = (const struct tomoyo_envp *) (argv + argc); + for (i = 0; i < numbers_count; i++) tomoyo_put_number_union(numbers_p++); for (i = 0; i < names_count; i++) @@ -321,6 +330,7 @@ static inline void tomoyo_del_path_group(struct list_head *element) { struct tomoyo_path_group *member = container_of(element, typeof(*member), head.list); + tomoyo_put_name(member->member_name); } @@ -335,6 +345,7 @@ static inline void tomoyo_del_group(struct list_head *element) { struct tomoyo_group *group = container_of(element, typeof(*group), head.list); + tomoyo_put_name(group->group_name); } @@ -476,6 +487,7 @@ static void tomoyo_collect_member(const enum tomoyo_policy_id id, { struct tomoyo_acl_head *member; struct tomoyo_acl_head *tmp; + list_for_each_entry_safe(member, tmp, member_list, list) { if (!member->is_deleted) continue; @@ -495,6 +507,7 @@ static void tomoyo_collect_acl(struct list_head *list) { struct tomoyo_acl_info *acl; struct tomoyo_acl_info *tmp; + list_for_each_entry_safe(acl, tmp, list, list) { if (!acl->is_deleted) continue; @@ -513,10 +526,12 @@ static void tomoyo_collect_entry(void) int i; enum tomoyo_policy_id id; struct tomoyo_policy_namespace *ns; + mutex_lock(&tomoyo_policy_lock); { struct tomoyo_domain_info *domain; struct tomoyo_domain_info *tmp; + list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list, list) { tomoyo_collect_acl(&domain->acl_info_list); @@ -534,6 +549,7 @@ static void tomoyo_collect_entry(void) { struct tomoyo_shared_acl_head *ptr; struct tomoyo_shared_acl_head *tmp; + list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list, list) { if (atomic_read(&ptr->users) > 0) @@ -547,6 +563,7 @@ static void tomoyo_collect_entry(void) struct list_head *list = &ns->group_list[i]; struct tomoyo_group *group; struct tomoyo_group *tmp; + switch (i) { case 0: id = TOMOYO_ID_PATH_GROUP; @@ -574,6 +591,7 @@ static void tomoyo_collect_entry(void) struct list_head *list = &tomoyo_name_list[i]; struct tomoyo_shared_acl_head *ptr; struct tomoyo_shared_acl_head *tmp; + list_for_each_entry_safe(ptr, tmp, list, list) { if (atomic_read(&ptr->users) > 0) continue; @@ -595,6 +613,7 @@ static int tomoyo_gc_thread(void *unused) { /* Garbage collector thread is exclusive. */ static DEFINE_MUTEX(tomoyo_gc_mutex); + if (!mutex_trylock(&tomoyo_gc_mutex)) goto out; tomoyo_collect_entry(); diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c index 21b0cc3a7e1a..a37c7dc66e44 100644 --- a/security/tomoyo/group.c +++ b/security/tomoyo/group.c @@ -75,11 +75,13 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type) { struct tomoyo_group *group = tomoyo_get_group(param, type); int error = -EINVAL; + if (!group) return -ENOMEM; param->list = &group->member_list; if (type == TOMOYO_PATH_GROUP) { struct tomoyo_path_group e = { }; + e.member_name = tomoyo_get_name(tomoyo_read_token(param)); if (!e.member_name) { error = -ENOMEM; @@ -90,6 +92,7 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type) tomoyo_put_name(e.member_name); } else if (type == TOMOYO_NUMBER_GROUP) { struct tomoyo_number_group e = { }; + if (param->data[0] == '@' || !tomoyo_parse_number_union(param, &e.number)) goto out; @@ -129,6 +132,7 @@ tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, const struct tomoyo_group *group) { struct tomoyo_path_group *member; + list_for_each_entry_rcu(member, &group->member_list, head.list) { if (member->head.is_deleted) continue; @@ -156,6 +160,7 @@ bool tomoyo_number_matches_group(const unsigned long min, { struct tomoyo_number_group *member; bool matched = false; + list_for_each_entry_rcu(member, &group->member_list, head.list) { if (member->head.is_deleted) continue; diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c index 81b951652051..3445ae6fd479 100644 --- a/security/tomoyo/load_policy.c +++ b/security/tomoyo/load_policy.c @@ -37,11 +37,12 @@ __setup("TOMOYO_loader=", tomoyo_loader_setup); static bool tomoyo_policy_loader_exists(void) { struct path path; + if (!tomoyo_loader) tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER; if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { - printk(KERN_INFO "Not activating Mandatory Access Control " - "as %s does not exist.\n", tomoyo_loader); + pr_info("Not activating Mandatory Access Control as %s does not exist.\n", + tomoyo_loader); return false; } path_put(&path); @@ -96,8 +97,7 @@ void tomoyo_load_policy(const char *filename) if (!tomoyo_policy_loader_exists()) return; done = true; - printk(KERN_INFO "Calling %s to load policy. Please wait.\n", - tomoyo_loader); + pr_info("Calling %s to load policy. Please wait.\n", tomoyo_loader); argv[0] = (char *) tomoyo_loader; argv[1] = NULL; envp[0] = "HOME=/"; diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 12477e0b0a11..2e7fcfa923c9 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -19,9 +19,9 @@ void tomoyo_warn_oom(const char *function) /* Reduce error messages. */ static pid_t tomoyo_last_pid; const pid_t pid = current->pid; + if (tomoyo_last_pid != pid) { - printk(KERN_WARNING "ERROR: Out of memory at %s.\n", - function); + pr_warn("ERROR: Out of memory at %s.\n", function); tomoyo_last_pid = pid; } if (!tomoyo_policy_loaded) @@ -48,6 +48,7 @@ bool tomoyo_memory_ok(void *ptr) { if (ptr) { const size_t s = ksize(ptr); + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s; if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= @@ -73,6 +74,7 @@ bool tomoyo_memory_ok(void *ptr) void *tomoyo_commit_ok(void *data, const unsigned int size) { void *ptr = kzalloc(size, GFP_NOFS); + if (tomoyo_memory_ok(ptr)) { memmove(ptr, data, size); memset(data, 0, size); @@ -98,6 +100,7 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, struct list_head *list; const char *group_name = tomoyo_read_token(param); bool found = false; + if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP) return NULL; e.group_name = tomoyo_get_name(group_name); @@ -116,6 +119,7 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, } if (!found) { struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { INIT_LIST_HEAD(&entry->member_list); atomic_set(&entry->head.users, 1); @@ -191,6 +195,7 @@ struct tomoyo_policy_namespace tomoyo_kernel_namespace; void __init tomoyo_mm_init(void) { int idx; + for (idx = 0; idx < TOMOYO_MAX_HASH; idx++) INIT_LIST_HEAD(&tomoyo_name_list[idx]); tomoyo_kernel_namespace.name = ""; diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 7dc7f59b7dde..2755971f50df 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -49,6 +49,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, { const struct tomoyo_mount_acl *acl = container_of(ptr, typeof(*acl), head); + return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) && tomoyo_compare_name_union(r->param.mount.type, @@ -89,6 +90,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, struct tomoyo_path_info rdir; int need_dev = 0; int error = -ENOMEM; + r->obj = &obj; /* Get fstype. */ diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 6ff8c21e4fff..85e6e31dd1e5 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -94,11 +94,13 @@ static char *tomoyo_get_absolute_path(const struct path *path, char * const buff const int buflen) { char *pos = ERR_PTR(-ENOMEM); + if (buflen >= 256) { /* go to whatever namespace root we are under */ pos = d_absolute_path(path, buffer, buflen - 1); if (!IS_ERR(pos) && *pos == '/' && pos[1]) { struct inode *inode = d_backing_inode(path->dentry); + if (inode && S_ISDIR(inode->i_mode)) { buffer[buflen - 2] = '/'; buffer[buflen - 1] = '\0'; @@ -123,10 +125,12 @@ static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer, const int buflen) { char *pos = ERR_PTR(-ENOMEM); + if (buflen >= 256) { pos = dentry_path_raw(dentry, buffer, buflen - 1); if (!IS_ERR(pos) && *pos == '/' && pos[1]) { struct inode *inode = d_backing_inode(dentry); + if (inode && S_ISDIR(inode->i_mode)) { buffer[buflen - 2] = '/'; buffer[buflen - 1] = '\0'; @@ -150,12 +154,14 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, { struct super_block *sb = dentry->d_sb; char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen); + if (IS_ERR(pos)) return pos; /* Convert from $PID to self if $PID is current thread. */ if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { char *ep; const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); + if (*ep == '/' && pid && pid == task_tgid_nr_ns(current, sb->s_fs_info)) { pos = ep - 5; @@ -170,6 +176,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, goto prepend_filesystem_name; { struct inode *inode = d_backing_inode(sb->s_root); + /* * Use filesystem name if filesystem does not support rename() * operation. @@ -182,6 +189,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, char name[64]; int name_len; const dev_t dev = sb->s_dev; + name[sizeof(name) - 1] = '\0'; snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), MINOR(dev)); @@ -197,6 +205,7 @@ prepend_filesystem_name: { const char *name = sb->s_type->name; const int name_len = strlen(name); + pos -= name_len + 1; if (pos < buffer) goto out; @@ -223,10 +232,10 @@ static char *tomoyo_get_socket_name(const struct path *path, char * const buffer struct inode *inode = d_backing_inode(path->dentry); struct socket *sock = inode ? SOCKET_I(inode) : NULL; struct sock *sk = sock ? sock->sk : NULL; + if (sk) { - snprintf(buffer, buflen, "socket:[family=%u:type=%u:" - "protocol=%u]", sk->sk_family, sk->sk_type, - sk->sk_protocol); + snprintf(buffer, buflen, "socket:[family=%u:type=%u:protocol=%u]", + sk->sk_family, sk->sk_type, sk->sk_protocol); } else { snprintf(buffer, buflen, "socket:[unknown]"); } @@ -255,12 +264,14 @@ char *tomoyo_realpath_from_path(const struct path *path) unsigned int buf_len = PAGE_SIZE / 2; struct dentry *dentry = path->dentry; struct super_block *sb; + if (!dentry) return NULL; sb = dentry->d_sb; while (1) { char *pos; struct inode *inode; + buf_len <<= 1; kfree(buf); buf = kmalloc(buf_len, GFP_NOFS); @@ -323,6 +334,7 @@ char *tomoyo_realpath_nofollow(const char *pathname) if (pathname && kern_path(pathname, 0, &path) == 0) { char *buf = tomoyo_realpath_from_path(&path); + path_put(&path); return buf; } diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 338872fa3d06..546281c5b233 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -21,6 +21,7 @@ static bool tomoyo_check_task_acl(struct tomoyo_request_info *r, { const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl), head); + return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname); } @@ -42,6 +43,7 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, { char *data; int error; + if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10) return -ENOMEM; data = memdup_user_nul(buf, count); @@ -52,6 +54,7 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, const int idx = tomoyo_read_lock(); struct tomoyo_path_info name; struct tomoyo_request_info r; + name.name = data; tomoyo_fill_path_info(&name); /* Check "task manual_domain_transition" permission. */ @@ -100,6 +103,7 @@ static ssize_t tomoyo_read_self(struct file *file, char __user *buf, const char *domain = tomoyo_domain()->domainname->name; loff_t len = strlen(domain); loff_t pos = *ppos; + if (pos >= len || !count) return 0; len -= pos; diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index c2ab6575e68e..716c92ec941a 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -105,6 +105,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) if (!s->old_domain_info) { const int idx = tomoyo_read_lock(); const int err = tomoyo_find_next_domain(bprm); + tomoyo_read_unlock(idx); return err; } @@ -151,6 +152,7 @@ static int tomoyo_path_truncate(const struct path *path) static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry) { struct path path = { .mnt = parent->mnt, .dentry = dentry }; + return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL); } @@ -167,6 +169,7 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry, umode_t mode) { struct path path = { .mnt = parent->mnt, .dentry = dentry }; + return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path, mode & S_IALLUGO); } @@ -182,6 +185,7 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry, static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry) { struct path path = { .mnt = parent->mnt, .dentry = dentry }; + return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL); } @@ -198,6 +202,7 @@ static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry, const char *old_name) { struct path path = { .mnt = parent->mnt, .dentry = dentry }; + return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name); } @@ -255,6 +260,7 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di { struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry }; struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry }; + return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2); } @@ -275,6 +281,7 @@ static int tomoyo_path_rename(const struct path *old_parent, { struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry }; struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry }; + return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2); } @@ -306,11 +313,11 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, */ static int tomoyo_file_open(struct file *f) { - int flags = f->f_flags; /* Don't check read permission here if called from do_execve(). */ if (current->in_execve) return 0; - return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags); + return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, + f->f_flags); } /** @@ -354,6 +361,7 @@ static int tomoyo_path_chmod(const struct path *path, umode_t mode) static int tomoyo_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { int error = 0; + if (uid_valid(uid)) error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, from_kuid(&init_user_ns, uid)); @@ -403,6 +411,7 @@ static int tomoyo_sb_mount(const char *dev_name, const struct path *path, static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) { struct path path = { .mnt = mnt, .dentry = mnt->mnt_root }; + return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL); } @@ -573,7 +582,7 @@ static int __init tomoyo_init(void) /* register ourselves with the security framework */ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); - printk(KERN_INFO "TOMOYO Linux initialized\n"); + pr_info("TOMOYO Linux initialized\n"); s->domain_info = &tomoyo_kernel_domain; atomic_inc(&tomoyo_kernel_domain.users); s->old_domain_info = NULL; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index badffc8271c8..0517cbdd7275 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -91,6 +91,7 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = { void tomoyo_convert_time(time64_t time64, struct tomoyo_time *stamp) { struct tm tm; + time64_to_tm(time64, 0, &tm); stamp->sec = tm.tm_sec; stamp->min = tm.tm_min; @@ -113,6 +114,7 @@ void tomoyo_convert_time(time64_t time64, struct tomoyo_time *stamp) bool tomoyo_permstr(const char *string, const char *keyword) { const char *cp = strstr(string, keyword); + if (cp) return cp == string || *(cp - 1) == '/'; return false; @@ -132,6 +134,7 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param) { char *pos = param->data; char *del = strchr(pos, ' '); + if (del) *del++ = '\0'; else @@ -152,6 +155,7 @@ const struct tomoyo_path_info *tomoyo_get_domainname { char *start = param->data; char *pos = start; + while (*pos) { if (*pos++ != ' ' || *pos++ == '/') continue; @@ -181,8 +185,10 @@ u8 tomoyo_parse_ulong(unsigned long *result, char **str) const char *cp = *str; char *ep; int base = 10; + if (*cp == '0') { char c = *(cp + 1); + if (c == 'x' || c == 'X') { base = 16; cp += 2; @@ -240,6 +246,7 @@ bool tomoyo_parse_name_union(struct tomoyo_acl_param *param, struct tomoyo_name_union *ptr) { char *filename; + if (param->data[0] == '@') { param->data++; ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP); @@ -266,6 +273,7 @@ bool tomoyo_parse_number_union(struct tomoyo_acl_param *param, char *data; u8 type; unsigned long v; + memset(ptr, 0, sizeof(*ptr)); if (param->data[0] == '@') { param->data++; @@ -429,6 +437,7 @@ static bool tomoyo_correct_word2(const char *string, size_t len) unsigned char c; unsigned char d; unsigned char e; + if (!len) goto out; while (len--) { @@ -533,6 +542,7 @@ bool tomoyo_correct_domain(const unsigned char *domainname) return true; while (1) { const unsigned char *cp = strchr(domainname, ' '); + if (!cp) break; if (*domainname != '/' || @@ -554,6 +564,7 @@ bool tomoyo_domain_def(const unsigned char *buffer) { const unsigned char *cp; int len; + if (*buffer != '<') return false; cp = strchr(buffer, ' '); @@ -668,6 +679,9 @@ static bool tomoyo_file_matches_pattern2(const char *filename, { while (filename < filename_end && pattern < pattern_end) { char c; + int i; + int j; + if (*pattern != '\\') { if (*filename++ != *pattern++) return false; @@ -676,8 +690,6 @@ static bool tomoyo_file_matches_pattern2(const char *filename, c = *filename; pattern++; switch (*pattern) { - int i; - int j; case '?': if (c == '/') { return false; @@ -985,6 +997,7 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, struct tomoyo_domain_info *domain, const u8 index) { u8 profile; + memset(r, 0, sizeof(*r)); if (!domain) domain = tomoyo_domain(); @@ -1018,6 +1031,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { u16 perm; u8 i; + if (ptr->is_deleted) continue; switch (ptr->type) { @@ -1062,9 +1076,8 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true; /* r->granted = false; */ tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]); - printk(KERN_WARNING "WARNING: " - "Domain '%s' has too many ACLs to hold. " - "Stopped learning mode.\n", domain->domainname->name); + pr_warn("WARNING: Domain '%s' has too many ACLs to hold. Stopped learning mode.\n", + domain->domainname->name); } return false; } -- cgit v1.2.3-59-g8ed1b From 4b42564181d683d767b495d7041b1f229468042f Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 24 Jan 2019 18:37:36 +0900 Subject: tomoyo: Allow multiple use_group lines. Being able to specify multiple "use_group" lines makes it easier to write whitelisted policies. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 44 +++++++++++++++++++++++++++++--------------- security/tomoyo/common.h | 3 ++- security/tomoyo/domain.c | 17 +++++++++++------ 3 files changed, 42 insertions(+), 22 deletions(-) (limited to 'security') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 0f8079b65e23..148ad50a1f94 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1174,7 +1174,7 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) struct tomoyo_domain_info *domain = head->w.domain; const bool is_delete = head->w.is_delete; bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); - unsigned int profile; + unsigned int idx; if (*data == '<') { int ret = 0; @@ -1192,24 +1192,27 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) if (!domain) return -EINVAL; ns = domain->ns; - if (sscanf(data, "use_profile %u", &profile) == 1 - && profile < TOMOYO_MAX_PROFILES) { - if (!tomoyo_policy_loaded || ns->profile_ptr[profile]) - domain->profile = (u8) profile; + if (sscanf(data, "use_profile %u", &idx) == 1 + && idx < TOMOYO_MAX_PROFILES) { + if (!tomoyo_policy_loaded || ns->profile_ptr[idx]) + if (!is_delete) + domain->profile = (u8) idx; return 0; } - if (sscanf(data, "use_group %u\n", &profile) == 1 - && profile < TOMOYO_MAX_ACL_GROUPS) { + if (sscanf(data, "use_group %u\n", &idx) == 1 + && idx < TOMOYO_MAX_ACL_GROUPS) { if (!is_delete) - domain->group = (u8) profile; + set_bit(idx, domain->group); + else + clear_bit(idx, domain->group); return 0; } - for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) { - const char *cp = tomoyo_dif[profile]; + for (idx = 0; idx < TOMOYO_MAX_DOMAIN_INFO_FLAGS; idx++) { + const char *cp = tomoyo_dif[idx]; if (strncmp(data, cp, strlen(cp) - 1)) continue; - domain->flags[profile] = !is_delete; + domain->flags[idx] = !is_delete; return 0; } return tomoyo_write_domain2(ns, &domain->acl_info_list, data, @@ -1629,22 +1632,33 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) tomoyo_set_lf(head); tomoyo_io_printf(head, "use_profile %u\n", domain->profile); - tomoyo_io_printf(head, "use_group %u\n", - domain->group); for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++) if (domain->flags[i]) tomoyo_set_string(head, tomoyo_dif[i]); + head->r.index = 0; head->r.step++; - tomoyo_set_lf(head); /* fall through */ case 1: + while (head->r.index < TOMOYO_MAX_ACL_GROUPS) { + i = head->r.index++; + if (!test_bit(i, domain->group)) + continue; + tomoyo_io_printf(head, "use_group %u\n", i); + if (!tomoyo_flush(head)) + return; + } + head->r.index = 0; + head->r.step++; + tomoyo_set_lf(head); + /* fall through */ + case 2: if (!tomoyo_read_domain2(head, &domain->acl_info_list)) return; head->r.step++; if (!tomoyo_set_lf(head)) return; /* fall through */ - case 2: + case 3: head->r.step = 0; if (head->r.print_this_domain_only) goto done; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f2c458ab9942..74dbd3bdc64e 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -684,8 +684,9 @@ struct tomoyo_domain_info { const struct tomoyo_path_info *domainname; /* Namespace for this domain. Never NULL. */ struct tomoyo_policy_namespace *ns; + /* Group numbers to use. */ + unsigned long group[TOMOYO_MAX_ACL_GROUPS / BITS_PER_LONG]; u8 profile; /* Profile number to use. */ - u8 group; /* Group number to use. */ bool is_deleted; /* Delete flag. */ bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS]; atomic_t users; /* Number of referring tasks. */ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index bf832b301412..8526a0a74023 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -162,8 +162,8 @@ void tomoyo_check_acl(struct tomoyo_request_info *r, { const struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; - bool retried = false; const struct list_head *list = &domain->acl_info_list; + u16 i = 0; retry: list_for_each_entry_rcu(ptr, list, list) { @@ -177,9 +177,10 @@ retry: r->granted = true; return; } - if (!retried) { - retried = true; - list = &domain->ns->acl_group[domain->group]; + for (; i < TOMOYO_MAX_ACL_GROUPS; i++) { + if (!test_bit(i, domain->group)) + continue; + list = &domain->ns->acl_group[i++]; goto retry; } r->granted = false; @@ -561,7 +562,7 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, const struct tomoyo_domain_info *domain = tomoyo_domain(); e.profile = domain->profile; - e.group = domain->group; + memcpy(e.group, domain->group, sizeof(e.group)); } e.domainname = tomoyo_get_name(domainname); if (!e.domainname) @@ -583,13 +584,17 @@ out: if (entry && transit) { if (created) { struct tomoyo_request_info r; + int i; tomoyo_init_request_info(&r, entry, TOMOYO_MAC_FILE_EXECUTE); r.granted = false; tomoyo_write_log(&r, "use_profile %u\n", entry->profile); - tomoyo_write_log(&r, "use_group %u\n", entry->group); + for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) + if (test_bit(i, entry->group)) + tomoyo_write_log(&r, "use_group %u\n", + i); tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); } } -- cgit v1.2.3-59-g8ed1b From aeca4e2ca65c1aeacfbe520684e6421719d99417 Mon Sep 17 00:00:00 2001 From: Micah Morton Date: Wed, 16 Jan 2019 07:46:06 -0800 Subject: LSM: add SafeSetID module that gates setid calls SafeSetID gates the setid family of syscalls to restrict UID/GID transitions from a given UID/GID to only those approved by a system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as allowing a user to set up user namespace UID mappings. For now, only gating the set*uid family of syscalls is supported, with support for set*gid coming in a future patch set. Signed-off-by: Micah Morton Acked-by: Kees Cook Signed-off-by: James Morris --- Documentation/admin-guide/LSM/SafeSetID.rst | 107 +++++++++++ Documentation/admin-guide/LSM/index.rst | 1 + security/Kconfig | 3 +- security/Makefile | 2 + security/safesetid/Kconfig | 12 ++ security/safesetid/Makefile | 7 + security/safesetid/lsm.c | 277 ++++++++++++++++++++++++++++ security/safesetid/lsm.h | 33 ++++ security/safesetid/securityfs.c | 193 +++++++++++++++++++ 9 files changed, 634 insertions(+), 1 deletion(-) create mode 100644 Documentation/admin-guide/LSM/SafeSetID.rst create mode 100644 security/safesetid/Kconfig create mode 100644 security/safesetid/Makefile create mode 100644 security/safesetid/lsm.c create mode 100644 security/safesetid/lsm.h create mode 100644 security/safesetid/securityfs.c (limited to 'security') diff --git a/Documentation/admin-guide/LSM/SafeSetID.rst b/Documentation/admin-guide/LSM/SafeSetID.rst new file mode 100644 index 000000000000..212434ef65ad --- /dev/null +++ b/Documentation/admin-guide/LSM/SafeSetID.rst @@ -0,0 +1,107 @@ +========= +SafeSetID +========= +SafeSetID is an LSM module that gates the setid family of syscalls to restrict +UID/GID transitions from a given UID/GID to only those approved by a +system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs +from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as +allowing a user to set up user namespace UID mappings. + + +Background +========== +In absence of file capabilities, processes spawned on a Linux system that need +to switch to a different user must be spawned with CAP_SETUID privileges. +CAP_SETUID is granted to programs running as root or those running as a non-root +user that have been explicitly given the CAP_SETUID runtime capability. It is +often preferable to use Linux runtime capabilities rather than file +capabilities, since using file capabilities to run a program with elevated +privileges opens up possible security holes since any user with access to the +file can exec() that program to gain the elevated privileges. + +While it is possible to implement a tree of processes by giving full +CAP_SET{U/G}ID capabilities, this is often at odds with the goals of running a +tree of processes under non-root user(s) in the first place. Specifically, +since CAP_SETUID allows changing to any user on the system, including the root +user, it is an overpowered capability for what is needed in this scenario, +especially since programs often only call setuid() to drop privileges to a +lesser-privileged user -- not elevate privileges. Unfortunately, there is no +generally feasible way in Linux to restrict the potential UIDs that a user can +switch to through setuid() beyond allowing a switch to any user on the system. +This SafeSetID LSM seeks to provide a solution for restricting setid +capabilities in such a way. + +The main use case for this LSM is to allow a non-root program to transition to +other untrusted uids without full blown CAP_SETUID capabilities. The non-root +program would still need CAP_SETUID to do any kind of transition, but the +additional restrictions imposed by this LSM would mean it is a "safer" version +of CAP_SETUID since the non-root program cannot take advantage of CAP_SETUID to +do any unapproved actions (e.g. setuid to uid 0 or create/enter new user +namespace). The higher level goal is to allow for uid-based sandboxing of system +services without having to give out CAP_SETUID all over the place just so that +non-root programs can drop to even-lesser-privileged uids. This is especially +relevant when one non-root daemon on the system should be allowed to spawn other +processes as different uids, but its undesirable to give the daemon a +basically-root-equivalent CAP_SETUID. + + +Other Approaches Considered +=========================== + +Solve this problem in userspace +------------------------------- +For candidate applications that would like to have restricted setid capabilities +as implemented in this LSM, an alternative option would be to simply take away +setid capabilities from the application completely and refactor the process +spawning semantics in the application (e.g. by using a privileged helper program +to do process spawning and UID/GID transitions). Unfortunately, there are a +number of semantics around process spawning that would be affected by this, such +as fork() calls where the program doesn???t immediately call exec() after the +fork(), parent processes specifying custom environment variables or command line +args for spawned child processes, or inheritance of file handles across a +fork()/exec(). Because of this, as solution that uses a privileged helper in +userspace would likely be less appealing to incorporate into existing projects +that rely on certain process-spawning semantics in Linux. + +Use user namespaces +------------------- +Another possible approach would be to run a given process tree in its own user +namespace and give programs in the tree setid capabilities. In this way, +programs in the tree could change to any desired UID/GID in the context of their +own user namespace, and only approved UIDs/GIDs could be mapped back to the +initial system user namespace, affectively preventing privilege escalation. +Unfortunately, it is not generally feasible to use user namespaces in isolation, +without pairing them with other namespace types, which is not always an option. +Linux checks for capabilities based off of the user namespace that ???owns??? some +entity. For example, Linux has the notion that network namespaces are owned by +the user namespace in which they were created. A consequence of this is that +capability checks for access to a given network namespace are done by checking +whether a task has the given capability in the context of the user namespace +that owns the network namespace -- not necessarily the user namespace under +which the given task runs. Therefore spawning a process in a new user namespace +effectively prevents it from accessing the network namespace owned by the +initial namespace. This is a deal-breaker for any application that expects to +retain the CAP_NET_ADMIN capability for the purpose of adjusting network +configurations. Using user namespaces in isolation causes problems regarding +other system interactions, including use of pid namespaces and device creation. + +Use an existing LSM +------------------- +None of the other in-tree LSMs have the capability to gate setid transitions, or +even employ the security_task_fix_setuid hook at all. SELinux says of that hook: +"Since setuid only affects the current process, and since the SELinux controls +are not based on the Linux identity attributes, SELinux does not need to control +this operation." + + +Directions for use +================== +This LSM hooks the setid syscalls to make sure transitions are allowed if an +applicable restriction policy is in place. Policies are configured through +securityfs by writing to the safesetid/add_whitelist_policy and +safesetid/flush_whitelist_policies files at the location where securityfs is +mounted. The format for adding a policy is ':', using literal +numbers, such as '123:456'. To flush the policies, any write to the file is +sufficient. Again, configuring a policy for a UID will prevent that UID from +obtaining auxiliary setid privileges, such as allowing a user to set up user +namespace UID mappings. diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst index 9842e21afd4a..a6ba95fbaa9f 100644 --- a/Documentation/admin-guide/LSM/index.rst +++ b/Documentation/admin-guide/LSM/index.rst @@ -46,3 +46,4 @@ subdirectories. Smack tomoyo Yama + SafeSetID diff --git a/security/Kconfig b/security/Kconfig index 78dc12b7eeb3..9555f4914492 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -236,12 +236,13 @@ source "security/tomoyo/Kconfig" source "security/apparmor/Kconfig" source "security/loadpin/Kconfig" source "security/yama/Kconfig" +source "security/safesetid/Kconfig" source "security/integrity/Kconfig" config LSM string "Ordered list of enabled LSMs" - default "yama,loadpin,integrity,selinux,smack,tomoyo,apparmor" + default "yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list will be ignored. This can be diff --git a/security/Makefile b/security/Makefile index 4d2d3782ddef..c598b904938f 100644 --- a/security/Makefile +++ b/security/Makefile @@ -10,6 +10,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_YAMA) += yama subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin +subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid # always enable default capabilities obj-y += commoncap.o @@ -25,6 +26,7 @@ obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ obj-$(CONFIG_SECURITY_YAMA) += yama/ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ +obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists diff --git a/security/safesetid/Kconfig b/security/safesetid/Kconfig new file mode 100644 index 000000000000..bf89a47ffcc8 --- /dev/null +++ b/security/safesetid/Kconfig @@ -0,0 +1,12 @@ +config SECURITY_SAFESETID + bool "Gate setid transitions to limit CAP_SET{U/G}ID capabilities" + default n + help + SafeSetID is an LSM module that gates the setid family of syscalls to + restrict UID/GID transitions from a given UID/GID to only those + approved by a system-wide whitelist. These restrictions also prohibit + the given UIDs/GIDs from obtaining auxiliary privileges associated + with CAP_SET{U/G}ID, such as allowing a user to set up user namespace + UID mappings. + + If you are unsure how to answer this question, answer N. diff --git a/security/safesetid/Makefile b/security/safesetid/Makefile new file mode 100644 index 000000000000..6b0660321164 --- /dev/null +++ b/security/safesetid/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the safesetid LSM. +# + +obj-$(CONFIG_SECURITY_SAFESETID) := safesetid.o +safesetid-y := lsm.o securityfs.o diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c new file mode 100644 index 000000000000..3a2c75ac810c --- /dev/null +++ b/security/safesetid/lsm.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SafeSetID Linux Security Module + * + * Author: Micah Morton + * + * Copyright (C) 2018 The Chromium OS Authors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ + +#define pr_fmt(fmt) "SafeSetID: " fmt + +#include +#include +#include +#include +#include +#include +#include + +/* Flag indicating whether initialization completed */ +int safesetid_initialized; + +#define NUM_BITS 8 /* 128 buckets in hash table */ + +static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS); + +/* + * Hash table entry to store safesetid policy signifying that 'parent' user + * can setid to 'child' user. + */ +struct entry { + struct hlist_node next; + struct hlist_node dlist; /* for deletion cleanup */ + uint64_t parent_kuid; + uint64_t child_kuid; +}; + +static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock); + +static bool check_setuid_policy_hashtable_key(kuid_t parent) +{ + struct entry *entry; + + rcu_read_lock(); + hash_for_each_possible_rcu(safesetid_whitelist_hashtable, + entry, next, __kuid_val(parent)) { + if (entry->parent_kuid == __kuid_val(parent)) { + rcu_read_unlock(); + return true; + } + } + rcu_read_unlock(); + + return false; +} + +static bool check_setuid_policy_hashtable_key_value(kuid_t parent, + kuid_t child) +{ + struct entry *entry; + + rcu_read_lock(); + hash_for_each_possible_rcu(safesetid_whitelist_hashtable, + entry, next, __kuid_val(parent)) { + if (entry->parent_kuid == __kuid_val(parent) && + entry->child_kuid == __kuid_val(child)) { + rcu_read_unlock(); + return true; + } + } + rcu_read_unlock(); + + return false; +} + +static int safesetid_security_capable(const struct cred *cred, + struct user_namespace *ns, + int cap, + unsigned int opts) +{ + if (cap == CAP_SETUID && + check_setuid_policy_hashtable_key(cred->uid)) { + if (!(opts & CAP_OPT_INSETID)) { + /* + * Deny if we're not in a set*uid() syscall to avoid + * giving powers gated by CAP_SETUID that are related + * to functionality other than calling set*uid() (e.g. + * allowing user to set up userns uid mappings). + */ + pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions", + __kuid_val(cred->uid)); + return -1; + } + } + return 0; +} + +static int check_uid_transition(kuid_t parent, kuid_t child) +{ + if (check_setuid_policy_hashtable_key_value(parent, child)) + return 0; + pr_warn("UID transition (%d -> %d) blocked", + __kuid_val(parent), + __kuid_val(child)); + /* + * Kill this process to avoid potential security vulnerabilities + * that could arise from a missing whitelist entry preventing a + * privileged process from dropping to a lesser-privileged one. + */ + force_sig(SIGKILL, current); + return -EACCES; +} + +/* + * Check whether there is either an exception for user under old cred struct to + * set*uid to user under new cred struct, or the UID transition is allowed (by + * Linux set*uid rules) even without CAP_SETUID. + */ +static int safesetid_task_fix_setuid(struct cred *new, + const struct cred *old, + int flags) +{ + + /* Do nothing if there are no setuid restrictions for this UID. */ + if (!check_setuid_policy_hashtable_key(old->uid)) + return 0; + + switch (flags) { + case LSM_SETID_RE: + /* + * Users for which setuid restrictions exist can only set the + * real UID to the real UID or the effective UID, unless an + * explicit whitelist policy allows the transition. + */ + if (!uid_eq(old->uid, new->uid) && + !uid_eq(old->euid, new->uid)) { + return check_uid_transition(old->uid, new->uid); + } + /* + * Users for which setuid restrictions exist can only set the + * effective UID to the real UID, the effective UID, or the + * saved set-UID, unless an explicit whitelist policy allows + * the transition. + */ + if (!uid_eq(old->uid, new->euid) && + !uid_eq(old->euid, new->euid) && + !uid_eq(old->suid, new->euid)) { + return check_uid_transition(old->euid, new->euid); + } + break; + case LSM_SETID_ID: + /* + * Users for which setuid restrictions exist cannot change the + * real UID or saved set-UID unless an explicit whitelist + * policy allows the transition. + */ + if (!uid_eq(old->uid, new->uid)) + return check_uid_transition(old->uid, new->uid); + if (!uid_eq(old->suid, new->suid)) + return check_uid_transition(old->suid, new->suid); + break; + case LSM_SETID_RES: + /* + * Users for which setuid restrictions exist cannot change the + * real UID, effective UID, or saved set-UID to anything but + * one of: the current real UID, the current effective UID or + * the current saved set-user-ID unless an explicit whitelist + * policy allows the transition. + */ + if (!uid_eq(new->uid, old->uid) && + !uid_eq(new->uid, old->euid) && + !uid_eq(new->uid, old->suid)) { + return check_uid_transition(old->uid, new->uid); + } + if (!uid_eq(new->euid, old->uid) && + !uid_eq(new->euid, old->euid) && + !uid_eq(new->euid, old->suid)) { + return check_uid_transition(old->euid, new->euid); + } + if (!uid_eq(new->suid, old->uid) && + !uid_eq(new->suid, old->euid) && + !uid_eq(new->suid, old->suid)) { + return check_uid_transition(old->suid, new->suid); + } + break; + case LSM_SETID_FS: + /* + * Users for which setuid restrictions exist cannot change the + * filesystem UID to anything but one of: the current real UID, + * the current effective UID or the current saved set-UID + * unless an explicit whitelist policy allows the transition. + */ + if (!uid_eq(new->fsuid, old->uid) && + !uid_eq(new->fsuid, old->euid) && + !uid_eq(new->fsuid, old->suid) && + !uid_eq(new->fsuid, old->fsuid)) { + return check_uid_transition(old->fsuid, new->fsuid); + } + break; + default: + pr_warn("Unknown setid state %d\n", flags); + force_sig(SIGKILL, current); + return -EINVAL; + } + return 0; +} + +int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child) +{ + struct entry *new; + + /* Return if entry already exists */ + if (check_setuid_policy_hashtable_key_value(parent, child)) + return 0; + + new = kzalloc(sizeof(struct entry), GFP_KERNEL); + if (!new) + return -ENOMEM; + new->parent_kuid = __kuid_val(parent); + new->child_kuid = __kuid_val(child); + spin_lock(&safesetid_whitelist_hashtable_spinlock); + hash_add_rcu(safesetid_whitelist_hashtable, + &new->next, + __kuid_val(parent)); + spin_unlock(&safesetid_whitelist_hashtable_spinlock); + return 0; +} + +void flush_safesetid_whitelist_entries(void) +{ + struct entry *entry; + struct hlist_node *hlist_node; + unsigned int bkt_loop_cursor; + HLIST_HEAD(free_list); + + /* + * Could probably use hash_for_each_rcu here instead, but this should + * be fine as well. + */ + spin_lock(&safesetid_whitelist_hashtable_spinlock); + hash_for_each_safe(safesetid_whitelist_hashtable, bkt_loop_cursor, + hlist_node, entry, next) { + hash_del_rcu(&entry->next); + hlist_add_head(&entry->dlist, &free_list); + } + spin_unlock(&safesetid_whitelist_hashtable_spinlock); + synchronize_rcu(); + hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) { + hlist_del(&entry->dlist); + kfree(entry); + } +} + +static struct security_hook_list safesetid_security_hooks[] = { + LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid), + LSM_HOOK_INIT(capable, safesetid_security_capable) +}; + +static int __init safesetid_security_init(void) +{ + security_add_hooks(safesetid_security_hooks, + ARRAY_SIZE(safesetid_security_hooks), "safesetid"); + + /* Report that SafeSetID successfully initialized */ + safesetid_initialized = 1; + + return 0; +} + +DEFINE_LSM(safesetid_security_init) = { + .init = safesetid_security_init, +}; diff --git a/security/safesetid/lsm.h b/security/safesetid/lsm.h new file mode 100644 index 000000000000..c1ea3c265fcf --- /dev/null +++ b/security/safesetid/lsm.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SafeSetID Linux Security Module + * + * Author: Micah Morton + * + * Copyright (C) 2018 The Chromium OS Authors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ +#ifndef _SAFESETID_H +#define _SAFESETID_H + +#include + +/* Flag indicating whether initialization completed */ +extern int safesetid_initialized; + +/* Function type. */ +enum safesetid_whitelist_file_write_type { + SAFESETID_WHITELIST_ADD, /* Add whitelist policy. */ + SAFESETID_WHITELIST_FLUSH, /* Flush whitelist policies. */ +}; + +/* Add entry to safesetid whitelist to allow 'parent' to setid to 'child'. */ +int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child); + +void flush_safesetid_whitelist_entries(void); + +#endif /* _SAFESETID_H */ diff --git a/security/safesetid/securityfs.c b/security/safesetid/securityfs.c new file mode 100644 index 000000000000..61be4ee459cc --- /dev/null +++ b/security/safesetid/securityfs.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SafeSetID Linux Security Module + * + * Author: Micah Morton + * + * Copyright (C) 2018 The Chromium OS Authors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ +#include +#include + +#include "lsm.h" + +static struct dentry *safesetid_policy_dir; + +struct safesetid_file_entry { + const char *name; + enum safesetid_whitelist_file_write_type type; + struct dentry *dentry; +}; + +static struct safesetid_file_entry safesetid_files[] = { + {.name = "add_whitelist_policy", + .type = SAFESETID_WHITELIST_ADD}, + {.name = "flush_whitelist_policies", + .type = SAFESETID_WHITELIST_FLUSH}, +}; + +/* + * In the case the input buffer contains one or more invalid UIDs, the kuid_t + * variables pointed to by 'parent' and 'child' will get updated but this + * function will return an error. + */ +static int parse_safesetid_whitelist_policy(const char __user *buf, + size_t len, + kuid_t *parent, + kuid_t *child) +{ + char *kern_buf; + char *parent_buf; + char *child_buf; + const char separator[] = ":"; + int ret; + size_t first_substring_length; + long parsed_parent; + long parsed_child; + + /* Duplicate string from user memory and NULL-terminate */ + kern_buf = memdup_user_nul(buf, len); + if (IS_ERR(kern_buf)) + return PTR_ERR(kern_buf); + + /* + * Format of |buf| string should be :. + * Find location of ":" in kern_buf (copied from |buf|). + */ + first_substring_length = strcspn(kern_buf, separator); + if (first_substring_length == 0 || first_substring_length == len) { + ret = -EINVAL; + goto free_kern; + } + + parent_buf = kmemdup_nul(kern_buf, first_substring_length, GFP_KERNEL); + if (!parent_buf) { + ret = -ENOMEM; + goto free_kern; + } + + ret = kstrtol(parent_buf, 0, &parsed_parent); + if (ret) + goto free_both; + + child_buf = kern_buf + first_substring_length + 1; + ret = kstrtol(child_buf, 0, &parsed_child); + if (ret) + goto free_both; + + *parent = make_kuid(current_user_ns(), parsed_parent); + if (!uid_valid(*parent)) { + ret = -EINVAL; + goto free_both; + } + + *child = make_kuid(current_user_ns(), parsed_child); + if (!uid_valid(*child)) { + ret = -EINVAL; + goto free_both; + } + +free_both: + kfree(parent_buf); +free_kern: + kfree(kern_buf); + return ret; +} + +static ssize_t safesetid_file_write(struct file *file, + const char __user *buf, + size_t len, + loff_t *ppos) +{ + struct safesetid_file_entry *file_entry = + file->f_inode->i_private; + kuid_t parent; + kuid_t child; + int ret; + + if (!ns_capable(current_user_ns(), CAP_MAC_ADMIN)) + return -EPERM; + + if (*ppos != 0) + return -EINVAL; + + switch (file_entry->type) { + case SAFESETID_WHITELIST_FLUSH: + flush_safesetid_whitelist_entries(); + break; + case SAFESETID_WHITELIST_ADD: + ret = parse_safesetid_whitelist_policy(buf, len, &parent, + &child); + if (ret) + return ret; + + ret = add_safesetid_whitelist_entry(parent, child); + if (ret) + return ret; + break; + default: + pr_warn("Unknown securityfs file %d\n", file_entry->type); + break; + } + + /* Return len on success so caller won't keep trying to write */ + return len; +} + +static const struct file_operations safesetid_file_fops = { + .write = safesetid_file_write, +}; + +static void safesetid_shutdown_securityfs(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { + struct safesetid_file_entry *entry = + &safesetid_files[i]; + securityfs_remove(entry->dentry); + entry->dentry = NULL; + } + + securityfs_remove(safesetid_policy_dir); + safesetid_policy_dir = NULL; +} + +static int __init safesetid_init_securityfs(void) +{ + int i; + int ret; + + if (!safesetid_initialized) + return 0; + + safesetid_policy_dir = securityfs_create_dir("safesetid", NULL); + if (!safesetid_policy_dir) { + ret = PTR_ERR(safesetid_policy_dir); + goto error; + } + + for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { + struct safesetid_file_entry *entry = + &safesetid_files[i]; + entry->dentry = securityfs_create_file( + entry->name, 0200, safesetid_policy_dir, + entry, &safesetid_file_fops); + if (IS_ERR(entry->dentry)) { + ret = PTR_ERR(entry->dentry); + goto error; + } + } + + return 0; + +error: + safesetid_shutdown_securityfs(); + return ret; +} +fs_initcall(safesetid_init_securityfs); -- cgit v1.2.3-59-g8ed1b From f67e20d20f28048867372209777d136e5ab148cf Mon Sep 17 00:00:00 2001 From: Micah Morton Date: Mon, 28 Jan 2019 12:30:56 -0800 Subject: LSM: Add 'name' field for SafeSetID in DEFINE_LSM Without this, system boot was crashing with: [0.174285] LSM: Security Framework initializing [0.175277] BUG: unable to handle kernel NULL pointer dereference ... [0.176272] Call Trace: [0.176272] ordered_lsm_parse+0x112/0x20b [0.176272] security_init+0x9b/0x3ab [0.176272] start_kernel+0x413/0x479 [0.176272] secondary_startup_64+0xa4/0xb0 Signed-off-by: Micah Morton Fixed-by: Kees Cook Signed-off-by: James Morris --- security/safesetid/lsm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security') diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c index 3a2c75ac810c..282a242beb86 100644 --- a/security/safesetid/lsm.c +++ b/security/safesetid/lsm.c @@ -274,4 +274,5 @@ static int __init safesetid_security_init(void) DEFINE_LSM(safesetid_security_init) = { .init = safesetid_security_init, + .name = "safesetid", }; -- cgit v1.2.3-59-g8ed1b From 2f87324be7736151bd1f9bf1d63b1eb0401011ba Mon Sep 17 00:00:00 2001 From: Micah Morton Date: Tue, 29 Jan 2019 10:54:24 -0800 Subject: LSM: SafeSetID: 'depend' on CONFIG_SECURITY This patch changes the Kconfig file for the SafeSetID LSM to depend on CONFIG_SECURITY as well as select CONFIG_SECURITYFS, since the policies for the LSM are configured through writing to securityfs. Signed-off-by: Micah Morton Signed-off-by: James Morris --- security/safesetid/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'security') diff --git a/security/safesetid/Kconfig b/security/safesetid/Kconfig index bf89a47ffcc8..4f415c4e3f93 100644 --- a/security/safesetid/Kconfig +++ b/security/safesetid/Kconfig @@ -1,5 +1,7 @@ config SECURITY_SAFESETID bool "Gate setid transitions to limit CAP_SET{U/G}ID capabilities" + depends on SECURITY + select SECURITYFS default n help SafeSetID is an LSM module that gates the setid family of syscalls to -- cgit v1.2.3-59-g8ed1b From 2181e084b26bddca22bc3f23364c15809cfed28b Mon Sep 17 00:00:00 2001 From: Micah Morton Date: Wed, 30 Jan 2019 07:56:43 -0800 Subject: LSM: SafeSetID: remove unused include The include for asm/syscall.h was needed in a prior version of lsm.c that checked return values of syscall_get_nr, but since we did away with that part of the code this include is no longer necessary. Take out this include since it breaks builds for certain architectures. We no longer have any arch-specific code in SafeSetID. Signed-off-by: Micah Morton Signed-off-by: James Morris --- security/safesetid/lsm.c | 1 - 1 file changed, 1 deletion(-) (limited to 'security') diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c index 282a242beb86..cecd38e2ac80 100644 --- a/security/safesetid/lsm.c +++ b/security/safesetid/lsm.c @@ -14,7 +14,6 @@ #define pr_fmt(fmt) "SafeSetID: " fmt -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From e7a44cfd639945a0dec749f896adc1d340c2a6aa Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 12 Feb 2019 07:58:31 +0000 Subject: LSM: fix return value check in safesetid_init_securityfs() In case of error, the function securityfs_create_dir() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Fixes: aeca4e2ca65c ("LSM: add SafeSetID module that gates setid calls") Signed-off-by: Wei Yongjun Acked-by: Kees Cook Signed-off-by: James Morris --- security/safesetid/securityfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/safesetid/securityfs.c b/security/safesetid/securityfs.c index 61be4ee459cc..2c6c829be044 100644 --- a/security/safesetid/securityfs.c +++ b/security/safesetid/securityfs.c @@ -167,7 +167,7 @@ static int __init safesetid_init_securityfs(void) return 0; safesetid_policy_dir = securityfs_create_dir("safesetid", NULL); - if (!safesetid_policy_dir) { + if (IS_ERR(safesetid_policy_dir)) { ret = PTR_ERR(safesetid_policy_dir); goto error; } -- cgit v1.2.3-59-g8ed1b From 861f4bcffcc9d8bca93374ad555801d990cb980c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 17 Feb 2019 15:52:47 +0900 Subject: tomoyo: Bump version. Update URLs and profile version. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- MAINTAINERS | 11 +++++------ security/tomoyo/common.c | 19 ++++++++++++------- security/tomoyo/common.h | 2 +- 3 files changed, 18 insertions(+), 14 deletions(-) (limited to 'security') diff --git a/MAINTAINERS b/MAINTAINERS index 51029a425dbe..6fd9e0015dac 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15380,12 +15380,11 @@ F: mm/shmem.c TOMOYO SECURITY MODULE M: Kentaro Takeda M: Tetsuo Handa -L: tomoyo-dev-en@lists.sourceforge.jp (subscribers-only, for developers in English) -L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English) -L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese) -L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese) -W: http://tomoyo.sourceforge.jp/ -T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.5.x/tomoyo-lsm/patches/ +L: tomoyo-dev-en@lists.osdn.me (subscribers-only, for developers in English) +L: tomoyo-users-en@lists.osdn.me (subscribers-only, for users in English) +L: tomoyo-dev@lists.osdn.me (subscribers-only, for developers in Japanese) +L: tomoyo-users@lists.osdn.me (subscribers-only, for users in Japanese) +W: https://tomoyo.osdn.jp/ S: Maintained F: security/tomoyo/ diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 148ad50a1f94..57988d95d33d 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -354,7 +354,7 @@ void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) INIT_LIST_HEAD(&ns->group_list[idx]); for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) INIT_LIST_HEAD(&ns->policy_list[idx]); - ns->profile_version = 20110903; + ns->profile_version = 20150505; tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); } @@ -2300,7 +2300,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) static void tomoyo_read_version(struct tomoyo_io_buffer *head) { if (!head->r.eof) { - tomoyo_io_printf(head, "2.5.0"); + tomoyo_io_printf(head, "2.6.0"); head->r.eof = true; } } @@ -2777,12 +2777,17 @@ void tomoyo_check_profile(void) const int idx = tomoyo_read_lock(); tomoyo_policy_loaded = true; - pr_info("TOMOYO: 2.5.0\n"); + pr_info("TOMOYO: 2.6.0\n"); list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { const u8 profile = domain->profile; - const struct tomoyo_policy_namespace *ns = domain->ns; + struct tomoyo_policy_namespace *ns = domain->ns; - if (ns->profile_version != 20110903) + if (ns->profile_version == 20110903) { + pr_info_once("Converting profile version from %u to %u.\n", + 20110903, 20150505); + ns->profile_version = 20150505; + } + if (ns->profile_version != 20150505) pr_err("Profile version %u is not supported.\n", ns->profile_version); else if (!ns->profile_ptr[profile]) @@ -2790,8 +2795,8 @@ void tomoyo_check_profile(void) profile, domain->domainname->name); else continue; - pr_err("Userland tools for TOMOYO 2.5 must be installed and policy must be initialized.\n"); - pr_err("Please see http://tomoyo.sourceforge.jp/2.5/ for more information.\n"); + pr_err("Userland tools for TOMOYO 2.6 must be installed and policy must be initialized.\n"); + pr_err("Please see https://tomoyo.osdn.jp/2.6/ for more information.\n"); panic("STOP!"); } tomoyo_read_unlock(idx); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 74dbd3bdc64e..050473df5809 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -910,7 +910,7 @@ struct tomoyo_policy_namespace { struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS]; /* List for connecting to tomoyo_namespace_list list. */ struct list_head namespace_list; - /* Profile version. Currently only 20110903 is defined. */ + /* Profile version. Currently only 20150505 is defined. */ unsigned int profile_version; /* Name of this namespace (e.g. "", "" ). */ const char *name; -- cgit v1.2.3-59-g8ed1b From 09186e503486da4a17f16f2f7c679e6e3e2a32f4 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 8 Feb 2019 14:54:53 -0600 Subject: security: mark expected switch fall-throughs and add a missing break In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warnings: security/integrity/ima/ima_template_lib.c:85:10: warning: this statement may fall through [-Wimplicit-fallthrough=] security/integrity/ima/ima_policy.c:940:18: warning: this statement may fall through [-Wimplicit-fallthrough=] security/integrity/ima/ima_policy.c:943:7: warning: this statement may fall through [-Wimplicit-fallthrough=] security/integrity/ima/ima_policy.c:972:21: warning: this statement may fall through [-Wimplicit-fallthrough=] security/integrity/ima/ima_policy.c:974:7: warning: this statement may fall through [-Wimplicit-fallthrough=] security/smack/smack_lsm.c:3391:9: warning: this statement may fall through [-Wimplicit-fallthrough=] security/apparmor/domain.c:569:6: warning: this statement may fall through [-Wimplicit-fallthrough=] Warning level 3 was used: -Wimplicit-fallthrough=3 Also, add a missing break statement to fix the following warning: security/integrity/ima/ima_appraise.c:116:26: warning: this statement may fall through [-Wimplicit-fallthrough=] Acked-by: John Johansen Acked-by: Casey Schaufler Signed-off-by: Gustavo A. R. Silva Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/apparmor/domain.c | 2 +- security/integrity/ima/ima_appraise.c | 1 + security/integrity/ima/ima_policy.c | 4 ++++ security/integrity/ima/ima_template_lib.c | 1 + security/smack/smack_lsm.c | 3 +-- 5 files changed, 8 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 726910bba84b..c7c619578095 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -572,7 +572,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile, stack = NULL; break; } - /* fall through to X_NAME */ + /* fall through - to X_NAME */ case AA_X_NAME: if (xindex & AA_X_CHILD) /* released by caller */ diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index a2baa85ea2f5..5fb7127bbe68 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -114,6 +114,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint, break; case CREDS_CHECK: iint->ima_creds_status = status; + break; case FILE_CHECK: case POST_SETATTR: iint->ima_file_status = status; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 8bc8a1c8cb3f..122797023bdb 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -938,10 +938,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) case Opt_uid_gt: case Opt_euid_gt: entry->uid_op = &uid_gt; + /* fall through */ case Opt_uid_lt: case Opt_euid_lt: if ((token == Opt_uid_lt) || (token == Opt_euid_lt)) entry->uid_op = &uid_lt; + /* fall through */ case Opt_uid_eq: case Opt_euid_eq: uid_token = (token == Opt_uid_eq) || @@ -970,9 +972,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) break; case Opt_fowner_gt: entry->fowner_op = &uid_gt; + /* fall through */ case Opt_fowner_lt: if (token == Opt_fowner_lt) entry->fowner_op = &uid_lt; + /* fall through */ case Opt_fowner_eq: ima_log_string_op(ab, "fowner", args[0].from, entry->fowner_op); diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 43752002c222..513b457ae900 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -83,6 +83,7 @@ static void ima_show_template_data_ascii(struct seq_file *m, /* skip ':' and '\0' */ buf_ptr += 2; buflen -= buf_ptr - field_data->data; + /* fall through */ case DATA_FMT_DIGEST: case DATA_FMT_HEX: if (!buflen) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 79d6d2a6a0bc..2309c696c6c8 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3293,13 +3293,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ final = &smack_known_star; /* - * Fall through. - * * If a smack value has been set we want to use it, * but since tmpfs isn't giving us the opportunity * to set mount options simulate setting the * superblock default. */ + /* Fall through */ default: /* * This isn't an understood special case. -- cgit v1.2.3-59-g8ed1b From e88ed488af0a70cbc925d2cce60788378bbe26e3 Mon Sep 17 00:00:00 2001 From: Micah Morton Date: Mon, 25 Feb 2019 14:17:10 -0800 Subject: LSM: Update function documentation for cap_capable This should have gone in with commit c1a85a00ea66cb6f0bd0f14e47c28c2b0999799f. Signed-off-by: Micah Morton Signed-off-by: James Morris --- security/commoncap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/commoncap.c b/security/commoncap.c index 188eaf59f82f..f1d117c3d8ae 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -57,7 +57,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) * @cred: The credentials to use * @ns: The user namespace in which we need the capability * @cap: The capability to check for - * @audit: Whether to write an audit message or not + * @opts: Bitmask of options defined in include/linux/security.h * * Determine whether the nominated task has the specified capability amongst * its effective set, returning 0 if it does, -ve if it does not. -- cgit v1.2.3-59-g8ed1b From 89a9684ea158dd7eef1728be9f0aed9a7d41cf19 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 12 Feb 2019 10:23:18 -0800 Subject: LSM: Ignore "security=" when "lsm=" is specified To avoid potential confusion, explicitly ignore "security=" when "lsm=" is used on the command line, and report that it is happening. Suggested-by: Tetsuo Handa Signed-off-by: Kees Cook Acked-by: Casey Schaufler Acked-by: John Johansen Signed-off-by: James Morris --- Documentation/admin-guide/kernel-parameters.txt | 10 ++++------ security/security.c | 8 ++++++-- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index e59e1471d4db..91c0251fdb86 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2321,7 +2321,7 @@ lsm=lsm1,...,lsmN [SECURITY] Choose order of LSM initialization. This - overrides CONFIG_LSM. + overrides CONFIG_LSM, and the "security=" parameter. machvec= [IA-64] Force the use of a particular machine-vector (machvec) in a generic kernel. @@ -4094,11 +4094,9 @@ Note: increases power consumption, thus should only be enabled if running jitter sensitive (HPC/RT) workloads. - security= [SECURITY] Choose a security module to enable at boot. - If this boot parameter is not specified, only the first - security module asking for security registration will be - loaded. An invalid security module name will be treated - as if no module has been chosen. + security= [SECURITY] Choose a legacy "major" security module to + enable at boot. This has been deprecated by the + "lsm=" parameter. selinux= [SELINUX] Disable or enable SELinux at boot time. Format: { "0" | "1" } diff --git a/security/security.c b/security/security.c index b6bff646d373..ed9b8cbf21cf 100644 --- a/security/security.c +++ b/security/security.c @@ -288,9 +288,13 @@ static void __init ordered_lsm_init(void) ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms), GFP_KERNEL); - if (chosen_lsm_order) + if (chosen_lsm_order) { + if (chosen_major_lsm) { + pr_info("security= is ignored because it is superseded by lsm=\n"); + chosen_major_lsm = NULL; + } ordered_lsm_parse(chosen_lsm_order, "cmdline"); - else + } else ordered_lsm_parse(builtin_lsm_order, "builtin"); for (lsm = ordered_lsms; *lsm; lsm++) -- cgit v1.2.3-59-g8ed1b From b102c11e1a10e1328c9610665e45dd07bf4a3696 Mon Sep 17 00:00:00 2001 From: Petr Vorel Date: Fri, 1 Mar 2019 00:54:48 +0100 Subject: LSM: Update list of SECURITYFS users in Kconfig Remove modules not using it (SELinux and SMACK aren't the only ones not using it). Signed-off-by: Petr Vorel Signed-off-by: James Morris --- security/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/Kconfig b/security/Kconfig index 9555f4914492..1d6463fb1450 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -40,8 +40,7 @@ config SECURITYFS bool "Enable the securityfs filesystem" help This will build the securityfs filesystem. It is currently used by - the TPM bios character driver and IMA, an integrity provider. It is - not used by SELinux or SMACK. + various security modules (AppArmor, IMA, SafeSetID, TOMOYO, TPM). If you are unsure how to answer this question, answer N. -- cgit v1.2.3-59-g8ed1b From 468e91cecb3218afd684b8c422490dfebe0691bb Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 1 Mar 2019 11:30:26 +0000 Subject: keys: fix missing __user in KEYCTL_PKEY_QUERY The arg5 of KEYCTL_PKEY_QUERY should have a __user pointer tag on it as it is a user pointer. This clears the following sparse warning for this: security/keys/keyctl.c:1755:43: warning: incorrect type in argument 3 (different address spaces) security/keys/keyctl.c:1755:43: expected struct keyctl_pkey_query [noderef] * security/keys/keyctl.c:1755:43: got struct keyctl_pkey_query * Signed-off-by: Ben Dooks Acked-by: Serge Hallyn Signed-off-by: James Morris --- security/keys/keyctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index e8093d025966..5efde255650b 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1751,7 +1751,7 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, return -EINVAL; return keyctl_pkey_query((key_serial_t)arg2, (const char __user *)arg4, - (struct keyctl_pkey_query *)arg5); + (struct keyctl_pkey_query __user *)arg5); case KEYCTL_PKEY_ENCRYPT: case KEYCTL_PKEY_DECRYPT: -- cgit v1.2.3-59-g8ed1b