Here is the next version of the patch implementing checkpoint and restore of LSM contexts. This is just handling IPC objects as a proof of concept. But actually, looking ahead and both files and tasks, I see that selinux stores several sids in the security structs. For instance, for tasks there is the current sid, exec sid, create sid, keycreate_sid, and sockcreate_sid. So I guess I'll have to ask the LSM for how many secids it wants to checkpoint, then checkpoint an array of contexts? >From 19669b07cdfef4d377f3f188e2421c4124e38708 Mon Sep 17 00:00:00 2001 From: Serge E. Hallyn <serue@xxxxxxxxxx> Date: Wed, 17 Jun 2009 12:00:21 -0400 Subject: [PATCH 1/1] cr: lsm: restore LSM contexts for ipc objects Introduce a cache of secids for checkpoint and restart. At checkpoint, it takes a secid, stores the corresponding context string, and stores the objref for later use. At restart, read the context from checkpoint image, ask the security module for a secid, and store the secid on the objhash. The per-object security c/r code will be responsible for getting secid from void*security at checkpoint time, and converting secid to void*security at restore time. The code to c/r contexts for IPC objects is also in this patch. For Smack, assign the label of the process doing sys_restart() if !capable(CAP_MAC_ADMIN), otherwise use the checkpointed label. For SELinux, define a new 'restore' permission for ipc objects. (A corresponding trival policy patch adding 'restore' to the common flask permissions for refpolicy is also needed). The caller of sys_restart() must have the class:restore permission to assign the checkpointed label, else restart will be refused. Signed-off-by: Serge E. Hallyn <serue@xxxxxxxxxx> --- checkpoint/objhash.c | 10 ++ checkpoint/sys.c | 15 ++ include/linux/checkpoint_hdr.h | 12 ++- include/linux/checkpoint_types.h | 7 + include/linux/security.h | 92 ++++++++++++ ipc/checkpoint.c | 20 ++- ipc/checkpoint_msg.c | 45 ++++++- ipc/checkpoint_sem.c | 18 +++- ipc/checkpoint_shm.c | 18 +++- ipc/util.h | 3 +- security/capability.c | 38 +++++ security/security.c | 169 ++++++++++++++++++++++ security/selinux/hooks.c | 48 ++++++ security/selinux/include/av_inherit.h | 8 +- security/selinux/include/av_permissions.h | 10 +- security/selinux/include/common_perm_to_string.h | 1 + security/smack/smack_lsm.c | 77 ++++++++++ 17 files changed, 571 insertions(+), 20 deletions(-) diff --git a/checkpoint/objhash.c b/checkpoint/objhash.c index b4bc2e4..c266375 100644 --- a/checkpoint/objhash.c +++ b/checkpoint/objhash.c @@ -18,6 +18,7 @@ #include <linux/sched.h> #include <linux/ipc_namespace.h> #include <linux/user_namespace.h> +#include <linux/security.h> #include <linux/checkpoint.h> #include <linux/checkpoint_hdr.h> @@ -365,6 +366,15 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = { .checkpoint = checkpoint_groupinfo, .restore = restore_groupinfo, }, + /* LSM context */ + { + .obj_name = "SECURITY", + .obj_type = CKPT_OBJ_SECURITY, + .ref_grab = obj_no_grab, + .ref_drop = obj_no_drop, + .checkpoint = checkpoint_security, + .restore = restore_security, + }, }; diff --git a/checkpoint/sys.c b/checkpoint/sys.c index 38a5299..06a2c00 100644 --- a/checkpoint/sys.c +++ b/checkpoint/sys.c @@ -20,6 +20,7 @@ #include <linux/file.h> #include <linux/uaccess.h> #include <linux/capability.h> +#include <linux/security.h> #include <linux/checkpoint.h> #include <linux/deferqueue.h> @@ -188,6 +189,16 @@ static void task_arr_free(struct ckpt_ctx *ctx) kfree(ctx->tasks_arr); } +void free_secreflist(struct ckpt_ctx *ctx) +{ + struct sec_store *s, *p; + + list_for_each_entry_safe(s, p, &ctx->secref_list, list) { + list_del(&s->list); + kfree(s); + } +} + static void ckpt_ctx_free(struct ckpt_ctx *ctx) { int ret; @@ -217,6 +228,7 @@ static void ckpt_ctx_free(struct ckpt_ctx *ctx) put_task_struct(ctx->root_task); if (ctx->root_freezer) put_task_struct(ctx->root_freezer); + free_secreflist(ctx); kfree(ctx->pids_arr); @@ -233,6 +245,9 @@ static struct ckpt_ctx *ckpt_ctx_alloc(int fd, unsigned long uflags, if (!ctx) return ERR_PTR(-ENOMEM); + spin_lock_init(&ctx->secref_lock); + INIT_LIST_HEAD(&ctx->secref_list); + ctx->uflags = uflags; ctx->kflags = kflags; ctx->ktime_begin = ktime_get(); diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h index e42e0db..e3fb9b3 100644 --- a/include/linux/checkpoint_hdr.h +++ b/include/linux/checkpoint_hdr.h @@ -62,6 +62,7 @@ enum { CKPT_HDR_USER, CKPT_HDR_GROUPINFO, CKPT_HDR_TASK_CREDS, + CKPT_HDR_SECURITY, /* 201-299: reserved for arch-dependent */ @@ -114,6 +115,7 @@ enum obj_type { CKPT_OBJ_CRED, CKPT_OBJ_USER, CKPT_OBJ_GROUPINFO, + CKPT_OBJ_SECURITY, CKPT_OBJ_MAX }; @@ -192,6 +194,12 @@ struct ckpt_capabilities { __u32 padding; } __attribute__((aligned(8))); +/* LSM security contexts (shared) */ +struct ckpt_hdr_sec { + struct ckpt_hdr h; + char str[]; +} __attribute__((aligned(8))); + struct ckpt_hdr_task_creds { struct ckpt_hdr h; __s32 cred_ref; @@ -418,7 +426,7 @@ struct ckpt_hdr_ipc_perms { __u32 cuid; __u32 cgid; __u32 mode; - __u32 _padding; + __s32 secref; __u64 seq; } __attribute__((aligned(8))); @@ -453,6 +461,8 @@ struct ckpt_hdr_ipc_msg_msg { struct ckpt_hdr h; __s32 m_type; __u32 m_ts; + __s32 secref; + __u32 padding; } __attribute__((aligned(8))); struct ckpt_hdr_ipc_sem { diff --git a/include/linux/checkpoint_types.h b/include/linux/checkpoint_types.h index 27fbe26..74c218a 100644 --- a/include/linux/checkpoint_types.h +++ b/include/linux/checkpoint_types.h @@ -73,6 +73,13 @@ struct ckpt_ctx { struct cred *realcred, *ecred; /* tmp storage for cred at restart */ struct ckpt_stats stats; /* statistics */ + + /* + * next two form basis for a mini-cache of LSM contexts + * turn it into a rbtree or hash + */ + spinlock_t secref_lock; + struct list_head secref_list; }; #endif /* __KERNEL__ */ diff --git a/include/linux/security.h b/include/linux/security.h index d5fd616..38f643c 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1092,6 +1092,16 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @msg_msg_free_security: * Deallocate the security structure for this message. * @msg contains the message structure to be modified. + * @msg_msg_getsecid: + * Get the secid associated with the msg_msg object. + * @msg contains the message object + * @secid contains a pointer to the location where result will be saved. + * In case of failure, @secid will be set to zero. + * @msg_msg_restore: + * Set security context on restored object + * @msg contains the newly created msg_msg object + * @secid contains the secid corresponding to the checkpointed context. + * Return 0 if a proper label was set and permission is granted. * * Security hooks for System V IPC Message Queues * @@ -1137,6 +1147,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @type contains the type of message requested. * @mode contains the operational flags. * Return 0 if permission is granted. + * @msg_queue_restore: + * Set permission on a newly restored msg_queue + * @msq contains the message queue being restored. + * @secid is the secid corresponding to the checkpointed context. + * Return 0 if an appropriate context was set and permission is granted. * * Security hooks for System V Shared Memory Segments * @@ -1172,6 +1187,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @shmaddr contains the address to attach memory region to. * @shmflg contains the operational flags. * Return 0 if permission is granted. + * @shm_restore: + * Set permission on a newly restored shm + * @shm contains the shm being restored. + * @secid is the secid corresponding to the checkpointed context. + * Return 0 if an appropriate context was set and permission is granted. * * Security hooks for System V Semaphores * @@ -1208,6 +1228,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @nsops contains the number of operations to perform. * @alter contains the flag indicating whether changes are to be made. * Return 0 if permission is granted. + * @sem_restore: + * Set permission on a newly restored sem + * @sem contains the sem being restored. + * @secid is the secid corresponding to the checkpointed context. + * Return 0 if an appropriate context was set and permission is granted. * * @ptrace_may_access: * Check permission before allowing the current process to trace the @@ -1499,6 +1524,8 @@ struct security_operations { int (*msg_msg_alloc_security) (struct msg_msg *msg); void (*msg_msg_free_security) (struct msg_msg *msg); + void (*msg_msg_getsecid) (struct msg_msg *msg, u32 *secid); + int (*msg_msg_restore) (struct msg_msg *msg, u32 secid); int (*msg_queue_alloc_security) (struct msg_queue *msq); void (*msg_queue_free_security) (struct msg_queue *msq); @@ -1510,6 +1537,7 @@ struct security_operations { struct msg_msg *msg, struct task_struct *target, long type, int mode); + int (*msg_queue_restore) (struct msg_queue *msq, u32 secid); int (*shm_alloc_security) (struct shmid_kernel *shp); void (*shm_free_security) (struct shmid_kernel *shp); @@ -1517,6 +1545,7 @@ struct security_operations { int (*shm_shmctl) (struct shmid_kernel *shp, int cmd); int (*shm_shmat) (struct shmid_kernel *shp, char __user *shmaddr, int shmflg); + int (*shm_restore) (struct shmid_kernel *shp, u32 secid); int (*sem_alloc_security) (struct sem_array *sma); void (*sem_free_security) (struct sem_array *sma); @@ -1524,6 +1553,7 @@ struct security_operations { int (*sem_semctl) (struct sem_array *sma, int cmd); int (*sem_semop) (struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter); + int (*sem_restore) (struct sem_array *sma, u32 secid); int (*netlink_send) (struct sock *sk, struct sk_buff *skb); int (*netlink_recv) (struct sk_buff *skb, int cap); @@ -1748,6 +1778,8 @@ int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag); void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid); int security_msg_msg_alloc(struct msg_msg *msg); void security_msg_msg_free(struct msg_msg *msg); +void security_msg_msg_getsecid(struct msg_msg *msg, u32 *secid); +int security_msg_msg_restore(struct msg_msg *msg, u32 secid); int security_msg_queue_alloc(struct msg_queue *msq); void security_msg_queue_free(struct msg_queue *msq); int security_msg_queue_associate(struct msg_queue *msq, int msqflg); @@ -1756,17 +1788,20 @@ int security_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg); int security_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, struct task_struct *target, long type, int mode); +int security_msg_queue_restore(struct msg_queue *msq, u32 secid); int security_shm_alloc(struct shmid_kernel *shp); void security_shm_free(struct shmid_kernel *shp); int security_shm_associate(struct shmid_kernel *shp, int shmflg); int security_shm_shmctl(struct shmid_kernel *shp, int cmd); int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg); +int security_shm_restore(struct shmid_kernel *shp, u32 secid); int security_sem_alloc(struct sem_array *sma); void security_sem_free(struct sem_array *sma); int security_sem_associate(struct sem_array *sma, int semflg); int security_sem_semctl(struct sem_array *sma, int cmd); int security_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter); +int security_sem_restore(struct sem_array *sma, u32 secid); void security_d_instantiate(struct dentry *dentry, struct inode *inode); int security_getprocattr(struct task_struct *p, char *name, char **value); int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); @@ -2393,6 +2428,18 @@ static inline int security_msg_msg_alloc(struct msg_msg *msg) return 0; } +static inline void security_msg_msg_getsecid(struct msg_msg *msg, u32 *secid) +{ + *secid = 0; +} + +static inline int security_msg_msg_restore(struct msg_msg *msg, u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + static inline void security_msg_msg_free(struct msg_msg *msg) { } @@ -2429,6 +2476,14 @@ static inline int security_msg_queue_msgrcv(struct msg_queue *msq, return 0; } +static inline int security_msg_queue_restore(struct msg_queue *msq, + u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + static inline int security_shm_alloc(struct shmid_kernel *shp) { return 0; @@ -2454,6 +2509,14 @@ static inline int security_shm_shmat(struct shmid_kernel *shp, return 0; } +static inline int security_shm_restore(struct shmid_kernel *shp, + u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + static inline int security_sem_alloc(struct sem_array *sma) { return 0; @@ -2479,6 +2542,14 @@ static inline int security_sem_semop(struct sem_array *sma, return 0; } +static inline int security_sem_restore(struct sem_array *sma, + u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode) { } @@ -2975,7 +3046,28 @@ static inline char *alloc_secdata(void) static inline void free_secdata(void *secdata) { } + +#endif /* CONFIG_SECURITY */ + +#ifdef CONFIG_CHECKPOINT +struct sec_store { + int secid; /* LSM secid */ + int secref; /* objhash reference */ + struct list_head list; +}; +#ifdef CONFIG_SECURITY +extern int checkpoint_security(struct ckpt_ctx *ctx, void *ptr); +extern void *restore_security(struct ckpt_ctx *ctx); +extern int checkpoint_security_getsecref(struct ckpt_ctx *ctx, int secid); +#else /* CONFIG_SECURITY */ +#define checkpoint_security checkpoint_bad +#define restore_security restore_bad +static inline int checkpoint_security_getsecref(struct ckpt_ctx *ctx, int secid) +{ + return 0; +} #endif /* CONFIG_SECURITY */ +#endif /* CONFIG_CHECKPOINT */ #endif /* ! __LINUX_SECURITY_H */ diff --git a/ipc/checkpoint.c b/ipc/checkpoint.c index 88996e2..b7641e2 100644 --- a/ipc/checkpoint.c +++ b/ipc/checkpoint.c @@ -15,6 +15,7 @@ #include <linux/msg.h> #include <linux/sched.h> #include <linux/ipc_namespace.h> +#include <linux/security.h> #include <linux/checkpoint.h> #include <linux/checkpoint_hdr.h> @@ -31,9 +32,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 secid; + if (ipcperms(perm, S_IROTH)) return -EACCES; @@ -46,6 +50,11 @@ int checkpoint_fill_ipc_perms(struct ckpt_hdr_ipc_perms *h, h->mode = perm->mode & S_IRWXUGO; h->seq = perm->seq; + security_ipc_getsecid(perm, &secid); + h->secref = checkpoint_security_getsecref(ctx, secid); + if (h->secref < 0) + return h->secref; + return 0; } @@ -185,13 +194,10 @@ int restore_load_ipc_perms(struct ckpt_hdr_ipc_perms *h, 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. + * The checkpointed ->security value will be restored + * (and verified) by our caller. */ + perm->security = NULL; return 0; } diff --git a/ipc/checkpoint_msg.c b/ipc/checkpoint_msg.c index 51385b0..ca55339 100644 --- a/ipc/checkpoint_msg.c +++ b/ipc/checkpoint_msg.c @@ -17,6 +17,7 @@ #include <linux/sched.h> #include <linux/syscalls.h> #include <linux/nsproxy.h> +#include <linux/security.h> #include <linux/ipc_namespace.h> #include "util.h" @@ -36,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; @@ -62,12 +63,19 @@ 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 ret, secid, secref; + + security_msg_msg_getsecid(msg, &secid); + secref = checkpoint_security_getsecref(ctx, secid); + if (secref < 0) + return secref; h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_IPC_MSG_MSG); if (!h) return -ENOMEM; + h->secref = secref; + h->m_type = msg->m_type; h->m_ts = msg->m_ts; @@ -175,11 +183,26 @@ static int load_ipc_msg_hdr(struct ckpt_ctx *ctx, struct msg_queue *msq) { int ret = 0; + int secid = 0; ret = restore_load_ipc_perms(&h->perms, &msq->q_perm); if (ret < 0) return ret; + if (h->perms.secref) { + struct sec_store *s; + s = ckpt_obj_fetch(ctx, h->perms.secref, CKPT_OBJ_SECURITY); + if (IS_ERR(s)) + return PTR_ERR(s); + secid = s->secid; + } + ret = security_msg_queue_alloc(msq); + if (ret) + return ret; + ret = security_msg_queue_restore(msq, secid); + if (ret < 0) + return ret; + ckpt_debug("msq: lspid %d lrpid %d qnum %lld qbytes %lld\n", h->q_lspid, h->q_lrpid, h->q_qnum, h->q_qbytes); @@ -201,7 +224,7 @@ static struct msg_msg *restore_msg_contents_one(struct ckpt_ctx *ctx, int *clen) struct msg_msg *msg = NULL; struct msg_msgseg *seg, **pseg; int total, len; - int ret; + int ret, secid = 0; h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_IPC_MSG_MSG); if (IS_ERR(h)) @@ -245,6 +268,22 @@ static struct msg_msg *restore_msg_contents_one(struct ckpt_ctx *ctx, int *clen) total -= len; } + if (h->secref) { + struct sec_store *s; + s = ckpt_obj_fetch(ctx, h->secref, CKPT_OBJ_SECURITY); + if (IS_ERR(s)) { + ret = PTR_ERR(s); + goto out; + } + secid = s->secid; + } + ret = security_msg_msg_alloc(msg); + if (ret) + goto out; + ret = security_msg_msg_restore(msg, secid); + if (ret < 0) + goto out; + msg->m_type = h->m_type; msg->m_ts = h->m_ts; *clen = h->m_ts; diff --git a/ipc/checkpoint_sem.c b/ipc/checkpoint_sem.c index 64d61ee..609750b 100644 --- a/ipc/checkpoint_sem.c +++ b/ipc/checkpoint_sem.c @@ -17,6 +17,7 @@ #include <linux/sched.h> #include <linux/syscalls.h> #include <linux/nsproxy.h> +#include <linux/security.h> #include <linux/ipc_namespace.h> struct msg_msg; @@ -37,7 +38,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; @@ -112,11 +113,26 @@ static int load_ipc_sem_hdr(struct ckpt_ctx *ctx, struct sem_array *sem) { int ret = 0; + int secid = 0; ret = restore_load_ipc_perms(&h->perms, &sem->sem_perm); if (ret < 0) return ret; + if (h->perms.secref) { + struct sec_store *s; + s = ckpt_obj_fetch(ctx, h->perms.secref, CKPT_OBJ_SECURITY); + if (IS_ERR(s)) + return PTR_ERR(s); + secid = s->secid; + } + ret = security_sem_alloc(sem); + if (ret) + return ret; + ret = security_sem_restore(sem, secid); + if (ret < 0) + return ret; + ckpt_debug("sem: nsems %u\n", h->sem_nsems); sem->sem_otime = h->sem_otime; diff --git a/ipc/checkpoint_shm.c b/ipc/checkpoint_shm.c index 41bacfb..a2a0e41 100644 --- a/ipc/checkpoint_shm.c +++ b/ipc/checkpoint_shm.c @@ -21,6 +21,7 @@ #include <linux/syscalls.h> #include <linux/nsproxy.h> #include <linux/ipc_namespace.h> +#include <linux/security.h> #include <linux/deferqueue.h> #include <linux/msg.h> /* needed for util.h that uses 'struct msg_msg' */ @@ -41,7 +42,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; @@ -148,11 +149,26 @@ static int load_ipc_shm_hdr(struct ckpt_ctx *ctx, struct shmid_kernel *shp) { int ret; + int secid = 0; ret = restore_load_ipc_perms(&h->perms, &shp->shm_perm); if (ret < 0) return ret; + if (h->perms.secref) { + struct sec_store *s; + s = ckpt_obj_fetch(ctx, h->perms.secref, CKPT_OBJ_SECURITY); + if (IS_ERR(s)) + return PTR_ERR(s); + secid = s->secid; + } + ret = security_shm_alloc(shp); + if (ret) + return ret; + ret = security_shm_restore(shp, secid); + if (ret < 0) + return ret; + ckpt_debug("shm: cprid %d lprid %d segsz %lld mlock %d\n", h->shm_cprid, h->shm_lprid, h->shm_segsz, h->mlock_uid); diff --git a/ipc/util.h b/ipc/util.h index fa974eb..bed25bd 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -199,7 +199,8 @@ extern void freeary(struct ipc_namespace *, struct kern_ipc_perm *); extern void do_shm_rmid(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, struct kern_ipc_perm *perm); diff --git a/security/capability.c b/security/capability.c index 21b6cea..d0d2e39 100644 --- a/security/capability.c +++ b/security/capability.c @@ -494,6 +494,18 @@ static void cap_msg_msg_free_security(struct msg_msg *msg) { } +static void cap_msg_msg_getsecid(struct msg_msg *msg, u32 *secid) +{ + *secid = 0; +} + +static int cap_msg_msg_restore(struct msg_msg *msg, u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + static int cap_msg_queue_alloc_security(struct msg_queue *msq) { return 0; @@ -525,6 +537,13 @@ static int cap_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, return 0; } +static int cap_msg_queue_restore(struct msg_queue *msq, u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + static int cap_shm_alloc_security(struct shmid_kernel *shp) { return 0; @@ -550,6 +569,13 @@ static int cap_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, return 0; } +static int cap_shm_restore(struct shmid_kernel *shp, u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + static int cap_sem_alloc_security(struct sem_array *sma) { return 0; @@ -575,6 +601,13 @@ static int cap_sem_semop(struct sem_array *sma, struct sembuf *sops, return 0; } +static int cap_sem_restore(struct sem_array *sma, u32 secid) +{ + if (secid) + return -EINVAL; + return 0; +} + #ifdef CONFIG_SECURITY_NETWORK static int cap_unix_stream_connect(struct socket *sock, struct socket *other, struct sock *newsk) @@ -977,22 +1010,27 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, ipc_getsecid); set_to_cap_if_null(ops, msg_msg_alloc_security); set_to_cap_if_null(ops, msg_msg_free_security); + set_to_cap_if_null(ops, msg_msg_getsecid); + set_to_cap_if_null(ops, msg_msg_restore); set_to_cap_if_null(ops, msg_queue_alloc_security); set_to_cap_if_null(ops, msg_queue_free_security); set_to_cap_if_null(ops, msg_queue_associate); set_to_cap_if_null(ops, msg_queue_msgctl); set_to_cap_if_null(ops, msg_queue_msgsnd); set_to_cap_if_null(ops, msg_queue_msgrcv); + set_to_cap_if_null(ops, msg_queue_restore); set_to_cap_if_null(ops, shm_alloc_security); set_to_cap_if_null(ops, shm_free_security); set_to_cap_if_null(ops, shm_associate); set_to_cap_if_null(ops, shm_shmctl); set_to_cap_if_null(ops, shm_shmat); + set_to_cap_if_null(ops, shm_restore); set_to_cap_if_null(ops, sem_alloc_security); set_to_cap_if_null(ops, sem_free_security); set_to_cap_if_null(ops, sem_associate); set_to_cap_if_null(ops, sem_semctl); set_to_cap_if_null(ops, sem_semop); + set_to_cap_if_null(ops, sem_restore); set_to_cap_if_null(ops, netlink_send); set_to_cap_if_null(ops, netlink_recv); set_to_cap_if_null(ops, d_instantiate); diff --git a/security/security.c b/security/security.c index 5284255..d737e0e 100644 --- a/security/security.c +++ b/security/security.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/security.h> +#include <linux/checkpoint.h> /* Boot-time LSM user choice */ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1]; @@ -832,6 +833,16 @@ void security_msg_msg_free(struct msg_msg *msg) security_ops->msg_msg_free_security(msg); } +void security_msg_msg_getsecid(struct msg_msg *msg, u32 *secid) +{ + security_ops->msg_msg_getsecid(msg, secid); +} + +int security_msg_msg_restore(struct msg_msg *msg, u32 secid) +{ + return security_ops->msg_msg_restore(msg, secid); +} + int security_msg_queue_alloc(struct msg_queue *msq) { return security_ops->msg_queue_alloc_security(msq); @@ -864,6 +875,11 @@ int security_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, return security_ops->msg_queue_msgrcv(msq, msg, target, type, mode); } +int security_msg_queue_restore(struct msg_queue *msq, u32 secid) +{ + return security_ops->msg_queue_restore(msq, secid); +} + int security_shm_alloc(struct shmid_kernel *shp) { return security_ops->shm_alloc_security(shp); @@ -889,6 +905,11 @@ int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmfl return security_ops->shm_shmat(shp, shmaddr, shmflg); } +int security_shm_restore(struct shmid_kernel *shp, u32 secid) +{ + return security_ops->shm_restore(shp, secid); +} + int security_sem_alloc(struct sem_array *sma) { return security_ops->sem_alloc_security(sma); @@ -915,6 +936,11 @@ int security_sem_semop(struct sem_array *sma, struct sembuf *sops, return security_ops->sem_semop(sma, sops, nsops, alter); } +int security_sem_restore(struct sem_array *sma, u32 secid) +{ + return security_ops->sem_restore(sma, secid); +} + void security_d_instantiate(struct dentry *dentry, struct inode *inode) { if (unlikely(inode && IS_PRIVATE(inode))) @@ -1247,3 +1273,146 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, } #endif /* CONFIG_AUDIT */ + +#ifdef CONFIG_CHECKPOINT + +static int do_checkpoint_security(struct ckpt_ctx *ctx, + struct sec_store *s) +{ + char *str; + int ret, len; + + ret = security_secid_to_secctx(s->secid, &str, &len); + if (ret) + return ret; + ret = ckpt_write_obj_type(ctx, str, len, CKPT_HDR_SECURITY); + security_release_secctx(str, len); + return ret; +} + +int checkpoint_security(struct ckpt_ctx *ctx, void *ptr) +{ + return do_checkpoint_security(ctx, (struct sec_store *) ptr); +} + +/* + * stupid ordered list insertion Replace with rbtree or hash + * return 1 if the secid was already stored + * call with ctx->secref_lock held + */ +static int insert_secref(struct ckpt_ctx *ctx, struct sec_store *news) +{ + struct sec_store *last=NULL, *s; + + if (list_empty(&ctx->secref_list)) { + list_add(&news->list, &ctx->secref_list); + return 0; + } + + list_for_each_entry(s, &ctx->secref_list, list) { + if (s->secid == news->secid) + /* race */ + return 1; + else if (s->secid < news->secid) + last = s; + else + break; + } + /* + * if last is not null, insert after last. Otherwise + * this should be the first item + */ + if (last) + list_add(&news->list, &last->list); + else + list_add(&news->list, &ctx->secref_list); + return 0; +} + +/* just need some sane value */ +#define MAX_STR_LEN 300 +static struct sec_store *do_restore_security(struct ckpt_ctx *ctx) +{ + struct ckpt_hdr_sec *h; + int id; + int ret, conflict; + struct sec_store *s; + + h = ckpt_read_buf_type(ctx, MAX_STR_LEN, CKPT_HDR_SECURITY); + if (IS_ERR(h)) + return ERR_PTR(PTR_ERR(h)); + ret = security_secctx_to_secid(h->str, strlen(h->str)+1, &id); + ckpt_hdr_put(ctx, h); + if (ret < 0) + return ERR_PTR(ret); + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return ERR_PTR(-ENOMEM); + s->secid = id; + spin_lock(&ctx->secref_lock); + conflict = insert_secref(ctx, s); + spin_unlock(&ctx->secref_lock); + if (conflict) /* not possible */ + return ERR_PTR(-EINVAL); + return s; +} + +void *restore_security(struct ckpt_ctx *ctx) +{ + return (void *) do_restore_security(ctx); +} + +/* + * if there's already a cached entry with secid, return + * its secref. Return -1 if there is no such entry. If + * LSMs are actually not supported, then a value of 0 may + * in fact be returned as the secref + */ +static int get_secref_from_secid(struct ckpt_ctx *ctx, int secid) +{ + int ret = -1; + struct sec_store *s; + + spin_lock(&ctx->secref_lock); + list_for_each_entry(s, &ctx->secref_list, list) + if (s->secid == secid) { + ret = s->secref; + break; + } + spin_unlock(&ctx->secref_lock); + return ret; +} + +/* + * get a objhash reference from a secid + * if already stored, return existing ref + * else checkpoint it and return the new ref + */ +int checkpoint_security_getsecref(struct ckpt_ctx *ctx, int secid) +{ + int ret, conflict=0, secref = 0; + struct sec_store *s; + +again: + ret = get_secref_from_secid(ctx, secid); + if (ret >= 0) + return ret; + BUG_ON(conflict!=0); /* a racer added it to list, but now it's gone? */ + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + s->secid = secid; + spin_lock(&ctx->secref_lock); + conflict = insert_secref(ctx, s); + if (conflict) { + spin_unlock(&ctx->secref_lock); + kfree(s); + goto again; + } + if (secid != 0) + secref = checkpoint_obj(ctx, s, CKPT_OBJ_SECURITY); + s->secref = secref; + spin_unlock(&ctx->secref_lock); + return secref; +} +#endif diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2fcad7c..591f545 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4675,6 +4675,19 @@ static void msg_msg_free_security(struct msg_msg *msg) kfree(msec); } +static int selinux_msg_msg_restore(struct msg_msg *msg, u32 secid) +{ + struct msg_security_struct *msec = msg->security; + u32 sid = current_sid(); + int ret; + + ret = avc_has_perm(sid, secid, SECCLASS_MSG, MSG__RESTORE, NULL); + if (ret) + return ret; + msec->sid = secid; + return 0; +} + static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, u32 perms) { @@ -4700,6 +4713,12 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg) msg_msg_free_security(msg); } +static void selinux_msg_msg_getsecid(struct msg_msg *msg, u32 *secid) +{ + struct msg_security_struct *msec = msg->security; + *secid = msec->sid; +} + /* message queue security operations */ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) { @@ -4841,6 +4860,14 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, return rc; } +static int selinux_msg_queue_restore(struct msg_queue *msq, u32 secid) +{ + struct ipc_security_struct *isec = msq->q_perm.security; + + isec->sid = secid; + return ipc_has_perm(&msq->q_perm, MSGQ__RESTORE); +} + /* Shared Memory security operations */ static int selinux_shm_alloc_security(struct shmid_kernel *shp) { @@ -4933,6 +4960,14 @@ static int selinux_shm_shmat(struct shmid_kernel *shp, return ipc_has_perm(&shp->shm_perm, perms); } +static int selinux_shm_restore(struct shmid_kernel *shp, u32 secid) +{ + struct ipc_security_struct *isec = shp->shm_perm.security; + + isec->sid = secid; + return ipc_has_perm(&shp->shm_perm, SHM__RESTORE); +} + /* Semaphore security operations */ static int selinux_sem_alloc_security(struct sem_array *sma) { @@ -5034,6 +5069,14 @@ static int selinux_sem_semop(struct sem_array *sma, return ipc_has_perm(&sma->sem_perm, perms); } +static int selinux_sem_restore(struct sem_array *sma, u32 secid) +{ + struct ipc_security_struct *isec = sma->sem_perm.security; + + isec->sid = secid; + return ipc_has_perm(&sma->sem_perm, SEM__RESTORE); +} + static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) { u32 av = 0; @@ -5415,6 +5458,8 @@ static struct security_operations selinux_ops = { .msg_msg_alloc_security = selinux_msg_msg_alloc_security, .msg_msg_free_security = selinux_msg_msg_free_security, + .msg_msg_getsecid = selinux_msg_msg_getsecid, + .msg_msg_restore = selinux_msg_msg_restore, .msg_queue_alloc_security = selinux_msg_queue_alloc_security, .msg_queue_free_security = selinux_msg_queue_free_security, @@ -5422,18 +5467,21 @@ static struct security_operations selinux_ops = { .msg_queue_msgctl = selinux_msg_queue_msgctl, .msg_queue_msgsnd = selinux_msg_queue_msgsnd, .msg_queue_msgrcv = selinux_msg_queue_msgrcv, + .msg_queue_restore = selinux_msg_queue_restore, .shm_alloc_security = selinux_shm_alloc_security, .shm_free_security = selinux_shm_free_security, .shm_associate = selinux_shm_associate, .shm_shmctl = selinux_shm_shmctl, .shm_shmat = selinux_shm_shmat, + .shm_restore = selinux_shm_restore, .sem_alloc_security = selinux_sem_alloc_security, .sem_free_security = selinux_sem_free_security, .sem_associate = selinux_sem_associate, .sem_semctl = selinux_sem_semctl, .sem_semop = selinux_sem_semop, + .sem_restore = selinux_sem_restore, .d_instantiate = selinux_d_instantiate, diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h index 8377a4b..0d7689d 100644 --- a/security/selinux/include/av_inherit.h +++ b/security/selinux/include/av_inherit.h @@ -15,10 +15,10 @@ S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL) S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL) S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL) - S_(SECCLASS_IPC, ipc, 0x00000200UL) - S_(SECCLASS_SEM, ipc, 0x00000200UL) - S_(SECCLASS_MSGQ, ipc, 0x00000200UL) - S_(SECCLASS_SHM, ipc, 0x00000200UL) + S_(SECCLASS_IPC, ipc, 0x00000400UL) + S_(SECCLASS_SEM, ipc, 0x00000400UL) + S_(SECCLASS_MSGQ, ipc, 0x00000400UL) + S_(SECCLASS_SHM, ipc, 0x00000400UL) S_(SECCLASS_NETLINK_ROUTE_SOCKET, socket, 0x00400000UL) S_(SECCLASS_NETLINK_FIREWALL_SOCKET, socket, 0x00400000UL) S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, socket, 0x00400000UL) diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index d645192..3c3eba6 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -47,6 +47,7 @@ #define COMMON_IPC__ASSOCIATE 0x00000040UL #define COMMON_IPC__UNIX_READ 0x00000080UL #define COMMON_IPC__UNIX_WRITE 0x00000100UL +#define COMMON_IPC__RESTORE 0x00000200UL #define FILESYSTEM__MOUNT 0x00000001UL #define FILESYSTEM__REMOUNT 0x00000002UL #define FILESYSTEM__UNMOUNT 0x00000004UL @@ -462,6 +463,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 @@ -471,6 +473,7 @@ #define SEM__ASSOCIATE 0x00000040UL #define SEM__UNIX_READ 0x00000080UL #define SEM__UNIX_WRITE 0x00000100UL +#define SEM__RESTORE 0x00000200UL #define MSGQ__CREATE 0x00000001UL #define MSGQ__DESTROY 0x00000002UL #define MSGQ__GETATTR 0x00000004UL @@ -480,9 +483,11 @@ #define MSGQ__ASSOCIATE 0x00000040UL #define MSGQ__UNIX_READ 0x00000080UL #define MSGQ__UNIX_WRITE 0x00000100UL -#define MSGQ__ENQUEUE 0x00000200UL +#define MSGQ__RESTORE 0x00000200UL +#define MSGQ__ENQUEUE 0x00000400UL #define MSG__SEND 0x00000001UL #define MSG__RECEIVE 0x00000002UL +#define MSG__RESTORE 0x00000004UL #define SHM__CREATE 0x00000001UL #define SHM__DESTROY 0x00000002UL #define SHM__GETATTR 0x00000004UL @@ -492,7 +497,8 @@ #define SHM__ASSOCIATE 0x00000040UL #define SHM__UNIX_READ 0x00000080UL #define SHM__UNIX_WRITE 0x00000100UL -#define SHM__LOCK 0x00000200UL +#define SHM__RESTORE 0x00000200UL +#define SHM__LOCK 0x00000400UL #define SECURITY__COMPUTE_AV 0x00000001UL #define SECURITY__COMPUTE_CREATE 0x00000002UL #define SECURITY__COMPUTE_MEMBER 0x00000004UL diff --git a/security/selinux/include/common_perm_to_string.h b/security/selinux/include/common_perm_to_string.h index ce5b6e2..fb94053 100644 --- a/security/selinux/include/common_perm_to_string.h +++ b/security/selinux/include/common_perm_to_string.h @@ -54,5 +54,6 @@ TB_(common_ipc_perm_to_string) S_("associate") S_("unix_read") S_("unix_write") + S_("restore") TE_(common_ipc_perm_to_string) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 98b3195..070aeb7 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1619,6 +1619,33 @@ static void smack_msg_msg_free_security(struct msg_msg *msg) } /** + * smack_msg_msg_getsecid - Extract smack security id + * @msg: the object + * @secid: where result will be saved + */ +static void smack_msg_msg_getsecid(struct msg_msg *msg, u32 *secid) +{ + char *smack = msg->security; + + *secid = smack_to_secid(smack); +} + +static int smack_msg_msg_restore(struct msg_msg *msg, u32 secid) +{ + char *smack = smack_from_secid(secid); + + if (smack == NULL) + return -EINVAL; + if (current_security() != smack) { + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + msg->security = smack; + } else + msg->security = current_security(); + return 0; +} + +/** * smack_of_shm - the smack pointer for the shm * @shp: the object * @@ -1727,6 +1754,21 @@ static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, return smk_curacc(ssp, may); } +static int smack_shm_restore(struct shmid_kernel *shp, u32 secid) +{ + char *smack = smack_from_secid(secid); + + if (smack == NULL) + return -EINVAL; + if (current_security() != smack) { + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + shp->shm_perm.security = smack; + } else + shp->shm_perm.security = current_security(); + return 0; +} + /** * smack_of_sem - the smack pointer for the sem * @sma: the object @@ -1842,6 +1884,21 @@ static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops, return smk_curacc(ssp, MAY_READWRITE); } +static int smack_sem_restore(struct sem_array *sma, u32 secid) +{ + char *smack = smack_from_secid(secid); + + if (smack == NULL) + return -EINVAL; + if (current_security() != smack) { + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + sma->sem_perm.security = smack; + } else + sma->sem_perm.security = current_security(); + return 0; +} + /** * smack_msg_alloc_security - Set the security blob for msg * @msq: the object @@ -1967,6 +2024,21 @@ static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, return smk_curacc(msp, MAY_READWRITE); } +static int smack_msg_queue_restore(struct msg_queue *msq, u32 secid) +{ + char *smack = smack_from_secid(secid); + + if (smack == NULL) + return -EINVAL; + if (current_security() != smack) { + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + msq->q_perm.security = smack; + } else + msq->q_perm.security = current_security(); + return 0; +} + /** * smack_ipc_permission - Smack access for ipc_permission() * @ipp: the object permissions @@ -2903,6 +2975,8 @@ struct security_operations smack_ops = { .msg_msg_alloc_security = smack_msg_msg_alloc_security, .msg_msg_free_security = smack_msg_msg_free_security, + .msg_msg_getsecid = smack_msg_msg_getsecid, + .msg_msg_restore = smack_msg_msg_restore, .msg_queue_alloc_security = smack_msg_queue_alloc_security, .msg_queue_free_security = smack_msg_queue_free_security, @@ -2910,18 +2984,21 @@ struct security_operations smack_ops = { .msg_queue_msgctl = smack_msg_queue_msgctl, .msg_queue_msgsnd = smack_msg_queue_msgsnd, .msg_queue_msgrcv = smack_msg_queue_msgrcv, + .msg_queue_restore = smack_msg_queue_restore, .shm_alloc_security = smack_shm_alloc_security, .shm_free_security = smack_shm_free_security, .shm_associate = smack_shm_associate, .shm_shmctl = smack_shm_shmctl, .shm_shmat = smack_shm_shmat, + .shm_restore = smack_shm_restore, .sem_alloc_security = smack_sem_alloc_security, .sem_free_security = smack_sem_free_security, .sem_associate = smack_sem_associate, .sem_semctl = smack_sem_semctl, .sem_semop = smack_sem_semop, + .sem_restore = smack_sem_restore, .netlink_send = cap_netlink_send, .netlink_recv = cap_netlink_recv, -- 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.