text: /* Asymmetric public-key cryptography key type
   *
   * See Documentation/security/asymmetric-keys.txt
   *
   * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public Licence
   * as published by the Free Software Foundation; either version
   * 2 of the Licence, or (at your option) any later version.
   */
  #include <keys/asymmetric-subtype.h>
  #include <keys/asymmetric-parser.h>
  #include <linux/seq_file.h>
  #include <linux/module.h>
  #include <linux/slab.h>
  #include "asymmetric_keys.h"
  
  MODULE_LICENSE("GPL");
  
  static LIST_HEAD(asymmetric_key_parsers);
  static DECLARE_RWSEM(asymmetric_key_parsers_sem);
  
  /*
   * Match asymmetric keys on (part of) their name
   * We have some shorthand methods for matching keys.  We allow:
   *
   *	"<desc>"	- request a key by description
   *	"id:<id>"	- request a key matching the ID
   *	"<subtype>:<id>" - request a key of a subtype
   */
  static int asymmetric_key_match(const struct key *key, const void *description)
  {
  	const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
  	const char *spec = description;
  	const char *id, *kid;
  	ptrdiff_t speclen;
  	size_t idlen, kidlen;
  
  	if (!subtype || !spec || !*spec)
  		return 0;
  
  	/* See if the full key description matches as is */
  	if (key->description && strcmp(key->description, description) == 0)
  		return 1;
  
  	/* All tests from here on break the criterion description into a
  	 * specifier, a colon and then an identifier.
  	 */
  	id = strchr(spec, ':');
  	if (!id)
  		return 0;
  
  	speclen = id - spec;
  	id++;
  
  	/* Anything after here requires a partial match on the ID string */
  	kid = asymmetric_key_id(key);
  	if (!kid)
  		return 0;
  
  	idlen = strlen(id);
  	kidlen = strlen(kid);
  	if (idlen > kidlen)
  		return 0;
  
  	kid += kidlen - idlen;
  	if (strcasecmp(id, kid) != 0)
  		return 0;
  
  	if (speclen == 2 &&
  	    memcmp(spec, "id", 2) == 0)
  		return 1;
  
  	if (speclen == subtype->name_len &&
  	    memcmp(spec, subtype->name, speclen) == 0)
  		return 1;
  
  	return 0;
  }
  
  /*
   * Describe the asymmetric key
   */
  static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
  {
  	const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
  	const char *kid = asymmetric_key_id(key);
  	size_t n;
  
  	seq_puts(m, key->description);
  
  	if (subtype) {
  		seq_puts(m, ": ");
  		subtype->describe(key, m);
  
  		if (kid) {
  			seq_putc(m, ' ');
  			n = strlen(kid);
  			if (n <= 8)
  				seq_puts(m, kid);
  			else
  				seq_puts(m, kid + n - 8);
  		}
  
  		seq_puts(m, " [");
  		/* put something here to indicate the key's capabilities */
  		seq_putc(m, ']');
  	}
  }
  
  /*
   * Preparse a asymmetric payload to get format the contents appropriately for the
   * internal payload to cut down on the number of scans of the data performed.
   *
   * We also generate a proposed description from the contents of the key that
   * can be used to name the key if the user doesn't want to provide one.
   */
  static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
  {
  	struct asymmetric_key_parser *parser;
  	int ret;
  
  	pr_devel("==>%s()\n", __func__);
  
  	if (prep->datalen == 0)
  		return -EINVAL;
  
  	down_read(&asymmetric_key_parsers_sem);
  
  	ret = -EBADMSG;
  	list_for_each_entry(parser, &asymmetric_key_parsers, link) {
  		pr_debug("Trying parser '%s'\n", parser->name);
  
  		ret = parser->parse(prep);
  		if (ret != -EBADMSG) {
  			pr_debug("Parser recognised the format (ret %d)\n",
  				 ret);
  			break;
  		}
  	}
  
  	up_read(&asymmetric_key_parsers_sem);
  	pr_devel("<==%s() = %d\n", __func__, ret);
  	return ret;
  }
  
  /*
   * Clean up the preparse data
   */
  static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
  {
  	struct asymmetric_key_subtype *subtype = prep->type_data[0];
  
  	pr_devel("==>%s()\n", __func__);
  
  	if (subtype) {
  		subtype->destroy(prep->payload);
  		module_put(subtype->owner);
  	}
  	kfree(prep->type_data[1]);
  	kfree(prep->description);
  }
  
  /*
   * Instantiate a asymmetric_key defined key.  The key was preparsed, so we just
   * have to transfer the data here.
   */
  static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
  {
  	int ret;
  
  	pr_devel("==>%s()\n", __func__);
  
  	ret = key_payload_reserve(key, prep->quotalen);
  	if (ret == 0) {
  		key->type_data.p[0] = prep->type_data[0];
  		key->type_data.p[1] = prep->type_data[1];
  		key->payload.data = prep->payload;
  		prep->type_data[0] = NULL;
  		prep->type_data[1] = NULL;
  		prep->payload = NULL;
  	}
  	pr_devel("<==%s() = %d\n", __func__, ret);
  	return ret;
  }
  
  /*
   * dispose of the data dangling from the corpse of a asymmetric key
   */
  static void asymmetric_key_destroy(struct key *key)
  {
  	struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
  	if (subtype) {
  		subtype->destroy(key->payload.data);
  		module_put(subtype->owner);
  		key->type_data.p[0] = NULL;
  	}
  	kfree(key->type_data.p[1]);
  	key->type_data.p[1] = NULL;
  }
  
  struct key_type key_type_asymmetric = {
  	.name		= "asymmetric",
  	.preparse	= asymmetric_key_preparse,
  	.free_preparse	= asymmetric_key_free_preparse,
  	.instantiate	= asymmetric_key_instantiate,
  	.match		= asymmetric_key_match,
  	.destroy	= asymmetric_key_destroy,
  	.describe	= asymmetric_key_describe,
  	.def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
  };
  EXPORT_SYMBOL_GPL(key_type_asymmetric);
  
  /**
   * register_asymmetric_key_parser - Register a asymmetric key blob parser
   * @parser: The parser to register
   */
  int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
  {
  	struct asymmetric_key_parser *cursor;
  	int ret;
  
  	down_write(&asymmetric_key_parsers_sem);
  
  	list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
  		if (strcmp(cursor->name, parser->name) == 0) {
  			pr_err("Asymmetric key parser '%s' already registered\n",
  			       parser->name);
  			ret = -EEXIST;
  			goto out;
  		}
  	}
  
  	list_add_tail(&parser->link, &asymmetric_key_parsers);
  
  	pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
  	ret = 0;
  
  out:
  	up_write(&asymmetric_key_parsers_sem);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
  
  /**
   * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
   * @parser: The parser to unregister
   */
  void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
  {
  	down_write(&asymmetric_key_parsers_sem);
  	list_del(&parser->link);
  	up_write(&asymmetric_key_parsers_sem);
  
  	pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
  }
  EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
  
  /*
   * Module stuff
   */
  static int __init asymmetric_key_init(void)
  {
  	return register_key_type(&key_type_asymmetric);
  }
  
  static void __exit asymmetric_key_cleanup(void)
  {
  	unregister_key_type(&key_type_asymmetric);
  }
  
  module_init(asymmetric_key_init);
  module_exit(asymmetric_key_cleanup);
  