| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* user_defined.c: user defined key type | 
 | 2 |  * | 
 | 3 |  * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | 
 | 4 |  * Written by David Howells (dhowells@redhat.com) | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or | 
 | 7 |  * modify it under the terms of the GNU General Public License | 
 | 8 |  * as published by the Free Software Foundation; either version | 
 | 9 |  * 2 of the License, or (at your option) any later version. | 
 | 10 |  */ | 
 | 11 |  | 
 | 12 | #include <linux/module.h> | 
 | 13 | #include <linux/init.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | #include <linux/slab.h> | 
 | 15 | #include <linux/seq_file.h> | 
 | 16 | #include <linux/err.h> | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 17 | #include <keys/user-type.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | #include <asm/uaccess.h> | 
 | 19 | #include "internal.h" | 
 | 20 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | /* | 
 | 22 |  * user defined keys take an arbitrary string as the description and an | 
 | 23 |  * arbitrary blob of data as the payload | 
 | 24 |  */ | 
 | 25 | struct key_type key_type_user = { | 
 | 26 | 	.name		= "user", | 
 | 27 | 	.instantiate	= user_instantiate, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 28 | 	.update		= user_update, | 
 | 29 | 	.match		= user_match, | 
| David Howells | 31204ed | 2006-06-26 00:24:51 -0700 | [diff] [blame] | 30 | 	.revoke		= user_revoke, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | 	.destroy	= user_destroy, | 
 | 32 | 	.describe	= user_describe, | 
 | 33 | 	.read		= user_read, | 
 | 34 | }; | 
 | 35 |  | 
| Michael Halcrow | 16c29b6 | 2005-06-23 22:00:58 -0700 | [diff] [blame] | 36 | EXPORT_SYMBOL_GPL(key_type_user); | 
 | 37 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 38 | /*****************************************************************************/ | 
 | 39 | /* | 
 | 40 |  * instantiate a user defined key | 
 | 41 |  */ | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 42 | int user_instantiate(struct key *key, const void *data, size_t datalen) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 | { | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 44 | 	struct user_key_payload *upayload; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | 	int ret; | 
 | 46 |  | 
 | 47 | 	ret = -EINVAL; | 
 | 48 | 	if (datalen <= 0 || datalen > 32767 || !data) | 
 | 49 | 		goto error; | 
 | 50 |  | 
 | 51 | 	ret = key_payload_reserve(key, datalen); | 
 | 52 | 	if (ret < 0) | 
 | 53 | 		goto error; | 
 | 54 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | 	ret = -ENOMEM; | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 56 | 	upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); | 
 | 57 | 	if (!upayload) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 | 		goto error; | 
 | 59 |  | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 60 | 	/* attach the data */ | 
 | 61 | 	upayload->datalen = datalen; | 
 | 62 | 	memcpy(upayload->data, data, datalen); | 
 | 63 | 	rcu_assign_pointer(key->payload.data, upayload); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | 	ret = 0; | 
 | 65 |  | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 66 | error: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 | 	return ret; | 
 | 68 |  | 
 | 69 | } /* end user_instantiate() */ | 
| David Howells | 31204ed | 2006-06-26 00:24:51 -0700 | [diff] [blame] | 70 |  | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 71 | EXPORT_SYMBOL_GPL(user_instantiate); | 
 | 72 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 73 | /*****************************************************************************/ | 
 | 74 | /* | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 75 |  * dispose of the old data from an updated user defined key | 
 | 76 |  */ | 
 | 77 | static void user_update_rcu_disposal(struct rcu_head *rcu) | 
 | 78 | { | 
 | 79 | 	struct user_key_payload *upayload; | 
 | 80 |  | 
 | 81 | 	upayload = container_of(rcu, struct user_key_payload, rcu); | 
 | 82 |  | 
 | 83 | 	kfree(upayload); | 
 | 84 |  | 
 | 85 | } /* end user_update_rcu_disposal() */ | 
 | 86 |  | 
 | 87 | /*****************************************************************************/ | 
 | 88 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 |  * update a user defined key | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 90 |  * - the key's semaphore is write-locked | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 |  */ | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 92 | int user_update(struct key *key, const void *data, size_t datalen) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | { | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 94 | 	struct user_key_payload *upayload, *zap; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 | 	int ret; | 
 | 96 |  | 
 | 97 | 	ret = -EINVAL; | 
 | 98 | 	if (datalen <= 0 || datalen > 32767 || !data) | 
 | 99 | 		goto error; | 
 | 100 |  | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 101 | 	/* construct a replacement payload */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 102 | 	ret = -ENOMEM; | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 103 | 	upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); | 
 | 104 | 	if (!upayload) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | 		goto error; | 
 | 106 |  | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 107 | 	upayload->datalen = datalen; | 
 | 108 | 	memcpy(upayload->data, data, datalen); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 |  | 
 | 110 | 	/* check the quota and attach the new data */ | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 111 | 	zap = upayload; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 |  | 
 | 113 | 	ret = key_payload_reserve(key, datalen); | 
 | 114 |  | 
 | 115 | 	if (ret == 0) { | 
 | 116 | 		/* attach the new data, displacing the old */ | 
 | 117 | 		zap = key->payload.data; | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 118 | 		rcu_assign_pointer(key->payload.data, upayload); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | 		key->expiry = 0; | 
 | 120 | 	} | 
 | 121 |  | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 122 | 	call_rcu(&zap->rcu, user_update_rcu_disposal); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 |  | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 124 | error: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 125 | 	return ret; | 
 | 126 |  | 
 | 127 | } /* end user_update() */ | 
 | 128 |  | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 129 | EXPORT_SYMBOL_GPL(user_update); | 
 | 130 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 131 | /*****************************************************************************/ | 
 | 132 | /* | 
 | 133 |  * match users on their name | 
 | 134 |  */ | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 135 | int user_match(const struct key *key, const void *description) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 136 | { | 
 | 137 | 	return strcmp(key->description, description) == 0; | 
 | 138 |  | 
 | 139 | } /* end user_match() */ | 
 | 140 |  | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 141 | EXPORT_SYMBOL_GPL(user_match); | 
 | 142 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | /*****************************************************************************/ | 
 | 144 | /* | 
| David Howells | 31204ed | 2006-06-26 00:24:51 -0700 | [diff] [blame] | 145 |  * dispose of the links from a revoked keyring | 
 | 146 |  * - called with the key sem write-locked | 
 | 147 |  */ | 
 | 148 | void user_revoke(struct key *key) | 
 | 149 | { | 
 | 150 | 	struct user_key_payload *upayload = key->payload.data; | 
 | 151 |  | 
 | 152 | 	/* clear the quota */ | 
 | 153 | 	key_payload_reserve(key, 0); | 
 | 154 |  | 
 | 155 | 	if (upayload) { | 
 | 156 | 		rcu_assign_pointer(key->payload.data, NULL); | 
 | 157 | 		call_rcu(&upayload->rcu, user_update_rcu_disposal); | 
 | 158 | 	} | 
 | 159 |  | 
 | 160 | } /* end user_revoke() */ | 
 | 161 |  | 
 | 162 | EXPORT_SYMBOL(user_revoke); | 
 | 163 |  | 
 | 164 | /*****************************************************************************/ | 
 | 165 | /* | 
 | 166 |  * dispose of the data dangling from the corpse of a user key | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 |  */ | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 168 | void user_destroy(struct key *key) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 169 | { | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 170 | 	struct user_key_payload *upayload = key->payload.data; | 
 | 171 |  | 
 | 172 | 	kfree(upayload); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 |  | 
 | 174 | } /* end user_destroy() */ | 
 | 175 |  | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 176 | EXPORT_SYMBOL_GPL(user_destroy); | 
 | 177 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 | /*****************************************************************************/ | 
 | 179 | /* | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 180 |  * describe the user key | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 181 |  */ | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 182 | void user_describe(const struct key *key, struct seq_file *m) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 183 | { | 
 | 184 | 	seq_puts(m, key->description); | 
 | 185 |  | 
 | 186 | 	seq_printf(m, ": %u", key->datalen); | 
 | 187 |  | 
 | 188 | } /* end user_describe() */ | 
 | 189 |  | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 190 | EXPORT_SYMBOL_GPL(user_describe); | 
 | 191 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | /*****************************************************************************/ | 
 | 193 | /* | 
 | 194 |  * read the key data | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 195 |  * - the key's semaphore is read-locked | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 196 |  */ | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 197 | long user_read(const struct key *key, char __user *buffer, size_t buflen) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | { | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 199 | 	struct user_key_payload *upayload; | 
 | 200 | 	long ret; | 
 | 201 |  | 
| David Howells | d9a9b4a | 2010-04-30 14:32:08 +0100 | [diff] [blame] | 202 | 	upayload = rcu_dereference_protected( | 
 | 203 | 		key->payload.data, rwsem_is_locked(&((struct key *)key)->sem)); | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 204 | 	ret = upayload->datalen; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 205 |  | 
 | 206 | 	/* we can return the data as is */ | 
 | 207 | 	if (buffer && buflen > 0) { | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 208 | 		if (buflen > upayload->datalen) | 
 | 209 | 			buflen = upayload->datalen; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 210 |  | 
| David Howells | 76d8aea | 2005-06-23 22:00:49 -0700 | [diff] [blame] | 211 | 		if (copy_to_user(buffer, upayload->data, buflen) != 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 | 			ret = -EFAULT; | 
 | 213 | 	} | 
 | 214 |  | 
 | 215 | 	return ret; | 
 | 216 |  | 
 | 217 | } /* end user_read() */ | 
| David Howells | 2aa349f | 2005-10-30 15:02:42 -0800 | [diff] [blame] | 218 |  | 
 | 219 | EXPORT_SYMBOL_GPL(user_read); |