+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;
+}
+