From fed306f2baa170220b0299198a39c6be2a91bf19 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 21 Apr 2006 13:16:49 +0100 Subject: [RBTREE] Update key.c to use rb_parent() accessor macro. Signed-off-by: David Woodhouse --- security/keys/key.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'security/keys') diff --git a/security/keys/key.c b/security/keys/key.c index b6061fa29da..3fdc49c6a02 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -211,12 +211,12 @@ static inline void key_alloc_serial(struct key *key) key->serial = 2; key_serial_next = key->serial + 1; - if (!parent->rb_parent) + if (!rb_parent(parent)) p = &key_serial_tree.rb_node; - else if (parent->rb_parent->rb_left == parent) - p = &parent->rb_parent->rb_left; + else if (rb_parent(parent)->rb_left == parent) + p = &(rb_parent(parent)->rb_left); else - p = &parent->rb_parent->rb_right; + p = &(rb_parent(parent)->rb_right); parent = rb_next(parent); if (!parent) -- cgit v1.2.3-70-g09d2 From d720024e94de4e8b7f10ee83c532926f3ad5d708 Mon Sep 17 00:00:00 2001 From: Michael LeMay Date: Thu, 22 Jun 2006 14:47:17 -0700 Subject: [PATCH] selinux: add hooks for key subsystem Introduce SELinux hooks to support the access key retention subsystem within the kernel. Incorporate new flask headers from a modified version of the SELinux reference policy, with support for the new security class representing retained keys. Extend the "key_alloc" security hook with a task parameter representing the intended ownership context for the key being allocated. Attach security information to root's default keyrings within the SELinux initialization routine. Has passed David's testsuite. Signed-off-by: Michael LeMay Signed-off-by: David Howells Signed-off-by: James Morris Acked-by: Chris Wright Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/keys.txt | 29 +++++++++++++ include/linux/key.h | 18 +++++--- include/linux/security.h | 10 +++-- kernel/user.c | 2 +- security/dummy.c | 2 +- security/keys/key.c | 8 ++-- security/keys/keyring.c | 5 ++- security/keys/process_keys.c | 15 ++++--- security/keys/request_key.c | 6 ++- security/keys/request_key_auth.c | 2 +- security/selinux/hooks.c | 64 ++++++++++++++++++++++++++++ security/selinux/include/av_perm_to_string.h | 6 +++ security/selinux/include/av_permissions.h | 8 ++++ security/selinux/include/class_to_string.h | 1 + security/selinux/include/flask.h | 1 + security/selinux/include/objsec.h | 5 +++ 16 files changed, 155 insertions(+), 27 deletions(-) (limited to 'security/keys') diff --git a/Documentation/keys.txt b/Documentation/keys.txt index aaa01b0e3ee..70302001270 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -19,6 +19,7 @@ This document has the following sections: - Key overview - Key service overview - Key access permissions + - SELinux support - New procfs files - Userspace system call interface - Kernel services @@ -232,6 +233,34 @@ For changing the ownership, group ID or permissions mask, being the owner of the key or having the sysadmin capability is sufficient. +=============== +SELINUX SUPPORT +=============== + +The security class "key" has been added to SELinux so that mandatory access +controls can be applied to keys created within various contexts. This support +is preliminary, and is likely to change quite significantly in the near future. +Currently, all of the basic permissions explained above are provided in SELinux +as well; SE Linux is simply invoked after all basic permission checks have been +performed. + +Each key is labeled with the same context as the task to which it belongs. +Typically, this is the same task that was running when the key was created. +The default keyrings are handled differently, but in a way that is very +intuitive: + + (*) The user and user session keyrings that are created when the user logs in + are currently labeled with the context of the login manager. + + (*) The keyrings associated with new threads are each labeled with the context + of their associated thread, and both session and process keyrings are + handled similarly. + +Note, however, that the default keyrings associated with the root user are +labeled with the default kernel context, since they are created early in the +boot process, before root has a chance to log in. + + ================ NEW PROCFS FILES ================ diff --git a/include/linux/key.h b/include/linux/key.h index cbf464ad958..8c275d12ef6 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -241,8 +241,9 @@ extern void unregister_key_type(struct key_type *ktype); extern struct key *key_alloc(struct key_type *type, const char *desc, - uid_t uid, gid_t gid, key_perm_t perm, - int not_in_quota); + uid_t uid, gid_t gid, + struct task_struct *ctx, + key_perm_t perm, int not_in_quota); extern int key_payload_reserve(struct key *key, size_t datalen); extern int key_instantiate_and_link(struct key *key, const void *data, @@ -292,7 +293,9 @@ extern int key_unlink(struct key *keyring, struct key *key); extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, - int not_in_quota, struct key *dest); + struct task_struct *ctx, + int not_in_quota, + struct key *dest); extern int keyring_clear(struct key *keyring); @@ -313,7 +316,8 @@ extern void keyring_replace_payload(struct key *key, void *replacement); * the userspace interface */ extern struct key root_user_keyring, root_session_keyring; -extern int alloc_uid_keyring(struct user_struct *user); +extern int alloc_uid_keyring(struct user_struct *user, + struct task_struct *ctx); extern void switch_uid_keyring(struct user_struct *new_user); extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk); extern int copy_thread_group_keys(struct task_struct *tsk); @@ -342,7 +346,7 @@ extern void key_init(void); #define make_key_ref(k) ({ NULL; }) #define key_ref_to_ptr(k) ({ NULL; }) #define is_key_possessed(k) 0 -#define alloc_uid_keyring(u) 0 +#define alloc_uid_keyring(u,c) 0 #define switch_uid_keyring(u) do { } while(0) #define __install_session_keyring(t, k) ({ NULL; }) #define copy_keys(f,t) 0 @@ -355,6 +359,10 @@ extern void key_init(void); #define key_fsgid_changed(t) do { } while(0) #define key_init() do { } while(0) +/* Initial keyrings */ +extern struct key root_user_keyring; +extern struct key root_session_keyring; + #endif /* CONFIG_KEYS */ #endif /* __KERNEL__ */ #endif /* _LINUX_KEY_H */ diff --git a/include/linux/security.h b/include/linux/security.h index 4dfb1b84a9b..47722d35553 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1313,7 +1313,7 @@ struct security_operations { /* key management security hooks */ #ifdef CONFIG_KEYS - int (*key_alloc)(struct key *key); + int (*key_alloc)(struct key *key, struct task_struct *tsk); void (*key_free)(struct key *key); int (*key_permission)(key_ref_t key_ref, struct task_struct *context, @@ -3008,9 +3008,10 @@ static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid #ifdef CONFIG_KEYS #ifdef CONFIG_SECURITY -static inline int security_key_alloc(struct key *key) +static inline int security_key_alloc(struct key *key, + struct task_struct *tsk) { - return security_ops->key_alloc(key); + return security_ops->key_alloc(key, tsk); } static inline void security_key_free(struct key *key) @@ -3027,7 +3028,8 @@ static inline int security_key_permission(key_ref_t key_ref, #else -static inline int security_key_alloc(struct key *key) +static inline int security_key_alloc(struct key *key, + struct task_struct *tsk) { return 0; } diff --git a/kernel/user.c b/kernel/user.c index 4b1eb745afa..6408c042429 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -148,7 +148,7 @@ struct user_struct * alloc_uid(uid_t uid) new->mq_bytes = 0; new->locked_shm = 0; - if (alloc_uid_keyring(new) < 0) { + if (alloc_uid_keyring(new, current) < 0) { kmem_cache_free(uid_cachep, new); return NULL; } diff --git a/security/dummy.c b/security/dummy.c index 64f6da0f422..6de4a4a5eb1 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -860,7 +860,7 @@ static int dummy_setprocattr(struct task_struct *p, char *name, void *value, siz } #ifdef CONFIG_KEYS -static inline int dummy_key_alloc(struct key *key) +static inline int dummy_key_alloc(struct key *key, struct task_struct *ctx) { return 0; } diff --git a/security/keys/key.c b/security/keys/key.c index 3fdc49c6a02..14a15abb773 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -247,8 +247,8 @@ static inline void key_alloc_serial(struct key *key) * instantiate the key or discard it before returning */ struct key *key_alloc(struct key_type *type, const char *desc, - uid_t uid, gid_t gid, key_perm_t perm, - int not_in_quota) + uid_t uid, gid_t gid, struct task_struct *ctx, + key_perm_t perm, int not_in_quota) { struct key_user *user = NULL; struct key *key; @@ -318,7 +318,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, #endif /* let the security module know about the key */ - ret = security_key_alloc(key); + ret = security_key_alloc(key, ctx); if (ret < 0) goto security_error; @@ -822,7 +822,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* allocate a new key */ key = key_alloc(ktype, description, current->fsuid, current->fsgid, - perm, not_in_quota); + current, perm, not_in_quota); if (IS_ERR(key)) { key_ref = ERR_PTR(PTR_ERR(key)); goto error_3; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index bffa924c1f8..1357207fc9d 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -240,13 +240,14 @@ static long keyring_read(const struct key *keyring, * allocate a keyring and link into the destination keyring */ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, - int not_in_quota, struct key *dest) + struct task_struct *ctx, int not_in_quota, + struct key *dest) { struct key *keyring; int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, + uid, gid, ctx, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, not_in_quota); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 217a0bef3c8..a50a91332fe 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -67,7 +67,8 @@ struct key root_session_keyring = { /* * allocate the keyrings to be associated with a UID */ -int alloc_uid_keyring(struct user_struct *user) +int alloc_uid_keyring(struct user_struct *user, + struct task_struct *ctx) { struct key *uid_keyring, *session_keyring; char buf[20]; @@ -76,7 +77,7 @@ int alloc_uid_keyring(struct user_struct *user) /* concoct a default session keyring */ sprintf(buf, "_uid_ses.%u", user->uid); - session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL); + session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx, 0, NULL); if (IS_ERR(session_keyring)) { ret = PTR_ERR(session_keyring); goto error; @@ -86,7 +87,7 @@ int alloc_uid_keyring(struct user_struct *user) * keyring */ sprintf(buf, "_uid.%u", user->uid); - uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, + uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx, 0, session_keyring); if (IS_ERR(uid_keyring)) { key_put(session_keyring); @@ -143,7 +144,7 @@ int install_thread_keyring(struct task_struct *tsk) sprintf(buf, "_tid.%u", tsk->pid); - keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, 1, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; @@ -177,7 +178,7 @@ int install_process_keyring(struct task_struct *tsk) if (!tsk->signal->process_keyring) { sprintf(buf, "_pid.%u", tsk->tgid); - keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, 1, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; @@ -217,7 +218,7 @@ static int install_session_keyring(struct task_struct *tsk, if (!keyring) { sprintf(buf, "_ses.%u", tsk->tgid); - keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, 1, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); } @@ -717,7 +718,7 @@ long join_session_keyring(const char *name) keyring = find_keyring_by_name(name, 0); if (PTR_ERR(keyring) == -ENOKEY) { /* not found - try and create a new one */ - keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL); + keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk, 0, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index f030a0ccbb9..eab66a06ca5 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -48,7 +48,8 @@ static int call_sbin_request_key(struct key *key, /* allocate a new session keyring */ sprintf(desc, "_req.%u", key->serial); - keyring = keyring_alloc(desc, current->fsuid, current->fsgid, 1, NULL); + keyring = keyring_alloc(desc, current->fsuid, current->fsgid, + current, 1, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error_alloc; @@ -137,7 +138,8 @@ static struct key *__request_key_construction(struct key_type *type, /* create a key and add it to the queue */ key = key_alloc(type, description, - current->fsuid, current->fsgid, KEY_POS_ALL, 0); + current->fsuid, current->fsgid, + current, KEY_POS_ALL, 0); if (IS_ERR(key)) goto alloc_failed; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index cce6ba6b032..0ecc2e8d2bd 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -148,7 +148,7 @@ struct key *request_key_auth_new(struct key *target, const char *callout_info) sprintf(desc, "%x", target->serial); authkey = key_alloc(&key_type_request_key_auth, desc, - current->fsuid, current->fsgid, + current->fsuid, current->fsgid, current, KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_USR_VIEW, 1); if (IS_ERR(authkey)) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 54adc9d31e9..524915dfda6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4252,6 +4252,57 @@ static int selinux_setprocattr(struct task_struct *p, return size; } +#ifdef CONFIG_KEYS + +static int selinux_key_alloc(struct key *k, struct task_struct *tsk) +{ + struct task_security_struct *tsec = tsk->security; + struct key_security_struct *ksec; + + ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL); + if (!ksec) + return -ENOMEM; + + ksec->obj = k; + ksec->sid = tsec->sid; + k->security = ksec; + + return 0; +} + +static void selinux_key_free(struct key *k) +{ + struct key_security_struct *ksec = k->security; + + k->security = NULL; + kfree(ksec); +} + +static int selinux_key_permission(key_ref_t key_ref, + struct task_struct *ctx, + key_perm_t perm) +{ + struct key *key; + struct task_security_struct *tsec; + struct key_security_struct *ksec; + + key = key_ref_to_ptr(key_ref); + + tsec = ctx->security; + ksec = key->security; + + /* if no specific permissions are requested, we skip the + permission check. No serious, additional covert channels + appear to be created. */ + if (perm == 0) + return 0; + + return avc_has_perm(tsec->sid, ksec->sid, + SECCLASS_KEY, perm, NULL); +} + +#endif + static struct security_operations selinux_ops = { .ptrace = selinux_ptrace, .capget = selinux_capget, @@ -4406,6 +4457,12 @@ static struct security_operations selinux_ops = { .xfrm_state_delete_security = selinux_xfrm_state_delete, .xfrm_policy_lookup = selinux_xfrm_policy_lookup, #endif + +#ifdef CONFIG_KEYS + .key_alloc = selinux_key_alloc, + .key_free = selinux_key_free, + .key_permission = selinux_key_permission, +#endif }; static __init int selinux_init(void) @@ -4441,6 +4498,13 @@ static __init int selinux_init(void) } else { printk(KERN_INFO "SELinux: Starting in permissive mode\n"); } + +#ifdef CONFIG_KEYS + /* Add security information to initial keyrings */ + security_key_alloc(&root_user_keyring, current); + security_key_alloc(&root_session_keyring, current); +#endif + return 0; } diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index 70ee65a5881..bc020bde6c8 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -242,3 +242,9 @@ S_(SECCLASS_PACKET, PACKET__SEND, "send") S_(SECCLASS_PACKET, PACKET__RECV, "recv") S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto") + S_(SECCLASS_KEY, KEY__VIEW, "view") + S_(SECCLASS_KEY, KEY__READ, "read") + S_(SECCLASS_KEY, KEY__WRITE, "write") + S_(SECCLASS_KEY, KEY__SEARCH, "search") + S_(SECCLASS_KEY, KEY__LINK, "link") + S_(SECCLASS_KEY, KEY__SETATTR, "setattr") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index 1d9cf3d306b..1205227a3a3 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -959,3 +959,11 @@ #define PACKET__SEND 0x00000001UL #define PACKET__RECV 0x00000002UL #define PACKET__RELABELTO 0x00000004UL + +#define KEY__VIEW 0x00000001UL +#define KEY__READ 0x00000002UL +#define KEY__WRITE 0x00000004UL +#define KEY__SEARCH 0x00000008UL +#define KEY__LINK 0x00000010UL +#define KEY__SETATTR 0x00000020UL + diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h index 3aec75fee4f..24303b61309 100644 --- a/security/selinux/include/class_to_string.h +++ b/security/selinux/include/class_to_string.h @@ -60,3 +60,4 @@ S_("netlink_kobject_uevent_socket") S_("appletalk_socket") S_("packet") + S_("key") diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h index a0eb9e281d1..95887aed2a6 100644 --- a/security/selinux/include/flask.h +++ b/security/selinux/include/flask.h @@ -62,6 +62,7 @@ #define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55 #define SECCLASS_APPLETALK_SOCKET 56 #define SECCLASS_PACKET 57 +#define SECCLASS_KEY 58 /* * Security identifier indices for initial entities diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 54c03077888..8f5547ad185 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -99,6 +99,11 @@ struct sk_security_struct { u32 peer_sid; /* SID of peer */ }; +struct key_security_struct { + struct key *obj; /* back pointer */ + u32 sid; /* SID of key */ +}; + extern unsigned int selinux_checkreqprot; #endif /* _SELINUX_OBJSEC_H_ */ -- cgit v1.2.3-70-g09d2 From 04c567d9313e4927b9835361d8ac0318ce65af6b Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 22 Jun 2006 14:47:18 -0700 Subject: [PATCH] Keys: Fix race between two instantiators of a key Add a revocation notification method to the key type and calls it whilst the key's semaphore is still write-locked after setting the revocation flag. The patch then uses this to maintain a reference on the task_struct of the process that calls request_key() for as long as the authorisation key remains unrevoked. This fixes a potential race between two processes both of which have assumed the authority to instantiate a key (one may have forked the other for example). The problem is that there's no locking around the check for revocation of the auth key and the use of the task_struct it points to, nor does the auth key keep a reference on the task_struct. Access to the "context" pointer in the auth key must thenceforth be done with the auth key semaphore held. The revocation method is called with the target key semaphore held write-locked and the search of the context process's keyrings is done with the auth key semaphore read-locked. The check for the revocation state of the auth key just prior to searching it is done after the auth key is read-locked for the search. This ensures that the auth key can't be revoked between the check and the search. The revocation notification method is added so that the context task_struct can be released as soon as instantiation happens rather than waiting for the auth key to be destroyed, thus avoiding the unnecessary pinning of the requesting process. Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/keys.txt | 10 +++++++++ include/linux/key.h | 5 +++++ security/keys/key.c | 4 ++++ security/keys/process_keys.c | 42 +++++++++++++++++++++++-------------- security/keys/request_key_auth.c | 45 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 89 insertions(+), 17 deletions(-) (limited to 'security/keys') diff --git a/Documentation/keys.txt b/Documentation/keys.txt index 70302001270..3bbe157b45e 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -964,6 +964,16 @@ The structure has a number of fields, some of which are mandatory: It is not safe to sleep in this method; the caller may hold spinlocks. + (*) void (*revoke)(struct key *key); + + This method is optional. It is called to discard part of the payload + data upon a key being revoked. The caller will have the key semaphore + write-locked. + + It is safe to sleep in this method, though care should be taken to avoid + a deadlock against the key semaphore. + + (*) void (*destroy)(struct key *key); This method is optional. It is called to discard the payload data on a key diff --git a/include/linux/key.h b/include/linux/key.h index 8c275d12ef6..e81ebf910d0 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -205,6 +205,11 @@ struct key_type { /* match a key against a description */ int (*match)(const struct key *key, const void *desc); + /* clear some of the data from a key on revokation (optional) + * - the key's semaphore will be write-locked by the caller + */ + void (*revoke)(struct key *key); + /* clear the data from a key (optional) */ void (*destroy)(struct key *key); diff --git a/security/keys/key.c b/security/keys/key.c index 14a15abb773..51f85155738 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -907,6 +907,10 @@ void key_revoke(struct key *key) * it */ down_write(&key->sem); set_bit(KEY_FLAG_REVOKED, &key->flags); + + if (key->type->revoke) + key->type->revoke(key); + up_write(&key->sem); } /* end key_revoke() */ diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index a50a91332fe..4d9825f9962 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -391,6 +391,8 @@ key_ref_t search_process_keyrings(struct key_type *type, struct request_key_auth *rka; key_ref_t key_ref, ret, err; + might_sleep(); + /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; * otherwise we want to return a sample error (probably -EACCES) if @@ -496,27 +498,35 @@ key_ref_t search_process_keyrings(struct key_type *type, */ if (context->request_key_auth && context == current && - type != &key_type_request_key_auth && - key_validate(context->request_key_auth) == 0 + type != &key_type_request_key_auth ) { - rka = context->request_key_auth->payload.data; + /* defend against the auth key being revoked */ + down_read(&context->request_key_auth->sem); - key_ref = search_process_keyrings(type, description, match, - rka->context); + if (key_validate(context->request_key_auth) == 0) { + rka = context->request_key_auth->payload.data; - if (!IS_ERR(key_ref)) - goto found; + key_ref = search_process_keyrings(type, description, + match, rka->context); - switch (PTR_ERR(key_ref)) { - case -EAGAIN: /* no key */ - if (ret) + up_read(&context->request_key_auth->sem); + + if (!IS_ERR(key_ref)) + goto found; + + switch (PTR_ERR(key_ref)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key_ref; break; - case -ENOKEY: /* negative key */ - ret = key_ref; - break; - default: - err = key_ref; - break; + default: + err = key_ref; + break; + } + } else { + up_read(&context->request_key_auth->sem); } } diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 0ecc2e8d2bd..cb9817ced3f 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -20,6 +20,7 @@ static int request_key_auth_instantiate(struct key *, const void *, size_t); static void request_key_auth_describe(const struct key *, struct seq_file *); +static void request_key_auth_revoke(struct key *); static void request_key_auth_destroy(struct key *); static long request_key_auth_read(const struct key *, char __user *, size_t); @@ -31,6 +32,7 @@ struct key_type key_type_request_key_auth = { .def_datalen = sizeof(struct request_key_auth), .instantiate = request_key_auth_instantiate, .describe = request_key_auth_describe, + .revoke = request_key_auth_revoke, .destroy = request_key_auth_destroy, .read = request_key_auth_read, }; @@ -91,6 +93,24 @@ static long request_key_auth_read(const struct key *key, } /* end request_key_auth_read() */ +/*****************************************************************************/ +/* + * handle revocation of an authorisation token key + * - called with the key sem write-locked + */ +static void request_key_auth_revoke(struct key *key) +{ + struct request_key_auth *rka = key->payload.data; + + kenter("{%d}", key->serial); + + if (rka->context) { + put_task_struct(rka->context); + rka->context = NULL; + } + +} /* end request_key_auth_revoke() */ + /*****************************************************************************/ /* * destroy an instantiation authorisation token key @@ -101,6 +121,11 @@ static void request_key_auth_destroy(struct key *key) kenter("{%d}", key->serial); + if (rka->context) { + put_task_struct(rka->context); + rka->context = NULL; + } + key_put(rka->target_key); kfree(rka); @@ -131,14 +156,26 @@ struct key *request_key_auth_new(struct key *target, const char *callout_info) * another process */ if (current->request_key_auth) { /* it is - use that instantiation context here too */ + down_read(¤t->request_key_auth->sem); + + /* if the auth key has been revoked, then the key we're + * servicing is already instantiated */ + if (test_bit(KEY_FLAG_REVOKED, + ¤t->request_key_auth->flags)) + goto auth_key_revoked; + irka = current->request_key_auth->payload.data; rka->context = irka->context; rka->pid = irka->pid; + get_task_struct(rka->context); + + up_read(¤t->request_key_auth->sem); } else { /* it isn't - use this process as the context */ rka->context = current; rka->pid = current->pid; + get_task_struct(rka->context); } rka->target_key = key_get(target); @@ -161,9 +198,15 @@ struct key *request_key_auth_new(struct key *target, const char *callout_info) if (ret < 0) goto error_inst; - kleave(" = {%d})", authkey->serial); + kleave(" = {%d}", authkey->serial); return authkey; +auth_key_revoked: + up_read(¤t->request_key_auth->sem); + kfree(rka); + kleave("= -EKEYREVOKED"); + return ERR_PTR(-EKEYREVOKED); + error_inst: key_revoke(authkey); key_put(authkey); -- cgit v1.2.3-70-g09d2