SSL_F_SSL_DO_HANDSHAKE:180:SSL_do_handshake
SSL_F_SSL_DUP_CA_LIST:408:SSL_dup_CA_list
SSL_F_SSL_ENABLE_CT:402:SSL_enable_ct
+SSL_F_SSL_GENERATE_SESSION_ID:547:ssl_generate_session_id
SSL_F_SSL_GET_NEW_SESSION:181:ssl_get_new_session
SSL_F_SSL_GET_PREV_SESSION:217:ssl_get_prev_session
SSL_F_SSL_GET_SERVER_CERT_INDEX:322:*
# define SSL_F_SSL_DO_HANDSHAKE 180
# define SSL_F_SSL_DUP_CA_LIST 408
# define SSL_F_SSL_ENABLE_CT 402
+# define SSL_F_SSL_GENERATE_SESSION_ID 547
# define SSL_F_SSL_GET_NEW_SESSION 181
# define SSL_F_SSL_GET_PREV_SESSION 217
# define SSL_F_SSL_GET_SERVER_CERT_INDEX 322
{ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_DO_HANDSHAKE, 0), "SSL_do_handshake"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_DUP_CA_LIST, 0), "SSL_dup_CA_list"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_ENABLE_CT, 0), "SSL_enable_ct"},
+ {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_GENERATE_SESSION_ID, 0),
+ "ssl_generate_session_id"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_GET_NEW_SESSION, 0),
"ssl_get_new_session"},
{ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_GET_PREV_SESSION, 0),
__owur CERT *ssl_cert_dup(CERT *cert);
void ssl_cert_clear_certs(CERT *c);
void ssl_cert_free(CERT *c);
+__owur int ssl_generate_session_id(SSL *s, SSL_SESSION *ss);
__owur int ssl_get_new_session(SSL *s, int session);
__owur int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello, int *al);
__owur SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket);
return 0;
}
+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:
+ SSLerr(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 */
+ SSLerr(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 */
+ SSLerr(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)) {
+ SSLerr(SSL_F_SSL_GENERATE_SESSION_ID, SSL_R_SSL_SESSION_ID_CONFLICT);
+ return 0;
+ }
+
+ return 1;
+}
+
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);
+ return 0;
/* If the context has a default timeout, use it */
if (s->session_ctx->session_timeout == 0)
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);
+ if (!ssl_generate_session_id(s, ss)) {
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);
+ return 0;
}
- sess_id_done:
if (s->ext.hostname) {
ss->ext.hostname = OPENSSL_strdup(s->ext.hostname);
if (ss->ext.hostname == NULL) {
if (s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS)
ss->flags |= SSL_SESS_FLAG_EXTMS;
- return (1);
+ return 1;
}
/*-
{
int ret = SSL_TLSEXT_ERR_NOACK;
int altmp = SSL_AD_UNRECOGNIZED_NAME;
+ int was_ticket = (SSL_get_options(s) & SSL_OP_NO_TICKET) == 0;
if (s->ctx != NULL && s->ctx->ext.servername_cb != 0)
ret = s->ctx->ext.servername_cb(s, &altmp,
ret = s->session_ctx->ext.servername_cb(s, &altmp,
s->session_ctx->ext.servername_arg);
+ /*
+ * If we're expecting to send a ticket, and tickets were previously enabled,
+ * and now tickets are disabled, then turn off expected ticket.
+ * Also, if this is not a resumption, create a new session ID
+ */
+ if (ret == SSL_TLSEXT_ERR_OK && s->ext.ticket_expected
+ && was_ticket && (SSL_get_options(s) & SSL_OP_NO_TICKET) != 0) {
+ s->ext.ticket_expected = 0;
+ if (!s->hit) {
+ SSL_SESSION* ss = SSL_get_session(s);
+
+ if (ss != NULL) {
+ OPENSSL_free(ss->ext.tick);
+ ss->ext.tick = NULL;
+ ss->ext.ticklen = 0;
+ ss->ext.tick_lifetime_hint = 0;
+ ss->ext.tick_age_add = 0;
+ ss->ext.tick_identity = 0;
+ if (!ssl_generate_session_id(s, ss)) {
+ ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+ altmp = SSL_AD_INTERNAL_ERROR;
+ }
+ } else {
+ ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+ altmp = SSL_AD_INTERNAL_ERROR;
+ }
+ }
+ }
+
switch (ret) {
case SSL_TLSEXT_ERR_ALERT_FATAL:
*al = altmp;
- Yes - a session ticket is expected
- No - a session ticket is not expected
+* SessionIdExpected - whether or not a session id is expected
+ - Ignore - do not check for a session id (default)
+ - Yes - a session id is expected
+ - No - a session id is not expected
+
* ResumptionExpected - whether or not resumption is expected (Resume mode only)
- Yes - resumed handshake
- No - full handshake (default)
handshake_status_t status = HANDSHAKE_RETRY;
const unsigned char* tick = NULL;
size_t tick_len = 0;
+ const unsigned char* sess_id = NULL;
+ unsigned int sess_id_len = 0;
SSL_SESSION* sess = NULL;
const unsigned char *proto = NULL;
/* API dictates unsigned int rather than size_t. */
ret->server_protocol = SSL_version(server.ssl);
ret->client_protocol = SSL_version(client.ssl);
ret->servername = server_ex_data.servername;
- if ((sess = SSL_get0_session(client.ssl)) != NULL)
+ if ((sess = SSL_get0_session(client.ssl)) != NULL) {
SSL_SESSION_get0_ticket(sess, &tick, &tick_len);
+ sess_id = SSL_SESSION_get_id(sess, &sess_id_len);
+ }
if (tick == NULL || tick_len == 0)
ret->session_ticket = SSL_TEST_SESSION_TICKET_NO;
else
ret->compression = (SSL_get_current_compression(client.ssl) == NULL)
? SSL_TEST_COMPRESSION_NO
: SSL_TEST_COMPRESSION_YES;
+ if (sess_id == NULL || sess_id_len == 0)
+ ret->session_id = SSL_TEST_SESSION_ID_NO;
+ else
+ ret->session_id = SSL_TEST_SESSION_ID_YES;
ret->session_ticket_do_not_call = server_ex_data.session_ticket_do_not_call;
#ifndef OPENSSL_NO_NEXTPROTONEG
int client_sign_type;
/* Client CA names */
STACK_OF(X509_NAME) *client_ca_names;
+ /* Session id status */
+ ssl_session_id_t session_id;
} HANDSHAKE_RESULT;
HANDSHAKE_RESULT *HANDSHAKE_RESULT_new(void);
[test-1]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = Yes
server = 1-sni-session-ticket-server-extra
client = 1-sni-session-ticket-client-extra
[test-2]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = Yes
server = 2-sni-session-ticket-server-extra
client = 2-sni-session-ticket-client-extra
[test-3]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = Yes
server = 3-sni-session-ticket-server-extra
client = 3-sni-session-ticket-client-extra
[test-4]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 4-sni-session-ticket-server-extra
client = 4-sni-session-ticket-client-extra
[test-5]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 5-sni-session-ticket-server-extra
client = 5-sni-session-ticket-client-extra
[test-6]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 6-sni-session-ticket-server-extra
client = 6-sni-session-ticket-client-extra
[test-7]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 7-sni-session-ticket-server-extra
client = 7-sni-session-ticket-client-extra
[test-8]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 8-sni-session-ticket-server-extra
client = 8-sni-session-ticket-client-extra
[test-9]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 9-sni-session-ticket-server-extra
client = 9-sni-session-ticket-client-extra
[test-10]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 10-sni-session-ticket-server-extra
client = 10-sni-session-ticket-client-extra
[test-11]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 11-sni-session-ticket-server-extra
client = 11-sni-session-ticket-client-extra
[test-12]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 12-sni-session-ticket-server-extra
client = 12-sni-session-ticket-client-extra
[test-13]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 13-sni-session-ticket-server-extra
client = 13-sni-session-ticket-client-extra
[test-14]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 14-sni-session-ticket-server-extra
client = 14-sni-session-ticket-client-extra
[test-15]
ExpectedResult = Success
ExpectedServerName = server1
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 15-sni-session-ticket-server-extra
client = 15-sni-session-ticket-client-extra
[test-16]
ExpectedResult = Success
ExpectedServerName = server2
+SessionIdExpected = Yes
SessionTicketExpected = No
server = 16-sni-session-ticket-server-extra
client = 16-sni-session-ticket-client-extra
foreach my $s1 ("SessionTicket", "-SessionTicket") {
foreach my $s2 ("SessionTicket", "-SessionTicket") {
foreach my $n ("server1", "server2") {
- my $result = expected_result($c, $s1, $s2, $n);
+ my $ticket_result = expected_result($c, $s1, $s2, $n);
+ my $session_id_result = "Yes"; # always, even with a ticket
push @tests, {
"name" => "sni-session-ticket",
"client" => {
"test" => {
"ExpectedServerName" => $n,
"ExpectedResult" => "Success",
- "SessionTicketExpected" => $result,
+ "SessionIdExpected" => $session_id_result,
+ "SessionTicketExpected" => $ticket_result,
}
};
}
return 1;
}
+static int check_session_id(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
+{
+ if (test_ctx->session_id_expected == SSL_TEST_SESSION_ID_IGNORE)
+ return 1;
+ if (!TEST_int_eq(result->session_id, test_ctx->session_id_expected)) {
+ TEST_info("Client SessionIdExpected mismatch, expected %s, got %s\n.",
+ ssl_session_id_name(test_ctx->session_id_expected),
+ ssl_session_id_name(result->session_id));
+ return 0;
+ }
+ return 1;
+}
+
static int check_compression(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
{
if (!TEST_int_eq(result->compression, test_ctx->compression_expected))
ret &= check_servername(result, test_ctx);
ret &= check_session_ticket(result, test_ctx);
ret &= check_compression(result, test_ctx);
+ ret &= check_session_id(result, test_ctx);
ret &= (result->session_ticket_do_not_call == 0);
#ifndef OPENSSL_NO_NEXTPROTONEG
ret &= check_npn(result, test_ctx);
IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, compression_expected)
+/* SessionIdExpected */
+
+static const test_enum ssl_session_id[] = {
+ {"Ignore", SSL_TEST_SESSION_ID_IGNORE},
+ {"Yes", SSL_TEST_SESSION_ID_YES},
+ {"No", SSL_TEST_SESSION_ID_NO},
+};
+
+__owur static int parse_session_id(SSL_TEST_CTX *test_ctx, const char *value)
+{
+ int ret_value;
+ if (!parse_enum(ssl_session_id, OSSL_NELEM(ssl_session_id),
+ &ret_value, value)) {
+ return 0;
+ }
+ test_ctx->session_id_expected = ret_value;
+ return 1;
+}
+
+const char *ssl_session_id_name(ssl_session_id_t server)
+{
+ return enum_name(ssl_session_id,
+ OSSL_NELEM(ssl_session_id),
+ server);
+}
+
/* Method */
static const test_enum ssl_test_methods[] = {
{ "ExpectedServerName", &parse_expected_servername },
{ "SessionTicketExpected", &parse_session_ticket },
{ "CompressionExpected", &parse_test_compression_expected },
+ { "SessionIdExpected", &parse_session_id },
{ "Method", &parse_test_method },
{ "ExpectedNPNProtocol", &parse_test_expected_npn_protocol },
{ "ExpectedALPNProtocol", &parse_test_expected_alpn_protocol },
SSL_TEST_COMPRESSION_YES
} ssl_compression_t;
+typedef enum {
+ SSL_TEST_SESSION_ID_IGNORE = 0, /* Default */
+ SSL_TEST_SESSION_ID_YES,
+ SSL_TEST_SESSION_ID_NO
+} ssl_session_id_t;
+
typedef enum {
SSL_TEST_METHOD_TLS = 0, /* Default */
SSL_TEST_METHOD_DTLS
STACK_OF(X509_NAME) *expected_client_ca_names;
/* Whether to use SCTP for the transport */
int use_sctp;
+ /* Whether to expect a session id from the server */
+ ssl_session_id_t session_id_expected;
} SSL_TEST_CTX;
const char *ssl_test_result_name(ssl_test_result_t result);
const char *ssl_servername_callback_name(ssl_servername_callback_t
servername_callback);
const char *ssl_session_ticket_name(ssl_session_ticket_t server);
+const char *ssl_session_id_name(ssl_session_id_t server);
const char *ssl_test_method_name(ssl_test_method_t method);
const char *ssl_handshake_mode_name(ssl_handshake_mode_t mode);
const char *ssl_ct_validation_name(ssl_ct_validation_t mode);
|| !TEST_str_eq(ctx->expected_alpn_protocol,
ctx2->expected_alpn_protocol)
|| !TEST_int_eq(ctx->resumption_expected,
- ctx2->resumption_expected))
+ ctx2->resumption_expected)
+ || !TEST_int_eq(ctx->session_id_expected,
+ ctx2->session_id_expected))
return 0;
return 1;
}
fixture->expected_ctx->expected_servername = SSL_TEST_SERVERNAME_SERVER2;
fixture->expected_ctx->session_ticket_expected = SSL_TEST_SESSION_TICKET_YES;
fixture->expected_ctx->compression_expected = SSL_TEST_COMPRESSION_NO;
+ fixture->expected_ctx->session_id_expected = SSL_TEST_SESSION_ID_IGNORE;
fixture->expected_ctx->resumption_expected = 1;
fixture->expected_ctx->extra.client.verify_callback =
"ssltest_unknown_servername_callback",
"ssltest_unknown_session_ticket_expected",
"ssltest_unknown_compression_expected",
+ "ssltest_unknown_session_id_expected",
"ssltest_unknown_method",
"ssltest_unknown_handshake_mode",
"ssltest_unknown_resumption_expected",
[ssltest_unknown_compression_expected]
CompressionExpected = Foo
+[ssltest_unknown_session_id_expected]
+SessionIdExpected = Foo
+
[ssltest_unknown_method]
Method = TLS2