162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Crypto user configuration API.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011 secunet Security Networks AG
662306a36Sopenharmony_ci * Copyright (C) 2011 Steffen Klassert <steffen.klassert@secunet.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/crypto.h>
1162306a36Sopenharmony_ci#include <linux/cryptouser.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/security.h>
1462306a36Sopenharmony_ci#include <net/netlink.h>
1562306a36Sopenharmony_ci#include <net/net_namespace.h>
1662306a36Sopenharmony_ci#include <net/sock.h>
1762306a36Sopenharmony_ci#include <crypto/internal/skcipher.h>
1862306a36Sopenharmony_ci#include <crypto/internal/rng.h>
1962306a36Sopenharmony_ci#include <crypto/akcipher.h>
2062306a36Sopenharmony_ci#include <crypto/kpp.h>
2162306a36Sopenharmony_ci#include <crypto/internal/cryptouser.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "internal.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define null_terminated(x)	(strnlen(x, sizeof(x)) < sizeof(x))
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic DEFINE_MUTEX(crypto_cfg_mutex);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct crypto_dump_info {
3062306a36Sopenharmony_ci	struct sk_buff *in_skb;
3162306a36Sopenharmony_ci	struct sk_buff *out_skb;
3262306a36Sopenharmony_ci	u32 nlmsg_seq;
3362306a36Sopenharmony_ci	u16 nlmsg_flags;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct crypto_alg *crypto_alg_match(struct crypto_user_alg *p, int exact)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct crypto_alg *q, *alg = NULL;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	down_read(&crypto_alg_sem);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	list_for_each_entry(q, &crypto_alg_list, cra_list) {
4362306a36Sopenharmony_ci		int match = 0;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci		if (crypto_is_larval(q))
4662306a36Sopenharmony_ci			continue;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		if ((q->cra_flags ^ p->cru_type) & p->cru_mask)
4962306a36Sopenharmony_ci			continue;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		if (strlen(p->cru_driver_name))
5262306a36Sopenharmony_ci			match = !strcmp(q->cra_driver_name,
5362306a36Sopenharmony_ci					p->cru_driver_name);
5462306a36Sopenharmony_ci		else if (!exact)
5562306a36Sopenharmony_ci			match = !strcmp(q->cra_name, p->cru_name);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		if (!match)
5862306a36Sopenharmony_ci			continue;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		if (unlikely(!crypto_mod_get(q)))
6162306a36Sopenharmony_ci			continue;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		alg = q;
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	up_read(&crypto_alg_sem);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return alg;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct crypto_report_cipher rcipher;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	memset(&rcipher, 0, sizeof(rcipher));
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	strscpy(rcipher.type, "cipher", sizeof(rcipher.type));
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	rcipher.blocksize = alg->cra_blocksize;
8162306a36Sopenharmony_ci	rcipher.min_keysize = alg->cra_cipher.cia_min_keysize;
8262306a36Sopenharmony_ci	rcipher.max_keysize = alg->cra_cipher.cia_max_keysize;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return nla_put(skb, CRYPTOCFGA_REPORT_CIPHER,
8562306a36Sopenharmony_ci		       sizeof(rcipher), &rcipher);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct crypto_report_comp rcomp;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	memset(&rcomp, 0, sizeof(rcomp));
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	strscpy(rcomp.type, "compression", sizeof(rcomp.type));
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS, sizeof(rcomp), &rcomp);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int crypto_report_one(struct crypto_alg *alg,
10062306a36Sopenharmony_ci			     struct crypto_user_alg *ualg, struct sk_buff *skb)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	memset(ualg, 0, sizeof(*ualg));
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	strscpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name));
10562306a36Sopenharmony_ci	strscpy(ualg->cru_driver_name, alg->cra_driver_name,
10662306a36Sopenharmony_ci		sizeof(ualg->cru_driver_name));
10762306a36Sopenharmony_ci	strscpy(ualg->cru_module_name, module_name(alg->cra_module),
10862306a36Sopenharmony_ci		sizeof(ualg->cru_module_name));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ualg->cru_type = 0;
11162306a36Sopenharmony_ci	ualg->cru_mask = 0;
11262306a36Sopenharmony_ci	ualg->cru_flags = alg->cra_flags;
11362306a36Sopenharmony_ci	ualg->cru_refcnt = refcount_read(&alg->cra_refcnt);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (nla_put_u32(skb, CRYPTOCFGA_PRIORITY_VAL, alg->cra_priority))
11662306a36Sopenharmony_ci		goto nla_put_failure;
11762306a36Sopenharmony_ci	if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
11862306a36Sopenharmony_ci		struct crypto_report_larval rl;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		memset(&rl, 0, sizeof(rl));
12162306a36Sopenharmony_ci		strscpy(rl.type, "larval", sizeof(rl.type));
12262306a36Sopenharmony_ci		if (nla_put(skb, CRYPTOCFGA_REPORT_LARVAL, sizeof(rl), &rl))
12362306a36Sopenharmony_ci			goto nla_put_failure;
12462306a36Sopenharmony_ci		goto out;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (alg->cra_type && alg->cra_type->report) {
12862306a36Sopenharmony_ci		if (alg->cra_type->report(skb, alg))
12962306a36Sopenharmony_ci			goto nla_put_failure;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		goto out;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) {
13562306a36Sopenharmony_ci	case CRYPTO_ALG_TYPE_CIPHER:
13662306a36Sopenharmony_ci		if (crypto_report_cipher(skb, alg))
13762306a36Sopenharmony_ci			goto nla_put_failure;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		break;
14062306a36Sopenharmony_ci	case CRYPTO_ALG_TYPE_COMPRESS:
14162306a36Sopenharmony_ci		if (crypto_report_comp(skb, alg))
14262306a36Sopenharmony_ci			goto nla_put_failure;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciout:
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cinla_put_failure:
15162306a36Sopenharmony_ci	return -EMSGSIZE;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int crypto_report_alg(struct crypto_alg *alg,
15562306a36Sopenharmony_ci			     struct crypto_dump_info *info)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct sk_buff *in_skb = info->in_skb;
15862306a36Sopenharmony_ci	struct sk_buff *skb = info->out_skb;
15962306a36Sopenharmony_ci	struct nlmsghdr *nlh;
16062306a36Sopenharmony_ci	struct crypto_user_alg *ualg;
16162306a36Sopenharmony_ci	int err = 0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, info->nlmsg_seq,
16462306a36Sopenharmony_ci			CRYPTO_MSG_GETALG, sizeof(*ualg), info->nlmsg_flags);
16562306a36Sopenharmony_ci	if (!nlh) {
16662306a36Sopenharmony_ci		err = -EMSGSIZE;
16762306a36Sopenharmony_ci		goto out;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	ualg = nlmsg_data(nlh);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	err = crypto_report_one(alg, ualg, skb);
17362306a36Sopenharmony_ci	if (err) {
17462306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
17562306a36Sopenharmony_ci		goto out;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ciout:
18162306a36Sopenharmony_ci	return err;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
18562306a36Sopenharmony_ci			 struct nlattr **attrs)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
18862306a36Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(in_nlh);
18962306a36Sopenharmony_ci	struct crypto_alg *alg;
19062306a36Sopenharmony_ci	struct sk_buff *skb;
19162306a36Sopenharmony_ci	struct crypto_dump_info info;
19262306a36Sopenharmony_ci	int err;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
19562306a36Sopenharmony_ci		return -EINVAL;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	alg = crypto_alg_match(p, 0);
19862306a36Sopenharmony_ci	if (!alg)
19962306a36Sopenharmony_ci		return -ENOENT;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	err = -ENOMEM;
20262306a36Sopenharmony_ci	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
20362306a36Sopenharmony_ci	if (!skb)
20462306a36Sopenharmony_ci		goto drop_alg;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	info.in_skb = in_skb;
20762306a36Sopenharmony_ci	info.out_skb = skb;
20862306a36Sopenharmony_ci	info.nlmsg_seq = in_nlh->nlmsg_seq;
20962306a36Sopenharmony_ci	info.nlmsg_flags = 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	err = crypto_report_alg(alg, &info);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cidrop_alg:
21462306a36Sopenharmony_ci	crypto_mod_put(alg);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (err) {
21762306a36Sopenharmony_ci		kfree_skb(skb);
21862306a36Sopenharmony_ci		return err;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return nlmsg_unicast(net->crypto_nlsk, skb, NETLINK_CB(in_skb).portid);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int crypto_dump_report(struct sk_buff *skb, struct netlink_callback *cb)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	const size_t start_pos = cb->args[0];
22762306a36Sopenharmony_ci	size_t pos = 0;
22862306a36Sopenharmony_ci	struct crypto_dump_info info;
22962306a36Sopenharmony_ci	struct crypto_alg *alg;
23062306a36Sopenharmony_ci	int res;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	info.in_skb = cb->skb;
23362306a36Sopenharmony_ci	info.out_skb = skb;
23462306a36Sopenharmony_ci	info.nlmsg_seq = cb->nlh->nlmsg_seq;
23562306a36Sopenharmony_ci	info.nlmsg_flags = NLM_F_MULTI;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	down_read(&crypto_alg_sem);
23862306a36Sopenharmony_ci	list_for_each_entry(alg, &crypto_alg_list, cra_list) {
23962306a36Sopenharmony_ci		if (pos >= start_pos) {
24062306a36Sopenharmony_ci			res = crypto_report_alg(alg, &info);
24162306a36Sopenharmony_ci			if (res == -EMSGSIZE)
24262306a36Sopenharmony_ci				break;
24362306a36Sopenharmony_ci			if (res)
24462306a36Sopenharmony_ci				goto out;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci		pos++;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci	cb->args[0] = pos;
24962306a36Sopenharmony_ci	res = skb->len;
25062306a36Sopenharmony_ciout:
25162306a36Sopenharmony_ci	up_read(&crypto_alg_sem);
25262306a36Sopenharmony_ci	return res;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int crypto_dump_report_done(struct netlink_callback *cb)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int crypto_update_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
26162306a36Sopenharmony_ci			     struct nlattr **attrs)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct crypto_alg *alg;
26462306a36Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(nlh);
26562306a36Sopenharmony_ci	struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL];
26662306a36Sopenharmony_ci	LIST_HEAD(list);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
26962306a36Sopenharmony_ci		return -EPERM;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
27262306a36Sopenharmony_ci		return -EINVAL;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (priority && !strlen(p->cru_driver_name))
27562306a36Sopenharmony_ci		return -EINVAL;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	alg = crypto_alg_match(p, 1);
27862306a36Sopenharmony_ci	if (!alg)
27962306a36Sopenharmony_ci		return -ENOENT;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	down_write(&crypto_alg_sem);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	crypto_remove_spawns(alg, &list, NULL);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (priority)
28662306a36Sopenharmony_ci		alg->cra_priority = nla_get_u32(priority);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	up_write(&crypto_alg_sem);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	crypto_mod_put(alg);
29162306a36Sopenharmony_ci	crypto_remove_final(&list);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
29762306a36Sopenharmony_ci			  struct nlattr **attrs)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct crypto_alg *alg;
30062306a36Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(nlh);
30162306a36Sopenharmony_ci	int err;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
30462306a36Sopenharmony_ci		return -EPERM;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
30762306a36Sopenharmony_ci		return -EINVAL;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	alg = crypto_alg_match(p, 1);
31062306a36Sopenharmony_ci	if (!alg)
31162306a36Sopenharmony_ci		return -ENOENT;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* We can not unregister core algorithms such as aes-generic.
31462306a36Sopenharmony_ci	 * We would loose the reference in the crypto_alg_list to this algorithm
31562306a36Sopenharmony_ci	 * if we try to unregister. Unregistering such an algorithm without
31662306a36Sopenharmony_ci	 * removing the module is not possible, so we restrict to crypto
31762306a36Sopenharmony_ci	 * instances that are build from templates. */
31862306a36Sopenharmony_ci	err = -EINVAL;
31962306a36Sopenharmony_ci	if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE))
32062306a36Sopenharmony_ci		goto drop_alg;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	err = -EBUSY;
32362306a36Sopenharmony_ci	if (refcount_read(&alg->cra_refcnt) > 2)
32462306a36Sopenharmony_ci		goto drop_alg;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	crypto_unregister_instance((struct crypto_instance *)alg);
32762306a36Sopenharmony_ci	err = 0;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cidrop_alg:
33062306a36Sopenharmony_ci	crypto_mod_put(alg);
33162306a36Sopenharmony_ci	return err;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
33562306a36Sopenharmony_ci			  struct nlattr **attrs)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int exact = 0;
33862306a36Sopenharmony_ci	const char *name;
33962306a36Sopenharmony_ci	struct crypto_alg *alg;
34062306a36Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(nlh);
34162306a36Sopenharmony_ci	struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL];
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
34462306a36Sopenharmony_ci		return -EPERM;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
34762306a36Sopenharmony_ci		return -EINVAL;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (strlen(p->cru_driver_name))
35062306a36Sopenharmony_ci		exact = 1;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (priority && !exact)
35362306a36Sopenharmony_ci		return -EINVAL;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	alg = crypto_alg_match(p, exact);
35662306a36Sopenharmony_ci	if (alg) {
35762306a36Sopenharmony_ci		crypto_mod_put(alg);
35862306a36Sopenharmony_ci		return -EEXIST;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (strlen(p->cru_driver_name))
36262306a36Sopenharmony_ci		name = p->cru_driver_name;
36362306a36Sopenharmony_ci	else
36462306a36Sopenharmony_ci		name = p->cru_name;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	alg = crypto_alg_mod_lookup(name, p->cru_type, p->cru_mask);
36762306a36Sopenharmony_ci	if (IS_ERR(alg))
36862306a36Sopenharmony_ci		return PTR_ERR(alg);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	down_write(&crypto_alg_sem);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (priority)
37362306a36Sopenharmony_ci		alg->cra_priority = nla_get_u32(priority);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	up_write(&crypto_alg_sem);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	crypto_mod_put(alg);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic int crypto_del_rng(struct sk_buff *skb, struct nlmsghdr *nlh,
38362306a36Sopenharmony_ci			  struct nlattr **attrs)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
38662306a36Sopenharmony_ci		return -EPERM;
38762306a36Sopenharmony_ci	return crypto_del_default_rng();
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci#define MSGSIZE(type) sizeof(struct type)
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic const int crypto_msg_min[CRYPTO_NR_MSGTYPES] = {
39362306a36Sopenharmony_ci	[CRYPTO_MSG_NEWALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
39462306a36Sopenharmony_ci	[CRYPTO_MSG_DELALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
39562306a36Sopenharmony_ci	[CRYPTO_MSG_UPDATEALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
39662306a36Sopenharmony_ci	[CRYPTO_MSG_GETALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
39762306a36Sopenharmony_ci	[CRYPTO_MSG_DELRNG	- CRYPTO_MSG_BASE] = 0,
39862306a36Sopenharmony_ci	[CRYPTO_MSG_GETSTAT	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
39962306a36Sopenharmony_ci};
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic const struct nla_policy crypto_policy[CRYPTOCFGA_MAX+1] = {
40262306a36Sopenharmony_ci	[CRYPTOCFGA_PRIORITY_VAL]   = { .type = NLA_U32},
40362306a36Sopenharmony_ci};
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci#undef MSGSIZE
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic const struct crypto_link {
40862306a36Sopenharmony_ci	int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
40962306a36Sopenharmony_ci	int (*dump)(struct sk_buff *, struct netlink_callback *);
41062306a36Sopenharmony_ci	int (*done)(struct netlink_callback *);
41162306a36Sopenharmony_ci} crypto_dispatch[CRYPTO_NR_MSGTYPES] = {
41262306a36Sopenharmony_ci	[CRYPTO_MSG_NEWALG	- CRYPTO_MSG_BASE] = { .doit = crypto_add_alg},
41362306a36Sopenharmony_ci	[CRYPTO_MSG_DELALG	- CRYPTO_MSG_BASE] = { .doit = crypto_del_alg},
41462306a36Sopenharmony_ci	[CRYPTO_MSG_UPDATEALG	- CRYPTO_MSG_BASE] = { .doit = crypto_update_alg},
41562306a36Sopenharmony_ci	[CRYPTO_MSG_GETALG	- CRYPTO_MSG_BASE] = { .doit = crypto_report,
41662306a36Sopenharmony_ci						       .dump = crypto_dump_report,
41762306a36Sopenharmony_ci						       .done = crypto_dump_report_done},
41862306a36Sopenharmony_ci	[CRYPTO_MSG_DELRNG	- CRYPTO_MSG_BASE] = { .doit = crypto_del_rng },
41962306a36Sopenharmony_ci	[CRYPTO_MSG_GETSTAT	- CRYPTO_MSG_BASE] = { .doit = crypto_reportstat},
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
42362306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
42662306a36Sopenharmony_ci	struct nlattr *attrs[CRYPTOCFGA_MAX+1];
42762306a36Sopenharmony_ci	const struct crypto_link *link;
42862306a36Sopenharmony_ci	int type, err;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	type = nlh->nlmsg_type;
43162306a36Sopenharmony_ci	if (type > CRYPTO_MSG_MAX)
43262306a36Sopenharmony_ci		return -EINVAL;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	type -= CRYPTO_MSG_BASE;
43562306a36Sopenharmony_ci	link = &crypto_dispatch[type];
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if ((type == (CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE) &&
43862306a36Sopenharmony_ci	    (nlh->nlmsg_flags & NLM_F_DUMP))) {
43962306a36Sopenharmony_ci		struct crypto_alg *alg;
44062306a36Sopenharmony_ci		unsigned long dump_alloc = 0;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		if (link->dump == NULL)
44362306a36Sopenharmony_ci			return -EINVAL;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		down_read(&crypto_alg_sem);
44662306a36Sopenharmony_ci		list_for_each_entry(alg, &crypto_alg_list, cra_list)
44762306a36Sopenharmony_ci			dump_alloc += CRYPTO_REPORT_MAXSIZE;
44862306a36Sopenharmony_ci		up_read(&crypto_alg_sem);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		{
45162306a36Sopenharmony_ci			struct netlink_dump_control c = {
45262306a36Sopenharmony_ci				.dump = link->dump,
45362306a36Sopenharmony_ci				.done = link->done,
45462306a36Sopenharmony_ci				.min_dump_alloc = min(dump_alloc, 65535UL),
45562306a36Sopenharmony_ci			};
45662306a36Sopenharmony_ci			err = netlink_dump_start(net->crypto_nlsk, skb, nlh, &c);
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		return err;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, crypto_msg_min[type], attrs,
46362306a36Sopenharmony_ci				     CRYPTOCFGA_MAX, crypto_policy, extack);
46462306a36Sopenharmony_ci	if (err < 0)
46562306a36Sopenharmony_ci		return err;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (link->doit == NULL)
46862306a36Sopenharmony_ci		return -EINVAL;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return link->doit(skb, nlh, attrs);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic void crypto_netlink_rcv(struct sk_buff *skb)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	mutex_lock(&crypto_cfg_mutex);
47662306a36Sopenharmony_ci	netlink_rcv_skb(skb, &crypto_user_rcv_msg);
47762306a36Sopenharmony_ci	mutex_unlock(&crypto_cfg_mutex);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int __net_init crypto_netlink_init(struct net *net)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct netlink_kernel_cfg cfg = {
48362306a36Sopenharmony_ci		.input	= crypto_netlink_rcv,
48462306a36Sopenharmony_ci	};
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	net->crypto_nlsk = netlink_kernel_create(net, NETLINK_CRYPTO, &cfg);
48762306a36Sopenharmony_ci	return net->crypto_nlsk == NULL ? -ENOMEM : 0;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic void __net_exit crypto_netlink_exit(struct net *net)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	netlink_kernel_release(net->crypto_nlsk);
49362306a36Sopenharmony_ci	net->crypto_nlsk = NULL;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic struct pernet_operations crypto_netlink_net_ops = {
49762306a36Sopenharmony_ci	.init = crypto_netlink_init,
49862306a36Sopenharmony_ci	.exit = crypto_netlink_exit,
49962306a36Sopenharmony_ci};
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic int __init crypto_user_init(void)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	return register_pernet_subsys(&crypto_netlink_net_ops);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic void __exit crypto_user_exit(void)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	unregister_pernet_subsys(&crypto_netlink_net_ops);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cimodule_init(crypto_user_init);
51262306a36Sopenharmony_cimodule_exit(crypto_user_exit);
51362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
51462306a36Sopenharmony_ciMODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>");
51562306a36Sopenharmony_ciMODULE_DESCRIPTION("Crypto userspace configuration API");
51662306a36Sopenharmony_ciMODULE_ALIAS("net-pf-16-proto-21");
517