int ossl_provider_forall_loaded(OPENSSL_CTX *ctx, int (*cb)(OSSL_PROVIDER *provider, void *cbdata), void *cbdata) { int ret = 1; int i; struct provider_store_st *store = get_provider_store(ctx); if (store != NULL) { int found_activated = 0; CRYPTO_THREAD_read_lock(store->lock); ret = provider_forall_loaded(store, &found_activated, cb, cbdata); /* * If there's nothing activated ever in this store, try to activate * all fallbacks. */ if (!found_activated && store->use_fallbacks) { int num_provs = sk_OSSL_PROVIDER_num(store->providers); int activated_fallback_count = 0; for (i = 0; i < num_provs; i++) { OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(store->providers, i); /* * Note that we don't care if the activation succeeds or * not. If it doesn't succeed, then the next loop will * fail anyway. */ if (prov->flag_fallback) { activated_fallback_count++; provider_activate(prov); } } if (activated_fallback_count > 0) { /* * We assume that all fallbacks have been added to the store * before any fallback is activated. * TODO: We may have to reconsider this, IF we find ourselves * adding fallbacks after any previous fallback has been * activated. */ store->use_fallbacks = 0; /* * Now that we've activated available fallbacks, try a * second sweep */ ret = provider_forall_loaded(store, NULL, cb, cbdata); } } CRYPTO_THREAD_unlock(store->lock); } return ret; }
static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *d) { ERR_STRING_DATA *p = NULL; CRYPTO_THREAD_read_lock(err_string_lock); p = lh_ERR_STRING_DATA_retrieve(int_error_hash, d); CRYPTO_THREAD_unlock(err_string_lock); return p; }
static int rand_status(void) { CRYPTO_THREAD_ID cur; int ret; int do_not_lock; if (!RUN_ONCE(&rand_lock_init, do_rand_lock_init)) return 0; cur = CRYPTO_THREAD_get_current_id(); /* * check if we already have the lock (could happen if a RAND_poll() * implementation calls RAND_status()) */ if (crypto_lock_rand) { CRYPTO_THREAD_read_lock(rand_tmp_lock); do_not_lock = CRYPTO_THREAD_compare_id(locking_threadid, cur); CRYPTO_THREAD_unlock(rand_tmp_lock); } else do_not_lock = 0; if (!do_not_lock) { CRYPTO_THREAD_write_lock(rand_lock); /* * Prevent deadlocks in case we end up in an async engine */ ASYNC_block_pause(); /* * prevent rand_bytes() from trying to obtain the lock again */ CRYPTO_THREAD_write_lock(rand_tmp_lock); locking_threadid = cur; CRYPTO_THREAD_unlock(rand_tmp_lock); crypto_lock_rand = 1; } if (!initialized) { RAND_poll(); initialized = 1; } ret = entropy >= ENTROPY_NEEDED; if (!do_not_lock) { /* before unlocking, we must clear 'crypto_lock_rand' */ crypto_lock_rand = 0; ASYNC_unblock_pause(); CRYPTO_THREAD_unlock(rand_lock); } return ret; }
static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *d) { ERR_STRING_DATA *p = NULL; LHASH_OF(ERR_STRING_DATA) *hash; CRYPTO_THREAD_read_lock(err_string_lock); hash = get_hash(0, 0); if (hash) p = lh_ERR_STRING_DATA_retrieve(hash, d); CRYPTO_THREAD_unlock(err_string_lock); return p; }
SSL_SESSION *SSL_get1_session(SSL *ssl) /* variant of SSL_get_session: caller really gets something */ { SSL_SESSION *sess; /* * Need to lock this all up rather than just use CRYPTO_add so that * somebody doesn't free ssl->session between when we check it's non-null * and when we up the reference count. */ CRYPTO_THREAD_read_lock(ssl->lock); sess = ssl->session; if (sess) SSL_SESSION_up_ref(sess); CRYPTO_THREAD_unlock(ssl->lock); return sess; }
BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, CRYPTO_RWLOCK *lock, const BIGNUM *mod, BN_CTX *ctx) { BN_MONT_CTX *ret; CRYPTO_THREAD_read_lock(lock); ret = *pmont; CRYPTO_THREAD_unlock(lock); if (ret) return ret; /* * We don't want to serialise globally while doing our lazy-init math in * BN_MONT_CTX_set. That punishes threads that are doing independent * things. Instead, punish the case where more than one thread tries to * lazy-init the same 'pmont', by having each do the lazy-init math work * independently and only use the one from the thread that wins the race * (the losers throw away the work they've done). */ ret = BN_MONT_CTX_new(); if (ret == NULL) return NULL; if (!BN_MONT_CTX_set(ret, mod, ctx)) { BN_MONT_CTX_free(ret); return NULL; } /* The locked compare-and-set, after the local work is done. */ CRYPTO_THREAD_write_lock(lock); if (*pmont) { BN_MONT_CTX_free(ret); ret = *pmont; } else *pmont = ret; CRYPTO_THREAD_unlock(lock); return ret; }
static int rand_add(const void *buf, int num, double add) { int i, j, k, st_idx; long md_c[2]; unsigned char local_md[MD_DIGEST_LENGTH]; EVP_MD_CTX *m; int do_not_lock; int rv = 0; if (!num) return 1; /* * (Based on the rand(3) manpage) * * The input is chopped up into units of 20 bytes (or less for * the last block). Each of these blocks is run through the hash * function as follows: The data passed to the hash function * is the current 'md', the same number of bytes from the 'state' * (the location determined by in incremented looping index) as * the current 'block', the new key data 'block', and 'count' * (which is incremented after each use). * The result of this is kept in 'md' and also xored into the * 'state' at the same locations that were used as input into the * hash function. */ m = EVP_MD_CTX_new(); if (m == NULL) goto err; if (!RUN_ONCE(&rand_lock_init, do_rand_lock_init)) goto err; /* check if we already have the lock */ if (crypto_lock_rand) { CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id(); CRYPTO_THREAD_read_lock(rand_tmp_lock); do_not_lock = CRYPTO_THREAD_compare_id(locking_threadid, cur); CRYPTO_THREAD_unlock(rand_tmp_lock); } else do_not_lock = 0; if (!do_not_lock) CRYPTO_THREAD_write_lock(rand_lock); st_idx = state_index; /* * use our own copies of the counters so that even if a concurrent thread * seeds with exactly the same data and uses the same subarray there's * _some_ difference */ md_c[0] = md_count[0]; md_c[1] = md_count[1]; memcpy(local_md, md, sizeof md); /* state_index <= state_num <= STATE_SIZE */ state_index += num; if (state_index >= STATE_SIZE) { state_index %= STATE_SIZE; state_num = STATE_SIZE; } else if (state_num < STATE_SIZE) { if (state_index > state_num) state_num = state_index; } /* state_index <= state_num <= STATE_SIZE */ /* * state[st_idx], ..., state[(st_idx + num - 1) % STATE_SIZE] are what we * will use now, but other threads may use them as well */ md_count[1] += (num / MD_DIGEST_LENGTH) + (num % MD_DIGEST_LENGTH > 0); if (!do_not_lock) CRYPTO_THREAD_unlock(rand_lock); for (i = 0; i < num; i += MD_DIGEST_LENGTH) { j = (num - i); j = (j > MD_DIGEST_LENGTH) ? MD_DIGEST_LENGTH : j; if (!MD_Init(m)) goto err; if (!MD_Update(m, local_md, MD_DIGEST_LENGTH)) goto err; k = (st_idx + j) - STATE_SIZE; if (k > 0) { if (!MD_Update(m, &(state[st_idx]), j - k)) goto err; if (!MD_Update(m, &(state[0]), k)) goto err; } else if (!MD_Update(m, &(state[st_idx]), j)) goto err; /* DO NOT REMOVE THE FOLLOWING CALL TO MD_Update()! */ if (!MD_Update(m, buf, j)) goto err; /* * We know that line may cause programs such as purify and valgrind * to complain about use of uninitialized data. The problem is not, * it's with the caller. Removing that line will make sure you get * really bad randomness and thereby other problems such as very * insecure keys. */ if (!MD_Update(m, (unsigned char *)&(md_c[0]), sizeof(md_c))) goto err; if (!MD_Final(m, local_md)) goto err; md_c[1]++; buf = (const char *)buf + j; for (k = 0; k < j; k++) { /* * Parallel threads may interfere with this, but always each byte * of the new state is the XOR of some previous value of its and * local_md (intermediate values may be lost). Alway using locking * could hurt performance more than necessary given that * conflicts occur only when the total seeding is longer than the * random state. */ state[st_idx++] ^= local_md[k]; if (st_idx >= STATE_SIZE) st_idx = 0; } } if (!do_not_lock) CRYPTO_THREAD_write_lock(rand_lock); /* * Don't just copy back local_md into md -- this could mean that other * thread's seeding remains without effect (except for the incremented * counter). By XORing it we keep at least as much entropy as fits into * md. */ for (k = 0; k < (int)sizeof(md); k++) { md[k] ^= local_md[k]; } if (entropy < ENTROPY_NEEDED) /* stop counting when we have enough */ entropy += add; if (!do_not_lock) CRYPTO_THREAD_unlock(rand_lock); rv = 1; err: EVP_MD_CTX_free(m); return rv; }
/*- * ssl_get_prev attempts to find an SSL_SESSION to be used to resume this * connection. It is only called by servers. * * hello: The parsed ClientHello data * * Returns: * -1: fatal error * 0: no session found * 1: 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->ext.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, CLIENTHELLO_MSG *hello, int *al) { /* This is used only by servers. */ SSL_SESSION *ret = NULL; int fatal = 0; int try_session_cache = 0; TICKET_RETURN r; if (SSL_IS_TLS13(s)) { if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes, SSL_EXT_CLIENT_HELLO, hello->pre_proc_exts, NULL, 0, al) || !tls_parse_extension(s, TLSEXT_IDX_psk, SSL_EXT_CLIENT_HELLO, hello->pre_proc_exts, NULL, 0, al)) return -1; ret = s->session; } else { /* sets s->ext.ticket_expected */ r = tls_get_ticket_from_client(s, hello, &ret); switch (r) { case TICKET_FATAL_ERR_MALLOC: case TICKET_FATAL_ERR_OTHER: fatal = 1; goto err; case TICKET_NONE: case TICKET_EMPTY: try_session_cache = 1; break; case TICKET_NO_DECRYPT: case TICKET_SUCCESS: case TICKET_SUCCESS_RENEW: break; } } if (try_session_cache && ret == NULL && !(s->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { SSL_SESSION data; data.ssl_version = s->version; memcpy(data.session_id, hello->session_id, hello->session_id_len); data.session_id_length = hello->session_id_len; CRYPTO_THREAD_read_lock(s->session_ctx->lock); ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data); if (ret != NULL) { /* don't allow other threads to steal it: */ SSL_SESSION_up_ref(ret); } CRYPTO_THREAD_unlock(s->session_ctx->lock); if (ret == NULL) s->session_ctx->stats.sess_miss++; } if (try_session_cache && ret == NULL && s->session_ctx->get_session_cb != NULL) { int copy = 1; ret = s->session_ctx->get_session_cb(s, hello->session_id, hello->session_id_len, ©); if (ret != NULL) { s->session_ctx->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) 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->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE)) { /* * The following should not return 1, otherwise, things are * very strange */ if (SSL_CTX_add_session(s->session_ctx, ret)) goto err; } } } if (ret == NULL) goto err; /* Now ret is non-NULL and we own one of its reference counts. */ /* Check TLS version consistency */ if (ret->ssl_version != s->version) goto err; 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). */ SSLerr(SSL_F_SSL_GET_PREV_SESSION, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); fatal = 1; goto err; } if (ret->timeout < (long)(time(NULL) - ret->time)) { /* timeout */ s->session_ctx->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; } /* Check extended master secret extension consistency */ if (ret->flags & SSL_SESS_FLAG_EXTMS) { /* If old session includes extms, but new does not: abort handshake */ if (!(s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS)) { SSLerr(SSL_F_SSL_GET_PREV_SESSION, SSL_R_INCONSISTENT_EXTMS); ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); fatal = 1; goto err; } } else if (s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS) { /* If new session includes extms, but old does not: do not resume */ goto err; } if (!SSL_IS_TLS13(s)) { /* We already did this for TLS1.3 */ SSL_SESSION_free(s->session); s->session = ret; } s->session_ctx->stats.sess_hit++; s->verify_result = s->session->verify_result; return 1; err: if (ret != NULL) { SSL_SESSION_free(ret); /* In TLSv1.3 s->session was already set to ret, so we NULL it out */ if (SSL_IS_TLS13(s)) s->session = NULL; if (!try_session_cache) { /* * The session was from a ticket, so we should issue a ticket for * the new session */ s->ext.ticket_expected = 1; } } if (fatal) { *al = SSL_AD_INTERNAL_ERROR; return -1; } return 0; }
int ssl_get_new_session(SSL *s, int session) { /* This gets used by clients and servers. */ unsigned int tmp; SSL_SESSION *ss = NULL; GEN_SESSION_CB cb = def_generate_session_id; if ((ss = SSL_SESSION_new()) == NULL) return (0); /* If the context has a default timeout, use it */ if (s->session_ctx->session_timeout == 0) ss->timeout = SSL_get_default_timeout(s); else ss->timeout = s->session_ctx->session_timeout; SSL_SESSION_free(s->session); s->session = NULL; if (session) { if (s->version == SSL3_VERSION) { ss->ssl_version = SSL3_VERSION; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else if (s->version == TLS1_VERSION) { ss->ssl_version = TLS1_VERSION; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else if (s->version == TLS1_1_VERSION) { ss->ssl_version = TLS1_1_VERSION; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else if (s->version == TLS1_2_VERSION) { ss->ssl_version = TLS1_2_VERSION; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else if (s->version == TLS1_3_VERSION) { ss->ssl_version = TLS1_3_VERSION; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else if (s->version == DTLS1_BAD_VER) { ss->ssl_version = DTLS1_BAD_VER; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else if (s->version == DTLS1_VERSION) { ss->ssl_version = DTLS1_VERSION; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else if (s->version == DTLS1_2_VERSION) { ss->ssl_version = DTLS1_2_VERSION; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else { SSLerr(SSL_F_SSL_GET_NEW_SESSION, SSL_R_UNSUPPORTED_SSL_VERSION); SSL_SESSION_free(ss); return (0); } /*- * If RFC5077 ticket, use empty session ID (as server). * Note that: * (a) ssl_get_prev_session() does lookahead into the * ClientHello extensions to find the session ticket. * When ssl_get_prev_session() fails, statem_srvr.c calls * ssl_get_new_session() in tls_process_client_hello(). * At that point, it has not yet parsed the extensions, * however, because of the lookahead, it already knows * whether a ticket is expected or not. * * (b) statem_clnt.c calls ssl_get_new_session() before parsing * ServerHello extensions, and before recording the session * ID received from the server, so this block is a noop. */ if (s->ext.ticket_expected) { ss->session_id_length = 0; goto sess_id_done; } /* Choose which callback will set the session ID */ CRYPTO_THREAD_read_lock(s->lock); CRYPTO_THREAD_read_lock(s->session_ctx->lock); if (s->generate_session_id) cb = s->generate_session_id; else if (s->session_ctx->generate_session_id) cb = s->session_ctx->generate_session_id; CRYPTO_THREAD_unlock(s->session_ctx->lock); CRYPTO_THREAD_unlock(s->lock); /* Choose a session ID */ memset(ss->session_id, 0, ss->session_id_length); tmp = (int)ss->session_id_length; if (!cb(s, ss->session_id, &tmp)) { /* The callback failed */ SSLerr(SSL_F_SSL_GET_NEW_SESSION, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED); SSL_SESSION_free(ss); return (0); } /* * Don't allow the callback to set the session length to zero. nor * set it higher than it was. */ if (tmp == 0 || tmp > ss->session_id_length) { /* The callback set an illegal length */ SSLerr(SSL_F_SSL_GET_NEW_SESSION, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH); SSL_SESSION_free(ss); return (0); } ss->session_id_length = tmp; /* Finally, check for a conflict */ if (SSL_has_matching_session_id(s, ss->session_id, (unsigned int)ss->session_id_length)) { SSLerr(SSL_F_SSL_GET_NEW_SESSION, SSL_R_SSL_SESSION_ID_CONFLICT); SSL_SESSION_free(ss); return (0); } sess_id_done: if (s->ext.hostname) { ss->ext.hostname = OPENSSL_strdup(s->ext.hostname); if (ss->ext.hostname == NULL) { SSLerr(SSL_F_SSL_GET_NEW_SESSION, ERR_R_INTERNAL_ERROR); SSL_SESSION_free(ss); return 0; } } } else { ss->session_id_length = 0; } if (s->sid_ctx_length > sizeof ss->sid_ctx) { SSLerr(SSL_F_SSL_GET_NEW_SESSION, ERR_R_INTERNAL_ERROR); SSL_SESSION_free(ss); return 0; } memcpy(ss->sid_ctx, s->sid_ctx, s->sid_ctx_length); ss->sid_ctx_length = s->sid_ctx_length; s->session = ss; ss->ssl_version = s->version; ss->verify_result = X509_V_OK; /* If client supports extended master secret set it in session */ if (s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS) ss->flags |= SSL_SESS_FLAG_EXTMS; return (1); }
void s_log(int level, const char *format, ...) { va_list ap; char *text, *stamp, *id; struct LIST *tmp; #ifdef USE_WIN32 DWORD libc_error; #else int libc_error; #endif int socket_error; time_t gmt; struct tm *timeptr; #if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT) struct tm timestruct; #endif TLS_DATA *tls_data; tls_data=tls_get(); if(!tls_data) { tls_data=tls_alloc(NULL, NULL, "log"); s_log(LOG_ERR, "INTERNAL ERROR: Uninitialized TLS at %s, line %d", __FILE__, __LINE__); } /* performance optimization: skip the trivial case early */ if(log_mode==LOG_MODE_CONFIGURED && level>tls_data->opt->log_level) return; libc_error=get_last_error(); socket_error=get_last_socket_error(); /* format the id to be logged */ time(&gmt); #if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT) timeptr=localtime_r(&gmt, ×truct); #else timeptr=localtime(&gmt); #endif stamp=str_printf("%04d.%02d.%02d %02d:%02d:%02d", timeptr->tm_year+1900, timeptr->tm_mon+1, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); id=str_printf("LOG%d[%s]", level, tls_data->id); /* format the text to be logged */ va_start(ap, format); text=str_vprintf(format, ap); va_end(ap); safestring(text); CRYPTO_THREAD_read_lock(stunnel_locks[LOCK_LOG_MODE]); if(log_mode==LOG_MODE_BUFFER) { /* save the text to log it later */ CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_LOG_BUFFER]); tmp=str_alloc_detached(sizeof(struct LIST)); tmp->next=NULL; tmp->opt=tls_data->opt; tmp->level=level; tmp->stamp=stamp; str_detach(tmp->stamp); tmp->id=id; str_detach(tmp->id); tmp->text=text; str_detach(tmp->text); if(tail) tail->next=tmp; else head=tmp; tail=tmp; CRYPTO_THREAD_write_unlock(stunnel_locks[LOCK_LOG_BUFFER]); } else { /* ready log the text directly */ log_raw(tls_data->opt, level, stamp, id, text); str_free(stamp); str_free(id); str_free(text); } CRYPTO_THREAD_read_unlock(stunnel_locks[LOCK_LOG_MODE]); set_last_error(libc_error); set_last_socket_error(socket_error); }
SSL_SESSION *lookup_sess_in_cache(SSL *s, const unsigned char *sess_id, size_t sess_id_len) { SSL_SESSION *ret = NULL; if ((s->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) == 0) { SSL_SESSION data; data.ssl_version = s->version; if (!ossl_assert(sess_id_len <= SSL_MAX_SSL_SESSION_ID_LENGTH)) return NULL; memcpy(data.session_id, sess_id, sess_id_len); data.session_id_length = sess_id_len; CRYPTO_THREAD_read_lock(s->session_ctx->lock); ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data); if (ret != NULL) { /* don't allow other threads to steal it: */ SSL_SESSION_up_ref(ret); } CRYPTO_THREAD_unlock(s->session_ctx->lock); if (ret == NULL) tsan_counter(&s->session_ctx->stats.sess_miss); } if (ret == NULL && s->session_ctx->get_session_cb != NULL) { int copy = 1; ret = s->session_ctx->get_session_cb(s, sess_id, sess_id_len, ©); if (ret != NULL) { tsan_counter(&s->session_ctx->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) 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->session_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) == 0) { /* * Either return value of SSL_CTX_add_session should not * interrupt the session resumption process. The return * value is intentionally ignored. */ (void)SSL_CTX_add_session(s->session_ctx, ret); } } } return ret; }
int ssl_generate_session_id(SSL *s, SSL_SESSION *ss) { unsigned int tmp; GEN_SESSION_CB cb = def_generate_session_id; switch (s->version) { case SSL3_VERSION: case TLS1_VERSION: case TLS1_1_VERSION: case TLS1_2_VERSION: case TLS1_3_VERSION: case DTLS1_BAD_VER: case DTLS1_VERSION: case DTLS1_2_VERSION: ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; break; default: SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID, SSL_R_UNSUPPORTED_SSL_VERSION); return 0; } /*- * If RFC5077 ticket, use empty session ID (as server). * Note that: * (a) ssl_get_prev_session() does lookahead into the * ClientHello extensions to find the session ticket. * When ssl_get_prev_session() fails, statem_srvr.c calls * ssl_get_new_session() in tls_process_client_hello(). * At that point, it has not yet parsed the extensions, * however, because of the lookahead, it already knows * whether a ticket is expected or not. * * (b) statem_clnt.c calls ssl_get_new_session() before parsing * ServerHello extensions, and before recording the session * ID received from the server, so this block is a noop. */ if (s->ext.ticket_expected) { ss->session_id_length = 0; return 1; } /* Choose which callback will set the session ID */ CRYPTO_THREAD_read_lock(s->lock); CRYPTO_THREAD_read_lock(s->session_ctx->lock); if (s->generate_session_id) cb = s->generate_session_id; else if (s->session_ctx->generate_session_id) cb = s->session_ctx->generate_session_id; CRYPTO_THREAD_unlock(s->session_ctx->lock); CRYPTO_THREAD_unlock(s->lock); /* Choose a session ID */ memset(ss->session_id, 0, ss->session_id_length); tmp = (int)ss->session_id_length; if (!cb(s, ss->session_id, &tmp)) { /* The callback failed */ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED); return 0; } /* * Don't allow the callback to set the session length to zero. nor * set it higher than it was. */ if (tmp == 0 || tmp > ss->session_id_length) { /* The callback set an illegal length */ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH); return 0; } ss->session_id_length = tmp; /* Finally, check for a conflict */ if (SSL_has_matching_session_id(s, ss->session_id, (unsigned int)ss->session_id_length)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID, SSL_R_SSL_SESSION_ID_CONFLICT); return 0; } return 1; }