1/************************************************************************************
2 Copyright (C) 2014 MariaDB Corporation AB
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with this library; if not see <http://www.gnu.org/licenses>
16 or write to the Free Software Foundation, Inc.,
17 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
18
19 *************************************************************************************/
20
21/*
22 * this is the abstraction layer for communication via SSL.
23 * The following SSL libraries/variants are currently supported:
24 * - openssl
25 * - gnutls
26 * - schannel (windows only)
27 *
28 * Different SSL variants are implemented as plugins
29 * On Windows schannel is implemented as (standard)
30 * built-in plugin.
31 */
32
33#ifdef HAVE_TLS
34
35#include <ma_global.h>
36#include <ma_sys.h>
37#include <ma_common.h>
38#include <string.h>
39#include <errmsg.h>
40#include <ma_pvio.h>
41#include <ma_tls.h>
42#include <mysql/client_plugin.h>
43#include <mariadb/ma_io.h>
44
45#ifdef HAVE_NONBLOCK
46#include <mariadb_async.h>
47#include <ma_context.h>
48#endif
49
50/* Errors should be handled via pvio callback function */
51my_bool ma_tls_initialized= FALSE;
52unsigned int mariadb_deinitialize_ssl= 1;
53
54const char *tls_protocol_version[]=
55 {"SSLv3", "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3", "Unknown"};
56
57MARIADB_TLS *ma_pvio_tls_init(MYSQL *mysql)
58{
59 MARIADB_TLS *ctls= NULL;
60
61 if (!ma_tls_initialized)
62 ma_tls_start(mysql->net.last_error, MYSQL_ERRMSG_SIZE);
63
64 if (!(ctls= (MARIADB_TLS *)calloc(1, sizeof(MARIADB_TLS))))
65 {
66 return NULL;
67 }
68
69 /* register error routine and methods */
70 ctls->pvio= mysql->net.pvio;
71 if (!(ctls->ssl= ma_tls_init(mysql)))
72 {
73 free(ctls);
74 ctls= NULL;
75 }
76 return ctls;
77}
78
79my_bool ma_pvio_tls_connect(MARIADB_TLS *ctls)
80{
81 my_bool rc;
82
83 if ((rc= ma_tls_connect(ctls)))
84 ma_tls_close(ctls);
85 return rc;
86}
87
88ssize_t ma_pvio_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
89{
90 return ma_tls_read(ctls, buffer, length);
91}
92
93ssize_t ma_pvio_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
94{
95 return ma_tls_write(ctls, buffer, length);
96}
97
98my_bool ma_pvio_tls_close(MARIADB_TLS *ctls)
99{
100 return ma_tls_close(ctls);
101}
102
103int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls)
104{
105 return ma_tls_verify_server_cert(ctls);
106}
107
108const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls)
109{
110 return ma_tls_get_cipher(ctls);
111}
112
113void ma_pvio_tls_end()
114{
115 ma_tls_end();
116}
117
118int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls)
119{
120 return ma_tls_get_protocol_version(ctls);
121}
122
123const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls)
124{
125 int version;
126
127 version= ma_tls_get_protocol_version(ctls);
128 if (version < 0 || version > PROTOCOL_MAX)
129 return tls_protocol_version[PROTOCOL_UNKNOWN];
130 return tls_protocol_version[version];
131}
132
133static signed char ma_hex2int(char c)
134{
135 if (c >= '0' && c <= '9')
136 return c - '0';
137 if (c >= 'A' && c <= 'F')
138 return 10 + c - 'A';
139 if (c >= 'a' && c <= 'f')
140 return 10 + c - 'a';
141 return -1;
142}
143
144static my_bool ma_pvio_tls_compare_fp(const char *cert_fp,
145 unsigned int cert_fp_len,
146 const char *fp, unsigned int fp_len)
147{
148 char *p= (char *)fp,
149 *c;
150
151 /* check length */
152 if (cert_fp_len != 20)
153 return 1;
154
155 /* We support two formats:
156 2 digits hex numbers, separated by colons (length=59)
157 20 * 2 digits hex numbers without separators (length = 40)
158 */
159 if (fp_len != (strchr(fp, ':') ? 59 : 40))
160 return 1;
161
162 for(c= (char *)cert_fp; c < cert_fp + cert_fp_len; c++)
163 {
164 signed char d1, d2;
165 if (*p == ':')
166 p++;
167 if (p - fp > (int)fp_len -1)
168 return 1;
169 if ((d1 = ma_hex2int(*p)) == - 1 ||
170 (d2 = ma_hex2int(*(p+1))) == -1 ||
171 (char)(d1 * 16 + d2) != *c)
172 return 1;
173 p+= 2;
174 }
175 return 0;
176}
177
178my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list)
179{
180 unsigned int cert_fp_len= 64;
181 char *cert_fp= NULL;
182 my_bool rc=1;
183 MYSQL *mysql= ctls->pvio->mysql;
184
185 cert_fp= (char *)malloc(cert_fp_len);
186
187 if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1)
188 goto end;
189 if (fp)
190 rc= ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, fp, (unsigned int)strlen(fp));
191 else if (fp_list)
192 {
193 MA_FILE *fp;
194 char buff[255];
195
196 if (!(fp = ma_open(fp_list, "r", mysql)))
197 goto end;
198
199 while (ma_gets(buff, sizeof(buff)-1, fp))
200 {
201 /* remove trailing new line character */
202 char *pos= strchr(buff, '\r');
203 if (!pos)
204 pos= strchr(buff, '\n');
205 if (pos)
206 *pos= '\0';
207
208 if (!ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, buff, (unsigned int)strlen(buff)))
209 {
210 /* finger print is valid: close file and exit */
211 ma_close(fp);
212 rc= 0;
213 goto end;
214 }
215 }
216
217 /* No finger print matched - close file and return error */
218 ma_close(fp);
219 }
220
221end:
222 if (cert_fp)
223 free(cert_fp);
224 if (rc)
225 {
226 my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
227 ER(CR_SSL_CONNECTION_ERROR),
228 "Fingerprint verification of server certificate failed");
229 }
230 return rc;
231}
232#endif /* HAVE_TLS */
233