linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: sds@tycho.nsa.gov (Stephen Smalley)
To: linux-security-module@vger.kernel.org
Subject: [PATCH v3 1/2] selinux: add brief info to policydb
Date: Thu, 11 May 2017 10:37:46 -0400	[thread overview]
Message-ID: <1494513466.10447.3.camel@tycho.nsa.gov> (raw)
In-Reply-To: <1494507551-4643-1-git-send-email-sbuisson@ddn.com>

On Thu, 2017-05-11 at 21:59 +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
> to detect changes to the policy, and forward it to Lustre servers.
> Depending on how the policy is enforced on Lustre client side,
> Lustre servers can refuse connection.
> 
> Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
> ---
> ?include/linux/lsm_hooks.h???????????| 16 ++++++++
> ?include/linux/security.h????????????|??7 ++++
> ?security/security.c?????????????????|??6 +++
> ?security/selinux/hooks.c????????????|??7 ++++
> ?security/selinux/include/security.h |??2 +
> ?security/selinux/selinuxfs.c????????|??2 +
> ?security/selinux/ss/policydb.c??????| 76
> +++++++++++++++++++++++++++++++++++++
> ?security/selinux/ss/policydb.h??????|??2 +
> ?security/selinux/ss/services.c??????| 62
> ++++++++++++++++++++++++++++++
> ?9 files changed, 180 insertions(+)
> 
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 080f34e..9cac282 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1336,6 +1336,20 @@
> ? *	@inode we wish to get the security context of.
> ? *	@ctx is a pointer in which to place the allocated security
> context.
> ? *	@ctxlen points to the place to put the length of @ctx.
> + *
> + * Security hooks for policy brief
> + *
> + * @policy_brief:
> + *
> + *	Returns a string containing a brief info of the policydb,
> in the
> + *	following form:
> + *	<0 or 1 for enforce>:<0 or 1 for
> checkreqprot>:<hashalg>=<checksum>
> + *
> + *	@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.
> + *
> ? * This is the main security structure.
> ? */
> ?
> @@ -1568,6 +1582,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 +1828,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..da245e8 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -6063,6 +6063,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 +6282,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..a0d4d7d 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(u32 requested);
> ?
> ?int security_policycap_supported(unsigned int req_cap);
> ?
> diff --git a/security/selinux/selinuxfs.c
> b/security/selinux/selinuxfs.c
> index 50062e7..8c9f5b7 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -159,6 +159,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(SECURITY__SETENFORCE);
> ?		if (selinux_enforcing)
> ?			avc_ss_reset(0);
> ?		selnl_notify_setenforce(selinux_enforcing);
> @@ -621,6 +622,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(SECURITY__SETCHECKREQPROT);
> ?	length = count;
> ?out:
> ?	kfree(page);
> diff --git a/security/selinux/ss/policydb.c
> b/security/selinux/ss/policydb.c
> index 0080122..58e73f5 100644
> --- a/security/selinux/ss/policydb.c
> +++ b/security/selinux/ss/policydb.c
> @@ -32,11 +32,13 @@
> ?#include <linux/errno.h>
> ?#include <linux/audit.h>
> ?#include <linux/flex_array.h>
> +#include <crypto/hash.h>
> ?#include "security.h"
> ?
> ?#include "policydb.h"
> ?#include "conditional.h"
> ?#include "mls.h"
> +#include "objsec.h"
> ?#include "services.h"
> ?
> ?#define _DEBUG_HASHES
> @@ -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,73 @@ 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";

const

> +	size_t hashsize;
> +	u8 *hashval;
> +	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);
> +	}
> +
> +	hashsize = crypto_shash_digestsize(tfm);

Let's do the crypto_alloc_shash() and crypto_shash_digestsize() once
during init, e.g. see init_profile_hash() in
security/apparmor/crypto.c.

> +	hashval = kmalloc(hashsize, GFP_KERNEL);
> +	if (hashval == NULL) {
> +		crypto_free_shash(tfm);
> +		return -ENOMEM;
> +	}
> +
> +	{
> +		int rc;
> +
> +		SHASH_DESC_ON_STACK(desc, tfm);
> +		desc->tfm = tfm;
> +		desc->flags = 0;
> +		rc = crypto_shash_digest(desc, fp->data, fp->len,
> hashval);
> +		crypto_free_shash(tfm);
> +		if (rc) {
> +			printk(KERN_ERR "Failed crypto_shash_digest:
> %d\n", rc);
> +			kfree(hashval);
> +			return rc;
> +		}
> +	}
> +
> +	/* 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,

We can also compute and save this size once during init; it won't ever
change at runtime.

> +					GFP_KERNEL);
> +	if (policydb->policybrief == NULL) {
> +		kfree(hashval);
> +		return -ENOMEM;
> +	}
> +
> +	sprintf(policydb->policybrief, "%d:%d:%s=",
> +		selinux_enforcing, selinux_checkreqprot, hashalg);
> +	p = policydb->policybrief + strlen(policydb->policybrief);
> +	for (idx = 0; idx < hashsize; idx++) {
> +		snprintf(p, 3, "%02x", hashval[idx]);
> +		p += 2;
> +	}
> +	kfree(hashval);
> +
> +	return 0;
> +}
> +
> +/*
> ? * Read the configuration data from a policy database binary
> ? * representation file into a policy database structure.
> ? */
> @@ -2238,6 +2309,11 @@ int policydb_read(struct policydb *p, void
> *fp)
> ?	if (rc)
> ?		return rc;
> ?
> +	/* Compute summary 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..31689d2f 100644
> --- a/security/selinux/ss/policydb.h
> +++ b/security/selinux/ss/policydb.h
> @@ -293,6 +293,8 @@ struct policydb {
> ?	size_t len;
> ?
> ?	unsigned int policyvers;
> +	/* summary computed on the policy */
> +	unsigned char *policybrief;
> ?
> ?	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..3bbe649 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -2170,6 +2170,68 @@ 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)
> +{
> +	size_t policybrief_len;
> +
> +	if (!ss_initialized || brief == NULL)
> +		return -EINVAL;
> +
> +	read_lock(&policy_rwlock);
> +	policybrief_len = strlen(policydb.policybrief);
> +	read_unlock(&policy_rwlock);

We don't need to compute this via strlen(); we can precompute during
init.  Then we don't need to fetch anything here.

> +
> +	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);
> +	*len = strlen(policydb.policybrief);
> +	strncpy(*brief, policydb.policybrief, *len);

If in fact the length could change at runtime, then this would be
unsafe, since it could have changed between dropping the lock and re-
acquiring it.  But this doesn't matter; we don't need to compute this
at runtime.

> +	read_unlock(&policy_rwlock);
> +
> +	return 0;
> +}
> +
> +void security_policydb_update_info(u32 requested)
> +{
> +	/* policy brief is in the form:
> +	?* <0 or 1 for enforce>:<0 or 1 for
> checkreqprot>:<hashalg>=<checksum>
> +	?*/
> +
> +	if (!ss_initialized)
> +		return;
> +
> +	/* update global policydb, needs write lock */
> +	write_lock_irq(&policy_rwlock);
> +	if (requested == SECURITY__SETENFORCE)
> +		policydb.policybrief[0] = '0' + selinux_enforcing;
> +	else if (requested == SECURITY__SETCHECKREQPROT)
> +		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

  parent reply	other threads:[~2017-05-11 14:37 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-11 12:59 [PATCH v3 1/2] selinux: add brief info to policydb Sebastien Buisson
2017-05-11 12:59 ` [PATCH v3 2/2] selinux: expose policy brief via selinuxfs Sebastien Buisson
2017-05-11 14:37 ` Stephen Smalley [this message]
2017-05-11 15:56 ` [PATCH v3 1/2] selinux: add brief info to policydb Casey Schaufler
2017-05-11 20:22   ` Stephen Smalley
2017-05-11 20:45     ` Casey Schaufler
2017-05-12 18:38       ` Roberts, William C
2017-05-12 22:22       ` Paul Moore
2017-05-12 22:55         ` William Roberts

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1494513466.10447.3.camel@tycho.nsa.gov \
    --to=sds@tycho.nsa.gov \
    --cc=linux-security-module@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).