* [PATCH v2 1/2] selinux: add brief info to policydb
@ 2017-05-05 10:10 Sebastien Buisson
2017-05-05 10:10 ` [PATCH v2 2/2] selinux: expose policy brief via selinuxfs Sebastien Buisson
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Sebastien Buisson @ 2017-05-05 10:10 UTC (permalink / raw)
To: linux-security-module
Add policybrief field to struct policydb. It holds a brief info
of the policydb, in the following form:
<0 or 1 for enforce>:<0 or 1 for checkreqprot>:<hashalg>=<checksum>
Policy brief is computed every time the policy is loaded, and when
enforce or checkreqprot are changed.
Add security_policy_brief hook to give access to policy brief to
the rest of the kernel. Lustre client makes use of this information.
Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
---
include/linux/lsm_hooks.h | 2 +
include/linux/security.h | 7 ++++
security/security.c | 6 +++
security/selinux/hooks.c | 11 +++++-
security/selinux/include/security.h | 2 +
security/selinux/selinuxfs.c | 6 ++-
security/selinux/ss/policydb.c | 70 +++++++++++++++++++++++++++++++++++
security/selinux/ss/policydb.h | 3 ++
security/selinux/ss/services.c | 73 +++++++++++++++++++++++++++++++++++++
9 files changed, 178 insertions(+), 2 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 080f34e..7139a07 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1568,6 +1568,7 @@
int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
+ int (*policy_brief)(char **brief, size_t *len, bool alloc);
#ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect)(struct sock *sock, struct sock *other,
struct sock *newsk);
@@ -1813,6 +1814,7 @@ struct security_hook_heads {
struct list_head inode_notifysecctx;
struct list_head inode_setsecctx;
struct list_head inode_getsecctx;
+ struct list_head policy_brief;
#ifdef CONFIG_SECURITY_NETWORK
struct list_head unix_stream_connect;
struct list_head unix_may_send;
diff --git a/include/linux/security.h b/include/linux/security.h
index af675b5..3b72053 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -377,6 +377,8 @@ int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
+
+int security_policy_brief(char **brief, size_t *len, bool alloc);
#else /* CONFIG_SECURITY */
struct security_mnt_opts {
};
@@ -1166,6 +1168,11 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32
{
return -EOPNOTSUPP;
}
+
+static inline int security_policy_brief(char **brief, size_t *len, bool alloc)
+{
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_SECURITY */
#ifdef CONFIG_SECURITY_NETWORK
diff --git a/security/security.c b/security/security.c
index b9fea39..954b391 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1285,6 +1285,12 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
}
EXPORT_SYMBOL(security_inode_getsecctx);
+int security_policy_brief(char **brief, size_t *len, bool alloc)
+{
+ return call_int_hook(policy_brief, -EOPNOTSUPP, brief, len, alloc);
+}
+EXPORT_SYMBOL(security_policy_brief);
+
#ifdef CONFIG_SECURITY_NETWORK
int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e67a526..b4dd605 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -104,8 +104,10 @@
static int __init enforcing_setup(char *str)
{
unsigned long enforcing;
- if (!kstrtoul(str, 0, &enforcing))
+ if (!kstrtoul(str, 0, &enforcing)) {
selinux_enforcing = enforcing ? 1 : 0;
+ security_policydb_update_info(NULL);
+ }
return 1;
}
__setup("enforcing=", enforcing_setup);
@@ -6063,6 +6065,11 @@ static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
*ctxlen = len;
return 0;
}
+
+static int selinux_policy_brief(char **brief, size_t *len, bool alloc)
+{
+ return security_policydb_brief(brief, len, alloc);
+}
#ifdef CONFIG_KEYS
static int selinux_key_alloc(struct key *k, const struct cred *cred,
@@ -6277,6 +6284,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx),
LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
+ LSM_HOOK_INIT(policy_brief, selinux_policy_brief),
+
LSM_HOOK_INIT(unix_stream_connect, selinux_socket_unix_stream_connect),
LSM_HOOK_INIT(unix_may_send, selinux_socket_unix_may_send),
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index f979c35..167e274 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -97,6 +97,8 @@ enum {
int security_load_policy(void *data, size_t len);
int security_read_policy(void **data, size_t *len);
size_t security_policydb_len(void);
+int security_policydb_brief(char **brief, size_t *len, bool alloc);
+void security_policydb_update_info(void *p);
int security_policycap_supported(unsigned int req_cap);
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index ce71718..b959ee7 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -55,8 +55,10 @@
static int __init checkreqprot_setup(char *str)
{
unsigned long checkreqprot;
- if (!kstrtoul(str, 0, &checkreqprot))
+ if (!kstrtoul(str, 0, &checkreqprot)) {
selinux_checkreqprot = checkreqprot ? 1 : 0;
+ security_policydb_update_info(NULL);
+ }
return 1;
}
__setup("checkreqprot=", checkreqprot_setup);
@@ -159,6 +161,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
selinux_enforcing = new_value;
+ security_policydb_update_info(NULL);
if (selinux_enforcing)
avc_ss_reset(0);
selnl_notify_setenforce(selinux_enforcing);
@@ -621,6 +624,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
goto out;
selinux_checkreqprot = new_value ? 1 : 0;
+ security_policydb_update_info(NULL);
length = count;
out:
kfree(page);
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 0080122..9eb2f82 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -32,6 +32,8 @@
#include <linux/errno.h>
#include <linux/audit.h>
#include <linux/flex_array.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
#include "security.h"
#include "policydb.h"
@@ -879,6 +881,8 @@ void policydb_destroy(struct policydb *p)
ebitmap_destroy(&p->filename_trans_ttypes);
ebitmap_destroy(&p->policycaps);
ebitmap_destroy(&p->permissive_map);
+
+ kfree(p->policybrief);
}
/*
@@ -2220,6 +2224,67 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
}
/*
+ * Compute summary of a policy database binary representation file,
+ * and store it into a policy database structure.
+ */
+static int policydb_brief(struct policydb *policydb, void *ptr)
+{
+ struct policy_file *fp = ptr;
+ struct crypto_shash *tfm;
+ char hashalg[] = "sha256";
+ int hashsize = SHA256_DIGEST_SIZE;
+ char hashval[hashsize];
+ int idx;
+ unsigned char *p;
+
+ if (policydb->policybrief)
+ return -EINVAL;
+
+ tfm = crypto_alloc_shash(hashalg, 0, 0);
+ if (IS_ERR(tfm)) {
+ printk(KERN_ERR "Failed to alloc crypto hash %s\n", hashalg);
+ return PTR_ERR(tfm);
+ }
+
+ {
+ int rc;
+
+ SHASH_DESC_ON_STACK(desc, tfm);
+ desc->tfm = tfm;
+ desc->flags = 0;
+ rc = crypto_shash_init(desc);
+ if (rc) {
+ printk(KERN_ERR "Failed to init shash\n");
+ crypto_free_shash(tfm);
+ return rc;
+ }
+
+ crypto_shash_update(desc, fp->data, fp->len);
+ crypto_shash_final(desc, hashval);
+ crypto_free_shash(tfm);
+ }
+
+ /* policy brief is in the form:
+ * <0 or 1 for enforce>:<0 or 1 for checkreqprot>:<hashalg>=<checksum>
+ */
+ policydb->policybrief = kzalloc(5 + strlen(hashalg) + 2*hashsize + 1,
+ GFP_KERNEL);
+ if (policydb->policybrief == NULL)
+ return -ENOMEM;
+
+ sprintf(policydb->policybrief, "x:x:%s=", hashalg);
+ security_policydb_update_info(policydb);
+ p = policydb->policybrief + strlen(policydb->policybrief);
+ for (idx = 0; idx < hashsize; idx++) {
+ snprintf(p, 3, "%02x", (unsigned char)(hashval[idx]));
+ p += 2;
+ }
+ policydb->policybrief_len = (size_t)(p - policydb->policybrief);
+
+ return 0;
+}
+
+/*
* Read the configuration data from a policy database binary
* representation file into a policy database structure.
*/
@@ -2238,6 +2303,11 @@ int policydb_read(struct policydb *p, void *fp)
if (rc)
return rc;
+ /* Compute sumarry of policy, and store it in policydb */
+ rc = policydb_brief(p, fp);
+ if (rc)
+ goto bad;
+
/* Read the magic number and string length. */
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 725d594..1fca438 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -293,6 +293,9 @@ struct policydb {
size_t len;
unsigned int policyvers;
+ /* summary computed on the policy */
+ unsigned char *policybrief;
+ size_t policybrief_len;
unsigned int reject_unknown : 1;
unsigned int allow_unknown : 1;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 60d9b02..9a94f8e 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2170,6 +2170,79 @@ size_t security_policydb_len(void)
}
/**
+ * security_policydb_brief - Get policydb brief
+ * @brief: pointer to buffer holding brief
+ * @len: in: brief buffer length if no alloc, out: brief string len
+ * @alloc: whether to allocate buffer for brief or not
+ *
+ * On success 0 is returned , or negative value on error.
+ **/
+int security_policydb_brief(char **brief, size_t *len, bool alloc)
+{
+ int rc = 0;
+ size_t policybrief_len;
+
+ if (brief == NULL)
+ return -EINVAL;
+
+ read_lock(&policy_rwlock);
+ policybrief_len = policydb.policybrief_len;
+ if (policydb.policybrief == NULL)
+ rc = -EAGAIN;
+ read_unlock(&policy_rwlock);
+
+ if (rc)
+ return rc;
+
+ if (alloc)
+ /* *brief must be kfreed by caller in this case */
+ *brief = kzalloc(policybrief_len + 1, GFP_KERNEL);
+ else
+ /*
+ * if !alloc, caller must pass a buffer that
+ * can hold policybrief_len+1 chars
+ */
+ if (*len < policybrief_len + 1) {
+ /* put in *len the string size we need to write */
+ *len = policybrief_len;
+ return -ENAMETOOLONG;
+ }
+
+ if (*brief == NULL)
+ return -ENOMEM;
+
+ read_lock(&policy_rwlock);
+ strncpy(*brief, policydb.policybrief, policydb.policybrief_len);
+ *len = policydb.policybrief_len;
+ read_unlock(&policy_rwlock);
+
+ return rc;
+}
+
+void security_policydb_update_info(void *p)
+{
+ /* policy brief is in the form:
+ * <0 or 1 for enforce>:<0 or 1 for checkreqprot>:<hashalg>=<checksum>
+ */
+ if (p) {
+ struct policydb *poldb = p;
+ /* update policydb given as parameter if possible */
+ if (poldb->policybrief) {
+ poldb->policybrief[0] = '0' + selinux_enforcing;
+ poldb->policybrief[2] = '0' + selinux_checkreqprot;
+ }
+ } else {
+ /* update global policydb, needs write lock */
+ write_lock_irq(&policy_rwlock);
+ if (policydb.policybrief) {
+ policydb.policybrief[0] = '0' + selinux_enforcing;
+ policydb.policybrief[2] = '0' + selinux_checkreqprot;
+ }
+ write_unlock_irq(&policy_rwlock);
+ }
+}
+
+/**
* security_port_sid - Obtain the SID for a port.
* @protocol: protocol number
* @port: port number
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] selinux: expose policy brief via selinuxfs
2017-05-05 10:10 [PATCH v2 1/2] selinux: add brief info to policydb Sebastien Buisson
@ 2017-05-05 10:10 ` Sebastien Buisson
2017-05-05 18:33 ` [PATCH v2 1/2] selinux: add brief info to policydb Casey Schaufler
2017-05-05 19:39 ` Stephen Smalley
2 siblings, 0 replies; 5+ messages in thread
From: Sebastien Buisson @ 2017-05-05 10:10 UTC (permalink / raw)
To: linux-security-module
Expose policy brief via selinuxfs.
Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
---
security/selinux/selinuxfs.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index b959ee7..8782b79 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -101,6 +101,7 @@ enum sel_inos {
SEL_STATUS, /* export current status using mmap() */
SEL_POLICY, /* allow userspace to read the in kernel policy */
SEL_VALIDATE_TRANS, /* compute validatetrans decision */
+ SEL_POLICYBRIEF,/* return policy summary */
SEL_INO_NEXT, /* The next inode number to use */
};
@@ -316,6 +317,29 @@ static ssize_t sel_read_policyvers(struct file *filp, char __user *buf,
.llseek = generic_file_llseek,
};
+static ssize_t sel_read_policybrief(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *tmpbuf;
+ size_t len;
+ ssize_t rc;
+
+ rc = security_policydb_brief(&tmpbuf, &len, true);
+ if (rc)
+ return rc;
+
+ rc = simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+
+ kfree(tmpbuf);
+
+ return rc;
+}
+
+static const struct file_operations sel_policybrief_ops = {
+ .read = sel_read_policybrief,
+ .llseek = generic_file_llseek,
+};
+
/* declaration for sel_write_load */
static int sel_make_bools(void);
static int sel_make_classes(void);
@@ -1829,6 +1853,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
[SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
[SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
S_IWUGO},
+ [SEL_POLICYBRIEF] = {"policybrief", &sel_policybrief_ops,
+ S_IRUGO},
/* last one */ {""}
};
ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] selinux: add brief info to policydb
2017-05-05 10:10 [PATCH v2 1/2] selinux: add brief info to policydb Sebastien Buisson
2017-05-05 10:10 ` [PATCH v2 2/2] selinux: expose policy brief via selinuxfs Sebastien Buisson
@ 2017-05-05 18:33 ` Casey Schaufler
2017-05-11 12:59 ` Sebastien Buisson
2017-05-05 19:39 ` Stephen Smalley
2 siblings, 1 reply; 5+ messages in thread
From: Casey Schaufler @ 2017-05-05 18:33 UTC (permalink / raw)
To: linux-security-module
On 5/5/2017 3:10 AM, Sebastien Buisson wrote:
> Add policybrief field to struct policydb. It holds a brief info
> of the policydb, in the following form:
> <0 or 1 for enforce>:<0 or 1 for checkreqprot>:<hashalg>=<checksum>
> Policy brief is computed every time the policy is loaded, and when
> enforce or checkreqprot are changed.
You need to add a description of policy_brief in lsm_hooks.h.
I would like you to describe the expectations Lustre has for
the content of a policy brief in general. That will make it
easier for AppArmor and Smack to support Lustre when the time
comes. How do you see policy_brief being used by a modules
with dynamic policy?
>
> Add security_policy_brief hook to give access to policy brief to
> the rest of the kernel. Lustre client makes use of this information.
>
> Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
> ---
> include/linux/lsm_hooks.h | 2 +
> include/linux/security.h | 7 ++++
> security/security.c | 6 +++
> security/selinux/hooks.c | 11 +++++-
> security/selinux/include/security.h | 2 +
> security/selinux/selinuxfs.c | 6 ++-
> security/selinux/ss/policydb.c | 70 +++++++++++++++++++++++++++++++++++
> security/selinux/ss/policydb.h | 3 ++
> security/selinux/ss/services.c | 73 +++++++++++++++++++++++++++++++++++++
> 9 files changed, 178 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 080f34e..7139a07 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1568,6 +1568,7 @@
> int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
> int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
>
> + int (*policy_brief)(char **brief, size_t *len, bool alloc);
> #ifdef CONFIG_SECURITY_NETWORK
> int (*unix_stream_connect)(struct sock *sock, struct sock *other,
> struct sock *newsk);
> @@ -1813,6 +1814,7 @@ struct security_hook_heads {
> struct list_head inode_notifysecctx;
> struct list_head inode_setsecctx;
> struct list_head inode_getsecctx;
> + struct list_head policy_brief;
> #ifdef CONFIG_SECURITY_NETWORK
> struct list_head unix_stream_connect;
> struct list_head unix_may_send;
> diff --git a/include/linux/security.h b/include/linux/security.h
> index af675b5..3b72053 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -377,6 +377,8 @@ int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
> int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
> int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
> int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
> +
> +int security_policy_brief(char **brief, size_t *len, bool alloc);
> #else /* CONFIG_SECURITY */
> struct security_mnt_opts {
> };
> @@ -1166,6 +1168,11 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32
> {
> return -EOPNOTSUPP;
> }
> +
> +static inline int security_policy_brief(char **brief, size_t *len, bool alloc)
> +{
> + return -EOPNOTSUPP;
> +}
> #endif /* CONFIG_SECURITY */
>
> #ifdef CONFIG_SECURITY_NETWORK
> diff --git a/security/security.c b/security/security.c
> index b9fea39..954b391 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1285,6 +1285,12 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
> }
> EXPORT_SYMBOL(security_inode_getsecctx);
>
> +int security_policy_brief(char **brief, size_t *len, bool alloc)
> +{
> + return call_int_hook(policy_brief, -EOPNOTSUPP, brief, len, alloc);
> +}
> +EXPORT_SYMBOL(security_policy_brief);
> +
> #ifdef CONFIG_SECURITY_NETWORK
>
> int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk)
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e67a526..b4dd605 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -104,8 +104,10 @@
> static int __init enforcing_setup(char *str)
> {
> unsigned long enforcing;
> - if (!kstrtoul(str, 0, &enforcing))
> + if (!kstrtoul(str, 0, &enforcing)) {
> selinux_enforcing = enforcing ? 1 : 0;
> + security_policydb_update_info(NULL);
> + }
> return 1;
> }
> __setup("enforcing=", enforcing_setup);
> @@ -6063,6 +6065,11 @@ static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
> *ctxlen = len;
> return 0;
> }
> +
> +static int selinux_policy_brief(char **brief, size_t *len, bool alloc)
> +{
> + return security_policydb_brief(brief, len, alloc);
> +}
> #ifdef CONFIG_KEYS
>
> static int selinux_key_alloc(struct key *k, const struct cred *cred,
> @@ -6277,6 +6284,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
> LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx),
> LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
>
> + LSM_HOOK_INIT(policy_brief, selinux_policy_brief),
> +
> LSM_HOOK_INIT(unix_stream_connect, selinux_socket_unix_stream_connect),
> LSM_HOOK_INIT(unix_may_send, selinux_socket_unix_may_send),
>
> diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
> index f979c35..167e274 100644
> --- a/security/selinux/include/security.h
> +++ b/security/selinux/include/security.h
> @@ -97,6 +97,8 @@ enum {
> int security_load_policy(void *data, size_t len);
> int security_read_policy(void **data, size_t *len);
> size_t security_policydb_len(void);
> +int security_policydb_brief(char **brief, size_t *len, bool alloc);
> +void security_policydb_update_info(void *p);
>
> int security_policycap_supported(unsigned int req_cap);
>
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index ce71718..b959ee7 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -55,8 +55,10 @@
> static int __init checkreqprot_setup(char *str)
> {
> unsigned long checkreqprot;
> - if (!kstrtoul(str, 0, &checkreqprot))
> + if (!kstrtoul(str, 0, &checkreqprot)) {
> selinux_checkreqprot = checkreqprot ? 1 : 0;
> + security_policydb_update_info(NULL);
> + }
> return 1;
> }
> __setup("checkreqprot=", checkreqprot_setup);
> @@ -159,6 +161,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
> from_kuid(&init_user_ns, audit_get_loginuid(current)),
> audit_get_sessionid(current));
> selinux_enforcing = new_value;
> + security_policydb_update_info(NULL);
> if (selinux_enforcing)
> avc_ss_reset(0);
> selnl_notify_setenforce(selinux_enforcing);
> @@ -621,6 +624,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
> goto out;
>
> selinux_checkreqprot = new_value ? 1 : 0;
> + security_policydb_update_info(NULL);
> length = count;
> out:
> kfree(page);
> diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
> index 0080122..9eb2f82 100644
> --- a/security/selinux/ss/policydb.c
> +++ b/security/selinux/ss/policydb.c
> @@ -32,6 +32,8 @@
> #include <linux/errno.h>
> #include <linux/audit.h>
> #include <linux/flex_array.h>
> +#include <crypto/hash.h>
> +#include <crypto/sha.h>
> #include "security.h"
>
> #include "policydb.h"
> @@ -879,6 +881,8 @@ void policydb_destroy(struct policydb *p)
> ebitmap_destroy(&p->filename_trans_ttypes);
> ebitmap_destroy(&p->policycaps);
> ebitmap_destroy(&p->permissive_map);
> +
> + kfree(p->policybrief);
> }
>
> /*
> @@ -2220,6 +2224,67 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
> }
>
> /*
> + * Compute summary of a policy database binary representation file,
> + * and store it into a policy database structure.
> + */
> +static int policydb_brief(struct policydb *policydb, void *ptr)
> +{
> + struct policy_file *fp = ptr;
> + struct crypto_shash *tfm;
> + char hashalg[] = "sha256";
> + int hashsize = SHA256_DIGEST_SIZE;
> + char hashval[hashsize];
> + int idx;
> + unsigned char *p;
> +
> + if (policydb->policybrief)
> + return -EINVAL;
> +
> + tfm = crypto_alloc_shash(hashalg, 0, 0);
> + if (IS_ERR(tfm)) {
> + printk(KERN_ERR "Failed to alloc crypto hash %s\n", hashalg);
> + return PTR_ERR(tfm);
> + }
> +
> + {
> + int rc;
> +
> + SHASH_DESC_ON_STACK(desc, tfm);
> + desc->tfm = tfm;
> + desc->flags = 0;
> + rc = crypto_shash_init(desc);
> + if (rc) {
> + printk(KERN_ERR "Failed to init shash\n");
> + crypto_free_shash(tfm);
> + return rc;
> + }
> +
> + crypto_shash_update(desc, fp->data, fp->len);
> + crypto_shash_final(desc, hashval);
> + crypto_free_shash(tfm);
> + }
> +
> + /* policy brief is in the form:
> + * <0 or 1 for enforce>:<0 or 1 for checkreqprot>:<hashalg>=<checksum>
> + */
> + policydb->policybrief = kzalloc(5 + strlen(hashalg) + 2*hashsize + 1,
> + GFP_KERNEL);
> + if (policydb->policybrief == NULL)
> + return -ENOMEM;
> +
> + sprintf(policydb->policybrief, "x:x:%s=", hashalg);
> + security_policydb_update_info(policydb);
> + p = policydb->policybrief + strlen(policydb->policybrief);
> + for (idx = 0; idx < hashsize; idx++) {
> + snprintf(p, 3, "%02x", (unsigned char)(hashval[idx]));
> + p += 2;
> + }
> + policydb->policybrief_len = (size_t)(p - policydb->policybrief);
> +
> + return 0;
> +}
> +
> +/*
> * Read the configuration data from a policy database binary
> * representation file into a policy database structure.
> */
> @@ -2238,6 +2303,11 @@ int policydb_read(struct policydb *p, void *fp)
> if (rc)
> return rc;
>
> + /* Compute sumarry of policy, and store it in policydb */
> + rc = policydb_brief(p, fp);
> + if (rc)
> + goto bad;
> +
> /* Read the magic number and string length. */
> rc = next_entry(buf, fp, sizeof(u32) * 2);
> if (rc)
> diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
> index 725d594..1fca438 100644
> --- a/security/selinux/ss/policydb.h
> +++ b/security/selinux/ss/policydb.h
> @@ -293,6 +293,9 @@ struct policydb {
> size_t len;
>
> unsigned int policyvers;
> + /* summary computed on the policy */
> + unsigned char *policybrief;
> + size_t policybrief_len;
>
> unsigned int reject_unknown : 1;
> unsigned int allow_unknown : 1;
> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
> index 60d9b02..9a94f8e 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -2170,6 +2170,79 @@ size_t security_policydb_len(void)
> }
>
> /**
> + * security_policydb_brief - Get policydb brief
> + * @brief: pointer to buffer holding brief
> + * @len: in: brief buffer length if no alloc, out: brief string len
> + * @alloc: whether to allocate buffer for brief or not
> + *
> + * On success 0 is returned , or negative value on error.
> + **/
> +int security_policydb_brief(char **brief, size_t *len, bool alloc)
> +{
> + int rc = 0;
> + size_t policybrief_len;
> +
> + if (brief == NULL)
> + return -EINVAL;
> +
> + read_lock(&policy_rwlock);
> + policybrief_len = policydb.policybrief_len;
> + if (policydb.policybrief == NULL)
> + rc = -EAGAIN;
> + read_unlock(&policy_rwlock);
> +
> + if (rc)
> + return rc;
> +
> + if (alloc)
> + /* *brief must be kfreed by caller in this case */
> + *brief = kzalloc(policybrief_len + 1, GFP_KERNEL);
> + else
> + /*
> + * if !alloc, caller must pass a buffer that
> + * can hold policybrief_len+1 chars
> + */
> + if (*len < policybrief_len + 1) {
> + /* put in *len the string size we need to write */
> + *len = policybrief_len;
> + return -ENAMETOOLONG;
> + }
> +
> + if (*brief == NULL)
> + return -ENOMEM;
> +
> + read_lock(&policy_rwlock);
> + strncpy(*brief, policydb.policybrief, policydb.policybrief_len);
> + *len = policydb.policybrief_len;
> + read_unlock(&policy_rwlock);
> +
> + return rc;
> +}
> +
> +void security_policydb_update_info(void *p)
> +{
> + /* policy brief is in the form:
> + * <0 or 1 for enforce>:<0 or 1 for checkreqprot>:<hashalg>=<checksum>
> + */
> + if (p) {
> + struct policydb *poldb = p;
> + /* update policydb given as parameter if possible */
> + if (poldb->policybrief) {
> + poldb->policybrief[0] = '0' + selinux_enforcing;
> + poldb->policybrief[2] = '0' + selinux_checkreqprot;
> + }
> + } else {
> + /* update global policydb, needs write lock */
> + write_lock_irq(&policy_rwlock);
> + if (policydb.policybrief) {
> + policydb.policybrief[0] = '0' + selinux_enforcing;
> + policydb.policybrief[2] = '0' + selinux_checkreqprot;
> + }
> + write_unlock_irq(&policy_rwlock);
> + }
> +}
> +
> +/**
> * security_port_sid - Obtain the SID for a port.
> * @protocol: protocol number
> * @port: port number
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] selinux: add brief info to policydb
2017-05-05 10:10 [PATCH v2 1/2] selinux: add brief info to policydb Sebastien Buisson
2017-05-05 10:10 ` [PATCH v2 2/2] selinux: expose policy brief via selinuxfs Sebastien Buisson
2017-05-05 18:33 ` [PATCH v2 1/2] selinux: add brief info to policydb Casey Schaufler
@ 2017-05-05 19:39 ` Stephen Smalley
2 siblings, 0 replies; 5+ messages in thread
From: Stephen Smalley @ 2017-05-05 19:39 UTC (permalink / raw)
To: linux-security-module
On Fri, 2017-05-05 at 19:10 +0900, Sebastien Buisson wrote:
> Add policybrief field to struct policydb. It holds a brief info
> of the policydb, in the following form:
> <0 or 1 for enforce>:<0 or 1 for checkreqprot>:<hashalg>=<checksum>
> Policy brief is computed every time the policy is loaded, and when
> enforce or checkreqprot are changed.
>
> Add security_policy_brief hook to give access to policy brief to
> the rest of the kernel. Lustre client makes use of this information.
>
> Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
> ---
> ?include/linux/lsm_hooks.h???????????|??2 +
> ?include/linux/security.h????????????|??7 ++++
> ?security/security.c?????????????????|??6 +++
> ?security/selinux/hooks.c????????????| 11 +++++-
> ?security/selinux/include/security.h |??2 +
> ?security/selinux/selinuxfs.c????????|??6 ++-
> ?security/selinux/ss/policydb.c??????| 70
> +++++++++++++++++++++++++++++++++++
> ?security/selinux/ss/policydb.h??????|??3 ++
> ?security/selinux/ss/services.c??????| 73
> +++++++++++++++++++++++++++++++++++++
> ?9 files changed, 178 insertions(+), 2 deletions(-)
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e67a526..b4dd605 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -104,8 +104,10 @@
> ?static int __init enforcing_setup(char *str)
> ?{
> ? unsigned long enforcing;
> - if (!kstrtoul(str, 0, &enforcing))
> + if (!kstrtoul(str, 0, &enforcing)) {
> ? selinux_enforcing = enforcing ? 1 : 0;
> + security_policydb_update_info(NULL);
I don't think you need this. You are unlikely to request the policy
brief until after policy has been loaded (and if you do, you'll only
get a partial result), so you can just defer setting up the enforcing
field until the initial policy load, and then update it on subsequent
writes to /sys/fs/selinux/enforce.
> + }
> ? return 1;
> ?}
> ?__setup("enforcing=", enforcing_setup);
> diff --git a/security/selinux/selinuxfs.c
> b/security/selinux/selinuxfs.c
> index ce71718..b959ee7 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -55,8 +55,10 @@
> ?static int __init checkreqprot_setup(char *str)
> ?{
> ? unsigned long checkreqprot;
> - if (!kstrtoul(str, 0, &checkreqprot))
> + if (!kstrtoul(str, 0, &checkreqprot)) {
> ? selinux_checkreqprot = checkreqprot ? 1 : 0;
> + security_policydb_update_info(NULL);
> + }
Ditto. Just initialize it with the rest of the info on initial policy
load, and update it on writes to checkreqprot.
> ? return 1;
> ?}
> ?__setup("checkreqprot=", checkreqprot_setup);
> diff --git a/security/selinux/ss/policydb.c
> b/security/selinux/ss/policydb.c
> index 0080122..9eb2f82 100644
> --- a/security/selinux/ss/policydb.c
> +++ b/security/selinux/ss/policydb.c
> @@ -32,6 +32,8 @@
> ?#include <linux/errno.h>
> ?#include <linux/audit.h>
> ?#include <linux/flex_array.h>
> +#include <crypto/hash.h>
> +#include <crypto/sha.h>
> ?#include "security.h"
> ?
> ?#include "policydb.h"
> @@ -879,6 +881,8 @@ void policydb_destroy(struct policydb *p)
> ? ebitmap_destroy(&p->filename_trans_ttypes);
> ? ebitmap_destroy(&p->policycaps);
> ? ebitmap_destroy(&p->permissive_map);
> +
> + kfree(p->policybrief);
> ?}
> ?
> ?/*
> @@ -2220,6 +2224,67 @@ static int ocontext_read(struct policydb *p,
> struct policydb_compat_info *info,
> ?}
> ?
> ?/*
> + * Compute summary of a policy database binary representation file,
> + * and store it into a policy database structure.
> + */
> +static int policydb_brief(struct policydb *policydb, void *ptr)
> +{
> + struct policy_file *fp = ptr;
> + struct crypto_shash *tfm;
> + char hashalg[] = "sha256";
> + int hashsize = SHA256_DIGEST_SIZE;
size_t
> + char hashval[hashsize];
unsigned char or u8
> + int idx;
> + unsigned char *p;
> +
> + if (policydb->policybrief)
> + return -EINVAL;
> +
> + tfm = crypto_alloc_shash(hashalg, 0, 0);
> + if (IS_ERR(tfm)) {
> + printk(KERN_ERR "Failed to alloc crypto hash %s\n",
> hashalg);
> + return PTR_ERR(tfm);
> + }
Should you be checking crypto_shash_digestsize(tfm) against sizeof
hashval, or allocating hashval based on it instead? Not a problem now,
but if you ever change the algorithm and forget to update the
hashsize...
> +
> + {
> + int rc;
> +
> + SHASH_DESC_ON_STACK(desc, tfm);
> + desc->tfm = tfm;
> + desc->flags = 0;
> + rc = crypto_shash_init(desc);
> + if (rc) {
> + printk(KERN_ERR "Failed to init shash\n");
> + crypto_free_shash(tfm);
> + return rc;
> + }
> +
> + crypto_shash_update(desc, fp->data, fp->len);
> + crypto_shash_final(desc, hashval);
Just use crypto_shash_digest(); handles _init, _update, and _final for
you in one call.
> + crypto_free_shash(tfm);
> + }
> +
> + /* policy brief is in the form:
> + ?* <0 or 1 for enforce>:<0 or 1 for
> checkreqprot>:<hashalg>=<checksum>
> + ?*/
> + policydb->policybrief = kzalloc(5 + strlen(hashalg) +
> 2*hashsize + 1,
> + GFP_KERNEL);
> + if (policydb->policybrief == NULL)
> + return -ENOMEM;
> +
> + sprintf(policydb->policybrief, "x:x:%s=", hashalg);
Couldn't you just directly set the enforcing and checkreqprot fields
above? No need to call another function.
> + security_policydb_update_info(policydb);
> + p = policydb->policybrief + strlen(policydb->policybrief);
> + for (idx = 0; idx < hashsize; idx++) {
> + snprintf(p, 3, "%02x", (unsigned
> char)(hashval[idx]));
No need to cast if you fix the type above.
> + p += 2;
> + }
> + policydb->policybrief_len = (size_t)(p - policydb-
> >policybrief);
This length is actually computable at build time, right? It isn't
variant based on policy, just on hash algorithm. No need to store it
in the policydb.
> +
> + return 0;
> +}
> +
> +/*
> ? * Read the configuration data from a policy database binary
> ? * representation file into a policy database structure.
> ? */
> @@ -2238,6 +2303,11 @@ int policydb_read(struct policydb *p, void
> *fp)
> ? if (rc)
> ? return rc;
> ?
> + /* Compute sumarry of policy, and store it in policydb */
summary
> + rc = policydb_brief(p, fp);
> + if (rc)
> + goto bad;
> +
> ? /* Read the magic number and string length. */
> ? rc = next_entry(buf, fp, sizeof(u32) * 2);
> ? if (rc)
> diff --git a/security/selinux/ss/services.c
> b/security/selinux/ss/services.c
> index 60d9b02..9a94f8e 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -2170,6 +2170,79 @@ size_t security_policydb_len(void)
> ?}
> ?
> ?/**
> + * security_policydb_brief - Get policydb brief
> + * @brief: pointer to buffer holding brief
> + * @len: in: brief buffer length if no alloc, out: brief string len
> + * @alloc: whether to allocate buffer for brief or not
> + *
> + * On success 0 is returned , or negative value on error.
> + **/
> +int security_policydb_brief(char **brief, size_t *len, bool alloc)
> +{
> + int rc = 0;
> + size_t policybrief_len;
> +
> + if (brief == NULL)
> + return -EINVAL;
You can return immediately if !ss_initialized.
> +
> + read_lock(&policy_rwlock);
> + policybrief_len = policydb.policybrief_len;
The length is fixed by the hash algorithm. No need to fetch it.
> + if (policydb.policybrief == NULL)
> + rc = -EAGAIN;
This shouldn't ever be possible if ss_initialized, right?
> + read_unlock(&policy_rwlock);
> +
> + if (rc)
> + return rc;
> +
> + if (alloc)
> + /* *brief must be kfreed by caller in this case */
> + *brief = kzalloc(policybrief_len + 1, GFP_KERNEL);
> + else
> + /*
> + ?* if !alloc, caller must pass a buffer that
> + ?* can hold policybrief_len+1 chars
> + ?*/
> + if (*len < policybrief_len + 1) {
> + /* put in *len the string size we need to
> write */
> + *len = policybrief_len;
> + return -ENAMETOOLONG;
> + }
> +
> + if (*brief == NULL)
> + return -ENOMEM;
> +
> + read_lock(&policy_rwlock);
> + strncpy(*brief, policydb.policybrief,
> policydb.policybrief_len);
> + *len = policydb.policybrief_len;
> + read_unlock(&policy_rwlock);
> +
> + return rc;
> +}
> +
> +void security_policydb_update_info(void *p)
> +{
> + /* policy brief is in the form:
> + ?* <0 or 1 for enforce>:<0 or 1 for
> checkreqprot>:<hashalg>=<checksum>
> + ?*/
if (!ss_initialized)
return;
> + if (p) {
> + struct policydb *poldb = p;
> + /* update policydb given as parameter if possible */
> + if (poldb->policybrief) {
> + poldb->policybrief[0] = '0' +
> selinux_enforcing;
> + poldb->policybrief[2] = '0' +
> selinux_checkreqprot;
This case could be handled directly in the caller.
> + }
> + } else {
> + /* update global policydb, needs write lock */
> + write_lock_irq(&policy_rwlock);
> + if (policydb.policybrief) {
Don't need this once ss_initialized is set.
> + policydb.policybrief[0] = '0' +
> selinux_enforcing;
> + policydb.policybrief[2] = '0' +
> selinux_checkreqprot;
> + }
Technically only need to update one of the two fields at any given
time, and the caller can specify which one. But maybe it isn't worth
it.
> + write_unlock_irq(&policy_rwlock);
> + }
> +}
> +
> +/**
> ? * security_port_sid - Obtain the SID for a port.
> ? * @protocol: protocol number
> ? * @port: port number
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] selinux: add brief info to policydb
2017-05-05 18:33 ` [PATCH v2 1/2] selinux: add brief info to policydb Casey Schaufler
@ 2017-05-11 12:59 ` Sebastien Buisson
0 siblings, 0 replies; 5+ messages in thread
From: Sebastien Buisson @ 2017-05-11 12:59 UTC (permalink / raw)
To: linux-security-module
2017-05-05 20:33 GMT+02:00 Casey Schaufler <casey@schaufler-ca.com>:
> How do you see policy_brief being used by a modules
> with dynamic policy?
Policy's brief is computed on the binary representation every time the
policy is loaded, and when enforce or checkreqprot are changed. It
should not be a problem with a dynamic policy, unless I am missing
something.
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2017-05-11 12:59 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-05-05 10:10 [PATCH v2 1/2] selinux: add brief info to policydb Sebastien Buisson
2017-05-05 10:10 ` [PATCH v2 2/2] selinux: expose policy brief via selinuxfs Sebastien Buisson
2017-05-05 18:33 ` [PATCH v2 1/2] selinux: add brief info to policydb Casey Schaufler
2017-05-11 12:59 ` Sebastien Buisson
2017-05-05 19:39 ` Stephen Smalley
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).