trilogy 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of trilogy might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +74 -0
- data/Rakefile +18 -0
- data/ext/trilogy-ruby/cast.c +272 -0
- data/ext/trilogy-ruby/cext.c +933 -0
- data/ext/trilogy-ruby/extconf.rb +16 -0
- data/ext/trilogy-ruby/inc/trilogy/blocking.h +163 -0
- data/ext/trilogy-ruby/inc/trilogy/buffer.h +64 -0
- data/ext/trilogy-ruby/inc/trilogy/builder.h +161 -0
- data/ext/trilogy-ruby/inc/trilogy/charset.h +277 -0
- data/ext/trilogy-ruby/inc/trilogy/client.h +546 -0
- data/ext/trilogy-ruby/inc/trilogy/error.h +43 -0
- data/ext/trilogy-ruby/inc/trilogy/packet_parser.h +34 -0
- data/ext/trilogy-ruby/inc/trilogy/protocol.h +756 -0
- data/ext/trilogy-ruby/inc/trilogy/reader.h +212 -0
- data/ext/trilogy-ruby/inc/trilogy/socket.h +111 -0
- data/ext/trilogy-ruby/inc/trilogy/vendor/curl_hostcheck.h +29 -0
- data/ext/trilogy-ruby/inc/trilogy/vendor/openssl_hostname_validation.h +51 -0
- data/ext/trilogy-ruby/inc/trilogy.h +8 -0
- data/ext/trilogy-ruby/src/blocking.c +241 -0
- data/ext/trilogy-ruby/src/buffer.c +60 -0
- data/ext/trilogy-ruby/src/builder.c +198 -0
- data/ext/trilogy-ruby/src/charset.c +212 -0
- data/ext/trilogy-ruby/src/client.c +728 -0
- data/ext/trilogy-ruby/src/error.c +17 -0
- data/ext/trilogy-ruby/src/packet_parser.c +140 -0
- data/ext/trilogy-ruby/src/protocol.c +676 -0
- data/ext/trilogy-ruby/src/reader.c +244 -0
- data/ext/trilogy-ruby/src/socket.c +623 -0
- data/ext/trilogy-ruby/src/vendor/curl_hostcheck.c +206 -0
- data/ext/trilogy-ruby/src/vendor/openssl_hostname_validation.c +175 -0
- data/ext/trilogy-ruby/trilogy-ruby.h +36 -0
- data/lib/trilogy/version.rb +3 -0
- data/lib/trilogy.rb +61 -0
- data/trilogy.gemspec +27 -0
- metadata +106 -0
@@ -0,0 +1,623 @@
|
|
1
|
+
#include <netdb.h>
|
2
|
+
#include <netinet/tcp.h>
|
3
|
+
#include <poll.h>
|
4
|
+
#include <sys/socket.h>
|
5
|
+
#include <sys/types.h>
|
6
|
+
#include <sys/un.h>
|
7
|
+
|
8
|
+
#include <errno.h>
|
9
|
+
#include <fcntl.h>
|
10
|
+
#include <stdlib.h>
|
11
|
+
#include <unistd.h>
|
12
|
+
|
13
|
+
#include "trilogy/error.h"
|
14
|
+
#include "trilogy/socket.h"
|
15
|
+
|
16
|
+
#if OPENSSL_VERSION_NUMBER < 0x1000200fL
|
17
|
+
#include "trilogy/vendor/openssl_hostname_validation.h"
|
18
|
+
#endif
|
19
|
+
|
20
|
+
struct trilogy_sock {
|
21
|
+
trilogy_sock_t base;
|
22
|
+
struct addrinfo *addr;
|
23
|
+
int fd;
|
24
|
+
SSL *ssl;
|
25
|
+
};
|
26
|
+
|
27
|
+
static int _cb_raw_fd(trilogy_sock_t *_sock)
|
28
|
+
{
|
29
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
30
|
+
return sock->fd;
|
31
|
+
}
|
32
|
+
|
33
|
+
static int _cb_wait(trilogy_sock_t *_sock, trilogy_wait_t wait)
|
34
|
+
{
|
35
|
+
struct pollfd pfd = {.fd = trilogy_sock_fd(_sock)};
|
36
|
+
|
37
|
+
switch (wait) {
|
38
|
+
case TRILOGY_WAIT_HANDSHAKE:
|
39
|
+
case TRILOGY_WAIT_READ:
|
40
|
+
pfd.events = POLLIN;
|
41
|
+
break;
|
42
|
+
case TRILOGY_WAIT_WRITE:
|
43
|
+
pfd.events = POLLOUT;
|
44
|
+
break;
|
45
|
+
default:
|
46
|
+
return TRILOGY_ERR;
|
47
|
+
}
|
48
|
+
|
49
|
+
while (1) {
|
50
|
+
int rc = poll(&pfd, 1, -1);
|
51
|
+
|
52
|
+
if (rc < 0) {
|
53
|
+
if (errno == EINTR) {
|
54
|
+
continue;
|
55
|
+
}
|
56
|
+
return TRILOGY_SYSERR;
|
57
|
+
}
|
58
|
+
|
59
|
+
return TRILOGY_OK;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
static ssize_t _cb_raw_read(trilogy_sock_t *_sock, void *buf, size_t nread)
|
64
|
+
{
|
65
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
66
|
+
ssize_t data_read = read(sock->fd, buf, nread);
|
67
|
+
if (data_read < 0) {
|
68
|
+
return (ssize_t)TRILOGY_SYSERR;
|
69
|
+
}
|
70
|
+
return data_read;
|
71
|
+
}
|
72
|
+
|
73
|
+
static ssize_t _cb_raw_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite)
|
74
|
+
{
|
75
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
76
|
+
ssize_t data_written = write(sock->fd, buf, nwrite);
|
77
|
+
if (data_written < 0) {
|
78
|
+
return (ssize_t)TRILOGY_SYSERR;
|
79
|
+
}
|
80
|
+
return data_written;
|
81
|
+
}
|
82
|
+
|
83
|
+
static int _cb_raw_close(trilogy_sock_t *_sock)
|
84
|
+
{
|
85
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
86
|
+
int rc = 0;
|
87
|
+
if (sock->fd != -1) {
|
88
|
+
rc = close(sock->fd);
|
89
|
+
}
|
90
|
+
if (sock->addr) {
|
91
|
+
freeaddrinfo(sock->addr);
|
92
|
+
}
|
93
|
+
|
94
|
+
free(sock->base.opts.hostname);
|
95
|
+
free(sock->base.opts.path);
|
96
|
+
free(sock->base.opts.database);
|
97
|
+
free(sock->base.opts.username);
|
98
|
+
free(sock->base.opts.password);
|
99
|
+
free(sock->base.opts.ssl_ca);
|
100
|
+
free(sock->base.opts.ssl_capath);
|
101
|
+
free(sock->base.opts.ssl_cert);
|
102
|
+
free(sock->base.opts.ssl_cipher);
|
103
|
+
free(sock->base.opts.ssl_crl);
|
104
|
+
free(sock->base.opts.ssl_crlpath);
|
105
|
+
free(sock->base.opts.ssl_key);
|
106
|
+
free(sock->base.opts.tls_ciphersuites);
|
107
|
+
|
108
|
+
free(sock);
|
109
|
+
return rc;
|
110
|
+
}
|
111
|
+
|
112
|
+
static int _cb_raw_shutdown(trilogy_sock_t *_sock) { return shutdown(trilogy_sock_fd(_sock), SHUT_RDWR); }
|
113
|
+
|
114
|
+
static int set_nonblocking_fd(int sock)
|
115
|
+
{
|
116
|
+
int flags = fcntl(sock, F_GETFL, 0);
|
117
|
+
|
118
|
+
if (flags < 0) {
|
119
|
+
return TRILOGY_SYSERR;
|
120
|
+
}
|
121
|
+
|
122
|
+
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
|
123
|
+
return TRILOGY_SYSERR;
|
124
|
+
}
|
125
|
+
|
126
|
+
return TRILOGY_OK;
|
127
|
+
}
|
128
|
+
|
129
|
+
static int raw_connect_internal(struct trilogy_sock *sock, const struct addrinfo *ai)
|
130
|
+
{
|
131
|
+
int sockerr;
|
132
|
+
socklen_t sockerr_len = sizeof(sockerr);
|
133
|
+
|
134
|
+
sock->fd = socket(ai->ai_family, SOCK_STREAM, ai->ai_protocol);
|
135
|
+
if (sock->fd < 0) {
|
136
|
+
return TRILOGY_SYSERR;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (sock->base.opts.keepalive_enabled) {
|
140
|
+
int flags = 1;
|
141
|
+
if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) {
|
142
|
+
goto fail;
|
143
|
+
}
|
144
|
+
#ifdef TCP_KEEPIDLE
|
145
|
+
if (sock->base.opts.keepalive_idle > 0) {
|
146
|
+
flags = sock->base.opts.keepalive_idle;
|
147
|
+
if (setsockopt(sock->fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&flags, sizeof(flags)) < 0) {
|
148
|
+
goto fail;
|
149
|
+
}
|
150
|
+
}
|
151
|
+
#endif
|
152
|
+
#ifdef TCP_KEEPINTVL
|
153
|
+
if (sock->base.opts.keepalive_interval > 0) {
|
154
|
+
flags = sock->base.opts.keepalive_interval;
|
155
|
+
if (setsockopt(sock->fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&flags, sizeof(flags)) < 0) {
|
156
|
+
goto fail;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
#endif
|
160
|
+
#ifdef TCP_KEEPCNT
|
161
|
+
if (sock->base.opts.keepalive_count > 0) {
|
162
|
+
flags = sock->base.opts.keepalive_count;
|
163
|
+
if (setsockopt(sock->fd, IPPROTO_TCP, TCP_KEEPCNT, (void *)&flags, sizeof(flags)) < 0) {
|
164
|
+
goto fail;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
#endif
|
168
|
+
}
|
169
|
+
|
170
|
+
if (set_nonblocking_fd(sock->fd) < 0) {
|
171
|
+
goto fail;
|
172
|
+
}
|
173
|
+
|
174
|
+
if (connect(sock->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
|
175
|
+
if (errno != EINPROGRESS && errno != EAGAIN) {
|
176
|
+
goto fail;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
if (trilogy_sock_wait_write((trilogy_sock_t *)sock) < 0) {
|
181
|
+
goto fail;
|
182
|
+
}
|
183
|
+
|
184
|
+
if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerr_len) < 0) {
|
185
|
+
goto fail;
|
186
|
+
}
|
187
|
+
|
188
|
+
if (sockerr != 0) {
|
189
|
+
// the socket failed to connect; since `getsockopt` doesn't set `errno`
|
190
|
+
// to a meaningful value, we must set it manually because clients always
|
191
|
+
// expect to find the error code there
|
192
|
+
errno = sockerr;
|
193
|
+
goto fail;
|
194
|
+
}
|
195
|
+
|
196
|
+
return TRILOGY_OK;
|
197
|
+
|
198
|
+
fail:
|
199
|
+
close(sock->fd);
|
200
|
+
sock->fd = -1;
|
201
|
+
return TRILOGY_SYSERR;
|
202
|
+
}
|
203
|
+
|
204
|
+
static int _cb_raw_connect(trilogy_sock_t *_sock)
|
205
|
+
{
|
206
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
207
|
+
const struct addrinfo *ai = sock->addr;
|
208
|
+
|
209
|
+
for (; ai; ai = ai->ai_next) {
|
210
|
+
int rc = raw_connect_internal(sock, ai);
|
211
|
+
if (rc == TRILOGY_OK)
|
212
|
+
return TRILOGY_OK;
|
213
|
+
else if (!ai->ai_next)
|
214
|
+
return rc;
|
215
|
+
}
|
216
|
+
|
217
|
+
return TRILOGY_ERR;
|
218
|
+
}
|
219
|
+
|
220
|
+
static char *strdupnullok(const char *str)
|
221
|
+
{
|
222
|
+
if (str == NULL) {
|
223
|
+
return NULL;
|
224
|
+
}
|
225
|
+
return strdup(str);
|
226
|
+
}
|
227
|
+
|
228
|
+
trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts)
|
229
|
+
{
|
230
|
+
struct trilogy_sock *sock = malloc(sizeof(struct trilogy_sock));
|
231
|
+
|
232
|
+
sock->base.connect_cb = _cb_raw_connect;
|
233
|
+
sock->base.read_cb = _cb_raw_read;
|
234
|
+
sock->base.write_cb = _cb_raw_write;
|
235
|
+
sock->base.wait_cb = _cb_wait;
|
236
|
+
sock->base.shutdown_cb = _cb_raw_shutdown;
|
237
|
+
sock->base.close_cb = _cb_raw_close;
|
238
|
+
sock->base.fd_cb = _cb_raw_fd;
|
239
|
+
sock->base.opts = *opts;
|
240
|
+
|
241
|
+
sock->base.opts.hostname = strdupnullok(opts->hostname);
|
242
|
+
sock->base.opts.path = strdupnullok(opts->path);
|
243
|
+
sock->base.opts.database = strdupnullok(opts->database);
|
244
|
+
sock->base.opts.username = strdupnullok(opts->username);
|
245
|
+
|
246
|
+
if (sock->base.opts.password) {
|
247
|
+
sock->base.opts.password = malloc(opts->password_len);
|
248
|
+
memcpy(sock->base.opts.password, opts->password, opts->password_len);
|
249
|
+
}
|
250
|
+
|
251
|
+
sock->base.opts.ssl_ca = strdupnullok(opts->ssl_ca);
|
252
|
+
sock->base.opts.ssl_capath = strdupnullok(opts->ssl_capath);
|
253
|
+
sock->base.opts.ssl_cert = strdupnullok(opts->ssl_cert);
|
254
|
+
sock->base.opts.ssl_cipher = strdupnullok(opts->ssl_cipher);
|
255
|
+
sock->base.opts.ssl_crl = strdupnullok(opts->ssl_crl);
|
256
|
+
sock->base.opts.ssl_crlpath = strdupnullok(opts->ssl_crlpath);
|
257
|
+
sock->base.opts.ssl_key = strdupnullok(opts->ssl_key);
|
258
|
+
sock->base.opts.tls_ciphersuites = strdupnullok(opts->tls_ciphersuites);
|
259
|
+
|
260
|
+
sock->fd = -1;
|
261
|
+
sock->addr = NULL;
|
262
|
+
sock->ssl = NULL;
|
263
|
+
|
264
|
+
return (trilogy_sock_t *)sock;
|
265
|
+
}
|
266
|
+
|
267
|
+
int trilogy_sock_resolve(trilogy_sock_t *_sock)
|
268
|
+
{
|
269
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
270
|
+
|
271
|
+
if (sock->base.opts.hostname != NULL) {
|
272
|
+
struct addrinfo hint = {.ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM};
|
273
|
+
|
274
|
+
char port[6];
|
275
|
+
snprintf(port, sizeof(port), "%hu", sock->base.opts.port);
|
276
|
+
|
277
|
+
if (getaddrinfo(sock->base.opts.hostname, port, &hint, &sock->addr) != 0) {
|
278
|
+
return TRILOGY_DNS_ERR;
|
279
|
+
}
|
280
|
+
} else if (sock->base.opts.path != NULL) {
|
281
|
+
struct sockaddr_un *sa;
|
282
|
+
|
283
|
+
if (strlen(sock->base.opts.path) + 1 > sizeof(sa->sun_path)) {
|
284
|
+
goto fail;
|
285
|
+
}
|
286
|
+
|
287
|
+
sa = calloc(1, sizeof(struct sockaddr_un));
|
288
|
+
sa->sun_family = AF_UNIX;
|
289
|
+
strcpy(sa->sun_path, sock->base.opts.path);
|
290
|
+
|
291
|
+
sock->addr = calloc(1, sizeof(struct addrinfo));
|
292
|
+
sock->addr->ai_family = PF_UNIX;
|
293
|
+
sock->addr->ai_socktype = SOCK_STREAM;
|
294
|
+
sock->addr->ai_addr = (struct sockaddr *)sa;
|
295
|
+
sock->addr->ai_addrlen = sizeof(struct sockaddr_un);
|
296
|
+
} else {
|
297
|
+
goto fail;
|
298
|
+
}
|
299
|
+
|
300
|
+
return TRILOGY_OK;
|
301
|
+
|
302
|
+
fail:
|
303
|
+
_cb_raw_close(_sock);
|
304
|
+
return TRILOGY_ERR;
|
305
|
+
}
|
306
|
+
|
307
|
+
static ssize_t ssl_io_return(struct trilogy_sock *sock, ssize_t ret)
|
308
|
+
{
|
309
|
+
if (ret < 0) {
|
310
|
+
int rc = SSL_get_error(sock->ssl, ret);
|
311
|
+
if (rc == SSL_ERROR_WANT_WRITE || rc == SSL_ERROR_WANT_READ) {
|
312
|
+
return (ssize_t)TRILOGY_AGAIN;
|
313
|
+
} else if (rc == SSL_ERROR_SYSCALL && errno != 0) {
|
314
|
+
return (ssize_t)TRILOGY_SYSERR;
|
315
|
+
}
|
316
|
+
return (ssize_t)TRILOGY_OPENSSL_ERR;
|
317
|
+
}
|
318
|
+
return ret;
|
319
|
+
}
|
320
|
+
|
321
|
+
static ssize_t _cb_ssl_read(trilogy_sock_t *_sock, void *buf, size_t nread)
|
322
|
+
{
|
323
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
324
|
+
ssize_t data_read = (ssize_t)SSL_read(sock->ssl, buf, (int)nread);
|
325
|
+
return ssl_io_return(sock, data_read);
|
326
|
+
}
|
327
|
+
|
328
|
+
static ssize_t _cb_ssl_write(trilogy_sock_t *_sock, const void *buf, size_t nwrite)
|
329
|
+
{
|
330
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
331
|
+
ssize_t data_written = (ssize_t)SSL_write(sock->ssl, buf, (int)nwrite);
|
332
|
+
return ssl_io_return(sock, data_written);
|
333
|
+
}
|
334
|
+
|
335
|
+
static int _cb_ssl_shutdown(trilogy_sock_t *_sock)
|
336
|
+
{
|
337
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
338
|
+
|
339
|
+
// If we have an SSL socket, it's invalid here and
|
340
|
+
// we need to close it. The OpenSSL explicitly states
|
341
|
+
// not to call SSL_shutdown on a broken SSL socket.
|
342
|
+
SSL_free(sock->ssl);
|
343
|
+
// Reset the handlers since we tore down SSL, so we
|
344
|
+
// fall back to the regular methods for detecting
|
345
|
+
// we have a closed connection and for the cleanup.
|
346
|
+
sock->base.read_cb = _cb_raw_read;
|
347
|
+
sock->base.write_cb = _cb_raw_write;
|
348
|
+
sock->base.shutdown_cb = _cb_raw_shutdown;
|
349
|
+
sock->base.close_cb = _cb_raw_close;
|
350
|
+
sock->ssl = NULL;
|
351
|
+
|
352
|
+
return _cb_raw_shutdown(_sock);
|
353
|
+
}
|
354
|
+
|
355
|
+
static int _cb_ssl_close(trilogy_sock_t *_sock)
|
356
|
+
{
|
357
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
358
|
+
if (sock->ssl != NULL) {
|
359
|
+
SSL_shutdown(sock->ssl);
|
360
|
+
SSL_free(sock->ssl);
|
361
|
+
sock->ssl = NULL;
|
362
|
+
}
|
363
|
+
return _cb_raw_close(_sock);
|
364
|
+
}
|
365
|
+
|
366
|
+
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
367
|
+
|
368
|
+
static int trilogy_tls_version_map[] = {0, TLS1_VERSION, TLS1_1_VERSION, TLS1_2_VERSION
|
369
|
+
#ifdef TLS1_3_VERSION
|
370
|
+
,
|
371
|
+
TLS1_3_VERSION
|
372
|
+
#else
|
373
|
+
,
|
374
|
+
0
|
375
|
+
#endif
|
376
|
+
};
|
377
|
+
|
378
|
+
int trilogy_set_min_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
|
379
|
+
{
|
380
|
+
int ssl_ver = trilogy_tls_version_map[version];
|
381
|
+
if (ssl_ver == 0) {
|
382
|
+
ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_CTX_SET_SSL_VERSION, SSL_R_UNSUPPORTED_PROTOCOL, NULL, 0);
|
383
|
+
return 0;
|
384
|
+
}
|
385
|
+
return SSL_CTX_set_min_proto_version(ctx, ssl_ver);
|
386
|
+
}
|
387
|
+
|
388
|
+
int trilogy_set_max_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
|
389
|
+
{
|
390
|
+
int ssl_ver = trilogy_tls_version_map[version];
|
391
|
+
if (ssl_ver == 0) {
|
392
|
+
ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_CTX_SET_SSL_VERSION, SSL_R_UNSUPPORTED_PROTOCOL, NULL, 0);
|
393
|
+
return 0;
|
394
|
+
}
|
395
|
+
return SSL_CTX_set_max_proto_version(ctx, ssl_ver);
|
396
|
+
}
|
397
|
+
#else
|
398
|
+
|
399
|
+
int trilogy_set_min_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
|
400
|
+
{
|
401
|
+
long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
|
402
|
+
switch (version) {
|
403
|
+
case TRILOGY_TLS_VERSION_13:
|
404
|
+
opts |= SSL_OP_NO_TLSv1_2;
|
405
|
+
case TRILOGY_TLS_VERSION_12:
|
406
|
+
opts |= SSL_OP_NO_TLSv1_1;
|
407
|
+
case TRILOGY_TLS_VERSION_11:
|
408
|
+
opts |= SSL_OP_NO_TLSv1;
|
409
|
+
default:
|
410
|
+
break;
|
411
|
+
}
|
412
|
+
// No need to handle 1.3 here since OpenSSL < 1.1.0 doesn't support that
|
413
|
+
// anyway
|
414
|
+
SSL_CTX_set_options(ctx, opts);
|
415
|
+
return 1;
|
416
|
+
}
|
417
|
+
|
418
|
+
int trilogy_set_max_proto_version(SSL_CTX *ctx, trilogy_tls_version_t version)
|
419
|
+
{
|
420
|
+
long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
|
421
|
+
switch (version) {
|
422
|
+
case TRILOGY_TLS_VERSION_10:
|
423
|
+
opts |= SSL_OP_NO_TLSv1_1;
|
424
|
+
case TRILOGY_TLS_VERSION_11:
|
425
|
+
opts |= SSL_OP_NO_TLSv1_2;
|
426
|
+
default:
|
427
|
+
break;
|
428
|
+
}
|
429
|
+
SSL_CTX_set_options(ctx, opts);
|
430
|
+
return 1;
|
431
|
+
}
|
432
|
+
#endif
|
433
|
+
|
434
|
+
static SSL_CTX *trilogy_ssl_ctx(const trilogy_sockopt_t *opts)
|
435
|
+
{
|
436
|
+
SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
437
|
+
|
438
|
+
// Now handle all the custom options that we're given.
|
439
|
+
if (opts->tls_min_version != TRILOGY_TLS_VERSION_UNDEF) {
|
440
|
+
if (!trilogy_set_min_proto_version(ssl_ctx, opts->tls_min_version)) {
|
441
|
+
goto fail;
|
442
|
+
}
|
443
|
+
} else {
|
444
|
+
if (!trilogy_set_min_proto_version(ssl_ctx, TRILOGY_TLS_VERSION_12)) {
|
445
|
+
goto fail;
|
446
|
+
}
|
447
|
+
}
|
448
|
+
|
449
|
+
if (opts->tls_max_version != TRILOGY_TLS_VERSION_UNDEF) {
|
450
|
+
if (!trilogy_set_max_proto_version(ssl_ctx, opts->tls_max_version)) {
|
451
|
+
goto fail;
|
452
|
+
}
|
453
|
+
}
|
454
|
+
|
455
|
+
if (opts->ssl_cipher) {
|
456
|
+
if (!SSL_CTX_set_cipher_list(ssl_ctx, opts->ssl_cipher)) {
|
457
|
+
goto fail;
|
458
|
+
}
|
459
|
+
} else {
|
460
|
+
// Use a secure cipher list, based on TLS 1.2 and with authenticated
|
461
|
+
// encryption.
|
462
|
+
if (!SSL_CTX_set_cipher_list(ssl_ctx, "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:"
|
463
|
+
"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:"
|
464
|
+
"ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305")) {
|
465
|
+
goto fail;
|
466
|
+
}
|
467
|
+
}
|
468
|
+
|
469
|
+
#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
|
470
|
+
if (opts->tls_ciphersuites) {
|
471
|
+
if (!SSL_CTX_set_ciphersuites(ssl_ctx, opts->tls_ciphersuites)) {
|
472
|
+
goto fail;
|
473
|
+
}
|
474
|
+
}
|
475
|
+
#endif
|
476
|
+
|
477
|
+
switch (opts->ssl_mode) {
|
478
|
+
case TRILOGY_SSL_DISABLED:
|
479
|
+
break;
|
480
|
+
case TRILOGY_SSL_VERIFY_IDENTITY:
|
481
|
+
case TRILOGY_SSL_VERIFY_CA:
|
482
|
+
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
483
|
+
break;
|
484
|
+
case TRILOGY_SSL_REQUIRED_NOVERIFY:
|
485
|
+
case TRILOGY_SSL_PREFERRED_NOVERIFY:
|
486
|
+
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
|
487
|
+
break;
|
488
|
+
}
|
489
|
+
|
490
|
+
if (opts->ssl_ca || opts->ssl_capath) {
|
491
|
+
if (!SSL_CTX_load_verify_locations(ssl_ctx, opts->ssl_ca, opts->ssl_capath)) {
|
492
|
+
goto fail;
|
493
|
+
}
|
494
|
+
} else {
|
495
|
+
// Use the default systems paths to verify the certificate
|
496
|
+
if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {
|
497
|
+
goto fail;
|
498
|
+
}
|
499
|
+
}
|
500
|
+
|
501
|
+
if (opts->ssl_cert || opts->ssl_key) {
|
502
|
+
if (opts->ssl_key) {
|
503
|
+
if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, opts->ssl_key, SSL_FILETYPE_PEM)) {
|
504
|
+
goto fail;
|
505
|
+
}
|
506
|
+
}
|
507
|
+
if (opts->ssl_cert) {
|
508
|
+
if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, opts->ssl_cert)) {
|
509
|
+
goto fail;
|
510
|
+
}
|
511
|
+
}
|
512
|
+
|
513
|
+
if (!SSL_CTX_check_private_key(ssl_ctx)) {
|
514
|
+
goto fail;
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
518
|
+
if (opts->ssl_crl || opts->ssl_crlpath) {
|
519
|
+
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
|
520
|
+
|
521
|
+
if (!X509_STORE_load_locations(store, opts->ssl_crl, opts->ssl_crlpath)) {
|
522
|
+
goto fail;
|
523
|
+
}
|
524
|
+
|
525
|
+
if (!X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL)) {
|
526
|
+
goto fail;
|
527
|
+
}
|
528
|
+
}
|
529
|
+
return ssl_ctx;
|
530
|
+
|
531
|
+
fail:
|
532
|
+
SSL_CTX_free(ssl_ctx);
|
533
|
+
return NULL;
|
534
|
+
}
|
535
|
+
|
536
|
+
int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
|
537
|
+
{
|
538
|
+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
|
539
|
+
|
540
|
+
SSL_CTX *ctx = trilogy_ssl_ctx(&sock->base.opts);
|
541
|
+
|
542
|
+
if (!ctx) {
|
543
|
+
return TRILOGY_OPENSSL_ERR;
|
544
|
+
}
|
545
|
+
|
546
|
+
sock->ssl = SSL_new(ctx);
|
547
|
+
SSL_CTX_free(ctx);
|
548
|
+
|
549
|
+
if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY && sock->base.opts.hostname == NULL) {
|
550
|
+
// If hostname validation is requested and no hostname provided, treat it as an error.
|
551
|
+
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
552
|
+
ERR_put_error(ERR_LIB_SSL, SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
|
553
|
+
#else
|
554
|
+
ERR_put_error(ERR_LIB_SSL, SSL_F_SSL3_GET_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL, 0);
|
555
|
+
#endif
|
556
|
+
goto fail;
|
557
|
+
}
|
558
|
+
|
559
|
+
// Set the SNI hostname for the connection
|
560
|
+
if (sock->base.opts.hostname != NULL) {
|
561
|
+
if (!SSL_set_tlsext_host_name(sock->ssl, sock->base.opts.hostname)) {
|
562
|
+
goto fail;
|
563
|
+
}
|
564
|
+
}
|
565
|
+
|
566
|
+
// Newer API available since 1.0.2, so we don't have to do manual work.
|
567
|
+
#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
|
568
|
+
if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY) {
|
569
|
+
X509_VERIFY_PARAM *param = SSL_get0_param(sock->ssl);
|
570
|
+
const char *hostname = sock->base.opts.hostname;
|
571
|
+
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
572
|
+
if (!X509_VERIFY_PARAM_set1_host(param, hostname, strlen(hostname))) {
|
573
|
+
goto fail;
|
574
|
+
}
|
575
|
+
}
|
576
|
+
#endif
|
577
|
+
|
578
|
+
if (!SSL_set_fd(sock->ssl, sock->fd))
|
579
|
+
goto fail;
|
580
|
+
|
581
|
+
for (;;) {
|
582
|
+
int ret = SSL_connect(sock->ssl);
|
583
|
+
if (ret == 1) {
|
584
|
+
#if OPENSSL_VERSION_NUMBER < 0x1000200fL
|
585
|
+
if (sock->base.opts.ssl_mode == TRILOGY_SSL_VERIFY_IDENTITY) {
|
586
|
+
if (validate_hostname(sock->base.opts.hostname, SSL_get_peer_certificate(sock->ssl)) != MatchFound) {
|
587
|
+
// Fake the error message to be the same as it would be on 1.0.2 and newer.
|
588
|
+
ERR_put_error(ERR_LIB_SSL, SSL_F_SSL3_GET_SERVER_CERTIFICATE, SSL_R_CERTIFICATE_VERIFY_FAILED, NULL,
|
589
|
+
0);
|
590
|
+
goto fail;
|
591
|
+
}
|
592
|
+
}
|
593
|
+
#endif
|
594
|
+
break;
|
595
|
+
}
|
596
|
+
|
597
|
+
switch (SSL_get_error(sock->ssl, ret)) {
|
598
|
+
case SSL_ERROR_WANT_READ:
|
599
|
+
if (trilogy_sock_wait_read(_sock) < 0)
|
600
|
+
goto fail;
|
601
|
+
break;
|
602
|
+
|
603
|
+
case SSL_ERROR_WANT_WRITE:
|
604
|
+
if (trilogy_sock_wait_write(_sock) < 0)
|
605
|
+
goto fail;
|
606
|
+
break;
|
607
|
+
|
608
|
+
default:
|
609
|
+
goto fail;
|
610
|
+
}
|
611
|
+
}
|
612
|
+
|
613
|
+
sock->base.read_cb = _cb_ssl_read;
|
614
|
+
sock->base.write_cb = _cb_ssl_write;
|
615
|
+
sock->base.shutdown_cb = _cb_ssl_shutdown;
|
616
|
+
sock->base.close_cb = _cb_ssl_close;
|
617
|
+
return TRILOGY_OK;
|
618
|
+
|
619
|
+
fail:
|
620
|
+
SSL_free(sock->ssl);
|
621
|
+
sock->ssl = NULL;
|
622
|
+
return TRILOGY_OPENSSL_ERR;
|
623
|
+
}
|