[Devel] Re: [PATCH linux-cr] cr: add selinux support (v6)
Oren Laadan
orenl at cs.columbia.edu
Tue Dec 22 16:25:59 PST 2009
Looks ok to my near-zero-SELinux-knowledge eyes.
Will add to v19-rc3.
Oren.
Serge E. Hallyn wrote:
> Documentation/checkpoint/readme.txt begins:
> """
> Application checkpoint/restart is the ability to save the state
> of a running application so that it can later resume its execution
> from the time at which it was checkpointed.
> """
>
> This patch adds the ability to checkpoint and restore selinux
> contexts for tasks, open files, and sysvipc objects. Contexts
> are checkpointed as strings. For tasks and files, where a security
> struct actually points to several contexts, all contexts are
> written out in one string, separated by ':::'.
>
> The default behaviors are to checkpoint contexts, but not to
> restore them. To attempt to restore them, sys_restart() must
> be given the RESTART_KEEP_LSM flag. If this is given then
> the caller of sys_restart() must have the new 'restore' permission
> to the target objclass, or for instance PROCESS__SETFSCREATE to
> itself to specify a create_sid.
>
> There are some tests under cr_tests/selinux at
> git://git.sr71.net/~hallyn/cr_tests.git.
>
> A corresponding simple refpolicy (and /usr/share/selinux/devel/include)
> patch is needed.
>
> The programs to checkpoint and restart (called 'checkpoint' and
> 'restart') come from git://git.ncl.cs.columbia.edu/pub/git/user-cr.git.
> This patch applies against the checkpoint/restart-enabled kernel
> tree at git://git.ncl.cs.columbia.edu/pub/git/linux-cr.git/.
>
> Changelog:
> Dec 09: update to use common_audit_data.
> oct 09: fix memory overrun in selinux_cred_checkpoint.
> oct 02: (Stephen Smalley suggestions):
> 1. s/__u32/u32/
> 2. enable the fown sid restoration
> 3. use process_restore to authorize resetting osid
> 4. don't make new hooks inline.
> oct 01: Remove some debugging that is redundant with
> avc log data.
> sep 10: (Most addressing suggestions by Stephen Smalley)
> 1. change xyz_get_ctx() to xyz_checkpoint().
> 2. check entrypoint permission on cred_restore
> 3. always dec context length by 1
> 4. don't allow SECSID_NULL when that's not valid
> 5. when SECSID_NULL is valid, restore it
> 6. c/r task->osid
> 7. Just print nothing instead of 'null' for SECSID_NULL
> 8. sids are __u32, as are lenghts passed to sid_to_context.
>
> Signed-off-by: Serge E. Hallyn <serue at us.ibm.com>
> ---
> checkpoint/restart.c | 1 +
> security/selinux/hooks.c | 366 ++++++++++++++++++++++++++
> security/selinux/include/av_perm_to_string.h | 5 +
> security/selinux/include/av_permissions.h | 5 +
> 4 files changed, 377 insertions(+), 0 deletions(-)
>
> diff --git a/checkpoint/restart.c b/checkpoint/restart.c
> index 88d791b..cc8e68d 100644
> --- a/checkpoint/restart.c
> +++ b/checkpoint/restart.c
> @@ -665,6 +665,7 @@ static int restore_lsm(struct ckpt_ctx *ctx)
>
> if (strcmp(ctx->lsm_name, "lsm_none") != 0 &&
> strcmp(ctx->lsm_name, "smack") != 0 &&
> + strcmp(ctx->lsm_name, "selinux") != 0 &&
> strcmp(ctx->lsm_name, "default") != 0) {
> ckpt_debug("c/r: RESTART_KEEP_LSM unsupported for %s\n",
> ctx->lsm_name);
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index bb230d5..cbcdea1 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -76,6 +76,7 @@
> #include <linux/selinux.h>
> #include <linux/mutex.h>
> #include <linux/posix-timers.h>
> +#include <linux/checkpoint.h>
>
> #include "avc.h"
> #include "objsec.h"
> @@ -2979,6 +2980,104 @@ static int selinux_file_permission(struct file *file, int mask)
> return selinux_revalidate_file_permission(file, mask);
> }
>
> +/*
> + * for file context, we print both the fsec->sid and fsec->fown_sid
> + * as string representations, separated by ':::'
> + * We don't touch isid - if you wanted that set you shoulda set up the
> + * fs correctly.
> + */
> +static char *selinux_file_checkpoint(void *security)
> +{
> + struct file_security_struct *fsec = security;
> + char *s1 = NULL, *s2 = NULL, *sfull;
> + u32 len1, len2, lenfull;
> + int ret;
> +
> + if (fsec->sid == 0 || fsec->fown_sid == 0)
> + return ERR_PTR(-EINVAL);
> +
> + ret = security_sid_to_context(fsec->sid, &s1, &len1);
> + if (ret)
> + return ERR_PTR(ret);
> + len1--;
> + ret = security_sid_to_context(fsec->fown_sid, &s2, &len2);
> + if (ret) {
> + kfree(s1);
> + return ERR_PTR(ret);
> + }
> + len2--;
> + lenfull = len1 + len2 + 3;
> + sfull = kmalloc(lenfull + 1, GFP_KERNEL);
> + if (!sfull) {
> + sfull = ERR_PTR(-ENOMEM);
> + goto out;
> + }
> + sfull[lenfull] = '\0';
> + sprintf(sfull, "%s:::%s", s1, s2);
> +
> +out:
> + kfree(s1);
> + kfree(s2);
> + return sfull;
> +}
> +
> +static int selinux_file_restore(struct file *file, char *ctx)
> +{
> + char *s1, *s2;
> + u32 sid1 = 0, sid2 = 0;
> + int ret = -EINVAL;
> + struct file_security_struct *fsec = file->f_security;
> +
> + /*
> + * Objhash made sure the string is null-terminated.
> + * We make a copy so we can mangle it.
> + */
> + s1 = kstrdup(ctx, GFP_KERNEL);
> + if (!s1)
> + return -ENOMEM;
> + s2 = strstr(s1, ":::");
> + if (!s2)
> + goto out;
> +
> + *s2 = '\0';
> + s2 += 3;
> + if (*s2 == '\0')
> + goto out;
> +
> + /* SECSID_NULL is not valid for file sids */
> + if (strlen(s1) == 0 || strlen(s2) == 0)
> + goto out;
> +
> + ret = security_context_to_sid(s1, strlen(s1), &sid1);
> + if (ret)
> + goto out;
> + ret = security_context_to_sid(s2, strlen(s2), &sid2);
> + if (ret)
> + goto out;
> +
> + if (sid1 && fsec->sid != sid1) {
> + ret = avc_has_perm(current_sid(), sid1, SECCLASS_FILE,
> + FILE__RESTORE, NULL);
> + if (ret)
> + goto out;
> + fsec->sid = sid1;
> + }
> +
> + if (sid2 && fsec->fown_sid != sid2) {
> + ret = avc_has_perm(current_sid(), sid2, SECCLASS_FILE,
> + FILE__FOWN_RESTORE, NULL);
> + if (ret)
> + goto out;
> + fsec->fown_sid = sid2;
> + }
> +
> + ret = 0;
> +
> +out:
> + kfree(s1);
> + return ret;
> +}
> +
> static int selinux_file_alloc_security(struct file *file)
> {
> return file_alloc_security(file);
> @@ -3237,6 +3336,186 @@ static int selinux_task_create(unsigned long clone_flags)
> return current_has_perm(current, PROCESS__FORK);
> }
>
> +#define NUMTASKSIDS 6
> +/*
> + * for cred context, we print:
> + * osid, sid, exec_sid, create_sid, keycreate_sid, sockcreate_sid;
> + * as string representations, separated by ':::'
> + */
> +static char *selinux_cred_checkpoint(void *security)
> +{
> + struct task_security_struct *tsec = security;
> + char *stmp, *sfull = NULL;
> + u32 slen, runlen;
> + int i, ret;
> + u32 sids[NUMTASKSIDS] = { tsec->osid, tsec->sid, tsec->exec_sid,
> + tsec->create_sid, tsec->keycreate_sid, tsec->sockcreate_sid };
> +
> + if (sids[0] == 0 || sids[1] == 0)
> + /* SECSID_NULL is not valid for osid or sid */
> + return ERR_PTR(-EINVAL);
> +
> + ret = security_sid_to_context(sids[0], &sfull, &runlen);
> + if (ret)
> + return ERR_PTR(ret);
> + runlen--;
> +
> + for (i = 1; i < NUMTASKSIDS; i++) {
> + if (sids[i] == 0) {
> + stmp = NULL;
> + slen = 0;
> + } else {
> + ret = security_sid_to_context(sids[i], &stmp, &slen);
> + if (ret) {
> + kfree(sfull);
> + return ERR_PTR(ret);
> + }
> + slen--;
> + }
> + /* slen + runlen + ':::' + \0 */
> + sfull = krealloc(sfull, slen + runlen + 3 + 1,
> + GFP_KERNEL);
> + if (!sfull) {
> + kfree(stmp);
> + return ERR_PTR(-ENOMEM);
> + }
> + sprintf(sfull+runlen, ":::%s", stmp ? stmp : "");
> + runlen += slen + 3;
> + kfree(stmp);
> + }
> +
> + return sfull;
> +}
> +
> +static inline int credrestore_nullvalid(int which)
> +{
> + int valid_array[NUMTASKSIDS] = {
> + 0, /* task osid */
> + 0, /* task sid */
> + 1, /* exec sid */
> + 1, /* create sid */
> + 1, /* keycreate_sid */
> + 1, /* sockcreate_sid */
> + };
> +
> + return valid_array[which];
> +}
> +
> +static int selinux_cred_restore(struct file *file, struct cred *cred,
> + char *ctx)
> +{
> + char *s, *s1, *s2 = NULL;
> + int ret = -EINVAL;
> + struct task_security_struct *tsec = cred->security;
> + int i;
> + u32 sids[NUMTASKSIDS];
> + struct inode *ctx_inode = file->f_dentry->d_inode;
> + struct common_audit_data ad;
> +
> + /*
> + * objhash made sure the string is null-terminated
> + * now we want our own copy so we can chop it up with \0's
> + */
> + s = kstrdup(ctx, GFP_KERNEL);
> + if (!s)
> + return -ENOMEM;
> +
> + s1 = s;
> + for (i = 0; i < NUMTASKSIDS; i++) {
> + if (i < NUMTASKSIDS-1) {
> + ret = -EINVAL;
> + s2 = strstr(s1, ":::");
> + if (!s2)
> + goto out;
> + *s2 = '\0';
> + s2 += 3;
> + }
> + if (strlen(s1) == 0) {
> + ret = -EINVAL;
> + if (credrestore_nullvalid(i))
> + sids[i] = 0;
> + else
> + goto out;
> + } else {
> + ret = security_context_to_sid(s1, strlen(s1), &sids[i]);
> + if (ret)
> + goto out;
> + }
> + s1 = s2;
> + }
> +
> + /*
> + * Check that these transitions are allowed, and effect them.
> + * XXX: Do these checks suffice?
> + */
> + if (tsec->osid != sids[0]) {
> + ret = avc_has_perm(current_sid(), sids[0], SECCLASS_PROCESS,
> + PROCESS__RESTORE, NULL);
> + if (ret)
> + goto out;
> + tsec->osid = sids[0];
> + }
> +
> + if (tsec->sid != sids[1]) {
> + struct inode_security_struct *isec;
> + ret = avc_has_perm(current_sid(), sids[1], SECCLASS_PROCESS,
> + PROCESS__RESTORE, NULL);
> + if (ret)
> + goto out;
> +
> + /* check whether checkpoint file type is a valid entry
> + * point to the new domain: we may want a specific
> + * 'restore_entrypoint' permission for this, but let's
> + * see if just entrypoint is deemed sufficient
> + */
> +
> + COMMON_AUDIT_DATA_INIT(&ad, FS);
> + ad.u.fs.path = file->f_path;
> +
> + isec = ctx_inode->i_security;
> + ret = avc_has_perm(sids[1], isec->sid, SECCLASS_FILE,
> + FILE__ENTRYPOINT, &ad);
> + if (ret)
> + goto out;
> + /* TODO: do we need to check for shared state? */
> + tsec->sid = sids[1];
> + }
> +
> + ret = -EPERM;
> + if (sids[2] != tsec->exec_sid) {
> + if (!current_has_perm(current, PROCESS__SETEXEC))
> + goto out;
> + tsec->exec_sid = sids[2];
> + }
> +
> + if (sids[3] != tsec->create_sid) {
> + if (!current_has_perm(current, PROCESS__SETFSCREATE))
> + goto out;
> + tsec->create_sid = sids[3];
> + }
> +
> + if (tsec->keycreate_sid != sids[4]) {
> + if (!current_has_perm(current, PROCESS__SETKEYCREATE))
> + goto out;
> + if (!may_create_key(sids[4], current))
> + goto out;
> + tsec->keycreate_sid = sids[4];
> + }
> +
> + if (tsec->sockcreate_sid != sids[5]) {
> + if (!current_has_perm(current, PROCESS__SETSOCKCREATE))
> + goto out;
> + tsec->sockcreate_sid = sids[5];
> + }
> +
> + ret = 0;
> +
> +out:
> + kfree(s);
> + return ret;
> +}
> +
> +
> /*
> * allocate the SELinux part of blank credentials
> */
> @@ -4762,6 +5041,44 @@ static void ipc_free_security(struct kern_ipc_perm *perm)
> kfree(isec);
> }
>
> +static char *selinux_msg_msg_checkpoint(void *security)
> +{
> + struct msg_security_struct *msec = security;
> + char *s;
> + u32 len;
> + int ret;
> +
> + if (msec->sid == 0)
> + return ERR_PTR(-EINVAL);
> +
> + ret = security_sid_to_context(msec->sid, &s, &len);
> + if (ret)
> + return ERR_PTR(ret);
> + return s;
> +}
> +
> +static int selinux_msg_msg_restore(struct msg_msg *msg, char *ctx)
> +{
> + struct msg_security_struct *msec = msg->security;
> + int ret;
> + u32 sid = 0;
> +
> + ret = security_context_to_sid(ctx, strlen(ctx), &sid);
> + if (ret)
> + return ret;
> +
> + if (msec->sid == sid)
> + return 0;
> +
> + ret = avc_has_perm(current_sid(), sid, SECCLASS_MSG,
> + MSG__RESTORE, NULL);
> + if (ret)
> + return ret;
> +
> + msec->sid = sid;
> + return 0;
> +}
> +
> static int msg_msg_alloc_security(struct msg_msg *msg)
> {
> struct msg_security_struct *msec;
> @@ -5165,6 +5482,47 @@ static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
> *secid = isec->sid;
> }
>
> +static char *selinux_ipc_checkpoint(void *security)
> +{
> + struct ipc_security_struct *isec = security;
> + char *s;
> + u32 len;
> + int ret;
> +
> + if (isec->sid == 0)
> + return ERR_PTR(-EINVAL);
> +
> + ret = security_sid_to_context(isec->sid, &s, &len);
> + if (ret)
> + return ERR_PTR(ret);
> + return s;
> +}
> +
> +static int selinux_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
> +{
> + struct ipc_security_struct *isec = ipcp->security;
> + int ret;
> + u32 sid = 0;
> + struct common_audit_data ad;
> +
> + ret = security_context_to_sid(ctx, strlen(ctx), &sid);
> + if (ret)
> + return ret;
> +
> + if (isec->sid == sid)
> + return 0;
> +
> + COMMON_AUDIT_DATA_INIT(&ad, IPC);
> + ad.u.ipc_id = ipcp->key;
> + ret = avc_has_perm(current_sid(), sid, SECCLASS_IPC,
> + IPC__RESTORE, &ad);
> + if (ret)
> + return ret;
> +
> + isec->sid = sid;
> + return 0;
> +}
> +
> static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
> {
> if (inode)
> @@ -5512,6 +5870,8 @@ static struct security_operations selinux_ops = {
> .inode_getsecid = selinux_inode_getsecid,
>
> .file_permission = selinux_file_permission,
> + .file_checkpoint = selinux_file_checkpoint,
> + .file_restore = selinux_file_restore,
> .file_alloc_security = selinux_file_alloc_security,
> .file_free_security = selinux_file_free_security,
> .file_ioctl = selinux_file_ioctl,
> @@ -5527,6 +5887,8 @@ static struct security_operations selinux_ops = {
>
> .task_create = selinux_task_create,
> .cred_alloc_blank = selinux_cred_alloc_blank,
> + .cred_checkpoint = selinux_cred_checkpoint,
> + .cred_restore = selinux_cred_restore,
> .cred_free = selinux_cred_free,
> .cred_prepare = selinux_cred_prepare,
> .cred_transfer = selinux_cred_transfer,
> @@ -5550,8 +5912,12 @@ static struct security_operations selinux_ops = {
>
> .ipc_permission = selinux_ipc_permission,
> .ipc_getsecid = selinux_ipc_getsecid,
> + .ipc_checkpoint = selinux_ipc_checkpoint,
> + .ipc_restore = selinux_ipc_restore,
>
> .msg_msg_alloc_security = selinux_msg_msg_alloc_security,
> + .msg_msg_checkpoint = selinux_msg_msg_checkpoint,
> + .msg_msg_restore = selinux_msg_msg_restore,
> .msg_msg_free_security = selinux_msg_msg_free_security,
>
> .msg_queue_alloc_security = selinux_msg_queue_alloc_security,
> diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
> index 2b683ad..c5a1838 100644
> --- a/security/selinux/include/av_perm_to_string.h
> +++ b/security/selinux/include/av_perm_to_string.h
> @@ -19,6 +19,8 @@
> S_(SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint")
> S_(SECCLASS_FILE, FILE__EXECMOD, "execmod")
> S_(SECCLASS_FILE, FILE__OPEN, "open")
> + S_(SECCLASS_FILE, FILE__RESTORE, "restore")
> + S_(SECCLASS_FILE, FILE__FOWN_RESTORE, "fown_restore")
> S_(SECCLASS_CHR_FILE, CHR_FILE__EXECUTE_NO_TRANS, "execute_no_trans")
> S_(SECCLASS_CHR_FILE, CHR_FILE__ENTRYPOINT, "entrypoint")
> S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
> @@ -88,9 +90,11 @@
> S_(SECCLASS_PROCESS, PROCESS__EXECHEAP, "execheap")
> S_(SECCLASS_PROCESS, PROCESS__SETKEYCREATE, "setkeycreate")
> S_(SECCLASS_PROCESS, PROCESS__SETSOCKCREATE, "setsockcreate")
> + S_(SECCLASS_PROCESS, PROCESS__RESTORE, "restore")
> S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue")
> S_(SECCLASS_MSG, MSG__SEND, "send")
> S_(SECCLASS_MSG, MSG__RECEIVE, "receive")
> + S_(SECCLASS_MSG, MSG__RESTORE, "restore")
> S_(SECCLASS_SHM, SHM__LOCK, "lock")
> S_(SECCLASS_SECURITY, SECURITY__COMPUTE_AV, "compute_av")
> S_(SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, "compute_create")
> @@ -108,6 +112,7 @@
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
> S_(SECCLASS_SYSTEM, SYSTEM__MODULE_REQUEST, "module_request")
> + S_(SECCLASS_IPC, IPC__RESTORE, "restore")
> S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown")
> S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override")
> S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search")
> diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
> index 0546d61..77ad07b 100644
> --- a/security/selinux/include/av_permissions.h
> +++ b/security/selinux/include/av_permissions.h
> @@ -101,6 +101,8 @@
> #define FILE__ENTRYPOINT 0x00040000UL
> #define FILE__EXECMOD 0x00080000UL
> #define FILE__OPEN 0x00100000UL
> +#define FILE__RESTORE 0x00200000UL
> +#define FILE__FOWN_RESTORE 0x00400000UL
> #define LNK_FILE__IOCTL 0x00000001UL
> #define LNK_FILE__READ 0x00000002UL
> #define LNK_FILE__WRITE 0x00000004UL
> @@ -475,6 +477,7 @@
> #define PROCESS__EXECHEAP 0x08000000UL
> #define PROCESS__SETKEYCREATE 0x10000000UL
> #define PROCESS__SETSOCKCREATE 0x20000000UL
> +#define PROCESS__RESTORE 0x40000000UL
> #define IPC__CREATE 0x00000001UL
> #define IPC__DESTROY 0x00000002UL
> #define IPC__GETATTR 0x00000004UL
> @@ -484,6 +487,7 @@
> #define IPC__ASSOCIATE 0x00000040UL
> #define IPC__UNIX_READ 0x00000080UL
> #define IPC__UNIX_WRITE 0x00000100UL
> +#define IPC__RESTORE 0x00000200UL
> #define SEM__CREATE 0x00000001UL
> #define SEM__DESTROY 0x00000002UL
> #define SEM__GETATTR 0x00000004UL
> @@ -505,6 +509,7 @@
> #define MSGQ__ENQUEUE 0x00000200UL
> #define MSG__SEND 0x00000001UL
> #define MSG__RECEIVE 0x00000002UL
> +#define MSG__RESTORE 0x00000004UL
> #define SHM__CREATE 0x00000001UL
> #define SHM__DESTROY 0x00000002UL
> #define SHM__GETATTR 0x00000004UL
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list