diff options
Diffstat (limited to 'security')
91 files changed, 4664 insertions, 2264 deletions
diff --git a/security/Kconfig b/security/Kconfig index e9c6ac724fe..beb86b500ad 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -103,7 +103,7 @@ config INTEL_TXT  config LSM_MMAP_MIN_ADDR  	int "Low address space for LSM to protect from user allocation"  	depends on SECURITY && SECURITY_SELINUX -	default 32768 if ARM +	default 32768 if ARM || (ARM64 && COMPAT)  	default 65536  	help  	  This is the portion of low virtual memory which should be protected diff --git a/security/Makefile b/security/Makefile index c26c81e9257..05f1c934d74 100644 --- a/security/Makefile +++ b/security/Makefile @@ -16,15 +16,14 @@ obj-$(CONFIG_MMU)			+= min_addr.o  # Object file lists  obj-$(CONFIG_SECURITY)			+= security.o capability.o  obj-$(CONFIG_SECURITYFS)		+= inode.o -# Must precede capability.o in order to stack properly. -obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o -obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o +obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/ +obj-$(CONFIG_SECURITY_SMACK)		+= smack/  obj-$(CONFIG_AUDIT)			+= lsm_audit.o -obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/built-in.o -obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o -obj-$(CONFIG_SECURITY_YAMA)		+= yama/built-in.o +obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/ +obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/ +obj-$(CONFIG_SECURITY_YAMA)		+= yama/  obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o  # Object integrity file lists  subdir-$(CONFIG_INTEGRITY)		+= integrity -obj-$(CONFIG_INTEGRITY)			+= integrity/built-in.o +obj-$(CONFIG_INTEGRITY)			+= integrity/ diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 95c2b2689a0..7db9954f1af 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -580,15 +580,13 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,  	/* check if the next ns is a sibling, parent, gp, .. */  	parent = ns->parent; -	while (parent) { +	while (ns != root) {  		mutex_unlock(&ns->lock);  		next = list_entry_next(ns, base.list);  		if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {  			mutex_lock(&next->lock);  			return next;  		} -		if (parent == root) -			return NULL;  		ns = parent;  		parent = parent->parent;  	} diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 031d2d9dd69..89c78658031 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -111,7 +111,6 @@ static const char *const aa_audit_type[] = {  static void audit_pre(struct audit_buffer *ab, void *ca)  {  	struct common_audit_data *sa = ca; -	struct task_struct *tsk = sa->aad->tsk ? sa->aad->tsk : current;  	if (aa_g_audit_header) {  		audit_log_format(ab, "apparmor="); @@ -132,11 +131,6 @@ static void audit_pre(struct audit_buffer *ab, void *ca)  	if (sa->aad->profile) {  		struct aa_profile *profile = sa->aad->profile; -		pid_t pid; -		rcu_read_lock(); -		pid = rcu_dereference(tsk->real_parent)->pid; -		rcu_read_unlock(); -		audit_log_format(ab, " parent=%d", pid);  		if (profile->ns != root_ns) {  			audit_log_format(ab, " namespace=");  			audit_log_untrustedstring(ab, profile->ns->base.hname); @@ -149,12 +143,6 @@ static void audit_pre(struct audit_buffer *ab, void *ca)  		audit_log_format(ab, " name=");  		audit_log_untrustedstring(ab, sa->aad->name);  	} - -	if (sa->aad->tsk) { -		audit_log_format(ab, " pid=%d comm=", tsk->pid); -		audit_log_untrustedstring(ab, tsk->comm); -	} -  }  /** @@ -212,7 +200,7 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,  	if (sa->aad->type == AUDIT_APPARMOR_KILL)  		(void)send_sig_info(SIGKILL, NULL, -				    sa->aad->tsk ?  sa->aad->tsk : current); +				    sa->u.tsk ?  sa->u.tsk : current);  	if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)  		return complain_error(sa->aad->error); diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 84d1f5f5387..1101c6f64bb 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -53,8 +53,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)  /**   * audit_caps - audit a capability - * @profile: profile confining task (NOT NULL) - * @task: task capability test was performed against (NOT NULL) + * @profile: profile being tested for confinement (NOT NULL)   * @cap: capability tested   * @error: error code returned by test   * @@ -63,8 +62,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)   *   * Returns: 0 or sa->error on success,  error code on failure   */ -static int audit_caps(struct aa_profile *profile, struct task_struct *task, -		      int cap, int error) +static int audit_caps(struct aa_profile *profile, int cap, int error)  {  	struct audit_cache *ent;  	int type = AUDIT_APPARMOR_AUTO; @@ -73,7 +71,6 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task,  	sa.type = LSM_AUDIT_DATA_CAP;  	sa.aad = &aad;  	sa.u.cap = cap; -	sa.aad->tsk = task;  	sa.aad->op = OP_CAPABLE;  	sa.aad->error = error; @@ -124,8 +121,7 @@ static int profile_capable(struct aa_profile *profile, int cap)  /**   * aa_capable - test permission to use capability - * @task: task doing capability test against (NOT NULL) - * @profile: profile confining @task (NOT NULL) + * @profile: profile being tested against (NOT NULL)   * @cap: capability to be tested   * @audit: whether an audit record should be generated   * @@ -133,8 +129,7 @@ static int profile_capable(struct aa_profile *profile, int cap)   *   * Returns: 0 on success, or else an error code.   */ -int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, -	       int audit) +int aa_capable(struct aa_profile *profile, int cap, int audit)  {  	int error = profile_capable(profile, cap); @@ -144,5 +139,5 @@ int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,  		return error;  	} -	return audit_caps(profile, task, cap, error); +	return audit_caps(profile, cap, error);  } diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index d6222ba4e91..532471d0b3a 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -15,14 +15,14 @@   * it should be.   */ -#include <linux/crypto.h> +#include <crypto/hash.h>  #include "include/apparmor.h"  #include "include/crypto.h"  static unsigned int apparmor_hash_size; -static struct crypto_hash *apparmor_tfm; +static struct crypto_shash *apparmor_tfm;  unsigned int aa_hash_size(void)  { @@ -32,35 +32,33 @@ unsigned int aa_hash_size(void)  int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,  			 size_t len)  { -	struct scatterlist sg[2]; -	struct hash_desc desc = { -		.tfm = apparmor_tfm, -		.flags = 0 -	}; +	struct { +		struct shash_desc shash; +		char ctx[crypto_shash_descsize(apparmor_tfm)]; +	} desc;  	int error = -ENOMEM;  	u32 le32_version = cpu_to_le32(version);  	if (!apparmor_tfm)  		return 0; -	sg_init_table(sg, 2); -	sg_set_buf(&sg[0], &le32_version, 4); -	sg_set_buf(&sg[1], (u8 *) start, len); -  	profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL);  	if (!profile->hash)  		goto fail; -	error = crypto_hash_init(&desc); +	desc.shash.tfm = apparmor_tfm; +	desc.shash.flags = 0; + +	error = crypto_shash_init(&desc.shash);  	if (error)  		goto fail; -	error = crypto_hash_update(&desc, &sg[0], 4); +	error = crypto_shash_update(&desc.shash, (u8 *) &le32_version, 4);  	if (error)  		goto fail; -	error = crypto_hash_update(&desc, &sg[1], len); +	error = crypto_shash_update(&desc.shash, (u8 *) start, len);  	if (error)  		goto fail; -	error = crypto_hash_final(&desc, profile->hash); +	error = crypto_shash_final(&desc.shash, profile->hash);  	if (error)  		goto fail; @@ -75,19 +73,19 @@ fail:  static int __init init_profile_hash(void)  { -	struct crypto_hash *tfm; +	struct crypto_shash *tfm;  	if (!apparmor_initialized)  		return 0; -	tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC); +	tfm = crypto_alloc_shash("sha1", 0, CRYPTO_ALG_ASYNC);  	if (IS_ERR(tfm)) {  		int error = PTR_ERR(tfm);  		AA_ERROR("failed to setup profile sha1 hashing: %d\n", error);  		return error;  	}  	apparmor_tfm = tfm; -	apparmor_hash_size = crypto_hash_digestsize(apparmor_tfm); +	apparmor_hash_size = crypto_shash_digestsize(apparmor_tfm);  	aa_info_message("AppArmor sha1 policy hashing enabled"); diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 26c607c971f..452567d3a08 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -50,23 +50,21 @@ void aa_free_domain_entries(struct aa_domain *domain)  /**   * may_change_ptraced_domain - check if can change profile on ptraced task - * @task: task we want to change profile of   (NOT NULL)   * @to_profile: profile to change to  (NOT NULL)   * - * Check if the task is ptraced and if so if the tracing task is allowed + * Check if current is ptraced and if so if the tracing task is allowed   * to trace the new domain   *   * Returns: %0 or error if change not allowed   */ -static int may_change_ptraced_domain(struct task_struct *task, -				     struct aa_profile *to_profile) +static int may_change_ptraced_domain(struct aa_profile *to_profile)  {  	struct task_struct *tracer;  	struct aa_profile *tracerp = NULL;  	int error = 0;  	rcu_read_lock(); -	tracer = ptrace_parent(task); +	tracer = ptrace_parent(current);  	if (tracer)  		/* released below */  		tracerp = aa_get_task_profile(tracer); @@ -75,7 +73,7 @@ static int may_change_ptraced_domain(struct task_struct *task,  	if (!tracer || unconfined(tracerp))  		goto out; -	error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); +	error = aa_may_ptrace(tracerp, to_profile, PTRACE_MODE_ATTACH);  out:  	rcu_read_unlock(); @@ -477,7 +475,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)  	}  	if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { -		error = may_change_ptraced_domain(current, new_profile); +		error = may_change_ptraced_domain(new_profile);  		if (error) {  			aa_put_profile(new_profile);  			goto audit; @@ -690,7 +688,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)  			}  		} -		error = may_change_ptraced_domain(current, hat); +		error = may_change_ptraced_domain(hat);  		if (error) {  			info = "ptraced";  			error = -EPERM; @@ -829,7 +827,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,  	}  	/* check if tracing task is allowed to trace target domain */ -	error = may_change_ptraced_domain(current, target); +	error = may_change_ptraced_domain(target);  	if (error) {  		info = "ptrace prevents transition";  		goto audit; diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 8fb1488a3cd..97130f88838 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -66,7 +66,6 @@ extern int apparmor_initialized __initdata;  char *aa_split_fqname(char *args, char **ns_name);  void aa_info_message(const char *str);  void *__aa_kvmalloc(size_t size, gfp_t flags); -void kvfree(void *buffer);  static inline void *kvmalloc(size_t size)  { diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 30e8d768725..ba3dfd17f23 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -109,7 +109,6 @@ struct apparmor_audit_data {  	void *profile;  	const char *name;  	const char *info; -	struct task_struct *tsk;  	union {  		void *target;  		struct { diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index 2e7c9d6a2f3..fc3fa381d85 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h @@ -4,7 +4,7 @@   * This file contains AppArmor capability mediation definitions.   *   * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2013 Canonical Ltd.   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License as @@ -38,8 +38,7 @@ struct aa_caps {  extern struct aa_fs_entry aa_fs_entry_caps[]; -int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, -	       int audit); +int aa_capable(struct aa_profile *profile, int cap, int audit);  static inline void aa_free_cap_rules(struct aa_caps *caps)  { diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index aeda0fbc8b2..288ca76e2fb 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -19,8 +19,8 @@  struct aa_profile; -int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, -		  struct aa_profile *tracee, unsigned int mode); +int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee, +		  unsigned int mode);  int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,  	      unsigned int mode); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index f2d4b6348cb..c28b0f20ab5 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -360,7 +360,9 @@ static inline void aa_put_replacedby(struct aa_replacedby *p)  static inline void __aa_update_replacedby(struct aa_profile *orig,  					  struct aa_profile *new)  { -	struct aa_profile *tmp = rcu_dereference(orig->replacedby->profile); +	struct aa_profile *tmp; +	tmp = rcu_dereference_protected(orig->replacedby->profile, +					mutex_is_locked(&orig->ns->lock));  	rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));  	orig->flags |= PFLAG_INVALID;  	aa_put_profile(tmp); diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index c51d2266587..777ac1c4725 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -54,15 +54,14 @@ static int aa_audit_ptrace(struct aa_profile *profile,  /**   * aa_may_ptrace - test if tracer task can trace the tracee - * @tracer_task: task who will do the tracing  (NOT NULL)   * @tracer: profile of the task doing the tracing  (NOT NULL)   * @tracee: task to be traced   * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH   *   * Returns: %0 else error code if permission denied or error   */ -int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, -		  struct aa_profile *tracee, unsigned int mode) +int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee, +		  unsigned int mode)  {  	/* TODO: currently only based on capability, not extended ptrace  	 *       rules, @@ -72,7 +71,7 @@ int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer,  	if (unconfined(tracer) || tracer == tracee)  		return 0;  	/* log this capability request */ -	return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); +	return aa_capable(tracer, CAP_SYS_PTRACE, 1);  }  /** @@ -101,7 +100,7 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,  	if (!unconfined(tracer_p)) {  		struct aa_profile *tracee_p = aa_get_task_profile(tracee); -		error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); +		error = aa_may_ptrace(tracer_p, tracee_p, mode);  		error = aa_audit_ptrace(tracer_p, tracee_p, error);  		aa_put_profile(tracee_p); diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 69689922c49..c1827e06845 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -104,17 +104,3 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)  	}  	return buffer;  } - -/** - * kvfree - free an allocation do by kvmalloc - * @buffer: buffer to free (MAYBE_NULL) - * - * Free a buffer allocated by kvmalloc - */ -void kvfree(void *buffer) -{ -	if (is_vmalloc_addr(buffer)) -		vfree(buffer); -	else -		kfree(buffer); -} diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index fb99e18123b..99810009333 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -145,7 +145,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,  	if (!error) {  		profile = aa_cred_profile(cred);  		if (!unconfined(profile)) -			error = aa_capable(current, profile, cap, audit); +			error = aa_capable(profile, cap, audit);  	}  	return error;  } @@ -751,7 +751,7 @@ module_param_named(enabled, apparmor_enabled, bool, S_IRUGO);  static int __init apparmor_enabled_setup(char *str)  {  	unsigned long enabled; -	int error = strict_strtoul(str, 0, &enabled); +	int error = kstrtoul(str, 0, &enabled);  	if (!error)  		apparmor_enabled = enabled ? 1 : 0;  	return 1; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 6172509fa2b..705c2879d3a 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -563,7 +563,8 @@ void __init aa_free_root_ns(void)  static void free_replacedby(struct aa_replacedby *r)  {  	if (r) { -		aa_put_profile(rcu_dereference(r->profile)); +		/* r->profile will not be updated any more as r is dead */ +		aa_put_profile(rcu_dereference_protected(r->profile, true));  		kzfree(r);  	}  } @@ -609,6 +610,7 @@ void aa_free_profile(struct aa_profile *profile)  	aa_put_dfa(profile->policy.dfa);  	aa_put_replacedby(profile->replacedby); +	kzfree(profile->hash);  	kzfree(profile);  } diff --git a/security/capability.c b/security/capability.c index dbeb9bc27b2..e76373de312 100644 --- a/security/capability.c +++ b/security/capability.c @@ -116,7 +116,7 @@ static int cap_dentry_init_security(struct dentry *dentry, int mode,  					struct qstr *name, void **ctx,  					u32 *ctxlen)  { -	return 0; +	return -EOPNOTSUPP;  }  static int cap_inode_alloc_security(struct inode *inode) @@ -757,7 +757,8 @@ static void cap_skb_owned_by(struct sk_buff *skb, struct sock *sk)  #ifdef CONFIG_SECURITY_NETWORK_XFRM  static int cap_xfrm_policy_alloc_security(struct xfrm_sec_ctx **ctxp, -					  struct xfrm_user_sec_ctx *sec_ctx) +					  struct xfrm_user_sec_ctx *sec_ctx, +					  gfp_t gfp)  {  	return 0;  } @@ -777,9 +778,15 @@ static int cap_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)  	return 0;  } -static int cap_xfrm_state_alloc_security(struct xfrm_state *x, -					 struct xfrm_user_sec_ctx *sec_ctx, -					 u32 secid) +static int cap_xfrm_state_alloc(struct xfrm_state *x, +				struct xfrm_user_sec_ctx *sec_ctx) +{ +	return 0; +} + +static int cap_xfrm_state_alloc_acquire(struct xfrm_state *x, +					struct xfrm_sec_ctx *polsec, +					u32 secid)  {  	return 0;  } @@ -872,7 +879,7 @@ static void cap_key_free(struct key *key)  }  static int cap_key_permission(key_ref_t key_ref, const struct cred *cred, -			      key_perm_t perm) +			      unsigned perm)  {  	return 0;  } @@ -1101,7 +1108,8 @@ void __init security_fixup_ops(struct security_operations *ops)  	set_to_cap_if_null(ops, xfrm_policy_clone_security);  	set_to_cap_if_null(ops, xfrm_policy_free_security);  	set_to_cap_if_null(ops, xfrm_policy_delete_security); -	set_to_cap_if_null(ops, xfrm_state_alloc_security); +	set_to_cap_if_null(ops, xfrm_state_alloc); +	set_to_cap_if_null(ops, xfrm_state_alloc_acquire);  	set_to_cap_if_null(ops, xfrm_state_free_security);  	set_to_cap_if_null(ops, xfrm_state_delete_security);  	set_to_cap_if_null(ops, xfrm_policy_lookup); diff --git a/security/device_cgroup.c b/security/device_cgroup.c index c123628d3f8..d9d69e6930e 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -58,19 +58,7 @@ static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)  static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)  { -	return css_to_devcgroup(task_css(task, devices_subsys_id)); -} - -struct cgroup_subsys devices_subsys; - -static int devcgroup_can_attach(struct cgroup_subsys_state *new_css, -				struct cgroup_taskset *set) -{ -	struct task_struct *task = cgroup_taskset_first(set); - -	if (current != task && !capable(CAP_SYS_ADMIN)) -		return -EPERM; -	return 0; +	return css_to_devcgroup(task_css(task, devices_cgrp_id));  }  /* @@ -194,7 +182,7 @@ static inline bool is_devcg_online(const struct dev_cgroup *devcg)  static int devcgroup_online(struct cgroup_subsys_state *css)  {  	struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); -	struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css)); +	struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css->parent);  	int ret = 0;  	mutex_lock(&devcgroup_mutex); @@ -284,10 +272,9 @@ static void set_majmin(char *str, unsigned m)  		sprintf(str, "%u", m);  } -static int devcgroup_seq_read(struct cgroup_subsys_state *css, -			      struct cftype *cft, struct seq_file *m) +static int devcgroup_seq_show(struct seq_file *m, void *v)  { -	struct dev_cgroup *devcgroup = css_to_devcgroup(css); +	struct dev_cgroup *devcgroup = css_to_devcgroup(seq_css(m));  	struct dev_exception_item *ex;  	char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; @@ -319,57 +306,138 @@ static int devcgroup_seq_read(struct cgroup_subsys_state *css,  }  /** - * may_access - verifies if a new exception is part of what is allowed - *		by a dev cgroup based on the default policy + - *		exceptions. This is used to make sure a child cgroup - *		won't have more privileges than its parent or to - *		verify if a certain access is allowed. - * @dev_cgroup: dev cgroup to be tested against - * @refex: new exception - * @behavior: behavior of the exception + * match_exception	- iterates the exception list trying to find a complete match + * @exceptions: list of exceptions + * @type: device type (DEV_BLOCK or DEV_CHAR) + * @major: device file major number, ~0 to match all + * @minor: device file minor number, ~0 to match all + * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD) + * + * It is considered a complete match if an exception is found that will + * contain the entire range of provided parameters. + * + * Return: true in case it matches an exception completely   */ -static bool may_access(struct dev_cgroup *dev_cgroup, -		       struct dev_exception_item *refex, -		       enum devcg_behavior behavior) +static bool match_exception(struct list_head *exceptions, short type, +			    u32 major, u32 minor, short access)  {  	struct dev_exception_item *ex; -	bool match = false; -	rcu_lockdep_assert(rcu_read_lock_held() || -			   lockdep_is_held(&devcgroup_mutex), -			   "device_cgroup::may_access() called without proper synchronization"); +	list_for_each_entry_rcu(ex, exceptions, list) { +		if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) +			continue; +		if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR)) +			continue; +		if (ex->major != ~0 && ex->major != major) +			continue; +		if (ex->minor != ~0 && ex->minor != minor) +			continue; +		/* provided access cannot have more than the exception rule */ +		if (access & (~ex->access)) +			continue; +		return true; +	} +	return false; +} + +/** + * match_exception_partial - iterates the exception list trying to find a partial match + * @exceptions: list of exceptions + * @type: device type (DEV_BLOCK or DEV_CHAR) + * @major: device file major number, ~0 to match all + * @minor: device file minor number, ~0 to match all + * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD) + * + * It is considered a partial match if an exception's range is found to + * contain *any* of the devices specified by provided parameters. This is + * used to make sure no extra access is being granted that is forbidden by + * any of the exception list. + * + * Return: true in case the provided range mat matches an exception completely + */ +static bool match_exception_partial(struct list_head *exceptions, short type, +				    u32 major, u32 minor, short access) +{ +	struct dev_exception_item *ex; -	list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) { -		if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) +	list_for_each_entry_rcu(ex, exceptions, list) { +		if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))  			continue; -		if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR)) +		if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))  			continue; -		if (ex->major != ~0 && ex->major != refex->major) +		/* +		 * We must be sure that both the exception and the provided +		 * range aren't masking all devices +		 */ +		if (ex->major != ~0 && major != ~0 && ex->major != major)  			continue; -		if (ex->minor != ~0 && ex->minor != refex->minor) +		if (ex->minor != ~0 && minor != ~0 && ex->minor != minor)  			continue; -		if (refex->access & (~ex->access)) +		/* +		 * In order to make sure the provided range isn't matching +		 * an exception, all its access bits shouldn't match the +		 * exception's access bits +		 */ +		if (!(access & ex->access))  			continue; -		match = true; -		break; +		return true;  	} +	return false; +} + +/** + * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions + * @dev_cgroup: dev cgroup to be tested against + * @refex: new exception + * @behavior: behavior of the exception's dev_cgroup + * + * This is used to make sure a child cgroup won't have more privileges + * than its parent + */ +static bool verify_new_ex(struct dev_cgroup *dev_cgroup, +		          struct dev_exception_item *refex, +		          enum devcg_behavior behavior) +{ +	bool match = false; + +	rcu_lockdep_assert(rcu_read_lock_held() || +			   lockdep_is_held(&devcgroup_mutex), +			   "device_cgroup:verify_new_ex called without proper synchronization");  	if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {  		if (behavior == DEVCG_DEFAULT_ALLOW) { -			/* the exception will deny access to certain devices */ +			/* +			 * new exception in the child doesn't matter, only +			 * adding extra restrictions +			 */   			return true;  		} else { -			/* the exception will allow access to certain devices */ +			/* +			 * new exception in the child will add more devices +			 * that can be acessed, so it can't match any of +			 * parent's exceptions, even slightly +			 */  +			match = match_exception_partial(&dev_cgroup->exceptions, +							refex->type, +							refex->major, +							refex->minor, +							refex->access); +  			if (match) -				/* -				 * a new exception allowing access shouldn't -				 * match an parent's exception -				 */  				return false;  			return true;  		}  	} else { -		/* only behavior == DEVCG_DEFAULT_DENY allowed here */ +		/* +		 * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore +		 * the new exception will add access to more devices and must +		 * be contained completely in an parent's exception to be +		 * allowed +		 */ +		match = match_exception(&dev_cgroup->exceptions, refex->type, +					refex->major, refex->minor, +					refex->access); +  		if (match)  			/* parent has an exception that matches the proposed */  			return true; @@ -387,11 +455,42 @@ static bool may_access(struct dev_cgroup *dev_cgroup,  static int parent_has_perm(struct dev_cgroup *childcg,  				  struct dev_exception_item *ex)  { -	struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); +	struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent);  	if (!parent)  		return 1; -	return may_access(parent, ex, childcg->behavior); +	return verify_new_ex(parent, ex, childcg->behavior); +} + +/** + * parent_allows_removal - verify if it's ok to remove an exception + * @childcg: child cgroup from where the exception will be removed + * @ex: exception being removed + * + * When removing an exception in cgroups with default ALLOW policy, it must + * be checked if removing it will give the child cgroup more access than the + * parent. + * + * Return: true if it's ok to remove exception, false otherwise + */ +static bool parent_allows_removal(struct dev_cgroup *childcg, +				  struct dev_exception_item *ex) +{ +	struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent); + +	if (!parent) +		return true; + +	/* It's always allowed to remove access to devices */ +	if (childcg->behavior == DEVCG_DEFAULT_DENY) +		return true; + +	/* +	 * Make sure you're not removing part or a whole exception existing in +	 * the parent cgroup +	 */ +	return !match_exception_partial(&parent->exceptions, ex->type, +					ex->major, ex->minor, ex->access);  }  /** @@ -488,13 +587,6 @@ static int propagate_exception(struct dev_cgroup *devcg_root,  	return rc;  } -static inline bool has_children(struct dev_cgroup *devcgroup) -{ -	struct cgroup *cgrp = devcgroup->css.cgroup; - -	return !list_empty(&cgrp->children); -} -  /*   * Modify the exception list using allow/deny rules.   * CAP_SYS_ADMIN is needed for this.  It's at least separate from CAP_MKNOD @@ -509,13 +601,13 @@ static inline bool has_children(struct dev_cgroup *devcgroup)   * parent cgroup has the access you're asking for.   */  static int devcgroup_update_access(struct dev_cgroup *devcgroup, -				   int filetype, const char *buffer) +				   int filetype, char *buffer)  {  	const char *b;  	char temp[12];		/* 11 + 1 characters needed for a u32 */  	int count, rc = 0;  	struct dev_exception_item ex; -	struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css)); +	struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent);  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; @@ -527,7 +619,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,  	case 'a':  		switch (filetype) {  		case DEVCG_ALLOW: -			if (has_children(devcgroup)) +			if (css_has_online_children(&devcgroup->css))  				return -EINVAL;  			if (!may_allow_all(parent)) @@ -543,7 +635,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,  				return rc;  			break;  		case DEVCG_DENY: -			if (has_children(devcgroup)) +			if (css_has_online_children(&devcgroup->css))  				return -EINVAL;  			dev_exception_clean(devcgroup); @@ -629,17 +721,21 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,  	switch (filetype) {  	case DEVCG_ALLOW: -		if (!parent_has_perm(devcgroup, &ex)) -			return -EPERM;  		/*  		 * If the default policy is to allow by default, try to remove  		 * an matching exception instead. And be silent about it: we  		 * don't want to break compatibility  		 */  		if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { +			/* Check if the parent allows removing it first */ +			if (!parent_allows_removal(devcgroup, &ex)) +				return -EPERM;  			dev_exception_rm(devcgroup, &ex); -			return 0; +			break;  		} + +		if (!parent_has_perm(devcgroup, &ex)) +			return -EPERM;  		rc = dev_exception_add(devcgroup, &ex);  		break;  	case DEVCG_DENY: @@ -664,45 +760,42 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,  	return rc;  } -static int devcgroup_access_write(struct cgroup_subsys_state *css, -				  struct cftype *cft, const char *buffer) +static ssize_t devcgroup_access_write(struct kernfs_open_file *of, +				      char *buf, size_t nbytes, loff_t off)  {  	int retval;  	mutex_lock(&devcgroup_mutex); -	retval = devcgroup_update_access(css_to_devcgroup(css), -					 cft->private, buffer); +	retval = devcgroup_update_access(css_to_devcgroup(of_css(of)), +					 of_cft(of)->private, strstrip(buf));  	mutex_unlock(&devcgroup_mutex); -	return retval; +	return retval ?: nbytes;  }  static struct cftype dev_cgroup_files[] = {  	{  		.name = "allow", -		.write_string  = devcgroup_access_write, +		.write = devcgroup_access_write,  		.private = DEVCG_ALLOW,  	},  	{  		.name = "deny", -		.write_string = devcgroup_access_write, +		.write = devcgroup_access_write,  		.private = DEVCG_DENY,  	},  	{  		.name = "list", -		.read_seq_string = devcgroup_seq_read, +		.seq_show = devcgroup_seq_show,  		.private = DEVCG_LIST,  	},  	{ }	/* terminate */  }; -struct cgroup_subsys devices_subsys = { -	.name = "devices", -	.can_attach = devcgroup_can_attach, +struct cgroup_subsys devices_cgrp_subsys = {  	.css_alloc = devcgroup_css_alloc,  	.css_free = devcgroup_css_free,  	.css_online = devcgroup_online,  	.css_offline = devcgroup_offline, -	.subsys_id = devices_subsys_id,  	.base_cftypes = dev_cgroup_files,  }; @@ -720,18 +813,18 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,  				        short access)  {  	struct dev_cgroup *dev_cgroup; -	struct dev_exception_item ex; -	int rc; - -	memset(&ex, 0, sizeof(ex)); -	ex.type = type; -	ex.major = major; -	ex.minor = minor; -	ex.access = access; +	bool rc;  	rcu_read_lock();  	dev_cgroup = task_devcgroup(current); -	rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior); +	if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) +		/* Can't match any of the exceptions, even partially */ +		rc = !match_exception_partial(&dev_cgroup->exceptions, +					      type, major, minor, access); +	else +		/* Need to match completely one exception to be allowed */ +		rc = match_exception(&dev_cgroup->exceptions, type, major, +				     minor, access);  	rcu_read_unlock();  	if (!rc) diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 0f9cffb1f9a..0793f4811cb 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -10,6 +10,6 @@ obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o  integrity-y := iint.o  subdir-$(CONFIG_IMA)			+= ima -obj-$(CONFIG_IMA)			+= ima/built-in.o +obj-$(CONFIG_IMA)			+= ima/  subdir-$(CONFIG_EVM)			+= evm -obj-$(CONFIG_EVM)			+= evm/built-in.o +obj-$(CONFIG_EVM)			+= evm/ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 0b759e17a13..b4af4ebc5be 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -28,7 +28,7 @@ static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {  };  int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, -					const char *digest, int digestlen) +			    const char *digest, int digestlen)  {  	if (id >= INTEGRITY_KEYRING_MAX)  		return -EINVAL; @@ -44,9 +44,10 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,  		}  	} -	switch (sig[0]) { +	switch (sig[1]) {  	case 1: -		return digsig_verify(keyring[id], sig, siglen, +		/* v1 API expect signature without xattr type */ +		return digsig_verify(keyring[id], sig + 1, siglen - 1,  				     digest, digestlen);  	case 2:  		return asymmetric_verify(keyring[id], sig, siglen, diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index b4754667659..9eae4809006 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -20,17 +20,6 @@  #include "integrity.h"  /* - * signature format v2 - for using with asymmetric keys - */ -struct signature_v2_hdr { -	uint8_t version;	/* signature format version */ -	uint8_t	hash_algo;	/* Digest algorithm [enum pkey_hash_algo] */ -	uint32_t keyid;		/* IMA key identifier - not X509/PGP specific*/ -	uint16_t sig_size;	/* signature size */ -	uint8_t sig[0];		/* signature payload */ -} __packed; - -/*   * Request an asymmetric key.   */  static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index fea9749c375..d606f3d12d6 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -1,10 +1,10 @@  config EVM  	boolean "EVM support" -	depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n) +	depends on SECURITY +	select KEYS +	select ENCRYPTED_KEYS  	select CRYPTO_HMAC -	select CRYPTO_MD5  	select CRYPTO_SHA1 -	select ENCRYPTED_KEYS  	default n  	help  	  EVM protects a file's security extended attributes against @@ -12,15 +12,41 @@ config EVM  	  If you are unsure how to answer this question, answer N. -config EVM_HMAC_VERSION -	int "EVM HMAC version" +if EVM + +menu "EVM options" + +config EVM_ATTR_FSUUID +	bool "FSUUID (version 2)" +	default y  	depends on EVM -	default 2  	help -	  This options adds EVM HMAC version support. -	  1 - original version -	  2 - add per filesystem unique identifier (UUID) (default) +	  Include filesystem UUID for HMAC calculation. -	  WARNING: changing the HMAC calculation method or adding  +	  Default value is 'selected', which is former version 2. +	  if 'not selected', it is former version 1 + +	  WARNING: changing the HMAC calculation method or adding  	  additional info to the calculation, requires existing EVM -	  labeled file systems to be relabeled.   +	  labeled file systems to be relabeled. + +config EVM_EXTRA_SMACK_XATTRS +	bool "Additional SMACK xattrs" +	depends on EVM && SECURITY_SMACK +	default n +	help +	  Include additional SMACK xattrs for HMAC calculation. + +	  In addition to the original security xattrs (eg. security.selinux, +	  security.SMACK64, security.capability, and security.ima) included +	  in the HMAC calculation, enabling this option includes newly defined +	  Smack xattrs: security.SMACK64EXEC, security.SMACK64TRANSMUTE and +	  security.SMACK64MMAP. + +	  WARNING: changing the HMAC calculation method or adding +	  additional info to the calculation, requires existing EVM +	  labeled file systems to be relabeled. + +endmenu + +endif diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 30bd1ec0232..88bfe77efa1 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -24,7 +24,10 @@  extern int evm_initialized;  extern char *evm_hmac;  extern char *evm_hash; -extern int evm_hmac_version; + +#define EVM_ATTR_FSUUID		0x0001 + +extern int evm_hmac_attrs;  extern struct crypto_shash *hmac_tfm;  extern struct crypto_shash *hash_tfm; @@ -32,19 +35,19 @@ extern struct crypto_shash *hash_tfm;  /* List of EVM protected security xattrs */  extern char *evm_config_xattrnames[]; -extern int evm_init_key(void); -extern int evm_update_evmxattr(struct dentry *dentry, -			       const char *req_xattr_name, -			       const char *req_xattr_value, -			       size_t req_xattr_value_len); -extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, -			 const char *req_xattr_value, -			 size_t req_xattr_value_len, char *digest); -extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, -			 const char *req_xattr_value, -			 size_t req_xattr_value_len, char *digest); -extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr, -			 char *hmac_val); -extern int evm_init_secfs(void); +int evm_init_key(void); +int evm_update_evmxattr(struct dentry *dentry, +			const char *req_xattr_name, +			const char *req_xattr_value, +			size_t req_xattr_value_len); +int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, +		  const char *req_xattr_value, +		  size_t req_xattr_value_len, char *digest); +int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, +		  const char *req_xattr_value, +		  size_t req_xattr_value_len, char *digest); +int evm_init_hmac(struct inode *inode, const struct xattr *xattr, +		  char *hmac_val); +int evm_init_secfs(void);  #endif diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 3bab89eb21d..5e9687f02e1 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -13,6 +13,8 @@   *	 Using root's kernel master key (kmk), calculate the HMAC   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/crypto.h>  #include <linux/xattr.h> @@ -103,14 +105,14 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,  		umode_t mode;  	} hmac_misc; -	memset(&hmac_misc, 0, sizeof hmac_misc); +	memset(&hmac_misc, 0, sizeof(hmac_misc));  	hmac_misc.ino = inode->i_ino;  	hmac_misc.generation = inode->i_generation;  	hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);  	hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);  	hmac_misc.mode = inode->i_mode; -	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc); -	if (evm_hmac_version > 1) +	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); +	if (evm_hmac_attrs & EVM_ATTR_FSUUID)  		crypto_shash_update(desc, inode->i_sb->s_uuid,  				    sizeof(inode->i_sb->s_uuid));  	crypto_shash_final(desc, digest); @@ -137,7 +139,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,  	int error;  	int size; -	if (!inode->i_op || !inode->i_op->getxattr) +	if (!inode->i_op->getxattr)  		return -EOPNOTSUPP;  	desc = init_desc(type);  	if (IS_ERR(desc)) @@ -221,7 +223,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,  	desc = init_desc(EVM_XATTR_HMAC);  	if (IS_ERR(desc)) { -		printk(KERN_INFO "init_desc failed\n"); +		pr_info("init_desc failed\n");  		return PTR_ERR(desc);  	} diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index af9b6852f4e..3bcb80df4d0 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -14,6 +14,8 @@   *	evm_inode_removexattr, and evm_verifyxattr   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/crypto.h>  #include <linux/audit.h> @@ -30,7 +32,7 @@ static char *integrity_status_msg[] = {  };  char *evm_hmac = "hmac(sha1)";  char *evm_hash = "sha1"; -int evm_hmac_version = CONFIG_EVM_HMAC_VERSION; +int evm_hmac_attrs;  char *evm_config_xattrnames[] = {  #ifdef CONFIG_SECURITY_SELINUX @@ -38,6 +40,11 @@ char *evm_config_xattrnames[] = {  #endif  #ifdef CONFIG_SECURITY_SMACK  	XATTR_NAME_SMACK, +#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS +	XATTR_NAME_SMACKEXEC, +	XATTR_NAME_SMACKTRANSMUTE, +	XATTR_NAME_SMACKMMAP, +#endif  #endif  #ifdef CONFIG_IMA_APPRAISE  	XATTR_NAME_IMA, @@ -55,6 +62,14 @@ static int __init evm_set_fixmode(char *str)  }  __setup("evm=", evm_set_fixmode); +static void __init evm_init_config(void) +{ +#ifdef CONFIG_EVM_ATTR_FSUUID +	evm_hmac_attrs |= EVM_ATTR_FSUUID; +#endif +	pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs); +} +  static int evm_find_protected_xattrs(struct dentry *dentry)  {  	struct inode *inode = dentry->d_inode; @@ -62,7 +77,7 @@ static int evm_find_protected_xattrs(struct dentry *dentry)  	int error;  	int count = 0; -	if (!inode->i_op || !inode->i_op->getxattr) +	if (!inode->i_op->getxattr)  		return -EOPNOTSUPP;  	for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) { @@ -123,7 +138,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,  		goto out;  	} -	xattr_len = rc - 1; +	xattr_len = rc;  	/* check value type */  	switch (xattr_data->type) { @@ -143,7 +158,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,  		if (rc)  			break;  		rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, -					xattr_data->digest, xattr_len, +					(const char *)xattr_data, xattr_len,  					calc.digest, sizeof(calc.digest));  		if (!rc) {  			/* we probably want to replace rsa with hmac here */ @@ -285,12 +300,20 @@ out:   * @xattr_value: pointer to the new extended attribute value   * @xattr_value_len: pointer to the new extended attribute value length   * - * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that - * the current value is valid. + * Before allowing the 'security.evm' protected xattr to be updated, + * verify the existing value is valid.  As only the kernel should have + * access to the EVM encrypted key needed to calculate the HMAC, prevent + * userspace from writing HMAC value.  Writing 'security.evm' requires + * requires CAP_SYS_ADMIN privileges.   */  int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,  		       const void *xattr_value, size_t xattr_value_len)  { +	const struct evm_ima_xattr_data *xattr_data = xattr_value; + +	if ((strcmp(xattr_name, XATTR_NAME_EVM) == 0) +	    && (xattr_data->type == EVM_XATTR_HMAC)) +		return -EPERM;  	return evm_protect_xattr(dentry, xattr_name, xattr_value,  				 xattr_value_len);  } @@ -430,9 +453,11 @@ static int __init init_evm(void)  {  	int error; +	evm_init_config(); +  	error = evm_init_secfs();  	if (error < 0) { -		printk(KERN_INFO "EVM: Error registering secfs\n"); +		pr_info("Error registering secfs\n");  		goto err;  	} @@ -449,7 +474,7 @@ static int __init evm_display_config(void)  	char **xattrname;  	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) -		printk(KERN_INFO "EVM: %s\n", *xattrname); +		pr_info("%s\n", *xattrname);  	return 0;  } diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c index b1753e98bf9..46408b9e62e 100644 --- a/security/integrity/evm/evm_posix_acl.c +++ b/security/integrity/evm/evm_posix_acl.c @@ -11,8 +11,9 @@  #include <linux/module.h>  #include <linux/xattr.h> +#include <linux/evm.h> -int posix_xattr_acl(char *xattr) +int posix_xattr_acl(const char *xattr)  {  	int xattr_len = strlen(xattr); diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 30f670ad6ac..cf12a04717d 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -13,6 +13,8 @@   *	- Get the key and enable EVM   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/uaccess.h>  #include <linux/module.h>  #include "evm.h" @@ -79,9 +81,9 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,  	error = evm_init_key();  	if (!error) {  		evm_initialized = 1; -		pr_info("EVM: initialized\n"); +		pr_info("initialized\n");  	} else -		pr_err("EVM: initialization failed\n"); +		pr_err("initialization failed\n");  	return count;  } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 74522dbd10a..a521edf4cbd 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -70,6 +70,8 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)  static void iint_free(struct integrity_iint_cache *iint)  { +	kfree(iint->ima_hash); +	iint->ima_hash = NULL;  	iint->version = 0;  	iint->flags = 0UL;  	iint->ima_file_status = INTEGRITY_UNKNOWN; @@ -149,7 +151,7 @@ static void init_once(void *foo)  {  	struct integrity_iint_cache *iint = foo; -	memset(iint, 0, sizeof *iint); +	memset(iint, 0, sizeof(*iint));  	iint->version = 0;  	iint->flags = 0UL;  	iint->ima_file_status = INTEGRITY_UNKNOWN; diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 39196abaff0..81a27971d88 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -9,6 +9,7 @@ config IMA  	select CRYPTO_HMAC  	select CRYPTO_MD5  	select CRYPTO_SHA1 +	select CRYPTO_HASH_INFO  	select TCG_TPM if HAS_IOMEM && !UML  	select TCG_TIS if TCG_TPM && X86  	select TCG_IBMVTPM if TCG_TPM && PPC64 @@ -45,6 +46,69 @@ config IMA_LSM_RULES  	help  	  Disabling this option will disregard LSM based policy rules. +choice +	prompt "Default template" +	default IMA_NG_TEMPLATE +	depends on IMA +	help +	  Select the default IMA measurement template. + +	  The original 'ima' measurement list template contains a +	  hash, defined as 20 bytes, and a null terminated pathname, +	  limited to 255 characters.  The 'ima-ng' measurement list +	  template permits both larger hash digests and longer +	  pathnames. + +	config IMA_TEMPLATE +		bool "ima" +	config IMA_NG_TEMPLATE +		bool "ima-ng (default)" +	config IMA_SIG_TEMPLATE +		bool "ima-sig" +endchoice + +config IMA_DEFAULT_TEMPLATE +	string +	depends on IMA +	default "ima" if IMA_TEMPLATE +	default "ima-ng" if IMA_NG_TEMPLATE +	default "ima-sig" if IMA_SIG_TEMPLATE + +choice +	prompt "Default integrity hash algorithm" +	default IMA_DEFAULT_HASH_SHA1 +	depends on IMA +	help +	   Select the default hash algorithm used for the measurement +	   list, integrity appraisal and audit log.  The compiled default +	   hash algorithm can be overwritten using the kernel command +	   line 'ima_hash=' option. + +	config IMA_DEFAULT_HASH_SHA1 +		bool "SHA1 (default)" +		depends on CRYPTO_SHA1 + +	config IMA_DEFAULT_HASH_SHA256 +		bool "SHA256" +		depends on CRYPTO_SHA256 && !IMA_TEMPLATE + +	config IMA_DEFAULT_HASH_SHA512 +		bool "SHA512" +		depends on CRYPTO_SHA512 && !IMA_TEMPLATE + +	config IMA_DEFAULT_HASH_WP512 +		bool "WP512" +		depends on CRYPTO_WP512 && !IMA_TEMPLATE +endchoice + +config IMA_DEFAULT_HASH +	string +	depends on IMA +	default "sha1" if IMA_DEFAULT_HASH_SHA1 +	default "sha256" if IMA_DEFAULT_HASH_SHA256 +	default "sha512" if IMA_DEFAULT_HASH_SHA512 +	default "wp512" if IMA_DEFAULT_HASH_WP512 +  config IMA_APPRAISE  	bool "Appraise integrity measurements"  	depends on IMA diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 56dfee7cbf6..d79263d2fdb 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -6,5 +6,5 @@  obj-$(CONFIG_IMA) += ima.o  ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ -	 ima_policy.o +	 ima_policy.o ima_template.o ima_template_lib.o  ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index b3dd616560f..f79fa8be203 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -26,7 +26,8 @@  #include "../integrity.h" -enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII }; +enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN, +		     IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII };  enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };  /* digest size for IMA, fits SHA1 or MD5 */ @@ -36,23 +37,48 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };  #define IMA_HASH_BITS 9  #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) +#define IMA_TEMPLATE_FIELD_ID_MAX_LEN	16 +#define IMA_TEMPLATE_NUM_FIELDS_MAX	15 + +#define IMA_TEMPLATE_IMA_NAME "ima" +#define IMA_TEMPLATE_IMA_FMT "d|n" +  /* set during initialization */  extern int ima_initialized;  extern int ima_used_chip; -extern char *ima_hash; +extern int ima_hash_algo;  extern int ima_appraise; -/* IMA inode template definition */ -struct ima_template_data { -	u8 digest[IMA_DIGEST_SIZE];	/* sha1/md5 measurement hash */ -	char file_name[IMA_EVENT_NAME_LEN_MAX + 1];	/* name + \0 */ +/* IMA template field data definition */ +struct ima_field_data { +	u8 *data; +	u32 len; +}; + +/* IMA template field definition */ +struct ima_template_field { +	const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN]; +	int (*field_init) (struct integrity_iint_cache *iint, struct file *file, +			   const unsigned char *filename, +			   struct evm_ima_xattr_data *xattr_value, +			   int xattr_len, struct ima_field_data *field_data); +	void (*field_show) (struct seq_file *m, enum ima_show_type show, +			    struct ima_field_data *field_data); +}; + +/* IMA template descriptor definition */ +struct ima_template_desc { +	char *name; +	char *fmt; +	int num_fields; +	struct ima_template_field **fields;  };  struct ima_template_entry { -	u8 digest[IMA_DIGEST_SIZE];	/* sha1 or md5 measurement hash */ -	const char *template_name; -	int template_len; -	struct ima_template_data template; +	u8 digest[TPM_DIGEST_SIZE];	/* sha1 or md5 measurement hash */ +	struct ima_template_desc *template_desc; /* template descriptor */ +	u32 template_data_len; +	struct ima_field_data template_data[0];	/* template related data */  };  struct ima_queue_entry { @@ -69,13 +95,22 @@ int ima_fs_init(void);  void ima_fs_cleanup(void);  int ima_inode_alloc(struct inode *inode);  int ima_add_template_entry(struct ima_template_entry *entry, int violation, -			   const char *op, struct inode *inode); -int ima_calc_file_hash(struct file *file, char *digest); -int ima_calc_buffer_hash(const void *data, int len, char *digest); -int ima_calc_boot_aggregate(char *digest); -void ima_add_violation(struct inode *inode, const unsigned char *filename, +			   const char *op, struct inode *inode, +			   const unsigned char *filename); +int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); +int ima_calc_field_array_hash(struct ima_field_data *field_data, +			      struct ima_template_desc *desc, int num_fields, +			      struct ima_digest_data *hash); +int __init ima_calc_boot_aggregate(struct ima_digest_data *hash); +void ima_add_violation(struct file *file, const unsigned char *filename,  		       const char *op, const char *cause);  int ima_init_crypto(void); +void ima_putc(struct seq_file *m, void *data, int datalen); +void ima_print_digest(struct seq_file *m, u8 *digest, int size); +struct ima_template_desc *ima_template_desc_current(void); +int ima_init_template(void); + +int ima_init_template(void);  /*   * used to protect h_table and sha_table @@ -98,14 +133,22 @@ static inline unsigned long ima_hash_key(u8 *digest)  int ima_get_action(struct inode *inode, int mask, int function);  int ima_must_measure(struct inode *inode, int mask, int function);  int ima_collect_measurement(struct integrity_iint_cache *iint, -			    struct file *file); +			    struct file *file, +			    struct evm_ima_xattr_data **xattr_value, +			    int *xattr_len);  void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, -			   const unsigned char *filename); +			   const unsigned char *filename, +			   struct evm_ima_xattr_data *xattr_value, +			   int xattr_len);  void ima_audit_measurement(struct integrity_iint_cache *iint,  			   const unsigned char *filename); +int ima_alloc_init_template(struct integrity_iint_cache *iint, +			    struct file *file, const unsigned char *filename, +			    struct evm_ima_xattr_data *xattr_value, +			    int xattr_len, struct ima_template_entry **entry);  int ima_store_template(struct ima_template_entry *entry, int violation, -		       struct inode *inode); -void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); +		       struct inode *inode, const unsigned char *filename); +void ima_free_template_entry(struct ima_template_entry *entry);  const char *ima_d_path(struct path *path, char **pathbuf);  /* rbtree tree calls to lookup, insert, delete @@ -131,17 +174,25 @@ void ima_delete_rules(void);  #ifdef CONFIG_IMA_APPRAISE  int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, -			     struct file *file, const unsigned char *filename); +			     struct file *file, const unsigned char *filename, +			     struct evm_ima_xattr_data *xattr_value, +			     int xattr_len);  int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);  void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);  enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,  					   int func); +void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, +		       struct ima_digest_data *hash); +int ima_read_xattr(struct dentry *dentry, +		   struct evm_ima_xattr_data **xattr_value);  #else  static inline int ima_appraise_measurement(int func,  					   struct integrity_iint_cache *iint,  					   struct file *file, -					   const unsigned char *filename) +					   const unsigned char *filename, +					   struct evm_ima_xattr_data *xattr_value, +					   int xattr_len)  {  	return INTEGRITY_UNKNOWN;  } @@ -162,6 +213,19 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c  {  	return INTEGRITY_UNKNOWN;  } + +static inline void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, +				     int xattr_len, +				     struct ima_digest_data *hash) +{ +} + +static inline int ima_read_xattr(struct dentry *dentry, +				 struct evm_ima_xattr_data **xattr_value) +{ +	return 0; +} +  #endif  /* LSM based policy rules require audit */ diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 1c03e8f1e0e..d9cd5ce14d2 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -18,9 +18,59 @@  #include <linux/fs.h>  #include <linux/xattr.h>  #include <linux/evm.h> +#include <crypto/hash_info.h>  #include "ima.h" -static const char *IMA_TEMPLATE_NAME = "ima"; +/* + * ima_free_template_entry - free an existing template entry + */ +void ima_free_template_entry(struct ima_template_entry *entry) +{ +	int i; + +	for (i = 0; i < entry->template_desc->num_fields; i++) +		kfree(entry->template_data[i].data); + +	kfree(entry); +} + +/* + * ima_alloc_init_template - create and initialize a new template entry + */ +int ima_alloc_init_template(struct integrity_iint_cache *iint, +			    struct file *file, const unsigned char *filename, +			    struct evm_ima_xattr_data *xattr_value, +			    int xattr_len, struct ima_template_entry **entry) +{ +	struct ima_template_desc *template_desc = ima_template_desc_current(); +	int i, result = 0; + +	*entry = kzalloc(sizeof(**entry) + template_desc->num_fields * +			 sizeof(struct ima_field_data), GFP_NOFS); +	if (!*entry) +		return -ENOMEM; + +	(*entry)->template_desc = template_desc; +	for (i = 0; i < template_desc->num_fields; i++) { +		struct ima_template_field *field = template_desc->fields[i]; +		u32 len; + +		result = field->field_init(iint, file, filename, +					   xattr_value, xattr_len, +					   &((*entry)->template_data[i])); +		if (result != 0) +			goto out; + +		len = (*entry)->template_data[i].len; +		(*entry)->template_data_len += sizeof(len); +		(*entry)->template_data_len += len; +	} +	return 0; +out: +	ima_free_template_entry(*entry); +	*entry = NULL; +	return result; +}  /*   * ima_store_template - store ima template measurements @@ -39,28 +89,35 @@ static const char *IMA_TEMPLATE_NAME = "ima";   * Returns 0 on success, error code otherwise   */  int ima_store_template(struct ima_template_entry *entry, -		       int violation, struct inode *inode) +		       int violation, struct inode *inode, +		       const unsigned char *filename)  { -	const char *op = "add_template_measure"; -	const char *audit_cause = "hashing_error"; +	static const char op[] = "add_template_measure"; +	static const char audit_cause[] = "hashing_error"; +	char *template_name = entry->template_desc->name;  	int result; - -	memset(entry->digest, 0, sizeof(entry->digest)); -	entry->template_name = IMA_TEMPLATE_NAME; -	entry->template_len = sizeof(entry->template); +	struct { +		struct ima_digest_data hdr; +		char digest[TPM_DIGEST_SIZE]; +	} hash;  	if (!violation) { -		result = ima_calc_buffer_hash(&entry->template, -						entry->template_len, -						entry->digest); +		int num_fields = entry->template_desc->num_fields; + +		/* this function uses default algo */ +		hash.hdr.algo = HASH_ALGO_SHA1; +		result = ima_calc_field_array_hash(&entry->template_data[0], +						   entry->template_desc, +						   num_fields, &hash.hdr);  		if (result < 0) {  			integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, -					    entry->template_name, op, +					    template_name, op,  					    audit_cause, result, 0);  			return result;  		} +		memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);  	} -	result = ima_add_template_entry(entry, violation, op, inode); +	result = ima_add_template_entry(entry, violation, op, inode, filename);  	return result;  } @@ -71,26 +128,26 @@ int ima_store_template(struct ima_template_entry *entry,   * By extending the PCR with 0xFF's instead of with zeroes, the PCR   * value is invalidated.   */ -void ima_add_violation(struct inode *inode, const unsigned char *filename, +void ima_add_violation(struct file *file, const unsigned char *filename,  		       const char *op, const char *cause)  {  	struct ima_template_entry *entry; +	struct inode *inode = file_inode(file);  	int violation = 1;  	int result;  	/* can overflow, only indicator */  	atomic_long_inc(&ima_htable.violations); -	entry = kmalloc(sizeof(*entry), GFP_KERNEL); -	if (!entry) { +	result = ima_alloc_init_template(NULL, file, filename, +					 NULL, 0, &entry); +	if (result < 0) {  		result = -ENOMEM;  		goto err_out;  	} -	memset(&entry->template, 0, sizeof(entry->template)); -	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); -	result = ima_store_template(entry, violation, inode); +	result = ima_store_template(entry, violation, inode, filename);  	if (result < 0) -		kfree(entry); +		ima_free_template_entry(entry);  err_out:  	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,  			    op, cause, result, 0); @@ -103,10 +160,10 @@ err_out:   * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK)   *   * The policy is defined in terms of keypairs: - * 		subj=, obj=, type=, func=, mask=, fsmagic= + *		subj=, obj=, type=, func=, mask=, fsmagic=   *	subj,obj, and type: are LSM specific. - * 	func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK - * 	mask: contains the permission mask + *	func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK + *	mask: contains the permission mask   *	fsmagic: hex value   *   * Returns IMA_MEASURE, IMA_APPRAISE mask. @@ -138,25 +195,55 @@ int ima_must_measure(struct inode *inode, int mask, int function)   * Return 0 on success, error code otherwise   */  int ima_collect_measurement(struct integrity_iint_cache *iint, -			    struct file *file) +			    struct file *file, +			    struct evm_ima_xattr_data **xattr_value, +			    int *xattr_len)  { +	const char *audit_cause = "failed";  	struct inode *inode = file_inode(file);  	const char *filename = file->f_dentry->d_name.name;  	int result = 0; +	struct { +		struct ima_digest_data hdr; +		char digest[IMA_MAX_DIGEST_SIZE]; +	} hash; + +	if (xattr_value) +		*xattr_len = ima_read_xattr(file->f_dentry, xattr_value);  	if (!(iint->flags & IMA_COLLECTED)) {  		u64 i_version = file_inode(file)->i_version; -		iint->ima_xattr.type = IMA_XATTR_DIGEST; -		result = ima_calc_file_hash(file, iint->ima_xattr.digest); +		if (file->f_flags & O_DIRECT) { +			audit_cause = "failed(directio)"; +			result = -EACCES; +			goto out; +		} + +		/* use default hash algorithm */ +		hash.hdr.algo = ima_hash_algo; + +		if (xattr_value) +			ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr); + +		result = ima_calc_file_hash(file, &hash.hdr);  		if (!result) { -			iint->version = i_version; -			iint->flags |= IMA_COLLECTED; +			int length = sizeof(hash.hdr) + hash.hdr.length; +			void *tmpbuf = krealloc(iint->ima_hash, length, +						GFP_NOFS); +			if (tmpbuf) { +				iint->ima_hash = tmpbuf; +				memcpy(iint->ima_hash, &hash, length); +				iint->version = i_version; +				iint->flags |= IMA_COLLECTED; +			} else +				result = -ENOMEM;  		}  	} +out:  	if (result)  		integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, -				    filename, "collect_data", "failed", +				    filename, "collect_data", audit_cause,  				    result, 0);  	return result;  } @@ -169,7 +256,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,   *   * We only get here if the inode has not already been measured,   * but the measurement could already exist: - * 	- multiple copies of the same file on either the same or + *	- multiple copies of the same file on either the same or   *	  different filesystems.   *	- the inode was previously flushed as well as the iint info,   *	  containing the hashing info. @@ -177,10 +264,12 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,   * Must be called with iint->mutex held.   */  void ima_store_measurement(struct integrity_iint_cache *iint, -			   struct file *file, const unsigned char *filename) +			   struct file *file, const unsigned char *filename, +			   struct evm_ima_xattr_data *xattr_value, +			   int xattr_len)  { -	const char *op = "add_template_measure"; -	const char *audit_cause = "ENOMEM"; +	static const char op[] = "add_template_measure"; +	static const char audit_cause[] = "ENOMEM";  	int result = -ENOMEM;  	struct inode *inode = file_inode(file);  	struct ima_template_entry *entry; @@ -189,37 +278,35 @@ void ima_store_measurement(struct integrity_iint_cache *iint,  	if (iint->flags & IMA_MEASURED)  		return; -	entry = kmalloc(sizeof(*entry), GFP_KERNEL); -	if (!entry) { +	result = ima_alloc_init_template(iint, file, filename, +					 xattr_value, xattr_len, &entry); +	if (result < 0) {  		integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,  				    op, audit_cause, result, 0);  		return;  	} -	memset(&entry->template, 0, sizeof(entry->template)); -	memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE); -	strcpy(entry->template.file_name, -	       (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ? -	       file->f_dentry->d_name.name : filename); -	result = ima_store_template(entry, violation, inode); +	result = ima_store_template(entry, violation, inode, filename);  	if (!result || result == -EEXIST)  		iint->flags |= IMA_MEASURED;  	if (result < 0) -		kfree(entry); +		ima_free_template_entry(entry);  }  void ima_audit_measurement(struct integrity_iint_cache *iint,  			   const unsigned char *filename)  {  	struct audit_buffer *ab; -	char hash[(IMA_DIGEST_SIZE * 2) + 1]; +	char hash[(iint->ima_hash->length * 2) + 1]; +	const char *algo_name = hash_algo_name[iint->ima_hash->algo]; +	char algo_hash[sizeof(hash) + strlen(algo_name) + 2];  	int i;  	if (iint->flags & IMA_AUDITED)  		return; -	for (i = 0; i < IMA_DIGEST_SIZE; i++) -		hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]); +	for (i = 0; i < iint->ima_hash->length; i++) +		hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);  	hash[i * 2] = '\0';  	ab = audit_log_start(current->audit_context, GFP_KERNEL, @@ -230,7 +317,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,  	audit_log_format(ab, "file=");  	audit_log_untrustedstring(ab, filename);  	audit_log_format(ab, " hash="); -	audit_log_untrustedstring(ab, hash); +	snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash); +	audit_log_untrustedstring(ab, algo_hash);  	audit_log_task_info(ab, current);  	audit_log_end(ab); @@ -252,5 +340,5 @@ const char *ima_d_path(struct path *path, char **pathbuf)  			pathname = NULL;  		}  	} -	return pathname; +	return pathname ?: (const char *)path->dentry->d_name.name;  } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 2d4becab891..d3113d4aaa3 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -15,6 +15,7 @@  #include <linux/magic.h>  #include <linux/ima.h>  #include <linux/evm.h> +#include <crypto/hash_info.h>  #include "ima.h" @@ -43,19 +44,31 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)  }  static int ima_fix_xattr(struct dentry *dentry, -			  struct integrity_iint_cache *iint) +			 struct integrity_iint_cache *iint)  { -	iint->ima_xattr.type = IMA_XATTR_DIGEST; -	return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, -				     (u8 *)&iint->ima_xattr, -				      sizeof(iint->ima_xattr), 0); +	int rc, offset; +	u8 algo = iint->ima_hash->algo; + +	if (algo <= HASH_ALGO_SHA1) { +		offset = 1; +		iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST; +	} else { +		offset = 0; +		iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; +		iint->ima_hash->xattr.ng.algo = algo; +	} +	rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, +				   &iint->ima_hash->xattr.data[offset], +				   (sizeof(iint->ima_hash->xattr) - offset) + +				   iint->ima_hash->length, 0); +	return rc;  }  /* Return specific func appraised cached result */  enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,  					   int func)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		return iint->ima_mmap_status;  	case BPRM_CHECK: @@ -71,7 +84,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,  static void ima_set_cache_status(struct integrity_iint_cache *iint,  				 int func, enum integrity_status status)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		iint->ima_mmap_status = status;  		break; @@ -90,7 +103,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,  static void ima_cache_flags(struct integrity_iint_cache *iint, int func)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);  		break; @@ -107,6 +120,50 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)  	}  } +void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, +		       struct ima_digest_data *hash) +{ +	struct signature_v2_hdr *sig; + +	if (!xattr_value || xattr_len < 2) +		return; + +	switch (xattr_value->type) { +	case EVM_IMA_XATTR_DIGSIG: +		sig = (typeof(sig))xattr_value; +		if (sig->version != 2 || xattr_len <= sizeof(*sig)) +			return; +		hash->algo = sig->hash_algo; +		break; +	case IMA_XATTR_DIGEST_NG: +		hash->algo = xattr_value->digest[0]; +		break; +	case IMA_XATTR_DIGEST: +		/* this is for backward compatibility */ +		if (xattr_len == 21) { +			unsigned int zero = 0; +			if (!memcmp(&xattr_value->digest[16], &zero, 4)) +				hash->algo = HASH_ALGO_MD5; +			else +				hash->algo = HASH_ALGO_SHA1; +		} else if (xattr_len == 17) +			hash->algo = HASH_ALGO_MD5; +		break; +	} +} + +int ima_read_xattr(struct dentry *dentry, +		   struct evm_ima_xattr_data **xattr_value) +{ +	struct inode *inode = dentry->d_inode; + +	if (!inode->i_op->getxattr) +		return 0; + +	return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, +				  0, GFP_NOFS); +} +  /*   * ima_appraise_measurement - appraise file measurement   * @@ -116,23 +173,22 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)   * Return 0 on success, error code otherwise   */  int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, -			     struct file *file, const unsigned char *filename) +			     struct file *file, const unsigned char *filename, +			     struct evm_ima_xattr_data *xattr_value, +			     int xattr_len)  { +	static const char op[] = "appraise_data"; +	char *cause = "unknown";  	struct dentry *dentry = file->f_dentry;  	struct inode *inode = dentry->d_inode; -	struct evm_ima_xattr_data *xattr_value = NULL;  	enum integrity_status status = INTEGRITY_UNKNOWN; -	const char *op = "appraise_data"; -	char *cause = "unknown"; -	int rc; +	int rc = xattr_len, hash_start = 0;  	if (!ima_appraise)  		return 0;  	if (!inode->i_op->getxattr)  		return INTEGRITY_UNKNOWN; -	rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, -				0, GFP_NOFS);  	if (rc <= 0) {  		if (rc && rc != -ENODATA)  			goto out; @@ -153,14 +209,25 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,  		goto out;  	}  	switch (xattr_value->type) { +	case IMA_XATTR_DIGEST_NG: +		/* first byte contains algorithm id */ +		hash_start = 1;  	case IMA_XATTR_DIGEST:  		if (iint->flags & IMA_DIGSIG_REQUIRED) {  			cause = "IMA signature required";  			status = INTEGRITY_FAIL;  			break;  		} -		rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, -			    IMA_DIGEST_SIZE); +		if (xattr_len - sizeof(xattr_value->type) - hash_start >= +				iint->ima_hash->length) +			/* xattr length may be longer. md5 hash in previous +			   version occupied 20 bytes in xattr, instead of 16 +			 */ +			rc = memcmp(&xattr_value->digest[hash_start], +				    iint->ima_hash->digest, +				    iint->ima_hash->length); +		else +			rc = -EINVAL;  		if (rc) {  			cause = "invalid-hash";  			status = INTEGRITY_FAIL; @@ -171,9 +238,9 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,  	case EVM_IMA_XATTR_DIGSIG:  		iint->flags |= IMA_DIGSIG;  		rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, -					     xattr_value->digest, rc - 1, -					     iint->ima_xattr.digest, -					     IMA_DIGEST_SIZE); +					     (const char *)xattr_value, rc, +					     iint->ima_hash->digest, +					     iint->ima_hash->length);  		if (rc == -EOPNOTSUPP) {  			status = INTEGRITY_UNKNOWN;  		} else if (rc) { @@ -203,7 +270,6 @@ out:  		ima_cache_flags(iint, func);  	}  	ima_set_cache_status(iint, func, status); -	kfree(xattr_value);  	return status;  } @@ -219,7 +285,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)  	if (iint->flags & IMA_DIGSIG)  		return; -	rc = ima_collect_measurement(iint, file); +	rc = ima_collect_measurement(iint, file, NULL, NULL);  	if (rc < 0)  		return; @@ -275,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,  	return 0;  } -static void ima_reset_appraise_flags(struct inode *inode) +static void ima_reset_appraise_flags(struct inode *inode, int digsig)  {  	struct integrity_iint_cache *iint; @@ -287,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode)  		return;  	iint->flags &= ~IMA_DONE_MASK; +	if (digsig) +		iint->flags |= IMA_DIGSIG;  	return;  }  int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,  		       const void *xattr_value, size_t xattr_value_len)  { +	const struct evm_ima_xattr_data *xvalue = xattr_value;  	int result;  	result = ima_protect_xattr(dentry, xattr_name, xattr_value,  				   xattr_value_len);  	if (result == 1) { -		ima_reset_appraise_flags(dentry->d_inode); +		ima_reset_appraise_flags(dentry->d_inode, +			 (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);  		result = 0;  	}  	return result; @@ -310,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)  	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);  	if (result == 1) { -		ima_reset_appraise_flags(dentry->d_inode); +		ima_reset_appraise_flags(dentry->d_inode, 0);  		result = 0;  	}  	return result; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index a02e0791cf1..ccd0ac8fa9a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -10,9 +10,11 @@   * the Free Software Foundation, version 2 of the License.   *   * File: ima_crypto.c - * 	Calculates md5/sha1 file hash, template hash, boot-aggreate hash + *	Calculates md5/sha1 file hash, template hash, boot-aggreate hash   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/kernel.h>  #include <linux/file.h>  #include <linux/crypto.h> @@ -20,57 +22,119 @@  #include <linux/err.h>  #include <linux/slab.h>  #include <crypto/hash.h> +#include <crypto/hash_info.h>  #include "ima.h"  static struct crypto_shash *ima_shash_tfm; +/** + * ima_kernel_read - read file content + * + * This is a function for reading file content instead of kernel_read(). + * It does not perform locking checks to ensure it cannot be blocked. + * It does not perform security checks because it is irrelevant for IMA. + * + */ +static int ima_kernel_read(struct file *file, loff_t offset, +			   char *addr, unsigned long count) +{ +	mm_segment_t old_fs; +	char __user *buf = addr; +	ssize_t ret; + +	if (!(file->f_mode & FMODE_READ)) +		return -EBADF; +	if (!file->f_op->read && !file->f_op->aio_read) +		return -EINVAL; + +	old_fs = get_fs(); +	set_fs(get_ds()); +	if (file->f_op->read) +		ret = file->f_op->read(file, buf, count, &offset); +	else +		ret = do_sync_read(file, buf, count, &offset); +	set_fs(old_fs); +	return ret; +} +  int ima_init_crypto(void)  {  	long rc; -	ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0); +	ima_shash_tfm = crypto_alloc_shash(hash_algo_name[ima_hash_algo], 0, 0);  	if (IS_ERR(ima_shash_tfm)) {  		rc = PTR_ERR(ima_shash_tfm); -		pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc); +		pr_err("Can not allocate %s (reason: %ld)\n", +		       hash_algo_name[ima_hash_algo], rc);  		return rc;  	}  	return 0;  } +static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo) +{ +	struct crypto_shash *tfm = ima_shash_tfm; +	int rc; + +	if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) { +		tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); +		if (IS_ERR(tfm)) { +			rc = PTR_ERR(tfm); +			pr_err("Can not allocate %s (reason: %d)\n", +			       hash_algo_name[algo], rc); +		} +	} +	return tfm; +} + +static void ima_free_tfm(struct crypto_shash *tfm) +{ +	if (tfm != ima_shash_tfm) +		crypto_free_shash(tfm); +} +  /*   * Calculate the MD5/SHA1 file digest   */ -int ima_calc_file_hash(struct file *file, char *digest) +static int ima_calc_file_hash_tfm(struct file *file, +				  struct ima_digest_data *hash, +				  struct crypto_shash *tfm)  {  	loff_t i_size, offset = 0;  	char *rbuf;  	int rc, read = 0;  	struct {  		struct shash_desc shash; -		char ctx[crypto_shash_descsize(ima_shash_tfm)]; +		char ctx[crypto_shash_descsize(tfm)];  	} desc; -	desc.shash.tfm = ima_shash_tfm; +	desc.shash.tfm = tfm;  	desc.shash.flags = 0; +	hash->length = crypto_shash_digestsize(tfm); +  	rc = crypto_shash_init(&desc.shash);  	if (rc != 0)  		return rc; -	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); -	if (!rbuf) { -		rc = -ENOMEM; +	i_size = i_size_read(file_inode(file)); + +	if (i_size == 0)  		goto out; -	} + +	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); +	if (!rbuf) +		return -ENOMEM; +  	if (!(file->f_mode & FMODE_READ)) {  		file->f_mode |= FMODE_READ;  		read = 1;  	} -	i_size = i_size_read(file_inode(file)); +  	while (offset < i_size) {  		int rbuf_len; -		rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE); +		rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE);  		if (rbuf_len < 0) {  			rc = rbuf_len;  			break; @@ -83,29 +147,99 @@ int ima_calc_file_hash(struct file *file, char *digest)  		if (rc)  			break;  	} -	kfree(rbuf); -	if (!rc) -		rc = crypto_shash_final(&desc.shash, digest);  	if (read)  		file->f_mode &= ~FMODE_READ; +	kfree(rbuf);  out: +	if (!rc) +		rc = crypto_shash_final(&desc.shash, hash->digest); +	return rc; +} + +int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) +{ +	struct crypto_shash *tfm; +	int rc; + +	tfm = ima_alloc_tfm(hash->algo); +	if (IS_ERR(tfm)) +		return PTR_ERR(tfm); + +	rc = ima_calc_file_hash_tfm(file, hash, tfm); + +	ima_free_tfm(tfm); +  	return rc;  }  /* - * Calculate the hash of a given buffer + * Calculate the hash of template data   */ -int ima_calc_buffer_hash(const void *data, int len, char *digest) +static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, +					 struct ima_template_desc *td, +					 int num_fields, +					 struct ima_digest_data *hash, +					 struct crypto_shash *tfm)  {  	struct {  		struct shash_desc shash; -		char ctx[crypto_shash_descsize(ima_shash_tfm)]; +		char ctx[crypto_shash_descsize(tfm)];  	} desc; +	int rc, i; -	desc.shash.tfm = ima_shash_tfm; +	desc.shash.tfm = tfm;  	desc.shash.flags = 0; -	return crypto_shash_digest(&desc.shash, data, len, digest); +	hash->length = crypto_shash_digestsize(tfm); + +	rc = crypto_shash_init(&desc.shash); +	if (rc != 0) +		return rc; + +	for (i = 0; i < num_fields; i++) { +		u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 }; +		u8 *data_to_hash = field_data[i].data; +		u32 datalen = field_data[i].len; + +		if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) { +			rc = crypto_shash_update(&desc.shash, +						(const u8 *) &field_data[i].len, +						sizeof(field_data[i].len)); +			if (rc) +				break; +		} else if (strcmp(td->fields[i]->field_id, "n") == 0) { +			memcpy(buffer, data_to_hash, datalen); +			data_to_hash = buffer; +			datalen = IMA_EVENT_NAME_LEN_MAX + 1; +		} +		rc = crypto_shash_update(&desc.shash, data_to_hash, datalen); +		if (rc) +			break; +	} + +	if (!rc) +		rc = crypto_shash_final(&desc.shash, hash->digest); + +	return rc; +} + +int ima_calc_field_array_hash(struct ima_field_data *field_data, +			      struct ima_template_desc *desc, int num_fields, +			      struct ima_digest_data *hash) +{ +	struct crypto_shash *tfm; +	int rc; + +	tfm = ima_alloc_tfm(hash->algo); +	if (IS_ERR(tfm)) +		return PTR_ERR(tfm); + +	rc = ima_calc_field_array_hash_tfm(field_data, desc, num_fields, +					   hash, tfm); + +	ima_free_tfm(tfm); + +	return rc;  }  static void __init ima_pcrread(int idx, u8 *pcr) @@ -114,22 +248,23 @@ static void __init ima_pcrread(int idx, u8 *pcr)  		return;  	if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0) -		pr_err("IMA: Error Communicating to TPM chip\n"); +		pr_err("Error Communicating to TPM chip\n");  }  /*   * Calculate the boot aggregate hash   */ -int __init ima_calc_boot_aggregate(char *digest) +static int __init ima_calc_boot_aggregate_tfm(char *digest, +					      struct crypto_shash *tfm)  { -	u8 pcr_i[IMA_DIGEST_SIZE]; +	u8 pcr_i[TPM_DIGEST_SIZE];  	int rc, i;  	struct {  		struct shash_desc shash; -		char ctx[crypto_shash_descsize(ima_shash_tfm)]; +		char ctx[crypto_shash_descsize(tfm)];  	} desc; -	desc.shash.tfm = ima_shash_tfm; +	desc.shash.tfm = tfm;  	desc.shash.flags = 0;  	rc = crypto_shash_init(&desc.shash); @@ -140,9 +275,26 @@ int __init ima_calc_boot_aggregate(char *digest)  	for (i = TPM_PCR0; i < TPM_PCR8; i++) {  		ima_pcrread(i, pcr_i);  		/* now accumulate with current aggregate */ -		rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE); +		rc = crypto_shash_update(&desc.shash, pcr_i, TPM_DIGEST_SIZE);  	}  	if (!rc)  		crypto_shash_final(&desc.shash, digest);  	return rc;  } + +int __init ima_calc_boot_aggregate(struct ima_digest_data *hash) +{ +	struct crypto_shash *tfm; +	int rc; + +	tfm = ima_alloc_tfm(hash->algo); +	if (IS_ERR(tfm)) +		return PTR_ERR(tfm); + +	hash->length = crypto_shash_digestsize(tfm); +	rc = ima_calc_boot_aggregate_tfm(hash->digest, tfm); + +	ima_free_tfm(tfm); + +	return rc; +} diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 38477c9c341..da92fcc08d1 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -88,8 +88,7 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)  	 * against concurrent list-extension  	 */  	rcu_read_lock(); -	qe = list_entry_rcu(qe->later.next, -			    struct ima_queue_entry, later); +	qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later);  	rcu_read_unlock();  	(*pos)++; @@ -100,7 +99,7 @@ static void ima_measurements_stop(struct seq_file *m, void *v)  {  } -static void ima_putc(struct seq_file *m, void *data, int datalen) +void ima_putc(struct seq_file *m, void *data, int datalen)  {  	while (datalen--)  		seq_putc(m, *(char *)data++); @@ -111,6 +110,7 @@ static void ima_putc(struct seq_file *m, void *data, int datalen)   *       char[20]=template digest   *       32bit-le=template name size   *       char[n]=template name + *       [eventdata length]   *       eventdata[n]=template specific data   */  static int ima_measurements_show(struct seq_file *m, void *v) @@ -120,6 +120,8 @@ static int ima_measurements_show(struct seq_file *m, void *v)  	struct ima_template_entry *e;  	int namelen;  	u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; +	bool is_ima_template = false; +	int i;  	/* get entry */  	e = qe->entry; @@ -131,21 +133,37 @@ static int ima_measurements_show(struct seq_file *m, void *v)  	 * PCR used is always the same (config option) in  	 * little-endian format  	 */ -	ima_putc(m, &pcr, sizeof pcr); +	ima_putc(m, &pcr, sizeof(pcr));  	/* 2nd: template digest */ -	ima_putc(m, e->digest, IMA_DIGEST_SIZE); +	ima_putc(m, e->digest, TPM_DIGEST_SIZE);  	/* 3rd: template name size */ -	namelen = strlen(e->template_name); -	ima_putc(m, &namelen, sizeof namelen); +	namelen = strlen(e->template_desc->name); +	ima_putc(m, &namelen, sizeof(namelen));  	/* 4th:  template name */ -	ima_putc(m, (void *)e->template_name, namelen); - -	/* 5th:  template specific data */ -	ima_template_show(m, (struct ima_template_data *)&e->template, -			  IMA_SHOW_BINARY); +	ima_putc(m, e->template_desc->name, namelen); + +	/* 5th:  template length (except for 'ima' template) */ +	if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) +		is_ima_template = true; + +	if (!is_ima_template) +		ima_putc(m, &e->template_data_len, +			 sizeof(e->template_data_len)); + +	/* 6th:  template specific data */ +	for (i = 0; i < e->template_desc->num_fields; i++) { +		enum ima_show_type show = IMA_SHOW_BINARY; +		struct ima_template_field *field = e->template_desc->fields[i]; + +		if (is_ima_template && strcmp(field->field_id, "d") == 0) +			show = IMA_SHOW_BINARY_NO_FIELD_LEN; +		if (is_ima_template && strcmp(field->field_id, "n") == 0) +			show = IMA_SHOW_BINARY_OLD_STRING_FMT; +		field->field_show(m, show, &e->template_data[i]); +	}  	return 0;  } @@ -168,41 +186,21 @@ static const struct file_operations ima_measurements_ops = {  	.release = seq_release,  }; -static void ima_print_digest(struct seq_file *m, u8 *digest) +void ima_print_digest(struct seq_file *m, u8 *digest, int size)  {  	int i; -	for (i = 0; i < IMA_DIGEST_SIZE; i++) +	for (i = 0; i < size; i++)  		seq_printf(m, "%02x", *(digest + i));  } -void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show) -{ -	struct ima_template_data *entry = e; -	int namelen; - -	switch (show) { -	case IMA_SHOW_ASCII: -		ima_print_digest(m, entry->digest); -		seq_printf(m, " %s\n", entry->file_name); -		break; -	case IMA_SHOW_BINARY: -		ima_putc(m, entry->digest, IMA_DIGEST_SIZE); - -		namelen = strlen(entry->file_name); -		ima_putc(m, &namelen, sizeof namelen); -		ima_putc(m, entry->file_name, namelen); -	default: -		break; -	} -} -  /* print in ascii */  static int ima_ascii_measurements_show(struct seq_file *m, void *v)  {  	/* the list never shrinks, so we don't need a lock here */  	struct ima_queue_entry *qe = v;  	struct ima_template_entry *e; +	int i;  	/* get entry */  	e = qe->entry; @@ -213,14 +211,21 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)  	seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);  	/* 2nd: SHA1 template hash */ -	ima_print_digest(m, e->digest); +	ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);  	/* 3th:  template name */ -	seq_printf(m, " %s ", e->template_name); +	seq_printf(m, " %s", e->template_desc->name);  	/* 4th:  template specific data */ -	ima_template_show(m, (struct ima_template_data *)&e->template, -			  IMA_SHOW_ASCII); +	for (i = 0; i < e->template_desc->num_fields; i++) { +		seq_puts(m, " "); +		if (e->template_data[i].len == 0) +			continue; + +		e->template_desc->fields[i]->field_show(m, IMA_SHOW_ASCII, +							&e->template_data[i]); +	} +	seq_puts(m, "\n");  	return 0;  } @@ -287,7 +292,7 @@ static atomic_t policy_opencount = ATOMIC_INIT(1);  /*   * ima_open_policy: sequentialize access to the policy file   */ -static int ima_open_policy(struct inode * inode, struct file * filp) +static int ima_open_policy(struct inode *inode, struct file *filp)  {  	/* No point in being allowed to open it if you aren't going to write */  	if (!(filp->f_flags & O_WRONLY)) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 162ea723db3..e8f9d70a465 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -14,10 +14,14 @@   * File: ima_init.c   *             initialization and cleanup functions   */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/scatterlist.h>  #include <linux/slab.h>  #include <linux/err.h> +#include <crypto/hash_info.h>  #include "ima.h"  /* name for boot aggregate entry */ @@ -41,31 +45,40 @@ int ima_used_chip;   */  static void __init ima_add_boot_aggregate(void)  { -	struct ima_template_entry *entry; -	const char *op = "add_boot_aggregate"; +	static const char op[] = "add_boot_aggregate";  	const char *audit_cause = "ENOMEM"; +	struct ima_template_entry *entry; +	struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;  	int result = -ENOMEM; -	int violation = 1; +	int violation = 0; +	struct { +		struct ima_digest_data hdr; +		char digest[TPM_DIGEST_SIZE]; +	} hash; -	entry = kmalloc(sizeof(*entry), GFP_KERNEL); -	if (!entry) -		goto err_out; +	memset(iint, 0, sizeof(*iint)); +	memset(&hash, 0, sizeof(hash)); +	iint->ima_hash = &hash.hdr; +	iint->ima_hash->algo = HASH_ALGO_SHA1; +	iint->ima_hash->length = SHA1_DIGEST_SIZE; -	memset(&entry->template, 0, sizeof(entry->template)); -	strncpy(entry->template.file_name, boot_aggregate_name, -		IMA_EVENT_NAME_LEN_MAX);  	if (ima_used_chip) { -		violation = 0; -		result = ima_calc_boot_aggregate(entry->template.digest); +		result = ima_calc_boot_aggregate(&hash.hdr);  		if (result < 0) {  			audit_cause = "hashing_error"; -			kfree(entry);  			goto err_out;  		}  	} -	result = ima_store_template(entry, violation, NULL); + +	result = ima_alloc_init_template(iint, NULL, boot_aggregate_name, +					 NULL, 0, &entry);  	if (result < 0) -		kfree(entry); +		return; + +	result = ima_store_template(entry, violation, NULL, +				    boot_aggregate_name); +	if (result < 0) +		ima_free_template_entry(entry);  	return;  err_out:  	integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op, @@ -74,7 +87,7 @@ err_out:  int __init ima_init(void)  { -	u8 pcr_i[IMA_DIGEST_SIZE]; +	u8 pcr_i[TPM_DIGEST_SIZE];  	int rc;  	ima_used_chip = 0; @@ -83,11 +96,15 @@ int __init ima_init(void)  		ima_used_chip = 1;  	if (!ima_used_chip) -		pr_info("IMA: No TPM chip found, activating TPM-bypass!\n"); +		pr_info("No TPM chip found, activating TPM-bypass!\n");  	rc = ima_init_crypto();  	if (rc)  		return rc; +	rc = ima_init_template(); +	if (rc != 0) +		return rc; +  	ima_add_boot_aggregate();	/* boot aggregate must be first entry */  	ima_init_policy(); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index e9508d5bbfc..09baa335ebc 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -24,6 +24,7 @@  #include <linux/slab.h>  #include <linux/xattr.h>  #include <linux/ima.h> +#include <crypto/hash_info.h>  #include "ima.h" @@ -35,11 +36,33 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;  int ima_appraise;  #endif -char *ima_hash = "sha1"; +int ima_hash_algo = HASH_ALGO_SHA1; +static int hash_setup_done; +  static int __init hash_setup(char *str)  { -	if (strncmp(str, "md5", 3) == 0) -		ima_hash = "md5"; +	struct ima_template_desc *template_desc = ima_template_desc_current(); +	int i; + +	if (hash_setup_done) +		return 1; + +	if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { +		if (strncmp(str, "sha1", 4) == 0) +			ima_hash_algo = HASH_ALGO_SHA1; +		else if (strncmp(str, "md5", 3) == 0) +			ima_hash_algo = HASH_ALGO_MD5; +		goto out; +	} + +	for (i = 0; i < HASH_ALGO__LAST; i++) { +		if (strcmp(str, hash_algo_name[i]) == 0) { +			ima_hash_algo = i; +			break; +		} +	} +out: +	hash_setup_done = 1;  	return 1;  }  __setup("ima_hash=", hash_setup); @@ -48,18 +71,16 @@ __setup("ima_hash=", hash_setup);   * ima_rdwr_violation_check   *   * Only invalidate the PCR for measured files: - * 	- Opening a file for write when already open for read, + *	- Opening a file for write when already open for read,   *	  results in a time of measure, time of use (ToMToU) error.   *	- Opening a file for read when already open for write, - * 	  could result in a file measurement error. + *	  could result in a file measurement error.   *   */  static void ima_rdwr_violation_check(struct file *file)  { -	struct dentry *dentry = file->f_path.dentry;  	struct inode *inode = file_inode(file);  	fmode_t mode = file->f_mode; -	int must_measure;  	bool send_tomtou = false, send_writers = false;  	char *pathbuf = NULL;  	const char *pathname; @@ -70,32 +91,30 @@ static void ima_rdwr_violation_check(struct file *file)  	mutex_lock(&inode->i_mutex);	/* file metadata: permissions, xattr */  	if (mode & FMODE_WRITE) { -		if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) -			send_tomtou = true; -		goto out; +		if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { +			struct integrity_iint_cache *iint; +			iint = integrity_iint_find(inode); +			/* IMA_MEASURE is set from reader side */ +			if (iint && (iint->flags & IMA_MEASURE)) +				send_tomtou = true; +		} +	} else { +		if ((atomic_read(&inode->i_writecount) > 0) && +		    ima_must_measure(inode, MAY_READ, FILE_CHECK)) +			send_writers = true;  	} -	must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK); -	if (!must_measure) -		goto out; - -	if (atomic_read(&inode->i_writecount) > 0) -		send_writers = true; -out:  	mutex_unlock(&inode->i_mutex);  	if (!send_tomtou && !send_writers)  		return;  	pathname = ima_d_path(&file->f_path, &pathbuf); -	if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX) -		pathname = dentry->d_name.name;  	if (send_tomtou) -		ima_add_violation(inode, pathname, -				  "invalid_pcr", "ToMToU"); +		ima_add_violation(file, pathname, "invalid_pcr", "ToMToU");  	if (send_writers) -		ima_add_violation(inode, pathname, +		ima_add_violation(file, pathname,  				  "invalid_pcr", "open_writers");  	kfree(pathbuf);  } @@ -144,9 +163,12 @@ static int process_measurement(struct file *file, const char *filename,  {  	struct inode *inode = file_inode(file);  	struct integrity_iint_cache *iint; +	struct ima_template_desc *template_desc = ima_template_desc_current();  	char *pathbuf = NULL;  	const char *pathname = NULL;  	int rc = -ENOMEM, action, must_appraise, _func; +	struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; +	int xattr_len = 0;  	if (!ima_initialized || !S_ISREG(inode->i_mode))  		return 0; @@ -185,18 +207,27 @@ static int process_measurement(struct file *file, const char *filename,  		goto out_digsig;  	} -	rc = ima_collect_measurement(iint, file); -	if (rc != 0) +	if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { +		if (action & IMA_APPRAISE_SUBMASK) +			xattr_ptr = &xattr_value; +	} else +		xattr_ptr = &xattr_value; + +	rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); +	if (rc != 0) { +		if (file->f_flags & O_DIRECT) +			rc = (iint->flags & IMA_PERMIT_DIRECTIO) ? 0 : -EACCES;  		goto out_digsig; +	} -	pathname = !filename ? ima_d_path(&file->f_path, &pathbuf) : filename; -	if (!pathname) -		pathname = (const char *)file->f_dentry->d_name.name; +	pathname = filename ?: ima_d_path(&file->f_path, &pathbuf);  	if (action & IMA_MEASURE) -		ima_store_measurement(iint, file, pathname); +		ima_store_measurement(iint, file, pathname, +				      xattr_value, xattr_len);  	if (action & IMA_APPRAISE_SUBMASK) -		rc = ima_appraise_measurement(_func, iint, file, pathname); +		rc = ima_appraise_measurement(_func, iint, file, pathname, +					      xattr_value, xattr_len);  	if (action & IMA_AUDIT)  		ima_audit_measurement(iint, pathname);  	kfree(pathbuf); @@ -205,6 +236,7 @@ out_digsig:  		rc = -EACCES;  out:  	mutex_unlock(&inode->i_mutex); +	kfree(xattr_value);  	if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))  		return -EACCES;  	return 0; @@ -244,9 +276,9 @@ int ima_file_mmap(struct file *file, unsigned long prot)  int ima_bprm_check(struct linux_binprm *bprm)  {  	return process_measurement(bprm->file, -				 (strcmp(bprm->filename, bprm->interp) == 0) ? -				 bprm->filename : bprm->interp, -				 MAY_EXEC, BPRM_CHECK); +				   (strcmp(bprm->filename, bprm->interp) == 0) ? +				   bprm->filename : bprm->interp, +				   MAY_EXEC, BPRM_CHECK);  }  /** @@ -263,8 +295,8 @@ int ima_file_check(struct file *file, int mask)  {  	ima_rdwr_violation_check(file);  	return process_measurement(file, NULL, -				 mask & (MAY_READ | MAY_WRITE | MAY_EXEC), -				 FILE_CHECK); +				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC), +				   FILE_CHECK);  }  EXPORT_SYMBOL_GPL(ima_file_check); @@ -294,6 +326,7 @@ static int __init init_ima(void)  {  	int error; +	hash_setup(CONFIG_IMA_DEFAULT_HASH);  	error = ima_init();  	if (!error)  		ima_initialized = 1; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 399433ad614..40a7488f672 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -7,7 +7,7 @@   * the Free Software Foundation, version 2 of the License.   *   * ima_policy.c - * 	- initialize default measure policy rules + *	- initialize default measure policy rules   *   */  #include <linux/module.h> @@ -21,8 +21,8 @@  #include "ima.h"  /* flags definitions */ -#define IMA_FUNC 	0x0001 -#define IMA_MASK 	0x0002 +#define IMA_FUNC	0x0001 +#define IMA_MASK	0x0002  #define IMA_FSMAGIC	0x0004  #define IMA_UID		0x0008  #define IMA_FOWNER	0x0010 @@ -69,36 +69,35 @@ struct ima_rule_entry {   * and running executables.   */  static struct ima_rule_entry default_rules[] = { -	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC, +	{.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC,  	 .flags = IMA_FUNC | IMA_MASK}, -	{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, +	{.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC,  	 .flags = IMA_FUNC | IMA_MASK}, -	{.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = GLOBAL_ROOT_UID, +	{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID,  	 .flags = IMA_FUNC | IMA_MASK | IMA_UID}, -	{.action = MEASURE,.func = MODULE_CHECK, .flags = IMA_FUNC}, +	{.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC},  };  static struct ima_rule_entry default_appraise_rules[] = { -	{.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC}, -	{.action = APPRAISE,.fowner = GLOBAL_ROOT_UID,.flags = IMA_FOWNER}, +	{.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = RAMFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +	{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER},  };  static LIST_HEAD(ima_default_rules); @@ -123,12 +122,12 @@ static int __init default_appraise_policy_setup(char *str)  }  __setup("ima_appraise_tcb", default_appraise_policy_setup); -/*  +/*   * Although the IMA policy does not change, the LSM policy can be   * reloaded, leaving the IMA LSM based rules referring to the old,   * stale LSM policy.   * - * Update the IMA LSM based rules to reflect the reloaded LSM policy.  + * Update the IMA LSM based rules to reflect the reloaded LSM policy.   * We assume the rules still exist; and BUG_ON() if they don't.   */  static void ima_lsm_update_rules(void) @@ -168,9 +167,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule,  	const struct cred *cred = current_cred();  	int i; -	if ((rule->flags & IMA_FUNC) && rule->func != func) +	if ((rule->flags & IMA_FUNC) && +	    (rule->func != func && func != POST_SETATTR))  		return false; -	if ((rule->flags & IMA_MASK) && rule->mask != mask) +	if ((rule->flags & IMA_MASK) && +	    (rule->mask != mask && func != POST_SETATTR))  		return false;  	if ((rule->flags & IMA_FSMAGIC)  	    && rule->fsmagic != inode->i_sb->s_magic) @@ -217,7 +218,7 @@ retry:  			retried = 1;  			ima_lsm_update_rules();  			goto retry; -		}  +		}  		if (!rc)  			return false;  	} @@ -233,7 +234,7 @@ static int get_subaction(struct ima_rule_entry *rule, int func)  	if (!(rule->flags & IMA_FUNC))  		return IMA_FILE_APPRAISE; -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		return IMA_MMAP_APPRAISE;  	case BPRM_CHECK: @@ -305,7 +306,7 @@ void __init ima_init_policy(void)  	measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0;  	appraise_entries = ima_use_appraise_tcb ?  			 ARRAY_SIZE(default_appraise_rules) : 0; -	 +  	for (i = 0; i < measure_entries + appraise_entries; i++) {  		if (i < measure_entries)  			list_add_tail(&default_rules[i].list, @@ -330,7 +331,7 @@ void __init ima_init_policy(void)   */  void ima_update_policy(void)  { -	const char *op = "policy_update"; +	static const char op[] = "policy_update";  	const char *cause = "already exists";  	int result = 1;  	int audit_info = 0; @@ -352,7 +353,7 @@ enum {  	Opt_obj_user, Opt_obj_role, Opt_obj_type,  	Opt_subj_user, Opt_subj_role, Opt_subj_type,  	Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner, -	Opt_appraise_type, Opt_fsuuid +	Opt_appraise_type, Opt_fsuuid, Opt_permit_directio  };  static match_table_t policy_tokens = { @@ -374,6 +375,7 @@ static match_table_t policy_tokens = {  	{Opt_uid, "uid=%s"},  	{Opt_fowner, "fowner=%s"},  	{Opt_appraise_type, "appraise_type=%s"}, +	{Opt_permit_directio, "permit_directio"},  	{Opt_err, NULL}  }; @@ -521,8 +523,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  				break;  			} -			result = strict_strtoul(args[0].from, 16, -						&entry->fsmagic); +			result = kstrtoul(args[0].from, 16, &entry->fsmagic);  			if (!result)  				entry->flags |= IMA_FSMAGIC;  			break; @@ -548,7 +549,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  				break;  			} -			result = strict_strtoul(args[0].from, 10, &lnum); +			result = kstrtoul(args[0].from, 10, &lnum);  			if (!result) {  				entry->uid = make_kuid(current_user_ns(), (uid_t)lnum);  				if (!uid_valid(entry->uid) || (((uid_t)lnum) != lnum)) @@ -565,7 +566,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  				break;  			} -			result = strict_strtoul(args[0].from, 10, &lnum); +			result = kstrtoul(args[0].from, 10, &lnum);  			if (!result) {  				entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum);  				if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) @@ -622,6 +623,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)  			else  				result = -EINVAL;  			break; +		case Opt_permit_directio: +			entry->flags |= IMA_PERMIT_DIRECTIO; +			break;  		case Opt_err:  			ima_log_string(ab, "UNKNOWN", p);  			result = -EINVAL; @@ -646,7 +650,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)   */  ssize_t ima_parse_add_rule(char *rule)  { -	const char *op = "update_policy"; +	static const char op[] = "update_policy";  	char *p;  	struct ima_rule_entry *entry;  	ssize_t result, len; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index ff63fe00c19..552705d5a78 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -18,6 +18,9 @@   *       The measurement list is append-only. No entry is   *       ever removed or changed during the boot-cycle.   */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/rculist.h>  #include <linux/slab.h> @@ -50,7 +53,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)  	key = ima_hash_key(digest_value);  	rcu_read_lock();  	hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) { -		rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE); +		rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE);  		if (rc == 0) {  			ret = qe;  			break; @@ -72,7 +75,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry)  	qe = kmalloc(sizeof(*qe), GFP_KERNEL);  	if (qe == NULL) { -		pr_err("IMA: OUT OF MEMORY ERROR creating queue entry.\n"); +		pr_err("OUT OF MEMORY ERROR creating queue entry\n");  		return -ENOMEM;  	}  	qe->entry = entry; @@ -95,8 +98,7 @@ static int ima_pcr_extend(const u8 *hash)  	result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);  	if (result != 0) -		pr_err("IMA: Error Communicating to TPM chip, result: %d\n", -		       result); +		pr_err("Error Communicating to TPM chip, result: %d\n", result);  	return result;  } @@ -104,9 +106,10 @@ static int ima_pcr_extend(const u8 *hash)   * and extend the pcr.   */  int ima_add_template_entry(struct ima_template_entry *entry, int violation, -			   const char *op, struct inode *inode) +			   const char *op, struct inode *inode, +			   const unsigned char *filename)  { -	u8 digest[IMA_DIGEST_SIZE]; +	u8 digest[TPM_DIGEST_SIZE];  	const char *audit_cause = "hash_added";  	char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];  	int audit_info = 1; @@ -114,7 +117,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,  	mutex_lock(&ima_extend_list_mutex);  	if (!violation) { -		memcpy(digest, entry->digest, sizeof digest); +		memcpy(digest, entry->digest, sizeof(digest));  		if (ima_lookup_digest_entry(digest)) {  			audit_cause = "hash_exists";  			result = -EEXIST; @@ -130,7 +133,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,  	}  	if (violation)		/* invalidate pcr */ -		memset(digest, 0xff, sizeof digest); +		memset(digest, 0xff, sizeof(digest));  	tpmresult = ima_pcr_extend(digest);  	if (tpmresult != 0) { @@ -141,8 +144,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,  	}  out:  	mutex_unlock(&ima_extend_list_mutex); -	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, -			    entry->template.file_name, +	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,  			    op, audit_cause, result, audit_info);  	return result;  } diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c new file mode 100644 index 00000000000..a076a967ec4 --- /dev/null +++ b/security/integrity/ima/ima_template.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu <roberto.sassu@polito.it> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_template.c + *      Helpers to manage template descriptors. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <crypto/hash_info.h> + +#include "ima.h" +#include "ima_template_lib.h" + +static struct ima_template_desc defined_templates[] = { +	{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, +	{.name = "ima-ng", .fmt = "d-ng|n-ng"}, +	{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, +}; + +static struct ima_template_field supported_fields[] = { +	{.field_id = "d", .field_init = ima_eventdigest_init, +	 .field_show = ima_show_template_digest}, +	{.field_id = "n", .field_init = ima_eventname_init, +	 .field_show = ima_show_template_string}, +	{.field_id = "d-ng", .field_init = ima_eventdigest_ng_init, +	 .field_show = ima_show_template_digest_ng}, +	{.field_id = "n-ng", .field_init = ima_eventname_ng_init, +	 .field_show = ima_show_template_string}, +	{.field_id = "sig", .field_init = ima_eventsig_init, +	 .field_show = ima_show_template_sig}, +}; + +static struct ima_template_desc *ima_template; +static struct ima_template_desc *lookup_template_desc(const char *name); + +static int __init ima_template_setup(char *str) +{ +	struct ima_template_desc *template_desc; +	int template_len = strlen(str); + +	/* +	 * Verify that a template with the supplied name exists. +	 * If not, use CONFIG_IMA_DEFAULT_TEMPLATE. +	 */ +	template_desc = lookup_template_desc(str); +	if (!template_desc) +		return 1; + +	/* +	 * Verify whether the current hash algorithm is supported +	 * by the 'ima' template. +	 */ +	if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 && +	    ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) { +		pr_err("template does not support hash alg\n"); +		return 1; +	} + +	ima_template = template_desc; +	return 1; +} +__setup("ima_template=", ima_template_setup); + +static struct ima_template_desc *lookup_template_desc(const char *name) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { +		if (strcmp(defined_templates[i].name, name) == 0) +			return defined_templates + i; +	} + +	return NULL; +} + +static struct ima_template_field *lookup_template_field(const char *field_id) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(supported_fields); i++) +		if (strncmp(supported_fields[i].field_id, field_id, +			    IMA_TEMPLATE_FIELD_ID_MAX_LEN) == 0) +			return &supported_fields[i]; +	return NULL; +} + +static int template_fmt_size(const char *template_fmt) +{ +	char c; +	int template_fmt_len = strlen(template_fmt); +	int i = 0, j = 0; + +	while (i < template_fmt_len) { +		c = template_fmt[i]; +		if (c == '|') +			j++; +		i++; +	} + +	return j + 1; +} + +static int template_desc_init_fields(const char *template_fmt, +				     struct ima_template_field ***fields, +				     int *num_fields) +{ +	char *c, *template_fmt_copy, *template_fmt_ptr; +	int template_num_fields = template_fmt_size(template_fmt); +	int i, result = 0; + +	if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) +		return -EINVAL; + +	/* copying is needed as strsep() modifies the original buffer */ +	template_fmt_copy = kstrdup(template_fmt, GFP_KERNEL); +	if (template_fmt_copy == NULL) +		return -ENOMEM; + +	*fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL); +	if (*fields == NULL) { +		result = -ENOMEM; +		goto out; +	} + +	template_fmt_ptr = template_fmt_copy; +	for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL && +	     i < template_num_fields; i++) { +		struct ima_template_field *f = lookup_template_field(c); + +		if (!f) { +			result = -ENOENT; +			goto out; +		} +		(*fields)[i] = f; +	} +	*num_fields = i; +out: +	if (result < 0) { +		kfree(*fields); +		*fields = NULL; +	} +	kfree(template_fmt_copy); +	return result; +} + +static int init_defined_templates(void) +{ +	int i = 0; +	int result = 0; + +	/* Init defined templates. */ +	for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { +		struct ima_template_desc *template = &defined_templates[i]; + +		result = template_desc_init_fields(template->fmt, +						   &(template->fields), +						   &(template->num_fields)); +		if (result < 0) +			return result; +	} +	return result; +} + +struct ima_template_desc *ima_template_desc_current(void) +{ +	if (!ima_template) +		ima_template = +		    lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE); +	return ima_template; +} + +int ima_init_template(void) +{ +	int result; + +	result = init_defined_templates(); +	if (result < 0) +		return result; + +	return 0; +} diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c new file mode 100644 index 00000000000..1506f024857 --- /dev/null +++ b/security/integrity/ima/ima_template_lib.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu <roberto.sassu@polito.it> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_template_lib.c + *      Library of supported template fields. + */ +#include <crypto/hash_info.h> + +#include "ima_template_lib.h" + +static bool ima_template_hash_algo_allowed(u8 algo) +{ +	if (algo == HASH_ALGO_SHA1 || algo == HASH_ALGO_MD5) +		return true; + +	return false; +} + +enum data_formats { +	DATA_FMT_DIGEST = 0, +	DATA_FMT_DIGEST_WITH_ALGO, +	DATA_FMT_STRING, +	DATA_FMT_HEX +}; + +static int ima_write_template_field_data(const void *data, const u32 datalen, +					 enum data_formats datafmt, +					 struct ima_field_data *field_data) +{ +	u8 *buf, *buf_ptr; +	u32 buflen = datalen; + +	if (datafmt == DATA_FMT_STRING) +		buflen = datalen + 1; + +	buf = kzalloc(buflen, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	memcpy(buf, data, datalen); + +	/* +	 * Replace all space characters with underscore for event names and +	 * strings. This avoid that, during the parsing of a measurements list, +	 * filenames with spaces or that end with the suffix ' (deleted)' are +	 * split into multiple template fields (the space is the delimitator +	 * character for measurements lists in ASCII format). +	 */ +	if (datafmt == DATA_FMT_STRING) { +		for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++) +			if (*buf_ptr == ' ') +				*buf_ptr = '_'; +	} + +	field_data->data = buf; +	field_data->len = buflen; +	return 0; +} + +static void ima_show_template_data_ascii(struct seq_file *m, +					 enum ima_show_type show, +					 enum data_formats datafmt, +					 struct ima_field_data *field_data) +{ +	u8 *buf_ptr = field_data->data, buflen = field_data->len; + +	switch (datafmt) { +	case DATA_FMT_DIGEST_WITH_ALGO: +		buf_ptr = strnchr(field_data->data, buflen, ':'); +		if (buf_ptr != field_data->data) +			seq_printf(m, "%s", field_data->data); + +		/* skip ':' and '\0' */ +		buf_ptr += 2; +		buflen -= buf_ptr - field_data->data; +	case DATA_FMT_DIGEST: +	case DATA_FMT_HEX: +		if (!buflen) +			break; +		ima_print_digest(m, buf_ptr, buflen); +		break; +	case DATA_FMT_STRING: +		seq_printf(m, "%s", buf_ptr); +		break; +	default: +		break; +	} +} + +static void ima_show_template_data_binary(struct seq_file *m, +					  enum ima_show_type show, +					  enum data_formats datafmt, +					  struct ima_field_data *field_data) +{ +	u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ? +	    strlen(field_data->data) : field_data->len; + +	if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) +		ima_putc(m, &len, sizeof(len)); + +	if (!len) +		return; + +	ima_putc(m, field_data->data, len); +} + +static void ima_show_template_field_data(struct seq_file *m, +					 enum ima_show_type show, +					 enum data_formats datafmt, +					 struct ima_field_data *field_data) +{ +	switch (show) { +	case IMA_SHOW_ASCII: +		ima_show_template_data_ascii(m, show, datafmt, field_data); +		break; +	case IMA_SHOW_BINARY: +	case IMA_SHOW_BINARY_NO_FIELD_LEN: +	case IMA_SHOW_BINARY_OLD_STRING_FMT: +		ima_show_template_data_binary(m, show, datafmt, field_data); +		break; +	default: +		break; +	} +} + +void ima_show_template_digest(struct seq_file *m, enum ima_show_type show, +			      struct ima_field_data *field_data) +{ +	ima_show_template_field_data(m, show, DATA_FMT_DIGEST, field_data); +} + +void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show, +				 struct ima_field_data *field_data) +{ +	ima_show_template_field_data(m, show, DATA_FMT_DIGEST_WITH_ALGO, +				     field_data); +} + +void ima_show_template_string(struct seq_file *m, enum ima_show_type show, +			      struct ima_field_data *field_data) +{ +	ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data); +} + +void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, +			   struct ima_field_data *field_data) +{ +	ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data); +} + +static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, +				       struct ima_field_data *field_data) +{ +	/* +	 * digest formats: +	 *  - DATA_FMT_DIGEST: digest +	 *  - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest, +	 *    where <hash algo> is provided if the hash algoritm is not +	 *    SHA1 or MD5 +	 */ +	u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 }; +	enum data_formats fmt = DATA_FMT_DIGEST; +	u32 offset = 0; + +	if (hash_algo < HASH_ALGO__LAST) { +		fmt = DATA_FMT_DIGEST_WITH_ALGO; +		offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s", +				   hash_algo_name[hash_algo]); +		buffer[offset] = ':'; +		offset += 2; +	} + +	if (digest) +		memcpy(buffer + offset, digest, digestsize); +	else +		/* +		 * If digest is NULL, the event being recorded is a violation. +		 * Make room for the digest by increasing the offset of +		 * IMA_DIGEST_SIZE. +		 */ +		offset += IMA_DIGEST_SIZE; + +	return ima_write_template_field_data(buffer, offset + digestsize, +					     fmt, field_data); +} + +/* + * This function writes the digest of an event (with size limit). + */ +int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, +			 const unsigned char *filename, +			 struct evm_ima_xattr_data *xattr_value, int xattr_len, +			 struct ima_field_data *field_data) +{ +	struct { +		struct ima_digest_data hdr; +		char digest[IMA_MAX_DIGEST_SIZE]; +	} hash; +	u8 *cur_digest = NULL; +	u32 cur_digestsize = 0; +	struct inode *inode; +	int result; + +	memset(&hash, 0, sizeof(hash)); + +	if (!iint)		/* recording a violation. */ +		goto out; + +	if (ima_template_hash_algo_allowed(iint->ima_hash->algo)) { +		cur_digest = iint->ima_hash->digest; +		cur_digestsize = iint->ima_hash->length; +		goto out; +	} + +	if (!file)		/* missing info to re-calculate the digest */ +		return -EINVAL; + +	inode = file_inode(file); +	hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ? +	    ima_hash_algo : HASH_ALGO_SHA1; +	result = ima_calc_file_hash(file, &hash.hdr); +	if (result) { +		integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, +				    filename, "collect_data", +				    "failed", result, 0); +		return result; +	} +	cur_digest = hash.hdr.digest; +	cur_digestsize = hash.hdr.length; +out: +	return ima_eventdigest_init_common(cur_digest, cur_digestsize, +					   HASH_ALGO__LAST, field_data); +} + +/* + * This function writes the digest of an event (without size limit). + */ +int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, +			    struct file *file, const unsigned char *filename, +			    struct evm_ima_xattr_data *xattr_value, +			    int xattr_len, struct ima_field_data *field_data) +{ +	u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1; +	u32 cur_digestsize = 0; + +	/* If iint is NULL, we are recording a violation. */ +	if (!iint) +		goto out; + +	cur_digest = iint->ima_hash->digest; +	cur_digestsize = iint->ima_hash->length; + +	hash_algo = iint->ima_hash->algo; +out: +	return ima_eventdigest_init_common(cur_digest, cur_digestsize, +					   hash_algo, field_data); +} + +static int ima_eventname_init_common(struct integrity_iint_cache *iint, +				     struct file *file, +				     const unsigned char *filename, +				     struct ima_field_data *field_data, +				     bool size_limit) +{ +	const char *cur_filename = NULL; +	u32 cur_filename_len = 0; + +	BUG_ON(filename == NULL && file == NULL); + +	if (filename) { +		cur_filename = filename; +		cur_filename_len = strlen(filename); + +		if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX) +			goto out; +	} + +	if (file) { +		cur_filename = file->f_dentry->d_name.name; +		cur_filename_len = strlen(cur_filename); +	} else +		/* +		 * Truncate filename if the latter is too long and +		 * the file descriptor is not available. +		 */ +		cur_filename_len = IMA_EVENT_NAME_LEN_MAX; +out: +	return ima_write_template_field_data(cur_filename, cur_filename_len, +					     DATA_FMT_STRING, field_data); +} + +/* + * This function writes the name of an event (with size limit). + */ +int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, +		       const unsigned char *filename, +		       struct evm_ima_xattr_data *xattr_value, int xattr_len, +		       struct ima_field_data *field_data) +{ +	return ima_eventname_init_common(iint, file, filename, +					 field_data, true); +} + +/* + * This function writes the name of an event (without size limit). + */ +int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file, +			  const unsigned char *filename, +			  struct evm_ima_xattr_data *xattr_value, int xattr_len, +			  struct ima_field_data *field_data) +{ +	return ima_eventname_init_common(iint, file, filename, +					 field_data, false); +} + +/* + *  ima_eventsig_init - include the file signature as part of the template data + */ +int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file, +		      const unsigned char *filename, +		      struct evm_ima_xattr_data *xattr_value, int xattr_len, +		      struct ima_field_data *field_data) +{ +	enum data_formats fmt = DATA_FMT_HEX; +	int rc = 0; + +	if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) +		goto out; + +	rc = ima_write_template_field_data(xattr_value, xattr_len, fmt, +					   field_data); +out: +	return rc; +} diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h new file mode 100644 index 00000000000..63f6b52cb1c --- /dev/null +++ b/security/integrity/ima/ima_template_lib.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Author: Roberto Sassu <roberto.sassu@polito.it> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_template_lib.h + *      Header for the library of supported template fields. + */ +#ifndef __LINUX_IMA_TEMPLATE_LIB_H +#define __LINUX_IMA_TEMPLATE_LIB_H + +#include <linux/seq_file.h> +#include "ima.h" + +void ima_show_template_digest(struct seq_file *m, enum ima_show_type show, +			      struct ima_field_data *field_data); +void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show, +				 struct ima_field_data *field_data); +void ima_show_template_string(struct seq_file *m, enum ima_show_type show, +			      struct ima_field_data *field_data); +void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, +			   struct ima_field_data *field_data); +int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, +			 const unsigned char *filename, +			 struct evm_ima_xattr_data *xattr_value, int xattr_len, +			 struct ima_field_data *field_data); +int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file, +		       const unsigned char *filename, +		       struct evm_ima_xattr_data *xattr_value, int xattr_len, +		       struct ima_field_data *field_data); +int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, +			    struct file *file, const unsigned char *filename, +			    struct evm_ima_xattr_data *xattr_value, +			    int xattr_len, struct ima_field_data *field_data); +int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file, +			  const unsigned char *filename, +			  struct evm_ima_xattr_data *xattr_value, int xattr_len, +			  struct ima_field_data *field_data); +int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file, +		      const unsigned char *filename, +		      struct evm_ima_xattr_data *xattr_value, int xattr_len, +		      struct ima_field_data *field_data); +#endif /* __LINUX_IMA_TEMPLATE_LIB_H */ diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index c42fb7a70de..33c0a70f6b1 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -30,6 +30,7 @@  #define IMA_ACTION_FLAGS	0xff000000  #define IMA_DIGSIG		0x01000000  #define IMA_DIGSIG_REQUIRED	0x02000000 +#define IMA_PERMIT_DIRECTIO	0x04000000  #define IMA_DO_MASK		(IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \  				 IMA_APPRAISE_SUBMASK) @@ -54,25 +55,57 @@ enum evm_ima_xattr_type {  	IMA_XATTR_DIGEST = 0x01,  	EVM_XATTR_HMAC,  	EVM_IMA_XATTR_DIGSIG, +	IMA_XATTR_DIGEST_NG,  };  struct evm_ima_xattr_data {  	u8 type;  	u8 digest[SHA1_DIGEST_SIZE]; -}  __attribute__((packed)); +} __packed; + +#define IMA_MAX_DIGEST_SIZE	64 + +struct ima_digest_data { +	u8 algo; +	u8 length; +	union { +		struct { +			u8 unused; +			u8 type; +		} sha1; +		struct { +			u8 type; +			u8 algo; +		} ng; +		u8 data[2]; +	} xattr; +	u8 digest[0]; +} __packed; + +/* + * signature format v2 - for using with asymmetric keys + */ +struct signature_v2_hdr { +	uint8_t type;		/* xattr type */ +	uint8_t version;	/* signature format version */ +	uint8_t	hash_algo;	/* Digest algorithm [enum pkey_hash_algo] */ +	uint32_t keyid;		/* IMA key identifier - not X509/PGP specific */ +	uint16_t sig_size;	/* signature size */ +	uint8_t sig[0];		/* signature payload */ +} __packed;  /* integrity data associated with an inode */  struct integrity_iint_cache { -	struct rb_node rb_node; /* rooted in integrity_iint_tree */ +	struct rb_node rb_node;	/* rooted in integrity_iint_tree */  	struct inode *inode;	/* back pointer to inode in question */  	u64 version;		/* track inode changes */  	unsigned long flags; -	struct evm_ima_xattr_data ima_xattr;  	enum integrity_status ima_file_status:4;  	enum integrity_status ima_mmap_status:4;  	enum integrity_status ima_bprm_status:4;  	enum integrity_status ima_module_status:4;  	enum integrity_status evm_status:4; +	struct ima_digest_data *ima_hash;  };  /* rbtree tree calls to lookup, insert, delete @@ -89,7 +122,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode);  #ifdef CONFIG_INTEGRITY_SIGNATURE  int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, -					const char *digest, int digestlen); +			    const char *digest, int digestlen);  #else diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c index d7efb30404a..90987d15b6f 100644 --- a/security/integrity/integrity_audit.c +++ b/security/integrity/integrity_audit.c @@ -7,7 +7,7 @@   * the Free Software Foundation, version 2 of the License.   *   * File: integrity_audit.c - * 	Audit calls for the integrity subsystem + *	Audit calls for the integrity subsystem   */  #include <linux/fs.h> @@ -22,7 +22,7 @@ static int __init integrity_audit_setup(char *str)  {  	unsigned long audit; -	if (!strict_strtoul(str, 0, &audit)) +	if (!kstrtoul(str, 0, &audit))  		integrity_audit_info = audit ? 1 : 0;  	return 1;  } @@ -33,13 +33,14 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,  			 const char *cause, int result, int audit_info)  {  	struct audit_buffer *ab; +	char name[TASK_COMM_LEN];  	if (!integrity_audit_info && audit_info == 1)	/* Skip info messages */  		return;  	ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);  	audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u", -			 current->pid, +			 task_pid_nr(current),  			 from_kuid(&init_user_ns, current_cred()->uid),  			 from_kuid(&init_user_ns, audit_get_loginuid(current)),  			 audit_get_sessionid(current)); @@ -49,7 +50,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,  	audit_log_format(ab, " cause=");  	audit_log_string(ab, cause);  	audit_log_format(ab, " comm="); -	audit_log_untrustedstring(ab, current->comm); +	audit_log_untrustedstring(ab, get_task_comm(name, current));  	if (fname) {  		audit_log_format(ab, " name=");  		audit_log_untrustedstring(ab, fname); diff --git a/security/keys/Kconfig b/security/keys/Kconfig index a90d6d300db..a4f3f8c48d6 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -4,6 +4,7 @@  config KEYS  	bool "Enable access key retention support" +	select ASSOCIATIVE_ARRAY  	help  	  This option provides support for retaining authentication tokens and  	  access keys in the kernel. @@ -19,6 +20,34 @@ config KEYS  	  If you are unsure as to whether this is required, answer N. +config PERSISTENT_KEYRINGS +	bool "Enable register of persistent per-UID keyrings" +	depends on KEYS +	help +	  This option provides a register of persistent per-UID keyrings, +	  primarily aimed at Kerberos key storage.  The keyrings are persistent +	  in the sense that they stay around after all processes of that UID +	  have exited, not that they survive the machine being rebooted. + +	  A particular keyring may be accessed by either the user whose keyring +	  it is or by a process with administrative privileges.  The active +	  LSMs gets to rule on which admin-level processes get to access the +	  cache. + +	  Keyrings are created and added into the register upon demand and get +	  removed if they expire (a default timeout is set upon creation). + +config BIG_KEYS +	bool "Large payload keys" +	depends on KEYS +	depends on TMPFS +	help +	  This option provides support for holding large keys within the kernel +	  (for example Kerberos ticket caches).  The data may be stored out to +	  swapspace by tmpfs. + +	  If you are unsure as to whether this is required, answer N. +  config TRUSTED_KEYS  	tristate "TRUSTED KEYS"  	depends on KEYS && TCG_TPM diff --git a/security/keys/Makefile b/security/keys/Makefile index 504aaa00838..dfb3a7beded 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -18,9 +18,11 @@ obj-y := \  obj-$(CONFIG_KEYS_COMPAT) += compat.o  obj-$(CONFIG_PROC_FS) += proc.o  obj-$(CONFIG_SYSCTL) += sysctl.o +obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o  #  # Key types  # +obj-$(CONFIG_BIG_KEYS) += big_key.o  obj-$(CONFIG_TRUSTED_KEYS) += trusted.o  obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ diff --git a/security/keys/big_key.c b/security/keys/big_key.c new file mode 100644 index 00000000000..8137b27d641 --- /dev/null +++ b/security/keys/big_key.c @@ -0,0 +1,207 @@ +/* Large capacity key type + * + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/seq_file.h> +#include <linux/file.h> +#include <linux/shmem_fs.h> +#include <linux/err.h> +#include <keys/user-type.h> +#include <keys/big_key-type.h> + +MODULE_LICENSE("GPL"); + +/* + * If the data is under this limit, there's no point creating a shm file to + * hold it as the permanently resident metadata for the shmem fs will be at + * least as large as the data. + */ +#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) + +/* + * big_key defined keys take an arbitrary string as the description and an + * arbitrary blob of data as the payload + */ +struct key_type key_type_big_key = { +	.name			= "big_key", +	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT, +	.instantiate		= big_key_instantiate, +	.match			= user_match, +	.revoke			= big_key_revoke, +	.destroy		= big_key_destroy, +	.describe		= big_key_describe, +	.read			= big_key_read, +}; + +/* + * Instantiate a big key + */ +int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ +	struct path *path = (struct path *)&key->payload.data2; +	struct file *file; +	ssize_t written; +	size_t datalen = prep->datalen; +	int ret; + +	ret = -EINVAL; +	if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) +		goto error; + +	/* Set an arbitrary quota */ +	ret = key_payload_reserve(key, 16); +	if (ret < 0) +		goto error; + +	key->type_data.x[1] = datalen; + +	if (datalen > BIG_KEY_FILE_THRESHOLD) { +		/* Create a shmem file to store the data in.  This will permit the data +		 * to be swapped out if needed. +		 * +		 * TODO: Encrypt the stored data with a temporary key. +		 */ +		file = shmem_kernel_file_setup("", datalen, 0); +		if (IS_ERR(file)) { +			ret = PTR_ERR(file); +			goto err_quota; +		} + +		written = kernel_write(file, prep->data, prep->datalen, 0); +		if (written != datalen) { +			ret = written; +			if (written >= 0) +				ret = -ENOMEM; +			goto err_fput; +		} + +		/* Pin the mount and dentry to the key so that we can open it again +		 * later +		 */ +		*path = file->f_path; +		path_get(path); +		fput(file); +	} else { +		/* Just store the data in a buffer */ +		void *data = kmalloc(datalen, GFP_KERNEL); +		if (!data) { +			ret = -ENOMEM; +			goto err_quota; +		} + +		key->payload.data = memcpy(data, prep->data, prep->datalen); +	} +	return 0; + +err_fput: +	fput(file); +err_quota: +	key_payload_reserve(key, 0); +error: +	return ret; +} + +/* + * dispose of the links from a revoked keyring + * - called with the key sem write-locked + */ +void big_key_revoke(struct key *key) +{ +	struct path *path = (struct path *)&key->payload.data2; + +	/* clear the quota */ +	key_payload_reserve(key, 0); +	if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) +		vfs_truncate(path, 0); +} + +/* + * dispose of the data dangling from the corpse of a big_key key + */ +void big_key_destroy(struct key *key) +{ +	if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) { +		struct path *path = (struct path *)&key->payload.data2; +		path_put(path); +		path->mnt = NULL; +		path->dentry = NULL; +	} else { +		kfree(key->payload.data); +		key->payload.data = NULL; +	} +} + +/* + * describe the big_key key + */ +void big_key_describe(const struct key *key, struct seq_file *m) +{ +	unsigned long datalen = key->type_data.x[1]; + +	seq_puts(m, key->description); + +	if (key_is_instantiated(key)) +		seq_printf(m, ": %lu [%s]", +			   datalen, +			   datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); +} + +/* + * read the key data + * - the key's semaphore is read-locked + */ +long big_key_read(const struct key *key, char __user *buffer, size_t buflen) +{ +	unsigned long datalen = key->type_data.x[1]; +	long ret; + +	if (!buffer || buflen < datalen) +		return datalen; + +	if (datalen > BIG_KEY_FILE_THRESHOLD) { +		struct path *path = (struct path *)&key->payload.data2; +		struct file *file; +		loff_t pos; + +		file = dentry_open(path, O_RDONLY, current_cred()); +		if (IS_ERR(file)) +			return PTR_ERR(file); + +		pos = 0; +		ret = vfs_read(file, buffer, datalen, &pos); +		fput(file); +		if (ret >= 0 && ret != datalen) +			ret = -EIO; +	} else { +		ret = datalen; +		if (copy_to_user(buffer, key->payload.data, datalen) != 0) +			ret = -EFAULT; +	} + +	return ret; +} + +/* + * Module stuff + */ +static int __init big_key_init(void) +{ +	return register_key_type(&key_type_big_key); +} + +static void __exit big_key_cleanup(void) +{ +	unregister_key_type(&key_type_big_key); +} + +module_init(big_key_init); +module_exit(big_key_cleanup); diff --git a/security/keys/compat.c b/security/keys/compat.c index d65fa7fa29b..347896548ad 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -65,8 +65,8 @@ no_payload:   * taking a 32-bit syscall are zero.  If you can, you should call sys_keyctl()   * directly.   */ -asmlinkage long compat_sys_keyctl(u32 option, -				  u32 arg2, u32 arg3, u32 arg4, u32 arg5) +COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, +		       u32, arg2, u32, arg3, u32, arg4, u32, arg5)  {  	switch (option) {  	case KEYCTL_GET_KEYRING_ID: @@ -138,6 +138,9 @@ asmlinkage long compat_sys_keyctl(u32 option,  	case KEYCTL_INVALIDATE:  		return keyctl_invalidate_key(arg2); +	case KEYCTL_GET_PERSISTENT: +		return keyctl_get_persistent(arg2, arg3); +  	default:  		return -EOPNOTSUPP;  	} diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 9e1e005c759..5fe443d120a 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -609,7 +609,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,  	long dlen;  	int ret; -	ret = strict_strtol(datalen, 10, &dlen); +	ret = kstrtol(datalen, 10, &dlen);  	if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)  		return ERR_PTR(-EINVAL); diff --git a/security/keys/gc.c b/security/keys/gc.c index d67c97bb102..d3222b6d7d5 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -131,50 +131,6 @@ void key_gc_keytype(struct key_type *ktype)  }  /* - * Garbage collect pointers from a keyring. - * - * Not called with any locks held.  The keyring's key struct will not be - * deallocated under us as only our caller may deallocate it. - */ -static void key_gc_keyring(struct key *keyring, time_t limit) -{ -	struct keyring_list *klist; -	int loop; - -	kenter("%x", key_serial(keyring)); - -	if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | -			      (1 << KEY_FLAG_REVOKED))) -		goto dont_gc; - -	/* scan the keyring looking for dead keys */ -	rcu_read_lock(); -	klist = rcu_dereference(keyring->payload.subscriptions); -	if (!klist) -		goto unlock_dont_gc; - -	loop = klist->nkeys; -	smp_rmb(); -	for (loop--; loop >= 0; loop--) { -		struct key *key = rcu_dereference(klist->keys[loop]); -		if (key_is_dead(key, limit)) -			goto do_gc; -	} - -unlock_dont_gc: -	rcu_read_unlock(); -dont_gc: -	kleave(" [no gc]"); -	return; - -do_gc: -	rcu_read_unlock(); - -	keyring_gc(keyring, limit); -	kleave(" [gc]"); -} - -/*   * Garbage collect a list of unreferenced, detached keys   */  static noinline void key_gc_unused_keys(struct list_head *keys) @@ -392,8 +348,7 @@ found_unreferenced_key:  	 */  found_keyring:  	spin_unlock(&key_serial_lock); -	kdebug("scan keyring %d", key->serial); -	key_gc_keyring(key, limit); +	keyring_gc(key, limit);  	goto maybe_resched;  	/* We found a dead key that is still referenced.  Reset its type and diff --git a/security/keys/internal.h b/security/keys/internal.h index d4f1468b9b5..5f20da01fd8 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -89,42 +89,53 @@ extern struct key_type *key_type_lookup(const char *type);  extern void key_type_put(struct key_type *ktype);  extern int __key_link_begin(struct key *keyring, -			    const struct key_type *type, -			    const char *description, -			    unsigned long *_prealloc); +			    const struct keyring_index_key *index_key, +			    struct assoc_array_edit **_edit);  extern int __key_link_check_live_key(struct key *keyring, struct key *key); -extern void __key_link(struct key *keyring, struct key *key, -		       unsigned long *_prealloc); +extern void __key_link(struct key *key, struct assoc_array_edit **_edit);  extern void __key_link_end(struct key *keyring, -			   struct key_type *type, -			   unsigned long prealloc); +			   const struct keyring_index_key *index_key, +			   struct assoc_array_edit *edit); -extern key_ref_t __keyring_search_one(key_ref_t keyring_ref, -				      const struct key_type *type, -				      const char *description, -				      key_perm_t perm); +extern key_ref_t find_key_to_update(key_ref_t keyring_ref, +				    const struct keyring_index_key *index_key);  extern struct key *keyring_search_instkey(struct key *keyring,  					  key_serial_t target_id); +extern int iterate_over_keyring(const struct key *keyring, +				int (*func)(const struct key *key, void *data), +				void *data); +  typedef int (*key_match_func_t)(const struct key *, const void *); +struct keyring_search_context { +	struct keyring_index_key index_key; +	const struct cred	*cred; +	key_match_func_t	match; +	const void		*match_data; +	unsigned		flags; +#define KEYRING_SEARCH_LOOKUP_TYPE	0x0001	/* [as type->def_lookup_type] */ +#define KEYRING_SEARCH_NO_STATE_CHECK	0x0002	/* Skip state checks */ +#define KEYRING_SEARCH_DO_STATE_CHECK	0x0004	/* Override NO_STATE_CHECK */ +#define KEYRING_SEARCH_NO_UPDATE_TIME	0x0008	/* Don't update times */ +#define KEYRING_SEARCH_NO_CHECK_PERM	0x0010	/* Don't check permissions */ +#define KEYRING_SEARCH_DETECT_TOO_DEEP	0x0020	/* Give an error on excessive depth */ + +	int (*iterator)(const void *object, void *iterator_data); + +	/* Internal stuff */ +	int			skipped_ret; +	bool			possessed; +	key_ref_t		result; +	struct timespec		now; +}; +  extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, -				    const struct cred *cred, -				    struct key_type *type, -				    const void *description, -				    key_match_func_t match, -				    bool no_state_check); - -extern key_ref_t search_my_process_keyrings(struct key_type *type, -					    const void *description, -					    key_match_func_t match, -					    bool no_state_check, -					    const struct cred *cred); -extern key_ref_t search_process_keyrings(struct key_type *type, -					 const void *description, -					 key_match_func_t match, -					 const struct cred *cred); +				    struct keyring_search_context *ctx); + +extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx); +extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);  extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check); @@ -165,20 +176,11 @@ extern int key_task_permission(const key_ref_t key_ref,  /*   * Check to see whether permission is granted to use a key in the desired way.   */ -static inline int key_permission(const key_ref_t key_ref, key_perm_t perm) +static inline int key_permission(const key_ref_t key_ref, unsigned perm)  {  	return key_task_permission(key_ref, current_cred(), perm);  } -/* required permissions */ -#define	KEY_VIEW	0x01	/* require permission to view attributes */ -#define	KEY_READ	0x02	/* require permission to read content */ -#define	KEY_WRITE	0x04	/* require permission to update / modify */ -#define	KEY_SEARCH	0x08	/* require permission to search (keyring) or find (key) */ -#define	KEY_LINK	0x10	/* require permission to link */ -#define	KEY_SETATTR	0x20	/* require permission to change attributes */ -#define	KEY_ALL		0x3f	/* all the above permissions */ -  /*   * Authorisation record for request_key().   */ @@ -202,7 +204,7 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id);  /*   * Determine whether a key is dead.   */ -static inline bool key_is_dead(struct key *key, time_t limit) +static inline bool key_is_dead(const struct key *key, time_t limit)  {  	return  		key->flags & ((1 << KEY_FLAG_DEAD) | @@ -244,6 +246,15 @@ extern long keyctl_invalidate_key(key_serial_t);  extern long keyctl_instantiate_key_common(key_serial_t,  					  const struct iovec *,  					  unsigned, size_t, key_serial_t); +#ifdef CONFIG_PERSISTENT_KEYRINGS +extern long keyctl_get_persistent(uid_t, key_serial_t); +extern unsigned persistent_keyring_expiry; +#else +static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) +{ +	return -EOPNOTSUPP; +} +#endif  /*   * Debugging key validation diff --git a/security/keys/key.c b/security/keys/key.c index 8fb7c7bd465..2048a110e7f 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -242,8 +242,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,  		}  	} -	desclen = strlen(desc) + 1; -	quotalen = desclen + type->def_datalen; +	desclen = strlen(desc); +	quotalen = desclen + 1 + type->def_datalen;  	/* get hold of the key tracking for this user */  	user = key_user_lookup(uid); @@ -272,12 +272,13 @@ struct key *key_alloc(struct key_type *type, const char *desc,  	}  	/* allocate and initialise the key and its description */ -	key = kmem_cache_alloc(key_jar, GFP_KERNEL); +	key = kmem_cache_zalloc(key_jar, GFP_KERNEL);  	if (!key)  		goto no_memory_2;  	if (desc) { -		key->description = kmemdup(desc, desclen, GFP_KERNEL); +		key->index_key.desc_len = desclen; +		key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);  		if (!key->description)  			goto no_memory_3;  	} @@ -285,22 +286,18 @@ struct key *key_alloc(struct key_type *type, const char *desc,  	atomic_set(&key->usage, 1);  	init_rwsem(&key->sem);  	lockdep_set_class(&key->sem, &type->lock_class); -	key->type = type; +	key->index_key.type = type;  	key->user = user;  	key->quotalen = quotalen;  	key->datalen = type->def_datalen;  	key->uid = uid;  	key->gid = gid;  	key->perm = perm; -	key->flags = 0; -	key->expiry = 0; -	key->payload.data = NULL; -	key->security = NULL;  	if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))  		key->flags |= 1 << KEY_FLAG_IN_QUOTA; - -	memset(&key->type_data, 0, sizeof(key->type_data)); +	if (flags & KEY_ALLOC_TRUSTED) +		key->flags |= 1 << KEY_FLAG_TRUSTED;  #ifdef KEY_DEBUGGING  	key->magic = KEY_DEBUG_MAGIC; @@ -408,7 +405,7 @@ static int __key_instantiate_and_link(struct key *key,  				      struct key_preparsed_payload *prep,  				      struct key *keyring,  				      struct key *authkey, -				      unsigned long *_prealloc) +				      struct assoc_array_edit **_edit)  {  	int ret, awaken; @@ -435,7 +432,7 @@ static int __key_instantiate_and_link(struct key *key,  			/* and link it into the destination keyring */  			if (keyring) -				__key_link(keyring, key, _prealloc); +				__key_link(key, _edit);  			/* disable the authorisation key */  			if (authkey) @@ -475,7 +472,7 @@ int key_instantiate_and_link(struct key *key,  			     struct key *authkey)  {  	struct key_preparsed_payload prep; -	unsigned long prealloc; +	struct assoc_array_edit *edit;  	int ret;  	memset(&prep, 0, sizeof(prep)); @@ -489,17 +486,15 @@ int key_instantiate_and_link(struct key *key,  	}  	if (keyring) { -		ret = __key_link_begin(keyring, key->type, key->description, -				       &prealloc); +		ret = __key_link_begin(keyring, &key->index_key, &edit);  		if (ret < 0)  			goto error_free_preparse;  	} -	ret = __key_instantiate_and_link(key, &prep, keyring, authkey, -					 &prealloc); +	ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);  	if (keyring) -		__key_link_end(keyring, key->type, prealloc); +		__key_link_end(keyring, &key->index_key, edit);  error_free_preparse:  	if (key->type->preparse) @@ -537,7 +532,7 @@ int key_reject_and_link(struct key *key,  			struct key *keyring,  			struct key *authkey)  { -	unsigned long prealloc; +	struct assoc_array_edit *edit;  	struct timespec now;  	int ret, awaken, link_ret = 0; @@ -548,8 +543,7 @@ int key_reject_and_link(struct key *key,  	ret = -EBUSY;  	if (keyring) -		link_ret = __key_link_begin(keyring, key->type, -					    key->description, &prealloc); +		link_ret = __key_link_begin(keyring, &key->index_key, &edit);  	mutex_lock(&key_construction_mutex); @@ -557,9 +551,10 @@ int key_reject_and_link(struct key *key,  	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {  		/* mark the key as being negatively instantiated */  		atomic_inc(&key->user->nikeys); +		key->type_data.reject_error = -error; +		smp_wmb();  		set_bit(KEY_FLAG_NEGATIVE, &key->flags);  		set_bit(KEY_FLAG_INSTANTIATED, &key->flags); -		key->type_data.reject_error = -error;  		now = current_kernel_time();  		key->expiry = now.tv_sec + timeout;  		key_schedule_gc(key->expiry + key_gc_delay); @@ -571,7 +566,7 @@ int key_reject_and_link(struct key *key,  		/* and link it into the destination keyring */  		if (keyring && link_ret == 0) -			__key_link(keyring, key, &prealloc); +			__key_link(key, &edit);  		/* disable the authorisation key */  		if (authkey) @@ -581,7 +576,7 @@ int key_reject_and_link(struct key *key,  	mutex_unlock(&key_construction_mutex);  	if (keyring) -		__key_link_end(keyring, key->type, prealloc); +		__key_link_end(keyring, &key->index_key, edit);  	/* wake up anyone waiting for a key to be constructed */  	if (awaken) @@ -645,7 +640,7 @@ found:  	/* this races with key_put(), but that doesn't matter since key_put()  	 * doesn't actually change the key  	 */ -	atomic_inc(&key->usage); +	__key_get(key);  error:  	spin_unlock(&key_serial_lock); @@ -719,7 +714,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,  	int ret;  	/* need write permission on the key to update it */ -	ret = key_permission(key_ref, KEY_WRITE); +	ret = key_permission(key_ref, KEY_NEED_WRITE);  	if (ret < 0)  		goto error; @@ -780,25 +775,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  			       key_perm_t perm,  			       unsigned long flags)  { -	unsigned long prealloc; +	struct keyring_index_key index_key = { +		.description	= description, +	};  	struct key_preparsed_payload prep; +	struct assoc_array_edit *edit;  	const struct cred *cred = current_cred(); -	struct key_type *ktype;  	struct key *keyring, *key = NULL;  	key_ref_t key_ref;  	int ret;  	/* look up the key type to see if it's one of the registered kernel  	 * types */ -	ktype = key_type_lookup(type); -	if (IS_ERR(ktype)) { +	index_key.type = key_type_lookup(type); +	if (IS_ERR(index_key.type)) {  		key_ref = ERR_PTR(-ENODEV);  		goto error;  	}  	key_ref = ERR_PTR(-EINVAL); -	if (!ktype->match || !ktype->instantiate || -	    (!description && !ktype->preparse)) +	if (!index_key.type->match || !index_key.type->instantiate || +	    (!index_key.description && !index_key.type->preparse))  		goto error_put_type;  	keyring = key_ref_to_ptr(keyring_ref); @@ -812,21 +809,28 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	memset(&prep, 0, sizeof(prep));  	prep.data = payload;  	prep.datalen = plen; -	prep.quotalen = ktype->def_datalen; -	if (ktype->preparse) { -		ret = ktype->preparse(&prep); +	prep.quotalen = index_key.type->def_datalen; +	prep.trusted = flags & KEY_ALLOC_TRUSTED; +	if (index_key.type->preparse) { +		ret = index_key.type->preparse(&prep);  		if (ret < 0) {  			key_ref = ERR_PTR(ret);  			goto error_put_type;  		} -		if (!description) -			description = prep.description; +		if (!index_key.description) +			index_key.description = prep.description;  		key_ref = ERR_PTR(-EINVAL); -		if (!description) +		if (!index_key.description)  			goto error_free_prep;  	} +	index_key.desc_len = strlen(index_key.description); + +	key_ref = ERR_PTR(-EPERM); +	if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) +		goto error_free_prep; +	flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; -	ret = __key_link_begin(keyring, ktype, description, &prealloc); +	ret = __key_link_begin(keyring, &index_key, &edit);  	if (ret < 0) {  		key_ref = ERR_PTR(ret);  		goto error_free_prep; @@ -834,7 +838,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	/* if we're going to allocate a new key, we're going to have  	 * to modify the keyring */ -	ret = key_permission(keyring_ref, KEY_WRITE); +	ret = key_permission(keyring_ref, KEY_NEED_WRITE);  	if (ret < 0) {  		key_ref = ERR_PTR(ret);  		goto error_link_end; @@ -844,10 +848,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	 * key of the same type and description in the destination keyring and  	 * update that instead if possible  	 */ -	if (ktype->update) { -		key_ref = __keyring_search_one(keyring_ref, ktype, description, -					       0); -		if (!IS_ERR(key_ref)) +	if (index_key.type->update) { +		key_ref = find_key_to_update(keyring_ref, &index_key); +		if (key_ref)  			goto found_matching_key;  	} @@ -856,23 +859,24 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  		perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;  		perm |= KEY_USR_VIEW; -		if (ktype->read) +		if (index_key.type->read)  			perm |= KEY_POS_READ; -		if (ktype == &key_type_keyring || ktype->update) +		if (index_key.type == &key_type_keyring || +		    index_key.type->update)  			perm |= KEY_POS_WRITE;  	}  	/* allocate a new key */ -	key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred, -			perm, flags); +	key = key_alloc(index_key.type, index_key.description, +			cred->fsuid, cred->fsgid, cred, perm, flags);  	if (IS_ERR(key)) {  		key_ref = ERR_CAST(key);  		goto error_link_end;  	}  	/* instantiate it and link it into the target keyring */ -	ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc); +	ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &edit);  	if (ret < 0) {  		key_put(key);  		key_ref = ERR_PTR(ret); @@ -882,12 +886,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	key_ref = make_key_ref(key, is_key_possessed(keyring_ref));  error_link_end: -	__key_link_end(keyring, ktype, prealloc); +	__key_link_end(keyring, &index_key, edit);  error_free_prep: -	if (ktype->preparse) -		ktype->free_preparse(&prep); +	if (index_key.type->preparse) +		index_key.type->free_preparse(&prep);  error_put_type: -	key_type_put(ktype); +	key_type_put(index_key.type);  error:  	return key_ref; @@ -895,7 +899,7 @@ error:  	/* we found a matching key, so we're going to try to update it  	 * - we can drop the locks first as we have the key pinned  	 */ -	__key_link_end(keyring, ktype, prealloc); +	__key_link_end(keyring, &index_key, edit);  	key_ref = __key_update(key_ref, &prep);  	goto error_free_prep; @@ -924,7 +928,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)  	key_check(key);  	/* the key must be writable */ -	ret = key_permission(key_ref, KEY_WRITE); +	ret = key_permission(key_ref, KEY_NEED_WRITE);  	if (ret < 0)  		goto error; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 33cfd27b4de..cd5bd0cef25 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -111,7 +111,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,  	}  	/* find the target keyring (which must be writable) */ -	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error3; @@ -195,7 +195,7 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,  	dest_ref = NULL;  	if (destringid) {  		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, -					   KEY_WRITE); +					   KEY_NEED_WRITE);  		if (IS_ERR(dest_ref)) {  			ret = PTR_ERR(dest_ref);  			goto error3; @@ -253,7 +253,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)  	long ret;  	lflags = create ? KEY_LOOKUP_CREATE : 0; -	key_ref = lookup_user_key(id, lflags, KEY_SEARCH); +	key_ref = lookup_user_key(id, lflags, KEY_NEED_SEARCH);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -334,7 +334,7 @@ long keyctl_update_key(key_serial_t id,  	}  	/* find the target key (which must be writable) */ -	key_ref = lookup_user_key(id, 0, KEY_WRITE); +	key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error2; @@ -365,12 +365,12 @@ long keyctl_revoke_key(key_serial_t id)  	key_ref_t key_ref;  	long ret; -	key_ref = lookup_user_key(id, 0, KEY_WRITE); +	key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		if (ret != -EACCES)  			goto error; -		key_ref = lookup_user_key(id, 0, KEY_SETATTR); +		key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);  		if (IS_ERR(key_ref)) {  			ret = PTR_ERR(key_ref);  			goto error; @@ -401,7 +401,7 @@ long keyctl_invalidate_key(key_serial_t id)  	kenter("%d", id); -	key_ref = lookup_user_key(id, 0, KEY_SEARCH); +	key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -428,7 +428,7 @@ long keyctl_keyring_clear(key_serial_t ringid)  	key_ref_t keyring_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref); @@ -470,13 +470,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)  	key_ref_t keyring_ref, key_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error;  	} -	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_LINK); +	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error2; @@ -505,7 +505,7 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)  	key_ref_t keyring_ref, key_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, 0, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error; @@ -548,7 +548,7 @@ long keyctl_describe_key(key_serial_t keyid,  	char *tmpbuf;  	long ret; -	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW); +	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);  	if (IS_ERR(key_ref)) {  		/* viewing a key under construction is permitted if we have the  		 * authorisation token handy */ @@ -639,7 +639,7 @@ long keyctl_keyring_search(key_serial_t ringid,  	}  	/* get the keyring at which to begin the search */ -	keyring_ref = lookup_user_key(ringid, 0, KEY_SEARCH); +	keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_SEARCH);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error2; @@ -649,7 +649,7 @@ long keyctl_keyring_search(key_serial_t ringid,  	dest_ref = NULL;  	if (destringid) {  		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, -					   KEY_WRITE); +					   KEY_NEED_WRITE);  		if (IS_ERR(dest_ref)) {  			ret = PTR_ERR(dest_ref);  			goto error3; @@ -676,7 +676,7 @@ long keyctl_keyring_search(key_serial_t ringid,  	/* link the resulting key to the destination keyring if we can */  	if (dest_ref) { -		ret = key_permission(key_ref, KEY_LINK); +		ret = key_permission(key_ref, KEY_NEED_LINK);  		if (ret < 0)  			goto error6; @@ -727,7 +727,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)  	key = key_ref_to_ptr(key_ref);  	/* see if we can read it directly */ -	ret = key_permission(key_ref, KEY_READ); +	ret = key_permission(key_ref, KEY_NEED_READ);  	if (ret == 0)  		goto can_read_key;  	if (ret != -EACCES) @@ -799,7 +799,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)  		goto error;  	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, -				  KEY_SETATTR); +				  KEY_NEED_SETATTR);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -905,7 +905,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)  		goto error;  	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, -				  KEY_SETATTR); +				  KEY_NEED_SETATTR);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -947,7 +947,7 @@ static long get_instantiation_keyring(key_serial_t ringid,  	/* if a specific keyring is nominated by ID, then use that */  	if (ringid > 0) { -		dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +		dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  		if (IS_ERR(dkref))  			return PTR_ERR(dkref);  		*_dest_keyring = key_ref_to_ptr(dkref); @@ -1315,7 +1315,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)  	long ret;  	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, -				  KEY_SETATTR); +				  KEY_NEED_SETATTR);  	if (IS_ERR(key_ref)) {  		/* setting the timeout on a key under construction is permitted  		 * if we have the authorisation token handy */ @@ -1418,7 +1418,7 @@ long keyctl_get_security(key_serial_t keyid,  	char *context;  	long ret; -	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW); +	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);  	if (IS_ERR(key_ref)) {  		if (PTR_ERR(key_ref) != -EACCES)  			return PTR_ERR(key_ref); @@ -1482,7 +1482,7 @@ long keyctl_session_to_parent(void)  	struct cred *cred;  	int ret; -	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); +	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_LINK);  	if (IS_ERR(keyring_r))  		return PTR_ERR(keyring_r); @@ -1667,6 +1667,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,  	case KEYCTL_INVALIDATE:  		return keyctl_invalidate_key((key_serial_t) arg2); +	case KEYCTL_GET_PERSISTENT: +		return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3); +  	default:  		return -EOPNOTSUPP;  	} diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 6ece7f2e570..9cf2575f0d9 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1,6 +1,6 @@  /* Keyring handling   * - * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-2005, 2008, 2013 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com)   *   * This program is free software; you can redistribute it and/or @@ -17,25 +17,11 @@  #include <linux/seq_file.h>  #include <linux/err.h>  #include <keys/keyring-type.h> +#include <keys/user-type.h> +#include <linux/assoc_array_priv.h>  #include <linux/uaccess.h>  #include "internal.h" -#define rcu_dereference_locked_keyring(keyring)				\ -	(rcu_dereference_protected(					\ -		(keyring)->payload.subscriptions,			\ -		rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) - -#define rcu_deref_link_locked(klist, index, keyring)			\ -	(rcu_dereference_protected(					\ -		(klist)->keys[index],					\ -		rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) - -#define MAX_KEYRING_LINKS						\ -	min_t(size_t, USHRT_MAX - 1,					\ -	      ((PAGE_SIZE - sizeof(struct keyring_list)) / sizeof(struct key *))) - -#define KEY_LINK_FIXQUOTA 1UL -  /*   * When plumbing the depths of the key tree, this sets a hard limit   * set on how deep we're willing to go. @@ -47,6 +33,28 @@   */  #define KEYRING_NAME_HASH_SIZE	(1 << 5) +/* + * We mark pointers we pass to the associative array with bit 1 set if + * they're keyrings and clear otherwise. + */ +#define KEYRING_PTR_SUBTYPE	0x2UL + +static inline bool keyring_ptr_is_keyring(const struct assoc_array_ptr *x) +{ +	return (unsigned long)x & KEYRING_PTR_SUBTYPE; +} +static inline struct key *keyring_ptr_to_key(const struct assoc_array_ptr *x) +{ +	void *object = assoc_array_ptr_to_leaf(x); +	return (struct key *)((unsigned long)object & ~KEYRING_PTR_SUBTYPE); +} +static inline void *keyring_key_to_ptr(struct key *key) +{ +	if (key->type == &key_type_keyring) +		return (void *)((unsigned long)key | KEYRING_PTR_SUBTYPE); +	return key; +} +  static struct list_head	keyring_name_hash[KEYRING_NAME_HASH_SIZE];  static DEFINE_RWLOCK(keyring_name_lock); @@ -67,7 +75,6 @@ static inline unsigned keyring_hash(const char *desc)   */  static int keyring_instantiate(struct key *keyring,  			       struct key_preparsed_payload *prep); -static int keyring_match(const struct key *keyring, const void *criterion);  static void keyring_revoke(struct key *keyring);  static void keyring_destroy(struct key *keyring);  static void keyring_describe(const struct key *keyring, struct seq_file *m); @@ -76,9 +83,9 @@ static long keyring_read(const struct key *keyring,  struct key_type key_type_keyring = {  	.name		= "keyring", -	.def_datalen	= sizeof(struct keyring_list), +	.def_datalen	= 0,  	.instantiate	= keyring_instantiate, -	.match		= keyring_match, +	.match		= user_match,  	.revoke		= keyring_revoke,  	.destroy	= keyring_destroy,  	.describe	= keyring_describe, @@ -127,6 +134,7 @@ static int keyring_instantiate(struct key *keyring,  	ret = -EINVAL;  	if (prep->datalen == 0) { +		assoc_array_init(&keyring->keys);  		/* make the keyring available by name if it has one */  		keyring_publish_name(keyring);  		ret = 0; @@ -136,15 +144,225 @@ static int keyring_instantiate(struct key *keyring,  }  /* - * Match keyrings on their name + * Multiply 64-bits by 32-bits to 96-bits and fold back to 64-bit.  Ideally we'd + * fold the carry back too, but that requires inline asm. + */ +static u64 mult_64x32_and_fold(u64 x, u32 y) +{ +	u64 hi = (u64)(u32)(x >> 32) * y; +	u64 lo = (u64)(u32)(x) * y; +	return lo + ((u64)(u32)hi << 32) + (u32)(hi >> 32); +} + +/* + * Hash a key type and description. + */ +static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key) +{ +	const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP; +	const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK; +	const char *description = index_key->description; +	unsigned long hash, type; +	u32 piece; +	u64 acc; +	int n, desc_len = index_key->desc_len; + +	type = (unsigned long)index_key->type; + +	acc = mult_64x32_and_fold(type, desc_len + 13); +	acc = mult_64x32_and_fold(acc, 9207); +	for (;;) { +		n = desc_len; +		if (n <= 0) +			break; +		if (n > 4) +			n = 4; +		piece = 0; +		memcpy(&piece, description, n); +		description += n; +		desc_len -= n; +		acc = mult_64x32_and_fold(acc, piece); +		acc = mult_64x32_and_fold(acc, 9207); +	} + +	/* Fold the hash down to 32 bits if need be. */ +	hash = acc; +	if (ASSOC_ARRAY_KEY_CHUNK_SIZE == 32) +		hash ^= acc >> 32; + +	/* Squidge all the keyrings into a separate part of the tree to +	 * ordinary keys by making sure the lowest level segment in the hash is +	 * zero for keyrings and non-zero otherwise. +	 */ +	if (index_key->type != &key_type_keyring && (hash & fan_mask) == 0) +		return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1; +	if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0) +		return (hash + (hash << level_shift)) & ~fan_mask; +	return hash; +} + +/* + * Build the next index key chunk. + * + * On 32-bit systems the index key is laid out as: + * + *	0	4	5	9... + *	hash	desclen	typeptr	desc[] + * + * On 64-bit systems: + * + *	0	8	9	17... + *	hash	desclen	typeptr	desc[] + * + * We return it one word-sized chunk at a time.   */ -static int keyring_match(const struct key *keyring, const void *description) +static unsigned long keyring_get_key_chunk(const void *data, int level) +{ +	const struct keyring_index_key *index_key = data; +	unsigned long chunk = 0; +	long offset = 0; +	int desc_len = index_key->desc_len, n = sizeof(chunk); + +	level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; +	switch (level) { +	case 0: +		return hash_key_type_and_desc(index_key); +	case 1: +		return ((unsigned long)index_key->type << 8) | desc_len; +	case 2: +		if (desc_len == 0) +			return (u8)((unsigned long)index_key->type >> +				    (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8)); +		n--; +		offset = 1; +	default: +		offset += sizeof(chunk) - 1; +		offset += (level - 3) * sizeof(chunk); +		if (offset >= desc_len) +			return 0; +		desc_len -= offset; +		if (desc_len > n) +			desc_len = n; +		offset += desc_len; +		do { +			chunk <<= 8; +			chunk |= ((u8*)index_key->description)[--offset]; +		} while (--desc_len > 0); + +		if (level == 2) { +			chunk <<= 8; +			chunk |= (u8)((unsigned long)index_key->type >> +				      (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8)); +		} +		return chunk; +	} +} + +static unsigned long keyring_get_object_key_chunk(const void *object, int level) +{ +	const struct key *key = keyring_ptr_to_key(object); +	return keyring_get_key_chunk(&key->index_key, level); +} + +static bool keyring_compare_object(const void *object, const void *data)  { -	return keyring->description && -		strcmp(keyring->description, description) == 0; +	const struct keyring_index_key *index_key = data; +	const struct key *key = keyring_ptr_to_key(object); + +	return key->index_key.type == index_key->type && +		key->index_key.desc_len == index_key->desc_len && +		memcmp(key->index_key.description, index_key->description, +		       index_key->desc_len) == 0;  }  /* + * Compare the index keys of a pair of objects and determine the bit position + * at which they differ - if they differ. + */ +static int keyring_diff_objects(const void *object, const void *data) +{ +	const struct key *key_a = keyring_ptr_to_key(object); +	const struct keyring_index_key *a = &key_a->index_key; +	const struct keyring_index_key *b = data; +	unsigned long seg_a, seg_b; +	int level, i; + +	level = 0; +	seg_a = hash_key_type_and_desc(a); +	seg_b = hash_key_type_and_desc(b); +	if ((seg_a ^ seg_b) != 0) +		goto differ; + +	/* The number of bits contributed by the hash is controlled by a +	 * constant in the assoc_array headers.  Everything else thereafter we +	 * can deal with as being machine word-size dependent. +	 */ +	level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8; +	seg_a = a->desc_len; +	seg_b = b->desc_len; +	if ((seg_a ^ seg_b) != 0) +		goto differ; + +	/* The next bit may not work on big endian */ +	level++; +	seg_a = (unsigned long)a->type; +	seg_b = (unsigned long)b->type; +	if ((seg_a ^ seg_b) != 0) +		goto differ; + +	level += sizeof(unsigned long); +	if (a->desc_len == 0) +		goto same; + +	i = 0; +	if (((unsigned long)a->description | (unsigned long)b->description) & +	    (sizeof(unsigned long) - 1)) { +		do { +			seg_a = *(unsigned long *)(a->description + i); +			seg_b = *(unsigned long *)(b->description + i); +			if ((seg_a ^ seg_b) != 0) +				goto differ_plus_i; +			i += sizeof(unsigned long); +		} while (i < (a->desc_len & (sizeof(unsigned long) - 1))); +	} + +	for (; i < a->desc_len; i++) { +		seg_a = *(unsigned char *)(a->description + i); +		seg_b = *(unsigned char *)(b->description + i); +		if ((seg_a ^ seg_b) != 0) +			goto differ_plus_i; +	} + +same: +	return -1; + +differ_plus_i: +	level += i; +differ: +	i = level * 8 + __ffs(seg_a ^ seg_b); +	return i; +} + +/* + * Free an object after stripping the keyring flag off of the pointer. + */ +static void keyring_free_object(void *object) +{ +	key_put(keyring_ptr_to_key(object)); +} + +/* + * Operations for keyring management by the index-tree routines. + */ +static const struct assoc_array_ops keyring_assoc_array_ops = { +	.get_key_chunk		= keyring_get_key_chunk, +	.get_object_key_chunk	= keyring_get_object_key_chunk, +	.compare_object		= keyring_compare_object, +	.diff_objects		= keyring_diff_objects, +	.free_object		= keyring_free_object, +}; + +/*   * Clean up a keyring when it is destroyed.  Unpublish its name if it had one   * and dispose of its data.   * @@ -155,9 +373,6 @@ static int keyring_match(const struct key *keyring, const void *description)   */  static void keyring_destroy(struct key *keyring)  { -	struct keyring_list *klist; -	int loop; -  	if (keyring->description) {  		write_lock(&keyring_name_lock); @@ -168,12 +383,7 @@ static void keyring_destroy(struct key *keyring)  		write_unlock(&keyring_name_lock);  	} -	klist = rcu_access_pointer(keyring->payload.subscriptions); -	if (klist) { -		for (loop = klist->nkeys - 1; loop >= 0; loop--) -			key_put(rcu_access_pointer(klist->keys[loop])); -		kfree(klist); -	} +	assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);  }  /* @@ -181,76 +391,88 @@ static void keyring_destroy(struct key *keyring)   */  static void keyring_describe(const struct key *keyring, struct seq_file *m)  { -	struct keyring_list *klist; -  	if (keyring->description)  		seq_puts(m, keyring->description);  	else  		seq_puts(m, "[anon]");  	if (key_is_instantiated(keyring)) { -		rcu_read_lock(); -		klist = rcu_dereference(keyring->payload.subscriptions); -		if (klist) -			seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); +		if (keyring->keys.nr_leaves_on_tree != 0) +			seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);  		else  			seq_puts(m, ": empty"); -		rcu_read_unlock();  	}  } +struct keyring_read_iterator_context { +	size_t			qty; +	size_t			count; +	key_serial_t __user	*buffer; +}; + +static int keyring_read_iterator(const void *object, void *data) +{ +	struct keyring_read_iterator_context *ctx = data; +	const struct key *key = keyring_ptr_to_key(object); +	int ret; + +	kenter("{%s,%d},,{%zu/%zu}", +	       key->type->name, key->serial, ctx->count, ctx->qty); + +	if (ctx->count >= ctx->qty) +		return 1; + +	ret = put_user(key->serial, ctx->buffer); +	if (ret < 0) +		return ret; +	ctx->buffer++; +	ctx->count += sizeof(key->serial); +	return 0; +} +  /*   * Read a list of key IDs from the keyring's contents in binary form   * - * The keyring's semaphore is read-locked by the caller. + * The keyring's semaphore is read-locked by the caller.  This prevents someone + * from modifying it under us - which could cause us to read key IDs multiple + * times.   */  static long keyring_read(const struct key *keyring,  			 char __user *buffer, size_t buflen)  { -	struct keyring_list *klist; -	struct key *key; -	size_t qty, tmp; -	int loop, ret; +	struct keyring_read_iterator_context ctx; +	unsigned long nr_keys; +	int ret; -	ret = 0; -	klist = rcu_dereference_locked_keyring(keyring); -	if (klist) { -		/* calculate how much data we could return */ -		qty = klist->nkeys * sizeof(key_serial_t); - -		if (buffer && buflen > 0) { -			if (buflen > qty) -				buflen = qty; - -			/* copy the IDs of the subscribed keys into the -			 * buffer */ -			ret = -EFAULT; - -			for (loop = 0; loop < klist->nkeys; loop++) { -				key = rcu_deref_link_locked(klist, loop, -							    keyring); - -				tmp = sizeof(key_serial_t); -				if (tmp > buflen) -					tmp = buflen; - -				if (copy_to_user(buffer, -						 &key->serial, -						 tmp) != 0) -					goto error; - -				buflen -= tmp; -				if (buflen == 0) -					break; -				buffer += tmp; -			} -		} +	kenter("{%d},,%zu", key_serial(keyring), buflen); + +	if (buflen & (sizeof(key_serial_t) - 1)) +		return -EINVAL; + +	nr_keys = keyring->keys.nr_leaves_on_tree; +	if (nr_keys == 0) +		return 0; -		ret = qty; +	/* Calculate how much data we could return */ +	ctx.qty = nr_keys * sizeof(key_serial_t); + +	if (!buffer || !buflen) +		return ctx.qty; + +	if (buflen > ctx.qty) +		ctx.qty = buflen; + +	/* Copy the IDs of the subscribed keys into the buffer */ +	ctx.buffer = (key_serial_t __user *)buffer; +	ctx.count = 0; +	ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx); +	if (ret < 0) { +		kleave(" = %d [iterate]", ret); +		return ret;  	} -error: -	return ret; +	kleave(" = %zu [ok]", ctx.count); +	return ctx.count;  }  /* @@ -277,227 +499,361 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,  }  EXPORT_SYMBOL(keyring_alloc); -/** - * keyring_search_aux - Search a keyring tree for a key matching some criteria - * @keyring_ref: A pointer to the keyring with possession indicator. - * @cred: The credentials to use for permissions checks. - * @type: The type of key to search for. - * @description: Parameter for @match. - * @match: Function to rule on whether or not a key is the one required. - * @no_state_check: Don't check if a matching key is bad - * - * Search the supplied keyring tree for a key that matches the criteria given. - * The root keyring and any linked keyrings must grant Search permission to the - * caller to be searchable and keys can only be found if they too grant Search - * to the caller. The possession flag on the root keyring pointer controls use - * of the possessor bits in permissions checking of the entire tree.  In - * addition, the LSM gets to forbid keyring searches and key matches. - * - * The search is performed as a breadth-then-depth search up to the prescribed - * limit (KEYRING_SEARCH_MAX_DEPTH). - * - * Keys are matched to the type provided and are then filtered by the match - * function, which is given the description to use in any way it sees fit.  The - * match function may use any attributes of a key that it wishes to to - * determine the match.  Normally the match function from the key type would be - * used. - * - * RCU is used to prevent the keyring key lists from disappearing without the - * need to take lots of locks. - * - * Returns a pointer to the found key and increments the key usage count if - * successful; -EAGAIN if no matching keys were found, or if expired or revoked - * keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the - * specified keyring wasn't a keyring. - * - * In the case of a successful return, the possession attribute from - * @keyring_ref is propagated to the returned key reference. +/* + * Iteration function to consider each key found.   */ -key_ref_t keyring_search_aux(key_ref_t keyring_ref, -			     const struct cred *cred, -			     struct key_type *type, -			     const void *description, -			     key_match_func_t match, -			     bool no_state_check) +static int keyring_search_iterator(const void *object, void *iterator_data)  { -	struct { -		/* Need a separate keylist pointer for RCU purposes */ -		struct key *keyring; -		struct keyring_list *keylist; -		int kix; -	} stack[KEYRING_SEARCH_MAX_DEPTH]; - -	struct keyring_list *keylist; -	struct timespec now; -	unsigned long possessed, kflags; -	struct key *keyring, *key; -	key_ref_t key_ref; -	long err; -	int sp, nkeys, kix; +	struct keyring_search_context *ctx = iterator_data; +	const struct key *key = keyring_ptr_to_key(object); +	unsigned long kflags = key->flags; -	keyring = key_ref_to_ptr(keyring_ref); -	possessed = is_key_possessed(keyring_ref); -	key_check(keyring); +	kenter("{%d}", key->serial); -	/* top keyring must have search permission to begin the search */ -	err = key_task_permission(keyring_ref, cred, KEY_SEARCH); -	if (err < 0) { -		key_ref = ERR_PTR(err); -		goto error; +	/* ignore keys not of this type */ +	if (key->type != ctx->index_key.type) { +		kleave(" = 0 [!type]"); +		return 0;  	} -	key_ref = ERR_PTR(-ENOTDIR); -	if (keyring->type != &key_type_keyring) -		goto error; +	/* skip invalidated, revoked and expired keys */ +	if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { +		if (kflags & ((1 << KEY_FLAG_INVALIDATED) | +			      (1 << KEY_FLAG_REVOKED))) { +			ctx->result = ERR_PTR(-EKEYREVOKED); +			kleave(" = %d [invrev]", ctx->skipped_ret); +			goto skipped; +		} -	rcu_read_lock(); +		if (key->expiry && ctx->now.tv_sec >= key->expiry) { +			ctx->result = ERR_PTR(-EKEYEXPIRED); +			kleave(" = %d [expire]", ctx->skipped_ret); +			goto skipped; +		} +	} -	now = current_kernel_time(); -	err = -EAGAIN; -	sp = 0; - -	/* firstly we should check to see if this top-level keyring is what we -	 * are looking for */ -	key_ref = ERR_PTR(-EAGAIN); -	kflags = keyring->flags; -	if (keyring->type == type && match(keyring, description)) { -		key = keyring; -		if (no_state_check) -			goto found; +	/* keys that don't match */ +	if (!ctx->match(key, ctx->match_data)) { +		kleave(" = 0 [!match]"); +		return 0; +	} -		/* check it isn't negative and hasn't expired or been -		 * revoked */ -		if (kflags & (1 << KEY_FLAG_REVOKED)) -			goto error_2; -		if (key->expiry && now.tv_sec >= key->expiry) -			goto error_2; -		key_ref = ERR_PTR(key->type_data.reject_error); -		if (kflags & (1 << KEY_FLAG_NEGATIVE)) -			goto error_2; -		goto found; +	/* key must have search permissions */ +	if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && +	    key_task_permission(make_key_ref(key, ctx->possessed), +				ctx->cred, KEY_NEED_SEARCH) < 0) { +		ctx->result = ERR_PTR(-EACCES); +		kleave(" = %d [!perm]", ctx->skipped_ret); +		goto skipped;  	} -	/* otherwise, the top keyring must not be revoked, expired, or -	 * negatively instantiated if we are to search it */ -	key_ref = ERR_PTR(-EAGAIN); -	if (kflags & ((1 << KEY_FLAG_INVALIDATED) | -		      (1 << KEY_FLAG_REVOKED) | -		      (1 << KEY_FLAG_NEGATIVE)) || -	    (keyring->expiry && now.tv_sec >= keyring->expiry)) -		goto error_2; - -	/* start processing a new keyring */ -descend: -	kflags = keyring->flags; -	if (kflags & ((1 << KEY_FLAG_INVALIDATED) | -		      (1 << KEY_FLAG_REVOKED))) -		goto not_this_keyring; +	if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { +		/* we set a different error code if we pass a negative key */ +		if (kflags & (1 << KEY_FLAG_NEGATIVE)) { +			smp_rmb(); +			ctx->result = ERR_PTR(key->type_data.reject_error); +			kleave(" = %d [neg]", ctx->skipped_ret); +			goto skipped; +		} +	} -	keylist = rcu_dereference(keyring->payload.subscriptions); -	if (!keylist) -		goto not_this_keyring; +	/* Found */ +	ctx->result = make_key_ref(key, ctx->possessed); +	kleave(" = 1 [found]"); +	return 1; -	/* iterate through the keys in this keyring first */ -	nkeys = keylist->nkeys; -	smp_rmb(); -	for (kix = 0; kix < nkeys; kix++) { -		key = rcu_dereference(keylist->keys[kix]); -		kflags = key->flags; +skipped: +	return ctx->skipped_ret; +} -		/* ignore keys not of this type */ -		if (key->type != type) -			continue; +/* + * Search inside a keyring for a key.  We can search by walking to it + * directly based on its index-key or we can iterate over the entire + * tree looking for it, based on the match function. + */ +static int search_keyring(struct key *keyring, struct keyring_search_context *ctx) +{ +	if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) == +	    KEYRING_SEARCH_LOOKUP_DIRECT) { +		const void *object; + +		object = assoc_array_find(&keyring->keys, +					  &keyring_assoc_array_ops, +					  &ctx->index_key); +		return object ? ctx->iterator(object, ctx) : 0; +	} +	return assoc_array_iterate(&keyring->keys, ctx->iterator, ctx); +} -		/* skip invalidated, revoked and expired keys */ -		if (!no_state_check) { -			if (kflags & ((1 << KEY_FLAG_INVALIDATED) | -				      (1 << KEY_FLAG_REVOKED))) -				continue; +/* + * Search a tree of keyrings that point to other keyrings up to the maximum + * depth. + */ +static bool search_nested_keyrings(struct key *keyring, +				   struct keyring_search_context *ctx) +{ +	struct { +		struct key *keyring; +		struct assoc_array_node *node; +		int slot; +	} stack[KEYRING_SEARCH_MAX_DEPTH]; -			if (key->expiry && now.tv_sec >= key->expiry) -				continue; -		} +	struct assoc_array_shortcut *shortcut; +	struct assoc_array_node *node; +	struct assoc_array_ptr *ptr; +	struct key *key; +	int sp = 0, slot; -		/* keys that don't match */ -		if (!match(key, description)) -			continue; +	kenter("{%d},{%s,%s}", +	       keyring->serial, +	       ctx->index_key.type->name, +	       ctx->index_key.description); -		/* key must have search permissions */ -		if (key_task_permission(make_key_ref(key, possessed), -					cred, KEY_SEARCH) < 0) -			continue; +	if (ctx->index_key.description) +		ctx->index_key.desc_len = strlen(ctx->index_key.description); -		if (no_state_check) +	/* Check to see if this top-level keyring is what we are looking for +	 * and whether it is valid or not. +	 */ +	if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE || +	    keyring_compare_object(keyring, &ctx->index_key)) { +		ctx->skipped_ret = 2; +		ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK; +		switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) { +		case 1:  			goto found; - -		/* we set a different error code if we pass a negative key */ -		if (kflags & (1 << KEY_FLAG_NEGATIVE)) { -			err = key->type_data.reject_error; -			continue; +		case 2: +			return false; +		default: +			break;  		} +	} + +	ctx->skipped_ret = 0; +	if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK) +		ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK; +	/* Start processing a new keyring */ +descend_to_keyring: +	kdebug("descend to %d", keyring->serial); +	if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | +			      (1 << KEY_FLAG_REVOKED))) +		goto not_this_keyring; + +	/* Search through the keys in this keyring before its searching its +	 * subtrees. +	 */ +	if (search_keyring(keyring, ctx))  		goto found; -	} -	/* search through the keyrings nested in this one */ -	kix = 0; -ascend: -	nkeys = keylist->nkeys; -	smp_rmb(); -	for (; kix < nkeys; kix++) { -		key = rcu_dereference(keylist->keys[kix]); -		if (key->type != &key_type_keyring) -			continue; +	/* Then manually iterate through the keyrings nested in this one. +	 * +	 * Start from the root node of the index tree.  Because of the way the +	 * hash function has been set up, keyrings cluster on the leftmost +	 * branch of the root node (root slot 0) or in the root node itself. +	 * Non-keyrings avoid the leftmost branch of the root entirely (root +	 * slots 1-15). +	 */ +	ptr = ACCESS_ONCE(keyring->keys.root); +	if (!ptr) +		goto not_this_keyring; -		/* recursively search nested keyrings -		 * - only search keyrings for which we have search permission +	if (assoc_array_ptr_is_shortcut(ptr)) { +		/* If the root is a shortcut, either the keyring only contains +		 * keyring pointers (everything clusters behind root slot 0) or +		 * doesn't contain any keyring pointers.  		 */ -		if (sp >= KEYRING_SEARCH_MAX_DEPTH) +		shortcut = assoc_array_ptr_to_shortcut(ptr); +		smp_read_barrier_depends(); +		if ((shortcut->index_key[0] & ASSOC_ARRAY_FAN_MASK) != 0) +			goto not_this_keyring; + +		ptr = ACCESS_ONCE(shortcut->next_node); +		node = assoc_array_ptr_to_node(ptr); +		goto begin_node; +	} + +	node = assoc_array_ptr_to_node(ptr); +	smp_read_barrier_depends(); + +	ptr = node->slots[0]; +	if (!assoc_array_ptr_is_meta(ptr)) +		goto begin_node; + +descend_to_node: +	/* Descend to a more distal node in this keyring's content tree and go +	 * through that. +	 */ +	kdebug("descend"); +	if (assoc_array_ptr_is_shortcut(ptr)) { +		shortcut = assoc_array_ptr_to_shortcut(ptr); +		smp_read_barrier_depends(); +		ptr = ACCESS_ONCE(shortcut->next_node); +		BUG_ON(!assoc_array_ptr_is_node(ptr)); +	} +	node = assoc_array_ptr_to_node(ptr); + +begin_node: +	kdebug("begin_node"); +	smp_read_barrier_depends(); +	slot = 0; +ascend_to_node: +	/* Go through the slots in a node */ +	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) { +		ptr = ACCESS_ONCE(node->slots[slot]); + +		if (assoc_array_ptr_is_meta(ptr) && node->back_pointer) +			goto descend_to_node; + +		if (!keyring_ptr_is_keyring(ptr))  			continue; -		if (key_task_permission(make_key_ref(key, possessed), -					cred, KEY_SEARCH) < 0) +		key = keyring_ptr_to_key(ptr); + +		if (sp >= KEYRING_SEARCH_MAX_DEPTH) { +			if (ctx->flags & KEYRING_SEARCH_DETECT_TOO_DEEP) { +				ctx->result = ERR_PTR(-ELOOP); +				return false; +			} +			goto not_this_keyring; +		} + +		/* Search a nested keyring */ +		if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && +		    key_task_permission(make_key_ref(key, ctx->possessed), +					ctx->cred, KEY_NEED_SEARCH) < 0)  			continue;  		/* stack the current position */  		stack[sp].keyring = keyring; -		stack[sp].keylist = keylist; -		stack[sp].kix = kix; +		stack[sp].node = node; +		stack[sp].slot = slot;  		sp++;  		/* begin again with the new keyring */  		keyring = key; -		goto descend; +		goto descend_to_keyring;  	} -	/* the keyring we're looking at was disqualified or didn't contain a -	 * matching key */ +	/* We've dealt with all the slots in the current node, so now we need +	 * to ascend to the parent and continue processing there. +	 */ +	ptr = ACCESS_ONCE(node->back_pointer); +	slot = node->parent_slot; + +	if (ptr && assoc_array_ptr_is_shortcut(ptr)) { +		shortcut = assoc_array_ptr_to_shortcut(ptr); +		smp_read_barrier_depends(); +		ptr = ACCESS_ONCE(shortcut->back_pointer); +		slot = shortcut->parent_slot; +	} +	if (!ptr) +		goto not_this_keyring; +	node = assoc_array_ptr_to_node(ptr); +	smp_read_barrier_depends(); +	slot++; + +	/* If we've ascended to the root (zero backpointer), we must have just +	 * finished processing the leftmost branch rather than the root slots - +	 * so there can't be any more keyrings for us to find. +	 */ +	if (node->back_pointer) { +		kdebug("ascend %d", slot); +		goto ascend_to_node; +	} + +	/* The keyring we're looking at was disqualified or didn't contain a +	 * matching key. +	 */  not_this_keyring: -	if (sp > 0) { -		/* resume the processing of a keyring higher up in the tree */ -		sp--; -		keyring = stack[sp].keyring; -		keylist = stack[sp].keylist; -		kix = stack[sp].kix + 1; -		goto ascend; +	kdebug("not_this_keyring %d", sp); +	if (sp <= 0) { +		kleave(" = false"); +		return false;  	} -	key_ref = ERR_PTR(err); -	goto error_2; +	/* Resume the processing of a keyring higher up in the tree */ +	sp--; +	keyring = stack[sp].keyring; +	node = stack[sp].node; +	slot = stack[sp].slot + 1; +	kdebug("ascend to %d [%d]", keyring->serial, slot); +	goto ascend_to_node; -	/* we found a viable match */ +	/* We found a viable match */  found: -	atomic_inc(&key->usage); -	key->last_used_at = now.tv_sec; -	keyring->last_used_at = now.tv_sec; -	while (sp > 0) -		stack[--sp].keyring->last_used_at = now.tv_sec; +	key = key_ref_to_ptr(ctx->result);  	key_check(key); -	key_ref = make_key_ref(key, possessed); -error_2: +	if (!(ctx->flags & KEYRING_SEARCH_NO_UPDATE_TIME)) { +		key->last_used_at = ctx->now.tv_sec; +		keyring->last_used_at = ctx->now.tv_sec; +		while (sp > 0) +			stack[--sp].keyring->last_used_at = ctx->now.tv_sec; +	} +	kleave(" = true"); +	return true; +} + +/** + * keyring_search_aux - Search a keyring tree for a key matching some criteria + * @keyring_ref: A pointer to the keyring with possession indicator. + * @ctx: The keyring search context. + * + * Search the supplied keyring tree for a key that matches the criteria given. + * The root keyring and any linked keyrings must grant Search permission to the + * caller to be searchable and keys can only be found if they too grant Search + * to the caller. The possession flag on the root keyring pointer controls use + * of the possessor bits in permissions checking of the entire tree.  In + * addition, the LSM gets to forbid keyring searches and key matches. + * + * The search is performed as a breadth-then-depth search up to the prescribed + * limit (KEYRING_SEARCH_MAX_DEPTH). + * + * Keys are matched to the type provided and are then filtered by the match + * function, which is given the description to use in any way it sees fit.  The + * match function may use any attributes of a key that it wishes to to + * determine the match.  Normally the match function from the key type would be + * used. + * + * RCU can be used to prevent the keyring key lists from disappearing without + * the need to take lots of locks. + * + * Returns a pointer to the found key and increments the key usage count if + * successful; -EAGAIN if no matching keys were found, or if expired or revoked + * keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the + * specified keyring wasn't a keyring. + * + * In the case of a successful return, the possession attribute from + * @keyring_ref is propagated to the returned key reference. + */ +key_ref_t keyring_search_aux(key_ref_t keyring_ref, +			     struct keyring_search_context *ctx) +{ +	struct key *keyring; +	long err; + +	ctx->iterator = keyring_search_iterator; +	ctx->possessed = is_key_possessed(keyring_ref); +	ctx->result = ERR_PTR(-EAGAIN); + +	keyring = key_ref_to_ptr(keyring_ref); +	key_check(keyring); + +	if (keyring->type != &key_type_keyring) +		return ERR_PTR(-ENOTDIR); + +	if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM)) { +		err = key_task_permission(keyring_ref, ctx->cred, KEY_NEED_SEARCH); +		if (err < 0) +			return ERR_PTR(err); +	} + +	rcu_read_lock(); +	ctx->now = current_kernel_time(); +	if (search_nested_keyrings(keyring, ctx)) +		__key_get(key_ref_to_ptr(ctx->result));  	rcu_read_unlock(); -error: -	return key_ref; +	return ctx->result;  }  /** @@ -507,77 +863,73 @@ error:   * @description: The name of the keyring we want to find.   *   * As keyring_search_aux() above, but using the current task's credentials and - * type's default matching function. + * type's default matching function and preferred search method.   */  key_ref_t keyring_search(key_ref_t keyring,  			 struct key_type *type,  			 const char *description)  { -	if (!type->match) +	struct keyring_search_context ctx = { +		.index_key.type		= type, +		.index_key.description	= description, +		.cred			= current_cred(), +		.match			= type->match, +		.match_data		= description, +		.flags			= (type->def_lookup_type | +					   KEYRING_SEARCH_DO_STATE_CHECK), +	}; + +	if (!ctx.match)  		return ERR_PTR(-ENOKEY); -	return keyring_search_aux(keyring, current->cred, -				  type, description, type->match, false); +	return keyring_search_aux(keyring, &ctx);  }  EXPORT_SYMBOL(keyring_search);  /* - * Search the given keyring only (no recursion). + * Search the given keyring for a key that might be updated.   *   * The caller must guarantee that the keyring is a keyring and that the - * permission is granted to search the keyring as no check is made here. - * - * RCU is used to make it unnecessary to lock the keyring key list here. + * permission is granted to modify the keyring as no check is made here.  The + * caller must also hold a lock on the keyring semaphore.   *   * Returns a pointer to the found key with usage count incremented if - * successful and returns -ENOKEY if not found.  Revoked keys and keys not - * providing the requested permission are skipped over. + * successful and returns NULL if not found.  Revoked and invalidated keys are + * skipped over.   *   * If successful, the possession indicator is propagated from the keyring ref   * to the returned key reference.   */ -key_ref_t __keyring_search_one(key_ref_t keyring_ref, -			       const struct key_type *ktype, -			       const char *description, -			       key_perm_t perm) +key_ref_t find_key_to_update(key_ref_t keyring_ref, +			     const struct keyring_index_key *index_key)  { -	struct keyring_list *klist; -	unsigned long possessed;  	struct key *keyring, *key; -	int nkeys, loop; +	const void *object;  	keyring = key_ref_to_ptr(keyring_ref); -	possessed = is_key_possessed(keyring_ref); -	rcu_read_lock(); +	kenter("{%d},{%s,%s}", +	       keyring->serial, index_key->type->name, index_key->description); -	klist = rcu_dereference(keyring->payload.subscriptions); -	if (klist) { -		nkeys = klist->nkeys; -		smp_rmb(); -		for (loop = 0; loop < nkeys ; loop++) { -			key = rcu_dereference(klist->keys[loop]); -			if (key->type == ktype && -			    (!key->type->match || -			     key->type->match(key, description)) && -			    key_permission(make_key_ref(key, possessed), -					   perm) == 0 && -			    !(key->flags & ((1 << KEY_FLAG_INVALIDATED) | -					    (1 << KEY_FLAG_REVOKED))) -			    ) -				goto found; -		} -	} +	object = assoc_array_find(&keyring->keys, &keyring_assoc_array_ops, +				  index_key); -	rcu_read_unlock(); -	return ERR_PTR(-ENOKEY); +	if (object) +		goto found; + +	kleave(" = NULL"); +	return NULL;  found: -	atomic_inc(&key->usage); -	keyring->last_used_at = key->last_used_at = -		current_kernel_time().tv_sec; -	rcu_read_unlock(); -	return make_key_ref(key, possessed); +	key = keyring_ptr_to_key(object); +	if (key->flags & ((1 << KEY_FLAG_INVALIDATED) | +			  (1 << KEY_FLAG_REVOKED))) { +		kleave(" = NULL [x]"); +		return NULL; +	} +	__key_get(key); +	kleave(" = {%d}", key->serial); +	return make_key_ref(key, is_key_possessed(keyring_ref));  }  /* @@ -621,7 +973,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)  			if (!skip_perm_check &&  			    key_permission(make_key_ref(keyring, 0), -					   KEY_SEARCH) < 0) +					   KEY_NEED_SEARCH) < 0)  				continue;  			/* we've got a match but we might end up racing with @@ -640,6 +992,23 @@ out:  	return keyring;  } +static int keyring_detect_cycle_iterator(const void *object, +					 void *iterator_data) +{ +	struct keyring_search_context *ctx = iterator_data; +	const struct key *key = keyring_ptr_to_key(object); + +	kenter("{%d}", key->serial); + +	/* We might get a keyring with matching index-key that is nonetheless a +	 * different keyring. */ +	if (key != ctx->match_data) +		return 0; + +	ctx->result = ERR_PTR(-EDEADLK); +	return 1; +} +  /*   * See if a cycle will will be created by inserting acyclic tree B in acyclic   * tree A at the topmost level (ie: as a direct child of A). @@ -649,116 +1018,39 @@ out:   */  static int keyring_detect_cycle(struct key *A, struct key *B)  { -	struct { -		struct keyring_list *keylist; -		int kix; -	} stack[KEYRING_SEARCH_MAX_DEPTH]; - -	struct keyring_list *keylist; -	struct key *subtree, *key; -	int sp, nkeys, kix, ret; +	struct keyring_search_context ctx = { +		.index_key	= A->index_key, +		.match_data	= A, +		.iterator	= keyring_detect_cycle_iterator, +		.flags		= (KEYRING_SEARCH_LOOKUP_DIRECT | +				   KEYRING_SEARCH_NO_STATE_CHECK | +				   KEYRING_SEARCH_NO_UPDATE_TIME | +				   KEYRING_SEARCH_NO_CHECK_PERM | +				   KEYRING_SEARCH_DETECT_TOO_DEEP), +	};  	rcu_read_lock(); - -	ret = -EDEADLK; -	if (A == B) -		goto cycle_detected; - -	subtree = B; -	sp = 0; - -	/* start processing a new keyring */ -descend: -	if (test_bit(KEY_FLAG_REVOKED, &subtree->flags)) -		goto not_this_keyring; - -	keylist = rcu_dereference(subtree->payload.subscriptions); -	if (!keylist) -		goto not_this_keyring; -	kix = 0; - -ascend: -	/* iterate through the remaining keys in this keyring */ -	nkeys = keylist->nkeys; -	smp_rmb(); -	for (; kix < nkeys; kix++) { -		key = rcu_dereference(keylist->keys[kix]); - -		if (key == A) -			goto cycle_detected; - -		/* recursively check nested keyrings */ -		if (key->type == &key_type_keyring) { -			if (sp >= KEYRING_SEARCH_MAX_DEPTH) -				goto too_deep; - -			/* stack the current position */ -			stack[sp].keylist = keylist; -			stack[sp].kix = kix; -			sp++; - -			/* begin again with the new keyring */ -			subtree = key; -			goto descend; -		} -	} - -	/* the keyring we're looking at was disqualified or didn't contain a -	 * matching key */ -not_this_keyring: -	if (sp > 0) { -		/* resume the checking of a keyring higher up in the tree */ -		sp--; -		keylist = stack[sp].keylist; -		kix = stack[sp].kix + 1; -		goto ascend; -	} - -	ret = 0; /* no cycles detected */ - -error: +	search_nested_keyrings(B, &ctx);  	rcu_read_unlock(); -	return ret; - -too_deep: -	ret = -ELOOP; -	goto error; - -cycle_detected: -	ret = -EDEADLK; -	goto error; -} - -/* - * Dispose of a keyring list after the RCU grace period, freeing the unlinked - * key - */ -static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) -{ -	struct keyring_list *klist = -		container_of(rcu, struct keyring_list, rcu); - -	if (klist->delkey != USHRT_MAX) -		key_put(rcu_access_pointer(klist->keys[klist->delkey])); -	kfree(klist); +	return PTR_ERR(ctx.result) == -EAGAIN ? 0 : PTR_ERR(ctx.result);  }  /*   * Preallocate memory so that a key can be linked into to a keyring.   */ -int __key_link_begin(struct key *keyring, const struct key_type *type, -		     const char *description, unsigned long *_prealloc) +int __key_link_begin(struct key *keyring, +		     const struct keyring_index_key *index_key, +		     struct assoc_array_edit **_edit)  	__acquires(&keyring->sem)  	__acquires(&keyring_serialise_link_sem)  { -	struct keyring_list *klist, *nklist; -	unsigned long prealloc; -	unsigned max; -	time_t lowest_lru; -	size_t size; -	int loop, lru, ret; +	struct assoc_array_edit *edit; +	int ret; + +	kenter("%d,%s,%s,", +	       keyring->serial, index_key->type->name, index_key->description); -	kenter("%d,%s,%s,", key_serial(keyring), type->name, description); +	BUG_ON(index_key->desc_len == 0);  	if (keyring->type != &key_type_keyring)  		return -ENOTDIR; @@ -771,100 +1063,39 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,  	/* serialise link/link calls to prevent parallel calls causing a cycle  	 * when linking two keyring in opposite orders */ -	if (type == &key_type_keyring) +	if (index_key->type == &key_type_keyring)  		down_write(&keyring_serialise_link_sem); -	klist = rcu_dereference_locked_keyring(keyring); - -	/* see if there's a matching key we can displace */ -	lru = -1; -	if (klist && klist->nkeys > 0) { -		lowest_lru = TIME_T_MAX; -		for (loop = klist->nkeys - 1; loop >= 0; loop--) { -			struct key *key = rcu_deref_link_locked(klist, loop, -								keyring); -			if (key->type == type && -			    strcmp(key->description, description) == 0) { -				/* Found a match - we'll replace the link with -				 * one to the new key.  We record the slot -				 * position. -				 */ -				klist->delkey = loop; -				prealloc = 0; -				goto done; -			} -			if (key->last_used_at < lowest_lru) { -				lowest_lru = key->last_used_at; -				lru = loop; -			} -		} -	} - -	/* If the keyring is full then do an LRU discard */ -	if (klist && -	    klist->nkeys == klist->maxkeys && -	    klist->maxkeys >= MAX_KEYRING_LINKS) { -		kdebug("LRU discard %d\n", lru); -		klist->delkey = lru; -		prealloc = 0; -		goto done; -	} - -	/* check that we aren't going to overrun the user's quota */ -	ret = key_payload_reserve(keyring, -				  keyring->datalen + KEYQUOTA_LINK_BYTES); -	if (ret < 0) +	/* Create an edit script that will insert/replace the key in the +	 * keyring tree. +	 */ +	edit = assoc_array_insert(&keyring->keys, +				  &keyring_assoc_array_ops, +				  index_key, +				  NULL); +	if (IS_ERR(edit)) { +		ret = PTR_ERR(edit);  		goto error_sem; +	} -	if (klist && klist->nkeys < klist->maxkeys) { -		/* there's sufficient slack space to append directly */ -		klist->delkey = klist->nkeys; -		prealloc = KEY_LINK_FIXQUOTA; -	} else { -		/* grow the key list */ -		max = 4; -		if (klist) { -			max += klist->maxkeys; -			if (max > MAX_KEYRING_LINKS) -				max = MAX_KEYRING_LINKS; -			BUG_ON(max <= klist->maxkeys); -		} - -		size = sizeof(*klist) + sizeof(struct key *) * max; - -		ret = -ENOMEM; -		nklist = kmalloc(size, GFP_KERNEL); -		if (!nklist) -			goto error_quota; - -		nklist->maxkeys = max; -		if (klist) { -			memcpy(nklist->keys, klist->keys, -			       sizeof(struct key *) * klist->nkeys); -			nklist->delkey = klist->nkeys; -			nklist->nkeys = klist->nkeys + 1; -			klist->delkey = USHRT_MAX; -		} else { -			nklist->nkeys = 1; -			nklist->delkey = 0; -		} - -		/* add the key into the new space */ -		RCU_INIT_POINTER(nklist->keys[nklist->delkey], NULL); -		prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA; +	/* If we're not replacing a link in-place then we're going to need some +	 * extra quota. +	 */ +	if (!edit->dead_leaf) { +		ret = key_payload_reserve(keyring, +					  keyring->datalen + KEYQUOTA_LINK_BYTES); +		if (ret < 0) +			goto error_cancel;  	} -done: -	*_prealloc = prealloc; +	*_edit = edit;  	kleave(" = 0");  	return 0; -error_quota: -	/* undo the quota changes */ -	key_payload_reserve(keyring, -			    keyring->datalen - KEYQUOTA_LINK_BYTES); +error_cancel: +	assoc_array_cancel_edit(edit);  error_sem: -	if (type == &key_type_keyring) +	if (index_key->type == &key_type_keyring)  		up_write(&keyring_serialise_link_sem);  error_krsem:  	up_write(&keyring->sem); @@ -895,60 +1126,12 @@ int __key_link_check_live_key(struct key *keyring, struct key *key)   * holds at most one link to any given key of a particular type+description   * combination.   */ -void __key_link(struct key *keyring, struct key *key, -		unsigned long *_prealloc) +void __key_link(struct key *key, struct assoc_array_edit **_edit)  { -	struct keyring_list *klist, *nklist; -	struct key *discard; - -	nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA); -	*_prealloc = 0; - -	kenter("%d,%d,%p", keyring->serial, key->serial, nklist); - -	klist = rcu_dereference_locked_keyring(keyring); - -	atomic_inc(&key->usage); -	keyring->last_used_at = key->last_used_at = -		current_kernel_time().tv_sec; - -	/* there's a matching key we can displace or an empty slot in a newly -	 * allocated list we can fill */ -	if (nklist) { -		kdebug("reissue %hu/%hu/%hu", -		       nklist->delkey, nklist->nkeys, nklist->maxkeys); - -		RCU_INIT_POINTER(nklist->keys[nklist->delkey], key); - -		rcu_assign_pointer(keyring->payload.subscriptions, nklist); - -		/* dispose of the old keyring list and, if there was one, the -		 * displaced key */ -		if (klist) { -			kdebug("dispose %hu/%hu/%hu", -			       klist->delkey, klist->nkeys, klist->maxkeys); -			call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); -		} -	} else if (klist->delkey < klist->nkeys) { -		kdebug("replace %hu/%hu/%hu", -		       klist->delkey, klist->nkeys, klist->maxkeys); - -		discard = rcu_dereference_protected( -			klist->keys[klist->delkey], -			rwsem_is_locked(&keyring->sem)); -		rcu_assign_pointer(klist->keys[klist->delkey], key); -		/* The garbage collector will take care of RCU -		 * synchronisation */ -		key_put(discard); -	} else { -		/* there's sufficient slack space to append directly */ -		kdebug("append %hu/%hu/%hu", -		       klist->delkey, klist->nkeys, klist->maxkeys); - -		RCU_INIT_POINTER(klist->keys[klist->delkey], key); -		smp_wmb(); -		klist->nkeys++; -	} +	__key_get(key); +	assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key)); +	assoc_array_apply_edit(*_edit); +	*_edit = NULL;  }  /* @@ -956,24 +1139,22 @@ void __key_link(struct key *keyring, struct key *key,   *   * Must be called with __key_link_begin() having being called.   */ -void __key_link_end(struct key *keyring, struct key_type *type, -		    unsigned long prealloc) +void __key_link_end(struct key *keyring, +		    const struct keyring_index_key *index_key, +		    struct assoc_array_edit *edit)  	__releases(&keyring->sem)  	__releases(&keyring_serialise_link_sem)  { -	BUG_ON(type == NULL); -	BUG_ON(type->name == NULL); -	kenter("%d,%s,%lx", keyring->serial, type->name, prealloc); +	BUG_ON(index_key->type == NULL); +	kenter("%d,%s,", keyring->serial, index_key->type->name); -	if (type == &key_type_keyring) +	if (index_key->type == &key_type_keyring)  		up_write(&keyring_serialise_link_sem); -	if (prealloc) { -		if (prealloc & KEY_LINK_FIXQUOTA) -			key_payload_reserve(keyring, -					    keyring->datalen - -					    KEYQUOTA_LINK_BYTES); -		kfree((struct keyring_list *)(prealloc & ~KEY_LINK_FIXQUOTA)); +	if (edit && !edit->dead_leaf) { +		key_payload_reserve(keyring, +				    keyring->datalen - KEYQUOTA_LINK_BYTES); +		assoc_array_cancel_edit(edit);  	}  	up_write(&keyring->sem);  } @@ -1000,20 +1181,28 @@ void __key_link_end(struct key *keyring, struct key_type *type,   */  int key_link(struct key *keyring, struct key *key)  { -	unsigned long prealloc; +	struct assoc_array_edit *edit;  	int ret; +	kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage)); +  	key_check(keyring);  	key_check(key); -	ret = __key_link_begin(keyring, key->type, key->description, &prealloc); +	if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) && +	    !test_bit(KEY_FLAG_TRUSTED, &key->flags)) +		return -EPERM; + +	ret = __key_link_begin(keyring, &key->index_key, &edit);  	if (ret == 0) { +		kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));  		ret = __key_link_check_live_key(keyring, key);  		if (ret == 0) -			__key_link(keyring, key, &prealloc); -		__key_link_end(keyring, key->type, prealloc); +			__key_link(key, &edit); +		__key_link_end(keyring, &key->index_key, edit);  	} +	kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));  	return ret;  }  EXPORT_SYMBOL(key_link); @@ -1037,90 +1226,37 @@ EXPORT_SYMBOL(key_link);   */  int key_unlink(struct key *keyring, struct key *key)  { -	struct keyring_list *klist, *nklist; -	int loop, ret; +	struct assoc_array_edit *edit; +	int ret;  	key_check(keyring);  	key_check(key); -	ret = -ENOTDIR;  	if (keyring->type != &key_type_keyring) -		goto error; +		return -ENOTDIR;  	down_write(&keyring->sem); -	klist = rcu_dereference_locked_keyring(keyring); -	if (klist) { -		/* search the keyring for the key */ -		for (loop = 0; loop < klist->nkeys; loop++) -			if (rcu_access_pointer(klist->keys[loop]) == key) -				goto key_is_present; +	edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops, +				  &key->index_key); +	if (IS_ERR(edit)) { +		ret = PTR_ERR(edit); +		goto error;  	} - -	up_write(&keyring->sem);  	ret = -ENOENT; -	goto error; - -key_is_present: -	/* we need to copy the key list for RCU purposes */ -	nklist = kmalloc(sizeof(*klist) + -			 sizeof(struct key *) * klist->maxkeys, -			 GFP_KERNEL); -	if (!nklist) -		goto nomem; -	nklist->maxkeys = klist->maxkeys; -	nklist->nkeys = klist->nkeys - 1; - -	if (loop > 0) -		memcpy(&nklist->keys[0], -		       &klist->keys[0], -		       loop * sizeof(struct key *)); - -	if (loop < nklist->nkeys) -		memcpy(&nklist->keys[loop], -		       &klist->keys[loop + 1], -		       (nklist->nkeys - loop) * sizeof(struct key *)); - -	/* adjust the user's quota */ -	key_payload_reserve(keyring, -			    keyring->datalen - KEYQUOTA_LINK_BYTES); - -	rcu_assign_pointer(keyring->payload.subscriptions, nklist); - -	up_write(&keyring->sem); - -	/* schedule for later cleanup */ -	klist->delkey = loop; -	call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); +	if (edit == NULL) +		goto error; +	assoc_array_apply_edit(edit); +	key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);  	ret = 0;  error: -	return ret; -nomem: -	ret = -ENOMEM;  	up_write(&keyring->sem); -	goto error; +	return ret;  }  EXPORT_SYMBOL(key_unlink); -/* - * Dispose of a keyring list after the RCU grace period, releasing the keys it - * links to. - */ -static void keyring_clear_rcu_disposal(struct rcu_head *rcu) -{ -	struct keyring_list *klist; -	int loop; - -	klist = container_of(rcu, struct keyring_list, rcu); - -	for (loop = klist->nkeys - 1; loop >= 0; loop--) -		key_put(rcu_access_pointer(klist->keys[loop])); - -	kfree(klist); -} -  /**   * keyring_clear - Clear a keyring   * @keyring: The keyring to clear. @@ -1131,33 +1267,25 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu)   */  int keyring_clear(struct key *keyring)  { -	struct keyring_list *klist; +	struct assoc_array_edit *edit;  	int ret; -	ret = -ENOTDIR; -	if (keyring->type == &key_type_keyring) { -		/* detach the pointer block with the locks held */ -		down_write(&keyring->sem); - -		klist = rcu_dereference_locked_keyring(keyring); -		if (klist) { -			/* adjust the quota */ -			key_payload_reserve(keyring, -					    sizeof(struct keyring_list)); - -			rcu_assign_pointer(keyring->payload.subscriptions, -					   NULL); -		} - -		up_write(&keyring->sem); +	if (keyring->type != &key_type_keyring) +		return -ENOTDIR; -		/* free the keys after the locks have been dropped */ -		if (klist) -			call_rcu(&klist->rcu, keyring_clear_rcu_disposal); +	down_write(&keyring->sem); +	edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops); +	if (IS_ERR(edit)) { +		ret = PTR_ERR(edit); +	} else { +		if (edit) +			assoc_array_apply_edit(edit); +		key_payload_reserve(keyring, 0);  		ret = 0;  	} +	up_write(&keyring->sem);  	return ret;  }  EXPORT_SYMBOL(keyring_clear); @@ -1169,111 +1297,68 @@ EXPORT_SYMBOL(keyring_clear);   */  static void keyring_revoke(struct key *keyring)  { -	struct keyring_list *klist; +	struct assoc_array_edit *edit; + +	edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops); +	if (!IS_ERR(edit)) { +		if (edit) +			assoc_array_apply_edit(edit); +		key_payload_reserve(keyring, 0); +	} +} + +static bool keyring_gc_select_iterator(void *object, void *iterator_data) +{ +	struct key *key = keyring_ptr_to_key(object); +	time_t *limit = iterator_data; -	klist = rcu_dereference_locked_keyring(keyring); +	if (key_is_dead(key, *limit)) +		return false; +	key_get(key); +	return true; +} -	/* adjust the quota */ -	key_payload_reserve(keyring, 0); +static int keyring_gc_check_iterator(const void *object, void *iterator_data) +{ +	const struct key *key = keyring_ptr_to_key(object); +	time_t *limit = iterator_data; -	if (klist) { -		rcu_assign_pointer(keyring->payload.subscriptions, NULL); -		call_rcu(&klist->rcu, keyring_clear_rcu_disposal); -	} +	key_check(key); +	return key_is_dead(key, *limit);  }  /* - * Collect garbage from the contents of a keyring, replacing the old list with - * a new one with the pointers all shuffled down. + * Garbage collect pointers from a keyring.   * - * Dead keys are classed as oned that are flagged as being dead or are revoked, - * expired or negative keys that were revoked or expired before the specified - * limit. + * Not called with any locks held.  The keyring's key struct will not be + * deallocated under us as only our caller may deallocate it.   */  void keyring_gc(struct key *keyring, time_t limit)  { -	struct keyring_list *klist, *new; -	struct key *key; -	int loop, keep, max; - -	kenter("{%x,%s}", key_serial(keyring), keyring->description); - -	down_write(&keyring->sem); - -	klist = rcu_dereference_locked_keyring(keyring); -	if (!klist) -		goto no_klist; - -	/* work out how many subscriptions we're keeping */ -	keep = 0; -	for (loop = klist->nkeys - 1; loop >= 0; loop--) -		if (!key_is_dead(rcu_deref_link_locked(klist, loop, keyring), -				 limit)) -			keep++; - -	if (keep == klist->nkeys) -		goto just_return; - -	/* allocate a new keyring payload */ -	max = roundup(keep, 4); -	new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *), -		      GFP_KERNEL); -	if (!new) -		goto nomem; -	new->maxkeys = max; -	new->nkeys = 0; -	new->delkey = 0; - -	/* install the live keys -	 * - must take care as expired keys may be updated back to life -	 */ -	keep = 0; -	for (loop = klist->nkeys - 1; loop >= 0; loop--) { -		key = rcu_deref_link_locked(klist, loop, keyring); -		if (!key_is_dead(key, limit)) { -			if (keep >= max) -				goto discard_new; -			RCU_INIT_POINTER(new->keys[keep++], key_get(key)); -		} -	} -	new->nkeys = keep; - -	/* adjust the quota */ -	key_payload_reserve(keyring, -			    sizeof(struct keyring_list) + -			    KEYQUOTA_LINK_BYTES * keep); +	int result; -	if (keep == 0) { -		rcu_assign_pointer(keyring->payload.subscriptions, NULL); -		kfree(new); -	} else { -		rcu_assign_pointer(keyring->payload.subscriptions, new); -	} +	kenter("%x{%s}", keyring->serial, keyring->description ?: ""); -	up_write(&keyring->sem); +	if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | +			      (1 << KEY_FLAG_REVOKED))) +		goto dont_gc; -	call_rcu(&klist->rcu, keyring_clear_rcu_disposal); -	kleave(" [yes]"); -	return; - -discard_new: -	new->nkeys = keep; -	keyring_clear_rcu_disposal(&new->rcu); -	up_write(&keyring->sem); -	kleave(" [discard]"); -	return; - -just_return: -	up_write(&keyring->sem); -	kleave(" [no dead]"); -	return; +	/* scan the keyring looking for dead keys */ +	rcu_read_lock(); +	result = assoc_array_iterate(&keyring->keys, +				     keyring_gc_check_iterator, &limit); +	rcu_read_unlock(); +	if (result == true) +		goto do_gc; -no_klist: -	up_write(&keyring->sem); -	kleave(" [no_klist]"); +dont_gc: +	kleave(" [no gc]");  	return; -nomem: +do_gc: +	down_write(&keyring->sem); +	assoc_array_gc(&keyring->keys, &keyring_assoc_array_ops, +		       keyring_gc_select_iterator, &limit);  	up_write(&keyring->sem); -	kleave(" [oom]"); +	kleave(" [gc]");  } diff --git a/security/keys/permission.c b/security/keys/permission.c index efcc0c855a0..732cc0beffd 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -28,7 +28,7 @@   * permissions bits or the LSM check.   */  int key_task_permission(const key_ref_t key_ref, const struct cred *cred, -			key_perm_t perm) +			unsigned perm)  {  	struct key *key;  	key_perm_t kperm; @@ -68,7 +68,7 @@ use_these_perms:  	if (is_key_possessed(key_ref))  		kperm |= key->perm >> 24; -	kperm = kperm & perm & KEY_ALL; +	kperm = kperm & perm & KEY_NEED_ALL;  	if (kperm != perm)  		return -EACCES; diff --git a/security/keys/persistent.c b/security/keys/persistent.c new file mode 100644 index 00000000000..c9fae5ea89f --- /dev/null +++ b/security/keys/persistent.c @@ -0,0 +1,167 @@ +/* General persistent per-UID keyrings register + * + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/user_namespace.h> +#include "internal.h" + +unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */ + +/* + * Create the persistent keyring register for the current user namespace. + * + * Called with the namespace's sem locked for writing. + */ +static int key_create_persistent_register(struct user_namespace *ns) +{ +	struct key *reg = keyring_alloc(".persistent_register", +					KUIDT_INIT(0), KGIDT_INIT(0), +					current_cred(), +					((KEY_POS_ALL & ~KEY_POS_SETATTR) | +					 KEY_USR_VIEW | KEY_USR_READ), +					KEY_ALLOC_NOT_IN_QUOTA, NULL); +	if (IS_ERR(reg)) +		return PTR_ERR(reg); + +	ns->persistent_keyring_register = reg; +	return 0; +} + +/* + * Create the persistent keyring for the specified user. + * + * Called with the namespace's sem locked for writing. + */ +static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, +				       struct keyring_index_key *index_key) +{ +	struct key *persistent; +	key_ref_t reg_ref, persistent_ref; + +	if (!ns->persistent_keyring_register) { +		long err = key_create_persistent_register(ns); +		if (err < 0) +			return ERR_PTR(err); +	} else { +		reg_ref = make_key_ref(ns->persistent_keyring_register, true); +		persistent_ref = find_key_to_update(reg_ref, index_key); +		if (persistent_ref) +			return persistent_ref; +	} + +	persistent = keyring_alloc(index_key->description, +				   uid, INVALID_GID, current_cred(), +				   ((KEY_POS_ALL & ~KEY_POS_SETATTR) | +				    KEY_USR_VIEW | KEY_USR_READ), +				   KEY_ALLOC_NOT_IN_QUOTA, +				   ns->persistent_keyring_register); +	if (IS_ERR(persistent)) +		return ERR_CAST(persistent); + +	return make_key_ref(persistent, true); +} + +/* + * Get the persistent keyring for a specific UID and link it to the nominated + * keyring. + */ +static long key_get_persistent(struct user_namespace *ns, kuid_t uid, +			       key_ref_t dest_ref) +{ +	struct keyring_index_key index_key; +	struct key *persistent; +	key_ref_t reg_ref, persistent_ref; +	char buf[32]; +	long ret; + +	/* Look in the register if it exists */ +	index_key.type = &key_type_keyring; +	index_key.description = buf; +	index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid)); + +	if (ns->persistent_keyring_register) { +		reg_ref = make_key_ref(ns->persistent_keyring_register, true); +		down_read(&ns->persistent_keyring_register_sem); +		persistent_ref = find_key_to_update(reg_ref, &index_key); +		up_read(&ns->persistent_keyring_register_sem); + +		if (persistent_ref) +			goto found; +	} + +	/* It wasn't in the register, so we'll need to create it.  We might +	 * also need to create the register. +	 */ +	down_write(&ns->persistent_keyring_register_sem); +	persistent_ref = key_create_persistent(ns, uid, &index_key); +	up_write(&ns->persistent_keyring_register_sem); +	if (!IS_ERR(persistent_ref)) +		goto found; + +	return PTR_ERR(persistent_ref); + +found: +	ret = key_task_permission(persistent_ref, current_cred(), KEY_NEED_LINK); +	if (ret == 0) { +		persistent = key_ref_to_ptr(persistent_ref); +		ret = key_link(key_ref_to_ptr(dest_ref), persistent); +		if (ret == 0) { +			key_set_timeout(persistent, persistent_keyring_expiry); +			ret = persistent->serial;		 +		} +	} + +	key_ref_put(persistent_ref); +	return ret; +} + +/* + * Get the persistent keyring for a specific UID and link it to the nominated + * keyring. + */ +long keyctl_get_persistent(uid_t _uid, key_serial_t destid) +{ +	struct user_namespace *ns = current_user_ns(); +	key_ref_t dest_ref; +	kuid_t uid; +	long ret; + +	/* -1 indicates the current user */ +	if (_uid == (uid_t)-1) { +		uid = current_uid(); +	} else { +		uid = make_kuid(ns, _uid); +		if (!uid_valid(uid)) +			return -EINVAL; + +		/* You can only see your own persistent cache if you're not +		 * sufficiently privileged. +		 */ +		if (!uid_eq(uid, current_uid()) && +		    !uid_eq(uid, current_euid()) && +		    !ns_capable(ns, CAP_SETUID)) +			return -EPERM; +	} + +	/* There must be a destination keyring */ +	dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); +	if (IS_ERR(dest_ref)) +		return PTR_ERR(dest_ref); +	if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) { +		ret = -ENOTDIR; +		goto out_put_dest; +	} + +	ret = key_get_persistent(ns, uid, dest_ref); + +out_put_dest: +	key_ref_put(dest_ref); +	return ret; +} diff --git a/security/keys/proc.c b/security/keys/proc.c index 217b6855e81..d3f6f2fd21d 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -182,7 +182,6 @@ static void proc_keys_stop(struct seq_file *p, void *v)  static int proc_keys_show(struct seq_file *m, void *v)  { -	const struct cred *cred = current_cred();  	struct rb_node *_p = v;  	struct key *key = rb_entry(_p, struct key, serial_node);  	struct timespec now; @@ -191,15 +190,23 @@ static int proc_keys_show(struct seq_file *m, void *v)  	char xbuf[12];  	int rc; +	struct keyring_search_context ctx = { +		.index_key.type		= key->type, +		.index_key.description	= key->description, +		.cred			= current_cred(), +		.match			= lookup_user_key_possessed, +		.match_data		= key, +		.flags			= (KEYRING_SEARCH_NO_STATE_CHECK | +					   KEYRING_SEARCH_LOOKUP_DIRECT), +	}; +  	key_ref = make_key_ref(key, 0);  	/* determine if the key is possessed by this process (a test we can  	 * skip if the key does not indicate the possessor can view it  	 */  	if (key->perm & KEY_POS_VIEW) { -		skey_ref = search_my_process_keyrings(key->type, key, -						      lookup_user_key_possessed, -						      true, cred); +		skey_ref = search_my_process_keyrings(&ctx);  		if (!IS_ERR(skey_ref)) {  			key_ref_put(skey_ref);  			key_ref = make_key_ref(key, 1); @@ -211,7 +218,7 @@ static int proc_keys_show(struct seq_file *m, void *v)  	 * - the caller holds a spinlock, and thus the RCU read lock, making our  	 *   access to __current_cred() safe  	 */ -	rc = key_task_permission(key_ref, cred, KEY_VIEW); +	rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW);  	if (rc < 0)  		return 0; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 42defae1e16..0cf8a130a26 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -235,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  		if (IS_ERR(keyring))  			return PTR_ERR(keyring);  	} else { -		atomic_inc(&keyring->usage); +		__key_get(keyring);  	}  	/* install the keyring */ @@ -319,11 +319,7 @@ void key_fsgid_changed(struct task_struct *tsk)   * In the case of a successful return, the possession attribute is set on the   * returned key reference.   */ -key_ref_t search_my_process_keyrings(struct key_type *type, -				     const void *description, -				     key_match_func_t match, -				     bool no_state_check, -				     const struct cred *cred) +key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)  {  	key_ref_t key_ref, ret, err; @@ -339,10 +335,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	err = ERR_PTR(-EAGAIN);  	/* search the thread keyring first */ -	if (cred->thread_keyring) { +	if (ctx->cred->thread_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->thread_keyring, 1), -			cred, type, description, match, no_state_check); +			make_key_ref(ctx->cred->thread_keyring, 1), ctx);  		if (!IS_ERR(key_ref))  			goto found; @@ -358,10 +353,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the process keyring second */ -	if (cred->process_keyring) { +	if (ctx->cred->process_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->process_keyring, 1), -			cred, type, description, match, no_state_check); +			make_key_ref(ctx->cred->process_keyring, 1), ctx);  		if (!IS_ERR(key_ref))  			goto found; @@ -379,11 +373,11 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the session keyring */ -	if (cred->session_keyring) { +	if (ctx->cred->session_keyring) {  		rcu_read_lock();  		key_ref = keyring_search_aux( -			make_key_ref(rcu_dereference(cred->session_keyring), 1), -			cred, type, description, match, no_state_check); +			make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1), +			ctx);  		rcu_read_unlock();  		if (!IS_ERR(key_ref)) @@ -402,10 +396,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  		}  	}  	/* or search the user-session keyring */ -	else if (cred->user->session_keyring) { +	else if (ctx->cred->user->session_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->user->session_keyring, 1), -			cred, type, description, match, no_state_check); +			make_key_ref(ctx->cred->user->session_keyring, 1), +			ctx);  		if (!IS_ERR(key_ref))  			goto found; @@ -437,18 +431,14 @@ found:   *   * Return same as search_my_process_keyrings().   */ -key_ref_t search_process_keyrings(struct key_type *type, -				  const void *description, -				  key_match_func_t match, -				  const struct cred *cred) +key_ref_t search_process_keyrings(struct keyring_search_context *ctx)  {  	struct request_key_auth *rka;  	key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;  	might_sleep(); -	key_ref = search_my_process_keyrings(type, description, match, -					     false, cred); +	key_ref = search_my_process_keyrings(ctx);  	if (!IS_ERR(key_ref))  		goto found;  	err = key_ref; @@ -457,18 +447,21 @@ key_ref_t search_process_keyrings(struct key_type *type,  	 * search the keyrings of the process mentioned there  	 * - we don't permit access to request_key auth keys via this method  	 */ -	if (cred->request_key_auth && -	    cred == current_cred() && -	    type != &key_type_request_key_auth +	if (ctx->cred->request_key_auth && +	    ctx->cred == current_cred() && +	    ctx->index_key.type != &key_type_request_key_auth  	    ) { +		const struct cred *cred = ctx->cred; +  		/* defend against the auth key being revoked */  		down_read(&cred->request_key_auth->sem); -		if (key_validate(cred->request_key_auth) == 0) { -			rka = cred->request_key_auth->payload.data; +		if (key_validate(ctx->cred->request_key_auth) == 0) { +			rka = ctx->cred->request_key_auth->payload.data; -			key_ref = search_process_keyrings(type, description, -							  match, rka->cred); +			ctx->cred = rka->cred; +			key_ref = search_process_keyrings(ctx); +			ctx->cred = cred;  			up_read(&cred->request_key_auth->sem); @@ -522,19 +515,23 @@ int lookup_user_key_possessed(const struct key *key, const void *target)  key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,  			  key_perm_t perm)  { +	struct keyring_search_context ctx = { +		.match	= lookup_user_key_possessed, +		.flags	= (KEYRING_SEARCH_NO_STATE_CHECK | +			   KEYRING_SEARCH_LOOKUP_DIRECT), +	};  	struct request_key_auth *rka; -	const struct cred *cred;  	struct key *key;  	key_ref_t key_ref, skey_ref;  	int ret;  try_again: -	cred = get_current_cred(); +	ctx.cred = get_current_cred();  	key_ref = ERR_PTR(-ENOKEY);  	switch (id) {  	case KEY_SPEC_THREAD_KEYRING: -		if (!cred->thread_keyring) { +		if (!ctx.cred->thread_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -546,13 +543,13 @@ try_again:  			goto reget_creds;  		} -		key = cred->thread_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->thread_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_PROCESS_KEYRING: -		if (!cred->process_keyring) { +		if (!ctx.cred->process_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -564,13 +561,13 @@ try_again:  			goto reget_creds;  		} -		key = cred->process_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->process_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_SESSION_KEYRING: -		if (!cred->session_keyring) { +		if (!ctx.cred->session_keyring) {  			/* always install a session keyring upon access if one  			 * doesn't exist yet */  			ret = install_user_keyrings(); @@ -580,13 +577,13 @@ try_again:  				ret = join_session_keyring(NULL);  			else  				ret = install_session_keyring( -					cred->user->session_keyring); +					ctx.cred->user->session_keyring);  			if (ret < 0)  				goto error;  			goto reget_creds; -		} else if (cred->session_keyring == -			   cred->user->session_keyring && +		} else if (ctx.cred->session_keyring == +			   ctx.cred->user->session_keyring &&  			   lflags & KEY_LOOKUP_CREATE) {  			ret = join_session_keyring(NULL);  			if (ret < 0) @@ -595,33 +592,33 @@ try_again:  		}  		rcu_read_lock(); -		key = rcu_dereference(cred->session_keyring); -		atomic_inc(&key->usage); +		key = rcu_dereference(ctx.cred->session_keyring); +		__key_get(key);  		rcu_read_unlock();  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_USER_KEYRING: -		if (!cred->user->uid_keyring) { +		if (!ctx.cred->user->uid_keyring) {  			ret = install_user_keyrings();  			if (ret < 0)  				goto error;  		} -		key = cred->user->uid_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->user->uid_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_USER_SESSION_KEYRING: -		if (!cred->user->session_keyring) { +		if (!ctx.cred->user->session_keyring) {  			ret = install_user_keyrings();  			if (ret < 0)  				goto error;  		} -		key = cred->user->session_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->user->session_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break; @@ -631,29 +628,29 @@ try_again:  		goto error;  	case KEY_SPEC_REQKEY_AUTH_KEY: -		key = cred->request_key_auth; +		key = ctx.cred->request_key_auth;  		if (!key)  			goto error; -		atomic_inc(&key->usage); +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_REQUESTOR_KEYRING: -		if (!cred->request_key_auth) +		if (!ctx.cred->request_key_auth)  			goto error; -		down_read(&cred->request_key_auth->sem); +		down_read(&ctx.cred->request_key_auth->sem);  		if (test_bit(KEY_FLAG_REVOKED, -			     &cred->request_key_auth->flags)) { +			     &ctx.cred->request_key_auth->flags)) {  			key_ref = ERR_PTR(-EKEYREVOKED);  			key = NULL;  		} else { -			rka = cred->request_key_auth->payload.data; +			rka = ctx.cred->request_key_auth->payload.data;  			key = rka->dest_keyring; -			atomic_inc(&key->usage); +			__key_get(key);  		} -		up_read(&cred->request_key_auth->sem); +		up_read(&ctx.cred->request_key_auth->sem);  		if (!key)  			goto error;  		key_ref = make_key_ref(key, 1); @@ -673,9 +670,13 @@ try_again:  		key_ref = make_key_ref(key, 0);  		/* check to see if we possess the key */ -		skey_ref = search_process_keyrings(key->type, key, -						   lookup_user_key_possessed, -						   cred); +		ctx.index_key.type		= key->type; +		ctx.index_key.description	= key->description; +		ctx.index_key.desc_len		= strlen(key->description); +		ctx.match_data			= key; +		kdebug("check possessed"); +		skey_ref = search_process_keyrings(&ctx); +		kdebug("possessed=%p", skey_ref);  		if (!IS_ERR(skey_ref)) {  			key_put(key); @@ -715,14 +716,14 @@ try_again:  		goto invalid_key;  	/* check the permissions */ -	ret = key_task_permission(key_ref, cred, perm); +	ret = key_task_permission(key_ref, ctx.cred, perm);  	if (ret < 0)  		goto invalid_key;  	key->last_used_at = current_kernel_time().tv_sec;  error: -	put_cred(cred); +	put_cred(ctx.cred);  	return key_ref;  invalid_key: @@ -733,7 +734,7 @@ invalid_key:  	/* if we attempted to install a keyring, then it may have caused new  	 * creds to be installed */  reget_creds: -	put_cred(cred); +	put_cred(ctx.cred);  	goto try_again;  } @@ -856,3 +857,13 @@ void key_change_session_keyring(struct callback_head *twork)  	commit_creds(new);  } + +/* + * Make sure that root's user and user-session keyrings exist. + */ +static int __init init_root_keyring(void) +{ +	return install_user_keyrings(); +} + +late_initcall(init_root_keyring); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index c411f9bb156..381411941cc 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -345,33 +345,34 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)   * May return a key that's already under construction instead if there was a   * race between two thread calling request_key().   */ -static int construct_alloc_key(struct key_type *type, -			       const char *description, +static int construct_alloc_key(struct keyring_search_context *ctx,  			       struct key *dest_keyring,  			       unsigned long flags,  			       struct key_user *user,  			       struct key **_key)  { -	const struct cred *cred = current_cred(); -	unsigned long prealloc; +	struct assoc_array_edit *edit;  	struct key *key;  	key_perm_t perm;  	key_ref_t key_ref;  	int ret; -	kenter("%s,%s,,,", type->name, description); +	kenter("%s,%s,,,", +	       ctx->index_key.type->name, ctx->index_key.description);  	*_key = NULL;  	mutex_lock(&user->cons_lock);  	perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;  	perm |= KEY_USR_VIEW; -	if (type->read) +	if (ctx->index_key.type->read)  		perm |= KEY_POS_READ; -	if (type == &key_type_keyring || type->update) +	if (ctx->index_key.type == &key_type_keyring || +	    ctx->index_key.type->update)  		perm |= KEY_POS_WRITE; -	key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred, +	key = key_alloc(ctx->index_key.type, ctx->index_key.description, +			ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,  			perm, flags);  	if (IS_ERR(key))  		goto alloc_failed; @@ -379,8 +380,7 @@ static int construct_alloc_key(struct key_type *type,  	set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);  	if (dest_keyring) { -		ret = __key_link_begin(dest_keyring, type, description, -				       &prealloc); +		ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);  		if (ret < 0)  			goto link_prealloc_failed;  	} @@ -390,16 +390,16 @@ static int construct_alloc_key(struct key_type *type,  	 * waited for locks */  	mutex_lock(&key_construction_mutex); -	key_ref = search_process_keyrings(type, description, type->match, cred); +	key_ref = search_process_keyrings(ctx);  	if (!IS_ERR(key_ref))  		goto key_already_present;  	if (dest_keyring) -		__key_link(dest_keyring, key, &prealloc); +		__key_link(key, &edit);  	mutex_unlock(&key_construction_mutex);  	if (dest_keyring) -		__key_link_end(dest_keyring, type, prealloc); +		__key_link_end(dest_keyring, &ctx->index_key, edit);  	mutex_unlock(&user->cons_lock);  	*_key = key;  	kleave(" = 0 [%d]", key_serial(key)); @@ -414,8 +414,8 @@ key_already_present:  	if (dest_keyring) {  		ret = __key_link_check_live_key(dest_keyring, key);  		if (ret == 0) -			__key_link(dest_keyring, key, &prealloc); -		__key_link_end(dest_keyring, type, prealloc); +			__key_link(key, &edit); +		__key_link_end(dest_keyring, &ctx->index_key, edit);  		if (ret < 0)  			goto link_check_failed;  	} @@ -444,8 +444,7 @@ alloc_failed:  /*   * Commence key construction.   */ -static struct key *construct_key_and_link(struct key_type *type, -					  const char *description, +static struct key *construct_key_and_link(struct keyring_search_context *ctx,  					  const char *callout_info,  					  size_t callout_len,  					  void *aux, @@ -464,8 +463,7 @@ static struct key *construct_key_and_link(struct key_type *type,  	construct_get_dest_keyring(&dest_keyring); -	ret = construct_alloc_key(type, description, dest_keyring, flags, user, -				  &key); +	ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);  	key_user_put(user);  	if (ret == 0) { @@ -529,17 +527,24 @@ struct key *request_key_and_link(struct key_type *type,  				 struct key *dest_keyring,  				 unsigned long flags)  { -	const struct cred *cred = current_cred(); +	struct keyring_search_context ctx = { +		.index_key.type		= type, +		.index_key.description	= description, +		.cred			= current_cred(), +		.match			= type->match, +		.match_data		= description, +		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT, +	};  	struct key *key;  	key_ref_t key_ref;  	int ret;  	kenter("%s,%s,%p,%zu,%p,%p,%lx", -	       type->name, description, callout_info, callout_len, aux, -	       dest_keyring, flags); +	       ctx.index_key.type->name, ctx.index_key.description, +	       callout_info, callout_len, aux, dest_keyring, flags);  	/* search all the process keyrings for a key */ -	key_ref = search_process_keyrings(type, description, type->match, cred); +	key_ref = search_process_keyrings(&ctx);  	if (!IS_ERR(key_ref)) {  		key = key_ref_to_ptr(key_ref); @@ -562,9 +567,8 @@ struct key *request_key_and_link(struct key_type *type,  		if (!callout_info)  			goto error; -		key = construct_key_and_link(type, description, callout_info, -					     callout_len, aux, dest_keyring, -					     flags); +		key = construct_key_and_link(&ctx, callout_info, callout_len, +					     aux, dest_keyring, flags);  	}  error: @@ -592,8 +596,10 @@ int wait_for_key_construction(struct key *key, bool intr)  			  intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);  	if (ret < 0)  		return ret; -	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) +	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { +		smp_rmb();  		return key->type_data.reject_error; +	}  	return key_validate(key);  }  EXPORT_SYMBOL(wait_for_key_construction); diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 85730d5a5a5..7495a93b4b9 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -18,6 +18,7 @@  #include <linux/slab.h>  #include <asm/uaccess.h>  #include "internal.h" +#include <keys/user-type.h>  static int request_key_auth_instantiate(struct key *,  					struct key_preparsed_payload *); @@ -222,32 +223,26 @@ error_alloc:  }  /* - * See if an authorisation key is associated with a particular key. - */ -static int key_get_instantiation_authkey_match(const struct key *key, -					       const void *_id) -{ -	struct request_key_auth *rka = key->payload.data; -	key_serial_t id = (key_serial_t)(unsigned long) _id; - -	return rka->target_key->serial == id; -} - -/*   * Search the current process's keyrings for the authorisation key for   * instantiation of a key.   */  struct key *key_get_instantiation_authkey(key_serial_t target_id)  { -	const struct cred *cred = current_cred(); +	char description[16]; +	struct keyring_search_context ctx = { +		.index_key.type		= &key_type_request_key_auth, +		.index_key.description	= description, +		.cred			= current_cred(), +		.match			= user_match, +		.match_data		= description, +		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT, +	};  	struct key *authkey;  	key_ref_t authkey_ref; -	authkey_ref = search_process_keyrings( -		&key_type_request_key_auth, -		(void *) (unsigned long) target_id, -		key_get_instantiation_authkey_match, -		cred); +	sprintf(description, "%x", target_id); + +	authkey_ref = search_process_keyrings(&ctx);  	if (IS_ERR(authkey_ref)) {  		authkey = ERR_CAST(authkey_ref); diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index ee32d181764..b68faa1a5cf 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -15,7 +15,7 @@  static const int zero, one = 1, max = INT_MAX; -ctl_table key_sysctls[] = { +struct ctl_table key_sysctls[] = {  	{  		.procname = "maxkeys",  		.data = &key_quota_maxkeys, @@ -61,5 +61,16 @@ ctl_table key_sysctls[] = {  		.extra1 = (void *) &zero,  		.extra2 = (void *) &max,  	}, +#ifdef CONFIG_PERSISTENT_KEYRINGS +	{ +		.procname = "persistent_keyring_expiry", +		.data = &persistent_keyring_expiry, +		.maxlen = sizeof(unsigned), +		.mode = 0644, +		.proc_handler = proc_dointvec_minmax, +		.extra1 = (void *) &zero, +		.extra2 = (void *) &max, +	}, +#endif  	{ }  }; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index e13fcf7636f..6b804aa4529 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -753,7 +753,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,  				return -EINVAL;  			break;  		case Opt_keyhandle: -			res = strict_strtoul(args[0].from, 16, &handle); +			res = kstrtoul(args[0].from, 16, &handle);  			if (res < 0)  				return -EINVAL;  			opt->keytype = SEAL_keytype; @@ -782,7 +782,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,  				return -EINVAL;  			break;  		case Opt_pcrlock: -			res = strict_strtoul(args[0].from, 10, &lock); +			res = kstrtoul(args[0].from, 10, &lock);  			if (res < 0)  				return -EINVAL;  			opt->pcrlock = lock; @@ -820,7 +820,7 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p,  		c = strsep(&datablob, " \t");  		if (!c)  			return -EINVAL; -		ret = strict_strtol(c, 10, &keylen); +		ret = kstrtol(c, 10, &keylen);  		if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)  			return -EINVAL;  		p->key_len = keylen; diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 55dc8893918..faa2caeb593 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -25,14 +25,15 @@ static int logon_vet_description(const char *desc);   * arbitrary blob of data as the payload   */  struct key_type key_type_user = { -	.name		= "user", -	.instantiate	= user_instantiate, -	.update		= user_update, -	.match		= user_match, -	.revoke		= user_revoke, -	.destroy	= user_destroy, -	.describe	= user_describe, -	.read		= user_read, +	.name			= "user", +	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT, +	.instantiate		= user_instantiate, +	.update			= user_update, +	.match			= user_match, +	.revoke			= user_revoke, +	.destroy		= user_destroy, +	.describe		= user_describe, +	.read			= user_read,  };  EXPORT_SYMBOL_GPL(key_type_user); @@ -45,6 +46,7 @@ EXPORT_SYMBOL_GPL(key_type_user);   */  struct key_type key_type_logon = {  	.name			= "logon", +	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT,  	.instantiate		= user_instantiate,  	.update			= user_update,  	.match			= user_match, diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 8d8d97dbb38..69fdf3bc765 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -220,7 +220,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,  	 */  	BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); -	audit_log_format(ab, " pid=%d comm=", tsk->pid); +	audit_log_format(ab, " pid=%d comm=", task_pid_nr(tsk));  	audit_log_untrustedstring(ab, tsk->comm);  	switch (a->type) { @@ -278,9 +278,12 @@ static void dump_common_audit_data(struct audit_buffer *ab,  	}  	case LSM_AUDIT_DATA_TASK:  		tsk = a->u.tsk; -		if (tsk && tsk->pid) { -			audit_log_format(ab, " pid=%d comm=", tsk->pid); -			audit_log_untrustedstring(ab, tsk->comm); +		if (tsk) { +			pid_t pid = task_pid_nr(tsk); +			if (pid) { +				audit_log_format(ab, " pid=%d comm=", pid); +				audit_log_untrustedstring(ab, tsk->comm); +			}  		}  		break;  	case LSM_AUDIT_DATA_NET: @@ -302,18 +305,19 @@ static void dump_common_audit_data(struct audit_buffer *ab,  						"faddr", "fport");  				break;  			} +#if IS_ENABLED(CONFIG_IPV6)  			case AF_INET6: {  				struct inet_sock *inet = inet_sk(sk); -				struct ipv6_pinfo *inet6 = inet6_sk(sk); -				print_ipv6_addr(ab, &inet6->rcv_saddr, +				print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr,  						inet->inet_sport,  						"laddr", "lport"); -				print_ipv6_addr(ab, &inet6->daddr, +				print_ipv6_addr(ab, &sk->sk_v6_daddr,  						inet->inet_dport,  						"faddr", "fport");  				break;  			} +#endif  			case AF_UNIX:  				u = unix_sk(sk);  				if (u->path.dentry) { @@ -396,7 +400,8 @@ void common_lsm_audit(struct common_audit_data *a,  	if (a == NULL)  		return;  	/* we use GFP_ATOMIC so we won't sleep */ -	ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC); +	ab = audit_log_start(current->audit_context, GFP_ATOMIC | __GFP_NOWARN, +			     AUDIT_AVC);  	if (ab == NULL)  		return; diff --git a/security/security.c b/security/security.c index 4dc31f4f270..31614e9e96e 100644 --- a/security/security.c +++ b/security/security.c @@ -433,11 +433,20 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir,  }  int security_path_rename(struct path *old_dir, struct dentry *old_dentry, -			 struct path *new_dir, struct dentry *new_dentry) +			 struct path *new_dir, struct dentry *new_dentry, +			 unsigned int flags)  {  	if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||  		     (new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))  		return 0; + +	if (flags & RENAME_EXCHANGE) { +		int err = security_ops->path_rename(new_dir, new_dentry, +						    old_dir, old_dentry); +		if (err) +			return err; +	} +  	return security_ops->path_rename(old_dir, old_dentry, new_dir,  					 new_dentry);  } @@ -524,11 +533,20 @@ int security_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,  }  int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, -			   struct inode *new_dir, struct dentry *new_dentry) +			   struct inode *new_dir, struct dentry *new_dentry, +			   unsigned int flags)  {          if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||              (new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))  		return 0; + +	if (flags & RENAME_EXCHANGE) { +		int err = security_ops->inode_rename(new_dir, new_dentry, +						     old_dir, old_dentry); +		if (err) +			return err; +	} +  	return security_ops->inode_rename(old_dir, old_dentry,  					   new_dir, new_dentry);  } @@ -1317,9 +1335,11 @@ void security_skb_owned_by(struct sk_buff *skb, struct sock *sk)  #ifdef CONFIG_SECURITY_NETWORK_XFRM -int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *sec_ctx) +int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, +			       struct xfrm_user_sec_ctx *sec_ctx, +			       gfp_t gfp)  { -	return security_ops->xfrm_policy_alloc_security(ctxp, sec_ctx); +	return security_ops->xfrm_policy_alloc_security(ctxp, sec_ctx, gfp);  }  EXPORT_SYMBOL(security_xfrm_policy_alloc); @@ -1340,22 +1360,17 @@ int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)  	return security_ops->xfrm_policy_delete_security(ctx);  } -int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx) +int security_xfrm_state_alloc(struct xfrm_state *x, +			      struct xfrm_user_sec_ctx *sec_ctx)  { -	return security_ops->xfrm_state_alloc_security(x, sec_ctx, 0); +	return security_ops->xfrm_state_alloc(x, sec_ctx);  }  EXPORT_SYMBOL(security_xfrm_state_alloc);  int security_xfrm_state_alloc_acquire(struct xfrm_state *x,  				      struct xfrm_sec_ctx *polsec, u32 secid)  { -	if (!polsec) -		return 0; -	/* -	 * We want the context to be taken from secid which is usually -	 * from the sock. -	 */ -	return security_ops->xfrm_state_alloc_security(x, NULL, secid); +	return security_ops->xfrm_state_alloc_acquire(x, polsec, secid);  }  int security_xfrm_state_delete(struct xfrm_state *x) @@ -1410,7 +1425,7 @@ void security_key_free(struct key *key)  }  int security_key_permission(key_ref_t key_ref, -			    const struct cred *cred, key_perm_t perm) +			    const struct cred *cred, unsigned perm)  {  	return security_ops->key_permission(key_ref, cred, perm);  } diff --git a/security/selinux/avc.c b/security/selinux/avc.c index dad36a6ab45..a18f1fa6440 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -444,11 +444,15 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)  	avc_dump_query(ab, ad->selinux_audit_data->ssid,  			   ad->selinux_audit_data->tsid,  			   ad->selinux_audit_data->tclass); +	if (ad->selinux_audit_data->denied) { +		audit_log_format(ab, " permissive=%u", +				 ad->selinux_audit_data->result ? 0 : 1); +	}  }  /* This is the slow part of avc audit with big stack footprint */  noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, -		u32 requested, u32 audited, u32 denied, +		u32 requested, u32 audited, u32 denied, int result,  		struct common_audit_data *a,  		unsigned flags)  { @@ -477,6 +481,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,  	sad.tsid = tsid;  	sad.audited = audited;  	sad.denied = denied; +	sad.result = result;  	a->selinux_audit_data = &sad; @@ -746,7 +751,6 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,   * @tclass: target security class   * @requested: requested permissions, interpreted based on @tclass   * @auditdata: auxiliary audit data - * @flags: VFS walk flags   *   * Check the AVC to determine whether the @requested permissions are granted   * for the SID pair (@ssid, @tsid), interpreting the permissions @@ -756,17 +760,15 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,   * permissions are granted, -%EACCES if any permissions are denied, or   * another -errno upon other errors.   */ -int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass, -		       u32 requested, struct common_audit_data *auditdata, -		       unsigned flags) +int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, +		 u32 requested, struct common_audit_data *auditdata)  {  	struct av_decision avd;  	int rc, rc2;  	rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); -	rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, -			flags); +	rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);  	if (rc2)  		return rc2;  	return rc; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a5091ec06aa..83d06db34d0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -53,6 +53,7 @@  #include <net/ip.h>		/* for local_port_range[] */  #include <net/sock.h>  #include <net/tcp.h>		/* struct or_callable used in sock_rcv_skb */ +#include <net/inet_connection_sock.h>  #include <net/net_namespace.h>  #include <net/netlabel.h>  #include <linux/uaccess.h> @@ -81,7 +82,6 @@  #include <linux/syslog.h>  #include <linux/user_namespace.h>  #include <linux/export.h> -#include <linux/security.h>  #include <linux/msg.h>  #include <linux/shm.h> @@ -95,8 +95,6 @@  #include "audit.h"  #include "avc_ss.h" -#define NUM_SEL_MNT_OPTS 5 -  extern struct security_operations *security_ops;  /* SECMARK reference count */ @@ -108,7 +106,7 @@ int selinux_enforcing;  static int __init enforcing_setup(char *str)  {  	unsigned long enforcing; -	if (!strict_strtoul(str, 0, &enforcing)) +	if (!kstrtoul(str, 0, &enforcing))  		selinux_enforcing = enforcing ? 1 : 0;  	return 1;  } @@ -121,7 +119,7 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;  static int __init selinux_enabled_setup(char *str)  {  	unsigned long enabled; -	if (!strict_strtoul(str, 0, &enabled)) +	if (!kstrtoul(str, 0, &enabled))  		selinux_enabled = enabled ? 1 : 0;  	return 1;  } @@ -139,12 +137,28 @@ static struct kmem_cache *sel_inode_cache;   * This function checks the SECMARK reference counter to see if any SECMARK   * targets are currently configured, if the reference counter is greater than   * zero SECMARK is considered to be enabled.  Returns true (1) if SECMARK is - * enabled, false (0) if SECMARK is disabled. + * enabled, false (0) if SECMARK is disabled.  If the always_check_network + * policy capability is enabled, SECMARK is always considered enabled.   *   */  static int selinux_secmark_enabled(void)  { -	return (atomic_read(&selinux_secmark_refcount) > 0); +	return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount)); +} + +/** + * selinux_peerlbl_enabled - Check to see if peer labeling is currently enabled + * + * Description: + * This function checks if NetLabel or labeled IPSEC is enabled.  Returns true + * (1) if any are enabled or false (0) if neither are enabled.  If the + * always_check_network policy capability is enabled, peer labeling + * is always considered enabled. + * + */ +static int selinux_peerlbl_enabled(void) +{ +	return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled());  }  /* @@ -219,6 +233,14 @@ static int inode_alloc_security(struct inode *inode)  	return 0;  } +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 = inode->i_security; @@ -229,8 +251,16 @@ static void inode_free_security(struct inode *inode)  		list_del_init(&isec->list);  	spin_unlock(&sbsec->isec_lock); -	inode->i_security = NULL; -	kmem_cache_free(sel_inode_cache, isec); +	/* +	 * 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) @@ -309,8 +339,11 @@ enum {  	Opt_defcontext = 3,  	Opt_rootcontext = 4,  	Opt_labelsupport = 5, +	Opt_nextmntopt = 6,  }; +#define NUM_SEL_MNT_OPTS	(Opt_nextmntopt - 1) +  static const match_table_t tokens = {  	{Opt_context, CONTEXT_STR "%s"},  	{Opt_fscontext, FSCONTEXT_STR "%s"}, @@ -355,6 +388,29 @@ static int may_context_mount_inode_relabel(u32 sid,  	return rc;  } +static int selinux_is_sblabel_mnt(struct super_block *sb) +{ +	struct superblock_security_struct *sbsec = sb->s_security; + +	if (sbsec->behavior == SECURITY_FS_USE_XATTR || +	    sbsec->behavior == SECURITY_FS_USE_TRANS || +	    sbsec->behavior == SECURITY_FS_USE_TASK) +		return 1; + +	/* Special handling for sysfs. Is genfs but also has setxattr handler*/ +	if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) +		return 1; + +	/* +	 * Special handling for rootfs. Is genfs but supports +	 * setting SELinux context on in-core inodes. +	 */ +	if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) +		return 1; + +	return 0; +} +  static int sb_finish_set_opts(struct super_block *sb)  {  	struct superblock_security_struct *sbsec = sb->s_security; @@ -388,8 +444,6 @@ static int sb_finish_set_opts(struct super_block *sb)  		}  	} -	sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP); -  	if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))  		printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",  		       sb->s_id, sb->s_type->name); @@ -398,15 +452,9 @@ static int sb_finish_set_opts(struct super_block *sb)  		       sb->s_id, sb->s_type->name,  		       labeling_behaviors[sbsec->behavior-1]); -	if (sbsec->behavior == SECURITY_FS_USE_GENFS || -	    sbsec->behavior == SECURITY_FS_USE_MNTPOINT || -	    sbsec->behavior == SECURITY_FS_USE_NONE || -	    sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) -		sbsec->flags &= ~SE_SBLABELSUPP; - -	/* Special handling for sysfs. Is genfs but also has setxattr handler*/ -	if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) -		sbsec->flags |= SE_SBLABELSUPP; +	sbsec->flags |= SE_SBINITIALIZED; +	if (selinux_is_sblabel_mnt(sb)) +		sbsec->flags |= SBLABEL_MNT;  	/* Initialize the root inode. */  	rc = inode_doinit_with_dentry(root_inode, root); @@ -460,15 +508,18 @@ static int selinux_get_mnt_opts(const struct super_block *sb,  	if (!ss_initialized)  		return -EINVAL; +	/* make sure we always check enough bits to cover the mask */ +	BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS)); +  	tmp = sbsec->flags & SE_MNTMASK;  	/* count the number of mount options for this sb */ -	for (i = 0; i < 8; i++) { +	for (i = 0; i < NUM_SEL_MNT_OPTS; i++) {  		if (tmp & 0x01)  			opts->num_mnt_opts++;  		tmp >>= 1;  	}  	/* Check if the Label support flag is set */ -	if (sbsec->flags & SE_SBLABELSUPP) +	if (sbsec->flags & SBLABEL_MNT)  		opts->num_mnt_opts++;  	opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC); @@ -515,9 +566,9 @@ static int selinux_get_mnt_opts(const struct super_block *sb,  		opts->mnt_opts[i] = context;  		opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;  	} -	if (sbsec->flags & SE_SBLABELSUPP) { +	if (sbsec->flags & SBLABEL_MNT) {  		opts->mnt_opts[i] = NULL; -		opts->mnt_opts_flags[i++] = SE_SBLABELSUPP; +		opts->mnt_opts_flags[i++] = SBLABEL_MNT;  	}  	BUG_ON(i != opts->num_mnt_opts); @@ -614,10 +665,10 @@ static int selinux_set_mnt_opts(struct super_block *sb,  	for (i = 0; i < num_opts; i++) {  		u32 sid; -		if (flags[i] == SE_SBLABELSUPP) +		if (flags[i] == SBLABEL_MNT)  			continue;  		rc = security_context_to_sid(mount_options[i], -					     strlen(mount_options[i]), &sid); +					     strlen(mount_options[i]), &sid, GFP_KERNEL);  		if (rc) {  			printk(KERN_WARNING "SELinux: security_context_to_sid"  			       "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -685,9 +736,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,  		 * Determine the labeling behavior to use for this  		 * filesystem type.  		 */ -		rc = security_fs_use((sbsec->flags & SE_SBPROC) ? -					"proc" : sb->s_type->name, -					&sbsec->behavior, &sbsec->sid); +		rc = security_fs_use(sb);  		if (rc) {  			printk(KERN_WARNING  				"%s: security_fs_use(%s) returned %d\n", @@ -1037,7 +1086,7 @@ static void selinux_write_opts(struct seq_file *m,  		case DEFCONTEXT_MNT:  			prefix = DEFCONTEXT_STR;  			break; -		case SE_SBLABELSUPP: +		case SBLABEL_MNT:  			seq_putc(m, ',');  			seq_puts(m, LABELSUPP_STR);  			continue; @@ -1369,15 +1418,33 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent  		isec->sid = sbsec->sid;  		if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { -			if (opt_dentry) { -				isec->sclass = inode_mode_to_security_class(inode->i_mode); -				rc = selinux_proc_get_sid(opt_dentry, -							  isec->sclass, -							  &sid); -				if (rc) -					goto out_unlock; -				isec->sid = sid; -			} +			/* We must have a dentry to determine the label on +			 * procfs inodes */ +			if (opt_dentry) +				/* Called from d_instantiate or +				 * d_splice_alias. */ +				dentry = dget(opt_dentry); +			else +				/* Called from selinux_complete_init, try to +				 * find a dentry. */ +				dentry = d_find_alias(inode); +			/* +			 * This can be hit on boot when a file is accessed +			 * before the policy is loaded.  When we load policy we +			 * may find inodes that have no dentry on the +			 * sbsec->isec_head list.  No reason to complain as +			 * these will get fixed up the next time we go through +			 * inode_doinit() with a dentry, before these inodes +			 * could be used again by userspace. +			 */ +			if (!dentry) +				goto out_unlock; +			isec->sclass = inode_mode_to_security_class(inode->i_mode); +			rc = selinux_proc_get_sid(dentry, isec->sclass, &sid); +			dput(dentry); +			if (rc) +				goto out_unlock; +			isec->sid = sid;  		}  		break;  	} @@ -1502,7 +1569,7 @@ static int cred_has_capability(const struct cred *cred,  	rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);  	if (audit == SECURITY_CAP_AUDIT) { -		int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0); +		int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad);  		if (rc2)  			return rc2;  	} @@ -1525,8 +1592,7 @@ static int task_has_system(struct task_struct *tsk,  static int inode_has_perm(const struct cred *cred,  			  struct inode *inode,  			  u32 perms, -			  struct common_audit_data *adp, -			  unsigned flags) +			  struct common_audit_data *adp)  {  	struct inode_security_struct *isec;  	u32 sid; @@ -1539,7 +1605,7 @@ static int inode_has_perm(const struct cred *cred,  	sid = cred_sid(cred);  	isec = inode->i_security; -	return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags); +	return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);  }  /* Same as inode_has_perm, but pass explicit audit data containing @@ -1554,7 +1620,7 @@ static inline int dentry_has_perm(const struct cred *cred,  	ad.type = LSM_AUDIT_DATA_DENTRY;  	ad.u.dentry = dentry; -	return inode_has_perm(cred, inode, av, &ad, 0); +	return inode_has_perm(cred, inode, av, &ad);  }  /* Same as inode_has_perm, but pass explicit audit data containing @@ -1569,7 +1635,7 @@ static inline int path_has_perm(const struct cred *cred,  	ad.type = LSM_AUDIT_DATA_PATH;  	ad.u.path = *path; -	return inode_has_perm(cred, inode, av, &ad, 0); +	return inode_has_perm(cred, inode, av, &ad);  }  /* Same as path_has_perm, but uses the inode from the file struct. */ @@ -1581,7 +1647,7 @@ static inline int file_path_has_perm(const struct cred *cred,  	ad.type = LSM_AUDIT_DATA_PATH;  	ad.u.path = file->f_path; -	return inode_has_perm(cred, file_inode(file), av, &ad, 0); +	return inode_has_perm(cred, file_inode(file), av, &ad);  }  /* Check whether a task can use an open file descriptor to @@ -1617,7 +1683,7 @@ static int file_has_perm(const struct cred *cred,  	/* av is zero if only checking access to the descriptor. */  	rc = 0;  	if (av) -		rc = inode_has_perm(cred, inode, av, &ad, 0); +		rc = inode_has_perm(cred, inode, av, &ad);  out:  	return rc; @@ -1650,7 +1716,7 @@ static int may_create(struct inode *dir,  	if (rc)  		return rc; -	if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) { +	if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {  		rc = security_transition_sid(sid, dsec->sid, tclass,  					     &dentry->d_name, &newsid);  		if (rc) @@ -2057,11 +2123,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)  		new_tsec->exec_sid = 0;  		/* -		 * Minimize confusion: if no_new_privs and a transition is -		 * explicitly requested, then fail the exec. +		 * Minimize confusion: if no_new_privs or nosuid and a +		 * transition is explicitly requested, then fail the exec.  		 */  		if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)  			return -EPERM; +		if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) +			return -EACCES;  	} else {  		/* Check for a default transition on this program. */  		rc = security_transition_sid(old_tsec->sid, isec->sid, @@ -2438,10 +2506,11 @@ static int selinux_sb_remount(struct super_block *sb, void *data)  		u32 sid;  		size_t len; -		if (flags[i] == SE_SBLABELSUPP) +		if (flags[i] == SBLABEL_MNT)  			continue;  		len = strlen(mount_options[i]); -		rc = security_context_to_sid(mount_options[i], len, &sid); +		rc = security_context_to_sid(mount_options[i], len, &sid, +					     GFP_KERNEL);  		if (rc) {  			printk(KERN_WARNING "SELinux: security_context_to_sid"  			       "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -2607,7 +2676,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,  	if ((sbsec->flags & SE_SBINITIALIZED) &&  	    (sbsec->behavior == SECURITY_FS_USE_MNTPOINT))  		newsid = sbsec->mntpoint_sid; -	else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) { +	else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {  		rc = security_transition_sid(sid, dsec->sid,  					     inode_mode_to_security_class(inode->i_mode),  					     qstr, &newsid); @@ -2629,7 +2698,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,  		isec->initialized = 1;  	} -	if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) +	if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))  		return -EOPNOTSUPP;  	if (name) @@ -2703,6 +2772,7 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na  static noinline int audit_inode_permission(struct inode *inode,  					   u32 perms, u32 audited, u32 denied, +					   int result,  					   unsigned flags)  {  	struct common_audit_data ad; @@ -2713,7 +2783,7 @@ static noinline int audit_inode_permission(struct inode *inode,  	ad.u.inode = inode;  	rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, -			    audited, denied, &ad, flags); +			    audited, denied, result, &ad, flags);  	if (rc)  		return rc;  	return 0; @@ -2755,7 +2825,7 @@ static int selinux_inode_permission(struct inode *inode, int mask)  	if (likely(!audited))  		return rc; -	rc2 = audit_inode_permission(inode, perms, audited, denied, flags); +	rc2 = audit_inode_permission(inode, perms, audited, denied, rc, flags);  	if (rc2)  		return rc2;  	return rc; @@ -2831,7 +2901,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  		return selinux_inode_setotherxattr(dentry, name);  	sbsec = inode->i_sb->s_security; -	if (!(sbsec->flags & SE_SBLABELSUPP)) +	if (!(sbsec->flags & SBLABEL_MNT))  		return -EOPNOTSUPP;  	if (!inode_owner_or_capable(inode)) @@ -2845,7 +2915,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  	if (rc)  		return rc; -	rc = security_context_to_sid(value, size, &newsid); +	rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL);  	if (rc == -EINVAL) {  		if (!capable(CAP_MAC_ADMIN)) {  			struct audit_buffer *ab; @@ -3002,7 +3072,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,  	if (!value || !size)  		return -EACCES; -	rc = security_context_to_sid((void *)value, size, &newsid); +	rc = security_context_to_sid((void *)value, size, &newsid, GFP_KERNEL);  	if (rc)  		return rc; @@ -3156,24 +3226,20 @@ error:  static int selinux_mmap_addr(unsigned long addr)  { -	int rc = 0; -	u32 sid = current_sid(); +	int rc; + +	/* do DAC check on address space usage */ +	rc = cap_mmap_addr(addr); +	if (rc) +		return rc; -	/* -	 * notice that we are intentionally putting the SELinux check before -	 * the secondary cap_file_mmap check.  This is such a likely attempt -	 * at bad behaviour/exploit that we always want to get the AVC, even -	 * if DAC would have also denied the operation. -	 */  	if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { +		u32 sid = current_sid();  		rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,  				  MEMPROTECT__MMAP_ZERO, NULL); -		if (rc) -			return rc;  	} -	/* do DAC check on address space usage */ -	return cap_mmap_addr(addr); +	return rc;  }  static int selinux_mmap_file(struct file *file, unsigned long reqprot, @@ -3254,6 +3320,9 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  	case F_GETLK:  	case F_SETLK:  	case F_SETLKW: +	case F_OFD_GETLK: +	case F_OFD_SETLK: +	case F_OFD_SETLKW:  #if BITS_PER_LONG == 32  	case F_GETLK64:  	case F_SETLK64: @@ -3792,8 +3861,12 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)  	u32 nlbl_sid;  	u32 nlbl_type; -	selinux_skb_xfrm_sid(skb, &xfrm_sid); -	selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); +	err = selinux_xfrm_skb_sid(skb, &xfrm_sid); +	if (unlikely(err)) +		return -EACCES; +	err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); +	if (unlikely(err)) +		return -EACCES;  	err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);  	if (unlikely(err)) { @@ -3806,6 +3879,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)  	return 0;  } +/** + * selinux_conn_sid - Determine the child socket label for a connection + * @sk_sid: the parent socket's SID + * @skb_sid: the packet's SID + * @conn_sid: the resulting connection SID + * + * If @skb_sid is valid then the user:role:type information from @sk_sid is + * combined with the MLS information from @skb_sid in order to create + * @conn_sid.  If @skb_sid is not valid then then @conn_sid is simply a copy + * of @sk_sid.  Returns zero on success, negative values on failure. + * + */ +static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) +{ +	int err = 0; + +	if (skb_sid != SECSID_NULL) +		err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); +	else +		*conn_sid = sk_sid; + +	return err; +} +  /* socket security operations */  static int socket_sockcreate_sid(const struct task_security_struct *tsec, @@ -3929,7 +4026,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in  		if (snum) {  			int low, high; -			inet_get_local_port_range(&low, &high); +			inet_get_local_port_range(sock_net(sk), &low, &high);  			if (snum < max(PROT_SOCK, low) || snum > high) {  				err = sel_netport_sid(sk->sk_protocol, @@ -4247,7 +4344,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  		return selinux_sock_rcv_skb_compat(sk, skb, family);  	secmark_active = selinux_secmark_enabled(); -	peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); +	peerlbl_active = selinux_peerlbl_enabled();  	if (!secmark_active && !peerlbl_active)  		return 0; @@ -4273,8 +4370,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  		}  		err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,  				   PEER__RECV, &ad); -		if (err) +		if (err) {  			selinux_netlbl_err(skb, err, 0); +			return err; +		}  	}  	if (secmark_active) { @@ -4411,27 +4510,18 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,  {  	struct sk_security_struct *sksec = sk->sk_security;  	int err; -	u16 family = sk->sk_family; -	u32 newsid; +	u16 family = req->rsk_ops->family; +	u32 connsid;  	u32 peersid; -	/* handle mapped IPv4 packets arriving via IPv6 sockets */ -	if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) -		family = PF_INET; -  	err = selinux_skb_peerlbl_sid(skb, family, &peersid);  	if (err)  		return err; -	if (peersid == SECSID_NULL) { -		req->secid = sksec->sid; -		req->peer_secid = SECSID_NULL; -	} else { -		err = security_sid_mls_copy(sksec->sid, peersid, &newsid); -		if (err) -			return err; -		req->secid = newsid; -		req->peer_secid = peersid; -	} +	err = selinux_conn_sid(sksec->sid, peersid, &connsid); +	if (err) +		return err; +	req->secid = connsid; +	req->peer_secid = peersid;  	return selinux_netlbl_inet_conn_request(req, family);  } @@ -4629,7 +4719,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,  	secmark_active = selinux_secmark_enabled();  	netlbl_active = netlbl_enabled(); -	peerlbl_active = netlbl_active || selinux_xfrm_enabled(); +	peerlbl_active = selinux_peerlbl_enabled();  	if (!secmark_active && !peerlbl_active)  		return NF_ACCEPT; @@ -4668,7 +4758,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,  	return NF_ACCEPT;  } -static unsigned int selinux_ipv4_forward(unsigned int hooknum, +static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,  					 struct sk_buff *skb,  					 const struct net_device *in,  					 const struct net_device *out, @@ -4678,7 +4768,7 @@ static unsigned int selinux_ipv4_forward(unsigned int hooknum,  }  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_forward(unsigned int hooknum, +static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,  					 struct sk_buff *skb,  					 const struct net_device *in,  					 const struct net_device *out, @@ -4691,6 +4781,7 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,  static unsigned int selinux_ip_output(struct sk_buff *skb,  				      u16 family)  { +	struct sock *sk;  	u32 sid;  	if (!netlbl_enabled()) @@ -4699,8 +4790,27 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,  	/* we do this in the LOCAL_OUT path and not the POST_ROUTING path  	 * because we want to make sure we apply the necessary labeling  	 * before IPsec is applied so we can leverage AH protection */ -	if (skb->sk) { -		struct sk_security_struct *sksec = skb->sk->sk_security; +	sk = skb->sk; +	if (sk) { +		struct sk_security_struct *sksec; + +		if (sk->sk_state == TCP_LISTEN) +			/* if the socket is the listening state then this +			 * packet is a SYN-ACK packet which means it needs to +			 * be labeled based on the connection/request_sock and +			 * not the parent socket.  unfortunately, we can't +			 * lookup the request_sock yet as it isn't queued on +			 * the parent socket until after the SYN-ACK is sent. +			 * the "solution" is to simply pass the packet as-is +			 * as any IP option based labeling should be copied +			 * from the initial connection request (in the IP +			 * layer).  it is far from ideal, but until we get a +			 * security label in the packet itself this is the +			 * best we can do. */ +			return NF_ACCEPT; + +		/* standard practice, label using the parent socket */ +		sksec = sk->sk_security;  		sid = sksec->sid;  	} else  		sid = SECINITSID_KERNEL; @@ -4710,7 +4820,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,  	return NF_ACCEPT;  } -static unsigned int selinux_ipv4_output(unsigned int hooknum, +static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,  					struct sk_buff *skb,  					const struct net_device *in,  					const struct net_device *out, @@ -4770,27 +4880,36 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	 * as fast and as clean as possible. */  	if (!selinux_policycap_netpeer)  		return selinux_ip_postroute_compat(skb, ifindex, family); + +	secmark_active = selinux_secmark_enabled(); +	peerlbl_active = selinux_peerlbl_enabled(); +	if (!secmark_active && !peerlbl_active) +		return NF_ACCEPT; + +	sk = skb->sk; +  #ifdef CONFIG_XFRM  	/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec  	 * packet transformation so allow the packet to pass without any checks  	 * since we'll have another chance to perform access control checks  	 * when the packet is on it's final way out.  	 * NOTE: there appear to be some IPv6 multicast cases where skb->dst -	 *       is NULL, in this case go ahead and apply access control. */ -	if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL) +	 *       is NULL, in this case go ahead and apply access control. +	 * NOTE: if this is a local socket (skb->sk != NULL) that is in the +	 *       TCP listening state we cannot wait until the XFRM processing +	 *       is done as we will miss out on the SA label if we do; +	 *       unfortunately, this means more work, but it is only once per +	 *       connection. */ +	if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && +	    !(sk != NULL && sk->sk_state == TCP_LISTEN))  		return NF_ACCEPT;  #endif -	secmark_active = selinux_secmark_enabled(); -	peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); -	if (!secmark_active && !peerlbl_active) -		return NF_ACCEPT; -	/* if the packet is being forwarded then get the peer label from the -	 * packet itself; otherwise check to see if it is from a local -	 * application or the kernel, if from an application get the peer label -	 * from the sending socket, otherwise use the kernel's sid */ -	sk = skb->sk;  	if (sk == NULL) { +		/* Without an associated socket the packet is either coming +		 * from the kernel or it is being forwarded; check the packet +		 * to determine which and if the packet is being forwarded +		 * query the packet directly to determine the security label. */  		if (skb->skb_iif) {  			secmark_perm = PACKET__FORWARD_OUT;  			if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) @@ -4799,7 +4918,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  			secmark_perm = PACKET__SEND;  			peer_sid = SECINITSID_KERNEL;  		} +	} else if (sk->sk_state == TCP_LISTEN) { +		/* Locally generated packet but the associated socket is in the +		 * listening state which means this is a SYN-ACK packet.  In +		 * this particular case the correct security label is assigned +		 * to the connection/request_sock but unfortunately we can't +		 * query the request_sock as it isn't queued on the parent +		 * socket until after the SYN-ACK packet is sent; the only +		 * viable choice is to regenerate the label like we do in +		 * selinux_inet_conn_request().  See also selinux_ip_output() +		 * for similar problems. */ +		u32 skb_sid; +		struct sk_security_struct *sksec = sk->sk_security; +		if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) +			return NF_DROP; +		/* At this point, if the returned skb peerlbl is SECSID_NULL +		 * and the packet has been through at least one XFRM +		 * transformation then we must be dealing with the "final" +		 * form of labeled IPsec packet; since we've already applied +		 * all of our access controls on this packet we can safely +		 * pass the packet. */ +		if (skb_sid == SECSID_NULL) { +			switch (family) { +			case PF_INET: +				if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) +					return NF_ACCEPT; +				break; +			case PF_INET6: +				if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) +					return NF_ACCEPT; +			default: +				return NF_DROP_ERR(-ECONNREFUSED); +			} +		} +		if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) +			return NF_DROP; +		secmark_perm = PACKET__SEND;  	} else { +		/* Locally generated packet, fetch the security label from the +		 * associated socket. */  		struct sk_security_struct *sksec = sk->sk_security;  		peer_sid = sksec->sid;  		secmark_perm = PACKET__SEND; @@ -4837,7 +4994,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	return NF_ACCEPT;  } -static unsigned int selinux_ipv4_postroute(unsigned int hooknum, +static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,  					   struct sk_buff *skb,  					   const struct net_device *in,  					   const struct net_device *out, @@ -4847,7 +5004,7 @@ static unsigned int selinux_ipv4_postroute(unsigned int hooknum,  }  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_postroute(unsigned int hooknum, +static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,  					   struct sk_buff *skb,  					   const struct net_device *in,  					   const struct net_device *out, @@ -5393,7 +5550,7 @@ static int selinux_setprocattr(struct task_struct *p,  			str[size-1] = 0;  			size--;  		} -		error = security_context_to_sid(value, size, &sid); +		error = security_context_to_sid(value, size, &sid, GFP_KERNEL);  		if (error == -EINVAL && !strcmp(name, "fscreate")) {  			if (!capable(CAP_MAC_ADMIN)) {  				struct audit_buffer *ab; @@ -5463,11 +5620,11 @@ static int selinux_setprocattr(struct task_struct *p,  		/* Check for ptracing, and update the task SID if ok.  		   Otherwise, leave SID unchanged and fail. */  		ptsid = 0; -		task_lock(p); +		rcu_read_lock();  		tracer = ptrace_parent(p);  		if (tracer)  			ptsid = task_sid(tracer); -		task_unlock(p); +		rcu_read_unlock();  		if (tracer) {  			error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, @@ -5502,7 +5659,7 @@ static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)  static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)  { -	return security_context_to_sid(secdata, seclen, secid); +	return security_context_to_sid(secdata, seclen, secid, GFP_KERNEL);  }  static void selinux_release_secctx(char *secdata, u32 seclen) @@ -5568,7 +5725,7 @@ static void selinux_key_free(struct key *k)  static int selinux_key_permission(key_ref_t key_ref,  				  const struct cred *cred, -				  key_perm_t perm) +				  unsigned perm)  {  	struct key *key;  	struct key_security_struct *ksec; @@ -5785,7 +5942,8 @@ static struct security_operations selinux_ops = {  	.xfrm_policy_clone_security =	selinux_xfrm_policy_clone,  	.xfrm_policy_free_security =	selinux_xfrm_policy_free,  	.xfrm_policy_delete_security =	selinux_xfrm_policy_delete, -	.xfrm_state_alloc_security =	selinux_xfrm_state_alloc, +	.xfrm_state_alloc =		selinux_xfrm_state_alloc, +	.xfrm_state_alloc_acquire =	selinux_xfrm_state_alloc_acquire,  	.xfrm_state_free_security =	selinux_xfrm_state_free,  	.xfrm_state_delete_security =	selinux_xfrm_state_delete,  	.xfrm_policy_lookup =		selinux_xfrm_policy_lookup, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 92d0ab561db..ddf8eec03f2 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -102,7 +102,7 @@ static inline u32 avc_audit_required(u32 requested,  }  int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, -		   u32 requested, u32 audited, u32 denied, +		   u32 requested, u32 audited, u32 denied, int result,  		   struct common_audit_data *a,  		   unsigned flags); @@ -130,15 +130,15 @@ static inline int avc_audit(u32 ssid, u32 tsid,  			    u16 tclass, u32 requested,  			    struct av_decision *avd,  			    int result, -			    struct common_audit_data *a, unsigned flags) +			    struct common_audit_data *a)  {  	u32 audited, denied;  	audited = avc_audit_required(requested, avd, result, 0, &denied);  	if (likely(!audited))  		return 0;  	return slow_avc_audit(ssid, tsid, tclass, -			      requested, audited, denied, -			      a, flags); +			      requested, audited, denied, result, +			      a, 0);  }  #define AVC_STRICT 1 /* Ignore permissive mode. */ @@ -147,17 +147,9 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,  			 unsigned flags,  			 struct av_decision *avd); -int avc_has_perm_flags(u32 ssid, u32 tsid, -		       u16 tclass, u32 requested, -		       struct common_audit_data *auditdata, -		       unsigned); - -static inline int avc_has_perm(u32 ssid, u32 tsid, -			       u16 tclass, u32 requested, -			       struct common_audit_data *auditdata) -{ -	return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0); -} +int avc_has_perm(u32 ssid, u32 tsid, +		 u16 tclass, u32 requested, +		 struct common_audit_data *auditdata);  u32 avc_policy_seqno(void); diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 14d04e63b1f..be491a74c1e 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -147,7 +147,7 @@ struct security_class_mapping secclass_map[] = {  	{ "peer", { "recv", NULL } },  	{ "capability2",  	  { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", -	    NULL } }, +	    "audit_read", NULL } },  	{ "kernel_service", { "use_as_override", "create_files_as", NULL } },  	{ "tun_socket",  	  { COMMON_SOCK_PERMS, "attach_queue", NULL } }, diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index aa47bcabb5f..078e553f52f 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -38,7 +38,10 @@ struct task_security_struct {  struct inode_security_struct {  	struct inode *inode;	/* back pointer to inode object */ -	struct list_head list;	/* list of inode_security_struct */ +	union { +		struct list_head list;	/* list of inode_security_struct */ +		struct rcu_head rcu;	/* for freeing the inode_security_struct */ +	};  	u32 task_sid;		/* SID of creating task */  	u32 sid;		/* SID of this object */  	u16 sclass;		/* security class of this object */ @@ -58,8 +61,8 @@ struct superblock_security_struct {  	u32 sid;			/* SID of file system superblock */  	u32 def_sid;			/* default SID for labeling */  	u32 mntpoint_sid;		/* SECURITY_FS_USE_MNTPOINT context for files */ -	unsigned int behavior;		/* labeling behavior */ -	unsigned char flags;		/* which mount options were specified */ +	unsigned short behavior;	/* labeling behavior */ +	unsigned short flags;		/* which mount options were specified */  	struct mutex lock;  	struct list_head isec_head;  	spinlock_t isec_lock; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 8fd8e18ea34..ce7852cf526 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -33,26 +33,28 @@  #define POLICYDB_VERSION_ROLETRANS	26  #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	27  #define POLICYDB_VERSION_DEFAULT_TYPE	28 +#define POLICYDB_VERSION_CONSTRAINT_NAMES	29  /* Range of policy versions we understand*/  #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE  #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX  #define POLICYDB_VERSION_MAX	CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE  #else -#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_DEFAULT_TYPE +#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_CONSTRAINT_NAMES  #endif  /* Mask for just the mount related flags */  #define SE_MNTMASK	0x0f  /* Super block security struct flags for mount options */ +/* BE CAREFUL, these need to be the low order bits for selinux_get_mnt_opts */  #define CONTEXT_MNT	0x01  #define FSCONTEXT_MNT	0x02  #define ROOTCONTEXT_MNT	0x04  #define DEFCONTEXT_MNT	0x08 +#define SBLABEL_MNT	0x10  /* Non-mount related flags */ -#define SE_SBINITIALIZED	0x10 -#define SE_SBPROC		0x20 -#define SE_SBLABELSUPP	0x40 +#define SE_SBINITIALIZED	0x0100 +#define SE_SBPROC		0x0200  #define CONTEXT_STR	"context="  #define FSCONTEXT_STR	"fscontext=" @@ -68,12 +70,15 @@ extern int selinux_enabled;  enum {  	POLICYDB_CAPABILITY_NETPEER,  	POLICYDB_CAPABILITY_OPENPERM, +	POLICYDB_CAPABILITY_REDHAT1, +	POLICYDB_CAPABILITY_ALWAYSNETWORK,  	__POLICYDB_CAPABILITY_MAX  };  #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)  extern int selinux_policycap_netpeer;  extern int selinux_policycap_openperm; +extern int selinux_policycap_alwaysnetwork;  /*   * type_datum properties @@ -129,7 +134,7 @@ int security_sid_to_context(u32 sid, char **scontext,  int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);  int security_context_to_sid(const char *scontext, u32 scontext_len, -	u32 *out_sid); +			    u32 *out_sid, gfp_t gfp);  int security_context_to_sid_default(const char *scontext, u32 scontext_len,  				    u32 *out_sid, u32 def_sid, gfp_t gfp_flags); @@ -172,8 +177,7 @@ int security_get_allow_unknown(void);  #define SECURITY_FS_USE_NATIVE		7 /* use native label support */  #define SECURITY_FS_USE_MAX		7 /* Highest SECURITY_FS_USE_XXX */ -int security_fs_use(const char *fstype, unsigned int *behavior, -	u32 *sid); +int security_fs_use(struct super_block *sb);  int security_genfs_sid(const char *fstype, char *name, u16 sclass,  	u32 *sid); diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 6713f04e30b..1450f85b946 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -10,29 +10,22 @@  #include <net/flow.h>  int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, -			      struct xfrm_user_sec_ctx *sec_ctx); +			      struct xfrm_user_sec_ctx *uctx, +			      gfp_t gfp);  int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,  			      struct xfrm_sec_ctx **new_ctxp);  void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx);  int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx);  int selinux_xfrm_state_alloc(struct xfrm_state *x, -	struct xfrm_user_sec_ctx *sec_ctx, u32 secid); +			     struct xfrm_user_sec_ctx *uctx); +int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, +				     struct xfrm_sec_ctx *polsec, u32 secid);  void selinux_xfrm_state_free(struct xfrm_state *x);  int selinux_xfrm_state_delete(struct xfrm_state *x);  int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);  int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, -			struct xfrm_policy *xp, const struct flowi *fl); - -/* - * Extract the security blob from the sock (it's actually on the socket) - */ -static inline struct inode_security_struct *get_sock_isec(struct sock *sk) -{ -	if (!sk->sk_socket) -		return NULL; - -	return SOCK_INODE(sk->sk_socket)->i_security; -} +				      struct xfrm_policy *xp, +				      const struct flowi *fl);  #ifdef CONFIG_SECURITY_NETWORK_XFRM  extern atomic_t selinux_xfrm_refcount; @@ -42,20 +35,22 @@ static inline int selinux_xfrm_enabled(void)  	return (atomic_read(&selinux_xfrm_refcount) > 0);  } -int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, -			struct common_audit_data *ad); -int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, -			struct common_audit_data *ad, u8 proto); +int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, +			      struct common_audit_data *ad); +int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, +				struct common_audit_data *ad, u8 proto);  int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid);  static inline void selinux_xfrm_notify_policyload(void)  {  	struct net *net; -	atomic_inc(&flow_cache_genid);  	rtnl_lock(); -	for_each_net(net) +	for_each_net(net) { +		atomic_inc(&net->xfrm.flow_cache_genid);  		rt_genid_bump_all(net); +	}  	rtnl_unlock();  }  #else @@ -64,19 +59,21 @@ static inline int selinux_xfrm_enabled(void)  	return 0;  } -static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, -			struct common_audit_data *ad) +static inline int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, +					    struct common_audit_data *ad)  {  	return 0;  } -static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, -			struct common_audit_data *ad, u8 proto) +static inline int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, +					      struct common_audit_data *ad, +					      u8 proto)  {  	return 0;  } -static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) +static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, +					      int ckall)  {  	*sid = SECSID_NULL;  	return 0; @@ -85,12 +82,12 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int  static inline void selinux_xfrm_notify_policyload(void)  {  } -#endif -static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid) +static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)  { -	int err = selinux_xfrm_decode_session(skb, sid, 0); -	BUG_ON(err); +	*sid = SECSID_NULL; +	return 0;  } +#endif  #endif /* _SELINUX_XFRM_H_ */ diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index da4b8b23328..0364120d1ec 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -101,6 +101,32 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)  }  /** + * selinux_netlbl_sock_getattr - Get the cached NetLabel secattr + * @sk: the socket + * @sid: the SID + * + * Query the socket's cached secattr and if the SID matches the cached value + * return the cache, otherwise return NULL. + * + */ +static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr( +							const struct sock *sk, +							u32 sid) +{ +	struct sk_security_struct *sksec = sk->sk_security; +	struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr; + +	if (secattr == NULL) +		return NULL; + +	if ((secattr->flags & NETLBL_SECATTR_SECID) && +	    (secattr->attr.secid == sid)) +		return secattr; + +	return NULL; +} + +/**   * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache   *   * Description: @@ -224,7 +250,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,  		struct sk_security_struct *sksec = sk->sk_security;  		if (sksec->nlbl_state != NLBL_REQSKB)  			return 0; -		secattr = sksec->nlbl_secattr; +		secattr = selinux_netlbl_sock_getattr(sk, sid);  	}  	if (secattr == NULL) {  		secattr = &secattr_storage; @@ -410,6 +436,9 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,  	     sksec->nlbl_state == NLBL_CONNLABELED)) {  		netlbl_secattr_init(&secattr);  		lock_sock(sk); +		/* call the netlabel function directly as we want to see the +		 * on-the-wire label that is assigned via the socket's options +		 * and not the cached netlabel/lsm attributes */  		rc = netlbl_sock_getattr(sk, &secattr);  		release_sock(sk);  		if (rc == 0) @@ -442,8 +471,7 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)  	    sksec->nlbl_state != NLBL_CONNLABELED)  		return 0; -	local_bh_disable(); -	bh_lock_sock_nested(sk); +	lock_sock(sk);  	/* connected sockets are allowed to disconnect when the address family  	 * is set to AF_UNSPEC, if that is what is happening we want to reset @@ -464,7 +492,6 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)  		sksec->nlbl_state = NLBL_CONNLABELED;  socket_connect_return: -	bh_unlock_sock(sk); -	local_bh_enable(); +	release_sock(sk);  	return rc;  } diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index c5454c0477c..03a72c32afd 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -166,6 +166,7 @@ static void sel_netnode_insert(struct sel_netnode *node)  		break;  	default:  		BUG(); +		return;  	}  	/* we need to impose a limit on the growth of the hash table so check @@ -225,6 +226,7 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)  		break;  	default:  		BUG(); +		ret = -EINVAL;  	}  	if (ret != 0)  		goto out; diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 855e464e92e..2df7b900e25 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -17,6 +17,7 @@  #include <linux/inet_diag.h>  #include <linux/xfrm.h>  #include <linux/audit.h> +#include <linux/sock_diag.h>  #include "flask.h"  #include "av_permissions.h" @@ -78,6 +79,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] =  {  	{ TCPDIAG_GETSOCK,	NETLINK_TCPDIAG_SOCKET__NLMSG_READ },  	{ DCCPDIAG_GETSOCK,	NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, +	{ SOCK_DIAG_BY_FAMILY,	NETLINK_TCPDIAG_SOCKET__NLMSG_READ },  };  static struct nlmsg_perm nlmsg_xfrm_perms[] = @@ -116,6 +118,8 @@ static struct nlmsg_perm nlmsg_audit_perms[] =  	{ AUDIT_MAKE_EQUIV,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },  	{ AUDIT_TTY_GET,	NETLINK_AUDIT_SOCKET__NLMSG_READ     },  	{ AUDIT_TTY_SET,	NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT	}, +	{ AUDIT_GET_FEATURE,	NETLINK_AUDIT_SOCKET__NLMSG_READ     }, +	{ AUDIT_SET_FEATURE,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },  }; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index ff427733c29..c71737f6d1c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -44,7 +44,9 @@  /* Policy capability filenames */  static char *policycap_names[] = {  	"network_peer_controls", -	"open_perms" +	"open_perms", +	"redhat1", +	"always_check_network"  };  unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; @@ -52,7 +54,7 @@ unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;  static int __init checkreqprot_setup(char *str)  {  	unsigned long checkreqprot; -	if (!strict_strtoul(str, 0, &checkreqprot)) +	if (!kstrtoul(str, 0, &checkreqprot))  		selinux_checkreqprot = checkreqprot ? 1 : 0;  	return 1;  } @@ -574,7 +576,7 @@ static ssize_t sel_write_context(struct file *file, char *buf, size_t size)  	if (length)  		goto out; -	length = security_context_to_sid(buf, size, &sid); +	length = security_context_to_sid(buf, size, &sid, GFP_KERNEL);  	if (length)  		goto out; @@ -729,11 +731,13 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)  		goto out; -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; @@ -815,11 +819,13 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)  		objname = namebuf;  	} -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; @@ -876,11 +882,13 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)  		goto out; -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; @@ -932,7 +940,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s", con, user) != 2)  		goto out; -	length = security_context_to_sid(con, strlen(con) + 1, &sid); +	length = security_context_to_sid(con, strlen(con) + 1, &sid, GFP_KERNEL);  	if (length)  		goto out; @@ -992,11 +1000,13 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)  		goto out; -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h index 149dda731fd..96fd947c494 100644 --- a/security/selinux/ss/constraint.h +++ b/security/selinux/ss/constraint.h @@ -48,6 +48,7 @@ struct constraint_expr {  	u32 op;			/* operator */  	struct ebitmap names;	/* names */ +	struct type_set *type_names;  	struct constraint_expr *next;   /* next expression */  }; diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 30f119b1d1e..820313a04d4 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -213,7 +213,12 @@ netlbl_import_failure:  }  #endif /* CONFIG_NETLABEL */ -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) +/* + * Check to see if all the bits set in e2 are also set in e1. Optionally, + * if last_e2bit is non-zero, the highest set bit in e2 cannot exceed + * last_e2bit. + */ +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit)  {  	struct ebitmap_node *n1, *n2;  	int i; @@ -223,14 +228,25 @@ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)  	n1 = e1->node;  	n2 = e2->node; +  	while (n1 && n2 && (n1->startbit <= n2->startbit)) {  		if (n1->startbit < n2->startbit) {  			n1 = n1->next;  			continue;  		} -		for (i = 0; i < EBITMAP_UNIT_NUMS; i++) { +		for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i]; ) +			i--;	/* Skip trailing NULL map entries */ +		if (last_e2bit && (i >= 0)) { +			u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE + +					 __fls(n2->maps[i]); +			if (lastsetbit > last_e2bit) +				return 0; +		} + +		while (i >= 0) {  			if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])  				return 0; +			i--;  		}  		n1 = n1->next; diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 922f8afa89d..712c8a7b8e8 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -16,7 +16,13 @@  #include <net/netlabel.h> -#define EBITMAP_UNIT_NUMS	((32 - sizeof(void *) - sizeof(u32))	\ +#ifdef CONFIG_64BIT +#define	EBITMAP_NODE_SIZE	64 +#else +#define	EBITMAP_NODE_SIZE	32 +#endif + +#define EBITMAP_UNIT_NUMS	((EBITMAP_NODE_SIZE-sizeof(void *)-sizeof(u32))\  					/ sizeof(unsigned long))  #define EBITMAP_UNIT_SIZE	BITS_PER_LONG  #define EBITMAP_SIZE		(EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE) @@ -117,7 +123,7 @@ static inline void ebitmap_node_clr_bit(struct ebitmap_node *n,  int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);  int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit);  int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);  int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);  void ebitmap_destroy(struct ebitmap *e); diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 933e735bb18..2cc49614984 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -6,6 +6,7 @@  #include <linux/kernel.h>  #include <linux/slab.h>  #include <linux/errno.h> +#include <linux/sched.h>  #include "hashtab.h"  struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), @@ -40,6 +41,8 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum)  	u32 hvalue;  	struct hashtab_node *prev, *cur, *newnode; +	cond_resched(); +  	if (!h || h->nel == HASHTAB_MAX_NODES)  		return -EINVAL; diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 40de8d3f208..d307b37ddc2 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -160,8 +160,6 @@ void mls_sid_to_context(struct context *context,  int mls_level_isvalid(struct policydb *p, struct mls_level *l)  {  	struct level_datum *levdatum; -	struct ebitmap_node *node; -	int i;  	if (!l->sens || l->sens > p->p_levels.nprim)  		return 0; @@ -170,19 +168,13 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)  	if (!levdatum)  		return 0; -	ebitmap_for_each_positive_bit(&l->cat, node, i) { -		if (i > p->p_cats.nprim) -			return 0; -		if (!ebitmap_get_bit(&levdatum->level->cat, i)) { -			/* -			 * Category may not be associated with -			 * sensitivity. -			 */ -			return 0; -		} -	} - -	return 1; +	/* +	 * Return 1 iff all the bits set in l->cat are also be set in +	 * levdatum->level->cat and no bit in l->cat is larger than +	 * p->p_cats.nprim. +	 */ +	return ebitmap_contains(&levdatum->level->cat, &l->cat, +				p->p_cats.nprim);  }  int mls_range_isvalid(struct policydb *p, struct mls_range *r) @@ -500,6 +492,8 @@ int mls_convert_context(struct policydb *oldp,  			rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);  			if (rc)  				return rc; + +			cond_resched();  		}  		ebitmap_destroy(&c->range.level[l].cat);  		c->range.level[l].cat = bitmap; diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h index 03bed52a805..e9364877413 100644 --- a/security/selinux/ss/mls_types.h +++ b/security/selinux/ss/mls_types.h @@ -35,7 +35,7 @@ static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)  static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)  {  	return ((l1->sens >= l2->sens) && -		ebitmap_contains(&l1->cat, &l2->cat)); +		ebitmap_contains(&l1->cat, &l2->cat, 0));  }  #define mls_level_incomp(l1, l2) \ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index c8adde3aff8..9c5cdc2caae 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -143,6 +143,11 @@ static struct policydb_compat_info policydb_compat[] = {  		.sym_num	= SYM_NUM,  		.ocon_num	= OCON_NUM,  	}, +	{ +		.version	= POLICYDB_VERSION_CONSTRAINT_NAMES, +		.sym_num	= SYM_NUM, +		.ocon_num	= OCON_NUM, +	},  };  static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -613,6 +618,19 @@ static int common_destroy(void *key, void *datum, void *p)  	return 0;  } +static void constraint_expr_destroy(struct constraint_expr *expr) +{ +	if (expr) { +		ebitmap_destroy(&expr->names); +		if (expr->type_names) { +			ebitmap_destroy(&expr->type_names->types); +			ebitmap_destroy(&expr->type_names->negset); +			kfree(expr->type_names); +		} +		kfree(expr); +	} +} +  static int cls_destroy(void *key, void *datum, void *p)  {  	struct class_datum *cladatum; @@ -628,10 +646,9 @@ static int cls_destroy(void *key, void *datum, void *p)  		while (constraint) {  			e = constraint->expr;  			while (e) { -				ebitmap_destroy(&e->names);  				etmp = e;  				e = e->next; -				kfree(etmp); +				constraint_expr_destroy(etmp);  			}  			ctemp = constraint;  			constraint = constraint->next; @@ -642,16 +659,14 @@ static int cls_destroy(void *key, void *datum, void *p)  		while (constraint) {  			e = constraint->expr;  			while (e) { -				ebitmap_destroy(&e->names);  				etmp = e;  				e = e->next; -				kfree(etmp); +				constraint_expr_destroy(etmp);  			}  			ctemp = constraint;  			constraint = constraint->next;  			kfree(ctemp);  		} -  		kfree(cladatum->comkey);  	}  	kfree(datum); @@ -1156,8 +1171,34 @@ bad:  	return rc;  } -static int read_cons_helper(struct constraint_node **nodep, int ncons, -			    int allowxtarget, void *fp) +static void type_set_init(struct type_set *t) +{ +	ebitmap_init(&t->types); +	ebitmap_init(&t->negset); +} + +static int type_set_read(struct type_set *t, void *fp) +{ +	__le32 buf[1]; +	int rc; + +	if (ebitmap_read(&t->types, fp)) +		return -EINVAL; +	if (ebitmap_read(&t->negset, fp)) +		return -EINVAL; + +	rc = next_entry(buf, fp, sizeof(u32)); +	if (rc < 0) +		return -EINVAL; +	t->flags = le32_to_cpu(buf[0]); + +	return 0; +} + + +static int read_cons_helper(struct policydb *p, +				struct constraint_node **nodep, +				int ncons, int allowxtarget, void *fp)  {  	struct constraint_node *c, *lc;  	struct constraint_expr *e, *le; @@ -1225,6 +1266,18 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons,  				rc = ebitmap_read(&e->names, fp);  				if (rc)  					return rc; +				if (p->policyvers >= +					POLICYDB_VERSION_CONSTRAINT_NAMES) { +						e->type_names = kzalloc(sizeof +						(*e->type_names), +						GFP_KERNEL); +					if (!e->type_names) +						return -ENOMEM; +					type_set_init(e->type_names); +					rc = type_set_read(e->type_names, fp); +					if (rc) +						return rc; +				}  				break;  			default:  				return -EINVAL; @@ -1301,7 +1354,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)  			goto bad;  	} -	rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); +	rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);  	if (rc)  		goto bad; @@ -1311,7 +1364,8 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)  		if (rc)  			goto bad;  		ncons = le32_to_cpu(buf[0]); -		rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); +		rc = read_cons_helper(p, &cladatum->validatetrans, +				ncons, 1, fp);  		if (rc)  			goto bad;  	} @@ -1941,7 +1995,19 @@ static int filename_trans_read(struct policydb *p, void *fp)  		if (rc)  			goto out; -		hashtab_insert(p->filename_trans, ft, otype); +		rc = hashtab_insert(p->filename_trans, ft, otype); +		if (rc) { +			/* +			 * Do not return -EEXIST to the caller, or the system +			 * will not boot. +			 */ +			if (rc != -EEXIST) +				goto out; +			/* But free memory to avoid memory leak. */ +			kfree(ft); +			kfree(name); +			kfree(otype); +		}  	}  	hash_eval(p->filename_trans, "filenametr");  	return 0; @@ -2753,6 +2819,24 @@ static int common_write(void *vkey, void *datum, void *ptr)  	return 0;  } +static int type_set_write(struct type_set *t, void *fp) +{ +	int rc; +	__le32 buf[1]; + +	if (ebitmap_write(&t->types, fp)) +		return -EINVAL; +	if (ebitmap_write(&t->negset, fp)) +		return -EINVAL; + +	buf[0] = cpu_to_le32(t->flags); +	rc = put_entry(buf, sizeof(u32), 1, fp); +	if (rc) +		return -EINVAL; + +	return 0; +} +  static int write_cons_helper(struct policydb *p, struct constraint_node *node,  			     void *fp)  { @@ -2784,6 +2868,12 @@ static int write_cons_helper(struct policydb *p, struct constraint_node *node,  				rc = ebitmap_write(&e->names, fp);  				if (rc)  					return rc; +				if (p->policyvers >= +					POLICYDB_VERSION_CONSTRAINT_NAMES) { +					rc = type_set_write(e->type_names, fp); +					if (rc) +						return rc; +				}  				break;  			default:  				break; @@ -3203,9 +3293,8 @@ static int range_write_helper(void *key, void *data, void *ptr)  static int range_write(struct policydb *p, void *fp)  { -	size_t nel;  	__le32 buf[1]; -	int rc; +	int rc, nel;  	struct policy_data pd;  	pd.p = p; @@ -3249,10 +3338,10 @@ static int filename_write_helper(void *key, void *data, void *ptr)  	if (rc)  		return rc; -	buf[0] = ft->stype; -	buf[1] = ft->ttype; -	buf[2] = ft->tclass; -	buf[3] = otype->otype; +	buf[0] = cpu_to_le32(ft->stype); +	buf[1] = cpu_to_le32(ft->ttype); +	buf[2] = cpu_to_le32(ft->tclass); +	buf[3] = cpu_to_le32(otype->otype);  	rc = put_entry(buf, sizeof(u32), 4, fp);  	if (rc) diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index da637471d4c..725d5945a97 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -154,6 +154,17 @@ struct cond_bool_datum {  struct cond_node;  /* + * type set preserves data needed to determine constraint info from + * policy source. This is not used by the kernel policy but allows + * utilities such as audit2allow to determine constraint denials. + */ +struct type_set { +	struct ebitmap types; +	struct ebitmap negset; +	u32 flags; +}; + +/*   * The configuration data includes security contexts for   * initial SIDs, unlabeled file systems, TCP and UDP port numbers,   * network interfaces, and nodes.  This structure stores the diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b4feecc3fe0..4bca49414a4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -72,6 +72,7 @@  int selinux_policycap_netpeer;  int selinux_policycap_openperm; +int selinux_policycap_alwaysnetwork;  static DEFINE_RWLOCK(policy_rwlock); @@ -1231,6 +1232,10 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,  	struct context context;  	int rc = 0; +	/* An empty security context is never valid. */ +	if (!scontext_len) +		return -EINVAL; +  	if (!ss_initialized) {  		int i; @@ -1284,16 +1289,18 @@ out:   * @scontext: security context   * @scontext_len: length in bytes   * @sid: security identifier, SID + * @gfp: context for the allocation   *   * Obtains a SID associated with the security context that   * has the string representation specified by @scontext.   * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient   * memory is available, or 0 on success.   */ -int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid) +int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid, +			    gfp_t gfp)  {  	return security_context_to_sid_core(scontext, scontext_len, -					    sid, SECSID_NULL, GFP_KERNEL, 0); +					    sid, SECSID_NULL, gfp, 0);  }  /** @@ -1812,6 +1819,8 @@ static void security_load_policycaps(void)  						  POLICYDB_CAPABILITY_NETPEER);  	selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,  						  POLICYDB_CAPABILITY_OPENPERM); +	selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, +						  POLICYDB_CAPABILITY_ALWAYSNETWORK);  }  static int security_preserve_bools(struct policydb *p); @@ -1828,7 +1837,7 @@ static int security_preserve_bools(struct policydb *p);   */  int security_load_policy(void *data, size_t len)  { -	struct policydb oldpolicydb, newpolicydb; +	struct policydb *oldpolicydb, *newpolicydb;  	struct sidtab oldsidtab, newsidtab;  	struct selinux_mapping *oldmap, *map = NULL;  	struct convert_context_args args; @@ -1837,12 +1846,19 @@ int security_load_policy(void *data, size_t len)  	int rc = 0;  	struct policy_file file = { data, len }, *fp = &file; +	oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL); +	if (!oldpolicydb) { +		rc = -ENOMEM; +		goto out; +	} +	newpolicydb = oldpolicydb + 1; +  	if (!ss_initialized) {  		avtab_cache_init();  		rc = policydb_read(&policydb, fp);  		if (rc) {  			avtab_cache_destroy(); -			return rc; +			goto out;  		}  		policydb.len = len; @@ -1852,14 +1868,14 @@ int security_load_policy(void *data, size_t len)  		if (rc) {  			policydb_destroy(&policydb);  			avtab_cache_destroy(); -			return rc; +			goto out;  		}  		rc = policydb_load_isids(&policydb, &sidtab);  		if (rc) {  			policydb_destroy(&policydb);  			avtab_cache_destroy(); -			return rc; +			goto out;  		}  		security_load_policycaps(); @@ -1871,36 +1887,36 @@ int security_load_policy(void *data, size_t len)  		selinux_status_update_policyload(seqno);  		selinux_netlbl_cache_invalidate();  		selinux_xfrm_notify_policyload(); -		return 0; +		goto out;  	}  #if 0  	sidtab_hash_eval(&sidtab, "sids");  #endif -	rc = policydb_read(&newpolicydb, fp); +	rc = policydb_read(newpolicydb, fp);  	if (rc) -		return rc; +		goto out; -	newpolicydb.len = len; +	newpolicydb->len = len;  	/* If switching between different policy types, log MLS status */ -	if (policydb.mls_enabled && !newpolicydb.mls_enabled) +	if (policydb.mls_enabled && !newpolicydb->mls_enabled)  		printk(KERN_INFO "SELinux: Disabling MLS support...\n"); -	else if (!policydb.mls_enabled && newpolicydb.mls_enabled) +	else if (!policydb.mls_enabled && newpolicydb->mls_enabled)  		printk(KERN_INFO "SELinux: Enabling MLS support...\n"); -	rc = policydb_load_isids(&newpolicydb, &newsidtab); +	rc = policydb_load_isids(newpolicydb, &newsidtab);  	if (rc) {  		printk(KERN_ERR "SELinux:  unable to load the initial SIDs\n"); -		policydb_destroy(&newpolicydb); -		return rc; +		policydb_destroy(newpolicydb); +		goto out;  	} -	rc = selinux_set_mapping(&newpolicydb, secclass_map, &map, &map_size); +	rc = selinux_set_mapping(newpolicydb, secclass_map, &map, &map_size);  	if (rc)  		goto err; -	rc = security_preserve_bools(&newpolicydb); +	rc = security_preserve_bools(newpolicydb);  	if (rc) {  		printk(KERN_ERR "SELinux:  unable to preserve booleans\n");  		goto err; @@ -1918,7 +1934,7 @@ int security_load_policy(void *data, size_t len)  	 * in the new SID table.  	 */  	args.oldp = &policydb; -	args.newp = &newpolicydb; +	args.newp = newpolicydb;  	rc = sidtab_map(&newsidtab, convert_context, &args);  	if (rc) {  		printk(KERN_ERR "SELinux:  unable to convert the internal" @@ -1928,12 +1944,12 @@ int security_load_policy(void *data, size_t len)  	}  	/* Save the old policydb and SID table to free later. */ -	memcpy(&oldpolicydb, &policydb, sizeof policydb); +	memcpy(oldpolicydb, &policydb, sizeof(policydb));  	sidtab_set(&oldsidtab, &sidtab);  	/* Install the new policydb and SID table. */  	write_lock_irq(&policy_rwlock); -	memcpy(&policydb, &newpolicydb, sizeof policydb); +	memcpy(&policydb, newpolicydb, sizeof(policydb));  	sidtab_set(&sidtab, &newsidtab);  	security_load_policycaps();  	oldmap = current_mapping; @@ -1943,7 +1959,7 @@ int security_load_policy(void *data, size_t len)  	write_unlock_irq(&policy_rwlock);  	/* Free the old policydb and SID table. */ -	policydb_destroy(&oldpolicydb); +	policydb_destroy(oldpolicydb);  	sidtab_destroy(&oldsidtab);  	kfree(oldmap); @@ -1953,14 +1969,17 @@ int security_load_policy(void *data, size_t len)  	selinux_netlbl_cache_invalidate();  	selinux_xfrm_notify_policyload(); -	return 0; +	rc = 0; +	goto out;  err:  	kfree(map);  	sidtab_destroy(&newsidtab); -	policydb_destroy(&newpolicydb); -	return rc; +	policydb_destroy(newpolicydb); +out: +	kfree(oldpolicydb); +	return rc;  }  size_t security_policydb_len(void) @@ -2323,17 +2342,14 @@ out:  /**   * security_fs_use - Determine how to handle labeling for a filesystem. - * @fstype: filesystem type - * @behavior: labeling behavior - * @sid: SID for filesystem (superblock) + * @sb: superblock in question   */ -int security_fs_use( -	const char *fstype, -	unsigned int *behavior, -	u32 *sid) +int security_fs_use(struct super_block *sb)  {  	int rc = 0;  	struct ocontext *c; +	struct superblock_security_struct *sbsec = sb->s_security; +	const char *fstype = sb->s_type->name;  	read_lock(&policy_rwlock); @@ -2345,21 +2361,21 @@ int security_fs_use(  	}  	if (c) { -		*behavior = c->v.behavior; +		sbsec->behavior = c->v.behavior;  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(&sidtab, &c->context[0],  						   &c->sid[0]);  			if (rc)  				goto out;  		} -		*sid = c->sid[0]; +		sbsec->sid = c->sid[0];  	} else { -		rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); +		rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, &sbsec->sid);  		if (rc) { -			*behavior = SECURITY_FS_USE_NONE; +			sbsec->behavior = SECURITY_FS_USE_NONE;  			rc = 0;  		} else { -			*behavior = SECURITY_FS_USE_GENFS; +			sbsec->behavior = SECURITY_FS_USE_GENFS;  		}  	} @@ -2938,25 +2954,21 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,  	struct selinux_audit_rule *rule = vrule;  	int match = 0; -	if (!rule) { -		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, -			  "selinux_audit_rule_match: missing rule\n"); +	if (unlikely(!rule)) { +		WARN_ONCE(1, "selinux_audit_rule_match: missing rule\n");  		return -ENOENT;  	}  	read_lock(&policy_rwlock);  	if (rule->au_seqno < latest_granting) { -		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, -			  "selinux_audit_rule_match: stale rule\n");  		match = -ESTALE;  		goto out;  	}  	ctxt = sidtab_search(&sidtab, sid); -	if (!ctxt) { -		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, -			  "selinux_audit_rule_match: unrecognized SID %d\n", +	if (unlikely(!ctxt)) { +		WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",  			  sid);  		match = -ENOENT;  		goto out; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index d0308188621..98b042630a9 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -56,7 +56,7 @@  atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);  /* - * Returns true if an LSM/SELinux context + * Returns true if the context is an LSM/SELinux context.   */  static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)  { @@ -66,7 +66,7 @@ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)  }  /* - * Returns true if the xfrm contains a security blob for SELinux + * Returns true if the xfrm contains a security blob for SELinux.   */  static inline int selinux_authorizable_xfrm(struct xfrm_state *x)  { @@ -74,48 +74,112 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)  }  /* - * LSM hook implementation that authorizes that a flow can use - * a xfrm policy rule. + * Allocates a xfrm_sec_state and populates it using the supplied security + * xfrm_user_sec_ctx context.   */ -int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) +static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, +				   struct xfrm_user_sec_ctx *uctx, +				   gfp_t gfp)  {  	int rc; -	u32 sel_sid; +	const struct task_security_struct *tsec = current_security(); +	struct xfrm_sec_ctx *ctx = NULL; +	u32 str_len; -	/* Context sid is either set to label or ANY_ASSOC */ -	if (ctx) { -		if (!selinux_authorizable_ctx(ctx)) -			return -EINVAL; - -		sel_sid = ctx->ctx_sid; -	} else -		/* -		 * All flows should be treated as polmatch'ing an -		 * otherwise applicable "non-labeled" policy. This -		 * would prevent inadvertent "leaks". -		 */ -		return 0; +	if (ctxp == NULL || uctx == NULL || +	    uctx->ctx_doi != XFRM_SC_DOI_LSM || +	    uctx->ctx_alg != XFRM_SC_ALG_SELINUX) +		return -EINVAL; + +	str_len = uctx->ctx_len; +	if (str_len >= PAGE_SIZE) +		return -ENOMEM; + +	ctx = kmalloc(sizeof(*ctx) + str_len + 1, gfp); +	if (!ctx) +		return -ENOMEM; -	rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__POLMATCH, -			  NULL); +	ctx->ctx_doi = XFRM_SC_DOI_LSM; +	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; +	ctx->ctx_len = str_len; +	memcpy(ctx->ctx_str, &uctx[1], str_len); +	ctx->ctx_str[str_len] = '\0'; +	rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid, gfp); +	if (rc) +		goto err; -	if (rc == -EACCES) -		return -ESRCH; +	rc = avc_has_perm(tsec->sid, ctx->ctx_sid, +			  SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); +	if (rc) +		goto err; +	*ctxp = ctx; +	atomic_inc(&selinux_xfrm_refcount); +	return 0; + +err: +	kfree(ctx);  	return rc;  }  /* + * Free the xfrm_sec_ctx structure. + */ +static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) +{ +	if (!ctx) +		return; + +	atomic_dec(&selinux_xfrm_refcount); +	kfree(ctx); +} + +/* + * Authorize the deletion of a labeled SA or policy rule. + */ +static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) +{ +	const struct task_security_struct *tsec = current_security(); + +	if (!ctx) +		return 0; + +	return avc_has_perm(tsec->sid, ctx->ctx_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, +			    NULL); +} + +/* + * LSM hook implementation that authorizes that a flow can use a xfrm policy + * rule. + */ +int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) +{ +	int rc; + +	/* All flows should be treated as polmatch'ing an otherwise applicable +	 * "non-labeled" policy. This would prevent inadvertent "leaks". */ +	if (!ctx) +		return 0; + +	/* Context sid is either set to label or ANY_ASSOC */ +	if (!selinux_authorizable_ctx(ctx)) +		return -EINVAL; + +	rc = avc_has_perm(fl_secid, ctx->ctx_sid, +			  SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL); +	return (rc == -EACCES ? -ESRCH : rc); +} + +/*   * LSM hook implementation that authorizes that a state matches   * the given policy, flow combo.   */ - -int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, -			const struct flowi *fl) +int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, +				      struct xfrm_policy *xp, +				      const struct flowi *fl)  {  	u32 state_sid; -	int rc;  	if (!xp->security)  		if (x->security) @@ -138,187 +202,112 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *  	if (fl->flowi_secid != state_sid)  		return 0; -	rc = avc_has_perm(fl->flowi_secid, state_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__SENDTO, -			  NULL)? 0:1; - -	/* -	 * We don't need a separate SA Vs. policy polmatch check -	 * since the SA is now of the same label as the flow and -	 * a flow Vs. policy polmatch check had already happened -	 * in selinux_xfrm_policy_lookup() above. -	 */ - -	return rc; +	/* We don't need a separate SA Vs. policy polmatch check since the SA +	 * is now of the same label as the flow and a flow Vs. policy polmatch +	 * check had already happened in selinux_xfrm_policy_lookup() above. */ +	return (avc_has_perm(fl->flowi_secid, state_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, +			    NULL) ? 0 : 1);  } -/* - * LSM hook implementation that checks and/or returns the xfrm sid for the - * incoming packet. - */ - -int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) +static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb)  { -	struct sec_path *sp; +	struct dst_entry *dst = skb_dst(skb); +	struct xfrm_state *x; -	*sid = SECSID_NULL; +	if (dst == NULL) +		return SECSID_NULL; +	x = dst->xfrm; +	if (x == NULL || !selinux_authorizable_xfrm(x)) +		return SECSID_NULL; -	if (skb == NULL) -		return 0; +	return x->security->ctx_sid; +} + +static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb, +					u32 *sid, int ckall) +{ +	u32 sid_session = SECSID_NULL; +	struct sec_path *sp = skb->sp; -	sp = skb->sp;  	if (sp) { -		int i, sid_set = 0; +		int i; -		for (i = sp->len-1; i >= 0; i--) { +		for (i = sp->len - 1; i >= 0; i--) {  			struct xfrm_state *x = sp->xvec[i];  			if (selinux_authorizable_xfrm(x)) {  				struct xfrm_sec_ctx *ctx = x->security; -				if (!sid_set) { -					*sid = ctx->ctx_sid; -					sid_set = 1; - +				if (sid_session == SECSID_NULL) { +					sid_session = ctx->ctx_sid;  					if (!ckall) -						break; -				} else if (*sid != ctx->ctx_sid) +						goto out; +				} else if (sid_session != ctx->ctx_sid) { +					*sid = SECSID_NULL;  					return -EINVAL; +				}  			}  		}  	} +out: +	*sid = sid_session;  	return 0;  }  /* - * Security blob allocation for xfrm_policy and xfrm_state - * CTX does not have a meaningful value on input + * LSM hook implementation that checks and/or returns the xfrm sid for the + * incoming packet.   */ -static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, -	struct xfrm_user_sec_ctx *uctx, u32 sid) +int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)  { -	int rc = 0; -	const struct task_security_struct *tsec = current_security(); -	struct xfrm_sec_ctx *ctx = NULL; -	char *ctx_str = NULL; -	u32 str_len; - -	BUG_ON(uctx && sid); - -	if (!uctx) -		goto not_from_user; - -	if (uctx->ctx_alg != XFRM_SC_ALG_SELINUX) -		return -EINVAL; - -	str_len = uctx->ctx_len; -	if (str_len >= PAGE_SIZE) -		return -ENOMEM; - -	*ctxp = ctx = kmalloc(sizeof(*ctx) + -			      str_len + 1, -			      GFP_KERNEL); - -	if (!ctx) -		return -ENOMEM; - -	ctx->ctx_doi = uctx->ctx_doi; -	ctx->ctx_len = str_len; -	ctx->ctx_alg = uctx->ctx_alg; - -	memcpy(ctx->ctx_str, -	       uctx+1, -	       str_len); -	ctx->ctx_str[str_len] = 0; -	rc = security_context_to_sid(ctx->ctx_str, -				     str_len, -				     &ctx->ctx_sid); - -	if (rc) -		goto out; - -	/* -	 * Does the subject have permission to set security context? -	 */ -	rc = avc_has_perm(tsec->sid, ctx->ctx_sid, -			  SECCLASS_ASSOCIATION, -			  ASSOCIATION__SETCONTEXT, NULL); -	if (rc) -		goto out; - -	return rc; - -not_from_user: -	rc = security_sid_to_context(sid, &ctx_str, &str_len); -	if (rc) -		goto out; - -	*ctxp = ctx = kmalloc(sizeof(*ctx) + -			      str_len, -			      GFP_ATOMIC); - -	if (!ctx) { -		rc = -ENOMEM; -		goto out; +	if (skb == NULL) { +		*sid = SECSID_NULL; +		return 0;  	} +	return selinux_xfrm_skb_sid_ingress(skb, sid, ckall); +} -	ctx->ctx_doi = XFRM_SC_DOI_LSM; -	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; -	ctx->ctx_sid = sid; -	ctx->ctx_len = str_len; -	memcpy(ctx->ctx_str, -	       ctx_str, -	       str_len); +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) +{ +	int rc; -	goto out2; +	rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0); +	if (rc == 0 && *sid == SECSID_NULL) +		*sid = selinux_xfrm_skb_sid_egress(skb); -out: -	*ctxp = NULL; -	kfree(ctx); -out2: -	kfree(ctx_str);  	return rc;  }  /* - * LSM hook implementation that allocs and transfers uctx spec to - * xfrm_policy. + * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.   */  int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, -			      struct xfrm_user_sec_ctx *uctx) +			      struct xfrm_user_sec_ctx *uctx, +			      gfp_t gfp)  { -	int err; - -	BUG_ON(!uctx); - -	err = selinux_xfrm_sec_ctx_alloc(ctxp, uctx, 0); -	if (err == 0) -		atomic_inc(&selinux_xfrm_refcount); - -	return err; +	return selinux_xfrm_alloc_user(ctxp, uctx, gfp);  } -  /* - * LSM hook implementation that copies security data structure from old to - * new for policy cloning. + * LSM hook implementation that copies security data structure from old to new + * for policy cloning.   */  int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,  			      struct xfrm_sec_ctx **new_ctxp)  {  	struct xfrm_sec_ctx *new_ctx; -	if (old_ctx) { -		new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len, -				  GFP_ATOMIC); -		if (!new_ctx) -			return -ENOMEM; +	if (!old_ctx) +		return 0; + +	new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len, +			  GFP_ATOMIC); +	if (!new_ctx) +		return -ENOMEM; +	atomic_inc(&selinux_xfrm_refcount); +	*new_ctxp = new_ctx; -		memcpy(new_ctx, old_ctx, sizeof(*new_ctx)); -		memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len); -		atomic_inc(&selinux_xfrm_refcount); -		*new_ctxp = new_ctx; -	}  	return 0;  } @@ -327,8 +316,7 @@ int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,   */  void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)  { -	atomic_dec(&selinux_xfrm_refcount); -	kfree(ctx); +	selinux_xfrm_free(ctx);  }  /* @@ -336,31 +324,58 @@ void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)   */  int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)  { -	const struct task_security_struct *tsec = current_security(); - -	if (!ctx) -		return 0; +	return selinux_xfrm_delete(ctx); +} -	return avc_has_perm(tsec->sid, ctx->ctx_sid, -			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, -			    NULL); +/* + * LSM hook implementation that allocates a xfrm_sec_state, populates it using + * the supplied security context, and assigns it to the xfrm_state. + */ +int selinux_xfrm_state_alloc(struct xfrm_state *x, +			     struct xfrm_user_sec_ctx *uctx) +{ +	return selinux_xfrm_alloc_user(&x->security, uctx, GFP_KERNEL);  }  /* - * LSM hook implementation that allocs and transfers sec_ctx spec to - * xfrm_state. + * LSM hook implementation that allocates a xfrm_sec_state and populates based + * on a secid.   */ -int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx, -		u32 secid) +int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, +				     struct xfrm_sec_ctx *polsec, u32 secid)  { -	int err; +	int rc; +	struct xfrm_sec_ctx *ctx; +	char *ctx_str = NULL; +	int str_len; + +	if (!polsec) +		return 0; + +	if (secid == 0) +		return -EINVAL; + +	rc = security_sid_to_context(secid, &ctx_str, &str_len); +	if (rc) +		return rc; -	BUG_ON(!x); +	ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC); +	if (!ctx) { +		rc = -ENOMEM; +		goto out; +	} -	err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid); -	if (err == 0) -		atomic_inc(&selinux_xfrm_refcount); -	return err; +	ctx->ctx_doi = XFRM_SC_DOI_LSM; +	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; +	ctx->ctx_sid = secid; +	ctx->ctx_len = str_len; +	memcpy(ctx->ctx_str, ctx_str, str_len); + +	x->security = ctx; +	atomic_inc(&selinux_xfrm_refcount); +out: +	kfree(ctx_str); +	return rc;  }  /* @@ -368,24 +383,15 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct   */  void selinux_xfrm_state_free(struct xfrm_state *x)  { -	atomic_dec(&selinux_xfrm_refcount); -	kfree(x->security); +	selinux_xfrm_free(x->security);  } - /* -  * LSM hook implementation that authorizes deletion of labeled SAs. -  */ +/* + * LSM hook implementation that authorizes deletion of labeled SAs. + */  int selinux_xfrm_state_delete(struct xfrm_state *x)  { -	const struct task_security_struct *tsec = current_security(); -	struct xfrm_sec_ctx *ctx = x->security; - -	if (!ctx) -		return 0; - -	return avc_has_perm(tsec->sid, ctx->ctx_sid, -			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, -			    NULL); +	return selinux_xfrm_delete(x->security);  }  /* @@ -395,14 +401,12 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)   * we need to check for unlabelled access since this may not have   * gone thru the IPSec process.   */ -int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, -				struct common_audit_data *ad) +int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, +			      struct common_audit_data *ad)  { -	int i, rc = 0; -	struct sec_path *sp; -	u32 sel_sid = SECINITSID_UNLABELED; - -	sp = skb->sp; +	int i; +	struct sec_path *sp = skb->sp; +	u32 peer_sid = SECINITSID_UNLABELED;  	if (sp) {  		for (i = 0; i < sp->len; i++) { @@ -410,23 +414,17 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,  			if (x && selinux_authorizable_xfrm(x)) {  				struct xfrm_sec_ctx *ctx = x->security; -				sel_sid = ctx->ctx_sid; +				peer_sid = ctx->ctx_sid;  				break;  			}  		}  	} -	/* -	 * This check even when there's no association involved is -	 * intended, according to Trent Jaeger, to make sure a -	 * process can't engage in non-ipsec communication unless -	 * explicitly allowed by policy. -	 */ - -	rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__RECVFROM, ad); - -	return rc; +	/* This check even when there's no association involved is intended, +	 * according to Trent Jaeger, to make sure a process can't engage in +	 * non-IPsec communication unless explicitly allowed by policy. */ +	return avc_has_perm(sk_sid, peer_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);  }  /* @@ -436,49 +434,38 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,   * If we do have a authorizable security association, then it has already been   * checked in the selinux_xfrm_state_pol_flow_match hook above.   */ -int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, -					struct common_audit_data *ad, u8 proto) +int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, +				struct common_audit_data *ad, u8 proto)  {  	struct dst_entry *dst; -	int rc = 0; - -	dst = skb_dst(skb); - -	if (dst) { -		struct dst_entry *dst_test; - -		for (dst_test = dst; dst_test != NULL; -		     dst_test = dst_test->child) { -			struct xfrm_state *x = dst_test->xfrm; - -			if (x && selinux_authorizable_xfrm(x)) -				goto out; -		} -	}  	switch (proto) {  	case IPPROTO_AH:  	case IPPROTO_ESP:  	case IPPROTO_COMP: -		/* -		 * We should have already seen this packet once before -		 * it underwent xfrm(s). No need to subject it to the -		 * unlabeled check. -		 */ -		goto out; +		/* We should have already seen this packet once before it +		 * underwent xfrm(s). No need to subject it to the unlabeled +		 * check. */ +		return 0;  	default:  		break;  	} -	/* -	 * This check even when there's no association involved is -	 * intended, according to Trent Jaeger, to make sure a -	 * process can't engage in non-ipsec communication unless -	 * explicitly allowed by policy. -	 */ +	dst = skb_dst(skb); +	if (dst) { +		struct dst_entry *iter; -	rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, -			  ASSOCIATION__SENDTO, ad); -out: -	return rc; +		for (iter = dst; iter != NULL; iter = iter->child) { +			struct xfrm_state *x = iter->xfrm; + +			if (x && selinux_authorizable_xfrm(x)) +				return 0; +		} +	} + +	/* This check even when there's no association involved is intended, +	 * according to Trent Jaeger, to make sure a process can't engage in +	 * non-IPsec communication unless explicitly allowed by policy. */ +	return avc_has_perm(sk_sid, SECINITSID_UNLABELED, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);  } diff --git a/security/smack/smack.h b/security/smack/smack.h index 076b8e8a51a..020307ef097 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -80,8 +80,8 @@ struct superblock_smack {  struct socket_smack {  	struct smack_known	*smk_out;	/* outbound label */ -	char			*smk_in;	/* inbound label */ -	char			*smk_packet;	/* TCP peer label */ +	struct smack_known	*smk_in;	/* inbound label */ +	struct smack_known	*smk_packet;	/* TCP peer label */  };  /* @@ -133,7 +133,7 @@ struct smk_port_label {  	struct list_head	list;  	struct sock		*smk_sock;	/* socket initialized on */  	unsigned short		smk_port;	/* the port number */ -	char			*smk_in;	/* incoming label */ +	struct smack_known	*smk_in;	/* inbound label */  	struct smack_known	*smk_out;	/* outgoing label */  }; @@ -177,9 +177,21 @@ struct smk_port_label {  #define SMACK_CIPSO_MAXCATNUM           184     /* 23 * 8 */  /* - * Flag for transmute access + * Ptrace rules   */ -#define MAY_TRANSMUTE	64 +#define SMACK_PTRACE_DEFAULT	0 +#define SMACK_PTRACE_EXACT	1 +#define SMACK_PTRACE_DRACONIAN	2 +#define SMACK_PTRACE_MAX	SMACK_PTRACE_DRACONIAN + +/* + * Flags for untraditional access modes. + * It shouldn't be necessary to avoid conflicts with definitions + * in fs.h, but do so anyway. + */ +#define MAY_TRANSMUTE	0x00001000	/* Controls directory labeling */ +#define MAY_LOCK	0x00002000	/* Locks should be writes, but ... */ +  /*   * Just to make the common cases easier to deal with   */ @@ -188,9 +200,9 @@ struct smk_port_label {  #define MAY_NOT		0  /* - * Number of access types used by Smack (rwxat) + * Number of access types used by Smack (rwxatl)   */ -#define SMK_NUM_ACCESS_TYPE 5 +#define SMK_NUM_ACCESS_TYPE 6  /* SMACK data */  struct smack_audit_data { @@ -221,6 +233,7 @@ struct inode_smack *new_inode_smack(char *);   */  int smk_access_entry(char *, char *, struct list_head *);  int smk_access(struct smack_known *, char *, int, struct smk_audit_info *); +int smk_tskacc(struct task_smack *, char *, u32, struct smk_audit_info *);  int smk_curacc(char *, u32, struct smk_audit_info *);  struct smack_known *smack_from_secid(const u32);  char *smk_parse_smack(const char *string, int len); @@ -237,8 +250,10 @@ u32 smack_to_secid(const char *);  extern int smack_cipso_direct;  extern int smack_cipso_mapped;  extern struct smack_known *smack_net_ambient; -extern char *smack_onlycap; +extern struct smack_known *smack_onlycap; +extern struct smack_known *smack_syslog_label;  extern const char *smack_cipso_option; +extern int smack_ptrace_rule;  extern struct smack_known smack_known_floor;  extern struct smack_known smack_known_hat; @@ -308,7 +323,7 @@ static inline int smack_privileged(int cap)  	if (!capable(cap))  		return 0; -	if (smack_onlycap == NULL || smack_onlycap == skp->smk_known) +	if (smack_onlycap == NULL || smack_onlycap == skp)  		return 1;  	return 0;  } diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index b3b59b1e93d..c062e9467b6 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -84,6 +84,8 @@ int log_policy = SMACK_AUDIT_DENIED;   *   * Do the object check first because that is more   * likely to differ. + * + * Allowing write access implies allowing locking.   */  int smk_access_entry(char *subject_label, char *object_label,  			struct list_head *rule_list) @@ -99,6 +101,11 @@ int smk_access_entry(char *subject_label, char *object_label,  		}  	} +	/* +	 * MAY_WRITE implies MAY_LOCK. +	 */ +	if ((may & MAY_WRITE) == MAY_WRITE) +		may |= MAY_LOCK;  	return may;  } @@ -185,20 +192,21 @@ out_audit:  }  /** - * smk_curacc - determine if current has a specific access to an object + * smk_tskacc - determine if a task has a specific access to an object + * @tsp: a pointer to the subject task   * @obj_label: a pointer to the object's Smack label   * @mode: the access requested, in "MAY" format   * @a : common audit data   * - * This function checks the current subject label/object label pair + * This function checks the subject task's label/object label pair   * in the access rule list and returns 0 if the access is permitted, - * non zero otherwise. It allows that current may have the capability + * non zero otherwise. It allows that the task may have the capability   * to override the rules.   */ -int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) +int smk_tskacc(struct task_smack *subject, char *obj_label, +	       u32 mode, struct smk_audit_info *a)  { -	struct task_smack *tsp = current_security(); -	struct smack_known *skp = smk_of_task(tsp); +	struct smack_known *skp = smk_of_task(subject);  	int may;  	int rc; @@ -212,7 +220,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)  		 * it can further restrict access.  		 */  		may = smk_access_entry(skp->smk_known, obj_label, -					&tsp->smk_rules); +					&subject->smk_rules);  		if (may < 0)  			goto out_audit;  		if ((mode & may) == mode) @@ -234,6 +242,24 @@ out_audit:  	return rc;  } +/** + * smk_curacc - determine if current has a specific access to an object + * @obj_label: a pointer to the object's Smack label + * @mode: the access requested, in "MAY" format + * @a : common audit data + * + * This function checks the current subject label/object label pair + * in the access rule list and returns 0 if the access is permitted, + * non zero otherwise. It allows that current may have the capability + * to override the rules. + */ +int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) +{ +	struct task_smack *tsp = current_security(); + +	return smk_tskacc(tsp, obj_label, mode, a); +} +  #ifdef CONFIG_AUDIT  /**   * smack_str_from_perm : helper to transalate an int to a @@ -245,6 +271,7 @@ out_audit:  static inline void smack_str_from_perm(char *string, int access)  {  	int i = 0; +  	if (access & MAY_READ)  		string[i++] = 'r';  	if (access & MAY_WRITE) @@ -255,6 +282,8 @@ static inline void smack_str_from_perm(char *string, int access)  		string[i++] = 'a';  	if (access & MAY_TRANSMUTE)  		string[i++] = 't'; +	if (access & MAY_LOCK) +		string[i++] = 'l';  	string[i] = '\0';  }  /** @@ -275,7 +304,10 @@ static void smack_log_callback(struct audit_buffer *ab, void *a)  	audit_log_untrustedstring(ab, sad->subject);  	audit_log_format(ab, " object=");  	audit_log_untrustedstring(ab, sad->object); -	audit_log_format(ab, " requested=%s", sad->request); +	if (sad->request[0] == '\0') +		audit_log_format(ab, " labels_differ"); +	else +		audit_log_format(ab, " requested=%s", sad->request);  }  /** diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8825375cc03..f2c30801ce4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -157,6 +157,74 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,  	return rc;  } +/** + * smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_* + * @mode - input mode in form of PTRACE_MODE_* + * + * Returns a converted MAY_* mode usable by smack rules + */ +static inline unsigned int smk_ptrace_mode(unsigned int mode) +{ +	switch (mode) { +	case PTRACE_MODE_READ: +		return MAY_READ; +	case PTRACE_MODE_ATTACH: +		return MAY_READWRITE; +	} + +	return 0; +} + +/** + * smk_ptrace_rule_check - helper for ptrace access + * @tracer: tracer process + * @tracee_label: label of the process that's about to be traced, + *                the pointer must originate from smack structures + * @mode: ptrace attachment mode (PTRACE_MODE_*) + * @func: name of the function that called us, used for audit + * + * Returns 0 on access granted, -error on error + */ +static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, +				 unsigned int mode, const char *func) +{ +	int rc; +	struct smk_audit_info ad, *saip = NULL; +	struct task_smack *tsp; +	struct smack_known *skp; + +	if ((mode & PTRACE_MODE_NOAUDIT) == 0) { +		smk_ad_init(&ad, func, LSM_AUDIT_DATA_TASK); +		smk_ad_setfield_u_tsk(&ad, tracer); +		saip = &ad; +	} + +	tsp = task_security(tracer); +	skp = smk_of_task(tsp); + +	if ((mode & PTRACE_MODE_ATTACH) && +	    (smack_ptrace_rule == SMACK_PTRACE_EXACT || +	     smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { +		if (skp->smk_known == tracee_label) +			rc = 0; +		else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) +			rc = -EACCES; +		else if (capable(CAP_SYS_PTRACE)) +			rc = 0; +		else +			rc = -EACCES; + +		if (saip) +			smack_log(skp->smk_known, tracee_label, 0, rc, saip); + +		return rc; +	} + +	/* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ +	rc = smk_tskacc(tsp, tracee_label, smk_ptrace_mode(mode), saip); +	return rc; +} +  /*   * LSM hooks.   * We he, that is fun! @@ -165,16 +233,15 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,  /**   * smack_ptrace_access_check - Smack approval on PTRACE_ATTACH   * @ctp: child task pointer - * @mode: ptrace attachment mode + * @mode: ptrace attachment mode (PTRACE_MODE_*)   *   * Returns 0 if access is OK, an error code otherwise   * - * Do the capability checks, and require read and write. + * Do the capability checks.   */  static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)  {  	int rc; -	struct smk_audit_info ad;  	struct smack_known *skp;  	rc = cap_ptrace_access_check(ctp, mode); @@ -182,10 +249,8 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)  		return rc;  	skp = smk_of_task(task_security(ctp)); -	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); -	smk_ad_setfield_u_tsk(&ad, ctp); -	rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad); +	rc = smk_ptrace_rule_check(current, skp->smk_known, mode, __func__);  	return rc;  } @@ -195,23 +260,21 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)   *   * Returns 0 if access is OK, an error code otherwise   * - * Do the capability checks, and require read and write. + * Do the capability checks, and require PTRACE_MODE_ATTACH.   */  static int smack_ptrace_traceme(struct task_struct *ptp)  {  	int rc; -	struct smk_audit_info ad;  	struct smack_known *skp;  	rc = cap_ptrace_traceme(ptp);  	if (rc != 0)  		return rc; -	skp = smk_of_task(task_security(ptp)); -	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); -	smk_ad_setfield_u_tsk(&ad, ptp); +	skp = smk_of_task(current_security()); -	rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad); +	rc = smk_ptrace_rule_check(ptp, skp->smk_known, +				   PTRACE_MODE_ATTACH, __func__);  	return rc;  } @@ -219,8 +282,6 @@ static int smack_ptrace_traceme(struct task_struct *ptp)   * smack_syslog - Smack approval on syslog   * @type: message type   * - * Require that the task has the floor label - *   * Returns 0 on success, error code otherwise.   */  static int smack_syslog(int typefrom_file) @@ -231,7 +292,7 @@ static int smack_syslog(int typefrom_file)  	if (smack_privileged(CAP_MAC_OVERRIDE))  		return 0; -	 if (skp != &smack_known_floor) +	if (smack_syslog_label != NULL && smack_syslog_label != skp)  		rc = -EACCES;  	return rc; @@ -341,10 +402,12 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)  	struct inode *inode = root->d_inode;  	struct superblock_smack *sp = sb->s_security;  	struct inode_smack *isp; +	struct smack_known *skp;  	char *op;  	char *commap;  	char *nsp;  	int transmute = 0; +	int specified = 0;  	if (sp->smk_initialized)  		return 0; @@ -359,41 +422,65 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)  		if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) {  			op += strlen(SMK_FSHAT);  			nsp = smk_import(op, 0); -			if (nsp != NULL) +			if (nsp != NULL) {  				sp->smk_hat = nsp; +				specified = 1; +			}  		} else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {  			op += strlen(SMK_FSFLOOR);  			nsp = smk_import(op, 0); -			if (nsp != NULL) +			if (nsp != NULL) {  				sp->smk_floor = nsp; +				specified = 1; +			}  		} else if (strncmp(op, SMK_FSDEFAULT,  				   strlen(SMK_FSDEFAULT)) == 0) {  			op += strlen(SMK_FSDEFAULT);  			nsp = smk_import(op, 0); -			if (nsp != NULL) +			if (nsp != NULL) {  				sp->smk_default = nsp; +				specified = 1; +			}  		} else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) {  			op += strlen(SMK_FSROOT);  			nsp = smk_import(op, 0); -			if (nsp != NULL) +			if (nsp != NULL) {  				sp->smk_root = nsp; +				specified = 1; +			}  		} else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) {  			op += strlen(SMK_FSTRANS);  			nsp = smk_import(op, 0);  			if (nsp != NULL) {  				sp->smk_root = nsp;  				transmute = 1; +				specified = 1;  			}  		}  	} +	if (!smack_privileged(CAP_MAC_ADMIN)) { +		/* +		 * Unprivileged mounts don't get to specify Smack values. +		 */ +		if (specified) +			return -EPERM; +		/* +		 * Unprivileged mounts get root and default from the caller. +		 */ +		skp = smk_of_current(); +		sp->smk_root = skp->smk_known; +		sp->smk_default = skp->smk_known; +	}  	/*  	 * Initialize the root inode.  	 */  	isp = inode->i_security; -	if (inode->i_security == NULL) { -		inode->i_security = new_inode_smack(sp->smk_root); -		isp = inode->i_security; +	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; @@ -423,53 +510,6 @@ static int smack_sb_statfs(struct dentry *dentry)  	return rc;  } -/** - * smack_sb_mount - Smack check for mounting - * @dev_name: unused - * @path: mount point - * @type: unused - * @flags: unused - * @data: unused - * - * Returns 0 if current can write the floor of the filesystem - * being mounted on, an error code otherwise. - */ -static int smack_sb_mount(const char *dev_name, struct path *path, -			  const char *type, unsigned long flags, void *data) -{ -	struct superblock_smack *sbp = path->dentry->d_sb->s_security; -	struct smk_audit_info ad; - -	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); -	smk_ad_setfield_u_fs_path(&ad, *path); - -	return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); -} - -/** - * smack_sb_umount - Smack check for unmounting - * @mnt: file system to unmount - * @flags: unused - * - * Returns 0 if current can write the floor of the filesystem - * being unmounted, an error code otherwise. - */ -static int smack_sb_umount(struct vfsmount *mnt, int flags) -{ -	struct superblock_smack *sbp; -	struct smk_audit_info ad; -	struct path path; - -	path.dentry = mnt->mnt_root; -	path.mnt = mnt; - -	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); -	smk_ad_setfield_u_fs_path(&ad, path); - -	sbp = path.dentry->d_sb->s_security; -	return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); -} -  /*   * BPRM hooks   */ @@ -478,7 +518,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)   * smack_bprm_set_creds - set creds for exec   * @bprm: the exec information   * - * Returns 0 if it gets a blob, -ENOMEM otherwise + * Returns 0 if it gets a blob, -EPERM if exec forbidden and -ENOMEM otherwise   */  static int smack_bprm_set_creds(struct linux_binprm *bprm)  { @@ -498,7 +538,22 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)  	if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)  		return 0; -	if (bprm->unsafe) +	if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { +		struct task_struct *tracer; +		rc = 0; + +		rcu_read_lock(); +		tracer = ptrace_parent(current); +		if (likely(tracer != NULL)) +			rc = smk_ptrace_rule_check(tracer, +						   isp->smk_task->smk_known, +						   PTRACE_MODE_ATTACH, +						   __func__); +		rcu_read_unlock(); + +		if (rc != 0) +			return rc; +	} else if (bprm->unsafe)  		return -EPERM;  	bsp->smk_task = isp->smk_task; @@ -837,31 +892,43 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,  				const void *value, size_t size, int flags)  {  	struct smk_audit_info ad; +	struct smack_known *skp; +	int check_priv = 0; +	int check_import = 0; +	int check_star = 0;  	int rc = 0; +	/* +	 * Check label validity here so import won't fail in post_setxattr +	 */  	if (strcmp(name, XATTR_NAME_SMACK) == 0 ||  	    strcmp(name, XATTR_NAME_SMACKIPIN) == 0 || -	    strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || -	    strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || -	    strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { -		if (!smack_privileged(CAP_MAC_ADMIN)) -			rc = -EPERM; -		/* -		 * check label validity here so import wont fail on -		 * post_setxattr -		 */ -		if (size == 0 || size >= SMK_LONGLABEL || -		    smk_import(value, size) == NULL) -			rc = -EINVAL; +	    strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) { +		check_priv = 1; +		check_import = 1; +	} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || +		   strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { +		check_priv = 1; +		check_import = 1; +		check_star = 1;  	} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { -		if (!smack_privileged(CAP_MAC_ADMIN)) -			rc = -EPERM; +		check_priv = 1;  		if (size != TRANS_TRUE_SIZE ||  		    strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)  			rc = -EINVAL;  	} else  		rc = cap_inode_setxattr(dentry, name, value, size, flags); +	if (check_priv && !smack_privileged(CAP_MAC_ADMIN)) +		rc = -EPERM; + +	if (rc == 0 && check_import) { +		skp = smk_import_entry(value, size); +		if (skp == NULL || (check_star && +		    (skp == &smack_known_star || skp == &smack_known_web))) +			rc = -EINVAL; +	} +  	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);  	smk_ad_setfield_u_fs_path_dentry(&ad, dentry); @@ -893,18 +960,20 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,  		return;  	} -	skp = smk_import_entry(value, size);  	if (strcmp(name, XATTR_NAME_SMACK) == 0) { +		skp = smk_import_entry(value, size);  		if (skp != NULL)  			isp->smk_inode = skp->smk_known;  		else  			isp->smk_inode = smack_known_invalid.smk_known;  	} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { +		skp = smk_import_entry(value, size);  		if (skp != NULL)  			isp->smk_task = skp;  		else  			isp->smk_task = &smack_known_invalid;  	} else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { +		skp = smk_import_entry(value, size);  		if (skp != NULL)  			isp->smk_mmap = skp;  		else @@ -951,24 +1020,37 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)  	    strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||  	    strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||  	    strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 || -	    strcmp(name, XATTR_NAME_SMACKMMAP)) { +	    strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {  		if (!smack_privileged(CAP_MAC_ADMIN))  			rc = -EPERM;  	} else  		rc = cap_inode_removexattr(dentry, name); +	if (rc != 0) +		return rc; +  	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);  	smk_ad_setfield_u_fs_path_dentry(&ad, dentry); -	if (rc == 0) -		rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); -	if (rc == 0) { -		isp = dentry->d_inode->i_security; +	rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); +	if (rc != 0) +		return rc; + +	isp = dentry->d_inode->i_security; +	/* +	 * Don't do anything special for these. +	 *	XATTR_NAME_SMACKIPIN +	 *	XATTR_NAME_SMACKIPOUT +	 *	XATTR_NAME_SMACKEXEC +	 */ +	if (strcmp(name, XATTR_NAME_SMACK) == 0)  		isp->smk_task = NULL; +	else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0)  		isp->smk_mmap = NULL; -	} +	else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) +		isp->smk_flags &= ~SMK_INODE_TRANSMUTE; -	return rc; +	return 0;  }  /** @@ -1013,7 +1095,7 @@ static int smack_inode_getsecurity(const struct inode *inode,  	ssp = sock->sk->sk_security;  	if (strcmp(name, XATTR_SMACK_IPIN) == 0) -		isp = ssp->smk_in; +		isp = ssp->smk_in->smk_known;  	else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)  		isp = ssp->smk_out->smk_known;  	else @@ -1146,7 +1228,7 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,   * @file: the object   * @cmd: unused   * - * Returns 0 if current has write access, error code otherwise + * Returns 0 if current has lock access, error code otherwise   */  static int smack_file_lock(struct file *file, unsigned int cmd)  { @@ -1154,7 +1236,7 @@ static int smack_file_lock(struct file *file, unsigned int cmd)  	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);  	smk_ad_setfield_u_fs_path(&ad, file->f_path); -	return smk_curacc(file->f_security, MAY_WRITE, &ad); +	return smk_curacc(file->f_security, MAY_LOCK, &ad);  }  /** @@ -1178,8 +1260,13 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,  	switch (cmd) {  	case F_GETLK: +		break;  	case F_SETLK:  	case F_SETLKW: +		smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); +		smk_ad_setfield_u_fs_path(&ad, file->f_path); +		rc = smk_curacc(file->f_security, MAY_LOCK, &ad); +		break;  	case F_SETOWN:  	case F_SETSIG:  		smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); @@ -1359,7 +1446,7 @@ static int smack_file_receive(struct file *file)  	int may = 0;  	struct smk_audit_info ad; -	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); +	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);  	smk_ad_setfield_u_fs_path(&ad, file->f_path);  	/*  	 * This code relies on bitmasks. @@ -1375,19 +1462,32 @@ static int smack_file_receive(struct file *file)  /**   * smack_file_open - Smack dentry open processing   * @file: the object - * @cred: unused + * @cred: task credential   *   * Set the security blob in the file structure. + * Allow the open only if the task has read access. There are + * many read operations (e.g. fstat) that you can do with an + * fd even if you have the file open write-only.   *   * Returns 0   */  static int smack_file_open(struct file *file, const struct cred *cred)  { +	struct task_smack *tsp = cred->security;  	struct inode_smack *isp = file_inode(file)->i_security; +	struct smk_audit_info ad; +	int rc; -	file->f_security = isp->smk_inode; +	if (smack_privileged(CAP_MAC_OVERRIDE)) +		return 0; -	return 0; +	smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); +	smk_ad_setfield_u_fs_path(&ad, file->f_path); +	rc = smk_access(tsp->smk_task, isp->smk_inode, MAY_READ, &ad); +	if (rc == 0) +		file->f_security = isp->smk_inode; + +	return rc;  }  /* @@ -1772,7 +1872,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)  	if (ssp == NULL)  		return -ENOMEM; -	ssp->smk_in = skp->smk_known; +	ssp->smk_in = skp;  	ssp->smk_out = skp;  	ssp->smk_packet = NULL; @@ -2012,7 +2112,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,  	if (act == SMK_RECEIVING) {  		skp = smack_net_ambient; -		object = ssp->smk_in; +		object = ssp->smk_in->smk_known;  	} else {  		skp = ssp->smk_out;  		object = smack_net_ambient->smk_known; @@ -2042,9 +2142,9 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,  	list_for_each_entry(spp, &smk_ipv6_port_list, list) {  		if (spp->smk_port != port)  			continue; -		object = spp->smk_in; +		object = spp->smk_in->smk_known;  		if (act == SMK_CONNECTING) -			ssp->smk_packet = spp->smk_out->smk_known; +			ssp->smk_packet = spp->smk_out;  		break;  	} @@ -2084,7 +2184,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,  	int rc = 0;  	if (value == NULL || size > SMK_LONGLABEL || size == 0) -		return -EACCES; +		return -EINVAL;  	skp = smk_import_entry(value, size);  	if (skp == NULL) @@ -2108,7 +2208,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,  	ssp = sock->sk->sk_security;  	if (strcmp(name, XATTR_SMACK_IPIN) == 0) -		ssp->smk_in = skp->smk_known; +		ssp->smk_in = skp;  	else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {  		ssp->smk_out = skp;  		if (sock->sk->sk_family == PF_INET) { @@ -2721,6 +2821,15 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)  	 * of the superblock.  	 */  	if (opt_dentry->d_parent == opt_dentry) { +		if (sbp->s_magic == CGROUP_SUPER_MAGIC) { +			/* +			 * The cgroup filesystem is never mounted, +			 * so there's no opportunity to set the mount +			 * options. +			 */ +			sbsp->smk_root = smack_known_star.smk_known; +			sbsp->smk_default = smack_known_star.smk_known; +		}  		isp->smk_inode = sbsp->smk_root;  		isp->smk_flags |= SMK_INODE_INSTANT;  		goto unlockandout; @@ -2734,16 +2843,20 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)  	 */  	switch (sbp->s_magic) {  	case SMACK_MAGIC: +	case PIPEFS_MAGIC: +	case SOCKFS_MAGIC: +	case CGROUP_SUPER_MAGIC:  		/*  		 * Casey says that it's a little embarrassing  		 * that the smack file system doesn't do  		 * extended attributes. -		 */ -		final = smack_known_star.smk_known; -		break; -	case PIPEFS_MAGIC: -		/* +		 *  		 * Casey says pipes are easy (?) +		 * +		 * Socket access is controlled by the socket +		 * structures associated with the task involved. +		 * +		 * Cgroupfs is special  		 */  		final = smack_known_star.smk_known;  		break; @@ -2755,13 +2868,6 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)  		 */  		final = ckp->smk_known;  		break; -	case SOCKFS_MAGIC: -		/* -		 * Socket access is controlled by the socket -		 * structures associated with the task involved. -		 */ -		final = smack_known_star.smk_known; -		break;  	case PROC_SUPER_MAGIC:  		/*  		 * Casey says procfs appears not to care. @@ -2842,8 +2948,17 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)  			if (rc >= 0)  				transflag = SMK_INODE_TRANSMUTE;  		} -		isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); -		isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); +		/* +		 * Don't let the exec or mmap label be "*" or "@". +		 */ +		skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); +		if (skp == &smack_known_star || skp == &smack_known_web) +			skp = NULL; +		isp->smk_task = skp; +		skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); +		if (skp == &smack_known_star || skp == &smack_known_web) +			skp = NULL; +		isp->smk_mmap = skp;  		dput(dp);  		break; @@ -2958,30 +3073,34 @@ static int smack_unix_stream_connect(struct sock *sock,  				     struct sock *other, struct sock *newsk)  {  	struct smack_known *skp; +	struct smack_known *okp;  	struct socket_smack *ssp = sock->sk_security;  	struct socket_smack *osp = other->sk_security;  	struct socket_smack *nsp = newsk->sk_security;  	struct smk_audit_info ad;  	int rc = 0; -  #ifdef CONFIG_AUDIT  	struct lsm_network_audit net; - -	smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); -	smk_ad_setfield_u_net_sk(&ad, other);  #endif  	if (!smack_privileged(CAP_MAC_OVERRIDE)) {  		skp = ssp->smk_out; -		rc = smk_access(skp, osp->smk_in, MAY_WRITE, &ad); +		okp = osp->smk_out; +#ifdef CONFIG_AUDIT +		smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); +		smk_ad_setfield_u_net_sk(&ad, other); +#endif +		rc = smk_access(skp, okp->smk_known, MAY_WRITE, &ad); +		if (rc == 0) +			rc = smk_access(okp, okp->smk_known, MAY_WRITE, NULL);  	}  	/*  	 * Cross reference the peer labels for SO_PEERSEC.  	 */  	if (rc == 0) { -		nsp->smk_packet = ssp->smk_out->smk_known; -		ssp->smk_packet = osp->smk_out->smk_known; +		nsp->smk_packet = ssp->smk_out; +		ssp->smk_packet = osp->smk_out;  	}  	return rc; @@ -3013,7 +3132,7 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)  		return 0;  	skp = ssp->smk_out; -	return smk_access(skp, osp->smk_in, MAY_WRITE, &ad); +	return smk_access(skp, osp->smk_in->smk_known, MAY_WRITE, &ad);  }  /** @@ -3108,7 +3227,7 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,  		if (found)  			return skp; -		if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known) +		if (ssp != NULL && ssp->smk_in == &smack_known_star)  			return &smack_known_web;  		return &smack_known_star;  	} @@ -3227,7 +3346,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  		 * This is the simplist possible security model  		 * for networking.  		 */ -		rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); +		rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad);  		if (rc != 0)  			netlbl_skbuff_err(skb, rc, 0);  		break; @@ -3262,7 +3381,7 @@ static int smack_socket_getpeersec_stream(struct socket *sock,  	ssp = sock->sk->sk_security;  	if (ssp->smk_packet != NULL) { -		rcp = ssp->smk_packet; +		rcp = ssp->smk_packet->smk_known;  		slen = strlen(rcp) + 1;  	} @@ -3347,7 +3466,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)  		return;  	ssp = sk->sk_security; -	ssp->smk_in = skp->smk_known; +	ssp->smk_in = skp;  	ssp->smk_out = skp;  	/* cssp->smk_packet is already set in smack_inet_csk_clone() */  } @@ -3407,7 +3526,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,  	 * Receiving a packet requires that the other end be able to write  	 * here. Read access is not required.  	 */ -	rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); +	rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad);  	if (rc != 0)  		return rc; @@ -3451,7 +3570,7 @@ static void smack_inet_csk_clone(struct sock *sk,  	if (req->peer_secid != 0) {  		skp = smack_from_secid(req->peer_secid); -		ssp->smk_packet = skp->smk_known; +		ssp->smk_packet = skp;  	} else  		ssp->smk_packet = NULL;  } @@ -3505,11 +3624,12 @@ static void smack_key_free(struct key *key)   * an error code otherwise   */  static int smack_key_permission(key_ref_t key_ref, -				const struct cred *cred, key_perm_t perm) +				const struct cred *cred, unsigned perm)  {  	struct key *keyp;  	struct smk_audit_info ad;  	struct smack_known *tkp = smk_of_task(cred->security); +	int request = 0;  	keyp = key_ref_to_ptr(key_ref);  	if (keyp == NULL) @@ -3530,7 +3650,11 @@ static int smack_key_permission(key_ref_t key_ref,  	ad.a.u.key_struct.key = keyp->serial;  	ad.a.u.key_struct.key_desc = keyp->description;  #endif -	return smk_access(tkp, keyp->security, MAY_READWRITE, &ad); +	if (perm & KEY_NEED_READ) +		request = MAY_READ; +	if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR)) +		request = MAY_WRITE; +	return smk_access(tkp, keyp->security, request, &ad);  }  #endif /* CONFIG_KEYS */ @@ -3615,9 +3739,8 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule,  	struct smack_known *skp;  	char *rule = vrule; -	if (!rule) { -		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, -			  "Smack: missing rule\n"); +	if (unlikely(!rule)) { +		WARN_ONCE(1, "Smack: missing rule\n");  		return -ENOENT;  	} @@ -3738,8 +3861,6 @@ struct security_operations smack_ops = {  	.sb_copy_data = 		smack_sb_copy_data,  	.sb_kern_mount = 		smack_sb_kern_mount,  	.sb_statfs = 			smack_sb_statfs, -	.sb_mount = 			smack_sb_mount, -	.sb_umount = 			smack_sb_umount,  	.bprm_set_creds =		smack_bprm_set_creds,  	.bprm_committing_creds =	smack_bprm_committing_creds, diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 80f4b4a4572..32b24882084 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -52,6 +52,8 @@ enum smk_inos {  	SMK_CIPSO2	= 17,	/* load long label -> CIPSO mapping */  	SMK_REVOKE_SUBJ	= 18,	/* set rules with subject label to '-' */  	SMK_CHANGE_RULE	= 19,	/* change or add rules (long labels) */ +	SMK_SYSLOG	= 20,	/* change syslog label) */ +	SMK_PTRACE	= 21,	/* set ptrace rule */  };  /* @@ -59,6 +61,7 @@ enum smk_inos {   */  static DEFINE_MUTEX(smack_cipso_lock);  static DEFINE_MUTEX(smack_ambient_lock); +static DEFINE_MUTEX(smack_syslog_lock);  static DEFINE_MUTEX(smk_netlbladdr_lock);  /* @@ -90,7 +93,22 @@ int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;   * everyone. It is expected that the hat (^) label   * will be used if any label is used.   */ -char *smack_onlycap; +struct smack_known *smack_onlycap; + +/* + * If this value is set restrict syslog use to the label specified. + * It can be reset via smackfs/syslog + */ +struct smack_known *smack_syslog_label; + +/* + * Ptrace current rule + * SMACK_PTRACE_DEFAULT    regular smack ptrace rules (/proc based) + * SMACK_PTRACE_EXACT      labels must match, but can be overriden with + *			   CAP_SYS_PTRACE + * SMACK_PTRACE_DRACONIAN  lables must match, CAP_SYS_PTRACE has no effect + */ +int smack_ptrace_rule = SMACK_PTRACE_DEFAULT;  /*   * Certain IP addresses may be designated as single label hosts. @@ -139,7 +157,7 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION;   * SMK_LOADLEN: Smack rule length   */  #define SMK_OACCESS	"rwxa" -#define SMK_ACCESS	"rwxat" +#define SMK_ACCESS	"rwxatl"  #define SMK_OACCESSLEN	(sizeof(SMK_OACCESS) - 1)  #define SMK_ACCESSLEN	(sizeof(SMK_ACCESS) - 1)  #define SMK_OLOADLEN	(SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN) @@ -282,6 +300,10 @@ static int smk_perm_from_str(const char *string)  		case 'T':  			perm |= MAY_TRANSMUTE;  			break; +		case 'l': +		case 'L': +			perm |= MAY_LOCK; +			break;  		default:  			return perm;  		} @@ -297,7 +319,8 @@ static int smk_perm_from_str(const char *string)   * @import: if non-zero, import labels   * @len: label length limit   * - * Returns 0 on success, -1 on failure + * Returns 0 on success, -EINVAL on failure and -ENOENT when either subject + * or object is missing.   */  static int smk_fill_rule(const char *subject, const char *object,  				const char *access1, const char *access2, @@ -310,28 +333,28 @@ static int smk_fill_rule(const char *subject, const char *object,  	if (import) {  		rule->smk_subject = smk_import_entry(subject, len);  		if (rule->smk_subject == NULL) -			return -1; +			return -EINVAL;  		rule->smk_object = smk_import(object, len);  		if (rule->smk_object == NULL) -			return -1; +			return -EINVAL;  	} else {  		cp = smk_parse_smack(subject, len);  		if (cp == NULL) -			return -1; +			return -EINVAL;  		skp = smk_find_entry(cp);  		kfree(cp);  		if (skp == NULL) -			return -1; +			return -ENOENT;  		rule->smk_subject = skp;  		cp = smk_parse_smack(object, len);  		if (cp == NULL) -			return -1; +			return -EINVAL;  		skp = smk_find_entry(cp);  		kfree(cp);  		if (skp == NULL) -			return -1; +			return -ENOENT;  		rule->smk_object = skp->smk_known;  	} @@ -377,6 +400,7 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,  {  	ssize_t cnt = 0;  	char *tok[4]; +	int rc;  	int i;  	/* @@ -401,10 +425,8 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,  	while (i < 4)  		tok[i++] = NULL; -	if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0)) -		return -1; - -	return cnt; +	rc = smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0); +	return rc == 0 ? cnt : rc;  }  #define SMK_FIXED24_FMT	0	/* Fixed 24byte label format */ @@ -452,7 +474,7 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,  		/*  		 * Minor hack for backward compatibility  		 */ -		if (count != SMK_OLOADLEN && count != SMK_LOADLEN) +		if (count < SMK_OLOADLEN || count > SMK_LOADLEN)  			return -EINVAL;  	} else {  		if (count >= PAGE_SIZE) { @@ -592,6 +614,8 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)  		seq_putc(s, 'a');  	if (srp->smk_access & MAY_TRANSMUTE)  		seq_putc(s, 't'); +	if (srp->smk_access & MAY_LOCK) +		seq_putc(s, 'l');  	seq_putc(s, '\n');  } @@ -1169,7 +1193,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,  	data[count] = '\0'; -	rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%d %s", +	rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",  		&host[0], &host[1], &host[2], &host[3], &m, smack);  	if (rc != 6) {  		rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s", @@ -1597,7 +1621,7 @@ static const struct file_operations smk_ambient_ops = {  };  /** - * smk_read_onlycap - read() for /smack/onlycap + * smk_read_onlycap - read() for smackfs/onlycap   * @filp: file pointer, not actually used   * @buf: where to put the result   * @cn: maximum to send along @@ -1616,7 +1640,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,  		return 0;  	if (smack_onlycap != NULL) -		smack = smack_onlycap; +		smack = smack_onlycap->smk_known;  	asize = strlen(smack) + 1; @@ -1627,7 +1651,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,  }  /** - * smk_write_onlycap - write() for /smack/onlycap + * smk_write_onlycap - write() for smackfs/onlycap   * @file: file pointer, not actually used   * @buf: where to get the data from   * @count: bytes sent @@ -1650,7 +1674,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,  	 * explicitly for clarity. The smk_access() implementation  	 * would use smk_access(smack_onlycap, MAY_WRITE)  	 */ -	if (smack_onlycap != NULL && smack_onlycap != skp->smk_known) +	if (smack_onlycap != NULL && smack_onlycap != skp)  		return -EPERM;  	data = kzalloc(count, GFP_KERNEL); @@ -1670,7 +1694,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,  	if (copy_from_user(data, buf, count) != 0)  		rc = -EFAULT;  	else -		smack_onlycap = smk_import(data, count); +		smack_onlycap = smk_import_entry(data, count);  	kfree(data);  	return rc; @@ -1850,11 +1874,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,  		res = smk_parse_long_rule(data, &rule, 0, 3);  	} -	if (res < 0) +	if (res >= 0) +		res = smk_access(rule.smk_subject, rule.smk_object, +				 rule.smk_access1, NULL); +	else if (res != -ENOENT)  		return -EINVAL; -	res = smk_access(rule.smk_subject, rule.smk_object, -				rule.smk_access1, NULL);  	data[0] = res == 0 ? '1' : '0';  	data[1] = '\0'; @@ -2137,7 +2162,7 @@ static ssize_t smk_write_change_rule(struct file *file, const char __user *buf,  	/*  	 * Must have privilege.  	 */ -	if (!capable(CAP_MAC_ADMIN)) +	if (!smack_privileged(CAP_MAC_ADMIN))  		return -EPERM;  	return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, @@ -2152,12 +2177,151 @@ static const struct file_operations smk_change_rule_ops = {  };  /** - * smk_fill_super - fill the /smackfs superblock + * smk_read_syslog - read() for smackfs/syslog + * @filp: file pointer, not actually used + * @buf: where to put the result + * @cn: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_syslog(struct file *filp, char __user *buf, +				size_t cn, loff_t *ppos) +{ +	struct smack_known *skp; +	ssize_t rc = -EINVAL; +	int asize; + +	if (*ppos != 0) +		return 0; + +	if (smack_syslog_label == NULL) +		skp = &smack_known_star; +	else +		skp = smack_syslog_label; + +	asize = strlen(skp->smk_known) + 1; + +	if (cn >= asize) +		rc = simple_read_from_buffer(buf, cn, ppos, skp->smk_known, +						asize); + +	return rc; +} + +/** + * smk_write_syslog - write() for smackfs/syslog + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_syslog(struct file *file, const char __user *buf, +				size_t count, loff_t *ppos) +{ +	char *data; +	struct smack_known *skp; +	int rc = count; + +	if (!smack_privileged(CAP_MAC_ADMIN)) +		return -EPERM; + +	data = kzalloc(count, GFP_KERNEL); +	if (data == NULL) +		return -ENOMEM; + +	if (copy_from_user(data, buf, count) != 0) +		rc = -EFAULT; +	else { +		skp = smk_import_entry(data, count); +		if (skp == NULL) +			rc = -EINVAL; +		else +			smack_syslog_label = smk_import_entry(data, count); +	} + +	kfree(data); +	return rc; +} + +static const struct file_operations smk_syslog_ops = { +	.read		= smk_read_syslog, +	.write		= smk_write_syslog, +	.llseek		= default_llseek, +}; + + +/** + * smk_read_ptrace - read() for /smack/ptrace + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_ptrace(struct file *filp, char __user *buf, +			       size_t count, loff_t *ppos) +{ +	char temp[32]; +	ssize_t rc; + +	if (*ppos != 0) +		return 0; + +	sprintf(temp, "%d\n", smack_ptrace_rule); +	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); +	return rc; +} + +/** + * smk_write_ptrace - write() for /smack/ptrace + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_ptrace(struct file *file, const char __user *buf, +				size_t count, loff_t *ppos) +{ +	char temp[32]; +	int i; + +	if (!smack_privileged(CAP_MAC_ADMIN)) +		return -EPERM; + +	if (*ppos != 0 || count >= sizeof(temp) || count == 0) +		return -EINVAL; + +	if (copy_from_user(temp, buf, count) != 0) +		return -EFAULT; + +	temp[count] = '\0'; + +	if (sscanf(temp, "%d", &i) != 1) +		return -EINVAL; +	if (i < SMACK_PTRACE_DEFAULT || i > SMACK_PTRACE_MAX) +		return -EINVAL; +	smack_ptrace_rule = i; + +	return count; +} + +static const struct file_operations smk_ptrace_ops = { +	.write		= smk_write_ptrace, +	.read		= smk_read_ptrace, +	.llseek		= default_llseek, +}; + +/** + * smk_fill_super - fill the smackfs superblock   * @sb: the empty superblock   * @data: unused   * @silent: unused   * - * Fill in the well known entries for /smack + * Fill in the well known entries for the smack filesystem   *   * Returns 0 on success, an error code on failure   */ @@ -2202,6 +2366,10 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)  			S_IRUGO|S_IWUSR},  		[SMK_CHANGE_RULE] = {  			"change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR}, +		[SMK_SYSLOG] = { +			"syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR}, +		[SMK_PTRACE] = { +			"ptrace", &smk_ptrace_ops, S_IRUGO|S_IWUSR},  		/* last one */  			{""}  	}; diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 80a09c37cac..a3386d11942 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -173,7 +173,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,  		 * Use filesystem name if filesystem does not support rename()  		 * operation.  		 */ -		if (inode->i_op && !inode->i_op->rename) +		if (!inode->i_op->rename)  			goto prepend_filesystem_name;  	}  	/* Prepend device name. */ @@ -282,7 +282,7 @@ char *tomoyo_realpath_from_path(struct path *path)  		 * Get local name for filesystems without rename() operation  		 * or dentry without vfsmount.  		 */ -		if (!path->mnt || (inode->i_op && !inode->i_op->rename)) +		if (!path->mnt || !inode->i_op->rename)  			pos = tomoyo_get_local_path(path->dentry, buf,  						    buf_len - 1);  		/* Get absolute name for the rest. */  | 
