523 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Crypto user configuration API.4 *5 * Copyright (C) 2011 secunet Security Networks AG6 * Copyright (C) 2011 Steffen Klassert <steffen.klassert@secunet.com>7 */8 9#include <linux/module.h>10#include <linux/crypto.h>11#include <linux/cryptouser.h>12#include <linux/sched.h>13#include <linux/security.h>14#include <net/netlink.h>15#include <net/net_namespace.h>16#include <net/sock.h>17#include <crypto/internal/skcipher.h>18#include <crypto/internal/rng.h>19#include <crypto/akcipher.h>20#include <crypto/kpp.h>21 22#include "internal.h"23 24#define null_terminated(x) (strnlen(x, sizeof(x)) < sizeof(x))25 26static DEFINE_MUTEX(crypto_cfg_mutex);27 28struct crypto_dump_info {29 struct sk_buff *in_skb;30 struct sk_buff *out_skb;31 u32 nlmsg_seq;32 u16 nlmsg_flags;33};34 35static struct crypto_alg *crypto_alg_match(struct crypto_user_alg *p, int exact)36{37 struct crypto_alg *q, *alg = NULL;38 39 down_read(&crypto_alg_sem);40 41 list_for_each_entry(q, &crypto_alg_list, cra_list) {42 int match = 0;43 44 if (crypto_is_larval(q))45 continue;46 47 if ((q->cra_flags ^ p->cru_type) & p->cru_mask)48 continue;49 50 if (strlen(p->cru_driver_name))51 match = !strcmp(q->cra_driver_name,52 p->cru_driver_name);53 else if (!exact)54 match = !strcmp(q->cra_name, p->cru_name);55 56 if (!match)57 continue;58 59 if (unlikely(!crypto_mod_get(q)))60 continue;61 62 alg = q;63 break;64 }65 66 up_read(&crypto_alg_sem);67 68 return alg;69}70 71static int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg)72{73 struct crypto_report_cipher rcipher;74 75 memset(&rcipher, 0, sizeof(rcipher));76 77 strscpy(rcipher.type, "cipher", sizeof(rcipher.type));78 79 rcipher.blocksize = alg->cra_blocksize;80 rcipher.min_keysize = alg->cra_cipher.cia_min_keysize;81 rcipher.max_keysize = alg->cra_cipher.cia_max_keysize;82 83 return nla_put(skb, CRYPTOCFGA_REPORT_CIPHER,84 sizeof(rcipher), &rcipher);85}86 87static int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg)88{89 struct crypto_report_comp rcomp;90 91 memset(&rcomp, 0, sizeof(rcomp));92 93 strscpy(rcomp.type, "compression", sizeof(rcomp.type));94 95 return nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS, sizeof(rcomp), &rcomp);96}97 98static int crypto_report_one(struct crypto_alg *alg,99 struct crypto_user_alg *ualg, struct sk_buff *skb)100{101 memset(ualg, 0, sizeof(*ualg));102 103 strscpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name));104 strscpy(ualg->cru_driver_name, alg->cra_driver_name,105 sizeof(ualg->cru_driver_name));106 strscpy(ualg->cru_module_name, module_name(alg->cra_module),107 sizeof(ualg->cru_module_name));108 109 ualg->cru_type = 0;110 ualg->cru_mask = 0;111 ualg->cru_flags = alg->cra_flags;112 ualg->cru_refcnt = refcount_read(&alg->cra_refcnt);113 114 if (nla_put_u32(skb, CRYPTOCFGA_PRIORITY_VAL, alg->cra_priority))115 goto nla_put_failure;116 if (alg->cra_flags & CRYPTO_ALG_LARVAL) {117 struct crypto_report_larval rl;118 119 memset(&rl, 0, sizeof(rl));120 strscpy(rl.type, "larval", sizeof(rl.type));121 if (nla_put(skb, CRYPTOCFGA_REPORT_LARVAL, sizeof(rl), &rl))122 goto nla_put_failure;123 goto out;124 }125 126 if (alg->cra_type && alg->cra_type->report) {127 if (alg->cra_type->report(skb, alg))128 goto nla_put_failure;129 130 goto out;131 }132 133 switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) {134 case CRYPTO_ALG_TYPE_CIPHER:135 if (crypto_report_cipher(skb, alg))136 goto nla_put_failure;137 138 break;139 case CRYPTO_ALG_TYPE_COMPRESS:140 if (crypto_report_comp(skb, alg))141 goto nla_put_failure;142 143 break;144 }145 146out:147 return 0;148 149nla_put_failure:150 return -EMSGSIZE;151}152 153static int crypto_report_alg(struct crypto_alg *alg,154 struct crypto_dump_info *info)155{156 struct sk_buff *in_skb = info->in_skb;157 struct sk_buff *skb = info->out_skb;158 struct nlmsghdr *nlh;159 struct crypto_user_alg *ualg;160 int err = 0;161 162 nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, info->nlmsg_seq,163 CRYPTO_MSG_GETALG, sizeof(*ualg), info->nlmsg_flags);164 if (!nlh) {165 err = -EMSGSIZE;166 goto out;167 }168 169 ualg = nlmsg_data(nlh);170 171 err = crypto_report_one(alg, ualg, skb);172 if (err) {173 nlmsg_cancel(skb, nlh);174 goto out;175 }176 177 nlmsg_end(skb, nlh);178 179out:180 return err;181}182 183static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,184 struct nlattr **attrs)185{186 struct net *net = sock_net(in_skb->sk);187 struct crypto_user_alg *p = nlmsg_data(in_nlh);188 struct crypto_alg *alg;189 struct sk_buff *skb;190 struct crypto_dump_info info;191 int err;192 193 if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))194 return -EINVAL;195 196 alg = crypto_alg_match(p, 0);197 if (!alg)198 return -ENOENT;199 200 err = -ENOMEM;201 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);202 if (!skb)203 goto drop_alg;204 205 info.in_skb = in_skb;206 info.out_skb = skb;207 info.nlmsg_seq = in_nlh->nlmsg_seq;208 info.nlmsg_flags = 0;209 210 err = crypto_report_alg(alg, &info);211 212drop_alg:213 crypto_mod_put(alg);214 215 if (err) {216 kfree_skb(skb);217 return err;218 }219 220 return nlmsg_unicast(net->crypto_nlsk, skb, NETLINK_CB(in_skb).portid);221}222 223static int crypto_dump_report(struct sk_buff *skb, struct netlink_callback *cb)224{225 const size_t start_pos = cb->args[0];226 size_t pos = 0;227 struct crypto_dump_info info;228 struct crypto_alg *alg;229 int res;230 231 info.in_skb = cb->skb;232 info.out_skb = skb;233 info.nlmsg_seq = cb->nlh->nlmsg_seq;234 info.nlmsg_flags = NLM_F_MULTI;235 236 down_read(&crypto_alg_sem);237 list_for_each_entry(alg, &crypto_alg_list, cra_list) {238 if (pos >= start_pos) {239 res = crypto_report_alg(alg, &info);240 if (res == -EMSGSIZE)241 break;242 if (res)243 goto out;244 }245 pos++;246 }247 cb->args[0] = pos;248 res = skb->len;249out:250 up_read(&crypto_alg_sem);251 return res;252}253 254static int crypto_dump_report_done(struct netlink_callback *cb)255{256 return 0;257}258 259static int crypto_update_alg(struct sk_buff *skb, struct nlmsghdr *nlh,260 struct nlattr **attrs)261{262 struct crypto_alg *alg;263 struct crypto_user_alg *p = nlmsg_data(nlh);264 struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL];265 LIST_HEAD(list);266 267 if (!netlink_capable(skb, CAP_NET_ADMIN))268 return -EPERM;269 270 if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))271 return -EINVAL;272 273 if (priority && !strlen(p->cru_driver_name))274 return -EINVAL;275 276 alg = crypto_alg_match(p, 1);277 if (!alg)278 return -ENOENT;279 280 down_write(&crypto_alg_sem);281 282 crypto_remove_spawns(alg, &list, NULL);283 284 if (priority)285 alg->cra_priority = nla_get_u32(priority);286 287 up_write(&crypto_alg_sem);288 289 crypto_mod_put(alg);290 crypto_remove_final(&list);291 292 return 0;293}294 295static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh,296 struct nlattr **attrs)297{298 struct crypto_alg *alg;299 struct crypto_user_alg *p = nlmsg_data(nlh);300 int err;301 302 if (!netlink_capable(skb, CAP_NET_ADMIN))303 return -EPERM;304 305 if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))306 return -EINVAL;307 308 alg = crypto_alg_match(p, 1);309 if (!alg)310 return -ENOENT;311 312 /* We can not unregister core algorithms such as aes-generic.313 * We would loose the reference in the crypto_alg_list to this algorithm314 * if we try to unregister. Unregistering such an algorithm without315 * removing the module is not possible, so we restrict to crypto316 * instances that are build from templates. */317 err = -EINVAL;318 if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE))319 goto drop_alg;320 321 err = -EBUSY;322 if (refcount_read(&alg->cra_refcnt) > 2)323 goto drop_alg;324 325 crypto_unregister_instance((struct crypto_instance *)alg);326 err = 0;327 328drop_alg:329 crypto_mod_put(alg);330 return err;331}332 333static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh,334 struct nlattr **attrs)335{336 int exact = 0;337 const char *name;338 struct crypto_alg *alg;339 struct crypto_user_alg *p = nlmsg_data(nlh);340 struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL];341 342 if (!netlink_capable(skb, CAP_NET_ADMIN))343 return -EPERM;344 345 if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name))346 return -EINVAL;347 348 if (strlen(p->cru_driver_name))349 exact = 1;350 351 if (priority && !exact)352 return -EINVAL;353 354 alg = crypto_alg_match(p, exact);355 if (alg) {356 crypto_mod_put(alg);357 return -EEXIST;358 }359 360 if (strlen(p->cru_driver_name))361 name = p->cru_driver_name;362 else363 name = p->cru_name;364 365 alg = crypto_alg_mod_lookup(name, p->cru_type, p->cru_mask);366 if (IS_ERR(alg))367 return PTR_ERR(alg);368 369 down_write(&crypto_alg_sem);370 371 if (priority)372 alg->cra_priority = nla_get_u32(priority);373 374 up_write(&crypto_alg_sem);375 376 crypto_mod_put(alg);377 378 return 0;379}380 381static int crypto_del_rng(struct sk_buff *skb, struct nlmsghdr *nlh,382 struct nlattr **attrs)383{384 if (!netlink_capable(skb, CAP_NET_ADMIN))385 return -EPERM;386 return crypto_del_default_rng();387}388 389static int crypto_reportstat(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,390 struct nlattr **attrs)391{392 /* No longer supported */393 return -ENOTSUPP;394}395 396#define MSGSIZE(type) sizeof(struct type)397 398static const int crypto_msg_min[CRYPTO_NR_MSGTYPES] = {399 [CRYPTO_MSG_NEWALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),400 [CRYPTO_MSG_DELALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),401 [CRYPTO_MSG_UPDATEALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),402 [CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),403 [CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = 0,404 [CRYPTO_MSG_GETSTAT - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),405};406 407static const struct nla_policy crypto_policy[CRYPTOCFGA_MAX+1] = {408 [CRYPTOCFGA_PRIORITY_VAL] = { .type = NLA_U32},409};410 411#undef MSGSIZE412 413static const struct crypto_link {414 int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);415 int (*dump)(struct sk_buff *, struct netlink_callback *);416 int (*done)(struct netlink_callback *);417} crypto_dispatch[CRYPTO_NR_MSGTYPES] = {418 [CRYPTO_MSG_NEWALG - CRYPTO_MSG_BASE] = { .doit = crypto_add_alg},419 [CRYPTO_MSG_DELALG - CRYPTO_MSG_BASE] = { .doit = crypto_del_alg},420 [CRYPTO_MSG_UPDATEALG - CRYPTO_MSG_BASE] = { .doit = crypto_update_alg},421 [CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE] = { .doit = crypto_report,422 .dump = crypto_dump_report,423 .done = crypto_dump_report_done},424 [CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = { .doit = crypto_del_rng },425 [CRYPTO_MSG_GETSTAT - CRYPTO_MSG_BASE] = { .doit = crypto_reportstat},426};427 428static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,429 struct netlink_ext_ack *extack)430{431 struct net *net = sock_net(skb->sk);432 struct nlattr *attrs[CRYPTOCFGA_MAX+1];433 const struct crypto_link *link;434 int type, err;435 436 type = nlh->nlmsg_type;437 if (type > CRYPTO_MSG_MAX)438 return -EINVAL;439 440 type -= CRYPTO_MSG_BASE;441 link = &crypto_dispatch[type];442 443 if ((type == (CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE) &&444 (nlh->nlmsg_flags & NLM_F_DUMP))) {445 struct crypto_alg *alg;446 unsigned long dump_alloc = 0;447 448 if (link->dump == NULL)449 return -EINVAL;450 451 down_read(&crypto_alg_sem);452 list_for_each_entry(alg, &crypto_alg_list, cra_list)453 dump_alloc += CRYPTO_REPORT_MAXSIZE;454 up_read(&crypto_alg_sem);455 456 {457 struct netlink_dump_control c = {458 .dump = link->dump,459 .done = link->done,460 .min_dump_alloc = min(dump_alloc, 65535UL),461 };462 err = netlink_dump_start(net->crypto_nlsk, skb, nlh, &c);463 }464 465 return err;466 }467 468 err = nlmsg_parse_deprecated(nlh, crypto_msg_min[type], attrs,469 CRYPTOCFGA_MAX, crypto_policy, extack);470 if (err < 0)471 return err;472 473 if (link->doit == NULL)474 return -EINVAL;475 476 return link->doit(skb, nlh, attrs);477}478 479static void crypto_netlink_rcv(struct sk_buff *skb)480{481 mutex_lock(&crypto_cfg_mutex);482 netlink_rcv_skb(skb, &crypto_user_rcv_msg);483 mutex_unlock(&crypto_cfg_mutex);484}485 486static int __net_init crypto_netlink_init(struct net *net)487{488 struct netlink_kernel_cfg cfg = {489 .input = crypto_netlink_rcv,490 };491 492 net->crypto_nlsk = netlink_kernel_create(net, NETLINK_CRYPTO, &cfg);493 return net->crypto_nlsk == NULL ? -ENOMEM : 0;494}495 496static void __net_exit crypto_netlink_exit(struct net *net)497{498 netlink_kernel_release(net->crypto_nlsk);499 net->crypto_nlsk = NULL;500}501 502static struct pernet_operations crypto_netlink_net_ops = {503 .init = crypto_netlink_init,504 .exit = crypto_netlink_exit,505};506 507static int __init crypto_user_init(void)508{509 return register_pernet_subsys(&crypto_netlink_net_ops);510}511 512static void __exit crypto_user_exit(void)513{514 unregister_pernet_subsys(&crypto_netlink_net_ops);515}516 517module_init(crypto_user_init);518module_exit(crypto_user_exit);519MODULE_LICENSE("GPL");520MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>");521MODULE_DESCRIPTION("Crypto userspace configuration API");522MODULE_ALIAS("net-pf-16-proto-21");523