in src/ssl/ssl_asn1.cc [555:809]
UniquePtr<SSL_SESSION> SSL_SESSION_parse(CBS *cbs,
const SSL_X509_METHOD *x509_method,
CRYPTO_BUFFER_POOL *pool) {
UniquePtr<SSL_SESSION> ret = ssl_session_new(x509_method);
if (!ret) {
return nullptr;
}
CBS session;
uint64_t version, ssl_version;
uint16_t unused;
if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&session, &version) ||
version != kVersion ||
!CBS_get_asn1_uint64(&session, &ssl_version) ||
// Require sessions have versions valid in either TLS or DTLS. The session
// will not be used by the handshake if not applicable, but, for
// simplicity, never parse a session that does not pass
// |ssl_protocol_version_from_wire|.
ssl_version > UINT16_MAX ||
!ssl_protocol_version_from_wire(&unused, ssl_version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
ret->ssl_version = ssl_version;
CBS cipher;
uint16_t cipher_value;
if (!CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) ||
!CBS_get_u16(&cipher, &cipher_value) ||
CBS_len(&cipher) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
ret->cipher = SSL_get_cipher_by_value(cipher_value);
if (ret->cipher == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_CIPHER);
return nullptr;
}
CBS session_id, secret;
if (!CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) ||
CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH ||
!CBS_get_asn1(&session, &secret, CBS_ASN1_OCTETSTRING) ||
CBS_len(&secret) > SSL_MAX_MASTER_KEY_LENGTH) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
OPENSSL_memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id));
ret->session_id_length = CBS_len(&session_id);
OPENSSL_memcpy(ret->secret, CBS_data(&secret), CBS_len(&secret));
ret->secret_length = CBS_len(&secret);
CBS child;
uint64_t timeout;
if (!CBS_get_asn1(&session, &child, kTimeTag) ||
!CBS_get_asn1_uint64(&child, &ret->time) ||
!CBS_get_asn1(&session, &child, kTimeoutTag) ||
!CBS_get_asn1_uint64(&child, &timeout) ||
timeout > UINT32_MAX) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
ret->timeout = (uint32_t)timeout;
CBS peer;
int has_peer;
if (!CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag) ||
(has_peer && CBS_len(&peer) == 0)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
// |peer| is processed with the certificate chain.
if (!SSL_SESSION_parse_bounded_octet_string(
&session, ret->sid_ctx, &ret->sid_ctx_length, sizeof(ret->sid_ctx),
kSessionIDContextTag) ||
!SSL_SESSION_parse_long(&session, &ret->verify_result, kVerifyResultTag,
X509_V_OK)) {
return nullptr;
}
// Skip the historical hostName field.
CBS unused_hostname;
if (!CBS_get_optional_asn1(&session, &unused_hostname, nullptr,
kHostNameTag)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
if (!SSL_SESSION_parse_string(&session, &ret->psk_identity,
kPSKIdentityTag) ||
!SSL_SESSION_parse_u32(&session, &ret->ticket_lifetime_hint,
kTicketLifetimeHintTag, 0) ||
!SSL_SESSION_parse_octet_string(&session, &ret->ticket, kTicketTag)) {
return nullptr;
}
if (CBS_peek_asn1_tag(&session, kPeerSHA256Tag)) {
CBS peer_sha256;
if (!CBS_get_asn1(&session, &child, kPeerSHA256Tag) ||
!CBS_get_asn1(&child, &peer_sha256, CBS_ASN1_OCTETSTRING) ||
CBS_len(&peer_sha256) != sizeof(ret->peer_sha256) ||
CBS_len(&child) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
OPENSSL_memcpy(ret->peer_sha256, CBS_data(&peer_sha256),
sizeof(ret->peer_sha256));
ret->peer_sha256_valid = 1;
} else {
ret->peer_sha256_valid = 0;
}
if (!SSL_SESSION_parse_bounded_octet_string(
&session, ret->original_handshake_hash,
&ret->original_handshake_hash_len,
sizeof(ret->original_handshake_hash), kOriginalHandshakeHashTag) ||
!SSL_SESSION_parse_crypto_buffer(&session,
&ret->signed_cert_timestamp_list,
kSignedCertTimestampListTag, pool) ||
!SSL_SESSION_parse_crypto_buffer(&session, &ret->ocsp_response,
kOCSPResponseTag, pool)) {
return nullptr;
}
int extended_master_secret;
if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret,
kExtendedMasterSecretTag,
0 /* default to false */)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
ret->extended_master_secret = !!extended_master_secret;
if (!SSL_SESSION_parse_u16(&session, &ret->group_id, kGroupIDTag, 0)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
CBS cert_chain;
CBS_init(&cert_chain, NULL, 0);
int has_cert_chain;
if (!CBS_get_optional_asn1(&session, &cert_chain, &has_cert_chain,
kCertChainTag) ||
(has_cert_chain && CBS_len(&cert_chain) == 0)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
if (has_cert_chain && !has_peer) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
if (has_peer || has_cert_chain) {
ret->certs.reset(sk_CRYPTO_BUFFER_new_null());
if (ret->certs == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return nullptr;
}
if (has_peer) {
UniquePtr<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new_from_CBS(&peer, pool));
if (!buffer ||
!PushToStack(ret->certs.get(), std::move(buffer))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return nullptr;
}
}
while (CBS_len(&cert_chain) > 0) {
CBS cert;
if (!CBS_get_any_asn1_element(&cert_chain, &cert, NULL, NULL) ||
CBS_len(&cert) == 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
UniquePtr<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new_from_CBS(&cert, pool));
if (buffer == nullptr ||
!PushToStack(ret->certs.get(), std::move(buffer))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return nullptr;
}
}
}
CBS age_add;
int age_add_present;
if (!CBS_get_optional_asn1_octet_string(&session, &age_add, &age_add_present,
kTicketAgeAddTag) ||
(age_add_present &&
!CBS_get_u32(&age_add, &ret->ticket_age_add)) ||
CBS_len(&age_add) != 0) {
return nullptr;
}
ret->ticket_age_add_valid = age_add_present != 0;
int is_server;
if (!CBS_get_optional_asn1_bool(&session, &is_server, kIsServerTag,
1 /* default to true */)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
/* TODO: in time we can include |is_server| for servers too, then we can
enforce that client and server sessions are never mixed up. */
ret->is_server = is_server;
int is_quic;
if (!SSL_SESSION_parse_u16(&session, &ret->peer_signature_algorithm,
kPeerSignatureAlgorithmTag, 0) ||
!SSL_SESSION_parse_u32(&session, &ret->ticket_max_early_data,
kTicketMaxEarlyDataTag, 0) ||
!SSL_SESSION_parse_u32(&session, &ret->auth_timeout, kAuthTimeoutTag,
ret->timeout) ||
!SSL_SESSION_parse_octet_string(&session, &ret->early_alpn,
kEarlyALPNTag) ||
!CBS_get_optional_asn1_bool(&session, &is_quic, kIsQuicTag,
/*default_value=*/false) ||
!SSL_SESSION_parse_octet_string(&session, &ret->quic_early_data_context,
kQuicEarlyDataContextTag)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
CBS settings;
int has_local_alps, has_peer_alps;
if (!CBS_get_optional_asn1_octet_string(&session, &settings, &has_local_alps,
kLocalALPSTag) ||
!ret->local_application_settings.CopyFrom(settings) ||
!CBS_get_optional_asn1_octet_string(&session, &settings, &has_peer_alps,
kPeerALPSTag) ||
!ret->peer_application_settings.CopyFrom(settings) ||
CBS_len(&session) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
ret->is_quic = is_quic;
// The two ALPS values and ALPN must be consistent.
if (has_local_alps != has_peer_alps ||
(has_local_alps && ret->early_alpn.empty())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
ret->has_application_settings = has_local_alps;
if (!x509_method->session_cache_objects(ret.get())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
}
return ret;
}