NCBI C++ ToolKit
tls.c
Go to the documentation of this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

Go to the SVN repository for this file.

1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Brian Bruns
3  * Copyright (C) 2004-2015 Ziglio Frediano
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 
21 #include <config.h>
22 
23 #include <stdio.h>
24 
25 #if HAVE_ERRNO_H
26 #include <errno.h>
27 #endif /* HAVE_ERRNO_H */
28 
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif /* HAVE_UNISTD_H */
32 
33 #if HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif /* HAVE_STDLIB_H */
36 
37 #if HAVE_STRING_H
38 #include <string.h>
39 #endif /* HAVE_STRING_H */
40 
41 #if HAVE_DIRENT_H
42 #include <dirent.h>
43 #endif /* HAVE_DIRENT_H */
44 
45 #if HAVE_SYS_STAT_H
46 #include <sys/stat.h>
47 #endif /* HAVE_SYS_STAT_H */
48 
49 #ifdef HAVE_SYS_SOCKET_H
50 #include <sys/socket.h>
51 #endif
52 
53 #include <freetds/tds.h>
54 #include <freetds/string.h>
55 #include <freetds/tls.h>
56 #include <freetds/alloca.h>
57 #include "replacements.h"
58 
59 #include <assert.h>
60 
61 /**
62  * \addtogroup network
63  * @{
64  */
65 
66 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
67 
68 #ifdef HAVE_GNUTLS
69 #define SSL_RET ssize_t
70 #define SSL_PULL_ARGS gnutls_transport_ptr_t ptr, void *data, size_t len
71 #define SSL_PUSH_ARGS gnutls_transport_ptr_t ptr, const void *data, size_t len
72 #define SSL_PTR ptr
73 #else
74 
75 /* some compatibility layer */
76 #if OPENSSL_VERSION_NUMBER < 0x1010000FL
77 static inline void
78 BIO_set_init(BIO *b, int init)
79 {
80  b->init = init;
81 }
82 
83 static inline void
84 BIO_set_data(BIO *b, void *ptr)
85 {
86  b->ptr = ptr;
87 }
88 
89 static inline void *
90 BIO_get_data(const BIO *b)
91 {
92  return b->ptr;
93 }
94 #define TLS_client_method SSLv23_client_method
95 #define TLS_ST_OK SSL_ST_OK
96 #endif
97 
98 #define SSL_RET int
99 #define SSL_PULL_ARGS BIO *bio, char *data, int len
100 #define SSL_PUSH_ARGS BIO *bio, const char *data, int len
101 #define SSL_PTR BIO_get_data(bio)
102 #endif
103 
104 static SSL_RET
105 tds_pull_func_login(SSL_PULL_ARGS)
106 {
107  TDSSOCKET *tds = (TDSSOCKET *) SSL_PTR;
108  int have;
109 
110  tdsdump_log(TDS_DBG_FUNC, "in tds_pull_func_login\n");
111 
112  /* here we are initializing (crypted inside TDS packets) */
113 
114  /* if we have some data send it */
115  /* here MARS is not already initialized so test is correct */
116  /* TODO test even after initializing ?? */
117  if (tds->out_pos > 8)
119 
120  for(;;) {
121  have = tds->in_len - tds->in_pos;
122  assert(have >= 0);
123  if (have > 0)
124  break;
125  if (tds_read_packet(tds) < 0)
126  return -1;
127  }
128  if (len > have)
129  len = have;
130  memcpy(data, tds->in_buf + tds->in_pos, len);
131  tds->in_pos += len;
132  return len;
133 }
134 
135 static SSL_RET
136 tds_push_func_login(SSL_PUSH_ARGS)
137 {
138  TDSSOCKET *tds = (TDSSOCKET *) SSL_PTR;
139 
140  tdsdump_log(TDS_DBG_FUNC, "in tds_push_func_login\n");
141 
142  /* initializing SSL, write crypted data inside normal TDS packets */
143  tds_put_n(tds, data, len);
144  return len;
145 }
146 
147 static SSL_RET
148 tds_pull_func(SSL_PULL_ARGS)
149 {
150  TDSCONNECTION *conn = (TDSCONNECTION *) SSL_PTR;
151  TDSSOCKET *tds;
152 
153  tdsdump_log(TDS_DBG_FUNC, "in tds_pull_func\n");
154 
155 #if ENABLE_ODBC_MARS
156  tds = conn->in_net_tds;
157  assert(tds);
158 #else
159  tds = (TDSSOCKET *) conn;
160 #endif
161 
162  /* already initialized (crypted TDS packets) */
163 
164  /* read directly from socket */
165  /* TODO we block write on other sessions */
166  /* also we should already have tested for data on socket */
167  return tds_goodread(tds, (unsigned char*) data, len);
168 }
169 
170 static SSL_RET
171 tds_push_func(SSL_PUSH_ARGS)
172 {
173  TDSCONNECTION *conn = (TDSCONNECTION *) SSL_PTR;
174  TDSSOCKET *tds;
175 
176  tdsdump_log(TDS_DBG_FUNC, "in tds_push_func\n");
177 
178  /* write to socket directly */
179 #if ENABLE_ODBC_MARS
180  tds = conn->in_net_tds;
181 #else
182  tds = (TDSSOCKET *) conn;
183 #endif
184  return tds_goodwrite(tds, (const unsigned char*) data, len);
185 }
186 
187 static int tls_initialized = 0;
188 static tds_mutex tls_mutex = TDS_MUTEX_INITIALIZER;
189 
190 #ifdef HAVE_GNUTLS
191 
192 static void
193 tds_tls_log( int level, const char* s)
194 {
195  tdsdump_log(TDS_DBG_INFO1, "GNUTLS: level %d:\n %s", level, s);
196 }
197 
198 #ifdef TDS_ATTRIBUTE_DESTRUCTOR
199 static void __attribute__((destructor))
200 tds_tls_deinit(void)
201 {
202  if (tls_initialized)
203  gnutls_global_deinit();
204 }
205 #endif
206 
207 #if defined(_THREAD_SAFE) && defined(TDS_HAVE_PTHREAD_MUTEX) && !defined(GNUTLS_USE_NETTLE)
208 GCRY_THREAD_OPTION_PTHREAD_IMPL;
209 #define tds_gcry_init() gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread)
210 #else
211 #define tds_gcry_init() do {} while(0)
212 #endif
213 
214 /* This piece of code is copied from GnuTLS new sources to handle IP in the certificate */
215 #if GNUTLS_VERSION_NUMBER < 0x030306
216 static int
217 check_ip(gnutls_x509_crt_t cert, const void *ip, unsigned ip_size)
218 {
219  char temp[16];
220  size_t temp_size;
221  unsigned i;
222  int ret = 0;
223 
224  /* try matching against:
225  * 1) a IPaddress alternative name (subjectAltName) extension
226  * in the certificate
227  */
228 
229  /* Check through all included subjectAltName extensions, comparing
230  * against all those of type IPAddress.
231  */
232  for (i = 0; ret >= 0; ++i) {
233  temp_size = sizeof(temp);
234  ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
235  temp,
236  &temp_size,
237  NULL);
238 
239  if (ret == GNUTLS_SAN_IPADDRESS) {
240  if (temp_size == ip_size && memcmp(temp, ip, ip_size) == 0)
241  return 1;
242  } else if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
243  ret = 0;
244  }
245  }
246 
247  /* not found a matching IP */
248  return 0;
249 }
250 
251 static int
252 tds_check_ip(gnutls_x509_crt_t cert, const char *hostname)
253 {
254  int ret;
255  union {
256  struct in_addr v4;
257  struct in6_addr v6;
258  } ip;
259  unsigned ip_size;
260 
261  /* check whether @hostname is an ip address */
262  if (strchr(hostname, ':') != NULL) {
263  ip_size = 16;
264  ret = inet_pton(AF_INET6, hostname, &ip.v6);
265  } else {
266  ip_size = 4;
267  ret = inet_pton(AF_INET, hostname, &ip.v4);
268  }
269 
270  if (ret != 0)
271  ret = check_ip(cert, &ip, ip_size);
272 
273  /* There are several misconfigured servers, that place their IP
274  * in the DNS field of subjectAlternativeName. Don't break these
275  * configurations and verify the IP as it would have been a DNS name. */
276 
277  return ret;
278 }
279 
280 /* function for replacing old GnuTLS version */
281 static int
282 tds_x509_crt_check_hostname(gnutls_x509_crt_t cert, const char *hostname)
283 {
284  int ret;
285 
286  ret = tds_check_ip(cert, hostname);
287  if (ret)
288  return ret;
289 
290  return gnutls_x509_crt_check_hostname(cert, hostname);
291 }
292 #define gnutls_x509_crt_check_hostname tds_x509_crt_check_hostname
293 
294 #endif
295 
296 #if GNUTLS_VERSION_MAJOR < 3
297 static int
298 tds_certificate_set_x509_system_trust(gnutls_certificate_credentials_t cred)
299 {
300  static const char ca_directory[] = "/etc/ssl/certs";
301  DIR *dir;
302  struct dirent *dent;
303 #ifdef HAVE_READDIR_R
304  struct dirent ent;
305 #endif
306  int rc;
307  int ncerts;
308  size_t ca_file_length;
309  char *ca_file;
310 
311 
312  dir = opendir(ca_directory);
313  if (!dir)
314  return 0;
315 
316  ca_file_length = strlen(ca_directory) + sizeof(dent->d_name) + 2;
317  ca_file = alloca(ca_file_length);
318 
319  ncerts = 0;
320  for (;;) {
321  struct stat st;
322 
323 #ifdef HAVE_READDIR_R
324  if (readdir_r(dir, &ent, &dent))
325  dent = NULL;
326 #else
327  dent = readdir(dir);
328 #endif
329  if (!dent)
330  break;
331 
332  snprintf(ca_file, ca_file_length, "%s/%s", ca_directory, dent->d_name);
333  if (stat(ca_file, &st) != 0)
334  continue;
335 
336  if (!S_ISREG(st.st_mode))
337  continue;
338 
339  rc = gnutls_certificate_set_x509_trust_file(cred, ca_file, GNUTLS_X509_FMT_PEM);
340  if (rc >= 0)
341  ncerts += rc;
342  }
343 
344  closedir(dir);
345  return ncerts;
346 }
347 #define gnutls_certificate_set_x509_system_trust tds_certificate_set_x509_system_trust
348 
349 #endif
350 
351 static int
352 tds_verify_certificate(gnutls_session_t session)
353 {
354  unsigned int status;
355  int ret;
356  TDSSOCKET *tds = (TDSSOCKET *) gnutls_transport_get_ptr(session);
357 
358 #ifdef ENABLE_DEVELOPING
359  unsigned int list_size;
360  const gnutls_datum_t *cert_list;
361 #endif
362 
363  if (!tds->login)
364  return GNUTLS_E_CERTIFICATE_ERROR;
365 
366  ret = gnutls_certificate_verify_peers2(session, &status);
367  if (ret < 0) {
368  tdsdump_log(TDS_DBG_ERROR, "Error verifying certificate: %s\n", gnutls_strerror(ret));
369  return GNUTLS_E_CERTIFICATE_ERROR;
370  }
371 
372 #ifdef ENABLE_DEVELOPING
373  cert_list = gnutls_certificate_get_peers(session, &list_size);
374  if (cert_list) {
375  gnutls_x509_crt_t cert;
376  gnutls_datum_t cinfo;
377  char buf[8192];
378  size_t size;
379 
380  gnutls_x509_crt_init(&cert);
381 
382  gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
383 
384  /* This is the preferred way of printing short information about
385  * a certificate. */
386  size = sizeof(buf);
387  ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, buf, &size);
388  if (ret == 0) {
389  FILE *f = fopen("cert.dat", "wb");
390  if (f) {
391  fwrite(buf, size, 1, f);
392  fclose(f);
393  }
394  }
395 
396  ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo);
397  if (ret == 0) {
398  tdsdump_log(TDS_DBG_INFO1, "Certificate info: %s\n", cinfo.data);
399  gnutls_free(cinfo.data);
400  }
401 
402  gnutls_x509_crt_deinit(cert);
403  }
404 #endif
405 
406  /* Certificate is not trusted */
407  if (status != 0) {
408  tdsdump_log(TDS_DBG_ERROR, "Certificate status: %u\n", status);
409  return GNUTLS_E_CERTIFICATE_ERROR;
410  }
411 
412  /* check hostname */
413  if (tds->login->check_ssl_hostname) {
414  const gnutls_datum_t *cert_list;
415  unsigned int list_size;
416  gnutls_x509_crt_t cert;
417 
418  cert_list = gnutls_certificate_get_peers(session, &list_size);
419  if (!cert_list) {
420  tdsdump_log(TDS_DBG_ERROR, "Error getting TLS session peers\n");
421  return GNUTLS_E_CERTIFICATE_ERROR;
422  }
423  gnutls_x509_crt_init(&cert);
424  gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
425  ret = gnutls_x509_crt_check_hostname(cert, tds_dstr_cstr(&tds->login->server_host_name));
426  gnutls_x509_crt_deinit(cert);
427  if (!ret) {
428  tdsdump_log(TDS_DBG_ERROR, "Certificate hostname does not match\n");
429  return GNUTLS_E_CERTIFICATE_ERROR;
430  }
431  }
432 
433  /* notify gnutls to continue handshake normally */
434  return 0;
435 }
436 
437 TDSRET
439 {
440  gnutls_session_t session;
441  gnutls_certificate_credentials_t xcred;
442  int ret;
443  const char *tls_msg;
444 
445  xcred = NULL;
446  session = NULL;
447  tls_msg = "initializing tls";
448 
449  if (!tls_initialized) {
450  ret = 0;
451  tds_mutex_lock(&tls_mutex);
452  if (!tls_initialized) {
453  tds_gcry_init();
454  ret = gnutls_global_init();
455  if (ret == 0)
456  tls_initialized = 1;
457  }
458  tds_mutex_unlock(&tls_mutex);
459  if (ret != 0)
460  goto cleanup;
461  }
462 
463  if (tds_write_dump && tls_initialized < 2) {
464  gnutls_global_set_log_level(11);
465  gnutls_global_set_log_function(tds_tls_log);
466  tls_initialized = 2;
467  }
468 
469  tls_msg = "allocating credentials";
470  ret = gnutls_certificate_allocate_credentials(&xcred);
471  if (ret != 0)
472  goto cleanup;
473 
474  if (!tds_dstr_isempty(&tds->login->cafile)) {
475  tls_msg = "loading CA file";
476  if (strcasecmp(tds_dstr_cstr(&tds->login->cafile), "system") == 0)
477  ret = gnutls_certificate_set_x509_system_trust(xcred);
478  else
479  ret = gnutls_certificate_set_x509_trust_file(xcred, tds_dstr_cstr(&tds->login->cafile), GNUTLS_X509_FMT_PEM);
480  if (ret <= 0)
481  goto cleanup;
482  if (!tds_dstr_isempty(&tds->login->crlfile)) {
483  tls_msg = "loading CRL file";
484  ret = gnutls_certificate_set_x509_crl_file(xcred, tds_dstr_cstr(&tds->login->crlfile), GNUTLS_X509_FMT_PEM);
485  if (ret <= 0)
486  goto cleanup;
487  }
488 #ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION
489  gnutls_certificate_set_verify_function(xcred, tds_verify_certificate);
490 #endif
491  }
492 
493  /* Initialize TLS session */
494  tls_msg = "initializing session";
495  ret = gnutls_init(&session, GNUTLS_CLIENT);
496  if (ret != 0)
497  goto cleanup;
498 
499  gnutls_transport_set_ptr(session, tds);
500  gnutls_transport_set_pull_function(session, tds_pull_func_login);
501  gnutls_transport_set_push_function(session, tds_push_func_login);
502 
503  /* NOTE: there functions return int however they cannot fail */
504 
505  /* use default priorities... */
506  gnutls_set_default_priority(session);
507 
508  /* ... but overwrite some */
509  ret = gnutls_priority_set_direct (session, "NORMAL:%COMPAT:-VERS-SSL3.0", NULL);
510  if (ret != 0)
511  goto cleanup;
512 
513  /* mssql does not like padding too much */
514 #ifdef HAVE_GNUTLS_RECORD_DISABLE_PADDING
515  gnutls_record_disable_padding(session);
516 #endif
517 
518  /* put the anonymous credentials to the current session */
519  tls_msg = "setting credential";
520  ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
521  if (ret != 0)
522  goto cleanup;
523 
524  /* Perform the TLS handshake */
525  tls_msg = "handshake";
526  ret = gnutls_handshake (session);
527  if (ret != 0)
528  goto cleanup;
529 
530 #ifndef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION
531  if (!tds_dstr_isempty(&tds->login->cafile)) {
532  ret = tds_verify_certificate(session);
533  if (ret != 0)
534  goto cleanup;
535  }
536 #endif
537 
538  tdsdump_log(TDS_DBG_INFO1, "handshake succeeded!!\n");
539 
540  gnutls_transport_set_ptr(session, tds->conn);
541  gnutls_transport_set_pull_function(session, tds_pull_func);
542  gnutls_transport_set_push_function(session, tds_push_func);
543 
544  tds->conn->tls_session = session;
545  tds->conn->tls_credentials = xcred;
546 
547  return TDS_SUCCESS;
548 
549 cleanup:
550  if (session)
551  gnutls_deinit(session);
552  if (xcred)
553  gnutls_certificate_free_credentials(xcred);
554  tdsdump_log(TDS_DBG_ERROR, "%s failed: %s\n", tls_msg, gnutls_strerror (ret));
555  return TDS_FAIL;
556 }
557 
558 void
560 {
561  if (conn->tls_session) {
562  gnutls_deinit((gnutls_session_t) conn->tls_session);
563  conn->tls_session = NULL;
564  }
565  if (conn->tls_credentials) {
566  gnutls_certificate_free_credentials((gnutls_certificate_credentials_t) conn->tls_credentials);
567  conn->tls_credentials = NULL;
568  }
569 }
570 
571 #else
572 static long
573 tds_ssl_ctrl_login(BIO *b, int cmd, long num, void *ptr)
574 {
575  TDSSOCKET *tds = (TDSSOCKET *) BIO_get_data(b);
576 
577  switch (cmd) {
578  case BIO_CTRL_FLUSH:
579  if (tds->out_pos > 8)
581  return 1;
582  }
583  return 0;
584 }
585 
586 static int
587 tds_ssl_free(BIO *a)
588 {
589  /* nothing to do but required */
590  return 1;
591 }
592 
593 #if OPENSSL_VERSION_NUMBER < 0x1010000FL
594 static BIO_METHOD tds_method_login[1] = {
595 {
596  BIO_TYPE_MEM,
597  "tds",
598  tds_push_func_login,
599  tds_pull_func_login,
600  NULL,
601  NULL,
602  tds_ssl_ctrl_login,
603  NULL,
604  tds_ssl_free,
605  NULL,
606 }};
607 
608 static BIO_METHOD tds_method[1] = {
609 {
610  BIO_TYPE_MEM,
611  "tds",
612  tds_push_func,
613  tds_pull_func,
614  NULL,
615  NULL,
616  NULL,
617  NULL,
618  tds_ssl_free,
619  NULL,
620 }};
621 
622 static inline void
623 tds_init_ssl_methods(void)
624 {
625 }
626 #else
627 static BIO_METHOD *tds_method_login;
628 static BIO_METHOD *tds_method;
629 
630 static void
631 tds_init_ssl_methods(void)
632 {
633  BIO_METHOD *meth;
634 
635  tds_method_login = meth = BIO_meth_new(BIO_TYPE_MEM, "tds");
636  BIO_meth_set_write(meth, tds_push_func_login);
637  BIO_meth_set_read(meth, tds_pull_func_login);
638  BIO_meth_set_ctrl(meth, tds_ssl_ctrl_login);
639  BIO_meth_set_destroy(meth, tds_ssl_free);
640 
641  tds_method = meth = BIO_meth_new(BIO_TYPE_MEM, "tds");
642  BIO_meth_set_write(meth, tds_push_func);
643  BIO_meth_set_read(meth, tds_pull_func);
644  BIO_meth_set_destroy(meth, tds_ssl_free);
645 }
646 
647 # ifdef TDS_ATTRIBUTE_DESTRUCTOR
648 static void __attribute__((destructor))
649 tds_deinit_openssl_methods(void)
650 {
651  BIO_meth_free(tds_method_login);
652  BIO_meth_free(tds_method);
653 }
654 # endif
655 #endif
656 
657 
658 static SSL_CTX *
659 tds_init_openssl(void)
660 {
661  const SSL_METHOD *meth;
662 
663  if (!tls_initialized) {
664  tds_mutex_lock(&tls_mutex);
665  if (!tls_initialized) {
666  SSL_library_init();
667  tds_init_ssl_methods();
668  tls_initialized = 1;
669  }
670  tds_mutex_unlock(&tls_mutex);
671  }
672  meth = TLS_client_method();
673  if (meth == NULL)
674  return NULL;
675  return SSL_CTX_new (meth);
676 }
677 
678 static int
679 check_wildcard(const char *host, const char *match)
680 {
681  const char *p, *w;
682  size_t n, lh, lm;
683 
684  /* U-label (binary) */
685  for (p = match; *p; ++p)
686  if ((unsigned char) *p >= 0x80)
687  return strcmp(host, match) == 0;
688 
689  for (;;) {
690  /* A-label (starts with xn--) */
691  if (strncasecmp(match, "xn--", 4) == 0)
692  break;
693 
694  /* match must not be in domain and domain should contains 2 parts */
695  w = strchr(match, '*');
696  p = strchr(match, '.');
697  if (!w || !p /* no wildcard or domain */
698  || p[1] == '.' /* empty domain */
699  || w > p || strchr(p, '*') != NULL) /* wildcard in domain */
700  break;
701  p = strchr(p+1, '.');
702  if (!p || p[1] == 0) /* not another domain */
703  break;
704 
705  /* check start */
706  n = w - match; /* prefix len */
707  if (n > 0 && strncasecmp(host, match, n) != 0)
708  return 0;
709 
710  /* check end */
711  lh = strlen(host);
712  lm = strlen(match);
713  n = lm - n - 1; /* suffix len */
714  if (lm - 1 > lh || strcasecmp(host+lh-n, match+lm-n) != 0 || host[0] == '.')
715  return 0;
716 
717  return 1;
718  }
719  return strcasecmp(host, match) == 0;
720 }
721 
722 #if ENABLE_EXTRA_CHECKS
723 static void
724 tds_check_wildcard_test(void)
725 {
726  assert(check_wildcard("foo", "foo") == 1);
727  assert(check_wildcard("FOO", "foo") == 1);
728  assert(check_wildcard("foo", "FOO") == 1);
729  assert(check_wildcard("\x90oo", "\x90OO") == 0);
730  assert(check_wildcard("xn--foo", "xn--foo") == 1);
731  assert(check_wildcard("xn--FOO", "XN--foo") == 1);
732  assert(check_wildcard("xn--a.example.org", "xn--*.example.org") == 0);
733  assert(check_wildcard("a.*", "a.*") == 1);
734  assert(check_wildcard("a.b", "a.*") == 0);
735  assert(check_wildcard("ab", "a*") == 0);
736  assert(check_wildcard("a.example.", "*.example.") == 0);
737  assert(check_wildcard("a.example.com", "*.example.com") == 1);
738  assert(check_wildcard("a.b.example.com", "a.*.example.com") == 0);
739  assert(check_wildcard("foo.example.com", "foo*.example.com") == 1);
740  assert(check_wildcard("fou.example.com", "foo*.example.com") == 0);
741  assert(check_wildcard("baz.example.com", "*baz.example.com") == 1);
742  assert(check_wildcard("buzz.example.com", "b*z.example.com") == 1);
743  assert(check_wildcard("bz.example.com", "b*z.example.com") == 1);
744  assert(check_wildcard(".example.com", "*.example.com") == 0);
745  assert(check_wildcard("example.com", "*.example.com") == 0);
746 }
747 #else
748 #define tds_check_wildcard_test() do { } while(0)
749 #endif
750 
751 static int
752 check_name_match(ASN1_STRING *name, const char *hostname)
753 {
754  char *name_utf8 = NULL;
755  int ret, name_len;
756 
757  name_len = ASN1_STRING_to_UTF8((unsigned char **) &name_utf8, name);
758  if (name_len < 0)
759  return 0;
760 
761  tdsdump_log(TDS_DBG_INFO1, "Got name %s\n", name_utf8);
762  ret = 0;
763  if (strlen(name_utf8) == name_len && check_wildcard(name_utf8, hostname))
764  ret = 1;
765  OPENSSL_free(name_utf8);
766  return ret;
767 }
768 
769 static int
770 check_alt_names(X509 *cert, const char *hostname)
771 {
772  STACK_OF(GENERAL_NAME) *alt_names;
773  int i, num;
774  int ret = 1;
775  union {
776  struct in_addr v4;
777  struct in6_addr v6;
778  } ip;
779  unsigned ip_size = 0;
780 
781  /* check whether @hostname is an ip address */
782  if (strchr(hostname, ':') != NULL) {
783  ip_size = 16;
784  ret = inet_pton(AF_INET6, hostname, &ip.v6);
785  } else {
786  ip_size = 4;
787  ret = inet_pton(AF_INET, hostname, &ip.v4);
788  }
789  if (ret == 0)
790  return -1;
791 
792  ret = -1;
793 
794  alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
795  if (!alt_names)
796  return ret;
797 
798  num = sk_GENERAL_NAME_num(alt_names);
799  tdsdump_log(TDS_DBG_INFO1, "Alt names number %d\n", num);
800  for (i = 0; i < num; ++i) {
801  const char *altptr;
802  size_t altlen;
803 
804  const GENERAL_NAME *name = sk_GENERAL_NAME_value(alt_names, i);
805  if (!name)
806  continue;
807 
808  altptr = (const char *) ASN1_STRING_data(name->d.ia5);
809  altlen = (size_t) ASN1_STRING_length(name->d.ia5);
810 
811  if (name->type == GEN_DNS && ip_size == 0) {
812  ret = 0;
813  if (!check_name_match(name->d.dNSName, hostname))
814  continue;
815  } else if (name->type == GEN_IPADD && ip_size != 0) {
816  ret = 0;
817  if (altlen != ip_size || memcmp(altptr, &ip, altlen) != 0)
818  continue;
819  } else {
820  continue;
821  }
822 
823  sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
824  return 1;
825  }
826  sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
827  return ret;
828 }
829 
830 static int
831 check_hostname(X509 *cert, const char *hostname)
832 {
833  int ret, i;
834  X509_NAME *subject;
835  ASN1_STRING *name;
836 
837  /* check by subject */
838  ret = check_alt_names(cert, hostname);
839  if (ret >= 0)
840  return ret;
841 
842  /* check by common name (old method) */
843  subject= X509_get_subject_name(cert);
844  if (!subject)
845  return 0;
846 
847  i = -1;
848  while (X509_NAME_get_index_by_NID(subject, NID_commonName, i) >=0)
849  i = X509_NAME_get_index_by_NID(subject, NID_commonName, i);
850  if (i < 0)
851  return 0;
852 
853  name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i));
854  if (!name)
855  return 0;
856 
857  return check_name_match(name, hostname);
858 }
859 
860 int
862 {
863 #define DEFAULT_OPENSSL_CTX_OPTIONS (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3)
864 #define DEFAULT_OPENSSL_CIPHERS "HIGH:!SSLv2:!aNULL:-DH"
865 
866  SSL *con;
867  SSL_CTX *ctx;
868  BIO *b, *b2;
869 
870  int ret;
871  const char *tls_msg;
872 
873  con = NULL;
874  b = NULL;
875  b2 = NULL;
876  ret = 1;
877 
878  tds_check_wildcard_test();
879 
881 
882  tls_msg = "initializing tls";
883  ctx = tds_init_openssl();
884  if (!ctx)
885  goto cleanup;
886 
887  SSL_CTX_set_options(ctx, DEFAULT_OPENSSL_CTX_OPTIONS);
888 
889  if (!tds_dstr_isempty(&tds->login->cafile)) {
890  tls_msg = "loading CA file";
891  if (strcasecmp(tds_dstr_cstr(&tds->login->cafile), "system") == 0)
892  ret = SSL_CTX_set_default_verify_paths(ctx);
893  else
894  ret = SSL_CTX_load_verify_locations(ctx, tds_dstr_cstr(&tds->login->cafile), NULL);
895  if (ret != 1)
896  goto cleanup;
897  if (!tds_dstr_isempty(&tds->login->crlfile)) {
898  X509_STORE *store = SSL_CTX_get_cert_store(ctx);
899  X509_LOOKUP *lookup;
900 
901  tls_msg = "loading CRL file";
902  if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))
903  || (!X509_load_crl_file(lookup, tds_dstr_cstr(&tds->login->crlfile), X509_FILETYPE_PEM)))
904  goto cleanup;
905 
906  X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
907  }
908  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
909  }
910 
911  /* Initialize TLS session */
912  tls_msg = "initializing session";
913  con = SSL_new(ctx);
914  if (!con)
915  goto cleanup;
916 
917  tls_msg = "creating bio";
918  b = BIO_new(tds_method_login);
919  if (!b)
920  goto cleanup;
921 
922  b2 = BIO_new(tds_method);
923  if (!b2)
924  goto cleanup;
925 
926  BIO_set_init(b, 1);
927  BIO_set_data(b, tds);
928  BIO_set_conn_hostname(b, tds_dstr_cstr(&tds->login->server_host_name));
929  SSL_set_bio(con, b, b);
930  b = NULL;
931 
932  /* use default priorities unless overridden by openssl ciphers setting in freetds.conf file... */
934  tdsdump_log(TDS_DBG_INFO1, "setting custom openssl cipher to:%s\n", tds_dstr_cstr(&tds->login->openssl_ciphers));
935  SSL_set_cipher_list(con, tds_dstr_cstr(&tds->login->openssl_ciphers) );
936  } else {
937  tdsdump_log(TDS_DBG_INFO1, "setting default openssl cipher to:%s\n", DEFAULT_OPENSSL_CIPHERS );
938  SSL_set_cipher_list(con, DEFAULT_OPENSSL_CIPHERS);
939  }
940 
941 #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
942  /* this disable a security improvement but allow connection... */
943  SSL_set_options(con, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
944 #endif
945 
946  /* Perform the TLS handshake */
947  tls_msg = "handshake";
948  SSL_set_connect_state(con);
949  ret = SSL_connect(con) != 1 || SSL_get_state(con) != TLS_ST_OK;
950  if (ret != 0)
951  goto cleanup;
952 
953  /* check certificate hostname */
955  X509 *cert;
956 
957  cert = SSL_get_peer_certificate(con);
958  tls_msg = "checking hostname";
959  if (!cert || !check_hostname(cert, tds_dstr_cstr(&tds->login->server_host_name)))
960  goto cleanup;
961  }
962 
963  tdsdump_log(TDS_DBG_INFO1, "handshake succeeded!!\n");
964 
965  BIO_set_init(b2, 1);
966  BIO_set_data(b2, tds->conn);
967  SSL_set_bio(con, b2, b2);
968 
969  tds->conn->tls_session = con;
970  tds->conn->tls_ctx = ctx;
971 
972  return TDS_SUCCESS;
973 
974 cleanup:
975  if (b2)
976  BIO_free(b2);
977  if (b)
978  BIO_free(b);
979  if (con) {
980  SSL_shutdown(con);
981  SSL_free(con);
982  }
983  SSL_CTX_free(ctx);
984  tdsdump_log(TDS_DBG_ERROR, "%s failed\n", tls_msg);
985  return TDS_FAIL;
986 }
987 
988 void
990 {
991  if (conn->tls_session) {
992  /* NOTE do not call SSL_shutdown here */
993  SSL_free(conn->tls_session);
994  conn->tls_session = NULL;
995  }
996  if (conn->tls_ctx) {
997  SSL_CTX_free(conn->tls_ctx);
998  conn->tls_ctx = NULL;
999  }
1000 }
1001 #endif
1002 
1003 #endif
1004 /** @} */
1005 
void * alloca(size_t)
static int lookup(const char *name, const struct lookup_int *table)
Definition: attributes.c:50
static TDSSOCKET * tds
Definition: collations.c:37
static CS_COMMAND * cmd
Definition: ct_dynamic.c:26
static void cleanup(void)
Definition: ct_dynamic.c:30
static CS_CONNECTION * conn
Definition: ct_dynamic.c:25
CS_CONTEXT * ctx
Definition: t0006.c:12
static const char ip[]
Definition: des.c:75
static void DLIST_NAME() init(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:40
#define NULL
Definition: ncbistd.hpp:225
static const char * tds_dstr_cstr(DSTR *s)
Returns a C version (NUL terminated string) of dstr.
Definition: string.h:66
static int tds_dstr_isempty(DSTR *s)
test if string is empty
Definition: string.h:48
char * buf
int i
yy_size_t n
int len
void destructor(T1 *p)
Definition: tree_msvc7.hpp:72
const struct ncbi::grid::netcache::search::fields::SIZE size
int strcmp(const char *str1, const char *str2)
Definition: odbc_utils.hpp:160
#define strncasecmp
#define strcasecmp
unsigned int a
Definition: ncbi_localip.c:102
double f(double x_, const double &y_)
Definition: njn_root.hpp:188
static int match(register const pcre_uchar *eptr, register const pcre_uchar *ecode, const pcre_uchar *mstart, int offset_top, match_data *md, eptrblock *eptrb, unsigned int rdepth)
Definition: pcre_exec.c:513
#define tds_goodread
#define tds_goodwrite
#define tds_put_n
#define tds_write_dump
#define tds_read_packet
#define tds_flush_packet
#define assert(x)
Definition: srv_diag.hpp:58
static string subject
void * tls_session
Definition: tds.h:1195
DSTR crlfile
certificate revocation file
Definition: tds.h:598
unsigned int check_ssl_hostname
Definition: tds.h:634
DSTR server_host_name
Definition: tds.h:593
DSTR openssl_ciphers
Definition: tds.h:599
DSTR cafile
certificate authorities file
Definition: tds.h:597
Information for a server connection.
Definition: tds.h:1211
TDSLOGIN * login
config for login stuff.
Definition: tds.h:1283
unsigned in_len
input buffer length
Definition: tds.h:1239
unsigned char * in_buf
Input buffer.
Definition: tds.h:1223
unsigned out_pos
current position in out_buf
Definition: tds.h:1238
unsigned in_pos
current position in in_buf
Definition: tds.h:1237
TDSCONNECTION conn[1]
Definition: tds.h:1215
Main include file for libtds.
#define TDS_FAIL
Definition: tds.h:204
#define tdsdump_log
Definition: tds.h:1561
#define TDS_DBG_INFO1
Definition: tds.h:900
int TDSRET
Definition: tds.h:201
#define TDS_DBG_ERROR
Definition: tds.h:903
#define TDS_SUCCESS
Definition: tds.h:203
#define TDS_DBG_FUNC
Definition: tds.h:898
#define tds_mutex_lock(x)
Definition: thread.h:421
#define tds_mutex_unlock(x)
Definition: thread.h:423
static void tds_ssl_deinit(TDSCONNECTION *conn)
Definition: tls.h:94
static TDSRET tds_ssl_init(TDSSOCKET *tds)
Definition: tls.h:88
Modified on Tue Apr 09 07:56:02 2024 by modify_doxy.py rev. 669887
Modified on Wed Apr 10 07:31:29 2024 by modify_doxy.py rev. 669887
Modified on Thu Apr 11 15:03:06 2024 by modify_doxy.py rev. 669887
Modified on Fri Apr 12 17:15:08 2024 by modify_doxy.py rev. 669887
Modified on Sat Apr 13 11:43:42 2024 by modify_doxy.py rev. 669887
Modified on Sun Apr 14 05:24:43 2024 by modify_doxy.py rev. 669887