From e46d0bc8fcdcd392eeddaa80da583d0432b7a4b8 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 2 Mar 2018 16:55:32 +0100 Subject: [PATCH] src: refactor GetPeerCertificate PR-URL: https://github.com/nodejs/node/pull/19087 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Daniel Bevenius --- src/node_crypto.cc | 172 ++++++++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 72 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index a21e37408de68a..bc9b925dbe226f 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1988,7 +1988,89 @@ static Local X509ToObject(Environment* env, X509* cert) { } -// TODO(indutny): Split it into multiple smaller functions +static Local AddIssuerChainToObject(X509** cert, + Local object, + STACK_OF(X509)* const peer_certs, + Environment* const env) { + Local context = env->isolate()->GetCurrentContext(); + *cert = sk_X509_delete(peer_certs, 0); + for (;;) { + int i; + for (i = 0; i < sk_X509_num(peer_certs); i++) { + X509* ca = sk_X509_value(peer_certs, i); + if (X509_check_issued(ca, *cert) != X509_V_OK) + continue; + + Local ca_info = X509ToObject(env, ca); + object->Set(context, env->issuercert_string(), ca_info).FromJust(); + object = ca_info; + + // NOTE: Intentionally freeing cert that is not used anymore. + X509_free(*cert); + + // Delete cert and continue aggregating issuers. + *cert = sk_X509_delete(peer_certs, i); + break; + } + + // Issuer not found, break out of the loop. + if (i == sk_X509_num(peer_certs)) + break; + } + sk_X509_pop_free(peer_certs, X509_free); + return object; +} + + +static bool CloneSSLCerts(X509** cert, + const STACK_OF(X509)* const ssl_certs, + STACK_OF(X509)** peer_certs) { + *peer_certs = sk_X509_new(nullptr); + bool result = true; + if (*cert != nullptr) + sk_X509_push(*peer_certs, *cert); + for (int i = 0; i < sk_X509_num(ssl_certs); i++) { + *cert = X509_dup(sk_X509_value(ssl_certs, i)); + if (*cert == nullptr) { + result = false; + break; + } + if (!sk_X509_push(*peer_certs, *cert)) { + result = false; + break; + } + } + if (!result) { + sk_X509_pop_free(*peer_certs, X509_free); + } + return result; +} + + +static Local GetLastIssuedCert(X509** cert, + const SSL* const ssl, + Local issuer_chain, + Environment* const env) { + Local context = env->isolate()->GetCurrentContext(); + while (X509_check_issued(*cert, *cert) != X509_V_OK) { + X509* ca; + if (SSL_CTX_get_issuer(SSL_get_SSL_CTX(ssl), *cert, &ca) <= 0) + break; + + Local ca_info = X509ToObject(env, ca); + issuer_chain->Set(context, env->issuercert_string(), ca_info).FromJust(); + issuer_chain = ca_info; + + // NOTE: Intentionally freeing cert that is not used anymore. + X509_free(*cert); + + // Delete cert and continue aggregating issuers. + *cert = ca; + } + return issuer_chain; +} + + template void SSLWrap::GetPeerCertificate( const FunctionCallbackInfo& args) { @@ -2000,97 +2082,43 @@ void SSLWrap::GetPeerCertificate( ClearErrorOnReturn clear_error_on_return; Local result; - Local info; + // Used to build the issuer certificate chain. + Local issuer_chain; // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` - // contains the `peer_certificate`, but on server it doesn't + // contains the `peer_certificate`, but on server it doesn't. X509* cert = w->is_server() ? SSL_get_peer_certificate(w->ssl_) : nullptr; STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(w->ssl_); STACK_OF(X509)* peer_certs = nullptr; - if (cert == nullptr && ssl_certs == nullptr) + if (cert == nullptr && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) goto done; - if (cert == nullptr && sk_X509_num(ssl_certs) == 0) - goto done; - - // Short result requested + // Short result requested. if (args.Length() < 1 || !args[0]->IsTrue()) { result = X509ToObject(env, cert == nullptr ? sk_X509_value(ssl_certs, 0) : cert); goto done; } - // Clone `ssl_certs`, because we are going to destruct it - peer_certs = sk_X509_new(nullptr); - if (cert != nullptr) - sk_X509_push(peer_certs, cert); - for (int i = 0; i < sk_X509_num(ssl_certs); i++) { - cert = X509_dup(sk_X509_value(ssl_certs, i)); - if (cert == nullptr) - goto done; - if (!sk_X509_push(peer_certs, cert)) - goto done; - } - - // First and main certificate - cert = sk_X509_value(peer_certs, 0); - result = X509ToObject(env, cert); - info = result; - - // Put issuer inside the object - cert = sk_X509_delete(peer_certs, 0); - while (sk_X509_num(peer_certs) > 0) { - int i; - for (i = 0; i < sk_X509_num(peer_certs); i++) { - X509* ca = sk_X509_value(peer_certs, i); - if (X509_check_issued(ca, cert) != X509_V_OK) - continue; - - Local ca_info = X509ToObject(env, ca); - info->Set(context, env->issuercert_string(), ca_info).FromJust(); - info = ca_info; - - // NOTE: Intentionally freeing cert that is not used anymore - X509_free(cert); - - // Delete cert and continue aggregating issuers - cert = sk_X509_delete(peer_certs, i); - break; - } - - // Issuer not found, break out of the loop - if (i == sk_X509_num(peer_certs)) - break; - } - - // Last certificate should be self-signed - while (X509_check_issued(cert, cert) != X509_V_OK) { - X509* ca; - if (SSL_CTX_get_issuer(SSL_get_SSL_CTX(w->ssl_), cert, &ca) <= 0) - break; - - Local ca_info = X509ToObject(env, ca); - info->Set(context, env->issuercert_string(), ca_info).FromJust(); - info = ca_info; + if (CloneSSLCerts(&cert, ssl_certs, &peer_certs)) { + // First and main certificate. + cert = sk_X509_value(peer_certs, 0); + result = X509ToObject(env, cert); - // NOTE: Intentionally freeing cert that is not used anymore - X509_free(cert); + issuer_chain = AddIssuerChainToObject(&cert, result, peer_certs, env); + issuer_chain = GetLastIssuedCert(&cert, w->ssl_, issuer_chain, env); + // Last certificate should be self-signed. + if (X509_check_issued(cert, cert) == X509_V_OK) + issuer_chain->Set(env->context(), + env->issuercert_string(), + issuer_chain).FromJust(); - // Delete cert and continue aggregating issuers - cert = ca; + CHECK_NE(cert, nullptr); } - // Self-issued certificate - if (X509_check_issued(cert, cert) == X509_V_OK) - info->Set(context, env->issuercert_string(), info).FromJust(); - - CHECK_NE(cert, nullptr); - done: if (cert != nullptr) X509_free(cert); - if (peer_certs != nullptr) - sk_X509_pop_free(peer_certs, X509_free); if (result.IsEmpty()) result = Object::New(env->isolate()); args.GetReturnValue().Set(result);