Line data Source code
1 :
2 : /*
3 : * Copyright (C) Yichun Zhang (agentzh)
4 : */
5 :
6 :
7 : #ifndef DDEBUG
8 : #define DDEBUG 0
9 : #endif
10 : #include "ddebug.h"
11 :
12 :
13 : #if (NGX_HTTP_SSL)
14 :
15 :
16 : #include "ngx_http_lua_cache.h"
17 : #include "ngx_http_lua_initworkerby.h"
18 : #include "ngx_http_lua_util.h"
19 : #include "ngx_http_ssl_module.h"
20 : #include "ngx_http_lua_contentby.h"
21 : #include "ngx_http_lua_ssl_session_fetchby.h"
22 : #include "ngx_http_lua_ssl.h"
23 : #include "ngx_http_lua_directive.h"
24 :
25 :
26 : /* Lua SSL cached session loading routines */
27 : static void ngx_http_lua_ssl_sess_fetch_done(void *data);
28 : static void ngx_http_lua_ssl_sess_fetch_aborted(void *data);
29 : static u_char *ngx_http_lua_log_ssl_sess_fetch_error(ngx_log_t *log,
30 : u_char *buf, size_t len);
31 : static ngx_int_t ngx_http_lua_ssl_sess_fetch_by_chunk(lua_State *L,
32 : ngx_http_request_t *r);
33 :
34 :
35 : /* load Lua code from a file for fetching cached SSL session */
36 : ngx_int_t
37 0 : ngx_http_lua_ssl_sess_fetch_handler_file(ngx_http_request_t *r,
38 : ngx_http_lua_srv_conf_t *lscf, lua_State *L)
39 : {
40 : ngx_int_t rc;
41 :
42 0 : rc = ngx_http_lua_cache_loadfile(r->connection->log, L,
43 0 : lscf->srv.ssl_sess_fetch_src.data,
44 0 : lscf->srv.ssl_sess_fetch_src_key);
45 0 : if (rc != NGX_OK) {
46 0 : return rc;
47 : }
48 :
49 : /* make sure we have a valid code chunk */
50 0 : ngx_http_lua_assert(lua_isfunction(L, -1));
51 :
52 0 : return ngx_http_lua_ssl_sess_fetch_by_chunk(L, r);
53 : }
54 :
55 :
56 : /* load lua code from an inline snippet for fetching cached SSL session */
57 : ngx_int_t
58 0 : ngx_http_lua_ssl_sess_fetch_handler_inline(ngx_http_request_t *r,
59 : ngx_http_lua_srv_conf_t *lscf, lua_State *L)
60 : {
61 : ngx_int_t rc;
62 :
63 0 : rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
64 0 : lscf->srv.ssl_sess_fetch_src.data,
65 : lscf->srv.ssl_sess_fetch_src.len,
66 0 : lscf->srv.ssl_sess_fetch_src_key,
67 : "=ssl_session_fetch_by_lua_block");
68 0 : if (rc != NGX_OK) {
69 0 : return rc;
70 : }
71 :
72 : /* make sure we have a valid code chunk */
73 0 : ngx_http_lua_assert(lua_isfunction(L, -1));
74 :
75 0 : return ngx_http_lua_ssl_sess_fetch_by_chunk(L, r);
76 : }
77 :
78 :
79 : char *
80 0 : ngx_http_lua_ssl_sess_fetch_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
81 : void *conf)
82 : {
83 : char *rv;
84 : ngx_conf_t save;
85 :
86 0 : save = *cf;
87 0 : cf->handler = ngx_http_lua_ssl_sess_fetch_by_lua;
88 0 : cf->handler_conf = conf;
89 :
90 0 : rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
91 :
92 0 : *cf = save;
93 :
94 0 : return rv;
95 : }
96 :
97 :
98 : /* conf parser for directive ssl_session_fetch_by_lua */
99 : char *
100 0 : ngx_http_lua_ssl_sess_fetch_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
101 : void *conf)
102 : {
103 : u_char *p;
104 : u_char *name;
105 : ngx_str_t *value;
106 0 : ngx_http_lua_srv_conf_t *lscf = conf;
107 :
108 : dd("enter");
109 :
110 : /* must specify a content handler */
111 0 : if (cmd->post == NULL) {
112 0 : return NGX_CONF_ERROR;
113 : }
114 :
115 0 : if (lscf->srv.ssl_sess_fetch_handler) {
116 0 : return "is duplicate";
117 : }
118 :
119 0 : if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {
120 0 : return NGX_CONF_ERROR;
121 : }
122 :
123 0 : value = cf->args->elts;
124 :
125 0 : lscf->srv.ssl_sess_fetch_handler =
126 0 : (ngx_http_lua_srv_conf_handler_pt) cmd->post;
127 :
128 0 : if (cmd->post == ngx_http_lua_ssl_sess_fetch_handler_file) {
129 : /* Lua code in an external file */
130 :
131 0 : name = ngx_http_lua_rebase_path(cf->pool, value[1].data,
132 0 : value[1].len);
133 0 : if (name == NULL) {
134 0 : return NGX_CONF_ERROR;
135 : }
136 :
137 0 : lscf->srv.ssl_sess_fetch_src.data = name;
138 0 : lscf->srv.ssl_sess_fetch_src.len = ngx_strlen(name);
139 :
140 0 : p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
141 0 : if (p == NULL) {
142 0 : return NGX_CONF_ERROR;
143 : }
144 :
145 0 : lscf->srv.ssl_sess_fetch_src_key = p;
146 :
147 0 : p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
148 0 : p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
149 0 : *p = '\0';
150 :
151 : } else {
152 : /* inlined Lua code */
153 :
154 0 : lscf->srv.ssl_sess_fetch_src = value[1];
155 :
156 0 : p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
157 0 : if (p == NULL) {
158 0 : return NGX_CONF_ERROR;
159 : }
160 :
161 0 : lscf->srv.ssl_sess_fetch_src_key = p;
162 :
163 0 : p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
164 0 : p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
165 0 : *p = '\0';
166 : }
167 :
168 0 : return NGX_CONF_OK;
169 : }
170 :
171 :
172 : /* cached session fetching callback to be set with SSL_CTX_sess_set_get_cb */
173 : ngx_ssl_session_t *
174 0 : ngx_http_lua_ssl_sess_fetch_handler(ngx_ssl_conn_t *ssl_conn, u_char *id,
175 : int len, int *copy)
176 : {
177 : lua_State *L;
178 : ngx_int_t rc;
179 0 : ngx_connection_t *c, *fc = NULL;
180 0 : ngx_http_request_t *r = NULL;
181 : ngx_pool_cleanup_t *cln;
182 : ngx_http_connection_t *hc;
183 : ngx_http_lua_ssl_ctx_t *cctx;
184 : ngx_http_lua_srv_conf_t *lscf;
185 : ngx_http_core_loc_conf_t *clcf;
186 :
187 : /* set copy to 0 as we expect OpenSSL to handle
188 : * the memory of returned session */
189 :
190 0 : *copy = 0;
191 :
192 0 : c = ngx_ssl_get_connection(ssl_conn);
193 :
194 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
195 : "ssl session fetch: connection reusable: %ud", c->reusable);
196 :
197 0 : cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
198 :
199 : dd("ssl sess_fetch handler, sess_fetch-ctx=%p", cctx);
200 :
201 0 : if (cctx && cctx->entered_sess_fetch_handler) {
202 : /* not the first time */
203 :
204 : dd("here: %d", (int) cctx->entered_sess_fetch_handler);
205 :
206 0 : if (cctx->done) {
207 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
208 : "ssl_session_fetch_by_lua*: "
209 : "sess get cb exit code: %d",
210 : cctx->exit_code);
211 :
212 : dd("lua ssl sess_fetch done, finally");
213 0 : return cctx->session;
214 : }
215 :
216 : #ifdef SSL_ERROR_PENDING_SESSION
217 : return SSL_magic_pending_session_ptr();
218 : #else
219 0 : ngx_log_error(NGX_LOG_CRIT, c->log, 0,
220 : "lua: cannot yield in sess get cb: "
221 : "missing async sess get cb support in OpenSSL");
222 0 : return NULL;
223 : #endif
224 : }
225 :
226 : dd("first time");
227 :
228 0 : ngx_reusable_connection(c, 0);
229 :
230 0 : hc = c->data;
231 :
232 0 : fc = ngx_http_lua_create_fake_connection(NULL);
233 0 : if (fc == NULL) {
234 0 : goto failed;
235 : }
236 :
237 0 : fc->log->handler = ngx_http_lua_log_ssl_sess_fetch_error;
238 0 : fc->log->data = fc;
239 :
240 0 : fc->addr_text = c->addr_text;
241 0 : fc->listening = c->listening;
242 :
243 0 : r = ngx_http_lua_create_fake_request(fc);
244 0 : if (r == NULL) {
245 0 : goto failed;
246 : }
247 :
248 0 : r->main_conf = hc->conf_ctx->main_conf;
249 0 : r->srv_conf = hc->conf_ctx->srv_conf;
250 0 : r->loc_conf = hc->conf_ctx->loc_conf;
251 :
252 0 : fc->log->file = c->log->file;
253 0 : fc->log->log_level = c->log->log_level;
254 0 : fc->ssl = c->ssl;
255 :
256 0 : clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
257 :
258 : #if defined(nginx_version) && nginx_version >= 1003014
259 :
260 : # if nginx_version >= 1009000
261 :
262 0 : ngx_set_connection_log(fc, clcf->error_log);
263 :
264 : # else
265 :
266 : ngx_http_set_connection_log(fc, clcf->error_log);
267 :
268 : # endif
269 :
270 : #else
271 :
272 : fc->log->file = clcf->error_log->file;
273 :
274 : if (!(fc->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
275 : fc->log->log_level = clcf->error_log->log_level;
276 : }
277 :
278 : #endif
279 :
280 0 : if (cctx == NULL) {
281 0 : cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));
282 0 : if (cctx == NULL) {
283 0 : goto failed; /* error */
284 : }
285 : }
286 :
287 0 : cctx->exit_code = 1; /* successful by default */
288 0 : cctx->connection = c;
289 0 : cctx->request = r;
290 0 : cctx->session_id.data = id;
291 0 : cctx->session_id.len = len;
292 0 : cctx->entered_sess_fetch_handler = 1;
293 0 : cctx->done = 0;
294 :
295 : dd("setting cctx = %p", cctx);
296 :
297 0 : if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx)
298 : == 0)
299 : {
300 0 : ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
301 0 : goto failed;
302 : }
303 :
304 0 : lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);
305 :
306 : /* TODO honor lua_code_cache off */
307 0 : L = ngx_http_lua_get_lua_vm(r, NULL);
308 :
309 0 : c->log->action = "fetching SSL session by lua";
310 :
311 0 : rc = lscf->srv.ssl_sess_fetch_handler(r, lscf, L);
312 :
313 0 : if (rc >= NGX_OK || rc == NGX_ERROR) {
314 0 : cctx->done = 1;
315 :
316 0 : if (cctx->cleanup) {
317 0 : *cctx->cleanup = NULL;
318 : }
319 :
320 0 : ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
321 : "ssl_session_fetch_by_lua*: handler return value: %i, "
322 : "sess get cb exit code: %d", rc, cctx->exit_code);
323 :
324 0 : c->log->action = "SSL handshaking";
325 0 : return cctx->session;
326 : }
327 :
328 : /* rc == NGX_DONE */
329 :
330 0 : cln = ngx_pool_cleanup_add(fc->pool, 0);
331 0 : if (cln == NULL) {
332 0 : goto failed;
333 : }
334 :
335 0 : cln->handler = ngx_http_lua_ssl_sess_fetch_done;
336 0 : cln->data = cctx;
337 :
338 0 : if (cctx->cleanup == NULL) {
339 : /* we only want exactly one cleanup handler to be registered with the
340 : * connection to clean up cctx when connection is aborted */
341 0 : cln = ngx_pool_cleanup_add(c->pool, 0);
342 0 : if (cln == NULL) {
343 0 : goto failed;
344 : }
345 :
346 0 : cln->data = cctx;
347 0 : cctx->cleanup = &cln->handler;
348 : }
349 :
350 0 : *cctx->cleanup = ngx_http_lua_ssl_sess_fetch_aborted;
351 :
352 : #ifdef SSL_ERROR_PENDING_SESSION
353 : return SSL_magic_pending_session_ptr();
354 : #else
355 0 : ngx_log_error(NGX_LOG_CRIT, c->log, 0,
356 : "lua: cannot yield in sess get cb: "
357 : "missing async sess get cb support in OpenSSL");
358 :
359 : /* fall through to the "failed" label below */
360 : #endif
361 :
362 0 : failed:
363 :
364 0 : if (r && r->pool) {
365 0 : ngx_http_lua_free_fake_request(r);
366 : }
367 :
368 0 : if (fc) {
369 0 : ngx_http_lua_close_fake_connection(fc);
370 : }
371 :
372 0 : return NULL;
373 : }
374 :
375 :
376 : static void
377 0 : ngx_http_lua_ssl_sess_fetch_done(void *data)
378 : {
379 : ngx_connection_t *c;
380 0 : ngx_http_lua_ssl_ctx_t *cctx = data;
381 :
382 : dd("lua ssl sess_fetch done");
383 :
384 0 : if (cctx->aborted) {
385 0 : return;
386 : }
387 :
388 0 : ngx_http_lua_assert(cctx->done == 0);
389 :
390 0 : cctx->done = 1;
391 :
392 0 : if (cctx->cleanup) {
393 0 : *cctx->cleanup = NULL;
394 : }
395 :
396 0 : c = cctx->connection;
397 :
398 0 : c->log->action = "SSL handshaking";
399 :
400 0 : ngx_post_event(c->write, &ngx_posted_events);
401 : }
402 :
403 :
404 : static void
405 0 : ngx_http_lua_ssl_sess_fetch_aborted(void *data)
406 : {
407 0 : ngx_http_lua_ssl_ctx_t *cctx = data;
408 :
409 : dd("lua ssl sess_fetch done");
410 :
411 0 : if (cctx->done) {
412 : /* completed successfully already */
413 0 : return;
414 : }
415 :
416 0 : ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,
417 : "ssl_session_fetch_by_lua*: sess_fetch cb aborted");
418 :
419 0 : cctx->aborted = 1;
420 0 : cctx->request->connection->ssl = NULL;
421 :
422 0 : ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR);
423 : }
424 :
425 :
426 : static u_char *
427 0 : ngx_http_lua_log_ssl_sess_fetch_error(ngx_log_t *log, u_char *buf, size_t len)
428 : {
429 : u_char *p;
430 : ngx_connection_t *c;
431 :
432 0 : if (log->action) {
433 0 : p = ngx_snprintf(buf, len, " while %s", log->action);
434 0 : len -= p - buf;
435 0 : buf = p;
436 : }
437 :
438 0 : p = ngx_snprintf(buf, len, ", context: ssl_session_fetch_by_lua*");
439 0 : len -= p - buf;
440 0 : buf = p;
441 :
442 0 : c = log->data;
443 :
444 0 : if (c->addr_text.len) {
445 0 : p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text);
446 0 : len -= p - buf;
447 0 : buf = p;
448 : }
449 :
450 0 : if (c && c->listening && c->listening->addr_text.len) {
451 0 : p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text);
452 0 : buf = p;
453 : }
454 :
455 0 : return buf;
456 : }
457 :
458 :
459 : /* initialize lua coroutine for fetching cached session */
460 : static ngx_int_t
461 0 : ngx_http_lua_ssl_sess_fetch_by_chunk(lua_State *L, ngx_http_request_t *r)
462 : {
463 : int co_ref;
464 : ngx_int_t rc;
465 : lua_State *co;
466 : ngx_http_lua_ctx_t *ctx;
467 : ngx_http_cleanup_t *cln;
468 :
469 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
470 :
471 0 : if (ctx == NULL) {
472 0 : ctx = ngx_http_lua_create_ctx(r);
473 0 : if (ctx == NULL) {
474 0 : rc = NGX_ERROR;
475 0 : ngx_http_lua_finalize_request(r, rc);
476 0 : return rc;
477 : }
478 :
479 : } else {
480 : dd("reset ctx");
481 0 : ngx_http_lua_reset_ctx(r, L, ctx);
482 : }
483 :
484 0 : ctx->entered_content_phase = 1;
485 :
486 : /* {{{ new coroutine to handle request */
487 0 : co = ngx_http_lua_new_thread(r, L, &co_ref);
488 :
489 0 : if (co == NULL) {
490 0 : ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
491 : "lua: failed to create new coroutine to handle request");
492 :
493 0 : rc = NGX_ERROR;
494 0 : ngx_http_lua_finalize_request(r, rc);
495 0 : return rc;
496 : }
497 :
498 : /* move code closure to new coroutine */
499 0 : lua_xmove(L, co, 1);
500 :
501 : /* set closure's env table to new coroutine's globals table */
502 0 : ngx_http_lua_get_globals_table(co);
503 0 : lua_setfenv(co, -2);
504 :
505 : /* save nginx request in coroutine globals table */
506 0 : ngx_http_lua_set_req(co, r);
507 :
508 0 : ctx->cur_co_ctx = &ctx->entry_co_ctx;
509 0 : ctx->cur_co_ctx->co = co;
510 0 : ctx->cur_co_ctx->co_ref = co_ref;
511 : #ifdef NGX_LUA_USE_ASSERT
512 0 : ctx->cur_co_ctx->co_top = 1;
513 : #endif
514 :
515 : /* register request cleanup hooks */
516 0 : if (ctx->cleanup == NULL) {
517 0 : cln = ngx_http_cleanup_add(r, 0);
518 0 : if (cln == NULL) {
519 0 : rc = NGX_ERROR;
520 0 : ngx_http_lua_finalize_request(r, rc);
521 0 : return rc;
522 : }
523 :
524 0 : cln->handler = ngx_http_lua_request_cleanup_handler;
525 0 : cln->data = ctx;
526 0 : ctx->cleanup = &cln->handler;
527 : }
528 :
529 0 : ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH;
530 :
531 0 : rc = ngx_http_lua_run_thread(L, r, ctx, 0);
532 :
533 0 : if (rc == NGX_ERROR || rc >= NGX_OK) {
534 : /* do nothing */
535 :
536 0 : } else if (rc == NGX_AGAIN) {
537 0 : rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);
538 :
539 0 : } else if (rc == NGX_DONE) {
540 0 : rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);
541 :
542 : } else {
543 0 : rc = NGX_OK;
544 : }
545 :
546 0 : ngx_http_lua_finalize_request(r, rc);
547 0 : return rc;
548 : }
549 :
550 :
551 : #ifndef NGX_LUA_NO_FFI_API
552 :
553 : /* de-serialized a SSL session and set it back to the request at lua context */
554 : int
555 0 : ngx_http_lua_ffi_ssl_set_serialized_session(ngx_http_request_t *r,
556 : const unsigned char *data, int len, char **err)
557 : {
558 : u_char *p;
559 : u_char buf[NGX_SSL_MAX_SESSION_SIZE];
560 : ngx_ssl_conn_t *ssl_conn;
561 : ngx_connection_t *c;
562 0 : ngx_ssl_session_t *session = NULL;
563 : ngx_http_lua_ssl_ctx_t *cctx;
564 :
565 0 : c = r->connection;
566 :
567 0 : if (c == NULL || c->ssl == NULL) {
568 0 : *err = "bad request";
569 0 : return NGX_ERROR;
570 : }
571 :
572 0 : ssl_conn = c->ssl->connection;
573 0 : if (ssl_conn == NULL) {
574 0 : *err = "bad ssl conn";
575 0 : return NGX_ERROR;
576 : }
577 :
578 0 : ngx_memcpy(buf, data, len);
579 0 : p = buf;
580 0 : session = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, len);
581 0 : if (session == NULL) {
582 0 : ERR_clear_error();
583 0 : *err = "failed to de-serialize session";
584 0 : return NGX_ERROR;
585 : }
586 :
587 0 : cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
588 0 : if (cctx == NULL) {
589 0 : *err = "bad lua context";
590 0 : return NGX_ERROR;
591 : }
592 :
593 0 : cctx->session = session;
594 :
595 0 : return NGX_OK;
596 : }
597 :
598 : #endif /* NGX_LUA_NO_FFI_API */
599 :
600 :
601 : #endif /* NGX_HTTP_SSL */
|