2012-09-24 20:11:48 +04:00
/* Instantiate a public key crypto key from an X.509 Certificate
*
* 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 .
*/
# define pr_fmt(fmt) "X.509: "fmt
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/mpi.h>
# include <linux/asn1_decoder.h>
# include <keys/asymmetric-subtype.h>
# include <keys/asymmetric-parser.h>
2013-08-20 22:36:27 +04:00
# include <keys/system_keyring.h>
2012-09-24 20:11:48 +04:00
# include <crypto/hash.h>
# include "asymmetric_keys.h"
# include "public_key.h"
# include "x509_parser.h"
2014-06-17 12:56:59 +04:00
static bool use_builtin_keys ;
2014-09-16 20:36:13 +04:00
static struct asymmetric_key_id * ca_keyid ;
2014-06-17 12:56:58 +04:00
# ifndef MODULE
2015-02-11 15:33:34 +03:00
static struct {
struct asymmetric_key_id id ;
unsigned char data [ 10 ] ;
} cakey ;
2014-06-17 12:56:58 +04:00
static int __init ca_keys_setup ( char * str )
{
if ( ! str ) /* default system keyring */
return 1 ;
2014-09-16 20:36:13 +04:00
if ( strncmp ( str , " id: " , 3 ) = = 0 ) {
2015-02-11 15:33:34 +03:00
struct asymmetric_key_id * p = & cakey . id ;
size_t hexlen = ( strlen ( str ) - 3 ) / 2 ;
int ret ;
if ( hexlen = = 0 | | hexlen > sizeof ( cakey . data ) ) {
pr_err ( " Missing or invalid ca_keys id \n " ) ;
return 1 ;
}
ret = __asymmetric_key_hex_to_key_id ( str + 3 , p , hexlen ) ;
if ( ret < 0 )
pr_err ( " Unparsable ca_keys id hex string \n " ) ;
else
2014-09-16 20:36:13 +04:00
ca_keyid = p ; /* owner key 'id:xxxxxx' */
} else if ( strcmp ( str , " builtin " ) = = 0 ) {
2014-06-17 12:56:59 +04:00
use_builtin_keys = true ;
2014-09-16 20:36:13 +04:00
}
2014-06-17 12:56:58 +04:00
return 1 ;
}
__setup ( " ca_keys= " , ca_keys_setup ) ;
# endif
2014-07-28 17:11:32 +04:00
/**
* x509_request_asymmetric_key - Request a key by X .509 certificate params .
* @ keyring : The keys to search .
2015-07-20 23:16:26 +03:00
* @ id : The issuer & serialNumber to look for or NULL .
* @ skid : The subjectKeyIdentifier to look for or NULL .
2014-10-06 18:21:05 +04:00
* @ partial : Use partial match if true , exact if false .
2014-07-28 17:11:32 +04:00
*
2015-07-20 23:16:26 +03:00
* Find a key in the given keyring by identifier . The preferred identifier is
* the issuer + serialNumber and the fallback identifier is the
* subjectKeyIdentifier . If both are given , the lookup is by the former , but
* the latter must also match .
2013-08-20 22:36:27 +04:00
*/
2014-07-28 17:11:32 +04:00
struct key * x509_request_asymmetric_key ( struct key * keyring ,
2015-07-20 23:16:26 +03:00
const struct asymmetric_key_id * id ,
const struct asymmetric_key_id * skid ,
2014-10-06 18:21:05 +04:00
bool partial )
2013-08-20 22:36:27 +04:00
{
2015-07-20 23:16:26 +03:00
struct key * key ;
key_ref_t ref ;
const char * lookup ;
char * req , * p ;
int len ;
if ( id ) {
lookup = id - > data ;
len = id - > len ;
} else {
lookup = skid - > data ;
len = skid - > len ;
}
2014-09-16 20:36:13 +04:00
/* Construct an identifier "id:<keyid>". */
2015-07-20 23:16:26 +03:00
p = req = kmalloc ( 2 + 1 + len * 2 + 1 , GFP_KERNEL ) ;
if ( ! req )
2013-08-20 22:36:27 +04:00
return ERR_PTR ( - ENOMEM ) ;
2014-10-06 18:21:05 +04:00
if ( partial ) {
* p + + = ' i ' ;
* p + + = ' d ' ;
} else {
* p + + = ' e ' ;
* p + + = ' x ' ;
}
2014-09-16 20:36:13 +04:00
* p + + = ' : ' ;
2015-07-20 23:16:26 +03:00
p = bin2hex ( p , lookup , len ) ;
2014-09-16 20:36:13 +04:00
* p = 0 ;
2013-08-20 22:36:27 +04:00
2015-07-20 23:16:26 +03:00
pr_debug ( " Look up: \" %s \" \n " , req ) ;
2013-08-20 22:36:27 +04:00
2015-07-20 23:16:26 +03:00
ref = keyring_search ( make_key_ref ( keyring , 1 ) ,
& key_type_asymmetric , req ) ;
if ( IS_ERR ( ref ) )
pr_debug ( " Request for key '%s' err %ld \n " , req , PTR_ERR ( ref ) ) ;
kfree ( req ) ;
2013-08-20 22:36:27 +04:00
2015-07-20 23:16:26 +03:00
if ( IS_ERR ( ref ) ) {
switch ( PTR_ERR ( ref ) ) {
2013-08-20 22:36:27 +04:00
/* Hide some search errors */
case - EACCES :
case - ENOTDIR :
case - EAGAIN :
return ERR_PTR ( - ENOKEY ) ;
default :
2015-07-20 23:16:26 +03:00
return ERR_CAST ( ref ) ;
}
}
key = key_ref_to_ptr ( ref ) ;
if ( id & & skid ) {
const struct asymmetric_key_ids * kids = asymmetric_key_ids ( key ) ;
if ( ! kids - > id [ 1 ] ) {
pr_debug ( " issuer+serial match, but expected SKID missing \n " ) ;
goto reject ;
}
if ( ! asymmetric_key_id_same ( skid , kids - > id [ 1 ] ) ) {
pr_debug ( " issuer+serial match, but SKID does not \n " ) ;
goto reject ;
2013-08-20 22:36:27 +04:00
}
}
2015-07-20 23:16:26 +03:00
pr_devel ( " <==%s() = 0 [%x] \n " , __func__ , key_serial ( key ) ) ;
return key ;
2013-08-20 22:36:27 +04:00
2015-07-20 23:16:26 +03:00
reject :
key_put ( key ) ;
return ERR_PTR ( - EKEYREJECTED ) ;
2013-08-20 22:36:27 +04:00
}
2014-08-03 15:54:48 +04:00
EXPORT_SYMBOL_GPL ( x509_request_asymmetric_key ) ;
2013-08-20 22:36:27 +04:00
2012-09-24 20:11:48 +04:00
/*
2013-08-30 19:18:02 +04:00
* Set up the signature parameters in an X .509 certificate . This involves
* digesting the signed data and extracting the signature .
2012-09-24 20:11:48 +04:00
*/
2013-08-30 19:18:02 +04:00
int x509_get_sig_params ( struct x509_certificate * cert )
2012-09-24 20:11:48 +04:00
{
struct crypto_shash * tfm ;
struct shash_desc * desc ;
size_t digest_size , desc_size ;
2013-08-30 19:18:02 +04:00
void * digest ;
2012-09-24 20:11:48 +04:00
int ret ;
pr_devel ( " ==>%s() \n " , __func__ ) ;
2013-08-30 19:18:02 +04:00
2014-09-16 20:36:15 +04:00
if ( cert - > unsupported_crypto )
return - ENOPKG ;
2013-08-30 19:18:02 +04:00
if ( cert - > sig . rsa . s )
return 0 ;
cert - > sig . rsa . s = mpi_read_raw_data ( cert - > raw_sig , cert - > raw_sig_size ) ;
if ( ! cert - > sig . rsa . s )
return - ENOMEM ;
cert - > sig . nr_mpi = 1 ;
2012-09-24 20:11:48 +04:00
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be .
*/
2013-05-06 16:58:15 +04:00
tfm = crypto_alloc_shash ( hash_algo_name [ cert - > sig . pkey_hash_algo ] , 0 , 0 ) ;
2014-09-16 20:36:15 +04:00
if ( IS_ERR ( tfm ) ) {
if ( PTR_ERR ( tfm ) = = - ENOENT ) {
cert - > unsupported_crypto = true ;
return - ENOPKG ;
}
return PTR_ERR ( tfm ) ;
}
2012-09-24 20:11:48 +04:00
desc_size = crypto_shash_descsize ( tfm ) + sizeof ( * desc ) ;
digest_size = crypto_shash_digestsize ( tfm ) ;
2013-08-30 19:18:02 +04:00
/* We allocate the hash operational data storage on the end of the
* digest storage space .
2012-09-24 20:11:48 +04:00
*/
ret = - ENOMEM ;
2013-08-30 19:18:02 +04:00
digest = kzalloc ( digest_size + desc_size , GFP_KERNEL ) ;
if ( ! digest )
goto error ;
2012-09-24 20:11:48 +04:00
2013-08-30 19:18:02 +04:00
cert - > sig . digest = digest ;
cert - > sig . digest_size = digest_size ;
2012-09-24 20:11:48 +04:00
2013-08-30 19:18:02 +04:00
desc = digest + digest_size ;
desc - > tfm = tfm ;
desc - > flags = CRYPTO_TFM_REQ_MAY_SLEEP ;
2012-09-24 20:11:48 +04:00
ret = crypto_shash_init ( desc ) ;
if ( ret < 0 )
goto error ;
2013-08-30 19:18:02 +04:00
might_sleep ( ) ;
ret = crypto_shash_finup ( desc , cert - > tbs , cert - > tbs_size , digest ) ;
error :
crypto_free_shash ( tfm ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , ret ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( x509_get_sig_params ) ;
2012-09-24 20:11:48 +04:00
2013-08-30 19:18:02 +04:00
/*
* Check the signature on a certificate using the provided public key
*/
int x509_check_signature ( const struct public_key * pub ,
struct x509_certificate * cert )
{
int ret ;
2012-09-24 20:11:48 +04:00
2013-08-30 19:18:02 +04:00
pr_devel ( " ==>%s() \n " , __func__ ) ;
2012-09-24 20:11:48 +04:00
2013-08-30 19:18:02 +04:00
ret = x509_get_sig_params ( cert ) ;
if ( ret < 0 )
return ret ;
2012-09-24 20:11:48 +04:00
2013-08-30 19:18:02 +04:00
ret = public_key_verify_signature ( pub , & cert - > sig ) ;
2014-09-16 20:36:15 +04:00
if ( ret = = - ENOPKG )
cert - > unsupported_crypto = true ;
2012-09-24 20:11:48 +04:00
pr_debug ( " Cert Verification: %d \n " , ret ) ;
return ret ;
}
2013-08-30 19:18:02 +04:00
EXPORT_SYMBOL_GPL ( x509_check_signature ) ;
2012-09-24 20:11:48 +04:00
2013-08-20 22:36:27 +04:00
/*
* Check the new certificate against the ones in the trust keyring . If one of
* those is the signing key and validates the new certificate , then mark the
* new certificate as being trusted .
*
* Return 0 if the new certificate was successfully validated , 1 if we couldn ' t
* find a matching parent certificate in the trusted list and an error if there
* is a matching certificate but the signature check fails .
*/
static int x509_validate_trust ( struct x509_certificate * cert ,
struct key * trust_keyring )
{
struct key * key ;
int ret = 1 ;
if ( ! trust_keyring )
return - EOPNOTSUPP ;
2015-07-20 23:16:26 +03:00
if ( ca_keyid & & ! asymmetric_key_id_partial ( cert - > akid_skid , ca_keyid ) )
2014-06-17 12:56:58 +04:00
return - EPERM ;
2015-07-20 23:16:26 +03:00
key = x509_request_asymmetric_key ( trust_keyring ,
cert - > akid_id , cert - > akid_skid ,
2014-10-06 18:21:05 +04:00
false ) ;
2013-08-20 22:36:27 +04:00
if ( ! IS_ERR ( key ) ) {
2014-06-17 12:56:59 +04:00
if ( ! use_builtin_keys
| | test_bit ( KEY_FLAG_BUILTIN , & key - > flags ) )
ret = x509_check_signature ( key - > payload . data , cert ) ;
2013-08-20 22:36:27 +04:00
key_put ( key ) ;
}
return ret ;
}
2012-09-24 20:11:48 +04:00
/*
* Attempt to parse a data blob for a key as an X509 certificate .
*/
static int x509_key_preparse ( struct key_preparsed_payload * prep )
{
2014-09-16 20:36:13 +04:00
struct asymmetric_key_ids * kids ;
2012-09-24 20:11:48 +04:00
struct x509_certificate * cert ;
2014-09-16 20:36:13 +04:00
const char * q ;
2012-09-24 20:11:48 +04:00
size_t srlen , sulen ;
2014-09-16 20:36:13 +04:00
char * desc = NULL , * p ;
2012-09-24 20:11:48 +04:00
int ret ;
cert = x509_cert_parse ( prep - > data , prep - > datalen ) ;
if ( IS_ERR ( cert ) )
return PTR_ERR ( cert ) ;
pr_devel ( " Cert Issuer: %s \n " , cert - > issuer ) ;
pr_devel ( " Cert Subject: %s \n " , cert - > subject ) ;
2013-08-30 19:18:15 +04:00
if ( cert - > pub - > pkey_algo > = PKEY_ALGO__LAST | |
cert - > sig . pkey_algo > = PKEY_ALGO__LAST | |
cert - > sig . pkey_hash_algo > = PKEY_HASH__LAST | |
! pkey_algo [ cert - > pub - > pkey_algo ] | |
! pkey_algo [ cert - > sig . pkey_algo ] | |
2013-05-06 16:58:15 +04:00
! hash_algo_name [ cert - > sig . pkey_hash_algo ] ) {
2013-08-30 19:18:15 +04:00
ret = - ENOPKG ;
goto error_free_cert ;
}
2013-08-30 19:15:24 +04:00
pr_devel ( " Cert Key Algo: %s \n " , pkey_algo_name [ cert - > pub - > pkey_algo ] ) ;
2015-07-29 18:58:32 +03:00
pr_devel ( " Cert Valid period: %lld-%lld \n " , cert - > valid_from , cert - > valid_to ) ;
2013-04-25 11:43:56 +04:00
pr_devel ( " Cert Signature: %s + %s \n " ,
pkey_algo_name [ cert - > sig . pkey_algo ] ,
2013-05-06 16:58:15 +04:00
hash_algo_name [ cert - > sig . pkey_hash_algo ] ) ;
2012-09-24 20:11:48 +04:00
2013-08-30 19:15:24 +04:00
cert - > pub - > algo = pkey_algo [ cert - > pub - > pkey_algo ] ;
2012-09-24 20:11:48 +04:00
cert - > pub - > id_type = PKEY_ID_X509 ;
2013-08-30 19:18:31 +04:00
/* Check the signature on the key if it appears to be self-signed */
2015-07-20 23:16:26 +03:00
if ( ( ! cert - > akid_skid & & ! cert - > akid_id ) | |
asymmetric_key_id_same ( cert - > skid , cert - > akid_skid ) | |
asymmetric_key_id_same ( cert - > id , cert - > akid_id ) ) {
2013-08-20 22:36:27 +04:00
ret = x509_check_signature ( cert - > pub , cert ) ; /* self-signed */
2012-09-24 20:11:48 +04:00
if ( ret < 0 )
goto error_free_cert ;
2013-08-20 22:36:27 +04:00
} else if ( ! prep - > trusted ) {
ret = x509_validate_trust ( cert , get_system_trusted_keyring ( ) ) ;
if ( ! ret )
prep - > trusted = 1 ;
2012-09-24 20:11:48 +04:00
}
/* Propose a description */
sulen = strlen ( cert - > subject ) ;
2014-10-03 19:17:02 +04:00
if ( cert - > raw_skid ) {
srlen = cert - > raw_skid_size ;
q = cert - > raw_skid ;
} else {
srlen = cert - > raw_serial_size ;
q = cert - > raw_serial ;
}
2014-09-16 20:36:13 +04:00
2012-09-24 20:11:48 +04:00
ret = - ENOMEM ;
2014-09-16 20:36:13 +04:00
desc = kmalloc ( sulen + 2 + srlen * 2 + 1 , GFP_KERNEL ) ;
2012-09-24 20:11:48 +04:00
if ( ! desc )
goto error_free_cert ;
2014-09-16 20:36:13 +04:00
p = memcpy ( desc , cert - > subject , sulen ) ;
p + = sulen ;
* p + + = ' : ' ;
* p + + = ' ' ;
p = bin2hex ( p , q , srlen ) ;
* p = 0 ;
kids = kmalloc ( sizeof ( struct asymmetric_key_ids ) , GFP_KERNEL ) ;
if ( ! kids )
goto error_free_desc ;
kids - > id [ 0 ] = cert - > id ;
kids - > id [ 1 ] = cert - > skid ;
2012-09-24 20:11:48 +04:00
/* We're pinning the module by being linked against it */
__module_get ( public_key_subtype . owner ) ;
prep - > type_data [ 0 ] = & public_key_subtype ;
2014-09-16 20:36:13 +04:00
prep - > type_data [ 1 ] = kids ;
2014-07-18 21:56:34 +04:00
prep - > payload [ 0 ] = cert - > pub ;
2012-09-24 20:11:48 +04:00
prep - > description = desc ;
prep - > quotalen = 100 ;
/* We've finished with the certificate */
cert - > pub = NULL ;
2014-09-16 20:36:13 +04:00
cert - > id = NULL ;
cert - > skid = NULL ;
2012-09-24 20:11:48 +04:00
desc = NULL ;
ret = 0 ;
2014-09-16 20:36:13 +04:00
error_free_desc :
kfree ( desc ) ;
2012-09-24 20:11:48 +04:00
error_free_cert :
x509_free_certificate ( cert ) ;
return ret ;
}
static struct asymmetric_key_parser x509_key_parser = {
. owner = THIS_MODULE ,
. name = " x509 " ,
. parse = x509_key_preparse ,
} ;
/*
* Module stuff
*/
static int __init x509_key_init ( void )
{
return register_asymmetric_key_parser ( & x509_key_parser ) ;
}
static void __exit x509_key_exit ( void )
{
unregister_asymmetric_key_parser ( & x509_key_parser ) ;
}
module_init ( x509_key_init ) ;
module_exit ( x509_key_exit ) ;
2013-09-17 15:14:55 +04:00
MODULE_DESCRIPTION ( " X.509 certificate parser " ) ;
MODULE_LICENSE ( " GPL " ) ;