2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @author Kyungwook Tak (k.tak@samsung.com)
20 * @brief Tizen Https Public Key Pinning implementation for libcurl.
27 #include <curl/curl.h>
29 #include "tpkp_common.h"
30 #include "tpkp_curl.h"
34 std::map<pid_t, std::string> s_urlmap;
37 inline CURLcode err_tpkp_to_curle(tpkp_e err) noexcept
40 case TPKP_E_NONE: return CURLE_OK;
41 case TPKP_E_MEMORY: return CURLE_OUT_OF_MEMORY;
42 case TPKP_E_INVALID_URL: return CURLE_URL_MALFORMAT;
43 case TPKP_E_NO_URL_DATA: return CURLE_SSL_CERTPROBLEM;
44 case TPKP_E_PUBKEY_MISMATCH: return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
45 case TPKP_E_INVALID_CERT:
46 case TPKP_E_INVALID_PEER_CERT_CHAIN:
47 case TPKP_E_FAILED_GET_PUBKEY_HASH: return CURLE_PEER_FAILED_VERIFICATION;
48 case TPKP_E_STD_EXCEPTION:
50 default: return CURLE_UNKNOWN_OPTION;
54 TPKP::RawBuffer getPubkeyHash(X509 *cert, TPKP::HashAlgo algo)
56 std::unique_ptr<EVP_PKEY, void(*)(EVP_PKEY *)>
57 pubkeyPtr(X509_get_pubkey(cert), EVP_PKEY_free);
59 TPKP_CHECK_THROW_EXCEPTION(pubkeyPtr,
60 TPKP_E_INVALID_CERT, "Failed to get pubkey from cert.");
62 unsigned char *der = nullptr;
63 auto len = i2d_PUBKEY(pubkeyPtr.get(), &der);
64 TPKP_CHECK_THROW_EXCEPTION(len > 0,
65 TPKP_E_INVALID_CERT, "Failed to convert pem pubkey to der.");
67 TPKP::RawBuffer pubkeyder(der, der + len);
69 unsigned char *hashResult = nullptr;
72 case TPKP::HashAlgo::SHA1:
73 out.resize(SHA_DIGEST_LENGTH);
74 hashResult = SHA1(pubkeyder.data(), pubkeyder.size(), out.data());
79 TPKP_CHECK_THROW_EXCEPTION(false,
80 TPKP_E_INTERNAL, "Invalid hash algo type in get_pubkey_hash");
83 TPKP_CHECK_THROW_EXCEPTION(hashResult,
84 TPKP_E_FAILED_GET_PUBKEY_HASH, "Failed to get pubkey haso by openssl SHA1.");
89 } // anonymous namespace
93 int tpkp_curl_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
95 tpkp_e res = TPKP::ExceptionSafe([&]{
96 TPKP_CHECK_THROW_EXCEPTION(preverify_ok != 0,
97 TPKP_E_INTERNAL, "verify callback already failed before enter tpkp_curl callback");
99 auto tid = TPKP::getThreadId();
103 std::lock_guard<std::mutex> lock(s_mutex);
107 TPKP_CHECK_THROW_EXCEPTION(!url.empty(),
108 TPKP_E_NO_URL_DATA, "No url for thread id[" << tid << "] in map");
110 SLOGD("get url[%s] of thread id[%u]", url.c_str(), tid);
112 TPKP::Context ctx(url);
113 if (!ctx.hasPins()) {
114 SLOGI("Skip. No static pin data for url: %s", url.c_str());
118 auto chain = X509_STORE_CTX_get1_chain(x509_ctx);
119 int num = sk_X509_num(chain);
120 TPKP_CHECK_THROW_EXCEPTION(num != -1,
121 TPKP_E_INVALID_PEER_CERT_CHAIN,
122 "Invalid cert chain from x509_ctx in verify callback.");
124 for (int i = 0; i < num; i++)
126 TPKP::HashAlgo::SHA1,
127 getPubkeyHash(sk_X509_value(chain, i), TPKP::HashAlgo::SHA1));
129 TPKP_CHECK_THROW_EXCEPTION(ctx.checkPubkeyPins(),
130 TPKP_E_PUBKEY_MISMATCH, "The pubkey mismatched with pinned data!");
133 return (res == TPKP_E_NONE) ? 1 : 0;
137 tpkp_e tpkp_curl_set_url_data(CURL *curl)
139 return TPKP::ExceptionSafe([&]{
141 curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
143 auto tid = TPKP::getThreadId();
146 std::lock_guard<std::mutex> lock(s_mutex);
150 SLOGD("set url[%s] of thread id[%u]", url, tid);
155 tpkp_e tpkp_curl_set_verify(CURL *curl, SSL_CTX *ssl_ctx)
157 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, tpkp_curl_verify_callback);
158 return tpkp_curl_set_url_data(curl);
162 CURLcode tpkp_curl_ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *)
164 return err_tpkp_to_curle(tpkp_curl_set_verify(curl, (SSL_CTX *)ssl_ctx));
168 void tpkp_curl_cleanup(void)
170 tpkp_e res = TPKP::ExceptionSafe([&]{
171 auto tid = TPKP::getThreadId();
174 std::lock_guard<std::mutex> lock(s_mutex);
178 SLOGD("cleanup url data for thread id[%u]", tid);
185 void tpkp_curl_cleanup_all(void)