]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdistdist/libssl.cc
dnsdist: Add OCSP stapling (from files) for DoT and DoH
[thirdparty/pdns.git] / pdns / dnsdistdist / libssl.cc
CommitLineData
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. */
20static pthread_mutex_t *openssllocks{nullptr};
21
22extern "C" {
23static 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
33static unsigned long openssl_pthreads_id_callback()
34{
35 return (unsigned long)pthread_self();
36}
37}
38
39static 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
50static 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 61static std::atomic<uint64_t> s_users;
4479df79 62#endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) */
ede152ec
RG
63
64void 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
75void 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
93int 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
117static 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
165std::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
201int 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 */