260 lines
5.7 KiB
C
Executable file
260 lines
5.7 KiB
C
Executable file
#include <linux/key-type.h>
|
|
#include <crypto/public_key.h>
|
|
#include <crypto/hash_info.h>
|
|
#include <keys/asymmetric-type.h>
|
|
#include <linux/key.h>
|
|
#include <linux/cred.h>
|
|
|
|
#include "proca_certificate_db.h"
|
|
#include "proca_log.h"
|
|
|
|
static struct key *proca_keyring;
|
|
static const char *proca_keyring_name = "_proca";
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
|
|
static inline int proca_verify_signature(struct key *key,
|
|
struct public_key_signature *pks,
|
|
struct signed_db *proca_signed_db)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
pks->hash_algo = hash_algo_name[HASH_ALGO_SHA256];
|
|
pks->nr_mpi = 1;
|
|
pks->rsa.s = mpi_read_raw_data(proca_signed_db->signature,
|
|
proca_signed_db->signature_size);
|
|
|
|
if (pks->rsa.s)
|
|
ret = verify_signature(key, pks);
|
|
|
|
mpi_free(pks->rsa.s);
|
|
|
|
return ret;
|
|
}
|
|
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
|
|
static inline int proca_verify_signature(struct key *key,
|
|
struct public_key_signature *pks,
|
|
struct signed_db *proca_signed_db)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
pks->pkey_algo = "rsa";
|
|
pks->hash_algo = hash_algo_name[HASH_ALGO_SHA256];
|
|
pks->s = proca_signed_db->signature;
|
|
pks->s_size = proca_signed_db->signature_size;
|
|
ret = verify_signature(key, pks);
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
static inline int proca_verify_signature(struct key *key,
|
|
struct public_key_signature *pks,
|
|
const char *signature, int sig_len)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
pks->pkey_algo = "rsa";
|
|
pks->encoding = "pkcs1";
|
|
pks->hash_algo = hash_algo_name[HASH_ALGO_SHA256];
|
|
pks->s = (u8 *)signature;
|
|
pks->s_size = sig_len;
|
|
ret = verify_signature(key, pks);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static struct key *proca_request_asymmetric_key(uint32_t keyid)
|
|
{
|
|
struct key *key;
|
|
char name[12];
|
|
|
|
snprintf(name, sizeof(name), "id:%08x", keyid);
|
|
|
|
PROCA_DEBUG_LOG("key search: \"%s\"\n", name);
|
|
|
|
if (proca_keyring) {
|
|
/* search in specific keyring */
|
|
key_ref_t kref;
|
|
|
|
kref = keyring_search(make_key_ref(proca_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;
|
|
}
|
|
}
|
|
|
|
PROCA_DEBUG_LOG("%s() = 0 [%x]\n", __func__, key_serial(key));
|
|
|
|
return key;
|
|
}
|
|
|
|
static int proca_asymmetric_verify(const char *signature, int sig_len,
|
|
const char *hash, int hash_len, uint32_t key_id)
|
|
{
|
|
struct public_key_signature pks;
|
|
struct key *key;
|
|
int ret = -ENOMEM;
|
|
|
|
key = proca_request_asymmetric_key(__be32_to_cpu(key_id));
|
|
|
|
memset(&pks, 0, sizeof(pks));
|
|
|
|
pks.digest = (u8 *)hash;
|
|
pks.digest_size = hash_len;
|
|
ret = proca_verify_signature(key, &pks, signature, sig_len);
|
|
key_put(key);
|
|
|
|
PROCA_DEBUG_LOG("%s() = %d\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int proca_digsig_verify(const char *signature, int sig_len,
|
|
const char *hash, int hash_len, uint32_t key_id)
|
|
{
|
|
if (!proca_keyring) {
|
|
proca_keyring = request_key(
|
|
&key_type_keyring, proca_keyring_name, NULL);
|
|
if (IS_ERR(proca_keyring)) {
|
|
int err = PTR_ERR(proca_keyring);
|
|
|
|
PROCA_ERROR_LOG("no %s keyring: %d\n", proca_keyring_name, err);
|
|
proca_keyring = NULL;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return proca_asymmetric_verify(signature, sig_len, hash, hash_len, key_id);
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
|
|
static inline struct key *proca_keyring_alloc(const char *description,
|
|
kuid_t uid, kgid_t gid, const struct cred *cred,
|
|
key_perm_t perm, unsigned long flags)
|
|
{
|
|
return keyring_alloc(description, uid, gid, cred,
|
|
perm, flags, NULL);
|
|
}
|
|
#else
|
|
static inline struct key *proca_keyring_alloc(const char *description,
|
|
kuid_t uid, kgid_t gid, const struct cred *cred,
|
|
key_perm_t perm, unsigned long flags)
|
|
{
|
|
return keyring_alloc(description, uid, gid, cred,
|
|
perm, flags, NULL, NULL);
|
|
}
|
|
#endif
|
|
|
|
int __init proca_load_x509_from_mem(const char *data, size_t size)
|
|
{
|
|
key_ref_t key;
|
|
int rc = 0;
|
|
|
|
if (!proca_keyring || size == 0)
|
|
return -EINVAL;
|
|
|
|
key = key_create_or_update(make_key_ref(proca_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);
|
|
PROCA_ERROR_LOG("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_PROCA_CERT_ENG
|
|
extern char proca_local_ca_start_eng[];
|
|
extern char proca_local_ca_end_eng[];
|
|
|
|
int __init proca_import_eng_key(void)
|
|
{
|
|
size_t size = proca_local_ca_end_eng - proca_local_ca_start_eng;
|
|
|
|
return proca_load_x509_from_mem(proca_local_ca_start_eng, size);
|
|
}
|
|
#else
|
|
|
|
int __init proca_import_eng_key(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PROCA_CERT_USER
|
|
extern char proca_local_ca_start_user[];
|
|
extern char proca_local_ca_end_user[];
|
|
|
|
int __init proca_import_user_key(void)
|
|
{
|
|
size_t size = proca_local_ca_end_user - proca_local_ca_start_user;
|
|
|
|
return proca_load_x509_from_mem(proca_local_ca_start_user, size);
|
|
}
|
|
#else
|
|
|
|
int __init proca_import_user_key(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int __init proca_load_built_x509(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = proca_import_eng_key();
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = proca_import_user_key();
|
|
|
|
return rc;
|
|
}
|
|
|
|
int __init proca_keyring_init(void)
|
|
{
|
|
const struct cred *cred = current_cred();
|
|
int err = 0;
|
|
|
|
proca_keyring = proca_keyring_alloc(proca_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(proca_keyring)) {
|
|
err = PTR_ERR(proca_keyring);
|
|
PROCA_ERROR_LOG("Can't allocate %s keyring (%d)\n",
|
|
proca_keyring_name, err);
|
|
proca_keyring = NULL;
|
|
}
|
|
|
|
return err;
|
|
}
|