2014-07-01 19:40:20 +04:00
/* Validate the trust chain of a PKCS#7 message.
*
* 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) "PKCS7: "fmt
# include <linux/kernel.h>
# include <linux/export.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/asn1.h>
# include <linux/key.h>
# include <keys/asymmetric-type.h>
# include "public_key.h"
# include "pkcs7_parser.h"
/**
* Check the trust on one PKCS # 7 SignedInfo block .
*/
2014-09-16 20:07:07 +04:00
static int pkcs7_validate_trust_one ( struct pkcs7_message * pkcs7 ,
struct pkcs7_signed_info * sinfo ,
struct key * trust_keyring )
2014-07-01 19:40:20 +04:00
{
struct public_key_signature * sig = & sinfo - > sig ;
struct x509_certificate * x509 , * last = NULL , * p ;
struct key * key ;
bool trusted ;
int ret ;
kenter ( " ,%u, " , sinfo - > index ) ;
2014-09-16 20:36:15 +04:00
if ( sinfo - > unsupported_crypto ) {
kleave ( " = -ENOPKG [cached] " ) ;
return - ENOPKG ;
}
2014-07-01 19:40:20 +04:00
for ( x509 = sinfo - > signer ; x509 ; x509 = x509 - > signer ) {
if ( x509 - > seen ) {
if ( x509 - > verified ) {
trusted = x509 - > trusted ;
goto verified ;
}
kleave ( " = -ENOKEY [cached] " ) ;
return - ENOKEY ;
}
x509 - > seen = true ;
/* Look to see if this certificate is present in the trusted
* keys .
*/
2014-10-06 18:21:05 +04:00
key = x509_request_asymmetric_key ( trust_keyring , x509 - > id ,
false ) ;
2014-09-16 20:36:17 +04:00
if ( ! IS_ERR ( key ) ) {
2014-07-01 19:40:20 +04:00
/* One of the X.509 certificates in the PKCS#7 message
* is apparently the same as one we already trust .
* Verify that the trusted variant can also validate
* the signature on the descendant .
*/
2014-09-16 20:36:17 +04:00
pr_devel ( " sinfo %u: Cert %u as key %x \n " ,
sinfo - > index , x509 - > index , key_serial ( key ) ) ;
2014-07-01 19:40:20 +04:00
goto matched ;
2014-09-16 20:36:17 +04:00
}
2014-07-01 19:40:20 +04:00
if ( key = = ERR_PTR ( - ENOMEM ) )
return - ENOMEM ;
/* Self-signed certificates form roots of their own, and if we
* don ' t know them , then we can ' t accept them .
*/
if ( x509 - > next = = x509 ) {
kleave ( " = -ENOKEY [unknown self-signed] " ) ;
return - ENOKEY ;
}
might_sleep ( ) ;
last = x509 ;
sig = & last - > sig ;
}
/* No match - see if the root certificate has a signer amongst the
* trusted keys .
*/
2015-07-20 23:16:26 +03:00
if ( last & & last - > akid_skid ) {
key = x509_request_asymmetric_key ( trust_keyring , last - > akid_skid ,
2014-10-06 18:21:05 +04:00
false ) ;
2014-09-16 20:36:17 +04:00
if ( ! IS_ERR ( key ) ) {
x509 = last ;
pr_devel ( " sinfo %u: Root cert %u signer is key %x \n " ,
sinfo - > index , x509 - > index , key_serial ( key ) ) ;
goto matched ;
}
if ( PTR_ERR ( key ) ! = - ENOKEY )
return PTR_ERR ( key ) ;
2014-07-01 19:40:20 +04:00
}
2014-09-16 20:36:17 +04:00
/* As a last resort, see if we have a trusted public key that matches
* the signed info directly .
*/
key = x509_request_asymmetric_key ( trust_keyring ,
2014-10-06 18:21:05 +04:00
sinfo - > signing_cert_id ,
false ) ;
2014-09-16 20:36:17 +04:00
if ( ! IS_ERR ( key ) ) {
pr_devel ( " sinfo %u: Direct signer is key %x \n " ,
sinfo - > index , key_serial ( key ) ) ;
x509 = NULL ;
goto matched ;
}
if ( PTR_ERR ( key ) ! = - ENOKEY )
return PTR_ERR ( key ) ;
kleave ( " = -ENOKEY [no backref] " ) ;
return - ENOKEY ;
2014-07-01 19:40:20 +04:00
matched :
ret = verify_signature ( key , sig ) ;
trusted = test_bit ( KEY_FLAG_TRUSTED , & key - > flags ) ;
key_put ( key ) ;
if ( ret < 0 ) {
if ( ret = = - ENOMEM )
return ret ;
kleave ( " = -EKEYREJECTED [verify %d] " , ret ) ;
return - EKEYREJECTED ;
}
verified :
2014-09-16 20:36:17 +04:00
if ( x509 ) {
x509 - > verified = true ;
for ( p = sinfo - > signer ; p ! = x509 ; p = p - > signer ) {
p - > verified = true ;
p - > trusted = trusted ;
}
2014-07-01 19:40:20 +04:00
}
sinfo - > trusted = trusted ;
kleave ( " = 0 " ) ;
return 0 ;
}
/**
* pkcs7_validate_trust - Validate PKCS # 7 trust chain
* @ pkcs7 : The PKCS # 7 certificate to validate
* @ trust_keyring : Signing certificates to use as starting points
* @ _trusted : Set to true if trustworth , false otherwise
*
* Validate that the certificate chain inside the PKCS # 7 message intersects
* keys we already know and trust .
*
* Returns , in order of descending priority :
*
* ( * ) - EKEYREJECTED if a signature failed to match for which we have a valid
* key , or :
*
* ( * ) 0 if at least one signature chain intersects with the keys in the trust
* keyring , or :
*
* ( * ) - ENOPKG if a suitable crypto module couldn ' t be found for a check on a
* chain .
*
* ( * ) - ENOKEY if we couldn ' t find a match for any of the signature chains in
* the message .
*
* May also return - ENOMEM .
*/
int pkcs7_validate_trust ( struct pkcs7_message * pkcs7 ,
struct key * trust_keyring ,
bool * _trusted )
{
struct pkcs7_signed_info * sinfo ;
struct x509_certificate * p ;
2014-09-16 20:36:15 +04:00
int cached_ret = - ENOKEY ;
int ret ;
2014-07-01 19:40:20 +04:00
for ( p = pkcs7 - > certs ; p ; p = p - > next )
p - > seen = false ;
for ( sinfo = pkcs7 - > signed_infos ; sinfo ; sinfo = sinfo - > next ) {
ret = pkcs7_validate_trust_one ( pkcs7 , sinfo , trust_keyring ) ;
2014-09-16 20:36:15 +04:00
switch ( ret ) {
case - ENOKEY :
continue ;
case - ENOPKG :
if ( cached_ret = = - ENOKEY )
2014-07-01 19:40:20 +04:00
cached_ret = - ENOPKG ;
2014-09-16 20:36:15 +04:00
continue ;
case 0 :
* _trusted | = sinfo - > trusted ;
cached_ret = 0 ;
continue ;
default :
return ret ;
2014-07-01 19:40:20 +04:00
}
}
return cached_ret ;
}
EXPORT_SYMBOL_GPL ( pkcs7_validate_trust ) ;