1
2 /*
3 * Copyright (C) NGINX, Inc.
4 * Copyright (C) Igor Sysoev
5 */
6
7 #include <nxt_main.h>
8 #include <cyassl/ssl.h>
9 #include <cyassl/error-ssl.h>
10
11
12 typedef struct {
13 CYASSL *session;
14
15 int ssl_error;
16 uint8_t times; /* 2 bits */
17
18 nxt_buf_mem_t buffer;
19 } nxt_cyassl_conn_t;
20
21
22 static nxt_int_t nxt_cyassl_server_init(nxt_ssltls_conf_t *conf);
23 static void nxt_cyassl_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
24 nxt_event_conn_t *c);
25 static void nxt_cyassl_session_cleanup(void *data);
26 static int nxt_cyassl_io_recv(CYASSL *ssl, char *buf, int size, void *data);
27 static int nxt_cyassl_io_send(CYASSL *ssl, char *buf, int size, void *data);
28 static void nxt_cyassl_conn_handshake(nxt_thread_t *thr, void *obj, void *data);
29 static void nxt_cyassl_conn_io_read(nxt_thread_t *thr, void *obj, void *data);
30 static void nxt_cyassl_conn_io_shutdown(nxt_thread_t *thr, void *obj,
31 void *data);
32 static ssize_t nxt_cyassl_conn_io_write_chunk(nxt_thread_t *thr,
33 nxt_event_conn_t *c, nxt_buf_t *b, size_t limit);
34 static ssize_t nxt_cyassl_conn_io_send(nxt_event_conn_t *c, void *buf,
35 size_t size);
36 static nxt_int_t nxt_cyassl_conn_test_error(nxt_thread_t *thr,
37 nxt_event_conn_t *c, int err, nxt_work_handler_t handler);
38 static void nxt_cdecl nxt_cyassl_conn_error(nxt_event_conn_t *c, nxt_err_t err,
39 const char *fmt, ...);
40 static nxt_uint_t nxt_cyassl_log_error_level(nxt_event_conn_t *c, nxt_err_t err,
41 int ssl_error);
42 static void nxt_cdecl nxt_cyassl_log_error(nxt_uint_t level, nxt_log_t *log,
43 int ret, const char *fmt, ...);
44 static u_char *nxt_cyassl_copy_error(int err, u_char *p, u_char *end);
45
46
47 const nxt_ssltls_lib_t nxt_cyassl_lib = {
48 nxt_cyassl_server_init,
49 NULL,
50 };
51
52
53 static nxt_event_conn_io_t nxt_cyassl_event_conn_io = {
54 NULL,
55 NULL,
56
57 nxt_cyassl_conn_io_read,
58 NULL,
59 NULL,
60
61 nxt_event_conn_io_write,
62 nxt_cyassl_conn_io_write_chunk,
63 NULL,
64 NULL,
65 nxt_cyassl_conn_io_send,
66
67 nxt_cyassl_conn_io_shutdown,
68 };
69
70
71 static nxt_int_t
nxt_cyassl_start(void)72 nxt_cyassl_start(void)
73 {
74 int err;
75 nxt_thread_t *thr;
76 static nxt_bool_t started;
77
78 if (nxt_fast_path(started)) {
79 return NXT_OK;
80 }
81
82 started = 1;
83
84 thr = nxt_thread();
85
86 /* TODO: CyaSSL_Cleanup() */
87
88 err = CyaSSL_Init();
89 if (err != SSL_SUCCESS) {
90 nxt_cyassl_log_error(NXT_LOG_ALERT, thr->log, err,
91 "CyaSSL_Init() failed");
92 return NXT_ERROR;
93 }
94
95 nxt_thread_log_error(NXT_LOG_INFO, "CyaSSL version: %s",
96 LIBCYASSL_VERSION_STRING);
97
98 /* CyaSSL_SetLoggingCb */
99 /* CyaSSL_SetAllocators */
100
101 return NXT_OK;
102 }
103
104
105 static nxt_int_t
nxt_cyassl_server_init(nxt_ssltls_conf_t * conf)106 nxt_cyassl_server_init(nxt_ssltls_conf_t *conf)
107 {
108 int err;
109 char *certificate, *key;
110 CYASSL_CTX *ctx;
111 nxt_thread_t *thr;
112
113 thr = nxt_thread();
114
115 if (nxt_slow_path(nxt_cyassl_start() != NXT_OK)) {
116 return NXT_ERROR;
117 }
118
119 ctx = CyaSSL_CTX_new(CyaSSLv23_server_method());
120 if (ctx == NULL) {
121 nxt_cyassl_log_error(NXT_LOG_ALERT, thr->log, 0,
122 "CyaSSL_CTX_new() failed");
123 return NXT_ERROR;
124 }
125
126 conf->ctx = ctx;
127 conf->conn_init = nxt_cyassl_conn_init;
128
129 certificate = conf->certificate;
130
131 err = CyaSSL_CTX_use_certificate_file(ctx, certificate, SSL_FILETYPE_PEM);
132 if (err != SSL_SUCCESS) {
133 nxt_cyassl_log_error(NXT_LOG_ALERT, thr->log, err,
134 "CyaSSL_CTX_use_certificate_file(\"%s\") failed",
135 certificate);
136 goto fail;
137 }
138
139 key = conf->certificate_key;
140
141 err = CyaSSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM);
142 if (err != SSL_SUCCESS) {
143 nxt_cyassl_log_error(NXT_LOG_ALERT, thr->log, err,
144 "CyaSSL_CTX_use_PrivateKey_file(\"%s\") failed",
145 key);
146 goto fail;
147 }
148
149 if (conf->ciphers != NULL) {
150 err = CyaSSL_CTX_set_cipher_list(ctx, conf->ciphers);
151 if (err != SSL_SUCCESS) {
152 nxt_cyassl_log_error(NXT_LOG_ALERT, thr->log, err,
153 "CyaSSL_CTX_set_cipher_list(\"%s\") failed",
154 conf->ciphers);
155 goto fail;
156 }
157 }
158
159 /* TODO: ca_certificate */
160
161 CyaSSL_SetIORecv(ctx, nxt_cyassl_io_recv);
162 CyaSSL_SetIOSend(ctx, nxt_cyassl_io_send);
163
164 return NXT_OK;
165
166 fail:
167
168 CyaSSL_CTX_free(ctx);
169
170 return NXT_ERROR;
171 }
172
173
174 static void
nxt_cyassl_conn_init(nxt_thread_t * thr,nxt_ssltls_conf_t * conf,nxt_event_conn_t * c)175 nxt_cyassl_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
176 nxt_event_conn_t *c)
177 {
178 CYASSL *s;
179 CYASSL_CTX *ctx;
180 nxt_cyassl_conn_t *ssltls;
181 nxt_mem_pool_cleanup_t *mpcl;
182
183 nxt_log_debug(c->socket.log, "cyassl conn init");
184
185 ssltls = nxt_mp_zget(c->mem_pool, sizeof(nxt_cyassl_conn_t));
186 if (ssltls == NULL) {
187 goto fail;
188 }
189
190 c->u.ssltls = ssltls;
191 nxt_buf_mem_set_size(&ssltls->buffer, conf->buffer_size);
192
193 mpcl = nxt_mem_pool_cleanup(c->mem_pool, 0);
194 if (mpcl == NULL) {
195 goto fail;
196 }
197
198 ctx = conf->ctx;
199
200 s = CyaSSL_new(ctx);
201 if (s == NULL) {
202 nxt_cyassl_log_error(NXT_LOG_ALERT, c->socket.log, 0,
203 "CyaSSL_new() failed");
204 goto fail;
205 }
206
207 ssltls->session = s;
208 mpcl->handler = nxt_cyassl_session_cleanup;
209 mpcl->data = ssltls;
210
211 CyaSSL_SetIOReadCtx(s, c);
212 CyaSSL_SetIOWriteCtx(s, c);
213
214 c->io = &nxt_cyassl_event_conn_io;
215 c->sendfile = NXT_CONN_SENDFILE_OFF;
216
217 nxt_cyassl_conn_handshake(thr, c, c->socket.data);
218 return;
219
220 fail:
221
222 nxt_event_conn_io_handle(thr, c->read_work_queue,
223 c->read_state->error_handler, c, c->socket.data);
224 }
225
226
227 static void
nxt_cyassl_session_cleanup(void * data)228 nxt_cyassl_session_cleanup(void *data)
229 {
230 nxt_cyassl_conn_t *ssltls;
231
232 ssltls = data;
233
234 nxt_thread_log_debug("cyassl session cleanup");
235
236 nxt_free(ssltls->buffer.start);
237
238 CyaSSL_free(ssltls->session);
239 }
240
241
242 static int
nxt_cyassl_io_recv(CYASSL * ssl,char * buf,int size,void * data)243 nxt_cyassl_io_recv(CYASSL *ssl, char *buf, int size, void *data)
244 {
245 ssize_t n;
246 nxt_thread_t *thr;
247 nxt_event_conn_t *c;
248
249 c = data;
250 thr = nxt_thread();
251
252 n = thr->engine->event->io->recv(c, (u_char *) buf, size, 0);
253
254 if (n > 0) {
255 return n;
256 }
257
258 if (n == 0) {
259 return CYASSL_CBIO_ERR_CONN_CLOSE;
260 }
261
262 if (n == NXT_AGAIN) {
263 return CYASSL_CBIO_ERR_WANT_READ;
264 }
265
266 return CYASSL_CBIO_ERR_GENERAL;
267 }
268
269
270 static int
nxt_cyassl_io_send(CYASSL * ssl,char * buf,int size,void * data)271 nxt_cyassl_io_send(CYASSL *ssl, char *buf, int size, void *data)
272 {
273 ssize_t n;
274 nxt_thread_t *thr;
275 nxt_event_conn_t *c;
276
277 c = data;
278 thr = nxt_thread();
279
280 n = thr->engine->event->io->send(c, (u_char *) buf, size);
281
282 if (n > 0) {
283 return n;
284 }
285
286 if (n == NXT_AGAIN) {
287 return CYASSL_CBIO_ERR_WANT_WRITE;
288 }
289
290 return CYASSL_CBIO_ERR_GENERAL;
291 }
292
293
294 static void
nxt_cyassl_conn_handshake(nxt_thread_t * thr,void * obj,void * data)295 nxt_cyassl_conn_handshake(nxt_thread_t *thr, void *obj, void *data)
296 {
297 int ret;
298 nxt_int_t n;
299 nxt_err_t err;
300 nxt_event_conn_t *c;
301 nxt_cyassl_conn_t *ssltls;
302
303 c = obj;
304 ssltls = c->u.ssltls;
305
306 nxt_log_debug(thr->log, "cyassl conn handshake: %d", ssltls->times);
307
308 /* "ssltls->times == 1" is suitable to run CyaSSL_negotiate() in job. */
309
310 ret = CyaSSL_negotiate(ssltls->session);
311
312 err = (ret != 0) ? nxt_socket_errno : 0;
313
314 nxt_thread_time_debug_update(thr);
315
316 nxt_log_debug(thr->log, "CyaSSL_negotiate(%d): %d", c->socket.fd, ret);
317
318 if (ret == 0) {
319 nxt_cyassl_conn_io_read(thr, c, data);
320 return;
321 }
322
323 n = nxt_cyassl_conn_test_error(thr, c, ret, nxt_cyassl_conn_handshake);
324
325 if (n == NXT_ERROR) {
326 nxt_cyassl_conn_error(c, err, "CyaSSL_negotiate(%d) failed",
327 c->socket.fd);
328
329 nxt_event_conn_io_handle(thr, c->read_work_queue,
330 c->read_state->error_handler, c, data);
331
332 } else if (ssltls->ssl_error == SSL_ERROR_WANT_READ && ssltls->times < 2) {
333 ssltls->times++;
334 }
335 }
336
337
338 static void
nxt_cyassl_conn_io_read(nxt_thread_t * thr,void * obj,void * data)339 nxt_cyassl_conn_io_read(nxt_thread_t *thr, void *obj, void *data)
340 {
341 int ret;
342 nxt_buf_t *b;
343 nxt_err_t err;
344 nxt_int_t n;
345 nxt_event_conn_t *c;
346 nxt_cyassl_conn_t *ssltls;
347 nxt_work_handler_t handler;
348
349 c = obj;
350
351 nxt_log_debug(thr->log, "cyassl conn read");
352
353 handler = c->read_state->ready_handler;
354 b = c->read;
355
356 /* b == NULL is used to test descriptor readiness. */
357
358 if (b != NULL) {
359 ssltls = c->u.ssltls;
360
361 ret = CyaSSL_read(ssltls->session, b->mem.free,
362 b->mem.end - b->mem.free);
363
364 err = (ret <= 0) ? nxt_socket_errno : 0;
365
366 nxt_log_debug(thr->log, "CyaSSL_read(%d, %p, %uz): %d",
367 c->socket.fd, b->mem.free, b->mem.end - b->mem.free, ret);
368
369 if (ret > 0) {
370 /* c->socket.read_ready is kept. */
371 b->mem.free += ret;
372 handler = c->read_state->ready_handler;
373
374 } else {
375 n = nxt_cyassl_conn_test_error(thr, c, ret,
376 nxt_cyassl_conn_io_read);
377
378 if (nxt_fast_path(n != NXT_ERROR)) {
379 return;
380 }
381
382 nxt_cyassl_conn_error(c, err, "CyaSSL_read(%d, %p, %uz) failed",
383 c->socket.fd, b->mem.free,
384 b->mem.end - b->mem.free);
385
386 handler = c->read_state->error_handler;
387 }
388 }
389
390 nxt_event_conn_io_handle(thr, c->read_work_queue, handler, c, data);
391 }
392
393
394 static ssize_t
nxt_cyassl_conn_io_write_chunk(nxt_thread_t * thr,nxt_event_conn_t * c,nxt_buf_t * b,size_t limit)395 nxt_cyassl_conn_io_write_chunk(nxt_thread_t *thr, nxt_event_conn_t *c,
396 nxt_buf_t *b, size_t limit)
397 {
398 nxt_cyassl_conn_t *ssltls;
399
400 nxt_log_debug(thr->log, "cyassl conn write chunk");
401
402 ssltls = c->u.ssltls;
403
404 return nxt_sendbuf_copy_coalesce(c, &ssltls->buffer, b, limit);
405 }
406
407
408 static ssize_t
nxt_cyassl_conn_io_send(nxt_event_conn_t * c,void * buf,size_t size)409 nxt_cyassl_conn_io_send(nxt_event_conn_t *c, void *buf, size_t size)
410 {
411 int ret;
412 nxt_err_t err;
413 nxt_int_t n;
414 nxt_cyassl_conn_t *ssltls;
415
416 nxt_log_debug(c->socket.log, "cyassl send");
417
418 ssltls = c->u.ssltls;
419
420 ret = CyaSSL_write(ssltls->session, buf, size);
421
422 if (ret <= 0) {
423 err = nxt_socket_errno;
424 c->socket.error = err;
425
426 } else {
427 err = 0;
428 }
429
430 nxt_log_debug(c->socket.log, "CyaSSL_write(%d, %p, %uz): %d",
431 c->socket.fd, buf, size, ret);
432
433 if (ret > 0) {
434 return ret;
435 }
436
437 n = nxt_cyassl_conn_test_error(nxt_thread(), c, ret,
438 nxt_event_conn_io_write);
439
440 if (nxt_slow_path(n == NXT_ERROR)) {
441 nxt_cyassl_conn_error(c, err, "CyaSSL_write(%d, %p, %uz) failed",
442 c->socket.fd, buf, size);
443 }
444
445 return n;
446 }
447
448
449 static void
nxt_cyassl_conn_io_shutdown(nxt_thread_t * thr,void * obj,void * data)450 nxt_cyassl_conn_io_shutdown(nxt_thread_t *thr, void *obj, void *data)
451 {
452 int ret;
453 nxt_event_conn_t *c;
454 nxt_cyassl_conn_t *ssltls;
455
456 c = obj;
457
458 nxt_log_debug(thr->log, "cyassl conn shutdown");
459
460 ssltls = c->u.ssltls;
461
462 ret = CyaSSL_shutdown(ssltls->session);
463
464 nxt_log_debug(thr->log, "CyaSSL_shutdown(%d): %d", c->socket.fd, ret);
465
466 if (nxt_slow_path(ret != SSL_SUCCESS)) {
467 nxt_cyassl_conn_error(c, 0, "CyaSSL_shutdown(%d) failed", c->socket.fd);
468 }
469
470 nxt_event_conn_io_handle(thr, c->write_work_queue,
471 c->write_state->close_handler, c, data);
472 }
473
474
475 static nxt_int_t
nxt_cyassl_conn_test_error(nxt_thread_t * thr,nxt_event_conn_t * c,int ret,nxt_work_handler_t handler)476 nxt_cyassl_conn_test_error(nxt_thread_t *thr, nxt_event_conn_t *c, int ret,
477 nxt_work_handler_t handler)
478 {
479 nxt_work_queue_t *wq;
480 nxt_cyassl_conn_t *ssltls;
481
482 ssltls = c->u.ssltls;
483 ssltls->ssl_error = CyaSSL_get_error(ssltls->session, ret);
484
485 nxt_log_debug(thr->log, "CyaSSL_get_error(): %d", ssltls->ssl_error);
486
487 switch (ssltls->ssl_error) {
488
489 case SSL_ERROR_WANT_READ:
490 nxt_event_fd_block_write(thr->engine, &c->socket);
491
492 c->socket.read_ready = 0;
493 c->socket.read_handler = handler;
494
495 if (nxt_event_fd_is_disabled(c->socket.read)) {
496 nxt_event_fd_enable_read(thr->engine, &c->socket);
497 }
498
499 return NXT_AGAIN;
500
501 case SSL_ERROR_WANT_WRITE:
502 nxt_event_fd_block_read(thr->engine, &c->socket);
503
504 c->socket.write_ready = 0;
505 c->socket.write_handler = handler;
506
507 if (nxt_event_fd_is_disabled(c->socket.write)) {
508 nxt_event_fd_enable_write(thr->engine, &c->socket);
509 }
510
511 return NXT_AGAIN;
512
513 case SSL_ERROR_ZERO_RETURN:
514 /* A "close notify" alert */
515
516 if (c->read_state != NULL) {
517 wq = c->read_work_queue;
518 handler = c->read_state->close_handler;
519
520 } else {
521 wq = c->write_work_queue;
522 handler = c->write_state->close_handler;
523 }
524
525 nxt_event_conn_io_handle(thr, wq, handler, c, c->socket.data);
526
527 return 0;
528
529 default:
530 return NXT_ERROR;
531 }
532 }
533
534
535 static void nxt_cdecl
nxt_cyassl_conn_error(nxt_event_conn_t * c,nxt_err_t err,const char * fmt,...)536 nxt_cyassl_conn_error(nxt_event_conn_t *c, nxt_err_t err, const char *fmt, ...)
537 {
538 u_char *p, *end;
539 va_list args;
540 nxt_uint_t level;
541 nxt_cyassl_conn_t *ssltls;
542 u_char msg[NXT_MAX_ERROR_STR];
543
544 ssltls = c->u.ssltls;
545
546 level = nxt_cyassl_log_error_level(c, err, ssltls->ssl_error);
547
548 if (nxt_log_level_enough(c->socket.log, level)) {
549
550 end = msg + sizeof(msg);
551
552 va_start(args, fmt);
553 p = nxt_vsprintf(msg, end, fmt, args);
554 va_end(args);
555
556 if (err != 0) {
557 p = nxt_sprintf(p, end, " %E", err);
558 }
559
560 p = nxt_cyassl_copy_error(ssltls->ssl_error, p, end);
561
562 nxt_log_error(level, c->socket.log, "%*s", p - msg, msg);
563 }
564 }
565
566
567 static nxt_uint_t
nxt_cyassl_log_error_level(nxt_event_conn_t * c,nxt_err_t err,int ssl_error)568 nxt_cyassl_log_error_level(nxt_event_conn_t *c, nxt_err_t err, int ssl_error)
569 {
570 switch (ssl_error) {
571
572 case SOCKET_ERROR_E: /* -208 */
573 case MATCH_SUITE_ERROR: /* -261 */
574 break;
575
576 default:
577 return NXT_LOG_ALERT;
578 }
579
580 return NXT_LOG_INFO;
581 }
582
583
584 static void nxt_cdecl
nxt_cyassl_log_error(nxt_uint_t level,nxt_log_t * log,int err,const char * fmt,...)585 nxt_cyassl_log_error(nxt_uint_t level, nxt_log_t *log, int err,
586 const char *fmt, ...)
587 {
588 u_char *p, *end;
589 va_list args;
590 u_char msg[NXT_MAX_ERROR_STR];
591
592 if (nxt_log_level_enough(log, level)) {
593
594 end = msg + sizeof(msg);
595
596 va_start(args, fmt);
597 p = nxt_vsprintf(msg, end, fmt, args);
598 va_end(args);
599
600 p = nxt_cyassl_copy_error(err, p, end);
601
602 nxt_log_error(level, log, "%*s", p - msg, msg);
603 }
604 }
605
606
607 static u_char *
nxt_cyassl_copy_error(int err,u_char * p,u_char * end)608 nxt_cyassl_copy_error(int err, u_char *p, u_char *end)
609 {
610 p = nxt_sprintf(p, end, " (SSL:%d ", err);
611
612 CyaSSL_ERR_error_string_n(err, (char *) p, end - p);
613
614 p += nxt_strlen(p);
615
616 if (p < end) {
617 *p++ = ')';
618 }
619
620 return p;
621 }
622