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