[RFC PATCH 28/30] ima: Load per ima namespace x509 certificate

From: krzysztof.struczynski
Date: Tue Aug 18 2020 - 11:52:16 EST


From: Krzysztof Struczynski <krzysztof.struczynski@xxxxxxxxxx>

If configured, load the x509 certificate when the first process is
born into the new ima namespace. User can set the path to the
certificate by writing to the x509_for_children entry in the ima
securityfs.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@xxxxxxxxxx>
---
security/integrity/digsig.c | 6 +--
security/integrity/ima/ima_ns.c | 69 ++++++++++++++++++++++++++++-----
security/integrity/integrity.h | 2 +-
3 files changed, 63 insertions(+), 14 deletions(-)

diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 523fc786c4d7..8cd54bc83892 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -170,8 +170,8 @@ int __init integrity_init_keyring(const unsigned int id)
return __integrity_init_keyring(id, perm, restriction);
}

-int __init integrity_add_key(const unsigned int id, const void *data,
- off_t size, key_perm_t perm)
+int integrity_add_key(const unsigned int id, const void *data,
+ off_t size, key_perm_t perm)
{
key_ref_t key;
int rc = 0;
@@ -195,7 +195,7 @@ int __init integrity_add_key(const unsigned int id, const void *data,

}

-int __init integrity_load_x509(const unsigned int id, const char *path)
+int integrity_load_x509(const unsigned int id, const char *path)
{
void *data;
loff_t size;
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 872dc6a96a96..11e1d896f603 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -48,6 +48,30 @@ static void dec_ima_namespaces(struct ucounts *ucounts)
return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES);
}

+#ifdef CONFIG_IMA_LOAD_X509
+static int ima_ns_load_x509(struct ima_namespace *ima_ns)
+{
+ int res = 0;
+ int unset_flags =
+ ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE;
+
+ if (!ima_ns->x509_path_for_children)
+ return res;
+
+ ima_ns->policy_data->ima_policy_flag &= ~unset_flags;
+ res = integrity_load_x509(INTEGRITY_KEYRING_IMA,
+ ima_ns->x509_path_for_children);
+ ima_ns->policy_data->ima_policy_flag |= unset_flags;
+
+ return res;
+}
+#else
+static inline int ima_ns_load_x509(struct ima_namespace *ima_ns)
+{
+ return 0;
+}
+#endif
+
static struct ima_namespace *ima_ns_alloc(void)
{
struct ima_namespace *ima_ns;
@@ -365,6 +389,10 @@ static int imans_activate(struct ima_namespace *ima_ns)
if (res < 0)
goto out;

+ res = ima_ns_load_x509(ima_ns);
+ if (res < 0)
+ goto out;
+
ima_ns->frozen = true;

/* Set current last element as list's head */
@@ -388,6 +416,7 @@ static int imans_install(struct nsset *nsset, struct ns_common *new)
int res;
struct nsproxy *nsproxy = nsset->nsproxy;
struct ima_namespace *ns = to_ima_ns(new);
+ struct ima_namespace *old_ns = nsproxy->ima_ns;

if (!current_is_single_threaded())
return -EUSERS;
@@ -396,14 +425,24 @@ static int imans_install(struct nsset *nsset, struct ns_common *new)
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
return -EPERM;

- res = imans_activate(ns);
- if (res)
- return res;
-
get_ima_ns(ns);
- put_ima_ns(nsproxy->ima_ns);
+ put_ima_ns(old_ns);
nsproxy->ima_ns = ns;

+ /* The activation has to take place, after attaching the new namespace
+ * to the nsproxy. This is because one part of the activation process,
+ * is loading the appraisal keys, which temporary disables appraisal in
+ * the current ima namespace, and it must not happen for the "old" ima
+ * namespace.
+ */
+ res = imans_activate(ns);
+ if (res) {
+ get_ima_ns(old_ns);
+ put_ima_ns(ns);
+ nsproxy->ima_ns = old_ns;
+ return res;
+ }
+
get_ima_ns(ns);
put_ima_ns(nsproxy->ima_ns_for_children);
nsproxy->ima_ns_for_children = ns;
@@ -416,6 +455,7 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk,
{
int res;
struct ima_namespace *ima_ns = nsproxy->ima_ns_for_children;
+ struct ima_namespace *old_ima_ns = nsproxy->ima_ns;

/* create_new_namespaces() already incremented the ref counter */
if (nsproxy->ima_ns == ima_ns)
@@ -431,14 +471,23 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk,
return res;
}

- res = imans_activate(ima_ns);
- if (res)
- return res;
-
get_ima_ns(ima_ns);
- put_ima_ns(nsproxy->ima_ns);
+ put_ima_ns(old_ima_ns);
nsproxy->ima_ns = ima_ns;

+ /* The activation has to take place, after attaching the new namespace
+ * to the nsproxy. This is because one part of the activation process,
+ * is loading the appraisal keys, which temporary disables appraisal in
+ * the current ima namespace, and it must not happen for the "old" ima
+ * namespace.
+ */
+ res = imans_activate(ima_ns);
+ if (res) {
+ get_ima_ns(old_ima_ns);
+ put_ima_ns(ima_ns);
+ nsproxy->ima_ns = old_ima_ns;
+ }
+
return res;
}

diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 207a1aef28e4..9b080b9fe242 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -179,7 +179,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
int integrity_modsig_verify(unsigned int id, const struct modsig *modsig);

int __init integrity_init_keyring(const unsigned int id);
-int __init integrity_load_x509(const unsigned int id, const char *path);
+int integrity_load_x509(const unsigned int id, const char *path);
int __init integrity_load_cert(const unsigned int id, const char *source,
const void *data, size_t len, key_perm_t perm);
#else
--
2.20.1