1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Crypto user configuration API. 4 * 5 * Copyright (C) 2017-2018 Corentin Labbe <clabbe@baylibre.com> 6 * 7 */ 8 9 #include <crypto/algapi.h> 10 #include <crypto/internal/cryptouser.h> 11 #include <linux/errno.h> 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/string.h> 15 #include <net/netlink.h> 16 #include <net/sock.h> 17 18 #define null_terminated(x) (strnlen(x, sizeof(x)) < sizeof(x)) 19 20 struct crypto_dump_info { 21 struct sk_buff *in_skb; 22 struct sk_buff *out_skb; 23 u32 nlmsg_seq; 24 u16 nlmsg_flags; 25 }; 26 27 static int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg) 28 { 29 struct crypto_stat_cipher rcipher; 30 31 memset(&rcipher, 0, sizeof(rcipher)); 32 33 strscpy(rcipher.type, "cipher", sizeof(rcipher.type)); 34 35 return nla_put(skb, CRYPTOCFGA_STAT_CIPHER, sizeof(rcipher), &rcipher); 36 } 37 38 static int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg) 39 { 40 struct crypto_stat_compress rcomp; 41 42 memset(&rcomp, 0, sizeof(rcomp)); 43 44 strscpy(rcomp.type, "compression", sizeof(rcomp.type)); 45 46 return nla_put(skb, CRYPTOCFGA_STAT_COMPRESS, sizeof(rcomp), &rcomp); 47 } 48 49 static int crypto_reportstat_one(struct crypto_alg *alg, 50 struct crypto_user_alg *ualg, 51 struct sk_buff *skb) 52 { 53 memset(ualg, 0, sizeof(*ualg)); 54 55 strscpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name)); 56 strscpy(ualg->cru_driver_name, alg->cra_driver_name, 57 sizeof(ualg->cru_driver_name)); 58 strscpy(ualg->cru_module_name, module_name(alg->cra_module), 59 sizeof(ualg->cru_module_name)); 60 61 ualg->cru_type = 0; 62 ualg->cru_mask = 0; 63 ualg->cru_flags = alg->cra_flags; 64 ualg->cru_refcnt = refcount_read(&alg->cra_refcnt); 65 66 if (nla_put_u32(skb, CRYPTOCFGA_PRIORITY_VAL, alg->cra_priority)) 67 goto nla_put_failure; 68 if (alg->cra_flags & CRYPTO_ALG_LARVAL) { 69 struct crypto_stat_larval rl; 70 71 memset(&rl, 0, sizeof(rl)); 72 strscpy(rl.type, "larval", sizeof(rl.type)); 73 if (nla_put(skb, CRYPTOCFGA_STAT_LARVAL, sizeof(rl), &rl)) 74 goto nla_put_failure; 75 goto out; 76 } 77 78 if (alg->cra_type && alg->cra_type->report_stat) { 79 if (alg->cra_type->report_stat(skb, alg)) 80 goto nla_put_failure; 81 goto out; 82 } 83 84 switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) { 85 case CRYPTO_ALG_TYPE_CIPHER: 86 if (crypto_report_cipher(skb, alg)) 87 goto nla_put_failure; 88 break; 89 case CRYPTO_ALG_TYPE_COMPRESS: 90 if (crypto_report_comp(skb, alg)) 91 goto nla_put_failure; 92 break; 93 default: 94 pr_err("ERROR: Unhandled alg %d in %s\n", 95 alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL), 96 __func__); 97 } 98 99 out: 100 return 0; 101 102 nla_put_failure: 103 return -EMSGSIZE; 104 } 105 106 static int crypto_reportstat_alg(struct crypto_alg *alg, 107 struct crypto_dump_info *info) 108 { 109 struct sk_buff *in_skb = info->in_skb; 110 struct sk_buff *skb = info->out_skb; 111 struct nlmsghdr *nlh; 112 struct crypto_user_alg *ualg; 113 int err = 0; 114 115 nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, info->nlmsg_seq, 116 CRYPTO_MSG_GETSTAT, sizeof(*ualg), info->nlmsg_flags); 117 if (!nlh) { 118 err = -EMSGSIZE; 119 goto out; 120 } 121 122 ualg = nlmsg_data(nlh); 123 124 err = crypto_reportstat_one(alg, ualg, skb); 125 if (err) { 126 nlmsg_cancel(skb, nlh); 127 goto out; 128 } 129 130 nlmsg_end(skb, nlh); 131 132 out: 133 return err; 134 } 135 136 int crypto_reportstat(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, 137 struct nlattr **attrs) 138 { 139 struct net *net = sock_net(in_skb->sk); 140 struct crypto_user_alg *p = nlmsg_data(in_nlh); 141 struct crypto_alg *alg; 142 struct sk_buff *skb; 143 struct crypto_dump_info info; 144 int err; 145 146 if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name)) 147 return -EINVAL; 148 149 alg = crypto_alg_match(p, 0); 150 if (!alg) 151 return -ENOENT; 152 153 err = -ENOMEM; 154 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 155 if (!skb) 156 goto drop_alg; 157 158 info.in_skb = in_skb; 159 info.out_skb = skb; 160 info.nlmsg_seq = in_nlh->nlmsg_seq; 161 info.nlmsg_flags = 0; 162 163 err = crypto_reportstat_alg(alg, &info); 164 165 drop_alg: 166 crypto_mod_put(alg); 167 168 if (err) { 169 kfree_skb(skb); 170 return err; 171 } 172 173 return nlmsg_unicast(net->crypto_nlsk, skb, NETLINK_CB(in_skb).portid); 174 } 175 176 MODULE_LICENSE("GPL"); 177