1 /***********************************************************************************************************************************
2 TLS Client
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #include <string.h>
7 #include <strings.h>
8
9 #include <openssl/x509v3.h>
10
11 #include "common/crypto/common.h"
12 #include "common/debug.h"
13 #include "common/log.h"
14 #include "common/io/client.h"
15 #include "common/io/io.h"
16 #include "common/io/tls/client.h"
17 #include "common/io/tls/session.h"
18 #include "common/memContext.h"
19 #include "common/stat.h"
20 #include "common/type/object.h"
21 #include "common/wait.h"
22
23 /***********************************************************************************************************************************
24 Statistics constants
25 ***********************************************************************************************************************************/
26 STRING_EXTERN(TLS_STAT_CLIENT_STR, TLS_STAT_CLIENT);
27 STRING_EXTERN(TLS_STAT_RETRY_STR, TLS_STAT_RETRY);
28 STRING_EXTERN(TLS_STAT_SESSION_STR, TLS_STAT_SESSION);
29
30 /***********************************************************************************************************************************
31 Object type
32 ***********************************************************************************************************************************/
33 typedef struct TlsClient
34 {
35 MemContext *memContext; // Mem context
36 const String *host; // Host to use for peer verification
37 TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
38 bool verifyPeer; // Should the peer (server) certificate be verified?
39 IoClient *ioClient; // Underlying client (usually a SocketClient)
40
41 SSL_CTX *context; // TLS context
42 } TlsClient;
43
44 /***********************************************************************************************************************************
45 Macros for function logging
46 ***********************************************************************************************************************************/
47 static String *
tlsClientToLog(const THIS_VOID)48 tlsClientToLog(const THIS_VOID)
49 {
50 THIS(const TlsClient);
51
52 return strNewFmt(
53 "{ioClient: %s, timeout: %" PRIu64", verifyPeer: %s}",
54 memContextFreeing(this->memContext) ? NULL_Z : strZ(ioClientToLog(this->ioClient)), this->timeout,
55 cvtBoolToConstZ(this->verifyPeer));
56 }
57
58 #define FUNCTION_LOG_TLS_CLIENT_TYPE \
59 TlsClient *
60 #define FUNCTION_LOG_TLS_CLIENT_FORMAT(value, buffer, bufferSize) \
61 FUNCTION_LOG_STRING_OBJECT_FORMAT(value, tlsClientToLog, buffer, bufferSize)
62
63 /***********************************************************************************************************************************
64 Free connection
65 ***********************************************************************************************************************************/
66 static void
tlsClientFreeResource(THIS_VOID)67 tlsClientFreeResource(THIS_VOID)
68 {
69 THIS(TlsClient);
70
71 FUNCTION_LOG_BEGIN(logLevelTrace);
72 FUNCTION_LOG_PARAM(TLS_CLIENT, this);
73 FUNCTION_LOG_END();
74
75 ASSERT(this != NULL);
76
77 SSL_CTX_free(this->context);
78
79 FUNCTION_LOG_RETURN_VOID();
80 }
81
82 /***********************************************************************************************************************************
83 Convert an ASN1 string used in certificates to a String
84 ***********************************************************************************************************************************/
85 static String *
asn1ToStr(ASN1_STRING * nameAsn1)86 asn1ToStr(ASN1_STRING *nameAsn1)
87 {
88 FUNCTION_TEST_BEGIN();
89 FUNCTION_TEST_PARAM_P(VOID, nameAsn1);
90 FUNCTION_TEST_END();
91
92 // The name should not be null
93 if (nameAsn1 == NULL) // {vm_covered}
94 THROW(CryptoError, "TLS certificate name entry is missing");
95
96 FUNCTION_TEST_RETURN( // {vm_covered}
97 strNewN(
98 #if OPENSSL_VERSION_NUMBER < 0x10100000L
99 (const char *)ASN1_STRING_data(nameAsn1),
100 #else
101 (const char *)ASN1_STRING_get0_data(nameAsn1),
102 #endif
103 (size_t)ASN1_STRING_length(nameAsn1)));
104 }
105
106 /***********************************************************************************************************************************
107 Check if a name from the server certificate matches the hostname
108
109 Matching is always case-insensitive since DNS is case insensitive.
110 ***********************************************************************************************************************************/
111 static bool
tlsClientHostVerifyName(const String * host,const String * name)112 tlsClientHostVerifyName(const String *host, const String *name)
113 {
114 FUNCTION_LOG_BEGIN(logLevelTrace);
115 FUNCTION_LOG_PARAM(STRING, host);
116 FUNCTION_LOG_PARAM(STRING, name);
117 FUNCTION_LOG_END();
118
119 ASSERT(host != NULL);
120 ASSERT(name != NULL);
121
122 // Reject embedded nulls in certificate common or alternative name to prevent attacks like CVE-2009-4034
123 if (strlen(strZ(name)) != strSize(name))
124 THROW(CryptoError, "TLS certificate name contains embedded null");
125
126 bool result = false;
127
128 // Try an exact match
129 if (strcasecmp(strZ(name), strZ(host)) == 0) // {vm_covered}
130 {
131 result = true; // {vm_covered}
132 }
133 // Else check if a wildcard certificate matches the host name
134 //
135 // The rules are:
136 // 1. Only match the '*' character as wildcard
137 // 2. Only match wildcards at the start of the string
138 // 3. The '*' character does *not* match '.', meaning that we match only a single pathname component.
139 // 4. Don't support more than one '*' in a single pattern.
140 //
141 // This is roughly in line with RFC2818, but contrary to what most browsers appear to be implementing (point 3 being the
142 // difference)
143 else if (strZ(name)[0] == '*' && strZ(name)[1] == '.' && strSize(name) > 2 && // {vm_covered}
144 strSize(name) < strSize(host) && // {vm_covered}
145 strcasecmp(strZ(name) + 1, strZ(host) + strSize(host) - strSize(name) + 1) == 0 && // {vm_covered}
146 strChr(host, '.') >= (int)(strSize(host) - strSize(name))) // {vm_covered}
147 {
148 result = true; // {vm_covered}
149 }
150
151 FUNCTION_LOG_RETURN(BOOL, result);
152 }
153
154 /***********************************************************************************************************************************
155 Verify that the server certificate matches the hostname we connected to
156
157 The certificate's Common Name and Subject Alternative Names are considered.
158 ***********************************************************************************************************************************/
159 static bool
tlsClientHostVerify(const String * host,X509 * certificate)160 tlsClientHostVerify(const String *host, X509 *certificate)
161 {
162 FUNCTION_LOG_BEGIN(logLevelTrace);
163 FUNCTION_LOG_PARAM(STRING, host);
164 FUNCTION_LOG_PARAM_P(VOID, certificate);
165 FUNCTION_LOG_END();
166
167 ASSERT(host != NULL);
168
169 bool result = false;
170
171 // Error if the certificate is NULL
172 if (certificate == NULL) // {vm_covered}
173 THROW(CryptoError, "No certificate presented by the TLS server"); // {vm_covered}
174
175 MEM_CONTEXT_TEMP_BEGIN() // {vm_covered}
176 {
177 // First get the subject alternative names from the certificate and compare them against the hostname
178 STACK_OF(GENERAL_NAME) *altNameStack = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i( // {vm_covered}
179 certificate, NID_subject_alt_name, NULL, NULL); // {vm_covered}
180 bool altNameFound = false; // {vm_covered}
181
182 if (altNameStack) // {vm_covered}
183 {
184 for (int altNameIdx = 0; altNameIdx < sk_GENERAL_NAME_num(altNameStack); altNameIdx++) // {vm_covered}
185 {
186 const GENERAL_NAME *name = sk_GENERAL_NAME_value(altNameStack, altNameIdx); // {vm_covered}
187 altNameFound = true; // {vm_covered}
188
189 if (name->type == GEN_DNS) // {vm_covered}
190 result = tlsClientHostVerifyName(host, asn1ToStr(name->d.dNSName)); // {vm_covered}
191
192 if (result != false) // {vm_covered}
193 break; // {vm_covered}
194 }
195
196 sk_GENERAL_NAME_pop_free(altNameStack, GENERAL_NAME_free); // {vm_covered}
197 }
198
199 // If no subject alternative name was found then check the common name. Per RFC 2818 and RFC 6125, if the subjectAltName
200 // extension of type dNSName is present the CN must be ignored.
201 if (!altNameFound) // {vm_covered}
202 {
203 X509_NAME *subjectName = X509_get_subject_name(certificate); // {vm_covered}
204 CHECK(subjectName != NULL); // {vm_covered}
205
206 int commonNameIndex = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1); // {vm_covered}
207 CHECK(commonNameIndex >= 0); // {vm_covered}
208
209 result = tlsClientHostVerifyName( // {vm_covered}
210 host, // {vm_covered}
211 asn1ToStr(X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjectName, commonNameIndex)))); // {vm_covered}
212 }
213 }
214 MEM_CONTEXT_TEMP_END(); // {vm_covered}
215
216 FUNCTION_LOG_RETURN(BOOL, result); // {vm_covered}
217 }
218
219 /***********************************************************************************************************************************
220 Open connection if this is a new client or if the connection was closed by the server
221 ***********************************************************************************************************************************/
222 static IoSession *
tlsClientOpen(THIS_VOID)223 tlsClientOpen(THIS_VOID)
224 {
225 THIS(TlsClient);
226
227 FUNCTION_LOG_BEGIN(logLevelTrace)
228 FUNCTION_LOG_PARAM(TLS_CLIENT, this);
229 FUNCTION_LOG_END();
230
231 ASSERT(this != NULL);
232
233 IoSession *result = NULL;
234 SSL *session = NULL;
235
236 MEM_CONTEXT_TEMP_BEGIN()
237 {
238 bool retry;
239 Wait *wait = waitNew(this->timeout);
240
241 do
242 {
243 // Assume there will be no retry
244 retry = false;
245
246 TRY_BEGIN()
247 {
248 // Open the underlying session first since this is mostly likely to fail
249 IoSession *ioSession = ioClientOpen(this->ioClient);
250
251 // Create internal TLS session. If there is a failure before the TlsSession object is created there may be a leak
252 // of the TLS session but this is likely to result in program termination so it doesn't seem worth coding for.
253 cryptoError((session = SSL_new(this->context)) == NULL, "unable to create TLS session");
254
255 // Set server host name used for validation
256 cryptoError(SSL_set_tlsext_host_name(session, strZ(this->host)) != 1, "unable to set TLS host name");
257
258 // Create the TLS session
259 result = tlsSessionNew(session, ioSession, this->timeout);
260 }
261 CATCH_ANY()
262 {
263 result = NULL;
264
265 // Retry if wait time has not expired
266 if (waitMore(wait))
267 {
268 LOG_DEBUG_FMT("retry %s: %s", errorTypeName(errorType()), errorMessage());
269 retry = true;
270
271 statInc(TLS_STAT_RETRY_STR);
272 }
273 else
274 RETHROW();
275 }
276 TRY_END();
277 }
278 while (retry);
279
280 ioSessionMove(result, memContextPrior());
281 }
282 MEM_CONTEXT_TEMP_END();
283
284 statInc(TLS_STAT_SESSION_STR);
285
286 // Verify that the certificate presented by the server is valid
287 if (this->verifyPeer) // {vm_covered}
288 {
289 // Verify that the chain of trust leads to a valid CA
290 long int verifyResult = SSL_get_verify_result(session); // {vm_covered}
291
292 if (verifyResult != X509_V_OK) // {vm_covered}
293 {
294 THROW_FMT( // {vm_covered}
295 CryptoError, "unable to verify certificate presented by '%s': [%ld] %s", // {vm_covered}
296 strZ(ioClientName(this->ioClient)), verifyResult, X509_verify_cert_error_string(verifyResult)); // {vm_covered}
297 }
298
299 // Verify that the hostname appears in the certificate
300 X509 *certificate = SSL_get_peer_certificate(session); // {vm_covered}
301 bool nameResult = tlsClientHostVerify(this->host, certificate); // {vm_covered}
302 X509_free(certificate); // {vm_covered}
303
304 if (!nameResult) // {vm_covered}
305 {
306 THROW_FMT( // {vm_covered}
307 CryptoError, // {vm_covered}
308 "unable to find hostname '%s' in certificate common name or subject alternative names", // {vm_covered}
309 strZ(this->host)); // {vm_covered}
310 }
311 }
312
313 FUNCTION_LOG_RETURN(IO_SESSION, result);
314 }
315
316 /**********************************************************************************************************************************/
317 static const String *
tlsClientName(THIS_VOID)318 tlsClientName(THIS_VOID)
319 {
320 THIS(TlsClient);
321
322 FUNCTION_TEST_BEGIN();
323 FUNCTION_TEST_PARAM(TLS_CLIENT, this);
324 FUNCTION_TEST_END();
325
326 ASSERT(this != NULL);
327
328 FUNCTION_TEST_RETURN(ioClientName(this->ioClient));
329 }
330
331 /**********************************************************************************************************************************/
332 static const IoClientInterface tlsClientInterface =
333 {
334 .type = IO_CLIENT_TLS_TYPE,
335 .name = tlsClientName,
336 .open = tlsClientOpen,
337 .toLog = tlsClientToLog,
338 };
339
340 IoClient *
tlsClientNew(IoClient * ioClient,const String * host,TimeMSec timeout,bool verifyPeer,const String * caFile,const String * caPath)341 tlsClientNew(IoClient *ioClient, const String *host, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath)
342 {
343 FUNCTION_LOG_BEGIN(logLevelDebug)
344 FUNCTION_LOG_PARAM(IO_CLIENT, ioClient);
345 FUNCTION_LOG_PARAM(STRING, host);
346 FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
347 FUNCTION_LOG_PARAM(BOOL, verifyPeer);
348 FUNCTION_LOG_PARAM(STRING, caFile);
349 FUNCTION_LOG_PARAM(STRING, caPath);
350 FUNCTION_LOG_END();
351
352 ASSERT(ioClient != NULL);
353
354 IoClient *this = NULL;
355
356 MEM_CONTEXT_NEW_BEGIN("TlsClient")
357 {
358 TlsClient *driver = memNew(sizeof(TlsClient));
359
360 *driver = (TlsClient)
361 {
362 .memContext = MEM_CONTEXT_NEW(),
363 .ioClient = ioClientMove(ioClient, MEM_CONTEXT_NEW()),
364 .host = strDup(host),
365 .timeout = timeout,
366 .verifyPeer = verifyPeer,
367 };
368
369 // Setup TLS context
370 // -------------------------------------------------------------------------------------------------------------------------
371 cryptoInit();
372
373 // Select the TLS method to use. To maintain compatibility with older versions of OpenSSL we need to use an SSL method,
374 // but SSL versions will be excluded in SSL_CTX_set_options().
375 const SSL_METHOD *method = SSLv23_method();
376 cryptoError(method == NULL, "unable to load TLS method");
377
378 // Create the TLS context
379 driver->context = SSL_CTX_new(method);
380 cryptoError(driver->context == NULL, "unable to create TLS context");
381
382 memContextCallbackSet(driver->memContext, tlsClientFreeResource, driver);
383
384 // Exclude SSL versions to only allow TLS and also disable compression
385 SSL_CTX_set_options(driver->context, (long)(SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION));
386
387 // Disable auto-retry to prevent SSL_read() from hanging
388 SSL_CTX_clear_mode(driver->context, SSL_MODE_AUTO_RETRY);
389
390 // Set location of CA certificates if the server certificate will be verified
391 // -------------------------------------------------------------------------------------------------------------------------
392 if (driver->verifyPeer)
393 {
394 // If the user specified a location
395 if (caFile != NULL || caPath != NULL) // {vm_covered}
396 {
397 cryptoError( // {vm_covered}
398 SSL_CTX_load_verify_locations(driver->context, strZNull(caFile), strZNull(caPath)) != 1, // {vm_covered}
399 "unable to set user-defined CA certificate location"); // {vm_covered}
400 }
401 // Else use the defaults
402 else
403 {
404 cryptoError(
405 SSL_CTX_set_default_verify_paths(driver->context) != 1, "unable to set default CA certificate location");
406 }
407 }
408
409 statInc(TLS_STAT_CLIENT_STR);
410
411 // Create client interface
412 this = ioClientNew(driver, &tlsClientInterface);
413 }
414 MEM_CONTEXT_NEW_END();
415
416 FUNCTION_LOG_RETURN(IO_CLIENT, this);
417 }
418