From 96e16e0e27b61dc701f5e4029c8ddfa3cc344ccc Mon Sep 17 00:00:00 2001 From: HuangHaitao Date: Fri, 6 Dec 2024 16:40:03 +0800 Subject: [PATCH] Add only verify root CA configuration Signed-off-by: HuangHaitao --- frameworks/cj/http/BUILD.gn | 1 + .../http/include/net_http_request_context.h | 15 ++ .../cj/http/src/net_http_client_exec.cpp | 171 +++++++++++++++++- .../cj/http/src/net_http_request_context.cpp | 30 +++ frameworks/js/napi/http/BUILD.gn | 5 +- .../async_context/include/request_context.h | 15 ++ .../async_context/src/request_context.cpp | 30 +++ .../napi/http/http_exec/include/http_exec.h | 2 + .../js/napi/http/http_exec/src/http_exec.cpp | 171 +++++++++++++++++- 9 files changed, 430 insertions(+), 10 deletions(-) diff --git a/frameworks/cj/http/BUILD.gn b/frameworks/cj/http/BUILD.gn index dcdd9d292..f8c1648e0 100644 --- a/frameworks/cj/http/BUILD.gn +++ b/frameworks/cj/http/BUILD.gn @@ -32,6 +32,7 @@ config("http_ffi_config") { defines += [ "HTTP_PROXY_ENABLE", "HTTP_MULTIPATH_CERT_ENABLE", + "HTTP_ONLY_VERIFY_ROOT_CA_ENABLE" ] cflags_cc += [ "-fstack-protector-strong", diff --git a/frameworks/cj/http/include/net_http_request_context.h b/frameworks/cj/http/include/net_http_request_context.h index ab6532de1..e62861c23 100644 --- a/frameworks/cj/http/include/net_http_request_context.h +++ b/frameworks/cj/http/include/net_http_request_context.h @@ -134,6 +134,18 @@ public: void SendResponse(); + bool IsRootCaVerified() const; + + void SetRootCaVerified(); + + bool IsRootCaVerifiedOk() const; + + void SetRootCaVerifiedOk(bool ok); + + void SetPinnedPubkey(std::string &pubkey); + + std::string GetPinnedPubkey() const; + std::function respCallback; std::shared_ptr streamingCallback{nullptr}; @@ -158,6 +170,9 @@ private: bool requestOK_ = false; bool permissionDenied_ = false; bool isDestroyed_ = false; + bool isRootCaVerified_ = false; + bool isRootCaVerifiedOk_ = false; + std::string pinnedPubkey_; bool GetRequestBody(CArrUI8 extraData); void HandleMethodForGet(CArrUI8 extraData); diff --git a/frameworks/cj/http/src/net_http_client_exec.cpp b/frameworks/cj/http/src/net_http_client_exec.cpp index 21ab41ac6..514861663 100644 --- a/frameworks/cj/http/src/net_http_client_exec.cpp +++ b/frameworks/cj/http/src/net_http_client_exec.cpp @@ -24,6 +24,14 @@ #ifdef HTTP_MULTIPATH_CERT_ENABLE #include #endif +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE +#ifndef HTTP_MULTIPATH_CERT_ENABLE +#include +#endif +#include +#include +#include +#endif #if HAS_NETMANAGER_BASE #include #endif @@ -75,6 +83,14 @@ static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list"; #endif +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE +static constexpr const int SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX = 1; +static constexpr const unsigned int SHA256_LEN = 32; +static constexpr const int SHA256_BASE64_LEN = 44; // 32-byte base64 -> 44 bytes +static constexpr const int PINNED_PREFIX_LEN = 8; // strlen("sha256//") +#endif + + bool NetHttpClientExec::AddCurlHandle(CURL *handle, RequestContext *context) { if (handle == nullptr || staticVariable_.curlMulti == nullptr) { @@ -538,10 +554,9 @@ bool NetHttpClientExec::SetOtherOption(CURL *curl, OHOS::NetStack::Http::Request return true; } -CURLcode SslCtxFunction(CURL *curl, void *ssl_ctx, void *parm) +CURLcode MultiPathSslCtxFunction(CURL *curl, void *ssl_ctx, const CertsPath *certsPath) { #ifdef HTTP_MULTIPATH_CERT_ENABLE - auto certsPath = static_cast(parm); if (certsPath == nullptr) { NETSTACK_LOGE("certsPath is null"); return CURLE_SSL_CERTPROBLEM; @@ -570,6 +585,147 @@ CURLcode SslCtxFunction(CURL *curl, void *ssl_ctx, void *parm) return CURLE_OK; } +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE + +static bool sha256sum(unsigned char *buf, size_t buflen, unsigned char *out, size_t outlen) +{ + if (out == nullptr || outlen < SHA256_BASE64_LEN) { + NETSTACK_LOGE("output buffer length too short."); + return false; + } + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + unsigned int digestLen = 0; + unsigned char digest[SHA256_LEN]; + if (!mdctx) { + NETSTACK_LOGE("create MD_CTX failed."); + return false; + } + if (!EVP_DigestInit(mdctx, EVP_sha256())) { + NETSTACK_LOGE("EVP_DigestInit failed."); + return false; + } + if (!EVP_DigestUpdate(mdctx, buf, buflen)) { + NETSTACK_LOGE("EVP_DigestUpdate failed."); + return false; + } + if (!EVP_DigestFinal_ex(mdctx, digest, &digestLen)) { + NETSTACK_LOGE("EVP_DigestFinal_ex failed."); + return false; + } + EVP_MD_CTX_free(mdctx); + if (digestLen != SHA256_LEN) { + NETSTACK_LOGE("SHA256 length invalid"); + return false; + } + int base64Len = EVP_EncodeBlock(out, digest, SHA256_LEN); + if (base64Len != SHA256_BASE64_LEN) { + NETSTACK_LOGE("SHA256-Base64 length invalid."); + return false; + } + return true; +} + +static int verify_cert_pubkey(X509 *cert, std::string &pinnedPubkey) +{ + if (pinnedPubkey.empty()) { + // if no pinned pubkey specified, don't pin (Curl default) + return CURLE_OK; + } + if (cert == nullptr) { + NETSTACK_LOGE("no cert specified."); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + unsigned char *certPubkey = NULL; + int pubkeyLen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &certPubkey); + unsigned char certPubKeyDigest[SHA256_BASE64_LEN + 1] = {0}; + if (!sha256sum(certPubkey, pubkeyLen, certPubKeyDigest, SHA256_BASE64_LEN + 1)) { + return CURLE_BAD_FUNCTION_ARGUMENT; + } + NETSTACK_LOGI("pubkey sha256: %{public}s", certPubKeyDigest); + std::string certPubKeyDigestStr(reinterpret_cast(certPubKeyDigest), SHA256_BASE64_LEN + 1); + int begin = 0; + while (begin < pinnedPubkey.size()) { + if (pinnedPubkey.find("sha256//", begin) != 0) { + NETSTACK_LOGE("pinnedPubkey format invalid, should be like sha256//[hash1];sha256//[hash2]"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + std::string candidate = pinnedPubkey.substr(begin + PINNED_PREFIX_LEN, SHA256_BASE64_LEN); + if (candidate.size() != SHA256_BASE64_LEN) { + NETSTACK_LOGE("pinnedPubkey format length invalid."); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if (candidate == certPubKeyDigestStr) { + return CURLE_OK; + } + begin += PINNED_PREFIX_LEN + SHA256_BASE64_LEN; + // check semicolon + if (begin < pinnedPubkey.size() && pinnedPubkey[begin] != ';') { + NETSTACK_LOGE("pinnedPubkey format invalid, should have semicolon separated."); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + // else: begin == pinnedPubkey, end of string, nothing to do. + begin++; + } + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; +} + +static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + X509 *cert = X509_STORE_CTX_get_current_cert(ctx); + int err = X509_STORE_CTX_get_error(ctx); + int depth = X509_STORE_CTX_get_error_depth(ctx); + + SSL *ssl = static_cast(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); + SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl); + RequestContext *requestContext = static_cast(SSL_CTX_get_ex_data(sslctx, + SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX)); + if (requestContext->IsRootCaVerifiedOk()) { + // root CA hash verified, normal procedure. + return preverify_ok; + } + int verifyResult = verify_cert_pubkey(cert, requestContext->GetPinnedPubkey()); + if (!requestContext->IsRootCaVerified()) { + // not verified yet, so this is the root CA verifying. + NETSTACK_LOGD("Verifying Root CA."); + requestContext->SetRootCaVerifiedOk(verifyResult == CURLE_OK); + requestContext->SetRootCaVerified(); + } + if (verifyResult != CURLE_OK && depth == 0) { + // peer site certificate, since root ca verify not ok, and peer site is also not ok + // return failed. + return 0; + } + return preverify_ok; +} +#endif // HTTP_ONLY_VERIFY_ROOT_CA_ENABLE + +CURLcode VerifyRootCaSslCtxFunction(CURL *curl, void *ssl_ctx, void *context) +{ +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE + SSL_CTX *ctx = static_cast(ssl_ctx); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); + SSL_CTX_set_ex_data(ctx, SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX, context); +#endif + return CURLE_OK; +} + +CURLcode SslCtxFunction(CURL *curl, void *ssl_ctx, void *parm) +{ + auto requestContext = static_cast(parm); + if (requestContext == nullptr) { + NETSTACK_LOGE("requestContext is null"); + return CURLE_SSL_CERTPROBLEM; + } + CURLcode result = MultiPathSslCtxFunction(curl, ssl_ctx, &requestContext->GetCertsPath()); + if (result != CURLE_OK) { + return result; + } + if (!requestContext->GetPinnedPubkey().empty()) { + return VerifyRootCaSslCtxFunction(curl, ssl_ctx, requestContext); + } + return CURLE_OK; +} + bool NetHttpClientExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context) { #ifndef NO_SSL_CERTIFICATION @@ -591,8 +747,6 @@ bool NetHttpClientExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http: context->SetCertsPath(std::move(certs), context->options.GetCaPath()); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 1L, context); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 2L, context); - NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION, SslCtxFunction, context); - NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, &context->GetCertsPath(), context); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context); #else NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context); @@ -605,12 +759,17 @@ bool NetHttpClientExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http: #endif // !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) // pin trusted certifcate keys. std::string pins; - auto ret1 = NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins); - if (ret1 != 0 || pins.empty()) { + if (NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins) != 0 || pins.empty()) { NETSTACK_LOGD("Get no pinset by host name"); + } else if (NetManagerStandard::NetConnClient::GetInstance().IsPinOpenModeVerifyRootCa(hostname)) { + context->SetPinnedPubkey(pins); } else { NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str(), context); } +#if defined(HTTP_MULTIPATH_CERT_ENABLE) || defined(HTTP_ONLY_VERIFY_ROOT_CA_ENABLE) + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION, SslCtxFunction, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, context, context); +#endif #else NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context); diff --git a/frameworks/cj/http/src/net_http_request_context.cpp b/frameworks/cj/http/src/net_http_request_context.cpp index fc52aab85..2c62f5fca 100644 --- a/frameworks/cj/http/src/net_http_request_context.cpp +++ b/frameworks/cj/http/src/net_http_request_context.cpp @@ -335,6 +335,36 @@ bool RequestContext::IsDestroyed() const return isDestroyed_; } +bool RequestContext::IsRootCaVerified() const +{ + return isRootCaVerified_; +} + +void RequestContext::SetRootCaVerified() +{ + isRootCaVerified_ = true; +} + +bool RequestContext::IsRootCaVerifiedOk() const +{ + return isRootCaVerifiedOk_; +} + +void RequestContext::SetRootCaVerifiedOk(bool ok) +{ + isRootCaVerifiedOk_ = ok; +} + +void RequestContext::SetPinnedPubkey(std::string &pubkey) +{ + pinnedPubkey_ = pubkey; +} + +std::string RequestContext::GetPinnedPubkey() const +{ + return pinnedPubkey_; +} + void RequestContext::SetCertsPath(std::vector &&certPathList, const std::string &certFile) { certsPath_.certPathList = std::move(certPathList); diff --git a/frameworks/js/napi/http/BUILD.gn b/frameworks/js/napi/http/BUILD.gn index 69b99a836..4dcebbe99 100644 --- a/frameworks/js/napi/http/BUILD.gn +++ b/frameworks/js/napi/http/BUILD.gn @@ -65,7 +65,10 @@ config("http_config") { } if (product_name != "ohos-sdk") { - defines += [ "HTTP_MULTIPATH_CERT_ENABLE" ] + defines += [ + "HTTP_MULTIPATH_CERT_ENABLE", + "HTTP_ONLY_VERIFY_ROOT_CA_ENABLE" + ] } if (netstack_http_boringssl) { diff --git a/frameworks/js/napi/http/async_context/include/request_context.h b/frameworks/js/napi/http/async_context/include/request_context.h index ecdfa21a9..e965b6183 100644 --- a/frameworks/js/napi/http/async_context/include/request_context.h +++ b/frameworks/js/napi/http/async_context/include/request_context.h @@ -139,6 +139,18 @@ public: void SendNetworkProfiler(); RequestTracer::Trace &GetTrace(); + + bool IsRootCaVerified() const; + + void SetRootCaVerified(); + + bool IsRootCaVerifiedOk() const; + + void SetRootCaVerifiedOk(bool ok); + + void SetPinnedPubkey(std::string &pubkey); + + std::string GetPinnedPubkey() const; private: uint32_t magicNumber_ = MAGIC_NUMBER; int32_t taskId_ = -1; @@ -160,6 +172,9 @@ private: curl_slist *curlHostList_ = nullptr; bool isAtomicService_ = false; std::string bundleName_; + bool isRootCaVerified_ = false; + bool isRootCaVerifiedOk_ = false; + std::string pinnedPubkey_; #if HAS_NETMANAGER_BASE std::unique_ptr networkProfilerUtils_; #endif diff --git a/frameworks/js/napi/http/async_context/src/request_context.cpp b/frameworks/js/napi/http/async_context/src/request_context.cpp index d16c3a743..1cd530ee7 100755 --- a/frameworks/js/napi/http/async_context/src/request_context.cpp +++ b/frameworks/js/napi/http/async_context/src/request_context.cpp @@ -890,4 +890,34 @@ RequestTracer::Trace &RequestContext::GetTrace() { return trace_; } + +bool RequestContext::IsRootCaVerified() const +{ + return isRootCaVerified_; +} + +void RequestContext::SetRootCaVerified() +{ + isRootCaVerified_ = true; +} + +bool RequestContext::IsRootCaVerifiedOk() const +{ + return isRootCaVerifiedOk_; +} + +void RequestContext::SetRootCaVerifiedOk(bool ok) +{ + isRootCaVerifiedOk_ = ok; +} + +void RequestContext::SetPinnedPubkey(std::string &pubkey) +{ + pinnedPubkey_ = pubkey; +} + +std::string RequestContext::GetPinnedPubkey() const +{ + return pinnedPubkey_; +} } // namespace OHOS::NetStack::Http diff --git a/frameworks/js/napi/http/http_exec/include/http_exec.h b/frameworks/js/napi/http/http_exec/include/http_exec.h index 41ac49f50..59035eef4 100644 --- a/frameworks/js/napi/http/http_exec/include/http_exec.h +++ b/frameworks/js/napi/http/http_exec/include/http_exec.h @@ -163,6 +163,8 @@ private: #endif static CURLcode SslCtxFunction(void *curl, void *ssl_ctx, void *parm); + static CURLcode MultiPathSslCtxFunction(CURL *curl, void *ssl_ctx, void *request_context); + static CURLcode VerifyRootCaSslCtxFunction(CURL *curl, void *ssl_ctx, void *context); static bool SetDnsCacheOption(CURL *curl, RequestContext *context); diff --git a/frameworks/js/napi/http/http_exec/src/http_exec.cpp b/frameworks/js/napi/http/http_exec/src/http_exec.cpp index da9c3aaa1..7cb9be819 100755 --- a/frameworks/js/napi/http/http_exec/src/http_exec.cpp +++ b/frameworks/js/napi/http/http_exec/src/http_exec.cpp @@ -27,6 +27,14 @@ #ifdef HTTP_MULTIPATH_CERT_ENABLE #include #endif +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE +#ifndef HTTP_MULTIPATH_CERT_ENABLE +#include +#endif +#include +#include +#include +#endif #if HAS_NETMANAGER_BASE #include #endif @@ -103,6 +111,13 @@ static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list"; #endif +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE +static constexpr const int SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX = 1; +static constexpr const unsigned int SHA256_LEN = 32; +static constexpr const int SHA256_BASE64_LEN = 44; // 32-byte base64 -> 44 bytes +static constexpr const int PINNED_PREFIX_LEN = 8; // strlen("sha256//") +#endif + static void RequestContextDeleter(RequestContext *context) { context->DeleteReference(); @@ -936,6 +951,23 @@ bool HttpExec::SetSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext } CURLcode HttpExec::SslCtxFunction(CURL *curl, void *ssl_ctx, void *request_context) +{ + auto requestContext = static_cast(request_context); + if (requestContext == nullptr) { + NETSTACK_LOGE("requestContext is null"); + return CURLE_SSL_CERTPROBLEM; + } + CURLcode result = MultiPathSslCtxFunction(curl, ssl_ctx, requestContext); + if (result != CURLE_OK) { + return result; + } + if (!requestContext->GetPinnedPubkey().empty()) { + return VerifyRootCaSslCtxFunction(curl, ssl_ctx, requestContext); + } + return CURLE_OK; +} + +CURLcode HttpExec::MultiPathSslCtxFunction(CURL *curl, void *ssl_ctx, void *request_context) { #ifdef HTTP_MULTIPATH_CERT_ENABLE auto requestContext = static_cast(request_context); @@ -969,6 +1001,134 @@ CURLcode HttpExec::SslCtxFunction(CURL *curl, void *ssl_ctx, void *request_conte return CURLE_OK; } +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE + +static bool sha256sum(unsigned char *buf, size_t buflen, unsigned char *out, size_t outlen) +{ + if (out == nullptr || outlen < SHA256_BASE64_LEN) { + NETSTACK_LOGE("output buffer length too short."); + return false; + } + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + unsigned int digestLen = 0; + unsigned char digest[SHA256_LEN]; + if (!mdctx) { + NETSTACK_LOGE("create MD_CTX failed."); + return false; + } + if (!EVP_DigestInit(mdctx, EVP_sha256())) { + NETSTACK_LOGE("EVP_DigestInit failed."); + return false; + } + if (!EVP_DigestUpdate(mdctx, buf, buflen)) { + NETSTACK_LOGE("EVP_DigestUpdate failed."); + return false; + } + if (!EVP_DigestFinal_ex(mdctx, digest, &digestLen)) { + NETSTACK_LOGE("EVP_DigestFinal_ex failed."); + return false; + } + EVP_MD_CTX_free(mdctx); + if (digestLen != SHA256_LEN) { + NETSTACK_LOGE("SHA256 length invalid"); + return false; + } + int base64Len = EVP_EncodeBlock(out, digest, SHA256_LEN); + if (base64Len != SHA256_BASE64_LEN) { + NETSTACK_LOGE("SHA256-Base64 length invalid."); + return false; + } + return true; +} + +static int verify_cert_pubkey(X509 *cert, std::string &pinnedPubkey) +{ + if (pinnedPubkey.empty()) { + // if no pinned pubkey specified, don't pin (Curl default) + return CURLE_OK; + } + if (cert == nullptr) { + NETSTACK_LOGE("no cert specified."); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + unsigned char *certPubkey = NULL; + int pubkeyLen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &certPubkey); + unsigned char certPubKeyDigest[SHA256_BASE64_LEN + 1] = {0}; + if (!sha256sum(certPubkey, pubkeyLen, certPubKeyDigest, SHA256_BASE64_LEN + 1)) { + return CURLE_BAD_FUNCTION_ARGUMENT; + } + NETSTACK_LOGI("pubkey sha256: %{public}s", certPubKeyDigest); + std::string certPubKeyDigestStr(reinterpret_cast(certPubKeyDigest), SHA256_BASE64_LEN + 1); + int begin = 0; + while (begin < pinnedPubkey.size()) { + if (pinnedPubkey.find("sha256//", begin) != 0) { + NETSTACK_LOGE("pinnedPubkey format invalid, should be like sha256//[hash1];sha256//[hash2]"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + std::string candidate = pinnedPubkey.substr(begin + PINNED_PREFIX_LEN, SHA256_BASE64_LEN); + if (candidate.size() != SHA256_BASE64_LEN) { + NETSTACK_LOGE("pinnedPubkey format length invalid."); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if (candidate == certPubKeyDigestStr) { + return CURLE_OK; + } + begin += PINNED_PREFIX_LEN + SHA256_BASE64_LEN; + // check semicolon + if (begin < pinnedPubkey.size() && pinnedPubkey[begin] != ';') { + NETSTACK_LOGE("pinnedPubkey format invalid, should have semicolon separated."); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + // else: begin == pinnedPubkey, end of string, nothing to do. + begin++; + } + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; +} + +static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + X509 *cert; + int err, depth; + SSL *ssl; + + cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + + ssl = static_cast(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); + SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl); + RequestContext *requestContext = static_cast(SSL_CTX_get_ex_data(sslctx, + SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX)); + if (requestContext->IsRootCaVerifiedOk()) { + // root CA hash verified, normal procedure. + return preverify_ok; + } + int verifyResult = verify_cert_pubkey(cert, requestContext->pinnedPubkey); + if (!requestContext->IsRootCaVerified()) { + // not verified yet, so this is the root CA verifying. + NETSTACK_LOGD("Verifying Root CA."); + requestContext->SetRootCaVerifiedOk(verifyResult == CURLE_OK); + requestContext->SetRootCaVerified(); + } + if (verifyResult != CURLE_OK && depth == 0) { + // peer site certificate, since root ca verify not ok, and peer site is also not ok + // return failed. + return 0; + } + return preverify_ok; +} +#endif + +CURLcode HttpExec::VerifyRootCaSslCtxFunction(CURL *curl, void *ssl_ctx, void *context) +{ +#ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE + SSL_CTX *ctx = static_cast(ssl_ctx); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); + SSL_CTX_set_ex_data(ctx, SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX, context); +#endif + return CURLE_OK; +} + [[maybe_unused]] void TrustUser0AndUserCa(std::vector &certs) { #ifdef HTTP_MULTIPATH_CERT_ENABLE @@ -1001,8 +1161,6 @@ bool HttpExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestC context->SetCertsPath(std::move(certs), context->options.GetCaPath()); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 1L, context); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 2L, context); - NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION, SslCtxFunction, context); - NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, context, context); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context); #else NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context); @@ -1014,16 +1172,23 @@ bool HttpExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestC NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context); #endif // !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) // pin trusted certifcate keys. - if (!NetManagerStandard::NetConnClient::GetInstance().IsPinOpenMode(hostname)) { + if (!NetManagerStandard::NetConnClient::GetInstance().IsPinOpenMode(hostname) || + NetManagerStandard::NetConnClient::GetInstance().IsPinOpenModeVerifyRootCa(hostname)) { std::string pins; auto ret1 = NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins); if (ret1 != 0 || pins.empty()) { NETSTACK_LOGD("Get no pinset by host name[%{public}s]", hostname.c_str()); + } else if (NetManagerStandard::NetConnClient::GetInstance().IsPinOpenModeVerifyRootCa(hostname)) { + context->SetPinnedPubkey(pins); } else { NETSTACK_LOGD("curl set pin =[%{public}s]", pins.c_str()); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str(), context); } } +#if defined(HTTP_MULTIPATH_CERT_ENABLE) || defined(HTTP_ONLY_VERIFY_ROOT_CA_ENABLE) + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION, SslCtxFunction, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, context, context); +#endif #else NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context); NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context); -- Gitee