]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
pki: Optimize certificate download for --scep and --est
authorAndreas Steffen <andreas.steffen@strongswan.org>
Sun, 21 Aug 2022 09:13:53 +0000 (11:13 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Thu, 25 Aug 2022 05:02:29 +0000 (07:02 +0200)
src/pki/commands/est.c
src/pki/commands/scep.c
src/pki/est/est_tls.c
src/pki/pki_cert.c
src/pki/pki_cert.h

index ceee5b8106eaebae2023f88d10ccee7d474e7b93..73e3ebc4512e50fded80b64c77f3023c920394da 100644 (file)
@@ -35,10 +35,11 @@ static int est()
 {
        char *arg, *url = NULL, *file = NULL, *error = NULL;
        char *client_cert_file = NULL, *client_key_file = NULL;
+       char *user_pass = NULL;
        cred_encoding_type_t form = CERT_ASN1_DER;
        chunk_t pkcs10_encoding = chunk_empty, est_response = chunk_empty;
        certificate_t *pkcs10 = NULL, *client_cert = NULL, *cacert = NULL;
-       mem_cred_t *creds = NULL;
+       mem_cred_t *creds = NULL, *client_creds = NULL;
        private_key_t *client_key = NULL;
        est_op_t est_op = EST_SIMPLE_ENROLL;
        est_tls_t *est_tls;
@@ -55,30 +56,33 @@ static int est()
        {
                switch (command_getopt(&arg))
                {
-                       case 'h':
+                       case 'h':       /* --help */
                                goto usage;
-                       case 'u':
+                       case 'u':       /* --url */
                                url = arg;
                                continue;
-                       case 'i':
+                       case 'i':       /* --in */
                                file = arg;
                                continue;
-                       case 'c':
+                       case 'c':       /* --cacert */
                                cacert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
                                                         CERT_X509,     BUILD_FROM_FILE, arg, BUILD_END);
                                if (!cacert)
                                {
                                        DBG1(DBG_APP, "could not load cacert file '%s'", arg);
-                                       goto end;
+                                       goto err;
                                }
                                creds->add_cert(creds, TRUE, cacert);
                                continue;
-                       case 'o':
+                       case 'o':       /* --cert */
                                client_cert_file = arg;
                                continue;
-                       case 'k':
+                       case 'k':       /* --key */
                                client_key_file = arg;
                                continue;
+                       case 'p':       /* --userpass */
+                               user_pass = arg;
+                               continue;
                        case 't':       /* --pollinterval */
                                poll_interval = atoi(arg);
                                if (poll_interval <= 0)
@@ -90,7 +94,7 @@ static int est()
                        case 'm':       /* --maxpolltime */
                                max_poll_time = atoi(arg);
                                continue;
-                       case 'f':
+                       case 'f':       /* --force */
                                if (!get_form(arg, &form, CRED_CERTIFICATE))
                                {
                                        error = "invalid certificate output format";
@@ -134,7 +138,7 @@ static int est()
                {
                        DBG1(DBG_APP, "reading PKCS#10 certificate request failed: %s\n",
                                                   strerror(errno));
-                       goto end;
+                       goto err;
                }
                pkcs10 = lib->creds->create(lib->creds, CRED_CERTIFICATE,
                                                                          CERT_PKCS10_REQUEST,
@@ -144,15 +148,21 @@ static int est()
        if (!pkcs10)
        {
                DBG1(DBG_APP, "parsing certificate request failed");
-               goto end;
+               goto err;
        }
 
        /* generate PKCS#10 encoding */
        if (!pkcs10->get_encoding(pkcs10, CERT_ASN1_DER, &pkcs10_encoding))
        {
                DBG1(DBG_APP, "encoding certificate request failed");
-               goto end;
+               pkcs10->destroy(pkcs10);
+               goto err;
        }
+       pkcs10->destroy(pkcs10);
+
+       /* create a separate set for the old client credentials */
+       client_creds = mem_cred_create();
+       lib->credmgr->add_set(lib->credmgr, &client_creds->set);
 
        if (client_cert_file)
        {
@@ -164,7 +174,7 @@ static int est()
                        DBG1(DBG_APP, "could not load client cert file '%s'", client_cert_file);
                        goto end;
                }
-               creds->add_cert(creds, FALSE, client_cert->get_ref(client_cert));
+               client_creds->add_cert(client_creds, FALSE, client_cert);
 
                /* load old client private key */
                client_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
@@ -174,11 +184,11 @@ static int est()
                        DBG1(DBG_APP, "parsing client private key failed");
                        goto end;
                }
-               creds->add_key(creds, client_key->get_ref(client_key));
+               client_creds->add_key(client_creds, client_key);
                est_op = EST_SIMPLE_REENROLL;
        }
 
-       est_tls = est_tls_create(url, client_cert, NULL);
+       est_tls = est_tls_create(url, client_cert, user_pass);
        if (!est_tls)
        {
                DBG1(DBG_APP, "TLS connection to EST server was not established");
@@ -187,6 +197,7 @@ static int est()
        if (!est_tls->request(est_tls, est_op, pkcs10_encoding, &est_response,
                                                  &http_code, &retry_after))
        {
+               est_tls->destroy(est_tls);
                DBG1(DBG_APP, "EST request failed: HTTP %u", http_code);
                goto end;
        }
@@ -213,6 +224,9 @@ static int est()
 
        while (http_code == EST_HTTP_CODE_ACCEPTED)
        {
+               chunk_free(&est_response);
+               est_tls->destroy(est_tls);
+
                if (max_poll_time > 0 &&
                   (time_monotonic(NULL) - poll_start) >= max_poll_time)
                {
@@ -221,10 +235,8 @@ static int est()
                }
                DBG1(DBG_APP, "  going to sleep for %d seconds", poll_interval);
                sleep(poll_interval);
-               chunk_free(&est_response);
 
-               est_tls->destroy(est_tls);
-               est_tls = est_tls_create(url, client_cert, NULL);
+               est_tls = est_tls_create(url, client_cert, user_pass);
                if (!est_tls)
                {
                        DBG1(DBG_APP, "TLS connection to EST server was not established");
@@ -234,22 +246,26 @@ static int est()
                                                          &http_code, &retry_after))
                {
                        DBG1(DBG_APP, "EST request failed: HTTP %u", http_code);
+                       est_tls->destroy(est_tls);
                        goto end;
                }
        }
+       est_tls->destroy(est_tls);
+
+end:
+       /* remove the old client certificate before extracting the new one */
+       lib->credmgr->remove_set(lib->credmgr, &client_creds->set);
+       client_creds->destroy(client_creds);
 
        if (http_code == EST_HTTP_CODE_OK)
        {
-               status = pki_cert_extract_cert(est_response, form, creds) ? 0 : 1;
+               status = pki_cert_extract_cert(est_response, form) ? 0 : 1;
        }
 
-end:
+err:
+       /* cleanup */
        lib->credmgr->remove_set(lib->credmgr, &creds->set);
        creds->destroy(creds);
-       DESTROY_IF(est_tls);
-       DESTROY_IF(client_cert);
-       DESTROY_IF(client_key);
-       DESTROY_IF(pkcs10);
        chunk_free(&pkcs10_encoding);
        chunk_free(&est_response);
 
@@ -271,14 +287,16 @@ static void __attribute__ ((constructor))reg()
                est, 'E', "est",
                "Enroll an X.509 certificate with an EST server",
                {"--url url [--in file] [--cacert file]+ [--cert file --key file]",
-                "[--interval time] [--maxpolltime time] [--outform der|pem]"},
+                "[-userpass username:password] [--interval time] [--maxpolltime time]",
+                "[--outform der|pem]"},
                {
                        {"help",        'h', 0, "show usage information"},
                        {"url",         'u', 1, "URL of the EST server"},
                        {"in",          'i', 1, "PKCS#10 input file, default: stdin"},
                        {"cacert",      'c', 1, "CA certificate"},
                        {"cert",        'o', 1, "Old certificate about to be renewed"},
-                       {"key",         'k', 1, "Old RSA private key about to be replaced"},
+                       {"key",         'k', 1, "Old private key about to be replaced"},
+                       {"userpass",    'p', 1, "username:password for http basic auth"},
                        {"interval",    't', 1, "poll interval, default: 60s"},
                        {"maxpolltime", 'm', 1, "maximum poll time, default: 0 (no limit)"},
                        {"outform",     'f', 1, "encoding of stored certificates, default: der"},
index 97a6fc285ec9a3bbf7ffde9a452dcffb040c733f..500735d17b041963736adfea83ac97d1ad8c5a6f 100644 (file)
@@ -61,7 +61,7 @@ static int scep()
        certificate_t *x509_ca_sig = NULL, *x509_ca_enc = NULL;
        identification_t *subject = NULL, *issuer = NULL;
        container_t *container = NULL;
-       mem_cred_t *creds = NULL;
+       mem_cred_t *creds = NULL, *client_creds = NULL;
        scep_msg_t scep_msg_type;
        scep_attributes_t attrs = empty_scep_attributes;
        uint32_t caps_flags;
@@ -127,7 +127,7 @@ static int scep()
                                if (!cert)
                                {
                                        DBG1(DBG_APP, "could not load cacert file '%s'", arg);
-                                       goto end;
+                                       goto err;
                                }
                                creds->add_cert(creds, TRUE, cert);
                                continue;
@@ -236,7 +236,7 @@ static int scep()
        if (subject->get_type(subject) != ID_DER_ASN1_DN)
        {
                DBG1(DBG_APP, "supplied --dn is not a distinguished name");
-               goto end;
+               goto err;
        }
 
        /* load RSA private key from file or stdin */
@@ -253,7 +253,7 @@ static int scep()
                if (!chunk_from_fd(0, &chunk))
                {
                        DBG1(DBG_APP, "reading private key failed: %s", strerror(errno));
-                       goto end;
+                       goto err;
                }
                private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
                                                                         BUILD_BLOB, chunk, BUILD_END);
@@ -262,7 +262,7 @@ static int scep()
        if (!private)
        {
                DBG1(DBG_APP, "parsing private key failed");
-               goto end;
+               goto err;
        }
        public = private->get_public_key(private);
 
@@ -271,7 +271,7 @@ static int scep()
                                                   &scep_response, &http_code))
        {
                DBG1(DBG_APP, "did not receive a valid scep response: HTTP %u", http_code);
-               goto end;
+               goto err;
        }
        caps_flags = scep_parse_caps(scep_response);
        chunk_free(&scep_response);
@@ -302,7 +302,7 @@ static int scep()
        {
                DBG1(DBG_APP, "%N digest algorithm not supported by CA",
                                          hash_algorithm_short_names, digest_alg);
-               goto end;
+               goto err;
        }
 
        /* check support of selected encryption algorithm */
@@ -322,7 +322,7 @@ static int scep()
        {
                DBG1(DBG_APP, "%N encryption algorithm not supported by CA",
                                          encryption_algorithm_names, cipher);
-               goto end;
+               goto err;
        }
        DBG2(DBG_APP, "%N digest and %N encryption algorithm supported by CA",
                                  hash_algorithm_short_names, digest_alg,
@@ -340,7 +340,7 @@ static int scep()
        if (!scheme)
        {
                DBG1(DBG_APP, "no signature scheme found");
-               goto end;
+               goto err;
        }
 
        /* generate PKCS#10 certificate request */
@@ -355,32 +355,46 @@ static int scep()
        if (!pkcs10)
        {
                DBG1(DBG_APP, "generating certificate request failed");
-               goto end;
+               goto err;
        }
 
        /* generate PKCS#10 encoding */
        if (!pkcs10->get_encoding(pkcs10, CERT_ASN1_DER, &pkcs10_encoding))
        {
                DBG1(DBG_APP, "encoding certificate request failed");
-               goto end;
+               pkcs10->destroy(pkcs10);
+               goto err;
        }
+       pkcs10->destroy(pkcs10);
 
        if (!scep_generate_transaction_id(public, &transID, &serialNumber))
        {
                DBG1(DBG_APP, "generating transaction ID failed");
-               goto end;
+               goto err;
        }
        DBG1(DBG_APP, "transaction ID: %.*s", (int)transID.len, transID.ptr);
 
        if (old_cert_file)
        {
+               /* check support of Renewal Operation */
+               if (!(caps_flags & SCEP_CAPS_RENEWAL))
+               {
+                       DBG1(DBG_APP, "Renewal operation not supported by SCEP server");
+                       goto err;
+               }
+               DBG2(DBG_APP, "SCEP Renewal operation supported");
+
+               /* set message type for SCEP renewal request */
+               scep_msg_type = renewal_via_pkcs_req ? SCEP_PKCSReq_MSG :
+                                                                                          SCEP_RenewalReq_MSG;
+
                /* load old client certificate */
                x509_signer  = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
                                                                        BUILD_FROM_FILE, old_cert_file, BUILD_END);
                if (!x509_signer)
                {
                        DBG1(DBG_APP, "could not load old cert file '%s'", old_cert_file);
-                       goto end;
+                       goto err;
                }
 
                /* load old RSA private key */
@@ -389,20 +403,8 @@ static int scep()
                if (!priv_signer)
                {
                        DBG1(DBG_APP, "parsing old private key failed");
-                       goto end;
+                       goto err;
                }
-
-               /* check support of Renewal Operation */
-               if (!(caps_flags & SCEP_CAPS_RENEWAL))
-               {
-                       DBG1(DBG_APP, "Renewal operation not supported by SCEP server");
-                       goto end;
-               }
-               DBG2(DBG_APP, "SCEP Renewal operation supported");
-
-               /* set message type for SCEP renewal request */
-               scep_msg_type = renewal_via_pkcs_req ? SCEP_PKCSReq_MSG :
-                                                                                          SCEP_RenewalReq_MSG;
        }
        else
        {
@@ -419,8 +421,8 @@ static int scep()
                                                                        BUILD_END);
                if (!x509_signer)
                {
-                       DBG1(DBG_APP, "generating self-sigend certificate failed");
-                       goto end;
+                       DBG1(DBG_APP, "generating self-signed certificate failed");
+                       goto err;
                }
 
                /* the signing key is identical to the client key */
@@ -429,8 +431,13 @@ static int scep()
                /* set message type for SCEP request */
                scep_msg_type = SCEP_PKCSReq_MSG;
        }
-       creds->add_cert(creds, FALSE, x509_signer->get_ref(x509_signer));
-       creds->add_key(creds, priv_signer->get_ref(priv_signer));
+
+       /* create a separate set for the self-signed or old client credentials */
+       client_creds = mem_cred_create();
+       lib->credmgr->add_set(lib->credmgr, &client_creds->set);
+
+       client_creds->add_cert(client_creds, FALSE, x509_signer);
+       client_creds->add_key(client_creds, priv_signer);
 
        /* load CA or RA certificate used for encryption */
        x509_ca_enc = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
@@ -566,11 +573,21 @@ static int scep()
        }
        container->destroy(container);
        container = NULL;
-
-       status = pki_cert_extract_cert(data, form, creds) ? 0 : 1;
-       chunk_free(&data);
+       status = 0;
 
 end:
+       /* remove the old client certificate before extracting the new one */
+       lib->credmgr->remove_set(lib->credmgr, &client_creds->set);
+       client_creds->destroy(client_creds);
+
+       if (status == 0)
+       {
+               status = pki_cert_extract_cert(data, form) ? 0 : 1;
+               chunk_free(&data);
+       }
+
+err:
+       /* cleanup */
        lib->credmgr->remove_set(lib->credmgr, &creds->set);
        creds->destroy(creds);
        san->destroy_offset(san, offsetof(identification_t, destroy));
@@ -578,9 +595,6 @@ end:
        DESTROY_IF(subject);
        DESTROY_IF(private);
        DESTROY_IF(public);
-       DESTROY_IF(priv_signer);
-       DESTROY_IF(x509_signer);
-       DESTROY_IF(pkcs10);
        DESTROY_IF(x509_ca_enc);
        DESTROY_IF(x509_ca_sig);
        DESTROY_IF(container);
index 2546bb0261666bb1bec34c9850eda5aed6af2be1..a10ed1416b0feae6b666193b98bdef1e315be13f 100644 (file)
@@ -218,7 +218,7 @@ METHOD(est_tls_t, request, bool,
        {
                return FALSE;
        }
-       DBG1(DBG_APP, "http: %B", &http);
+       DBG2(DBG_APP, "http request: %B", &http);
 
        /* send https request */
        if (this->tls->write(this->tls, http.ptr, http.len) != http.len)
@@ -237,7 +237,7 @@ METHOD(est_tls_t, request, bool,
                return FALSE;
        }
        response = chunk_create(buf, len);
-       DBG1(DBG_APP, "response: %B", &response);
+       DBG2(DBG_APP, "http response: %B", &response);
 
        if (!parse_http_header(&response, http_code, &content_len, &base64,
                                                   retry_after))
index d3c49f2c809c1fe580d7fb0bc8a6f5aa99c47a7c..202284e43ef50669c74ed9e032bfbe788c7b4294 100644 (file)
@@ -348,6 +348,7 @@ bool pki_cert_extract_cacerts(chunk_t data, char *caout, char *raout,
                        cert_type = get_pki_cert_type(cert);
                        if (cert_type != CERT_TYPE_ROOT_CA)
                        {
+                               certificate_t *cert_found = NULL;
                                enumerator_t *certs;
                                bool trusted;
 
@@ -359,7 +360,8 @@ bool pki_cert_extract_cacerts(chunk_t data, char *caout, char *raout,
                                /* establish trust relativ to root CA */
                                certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
                                                                        KEY_ANY, cert->get_subject(cert), FALSE);
-                               trusted = certs->enumerate(certs, &cert, NULL);
+                               trusted = certs->enumerate(certs, &cert_found, NULL) &&
+                                                 (cert_found == cert);
                                certs->destroy(certs);
 
                                cert_type_count[cert_type]++;
@@ -394,12 +396,12 @@ end:
  * Extract an X.509 client certificates from PKCS#7 container
  * check trust as well as validity and write to stdout
  */
-bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form,
-                           mem_cred_t *creds)
+bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form)
 {
        pkcs7_t *pkcs7;
        container_t *container;
        certificate_t *cert;
+       mem_cred_t *client_creds;
        chunk_t cert_encoding = chunk_empty;
        enumerator_t *enumerator;
        bool stored = FALSE;
@@ -413,6 +415,10 @@ bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form,
                return FALSE;
        }
 
+       lib->credmgr->flush_cache(lib->credmgr, CERT_X509);
+       client_creds = mem_cred_create();
+       lib->credmgr->add_set(lib->credmgr, &client_creds->set);
+
        /* store the end entity certificate */
        pkcs7 = (pkcs7_t*)container;
        enumerator = pkcs7->create_cert_enumerator(pkcs7);
@@ -420,13 +426,17 @@ bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form,
        while (enumerator->enumerate(enumerator, &cert))
        {
                x509_t *x509 = (x509_t*)cert;
+               certificate_t *cert_found = NULL;
                enumerator_t *certs;
+               chunk_t serial;
                time_t from, until;
                bool trusted, valid;
 
                if (!(x509->get_flags(x509) & X509_CA))
                {
-                       DBG1(DBG_APP, "certificate \"%Y\"", cert->get_subject(cert));
+                       DBG1(DBG_APP, "issued certificate \"%Y\"", cert->get_subject(cert));
+                       serial = x509->get_serial(x509);
+                       DBG1(DBG_APP, "  serial: %#B", &serial);
 
                        if (stored)
                        {
@@ -434,20 +444,20 @@ bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form,
                                continue;
                        }
 
-                       /* establish trust relativ to root CA */
-                       creds->add_cert(creds, FALSE, cert->get_ref(cert));
+                       /* establish trust relative to root CA */
+                       client_creds->add_cert(client_creds, FALSE, cert->get_ref(cert));
                        certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
                                                                KEY_ANY, cert->get_subject(cert), FALSE);
-                       trusted = certs->enumerate(certs, &cert, NULL);
-                       valid = cert->get_validity(cert, NULL, &from, &until);
+                       trusted = certs->enumerate(certs, &cert_found, NULL) &&
+                                         (cert_found == cert);
+                       certs->destroy(certs);
 
-                       DBG1(DBG_APP, "certificate is %strusted, valid from %T until %T "
-                                                 "(currently %svalid)",
+                       valid = cert->get_validity(cert, NULL, &from, &until);
+                       DBG1(DBG_APP, "issued certificate is %strusted, "
+                                                 "valid from %T until %T (currently %svalid)",
                                                  trusted ? "" : "not ", &from, FALSE, &until, FALSE,
                                                  valid ? "" : "not ");
 
-                       certs->destroy(certs);
-
                        if (!cert->get_encoding(cert, form, &cert_encoding))
                        {
                                DBG1(DBG_APP, "encoding certificate failed");
@@ -467,6 +477,8 @@ bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form,
        }
        enumerator->destroy(enumerator);
        container->destroy(container);
+       lib->credmgr->remove_set(lib->credmgr, &client_creds->set);
+       client_creds->destroy(client_creds);
 
        return stored;
 }
index 853436c9ead9e0a4af2ca72df33d850e5da9a4a6..69746e6b22b317439f1527ee0d7c64e1370cd1b5 100644 (file)
@@ -37,7 +37,6 @@ bool pki_cert_extract_cacerts(chunk_t data, char *caout, char *raout,
  * Extract an X.509 client certificates from PKCS#7 container
  * check trust as well as validity and write to stdout
  */
-bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form,
-                           mem_cred_t *creds);
+bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form);
 
 #endif /** PKI_CERT_H_ @}*/