[ This patch is against branch ckpt-v17-dev of the checkpoint/restart
kernel tree at git://git.ncl.cs.columbia.edu/pub/git/linux-cr.git/ ]

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.
"""

[ Note - switching to not having to kmalloc/kfree for smack
labels is not yet addressed.  I want to first handle the
socket LSM label c/r, after which I'll be able to better
judge what makes sense and what doesn't. ]

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 <[email protected]>
---
 checkpoint/restart.c       |    1 +
 security/security.c        |    8 +++
 security/smack/smack.h     |    1 +
 security/smack/smack_lsm.c |  141 ++++++++++++++++++++++++++++++++++++++++++++
 security/smack/smackfs.c   |   83 ++++++++++++++++++++++++++
 5 files changed, 234 insertions(+), 0 deletions(-)

diff --git a/checkpoint/restart.c b/checkpoint/restart.c
index 4703ff8..a498983 100644
--- a/checkpoint/restart.c
+++ b/checkpoint/restart.c
@@ -436,6 +436,7 @@ static int restore_read_header(struct ckpt_ctx *ctx)
                }
                /* to be implemented later, per-lsm */
                if (strcmp(ctx->lsm_name, "lsm_none") != 0 &&
+                               strcmp(ctx->lsm_name, "smack") != 0 &&
                                strcmp(ctx->lsm_name, "default") != 0) {
                        pr_warning("c/r: RESTART_KEEP_LSM unsupported for %s\n",
                                        ctx->lsm_name);
diff --git a/security/security.c b/security/security.c
index 7b9055c..dba57b0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1330,6 +1330,14 @@ int security_checkpoint_obj(struct ckpt_ctx *ctx, void 
*security,
        char *str;
        int len;
 
+       /*
+        * to simplify the LSM code, short-cut a null security
+        * here - hopefully not actually needed; test without
+        * this one day.
+        */
+       if (!security)
+               return -EOPNOTSUPP;
+
        switch (sectype) {
        case CKPT_SECURITY_MSG_MSG:
                str = security_msg_msg_checkpoint(security);
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

_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers

_______________________________________________
Devel mailing list
[email protected]
https://openvz.org/mailman/listinfo/devel

Reply via email to