Commit c1a85a00 authored by Micah Morton's avatar Micah Morton Committed by James Morris
Browse files

LSM: generalize flag passing to security_capable



This patch provides a general mechanism for passing flags to the
security_capable LSM hook. It replaces the specific 'audit' flag that is
used to tell security_capable whether it should log an audit message for
the given capability check. The reason for generalizing this flag
passing is so we can add an additional flag that signifies whether
security_capable is being called by a setid syscall (which is needed by
the proposed SafeSetID LSM).

Signed-off-by: default avatarMicah Morton <mortonm@chromium.org>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarJames Morris <james.morris@microsoft.com>
parent 2233975c
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -1270,7 +1270,7 @@
 *	@cred contains the credentials to use.
 *	@ns contains the user namespace we want the capability in
 *	@cap contains the capability <include/linux/capability.h>.
 *	@audit contains whether to write an audit message or not
 *	@opts contains options for the capable check <include/linux/security.h>
 *	Return 0 if the capability is granted for @tsk.
 * @syslog:
 *	Check permission before accessing the kernel message ring or changing
@@ -1446,8 +1446,10 @@ union security_list_options {
			const kernel_cap_t *effective,
			const kernel_cap_t *inheritable,
			const kernel_cap_t *permitted);
	int (*capable)(const struct cred *cred, struct user_namespace *ns,
			int cap, int audit);
	int (*capable)(const struct cred *cred,
			struct user_namespace *ns,
			int cap,
			unsigned int opts);
	int (*quotactl)(int cmds, int type, int id, struct super_block *sb);
	int (*quota_on)(struct dentry *dentry);
	int (*syslog)(int type);
+14 −14
Original line number Diff line number Diff line
@@ -54,9 +54,12 @@ struct xattr;
struct xfrm_sec_ctx;
struct mm_struct;

/* Default (no) options for the capable function */
#define CAP_OPT_NONE 0x0
/* If capable should audit the security request */
#define SECURITY_CAP_NOAUDIT 0
#define SECURITY_CAP_AUDIT 1
#define CAP_OPT_NOAUDIT BIT(1)
/* If capable is being called by a setid function */
#define CAP_OPT_INSETID BIT(2)

/* LSM Agnostic defines for sb_set_mnt_opts */
#define SECURITY_LSM_NATIVE_LABELS	1
@@ -72,7 +75,7 @@ enum lsm_event {

/* These functions are in security/commoncap.c */
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
		       int cap, int audit);
		       int cap, unsigned int opts);
extern int cap_settime(const struct timespec64 *ts, const struct timezone *tz);
extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode);
extern int cap_ptrace_traceme(struct task_struct *parent);
@@ -207,10 +210,10 @@ int security_capset(struct cred *new, const struct cred *old,
		    const kernel_cap_t *effective,
		    const kernel_cap_t *inheritable,
		    const kernel_cap_t *permitted);
int security_capable(const struct cred *cred, struct user_namespace *ns,
			int cap);
int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns,
			     int cap);
int security_capable(const struct cred *cred,
		       struct user_namespace *ns,
		       int cap,
		       unsigned int opts);
int security_quotactl(int cmds, int type, int id, struct super_block *sb);
int security_quota_on(struct dentry *dentry);
int security_syslog(int type);
@@ -464,14 +467,11 @@ static inline int security_capset(struct cred *new,
}

static inline int security_capable(const struct cred *cred,
				   struct user_namespace *ns, int cap)
				   struct user_namespace *ns,
				   int cap,
				   unsigned int opts)
{
	return cap_capable(cred, ns, cap, SECURITY_CAP_AUDIT);
}

static inline int security_capable_noaudit(const struct cred *cred,
					   struct user_namespace *ns, int cap) {
	return cap_capable(cred, ns, cap, SECURITY_CAP_NOAUDIT);
	return cap_capable(cred, ns, cap, opts);
}

static inline int security_quotactl(int cmds, int type, int id,
+13 −9
Original line number Diff line number Diff line
@@ -299,7 +299,7 @@ bool has_ns_capability(struct task_struct *t,
	int ret;

	rcu_read_lock();
	ret = security_capable(__task_cred(t), ns, cap);
	ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NONE);
	rcu_read_unlock();

	return (ret == 0);
@@ -340,7 +340,7 @@ bool has_ns_capability_noaudit(struct task_struct *t,
	int ret;

	rcu_read_lock();
	ret = security_capable_noaudit(__task_cred(t), ns, cap);
	ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NOAUDIT);
	rcu_read_unlock();

	return (ret == 0);
@@ -363,7 +363,9 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
	return has_ns_capability_noaudit(t, &init_user_ns, cap);
}

static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
static bool ns_capable_common(struct user_namespace *ns,
			      int cap,
			      unsigned int opts)
{
	int capable;

@@ -372,8 +374,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
		BUG();
	}

	capable = audit ? security_capable(current_cred(), ns, cap) :
			  security_capable_noaudit(current_cred(), ns, cap);
	capable = security_capable(current_cred(), ns, cap, opts);
	if (capable == 0) {
		current->flags |= PF_SUPERPRIV;
		return true;
@@ -394,7 +395,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
 */
bool ns_capable(struct user_namespace *ns, int cap)
{
	return ns_capable_common(ns, cap, true);
	return ns_capable_common(ns, cap, CAP_OPT_NONE);
}
EXPORT_SYMBOL(ns_capable);

@@ -412,7 +413,7 @@ EXPORT_SYMBOL(ns_capable);
 */
bool ns_capable_noaudit(struct user_namespace *ns, int cap)
{
	return ns_capable_common(ns, cap, false);
	return ns_capable_common(ns, cap, CAP_OPT_NOAUDIT);
}
EXPORT_SYMBOL(ns_capable_noaudit);

@@ -448,10 +449,11 @@ EXPORT_SYMBOL(capable);
bool file_ns_capable(const struct file *file, struct user_namespace *ns,
		     int cap)
{

	if (WARN_ON_ONCE(!cap_valid(cap)))
		return false;

	if (security_capable(file->f_cred, ns, cap) == 0)
	if (security_capable(file->f_cred, ns, cap, CAP_OPT_NONE) == 0)
		return true;

	return false;
@@ -500,10 +502,12 @@ bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
{
	int ret = 0;  /* An absent tracer adds no restrictions */
	const struct cred *cred;

	rcu_read_lock();
	cred = rcu_dereference(tsk->ptracer_cred);
	if (cred)
		ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
		ret = security_capable(cred, ns, CAP_SYS_PTRACE,
				       CAP_OPT_NOAUDIT);
	rcu_read_unlock();
	return (ret == 0);
}
+2 −2
Original line number Diff line number Diff line
@@ -443,8 +443,8 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
	 * behavior of privileged children.
	 */
	if (!task_no_new_privs(current) &&
	    security_capable_noaudit(current_cred(), current_user_ns(),
				     CAP_SYS_ADMIN) != 0)
	    security_capable(current_cred(), current_user_ns(),
				     CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) != 0)
		return ERR_PTR(-EACCES);

	/* Allocate a new seccomp_filter */
+7 −7
Original line number Diff line number Diff line
@@ -110,13 +110,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
 * profile_capable - test if profile allows use of capability @cap
 * @profile: profile being enforced    (NOT NULL, NOT unconfined)
 * @cap: capability to test if allowed
 * @audit: whether an audit record should be generated
 * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
 * @sa: audit data (MAY BE NULL indicating no auditing)
 *
 * Returns: 0 if allowed else -EPERM
 */
static int profile_capable(struct aa_profile *profile, int cap, int audit,
			   struct common_audit_data *sa)
static int profile_capable(struct aa_profile *profile, int cap,
			   unsigned int opts, struct common_audit_data *sa)
{
	int error;

@@ -126,7 +126,7 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit,
	else
		error = -EPERM;

	if (audit == SECURITY_CAP_NOAUDIT) {
	if (opts & CAP_OPT_NOAUDIT) {
		if (!COMPLAIN_MODE(profile))
			return error;
		/* audit the cap request in complain mode but note that it
@@ -142,13 +142,13 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit,
 * aa_capable - test permission to use capability
 * @label: label being tested for capability (NOT NULL)
 * @cap: capability to be tested
 * @audit: whether an audit record should be generated
 * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
 *
 * Look up capability in profile capability set.
 *
 * Returns: 0 on success, or else an error code.
 */
int aa_capable(struct aa_label *label, int cap, int audit)
int aa_capable(struct aa_label *label, int cap, unsigned int opts)
{
	struct aa_profile *profile;
	int error = 0;
@@ -156,7 +156,7 @@ int aa_capable(struct aa_label *label, int cap, int audit)

	sa.u.cap = cap;
	error = fn_for_each_confined(label, profile,
			profile_capable(profile, cap, audit, &sa));
			profile_capable(profile, cap, opts, &sa));

	return error;
}
Loading