Commit | Line | Data |
---|---|---|
d7e09d03 PT |
1 | /* |
2 | * GPL HEADER START | |
3 | * | |
4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 only, | |
8 | * as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License version 2 for more details (a copy is included | |
14 | * in the LICENSE file that accompanied this code). | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * version 2 along with this program; If not, see | |
18 | * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf | |
19 | * | |
20 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
21 | * CA 95054 USA or visit www.sun.com if you need additional information or | |
22 | * have any questions. | |
23 | * | |
24 | * GPL HEADER END | |
25 | */ | |
26 | /* | |
27 | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
28 | * Use is subject to license terms. | |
29 | * | |
30 | * Copyright (c) 2012, Intel Corporation. | |
31 | */ | |
32 | /* | |
33 | * This file is part of Lustre, http://www.lustre.org/ | |
34 | * Lustre is a trademark of Sun Microsystems, Inc. | |
35 | * | |
36 | * lustre/obdclass/capa.c | |
37 | * | |
38 | * Lustre Capability Hash Management | |
39 | * | |
40 | * Author: Lai Siyao<lsy@clusterfs.com> | |
41 | */ | |
42 | ||
43 | #define DEBUG_SUBSYSTEM S_SEC | |
44 | ||
d7e09d03 PT |
45 | #include <linux/fs.h> |
46 | #include <asm/unistd.h> | |
47 | #include <linux/slab.h> | |
48 | #include <linux/module.h> | |
2b53313a | 49 | #include <linux/crypto.h> |
d7e09d03 PT |
50 | |
51 | #include <obd_class.h> | |
52 | #include <lustre_debug.h> | |
53 | #include <lustre/lustre_idl.h> | |
54 | ||
55 | #include <linux/list.h> | |
56 | #include <lustre_capa.h> | |
57 | ||
58 | #define NR_CAPAHASH 32 | |
59 | #define CAPA_HASH_SIZE 3000 /* for MDS & OSS */ | |
60 | ||
61 | struct kmem_cache *capa_cachep = NULL; | |
62 | ||
63 | /* lock for capa hash/capa_list/fo_capa_keys */ | |
64 | DEFINE_SPINLOCK(capa_lock); | |
65 | ||
66 | struct list_head capa_list[CAPA_SITE_MAX]; | |
67 | ||
68 | static struct capa_hmac_alg capa_hmac_algs[] = { | |
69 | DEF_CAPA_HMAC_ALG("sha1", SHA1, 20, 20), | |
70 | }; | |
71 | /* capa count */ | |
72 | int capa_count[CAPA_SITE_MAX] = { 0, }; | |
73 | ||
74 | EXPORT_SYMBOL(capa_cachep); | |
75 | EXPORT_SYMBOL(capa_list); | |
76 | EXPORT_SYMBOL(capa_lock); | |
77 | EXPORT_SYMBOL(capa_count); | |
78 | ||
2b53313a GKH |
79 | static inline |
80 | unsigned int ll_crypto_tfm_alg_min_keysize(struct crypto_blkcipher *tfm) | |
81 | { | |
82 | return crypto_blkcipher_tfm(tfm)->__crt_alg->cra_blkcipher.min_keysize; | |
83 | } | |
84 | ||
d7e09d03 PT |
85 | struct hlist_head *init_capa_hash(void) |
86 | { | |
87 | struct hlist_head *hash; | |
88 | int nr_hash, i; | |
89 | ||
90 | OBD_ALLOC(hash, PAGE_CACHE_SIZE); | |
91 | if (!hash) | |
92 | return NULL; | |
93 | ||
94 | nr_hash = PAGE_CACHE_SIZE / sizeof(struct hlist_head); | |
95 | LASSERT(nr_hash > NR_CAPAHASH); | |
96 | ||
97 | for (i = 0; i < NR_CAPAHASH; i++) | |
98 | INIT_HLIST_HEAD(hash + i); | |
99 | return hash; | |
100 | } | |
101 | EXPORT_SYMBOL(init_capa_hash); | |
102 | ||
103 | static inline int capa_on_server(struct obd_capa *ocapa) | |
104 | { | |
105 | return ocapa->c_site == CAPA_SITE_SERVER; | |
106 | } | |
107 | ||
108 | static inline void capa_delete(struct obd_capa *ocapa) | |
109 | { | |
110 | LASSERT(capa_on_server(ocapa)); | |
111 | hlist_del_init(&ocapa->u.tgt.c_hash); | |
112 | list_del_init(&ocapa->c_list); | |
113 | capa_count[ocapa->c_site]--; | |
114 | /* release the ref when alloc */ | |
115 | capa_put(ocapa); | |
116 | } | |
117 | ||
118 | void cleanup_capa_hash(struct hlist_head *hash) | |
119 | { | |
120 | int i; | |
121 | struct hlist_node *next; | |
122 | struct obd_capa *oc; | |
123 | ||
124 | spin_lock(&capa_lock); | |
125 | for (i = 0; i < NR_CAPAHASH; i++) { | |
126 | hlist_for_each_entry_safe(oc, next, hash + i, | |
127 | u.tgt.c_hash) | |
128 | capa_delete(oc); | |
129 | } | |
130 | spin_unlock(&capa_lock); | |
131 | ||
132 | OBD_FREE(hash, PAGE_CACHE_SIZE); | |
133 | } | |
134 | EXPORT_SYMBOL(cleanup_capa_hash); | |
135 | ||
136 | static inline int capa_hashfn(struct lu_fid *fid) | |
137 | { | |
138 | return (fid_oid(fid) ^ fid_ver(fid)) * | |
139 | (unsigned long)(fid_seq(fid) + 1) % NR_CAPAHASH; | |
140 | } | |
141 | ||
142 | /* capa renewal time check is earlier than that on client, which is to prevent | |
143 | * client renew right after obtaining it. */ | |
144 | static inline int capa_is_to_expire(struct obd_capa *oc) | |
145 | { | |
146 | return cfs_time_before(cfs_time_sub(oc->c_expiry, | |
147 | cfs_time_seconds(oc->c_capa.lc_timeout)*2/3), | |
148 | cfs_time_current()); | |
149 | } | |
150 | ||
151 | static struct obd_capa *find_capa(struct lustre_capa *capa, | |
152 | struct hlist_head *head, int alive) | |
153 | { | |
154 | struct obd_capa *ocapa; | |
155 | int len = alive ? offsetof(struct lustre_capa, lc_keyid):sizeof(*capa); | |
156 | ||
157 | hlist_for_each_entry(ocapa, head, u.tgt.c_hash) { | |
158 | if (memcmp(&ocapa->c_capa, capa, len)) | |
159 | continue; | |
160 | /* don't return one that will expire soon in this case */ | |
161 | if (alive && capa_is_to_expire(ocapa)) | |
162 | continue; | |
163 | ||
164 | LASSERT(capa_on_server(ocapa)); | |
165 | ||
166 | DEBUG_CAPA(D_SEC, &ocapa->c_capa, "found"); | |
167 | return ocapa; | |
168 | } | |
169 | ||
170 | return NULL; | |
171 | } | |
172 | ||
173 | #define LRU_CAPA_DELETE_COUNT 12 | |
174 | static inline void capa_delete_lru(struct list_head *head) | |
175 | { | |
176 | struct obd_capa *ocapa; | |
177 | struct list_head *node = head->next; | |
178 | int count = 0; | |
179 | ||
180 | /* free LRU_CAPA_DELETE_COUNT unused capa from head */ | |
181 | while (count++ < LRU_CAPA_DELETE_COUNT) { | |
182 | ocapa = list_entry(node, struct obd_capa, c_list); | |
183 | node = node->next; | |
184 | if (atomic_read(&ocapa->c_refc)) | |
185 | continue; | |
186 | ||
187 | DEBUG_CAPA(D_SEC, &ocapa->c_capa, "free lru"); | |
188 | capa_delete(ocapa); | |
189 | } | |
190 | } | |
191 | ||
192 | /* add or update */ | |
193 | struct obd_capa *capa_add(struct hlist_head *hash, struct lustre_capa *capa) | |
194 | { | |
195 | struct hlist_head *head = hash + capa_hashfn(&capa->lc_fid); | |
196 | struct obd_capa *ocapa, *old = NULL; | |
197 | struct list_head *list = &capa_list[CAPA_SITE_SERVER]; | |
198 | ||
199 | ocapa = alloc_capa(CAPA_SITE_SERVER); | |
200 | if (IS_ERR(ocapa)) | |
201 | return NULL; | |
202 | ||
203 | spin_lock(&capa_lock); | |
204 | old = find_capa(capa, head, 0); | |
205 | if (!old) { | |
206 | ocapa->c_capa = *capa; | |
207 | set_capa_expiry(ocapa); | |
208 | hlist_add_head(&ocapa->u.tgt.c_hash, head); | |
209 | list_add_tail(&ocapa->c_list, list); | |
210 | capa_get(ocapa); | |
211 | capa_count[CAPA_SITE_SERVER]++; | |
212 | if (capa_count[CAPA_SITE_SERVER] > CAPA_HASH_SIZE) | |
213 | capa_delete_lru(list); | |
214 | spin_unlock(&capa_lock); | |
215 | return ocapa; | |
216 | } else { | |
217 | capa_get(old); | |
218 | spin_unlock(&capa_lock); | |
219 | capa_put(ocapa); | |
220 | return old; | |
221 | } | |
222 | } | |
223 | EXPORT_SYMBOL(capa_add); | |
224 | ||
225 | struct obd_capa *capa_lookup(struct hlist_head *hash, struct lustre_capa *capa, | |
226 | int alive) | |
227 | { | |
228 | struct obd_capa *ocapa; | |
229 | ||
230 | spin_lock(&capa_lock); | |
231 | ocapa = find_capa(capa, hash + capa_hashfn(&capa->lc_fid), alive); | |
232 | if (ocapa) { | |
233 | list_move_tail(&ocapa->c_list, | |
234 | &capa_list[CAPA_SITE_SERVER]); | |
235 | capa_get(ocapa); | |
236 | } | |
237 | spin_unlock(&capa_lock); | |
238 | ||
239 | return ocapa; | |
240 | } | |
241 | EXPORT_SYMBOL(capa_lookup); | |
242 | ||
2b53313a GKH |
243 | static inline int ll_crypto_hmac(struct crypto_hash *tfm, |
244 | u8 *key, unsigned int *keylen, | |
245 | struct scatterlist *sg, | |
246 | unsigned int size, u8 *result) | |
247 | { | |
248 | struct hash_desc desc; | |
249 | int rv; | |
250 | desc.tfm = tfm; | |
251 | desc.flags = 0; | |
252 | rv = crypto_hash_setkey(desc.tfm, key, *keylen); | |
253 | if (rv) { | |
254 | CERROR("failed to hash setkey: %d\n", rv); | |
255 | return rv; | |
256 | } | |
257 | return crypto_hash_digest(&desc, sg, size, result); | |
258 | } | |
259 | ||
d7e09d03 PT |
260 | int capa_hmac(__u8 *hmac, struct lustre_capa *capa, __u8 *key) |
261 | { | |
2b53313a | 262 | struct crypto_hash *tfm; |
d7e09d03 PT |
263 | struct capa_hmac_alg *alg; |
264 | int keylen; | |
265 | struct scatterlist sl; | |
266 | ||
267 | if (capa_alg(capa) != CAPA_HMAC_ALG_SHA1) { | |
268 | CERROR("unknown capability hmac algorithm!\n"); | |
269 | return -EFAULT; | |
270 | } | |
271 | ||
272 | alg = &capa_hmac_algs[capa_alg(capa)]; | |
273 | ||
2b53313a | 274 | tfm = crypto_alloc_hash(alg->ha_name, 0, 0); |
d7e09d03 PT |
275 | if (!tfm) { |
276 | CERROR("crypto_alloc_tfm failed, check whether your kernel" | |
277 | "has crypto support!\n"); | |
278 | return -ENOMEM; | |
279 | } | |
280 | keylen = alg->ha_keylen; | |
281 | ||
282 | sg_set_page(&sl, virt_to_page(capa), | |
283 | offsetof(struct lustre_capa, lc_hmac), | |
284 | (unsigned long)(capa) % PAGE_CACHE_SIZE); | |
285 | ||
286 | ll_crypto_hmac(tfm, key, &keylen, &sl, sl.length, hmac); | |
2b53313a | 287 | crypto_free_hash(tfm); |
d7e09d03 PT |
288 | |
289 | return 0; | |
290 | } | |
291 | EXPORT_SYMBOL(capa_hmac); | |
292 | ||
293 | int capa_encrypt_id(__u32 *d, __u32 *s, __u8 *key, int keylen) | |
294 | { | |
2b53313a | 295 | struct crypto_blkcipher *tfm; |
d7e09d03 PT |
296 | struct scatterlist sd; |
297 | struct scatterlist ss; | |
298 | struct blkcipher_desc desc; | |
299 | unsigned int min; | |
300 | int rc; | |
301 | char alg[CRYPTO_MAX_ALG_NAME+1] = "aes"; | |
d7e09d03 PT |
302 | |
303 | /* passing "aes" in a variable instead of a constant string keeps gcc | |
304 | * 4.3.2 happy */ | |
36e607a1 | 305 | tfm = crypto_alloc_blkcipher(alg, 0, 0 ); |
d7e09d03 PT |
306 | if (IS_ERR(tfm)) { |
307 | CERROR("failed to load transform for aes\n"); | |
0a3bdb00 | 308 | return PTR_ERR(tfm); |
d7e09d03 PT |
309 | } |
310 | ||
311 | min = ll_crypto_tfm_alg_min_keysize(tfm); | |
312 | if (keylen < min) { | |
313 | CERROR("keylen at least %d bits for aes\n", min * 8); | |
314 | GOTO(out, rc = -EINVAL); | |
315 | } | |
316 | ||
2b53313a | 317 | rc = crypto_blkcipher_setkey(tfm, key, min); |
d7e09d03 PT |
318 | if (rc) { |
319 | CERROR("failed to setting key for aes\n"); | |
320 | GOTO(out, rc); | |
321 | } | |
322 | ||
323 | sg_set_page(&sd, virt_to_page(d), 16, | |
324 | (unsigned long)(d) % PAGE_CACHE_SIZE); | |
325 | ||
326 | sg_set_page(&ss, virt_to_page(s), 16, | |
327 | (unsigned long)(s) % PAGE_CACHE_SIZE); | |
328 | desc.tfm = tfm; | |
329 | desc.info = NULL; | |
330 | desc.flags = 0; | |
2b53313a | 331 | rc = crypto_blkcipher_encrypt(&desc, &sd, &ss, 16); |
d7e09d03 PT |
332 | if (rc) { |
333 | CERROR("failed to encrypt for aes\n"); | |
334 | GOTO(out, rc); | |
335 | } | |
336 | ||
d7e09d03 | 337 | out: |
2b53313a | 338 | crypto_free_blkcipher(tfm); |
d7e09d03 PT |
339 | return rc; |
340 | } | |
341 | EXPORT_SYMBOL(capa_encrypt_id); | |
342 | ||
343 | int capa_decrypt_id(__u32 *d, __u32 *s, __u8 *key, int keylen) | |
344 | { | |
2b53313a | 345 | struct crypto_blkcipher *tfm; |
d7e09d03 PT |
346 | struct scatterlist sd; |
347 | struct scatterlist ss; | |
348 | struct blkcipher_desc desc; | |
349 | unsigned int min; | |
350 | int rc; | |
351 | char alg[CRYPTO_MAX_ALG_NAME+1] = "aes"; | |
d7e09d03 PT |
352 | |
353 | /* passing "aes" in a variable instead of a constant string keeps gcc | |
354 | * 4.3.2 happy */ | |
36e607a1 | 355 | tfm = crypto_alloc_blkcipher(alg, 0, 0 ); |
d7e09d03 PT |
356 | if (IS_ERR(tfm)) { |
357 | CERROR("failed to load transform for aes\n"); | |
0a3bdb00 | 358 | return PTR_ERR(tfm); |
d7e09d03 PT |
359 | } |
360 | ||
361 | min = ll_crypto_tfm_alg_min_keysize(tfm); | |
362 | if (keylen < min) { | |
363 | CERROR("keylen at least %d bits for aes\n", min * 8); | |
364 | GOTO(out, rc = -EINVAL); | |
365 | } | |
366 | ||
2b53313a | 367 | rc = crypto_blkcipher_setkey(tfm, key, min); |
d7e09d03 PT |
368 | if (rc) { |
369 | CERROR("failed to setting key for aes\n"); | |
370 | GOTO(out, rc); | |
371 | } | |
372 | ||
373 | sg_set_page(&sd, virt_to_page(d), 16, | |
374 | (unsigned long)(d) % PAGE_CACHE_SIZE); | |
375 | ||
376 | sg_set_page(&ss, virt_to_page(s), 16, | |
377 | (unsigned long)(s) % PAGE_CACHE_SIZE); | |
378 | ||
379 | desc.tfm = tfm; | |
380 | desc.info = NULL; | |
381 | desc.flags = 0; | |
2b53313a | 382 | rc = crypto_blkcipher_decrypt(&desc, &sd, &ss, 16); |
d7e09d03 PT |
383 | if (rc) { |
384 | CERROR("failed to decrypt for aes\n"); | |
385 | GOTO(out, rc); | |
386 | } | |
387 | ||
d7e09d03 | 388 | out: |
2b53313a | 389 | crypto_free_blkcipher(tfm); |
d7e09d03 PT |
390 | return rc; |
391 | } | |
392 | EXPORT_SYMBOL(capa_decrypt_id); | |
393 | ||
394 | void capa_cpy(void *capa, struct obd_capa *ocapa) | |
395 | { | |
396 | spin_lock(&ocapa->c_lock); | |
397 | *(struct lustre_capa *)capa = ocapa->c_capa; | |
398 | spin_unlock(&ocapa->c_lock); | |
399 | } | |
400 | EXPORT_SYMBOL(capa_cpy); | |
401 | ||
402 | void _debug_capa(struct lustre_capa *c, | |
403 | struct libcfs_debug_msg_data *msgdata, | |
404 | const char *fmt, ... ) | |
405 | { | |
406 | va_list args; | |
407 | va_start(args, fmt); | |
408 | libcfs_debug_vmsg2(msgdata, fmt, args, | |
409 | " capability@%p fid "DFID" opc "LPX64" uid "LPU64 | |
410 | " gid "LPU64" flags %u alg %d keyid %u timeout %u " | |
411 | "expiry %u\n", c, PFID(capa_fid(c)), capa_opc(c), | |
412 | capa_uid(c), capa_gid(c), capa_flags(c), | |
413 | capa_alg(c), capa_keyid(c), capa_timeout(c), | |
414 | capa_expiry(c)); | |
415 | va_end(args); | |
416 | } | |
417 | EXPORT_SYMBOL(_debug_capa); |