blob: fde552eb867e79c177516113d64b2341ff88e2b2 [file] [log] [blame]
/*
* FIVE keyring
*
* Copyright (C) 2017 Samsung Electronics, Inc.
* Yevgen Kopylov, <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/key-type.h>
#include <crypto/public_key.h>
#include <keys/asymmetric-type.h>
#include <crypto/hash_info.h>
#include "five.h"
#include "five_crypto_comp.h"
#include "five_porting.h"
static struct key *five_keyring;
#ifndef CONFIG_FIVE_TRUSTED_KEYRING
static const char *five_keyring_name = "_five";
#else
static const char *five_keyring_name = ".five";
#endif
/*
* Request an asymmetric key.
*/
static struct key *five_request_asymmetric_key(uint32_t keyid)
{
struct key *key;
char name[12];
snprintf(name, sizeof(name), "id:%08x", keyid);
pr_debug("key search: \"%s\"\n", name);
if (five_keyring) {
/* search in specific keyring */
key_ref_t kref;
kref = keyring_search(make_key_ref(five_keyring, 1),
&key_type_asymmetric, name, true);
if (IS_ERR(kref))
key = ERR_CAST(kref);
else
key = key_ref_to_ptr(kref);
} else {
return ERR_PTR(-ENOKEY);
}
if (IS_ERR(key)) {
switch (PTR_ERR(key)) {
/* Hide some search errors */
case -EACCES:
case -ENOTDIR:
case -EAGAIN:
return ERR_PTR(-ENOKEY);
default:
return key;
}
}
pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key));
return key;
}
static int five_asymmetric_verify(struct five_cert *cert,
const char *data, int datalen)
{
struct public_key_signature pks;
struct five_cert_header *header;
struct key *key;
int ret = -ENOMEM;
header = (struct five_cert_header *)cert->body.header->value;
if (header->hash_algo >= HASH_ALGO__LAST)
return -ENOPKG;
key = five_request_asymmetric_key(__be32_to_cpu(header->key_id));
if (IS_ERR(key))
return PTR_ERR(key);
memset(&pks, 0, sizeof(pks));
pks.digest = (u8 *)data;
pks.digest_size = datalen;
ret = five_verify_signature(key, &pks, cert, header);
key_put(key);
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
int five_digsig_verify(struct five_cert *cert,
const char *digest, int digestlen)
{
if (!five_keyring) {
five_keyring =
request_key(&key_type_keyring, five_keyring_name, NULL);
if (IS_ERR(five_keyring)) {
int err = PTR_ERR(five_keyring);
pr_err("no %s keyring: %d\n", five_keyring_name, err);
five_keyring = NULL;
return err;
}
}
return five_asymmetric_verify(cert, digest, digestlen);
}
static int __init five_load_x509_from_mem(const char *data, size_t size)
{
key_ref_t key;
int rc = 0;
if (!five_keyring || size == 0)
return -EINVAL;
key = key_create_or_update(make_key_ref(five_keyring, 1),
"asymmetric",
NULL,
data,
size,
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key)) {
rc = PTR_ERR(key);
pr_err("Problem loading X.509 certificate (%d): %s\n",
rc, "built-in");
} else {
pr_notice("Loaded X.509 cert '%s': %s\n",
key_ref_to_ptr(key)->description, "built-in");
key_ref_put(key);
}
return rc;
}
#ifdef CONFIG_FIVE_CERT_ENG
extern char five_local_ca_start_eng[];
extern char five_local_ca_end_eng[];
static int __init five_import_eng_key(void)
{
size_t size = five_local_ca_end_eng - five_local_ca_start_eng;
return five_load_x509_from_mem(five_local_ca_start_eng, size);
}
#else
static int __init five_import_eng_key(void)
{
return 0;
}
#endif
#ifdef CONFIG_FIVE_CERT_USER
extern char five_local_ca_start_user[];
extern char five_local_ca_end_user[];
static int __init five_import_user_key(void)
{
size_t size = five_local_ca_end_user - five_local_ca_start_user;
return five_load_x509_from_mem(five_local_ca_start_user, size);
}
#else
static int __init five_import_user_key(void)
{
return 0;
}
#endif
int __init five_load_built_x509(void)
{
int rc;
rc = five_import_eng_key();
if (rc)
return rc;
rc = five_import_user_key();
return rc;
}
int __init five_keyring_init(void)
{
const struct cred *cred = current_cred();
int err = 0;
five_keyring = five_keyring_alloc(five_keyring_name, KUIDT_INIT(0),
KGIDT_INIT(0), cred,
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_SEARCH),
KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(five_keyring)) {
err = PTR_ERR(five_keyring);
pr_info("Can't allocate %s keyring (%d)\n",
five_keyring_name, err);
five_keyring = NULL;
}
return err;
}