]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Add x509_truncate() to truncate a certificate chain
authorMichael Brown <mcb30@ipxe.org>
Tue, 13 Feb 2024 16:27:31 +0000 (16:27 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 14 Feb 2024 16:40:05 +0000 (16:40 +0000)
Downloading a cross-signed certificate chain to partially replace
(rather than simply extend) an existing chain will require the ability
to discard all certificates after a specified link in the chain.

Extract the relevant logic from x509_free_chain() and expose it
separately as x509_truncate().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/crypto/x509.c
src/include/ipxe/x509.h
src/tests/x509_test.c

index 1f017eb03f50d6817713cacf0aef4e19c52cb2c7..92318093e0d3a15370d5685872d923fca00f7812 100644 (file)
@@ -1603,19 +1603,12 @@ int x509_check_name ( struct x509_certificate *cert, const char *name ) {
 static void x509_free_chain ( struct refcnt *refcnt ) {
        struct x509_chain *chain =
                container_of ( refcnt, struct x509_chain, refcnt );
-       struct x509_link *link;
-       struct x509_link *tmp;
 
        DBGC2 ( chain, "X509 chain %p freed\n", chain );
 
-       /* Free each link in the chain */
-       list_for_each_entry_safe ( link, tmp, &chain->links, list ) {
-               x509_put ( link->cert );
-               list_del ( &link->list );
-               free ( link );
-       }
-
        /* Free chain */
+       x509_truncate ( chain, NULL );
+       assert ( list_empty ( &chain->links ) );
        free ( chain );
 }
 
@@ -1696,6 +1689,27 @@ int x509_append_raw ( struct x509_chain *chain, const void *data,
        return rc;
 }
 
+/**
+ * Truncate X.509 certificate chain
+ *
+ * @v chain            X.509 certificate chain
+ * @v link             Link after which to truncate chain, or NULL
+ */
+void x509_truncate ( struct x509_chain *chain, struct x509_link *link ) {
+       struct x509_link *tmp;
+
+       /* Truncate entire chain if no link is specified */
+       if ( ! link )
+               link = list_entry ( &chain->links, struct x509_link, list );
+
+       /* Free each link in the chain */
+       list_for_each_entry_safe_continue ( link, tmp, &chain->links, list ) {
+               x509_put ( link->cert );
+               list_del ( &link->list );
+               free ( link );
+       }
+}
+
 /**
  * Identify X.509 certificate by subject
  *
index c703c8f10c723b10baade518eb2ea3f2b28aff69..5cad4597ddf45ca44e8964399269da225746a6e0 100644 (file)
@@ -391,6 +391,7 @@ extern int x509_append ( struct x509_chain *chain,
                         struct x509_certificate *cert );
 extern int x509_append_raw ( struct x509_chain *chain, const void *data,
                             size_t len );
+extern void x509_truncate ( struct x509_chain *chain, struct x509_link *link );
 extern int x509_auto_append ( struct x509_chain *chain,
                              struct x509_chain *certs );
 extern int x509_validate_chain ( struct x509_chain *chain, time_t time,
index b6cba575c41e7172f67ea3a4f1f001951ab10571..bc90320414240245ae1e1914408bef3caabc0b66 100644 (file)
@@ -984,6 +984,7 @@ static void x509_validate_chain_fail_okx ( struct x509_test_chain *chn,
  *
  */
 static void x509_test_exec ( void ) {
+       struct x509_link *link;
 
        /* Parse all certificates */
        x509_certificate_ok ( &root_crt );
@@ -1089,6 +1090,18 @@ static void x509_test_exec ( void ) {
        x509_validate_chain_fail_ok ( &useless_chain, test_ca_expired,
                                      &empty_store, &test_root );
 
+       /* Check chain truncation */
+       link = list_last_entry ( &server_chain.chain->links,
+                                struct x509_link, list );
+       ok ( link->cert == root_crt.cert );
+       link = list_prev_entry ( link, &server_chain.chain->links, list );
+       ok ( link->cert == intermediate_crt.cert );
+       x509_validate_chain_ok ( &server_chain, test_time,
+                                &empty_store, &test_root );
+       x509_truncate ( server_chain.chain, link );
+       x509_validate_chain_fail_ok ( &server_chain, test_time,
+                                     &empty_store, &test_root );
+
        /* Sanity check */
        assert ( list_empty ( &empty_store.links ) );