root/crypto/asymmetric_keys/asymmetric_type.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. find_asymmetric_key
  2. asymmetric_key_generate_id
  3. asymmetric_key_id_same
  4. asymmetric_key_id_partial
  5. asymmetric_match_key_ids
  6. __asymmetric_key_hex_to_key_id
  7. asymmetric_key_hex_to_key_id
  8. asymmetric_key_cmp
  9. asymmetric_key_cmp_partial
  10. asymmetric_key_match_preparse
  11. asymmetric_key_match_free
  12. asymmetric_key_describe
  13. asymmetric_key_preparse
  14. asymmetric_key_free_kids
  15. asymmetric_key_free_preparse
  16. asymmetric_key_destroy
  17. asymmetric_restriction_alloc
  18. asymmetric_lookup_restriction
  19. asymmetric_key_eds_op
  20. asymmetric_key_verify_signature
  21. register_asymmetric_key_parser
  22. unregister_asymmetric_key_parser
  23. asymmetric_key_init
  24. asymmetric_key_cleanup

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /* Asymmetric public-key cryptography key type
   3  *
   4  * See Documentation/crypto/asymmetric-keys.txt
   5  *
   6  * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
   7  * Written by David Howells (dhowells@redhat.com)
   8  */
   9 #include <keys/asymmetric-subtype.h>
  10 #include <keys/asymmetric-parser.h>
  11 #include <crypto/public_key.h>
  12 #include <linux/seq_file.h>
  13 #include <linux/module.h>
  14 #include <linux/slab.h>
  15 #include <linux/ctype.h>
  16 #include <keys/system_keyring.h>
  17 #include <keys/user-type.h>
  18 #include "asymmetric_keys.h"
  19 
  20 MODULE_LICENSE("GPL");
  21 
  22 const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
  23         [VERIFYING_MODULE_SIGNATURE]            = "mod sig",
  24         [VERIFYING_FIRMWARE_SIGNATURE]          = "firmware sig",
  25         [VERIFYING_KEXEC_PE_SIGNATURE]          = "kexec PE sig",
  26         [VERIFYING_KEY_SIGNATURE]               = "key sig",
  27         [VERIFYING_KEY_SELF_SIGNATURE]          = "key self sig",
  28         [VERIFYING_UNSPECIFIED_SIGNATURE]       = "unspec sig",
  29 };
  30 EXPORT_SYMBOL_GPL(key_being_used_for);
  31 
  32 static LIST_HEAD(asymmetric_key_parsers);
  33 static DECLARE_RWSEM(asymmetric_key_parsers_sem);
  34 
  35 /**
  36  * find_asymmetric_key - Find a key by ID.
  37  * @keyring: The keys to search.
  38  * @id_0: The first ID to look for or NULL.
  39  * @id_1: The second ID to look for or NULL.
  40  * @partial: Use partial match if true, exact if false.
  41  *
  42  * Find a key in the given keyring by identifier.  The preferred identifier is
  43  * the id_0 and the fallback identifier is the id_1.  If both are given, the
  44  * lookup is by the former, but the latter must also match.
  45  */
  46 struct key *find_asymmetric_key(struct key *keyring,
  47                                 const struct asymmetric_key_id *id_0,
  48                                 const struct asymmetric_key_id *id_1,
  49                                 bool partial)
  50 {
  51         struct key *key;
  52         key_ref_t ref;
  53         const char *lookup;
  54         char *req, *p;
  55         int len;
  56 
  57         BUG_ON(!id_0 && !id_1);
  58 
  59         if (id_0) {
  60                 lookup = id_0->data;
  61                 len = id_0->len;
  62         } else {
  63                 lookup = id_1->data;
  64                 len = id_1->len;
  65         }
  66 
  67         /* Construct an identifier "id:<keyid>". */
  68         p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
  69         if (!req)
  70                 return ERR_PTR(-ENOMEM);
  71 
  72         if (partial) {
  73                 *p++ = 'i';
  74                 *p++ = 'd';
  75         } else {
  76                 *p++ = 'e';
  77                 *p++ = 'x';
  78         }
  79         *p++ = ':';
  80         p = bin2hex(p, lookup, len);
  81         *p = 0;
  82 
  83         pr_debug("Look up: \"%s\"\n", req);
  84 
  85         ref = keyring_search(make_key_ref(keyring, 1),
  86                              &key_type_asymmetric, req, true);
  87         if (IS_ERR(ref))
  88                 pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
  89         kfree(req);
  90 
  91         if (IS_ERR(ref)) {
  92                 switch (PTR_ERR(ref)) {
  93                         /* Hide some search errors */
  94                 case -EACCES:
  95                 case -ENOTDIR:
  96                 case -EAGAIN:
  97                         return ERR_PTR(-ENOKEY);
  98                 default:
  99                         return ERR_CAST(ref);
 100                 }
 101         }
 102 
 103         key = key_ref_to_ptr(ref);
 104         if (id_0 && id_1) {
 105                 const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
 106 
 107                 if (!kids->id[1]) {
 108                         pr_debug("First ID matches, but second is missing\n");
 109                         goto reject;
 110                 }
 111                 if (!asymmetric_key_id_same(id_1, kids->id[1])) {
 112                         pr_debug("First ID matches, but second does not\n");
 113                         goto reject;
 114                 }
 115         }
 116 
 117         pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
 118         return key;
 119 
 120 reject:
 121         key_put(key);
 122         return ERR_PTR(-EKEYREJECTED);
 123 }
 124 EXPORT_SYMBOL_GPL(find_asymmetric_key);
 125 
 126 /**
 127  * asymmetric_key_generate_id: Construct an asymmetric key ID
 128  * @val_1: First binary blob
 129  * @len_1: Length of first binary blob
 130  * @val_2: Second binary blob
 131  * @len_2: Length of second binary blob
 132  *
 133  * Construct an asymmetric key ID from a pair of binary blobs.
 134  */
 135 struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
 136                                                      size_t len_1,
 137                                                      const void *val_2,
 138                                                      size_t len_2)
 139 {
 140         struct asymmetric_key_id *kid;
 141 
 142         kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
 143                       GFP_KERNEL);
 144         if (!kid)
 145                 return ERR_PTR(-ENOMEM);
 146         kid->len = len_1 + len_2;
 147         memcpy(kid->data, val_1, len_1);
 148         memcpy(kid->data + len_1, val_2, len_2);
 149         return kid;
 150 }
 151 EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
 152 
 153 /**
 154  * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
 155  * @kid_1, @kid_2: The key IDs to compare
 156  */
 157 bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
 158                             const struct asymmetric_key_id *kid2)
 159 {
 160         if (!kid1 || !kid2)
 161                 return false;
 162         if (kid1->len != kid2->len)
 163                 return false;
 164         return memcmp(kid1->data, kid2->data, kid1->len) == 0;
 165 }
 166 EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
 167 
 168 /**
 169  * asymmetric_key_id_partial - Return true if two asymmetric keys IDs
 170  * partially match
 171  * @kid_1, @kid_2: The key IDs to compare
 172  */
 173 bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
 174                                const struct asymmetric_key_id *kid2)
 175 {
 176         if (!kid1 || !kid2)
 177                 return false;
 178         if (kid1->len < kid2->len)
 179                 return false;
 180         return memcmp(kid1->data + (kid1->len - kid2->len),
 181                       kid2->data, kid2->len) == 0;
 182 }
 183 EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
 184 
 185 /**
 186  * asymmetric_match_key_ids - Search asymmetric key IDs
 187  * @kids: The list of key IDs to check
 188  * @match_id: The key ID we're looking for
 189  * @match: The match function to use
 190  */
 191 static bool asymmetric_match_key_ids(
 192         const struct asymmetric_key_ids *kids,
 193         const struct asymmetric_key_id *match_id,
 194         bool (*match)(const struct asymmetric_key_id *kid1,
 195                       const struct asymmetric_key_id *kid2))
 196 {
 197         int i;
 198 
 199         if (!kids || !match_id)
 200                 return false;
 201         for (i = 0; i < ARRAY_SIZE(kids->id); i++)
 202                 if (match(kids->id[i], match_id))
 203                         return true;
 204         return false;
 205 }
 206 
 207 /* helper function can be called directly with pre-allocated memory */
 208 inline int __asymmetric_key_hex_to_key_id(const char *id,
 209                                    struct asymmetric_key_id *match_id,
 210                                    size_t hexlen)
 211 {
 212         match_id->len = hexlen;
 213         return hex2bin(match_id->data, id, hexlen);
 214 }
 215 
 216 /**
 217  * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
 218  * @id: The ID as a hex string.
 219  */
 220 struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
 221 {
 222         struct asymmetric_key_id *match_id;
 223         size_t asciihexlen;
 224         int ret;
 225 
 226         if (!*id)
 227                 return ERR_PTR(-EINVAL);
 228         asciihexlen = strlen(id);
 229         if (asciihexlen & 1)
 230                 return ERR_PTR(-EINVAL);
 231 
 232         match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2,
 233                            GFP_KERNEL);
 234         if (!match_id)
 235                 return ERR_PTR(-ENOMEM);
 236         ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2);
 237         if (ret < 0) {
 238                 kfree(match_id);
 239                 return ERR_PTR(-EINVAL);
 240         }
 241         return match_id;
 242 }
 243 
 244 /*
 245  * Match asymmetric keys by an exact match on an ID.
 246  */
 247 static bool asymmetric_key_cmp(const struct key *key,
 248                                const struct key_match_data *match_data)
 249 {
 250         const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
 251         const struct asymmetric_key_id *match_id = match_data->preparsed;
 252 
 253         return asymmetric_match_key_ids(kids, match_id,
 254                                         asymmetric_key_id_same);
 255 }
 256 
 257 /*
 258  * Match asymmetric keys by a partial match on an IDs.
 259  */
 260 static bool asymmetric_key_cmp_partial(const struct key *key,
 261                                        const struct key_match_data *match_data)
 262 {
 263         const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
 264         const struct asymmetric_key_id *match_id = match_data->preparsed;
 265 
 266         return asymmetric_match_key_ids(kids, match_id,
 267                                         asymmetric_key_id_partial);
 268 }
 269 
 270 /*
 271  * Preparse the match criterion.  If we don't set lookup_type and cmp,
 272  * the default will be an exact match on the key description.
 273  *
 274  * There are some specifiers for matching key IDs rather than by the key
 275  * description:
 276  *
 277  *      "id:<id>" - find a key by partial match on any available ID
 278  *      "ex:<id>" - find a key by exact match on any available ID
 279  *
 280  * These have to be searched by iteration rather than by direct lookup because
 281  * the key is hashed according to its description.
 282  */
 283 static int asymmetric_key_match_preparse(struct key_match_data *match_data)
 284 {
 285         struct asymmetric_key_id *match_id;
 286         const char *spec = match_data->raw_data;
 287         const char *id;
 288         bool (*cmp)(const struct key *, const struct key_match_data *) =
 289                 asymmetric_key_cmp;
 290 
 291         if (!spec || !*spec)
 292                 return -EINVAL;
 293         if (spec[0] == 'i' &&
 294             spec[1] == 'd' &&
 295             spec[2] == ':') {
 296                 id = spec + 3;
 297                 cmp = asymmetric_key_cmp_partial;
 298         } else if (spec[0] == 'e' &&
 299                    spec[1] == 'x' &&
 300                    spec[2] == ':') {
 301                 id = spec + 3;
 302         } else {
 303                 goto default_match;
 304         }
 305 
 306         match_id = asymmetric_key_hex_to_key_id(id);
 307         if (IS_ERR(match_id))
 308                 return PTR_ERR(match_id);
 309 
 310         match_data->preparsed = match_id;
 311         match_data->cmp = cmp;
 312         match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
 313         return 0;
 314 
 315 default_match:
 316         return 0;
 317 }
 318 
 319 /*
 320  * Free the preparsed the match criterion.
 321  */
 322 static void asymmetric_key_match_free(struct key_match_data *match_data)
 323 {
 324         kfree(match_data->preparsed);
 325 }
 326 
 327 /*
 328  * Describe the asymmetric key
 329  */
 330 static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
 331 {
 332         const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
 333         const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
 334         const struct asymmetric_key_id *kid;
 335         const unsigned char *p;
 336         int n;
 337 
 338         seq_puts(m, key->description);
 339 
 340         if (subtype) {
 341                 seq_puts(m, ": ");
 342                 subtype->describe(key, m);
 343 
 344                 if (kids && kids->id[1]) {
 345                         kid = kids->id[1];
 346                         seq_putc(m, ' ');
 347                         n = kid->len;
 348                         p = kid->data;
 349                         if (n > 4) {
 350                                 p += n - 4;
 351                                 n = 4;
 352                         }
 353                         seq_printf(m, "%*phN", n, p);
 354                 }
 355 
 356                 seq_puts(m, " [");
 357                 /* put something here to indicate the key's capabilities */
 358                 seq_putc(m, ']');
 359         }
 360 }
 361 
 362 /*
 363  * Preparse a asymmetric payload to get format the contents appropriately for the
 364  * internal payload to cut down on the number of scans of the data performed.
 365  *
 366  * We also generate a proposed description from the contents of the key that
 367  * can be used to name the key if the user doesn't want to provide one.
 368  */
 369 static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
 370 {
 371         struct asymmetric_key_parser *parser;
 372         int ret;
 373 
 374         pr_devel("==>%s()\n", __func__);
 375 
 376         if (prep->datalen == 0)
 377                 return -EINVAL;
 378 
 379         down_read(&asymmetric_key_parsers_sem);
 380 
 381         ret = -EBADMSG;
 382         list_for_each_entry(parser, &asymmetric_key_parsers, link) {
 383                 pr_debug("Trying parser '%s'\n", parser->name);
 384 
 385                 ret = parser->parse(prep);
 386                 if (ret != -EBADMSG) {
 387                         pr_debug("Parser recognised the format (ret %d)\n",
 388                                  ret);
 389                         break;
 390                 }
 391         }
 392 
 393         up_read(&asymmetric_key_parsers_sem);
 394         pr_devel("<==%s() = %d\n", __func__, ret);
 395         return ret;
 396 }
 397 
 398 /*
 399  * Clean up the key ID list
 400  */
 401 static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
 402 {
 403         int i;
 404 
 405         if (kids) {
 406                 for (i = 0; i < ARRAY_SIZE(kids->id); i++)
 407                         kfree(kids->id[i]);
 408                 kfree(kids);
 409         }
 410 }
 411 
 412 /*
 413  * Clean up the preparse data
 414  */
 415 static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
 416 {
 417         struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype];
 418         struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids];
 419 
 420         pr_devel("==>%s()\n", __func__);
 421 
 422         if (subtype) {
 423                 subtype->destroy(prep->payload.data[asym_crypto],
 424                                  prep->payload.data[asym_auth]);
 425                 module_put(subtype->owner);
 426         }
 427         asymmetric_key_free_kids(kids);
 428         kfree(prep->description);
 429 }
 430 
 431 /*
 432  * dispose of the data dangling from the corpse of a asymmetric key
 433  */
 434 static void asymmetric_key_destroy(struct key *key)
 435 {
 436         struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
 437         struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
 438         void *data = key->payload.data[asym_crypto];
 439         void *auth = key->payload.data[asym_auth];
 440 
 441         key->payload.data[asym_crypto] = NULL;
 442         key->payload.data[asym_subtype] = NULL;
 443         key->payload.data[asym_key_ids] = NULL;
 444         key->payload.data[asym_auth] = NULL;
 445 
 446         if (subtype) {
 447                 subtype->destroy(data, auth);
 448                 module_put(subtype->owner);
 449         }
 450 
 451         asymmetric_key_free_kids(kids);
 452 }
 453 
 454 static struct key_restriction *asymmetric_restriction_alloc(
 455         key_restrict_link_func_t check,
 456         struct key *key)
 457 {
 458         struct key_restriction *keyres =
 459                 kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
 460 
 461         if (!keyres)
 462                 return ERR_PTR(-ENOMEM);
 463 
 464         keyres->check = check;
 465         keyres->key = key;
 466         keyres->keytype = &key_type_asymmetric;
 467 
 468         return keyres;
 469 }
 470 
 471 /*
 472  * look up keyring restrict functions for asymmetric keys
 473  */
 474 static struct key_restriction *asymmetric_lookup_restriction(
 475         const char *restriction)
 476 {
 477         char *restrict_method;
 478         char *parse_buf;
 479         char *next;
 480         struct key_restriction *ret = ERR_PTR(-EINVAL);
 481 
 482         if (strcmp("builtin_trusted", restriction) == 0)
 483                 return asymmetric_restriction_alloc(
 484                         restrict_link_by_builtin_trusted, NULL);
 485 
 486         if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
 487                 return asymmetric_restriction_alloc(
 488                         restrict_link_by_builtin_and_secondary_trusted, NULL);
 489 
 490         parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
 491         if (!parse_buf)
 492                 return ERR_PTR(-ENOMEM);
 493 
 494         next = parse_buf;
 495         restrict_method = strsep(&next, ":");
 496 
 497         if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
 498                 char *key_text;
 499                 key_serial_t serial;
 500                 struct key *key;
 501                 key_restrict_link_func_t link_fn =
 502                         restrict_link_by_key_or_keyring;
 503                 bool allow_null_key = false;
 504 
 505                 key_text = strsep(&next, ":");
 506 
 507                 if (next) {
 508                         if (strcmp(next, "chain") != 0)
 509                                 goto out;
 510 
 511                         link_fn = restrict_link_by_key_or_keyring_chain;
 512                         allow_null_key = true;
 513                 }
 514 
 515                 if (kstrtos32(key_text, 0, &serial) < 0)
 516                         goto out;
 517 
 518                 if ((serial == 0) && allow_null_key) {
 519                         key = NULL;
 520                 } else {
 521                         key = key_lookup(serial);
 522                         if (IS_ERR(key)) {
 523                                 ret = ERR_CAST(key);
 524                                 goto out;
 525                         }
 526                 }
 527 
 528                 ret = asymmetric_restriction_alloc(link_fn, key);
 529                 if (IS_ERR(ret))
 530                         key_put(key);
 531         }
 532 
 533 out:
 534         kfree(parse_buf);
 535         return ret;
 536 }
 537 
 538 int asymmetric_key_eds_op(struct kernel_pkey_params *params,
 539                           const void *in, void *out)
 540 {
 541         const struct asymmetric_key_subtype *subtype;
 542         struct key *key = params->key;
 543         int ret;
 544 
 545         pr_devel("==>%s()\n", __func__);
 546 
 547         if (key->type != &key_type_asymmetric)
 548                 return -EINVAL;
 549         subtype = asymmetric_key_subtype(key);
 550         if (!subtype ||
 551             !key->payload.data[0])
 552                 return -EINVAL;
 553         if (!subtype->eds_op)
 554                 return -ENOTSUPP;
 555 
 556         ret = subtype->eds_op(params, in, out);
 557 
 558         pr_devel("<==%s() = %d\n", __func__, ret);
 559         return ret;
 560 }
 561 
 562 static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
 563                                            const void *in, const void *in2)
 564 {
 565         struct public_key_signature sig = {
 566                 .s_size         = params->in2_len,
 567                 .digest_size    = params->in_len,
 568                 .encoding       = params->encoding,
 569                 .hash_algo      = params->hash_algo,
 570                 .digest         = (void *)in,
 571                 .s              = (void *)in2,
 572         };
 573 
 574         return verify_signature(params->key, &sig);
 575 }
 576 
 577 struct key_type key_type_asymmetric = {
 578         .name                   = "asymmetric",
 579         .preparse               = asymmetric_key_preparse,
 580         .free_preparse          = asymmetric_key_free_preparse,
 581         .instantiate            = generic_key_instantiate,
 582         .match_preparse         = asymmetric_key_match_preparse,
 583         .match_free             = asymmetric_key_match_free,
 584         .destroy                = asymmetric_key_destroy,
 585         .describe               = asymmetric_key_describe,
 586         .lookup_restriction     = asymmetric_lookup_restriction,
 587         .asym_query             = query_asymmetric_key,
 588         .asym_eds_op            = asymmetric_key_eds_op,
 589         .asym_verify_signature  = asymmetric_key_verify_signature,
 590 };
 591 EXPORT_SYMBOL_GPL(key_type_asymmetric);
 592 
 593 /**
 594  * register_asymmetric_key_parser - Register a asymmetric key blob parser
 595  * @parser: The parser to register
 596  */
 597 int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
 598 {
 599         struct asymmetric_key_parser *cursor;
 600         int ret;
 601 
 602         down_write(&asymmetric_key_parsers_sem);
 603 
 604         list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
 605                 if (strcmp(cursor->name, parser->name) == 0) {
 606                         pr_err("Asymmetric key parser '%s' already registered\n",
 607                                parser->name);
 608                         ret = -EEXIST;
 609                         goto out;
 610                 }
 611         }
 612 
 613         list_add_tail(&parser->link, &asymmetric_key_parsers);
 614 
 615         pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
 616         ret = 0;
 617 
 618 out:
 619         up_write(&asymmetric_key_parsers_sem);
 620         return ret;
 621 }
 622 EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
 623 
 624 /**
 625  * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
 626  * @parser: The parser to unregister
 627  */
 628 void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
 629 {
 630         down_write(&asymmetric_key_parsers_sem);
 631         list_del(&parser->link);
 632         up_write(&asymmetric_key_parsers_sem);
 633 
 634         pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
 635 }
 636 EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
 637 
 638 /*
 639  * Module stuff
 640  */
 641 static int __init asymmetric_key_init(void)
 642 {
 643         return register_key_type(&key_type_asymmetric);
 644 }
 645 
 646 static void __exit asymmetric_key_cleanup(void)
 647 {
 648         unregister_key_type(&key_type_asymmetric);
 649 }
 650 
 651 module_init(asymmetric_key_init);
 652 module_exit(asymmetric_key_cleanup);

/* [<][>][^][v][top][bottom][index][help] */