2ba5cfec54e6c45ae293908fb54d3d74793b79e0
[platform/core/security/pubkey-pinning.git] / src / curl / tpkp_curl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /*
17  * @file        tpkp_curl.cpp
18  * @author      Kyungwook Tak (k.tak@samsung.com)
19  * @version     1.0
20  * @brief       Tizen Https Public Key Pinning implementation for libcurl.
21  */
22 #include <string>
23 #include <memory>
24 #include <map>
25 #include <mutex>
26
27 #include <curl/curl.h>
28
29 #include "tpkp_common.h"
30 #include "tpkp_curl.h"
31
32 namespace {
33
34 std::map<pid_t, std::string> s_urlmap;
35 std::mutex s_mutex;
36
37 inline CURLcode err_tpkp_to_curle(tpkp_e err) noexcept
38 {
39         switch (err) {
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:
49         case TPKP_E_INTERNAL:
50         default:                             return CURLE_UNKNOWN_OPTION;
51         }
52 }
53
54 TPKP::RawBuffer getPubkeyHash(X509 *cert, TPKP::HashAlgo algo)
55 {
56         std::unique_ptr<EVP_PKEY, void(*)(EVP_PKEY *)>
57                 pubkeyPtr(X509_get_pubkey(cert), EVP_PKEY_free);
58
59         TPKP_CHECK_THROW_EXCEPTION(pubkeyPtr,
60                 TPKP_E_INVALID_CERT, "Failed to get pubkey from cert.");
61
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.");
66
67         TPKP::RawBuffer pubkeyder(der, der + len);
68         free(der);
69         unsigned char *hashResult = nullptr;
70         TPKP::RawBuffer out;
71         switch (algo) {
72         case TPKP::HashAlgo::SHA1:
73                 out.resize(SHA_DIGEST_LENGTH);
74                 hashResult = SHA1(pubkeyder.data(), pubkeyder.size(), out.data());
75
76                 break;
77
78         default:
79                 TPKP_CHECK_THROW_EXCEPTION(false,
80                         TPKP_E_INTERNAL, "Invalid hash algo type in get_pubkey_hash");
81         }
82
83         TPKP_CHECK_THROW_EXCEPTION(hashResult,
84                 TPKP_E_FAILED_GET_PUBKEY_HASH, "Failed to get pubkey haso by openssl SHA1.");
85
86         return out;
87 }
88
89 } // anonymous namespace
90
91
92 EXPORT_API
93 int tpkp_curl_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
94 {
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");
98
99                 auto tid = TPKP::getThreadId();
100                 std::string url;
101
102                 {
103                         std::lock_guard<std::mutex> lock(s_mutex);
104                         url = s_urlmap[tid];
105                 }
106
107                 TPKP_CHECK_THROW_EXCEPTION(!url.empty(),
108                         TPKP_E_NO_URL_DATA, "No url for thread id[" << tid << "] in map");
109
110                 SLOGD("get url[%s] of thread id[%u]", url.c_str(), tid);
111
112                 TPKP::Context ctx(url);
113                 if (!ctx.hasPins()) {
114                         SLOGI("Skip. No static pin data for url: %s", url.c_str());
115                         return;
116                 }
117
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.");
123
124                 for (int i = 0; i < num; i++)
125                         ctx.addPubkeyHash(
126                                 TPKP::HashAlgo::SHA1,
127                                 getPubkeyHash(sk_X509_value(chain, i), TPKP::HashAlgo::SHA1));
128
129                 TPKP_CHECK_THROW_EXCEPTION(ctx.checkPubkeyPins(),
130                         TPKP_E_PUBKEY_MISMATCH, "The pubkey mismatched with pinned data!");
131         });
132
133         return (res == TPKP_E_NONE) ? 1 : 0;
134 }
135
136 EXPORT_API
137 tpkp_e tpkp_curl_set_url_data(CURL *curl)
138 {
139         return TPKP::ExceptionSafe([&]{
140                 char *url = nullptr;
141                 curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
142
143                 auto tid = TPKP::getThreadId();
144
145                 {
146                         std::lock_guard<std::mutex> lock(s_mutex);
147                         s_urlmap[tid] = url;
148                 }
149
150                 SLOGD("set url[%s] of thread id[%u]", url, tid);
151         });
152 }
153
154 EXPORT_API
155 tpkp_e tpkp_curl_set_verify(CURL *curl, SSL_CTX *ssl_ctx)
156 {
157         SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, tpkp_curl_verify_callback);
158         return tpkp_curl_set_url_data(curl);
159 }
160
161 EXPORT_API
162 CURLcode tpkp_curl_ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *)
163 {
164         return err_tpkp_to_curle(tpkp_curl_set_verify(curl, (SSL_CTX *)ssl_ctx));
165 }
166
167 EXPORT_API
168 void tpkp_curl_cleanup(void)
169 {
170         tpkp_e res = TPKP::ExceptionSafe([&]{
171                 auto tid = TPKP::getThreadId();
172
173                 {
174                         std::lock_guard<std::mutex> lock(s_mutex);
175                         s_urlmap.erase(tid);
176                 }
177
178                 SLOGD("cleanup url data for thread id[%u]", tid);
179         });
180
181         (void) res;
182 }
183
184 EXPORT_API
185 void tpkp_curl_cleanup_all(void)
186 {
187         s_urlmap.clear();
188 }