[PATCH 04/22] CRED: Request a credential record for a kernel service

From: David Howells
Date: Fri Sep 21 2007 - 15:50:20 EST


Request a credential record for the named kernel service. This produces a
cred struct with appropriate DAC and MAC controls for effecting that service.
It may be used to override the credentials on a task to do work on that task's
behalf.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

include/linux/cred.h | 2 +
include/linux/security.h | 45 ++++++++++++++++++++++++++++++
kernel/cred.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++
security/dummy.c | 13 +++++++++
security/selinux/hooks.c | 47 ++++++++++++++++++++++++++++++++
5 files changed, 175 insertions(+), 0 deletions(-)

diff --git a/include/linux/cred.h b/include/linux/cred.h
index f2df1c3..fcbfc89 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -49,6 +49,8 @@ extern void change_fsgid(struct cred *, gid_t);
extern void change_groups(struct cred *, struct group_info *);
extern void change_cap(struct cred *, kernel_cap_t);
extern struct cred *dup_cred(const struct cred *);
+extern struct cred *get_kernel_cred(const char *, struct task_struct *);
+extern int change_create_files_as(struct cred *, struct inode *);

/**
* get_cred - Get an extra reference on a credentials record
diff --git a/include/linux/security.h b/include/linux/security.h
index 74cc204..7f11e6d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -514,6 +514,20 @@ struct request_sock;
* @cred_destroy:
* Destroy the credentials attached to a cred structure.
* @cred points to the credentials structure that is to be destroyed.
+ * @cred_kernel_act_as:
+ * Set the credentials for a kernel service to act as (subjective context).
+ * @cred points to the credentials structure to be filled in.
+ * @service names the service making the request.
+ * @daemon: A userspace daemon to be used as a base for the context.
+ * @dentry: A file or dir to be used as a base for the file creation
+ * context.
+ * Return 0 if successful.
+ * @cred_create_files_as:
+ * Set the file creation context in a credentials record to be the same as
+ * the objective context of an inode.
+ * @cred points to the credentials structure to be altered.
+ * @inode points to the inode to use as a reference.
+ * Return 0 if successful.
*
* Security hooks for task operations.
*
@@ -1270,6 +1284,9 @@ struct security_operations {

int (*cred_dup)(struct cred *cred);
void (*cred_destroy)(struct cred *cred);
+ int (*cred_kernel_act_as)(struct cred *cred, const char *service,
+ struct task_struct *daemon);
+ int (*cred_create_files_as)(struct cred *cred, struct inode *inode);

int (*task_create) (unsigned long clone_flags);
int (*task_alloc_security) (struct task_struct * p);
@@ -1888,6 +1905,21 @@ static inline void security_cred_destroy(struct cred *cred)
return security_ops->cred_destroy(cred);
}

+static inline int security_cred_kernel_act_as(struct cred *cred,
+ const char *service,
+ struct task_struct *daemon)
+{
+ return security_ops->cred_kernel_act_as(cred, service, daemon);
+}
+
+static inline int security_cred_create_files_as(struct cred *cred,
+ struct inode *inode)
+{
+ if (IS_PRIVATE(inode))
+ return -EINVAL;
+ return security_ops->cred_create_files_as(cred, inode);
+}
+
static inline int security_task_create (unsigned long clone_flags)
{
return security_ops->task_create (clone_flags);
@@ -2579,6 +2611,19 @@ static inline void security_cred_destroy(struct cred *cred)
{
}

+static inline int security_cred_kernel_act_as(struct cred *cred,
+ const char *service,
+ struct task_struct *daemon)
+{
+ return 0;
+}
+
+static inline int security_cred_create_files_as(struct cred *cred,
+ struct inode *inode)
+{
+ return 0;
+}
+
static inline int security_task_create (unsigned long clone_flags)
{
return 0;
diff --git a/kernel/cred.c b/kernel/cred.c
index c391f66..46a6661 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -206,3 +206,71 @@ void change_cap(struct cred *cred, kernel_cap_t cap)
}

EXPORT_SYMBOL(change_cap);
+
+/**
+ * get_kernel_cred - Get credentials for a named kernel service
+ * @service: The name of the service
+ * @daemon: A userspace daemon to be used as a base for the context
+ *
+ * Get a set of credentials for a specific kernel service. These can then be
+ * used to override a task's credentials so that work can be done on behalf of
+ * that task.
+ *
+ * @daemon is used to provide a base for the security context, but can be NULL.
+ * If @deamon is supplied, then the cred's uid, gid and groups list will be
+ * derived from that; otherwise they'll be set to 0 and no groups.
+ *
+ * @daemon is also passd to the LSM module as a base from which to initialise
+ * any MAC controls.
+ *
+ * The caller may change these controls afterwards if desired.
+ */
+struct cred *get_kernel_cred(const char *service,
+ struct task_struct *daemon)
+{
+ struct cred *cred, *dcred;
+ int ret;
+
+ cred = kzalloc(sizeof *cred, GFP_KERNEL);
+ if (!cred)
+ return ERR_PTR(-ENOMEM);
+
+ if (daemon) {
+ rcu_read_lock();
+ dcred = task_cred(daemon);
+ cred->uid = dcred->uid;
+ cred->gid = dcred->gid;
+ cred->group_info = dcred->group_info;
+ atomic_inc(&cred->group_info->usage);
+ rcu_read_unlock();
+ } else {
+ cred->group_info = &init_groups;
+ atomic_inc(&init_groups.usage);
+ }
+
+ ret = security_cred_kernel_act_as(cred, service, daemon);
+ if (ret < 0) {
+ put_cred(cred);
+ return ERR_PTR(ret);
+ }
+
+ return cred;
+}
+
+EXPORT_SYMBOL(get_kernel_cred);
+
+/**
+ * change_create_files_as - Change the file creation context in a new cred record
+ * @cred: The credential record to alter
+ * @inode: The inode to take the context from
+ *
+ * Change the file creation context in a new credentials record to be the same
+ * as the object context of the specified inode, so that the new inodes have
+ * the same MAC context as that inode.
+ */
+int change_create_files_as(struct cred *cred, struct inode *inode)
+{
+ return security_cred_create_files_as(cred, inode);
+}
+
+EXPORT_SYMBOL(change_create_files_as);
diff --git a/security/dummy.c b/security/dummy.c
index f403658..004bb21 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -488,6 +488,17 @@ static void dummy_cred_destroy(struct cred *cred)
{
}

+static int dummy_cred_kernel_act_as(struct cred *cred, const char *service,
+ struct task_struct *daemon)
+{
+ return 0;
+}
+
+static int dummy_cred_create_files_as(struct cred *cred, struct inode *inode)
+{
+ return 0;
+}
+
static int dummy_task_create (unsigned long clone_flags)
{
return 0;
@@ -1063,6 +1074,8 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, file_receive);
set_to_dummy_if_null(ops, cred_dup);
set_to_dummy_if_null(ops, cred_destroy);
+ set_to_dummy_if_null(ops, cred_kernel_act_as);
+ set_to_dummy_if_null(ops, cred_create_files_as);
set_to_dummy_if_null(ops, task_create);
set_to_dummy_if_null(ops, task_alloc_security);
set_to_dummy_if_null(ops, task_free_security);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 4e72dbb..fa11211 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2771,6 +2771,51 @@ static void selinux_cred_destroy(struct cred *cred)
kfree(cred->security);
}

+/*
+ * get the credentials for a kernel service, deriving the subjective context
+ * from the credentials of a userspace daemon if one supplied
+ * - all the creation contexts are set to unlabelled
+ */
+static int selinux_cred_kernel_act_as(struct cred *cred,
+ const char *service,
+ struct task_struct *daemon)
+{
+ struct task_security_struct *tsec;
+ struct cred_security_struct *csec;
+ u32 ksid;
+ int ret;
+
+ tsec = daemon ? daemon->security : init_task.security;
+
+ ret = security_transition_sid(tsec->victim_sid, SECINITSID_KERNEL,
+ SECCLASS_PROCESS, &ksid);
+ if (ret < 0)
+ return ret;
+
+ csec = kzalloc(sizeof(struct cred_security_struct), GFP_KERNEL);
+ if (!csec)
+ return -ENOMEM;
+
+ csec->action_sid = ksid;
+ csec->create_sid = SECINITSID_UNLABELED;
+ csec->keycreate_sid = SECINITSID_UNLABELED;
+ csec->sockcreate_sid = SECINITSID_UNLABELED;
+ cred->security = csec;
+ return 0;
+}
+
+/*
+ * set the file creation context in a credentials record to the same as the
+ * objective context of the specified inode
+ */
+static int selinux_cred_create_files_as(struct cred *cred, struct inode *inode)
+{
+ struct cred_security_struct *csec = cred->security;
+ struct inode_security_struct *isec = inode->i_security;
+
+ csec->create_sid = isec->sid;
+ return 0;
+}

/* task security operations */

@@ -4887,6 +4932,8 @@ static struct security_operations selinux_ops = {

.cred_dup = selinux_cred_dup,
.cred_destroy = selinux_cred_destroy,
+ .cred_kernel_act_as = selinux_cred_kernel_act_as,
+ .cred_create_files_as = selinux_cred_create_files_as,

.task_create = selinux_task_create,
.task_alloc_security = selinux_task_alloc_security,

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/