2012-09-24 17:11:48 +01: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 14:36:27 -04:00
# include <keys/system_keyring.h>
2012-09-24 17:11:48 +01:00
# include <crypto/hash.h>
# include "asymmetric_keys.h"
# include "public_key.h"
# include "x509_parser.h"
2013-08-20 14:36:27 -04:00
/*
* Find a key in the given keyring by issuer and authority .
*/
static struct key * x509_request_asymmetric_key (
struct key * keyring ,
const char * signer , size_t signer_len ,
const char * authority , size_t auth_len )
{
key_ref_t key ;
char * id ;
/* Construct an identifier. */
id = kmalloc ( signer_len + 2 + auth_len + 1 , GFP_KERNEL ) ;
if ( ! id )
return ERR_PTR ( - ENOMEM ) ;
memcpy ( id , signer , signer_len ) ;
id [ signer_len + 0 ] = ' : ' ;
id [ signer_len + 1 ] = ' ' ;
memcpy ( id + signer_len + 2 , authority , auth_len ) ;
id [ signer_len + 2 + auth_len ] = 0 ;
pr_debug ( " Look up: \" %s \" \n " , id ) ;
key = keyring_search ( make_key_ref ( keyring , 1 ) ,
& key_type_asymmetric , id ) ;
if ( IS_ERR ( key ) )
pr_debug ( " Request for module key '%s' err %ld \n " ,
id , PTR_ERR ( key ) ) ;
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 ) ;
}
2012-09-24 17:11:48 +01:00
/*
2013-08-30 16:18:02 +01:00
* Set up the signature parameters in an X .509 certificate . This involves
* digesting the signed data and extracting the signature .
2012-09-24 17:11:48 +01:00
*/
2013-08-30 16:18:02 +01:00
int x509_get_sig_params ( struct x509_certificate * cert )
2012-09-24 17:11:48 +01:00
{
struct crypto_shash * tfm ;
struct shash_desc * desc ;
size_t digest_size , desc_size ;
2013-08-30 16:18:02 +01:00
void * digest ;
2012-09-24 17:11:48 +01:00
int ret ;
pr_devel ( " ==>%s() \n " , __func__ ) ;
2013-08-30 16:18:02 +01: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 17:11:48 +01:00
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be .
*/
2013-08-30 16:18:02 +01:00
tfm = crypto_alloc_shash ( pkey_hash_algo_name [ cert - > sig . pkey_hash_algo ] , 0 , 0 ) ;
2012-09-24 17:11:48 +01:00
if ( IS_ERR ( tfm ) )
return ( PTR_ERR ( tfm ) = = - ENOENT ) ? - ENOPKG : PTR_ERR ( tfm ) ;
desc_size = crypto_shash_descsize ( tfm ) + sizeof ( * desc ) ;
digest_size = crypto_shash_digestsize ( tfm ) ;
2013-08-30 16:18:02 +01:00
/* We allocate the hash operational data storage on the end of the
* digest storage space .
2012-09-24 17:11:48 +01:00
*/
ret = - ENOMEM ;
2013-08-30 16:18:02 +01:00
digest = kzalloc ( digest_size + desc_size , GFP_KERNEL ) ;
if ( ! digest )
goto error ;
2012-09-24 17:11:48 +01:00
2013-08-30 16:18:02 +01:00
cert - > sig . digest = digest ;
cert - > sig . digest_size = digest_size ;
2012-09-24 17:11:48 +01:00
2013-08-30 16:18:02 +01:00
desc = digest + digest_size ;
desc - > tfm = tfm ;
desc - > flags = CRYPTO_TFM_REQ_MAY_SLEEP ;
2012-09-24 17:11:48 +01:00
ret = crypto_shash_init ( desc ) ;
if ( ret < 0 )
goto error ;
2013-08-30 16:18:02 +01: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 17:11:48 +01:00
2013-08-30 16:18:02 +01: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 17:11:48 +01:00
2013-08-30 16:18:02 +01:00
pr_devel ( " ==>%s() \n " , __func__ ) ;
2012-09-24 17:11:48 +01:00
2013-08-30 16:18:02 +01:00
ret = x509_get_sig_params ( cert ) ;
if ( ret < 0 )
return ret ;
2012-09-24 17:11:48 +01:00
2013-08-30 16:18:02 +01:00
ret = public_key_verify_signature ( pub , & cert - > sig ) ;
2012-09-24 17:11:48 +01:00
pr_debug ( " Cert Verification: %d \n " , ret ) ;
return ret ;
}
2013-08-30 16:18:02 +01:00
EXPORT_SYMBOL_GPL ( x509_check_signature ) ;
2012-09-24 17:11:48 +01:00
2013-08-20 14: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 )
{
const struct public_key * pk ;
struct key * key ;
int ret = 1 ;
key = x509_request_asymmetric_key ( trust_keyring ,
cert - > issuer , strlen ( cert - > issuer ) ,
cert - > authority ,
strlen ( cert - > authority ) ) ;
if ( ! IS_ERR ( key ) ) {
pk = key - > payload . data ;
ret = x509_check_signature ( pk , cert ) ;
}
return ret ;
}
2012-09-24 17:11:48 +01:00
/*
* Attempt to parse a data blob for a key as an X509 certificate .
*/
static int x509_key_preparse ( struct key_preparsed_payload * prep )
{
struct x509_certificate * cert ;
size_t srlen , sulen ;
char * desc = NULL ;
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 16:18:15 +01: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 ] | |
! pkey_hash_algo_name [ cert - > sig . pkey_hash_algo ] ) {
ret = - ENOPKG ;
goto error_free_cert ;
}
2013-08-30 16:15:24 +01:00
pr_devel ( " Cert Key Algo: %s \n " , pkey_algo_name [ cert - > pub - > pkey_algo ] ) ;
2012-10-04 14:21:23 +01:00
pr_devel ( " Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d \n " ,
2012-10-02 14:36:16 +01: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 14:21:23 +01:00
pr_devel ( " Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d \n " ,
2012-10-02 14:36:16 +01: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 ) ;
2012-09-24 17:11:48 +01:00
pr_devel ( " Cert Signature: %s + %s \n " ,
2013-08-30 16:18:02 +01:00
pkey_algo_name [ cert - > sig . pkey_algo ] ,
pkey_hash_algo_name [ cert - > sig . pkey_hash_algo ] ) ;
2012-09-24 17:11:48 +01:00
2013-08-30 16:18:31 +01:00
if ( ! cert - > fingerprint ) {
pr_warn ( " Cert for '%s' must have a SubjKeyId extension \n " ,
2012-09-24 17:11:48 +01:00
cert - > subject ) ;
ret = - EKEYREJECTED ;
goto error_free_cert ;
}
2013-08-30 16:15:24 +01:00
cert - > pub - > algo = pkey_algo [ cert - > pub - > pkey_algo ] ;
2012-09-24 17:11:48 +01:00
cert - > pub - > id_type = PKEY_ID_X509 ;
2013-08-30 16:18:31 +01:00
/* Check the signature on the key if it appears to be self-signed */
if ( ! cert - > authority | |
strcmp ( cert - > fingerprint , cert - > authority ) = = 0 ) {
2013-08-20 14:36:27 -04:00
ret = x509_check_signature ( cert - > pub , cert ) ; /* self-signed */
2012-09-24 17:11:48 +01:00
if ( ret < 0 )
goto error_free_cert ;
2013-08-20 14:36:27 -04:00
} else {
ret = x509_validate_trust ( cert , system_trusted_keyring ) ;
if ( ! ret )
prep - > trusted = 1 ;
2012-09-24 17:11:48 +01:00
}
/* Propose a description */
sulen = strlen ( cert - > subject ) ;
srlen = strlen ( cert - > fingerprint ) ;
ret = - ENOMEM ;
desc = kmalloc ( sulen + 2 + srlen + 1 , GFP_KERNEL ) ;
if ( ! desc )
goto error_free_cert ;
memcpy ( desc , cert - > subject , sulen ) ;
desc [ sulen ] = ' : ' ;
desc [ sulen + 1 ] = ' ' ;
memcpy ( desc + sulen + 2 , cert - > fingerprint , srlen ) ;
desc [ sulen + 2 + srlen ] = 0 ;
/* We're pinning the module by being linked against it */
__module_get ( public_key_subtype . owner ) ;
prep - > type_data [ 0 ] = & public_key_subtype ;
prep - > type_data [ 1 ] = cert - > fingerprint ;
prep - > payload = cert - > pub ;
prep - > description = desc ;
prep - > quotalen = 100 ;
/* We've finished with the certificate */
cert - > pub = NULL ;
cert - > fingerprint = NULL ;
desc = NULL ;
ret = 0 ;
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 ) ;