xref: /unit/src/nxt_cyassl.c (revision 564:762f8c976ead)
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