/* ssl_get_prev attempts to find an SSL_SESSION to be used to resume this * connection. It is only called by servers. * * ctx: contains the early callback context, which is the result of a * shallow parse of the ClientHello. * * Returns: * -1: error * 0: a session may have been found. * * Side effects: * - If a session is found then s->session is pointed at it (after freeing an * existing session if need be) and s->verify_result is set from the session. * - Both for new and resumed sessions, s->tlsext_ticket_expected is set to 1 * if the server should issue a new session ticket (to 0 otherwise). */ int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx) { /* This is used only by servers. */ SSL_SESSION *ret = NULL; int fatal = 0; int try_session_cache = 1; int r; if (ctx->session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { goto err; } if (ctx->session_id_len == 0) { try_session_cache = 0; } r = tls1_process_ticket(s, ctx, &ret); /* sets s->tlsext_ticket_expected */ switch (r) { case -1: /* Error during processing */ fatal = 1; goto err; case 0: /* No ticket found */ case 1: /* Zero length ticket found */ break; /* Ok to carry on processing session id. */ case 2: /* Ticket found but not decrypted. */ case 3: /* Ticket decrypted, *ret has been set. */ try_session_cache = 0; break; default: abort(); } if (try_session_cache && ret == NULL && !(s->initial_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { SSL_SESSION data; data.ssl_version = s->version; data.session_id_length = ctx->session_id_len; if (ctx->session_id_len == 0) { return 0; } memcpy(data.session_id, ctx->session_id, ctx->session_id_len); CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); ret = SSL_SESSION_up_ref(lh_SSL_SESSION_retrieve(s->initial_ctx->sessions, &data)); CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); } if (try_session_cache && ret == NULL && s->initial_ctx->get_session_cb != NULL) { int copy = 1; ret = s->initial_ctx->get_session_cb(s, (uint8_t *)ctx->session_id, ctx->session_id_len, ©); if (ret != NULL) { if (ret == SSL_magic_pending_session_ptr()) { /* This is a magic value which indicates that the callback needs to * unwind the stack and figure out the session asynchronously. */ return PENDING_SESSION; } /* Increment reference count now if the session callback asks us to do so * (note that if the session structures returned by the callback are * shared between threads, it must handle the reference count itself * [i.e. copy == 0], or things won't be thread-safe). */ if (copy) { SSL_SESSION_up_ref(ret); } /* Add the externally cached session to the internal cache as well if and * only if we are supposed to. */ if (!(s->initial_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE)) { /* The following should not return 1, otherwise, things are very * strange */ SSL_CTX_add_session(s->initial_ctx, ret); } } } if (ret == NULL) { goto err; } /* Now ret is non-NULL and we own one of its reference counts. */ if (ret->sid_ctx_length != s->sid_ctx_length || memcmp(ret->sid_ctx, s->sid_ctx, ret->sid_ctx_length)) { /* We have the session requested by the client, but we don't want to use it * in this context. */ goto err; /* treat like cache miss */ } if ((s->verify_mode & SSL_VERIFY_PEER) && s->sid_ctx_length == 0) { /* We can't be sure if this session is being used out of context, which is * especially important for SSL_VERIFY_PEER. The application should have * used SSL[_CTX]_set_session_id_context. * * For this error case, we generate an error instead of treating the event * like a cache miss (otherwise it would be easy for applications to * effectively disable the session cache by accident without anyone * noticing). */ OPENSSL_PUT_ERROR(SSL, ssl_get_prev_session, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); fatal = 1; goto err; } if (ret->timeout < (long)(time(NULL) - ret->time)) { /* timeout */ if (try_session_cache) { /* session was from the cache, so remove it */ SSL_CTX_remove_session(s->initial_ctx, ret); } goto err; } if (s->session != NULL) { SSL_SESSION_free(s->session); } s->session = ret; s->verify_result = s->session->verify_result; return 1; err: if (ret != NULL) { SSL_SESSION_free(ret); if (!try_session_cache) { /* The session was from a ticket, so we should * issue a ticket for the new session */ s->tlsext_ticket_expected = 1; } } if (fatal) { return -1; } return 0; }
/* ssl_lookup_session looks up |session_id| in the session cache and sets * |*out_session| to an |SSL_SESSION| object if found. The caller takes * ownership of the result. */ static enum ssl_session_result_t ssl_lookup_session( SSL *ssl, SSL_SESSION **out_session, const uint8_t *session_id, size_t session_id_len) { *out_session = NULL; if (session_id_len == 0 || session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { return ssl_session_success; } SSL_SESSION *session; /* Try the internal cache, if it exists. */ if (!(ssl->initial_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { SSL_SESSION data; data.ssl_version = ssl->version; data.session_id_length = session_id_len; memcpy(data.session_id, session_id, session_id_len); CRYPTO_MUTEX_lock_read(&ssl->initial_ctx->lock); session = lh_SSL_SESSION_retrieve(ssl->initial_ctx->sessions, &data); if (session != NULL) { SSL_SESSION_up_ref(session); } /* TODO(davidben): This should probably move it to the front of the list. */ CRYPTO_MUTEX_unlock(&ssl->initial_ctx->lock); if (session != NULL) { *out_session = session; return ssl_session_success; } } /* Fall back to the external cache, if it exists. */ if (ssl->initial_ctx->get_session_cb == NULL) { return ssl_session_success; } int copy = 1; session = ssl->initial_ctx->get_session_cb(ssl, (uint8_t *)session_id, session_id_len, ©); if (session == NULL) { return ssl_session_success; } if (session == SSL_magic_pending_session_ptr()) { return ssl_session_retry; } /* Increment reference count now if the session callback asks us to do so * (note that if the session structures returned by the callback are shared * between threads, it must handle the reference count itself [i.e. copy == * 0], or things won't be thread-safe). */ if (copy) { SSL_SESSION_up_ref(session); } /* Add the externally cached session to the internal cache if necessary. */ if (!(ssl->initial_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE)) { SSL_CTX_add_session(ssl->initial_ctx, session); } *out_session = session; return ssl_session_success; }
/* cached session fetching callback to be set with SSL_CTX_sess_set_get_cb */ ngx_ssl_session_t * ngx_http_lua_ssl_sess_fetch_handler(ngx_ssl_conn_t *ssl_conn, u_char *id, int len, int *copy) { lua_State *L; ngx_int_t rc; ngx_connection_t *c, *fc = NULL; ngx_http_request_t *r = NULL; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; ngx_http_lua_ssl_ctx_t *cctx; ngx_http_lua_srv_conf_t *lscf; ngx_http_core_loc_conf_t *clcf; /* set copy to 0 as we expect OpenSSL to handle * the memory of returned session */ *copy = 0; c = ngx_ssl_get_connection(ssl_conn); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ssl session fetch: connection reusable: %ud", c->reusable); cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); dd("ssl sess_fetch handler, sess_fetch-ctx=%p", cctx); if (cctx && cctx->entered_sess_fetch_handler) { /* not the first time */ dd("here: %d", (int) cctx->entered_sess_fetch_handler); if (cctx->done) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "ssl_session_fetch_by_lua*: " "sess get cb exit code: %d", cctx->exit_code); dd("lua ssl sess_fetch done, finally"); return cctx->session; } #ifdef SSL_ERROR_PENDING_SESSION return SSL_magic_pending_session_ptr(); #else ngx_log_error(NGX_LOG_CRIT, c->log, 0, "lua: cannot yield in sess get cb: " "missing async sess get cb support in OpenSSL"); return NULL; #endif } dd("first time"); ngx_reusable_connection(c, 0); hc = c->data; fc = ngx_http_lua_create_fake_connection(NULL); if (fc == NULL) { goto failed; } fc->log->handler = ngx_http_lua_log_ssl_sess_fetch_error; fc->log->data = fc; fc->addr_text = c->addr_text; fc->listening = c->listening; r = ngx_http_lua_create_fake_request(fc); if (r == NULL) { goto failed; } r->main_conf = hc->conf_ctx->main_conf; r->srv_conf = hc->conf_ctx->srv_conf; r->loc_conf = hc->conf_ctx->loc_conf; fc->log->file = c->log->file; fc->log->log_level = c->log->log_level; fc->ssl = c->ssl; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); #if defined(nginx_version) && nginx_version >= 1003014 # if nginx_version >= 1009000 ngx_set_connection_log(fc, clcf->error_log); # else ngx_http_set_connection_log(fc, clcf->error_log); # endif #else fc->log->file = clcf->error_log->file; if (!(fc->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { fc->log->log_level = clcf->error_log->log_level; } #endif if (cctx == NULL) { cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t)); if (cctx == NULL) { goto failed; /* error */ } } cctx->exit_code = 1; /* successful by default */ cctx->connection = c; cctx->request = r; cctx->session_id.data = id; cctx->session_id.len = len; cctx->entered_sess_fetch_handler = 1; cctx->done = 0; dd("setting cctx = %p", cctx); if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx) == 0) { ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); goto failed; } lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module); /* TODO honor lua_code_cache off */ L = ngx_http_lua_get_lua_vm(r, NULL); c->log->action = "fetching SSL session by lua"; rc = lscf->srv.ssl_sess_fetch_handler(r, lscf, L); if (rc >= NGX_OK || rc == NGX_ERROR) { cctx->done = 1; if (cctx->cleanup) { *cctx->cleanup = NULL; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "ssl_session_fetch_by_lua*: handler return value: %i, " "sess get cb exit code: %d", rc, cctx->exit_code); c->log->action = "SSL handshaking"; return cctx->session; } /* rc == NGX_DONE */ cln = ngx_pool_cleanup_add(fc->pool, 0); if (cln == NULL) { goto failed; } cln->handler = ngx_http_lua_ssl_sess_fetch_done; cln->data = cctx; if (cctx->cleanup == NULL) { /* we only want exactly one cleanup handler to be registered with the * connection to clean up cctx when connection is aborted */ cln = ngx_pool_cleanup_add(c->pool, 0); if (cln == NULL) { goto failed; } cln->data = cctx; cctx->cleanup = &cln->handler; } *cctx->cleanup = ngx_http_lua_ssl_sess_fetch_aborted; #ifdef SSL_ERROR_PENDING_SESSION return SSL_magic_pending_session_ptr(); #else ngx_log_error(NGX_LOG_CRIT, c->log, 0, "lua: cannot yield in sess get cb: " "missing async sess get cb support in OpenSSL"); /* fall through to the "failed" label below */ #endif failed: if (r && r->pool) { ngx_http_lua_free_fake_request(r); } if (fc) { ngx_http_lua_close_fake_connection(fc); } return NULL; }