* [PATCH 4/4] cr: add selinux support (v6)
2009-10-09 20:55 [PATCH 1/4] debug: add a few ckpt_debugs Serge E. Hallyn
@ 2009-10-09 20:57 ` Serge E. Hallyn
0 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-09 20:57 UTC (permalink / raw)
To: Oren Laadan
Cc: Linux Containers, linux-security-module-u79uwXL29TY76Z2rM5mHXA,
SELinux
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:
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-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
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 a5351ab..dbe8b2d 100644
--- a/checkpoint/restart.c
+++ b/checkpoint/restart.c
@@ -648,6 +648,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 8d8b69c..e6994eb 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"
@@ -2961,6 +2962,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);
@@ -3219,6 +3318,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 avc_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
+ */
+
+ AVC_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;
+}
+
+
/*
* detach and free the LSM part of a set of credentials
*/
@@ -4658,6 +4937,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;
@@ -5061,6 +5378,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 avc_audit_data ad;
+
+ ret = security_context_to_sid(ctx, strlen(ctx), &sid);
+ if (ret)
+ return ret;
+
+ if (isec->sid == sid)
+ return 0;
+
+ AVC_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)
@@ -5382,6 +5740,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,
@@ -5396,6 +5756,8 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
+ .cred_checkpoint = selinux_cred_checkpoint,
+ .cred_restore = selinux_cred_restore,
.cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare,
.kernel_act_as = selinux_kernel_act_as,
@@ -5417,8 +5779,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 31df1d7..a2c35d7 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")
@@ -107,6 +111,7 @@
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
+ 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 d645192..58ad588 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
@@ -453,6 +455,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
@@ -462,6 +465,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
@@ -483,6 +487,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
--
1.6.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 4/4] cr: add selinux support (v6)
@ 2009-10-09 20:57 ` Serge E. Hallyn
0 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-09 20:57 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers, linux-security-module, SELinux
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:
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@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 a5351ab..dbe8b2d 100644
--- a/checkpoint/restart.c
+++ b/checkpoint/restart.c
@@ -648,6 +648,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 8d8b69c..e6994eb 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"
@@ -2961,6 +2962,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);
@@ -3219,6 +3318,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 avc_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
+ */
+
+ AVC_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;
+}
+
+
/*
* detach and free the LSM part of a set of credentials
*/
@@ -4658,6 +4937,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;
@@ -5061,6 +5378,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 avc_audit_data ad;
+
+ ret = security_context_to_sid(ctx, strlen(ctx), &sid);
+ if (ret)
+ return ret;
+
+ if (isec->sid == sid)
+ return 0;
+
+ AVC_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)
@@ -5382,6 +5740,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,
@@ -5396,6 +5756,8 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
+ .cred_checkpoint = selinux_cred_checkpoint,
+ .cred_restore = selinux_cred_restore,
.cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare,
.kernel_act_as = selinux_kernel_act_as,
@@ -5417,8 +5779,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 31df1d7..a2c35d7 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")
@@ -107,6 +111,7 @@
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
+ 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 d645192..58ad588 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
@@ -453,6 +455,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
@@ -462,6 +465,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
@@ -483,6 +487,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
--
1.6.1
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 4/4] cr: add selinux support (v6)
2009-10-09 20:57 ` Serge E. Hallyn
@ 2009-10-09 21:15 ` Daniel J Walsh
-1 siblings, 0 replies; 19+ messages in thread
From: Daniel J Walsh @ 2009-10-09 21:15 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Linux Containers, linux-security-module-u79uwXL29TY76Z2rM5mHXA,
SELinux
On 10/09/2009 04:57 PM, 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:
> 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-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
> ---
> 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 a5351ab..dbe8b2d 100644
> --- a/checkpoint/restart.c
> +++ b/checkpoint/restart.c
> @@ -648,6 +648,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 8d8b69c..e6994eb 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"
> @@ -2961,6 +2962,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);
> @@ -3219,6 +3318,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 avc_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
> + */
> +
> + AVC_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;
> +}
> +
> +
> /*
> * detach and free the LSM part of a set of credentials
> */
> @@ -4658,6 +4937,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;
> @@ -5061,6 +5378,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 avc_audit_data ad;
> +
> + ret = security_context_to_sid(ctx, strlen(ctx), &sid);
> + if (ret)
> + return ret;
> +
> + if (isec->sid == sid)
> + return 0;
> +
> + AVC_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)
> @@ -5382,6 +5740,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,
> @@ -5396,6 +5756,8 @@ static struct security_operations selinux_ops = {
> .dentry_open = selinux_dentry_open,
>
> .task_create = selinux_task_create,
> + .cred_checkpoint = selinux_cred_checkpoint,
> + .cred_restore = selinux_cred_restore,
> .cred_free = selinux_cred_free,
> .cred_prepare = selinux_cred_prepare,
> .kernel_act_as = selinux_kernel_act_as,
> @@ -5417,8 +5779,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 31df1d7..a2c35d7 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")
> @@ -107,6 +111,7 @@
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
> + 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 d645192..58ad588 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
> @@ -453,6 +455,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
> @@ -462,6 +465,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
> @@ -483,6 +487,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
I think you need to be careful with this. We do not want the process to start with the same MCS Label as when it stopped in case some other virtual machine has grabbed the MCS Label. When the image is taken down it should give up probably give up its labels and allow the system to relabel to a known state. When it starts the image again, new labels should be selected and set on disk and the process resumed with the new label.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 4/4] cr: add selinux support (v6)
@ 2009-10-09 21:15 ` Daniel J Walsh
0 siblings, 0 replies; 19+ messages in thread
From: Daniel J Walsh @ 2009-10-09 21:15 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Oren Laadan, Linux Containers, linux-security-module, SELinux
On 10/09/2009 04:57 PM, 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:
> 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@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 a5351ab..dbe8b2d 100644
> --- a/checkpoint/restart.c
> +++ b/checkpoint/restart.c
> @@ -648,6 +648,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 8d8b69c..e6994eb 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"
> @@ -2961,6 +2962,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);
> @@ -3219,6 +3318,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 avc_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
> + */
> +
> + AVC_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;
> +}
> +
> +
> /*
> * detach and free the LSM part of a set of credentials
> */
> @@ -4658,6 +4937,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;
> @@ -5061,6 +5378,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 avc_audit_data ad;
> +
> + ret = security_context_to_sid(ctx, strlen(ctx), &sid);
> + if (ret)
> + return ret;
> +
> + if (isec->sid == sid)
> + return 0;
> +
> + AVC_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)
> @@ -5382,6 +5740,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,
> @@ -5396,6 +5756,8 @@ static struct security_operations selinux_ops = {
> .dentry_open = selinux_dentry_open,
>
> .task_create = selinux_task_create,
> + .cred_checkpoint = selinux_cred_checkpoint,
> + .cred_restore = selinux_cred_restore,
> .cred_free = selinux_cred_free,
> .cred_prepare = selinux_cred_prepare,
> .kernel_act_as = selinux_kernel_act_as,
> @@ -5417,8 +5779,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 31df1d7..a2c35d7 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")
> @@ -107,6 +111,7 @@
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
> S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
> + 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 d645192..58ad588 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
> @@ -453,6 +455,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
> @@ -462,6 +465,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
> @@ -483,6 +487,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
I think you need to be careful with this. We do not want the process to start with the same MCS Label as when it stopped in case some other virtual machine has grabbed the MCS Label. When the image is taken down it should give up probably give up its labels and allow the system to relabel to a known state. When it starts the image again, new labels should be selected and set on disk and the process resumed with the new label.
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 4/4] cr: add selinux support (v6)
2009-10-09 21:15 ` Daniel J Walsh
@ 2009-10-09 22:31 ` Serge E. Hallyn
-1 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-09 22:31 UTC (permalink / raw)
To: Daniel J Walsh
Cc: Linux Containers, linux-security-module-u79uwXL29TY76Z2rM5mHXA,
SELinux
Quoting Daniel J Walsh (dwalsh-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org):
> On 10/09/2009 04:57 PM, Serge E. Hallyn wrote:
> I think you need to be careful with this. We do not want the process to
> start with the same MCS Label as when it stopped in case some other virtual
> machine has grabbed the MCS Label. When the image is taken down it should
> give up probably give up its labels and allow the system to relabel to a
> known state. When it starts the image again, new labels should be selected
> and set on disk and the process resumed with the new label.
With this patchset, the default behavior remains to simply run the
restarted task in whatever context is calculated by the usual TE
rules (i.e. current context unless the type of the 'restart' program
was an entry type to some domain and a domain transition was requested
or automatic). The request to restore checkpointed labels is done with
a special flag.
What you actually are suggesting is the ability to make fine-grained
changes to some of the checkpointed labels, but to still restore
the checkpointed labels (with changes). I think it is pretty
generally accepted that such things should be done by editing the
checkpoint image in userspace. A pretty simple program could
run through the image looking for CKPT_HDR_SEC fields and
change the MCS labels where appropriate.
I haven't written such a program (or used MCS), but if you like
I'll certainly write it and I suspect Oren would be happy to have
it in user-cr/ (where the checkpoint and restart programs live) as
an example of how to make such substitutions generally.
thanks,
-serge
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 4/4] cr: add selinux support (v6)
@ 2009-10-09 22:31 ` Serge E. Hallyn
0 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-09 22:31 UTC (permalink / raw)
To: Daniel J Walsh
Cc: Oren Laadan, Linux Containers, linux-security-module, SELinux
Quoting Daniel J Walsh (dwalsh@redhat.com):
> On 10/09/2009 04:57 PM, Serge E. Hallyn wrote:
> I think you need to be careful with this. We do not want the process to
> start with the same MCS Label as when it stopped in case some other virtual
> machine has grabbed the MCS Label. When the image is taken down it should
> give up probably give up its labels and allow the system to relabel to a
> known state. When it starts the image again, new labels should be selected
> and set on disk and the process resumed with the new label.
With this patchset, the default behavior remains to simply run the
restarted task in whatever context is calculated by the usual TE
rules (i.e. current context unless the type of the 'restart' program
was an entry type to some domain and a domain transition was requested
or automatic). The request to restore checkpointed labels is done with
a special flag.
What you actually are suggesting is the ability to make fine-grained
changes to some of the checkpointed labels, but to still restore
the checkpointed labels (with changes). I think it is pretty
generally accepted that such things should be done by editing the
checkpoint image in userspace. A pretty simple program could
run through the image looking for CKPT_HDR_SEC fields and
change the MCS labels where appropriate.
I haven't written such a program (or used MCS), but if you like
I'll certainly write it and I suspect Oren would be happy to have
it in user-cr/ (where the checkpoint and restart programs live) as
an example of how to make such substitutions generally.
thanks,
-serge
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 1/4] add lsm name and lsm_info (policy header) to container info
@ 2009-10-19 14:43 Serge E. Hallyn
[not found] ` <20091019144315.GA30535-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-19 14:43 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
The LSM name is 'selinux', 'smack', 'tomoyo', or 'dummy'. We
add that to the container configuration section. We also add
a LSM policy configuration section. That is placed after the LSM
name. It is written by the LSM in security_checkpoint_header(),
called during checkpoint container(), and read by the LSM during
security_may_restart(), which is called from restore_lsm() in
restore_container().
Signed-off-by: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
Documentation/checkpoint/readme.txt | 24 ++++++++++++++
checkpoint/checkpoint.c | 13 +++++++-
checkpoint/restart.c | 41 ++++++++++++++++++++++++
checkpoint/sys.c | 21 ++++++++++++
include/linux/checkpoint.h | 7 +++-
include/linux/checkpoint_hdr.h | 16 +++++++++
include/linux/checkpoint_types.h | 2 +
include/linux/security.h | 58 +++++++++++++++++++++++++++++++++++
security/capability.c | 24 ++++++++++++++
security/security.c | 26 +++++++++++++++
10 files changed, 230 insertions(+), 2 deletions(-)
diff --git a/Documentation/checkpoint/readme.txt b/Documentation/checkpoint/readme.txt
index 665f75b..40bdf8f 100644
--- a/Documentation/checkpoint/readme.txt
+++ b/Documentation/checkpoint/readme.txt
@@ -340,6 +340,30 @@ So that's why we don't want CAP_SYS_ADMIN required up-front. That way
we will be forced to more carefully review each of those features.
However, this can be controlled with a sysctl-variable.
+LSM
+===
+
+Security modules use custom labels on subjects and objects to
+further mediate access decisions beyond DAC controls. When
+checkpoint applications, these labels are [ work in progress ]
+checkpointed along with the objects. At restart, the
+RESTART_KEEP_LSM flag tells the kernel whether re-created objects
+whould keep their checkpointed labels, or get automatically
+recalculated labels. Since checkpointed labels will only make
+sense to the same LSM which was active at checkpoint time,
+sys_restart() with the RESTART_KEEP_LSM flag will fail with
+-EINVAL if the LSM active at restart is not the same as that
+active at checkpoint. If RESTART_KEEP_LSM is not specified,
+then objects will be given whatever default labels the LSM and
+their optional policy decide. Of course, when RESTART_KEEP_LSM
+is specified, the LSM may choose a different label than the
+checkpointed one, or fail the entire restart if the caller
+does not have permission to create objects with the checkpointed
+label.
+
+It should always be safe to take a checkpoint of an application
+under LSM_1, and restart it without the RESTART_KEEP_LSM flag
+under LSM_2.
Kernel interfaces
=================
diff --git a/checkpoint/checkpoint.c b/checkpoint/checkpoint.c
index 6eb8f3b..b8c323c 100644
--- a/checkpoint/checkpoint.c
+++ b/checkpoint/checkpoint.c
@@ -366,7 +366,18 @@ static int checkpoint_container(struct ckpt_ctx *ctx)
ret = ckpt_write_obj(ctx, &h->h);
ckpt_hdr_put(ctx, h);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ memset(ctx->lsm_name, 0, CHECKPOINT_LSM_NAME_MAX + 1);
+ strlcpy(ctx->lsm_name, security_get_lsm_name(),
+ CHECKPOINT_LSM_NAME_MAX + 1);
+ ret = ckpt_write_buffer(ctx, ctx->lsm_name,
+ CHECKPOINT_LSM_NAME_MAX + 1);
+ if (ret < 0)
+ return ret;
+
+ return security_checkpoint_header(ctx);
}
/* write the checkpoint trailer */
diff --git a/checkpoint/restart.c b/checkpoint/restart.c
index 32a9fc5..0cd721c 100644
--- a/checkpoint/restart.c
+++ b/checkpoint/restart.c
@@ -624,6 +624,42 @@ static int restore_read_header(struct ckpt_ctx *ctx)
return ret;
}
+/* read the LSM configuration section */
+static int restore_lsm(struct ckpt_ctx *ctx)
+{
+ int ret;
+ char *cur_lsm = security_get_lsm_name();
+
+ ret = _ckpt_read_buffer(ctx, ctx->lsm_name,
+ CHECKPOINT_LSM_NAME_MAX + 1);
+ if (ret < 0) {
+ ckpt_debug("Error %d reading lsm name\n", ret);
+ return ret;
+ }
+
+ if (!(ctx->uflags & RESTART_KEEP_LSM))
+ goto skip_lsm;
+
+ if (strncmp(cur_lsm, ctx->lsm_name, CHECKPOINT_LSM_NAME_MAX + 1) != 0) {
+ ckpt_debug("c/r: checkpointed LSM %s, current is %s.\n",
+ ctx->lsm_name, cur_lsm);
+ return -EPERM;
+ }
+
+ if (strcmp(ctx->lsm_name, "lsm_none") != 0 &&
+ strcmp(ctx->lsm_name, "default") != 0) {
+ ckpt_debug("c/r: RESTART_KEEP_LSM unsupported for %s\n",
+ ctx->lsm_name);
+ return -ENOSYS;
+ }
+
+skip_lsm:
+ ret = security_may_restart(ctx);
+ if (ret < 0)
+ ckpt_debug("security_may_restart returned %d\n", ret);
+ return ret;
+}
+
/* read the container configuration section */
static int restore_container(struct ckpt_ctx *ctx)
{
@@ -635,6 +671,11 @@ static int restore_container(struct ckpt_ctx *ctx)
return PTR_ERR(h);
ckpt_hdr_put(ctx, h);
+ /* read the LSM name and info which follow ("are a part of")
+ * the ckpt_hdr_container */
+ ret = restore_lsm(ctx);
+ if (ret < 0)
+ ckpt_debug("Error %d on LSM configuration\n", ret);
return ret;
}
diff --git a/checkpoint/sys.c b/checkpoint/sys.c
index 260a1ee..5b65eb0 100644
--- a/checkpoint/sys.c
+++ b/checkpoint/sys.c
@@ -169,6 +169,27 @@ void *ckpt_hdr_get_type(struct ckpt_ctx *ctx, int len, int type)
return h;
}
+#define DUMMY_LSM_INFO "dummy"
+
+int ckpt_write_dummy_lsm_info(struct ckpt_ctx *ctx)
+{
+ return ckpt_write_obj_type(ctx, DUMMY_LSM_INFO,
+ strlen(DUMMY_LSM_INFO), CKPT_HDR_LSM_INFO);
+}
+
+/*
+ * ckpt_snarf_lsm_info
+ * If there is a CKPT_HDR_LSM_INFO field, toss it.
+ * Used when the current LSM doesn't care about this field.
+ */
+void ckpt_snarf_lsm_info(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr *h;
+
+ h = ckpt_read_buf_type(ctx, CKPT_LSM_INFO_LEN, CKPT_HDR_LSM_INFO);
+ if (!IS_ERR(h))
+ ckpt_hdr_put(ctx, h);
+}
/*
* Helpers to manage c/r contexts: allocated for each checkpoint and/or
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index dfcb59b..eedd5e7 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -10,7 +10,7 @@
* distribution for more details.
*/
-#define CHECKPOINT_VERSION 3
+#define CHECKPOINT_VERSION 4
/* checkpoint user flags */
#define CHECKPOINT_SUBTREE 0x1
@@ -19,6 +19,7 @@
#define RESTART_TASKSELF 0x1
#define RESTART_FROZEN 0x2
#define RESTART_GHOST 0x4
+#define RESTART_KEEP_LSM 0x8
#ifdef __KERNEL__
#ifdef CONFIG_CHECKPOINT
@@ -48,7 +49,9 @@
#define RESTART_USER_FLAGS \
(RESTART_TASKSELF | \
RESTART_FROZEN | \
+ RESTART_KEEP_LSM | \
RESTART_GHOST)
+#define CKPT_LSM_INFO_LEN 200
extern int walk_task_subtree(struct task_struct *task,
int (*func)(struct task_struct *, void *),
@@ -62,6 +65,8 @@ extern void _ckpt_hdr_put(struct ckpt_ctx *ctx, void *ptr, int n);
extern void ckpt_hdr_put(struct ckpt_ctx *ctx, void *ptr);
extern void *ckpt_hdr_get(struct ckpt_ctx *ctx, int n);
extern void *ckpt_hdr_get_type(struct ckpt_ctx *ctx, int n, int type);
+extern int ckpt_write_dummy_lsm_info(struct ckpt_ctx *ctx);
+extern void ckpt_snarf_lsm_info(struct ckpt_ctx *ctx);
extern int ckpt_write_obj(struct ckpt_ctx *ctx, struct ckpt_hdr *h);
extern int ckpt_write_obj_type(struct ckpt_ctx *ctx,
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index ff2e4aa..636e189 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -27,6 +27,15 @@
#endif
/*
+ * /usr/include/linux/security.h is not exported to userspace, so
+ * we need this value here for userspace restart.c to read.
+ *
+ * CHECKPOINT_LSM_NAME_MAX should be SECURITY_NAME_MAX
+ * security_may_restart() has a BUILD_BUG_ON to enforce that.
+ */
+#define CHECKPOINT_LSM_NAME_MAX 10
+
+/*
* To maintain compatibility between 32-bit and 64-bit architecture flavors,
* keep data 64-bit aligned: use padding for structure members, and use
* __attribute__((aligned (8))) for the entire structure.
@@ -71,6 +80,8 @@ enum {
#define CKPT_HDR_STRING CKPT_HDR_STRING
CKPT_HDR_OBJREF,
#define CKPT_HDR_OBJREF CKPT_HDR_OBJREF
+ CKPT_HDR_LSM_INFO,
+#define CKPT_HDR_LSM_INFO CKPT_HDR_LSM_INFO
CKPT_HDR_TREE = 101,
#define CKPT_HDR_TREE CKPT_HDR_TREE
@@ -252,6 +263,11 @@ struct ckpt_const {
/* container configuration section header */
struct ckpt_hdr_container {
struct ckpt_hdr h;
+ /*
+ * the header is followed by the string:
+ * char lsm_name[SECURITY_NAME_MAX + 1]
+ * plus the CKPT_HDR_LSM_INFO section
+ */
};
/* checkpoint image header */
diff --git a/include/linux/checkpoint_types.h b/include/linux/checkpoint_types.h
index fa57cdc..b7d3053 100644
--- a/include/linux/checkpoint_types.h
+++ b/include/linux/checkpoint_types.h
@@ -21,6 +21,7 @@
#include <linux/fs.h>
#include <linux/ktime.h>
#include <linux/wait.h>
+#include <linux/security.h>
struct ckpt_stats {
int uts_ns;
@@ -42,6 +43,7 @@ struct ckpt_ctx {
struct task_struct *root_task; /* [container] root task */
struct nsproxy *root_nsproxy; /* [container] root nsproxy */
struct task_struct *root_freezer; /* [container] root task */
+ char lsm_name[SECURITY_NAME_MAX + 1]; /* security module at ckpt */
unsigned long kflags; /* kerenl flags */
unsigned long uflags; /* user flags */
diff --git a/include/linux/security.h b/include/linux/security.h
index 1f16eea..99e4ebc 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -136,6 +136,13 @@ static inline unsigned long round_hint_to_min(unsigned long hint)
extern int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos);
+#ifdef CONFIG_CHECKPOINT
+struct ckpt_ctx;
+
+void ckpt_snarf_lsm_info(struct ckpt_ctx *ctx);
+int ckpt_write_dummy_lsm_info(struct ckpt_ctx *ctx);
+#endif
+
#ifdef CONFIG_SECURITY
struct security_mnt_opts {
@@ -1320,6 +1327,28 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @secdata contains the security context.
* @seclen contains the length of the security context.
*
+ * Security hooks for Checkpoint/restart
+ * (In addition to *_checkpoint and *_restore)
+ *
+ * @may_restart:
+ * Authorize sys_restart().
+ * Note that all construction of kernel resources, credentials,
+ * etc is already authorized per the caller's credentials. This
+ * hook is intended for the LSM to make further decisions about
+ * a task not being allowed to restart at all, for instance if
+ * the policy has changed since checkpoint.
+ * @ctx is the checkpoint/restart context (see <linux/checkpoint_types.h>)
+ * Return 0 if allowed, <0 on error.
+ *
+ * @checkpoint_header:
+ * Optionally write out a LSM-specific checkpoint header. This is
+ * a chance to write out policy information, for instance. The same
+ * LSM on restart can then use the info in security_may_restart() to
+ * refuse restart (for instance) across policy changes.
+ * The info is to be written as a an object of type CKPT_HDR_LSM_INFO.
+ * @ctx is the checkpoint/restart context (see <linux/checkpoint_types.h>)
+ * Return 0 on success, <0 on error.
+ *
* Security hooks for Audit
*
* @audit_rule_init:
@@ -1556,6 +1585,11 @@ struct security_operations {
int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
void (*release_secctx) (char *secdata, u32 seclen);
+#ifdef CONFIG_CHECKPOINT
+ int (*may_restart) (struct ckpt_ctx *ctx);
+ int (*checkpoint_header) (struct ckpt_ctx *ctx);
+#endif
+
#ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect) (struct socket *sock,
struct socket *other, struct sock *newsk);
@@ -1796,6 +1830,12 @@ int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
void security_release_secctx(char *secdata, u32 seclen);
+#ifdef CONFIG_CHECKPOINT
+int security_may_restart(struct ckpt_ctx *ctx);
+int security_checkpoint_header(struct ckpt_ctx *ctx);
+#endif /* CONFIG_CHECKPOINT */
+
+char *security_get_lsm_name(void);
#else /* CONFIG_SECURITY */
struct security_mnt_opts {
};
@@ -1818,6 +1858,12 @@ static inline int security_init(void)
return 0;
}
+#define DEFAULT_LSM_NAME "lsm_none"
+static inline char *security_get_lsm_name(void)
+{
+ return DEFAULT_LSM_NAME;
+}
+
static inline int security_ptrace_may_access(struct task_struct *child,
unsigned int mode)
{
@@ -2537,6 +2583,18 @@ static inline int security_secctx_to_secid(const char *secdata,
static inline void security_release_secctx(char *secdata, u32 seclen)
{
}
+
+#ifdef CONFIG_CHECKPOINT
+static inline int security_may_restart(struct ckpt_ctx *ctx)
+{
+ ckpt_snarf_lsm_info(ctx);
+ return 0;
+}
+static inline int security_checkpoint_header(struct ckpt_ctx *ctx)
+{
+ return ckpt_write_dummy_lsm_info(ctx);
+}
+#endif /* CONFIG_CHECKPOINT */
#endif /* CONFIG_SECURITY */
#ifdef CONFIG_SECURITY_NETWORK
diff --git a/security/capability.c b/security/capability.c
index 88f752e..23026e2 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -792,6 +792,26 @@ static void cap_release_secctx(char *secdata, u32 seclen)
{
}
+#ifdef CONFIG_CHECKPOINT
+static int cap_may_restart(struct ckpt_ctx *ctx)
+{
+ /*
+ * Note that all construction of kernel resources, credentials,
+ * etc is already authorized per the caller's credentials. This
+ * hook is intended for the LSM to make further decisions about
+ * a task not being allowed to restart at all, for instance if
+ * the policy has changed since checkpoint.
+ */
+ ckpt_snarf_lsm_info(ctx);
+ return 0;
+}
+
+static int cap_checkpoint_header(struct ckpt_ctx *ctx)
+{
+ return ckpt_write_dummy_lsm_info(ctx);
+}
+#endif
+
#ifdef CONFIG_KEYS
static int cap_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
@@ -992,6 +1012,10 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, secid_to_secctx);
set_to_cap_if_null(ops, secctx_to_secid);
set_to_cap_if_null(ops, release_secctx);
+#ifdef CONFIG_CHECKPOINT
+ set_to_cap_if_null(ops, may_restart);
+ set_to_cap_if_null(ops, checkpoint_header);
+#endif
#ifdef CONFIG_SECURITY_NETWORK
set_to_cap_if_null(ops, unix_stream_connect);
set_to_cap_if_null(ops, unix_may_send);
diff --git a/security/security.c b/security/security.c
index dc7674f..e4fa91a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -16,6 +16,9 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/security.h>
+#ifdef CONFIG_CHECKPOINT
+#include <linux/checkpoint.h>
+#endif
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1];
@@ -122,6 +125,11 @@ int register_security(struct security_operations *ops)
return 0;
}
+char *security_get_lsm_name(void)
+{
+ return security_ops->name;
+}
+
/* Security operations */
int security_ptrace_may_access(struct task_struct *child, unsigned int mode)
@@ -959,6 +967,24 @@ void security_release_secctx(char *secdata, u32 seclen)
}
EXPORT_SYMBOL(security_release_secctx);
+#ifdef CONFIG_CHECKPOINT
+int security_may_restart(struct ckpt_ctx *ctx)
+{
+ /*
+ * SECURITY_NAME_MAX is defined in linux/security.h,
+ * CHECKPOINT_LSM_NAME_MAX in linux/checkpoint_hdr.h
+ */
+ BUILD_BUG_ON(CHECKPOINT_LSM_NAME_MAX != SECURITY_NAME_MAX);
+
+ return security_ops->may_restart(ctx);
+}
+
+int security_checkpoint_header(struct ckpt_ctx *ctx)
+{
+ return security_ops->checkpoint_header(ctx);
+}
+#endif
+
#ifdef CONFIG_SECURITY_NETWORK
int security_unix_stream_connect(struct socket *sock, struct socket *other,
--
1.6.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <20091019144315.GA30535-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2009-10-19 14:43 ` Serge E. Hallyn
[not found] ` <20091019144341.GA30566-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-19 14:44 ` [PATCH user-cr] restart: accept the lsm_name field in header and add -k flag (v2) Serge E. Hallyn
` (2 subsequent siblings)
3 siblings, 1 reply; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-19 14:43 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
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 generic support for c/r of LSM credentials. Support
for Smack and SELinux (and TOMOYO if appropriate) will be added later.
Capabilities is already supported through generic creds code.
This patch supports ipc_perm, msg_msg, cred (task) and file ->security
fields. Inodes, superblocks, netif, and xfrm currently are restored
not through sys_restart() but through container creation, and so the
security fields should be done then as well. Network should be added
when network c/r is added.
Briefly, all security fields must be exported by the LSM as a simple
null-terminated string. They are checkpointed through the
security_checkpoint_obj() helper, because we must pass it an extra
sectype field. Splitting SECURITY_OBJ_SEC into one type per object
type would not work because, in Smack, one void* security is used for
all object types. But we must pass the sectype field because in
SELinux a different type of structure is stashed in each object type.
The RESTART_KEEP_LSM flag indicates that the LSM should
attempt to reuse checkpointed security labels. It is always
invalid when the LSM at restart differs from that at checkpoint.
It is currently only usable for capabilities.
(For capabilities, restart without RESTART_KEEP_LSM is technically
not implemented. There actually might be a use case for that,
but the safety of it is dubious so for now we always re-create
checkpointed capability sets whether RESTART_KEEP_LSM is
specified or not)
Changelog:
sep 3: fix memory leak on LSM restore error path
Sep 3: provide 2 hooks, may_restart and checkpoint_header, to facilitate
an LSM tracking policy changes.
sep 10: merge RESTART_KEEP_LSM patch with basic LSM c/r
support patches.
sep 10: rename security_xyz_get_ctx() to security_xyz_checkpoint(),
in order to avoid confusing with the various other 'context'
helpers in the security_ namespace, relating to secids and
sysfs xattrs.
sep 10: pass context file to security_cred_restore. SELinux will
want the file's security context to authorize it as an
entrypoint for the new process context.
oct 01: roll up some generic c/r debug hunks from selinux patch.
oct 05: address set of Oren comments, including:
1. fix memleak in restore_msg_contents_one
2. use a separate container checkpoint image section
3. define SECURITY_CTX_NONE
4. allocate the right size to l in security_checkpoint_obj
5. fix ckpt_hdr_lsm alignment
oct 09: at checkpoint, key on the void*security in the objhash,
and don't cache the ckpt_stored_lsm at all. At restart,
do_restore_security (now moved to security/security.c)
creates and caches the ckpt_stored_lsm.
oct 19: At checkpoint, we insert the void* security into the
objhash. The first time that we do so, we next write out
the string representation of the context to the checkpoint
image, along with the value of the objref for the void*
security, and insert that into the objhash. Then at
restart, when we read a LSM context, we read the objref
which the void* security had at checkpoint, and we then
insert the string context with that objref as well.
Signed-off-by: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
checkpoint/files.c | 29 ++++++-
checkpoint/objhash.c | 102 +++++++++++++++++++++++
include/linux/checkpoint.h | 1 +
include/linux/checkpoint_hdr.h | 20 +++++
include/linux/checkpoint_types.h | 7 ++
include/linux/security.h | 143 ++++++++++++++++++++++++++++++++
ipc/checkpoint.c | 21 ++---
ipc/checkpoint_msg.c | 17 +++-
ipc/checkpoint_sem.c | 4 +-
ipc/checkpoint_shm.c | 4 +-
ipc/util.h | 6 +-
kernel/cred.c | 29 ++++++-
security/capability.c | 48 +++++++++++
security/security.c | 167 ++++++++++++++++++++++++++++++++++++++
14 files changed, 571 insertions(+), 27 deletions(-)
diff --git a/checkpoint/files.c b/checkpoint/files.c
index f6de07e..2e110e5 100644
--- a/checkpoint/files.c
+++ b/checkpoint/files.c
@@ -145,9 +145,25 @@ static int scan_fds(struct files_struct *files, int **fdtable)
return n;
}
+#ifdef CONFIG_SECURITY
+int checkpoint_file_security(struct ckpt_ctx *ctx, struct file *file,
+ int *secref)
+{
+ return security_checkpoint_obj(ctx, file->f_security,
+ CKPT_SECURITY_FILE, secref);
+}
+#else
+int checkpoint_file_security(struct ckpt_ctx *ctx, struct file *file,
+ int *secref)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
struct ckpt_hdr_file *h)
{
+ int ret;
struct cred *f_cred = (struct cred *) file->f_cred;
h->f_flags = file->f_flags;
@@ -159,8 +175,12 @@ int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
if (h->f_credref < 0)
return h->f_credref;
- ckpt_debug("file %s credref %d", file->f_dentry->d_name.name,
- h->f_credref);
+ ret = checkpoint_file_security(ctx, file, &h->f_secref);
+ if (ret < 0)
+ return ret;
+
+ ckpt_debug("file %s credref %d secref %d\n",
+ file->f_dentry->d_name.name, h->f_credref, h->f_secref);
/* FIX: need also file->f_owner, etc */
@@ -510,6 +530,11 @@ int restore_file_common(struct ckpt_ctx *ctx, struct file *file,
put_cred(file->f_cred);
file->f_cred = get_cred(cred);
+ ret = security_restore_obj(ctx, (void *) file, CKPT_SECURITY_FILE,
+ h->f_secref);
+ if (ret < 0)
+ return ret;
+
/* safe to set 1st arg (fd) to 0, as command is F_SETFL */
ret = vfs_fcntl(0, F_SETFL, h->f_flags & CKPT_SETFL_MASK, file);
if (ret < 0)
diff --git a/checkpoint/objhash.c b/checkpoint/objhash.c
index 730dd82..054416c 100644
--- a/checkpoint/objhash.c
+++ b/checkpoint/objhash.c
@@ -16,6 +16,7 @@
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/sched.h>
+#include <linux/kref.h>
#include <linux/ipc_namespace.h>
#include <linux/user_namespace.h>
#include <linux/checkpoint.h>
@@ -285,6 +286,36 @@ static int obj_tty_users(void *ptr)
return atomic_read(&((struct tty_struct *) ptr)->kref.refcount);
}
+static void lsm_string_free(struct kref *kref)
+{
+ struct ckpt_lsm_string *s = container_of(kref, struct ckpt_lsm_string,
+ kref);
+ kfree(s->string);
+ kfree(s);
+}
+
+static int lsm_string_grab(void *ptr)
+{
+ struct ckpt_lsm_string *s = ptr;
+ kref_get(&s->kref);
+ return 0;
+}
+
+static void lsm_string_drop(void *ptr, int lastref)
+{
+ struct ckpt_lsm_string *s = ptr;
+ kref_put(&s->kref, lsm_string_free);
+}
+
+/* security context strings */
+static int checkpoint_lsm_string(struct ckpt_ctx *ctx, void *ptr);
+static struct ckpt_lsm_string *restore_lsm_string(struct ckpt_ctx *ctx);
+static void *restore_lsm_string_wrap(struct ckpt_ctx *ctx)
+{
+ return (void *)restore_lsm_string(ctx);
+}
+
+
static struct ckpt_obj_ops ckpt_obj_ops[] = {
/* ignored object */
{
@@ -433,6 +464,22 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = {
.checkpoint = checkpoint_tty,
.restore = restore_tty,
},
+ /* LSM void *security on objhash - at checkpoint */
+ {
+ .obj_name = "SECURITY PTR",
+ .obj_type = CKPT_OBJ_SECURITY_PTR,
+ .ref_drop = obj_no_drop,
+ .ref_grab = obj_no_grab,
+ },
+ /* LSM security strings - at restart */
+ {
+ .obj_name = "SECURITY STRING",
+ .obj_type = CKPT_OBJ_SECURITY,
+ .ref_grab = lsm_string_grab,
+ .ref_drop = lsm_string_drop,
+ .checkpoint = checkpoint_lsm_string,
+ .restore = restore_lsm_string_wrap,
+ },
};
@@ -1010,3 +1057,58 @@ void *ckpt_obj_fetch(struct ckpt_ctx *ctx, int objref, enum obj_type type)
ckpt_debug("%s ref %d\n", obj->ops->obj_name, obj->objref);
return (obj->ops->obj_type == type ? obj->ptr : ERR_PTR(-ENOMSG));
}
+
+/* security context strings */
+static int checkpoint_lsm_string(struct ckpt_ctx *ctx, void *ptr)
+{
+ struct ckpt_hdr_lsm *h;
+ struct ckpt_lsm_string *l = ptr;
+ int ret;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SECURITY);
+ if (!h)
+ return -ENOMEM;
+ h->sectype = l->sectype;
+ h->ptrref = l->ptrref;
+ ret = ckpt_write_obj(ctx, &h->h);
+ ckpt_hdr_put(ctx, h);
+
+ if (ret < 0)
+ return ret;
+ return ckpt_write_string(ctx, l->string, strlen(l->string)+1);
+}
+
+static struct ckpt_lsm_string *restore_lsm_string(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_lsm *h;
+ struct ckpt_lsm_string *l;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SECURITY);
+ if (IS_ERR(h)) {
+ ckpt_debug("ckpt_read_obj_type returned %ld\n", PTR_ERR(h));
+ return ERR_PTR(PTR_ERR(h));
+ }
+
+ l = kzalloc(sizeof(*l), GFP_KERNEL);
+ if (!l) {
+ l = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ l->string = ckpt_read_string(ctx, CKPT_LSM_STRING_MAX);
+ if (IS_ERR(l->string)) {
+ void *s = l->string;
+ ckpt_debug("ckpt_read_string returned %ld\n", PTR_ERR(s));
+ kfree(l);
+ l = s;
+ goto out;
+ }
+ kref_init(&l->kref);
+ l->sectype = h->sectype;
+ /* l is just a placeholder, don't grab a ref */
+ ckpt_obj_insert(ctx, l, h->ptrref, CKPT_OBJ_SECURITY);
+
+out:
+ ckpt_hdr_put(ctx, h);
+ return l;
+}
+
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index eedd5e7..1a5d66f 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -52,6 +52,7 @@
RESTART_KEEP_LSM | \
RESTART_GHOST)
#define CKPT_LSM_INFO_LEN 200
+#define CKPT_LSM_STRING_MAX 1024
extern int walk_task_subtree(struct task_struct *task,
int (*func)(struct task_struct *, void *),
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index 636e189..3a95dcd 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -82,6 +82,8 @@ enum {
#define CKPT_HDR_OBJREF CKPT_HDR_OBJREF
CKPT_HDR_LSM_INFO,
#define CKPT_HDR_LSM_INFO CKPT_HDR_LSM_INFO
+ CKPT_HDR_SECURITY,
+#define CKPT_HDR_SECURITY CKPT_HDR_SECURITY
CKPT_HDR_TREE = 101,
#define CKPT_HDR_TREE CKPT_HDR_TREE
@@ -234,6 +236,10 @@ enum obj_type {
#define CKPT_OBJ_SOCK CKPT_OBJ_SOCK
CKPT_OBJ_TTY,
#define CKPT_OBJ_TTY CKPT_OBJ_TTY
+ CKPT_OBJ_SECURITY_PTR,
+#define CKPT_OBJ_SECURITY_PTR CKPT_OBJ_SECURITY_PTR
+ CKPT_OBJ_SECURITY,
+#define CKPT_OBJ_SECURITY CKPT_OBJ_SECURITY
CKPT_OBJ_MAX
#define CKPT_OBJ_MAX CKPT_OBJ_MAX
};
@@ -364,6 +370,7 @@ struct ckpt_hdr_cred {
__u32 gid, sgid, egid, fsgid;
__s32 user_ref;
__s32 groupinfo_ref;
+ __s32 sec_ref;
struct ckpt_capabilities cap_s;
} __attribute__((aligned(8)));
@@ -376,6 +383,16 @@ struct ckpt_hdr_groupinfo {
__u32 groups[0];
} __attribute__((aligned(8)));
+struct ckpt_hdr_lsm {
+ struct ckpt_hdr h;
+ __s32 ptrref;
+ __u8 sectype;
+ __u8 padding;
+ /*
+ * This is followed by a string of size len+1,
+ * null-terminated
+ */
+} __attribute__((aligned(8)));
/*
* todo - keyrings and LSM
* These may be better done with userspace help though
@@ -505,6 +522,7 @@ struct ckpt_hdr_file {
__s32 f_credref;
__u64 f_pos;
__u64 f_version;
+ __s32 f_secref;
} __attribute__((aligned(8)));
struct ckpt_hdr_file_generic {
@@ -765,6 +783,7 @@ struct ckpt_hdr_ipc_perms {
__u32 mode;
__u32 _padding;
__u64 seq;
+ __s32 sec_ref;
} __attribute__((aligned(8)));
struct ckpt_hdr_ipc_shm {
@@ -798,6 +817,7 @@ struct ckpt_hdr_ipc_msg_msg {
struct ckpt_hdr h;
__s32 m_type;
__u32 m_ts;
+ __s32 sec_ref;
} __attribute__((aligned(8)));
struct ckpt_hdr_ipc_sem {
diff --git a/include/linux/checkpoint_types.h b/include/linux/checkpoint_types.h
index b7d3053..96a7811 100644
--- a/include/linux/checkpoint_types.h
+++ b/include/linux/checkpoint_types.h
@@ -89,6 +89,13 @@ struct ckpt_ctx {
#endif
};
+/* stored on hashtable */
+struct ckpt_lsm_string {
+ struct kref kref;
+ int sectype; /* Containing object (file,cred,&c) */
+ int ptrref; /* the objref for the void* security */
+ char *string;
+};
#endif /* __KERNEL__ */
#endif /* _LINUX_CHECKPOINT_TYPES_H_ */
diff --git a/include/linux/security.h b/include/linux/security.h
index 99e4ebc..db4bbdb 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -43,6 +43,9 @@
#define SECURITY_CAP_NOAUDIT 0
#define SECURITY_CAP_AUDIT 1
+/* checkpoint 'N/A' in a checkpoint image for a security context */
+#define SECURITY_CTX_NONE -1
+
struct ctl_table;
struct audit_krule;
@@ -581,6 +584,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* created.
* @file contains the file structure to secure.
* Return 0 if the hook is successful and permission is granted.
+ * @file_checkpoint:
+ * Return a string representing the security context on a file.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @file_restore:
+ * Set a security context on a file according to the checkpointed context.
+ * @file contains the file.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
* @file_free_security:
* Deallocate and free any security structures stored in file->f_security.
* @file contains the file structure being modified.
@@ -660,6 +672,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* manual page for definitions of the @clone_flags.
* @clone_flags contains the flags indicating what should be shared.
* Return 0 if permission is granted.
+ * @cred_checkpoint:
+ * Return a string representing the security context on the task cred.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @cred_restore:
+ * Set a security context on a task cred according to the checkpointed
+ * context.
+ * @file contains the checkpoint file
+ * @cred contains the cred.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
* @cred_free:
* @cred points to the credentials.
* Deallocate and clear the cred->security field in a set of credentials.
@@ -1108,6 +1131,19 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @ipcp contains the kernel IPC permission structure.
* @secid contains a pointer to the location where result will be saved.
* In case of failure, @secid will be set to zero.
+ * @ipc_checkpoint:
+ * Return a string representing the security context on the IPC
+ * permission structure.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @ipc_restore:
+ * Set a security context on a IPC permission structure according to
+ * the checkpointed context.
+ * @ipcp contains the IPC permission structure, which will have
+ * already been allocated and initialized when the IPC structure was
+ * created.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
*
* Security hooks for individual messages held in System V IPC message queues
* @msg_msg_alloc_security:
@@ -1116,6 +1152,16 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* created.
* @msg contains the message structure to be modified.
* Return 0 if operation was successful and permission is granted.
+ * @msg_msg_checkpoint:
+ * Return a string representing the security context on an msg_msg
+ * struct.
+ * @security contains the security field
+ * Returns a char* which the caller will free, or -error on error.
+ * @msg_msg_restore:
+ * Set msg_msg->security according to the checkpointed context.
+ * @msg contains the message structure to be modified.
+ * @ctx contains a string representation of the checkpointed context.
+ * Return 0 on success, -error on failure.
* @msg_msg_free_security:
* Deallocate the security structure for this message.
* @msg contains the message structure to be modified.
@@ -1492,6 +1538,8 @@ struct security_operations {
int (*file_permission) (struct file *file, int mask);
int (*file_alloc_security) (struct file *file);
+ char *(*file_checkpoint) (void *security);
+ int (*file_restore) (struct file *file, char *ctx);
void (*file_free_security) (struct file *file);
int (*file_ioctl) (struct file *file, unsigned int cmd,
unsigned long arg);
@@ -1512,6 +1560,8 @@ struct security_operations {
int (*dentry_open) (struct file *file, const struct cred *cred);
int (*task_create) (unsigned long clone_flags);
+ char *(*cred_checkpoint) (void *security);
+ int (*cred_restore) (struct file *file, struct cred *cred, char *ctx);
void (*cred_free) (struct cred *cred);
int (*cred_prepare)(struct cred *new, const struct cred *old,
gfp_t gfp);
@@ -1545,8 +1595,12 @@ struct security_operations {
int (*ipc_permission) (struct kern_ipc_perm *ipcp, short flag);
void (*ipc_getsecid) (struct kern_ipc_perm *ipcp, u32 *secid);
+ char *(*ipc_checkpoint) (void *security);
+ int (*ipc_restore) (struct kern_ipc_perm *ipcp, char *ctx);
int (*msg_msg_alloc_security) (struct msg_msg *msg);
+ char *(*msg_msg_checkpoint) (void *security);
+ int (*msg_msg_restore) (struct msg_msg *msg, char *ctx);
void (*msg_msg_free_security) (struct msg_msg *msg);
int (*msg_queue_alloc_security) (struct msg_queue *msq);
@@ -1755,6 +1809,8 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer
void security_inode_getsecid(const struct inode *inode, u32 *secid);
int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
+char *security_file_checkpoint(void *security);
+int security_file_restore(struct file *file, char *ctx);
void security_file_free(struct file *file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_mmap(struct file *file, unsigned long reqprot,
@@ -1770,6 +1826,8 @@ int security_file_send_sigiotask(struct task_struct *tsk,
int security_file_receive(struct file *file);
int security_dentry_open(struct file *file, const struct cred *cred);
int security_task_create(unsigned long clone_flags);
+char *security_cred_checkpoint(void *security);
+int security_cred_restore(struct file *file, struct cred *cred, char *ctx);
void security_cred_free(struct cred *cred);
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
void security_commit_creds(struct cred *new, const struct cred *old);
@@ -1800,7 +1858,11 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
void security_task_to_inode(struct task_struct *p, struct inode *inode);
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid);
+char *security_ipc_checkpoint(void *security);
+int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx);
int security_msg_msg_alloc(struct msg_msg *msg);
+char *security_msg_msg_checkpoint(void *security);
+int security_msg_msg_restore(struct msg_msg *msg, char *ctx);
void security_msg_msg_free(struct msg_msg *msg);
int security_msg_queue_alloc(struct msg_queue *msq);
void security_msg_queue_free(struct msg_queue *msq);
@@ -2248,6 +2310,19 @@ static inline int security_file_alloc(struct file *file)
return 0;
}
+static inline char *security_file_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_file_restore(struct file *file, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_file_free(struct file *file)
{ }
@@ -2312,6 +2387,20 @@ static inline int security_task_create(unsigned long clone_flags)
return 0;
}
+static inline char *security_cred_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_cred_restore(struct file *file, struct cred *cred,
+ char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_cred_free(struct cred *cred)
{ }
@@ -2454,11 +2543,37 @@ static inline void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
*secid = 0;
}
+static inline char *security_ipc_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline int security_msg_msg_alloc(struct msg_msg *msg)
{
return 0;
}
+static inline char *security_msg_msg_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_msg_msg_free(struct msg_msg *msg)
{ }
@@ -3055,5 +3170,33 @@ static inline void free_secdata(void *secdata)
{ }
#endif /* CONFIG_SECURITY */
+#ifdef CONFIG_CHECKPOINT
+#define CKPT_SECURITY_MSG_MSG 1
+#define CKPT_SECURITY_IPC 2
+#define CKPT_SECURITY_FILE 3
+#define CKPT_SECURITY_CRED 4
+#define CKPT_SECURITY_MAX 4
+
+#ifdef CONFIG_SECURITY
+int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
+ int sectype, int *secref);
+int security_restore_obj(struct ckpt_ctx *ctx, void *obj,
+ int sectype, int secref);
+#else
+static inline int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
+ int sectype, int *secref)
+{
+ *secref = SECURITY_CTX_NONE;
+ return 0;
+}
+static inline int security_restore_obj(struct ckpt_ctx *ctx, void *obj,
+ int sectype, int secref)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITY */
+
+#endif /* CONFIG_CHECKPOINT */
+
#endif /* ! __LINUX_SECURITY_H */
diff --git a/ipc/checkpoint.c b/ipc/checkpoint.c
index 8e6e9ba..b0949ea 100644
--- a/ipc/checkpoint.c
+++ b/ipc/checkpoint.c
@@ -31,7 +31,8 @@ static char *ipc_ind_to_str[] = { "sem", "msg", "shm" };
* Checkpoint
*/
-int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+int checkpoint_fill_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm)
{
if (ipcperms(perm, S_IROTH))
@@ -45,8 +46,8 @@ int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
h->cgid = perm->cgid;
h->mode = perm->mode & S_IRWXUGO;
h->seq = perm->seq;
-
- return 0;
+ return security_checkpoint_obj(ctx, perm->security, CKPT_SECURITY_IPC,
+ &h->sec_ref);
}
static int checkpoint_ipc_any(struct ckpt_ctx *ctx,
@@ -176,7 +177,8 @@ static int validate_created_perms(struct ckpt_hdr_ipc_perms *h)
return 1;
}
-int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+int restore_load_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm)
{
if (h->id < 0)
@@ -205,16 +207,9 @@ int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
perm->cgid = h->cgid;
perm->mode = h->mode;
perm->seq = h->seq;
- /*
- * Todo: restore perm->security.
- * At the moment it gets set by security_x_alloc() called through
- * ipcget()->ipcget_public()->ops-.getnew (->nequeue for instance)
- * We will want to ask the LSM to consider resetting the
- * checkpointed ->security, based on current_security(),
- * the checkpointed ->security, and the checkpoint file context.
- */
- return 0;
+ return security_restore_obj(ctx, (void *)perm, CKPT_SECURITY_IPC,
+ h->sec_ref);
}
static int restore_ipc_any(struct ckpt_ctx *ctx, struct ipc_namespace *ipc_ns,
diff --git a/ipc/checkpoint_msg.c b/ipc/checkpoint_msg.c
index b933c19..17151b2 100644
--- a/ipc/checkpoint_msg.c
+++ b/ipc/checkpoint_msg.c
@@ -37,7 +37,7 @@ static int fill_ipc_msg_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&msq->q_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &msq->q_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &msq->q_perm);
if (ret < 0)
goto unlock;
@@ -63,14 +63,20 @@ static int checkpoint_msg_contents(struct ckpt_ctx *ctx, struct msg_msg *msg)
struct ckpt_hdr_ipc_msg_msg *h;
struct msg_msgseg *seg;
int total, len;
- int ret;
+ int secref, ret;
+ ret = security_checkpoint_obj(ctx, msg->security,
+ CKPT_SECURITY_MSG_MSG,
+ &secref);
+ if (ret < 0)
+ return ret;
h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_IPC_MSG_MSG);
if (!h)
return -ENOMEM;
h->m_type = msg->m_type;
h->m_ts = msg->m_ts;
+ h->sec_ref = secref;
ret = ckpt_write_obj(ctx, &h->h);
ckpt_hdr_put(ctx, h);
@@ -177,7 +183,7 @@ static int load_ipc_msg_hdr(struct ckpt_ctx *ctx,
{
int ret = 0;
- ret = restore_load_ipc_perms(&h->perms, &msq->q_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &msq->q_perm);
if (ret < 0)
return ret;
@@ -224,6 +230,11 @@ static struct msg_msg *restore_msg_contents_one(struct ckpt_ctx *ctx, int *clen)
msg->next = NULL;
pseg = &msg->next;
+ ret = security_restore_obj(ctx, (void *) msg, CKPT_SECURITY_MSG_MSG,
+ h->sec_ref);
+ if (ret < 0)
+ goto out;
+
ret = _ckpt_read_buffer(ctx, (msg + 1), len);
if (ret < 0)
goto out;
diff --git a/ipc/checkpoint_sem.c b/ipc/checkpoint_sem.c
index 76eb2b9..53a19ed 100644
--- a/ipc/checkpoint_sem.c
+++ b/ipc/checkpoint_sem.c
@@ -37,7 +37,7 @@ static int fill_ipc_sem_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&sem->sem_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &sem->sem_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &sem->sem_perm);
if (ret < 0)
goto unlock;
@@ -113,7 +113,7 @@ static int load_ipc_sem_hdr(struct ckpt_ctx *ctx,
{
int ret = 0;
- ret = restore_load_ipc_perms(&h->perms, &sem->sem_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &sem->sem_perm);
if (ret < 0)
return ret;
diff --git a/ipc/checkpoint_shm.c b/ipc/checkpoint_shm.c
index 826e430..01091d9 100644
--- a/ipc/checkpoint_shm.c
+++ b/ipc/checkpoint_shm.c
@@ -41,7 +41,7 @@ static int fill_ipc_shm_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&shp->shm_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &shp->shm_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &shp->shm_perm);
if (ret < 0)
goto unlock;
@@ -165,7 +165,7 @@ static int load_ipc_shm_hdr(struct ckpt_ctx *ctx,
{
int ret;
- ret = restore_load_ipc_perms(&h->perms, &shp->shm_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &shp->shm_perm);
if (ret < 0)
return ret;
diff --git a/ipc/util.h b/ipc/util.h
index ba080de..ce34de0 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -199,9 +199,11 @@ void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp);
#ifdef CONFIG_CHECKPOINT
-extern int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+extern int checkpoint_fill_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm);
-extern int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+extern int restore_load_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm);
extern int ckpt_collect_ipc_shm(int id, void *p, void *data);
diff --git a/kernel/cred.c b/kernel/cred.c
index 62d28a4..430fb49 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -706,10 +706,25 @@ int cred_setfsgid(struct cred *new, gid_t gid, gid_t *old_fsgid)
}
#ifdef CONFIG_CHECKPOINT
+#ifdef CONFIG_SECURITY
+int checkpoint_cred_security(struct ckpt_ctx *ctx, struct cred *cred,
+ int *secref)
+{
+ return security_checkpoint_obj(ctx, cred->security,
+ CKPT_SECURITY_CRED, secref);
+}
+#else
+int checkpoint_cred_security(struct ckpt_ctx *ctx, struct cred *cred,
+ int *secref)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
{
int ret;
- int groupinfo_ref, user_ref;
+ int groupinfo_ref, user_ref, secref;
struct ckpt_hdr_cred *h;
groupinfo_ref = checkpoint_obj(ctx, cred->group_info,
@@ -719,13 +734,16 @@ static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
user_ref = checkpoint_obj(ctx, cred->user, CKPT_OBJ_USER);
if (user_ref < 0)
return user_ref;
+ ret = checkpoint_cred_security(ctx, cred, &secref);
+ if (ret < 0)
+ return ret;
h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_CRED);
if (!h)
return -ENOMEM;
- ckpt_debug("cred uid %d fsuid %d gid %d\n", cred->uid, cred->fsuid,
- cred->gid);
+ ckpt_debug("cred uid %d fsuid %d gid %d secref %d\n", cred->uid,
+ cred->fsuid, cred->gid, secref);
h->uid = cred->uid;
h->suid = cred->suid;
h->euid = cred->euid;
@@ -735,6 +753,7 @@ static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
h->sgid = cred->sgid;
h->egid = cred->egid;
h->fsgid = cred->fsgid;
+ h->sec_ref = secref;
checkpoint_capabilities(&h->cap_s, cred);
@@ -808,6 +827,10 @@ static struct cred *do_restore_cred(struct ckpt_ctx *ctx)
ret = cred_setfsgid(cred, h->fsgid, &oldgid);
if (oldgid != h->fsgid && ret < 0)
goto err_putcred;
+ ret = security_restore_obj(ctx, (void *) cred, CKPT_SECURITY_CRED,
+ h->sec_ref);
+ if (ret)
+ goto err_putcred;
ret = restore_capabilities(&h->cap_s, cred);
if (ret)
goto err_putcred;
diff --git a/security/capability.c b/security/capability.c
index 23026e2..174c3ef 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -315,6 +315,16 @@ static int cap_file_permission(struct file *file, int mask)
return 0;
}
+static inline char *cap_file_checkpoint(void *security)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+static int cap_file_restore(struct file *file, char *ctx)
+{
+ return -EOPNOTSUPP;
+}
+
static int cap_file_alloc_security(struct file *file)
{
return 0;
@@ -373,6 +383,16 @@ static int cap_task_create(unsigned long clone_flags)
return 0;
}
+static char *cap_cred_checkpoint(void *security)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+static int cap_cred_restore(struct file *file, struct cred *cred, char *ctx)
+{
+ return -EOPNOTSUPP;
+}
+
static void cap_cred_free(struct cred *cred)
{
}
@@ -476,11 +496,31 @@ static void cap_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
*secid = 0;
}
+static char *cap_ipc_checkpoint(void *security)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+static int cap_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ return -EOPNOTSUPP;
+}
+
static int cap_msg_msg_alloc_security(struct msg_msg *msg)
{
return 0;
}
+static inline char *cap_msg_msg_checkpoint(void *security)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+static int cap_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ return -EOPNOTSUPP;
+}
+
static void cap_msg_msg_free_security(struct msg_msg *msg)
{
}
@@ -948,6 +988,8 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, path_truncate);
#endif
set_to_cap_if_null(ops, file_permission);
+ set_to_cap_if_null(ops, file_checkpoint);
+ set_to_cap_if_null(ops, file_restore);
set_to_cap_if_null(ops, file_alloc_security);
set_to_cap_if_null(ops, file_free_security);
set_to_cap_if_null(ops, file_ioctl);
@@ -960,6 +1002,8 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, file_receive);
set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create);
+ set_to_cap_if_null(ops, cred_checkpoint);
+ set_to_cap_if_null(ops, cred_restore);
set_to_cap_if_null(ops, cred_free);
set_to_cap_if_null(ops, cred_prepare);
set_to_cap_if_null(ops, cred_commit);
@@ -986,7 +1030,11 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, task_to_inode);
set_to_cap_if_null(ops, ipc_permission);
set_to_cap_if_null(ops, ipc_getsecid);
+ set_to_cap_if_null(ops, ipc_checkpoint);
+ set_to_cap_if_null(ops, ipc_restore);
set_to_cap_if_null(ops, msg_msg_alloc_security);
+ set_to_cap_if_null(ops, msg_msg_checkpoint);
+ set_to_cap_if_null(ops, msg_msg_restore);
set_to_cap_if_null(ops, msg_msg_free_security);
set_to_cap_if_null(ops, msg_queue_alloc_security);
set_to_cap_if_null(ops, msg_queue_free_security);
diff --git a/security/security.c b/security/security.c
index e4fa91a..7a00cdc 100644
--- a/security/security.c
+++ b/security/security.c
@@ -633,6 +633,16 @@ int security_file_alloc(struct file *file)
return security_ops->file_alloc_security(file);
}
+char *security_file_checkpoint(void *security)
+{
+ return security_ops->file_checkpoint(security);
+}
+
+int security_file_restore(struct file *file, char *ctx)
+{
+ return security_ops->file_restore(file, ctx);
+}
+
void security_file_free(struct file *file)
{
security_ops->file_free_security(file);
@@ -692,6 +702,16 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(clone_flags);
}
+char *security_cred_checkpoint(void *security)
+{
+ return security_ops->cred_checkpoint(security);
+}
+
+int security_cred_restore(struct file *file, struct cred *cred, char *ctx)
+{
+ return security_ops->cred_restore(file, cred, ctx);
+}
+
void security_cred_free(struct cred *cred)
{
security_ops->cred_free(cred);
@@ -827,11 +847,31 @@ void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
security_ops->ipc_getsecid(ipcp, secid);
}
+char *security_ipc_checkpoint(void *security)
+{
+ return security_ops->ipc_checkpoint(security);
+}
+
+int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ return security_ops->ipc_restore(ipcp, ctx);
+}
+
int security_msg_msg_alloc(struct msg_msg *msg)
{
return security_ops->msg_msg_alloc_security(msg);
}
+char *security_msg_msg_checkpoint(void *security)
+{
+ return security_ops->msg_msg_checkpoint(security);
+}
+
+int security_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ return security_ops->msg_msg_restore(msg, ctx);
+}
+
void security_msg_msg_free(struct msg_msg *msg)
{
security_ops->msg_msg_free_security(msg);
@@ -1270,3 +1310,130 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
}
#endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_CHECKPOINT
+
+/**
+ * security_checkpoint_obj - if first checkpoint of this void* security,
+ * then 1. ask the LSM for a string representing the context, 2. checkpoint
+ * that string
+ * @ctx: the checkpoint context
+ * @security: the void* security being checkpointed
+ * @sectype: indicates the type of object, because LSMs can (and do) store
+ * @secref: We return the objref here
+ * different types of data for different types of objects.
+ *
+ * Returns the objref of the checkpointed ckpt_lsm_string representing the
+ * context, or -error on error.
+ *
+ * This is only used at checkpoint of course.
+ */
+int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
+ int sectype, int *secref)
+{
+ int new, ret = -ENOMEM;
+ char *str;
+ struct ckpt_lsm_string *l;
+
+ if (!security)
+ return -EOPNOTSUPP;
+
+ *secref = ckpt_obj_lookup_add(ctx, security, CKPT_OBJ_SECURITY_PTR,
+ &new);
+ if (!new)
+ return 0;
+
+ /*
+ * Ask the LSM for a string representation
+ */
+ switch (sectype) {
+ case CKPT_SECURITY_MSG_MSG:
+ str = security_msg_msg_checkpoint(security);
+ break;
+ case CKPT_SECURITY_IPC:
+ str = security_ipc_checkpoint(security);
+ break;
+ case CKPT_SECURITY_FILE:
+ str = security_file_checkpoint(security);
+ break;
+ case CKPT_SECURITY_CRED:
+ str = security_cred_checkpoint(security);
+ break;
+ default:
+ str = ERR_PTR(-EINVAL);
+ break;
+ }
+
+ if (IS_ERR(str)) {
+ if (PTR_ERR(str) == -EOPNOTSUPP) {
+ *secref = SECURITY_CTX_NONE;
+ return 0;
+ }
+ return PTR_ERR(str);
+ }
+
+ l = kzalloc(sizeof(*l), GFP_KERNEL);
+ if (!l) {
+ kfree(str);
+ return -ENOMEM;
+ }
+ l->ptrref = *secref;
+ l->sectype = sectype;
+ l->string = str;
+ kref_init(&l->kref);
+ ret = checkpoint_obj(ctx, l, CKPT_OBJ_SECURITY);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * v is (void *) some_object, where some_object is
+ * struct file, struct msg_msg, struct ipc_perm, or struct cred
+ */
+int security_restore_obj(struct ckpt_ctx *ctx, void *v, int sectype,
+ int secref)
+{
+ struct ckpt_lsm_string *l;
+ int ret;
+
+ /* return if caller didn't want to restore checkpointed labels */
+ if (!(ctx->uflags & RESTART_KEEP_LSM))
+ return 0;
+
+ l = ckpt_obj_fetch(ctx, secref, CKPT_OBJ_SECURITY);
+ if (IS_ERR(l))
+ return PTR_ERR(l);
+
+ /* return if checkpointed label was "Not Applicable" */
+ if (secref == SECURITY_CTX_NONE)
+ return 0;
+
+ /* Ask the LSM to apply a void*security to the object
+ * based on the checkpointed context string */
+ switch (sectype) {
+ case CKPT_SECURITY_IPC:
+ ret = security_ipc_restore(v, l->string);
+ break;
+ case CKPT_SECURITY_MSG_MSG:
+ ret = security_msg_msg_restore(v, l->string);
+ break;
+ case CKPT_SECURITY_FILE:
+ ret = security_file_restore(v, l->string);
+ break;
+ case CKPT_SECURITY_CRED:
+ ret = security_cred_restore(ctx->file, v, l->string);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ ckpt_debug("sectype %d objref %d lsm's hook returned %d\n",
+ sectype, secref, ret);
+ if (ret == -EOPNOTSUPP)
+ ret = 0;
+
+ return ret;
+}
+#endif
--
1.6.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH user-cr] restart: accept the lsm_name field in header and add -k flag (v2)
[not found] ` <20091019144315.GA30535-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-19 14:43 ` [PATCH 2/4] cr: add generic LSM c/r support (v6) Serge E. Hallyn
@ 2009-10-19 14:44 ` Serge E. Hallyn
2009-10-19 14:44 ` [PATCH 3/4] cr: add smack support to lsm c/r (v6) Serge E. Hallyn
2009-10-19 14:44 ` [PATCH 4/4] cr: add selinux support (v6) Serge E. Hallyn
3 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-19 14:44 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
The checkpoint file header now has an 11-character string
containing the name of the active LSM, following the uts
info, and a variable length buffer type conaining LSM-specific
version information (for instance a sha1sum of policy).
Handle these.
Also add a -k (--keeplsm) flag to tell restart to set the
RESTART_KEEP_LSM flag to sys_restart().
Changelog:
oct 15: separate out from container config section patch
oct 05: 1. move keep_lsm into arg struct
2. read a separate container config section
3. use CHECKPOINT_LSM_NAME_MAX
Signed-off-by: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
restart.c | 39 +++++++++++++++++++++++++++++++++++++--
1 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/restart.c b/restart.c
index fbaab88..dd2dc12 100644
--- a/restart.c
+++ b/restart.c
@@ -68,6 +68,7 @@ static char usage_str[] =
" --signal=SIG send SIG to root task on SIGINT (default: SIGKILL\n"
" to container root, SIGINT otherwise)\n"
" -w,--wait wait for root task to termiate (default)\n"
+" -k,--keeplsm Try to recreate original LSM labels on all objects\n"
" --show-status show exit status of root task (implies -w)\n"
" --copy-status imitate exit status of root task (implies -w)\n"
" -W,--no-wait do not wait for root task to terminate\n"
@@ -352,6 +353,7 @@ struct args {
int copy_status;
char *freezer;
char *input;
+ int keep_lsm;
};
static void usage(char *str)
@@ -382,6 +384,7 @@ static void parse_args(struct args *args, int argc, char *argv[])
{ "self", no_argument, NULL, 6},
{ "signal", required_argument, NULL, 4 },
{ "inspect", no_argument, NULL, 5 },
+ { "keeplsm", no_argument, NULL, 'k' },
{ "input", required_argument, NULL, 'i' },
{ "root", required_argument, NULL, 'r' },
{ "wait", no_argument, NULL, 'w' },
@@ -393,7 +396,7 @@ static void parse_args(struct args *args, int argc, char *argv[])
{ "debug", no_argument, NULL, 'd' },
{ NULL, 0, NULL, 0 }
};
- static char optc[] = "hdvpPwWF:r:i:";
+ static char optc[] = "hdvpkPwWF:r:i:";
int sig;
@@ -448,6 +451,9 @@ static void parse_args(struct args *args, int argc, char *argv[])
case 'w':
args->wait = 1;
break;
+ case 'k':
+ args->keep_lsm = RESTART_KEEP_LSM;
+ break;
case 'W':
args->wait = 0;
break;
@@ -929,6 +935,7 @@ static int ckpt_coordinator(struct ckpt_ctx *ctx)
if (ctx->args->freezer)
flags |= RESTART_FROZEN;
+ flags |= ctx->args->keep_lsm;
ret = restart(root_pid, STDIN_FILENO, flags);
if (ret < 0) {
@@ -1588,6 +1595,8 @@ static int ckpt_make_tree(struct ckpt_ctx *ctx, struct task *task)
if (task->flags & (TASK_GHOST | TASK_DEAD))
flags |= RESTART_GHOST;
+ flags |= ctx->args->keep_lsm;
+
/* on success this doesn't return */
ckpt_dbg("about to call sys_restart(), flags %#lx\n", flags);
ret = restart(0, STDIN_FILENO, flags);
@@ -2134,10 +2143,23 @@ static int ckpt_read_header_arch(struct ckpt_ctx *ctx)
static int ckpt_read_container(struct ckpt_ctx *ctx)
{
+ int ret;
struct ckpt_hdr_container *h;
+ char *ptr;
h = (struct ckpt_hdr_container *) ctx->container;
- return ckpt_read_obj_type(ctx, h, sizeof(*h), CKPT_HDR_CONTAINER);
+ ret = ckpt_read_obj_type(ctx, h, sizeof(*h), CKPT_HDR_CONTAINER);
+ if (ret < 0)
+ return ret;
+
+ ptr = (char *) h;
+ ptr += ((struct ckpt_hdr *) ptr)->len;
+ ret = ckpt_read_obj_buffer(ctx, ptr, CHECKPOINT_LSM_NAME_MAX + 1);
+ if (ret < 0)
+ return ret;
+
+ ptr += ((struct ckpt_hdr *) ptr)->len;
+ return ckpt_read_obj_type(ctx, ptr, 200, CKPT_HDR_LSM_INFO);
}
static int ckpt_read_tree(struct ckpt_ctx *ctx)
@@ -2215,9 +2237,22 @@ static int ckpt_write_header_arch(struct ckpt_ctx *ctx)
static int ckpt_write_container(struct ckpt_ctx *ctx)
{
char *ptr;
+ int ret;
ptr = (char *) ctx->container;
/* write the container info section */
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) ptr);
+ if (ret < 0)
+ return ret;
+
+ /* write the lsm name buffer */
+ ptr += ((struct ckpt_hdr *) ptr)->len;
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) ptr);
+ if (ret < 0)
+ return ret;
+
+ /* write the lsm policy section */
+ ptr += ((struct ckpt_hdr *) ptr)->len;
return ckpt_write_obj(ctx, (struct ckpt_hdr *) ptr);
}
--
1.6.1.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 3/4] cr: add smack support to lsm c/r (v6)
[not found] ` <20091019144315.GA30535-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-19 14:43 ` [PATCH 2/4] cr: add generic LSM c/r support (v6) Serge E. Hallyn
2009-10-19 14:44 ` [PATCH user-cr] restart: accept the lsm_name field in header and add -k flag (v2) Serge E. Hallyn
@ 2009-10-19 14:44 ` Serge E. Hallyn
2009-10-19 14:44 ` [PATCH 4/4] cr: add selinux support (v6) Serge E. Hallyn
3 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-19 14:44 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
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 implements checkpoint and restore of Smack security
labels. The rules are the same as in previous versions:
1. when objects are created during restore() they are
automatically labeled with current_security().
2. if there was a label checkpointed with the object,
and that label != current_security() (which is the
same as obj->security), then the object is relabeled
if the sys_restart() caller has CAP_MAC_ADMIN.
Otherwise we return -EPERM.
This has been tested by checkpointing tasks under labels
_, vs1, and vs2, and restarting from tasks under _, vs1,
and vs2, with and without CAP_MAC_ADMIN in the bounding
set, and with and without the '-k' (keep_lsm) flag to mktree.
Expected results:
#shell 1:
echo vs1 > /proc/self/attr/current
ckpt > out
echo vs2 > /proc/self/attr/current
mktree -F /cgroup/2 < out
(frozen)
# shell 2:
cat /proc/`pidof ckpt`/attr/current
vs2
echo THAWED > /cgroup/2/freezer.state
# shell 1:
mktree -k -F /cgroup/2 < out
(frozen)
# shell 2:
cat /proc/`pidof ckpt`/attr/current
vs1
echo THAWED > /cgroup/2/freezer.state
# shell 1:
capsh --drop=cap_mac_admin --
mktree -k -F /cgroup/2 < out
(permission denied)
There are testcases in git://git.sr71.net/~hallyn/cr_tests.git
under cr_tests/smack, which automate the above (and pass).
Changelog:
sep 3: add a version to smack lsm, accessible through
/smack/version (Casey and Serge)
sep 10: rename xyz_get_ctx() to xyz_checkpoint()
Signed-off-by: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Acked-by: Casey Schaufler <casey-iSGtlc1asvQWG2LlvL+J4A@public.gmane.org>
---
checkpoint/restart.c | 1 +
security/smack/smack.h | 1 +
security/smack/smack_lsm.c | 141 ++++++++++++++++++++++++++++++++++++++++++++
security/smack/smackfs.c | 83 ++++++++++++++++++++++++++
4 files changed, 226 insertions(+), 0 deletions(-)
diff --git a/checkpoint/restart.c b/checkpoint/restart.c
index 0cd721c..caadde2 100644
--- a/checkpoint/restart.c
+++ b/checkpoint/restart.c
@@ -647,6 +647,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, "default") != 0) {
ckpt_debug("c/r: RESTART_KEEP_LSM unsupported for %s\n",
ctx->lsm_name);
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 243bec1..b5c1ce6 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -216,6 +216,7 @@ u32 smack_to_secid(const char *);
extern int smack_cipso_direct;
extern char *smack_net_ambient;
extern char *smack_onlycap;
+extern char *smack_version;
extern const char *smack_cipso_option;
extern struct smack_known smack_known_floor;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 0023182..1058de4 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -27,6 +27,7 @@
#include <linux/udp.h>
#include <linux/mutex.h>
#include <linux/pipe_fs_i.h>
+#include <linux/checkpoint.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <linux/audit.h>
@@ -892,6 +893,28 @@ static int smack_file_permission(struct file *file, int mask)
return 0;
}
+static inline char *smack_file_checkpoint(void *security)
+{
+ return kstrdup((char *)security, GFP_KERNEL);
+}
+
+static inline int smack_file_restore(struct file *file, char *ctx)
+{
+ char *newsmack = smk_import(ctx, 0);
+
+ if (newsmack == NULL)
+ return -EINVAL;
+ /* I think by definition, file->f_security == current_security
+ * right now, but let's assume somehow it might not be */
+ if (newsmack == file->f_security)
+ return 0;
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+ file->f_security = newsmack;
+
+ return 0;
+}
+
/**
* smack_file_alloc_security - assign a file security blob
* @file: the object
@@ -1079,6 +1102,27 @@ static int smack_file_receive(struct file *file)
* Task hooks
*/
+static inline char *smack_cred_checkpoint(void *security)
+{
+ return kstrdup((char *)security, GFP_KERNEL);
+}
+
+static inline int smack_cred_restore(struct file *file, struct cred *cred,
+ char *ctx)
+{
+ char *newsmack = smk_import(ctx, 0);
+
+ if (newsmack == NULL)
+ return -EINVAL;
+ if (newsmack == cred->security)
+ return 0;
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+ cred->security = newsmack;
+
+ return 0;
+}
+
/**
* smack_cred_free - "free" task-level security credentials
* @cred: the credentials in question
@@ -1742,6 +1786,26 @@ static int smack_msg_msg_alloc_security(struct msg_msg *msg)
return 0;
}
+static inline char *smack_msg_msg_checkpoint(void *security)
+{
+ return kstrdup((char *)security, GFP_KERNEL);
+}
+
+static inline int smack_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ char *newsmack = smk_import(ctx, 0);
+
+ if (newsmack == NULL)
+ return -EINVAL;
+ if (newsmack == msg->security)
+ return 0;
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+ msg->security = newsmack;
+
+ return 0;
+}
+
/**
* smack_msg_msg_free_security - Clear the security blob for msg_msg
* @msg: the object
@@ -2175,6 +2239,26 @@ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
*secid = smack_to_secid(smack);
}
+static inline char *smack_ipc_checkpoint(void *security)
+{
+ return kstrdup((char *)security, GFP_KERNEL);
+}
+
+static inline int smack_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ char *newsmack = smk_import(ctx, 0);
+
+ if (newsmack == NULL)
+ return -EINVAL;
+ if (newsmack == ipcp->security)
+ return 0;
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+ ipcp->security = newsmack;
+
+ return 0;
+}
+
/**
* smack_d_instantiate - Make sure the blob is correct on an inode
* @opt_dentry: unused
@@ -3029,6 +3113,51 @@ static void smack_release_secctx(char *secdata, u32 seclen)
{
}
+#ifdef CONFIG_CHECKPOINT
+
+static int smack_may_restart(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr *chp;
+ char *saved_version;
+ int ret = 0;
+
+ chp = ckpt_read_buf_type(ctx, CKPT_LSM_INFO_LEN, CKPT_HDR_LSM_INFO);
+ if (IS_ERR(chp))
+ return PTR_ERR(chp);
+
+ /*
+ * After the checkpoint header comes the null terminated
+ * Smack "policy" version. This will usually be the
+ * floor label "_".
+ */
+ saved_version = (char *)(chp + 1);
+
+ /*
+ * Of course, it is possible that a "policy" version mismatch
+ * is not considered threatening.
+ */
+ if (!(ctx->uflags & RESTART_KEEP_LSM))
+ goto skip;
+
+ if (strcmp(saved_version, smack_version) != 0) {
+ ckpt_debug("Smack version at checkpoint was"
+ "\"%s\", now is \"%s\".\n",
+ saved_version, smack_version);
+ ret = -EINVAL;
+ }
+skip:
+ ckpt_hdr_put(ctx, chp);
+ return ret;
+}
+
+static int smack_checkpoint_header(struct ckpt_ctx *ctx)
+{
+ return ckpt_write_obj_type(ctx, smack_version,
+ strlen(smack_version) + 1,
+ CKPT_HDR_LSM_INFO);
+}
+#endif
+
struct security_operations smack_ops = {
.name = "smack",
@@ -3064,6 +3193,8 @@ struct security_operations smack_ops = {
.inode_getsecid = smack_inode_getsecid,
.file_permission = smack_file_permission,
+ .file_checkpoint = smack_file_checkpoint,
+ .file_restore = smack_file_restore,
.file_alloc_security = smack_file_alloc_security,
.file_free_security = smack_file_free_security,
.file_ioctl = smack_file_ioctl,
@@ -3073,6 +3204,8 @@ struct security_operations smack_ops = {
.file_send_sigiotask = smack_file_send_sigiotask,
.file_receive = smack_file_receive,
+ .cred_checkpoint = smack_cred_checkpoint,
+ .cred_restore = smack_cred_restore,
.cred_free = smack_cred_free,
.cred_prepare = smack_cred_prepare,
.cred_commit = smack_cred_commit,
@@ -3094,8 +3227,12 @@ struct security_operations smack_ops = {
.ipc_permission = smack_ipc_permission,
.ipc_getsecid = smack_ipc_getsecid,
+ .ipc_checkpoint = smack_ipc_checkpoint,
+ .ipc_restore = smack_ipc_restore,
.msg_msg_alloc_security = smack_msg_msg_alloc_security,
+ .msg_msg_checkpoint = smack_msg_msg_checkpoint,
+ .msg_msg_restore = smack_msg_msg_restore,
.msg_msg_free_security = smack_msg_msg_free_security,
.msg_queue_alloc_security = smack_msg_queue_alloc_security,
@@ -3155,6 +3292,10 @@ struct security_operations smack_ops = {
.secid_to_secctx = smack_secid_to_secctx,
.secctx_to_secid = smack_secctx_to_secid,
.release_secctx = smack_release_secctx,
+#ifdef CONFIG_CHECKPOINT
+ .may_restart = smack_may_restart,
+ .checkpoint_header = smack_checkpoint_header,
+#endif
};
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index f83a809..0f0d5aa 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -42,6 +42,7 @@ enum smk_inos {
SMK_NETLBLADDR = 8, /* single label hosts */
SMK_ONLYCAP = 9, /* the only "capable" label */
SMK_LOGGING = 10, /* logging */
+ SMK_VERSION = 11, /* logging */
};
/*
@@ -51,6 +52,7 @@ static DEFINE_MUTEX(smack_list_lock);
static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock);
static DEFINE_MUTEX(smk_netlbladdr_lock);
+static DEFINE_MUTEX(smack_version_lock);
/*
* This is the "ambient" label for network traffic.
@@ -60,6 +62,16 @@ static DEFINE_MUTEX(smk_netlbladdr_lock);
char *smack_net_ambient = smack_known_floor.smk_known;
/*
+ * This is the policy version. In the interest of simplicity the
+ * policy version is a string that meets all of the requirements
+ * of a Smack label. This is enforced by the expedient of
+ * importing it like a label. The policy version is thus always
+ * also a valid label on the system. This may prove useful under
+ * some as yet undiscovered circumstance.
+ */
+char *smack_version = smack_known_floor.smk_known;
+
+/*
* This is the level in a CIPSO header that indicates a
* smack label is contained directly in the category set.
* It can be reset via smackfs/direct
@@ -1255,6 +1267,75 @@ static const struct file_operations smk_logging_ops = {
.read = smk_read_logging,
.write = smk_write_logging,
};
+
+#define SMK_VERSIONLEN 12
+/**
+ * smk_read_version - read() for /smack/version
+ * @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_version(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int rc;
+
+ if (*ppos != 0)
+ return 0;
+
+ mutex_lock(&smack_version_lock);
+
+ rc = simple_read_from_buffer(buf, count, ppos, smack_version,
+ strlen(smack_version) + 1);
+
+ mutex_unlock(&smack_version_lock);
+
+ return rc;
+}
+
+/**
+ * smk_write_version - write() for /smack/version
+ * @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_version(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *smack;
+ char in[SMK_LABELLEN];
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= SMK_LABELLEN)
+ return -EINVAL;
+
+ if (copy_from_user(in, buf, count) != 0)
+ return -EFAULT;
+
+ smack = smk_import(in, count);
+ if (smack == NULL)
+ return -EINVAL;
+
+ mutex_lock(&smack_version_lock);
+ smack_version = smack;
+ mutex_unlock(&smack_version_lock);
+
+ return count;
+}
+
+static const struct file_operations smk_version_ops = {
+ .read = smk_read_version,
+ .write = smk_write_version,
+};
+
/**
* smk_fill_super - fill the /smackfs superblock
* @sb: the empty superblock
@@ -1287,6 +1368,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
[SMK_LOGGING] =
{"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
+ [SMK_VERSION] =
+ {"version", &smk_version_ops, S_IRUGO|S_IWUSR},
/* last one */ {""}
};
--
1.6.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 4/4] cr: add selinux support (v6)
[not found] ` <20091019144315.GA30535-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
` (2 preceding siblings ...)
2009-10-19 14:44 ` [PATCH 3/4] cr: add smack support to lsm c/r (v6) Serge E. Hallyn
@ 2009-10-19 14:44 ` Serge E. Hallyn
3 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-19 14:44 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
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:
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-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
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 caadde2..5b2a4dc 100644
--- a/checkpoint/restart.c
+++ b/checkpoint/restart.c
@@ -648,6 +648,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 8d8b69c..e6994eb 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"
@@ -2961,6 +2962,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);
@@ -3219,6 +3318,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 avc_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
+ */
+
+ AVC_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;
+}
+
+
/*
* detach and free the LSM part of a set of credentials
*/
@@ -4658,6 +4937,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;
@@ -5061,6 +5378,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 avc_audit_data ad;
+
+ ret = security_context_to_sid(ctx, strlen(ctx), &sid);
+ if (ret)
+ return ret;
+
+ if (isec->sid == sid)
+ return 0;
+
+ AVC_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)
@@ -5382,6 +5740,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,
@@ -5396,6 +5756,8 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
+ .cred_checkpoint = selinux_cred_checkpoint,
+ .cred_restore = selinux_cred_restore,
.cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare,
.kernel_act_as = selinux_kernel_act_as,
@@ -5417,8 +5779,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 31df1d7..a2c35d7 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")
@@ -107,6 +111,7 @@
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
+ 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 d645192..58ad588 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
@@ -453,6 +455,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
@@ -462,6 +465,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
@@ -483,6 +487,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
--
1.6.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <20091019144341.GA30566-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2009-10-19 18:13 ` Oren Laadan
[not found] ` <4ADCAC5B.9080205-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
0 siblings, 1 reply; 19+ messages in thread
From: Oren Laadan @ 2009-10-19 18:13 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: Linux Containers
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 generic support for c/r of LSM credentials. Support
> for Smack and SELinux (and TOMOYO if appropriate) will be added later.
> Capabilities is already supported through generic creds code.
>
> This patch supports ipc_perm, msg_msg, cred (task) and file ->security
> fields. Inodes, superblocks, netif, and xfrm currently are restored
> not through sys_restart() but through container creation, and so the
> security fields should be done then as well. Network should be added
> when network c/r is added.
>
> Briefly, all security fields must be exported by the LSM as a simple
> null-terminated string. They are checkpointed through the
> security_checkpoint_obj() helper, because we must pass it an extra
> sectype field. Splitting SECURITY_OBJ_SEC into one type per object
> type would not work because, in Smack, one void* security is used for
> all object types. But we must pass the sectype field because in
> SELinux a different type of structure is stashed in each object type.
>
> The RESTART_KEEP_LSM flag indicates that the LSM should
> attempt to reuse checkpointed security labels. It is always
> invalid when the LSM at restart differs from that at checkpoint.
> It is currently only usable for capabilities.
>
> (For capabilities, restart without RESTART_KEEP_LSM is technically
> not implemented. There actually might be a use case for that,
> but the safety of it is dubious so for now we always re-create
> checkpointed capability sets whether RESTART_KEEP_LSM is
> specified or not)
[...]
> oct 19: At checkpoint, we insert the void* security into the
> objhash. The first time that we do so, we next write out
> the string representation of the context to the checkpoint
> image, along with the value of the objref for the void*
> security, and insert that into the objhash. Then at
> restart, when we read a LSM context, we read the objref
> which the void* security had at checkpoint, and we then
> insert the string context with that objref as well.
I hoped to see similar comment inlined in the code.
[...]
> @@ -159,8 +175,12 @@ int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
> if (h->f_credref < 0)
> return h->f_credref;
>
> - ckpt_debug("file %s credref %d", file->f_dentry->d_name.name,
> - h->f_credref);
> + ret = checkpoint_file_security(ctx, file, &h->f_secref);
> + if (ret < 0)
> + return ret;
How about a ckpt_write_err() here, or in checkpoint_file_security(),
or in security_checkpoint_obj(), or all of the above.
> +
> + ckpt_debug("file %s credref %d secref %d\n",
> + file->f_dentry->d_name.name, h->f_credref, h->f_secref);
[...]
> @@ -433,6 +464,22 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = {
> .checkpoint = checkpoint_tty,
> .restore = restore_tty,
> },
> + /* LSM void *security on objhash - at checkpoint */
> + {
> + .obj_name = "SECURITY PTR",
> + .obj_type = CKPT_OBJ_SECURITY_PTR,
> + .ref_drop = obj_no_drop,
> + .ref_grab = obj_no_grab,
> + },
I really wish there was a comment explaining why it's ok to not
drop/grab the reference (because the security is always referenced
by an object that is itself grabbed, e.g. file, ipc etc ?).
> + /* LSM security strings - at restart */
> + {
> + .obj_name = "SECURITY STRING",
> + .obj_type = CKPT_OBJ_SECURITY,
> + .ref_grab = lsm_string_grab,
> + .ref_drop = lsm_string_drop,
> + .checkpoint = checkpoint_lsm_string,
> + .restore = restore_lsm_string_wrap,
> + },
> };
[...]
> @@ -376,6 +383,16 @@ struct ckpt_hdr_groupinfo {
> __u32 groups[0];
> } __attribute__((aligned(8)));
>
> +struct ckpt_hdr_lsm {
> + struct ckpt_hdr h;
> + __s32 ptrref;
> + __u8 sectype;
> + __u8 padding;
I don't think padding is necessary here...
> + /*
> + * This is followed by a string of size len+1,
> + * null-terminated
> + */
> +} __attribute__((aligned(8)));
[...]
> +/**
> + * security_checkpoint_obj - if first checkpoint of this void* security,
> + * then 1. ask the LSM for a string representing the context, 2. checkpoint
> + * that string
> + * @ctx: the checkpoint context
> + * @security: the void* security being checkpointed
> + * @sectype: indicates the type of object, because LSMs can (and do) store
> + * @secref: We return the objref here
> + * different types of data for different types of objects.
> + *
> + * Returns the objref of the checkpointed ckpt_lsm_string representing the
> + * context, or -error on error.
> + *
> + * This is only used at checkpoint of course.
> + */
> +int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
> + int sectype, int *secref)
This function returns 0 for success or a negative error. It should
return the @secref instead of passing it by reference (see your
description of the return value above !)
[...]
> + /* Ask the LSM to apply a void*security to the object
> + * based on the checkpointed context string */
> + switch (sectype) {
> + case CKPT_SECURITY_IPC:
> + ret = security_ipc_restore(v, l->string);
Nit: I know it's not strictly necessary, but adding an explicit
type conversion here and in the other three seems cleaner
> + break;
> + case CKPT_SECURITY_MSG_MSG:
> + ret = security_msg_msg_restore(v, l->string);
> + break;
> + case CKPT_SECURITY_FILE:
> + ret = security_file_restore(v, l->string);
> + break;
> + case CKPT_SECURITY_CRED:
> + ret = security_cred_restore(ctx->file, v, l->string);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> + if (ret)
> + ckpt_debug("sectype %d objref %d lsm's hook returned %d\n",
> + sectype, secref, ret);
> + if (ret == -EOPNOTSUPP)
> + ret = 0;
If you silently ignore EOPNOTSUPP, how can a user tell if a restarted
succeeded fully or by silently skipping the security part ?
Should the user approve this behavior (e.g. RESTART_SECURITY_LAX...) ?
Also, I suggest the s/EOPNOTSUPP/ENOSYS/ all over the patch.
> +
> + return ret;
> +}
> +#endif
Oren.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <4ADCAC5B.9080205-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
@ 2009-10-19 19:02 ` Serge E. Hallyn
[not found] ` <20091019190227.GA7201-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-20 1:16 ` Serge E. Hallyn
1 sibling, 1 reply; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-19 19:02 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
> > oct 19: At checkpoint, we insert the void* security into the
> > objhash. The first time that we do so, we next write out
> > the string representation of the context to the checkpoint
> > image, along with the value of the objref for the void*
> > security, and insert that into the objhash. Then at
> > restart, when we read a LSM context, we read the objref
> > which the void* security had at checkpoint, and we then
> > insert the string context with that objref as well.
>
> I hoped to see similar comment inlined in the code.
If we're happy with this approach, then I will add good comments above
security_checkpoint_obj and security_restore_obj, and above the objhash
entries.
> [...]
>
> > @@ -159,8 +175,12 @@ int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
> > if (h->f_credref < 0)
> > return h->f_credref;
> >
> > - ckpt_debug("file %s credref %d", file->f_dentry->d_name.name,
> > - h->f_credref);
> > + ret = checkpoint_file_security(ctx, file, &h->f_secref);
> > + if (ret < 0)
> > + return ret;
>
> How about a ckpt_write_err() here, or in checkpoint_file_security(),
> or in security_checkpoint_obj(), or all of the above.
Yeah, definately, at each of the checkpoint failures I add.
> > +
> > + ckpt_debug("file %s credref %d secref %d\n",
> > + file->f_dentry->d_name.name, h->f_credref, h->f_secref);
>
> [...]
>
> > @@ -433,6 +464,22 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = {
> > .checkpoint = checkpoint_tty,
> > .restore = restore_tty,
> > },
> > + /* LSM void *security on objhash - at checkpoint */
> > + {
> > + .obj_name = "SECURITY PTR",
> > + .obj_type = CKPT_OBJ_SECURITY_PTR,
> > + .ref_drop = obj_no_drop,
> > + .ref_grab = obj_no_grab,
> > + },
>
> I really wish there was a comment explaining why it's ok to not
> drop/grab the reference (because the security is always referenced
> by an object that is itself grabbed, e.g. file, ipc etc ?).
Yes.
> > + /* LSM security strings - at restart */
> > + {
> > + .obj_name = "SECURITY STRING",
> > + .obj_type = CKPT_OBJ_SECURITY,
> > + .ref_grab = lsm_string_grab,
> > + .ref_drop = lsm_string_drop,
> > + .checkpoint = checkpoint_lsm_string,
> > + .restore = restore_lsm_string_wrap,
> > + },
> > };
>
> [...]
>
> > @@ -376,6 +383,16 @@ struct ckpt_hdr_groupinfo {
> > __u32 groups[0];
> > } __attribute__((aligned(8)));
> >
> > +struct ckpt_hdr_lsm {
> > + struct ckpt_hdr h;
> > + __s32 ptrref;
> > + __u8 sectype;
> > + __u8 padding;
>
> I don't think padding is necessary here...
Ok, I'll take it out.
> > + /*
> > + * This is followed by a string of size len+1,
> > + * null-terminated
> > + */
> > +} __attribute__((aligned(8)));
>
> [...]
>
> > +/**
> > + * security_checkpoint_obj - if first checkpoint of this void* security,
> > + * then 1. ask the LSM for a string representing the context, 2. checkpoint
> > + * that string
> > + * @ctx: the checkpoint context
> > + * @security: the void* security being checkpointed
> > + * @sectype: indicates the type of object, because LSMs can (and do) store
> > + * @secref: We return the objref here
> > + * different types of data for different types of objects.
> > + *
> > + * Returns the objref of the checkpointed ckpt_lsm_string representing the
> > + * context, or -error on error.
> > + *
> > + * This is only used at checkpoint of course.
> > + */
> > +int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
> > + int sectype, int *secref)
>
> This function returns 0 for success or a negative error. It should
> return the @secref instead of passing it by reference (see your
> description of the return value above !)
>
> [...]
Yes the comment is out of date but the API is imo an improvement.
Note that SECURITY_CTX_NONE, -1, is a meaningful secref, and at
the sametime -EPERM, -1, is conceivably a valid error code (though
at the moment no lsm will return it).
So I think overloading the secref with error codes is wrong here.
> > + /* Ask the LSM to apply a void*security to the object
> > + * based on the checkpointed context string */
> > + switch (sectype) {
> > + case CKPT_SECURITY_IPC:
> > + ret = security_ipc_restore(v, l->string);
>
> Nit: I know it's not strictly necessary, but adding an explicit
> type conversion here and in the other three seems cleaner
Hmm, it'll make some lines wrap making it harder to read, so it
was a tossup. But ok I'll change it.
> > + break;
> > + case CKPT_SECURITY_MSG_MSG:
> > + ret = security_msg_msg_restore(v, l->string);
> > + break;
> > + case CKPT_SECURITY_FILE:
> > + ret = security_file_restore(v, l->string);
> > + break;
> > + case CKPT_SECURITY_CRED:
> > + ret = security_cred_restore(ctx->file, v, l->string);
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + }
> > + if (ret)
> > + ckpt_debug("sectype %d objref %d lsm's hook returned %d\n",
> > + sectype, secref, ret);
> > + if (ret == -EOPNOTSUPP)
> > + ret = 0;
>
> If you silently ignore EOPNOTSUPP, how can a user tell if a restarted
> succeeded fully or by silently skipping the security part ?
Actually I agree, EOPNOTSUPP (or ENOSYS) with RESTART_KEEP_LSM should
just return failure.
> Should the user approve this behavior (e.g. RESTART_SECURITY_LAX...) ?
>
> Also, I suggest the s/EOPNOTSUPP/ENOSYS/ all over the patch.
thanks,
-serge
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <4ADCAC5B.9080205-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
2009-10-19 19:02 ` Serge E. Hallyn
@ 2009-10-20 1:16 ` Serge E. Hallyn
1 sibling, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-20 1:16 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
Here is an updated version hopefully addressing your comments. I also
fixed up the fact that checkpoint was broken under a few configs!
The rest of the patches are unchanged.
Subject: [PATCH 2/4] cr: add generic LSM c/r support (v7)
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 generic support for c/r of LSM credentials. Support
for Smack and SELinux (and TOMOYO if appropriate) will be added later.
Capabilities is already supported through generic creds code.
This patch supports ipc_perm, msg_msg, cred (task) and file ->security
fields. Inodes, superblocks, netif, and xfrm currently are restored
not through sys_restart() but through container creation, and so the
security fields should be done then as well. Network should be added
when network c/r is added.
Briefly, all security fields must be exported by the LSM as a simple
null-terminated string. They are checkpointed through the
security_checkpoint_obj() helper, because we must pass it an extra
sectype field. Splitting SECURITY_OBJ_SEC into one type per object
type would not work because, in Smack, one void* security is used for
all object types. But we must pass the sectype field because in
SELinux a different type of structure is stashed in each object type.
The RESTART_KEEP_LSM flag indicates that the LSM should
attempt to reuse checkpointed security labels. It is always
invalid when the LSM at restart differs from that at checkpoint.
It is currently only usable for capabilities.
(For capabilities, restart without RESTART_KEEP_LSM is technically
not implemented. There actually might be a use case for that,
but the safety of it is dubious so for now we always re-create
checkpointed capability sets whether RESTART_KEEP_LSM is
specified or not)
Changelog:
sep 3: fix memory leak on LSM restore error path
Sep 3: provide 2 hooks, may_restart and checkpoint_header, to facilitate
an LSM tracking policy changes.
sep 10: merge RESTART_KEEP_LSM patch with basic LSM c/r
support patches.
sep 10: rename security_xyz_get_ctx() to security_xyz_checkpoint(),
in order to avoid confusing with the various other 'context'
helpers in the security_ namespace, relating to secids and
sysfs xattrs.
sep 10: pass context file to security_cred_restore. SELinux will
want the file's security context to authorize it as an
entrypoint for the new process context.
oct 01: roll up some generic c/r debug hunks from selinux patch.
oct 05: address set of Oren comments, including:
1. fix memleak in restore_msg_contents_one
2. use a separate container checkpoint image section
3. define SECURITY_CTX_NONE
4. allocate the right size to l in security_checkpoint_obj
5. fix ckpt_hdr_lsm alignment
oct 09: at checkpoint, key on the void*security in the objhash,
and don't cache the ckpt_stored_lsm at all. At restart,
do_restore_security (now moved to security/security.c)
creates and caches the ckpt_stored_lsm.
oct 19: At checkpoint, we insert the void* security into the
objhash. The first time that we do so, we next write out
the string representation of the context to the checkpoint
image, along with the value of the objref for the void*
security, and insert that into the objhash. Then at
restart, when we read a LSM context, we read the objref
which the void* security had at checkpoint, and we then
insert the string context with that objref as well.
oct 19: Address a bunch of Oren comments: add ckpt_write_err()s,
more commenting, use -ENOSYS not -EOPNOTSUPP. The biggest
change is always failing restart when RESTART_KEEP_LSM is
used but one of the security_XYZ_restore() returns -ENOSYS.
Signed-off-by: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
checkpoint/files.c | 33 ++++++-
checkpoint/objhash.c | 130 +++++++++++++++++++++++++
include/linux/checkpoint.h | 1 +
include/linux/checkpoint_hdr.h | 19 ++++
include/linux/checkpoint_types.h | 7 ++
include/linux/security.h | 171 +++++++++++++++++++++++++++++++++
ipc/checkpoint.c | 27 +++---
ipc/checkpoint_msg.c | 19 +++-
ipc/checkpoint_sem.c | 4 +-
ipc/checkpoint_shm.c | 4 +-
ipc/util.h | 6 +-
kernel/cred.c | 32 ++++++-
security/capability.c | 48 +++++++++
security/security.c | 195 ++++++++++++++++++++++++++++++++++++++
14 files changed, 669 insertions(+), 27 deletions(-)
diff --git a/checkpoint/files.c b/checkpoint/files.c
index f6de07e..4b143ee 100644
--- a/checkpoint/files.c
+++ b/checkpoint/files.c
@@ -145,9 +145,26 @@ static int scan_fds(struct files_struct *files, int **fdtable)
return n;
}
+#ifdef CONFIG_SECURITY
+int checkpoint_file_security(struct ckpt_ctx *ctx, struct file *file,
+ int *secref)
+{
+ return security_checkpoint_obj(ctx, file->f_security,
+ CKPT_SECURITY_FILE, secref);
+}
+#else
+int checkpoint_file_security(struct ckpt_ctx *ctx, struct file *file,
+ int *secref)
+{
+ *secref = SECURITY_CTX_NONE;
+ return 0;
+}
+#endif
+
int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
struct ckpt_hdr_file *h)
{
+ int ret;
struct cred *f_cred = (struct cred *) file->f_cred;
h->f_flags = file->f_flags;
@@ -159,8 +176,15 @@ int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
if (h->f_credref < 0)
return h->f_credref;
- ckpt_debug("file %s credref %d", file->f_dentry->d_name.name,
- h->f_credref);
+ ret = checkpoint_file_security(ctx, file, &h->f_secref);
+ if (ret < 0) {
+ ckpt_write_err(ctx, "TEO", "file->f_security", ret,
+ h->f_secref);
+ return ret;
+ }
+
+ ckpt_debug("file %s credref %d secref %d\n",
+ file->f_dentry->d_name.name, h->f_credref, h->f_secref);
/* FIX: need also file->f_owner, etc */
@@ -510,6 +534,11 @@ int restore_file_common(struct ckpt_ctx *ctx, struct file *file,
put_cred(file->f_cred);
file->f_cred = get_cred(cred);
+ ret = security_restore_obj(ctx, (void *) file, CKPT_SECURITY_FILE,
+ h->f_secref);
+ if (ret < 0)
+ return ret;
+
/* safe to set 1st arg (fd) to 0, as command is F_SETFL */
ret = vfs_fcntl(0, F_SETFL, h->f_flags & CKPT_SETFL_MASK, file);
if (ret < 0)
diff --git a/checkpoint/objhash.c b/checkpoint/objhash.c
index 730dd82..16ca37e 100644
--- a/checkpoint/objhash.c
+++ b/checkpoint/objhash.c
@@ -16,6 +16,7 @@
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/sched.h>
+#include <linux/kref.h>
#include <linux/ipc_namespace.h>
#include <linux/user_namespace.h>
#include <linux/checkpoint.h>
@@ -285,6 +286,36 @@ static int obj_tty_users(void *ptr)
return atomic_read(&((struct tty_struct *) ptr)->kref.refcount);
}
+static void lsm_string_free(struct kref *kref)
+{
+ struct ckpt_lsm_string *s = container_of(kref, struct ckpt_lsm_string,
+ kref);
+ kfree(s->string);
+ kfree(s);
+}
+
+static int lsm_string_grab(void *ptr)
+{
+ struct ckpt_lsm_string *s = ptr;
+ kref_get(&s->kref);
+ return 0;
+}
+
+static void lsm_string_drop(void *ptr, int lastref)
+{
+ struct ckpt_lsm_string *s = ptr;
+ kref_put(&s->kref, lsm_string_free);
+}
+
+/* security context strings */
+static int checkpoint_lsm_string(struct ckpt_ctx *ctx, void *ptr);
+static struct ckpt_lsm_string *restore_lsm_string(struct ckpt_ctx *ctx);
+static void *restore_lsm_string_wrap(struct ckpt_ctx *ctx)
+{
+ return (void *)restore_lsm_string(ctx);
+}
+
+
static struct ckpt_obj_ops ckpt_obj_ops[] = {
/* ignored object */
{
@@ -433,6 +464,33 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = {
.checkpoint = checkpoint_tty,
.restore = restore_tty,
},
+ /*
+ * LSM void *security on objhash - at checkpoint
+ * We don't take a ref because we won't be doing
+ * anything more with this void* - unless we happen
+ * to run into it again through some other objects's
+ * ->security (in which case that object has it pinned).
+ */
+ {
+ .obj_name = "SECURITY PTR",
+ .obj_type = CKPT_OBJ_SECURITY_PTR,
+ .ref_drop = obj_no_drop,
+ .ref_grab = obj_no_grab,
+ },
+ /*
+ * LSM security strings - at restart
+ * This is a struct which we malloc during restart and
+ * must be freed (by objhash cleanup) at the end of
+ * restart
+ */
+ {
+ .obj_name = "SECURITY STRING",
+ .obj_type = CKPT_OBJ_SECURITY,
+ .ref_grab = lsm_string_grab,
+ .ref_drop = lsm_string_drop,
+ .checkpoint = checkpoint_lsm_string,
+ .restore = restore_lsm_string_wrap,
+ },
};
@@ -1010,3 +1068,75 @@ void *ckpt_obj_fetch(struct ckpt_ctx *ctx, int objref, enum obj_type type)
ckpt_debug("%s ref %d\n", obj->ops->obj_name, obj->objref);
return (obj->ops->obj_type == type ? obj->ptr : ERR_PTR(-ENOMSG));
}
+
+/*
+ * checkpoint a security context string. This is done by
+ * security/security.c:security_checkpoint_obj() when it checkpoints
+ * a void*security whose context string has not yet been written out.
+ * The objref for the void*security (which is not itself written out
+ * to the checkpoint image) is stored alongside the context string,
+ * as is the type of object which contained the void* security, i.e.
+ * struct file, struct cred, etc.
+ */
+static int checkpoint_lsm_string(struct ckpt_ctx *ctx, void *ptr)
+{
+ struct ckpt_hdr_lsm *h;
+ struct ckpt_lsm_string *l = ptr;
+ int ret;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SECURITY);
+ if (!h)
+ return -ENOMEM;
+ h->sectype = l->sectype;
+ h->ptrref = l->ptrref;
+ ret = ckpt_write_obj(ctx, &h->h);
+ ckpt_hdr_put(ctx, h);
+
+ if (ret < 0)
+ return ret;
+ return ckpt_write_string(ctx, l->string, strlen(l->string)+1);
+}
+
+/*
+ * callback invoked when a security context string is found in a
+ * checkpoint image at restart. The context string is saved in the object
+ * hash. The objref under which the void* security was inserted in the
+ * objhash at checkpoint is also found here, and we re-insert this context
+ * string a second time under that objref. This is because objects which
+ * had this context will have the objref of the void*security, not of the
+ * context string.
+ */
+static struct ckpt_lsm_string *restore_lsm_string(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_lsm *h;
+ struct ckpt_lsm_string *l;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SECURITY);
+ if (IS_ERR(h)) {
+ ckpt_debug("ckpt_read_obj_type returned %ld\n", PTR_ERR(h));
+ return ERR_PTR(PTR_ERR(h));
+ }
+
+ l = kzalloc(sizeof(*l), GFP_KERNEL);
+ if (!l) {
+ l = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ l->string = ckpt_read_string(ctx, CKPT_LSM_STRING_MAX);
+ if (IS_ERR(l->string)) {
+ void *s = l->string;
+ ckpt_debug("ckpt_read_string returned %ld\n", PTR_ERR(s));
+ kfree(l);
+ l = s;
+ goto out;
+ }
+ kref_init(&l->kref);
+ l->sectype = h->sectype;
+ /* l is just a placeholder, don't grab a ref */
+ ckpt_obj_insert(ctx, l, h->ptrref, CKPT_OBJ_SECURITY);
+
+out:
+ ckpt_hdr_put(ctx, h);
+ return l;
+}
+
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index eedd5e7..1a5d66f 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -52,6 +52,7 @@
RESTART_KEEP_LSM | \
RESTART_GHOST)
#define CKPT_LSM_INFO_LEN 200
+#define CKPT_LSM_STRING_MAX 1024
extern int walk_task_subtree(struct task_struct *task,
int (*func)(struct task_struct *, void *),
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index 636e189..c4259b2 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -82,6 +82,8 @@ enum {
#define CKPT_HDR_OBJREF CKPT_HDR_OBJREF
CKPT_HDR_LSM_INFO,
#define CKPT_HDR_LSM_INFO CKPT_HDR_LSM_INFO
+ CKPT_HDR_SECURITY,
+#define CKPT_HDR_SECURITY CKPT_HDR_SECURITY
CKPT_HDR_TREE = 101,
#define CKPT_HDR_TREE CKPT_HDR_TREE
@@ -234,6 +236,10 @@ enum obj_type {
#define CKPT_OBJ_SOCK CKPT_OBJ_SOCK
CKPT_OBJ_TTY,
#define CKPT_OBJ_TTY CKPT_OBJ_TTY
+ CKPT_OBJ_SECURITY_PTR,
+#define CKPT_OBJ_SECURITY_PTR CKPT_OBJ_SECURITY_PTR
+ CKPT_OBJ_SECURITY,
+#define CKPT_OBJ_SECURITY CKPT_OBJ_SECURITY
CKPT_OBJ_MAX
#define CKPT_OBJ_MAX CKPT_OBJ_MAX
};
@@ -364,6 +370,7 @@ struct ckpt_hdr_cred {
__u32 gid, sgid, egid, fsgid;
__s32 user_ref;
__s32 groupinfo_ref;
+ __s32 sec_ref;
struct ckpt_capabilities cap_s;
} __attribute__((aligned(8)));
@@ -376,6 +383,15 @@ struct ckpt_hdr_groupinfo {
__u32 groups[0];
} __attribute__((aligned(8)));
+struct ckpt_hdr_lsm {
+ struct ckpt_hdr h;
+ __s32 ptrref;
+ __u8 sectype;
+ /*
+ * This is followed by a string of size len+1,
+ * null-terminated
+ */
+} __attribute__((aligned(8)));
/*
* todo - keyrings and LSM
* These may be better done with userspace help though
@@ -505,6 +521,7 @@ struct ckpt_hdr_file {
__s32 f_credref;
__u64 f_pos;
__u64 f_version;
+ __s32 f_secref;
} __attribute__((aligned(8)));
struct ckpt_hdr_file_generic {
@@ -765,6 +782,7 @@ struct ckpt_hdr_ipc_perms {
__u32 mode;
__u32 _padding;
__u64 seq;
+ __s32 sec_ref;
} __attribute__((aligned(8)));
struct ckpt_hdr_ipc_shm {
@@ -798,6 +816,7 @@ struct ckpt_hdr_ipc_msg_msg {
struct ckpt_hdr h;
__s32 m_type;
__u32 m_ts;
+ __s32 sec_ref;
} __attribute__((aligned(8)));
struct ckpt_hdr_ipc_sem {
diff --git a/include/linux/checkpoint_types.h b/include/linux/checkpoint_types.h
index b7d3053..96a7811 100644
--- a/include/linux/checkpoint_types.h
+++ b/include/linux/checkpoint_types.h
@@ -89,6 +89,13 @@ struct ckpt_ctx {
#endif
};
+/* stored on hashtable */
+struct ckpt_lsm_string {
+ struct kref kref;
+ int sectype; /* Containing object (file,cred,&c) */
+ int ptrref; /* the objref for the void* security */
+ char *string;
+};
#endif /* __KERNEL__ */
#endif /* _LINUX_CHECKPOINT_TYPES_H_ */
diff --git a/include/linux/security.h b/include/linux/security.h
index 99e4ebc..8d7df35 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -43,6 +43,9 @@
#define SECURITY_CAP_NOAUDIT 0
#define SECURITY_CAP_AUDIT 1
+/* checkpoint 'N/A' in a checkpoint image for a security context */
+#define SECURITY_CTX_NONE -1
+
struct ctl_table;
struct audit_krule;
@@ -581,6 +584,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* created.
* @file contains the file structure to secure.
* Return 0 if the hook is successful and permission is granted.
+ * @file_checkpoint:
+ * Return a string representing the security context on a file.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @file_restore:
+ * Set a security context on a file according to the checkpointed context.
+ * @file contains the file.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
* @file_free_security:
* Deallocate and free any security structures stored in file->f_security.
* @file contains the file structure being modified.
@@ -660,6 +672,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* manual page for definitions of the @clone_flags.
* @clone_flags contains the flags indicating what should be shared.
* Return 0 if permission is granted.
+ * @cred_checkpoint:
+ * Return a string representing the security context on the task cred.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @cred_restore:
+ * Set a security context on a task cred according to the checkpointed
+ * context.
+ * @file contains the checkpoint file
+ * @cred contains the cred.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
* @cred_free:
* @cred points to the credentials.
* Deallocate and clear the cred->security field in a set of credentials.
@@ -1108,6 +1131,19 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @ipcp contains the kernel IPC permission structure.
* @secid contains a pointer to the location where result will be saved.
* In case of failure, @secid will be set to zero.
+ * @ipc_checkpoint:
+ * Return a string representing the security context on the IPC
+ * permission structure.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @ipc_restore:
+ * Set a security context on a IPC permission structure according to
+ * the checkpointed context.
+ * @ipcp contains the IPC permission structure, which will have
+ * already been allocated and initialized when the IPC structure was
+ * created.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
*
* Security hooks for individual messages held in System V IPC message queues
* @msg_msg_alloc_security:
@@ -1116,6 +1152,16 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* created.
* @msg contains the message structure to be modified.
* Return 0 if operation was successful and permission is granted.
+ * @msg_msg_checkpoint:
+ * Return a string representing the security context on an msg_msg
+ * struct.
+ * @security contains the security field
+ * Returns a char* which the caller will free, or -error on error.
+ * @msg_msg_restore:
+ * Set msg_msg->security according to the checkpointed context.
+ * @msg contains the message structure to be modified.
+ * @ctx contains a string representation of the checkpointed context.
+ * Return 0 on success, -error on failure.
* @msg_msg_free_security:
* Deallocate the security structure for this message.
* @msg contains the message structure to be modified.
@@ -1492,6 +1538,8 @@ struct security_operations {
int (*file_permission) (struct file *file, int mask);
int (*file_alloc_security) (struct file *file);
+ char *(*file_checkpoint) (void *security);
+ int (*file_restore) (struct file *file, char *ctx);
void (*file_free_security) (struct file *file);
int (*file_ioctl) (struct file *file, unsigned int cmd,
unsigned long arg);
@@ -1512,6 +1560,8 @@ struct security_operations {
int (*dentry_open) (struct file *file, const struct cred *cred);
int (*task_create) (unsigned long clone_flags);
+ char *(*cred_checkpoint) (void *security);
+ int (*cred_restore) (struct file *file, struct cred *cred, char *ctx);
void (*cred_free) (struct cred *cred);
int (*cred_prepare)(struct cred *new, const struct cred *old,
gfp_t gfp);
@@ -1545,8 +1595,12 @@ struct security_operations {
int (*ipc_permission) (struct kern_ipc_perm *ipcp, short flag);
void (*ipc_getsecid) (struct kern_ipc_perm *ipcp, u32 *secid);
+ char *(*ipc_checkpoint) (void *security);
+ int (*ipc_restore) (struct kern_ipc_perm *ipcp, char *ctx);
int (*msg_msg_alloc_security) (struct msg_msg *msg);
+ char *(*msg_msg_checkpoint) (void *security);
+ int (*msg_msg_restore) (struct msg_msg *msg, char *ctx);
void (*msg_msg_free_security) (struct msg_msg *msg);
int (*msg_queue_alloc_security) (struct msg_queue *msq);
@@ -1755,6 +1809,8 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer
void security_inode_getsecid(const struct inode *inode, u32 *secid);
int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
+char *security_file_checkpoint(void *security);
+int security_file_restore(struct file *file, char *ctx);
void security_file_free(struct file *file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_mmap(struct file *file, unsigned long reqprot,
@@ -1770,6 +1826,8 @@ int security_file_send_sigiotask(struct task_struct *tsk,
int security_file_receive(struct file *file);
int security_dentry_open(struct file *file, const struct cred *cred);
int security_task_create(unsigned long clone_flags);
+char *security_cred_checkpoint(void *security);
+int security_cred_restore(struct file *file, struct cred *cred, char *ctx);
void security_cred_free(struct cred *cred);
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
void security_commit_creds(struct cred *new, const struct cred *old);
@@ -1800,7 +1858,11 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
void security_task_to_inode(struct task_struct *p, struct inode *inode);
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid);
+char *security_ipc_checkpoint(void *security);
+int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx);
int security_msg_msg_alloc(struct msg_msg *msg);
+char *security_msg_msg_checkpoint(void *security);
+int security_msg_msg_restore(struct msg_msg *msg, char *ctx);
void security_msg_msg_free(struct msg_msg *msg);
int security_msg_queue_alloc(struct msg_queue *msq);
void security_msg_queue_free(struct msg_queue *msq);
@@ -2248,6 +2310,19 @@ static inline int security_file_alloc(struct file *file)
return 0;
}
+static inline char *security_file_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_file_restore(struct file *file, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_file_free(struct file *file)
{ }
@@ -2312,6 +2387,20 @@ static inline int security_task_create(unsigned long clone_flags)
return 0;
}
+static inline char *security_cred_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_cred_restore(struct file *file, struct cred *cred,
+ char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_cred_free(struct cred *cred)
{ }
@@ -2454,11 +2543,37 @@ static inline void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
*secid = 0;
}
+static inline char *security_ipc_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline int security_msg_msg_alloc(struct msg_msg *msg)
{
return 0;
}
+static inline char *security_msg_msg_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_msg_msg_free(struct msg_msg *msg)
{ }
@@ -3055,5 +3170,61 @@ static inline void free_secdata(void *secdata)
{ }
#endif /* CONFIG_SECURITY */
+#ifdef CONFIG_CHECKPOINT
+#define CKPT_SECURITY_MSG_MSG 1
+#define CKPT_SECURITY_IPC 2
+#define CKPT_SECURITY_FILE 3
+#define CKPT_SECURITY_CRED 4
+#define CKPT_SECURITY_MAX 4
+
+#ifdef CONFIG_SECURITY
+/*
+ * @security_checkpoint_obj:
+ * Checkpoint a LSM security context. The context is written out
+ * as a string. An integer objref uniquely representing the
+ * security context in this checkpoint image will be placed in @secref.
+ * If the security context has already been written out, then the
+ * objref of that already written-out context will be used.
+ * @ctx: the checkpoint context.
+ * @security: the void*security being checkpointed.
+ * @sectype: represents the type of object which contained the
+ * void *security.
+ * @secref: pointer to integer in which to return the objref.
+ * Return 0 on success, or -error on error.
+ */
+int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
+ int sectype, int *secref);
+/*
+ * @security_restore_obj:
+ * Re-create a checkpointed LSM security context. The LSM will decide
+ * based upon the string representation which actual security context
+ * to assign.
+ * @ctx: the checkpoint context.
+ * @obj: The object containing the security context to be restored (cast
+ * to a void *).
+ * @sectype: represents the type of object which contained the
+ * void *security.
+ * @secref: an integer objref for the string representation of the
+ * security context to be restored.
+ * Return 0 on success, or -error on error.
+ */
+int security_restore_obj(struct ckpt_ctx *ctx, void *obj,
+ int sectype, int secref);
+#else
+static inline int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
+ int sectype, int *secref)
+{
+ *secref = SECURITY_CTX_NONE;
+ return 0;
+}
+static inline int security_restore_obj(struct ckpt_ctx *ctx, void *obj,
+ int sectype, int secref)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITY */
+
+#endif /* CONFIG_CHECKPOINT */
+
#endif /* ! __LINUX_SECURITY_H */
diff --git a/ipc/checkpoint.c b/ipc/checkpoint.c
index 8e6e9ba..97d1cdb 100644
--- a/ipc/checkpoint.c
+++ b/ipc/checkpoint.c
@@ -31,9 +31,12 @@ static char *ipc_ind_to_str[] = { "sem", "msg", "shm" };
* Checkpoint
*/
-int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+int checkpoint_fill_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm)
{
+ int ret;
+
if (ipcperms(perm, S_IROTH))
return -EACCES;
@@ -45,8 +48,12 @@ int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
h->cgid = perm->cgid;
h->mode = perm->mode & S_IRWXUGO;
h->seq = perm->seq;
-
- return 0;
+ ret = security_checkpoint_obj(ctx, perm->security, CKPT_SECURITY_IPC,
+ &h->sec_ref);
+ if (ret < 0)
+ ckpt_write_err(ctx, "TEO", "ipc_perm->security", ret,
+ h->sec_ref);
+ return ret;
}
static int checkpoint_ipc_any(struct ckpt_ctx *ctx,
@@ -176,7 +183,8 @@ static int validate_created_perms(struct ckpt_hdr_ipc_perms *h)
return 1;
}
-int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+int restore_load_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm)
{
if (h->id < 0)
@@ -205,16 +213,9 @@ int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
perm->cgid = h->cgid;
perm->mode = h->mode;
perm->seq = h->seq;
- /*
- * Todo: restore perm->security.
- * At the moment it gets set by security_x_alloc() called through
- * ipcget()->ipcget_public()->ops-.getnew (->nequeue for instance)
- * We will want to ask the LSM to consider resetting the
- * checkpointed ->security, based on current_security(),
- * the checkpointed ->security, and the checkpoint file context.
- */
- return 0;
+ return security_restore_obj(ctx, (void *)perm, CKPT_SECURITY_IPC,
+ h->sec_ref);
}
static int restore_ipc_any(struct ckpt_ctx *ctx, struct ipc_namespace *ipc_ns,
diff --git a/ipc/checkpoint_msg.c b/ipc/checkpoint_msg.c
index b933c19..958b5a8 100644
--- a/ipc/checkpoint_msg.c
+++ b/ipc/checkpoint_msg.c
@@ -37,7 +37,7 @@ static int fill_ipc_msg_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&msq->q_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &msq->q_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &msq->q_perm);
if (ret < 0)
goto unlock;
@@ -63,14 +63,22 @@ static int checkpoint_msg_contents(struct ckpt_ctx *ctx, struct msg_msg *msg)
struct ckpt_hdr_ipc_msg_msg *h;
struct msg_msgseg *seg;
int total, len;
- int ret;
+ int secref, ret;
+ ret = security_checkpoint_obj(ctx, msg->security,
+ CKPT_SECURITY_MSG_MSG,
+ &secref);
+ if (ret < 0) {
+ ckpt_write_err(ctx, "TEO", "msg_msg->security", ret, secref);
+ return ret;
+ }
h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_IPC_MSG_MSG);
if (!h)
return -ENOMEM;
h->m_type = msg->m_type;
h->m_ts = msg->m_ts;
+ h->sec_ref = secref;
ret = ckpt_write_obj(ctx, &h->h);
ckpt_hdr_put(ctx, h);
@@ -177,7 +185,7 @@ static int load_ipc_msg_hdr(struct ckpt_ctx *ctx,
{
int ret = 0;
- ret = restore_load_ipc_perms(&h->perms, &msq->q_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &msq->q_perm);
if (ret < 0)
return ret;
@@ -224,6 +232,11 @@ static struct msg_msg *restore_msg_contents_one(struct ckpt_ctx *ctx, int *clen)
msg->next = NULL;
pseg = &msg->next;
+ ret = security_restore_obj(ctx, (void *) msg, CKPT_SECURITY_MSG_MSG,
+ h->sec_ref);
+ if (ret < 0)
+ goto out;
+
ret = _ckpt_read_buffer(ctx, (msg + 1), len);
if (ret < 0)
goto out;
diff --git a/ipc/checkpoint_sem.c b/ipc/checkpoint_sem.c
index 76eb2b9..53a19ed 100644
--- a/ipc/checkpoint_sem.c
+++ b/ipc/checkpoint_sem.c
@@ -37,7 +37,7 @@ static int fill_ipc_sem_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&sem->sem_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &sem->sem_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &sem->sem_perm);
if (ret < 0)
goto unlock;
@@ -113,7 +113,7 @@ static int load_ipc_sem_hdr(struct ckpt_ctx *ctx,
{
int ret = 0;
- ret = restore_load_ipc_perms(&h->perms, &sem->sem_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &sem->sem_perm);
if (ret < 0)
return ret;
diff --git a/ipc/checkpoint_shm.c b/ipc/checkpoint_shm.c
index 826e430..01091d9 100644
--- a/ipc/checkpoint_shm.c
+++ b/ipc/checkpoint_shm.c
@@ -41,7 +41,7 @@ static int fill_ipc_shm_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&shp->shm_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &shp->shm_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &shp->shm_perm);
if (ret < 0)
goto unlock;
@@ -165,7 +165,7 @@ static int load_ipc_shm_hdr(struct ckpt_ctx *ctx,
{
int ret;
- ret = restore_load_ipc_perms(&h->perms, &shp->shm_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &shp->shm_perm);
if (ret < 0)
return ret;
diff --git a/ipc/util.h b/ipc/util.h
index ba080de..ce34de0 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -199,9 +199,11 @@ void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp);
#ifdef CONFIG_CHECKPOINT
-extern int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+extern int checkpoint_fill_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm);
-extern int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+extern int restore_load_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm);
extern int ckpt_collect_ipc_shm(int id, void *p, void *data);
diff --git a/kernel/cred.c b/kernel/cred.c
index 62d28a4..733d10f 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -706,10 +706,26 @@ int cred_setfsgid(struct cred *new, gid_t gid, gid_t *old_fsgid)
}
#ifdef CONFIG_CHECKPOINT
+#ifdef CONFIG_SECURITY
+int checkpoint_cred_security(struct ckpt_ctx *ctx, struct cred *cred,
+ int *secref)
+{
+ return security_checkpoint_obj(ctx, cred->security,
+ CKPT_SECURITY_CRED, secref);
+}
+#else
+int checkpoint_cred_security(struct ckpt_ctx *ctx, struct cred *cred,
+ int *secref)
+{
+ *secref = SECURITY_CTX_NONE;
+ return 0;
+}
+#endif
+
static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
{
int ret;
- int groupinfo_ref, user_ref;
+ int groupinfo_ref, user_ref, secref = SECURITY_CTX_NONE;
struct ckpt_hdr_cred *h;
groupinfo_ref = checkpoint_obj(ctx, cred->group_info,
@@ -719,13 +735,18 @@ static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
user_ref = checkpoint_obj(ctx, cred->user, CKPT_OBJ_USER);
if (user_ref < 0)
return user_ref;
+ ret = checkpoint_cred_security(ctx, cred, &secref);
+ if (ret < 0) {
+ ckpt_write_err(ctx, "TEO", "cred->security", ret, secref);
+ return ret;
+ }
h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_CRED);
if (!h)
return -ENOMEM;
- ckpt_debug("cred uid %d fsuid %d gid %d\n", cred->uid, cred->fsuid,
- cred->gid);
+ ckpt_debug("cred uid %d fsuid %d gid %d secref %d\n", cred->uid,
+ cred->fsuid, cred->gid, secref);
h->uid = cred->uid;
h->suid = cred->suid;
h->euid = cred->euid;
@@ -735,6 +756,7 @@ static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
h->sgid = cred->sgid;
h->egid = cred->egid;
h->fsgid = cred->fsgid;
+ h->sec_ref = secref;
checkpoint_capabilities(&h->cap_s, cred);
@@ -808,6 +830,10 @@ static struct cred *do_restore_cred(struct ckpt_ctx *ctx)
ret = cred_setfsgid(cred, h->fsgid, &oldgid);
if (oldgid != h->fsgid && ret < 0)
goto err_putcred;
+ ret = security_restore_obj(ctx, (void *) cred, CKPT_SECURITY_CRED,
+ h->sec_ref);
+ if (ret)
+ goto err_putcred;
ret = restore_capabilities(&h->cap_s, cred);
if (ret)
goto err_putcred;
diff --git a/security/capability.c b/security/capability.c
index 23026e2..de1e3a1 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -315,6 +315,16 @@ static int cap_file_permission(struct file *file, int mask)
return 0;
}
+static inline char *cap_file_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_file_restore(struct file *file, char *ctx)
+{
+ return -ENOSYS;
+}
+
static int cap_file_alloc_security(struct file *file)
{
return 0;
@@ -373,6 +383,16 @@ static int cap_task_create(unsigned long clone_flags)
return 0;
}
+static char *cap_cred_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_cred_restore(struct file *file, struct cred *cred, char *ctx)
+{
+ return -ENOSYS;
+}
+
static void cap_cred_free(struct cred *cred)
{
}
@@ -476,11 +496,31 @@ static void cap_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
*secid = 0;
}
+static char *cap_ipc_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ return -ENOSYS;
+}
+
static int cap_msg_msg_alloc_security(struct msg_msg *msg)
{
return 0;
}
+static inline char *cap_msg_msg_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ return -ENOSYS;
+}
+
static void cap_msg_msg_free_security(struct msg_msg *msg)
{
}
@@ -948,6 +988,8 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, path_truncate);
#endif
set_to_cap_if_null(ops, file_permission);
+ set_to_cap_if_null(ops, file_checkpoint);
+ set_to_cap_if_null(ops, file_restore);
set_to_cap_if_null(ops, file_alloc_security);
set_to_cap_if_null(ops, file_free_security);
set_to_cap_if_null(ops, file_ioctl);
@@ -960,6 +1002,8 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, file_receive);
set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create);
+ set_to_cap_if_null(ops, cred_checkpoint);
+ set_to_cap_if_null(ops, cred_restore);
set_to_cap_if_null(ops, cred_free);
set_to_cap_if_null(ops, cred_prepare);
set_to_cap_if_null(ops, cred_commit);
@@ -986,7 +1030,11 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, task_to_inode);
set_to_cap_if_null(ops, ipc_permission);
set_to_cap_if_null(ops, ipc_getsecid);
+ set_to_cap_if_null(ops, ipc_checkpoint);
+ set_to_cap_if_null(ops, ipc_restore);
set_to_cap_if_null(ops, msg_msg_alloc_security);
+ set_to_cap_if_null(ops, msg_msg_checkpoint);
+ set_to_cap_if_null(ops, msg_msg_restore);
set_to_cap_if_null(ops, msg_msg_free_security);
set_to_cap_if_null(ops, msg_queue_alloc_security);
set_to_cap_if_null(ops, msg_queue_free_security);
diff --git a/security/security.c b/security/security.c
index e4fa91a..b7e15a3 100644
--- a/security/security.c
+++ b/security/security.c
@@ -633,6 +633,16 @@ int security_file_alloc(struct file *file)
return security_ops->file_alloc_security(file);
}
+char *security_file_checkpoint(void *security)
+{
+ return security_ops->file_checkpoint(security);
+}
+
+int security_file_restore(struct file *file, char *ctx)
+{
+ return security_ops->file_restore(file, ctx);
+}
+
void security_file_free(struct file *file)
{
security_ops->file_free_security(file);
@@ -692,6 +702,16 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(clone_flags);
}
+char *security_cred_checkpoint(void *security)
+{
+ return security_ops->cred_checkpoint(security);
+}
+
+int security_cred_restore(struct file *file, struct cred *cred, char *ctx)
+{
+ return security_ops->cred_restore(file, cred, ctx);
+}
+
void security_cred_free(struct cred *cred)
{
security_ops->cred_free(cred);
@@ -827,11 +847,31 @@ void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
security_ops->ipc_getsecid(ipcp, secid);
}
+char *security_ipc_checkpoint(void *security)
+{
+ return security_ops->ipc_checkpoint(security);
+}
+
+int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ return security_ops->ipc_restore(ipcp, ctx);
+}
+
int security_msg_msg_alloc(struct msg_msg *msg)
{
return security_ops->msg_msg_alloc_security(msg);
}
+char *security_msg_msg_checkpoint(void *security)
+{
+ return security_ops->msg_msg_checkpoint(security);
+}
+
+int security_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ return security_ops->msg_msg_restore(msg, ctx);
+}
+
void security_msg_msg_free(struct msg_msg *msg)
{
security_ops->msg_msg_free_security(msg);
@@ -1270,3 +1310,158 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
}
#endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_CHECKPOINT
+
+/**
+ * security_checkpoint_obj - called during application checkpoint to
+ * record the security context of objects.
+ *
+ * First we add the void*security address to the objhash as a type
+ * CKPT_OBJ_SECURITY_PTR. This records the fact that we've seen this
+ * context. If we've seen the context before, then we simply place the
+ * recorded objref in *secref and return success.
+
+ * If this is the first time we've seen this context for this checkpoint
+ * image, then we
+ * 1. ask the LSM for a string representation of the context
+ * 2. create a struct ckpt_lsm_string pointing to the string and to the
+ * objref which we got for the void*security in the objhash.
+ * 3. write that out to the checkpoint image as a CKPT_OBJ_SECURITY. it
+ * will be freed when the objhash is cleared.
+ *
+ * Returns 0 on success, in which case a valid objref is placed in *secref,
+ * or returns -error on error.
+ *
+ * This is only used at checkpoint of course.
+ */
+int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
+ int sectype, int *secref)
+{
+ int new, ret = -ENOMEM;
+ char *str;
+ struct ckpt_lsm_string *l;
+
+ if (!security) {
+ *secref = SECURITY_CTX_NONE;
+ return 0;
+ }
+
+ *secref = ckpt_obj_lookup_add(ctx, security, CKPT_OBJ_SECURITY_PTR,
+ &new);
+ if (!new)
+ return 0;
+
+ /*
+ * Ask the LSM for a string representation
+ */
+ switch (sectype) {
+ case CKPT_SECURITY_MSG_MSG:
+ str = security_msg_msg_checkpoint(security);
+ break;
+ case CKPT_SECURITY_IPC:
+ str = security_ipc_checkpoint(security);
+ break;
+ case CKPT_SECURITY_FILE:
+ str = security_file_checkpoint(security);
+ break;
+ case CKPT_SECURITY_CRED:
+ str = security_cred_checkpoint(security);
+ break;
+ default:
+ str = ERR_PTR(-EINVAL);
+ break;
+ }
+
+ if (IS_ERR(str)) {
+ if (PTR_ERR(str) == -ENOSYS) {
+ *secref = SECURITY_CTX_NONE;
+ return 0;
+ }
+ return PTR_ERR(str);
+ }
+
+ l = kzalloc(sizeof(*l), GFP_KERNEL);
+ if (!l) {
+ kfree(str);
+ return -ENOMEM;
+ }
+ l->ptrref = *secref;
+ l->sectype = sectype;
+ l->string = str;
+ kref_init(&l->kref);
+ ret = checkpoint_obj(ctx, l, CKPT_OBJ_SECURITY);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Choose a security context for an object being restored during
+ * application restart. @v is an object (file, cred, etc) containing
+ * a security context and being re-created. It has been type-cast
+ * to a void*. @sectype tells us what sort of object v is. @secref
+ * is the objhash id representing the security context.
+ *
+ * If sys_restart() was called without the RESTART_KEEP_LSM flag,
+ * then default security contexts will be assigned to the re-created
+ * object (in fact, they already have by this point). Otherwise, the
+ * LSM is expected to use the string context representation to assign
+ * the same security context to this object (if allowed).
+ *
+ * At checkpoint time, @secref was the objref for the void*security
+ * (which was not written to disk). The
+ * checkpoint/objhash.c:restore_lsm_string() function should, before we
+ * get here, have read the context string in the checkpoint image, and
+ * inserted a second copy of the struct ckpt_lsm_string on the objhash,
+ * with this objref.
+ *
+ * Returns 0 on success, -error on error.
+ */
+int security_restore_obj(struct ckpt_ctx *ctx, void *v, int sectype,
+ int secref)
+{
+ struct ckpt_lsm_string *l;
+ int ret;
+
+ /* return if caller didn't want to restore checkpointed labels */
+ if (!(ctx->uflags & RESTART_KEEP_LSM))
+ return 0;
+
+ l = ckpt_obj_fetch(ctx, secref, CKPT_OBJ_SECURITY);
+ if (IS_ERR(l))
+ return PTR_ERR(l);
+
+ /* return if checkpointed label was "Not Applicable" */
+ if (secref == SECURITY_CTX_NONE)
+ return 0;
+
+ /* Ask the LSM to apply a void*security to the object
+ * based on the checkpointed context string */
+ switch (sectype) {
+ case CKPT_SECURITY_IPC:
+ ret = security_ipc_restore((struct kern_ipc_perm *) v,
+ l->string);
+ break;
+ case CKPT_SECURITY_MSG_MSG:
+ ret = security_msg_msg_restore((struct msg_msg *) v,
+ l->string);
+ break;
+ case CKPT_SECURITY_FILE:
+ ret = security_file_restore((struct file *) v, l->string);
+ break;
+ case CKPT_SECURITY_CRED:
+ ret = security_cred_restore(ctx->file, (struct cred *) v,
+ l->string);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ ckpt_debug("sectype %d objref %d lsm's hook returned %d\n",
+ sectype, secref, ret);
+
+ return ret;
+}
+#endif
--
1.6.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <20091019190227.GA7201-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2009-10-21 1:03 ` Oren Laadan
[not found] ` <4ADE5DEA.2000606-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
0 siblings, 1 reply; 19+ messages in thread
From: Oren Laadan @ 2009-10-21 1:03 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: Linux Containers
Serge E. Hallyn wrote:
> Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
>>> oct 19: At checkpoint, we insert the void* security into the
>>> objhash. The first time that we do so, we next write out
>>> the string representation of the context to the checkpoint
>>> image, along with the value of the objref for the void*
>>> security, and insert that into the objhash. Then at
>>> restart, when we read a LSM context, we read the objref
>>> which the void* security had at checkpoint, and we then
>>> insert the string context with that objref as well.
>> I hoped to see similar comment inlined in the code.
>
> If we're happy with this approach, then I will add good comments above
> security_checkpoint_obj and security_restore_obj, and above the objhash
> entries.
[...]
>>> +/**
>>> + * security_checkpoint_obj - if first checkpoint of this void* security,
>>> + * then 1. ask the LSM for a string representing the context, 2. checkpoint
>>> + * that string
>>> + * @ctx: the checkpoint context
>>> + * @security: the void* security being checkpointed
>>> + * @sectype: indicates the type of object, because LSMs can (and do) store
>>> + * @secref: We return the objref here
>>> + * different types of data for different types of objects.
>>> + *
>>> + * Returns the objref of the checkpointed ckpt_lsm_string representing the
>>> + * context, or -error on error.
>>> + *
>>> + * This is only used at checkpoint of course.
>>> + */
>>> +int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
>>> + int sectype, int *secref)
>> This function returns 0 for success or a negative error. It should
>> return the @secref instead of passing it by reference (see your
>> description of the return value above !)
>>
>> [...]
>
> Yes the comment is out of date but the API is imo an improvement.
> Note that SECURITY_CTX_NONE, -1, is a meaningful secref, and at
> the sametime -EPERM, -1, is conceivably a valid error code (though
> at the moment no lsm will return it).
>
> So I think overloading the secref with error codes is wrong here.
How about #define SECURITY_CTX_NONE 0 ?
it isn't a valid objref anyway.
[...]
Oren.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <4ADE5DEA.2000606-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
@ 2009-10-21 1:18 ` Serge E. Hallyn
[not found] ` <20091021011846.GA26728-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-21 1:18 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
>
>
> Serge E. Hallyn wrote:
> > Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
> >>> oct 19: At checkpoint, we insert the void* security into the
> >>> objhash. The first time that we do so, we next write out
> >>> the string representation of the context to the checkpoint
> >>> image, along with the value of the objref for the void*
> >>> security, and insert that into the objhash. Then at
> >>> restart, when we read a LSM context, we read the objref
> >>> which the void* security had at checkpoint, and we then
> >>> insert the string context with that objref as well.
> >> I hoped to see similar comment inlined in the code.
> >
> > If we're happy with this approach, then I will add good comments above
> > security_checkpoint_obj and security_restore_obj, and above the objhash
> > entries.
>
> [...]
>
> >>> +/**
> >>> + * security_checkpoint_obj - if first checkpoint of this void* security,
> >>> + * then 1. ask the LSM for a string representing the context, 2. checkpoint
> >>> + * that string
> >>> + * @ctx: the checkpoint context
> >>> + * @security: the void* security being checkpointed
> >>> + * @sectype: indicates the type of object, because LSMs can (and do) store
> >>> + * @secref: We return the objref here
> >>> + * different types of data for different types of objects.
> >>> + *
> >>> + * Returns the objref of the checkpointed ckpt_lsm_string representing the
> >>> + * context, or -error on error.
> >>> + *
> >>> + * This is only used at checkpoint of course.
> >>> + */
> >>> +int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
> >>> + int sectype, int *secref)
> >> This function returns 0 for success or a negative error. It should
> >> return the @secref instead of passing it by reference (see your
> >> description of the return value above !)
> >>
> >> [...]
> >
> > Yes the comment is out of date but the API is imo an improvement.
> > Note that SECURITY_CTX_NONE, -1, is a meaningful secref, and at
> > the sametime -EPERM, -1, is conceivably a valid error code (though
> > at the moment no lsm will return it).
> >
> > So I think overloading the secref with error codes is wrong here.
>
> How about #define SECURITY_CTX_NONE 0 ?
> it isn't a valid objref anyway.
Ok, I thought we'd decided using 0 wasn't right for some reason, but
I'll do that then.
-serge
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <20091021011846.GA26728-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2009-10-21 1:21 ` Oren Laadan
[not found] ` <4ADE621E.2080603-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
0 siblings, 1 reply; 19+ messages in thread
From: Oren Laadan @ 2009-10-21 1:21 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: Linux Containers
Serge E. Hallyn wrote:
> Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
>>
>> Serge E. Hallyn wrote:
>>> Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
>>>>> oct 19: At checkpoint, we insert the void* security into the
>>>>> objhash. The first time that we do so, we next write out
>>>>> the string representation of the context to the checkpoint
>>>>> image, along with the value of the objref for the void*
>>>>> security, and insert that into the objhash. Then at
>>>>> restart, when we read a LSM context, we read the objref
>>>>> which the void* security had at checkpoint, and we then
>>>>> insert the string context with that objref as well.
>>>> I hoped to see similar comment inlined in the code.
>>> If we're happy with this approach, then I will add good comments above
>>> security_checkpoint_obj and security_restore_obj, and above the objhash
>>> entries.
>> [...]
>>
>>>>> +/**
>>>>> + * security_checkpoint_obj - if first checkpoint of this void* security,
>>>>> + * then 1. ask the LSM for a string representing the context, 2. checkpoint
>>>>> + * that string
>>>>> + * @ctx: the checkpoint context
>>>>> + * @security: the void* security being checkpointed
>>>>> + * @sectype: indicates the type of object, because LSMs can (and do) store
>>>>> + * @secref: We return the objref here
>>>>> + * different types of data for different types of objects.
>>>>> + *
>>>>> + * Returns the objref of the checkpointed ckpt_lsm_string representing the
>>>>> + * context, or -error on error.
>>>>> + *
>>>>> + * This is only used at checkpoint of course.
>>>>> + */
>>>>> +int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
>>>>> + int sectype, int *secref)
>>>> This function returns 0 for success or a negative error. It should
>>>> return the @secref instead of passing it by reference (see your
>>>> description of the return value above !)
>>>>
>>>> [...]
>>> Yes the comment is out of date but the API is imo an improvement.
>>> Note that SECURITY_CTX_NONE, -1, is a meaningful secref, and at
>>> the sametime -EPERM, -1, is conceivably a valid error code (though
>>> at the moment no lsm will return it).
>>>
>>> So I think overloading the secref with error codes is wrong here.
>> How about #define SECURITY_CTX_NONE 0 ?
>> it isn't a valid objref anyway.
>
> Ok, I thought we'd decided using 0 wasn't right for some reason, but
> I'll do that then.
An objref == 0 may have a special meaning when handled by restore_obj().
But here it's entirely private to security_{checkpoint/restart}_obj().
Oren.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/4] cr: add generic LSM c/r support (v6)
[not found] ` <4ADE621E.2080603-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
@ 2009-10-21 5:01 ` Serge E. Hallyn
0 siblings, 0 replies; 19+ messages in thread
From: Serge E. Hallyn @ 2009-10-21 5:01 UTC (permalink / raw)
To: Oren Laadan; +Cc: Linux Containers
Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
> >>> So I think overloading the secref with error codes is wrong here.
> >> How about #define SECURITY_CTX_NONE 0 ?
> >> it isn't a valid objref anyway.
> >
> > Ok, I thought we'd decided using 0 wasn't right for some reason, but
> > I'll do that then.
>
> An objref == 0 may have a special meaning when handled by restore_obj().
>
> But here it's entirely private to security_{checkpoint/restart}_obj().
>
> Oren.
So here is a new version doing that:
From: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Date: Fri, 16 Oct 2009 14:05:33 -0400
Subject: [PATCH 2/4] cr: add generic LSM c/r support (v7)
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 generic support for c/r of LSM credentials. Support
for Smack and SELinux (and TOMOYO if appropriate) will be added later.
Capabilities is already supported through generic creds code.
This patch supports ipc_perm, msg_msg, cred (task) and file ->security
fields. Inodes, superblocks, netif, and xfrm currently are restored
not through sys_restart() but through container creation, and so the
security fields should be done then as well. Network should be added
when network c/r is added.
Briefly, all security fields must be exported by the LSM as a simple
null-terminated string. They are checkpointed through the
security_checkpoint_obj() helper, because we must pass it an extra
sectype field. Splitting SECURITY_OBJ_SEC into one type per object
type would not work because, in Smack, one void* security is used for
all object types. But we must pass the sectype field because in
SELinux a different type of structure is stashed in each object type.
The RESTART_KEEP_LSM flag indicates that the LSM should
attempt to reuse checkpointed security labels. It is always
invalid when the LSM at restart differs from that at checkpoint.
It is currently only usable for capabilities.
(For capabilities, restart without RESTART_KEEP_LSM is technically
not implemented. There actually might be a use case for that,
but the safety of it is dubious so for now we always re-create
checkpointed capability sets whether RESTART_KEEP_LSM is
specified or not)
Changelog:
sep 3: fix memory leak on LSM restore error path
Sep 3: provide 2 hooks, may_restart and checkpoint_header, to facilitate
an LSM tracking policy changes.
sep 10: merge RESTART_KEEP_LSM patch with basic LSM c/r
support patches.
sep 10: rename security_xyz_get_ctx() to security_xyz_checkpoint(),
in order to avoid confusing with the various other 'context'
helpers in the security_ namespace, relating to secids and
sysfs xattrs.
sep 10: pass context file to security_cred_restore. SELinux will
want the file's security context to authorize it as an
entrypoint for the new process context.
oct 01: roll up some generic c/r debug hunks from selinux patch.
oct 05: address set of Oren comments, including:
1. fix memleak in restore_msg_contents_one
2. use a separate container checkpoint image section
3. define SECURITY_CTX_NONE
4. allocate the right size to l in security_checkpoint_obj
5. fix ckpt_hdr_lsm alignment
oct 09: at checkpoint, key on the void*security in the objhash,
and don't cache the ckpt_stored_lsm at all. At restart,
do_restore_security (now moved to security/security.c)
creates and caches the ckpt_stored_lsm.
oct 19: At checkpoint, we insert the void* security into the
objhash. The first time that we do so, we next write out
the string representation of the context to the checkpoint
image, along with the value of the objref for the void*
security, and insert that into the objhash. Then at
restart, when we read a LSM context, we read the objref
which the void* security had at checkpoint, and we then
insert the string context with that objref as well.
oct 19: Address a bunch of Oren comments: add ckpt_write_err()s,
more commenting, use -ENOSYS not -EOPNOTSUPP. The biggest
change is always failing restart when RESTART_KEEP_LSM is
used but one of the security_XYZ_restore() returns -ENOSYS.
oct 20: SECURITY_CTX_NONE becomes 0 (from -1) and security_checkpoint_obj
returns an objref or SECURITY_CTX_NONE on success.
Signed-off-by: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
checkpoint/files.c | 28 +++++-
checkpoint/objhash.c | 130 ++++++++++++++++++++++++++
include/linux/checkpoint.h | 4 +
include/linux/checkpoint_hdr.h | 19 ++++
include/linux/checkpoint_types.h | 7 ++
include/linux/security.h | 168 +++++++++++++++++++++++++++++++++
ipc/checkpoint.c | 24 +++---
ipc/checkpoint_msg.c | 18 +++-
ipc/checkpoint_sem.c | 4 +-
ipc/checkpoint_shm.c | 4 +-
ipc/util.h | 6 +-
kernel/cred.c | 28 +++++-
security/capability.c | 48 ++++++++++
security/security.c | 191 ++++++++++++++++++++++++++++++++++++++
14 files changed, 653 insertions(+), 26 deletions(-)
diff --git a/checkpoint/files.c b/checkpoint/files.c
index f6de07e..0d24ebc 100644
--- a/checkpoint/files.c
+++ b/checkpoint/files.c
@@ -145,6 +145,19 @@ static int scan_fds(struct files_struct *files, int **fdtable)
return n;
}
+#ifdef CONFIG_SECURITY
+int checkpoint_file_security(struct ckpt_ctx *ctx, struct file *file)
+{
+ return security_checkpoint_obj(ctx, file->f_security,
+ CKPT_SECURITY_FILE);
+}
+#else
+int checkpoint_file_security(struct ckpt_ctx *ctx, struct file *file)
+{
+ return SECURITY_CTX_NONE;
+}
+#endif
+
int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
struct ckpt_hdr_file *h)
{
@@ -159,8 +172,14 @@ int checkpoint_file_common(struct ckpt_ctx *ctx, struct file *file,
if (h->f_credref < 0)
return h->f_credref;
- ckpt_debug("file %s credref %d", file->f_dentry->d_name.name,
- h->f_credref);
+ h->f_secref = checkpoint_file_security(ctx, file);
+ if (h->f_secref < 0) {
+ ckpt_write_err(ctx, "TE", "file->f_security", h->f_secref);
+ return h->f_secref;
+ }
+
+ ckpt_debug("file %s credref %d secref %d\n",
+ file->f_dentry->d_name.name, h->f_credref, h->f_secref);
/* FIX: need also file->f_owner, etc */
@@ -510,6 +529,11 @@ int restore_file_common(struct ckpt_ctx *ctx, struct file *file,
put_cred(file->f_cred);
file->f_cred = get_cred(cred);
+ ret = security_restore_obj(ctx, (void *) file, CKPT_SECURITY_FILE,
+ h->f_secref);
+ if (ret < 0)
+ return ret;
+
/* safe to set 1st arg (fd) to 0, as command is F_SETFL */
ret = vfs_fcntl(0, F_SETFL, h->f_flags & CKPT_SETFL_MASK, file);
if (ret < 0)
diff --git a/checkpoint/objhash.c b/checkpoint/objhash.c
index 730dd82..2440442 100644
--- a/checkpoint/objhash.c
+++ b/checkpoint/objhash.c
@@ -16,6 +16,7 @@
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/sched.h>
+#include <linux/kref.h>
#include <linux/ipc_namespace.h>
#include <linux/user_namespace.h>
#include <linux/checkpoint.h>
@@ -285,6 +286,36 @@ static int obj_tty_users(void *ptr)
return atomic_read(&((struct tty_struct *) ptr)->kref.refcount);
}
+void lsm_string_free(struct kref *kref)
+{
+ struct ckpt_lsm_string *s = container_of(kref, struct ckpt_lsm_string,
+ kref);
+ kfree(s->string);
+ kfree(s);
+}
+
+static int lsm_string_grab(void *ptr)
+{
+ struct ckpt_lsm_string *s = ptr;
+ kref_get(&s->kref);
+ return 0;
+}
+
+static void lsm_string_drop(void *ptr, int lastref)
+{
+ struct ckpt_lsm_string *s = ptr;
+ kref_put(&s->kref, lsm_string_free);
+}
+
+/* security context strings */
+static int checkpoint_lsm_string(struct ckpt_ctx *ctx, void *ptr);
+static struct ckpt_lsm_string *restore_lsm_string(struct ckpt_ctx *ctx);
+static void *restore_lsm_string_wrap(struct ckpt_ctx *ctx)
+{
+ return (void *)restore_lsm_string(ctx);
+}
+
+
static struct ckpt_obj_ops ckpt_obj_ops[] = {
/* ignored object */
{
@@ -433,6 +464,33 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = {
.checkpoint = checkpoint_tty,
.restore = restore_tty,
},
+ /*
+ * LSM void *security on objhash - at checkpoint
+ * We don't take a ref because we won't be doing
+ * anything more with this void* - unless we happen
+ * to run into it again through some other objects's
+ * ->security (in which case that object has it pinned).
+ */
+ {
+ .obj_name = "SECURITY PTR",
+ .obj_type = CKPT_OBJ_SECURITY_PTR,
+ .ref_drop = obj_no_drop,
+ .ref_grab = obj_no_grab,
+ },
+ /*
+ * LSM security strings - at restart
+ * This is a struct which we malloc during restart and
+ * must be freed (by objhash cleanup) at the end of
+ * restart
+ */
+ {
+ .obj_name = "SECURITY STRING",
+ .obj_type = CKPT_OBJ_SECURITY,
+ .ref_grab = lsm_string_grab,
+ .ref_drop = lsm_string_drop,
+ .checkpoint = checkpoint_lsm_string,
+ .restore = restore_lsm_string_wrap,
+ },
};
@@ -1010,3 +1068,75 @@ void *ckpt_obj_fetch(struct ckpt_ctx *ctx, int objref, enum obj_type type)
ckpt_debug("%s ref %d\n", obj->ops->obj_name, obj->objref);
return (obj->ops->obj_type == type ? obj->ptr : ERR_PTR(-ENOMSG));
}
+
+/*
+ * checkpoint a security context string. This is done by
+ * security/security.c:security_checkpoint_obj() when it checkpoints
+ * a void*security whose context string has not yet been written out.
+ * The objref for the void*security (which is not itself written out
+ * to the checkpoint image) is stored alongside the context string,
+ * as is the type of object which contained the void* security, i.e.
+ * struct file, struct cred, etc.
+ */
+static int checkpoint_lsm_string(struct ckpt_ctx *ctx, void *ptr)
+{
+ struct ckpt_hdr_lsm *h;
+ struct ckpt_lsm_string *l = ptr;
+ int ret;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SECURITY);
+ if (!h)
+ return -ENOMEM;
+ h->sectype = l->sectype;
+ h->ptrref = l->ptrref;
+ ret = ckpt_write_obj(ctx, &h->h);
+ ckpt_hdr_put(ctx, h);
+
+ if (ret < 0)
+ return ret;
+ return ckpt_write_string(ctx, l->string, strlen(l->string)+1);
+}
+
+/*
+ * callback invoked when a security context string is found in a
+ * checkpoint image at restart. The context string is saved in the object
+ * hash. The objref under which the void* security was inserted in the
+ * objhash at checkpoint is also found here, and we re-insert this context
+ * string a second time under that objref. This is because objects which
+ * had this context will have the objref of the void*security, not of the
+ * context string.
+ */
+static struct ckpt_lsm_string *restore_lsm_string(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_lsm *h;
+ struct ckpt_lsm_string *l;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SECURITY);
+ if (IS_ERR(h)) {
+ ckpt_debug("ckpt_read_obj_type returned %ld\n", PTR_ERR(h));
+ return ERR_PTR(PTR_ERR(h));
+ }
+
+ l = kzalloc(sizeof(*l), GFP_KERNEL);
+ if (!l) {
+ l = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ l->string = ckpt_read_string(ctx, CKPT_LSM_STRING_MAX);
+ if (IS_ERR(l->string)) {
+ void *s = l->string;
+ ckpt_debug("ckpt_read_string returned %ld\n", PTR_ERR(s));
+ kfree(l);
+ l = s;
+ goto out;
+ }
+ kref_init(&l->kref);
+ l->sectype = h->sectype;
+ /* l is just a placeholder, don't grab a ref */
+ ckpt_obj_insert(ctx, l, h->ptrref, CKPT_OBJ_SECURITY);
+
+out:
+ ckpt_hdr_put(ctx, h);
+ return l;
+}
+
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index eedd5e7..6d38f55 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -52,6 +52,7 @@
RESTART_KEEP_LSM | \
RESTART_GHOST)
#define CKPT_LSM_INFO_LEN 200
+#define CKPT_LSM_STRING_MAX 1024
extern int walk_task_subtree(struct task_struct *task,
int (*func)(struct task_struct *, void *),
@@ -100,6 +101,9 @@ extern char *ckpt_fill_fname(struct path *path, struct path *root,
extern pid_t ckpt_pid_nr(struct ckpt_ctx *ctx, struct pid *pid);
extern struct pid *_ckpt_find_pgrp(struct ckpt_ctx *ctx, pid_t pgid);
+/* defined in objhash.c and also used in security/security.c */
+extern void lsm_string_free(struct kref *kref);
+
/* socket functions */
extern int ckpt_sock_getnames(struct ckpt_ctx *ctx,
struct socket *socket,
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index 636e189..c4259b2 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -82,6 +82,8 @@ enum {
#define CKPT_HDR_OBJREF CKPT_HDR_OBJREF
CKPT_HDR_LSM_INFO,
#define CKPT_HDR_LSM_INFO CKPT_HDR_LSM_INFO
+ CKPT_HDR_SECURITY,
+#define CKPT_HDR_SECURITY CKPT_HDR_SECURITY
CKPT_HDR_TREE = 101,
#define CKPT_HDR_TREE CKPT_HDR_TREE
@@ -234,6 +236,10 @@ enum obj_type {
#define CKPT_OBJ_SOCK CKPT_OBJ_SOCK
CKPT_OBJ_TTY,
#define CKPT_OBJ_TTY CKPT_OBJ_TTY
+ CKPT_OBJ_SECURITY_PTR,
+#define CKPT_OBJ_SECURITY_PTR CKPT_OBJ_SECURITY_PTR
+ CKPT_OBJ_SECURITY,
+#define CKPT_OBJ_SECURITY CKPT_OBJ_SECURITY
CKPT_OBJ_MAX
#define CKPT_OBJ_MAX CKPT_OBJ_MAX
};
@@ -364,6 +370,7 @@ struct ckpt_hdr_cred {
__u32 gid, sgid, egid, fsgid;
__s32 user_ref;
__s32 groupinfo_ref;
+ __s32 sec_ref;
struct ckpt_capabilities cap_s;
} __attribute__((aligned(8)));
@@ -376,6 +383,15 @@ struct ckpt_hdr_groupinfo {
__u32 groups[0];
} __attribute__((aligned(8)));
+struct ckpt_hdr_lsm {
+ struct ckpt_hdr h;
+ __s32 ptrref;
+ __u8 sectype;
+ /*
+ * This is followed by a string of size len+1,
+ * null-terminated
+ */
+} __attribute__((aligned(8)));
/*
* todo - keyrings and LSM
* These may be better done with userspace help though
@@ -505,6 +521,7 @@ struct ckpt_hdr_file {
__s32 f_credref;
__u64 f_pos;
__u64 f_version;
+ __s32 f_secref;
} __attribute__((aligned(8)));
struct ckpt_hdr_file_generic {
@@ -765,6 +782,7 @@ struct ckpt_hdr_ipc_perms {
__u32 mode;
__u32 _padding;
__u64 seq;
+ __s32 sec_ref;
} __attribute__((aligned(8)));
struct ckpt_hdr_ipc_shm {
@@ -798,6 +816,7 @@ struct ckpt_hdr_ipc_msg_msg {
struct ckpt_hdr h;
__s32 m_type;
__u32 m_ts;
+ __s32 sec_ref;
} __attribute__((aligned(8)));
struct ckpt_hdr_ipc_sem {
diff --git a/include/linux/checkpoint_types.h b/include/linux/checkpoint_types.h
index b7d3053..96a7811 100644
--- a/include/linux/checkpoint_types.h
+++ b/include/linux/checkpoint_types.h
@@ -89,6 +89,13 @@ struct ckpt_ctx {
#endif
};
+/* stored on hashtable */
+struct ckpt_lsm_string {
+ struct kref kref;
+ int sectype; /* Containing object (file,cred,&c) */
+ int ptrref; /* the objref for the void* security */
+ char *string;
+};
#endif /* __KERNEL__ */
#endif /* _LINUX_CHECKPOINT_TYPES_H_ */
diff --git a/include/linux/security.h b/include/linux/security.h
index 99e4ebc..e68cc82 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -43,6 +43,9 @@
#define SECURITY_CAP_NOAUDIT 0
#define SECURITY_CAP_AUDIT 1
+/* checkpoint 'N/A' in a checkpoint image for a security context */
+#define SECURITY_CTX_NONE 0
+
struct ctl_table;
struct audit_krule;
@@ -581,6 +584,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* created.
* @file contains the file structure to secure.
* Return 0 if the hook is successful and permission is granted.
+ * @file_checkpoint:
+ * Return a string representing the security context on a file.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @file_restore:
+ * Set a security context on a file according to the checkpointed context.
+ * @file contains the file.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
* @file_free_security:
* Deallocate and free any security structures stored in file->f_security.
* @file contains the file structure being modified.
@@ -660,6 +672,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* manual page for definitions of the @clone_flags.
* @clone_flags contains the flags indicating what should be shared.
* Return 0 if permission is granted.
+ * @cred_checkpoint:
+ * Return a string representing the security context on the task cred.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @cred_restore:
+ * Set a security context on a task cred according to the checkpointed
+ * context.
+ * @file contains the checkpoint file
+ * @cred contains the cred.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
* @cred_free:
* @cred points to the credentials.
* Deallocate and clear the cred->security field in a set of credentials.
@@ -1108,6 +1131,19 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @ipcp contains the kernel IPC permission structure.
* @secid contains a pointer to the location where result will be saved.
* In case of failure, @secid will be set to zero.
+ * @ipc_checkpoint:
+ * Return a string representing the security context on the IPC
+ * permission structure.
+ * @security contains the security field.
+ * Returns a char* which the caller will free, or -error on error.
+ * @ipc_restore:
+ * Set a security context on a IPC permission structure according to
+ * the checkpointed context.
+ * @ipcp contains the IPC permission structure, which will have
+ * already been allocated and initialized when the IPC structure was
+ * created.
+ * @ctx contains a string representation of the checkpointed context.
+ * Returns 0 on success, -error on failure.
*
* Security hooks for individual messages held in System V IPC message queues
* @msg_msg_alloc_security:
@@ -1116,6 +1152,16 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* created.
* @msg contains the message structure to be modified.
* Return 0 if operation was successful and permission is granted.
+ * @msg_msg_checkpoint:
+ * Return a string representing the security context on an msg_msg
+ * struct.
+ * @security contains the security field
+ * Returns a char* which the caller will free, or -error on error.
+ * @msg_msg_restore:
+ * Set msg_msg->security according to the checkpointed context.
+ * @msg contains the message structure to be modified.
+ * @ctx contains a string representation of the checkpointed context.
+ * Return 0 on success, -error on failure.
* @msg_msg_free_security:
* Deallocate the security structure for this message.
* @msg contains the message structure to be modified.
@@ -1492,6 +1538,8 @@ struct security_operations {
int (*file_permission) (struct file *file, int mask);
int (*file_alloc_security) (struct file *file);
+ char *(*file_checkpoint) (void *security);
+ int (*file_restore) (struct file *file, char *ctx);
void (*file_free_security) (struct file *file);
int (*file_ioctl) (struct file *file, unsigned int cmd,
unsigned long arg);
@@ -1512,6 +1560,8 @@ struct security_operations {
int (*dentry_open) (struct file *file, const struct cred *cred);
int (*task_create) (unsigned long clone_flags);
+ char *(*cred_checkpoint) (void *security);
+ int (*cred_restore) (struct file *file, struct cred *cred, char *ctx);
void (*cred_free) (struct cred *cred);
int (*cred_prepare)(struct cred *new, const struct cred *old,
gfp_t gfp);
@@ -1545,8 +1595,12 @@ struct security_operations {
int (*ipc_permission) (struct kern_ipc_perm *ipcp, short flag);
void (*ipc_getsecid) (struct kern_ipc_perm *ipcp, u32 *secid);
+ char *(*ipc_checkpoint) (void *security);
+ int (*ipc_restore) (struct kern_ipc_perm *ipcp, char *ctx);
int (*msg_msg_alloc_security) (struct msg_msg *msg);
+ char *(*msg_msg_checkpoint) (void *security);
+ int (*msg_msg_restore) (struct msg_msg *msg, char *ctx);
void (*msg_msg_free_security) (struct msg_msg *msg);
int (*msg_queue_alloc_security) (struct msg_queue *msq);
@@ -1755,6 +1809,8 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer
void security_inode_getsecid(const struct inode *inode, u32 *secid);
int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
+char *security_file_checkpoint(void *security);
+int security_file_restore(struct file *file, char *ctx);
void security_file_free(struct file *file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_mmap(struct file *file, unsigned long reqprot,
@@ -1770,6 +1826,8 @@ int security_file_send_sigiotask(struct task_struct *tsk,
int security_file_receive(struct file *file);
int security_dentry_open(struct file *file, const struct cred *cred);
int security_task_create(unsigned long clone_flags);
+char *security_cred_checkpoint(void *security);
+int security_cred_restore(struct file *file, struct cred *cred, char *ctx);
void security_cred_free(struct cred *cred);
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
void security_commit_creds(struct cred *new, const struct cred *old);
@@ -1800,7 +1858,11 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
void security_task_to_inode(struct task_struct *p, struct inode *inode);
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid);
+char *security_ipc_checkpoint(void *security);
+int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx);
int security_msg_msg_alloc(struct msg_msg *msg);
+char *security_msg_msg_checkpoint(void *security);
+int security_msg_msg_restore(struct msg_msg *msg, char *ctx);
void security_msg_msg_free(struct msg_msg *msg);
int security_msg_queue_alloc(struct msg_queue *msq);
void security_msg_queue_free(struct msg_queue *msq);
@@ -2248,6 +2310,19 @@ static inline int security_file_alloc(struct file *file)
return 0;
}
+static inline char *security_file_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_file_restore(struct file *file, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_file_free(struct file *file)
{ }
@@ -2312,6 +2387,20 @@ static inline int security_task_create(unsigned long clone_flags)
return 0;
}
+static inline char *security_cred_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_cred_restore(struct file *file, struct cred *cred,
+ char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_cred_free(struct cred *cred)
{ }
@@ -2454,11 +2543,37 @@ static inline void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
*secid = 0;
}
+static inline char *security_ipc_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline int security_msg_msg_alloc(struct msg_msg *msg)
{
return 0;
}
+static inline char *security_msg_msg_checkpoint(void *security)
+{
+ /* this shouldn't ever get called if SECURITY=n */
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int security_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ /* we're asked to recreate security contexts for an LSM which had
+ * contexts, but CONFIG_SECURITY=n now! */
+ return -EINVAL;
+}
+
static inline void security_msg_msg_free(struct msg_msg *msg)
{ }
@@ -3055,5 +3170,58 @@ static inline void free_secdata(void *secdata)
{ }
#endif /* CONFIG_SECURITY */
+#ifdef CONFIG_CHECKPOINT
+#define CKPT_SECURITY_MSG_MSG 1
+#define CKPT_SECURITY_IPC 2
+#define CKPT_SECURITY_FILE 3
+#define CKPT_SECURITY_CRED 4
+#define CKPT_SECURITY_MAX 4
+
+#ifdef CONFIG_SECURITY
+/*
+ * @security_checkpoint_obj:
+ * Checkpoint a LSM security context. The context is written out
+ * as a string. A positive integer objref uniquely representing the
+ * security context in this checkpoint image will be returned.
+ * If the security context has already been written out, then the
+ * objref of that already written-out context will be used.
+ * @ctx: the checkpoint context.
+ * @security: the void*security being checkpointed.
+ * @sectype: represents the type of object which contained the
+ * void *security.
+ * Return 0 or a valid objref on success, or -error on error.
+ */
+int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security, int sectype);
+/*
+ * @security_restore_obj:
+ * Re-create a checkpointed LSM security context. The LSM will decide
+ * based upon the string representation which actual security context
+ * to assign.
+ * @ctx: the checkpoint context.
+ * @obj: The object containing the security context to be restored (cast
+ * to a void *).
+ * @sectype: represents the type of object which contained the
+ * void *security.
+ * @secref: an integer objref for the string representation of the
+ * security context to be restored.
+ * Return 0 on success, or -error on error.
+ */
+int security_restore_obj(struct ckpt_ctx *ctx, void *obj,
+ int sectype, int secref);
+#else
+static inline int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security,
+ int sectype)
+{
+ return SECURITY_CTX_NONE;
+}
+static inline int security_restore_obj(struct ckpt_ctx *ctx, void *obj,
+ int sectype, int secref)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITY */
+
+#endif /* CONFIG_CHECKPOINT */
+
#endif /* ! __LINUX_SECURITY_H */
diff --git a/ipc/checkpoint.c b/ipc/checkpoint.c
index 8e6e9ba..cf79da3 100644
--- a/ipc/checkpoint.c
+++ b/ipc/checkpoint.c
@@ -31,7 +31,8 @@ static char *ipc_ind_to_str[] = { "sem", "msg", "shm" };
* Checkpoint
*/
-int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+int checkpoint_fill_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm)
{
if (ipcperms(perm, S_IROTH))
@@ -45,7 +46,12 @@ int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
h->cgid = perm->cgid;
h->mode = perm->mode & S_IRWXUGO;
h->seq = perm->seq;
-
+ h->sec_ref = security_checkpoint_obj(ctx, perm->security,
+ CKPT_SECURITY_IPC);
+ if (h->sec_ref < 0) {
+ ckpt_write_err(ctx, "TE", "ipc_perm->security", h->sec_ref);
+ return h->sec_ref;
+ }
return 0;
}
@@ -176,7 +182,8 @@ static int validate_created_perms(struct ckpt_hdr_ipc_perms *h)
return 1;
}
-int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+int restore_load_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm)
{
if (h->id < 0)
@@ -205,16 +212,9 @@ int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
perm->cgid = h->cgid;
perm->mode = h->mode;
perm->seq = h->seq;
- /*
- * Todo: restore perm->security.
- * At the moment it gets set by security_x_alloc() called through
- * ipcget()->ipcget_public()->ops-.getnew (->nequeue for instance)
- * We will want to ask the LSM to consider resetting the
- * checkpointed ->security, based on current_security(),
- * the checkpointed ->security, and the checkpoint file context.
- */
- return 0;
+ return security_restore_obj(ctx, (void *)perm, CKPT_SECURITY_IPC,
+ h->sec_ref);
}
static int restore_ipc_any(struct ckpt_ctx *ctx, struct ipc_namespace *ipc_ns,
diff --git a/ipc/checkpoint_msg.c b/ipc/checkpoint_msg.c
index b933c19..aee2df7 100644
--- a/ipc/checkpoint_msg.c
+++ b/ipc/checkpoint_msg.c
@@ -37,7 +37,7 @@ static int fill_ipc_msg_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&msq->q_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &msq->q_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &msq->q_perm);
if (ret < 0)
goto unlock;
@@ -63,14 +63,21 @@ static int checkpoint_msg_contents(struct ckpt_ctx *ctx, struct msg_msg *msg)
struct ckpt_hdr_ipc_msg_msg *h;
struct msg_msgseg *seg;
int total, len;
- int ret;
+ int secref, ret;
+ secref = security_checkpoint_obj(ctx, msg->security,
+ CKPT_SECURITY_MSG_MSG);
+ if (secref < 0) {
+ ckpt_write_err(ctx, "TE", "msg_msg->security", secref);
+ return secref;
+ }
h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_IPC_MSG_MSG);
if (!h)
return -ENOMEM;
h->m_type = msg->m_type;
h->m_ts = msg->m_ts;
+ h->sec_ref = secref;
ret = ckpt_write_obj(ctx, &h->h);
ckpt_hdr_put(ctx, h);
@@ -177,7 +184,7 @@ static int load_ipc_msg_hdr(struct ckpt_ctx *ctx,
{
int ret = 0;
- ret = restore_load_ipc_perms(&h->perms, &msq->q_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &msq->q_perm);
if (ret < 0)
return ret;
@@ -224,6 +231,11 @@ static struct msg_msg *restore_msg_contents_one(struct ckpt_ctx *ctx, int *clen)
msg->next = NULL;
pseg = &msg->next;
+ ret = security_restore_obj(ctx, (void *) msg, CKPT_SECURITY_MSG_MSG,
+ h->sec_ref);
+ if (ret < 0)
+ goto out;
+
ret = _ckpt_read_buffer(ctx, (msg + 1), len);
if (ret < 0)
goto out;
diff --git a/ipc/checkpoint_sem.c b/ipc/checkpoint_sem.c
index 76eb2b9..53a19ed 100644
--- a/ipc/checkpoint_sem.c
+++ b/ipc/checkpoint_sem.c
@@ -37,7 +37,7 @@ static int fill_ipc_sem_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&sem->sem_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &sem->sem_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &sem->sem_perm);
if (ret < 0)
goto unlock;
@@ -113,7 +113,7 @@ static int load_ipc_sem_hdr(struct ckpt_ctx *ctx,
{
int ret = 0;
- ret = restore_load_ipc_perms(&h->perms, &sem->sem_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &sem->sem_perm);
if (ret < 0)
return ret;
diff --git a/ipc/checkpoint_shm.c b/ipc/checkpoint_shm.c
index 826e430..01091d9 100644
--- a/ipc/checkpoint_shm.c
+++ b/ipc/checkpoint_shm.c
@@ -41,7 +41,7 @@ static int fill_ipc_shm_hdr(struct ckpt_ctx *ctx,
ipc_lock_by_ptr(&shp->shm_perm);
- ret = checkpoint_fill_ipc_perms(&h->perms, &shp->shm_perm);
+ ret = checkpoint_fill_ipc_perms(ctx, &h->perms, &shp->shm_perm);
if (ret < 0)
goto unlock;
@@ -165,7 +165,7 @@ static int load_ipc_shm_hdr(struct ckpt_ctx *ctx,
{
int ret;
- ret = restore_load_ipc_perms(&h->perms, &shp->shm_perm);
+ ret = restore_load_ipc_perms(ctx, &h->perms, &shp->shm_perm);
if (ret < 0)
return ret;
diff --git a/ipc/util.h b/ipc/util.h
index ba080de..ce34de0 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -199,9 +199,11 @@ void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp);
#ifdef CONFIG_CHECKPOINT
-extern int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+extern int checkpoint_fill_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm);
-extern int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h,
+extern int restore_load_ipc_perms(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_ipc_perms *h,
struct kern_ipc_perm *perm);
extern int ckpt_collect_ipc_shm(int id, void *p, void *data);
diff --git a/kernel/cred.c b/kernel/cred.c
index 62d28a4..aec9f56 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -706,10 +706,22 @@ int cred_setfsgid(struct cred *new, gid_t gid, gid_t *old_fsgid)
}
#ifdef CONFIG_CHECKPOINT
+#ifdef CONFIG_SECURITY
+int checkpoint_cred_security(struct ckpt_ctx *ctx, struct cred *cred)
+{
+ return security_checkpoint_obj(ctx, cred->security, CKPT_SECURITY_CRED);
+}
+#else
+int checkpoint_cred_security(struct ckpt_ctx *ctx, struct cred *cred)
+{
+ return SECURITY_CTX_NONE;
+}
+#endif
+
static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
{
int ret;
- int groupinfo_ref, user_ref;
+ int groupinfo_ref, user_ref, secref;
struct ckpt_hdr_cred *h;
groupinfo_ref = checkpoint_obj(ctx, cred->group_info,
@@ -719,13 +731,18 @@ static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
user_ref = checkpoint_obj(ctx, cred->user, CKPT_OBJ_USER);
if (user_ref < 0)
return user_ref;
+ secref = checkpoint_cred_security(ctx, cred);
+ if (secref < 0) {
+ ckpt_write_err(ctx, "TE", "cred->security", secref);
+ return secref;
+ }
h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_CRED);
if (!h)
return -ENOMEM;
- ckpt_debug("cred uid %d fsuid %d gid %d\n", cred->uid, cred->fsuid,
- cred->gid);
+ ckpt_debug("cred uid %d fsuid %d gid %d secref %d\n", cred->uid,
+ cred->fsuid, cred->gid, secref);
h->uid = cred->uid;
h->suid = cred->suid;
h->euid = cred->euid;
@@ -735,6 +752,7 @@ static int do_checkpoint_cred(struct ckpt_ctx *ctx, struct cred *cred)
h->sgid = cred->sgid;
h->egid = cred->egid;
h->fsgid = cred->fsgid;
+ h->sec_ref = secref;
checkpoint_capabilities(&h->cap_s, cred);
@@ -808,6 +826,10 @@ static struct cred *do_restore_cred(struct ckpt_ctx *ctx)
ret = cred_setfsgid(cred, h->fsgid, &oldgid);
if (oldgid != h->fsgid && ret < 0)
goto err_putcred;
+ ret = security_restore_obj(ctx, (void *) cred, CKPT_SECURITY_CRED,
+ h->sec_ref);
+ if (ret)
+ goto err_putcred;
ret = restore_capabilities(&h->cap_s, cred);
if (ret)
goto err_putcred;
diff --git a/security/capability.c b/security/capability.c
index 23026e2..de1e3a1 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -315,6 +315,16 @@ static int cap_file_permission(struct file *file, int mask)
return 0;
}
+static inline char *cap_file_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_file_restore(struct file *file, char *ctx)
+{
+ return -ENOSYS;
+}
+
static int cap_file_alloc_security(struct file *file)
{
return 0;
@@ -373,6 +383,16 @@ static int cap_task_create(unsigned long clone_flags)
return 0;
}
+static char *cap_cred_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_cred_restore(struct file *file, struct cred *cred, char *ctx)
+{
+ return -ENOSYS;
+}
+
static void cap_cred_free(struct cred *cred)
{
}
@@ -476,11 +496,31 @@ static void cap_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
*secid = 0;
}
+static char *cap_ipc_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ return -ENOSYS;
+}
+
static int cap_msg_msg_alloc_security(struct msg_msg *msg)
{
return 0;
}
+static inline char *cap_msg_msg_checkpoint(void *security)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static int cap_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ return -ENOSYS;
+}
+
static void cap_msg_msg_free_security(struct msg_msg *msg)
{
}
@@ -948,6 +988,8 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, path_truncate);
#endif
set_to_cap_if_null(ops, file_permission);
+ set_to_cap_if_null(ops, file_checkpoint);
+ set_to_cap_if_null(ops, file_restore);
set_to_cap_if_null(ops, file_alloc_security);
set_to_cap_if_null(ops, file_free_security);
set_to_cap_if_null(ops, file_ioctl);
@@ -960,6 +1002,8 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, file_receive);
set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create);
+ set_to_cap_if_null(ops, cred_checkpoint);
+ set_to_cap_if_null(ops, cred_restore);
set_to_cap_if_null(ops, cred_free);
set_to_cap_if_null(ops, cred_prepare);
set_to_cap_if_null(ops, cred_commit);
@@ -986,7 +1030,11 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, task_to_inode);
set_to_cap_if_null(ops, ipc_permission);
set_to_cap_if_null(ops, ipc_getsecid);
+ set_to_cap_if_null(ops, ipc_checkpoint);
+ set_to_cap_if_null(ops, ipc_restore);
set_to_cap_if_null(ops, msg_msg_alloc_security);
+ set_to_cap_if_null(ops, msg_msg_checkpoint);
+ set_to_cap_if_null(ops, msg_msg_restore);
set_to_cap_if_null(ops, msg_msg_free_security);
set_to_cap_if_null(ops, msg_queue_alloc_security);
set_to_cap_if_null(ops, msg_queue_free_security);
diff --git a/security/security.c b/security/security.c
index e4fa91a..51ed7aa 100644
--- a/security/security.c
+++ b/security/security.c
@@ -633,6 +633,16 @@ int security_file_alloc(struct file *file)
return security_ops->file_alloc_security(file);
}
+char *security_file_checkpoint(void *security)
+{
+ return security_ops->file_checkpoint(security);
+}
+
+int security_file_restore(struct file *file, char *ctx)
+{
+ return security_ops->file_restore(file, ctx);
+}
+
void security_file_free(struct file *file)
{
security_ops->file_free_security(file);
@@ -692,6 +702,16 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(clone_flags);
}
+char *security_cred_checkpoint(void *security)
+{
+ return security_ops->cred_checkpoint(security);
+}
+
+int security_cred_restore(struct file *file, struct cred *cred, char *ctx)
+{
+ return security_ops->cred_restore(file, cred, ctx);
+}
+
void security_cred_free(struct cred *cred)
{
security_ops->cred_free(cred);
@@ -827,11 +847,31 @@ void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
security_ops->ipc_getsecid(ipcp, secid);
}
+char *security_ipc_checkpoint(void *security)
+{
+ return security_ops->ipc_checkpoint(security);
+}
+
+int security_ipc_restore(struct kern_ipc_perm *ipcp, char *ctx)
+{
+ return security_ops->ipc_restore(ipcp, ctx);
+}
+
int security_msg_msg_alloc(struct msg_msg *msg)
{
return security_ops->msg_msg_alloc_security(msg);
}
+char *security_msg_msg_checkpoint(void *security)
+{
+ return security_ops->msg_msg_checkpoint(security);
+}
+
+int security_msg_msg_restore(struct msg_msg *msg, char *ctx)
+{
+ return security_ops->msg_msg_restore(msg, ctx);
+}
+
void security_msg_msg_free(struct msg_msg *msg)
{
security_ops->msg_msg_free_security(msg);
@@ -1270,3 +1310,154 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
}
#endif /* CONFIG_AUDIT */
+
+#ifdef CONFIG_CHECKPOINT
+
+/**
+ * security_checkpoint_obj - called during application checkpoint to
+ * record the security context of objects.
+ *
+ * First we add the void*security address to the objhash as a type
+ * CKPT_OBJ_SECURITY_PTR. This records the fact that we've seen this
+ * context. If we've seen the context before, then we simply place the
+ * recorded objref in *secref and return success.
+
+ * If this is the first time we've seen this context for this checkpoint
+ * image, then we
+ * 1. ask the LSM for a string representation of the context
+ * 2. create a struct ckpt_lsm_string pointing to the string and to the
+ * objref which we got for the void*security in the objhash.
+ * 3. write that out to the checkpoint image as a CKPT_OBJ_SECURITY. it
+ * will be freed when the objhash is cleared.
+ *
+ * Returns 0 or a valid objref on success, or -error on error.
+ *
+ * This is only used at checkpoint of course.
+ */
+int security_checkpoint_obj(struct ckpt_ctx *ctx, void *security, int sectype)
+{
+ int new, ret = -ENOMEM;
+ char *str;
+ struct ckpt_lsm_string *l;
+ int secref;
+
+ if (!security)
+ return SECURITY_CTX_NONE;
+
+ secref = ckpt_obj_lookup_add(ctx, security, CKPT_OBJ_SECURITY_PTR,
+ &new);
+ if (!new)
+ return secref;
+
+ /*
+ * Ask the LSM for a string representation
+ */
+ switch (sectype) {
+ case CKPT_SECURITY_MSG_MSG:
+ str = security_msg_msg_checkpoint(security);
+ break;
+ case CKPT_SECURITY_IPC:
+ str = security_ipc_checkpoint(security);
+ break;
+ case CKPT_SECURITY_FILE:
+ str = security_file_checkpoint(security);
+ break;
+ case CKPT_SECURITY_CRED:
+ str = security_cred_checkpoint(security);
+ break;
+ default:
+ str = ERR_PTR(-EINVAL);
+ break;
+ }
+
+ if (IS_ERR(str)) {
+ if (PTR_ERR(str) == -ENOSYS)
+ return SECURITY_CTX_NONE;
+ return PTR_ERR(str);
+ }
+
+ l = kzalloc(sizeof(*l), GFP_KERNEL);
+ if (!l) {
+ kfree(str);
+ return -ENOMEM;
+ }
+ l->ptrref = secref;
+ l->sectype = sectype;
+ l->string = str;
+ kref_init(&l->kref);
+ ret = checkpoint_obj(ctx, l, CKPT_OBJ_SECURITY);
+ kref_put(&l->kref, lsm_string_free);
+ if (ret < 0)
+ return ret;
+
+ return secref;
+}
+
+/*
+ * Choose a security context for an object being restored during
+ * application restart. @v is an object (file, cred, etc) containing
+ * a security context and being re-created. It has been type-cast
+ * to a void*. @sectype tells us what sort of object v is. @secref
+ * is the objhash id representing the security context.
+ *
+ * If sys_restart() was called without the RESTART_KEEP_LSM flag,
+ * then default security contexts will be assigned to the re-created
+ * object (in fact, they already have by this point). Otherwise, the
+ * LSM is expected to use the string context representation to assign
+ * the same security context to this object (if allowed).
+ *
+ * At checkpoint time, @secref was the objref for the void*security
+ * (which was not written to disk). The
+ * checkpoint/objhash.c:restore_lsm_string() function should, before we
+ * get here, have read the context string in the checkpoint image, and
+ * inserted a second copy of the struct ckpt_lsm_string on the objhash,
+ * with this objref.
+ *
+ * Returns 0 on success, -error on error.
+ */
+int security_restore_obj(struct ckpt_ctx *ctx, void *v, int sectype,
+ int secref)
+{
+ struct ckpt_lsm_string *l;
+ int ret;
+
+ /* return if caller didn't want to restore checkpointed labels */
+ if (!(ctx->uflags & RESTART_KEEP_LSM))
+ return 0;
+
+ l = ckpt_obj_fetch(ctx, secref, CKPT_OBJ_SECURITY);
+ if (IS_ERR(l))
+ return PTR_ERR(l);
+
+ /* return if checkpointed label was "Not Applicable" */
+ if (secref == SECURITY_CTX_NONE)
+ return 0;
+
+ /* Ask the LSM to apply a void*security to the object
+ * based on the checkpointed context string */
+ switch (sectype) {
+ case CKPT_SECURITY_IPC:
+ ret = security_ipc_restore((struct kern_ipc_perm *) v,
+ l->string);
+ break;
+ case CKPT_SECURITY_MSG_MSG:
+ ret = security_msg_msg_restore((struct msg_msg *) v,
+ l->string);
+ break;
+ case CKPT_SECURITY_FILE:
+ ret = security_file_restore((struct file *) v, l->string);
+ break;
+ case CKPT_SECURITY_CRED:
+ ret = security_cred_restore(ctx->file, (struct cred *) v,
+ l->string);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ ckpt_debug("sectype %d objref %d lsm's hook returned %d\n",
+ sectype, secref, ret);
+
+ return ret;
+}
+#endif
--
1.5.4.3
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 4/4] cr: add selinux support (v6)
[not found] ` <1257955132-8398-1-git-send-email-serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2009-11-11 15:58 ` serue-r/Jw6+rmf7HQT0dZR+AlfA
0 siblings, 0 replies; 19+ messages in thread
From: serue-r/Jw6+rmf7HQT0dZR+AlfA @ 2009-11-11 15:58 UTC (permalink / raw)
To: containers-qjLDD68F18O7TbgM5vRIOg
From: Serge E. Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
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:
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-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
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 507b599..701c184 100644
--- a/checkpoint/restart.c
+++ b/checkpoint/restart.c
@@ -661,6 +661,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 8d8b69c..e6994eb 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"
@@ -2961,6 +2962,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);
@@ -3219,6 +3318,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 avc_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
+ */
+
+ AVC_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;
+}
+
+
/*
* detach and free the LSM part of a set of credentials
*/
@@ -4658,6 +4937,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;
@@ -5061,6 +5378,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 avc_audit_data ad;
+
+ ret = security_context_to_sid(ctx, strlen(ctx), &sid);
+ if (ret)
+ return ret;
+
+ if (isec->sid == sid)
+ return 0;
+
+ AVC_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)
@@ -5382,6 +5740,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,
@@ -5396,6 +5756,8 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
+ .cred_checkpoint = selinux_cred_checkpoint,
+ .cred_restore = selinux_cred_restore,
.cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare,
.kernel_act_as = selinux_kernel_act_as,
@@ -5417,8 +5779,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 31df1d7..a2c35d7 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")
@@ -107,6 +111,7 @@
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
+ 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 d645192..58ad588 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
@@ -453,6 +455,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
@@ -462,6 +465,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
@@ -483,6 +487,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
--
1.6.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
end of thread, other threads:[~2009-11-11 15:58 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-10-19 14:43 [PATCH 1/4] add lsm name and lsm_info (policy header) to container info Serge E. Hallyn
[not found] ` <20091019144315.GA30535-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-19 14:43 ` [PATCH 2/4] cr: add generic LSM c/r support (v6) Serge E. Hallyn
[not found] ` <20091019144341.GA30566-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-19 18:13 ` Oren Laadan
[not found] ` <4ADCAC5B.9080205-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
2009-10-19 19:02 ` Serge E. Hallyn
[not found] ` <20091019190227.GA7201-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-21 1:03 ` Oren Laadan
[not found] ` <4ADE5DEA.2000606-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
2009-10-21 1:18 ` Serge E. Hallyn
[not found] ` <20091021011846.GA26728-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-21 1:21 ` Oren Laadan
[not found] ` <4ADE621E.2080603-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
2009-10-21 5:01 ` Serge E. Hallyn
2009-10-20 1:16 ` Serge E. Hallyn
2009-10-19 14:44 ` [PATCH user-cr] restart: accept the lsm_name field in header and add -k flag (v2) Serge E. Hallyn
2009-10-19 14:44 ` [PATCH 3/4] cr: add smack support to lsm c/r (v6) Serge E. Hallyn
2009-10-19 14:44 ` [PATCH 4/4] cr: add selinux support (v6) Serge E. Hallyn
-- strict thread matches above, loose matches on Subject: below --
2009-11-11 15:58 [PATCH 0/4] Introduction: LSM c/r patchset serue-r/Jw6+rmf7HQT0dZR+AlfA
[not found] ` <1257955132-8398-1-git-send-email-serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-11-11 15:58 ` [PATCH 4/4] cr: add selinux support (v6) serue-r/Jw6+rmf7HQT0dZR+AlfA
2009-10-09 20:55 [PATCH 1/4] debug: add a few ckpt_debugs Serge E. Hallyn
[not found] ` <20091009205552.GA5778-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-09 20:57 ` [PATCH 4/4] cr: add selinux support (v6) Serge E. Hallyn
2009-10-09 20:57 ` Serge E. Hallyn
[not found] ` <20091009205731.GC5823-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-10-09 21:15 ` Daniel J Walsh
2009-10-09 21:15 ` Daniel J Walsh
[not found] ` <4ACFA7F1.6060209-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2009-10-09 22:31 ` Serge E. Hallyn
2009-10-09 22:31 ` Serge E. Hallyn
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.