18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Crypto user configuration API.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 secunet Security Networks AG
68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Steffen Klassert <steffen.klassert@secunet.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/crypto.h>
118c2ecf20Sopenharmony_ci#include <linux/cryptouser.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci#include <linux/security.h>
148c2ecf20Sopenharmony_ci#include <net/netlink.h>
158c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
168c2ecf20Sopenharmony_ci#include <net/sock.h>
178c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h>
188c2ecf20Sopenharmony_ci#include <crypto/internal/rng.h>
198c2ecf20Sopenharmony_ci#include <crypto/akcipher.h>
208c2ecf20Sopenharmony_ci#include <crypto/kpp.h>
218c2ecf20Sopenharmony_ci#include <crypto/internal/cryptouser.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "internal.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define null_terminated(x)	(strnlen(x, sizeof(x)) < sizeof(x))
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(crypto_cfg_mutex);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct crypto_dump_info {
308c2ecf20Sopenharmony_ci	struct sk_buff *in_skb;
318c2ecf20Sopenharmony_ci	struct sk_buff *out_skb;
328c2ecf20Sopenharmony_ci	u32 nlmsg_seq;
338c2ecf20Sopenharmony_ci	u16 nlmsg_flags;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct crypto_alg *crypto_alg_match(struct crypto_user_alg *p, int exact)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct crypto_alg *q, *alg = NULL;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	down_read(&crypto_alg_sem);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	list_for_each_entry(q, &crypto_alg_list, cra_list) {
438c2ecf20Sopenharmony_ci		int match = 0;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		if (crypto_is_larval(q))
468c2ecf20Sopenharmony_ci			continue;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci		if ((q->cra_flags ^ p->cru_type) & p->cru_mask)
498c2ecf20Sopenharmony_ci			continue;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		if (strlen(p->cru_driver_name))
528c2ecf20Sopenharmony_ci			match = !strcmp(q->cra_driver_name,
538c2ecf20Sopenharmony_ci					p->cru_driver_name);
548c2ecf20Sopenharmony_ci		else if (!exact)
558c2ecf20Sopenharmony_ci			match = !strcmp(q->cra_name, p->cru_name);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		if (!match)
588c2ecf20Sopenharmony_ci			continue;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		if (unlikely(!crypto_mod_get(q)))
618c2ecf20Sopenharmony_ci			continue;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		alg = q;
648c2ecf20Sopenharmony_ci		break;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	up_read(&crypto_alg_sem);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return alg;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct crypto_report_cipher rcipher;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	memset(&rcipher, 0, sizeof(rcipher));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	strscpy(rcipher.type, "cipher", sizeof(rcipher.type));
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	rcipher.blocksize = alg->cra_blocksize;
818c2ecf20Sopenharmony_ci	rcipher.min_keysize = alg->cra_cipher.cia_min_keysize;
828c2ecf20Sopenharmony_ci	rcipher.max_keysize = alg->cra_cipher.cia_max_keysize;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return nla_put(skb, CRYPTOCFGA_REPORT_CIPHER,
858c2ecf20Sopenharmony_ci		       sizeof(rcipher), &rcipher);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct crypto_report_comp rcomp;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	memset(&rcomp, 0, sizeof(rcomp));
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	strscpy(rcomp.type, "compression", sizeof(rcomp.type));
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS, sizeof(rcomp), &rcomp);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int crypto_report_one(struct crypto_alg *alg,
1008c2ecf20Sopenharmony_ci			     struct crypto_user_alg *ualg, struct sk_buff *skb)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	memset(ualg, 0, sizeof(*ualg));
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	strscpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name));
1058c2ecf20Sopenharmony_ci	strscpy(ualg->cru_driver_name, alg->cra_driver_name,
1068c2ecf20Sopenharmony_ci		sizeof(ualg->cru_driver_name));
1078c2ecf20Sopenharmony_ci	strscpy(ualg->cru_module_name, module_name(alg->cra_module),
1088c2ecf20Sopenharmony_ci		sizeof(ualg->cru_module_name));
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	ualg->cru_type = 0;
1118c2ecf20Sopenharmony_ci	ualg->cru_mask = 0;
1128c2ecf20Sopenharmony_ci	ualg->cru_flags = alg->cra_flags;
1138c2ecf20Sopenharmony_ci	ualg->cru_refcnt = refcount_read(&alg->cra_refcnt);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, CRYPTOCFGA_PRIORITY_VAL, alg->cra_priority))
1168c2ecf20Sopenharmony_ci		goto nla_put_failure;
1178c2ecf20Sopenharmony_ci	if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
1188c2ecf20Sopenharmony_ci		struct crypto_report_larval rl;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		memset(&rl, 0, sizeof(rl));
1218c2ecf20Sopenharmony_ci		strscpy(rl.type, "larval", sizeof(rl.type));
1228c2ecf20Sopenharmony_ci		if (nla_put(skb, CRYPTOCFGA_REPORT_LARVAL, sizeof(rl), &rl))
1238c2ecf20Sopenharmony_ci			goto nla_put_failure;
1248c2ecf20Sopenharmony_ci		goto out;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (alg->cra_type && alg->cra_type->report) {
1288c2ecf20Sopenharmony_ci		if (alg->cra_type->report(skb, alg))
1298c2ecf20Sopenharmony_ci			goto nla_put_failure;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		goto out;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) {
1358c2ecf20Sopenharmony_ci	case CRYPTO_ALG_TYPE_CIPHER:
1368c2ecf20Sopenharmony_ci		if (crypto_report_cipher(skb, alg))
1378c2ecf20Sopenharmony_ci			goto nla_put_failure;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	case CRYPTO_ALG_TYPE_COMPRESS:
1418c2ecf20Sopenharmony_ci		if (crypto_report_comp(skb, alg))
1428c2ecf20Sopenharmony_ci			goto nla_put_failure;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ciout:
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cinla_put_failure:
1518c2ecf20Sopenharmony_ci	return -EMSGSIZE;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int crypto_report_alg(struct crypto_alg *alg,
1558c2ecf20Sopenharmony_ci			     struct crypto_dump_info *info)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct sk_buff *in_skb = info->in_skb;
1588c2ecf20Sopenharmony_ci	struct sk_buff *skb = info->out_skb;
1598c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
1608c2ecf20Sopenharmony_ci	struct crypto_user_alg *ualg;
1618c2ecf20Sopenharmony_ci	int err = 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, info->nlmsg_seq,
1648c2ecf20Sopenharmony_ci			CRYPTO_MSG_GETALG, sizeof(*ualg), info->nlmsg_flags);
1658c2ecf20Sopenharmony_ci	if (!nlh) {
1668c2ecf20Sopenharmony_ci		err = -EMSGSIZE;
1678c2ecf20Sopenharmony_ci		goto out;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ualg = nlmsg_data(nlh);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	err = crypto_report_one(alg, ualg, skb);
1738c2ecf20Sopenharmony_ci	if (err) {
1748c2ecf20Sopenharmony_ci		nlmsg_cancel(skb, nlh);
1758c2ecf20Sopenharmony_ci		goto out;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ciout:
1818c2ecf20Sopenharmony_ci	return err;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
1858c2ecf20Sopenharmony_ci			 struct nlattr **attrs)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
1888c2ecf20Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(in_nlh);
1898c2ecf20Sopenharmony_ci	struct crypto_alg *alg;
1908c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1918c2ecf20Sopenharmony_ci	struct crypto_dump_info info;
1928c2ecf20Sopenharmony_ci	int err;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
1958c2ecf20Sopenharmony_ci		return -EINVAL;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	alg = crypto_alg_match(p, 0);
1988c2ecf20Sopenharmony_ci	if (!alg)
1998c2ecf20Sopenharmony_ci		return -ENOENT;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	err = -ENOMEM;
2028c2ecf20Sopenharmony_ci	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2038c2ecf20Sopenharmony_ci	if (!skb)
2048c2ecf20Sopenharmony_ci		goto drop_alg;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	info.in_skb = in_skb;
2078c2ecf20Sopenharmony_ci	info.out_skb = skb;
2088c2ecf20Sopenharmony_ci	info.nlmsg_seq = in_nlh->nlmsg_seq;
2098c2ecf20Sopenharmony_ci	info.nlmsg_flags = 0;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	err = crypto_report_alg(alg, &info);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cidrop_alg:
2148c2ecf20Sopenharmony_ci	crypto_mod_put(alg);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (err) {
2178c2ecf20Sopenharmony_ci		kfree_skb(skb);
2188c2ecf20Sopenharmony_ci		return err;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return nlmsg_unicast(net->crypto_nlsk, skb, NETLINK_CB(in_skb).portid);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int crypto_dump_report(struct sk_buff *skb, struct netlink_callback *cb)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	const size_t start_pos = cb->args[0];
2278c2ecf20Sopenharmony_ci	size_t pos = 0;
2288c2ecf20Sopenharmony_ci	struct crypto_dump_info info;
2298c2ecf20Sopenharmony_ci	struct crypto_alg *alg;
2308c2ecf20Sopenharmony_ci	int res;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	info.in_skb = cb->skb;
2338c2ecf20Sopenharmony_ci	info.out_skb = skb;
2348c2ecf20Sopenharmony_ci	info.nlmsg_seq = cb->nlh->nlmsg_seq;
2358c2ecf20Sopenharmony_ci	info.nlmsg_flags = NLM_F_MULTI;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	down_read(&crypto_alg_sem);
2388c2ecf20Sopenharmony_ci	list_for_each_entry(alg, &crypto_alg_list, cra_list) {
2398c2ecf20Sopenharmony_ci		if (pos >= start_pos) {
2408c2ecf20Sopenharmony_ci			res = crypto_report_alg(alg, &info);
2418c2ecf20Sopenharmony_ci			if (res == -EMSGSIZE)
2428c2ecf20Sopenharmony_ci				break;
2438c2ecf20Sopenharmony_ci			if (res)
2448c2ecf20Sopenharmony_ci				goto out;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci		pos++;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci	cb->args[0] = pos;
2498c2ecf20Sopenharmony_ci	res = skb->len;
2508c2ecf20Sopenharmony_ciout:
2518c2ecf20Sopenharmony_ci	up_read(&crypto_alg_sem);
2528c2ecf20Sopenharmony_ci	return res;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int crypto_dump_report_done(struct netlink_callback *cb)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int crypto_update_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
2618c2ecf20Sopenharmony_ci			     struct nlattr **attrs)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct crypto_alg *alg;
2648c2ecf20Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(nlh);
2658c2ecf20Sopenharmony_ci	struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL];
2668c2ecf20Sopenharmony_ci	LIST_HEAD(list);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
2698c2ecf20Sopenharmony_ci		return -EPERM;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
2728c2ecf20Sopenharmony_ci		return -EINVAL;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (priority && !strlen(p->cru_driver_name))
2758c2ecf20Sopenharmony_ci		return -EINVAL;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	alg = crypto_alg_match(p, 1);
2788c2ecf20Sopenharmony_ci	if (!alg)
2798c2ecf20Sopenharmony_ci		return -ENOENT;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	down_write(&crypto_alg_sem);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	crypto_remove_spawns(alg, &list, NULL);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (priority)
2868c2ecf20Sopenharmony_ci		alg->cra_priority = nla_get_u32(priority);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	up_write(&crypto_alg_sem);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	crypto_mod_put(alg);
2918c2ecf20Sopenharmony_ci	crypto_remove_final(&list);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return 0;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
2978c2ecf20Sopenharmony_ci			  struct nlattr **attrs)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct crypto_alg *alg;
3008c2ecf20Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(nlh);
3018c2ecf20Sopenharmony_ci	int err;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
3048c2ecf20Sopenharmony_ci		return -EPERM;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
3078c2ecf20Sopenharmony_ci		return -EINVAL;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	alg = crypto_alg_match(p, 1);
3108c2ecf20Sopenharmony_ci	if (!alg)
3118c2ecf20Sopenharmony_ci		return -ENOENT;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* We can not unregister core algorithms such as aes-generic.
3148c2ecf20Sopenharmony_ci	 * We would loose the reference in the crypto_alg_list to this algorithm
3158c2ecf20Sopenharmony_ci	 * if we try to unregister. Unregistering such an algorithm without
3168c2ecf20Sopenharmony_ci	 * removing the module is not possible, so we restrict to crypto
3178c2ecf20Sopenharmony_ci	 * instances that are build from templates. */
3188c2ecf20Sopenharmony_ci	err = -EINVAL;
3198c2ecf20Sopenharmony_ci	if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE))
3208c2ecf20Sopenharmony_ci		goto drop_alg;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	err = -EBUSY;
3238c2ecf20Sopenharmony_ci	if (refcount_read(&alg->cra_refcnt) > 2)
3248c2ecf20Sopenharmony_ci		goto drop_alg;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	crypto_unregister_instance((struct crypto_instance *)alg);
3278c2ecf20Sopenharmony_ci	err = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cidrop_alg:
3308c2ecf20Sopenharmony_ci	crypto_mod_put(alg);
3318c2ecf20Sopenharmony_ci	return err;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
3358c2ecf20Sopenharmony_ci			  struct nlattr **attrs)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	int exact = 0;
3388c2ecf20Sopenharmony_ci	const char *name;
3398c2ecf20Sopenharmony_ci	struct crypto_alg *alg;
3408c2ecf20Sopenharmony_ci	struct crypto_user_alg *p = nlmsg_data(nlh);
3418c2ecf20Sopenharmony_ci	struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL];
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
3448c2ecf20Sopenharmony_ci		return -EPERM;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))
3478c2ecf20Sopenharmony_ci		return -EINVAL;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if (strlen(p->cru_driver_name))
3508c2ecf20Sopenharmony_ci		exact = 1;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (priority && !exact)
3538c2ecf20Sopenharmony_ci		return -EINVAL;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	alg = crypto_alg_match(p, exact);
3568c2ecf20Sopenharmony_ci	if (alg) {
3578c2ecf20Sopenharmony_ci		crypto_mod_put(alg);
3588c2ecf20Sopenharmony_ci		return -EEXIST;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (strlen(p->cru_driver_name))
3628c2ecf20Sopenharmony_ci		name = p->cru_driver_name;
3638c2ecf20Sopenharmony_ci	else
3648c2ecf20Sopenharmony_ci		name = p->cru_name;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	alg = crypto_alg_mod_lookup(name, p->cru_type, p->cru_mask);
3678c2ecf20Sopenharmony_ci	if (IS_ERR(alg))
3688c2ecf20Sopenharmony_ci		return PTR_ERR(alg);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	down_write(&crypto_alg_sem);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (priority)
3738c2ecf20Sopenharmony_ci		alg->cra_priority = nla_get_u32(priority);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	up_write(&crypto_alg_sem);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	crypto_mod_put(alg);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int crypto_del_rng(struct sk_buff *skb, struct nlmsghdr *nlh,
3838c2ecf20Sopenharmony_ci			  struct nlattr **attrs)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
3868c2ecf20Sopenharmony_ci		return -EPERM;
3878c2ecf20Sopenharmony_ci	return crypto_del_default_rng();
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci#define MSGSIZE(type) sizeof(struct type)
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic const int crypto_msg_min[CRYPTO_NR_MSGTYPES] = {
3938c2ecf20Sopenharmony_ci	[CRYPTO_MSG_NEWALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
3948c2ecf20Sopenharmony_ci	[CRYPTO_MSG_DELALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
3958c2ecf20Sopenharmony_ci	[CRYPTO_MSG_UPDATEALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
3968c2ecf20Sopenharmony_ci	[CRYPTO_MSG_GETALG	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
3978c2ecf20Sopenharmony_ci	[CRYPTO_MSG_DELRNG	- CRYPTO_MSG_BASE] = 0,
3988c2ecf20Sopenharmony_ci	[CRYPTO_MSG_GETSTAT	- CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
3998c2ecf20Sopenharmony_ci};
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct nla_policy crypto_policy[CRYPTOCFGA_MAX+1] = {
4028c2ecf20Sopenharmony_ci	[CRYPTOCFGA_PRIORITY_VAL]   = { .type = NLA_U32},
4038c2ecf20Sopenharmony_ci};
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci#undef MSGSIZE
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic const struct crypto_link {
4088c2ecf20Sopenharmony_ci	int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
4098c2ecf20Sopenharmony_ci	int (*dump)(struct sk_buff *, struct netlink_callback *);
4108c2ecf20Sopenharmony_ci	int (*done)(struct netlink_callback *);
4118c2ecf20Sopenharmony_ci} crypto_dispatch[CRYPTO_NR_MSGTYPES] = {
4128c2ecf20Sopenharmony_ci	[CRYPTO_MSG_NEWALG	- CRYPTO_MSG_BASE] = { .doit = crypto_add_alg},
4138c2ecf20Sopenharmony_ci	[CRYPTO_MSG_DELALG	- CRYPTO_MSG_BASE] = { .doit = crypto_del_alg},
4148c2ecf20Sopenharmony_ci	[CRYPTO_MSG_UPDATEALG	- CRYPTO_MSG_BASE] = { .doit = crypto_update_alg},
4158c2ecf20Sopenharmony_ci	[CRYPTO_MSG_GETALG	- CRYPTO_MSG_BASE] = { .doit = crypto_report,
4168c2ecf20Sopenharmony_ci						       .dump = crypto_dump_report,
4178c2ecf20Sopenharmony_ci						       .done = crypto_dump_report_done},
4188c2ecf20Sopenharmony_ci	[CRYPTO_MSG_DELRNG	- CRYPTO_MSG_BASE] = { .doit = crypto_del_rng },
4198c2ecf20Sopenharmony_ci	[CRYPTO_MSG_GETSTAT	- CRYPTO_MSG_BASE] = { .doit = crypto_reportstat},
4208c2ecf20Sopenharmony_ci};
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4238c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
4268c2ecf20Sopenharmony_ci	struct nlattr *attrs[CRYPTOCFGA_MAX+1];
4278c2ecf20Sopenharmony_ci	const struct crypto_link *link;
4288c2ecf20Sopenharmony_ci	int type, err;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	type = nlh->nlmsg_type;
4318c2ecf20Sopenharmony_ci	if (type > CRYPTO_MSG_MAX)
4328c2ecf20Sopenharmony_ci		return -EINVAL;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	type -= CRYPTO_MSG_BASE;
4358c2ecf20Sopenharmony_ci	link = &crypto_dispatch[type];
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if ((type == (CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE) &&
4388c2ecf20Sopenharmony_ci	    (nlh->nlmsg_flags & NLM_F_DUMP))) {
4398c2ecf20Sopenharmony_ci		struct crypto_alg *alg;
4408c2ecf20Sopenharmony_ci		unsigned long dump_alloc = 0;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		if (link->dump == NULL)
4438c2ecf20Sopenharmony_ci			return -EINVAL;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		down_read(&crypto_alg_sem);
4468c2ecf20Sopenharmony_ci		list_for_each_entry(alg, &crypto_alg_list, cra_list)
4478c2ecf20Sopenharmony_ci			dump_alloc += CRYPTO_REPORT_MAXSIZE;
4488c2ecf20Sopenharmony_ci		up_read(&crypto_alg_sem);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		{
4518c2ecf20Sopenharmony_ci			struct netlink_dump_control c = {
4528c2ecf20Sopenharmony_ci				.dump = link->dump,
4538c2ecf20Sopenharmony_ci				.done = link->done,
4548c2ecf20Sopenharmony_ci				.min_dump_alloc = min(dump_alloc, 65535UL),
4558c2ecf20Sopenharmony_ci			};
4568c2ecf20Sopenharmony_ci			err = netlink_dump_start(net->crypto_nlsk, skb, nlh, &c);
4578c2ecf20Sopenharmony_ci		}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		return err;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, crypto_msg_min[type], attrs,
4638c2ecf20Sopenharmony_ci				     CRYPTOCFGA_MAX, crypto_policy, extack);
4648c2ecf20Sopenharmony_ci	if (err < 0)
4658c2ecf20Sopenharmony_ci		return err;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (link->doit == NULL)
4688c2ecf20Sopenharmony_ci		return -EINVAL;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	return link->doit(skb, nlh, attrs);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic void crypto_netlink_rcv(struct sk_buff *skb)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	mutex_lock(&crypto_cfg_mutex);
4768c2ecf20Sopenharmony_ci	netlink_rcv_skb(skb, &crypto_user_rcv_msg);
4778c2ecf20Sopenharmony_ci	mutex_unlock(&crypto_cfg_mutex);
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic int __net_init crypto_netlink_init(struct net *net)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct netlink_kernel_cfg cfg = {
4838c2ecf20Sopenharmony_ci		.input	= crypto_netlink_rcv,
4848c2ecf20Sopenharmony_ci	};
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	net->crypto_nlsk = netlink_kernel_create(net, NETLINK_CRYPTO, &cfg);
4878c2ecf20Sopenharmony_ci	return net->crypto_nlsk == NULL ? -ENOMEM : 0;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic void __net_exit crypto_netlink_exit(struct net *net)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	netlink_kernel_release(net->crypto_nlsk);
4938c2ecf20Sopenharmony_ci	net->crypto_nlsk = NULL;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic struct pernet_operations crypto_netlink_net_ops = {
4978c2ecf20Sopenharmony_ci	.init = crypto_netlink_init,
4988c2ecf20Sopenharmony_ci	.exit = crypto_netlink_exit,
4998c2ecf20Sopenharmony_ci};
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic int __init crypto_user_init(void)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	return register_pernet_subsys(&crypto_netlink_net_ops);
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic void __exit crypto_user_exit(void)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&crypto_netlink_net_ops);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cimodule_init(crypto_user_init);
5128c2ecf20Sopenharmony_cimodule_exit(crypto_user_exit);
5138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>");
5158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Crypto userspace configuration API");
5168c2ecf20Sopenharmony_ciMODULE_ALIAS("net-pf-16-proto-21");
517