/* 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_get_prev attempts to find an SSL_SESSION to be used to resume this * connection. It is only called by servers. * * session_id: points at the session ID in the ClientHello. This code will * read past the end of this in order to parse out the session ticket * extension, if any. * len: the length of the session ID. * limit: a pointer to the first byte after 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->internal->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, unsigned char *session_id, int len, const unsigned char *limit) { SSL_SESSION *ret = NULL; int fatal = 0; int try_session_cache = 1; int r; /* This is used only by servers. */ if (len > SSL_MAX_SSL_SESSION_ID_LENGTH) goto err; if (len == 0) try_session_cache = 0; /* Sets s->internal->tlsext_ticket_expected. */ r = tls1_process_ticket(s, session_id, len, limit, &ret); 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->session_ctx->internal->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { SSL_SESSION data; data.ssl_version = s->version; data.session_id_length = len; memcpy(data.session_id, session_id, len); CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); ret = lh_SSL_SESSION_retrieve(s->session_ctx->internal->sessions, &data); if (ret != NULL) { /* Don't allow other threads to steal it. */ CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION); } CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); if (ret == NULL) s->session_ctx->internal->stats.sess_miss++; } if (try_session_cache && ret == NULL && s->session_ctx->internal->get_session_cb != NULL) { int copy = 1; if ((ret = s->session_ctx->internal->get_session_cb(s, session_id, len, ©))) { s->session_ctx->internal->stats.sess_cb_hit++; /* * 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) CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION); /* * Add the externally cached session to the internal * cache as well if and only if we are supposed to. */ if (!(s->session_ctx->internal->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->session_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 || timingsafe_memcmp(ret->sid_ctx, s->sid_ctx, ret->sid_ctx_length) != 0) { /* 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). */ SSLerror(s, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); fatal = 1; goto err; } if (ret->cipher == NULL) { ret->cipher = ssl3_get_cipher_by_id(ret->cipher_id); if (ret->cipher == NULL) goto err; } if (ret->timeout < (time(NULL) - ret->time)) { /* timeout */ s->session_ctx->internal->stats.sess_timeout++; if (try_session_cache) { /* session was from the cache, so remove it */ SSL_CTX_remove_session(s->session_ctx, ret); } goto err; } s->session_ctx->internal->stats.sess_hit++; 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->internal->tlsext_ticket_expected = 1; } } if (fatal) return -1; else return 0; }