]>
Commit | Line | Data |
---|---|---|
ede152ec RG |
1 | |
2 | #include "config.h" | |
3 | #include "libssl.hh" | |
4 | ||
5 | #ifdef HAVE_LIBSSL | |
6 | ||
7 | #include <atomic> | |
be3183ed RG |
8 | #include <fstream> |
9 | #include <cstring> | |
ede152ec | 10 | #include <pthread.h> |
be3183ed | 11 | |
ede152ec RG |
12 | #include <openssl/conf.h> |
13 | #include <openssl/err.h> | |
be3183ed | 14 | #include <openssl/ocsp.h> |
ede152ec RG |
15 | #include <openssl/rand.h> |
16 | #include <openssl/ssl.h> | |
17 | ||
18 | #if (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) | |
19 | /* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */ | |
20 | static pthread_mutex_t *openssllocks{nullptr}; | |
21 | ||
22 | extern "C" { | |
23 | static void openssl_pthreads_locking_callback(int mode, int type, const char *file, int line) | |
24 | { | |
25 | if (mode & CRYPTO_LOCK) { | |
26 | pthread_mutex_lock(&(openssllocks[type])); | |
27 | ||
28 | } else { | |
29 | pthread_mutex_unlock(&(openssllocks[type])); | |
30 | } | |
31 | } | |
32 | ||
33 | static unsigned long openssl_pthreads_id_callback() | |
34 | { | |
35 | return (unsigned long)pthread_self(); | |
36 | } | |
37 | } | |
38 | ||
39 | static void openssl_thread_setup() | |
40 | { | |
41 | openssllocks = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); | |
42 | ||
43 | for (int i = 0; i < CRYPTO_num_locks(); i++) | |
44 | pthread_mutex_init(&(openssllocks[i]), NULL); | |
45 | ||
46 | CRYPTO_set_id_callback(openssl_pthreads_id_callback); | |
47 | CRYPTO_set_locking_callback(openssl_pthreads_locking_callback); | |
48 | } | |
49 | ||
50 | static void openssl_thread_cleanup() | |
51 | { | |
52 | CRYPTO_set_locking_callback(NULL); | |
53 | ||
54 | for (int i=0; i<CRYPTO_num_locks(); i++) { | |
55 | pthread_mutex_destroy(&(openssllocks[i])); | |
56 | } | |
57 | ||
58 | OPENSSL_free(openssllocks); | |
59 | } | |
60 | ||
ede152ec | 61 | static std::atomic<uint64_t> s_users; |
4479df79 | 62 | #endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) */ |
ede152ec RG |
63 | |
64 | void registerOpenSSLUser() | |
65 | { | |
4479df79 | 66 | #if (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) |
ede152ec RG |
67 | if (s_users.fetch_add(1) == 0) { |
68 | SSL_load_error_strings(); | |
69 | OpenSSL_add_ssl_algorithms(); | |
70 | openssl_thread_setup(); | |
71 | } | |
4479df79 | 72 | #endif |
ede152ec RG |
73 | } |
74 | ||
75 | void unregisterOpenSSLUser() | |
76 | { | |
4479df79 | 77 | #if (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) |
ede152ec RG |
78 | if (s_users.fetch_sub(1) == 1) { |
79 | ERR_free_strings(); | |
80 | ||
81 | EVP_cleanup(); | |
82 | ||
83 | CONF_modules_finish(); | |
84 | CONF_modules_free(); | |
85 | CONF_modules_unload(1); | |
86 | ||
87 | CRYPTO_cleanup_all_ex_data(); | |
88 | openssl_thread_cleanup(); | |
89 | } | |
4479df79 | 90 | #endif |
ede152ec RG |
91 | } |
92 | ||
be3183ed RG |
93 | int libssl_ocsp_stapling_callback(SSL* ssl, const std::map<int, std::string>& ocspMap) |
94 | { | |
95 | auto pkey = SSL_get_privatekey(ssl); | |
96 | if (pkey == nullptr) { | |
97 | return SSL_TLSEXT_ERR_NOACK; | |
98 | } | |
99 | ||
100 | /* look for an OCSP response for the corresponding private key type (RSA, ECDSA..) */ | |
101 | const auto& data = ocspMap.find(EVP_PKEY_base_id(pkey)); | |
102 | if (data == ocspMap.end()) { | |
103 | return SSL_TLSEXT_ERR_NOACK; | |
104 | } | |
105 | ||
106 | /* we need to allocate a copy because OpenSSL will free the pointer passed to SSL_set_tlsext_status_ocsp_resp() */ | |
107 | void* copy = OPENSSL_malloc(data->second.size()); | |
108 | if (copy == nullptr) { | |
109 | return SSL_TLSEXT_ERR_NOACK; | |
110 | } | |
111 | ||
112 | memcpy(copy, data->second.data(), data->second.size()); | |
113 | SSL_set_tlsext_status_ocsp_resp(ssl, copy, data->second.size()); | |
114 | return SSL_TLSEXT_ERR_OK; | |
115 | } | |
116 | ||
117 | static bool libssl_validate_ocsp_response(const std::string& response) | |
118 | { | |
119 | auto responsePtr = reinterpret_cast<const unsigned char *>(response.data()); | |
120 | std::unique_ptr<OCSP_RESPONSE, void(*)(OCSP_RESPONSE*)> resp(d2i_OCSP_RESPONSE(nullptr, &responsePtr, response.size()), OCSP_RESPONSE_free); | |
121 | if (resp == nullptr) { | |
122 | throw std::runtime_error("Unable to parse OCSP response"); | |
123 | } | |
124 | ||
125 | int status = OCSP_response_status(resp.get()); | |
126 | if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { | |
127 | throw std::runtime_error("OCSP response status is not successful: " + std::to_string(status)); | |
128 | } | |
129 | ||
130 | std::unique_ptr<OCSP_BASICRESP, void(*)(OCSP_BASICRESP*)> basic(OCSP_response_get1_basic(resp.get()), OCSP_BASICRESP_free); | |
131 | if (basic == nullptr) { | |
132 | throw std::runtime_error("Error getting a basic OCSP response"); | |
133 | } | |
134 | ||
135 | if (OCSP_resp_count(basic.get()) != 1) { | |
136 | throw std::runtime_error("More than one single response in an OCSP basic response"); | |
137 | } | |
138 | ||
139 | auto singleResponse = OCSP_resp_get0(basic.get(), 0); | |
140 | if (singleResponse == nullptr) { | |
141 | throw std::runtime_error("Error getting a single response from the basic OCSP response"); | |
142 | } | |
143 | ||
144 | int reason; | |
145 | ASN1_GENERALIZEDTIME* revTime = nullptr; | |
146 | ASN1_GENERALIZEDTIME* thisUpdate = nullptr; | |
147 | ASN1_GENERALIZEDTIME* nextUpdate = nullptr; | |
148 | ||
149 | auto singleResponseStatus = OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate); | |
150 | if (singleResponseStatus != V_OCSP_CERTSTATUS_GOOD) { | |
151 | throw std::runtime_error("Invalid status for OCSP single response (" + std::to_string(singleResponseStatus) + ")"); | |
152 | } | |
153 | if (thisUpdate == nullptr || nextUpdate == nullptr) { | |
154 | throw std::runtime_error("Error getting validity of OCSP single response"); | |
155 | } | |
156 | ||
157 | auto validityResult = OCSP_check_validity(thisUpdate, nextUpdate, /* 5 minutes of leeway */ 5 * 60, -1); | |
158 | if (validityResult == 0) { | |
159 | throw std::runtime_error("OCSP single response is not yet, or no longer, valid"); | |
160 | } | |
161 | ||
162 | return true; | |
163 | } | |
164 | ||
165 | std::map<int, std::string> libssl_load_ocsp_responses(const std::vector<std::string>& ocspFiles, std::vector<int> keyTypes) | |
166 | { | |
167 | std::map<int, std::string> ocspResponses; | |
168 | ||
169 | if (ocspFiles.size() > keyTypes.size()) { | |
170 | throw std::runtime_error("More OCSP files than certificates and keys loaded!"); | |
171 | } | |
172 | ||
173 | size_t count = 0; | |
174 | for (const auto& filename : ocspFiles) { | |
175 | std::ifstream file(filename, std::ios::binary); | |
176 | std::string content; | |
177 | while(file) { | |
178 | char buffer[4096]; | |
179 | file.read(buffer, sizeof(buffer)); | |
180 | if (file.bad()) { | |
181 | file.close(); | |
182 | throw std::runtime_error("Unable to load OCSP response from '" + filename + "'"); | |
183 | } | |
184 | content.append(buffer, file.gcount()); | |
185 | } | |
186 | file.close(); | |
187 | ||
188 | try { | |
189 | libssl_validate_ocsp_response(content); | |
190 | ocspResponses.insert({keyTypes.at(count), std::move(content)}); | |
191 | } | |
192 | catch (const std::exception& e) { | |
193 | throw std::runtime_error("Error checking the validity of OCSP response from '" + filename + "': " + e.what()); | |
194 | } | |
195 | ++count; | |
196 | } | |
197 | ||
198 | return ocspResponses; | |
199 | } | |
200 | ||
201 | int libssl_get_last_key_type(std::unique_ptr<SSL_CTX, void(*)(SSL_CTX*)>& ctx) | |
202 | { | |
203 | #if (OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined LIBRESSL_VERSION_NUMBER) | |
204 | auto pkey = SSL_CTX_get0_privatekey(ctx.get()); | |
205 | #else | |
206 | auto temp = std::unique_ptr<SSL, void(*)(SSL*)>(SSL_new(ctx.get()), SSL_free); | |
207 | if (!temp) { | |
208 | return -1; | |
209 | } | |
210 | auto pkey = SSL_get_privatekey(temp.get()); | |
211 | #endif | |
212 | ||
213 | if (!pkey) { | |
214 | return -1; | |
215 | } | |
216 | ||
217 | return EVP_PKEY_base_id(pkey); | |
218 | } | |
219 | ||
ede152ec | 220 | #endif /* HAVE_LIBSSL */ |