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
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 ) {
struct asymmetric_key_id * p ;
2014-10-06 20:02:32 +04:00
p = asymmetric_key_hex_to_key_id ( str + 3 ) ;
2014-09-16 20:36:13 +04:00
if ( p = = ERR_PTR ( - EINVAL ) )
pr_err ( " Unparsable hex string in ca_keys \n " ) ;
else if ( ! IS_ERR ( p ) )
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 .
2014-09-16 20:36:13 +04:00
* @ kid : The key ID .
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
*
* Find a key in the given keyring by subject name and key ID . These might ,
* for instance , be the issuer name and the authority key ID of an X .509
* certificate that needs to be verified .
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 ,
2014-10-06 18:21:05 +04:00
const struct asymmetric_key_id * kid ,
bool partial )
2013-08-20 22:36:27 +04:00
{
key_ref_t key ;
2014-09-16 20:36:13 +04:00
char * id , * p ;
2013-08-20 22:36:27 +04:00
2014-09-16 20:36:13 +04:00
/* Construct an identifier "id:<keyid>". */
p = id = kmalloc ( 2 + 1 + kid - > len * 2 + 1 , GFP_KERNEL ) ;
2013-08-20 22:36:27 +04:00
if ( ! id )
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 + + = ' : ' ;
p = bin2hex ( p , kid - > data , kid - > len ) ;
* p = 0 ;
2013-08-20 22:36:27 +04:00
pr_debug ( " Look up: \" %s \" \n " , id ) ;
key = keyring_search ( make_key_ref ( keyring , 1 ) ,
& key_type_asymmetric , id ) ;
if ( IS_ERR ( key ) )
2014-07-28 17:11:32 +04:00
pr_debug ( " Request for key '%s' err %ld \n " , id , PTR_ERR ( key ) ) ;
2013-08-20 22:36:27 +04:00
kfree ( id ) ;
if ( IS_ERR ( key ) ) {
switch ( PTR_ERR ( key ) ) {
/* Hide some search errors */
case - EACCES :
case - ENOTDIR :
case - EAGAIN :
return ERR_PTR ( - ENOKEY ) ;
default :
return ERR_CAST ( key ) ;
}
}
pr_devel ( " <==%s() = 0 [%x] \n " , __func__ ,
key_serial ( key_ref_to_ptr ( key ) ) ) ;
return key_ref_to_ptr ( key ) ;
}
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 ;
2014-10-06 18:21:05 +04:00
if ( ca_keyid & & ! asymmetric_key_id_partial ( cert - > authority , ca_keyid ) )
2014-06-17 12:56:58 +04:00
return - EPERM ;
2014-10-06 18:21:05 +04:00
key = x509_request_asymmetric_key ( trust_keyring , cert - > authority ,
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 ] ) ;
2012-10-04 17:21:23 +04:00
pr_devel ( " Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d \n " ,
2012-10-02 17:36:16 +04:00
cert - > valid_from . tm_year + 1900 , cert - > valid_from . tm_mon + 1 ,
cert - > valid_from . tm_mday , cert - > valid_from . tm_hour ,
cert - > valid_from . tm_min , cert - > valid_from . tm_sec ) ;
2012-10-04 17:21:23 +04:00
pr_devel ( " Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d \n " ,
2012-10-02 17:36:16 +04:00
cert - > valid_to . tm_year + 1900 , cert - > valid_to . tm_mon + 1 ,
cert - > valid_to . tm_mday , cert - > valid_to . tm_hour ,
cert - > valid_to . tm_min , cert - > valid_to . tm_sec ) ;
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 */
if ( ! cert - > authority | |
2014-09-16 20:36:13 +04:00
asymmetric_key_id_same ( cert - > skid , cert - > authority ) ) {
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
if ( srlen > 1 & & * q = = 0 ) {
srlen - - ;
q + + ;
}
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 " ) ;