From: Andreas Steffen Date: Sun, 21 Aug 2022 09:13:53 +0000 (+0200) Subject: pki: Optimize certificate download for --scep and --est X-Git-Tag: 5.9.8dr1~2^2~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2b53b1055daafb2253a084a2ec4aa39e204fb89a;p=thirdparty%2Fstrongswan.git pki: Optimize certificate download for --scep and --est --- diff --git a/src/pki/commands/est.c b/src/pki/commands/est.c index ceee5b8106..73e3ebc451 100644 --- a/src/pki/commands/est.c +++ b/src/pki/commands/est.c @@ -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"}, diff --git a/src/pki/commands/scep.c b/src/pki/commands/scep.c index 97a6fc285e..500735d17b 100644 --- a/src/pki/commands/scep.c +++ b/src/pki/commands/scep.c @@ -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); diff --git a/src/pki/est/est_tls.c b/src/pki/est/est_tls.c index 2546bb0261..a10ed1416b 100644 --- a/src/pki/est/est_tls.c +++ b/src/pki/est/est_tls.c @@ -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)) diff --git a/src/pki/pki_cert.c b/src/pki/pki_cert.c index d3c49f2c80..202284e43e 100644 --- a/src/pki/pki_cert.c +++ b/src/pki/pki_cert.c @@ -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; } diff --git a/src/pki/pki_cert.h b/src/pki/pki_cert.h index 853436c9ea..69746e6b22 100644 --- a/src/pki/pki_cert.h +++ b/src/pki/pki_cert.h @@ -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_ @}*/