[PATCH v3 1/2] selinux: add brief info to policydb

Roberts, William C william.c.roberts at intel.com
Fri May 12 18:38:20 UTC 2017



> -----Original Message-----
> From: owner-linux-security-module at vger.kernel.org [mailto:owner-linux-
> security-module at vger.kernel.org] On Behalf Of Casey Schaufler
> Sent: Thursday, May 11, 2017 1:46 PM
> To: Stephen Smalley <sds at tycho.nsa.gov>; Sebastien Buisson
> <sbuisson.ddn at gmail.com>; linux-security-module at vger.kernel.org; linux-
> kernel at vger.kernel.org; selinux at tycho.nsa.gov
> Cc: Sebastien Buisson <sbuisson at ddn.com>; james.l.morris at oracle.com
> Subject: Re: [PATCH v3 1/2] selinux: add brief info to policydb
> 
> On 5/11/2017 1:22 PM, Stephen Smalley wrote:
> > On Thu, 2017-05-11 at 08:56 -0700, Casey Schaufler wrote:
> >> On 5/11/2017 5:59 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.
> >>>
> >>> 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 at 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>
> >> This sure looks like SELinux specific information. If the Spiffy
> >> security module has multiple values for enforcement (e.g. off, soft
> >> and hard) this interface definition does not work. What is a
> >> "checkreqprot", and what is it for?
> >>
> >> I expect that you have no interest (or incentive) in supporting
> >> security modules other than SELinux, and that's OK. What's I'm after
> >> is an interface that another security module could use if someone
> >> where interested (or inspired) to do so.
> >>
> >> Rather than a string with predefined positional values (something I
> >> was taught not to do when 1 MIPS and 1 MEG was a big computer) you
> >> might use "enforce=<value>:checkreqprot=<value>:hashalg=<checksum>"
> > No objection to the above, although it makes his updating code for
> > enforce/checkreqprot a bit uglier.
> 
> Sure, but can you imagine trying to use find(1) if the options where positional?
> 
> 
> >> for SELinux and define @policy_brief as
> >>
> >> 	A string containing colon separated name and value pairs
> >> 	that will be parsed and interpreted by the security module
> >> 	or modules.
> > Actually, I'm not clear it will be parsed or interpreted by the
> > security module(s).  I think he is just fetching it and then doing a
> > simple comparison to check for inconsistencies between clients and the
> > server, and optionally admins/users can read it and interpret it as
> > they see fit.
> 
> OK, in which case human eyes *need* the name as well as the value.
> That, and strcmp(value, "enforce=0") is no harder than strcmp(value, "0").
> 
> >> You already have it right for the "hashalg" field. If you want to be
> >> really forward looking you could use names field names that identify
> >> the security module that uses them, such as
> >>
> >> 	"selinux/enforce=1:selinux/checkreqprot=0:selinux/MD5=8675309"
> > That seems a bit verbose, particularly the duplicated selinux/ bit.
> 
> True that, on the other hand
> 
> 	"selinux(enforce=1:checkreqprot=0:MD5=8675309)"
> 
> would be harder to parse. Either works for me.
> 
> >> Note that I put "selinux/" onto the MD5 as well. You may have
> >> multiple modules that use this interface on a system at the same time
> >> in the not to distant future:
> >>
> >> 	selinux/enforce=1:selinux/checkreqprot=0:
> >> 	selinux/MD5=8675309:spiffy/enforce=soft:
> >> 	spiffy/updatefreq=32544:spiffy/SHA256=84521
> >>
> >> If you deal with this now it won't be a major headache to deal with
> >> later.
> >>
> >>> + *
> >>> + *	@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";
> >>> +	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);
> >>> +	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,
> >>> +					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);
> >>> +
> >>> +	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) {

Kind of a weird way to do else if...

> >>> +			/* 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);

Isn't this: policybrief_len from above? No need to recompute.

> >>> +	strncpy(*brief, policydb.policybrief, *len);
> >>> +	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
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ¥Šwÿº{.nÇ+‰·¥Š{±þÇœº¸­Ëù¨vé^þ)í…æèw*jg¬±¨¶‰šŽŠÝ¢jÿ¾«þG«éÿ¢¸¢·¦j:+v‰¨ŠwèjØm¶Ÿÿþø¯ù®w¥þŠàþf£¢·hšâúÿ†Ù¥



More information about the Linux-security-module-archive mailing list