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