162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Crypto user configuration API. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017-2018 Corentin Labbe <clabbe@baylibre.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <crypto/algapi.h> 1062306a36Sopenharmony_ci#include <crypto/internal/cryptouser.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <net/netlink.h> 1662306a36Sopenharmony_ci#include <net/sock.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define null_terminated(x) (strnlen(x, sizeof(x)) < sizeof(x)) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct crypto_dump_info { 2162306a36Sopenharmony_ci struct sk_buff *in_skb; 2262306a36Sopenharmony_ci struct sk_buff *out_skb; 2362306a36Sopenharmony_ci u32 nlmsg_seq; 2462306a36Sopenharmony_ci u16 nlmsg_flags; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct crypto_stat_cipher rcipher; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci memset(&rcipher, 0, sizeof(rcipher)); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci strscpy(rcipher.type, "cipher", sizeof(rcipher.type)); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return nla_put(skb, CRYPTOCFGA_STAT_CIPHER, sizeof(rcipher), &rcipher); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct crypto_stat_compress rcomp; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci memset(&rcomp, 0, sizeof(rcomp)); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci strscpy(rcomp.type, "compression", sizeof(rcomp.type)); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return nla_put(skb, CRYPTOCFGA_STAT_COMPRESS, sizeof(rcomp), &rcomp); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int crypto_reportstat_one(struct crypto_alg *alg, 5062306a36Sopenharmony_ci struct crypto_user_alg *ualg, 5162306a36Sopenharmony_ci struct sk_buff *skb) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci memset(ualg, 0, sizeof(*ualg)); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci strscpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name)); 5662306a36Sopenharmony_ci strscpy(ualg->cru_driver_name, alg->cra_driver_name, 5762306a36Sopenharmony_ci sizeof(ualg->cru_driver_name)); 5862306a36Sopenharmony_ci strscpy(ualg->cru_module_name, module_name(alg->cra_module), 5962306a36Sopenharmony_ci sizeof(ualg->cru_module_name)); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ualg->cru_type = 0; 6262306a36Sopenharmony_ci ualg->cru_mask = 0; 6362306a36Sopenharmony_ci ualg->cru_flags = alg->cra_flags; 6462306a36Sopenharmony_ci ualg->cru_refcnt = refcount_read(&alg->cra_refcnt); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (nla_put_u32(skb, CRYPTOCFGA_PRIORITY_VAL, alg->cra_priority)) 6762306a36Sopenharmony_ci goto nla_put_failure; 6862306a36Sopenharmony_ci if (alg->cra_flags & CRYPTO_ALG_LARVAL) { 6962306a36Sopenharmony_ci struct crypto_stat_larval rl; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci memset(&rl, 0, sizeof(rl)); 7262306a36Sopenharmony_ci strscpy(rl.type, "larval", sizeof(rl.type)); 7362306a36Sopenharmony_ci if (nla_put(skb, CRYPTOCFGA_STAT_LARVAL, sizeof(rl), &rl)) 7462306a36Sopenharmony_ci goto nla_put_failure; 7562306a36Sopenharmony_ci goto out; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (alg->cra_type && alg->cra_type->report_stat) { 7962306a36Sopenharmony_ci if (alg->cra_type->report_stat(skb, alg)) 8062306a36Sopenharmony_ci goto nla_put_failure; 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) { 8562306a36Sopenharmony_ci case CRYPTO_ALG_TYPE_CIPHER: 8662306a36Sopenharmony_ci if (crypto_report_cipher(skb, alg)) 8762306a36Sopenharmony_ci goto nla_put_failure; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci case CRYPTO_ALG_TYPE_COMPRESS: 9062306a36Sopenharmony_ci if (crypto_report_comp(skb, alg)) 9162306a36Sopenharmony_ci goto nla_put_failure; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci default: 9462306a36Sopenharmony_ci pr_err("ERROR: Unhandled alg %d in %s\n", 9562306a36Sopenharmony_ci alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL), 9662306a36Sopenharmony_ci __func__); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciout: 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cinla_put_failure: 10362306a36Sopenharmony_ci return -EMSGSIZE; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int crypto_reportstat_alg(struct crypto_alg *alg, 10762306a36Sopenharmony_ci struct crypto_dump_info *info) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct sk_buff *in_skb = info->in_skb; 11062306a36Sopenharmony_ci struct sk_buff *skb = info->out_skb; 11162306a36Sopenharmony_ci struct nlmsghdr *nlh; 11262306a36Sopenharmony_ci struct crypto_user_alg *ualg; 11362306a36Sopenharmony_ci int err = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, info->nlmsg_seq, 11662306a36Sopenharmony_ci CRYPTO_MSG_GETSTAT, sizeof(*ualg), info->nlmsg_flags); 11762306a36Sopenharmony_ci if (!nlh) { 11862306a36Sopenharmony_ci err = -EMSGSIZE; 11962306a36Sopenharmony_ci goto out; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ualg = nlmsg_data(nlh); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci err = crypto_reportstat_one(alg, ualg, skb); 12562306a36Sopenharmony_ci if (err) { 12662306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci nlmsg_end(skb, nlh); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciout: 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint crypto_reportstat(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, 13762306a36Sopenharmony_ci struct nlattr **attrs) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 14062306a36Sopenharmony_ci struct crypto_user_alg *p = nlmsg_data(in_nlh); 14162306a36Sopenharmony_ci struct crypto_alg *alg; 14262306a36Sopenharmony_ci struct sk_buff *skb; 14362306a36Sopenharmony_ci struct crypto_dump_info info; 14462306a36Sopenharmony_ci int err; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name)) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci alg = crypto_alg_match(p, 0); 15062306a36Sopenharmony_ci if (!alg) 15162306a36Sopenharmony_ci return -ENOENT; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci err = -ENOMEM; 15462306a36Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 15562306a36Sopenharmony_ci if (!skb) 15662306a36Sopenharmony_ci goto drop_alg; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci info.in_skb = in_skb; 15962306a36Sopenharmony_ci info.out_skb = skb; 16062306a36Sopenharmony_ci info.nlmsg_seq = in_nlh->nlmsg_seq; 16162306a36Sopenharmony_ci info.nlmsg_flags = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci err = crypto_reportstat_alg(alg, &info); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cidrop_alg: 16662306a36Sopenharmony_ci crypto_mod_put(alg); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (err) { 16962306a36Sopenharmony_ci kfree_skb(skb); 17062306a36Sopenharmony_ci return err; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return nlmsg_unicast(net->crypto_nlsk, skb, NETLINK_CB(in_skb).portid); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 177