]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[x509] Make root of trust a reference-counted structure
authorMichael Brown <mcb30@ipxe.org>
Wed, 9 Dec 2020 16:19:03 +0000 (16:19 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 9 Dec 2020 16:45:50 +0000 (16:45 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/crypto/rootcert.c
src/crypto/x509.c
src/include/ipxe/x509.h
src/net/tls.c
src/net/validator.c
src/tests/cms_test.c
src/tests/x509_test.c

index 867ff50e8b64e551376c0c8b2dc8a5e3a9986301..0835ff071ac15eb746e4fe6bea17e6d5703a40f4 100644 (file)
@@ -71,6 +71,7 @@ static struct setting trust_setting __setting ( SETTING_CRYPTO, trust ) = {
 
 /** Root certificates */
 struct x509_root root_certificates = {
+       .refcnt = REF_INIT ( ref_no_free ),
        .digest = &sha256_algorithm,
        .count = ( sizeof ( fingerprints ) / FINGERPRINT_LEN ),
        .fingerprints = fingerprints,
index fe514e269afa02229c4e840b7315ce5022c68d4e..892d8f8d5713527c0f77aab02d10af7f15594b3f 100644 (file)
@@ -122,6 +122,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define EINFO_EACCES_USELESS \
        __einfo_uniqify ( EINFO_EACCES, 0x0b, "No usable certificates" )
 
+/**
+ * Free X.509 certificate
+ *
+ * @v refcnt           Reference count
+ */
+static void x509_free ( struct refcnt *refcnt ) {
+       struct x509_certificate *cert =
+               container_of ( refcnt, struct x509_certificate, refcnt );
+
+       x509_root_put ( cert->root );
+       free ( cert );
+}
+
 /**
  * Get X.509 certificate display name
  *
@@ -1075,7 +1088,7 @@ int x509_certificate ( const void *data, size_t len,
        *cert = zalloc ( sizeof ( **cert ) + cursor.len );
        if ( ! *cert )
                return -ENOMEM;
-       ref_init ( &(*cert)->refcnt, NULL );
+       ref_init ( &(*cert)->refcnt, x509_free );
        raw = ( *cert + 1 );
 
        /* Copy raw data */
@@ -1310,6 +1323,35 @@ int x509_is_valid ( struct x509_certificate *cert, struct x509_root *root ) {
        return ( cert->root == root );
 }
 
+/**
+ * Set X.509 certificate as validated
+ *
+ * @v cert             X.509 certificate
+ * @v issuer           Issuing X.509 certificate (or NULL)
+ * @v root             Root certificate list
+ */
+static void x509_set_valid ( struct x509_certificate *cert,
+                            struct x509_certificate *issuer,
+                            struct x509_root *root ) {
+       unsigned int max_path_remaining;
+
+       /* Sanity checks */
+       assert ( root != NULL );
+       assert ( ( issuer == NULL ) || ( issuer->path_remaining >= 1 ) );
+
+       /* Record validation root */
+       x509_root_put ( cert->root );
+       cert->root = x509_root_get ( root );
+
+       /* Calculate effective path length */
+       cert->path_remaining = ( cert->extensions.basic.path_len + 1 );
+       if ( issuer ) {
+               max_path_remaining = ( issuer->path_remaining - 1 );
+               if ( cert->path_remaining > max_path_remaining )
+                       cert->path_remaining = max_path_remaining;
+       }
+}
+
 /**
  * Validate X.509 certificate
  *
@@ -1328,7 +1370,6 @@ int x509_is_valid ( struct x509_certificate *cert, struct x509_root *root ) {
 int x509_validate ( struct x509_certificate *cert,
                    struct x509_certificate *issuer,
                    time_t time, struct x509_root *root ) {
-       unsigned int max_path_remaining;
        int rc;
 
        /* Use default root certificate store if none specified */
@@ -1345,8 +1386,7 @@ int x509_validate ( struct x509_certificate *cert,
 
        /* Succeed if certificate is a trusted root certificate */
        if ( x509_check_root ( cert, root ) == 0 ) {
-               cert->root = root;
-               cert->path_remaining = ( cert->extensions.basic.path_len + 1 );
+               x509_set_valid ( cert, NULL, root );
                return 0;
        }
 
@@ -1384,14 +1424,8 @@ int x509_validate ( struct x509_certificate *cert,
                return -EACCES_OCSP_REQUIRED;
        }
 
-       /* Calculate effective path length */
-       cert->path_remaining = ( issuer->path_remaining - 1 );
-       max_path_remaining = ( cert->extensions.basic.path_len + 1 );
-       if ( cert->path_remaining > max_path_remaining )
-               cert->path_remaining = max_path_remaining;
-
        /* Mark certificate as valid */
-       cert->root = root;
+       x509_set_valid ( cert, issuer, root );
 
        DBGC ( cert, "X509 %p \"%s\" successfully validated using ",
               cert, x509_name ( cert ) );
index cac2f19f08bb4d5e013ae64bb5fe817cc496fd6b..c703c8f10c723b10baade518eb2ea3f2b28aff69 100644 (file)
@@ -340,8 +340,10 @@ struct x509_access_method {
                          const struct asn1_cursor *raw );
 };
 
-/** An X.509 root certificate store */
+/** An X.509 root certificate list */
 struct x509_root {
+       /** Reference count */
+       struct refcnt refcnt;
        /** Fingerprint digest algorithm */
        struct digest_algorithm *digest;
        /** Number of certificates */
@@ -350,6 +352,28 @@ struct x509_root {
        const void *fingerprints;
 };
 
+/**
+ * Get reference to X.509 root certificate list
+ *
+ * @v root             X.509 root certificate list
+ * @ret root           X.509 root certificate list
+ */
+static inline __attribute__ (( always_inline )) struct x509_root *
+x509_root_get ( struct x509_root *root ) {
+       ref_get ( &root->refcnt );
+       return root;
+}
+
+/**
+ * Drop reference to X.509 root certificate list
+ *
+ * @v root             X.509 root certificate list
+ */
+static inline __attribute__ (( always_inline )) void
+x509_root_put ( struct x509_root *root ) {
+       ref_put ( &root->refcnt );
+}
+
 extern const char * x509_name ( struct x509_certificate *cert );
 extern int x509_parse ( struct x509_certificate *cert,
                        const struct asn1_cursor *raw );
@@ -391,6 +415,7 @@ extern int x509_check_time ( struct x509_certificate *cert, time_t time );
  * @v cert             X.509 certificate
  */
 static inline void x509_invalidate ( struct x509_certificate *cert ) {
+       x509_root_put ( cert->root );
        cert->root = NULL;
        cert->path_remaining = 0;
 }
index c04f0d55740bd8bafca279ac69f9e4b26782f4da..f5459a2af5ef435b74900e6a00ed0a4824c32455 100644 (file)
@@ -380,6 +380,7 @@ static void free_tls ( struct refcnt *refcnt ) {
        }
        x509_chain_put ( tls->certs );
        x509_chain_put ( tls->chain );
+       x509_root_put ( tls->root );
 
        /* Drop reference to session */
        assert ( list_empty ( &tls->list ) );
@@ -3163,7 +3164,7 @@ int add_tls ( struct interface *xfer, const char *name,
        intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt );
        process_init_stopped ( &tls->process, &tls_process_desc,
                               &tls->refcnt );
-       tls->root = root;
+       tls->root = x509_root_get ( root );
        tls->version = TLS_VERSION_TLS_1_2;
        tls_clear_cipher ( tls, &tls->tx_cipherspec );
        tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
index c407a09b7427eb88f165b3acfe4eee7233b41886..693d4464b36ab2cbe6606648e6ce3637bba9413b 100644 (file)
@@ -116,6 +116,7 @@ static void validator_free ( struct refcnt *refcnt ) {
 
        DBGC2 ( validator, "VALIDATOR %p \"%s\" freed\n",
                validator, validator_name ( validator ) );
+       x509_root_put ( validator->root );
        x509_chain_put ( validator->chain );
        ocsp_put ( validator->ocsp );
        xferbuf_free ( &validator->buffer );
@@ -650,7 +651,7 @@ int create_validator ( struct interface *job, struct x509_chain *chain,
                    &validator->refcnt );
        process_init ( &validator->process, &validator_process_desc,
                       &validator->refcnt );
-       validator->root = root;
+       validator->root = x509_root_get ( root );
        validator->chain = x509_chain_get ( chain );
        xferbuf_malloc_init ( &validator->buffer );
 
index b805a9974f80e8e0bd522eac399b53da428cbf7a..f35fa206d922c3c30111f546fb3076650109249e 100644 (file)
@@ -1317,6 +1317,7 @@ static struct x509_chain empty_store = {
 
 /** Root certificate list containing the iPXE self-test root CA */
 static struct x509_root test_root = {
+       .refcnt = REF_INIT ( ref_no_free ),
        .digest = &cms_test_algorithm,
        .count = 1,
        .fingerprints = root_crt_fingerprint,
@@ -1331,6 +1332,7 @@ static uint8_t dummy_fingerprint[] =
 
 /** Certificate store containing a dummy fingerprint */
 static struct x509_root dummy_root = {
+       .refcnt = REF_INIT ( ref_no_free ),
        .digest = &cms_test_algorithm,
        .count = 1,
        .fingerprints = dummy_fingerprint,
index 2915b90689883f9b39ba5acf5fd6b39ef56a3c4a..256c3e85e8eb270df711c0ea6a068d96811c9f06 100644 (file)
@@ -674,6 +674,7 @@ static struct x509_chain empty_store = {
 
 /** Root certificate list containing the iPXE self-test root CA */
 static struct x509_root test_root = {
+       .refcnt = REF_INIT ( ref_no_free ),
        .digest = &x509_test_algorithm,
        .count = 1,
        .fingerprints = root_crt_fingerprint,
@@ -681,6 +682,7 @@ static struct x509_root test_root = {
 
 /** Root certificate list containing the iPXE self-test intermediate CA */
 static struct x509_root intermediate_root = {
+       .refcnt = REF_INIT ( ref_no_free ),
        .digest = &x509_test_algorithm,
        .count = 1,
        .fingerprints = intermediate_crt_fingerprint,
@@ -695,6 +697,7 @@ static uint8_t dummy_fingerprint[] =
 
 /** Certificate store containing a dummy fingerprint */
 static struct x509_root dummy_root = {
+       .refcnt = REF_INIT ( ref_no_free ),
        .digest = &x509_test_algorithm,
        .count = 1,
        .fingerprints = dummy_fingerprint,