iPXE
certstore.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include <ipxe/init.h>
00029 #include <ipxe/dhcp.h>
00030 #include <ipxe/settings.h>
00031 #include <ipxe/malloc.h>
00032 #include <ipxe/crypto.h>
00033 #include <ipxe/asn1.h>
00034 #include <ipxe/x509.h>
00035 #include <ipxe/certstore.h>
00036 
00037 /** @file
00038  *
00039  * Certificate store
00040  *
00041  */
00042 
00043 /** Raw certificate data for all permanent stored certificates */
00044 #undef CERT
00045 #define CERT( _index, _path )                                           \
00046         extern char stored_cert_ ## _index ## _data[];                  \
00047         extern char stored_cert_ ## _index ## _len[];                   \
00048         __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t"       \
00049                   "\nstored_cert_" #_index "_data:\n\t"                 \
00050                   ".incbin \"" _path "\"\n\t"                           \
00051                   "\nstored_cert_" #_index "_end:\n\t"                  \
00052                   ".equ stored_cert_" #_index "_len, "                  \
00053                         "( stored_cert_" #_index "_end - "              \
00054                         "  stored_cert_" #_index "_data )\n\t"          \
00055                   ".previous\n\t" );
00056 CERT_ALL
00057 
00058 /** Raw certificate cursors for all permanent stored certificates */
00059 #undef CERT
00060 #define CERT( _index, _path ) {                                         \
00061         .data = stored_cert_ ## _index ## _data,                        \
00062         .len = ( size_t ) stored_cert_ ## _index ## _len,               \
00063 },
00064 static struct asn1_cursor certstore_raw[] = {
00065         CERT_ALL
00066 };
00067 
00068 /** X.509 certificate structures for all permanent stored certificates */
00069 static struct x509_certificate certstore_certs[ sizeof ( certstore_raw ) /
00070                                                 sizeof ( certstore_raw[0] ) ];
00071 
00072 /** Certificate store */
00073 struct x509_chain certstore = {
00074         .refcnt = REF_INIT ( ref_no_free ),
00075         .links = LIST_HEAD_INIT ( certstore.links ),
00076 };
00077 
00078 /**
00079  * Mark stored certificate as most recently used
00080  *
00081  * @v cert              X.509 certificate
00082  * @ret cert            X.509 certificate
00083  */
00084 static struct x509_certificate *
00085 certstore_found ( struct x509_certificate *cert ) {
00086 
00087         /* Mark as most recently used */
00088         list_del ( &cert->store.list );
00089         list_add ( &cert->store.list, &certstore.links );
00090         DBGC2 ( &certstore, "CERTSTORE found certificate %s\n",
00091                 x509_name ( cert ) );
00092 
00093         return cert;
00094 }
00095 
00096 /**
00097  * Find certificate in store
00098  *
00099  * @v raw               Raw certificate data
00100  * @ret cert            X.509 certificate, or NULL if not found
00101  */
00102 struct x509_certificate * certstore_find ( struct asn1_cursor *raw ) {
00103         struct x509_certificate *cert;
00104 
00105         /* Search for certificate within store */
00106         list_for_each_entry ( cert, &certstore.links, store.list ) {
00107                 if ( asn1_compare ( raw, &cert->raw ) == 0 )
00108                         return certstore_found ( cert );
00109         }
00110         return NULL;
00111 }
00112 
00113 /**
00114  * Find certificate in store corresponding to a private key
00115  *
00116  * @v key               Private key
00117  * @ret cert            X.509 certificate, or NULL if not found
00118  */
00119 struct x509_certificate * certstore_find_key ( struct asn1_cursor *key ) {
00120         struct x509_certificate *cert;
00121 
00122         /* Search for certificate within store */
00123         list_for_each_entry ( cert, &certstore.links, store.list ) {
00124                 if ( pubkey_match ( cert->signature_algorithm->pubkey,
00125                                     key->data, key->len,
00126                                     cert->subject.public_key.raw.data,
00127                                     cert->subject.public_key.raw.len ) == 0 )
00128                         return certstore_found ( cert );
00129         }
00130         return NULL;
00131 }
00132 
00133 /**
00134  * Add certificate to store
00135  *
00136  * @v cert              X.509 certificate
00137  */
00138 void certstore_add ( struct x509_certificate *cert ) {
00139 
00140         /* Add certificate to store */
00141         cert->store.cert = cert;
00142         x509_get ( cert );
00143         list_add ( &cert->store.list, &certstore.links );
00144         DBGC ( &certstore, "CERTSTORE added certificate %s\n",
00145                x509_name ( cert ) );
00146 }
00147 
00148 /**
00149  * Remove certificate from store
00150  *
00151  * @v cert              X.509 certificate
00152  */
00153 void certstore_del ( struct x509_certificate *cert ) {
00154 
00155         /* Ignore attempts to remove permanent certificates */
00156         if ( cert->flags & X509_FL_PERMANENT )
00157                 return;
00158 
00159         /* Remove certificate from store */
00160         DBGC ( &certstore, "CERTSTORE removed certificate %s\n",
00161                x509_name ( cert ) );
00162         list_del ( &cert->store.list );
00163         x509_put ( cert );
00164 }
00165 
00166 /**
00167  * Discard a stored certificate
00168  *
00169  * @ret discarded       Number of cached items discarded
00170  */
00171 static unsigned int certstore_discard ( void ) {
00172         struct x509_certificate *cert;
00173 
00174         /* Discard the least recently used certificate for which the
00175          * only reference is held by the store itself.
00176          */
00177         list_for_each_entry_reverse ( cert, &certstore.links, store.list ) {
00178 
00179                 /* Skip certificates for which another reference is held */
00180                 if ( cert->refcnt.count > 0 )
00181                         continue;
00182 
00183                 /* Skip certificates that were added at build time or
00184                  * added explicitly at run time.
00185                  */
00186                 if ( cert->flags & ( X509_FL_PERMANENT | X509_FL_EXPLICIT ) )
00187                         continue;
00188 
00189                 /* Discard certificate */
00190                 certstore_del ( cert );
00191                 return 1;
00192         }
00193 
00194         return 0;
00195 }
00196 
00197 /** Certificate store cache discarder */
00198 struct cache_discarder certstore_discarder __cache_discarder ( CACHE_NORMAL ) ={
00199         .discard = certstore_discard,
00200 };
00201 
00202 /**
00203  * Construct permanent certificate store
00204  *
00205  */
00206 static void certstore_init ( void ) {
00207         struct asn1_cursor *raw;
00208         struct x509_certificate *cert;
00209         int i;
00210         int rc;
00211 
00212         /* Skip if we have no permanent stored certificates */
00213         if ( ! sizeof ( certstore_raw ) )
00214                 return;
00215 
00216         /* Add certificates */
00217         for ( i = 0 ; i < ( int ) ( sizeof ( certstore_raw ) /
00218                                     sizeof ( certstore_raw[0] ) ) ; i++ ) {
00219 
00220                 /* Skip if certificate already present in store */
00221                 raw = &certstore_raw[i];
00222                 if ( ( cert = certstore_find ( raw ) ) != NULL ) {
00223                         DBGC ( &certstore, "CERTSTORE permanent certificate %d "
00224                                "is a duplicate of %s\n", i, x509_name ( cert ));
00225                         continue;
00226                 }
00227 
00228                 /* Parse certificate */
00229                 cert = &certstore_certs[i];
00230                 ref_init ( &cert->refcnt, ref_no_free );
00231                 if ( ( rc = x509_parse ( cert, raw ) ) != 0 ) {
00232                         DBGC ( &certstore, "CERTSTORE could not parse "
00233                                "permanent certificate %d: %s\n",
00234                                i, strerror ( rc ) );
00235                         continue;
00236                 }
00237 
00238                 /* Add certificate to store.  Certificate will never
00239                  * be discarded from the store, since we retain a
00240                  * permanent reference to it.
00241                  */
00242                 certstore_add ( cert );
00243                 cert->flags |= X509_FL_PERMANENT;
00244                 DBGC ( &certstore, "CERTSTORE permanent certificate %d is %s\n",
00245                        i, x509_name ( cert ) );
00246         }
00247 }
00248 
00249 /** Certificate store initialisation function */
00250 struct init_fn certstore_init_fn __init_fn ( INIT_LATE ) = {
00251         .initialise = certstore_init,
00252 };
00253 
00254 /** Additional certificate setting */
00255 static struct setting cert_setting __setting ( SETTING_CRYPTO, cert ) = {
00256         .name = "cert",
00257         .description = "Certificate",
00258         .tag = DHCP_EB_CERT,
00259         .type = &setting_type_hex,
00260 };
00261 
00262 /**
00263  * Apply certificate store configuration settings
00264  *
00265  * @ret rc              Return status code
00266  */
00267 static int certstore_apply_settings ( void ) {
00268         static struct x509_certificate *cert = NULL;
00269         struct x509_certificate *old_cert;
00270         void *cert_data;
00271         int len;
00272         int rc;
00273 
00274         /* Record any existing additional certificate */
00275         old_cert = cert;
00276         cert = NULL;
00277 
00278         /* Add additional certificate, if any */
00279         if ( ( len = fetch_raw_setting_copy ( NULL, &cert_setting,
00280                                               &cert_data ) ) >= 0 ) {
00281                 if ( ( rc = x509_certificate ( cert_data, len, &cert ) ) == 0 ){
00282                         DBGC ( &certstore, "CERTSTORE added additional "
00283                                "certificate %s\n", x509_name ( cert ) );
00284                 } else {
00285                         DBGC ( &certstore, "CERTSTORE could not parse "
00286                                "additional certificate: %s\n",
00287                                strerror ( rc ) );
00288                         /* Do not fail; leave as an unusable certificate */
00289                 }
00290                 free ( cert_data );
00291         }
00292 
00293         /* Free old additional certificiate.  Do this after reparsing
00294          * the additional certificate; in the common case that the
00295          * certificate has not changed, this will allow the stored
00296          * certificate to be reused.
00297          */
00298         x509_put ( old_cert );
00299 
00300         return 0;
00301 }
00302 
00303 /** Certificate store settings applicator */
00304 struct settings_applicator certstore_applicator __settings_applicator = {
00305         .apply = certstore_apply_settings,
00306 };