[ 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 <serue@xxxxxxxxxx> --- 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 -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with the words "unsubscribe selinux" without quotes as the message.