1 #include "memcached.h"
2 
3 #ifdef TLS
4 
5 #include "tls.h"
6 #include <string.h>
7 #include <sysexits.h>
8 #include <sys/param.h>
9 #include <openssl/ssl.h>
10 #include <openssl/err.h>
11 
12 /* constant session ID context for application-level SSL session scoping.
13  * used in server-side SSL session caching, when enabled. */
14 #define SESSION_ID_CONTEXT "memcached"
15 
16 #ifndef MAXPATHLEN
17 #define MAXPATHLEN 4096
18 #endif
19 
20 static ssize_t ssl_read(conn *c, void *buf, size_t count);
21 static ssize_t ssl_sendmsg(conn *c, struct msghdr *msg, int flags);
22 static ssize_t ssl_write(conn *c, void *buf, size_t count);
23 static void print_ssl_error(char *buff, size_t len);
24 static void ssl_callback(const SSL *s, int where, int ret);
25 static int ssl_new_session_callback(SSL *s, SSL_SESSION *sess);
26 
27 static pthread_mutex_t ssl_ctx_lock = PTHREAD_MUTEX_INITIALIZER;
28 
29 const unsigned ERROR_MSG_SIZE = 64;
30 const size_t SSL_ERROR_MSG_SIZE = 256;
31 
SSL_LOCK(void)32 static void SSL_LOCK(void) {
33     pthread_mutex_lock(&(ssl_ctx_lock));
34 }
35 
SSL_UNLOCK(void)36 static void SSL_UNLOCK(void) {
37     pthread_mutex_unlock(&(ssl_ctx_lock));
38 }
39 
ssl_accept(conn * c,int sfd,bool * fail)40 void *ssl_accept(conn *c, int sfd, bool *fail) {
41     SSL *ssl = NULL;
42     if (c->ssl_enabled) {
43         assert(IS_TCP(c->transport) && settings.ssl_enabled);
44 
45         if (settings.ssl_ctx == NULL) {
46             if (settings.verbose) {
47                 fprintf(stderr, "SSL context is not initialized\n");
48             }
49             *fail = true;
50             return NULL;
51         }
52         SSL_LOCK();
53         ssl = SSL_new(settings.ssl_ctx);
54         SSL_UNLOCK();
55         if (ssl == NULL) {
56             if (settings.verbose) {
57                 fprintf(stderr, "Failed to created the SSL object\n");
58             }
59             *fail = true;
60             ERR_clear_error();
61             return NULL;
62         }
63         SSL_set_fd(ssl, sfd);
64 
65         if (c->ssl_enabled == MC_SSL_ENABLED_NOPEER) {
66             // Don't enforce peer certs for this socket.
67             SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
68         } else if (c->ssl_enabled == MC_SSL_ENABLED_PEER) {
69             // Force peer validation for this socket.
70             SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
71         }
72 
73         ERR_clear_error();
74         int ret = SSL_accept(ssl);
75         if (ret <= 0) {
76             int err = SSL_get_error(ssl, ret);
77             if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
78                 // we're actually fine, let the worker thread continue.
79                 ERR_clear_error();
80             } else {
81                 // TODO: ship full error to log stream? conn events?
82                 // SYSCALL specifically means we need to check errno/strerror.
83                 // Else we need to look at the main error stack.
84                 if (err == SSL_ERROR_SYSCALL) {
85                     LOGGER_LOG(NULL, LOG_CONNEVENTS, LOGGER_CONNECTION_TLSERROR,
86                             NULL, c->sfd, strerror(errno));
87                 } else {
88                     char ssl_err[SSL_ERROR_MSG_SIZE];
89                     // OpenSSL internal error. One or more, but lets only care about
90                     // the top error for now.
91                     print_ssl_error(ssl_err, SSL_ERROR_MSG_SIZE);
92                     LOGGER_LOG(NULL, LOG_CONNEVENTS, LOGGER_CONNECTION_TLSERROR,
93                             NULL, c->sfd, ssl_err);
94                 }
95                 ERR_clear_error();
96                 SSL_free(ssl);
97                 STATS_LOCK();
98                 stats.ssl_handshake_errors++;
99                 STATS_UNLOCK();
100                 *fail = true;
101                 return NULL;
102             }
103         }
104     }
105 
106     return ssl;
107 }
108 
109 /*
110  * Note on setting errno in the follow functions:
111  * We either have to refactor callers of read/write/sendmsg to take an error
112  * flag to find out if we're in an EAGAIN state, or we ensure the errno is set
113  * properly before returning from our TLS call. We do this because it's
114  * _possible_ for OpenSSL to do something weird and land with an errno that
115  * doesn't match the WANT_READ|WRITE state.
116  *
117  * Also: we _might_ have to communicate from these calls if we need to wait on
118  * reads or write. Since I haven't yet proved that's even possible I'll save
119  * that for a future refactor.
120  */
121 
122 // TODO: add int offset, and find the nth NID here.
123 // or different function that accepts a string, then does etc?
124 // Caller _must immediately_ use the string and not store the pointer.
ssl_get_peer_cn(conn * c,int * len)125 const unsigned char *ssl_get_peer_cn(conn *c, int *len) {
126     if (!c->ssl) {
127         return NULL;
128     }
129 
130     // can't use get0 to avoid getting a reference since that requires 3.0.0+
131     X509 *cert = SSL_get_peer_certificate(c->ssl);
132     if (cert == NULL) {
133         return NULL;
134     }
135     X509_NAME *name = X509_get_subject_name(cert);
136     if (name == NULL) {
137         X509_free(cert);
138         return NULL;
139     }
140 
141     int r = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
142     if (r == -1) {
143         X509_free(cert);
144         return NULL;
145     }
146     ASN1_STRING *asn1 = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, r));
147 
148     if (asn1 == NULL) {
149         X509_free(cert);
150         return NULL;
151     }
152     *len = ASN1_STRING_length(asn1);
153     X509_free(cert);
154     return ASN1_STRING_get0_data(asn1);
155 }
156 
157 /*
158  * Reads decrypted data from the underlying BIO read buffers,
159  * which reads from the socket.
160  */
ssl_read(conn * c,void * buf,size_t count)161 static ssize_t ssl_read(conn *c, void *buf, size_t count) {
162     assert (c != NULL);
163     /* TODO : document the state machine interactions for SSL_read with
164         non-blocking sockets/ SSL re-negotiations
165     */
166 
167     ssize_t ret = SSL_read(c->ssl, buf, count);
168     if (ret <= 0) {
169         int err = SSL_get_error(c->ssl, ret);
170         if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
171             errno = EAGAIN;
172         } else if (err == SSL_ERROR_ZERO_RETURN) {
173             // TLS session is closed... let the caller move this along.
174             return 0;
175         } else if (err == SSL_ERROR_SYSCALL) {
176             // need to rely on errno to find out what happened
177             LOGGER_LOG(c->thread->l, LOG_CONNEVENTS, LOGGER_CONNECTION_TLSERROR,
178                     NULL, c->sfd, strerror(errno));
179         } else if (ret != 0) {
180             char ssl_err[SSL_ERROR_MSG_SIZE];
181             // OpenSSL internal error. One or more, but lets only care about
182             // the top error for now.
183             print_ssl_error(ssl_err, SSL_ERROR_MSG_SIZE);
184             LOGGER_LOG(c->thread->l, LOG_CONNEVENTS, LOGGER_CONNECTION_TLSERROR,
185                     NULL, c->sfd, ssl_err);
186             STATS_LOCK();
187             stats.ssl_proto_errors++;
188             STATS_UNLOCK();
189         }
190         ERR_clear_error();
191     }
192 
193     return ret;
194 }
195 
196 /*
197  * Writes data to the underlying BIO write buffers,
198  * which encrypt and write them to the socket.
199  */
ssl_write(conn * c,void * buf,size_t count)200 static ssize_t ssl_write(conn *c, void *buf, size_t count) {
201     assert (c != NULL);
202 
203     ssize_t ret = SSL_write(c->ssl, buf, count);
204     if (ret <= 0) {
205         int err = SSL_get_error(c->ssl, ret);
206         if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
207             errno = EAGAIN;
208         } else if (err == SSL_ERROR_ZERO_RETURN) {
209             // TLS session is closed... let the caller move this along.
210             return 0;
211         } else if (err == SSL_ERROR_SYSCALL) {
212             // need to rely on errno to find out what happened
213             LOGGER_LOG(c->thread->l, LOG_CONNEVENTS, LOGGER_CONNECTION_TLSERROR,
214                     NULL, c->sfd, strerror(errno));
215         } else if (ret != 0) {
216             char ssl_err[SSL_ERROR_MSG_SIZE];
217             // OpenSSL internal error. One or more, but lets only care about
218             // the top error for now.
219             print_ssl_error(ssl_err, SSL_ERROR_MSG_SIZE);
220             LOGGER_LOG(c->thread->l, LOG_CONNEVENTS, LOGGER_CONNECTION_TLSERROR,
221                     NULL, c->sfd, ssl_err);
222             STATS_LOCK();
223             stats.ssl_proto_errors++;
224             STATS_UNLOCK();
225         }
226         ERR_clear_error();
227     }
228     return ret;
229 }
230 
231 /*
232  * SSL sendmsg implementation. Perform a SSL_write.
233  */
ssl_sendmsg(conn * c,struct msghdr * msg,int flags)234 static ssize_t ssl_sendmsg(conn *c, struct msghdr *msg, int flags) {
235     assert (c != NULL);
236     size_t buf_remain = settings.ssl_wbuf_size;
237     size_t bytes = 0;
238     size_t to_copy;
239     int i;
240 
241     // ssl_wbuf is pointing to the buffer allocated in the worker thread.
242     assert(c->ssl_wbuf);
243     // TODO: allocate a fix buffer in crawler/logger if they start using
244     // the sendmsg method. Also, set c->ssl_wbuf  when the side thread
245     // start owning the connection and reset the pointer in
246     // conn_worker_readd.
247     // Currently this connection would not be served by a different thread
248     // than the one it's assigned.
249     assert(pthread_equal(c->thread->thread_id, pthread_self()) != 0);
250 
251     char *bp = c->ssl_wbuf;
252     for (i = 0; i < msg->msg_iovlen; i++) {
253         size_t len = msg->msg_iov[i].iov_len;
254         to_copy = len < buf_remain ? len : buf_remain;
255 
256         memcpy(bp + bytes, (void*)msg->msg_iov[i].iov_base, to_copy);
257         buf_remain -= to_copy;
258         bytes += to_copy;
259         if (buf_remain == 0)
260             break;
261     }
262     /* TODO : document the state machine interactions for SSL_write with
263         non-blocking sockets/ SSL re-negotiations
264     */
265     return ssl_write(c, c->ssl_wbuf, bytes);
266 }
267 
268 /*
269  * Prints an SSL error into the buff, if there's any.
270  */
print_ssl_error(char * buff,size_t len)271 static void print_ssl_error(char *buff, size_t len) {
272     unsigned long err;
273     if ((err = ERR_get_error()) != 0) {
274         ERR_error_string_n(err, buff, len);
275     }
276 }
277 
278 /*
279  * Loads server certificates to the SSL context and validate them.
280  * @return whether certificates are successfully loaded and verified or not.
281  * @param error_msg contains the error when unsuccessful.
282  */
load_server_certificates(char ** errmsg)283 static bool load_server_certificates(char **errmsg) {
284     bool success = false;
285 
286     const size_t CRLF_NULLCHAR_LEN = 3;
287     char *error_msg = malloc(MAXPATHLEN + ERROR_MSG_SIZE +
288         SSL_ERROR_MSG_SIZE);
289     size_t errmax = MAXPATHLEN + ERROR_MSG_SIZE + SSL_ERROR_MSG_SIZE -
290         CRLF_NULLCHAR_LEN;
291 
292     if (error_msg == NULL) {
293         *errmsg = NULL;
294         return false;
295     }
296 
297     if (settings.ssl_ctx == NULL) {
298         snprintf(error_msg, errmax, "Error TLS not enabled\r\n");
299         *errmsg = error_msg;
300         return false;
301     }
302 
303     char *ssl_err_msg = malloc(SSL_ERROR_MSG_SIZE);
304     if (ssl_err_msg == NULL) {
305         free(error_msg);
306         *errmsg = NULL;
307         return false;
308     }
309     bzero(ssl_err_msg, SSL_ERROR_MSG_SIZE);
310     size_t err_msg_size = 0;
311 
312     SSL_LOCK();
313     if (!SSL_CTX_use_certificate_chain_file(settings.ssl_ctx,
314         settings.ssl_chain_cert)) {
315         print_ssl_error(ssl_err_msg, SSL_ERROR_MSG_SIZE);
316         err_msg_size = snprintf(error_msg, errmax, "Error loading the certificate chain: "
317             "%s : %s", settings.ssl_chain_cert, ssl_err_msg);
318     } else if (!SSL_CTX_use_PrivateKey_file(settings.ssl_ctx, settings.ssl_key,
319                                         settings.ssl_keyformat)) {
320         print_ssl_error(ssl_err_msg, SSL_ERROR_MSG_SIZE);
321         err_msg_size = snprintf(error_msg, errmax, "Error loading the key: %s : %s",
322             settings.ssl_key, ssl_err_msg);
323     } else if (!SSL_CTX_check_private_key(settings.ssl_ctx)) {
324         print_ssl_error(ssl_err_msg, SSL_ERROR_MSG_SIZE);
325         err_msg_size = snprintf(error_msg, errmax, "Error validating the certificate: %s",
326             ssl_err_msg);
327     } else if (settings.ssl_ca_cert) {
328         if (!SSL_CTX_load_verify_locations(settings.ssl_ctx,
329           settings.ssl_ca_cert, NULL)) {
330             print_ssl_error(ssl_err_msg, SSL_ERROR_MSG_SIZE);
331             err_msg_size = snprintf(error_msg, errmax,
332               "Error loading the CA certificate: %s : %s",
333               settings.ssl_ca_cert, ssl_err_msg);
334         } else {
335             SSL_CTX_set_client_CA_list(settings.ssl_ctx,
336               SSL_load_client_CA_file(settings.ssl_ca_cert));
337             success = true;
338         }
339     } else {
340         success = true;
341     }
342     SSL_UNLOCK();
343     free(ssl_err_msg);
344     if (success) {
345         settings.ssl_last_cert_refresh_time = current_time;
346         free(error_msg);
347     } else {
348         *errmsg = error_msg;
349         error_msg += (err_msg_size >= errmax ? errmax - 1: err_msg_size);
350         snprintf(error_msg, CRLF_NULLCHAR_LEN, "\r\n");
351         // Print if there are more errors and drain the queue.
352         ERR_print_errors_fp(stderr);
353     }
354     return success;
355 }
356 
ssl_conn_close(void * ssl_in)357 void ssl_conn_close(void *ssl_in) {
358     SSL *ssl = ssl_in;
359     SSL_shutdown(ssl);
360     SSL_free(ssl);
361 }
362 
ssl_init_conn(conn * c,void * ssl_in)363 void ssl_init_conn(conn *c, void *ssl_in) {
364     if (ssl_in) {
365         SSL *ssl = ssl_in;
366         c->ssl = (SSL*)ssl;
367         c->read = ssl_read;
368         c->sendmsg = ssl_sendmsg;
369         c->write = ssl_write;
370         SSL_set_info_callback(c->ssl, ssl_callback);
371     }
372 }
373 
ssl_init_settings(void)374 void ssl_init_settings(void) {
375     settings.ssl_enabled = false;
376     settings.ssl_ctx = NULL;
377     settings.ssl_chain_cert = NULL;
378     settings.ssl_key = NULL;
379     settings.ssl_verify_mode = SSL_VERIFY_NONE;
380     settings.ssl_keyformat = SSL_FILETYPE_PEM;
381     settings.ssl_ciphers = NULL;
382     settings.ssl_ca_cert = NULL;
383     settings.ssl_last_cert_refresh_time = current_time;
384     settings.ssl_wbuf_size = 16 * 1024; // default is 16KB (SSL max frame size is 17KB)
385     settings.ssl_session_cache = false;
386     settings.ssl_kernel_tls = false;
387     settings.ssl_min_version = TLS1_2_VERSION;
388 }
389 
390 /*
391  * Verify SSL settings and initiates the SSL context.
392  */
ssl_init(void)393 int ssl_init(void) {
394     assert(settings.ssl_enabled);
395 
396     OPENSSL_init_ssl(0, NULL);
397 
398     // SSL context for the process. All connections will share one
399     // process level context.
400     settings.ssl_ctx = SSL_CTX_new(TLS_server_method());
401 
402     SSL_CTX_set_min_proto_version(settings.ssl_ctx, settings.ssl_min_version);
403 
404     // The server certificate, private key and validations.
405     char *error_msg;
406     if (!load_server_certificates(&error_msg)) {
407         fprintf(stderr, "%s", error_msg);
408         free(error_msg);
409         exit(EX_USAGE);
410     }
411 
412     // The verification mode of client certificate, default is SSL_VERIFY_PEER.
413     SSL_CTX_set_verify(settings.ssl_ctx, settings.ssl_verify_mode, NULL);
414     if (settings.ssl_ciphers && !SSL_CTX_set_cipher_list(settings.ssl_ctx,
415                                                     settings.ssl_ciphers)) {
416         fprintf(stderr, "Error setting the provided cipher(s): %s\n",
417                 settings.ssl_ciphers);
418         exit(EX_USAGE);
419     }
420 
421     // Optional session caching; default disabled.
422     if (settings.ssl_session_cache) {
423         SSL_CTX_sess_set_new_cb(settings.ssl_ctx, ssl_new_session_callback);
424         SSL_CTX_set_session_cache_mode(settings.ssl_ctx, SSL_SESS_CACHE_SERVER);
425         SSL_CTX_set_session_id_context(settings.ssl_ctx,
426                                        (const unsigned char *) SESSION_ID_CONTEXT,
427                                        strlen(SESSION_ID_CONTEXT));
428     } else {
429         SSL_CTX_set_session_cache_mode(settings.ssl_ctx, SSL_SESS_CACHE_OFF);
430     }
431 
432     // Optional kernel TLS offload; default disabled.
433     if (settings.ssl_kernel_tls) {
434 #if defined(SSL_OP_ENABLE_KTLS)
435         SSL_CTX_set_options(settings.ssl_ctx, SSL_OP_ENABLE_KTLS);
436 #else
437         fprintf(stderr, "Kernel TLS offload is not available\n");
438         exit(EX_USAGE);
439 #endif
440     }
441 
442 #ifdef SSL_OP_NO_RENEGOTIATION
443     // Disable TLS re-negotiation if SSL_OP_NO_RENEGOTIATION is defined for
444     // openssl 1.1.0h or above
445     SSL_CTX_set_options(settings.ssl_ctx, SSL_OP_NO_RENEGOTIATION);
446 #endif
447 
448     // Release TLS read/write buffers of idle connections
449     SSL_CTX_set_mode(settings.ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
450 
451     return 0;
452 }
453 
454 /*
455  * This method is registered with each SSL connection and abort the SSL session
456  * if a client initiates a renegotiation for openssl versions before 1.1.0h.
457  * For openssl 1.1.0h and above, TLS re-negotiation is disabled by setting the
458  * SSL_OP_NO_RENEGOTIATION option in SSL_CTX_set_options.
459  */
ssl_callback(const SSL * s,int where,int ret)460 void ssl_callback(const SSL *s, int where, int ret) {
461     // useful for debugging.
462     // fprintf(stderr, "WHERE: %d RET: %d CODE: %s LONG: %s\n", where, ret, SSL_state_string(s), SSL_state_string_long(s));
463 #ifndef SSL_OP_NO_RENEGOTIATION
464     SSL* ssl = (SSL*)s;
465     if (SSL_in_before(ssl)) {
466         fprintf(stderr, "%d: SSL renegotiation is not supported, "
467                 "closing the connection\n", SSL_get_fd(ssl));
468         SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
469         return;
470     }
471 #endif
472 }
473 
474 /*
475  * This method is invoked with every new successfully negotiated SSL session,
476  * when server-side session caching is enabled. Note that this method is not
477  * invoked when a session is reused.
478  */
ssl_new_session_callback(SSL * s,SSL_SESSION * sess)479 int ssl_new_session_callback(SSL *s, SSL_SESSION *sess) {
480     STATS_LOCK();
481     stats.ssl_new_sessions++;
482     STATS_UNLOCK();
483 
484     return 0;
485 }
486 
refresh_certs(char ** errmsg)487 bool refresh_certs(char **errmsg) {
488     return load_server_certificates(errmsg);
489 }
490 
ssl_help(void)491 void ssl_help(void) {
492     printf("   - ssl_chain_cert:      certificate chain file in PEM format\n"
493            "   - ssl_key:             private key, if not part of the -ssl_chain_cert\n"
494            "   - ssl_keyformat:       private key format (PEM, DER or ENGINE) (default: PEM)\n");
495     printf("   - ssl_verify_mode:     peer certificate verification mode, default is 0(None).\n"
496            "                          valid values are 0(None), 1(Request), 2(Require)\n"
497            "                          or 3(Once)\n");
498     printf("   - ssl_ciphers:         specify cipher list to be used\n"
499            "   - ssl_ca_cert:         PEM format file of acceptable client CA's\n"
500            "   - ssl_wbuf_size:       size in kilobytes of per-connection SSL output buffer\n"
501            "                          (default: %u)\n", settings.ssl_wbuf_size / (1 << 10));
502     printf("   - ssl_session_cache:   enable server-side SSL session cache, to support session\n"
503            "                          resumption\n"
504            "   - ssl_kernel_tls:      enable kernel TLS offload\n"
505            "   - ssl_min_version:     minimum protocol version to accept (default: %s)\n",
506            ssl_proto_text(settings.ssl_min_version));
507 #if defined(TLS1_3_VERSION)
508     printf("                          valid values are 0(%s), 1(%s), 2(%s), or 3(%s).\n",
509            ssl_proto_text(TLS1_VERSION), ssl_proto_text(TLS1_1_VERSION),
510            ssl_proto_text(TLS1_2_VERSION), ssl_proto_text(TLS1_3_VERSION));
511 #else
512     printf("                          valid values are 0(%s), 1(%s), or 2(%s).\n",
513            ssl_proto_text(TLS1_VERSION), ssl_proto_text(TLS1_1_VERSION),
514            ssl_proto_text(TLS1_2_VERSION));
515 #endif
516     verify_default("ssl_keyformat", settings.ssl_keyformat == SSL_FILETYPE_PEM);
517     verify_default("ssl_verify_mode", settings.ssl_verify_mode == SSL_VERIFY_NONE);
518     verify_default("ssl_min_version", settings.ssl_min_version == TLS1_2_VERSION);
519 }
520 
ssl_proto_text(int version)521 const char *ssl_proto_text(int version) {
522     switch (version) {
523         case TLS1_VERSION:
524             return "tlsv1.0";
525         case TLS1_1_VERSION:
526             return "tlsv1.1";
527         case TLS1_2_VERSION:
528             return "tlsv1.2";
529 #if defined(TLS1_3_VERSION)
530         case TLS1_3_VERSION:
531             return "tlsv1.3";
532 #endif
533         default:
534             return "unknown";
535     }
536 }
537 
538 // TODO: would be nice to pull the entire set of startup option parsing into
539 // here like we do with extstore. To save time I'm only pulling subsection
540 // that require openssl headers to start.
ssl_set_verify_mode(int verify)541 bool ssl_set_verify_mode(int verify) {
542     switch(verify) {
543         case 0:
544             settings.ssl_verify_mode = SSL_VERIFY_NONE;
545             break;
546         case 1:
547             settings.ssl_verify_mode = SSL_VERIFY_PEER;
548             break;
549         case 2:
550             settings.ssl_verify_mode = SSL_VERIFY_PEER |
551                                         SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
552             break;
553         case 3:
554             settings.ssl_verify_mode = SSL_VERIFY_PEER |
555                                         SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
556                                         SSL_VERIFY_CLIENT_ONCE;
557             break;
558         default:
559             return false;
560     }
561     return true;
562 }
563 
ssl_set_min_version(int version)564 bool ssl_set_min_version(int version) {
565     switch (version) {
566         case 0:
567             settings.ssl_min_version = TLS1_VERSION;
568             break;
569         case 1:
570             settings.ssl_min_version = TLS1_1_VERSION;
571             break;
572         case 2:
573             settings.ssl_min_version = TLS1_2_VERSION;
574             break;
575 #if defined(TLS1_3_VERSION)
576         case 3:
577             settings.ssl_min_version = TLS1_3_VERSION;
578             break;
579 #endif
580         default:
581             return false;
582     }
583     return true;
584 }
585 
586 #endif // ifdef TLS
587