diff options
Diffstat (limited to 'net/rxrpc/ar-key.c')
-rw-r--r-- | net/rxrpc/ar-key.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c new file mode 100644 index 000000000000..7e049ff6ae60 --- /dev/null +++ b/net/rxrpc/ar-key.c | |||
@@ -0,0 +1,334 @@ | |||
1 | /* RxRPC key management | ||
2 | * | ||
3 | * Copyright (C) 2007 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 | * RxRPC keys should have a description of describing their purpose: | ||
12 | * "afs@CAMBRIDGE.REDHAT.COM> | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/net.h> | ||
17 | #include <linux/skbuff.h> | ||
18 | #include <linux/key.h> | ||
19 | #include <linux/crypto.h> | ||
20 | #include <net/sock.h> | ||
21 | #include <net/af_rxrpc.h> | ||
22 | #include <keys/rxrpc-type.h> | ||
23 | #include <keys/user-type.h> | ||
24 | #include "ar-internal.h" | ||
25 | |||
26 | static int rxrpc_instantiate(struct key *, const void *, size_t); | ||
27 | static int rxrpc_instantiate_s(struct key *, const void *, size_t); | ||
28 | static void rxrpc_destroy(struct key *); | ||
29 | static void rxrpc_destroy_s(struct key *); | ||
30 | static void rxrpc_describe(const struct key *, struct seq_file *); | ||
31 | |||
32 | /* | ||
33 | * rxrpc defined keys take an arbitrary string as the description and an | ||
34 | * arbitrary blob of data as the payload | ||
35 | */ | ||
36 | struct key_type key_type_rxrpc = { | ||
37 | .name = "rxrpc", | ||
38 | .instantiate = rxrpc_instantiate, | ||
39 | .match = user_match, | ||
40 | .destroy = rxrpc_destroy, | ||
41 | .describe = rxrpc_describe, | ||
42 | }; | ||
43 | |||
44 | EXPORT_SYMBOL(key_type_rxrpc); | ||
45 | |||
46 | /* | ||
47 | * rxrpc server defined keys take "<serviceId>:<securityIndex>" as the | ||
48 | * description and an 8-byte decryption key as the payload | ||
49 | */ | ||
50 | struct key_type key_type_rxrpc_s = { | ||
51 | .name = "rxrpc_s", | ||
52 | .instantiate = rxrpc_instantiate_s, | ||
53 | .match = user_match, | ||
54 | .destroy = rxrpc_destroy_s, | ||
55 | .describe = rxrpc_describe, | ||
56 | }; | ||
57 | |||
58 | /* | ||
59 | * instantiate an rxrpc defined key | ||
60 | * data should be of the form: | ||
61 | * OFFSET LEN CONTENT | ||
62 | * 0 4 key interface version number | ||
63 | * 4 2 security index (type) | ||
64 | * 6 2 ticket length | ||
65 | * 8 4 key expiry time (time_t) | ||
66 | * 12 4 kvno | ||
67 | * 16 8 session key | ||
68 | * 24 [len] ticket | ||
69 | * | ||
70 | * if no data is provided, then a no-security key is made | ||
71 | */ | ||
72 | static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) | ||
73 | { | ||
74 | const struct rxkad_key *tsec; | ||
75 | struct rxrpc_key_payload *upayload; | ||
76 | size_t plen; | ||
77 | u32 kver; | ||
78 | int ret; | ||
79 | |||
80 | _enter("{%x},,%zu", key_serial(key), datalen); | ||
81 | |||
82 | /* handle a no-security key */ | ||
83 | if (!data && datalen == 0) | ||
84 | return 0; | ||
85 | |||
86 | /* get the key interface version number */ | ||
87 | ret = -EINVAL; | ||
88 | if (datalen <= 4 || !data) | ||
89 | goto error; | ||
90 | memcpy(&kver, data, sizeof(kver)); | ||
91 | data += sizeof(kver); | ||
92 | datalen -= sizeof(kver); | ||
93 | |||
94 | _debug("KEY I/F VERSION: %u", kver); | ||
95 | |||
96 | ret = -EKEYREJECTED; | ||
97 | if (kver != 1) | ||
98 | goto error; | ||
99 | |||
100 | /* deal with a version 1 key */ | ||
101 | ret = -EINVAL; | ||
102 | if (datalen < sizeof(*tsec)) | ||
103 | goto error; | ||
104 | |||
105 | tsec = data; | ||
106 | if (datalen != sizeof(*tsec) + tsec->ticket_len) | ||
107 | goto error; | ||
108 | |||
109 | _debug("SCIX: %u", tsec->security_index); | ||
110 | _debug("TLEN: %u", tsec->ticket_len); | ||
111 | _debug("EXPY: %x", tsec->expiry); | ||
112 | _debug("KVNO: %u", tsec->kvno); | ||
113 | _debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x", | ||
114 | tsec->session_key[0], tsec->session_key[1], | ||
115 | tsec->session_key[2], tsec->session_key[3], | ||
116 | tsec->session_key[4], tsec->session_key[5], | ||
117 | tsec->session_key[6], tsec->session_key[7]); | ||
118 | if (tsec->ticket_len >= 8) | ||
119 | _debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x", | ||
120 | tsec->ticket[0], tsec->ticket[1], | ||
121 | tsec->ticket[2], tsec->ticket[3], | ||
122 | tsec->ticket[4], tsec->ticket[5], | ||
123 | tsec->ticket[6], tsec->ticket[7]); | ||
124 | |||
125 | ret = -EPROTONOSUPPORT; | ||
126 | if (tsec->security_index != 2) | ||
127 | goto error; | ||
128 | |||
129 | key->type_data.x[0] = tsec->security_index; | ||
130 | |||
131 | plen = sizeof(*upayload) + tsec->ticket_len; | ||
132 | ret = key_payload_reserve(key, plen); | ||
133 | if (ret < 0) | ||
134 | goto error; | ||
135 | |||
136 | ret = -ENOMEM; | ||
137 | upayload = kmalloc(plen, GFP_KERNEL); | ||
138 | if (!upayload) | ||
139 | goto error; | ||
140 | |||
141 | /* attach the data */ | ||
142 | memcpy(&upayload->k, tsec, sizeof(*tsec)); | ||
143 | memcpy(&upayload->k.ticket, (void *)tsec + sizeof(*tsec), | ||
144 | tsec->ticket_len); | ||
145 | key->payload.data = upayload; | ||
146 | key->expiry = tsec->expiry; | ||
147 | ret = 0; | ||
148 | |||
149 | error: | ||
150 | return ret; | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * instantiate a server secret key | ||
155 | * data should be a pointer to the 8-byte secret key | ||
156 | */ | ||
157 | static int rxrpc_instantiate_s(struct key *key, const void *data, | ||
158 | size_t datalen) | ||
159 | { | ||
160 | struct crypto_blkcipher *ci; | ||
161 | |||
162 | _enter("{%x},,%zu", key_serial(key), datalen); | ||
163 | |||
164 | if (datalen != 8) | ||
165 | return -EINVAL; | ||
166 | |||
167 | memcpy(&key->type_data, data, 8); | ||
168 | |||
169 | ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); | ||
170 | if (IS_ERR(ci)) { | ||
171 | _leave(" = %ld", PTR_ERR(ci)); | ||
172 | return PTR_ERR(ci); | ||
173 | } | ||
174 | |||
175 | if (crypto_blkcipher_setkey(ci, data, 8) < 0) | ||
176 | BUG(); | ||
177 | |||
178 | key->payload.data = ci; | ||
179 | _leave(" = 0"); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * dispose of the data dangling from the corpse of a rxrpc key | ||
185 | */ | ||
186 | static void rxrpc_destroy(struct key *key) | ||
187 | { | ||
188 | kfree(key->payload.data); | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * dispose of the data dangling from the corpse of a rxrpc key | ||
193 | */ | ||
194 | static void rxrpc_destroy_s(struct key *key) | ||
195 | { | ||
196 | if (key->payload.data) { | ||
197 | crypto_free_blkcipher(key->payload.data); | ||
198 | key->payload.data = NULL; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * describe the rxrpc key | ||
204 | */ | ||
205 | static void rxrpc_describe(const struct key *key, struct seq_file *m) | ||
206 | { | ||
207 | seq_puts(m, key->description); | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * grab the security key for a socket | ||
212 | */ | ||
213 | int rxrpc_request_key(struct rxrpc_sock *rx, char __user *optval, int optlen) | ||
214 | { | ||
215 | struct key *key; | ||
216 | char *description; | ||
217 | |||
218 | _enter(""); | ||
219 | |||
220 | if (optlen <= 0 || optlen > PAGE_SIZE - 1) | ||
221 | return -EINVAL; | ||
222 | |||
223 | description = kmalloc(optlen + 1, GFP_KERNEL); | ||
224 | if (!description) | ||
225 | return -ENOMEM; | ||
226 | |||
227 | if (copy_from_user(description, optval, optlen)) { | ||
228 | kfree(description); | ||
229 | return -EFAULT; | ||
230 | } | ||
231 | description[optlen] = 0; | ||
232 | |||
233 | key = request_key(&key_type_rxrpc, description, NULL); | ||
234 | if (IS_ERR(key)) { | ||
235 | kfree(description); | ||
236 | _leave(" = %ld", PTR_ERR(key)); | ||
237 | return PTR_ERR(key); | ||
238 | } | ||
239 | |||
240 | rx->key = key; | ||
241 | kfree(description); | ||
242 | _leave(" = 0 [key %x]", key->serial); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * grab the security keyring for a server socket | ||
248 | */ | ||
249 | int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval, | ||
250 | int optlen) | ||
251 | { | ||
252 | struct key *key; | ||
253 | char *description; | ||
254 | |||
255 | _enter(""); | ||
256 | |||
257 | if (optlen <= 0 || optlen > PAGE_SIZE - 1) | ||
258 | return -EINVAL; | ||
259 | |||
260 | description = kmalloc(optlen + 1, GFP_KERNEL); | ||
261 | if (!description) | ||
262 | return -ENOMEM; | ||
263 | |||
264 | if (copy_from_user(description, optval, optlen)) { | ||
265 | kfree(description); | ||
266 | return -EFAULT; | ||
267 | } | ||
268 | description[optlen] = 0; | ||
269 | |||
270 | key = request_key(&key_type_keyring, description, NULL); | ||
271 | if (IS_ERR(key)) { | ||
272 | kfree(description); | ||
273 | _leave(" = %ld", PTR_ERR(key)); | ||
274 | return PTR_ERR(key); | ||
275 | } | ||
276 | |||
277 | rx->securities = key; | ||
278 | kfree(description); | ||
279 | _leave(" = 0 [key %x]", key->serial); | ||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * generate a server data key | ||
285 | */ | ||
286 | int rxrpc_get_server_data_key(struct rxrpc_connection *conn, | ||
287 | const void *session_key, | ||
288 | time_t expiry, | ||
289 | u32 kvno) | ||
290 | { | ||
291 | struct key *key; | ||
292 | int ret; | ||
293 | |||
294 | struct { | ||
295 | u32 kver; | ||
296 | struct rxkad_key tsec; | ||
297 | } data; | ||
298 | |||
299 | _enter(""); | ||
300 | |||
301 | key = key_alloc(&key_type_rxrpc, "x", 0, 0, current, 0, | ||
302 | KEY_ALLOC_NOT_IN_QUOTA); | ||
303 | if (IS_ERR(key)) { | ||
304 | _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); | ||
305 | return -ENOMEM; | ||
306 | } | ||
307 | |||
308 | _debug("key %d", key_serial(key)); | ||
309 | |||
310 | data.kver = 1; | ||
311 | data.tsec.security_index = 2; | ||
312 | data.tsec.ticket_len = 0; | ||
313 | data.tsec.expiry = expiry; | ||
314 | data.tsec.kvno = 0; | ||
315 | |||
316 | memcpy(&data.tsec.session_key, session_key, | ||
317 | sizeof(data.tsec.session_key)); | ||
318 | |||
319 | ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL); | ||
320 | if (ret < 0) | ||
321 | goto error; | ||
322 | |||
323 | conn->key = key; | ||
324 | _leave(" = 0 [%d]", key_serial(key)); | ||
325 | return 0; | ||
326 | |||
327 | error: | ||
328 | key_revoke(key); | ||
329 | key_put(key); | ||
330 | _leave(" = -ENOMEM [ins %d]", ret); | ||
331 | return -ENOMEM; | ||
332 | } | ||
333 | |||
334 | EXPORT_SYMBOL(rxrpc_get_server_data_key); | ||