api.c 9.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * Scatterlist Cryptographic API.
 *
 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
 * Copyright (c) 2002 David S. Miller (davem@redhat.com)
6
 * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16
 *
 * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
 * and Nettle, by Niels Mller.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) 
 * any later version.
 *
 */
17

H
Herbert Xu 已提交
18
#include <linux/err.h>
L
Linus Torvalds 已提交
19
#include <linux/errno.h>
20
#include <linux/kernel.h>
21
#include <linux/kmod.h>
H
Herbert Xu 已提交
22
#include <linux/module.h>
23
#include <linux/param.h>
H
Herbert Xu 已提交
24
#include <linux/sched.h>
L
Linus Torvalds 已提交
25
#include <linux/slab.h>
26
#include <linux/string.h>
L
Linus Torvalds 已提交
27 28 29
#include "internal.h"

LIST_HEAD(crypto_alg_list);
30
EXPORT_SYMBOL_GPL(crypto_alg_list);
L
Linus Torvalds 已提交
31
DECLARE_RWSEM(crypto_alg_sem);
32
EXPORT_SYMBOL_GPL(crypto_alg_sem);
L
Linus Torvalds 已提交
33

34 35 36
BLOCKING_NOTIFIER_HEAD(crypto_chain);
EXPORT_SYMBOL_GPL(crypto_chain);

37
static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
L
Linus Torvalds 已提交
38
{
39 40 41 42
	atomic_inc(&alg->cra_refcnt);
	return alg;
}

43
struct crypto_alg *crypto_mod_get(struct crypto_alg *alg)
44 45
{
	return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL;
L
Linus Torvalds 已提交
46
}
47
EXPORT_SYMBOL_GPL(crypto_mod_get);
L
Linus Torvalds 已提交
48

49
void crypto_mod_put(struct crypto_alg *alg)
L
Linus Torvalds 已提交
50
{
51
	crypto_alg_put(alg);
L
Linus Torvalds 已提交
52 53
	module_put(alg->cra_module);
}
54
EXPORT_SYMBOL_GPL(crypto_mod_put);
L
Linus Torvalds 已提交
55

56
struct crypto_alg *__crypto_alg_lookup(const char *name, u32 type, u32 mask)
L
Linus Torvalds 已提交
57 58
{
	struct crypto_alg *q, *alg = NULL;
59
	int best = -2;
L
Linus Torvalds 已提交
60 61

	list_for_each_entry(q, &crypto_alg_list, cra_list) {
62 63
		int exact, fuzzy;

H
Herbert Xu 已提交
64 65 66
		if (crypto_is_moribund(q))
			continue;

67 68 69 70 71 72 73
		if ((q->cra_flags ^ type) & mask)
			continue;

		if (crypto_is_larval(q) &&
		    ((struct crypto_larval *)q)->mask != mask)
			continue;

74 75 76 77 78
		exact = !strcmp(q->cra_driver_name, name);
		fuzzy = !strcmp(q->cra_name, name);
		if (!exact && !(fuzzy && q->cra_priority > best))
			continue;

79
		if (unlikely(!crypto_mod_get(q)))
80 81 82 83
			continue;

		best = q->cra_priority;
		if (alg)
84
			crypto_mod_put(alg);
85 86 87
		alg = q;

		if (exact)
L
Linus Torvalds 已提交
88 89
			break;
	}
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

	return alg;
}
EXPORT_SYMBOL_GPL(__crypto_alg_lookup);

static void crypto_larval_destroy(struct crypto_alg *alg)
{
	struct crypto_larval *larval = (void *)alg;

	BUG_ON(!crypto_is_larval(alg));
	if (larval->adult)
		crypto_mod_put(larval->adult);
	kfree(larval);
}

105 106
static struct crypto_alg *crypto_larval_alloc(const char *name, u32 type,
					      u32 mask)
107 108 109 110 111 112
{
	struct crypto_alg *alg;
	struct crypto_larval *larval;

	larval = kzalloc(sizeof(*larval), GFP_KERNEL);
	if (!larval)
H
Herbert Xu 已提交
113
		return ERR_PTR(-ENOMEM);
114

115 116
	larval->mask = mask;
	larval->alg.cra_flags = CRYPTO_ALG_LARVAL | type;
117 118 119 120 121 122 123 124
	larval->alg.cra_priority = -1;
	larval->alg.cra_destroy = crypto_larval_destroy;

	atomic_set(&larval->alg.cra_refcnt, 2);
	strlcpy(larval->alg.cra_name, name, CRYPTO_MAX_ALG_NAME);
	init_completion(&larval->completion);

	down_write(&crypto_alg_sem);
125
	alg = __crypto_alg_lookup(name, type, mask);
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
	if (!alg) {
		alg = &larval->alg;
		list_add(&alg->cra_list, &crypto_alg_list);
	}
	up_write(&crypto_alg_sem);

	if (alg != &larval->alg)
		kfree(larval);

	return alg;
}

static void crypto_larval_kill(struct crypto_alg *alg)
{
	struct crypto_larval *larval = (void *)alg;

	down_write(&crypto_alg_sem);
	list_del(&alg->cra_list);
	up_write(&crypto_alg_sem);
	complete(&larval->completion);
	crypto_alg_put(alg);
}

static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg)
{
	struct crypto_larval *larval = (void *)alg;

	wait_for_completion_interruptible_timeout(&larval->completion, 60 * HZ);
	alg = larval->adult;
H
Herbert Xu 已提交
155 156 157 158 159
	if (alg) {
		if (!crypto_mod_get(alg))
			alg = ERR_PTR(-EAGAIN);
	} else
		alg = ERR_PTR(-ENOENT);
160 161 162 163 164
	crypto_mod_put(&larval->alg);

	return alg;
}

165 166
static struct crypto_alg *crypto_alg_lookup(const char *name, u32 type,
					    u32 mask)
167 168 169 170
{
	struct crypto_alg *alg;

	down_read(&crypto_alg_sem);
171
	alg = __crypto_alg_lookup(name, type, mask);
L
Linus Torvalds 已提交
172
	up_read(&crypto_alg_sem);
173

L
Linus Torvalds 已提交
174 175 176
	return alg;
}

177
struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask)
178
{
179 180
	struct crypto_alg *alg;
	struct crypto_alg *larval;
H
Herbert Xu 已提交
181
	int ok;
182

H
Herbert Xu 已提交
183 184 185 186
	if (!name)
		return ERR_PTR(-ENOENT);

	mask &= ~(CRYPTO_ALG_LARVAL | CRYPTO_ALG_DEAD);
187 188 189 190
	type &= mask;

	alg = try_then_request_module(crypto_alg_lookup(name, type, mask),
				      name);
191 192 193
	if (alg)
		return crypto_is_larval(alg) ? crypto_larval_wait(alg) : alg;

194
	larval = crypto_larval_alloc(name, type, mask);
H
Herbert Xu 已提交
195
	if (IS_ERR(larval) || !crypto_is_larval(larval))
196 197
		return larval;

H
Herbert Xu 已提交
198 199 200 201 202 203 204
	ok = crypto_notify(CRYPTO_MSG_ALG_REQUEST, larval);
	if (ok == NOTIFY_DONE) {
		request_module("cryptomgr");
		ok = crypto_notify(CRYPTO_MSG_ALG_REQUEST, larval);
	}

	if (ok == NOTIFY_STOP)
205 206 207
		alg = crypto_larval_wait(larval);
	else {
		crypto_mod_put(larval);
H
Herbert Xu 已提交
208
		alg = ERR_PTR(-ENOENT);
209 210 211
	}
	crypto_larval_kill(larval);
	return alg;
212
}
213
EXPORT_SYMBOL_GPL(crypto_alg_mod_lookup);
214

215
static int crypto_init_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
L
Linus Torvalds 已提交
216
{
217
	const struct crypto_type *type_obj = tfm->__crt_alg->cra_type;
218

219 220
	if (type_obj)
		return type_obj->init(tfm, type, mask);
221

L
Linus Torvalds 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	switch (crypto_tfm_alg_type(tfm)) {
	case CRYPTO_ALG_TYPE_CIPHER:
		return crypto_init_cipher_ops(tfm);
		
	case CRYPTO_ALG_TYPE_DIGEST:
		return crypto_init_digest_ops(tfm);
		
	case CRYPTO_ALG_TYPE_COMPRESS:
		return crypto_init_compress_ops(tfm);
	
	default:
		break;
	}
	
	BUG();
	return -EINVAL;
}

static void crypto_exit_ops(struct crypto_tfm *tfm)
{
242 243 244 245 246 247 248 249
	const struct crypto_type *type = tfm->__crt_alg->cra_type;

	if (type) {
		if (type->exit)
			type->exit(tfm);
		return;
	}

L
Linus Torvalds 已提交
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
	switch (crypto_tfm_alg_type(tfm)) {
	case CRYPTO_ALG_TYPE_CIPHER:
		crypto_exit_cipher_ops(tfm);
		break;
		
	case CRYPTO_ALG_TYPE_DIGEST:
		crypto_exit_digest_ops(tfm);
		break;
		
	case CRYPTO_ALG_TYPE_COMPRESS:
		crypto_exit_compress_ops(tfm);
		break;
	
	default:
		BUG();
		
	}
}

269
static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask)
270
{
271
	const struct crypto_type *type_obj = alg->cra_type;
272 273
	unsigned int len;

274
	len = alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1);
275 276
	if (type_obj)
		return len + type_obj->ctxsize(alg, type, mask);
277

278 279 280 281 282
	switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
	default:
		BUG();

	case CRYPTO_ALG_TYPE_CIPHER:
283
		len += crypto_cipher_ctxsize(alg);
284 285 286
		break;
		
	case CRYPTO_ALG_TYPE_DIGEST:
287
		len += crypto_digest_ctxsize(alg);
288 289 290
		break;
		
	case CRYPTO_ALG_TYPE_COMPRESS:
291
		len += crypto_compress_ctxsize(alg);
292 293 294
		break;
	}

295
	return len;
296 297
}

H
Herbert Xu 已提交
298 299 300 301 302 303 304 305
void crypto_shoot_alg(struct crypto_alg *alg)
{
	down_write(&crypto_alg_sem);
	alg->cra_flags |= CRYPTO_ALG_DYING;
	up_write(&crypto_alg_sem);
}
EXPORT_SYMBOL_GPL(crypto_shoot_alg);

306 307
struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
				      u32 mask)
L
Linus Torvalds 已提交
308 309
{
	struct crypto_tfm *tfm = NULL;
310
	unsigned int tfm_size;
H
Herbert Xu 已提交
311
	int err = -ENOMEM;
312

313
	tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, type, mask);
314
	tfm = kzalloc(tfm_size, GFP_KERNEL);
L
Linus Torvalds 已提交
315
	if (tfm == NULL)
316
		goto out_err;
L
Linus Torvalds 已提交
317 318

	tfm->__crt_alg = alg;
H
Herbert Xu 已提交
319

320
	err = crypto_init_ops(tfm, type, mask);
H
Herbert Xu 已提交
321
	if (err)
L
Linus Torvalds 已提交
322
		goto out_free_tfm;
323

H
Herbert Xu 已提交
324 325 326
	if (alg->cra_init && (err = alg->cra_init(tfm))) {
		if (err == -EAGAIN)
			crypto_shoot_alg(alg);
327
		goto cra_init_failed;
H
Herbert Xu 已提交
328
	}
L
Linus Torvalds 已提交
329 330 331

	goto out;

332 333
cra_init_failed:
	crypto_exit_ops(tfm);
L
Linus Torvalds 已提交
334 335
out_free_tfm:
	kfree(tfm);
336
out_err:
H
Herbert Xu 已提交
337
	tfm = ERR_PTR(err);
L
Linus Torvalds 已提交
338 339 340
out:
	return tfm;
}
H
Herbert Xu 已提交
341 342
EXPORT_SYMBOL_GPL(__crypto_alloc_tfm);

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
/*
 *	crypto_alloc_base - Locate algorithm and allocate transform
 *	@alg_name: Name of algorithm
 *	@type: Type of algorithm
 *	@mask: Mask for type comparison
 *
 *	crypto_alloc_base() will first attempt to locate an already loaded
 *	algorithm.  If that fails and the kernel supports dynamically loadable
 *	modules, it will then attempt to load a module of the same name or
 *	alias.  If that fails it will send a query to any loaded crypto manager
 *	to construct an algorithm on the fly.  A refcount is grabbed on the
 *	algorithm which is then associated with the new transform.
 *
 *	The returned transform is of a non-determinate type.  Most people
 *	should use one of the more specific allocation functions such as
 *	crypto_alloc_blkcipher.
 *
 *	In case of error the return value is an error pointer.
 */
struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask)
{
	struct crypto_tfm *tfm;
	int err;

	for (;;) {
		struct crypto_alg *alg;

		alg = crypto_alg_mod_lookup(alg_name, type, mask);
371 372
		if (IS_ERR(alg)) {
			err = PTR_ERR(alg);
373
			goto err;
374
		}
375

376
		tfm = __crypto_alloc_tfm(alg, type, mask);
377
		if (!IS_ERR(tfm))
378
			return tfm;
379 380 381 382 383 384 385 386 387 388 389

		crypto_mod_put(alg);
		err = PTR_ERR(tfm);

err:
		if (err != -EAGAIN)
			break;
		if (signal_pending(current)) {
			err = -EINTR;
			break;
		}
390
	}
391

392
	return ERR_PTR(err);
393 394 395 396 397 398 399 400 401 402
}
EXPORT_SYMBOL_GPL(crypto_alloc_base);
 
/*
 *	crypto_free_tfm - Free crypto transform
 *	@tfm: Transform to free
 *
 *	crypto_free_tfm() frees up the transform and any associated resources,
 *	then drops the refcount on the associated algorithm.
 */
L
Linus Torvalds 已提交
403 404
void crypto_free_tfm(struct crypto_tfm *tfm)
{
405 406 407 408 409 410 411 412
	struct crypto_alg *alg;
	int size;

	if (unlikely(!tfm))
		return;

	alg = tfm->__crt_alg;
	size = sizeof(*tfm) + alg->cra_ctxsize;
L
Linus Torvalds 已提交
413

414 415
	if (alg->cra_exit)
		alg->cra_exit(tfm);
L
Linus Torvalds 已提交
416
	crypto_exit_ops(tfm);
417
	crypto_mod_put(alg);
L
Linus Torvalds 已提交
418 419 420 421 422
	memset(tfm, 0, size);
	kfree(tfm);
}

EXPORT_SYMBOL_GPL(crypto_free_tfm);
423 424 425 426 427 428 429 430 431 432 433 434 435 436

int crypto_has_alg(const char *name, u32 type, u32 mask)
{
	int ret = 0;
	struct crypto_alg *alg = crypto_alg_mod_lookup(name, type, mask);
	
	if (!IS_ERR(alg)) {
		crypto_mod_put(alg);
		ret = 1;
	}
	
	return ret;
}
EXPORT_SYMBOL_GPL(crypto_has_alg);