// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/http/http_response_info.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/pickle.h" #include "base/time/time.h" #include "net/base/net_errors.h" #include "net/cert/sct_status_flags.h" #include "net/cert/signed_certificate_timestamp.h" #include "net/cert/x509_certificate.h" #include "net/http/http_response_headers.h" #include "net/ssl/ssl_cert_request_info.h" #include "net/ssl/ssl_connection_status_flags.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h" #include "third_party/boringssl/src/include/openssl/ssl.h" using base::Time; namespace net { namespace { bool KeyExchangeGroupIsValid(int ssl_connection_status) { // TLS 1.3 and later always treat the field correctly. if (SSLConnectionStatusToVersion(ssl_connection_status) >= SSL_CONNECTION_VERSION_TLS1_3) { return true; } // Prior to TLS 1.3, only ECDHE ciphers have groups. const SSL_CIPHER* cipher = SSL_get_cipher_by_value( SSLConnectionStatusToCipherSuite(ssl_connection_status)); return cipher && SSL_CIPHER_get_kx_nid(cipher) == NID_kx_ecdhe; } } // namespace // These values can be bit-wise combined to form the flags field of the // serialized HttpResponseInfo. enum { // The version of the response info used when persisting response info. RESPONSE_INFO_VERSION = 3, // The minimum version supported for deserializing response info. RESPONSE_INFO_MINIMUM_VERSION = 3, // We reserve up to 8 bits for the version number. RESPONSE_INFO_VERSION_MASK = 0xFF, // This bit is set if the response info has a cert at the end. // Version 1 serialized only the end-entity certificate, while subsequent // versions include the available certificate chain. RESPONSE_INFO_HAS_CERT = 1 << 8, // This bit was historically set if the response info had a security-bits // field (security strength, in bits, of the SSL connection) at the end. RESPONSE_INFO_HAS_SECURITY_BITS = 1 << 9, // This bit is set if the response info has a cert status at the end. RESPONSE_INFO_HAS_CERT_STATUS = 1 << 10, // This bit is set if the response info has vary header data. RESPONSE_INFO_HAS_VARY_DATA = 1 << 11, // This bit is set if the request was cancelled before completion. RESPONSE_INFO_TRUNCATED = 1 << 12, // This bit is set if the response was received via SPDY. RESPONSE_INFO_WAS_SPDY = 1 << 13, // This bit is set if the request has ALPN negotiated. RESPONSE_INFO_WAS_ALPN = 1 << 14, // This bit is set if the request was fetched via an explicit proxy. RESPONSE_INFO_WAS_PROXY = 1 << 15, // This bit is set if the response info has an SSL connection status field. // This contains the ciphersuite used to fetch the resource as well as the // protocol version, compression method and whether SSLv3 fallback was used. RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS = 1 << 16, // This bit is set if the response info has protocol version. RESPONSE_INFO_HAS_ALPN_NEGOTIATED_PROTOCOL = 1 << 17, // This bit is set if the response info has connection info. RESPONSE_INFO_HAS_CONNECTION_INFO = 1 << 18, // This bit is set if the request has http authentication. RESPONSE_INFO_USE_HTTP_AUTHENTICATION = 1 << 19, // This bit is set if ssl_info has SCTs. RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS = 1 << 20, RESPONSE_INFO_UNUSED_SINCE_PREFETCH = 1 << 21, // This bit is set if the response has a key exchange group. RESPONSE_INFO_HAS_KEY_EXCHANGE_GROUP = 1 << 22, // This bit is set if ssl_info recorded that PKP was bypassed due to a local // trust anchor. RESPONSE_INFO_PKP_BYPASSED = 1 << 23, // This bit is set if stale_revalidate_time is stored. RESPONSE_INFO_HAS_STALENESS = 1 << 24, // This bit is set if the response has a peer signature algorithm. RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM = 1 << 25, // This bit is set if the response is a prefetch whose reuse should be // restricted in some way. RESPONSE_INFO_RESTRICTED_PREFETCH = 1 << 26, // This bit is set if the response has a nonempty `dns_aliases` entry. RESPONSE_INFO_HAS_DNS_ALIASES = 1 << 27, // This bit is set for an entry in the single-keyed cache that has been marked // unusable due to the checksum not matching. RESPONSE_INFO_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE = 1 << 28, // This bit is set if the response has `encrypted_client_hello` set. RESPONSE_INFO_ENCRYPTED_CLIENT_HELLO = 1 << 29, // This enum only has a few bits (`1 << 31` is the limit). If allocating the // last flag, instead allocate it as `RESPONSE_INFO_HAS_EXTRA_FLAGS` to // signal another flags word. }; HttpResponseInfo::ConnectionInfoCoarse HttpResponseInfo::ConnectionInfoToCoarse( ConnectionInfo info) { switch (info) { case CONNECTION_INFO_HTTP0_9: case CONNECTION_INFO_HTTP1_0: case CONNECTION_INFO_HTTP1_1: return CONNECTION_INFO_COARSE_HTTP1; case CONNECTION_INFO_HTTP2: case CONNECTION_INFO_DEPRECATED_SPDY2: case CONNECTION_INFO_DEPRECATED_SPDY3: case CONNECTION_INFO_DEPRECATED_HTTP2_14: case CONNECTION_INFO_DEPRECATED_HTTP2_15: return CONNECTION_INFO_COARSE_HTTP2; case CONNECTION_INFO_QUIC_UNKNOWN_VERSION: case CONNECTION_INFO_QUIC_32: case CONNECTION_INFO_QUIC_33: case CONNECTION_INFO_QUIC_34: case CONNECTION_INFO_QUIC_35: case CONNECTION_INFO_QUIC_36: case CONNECTION_INFO_QUIC_37: case CONNECTION_INFO_QUIC_38: case CONNECTION_INFO_QUIC_39: case CONNECTION_INFO_QUIC_40: case CONNECTION_INFO_QUIC_41: case CONNECTION_INFO_QUIC_42: case CONNECTION_INFO_QUIC_43: case CONNECTION_INFO_QUIC_44: case CONNECTION_INFO_QUIC_45: case CONNECTION_INFO_QUIC_46: case CONNECTION_INFO_QUIC_47: case CONNECTION_INFO_QUIC_Q048: case CONNECTION_INFO_QUIC_T048: case CONNECTION_INFO_QUIC_Q049: case CONNECTION_INFO_QUIC_T049: case CONNECTION_INFO_QUIC_Q050: case CONNECTION_INFO_QUIC_T050: case CONNECTION_INFO_QUIC_Q099: case CONNECTION_INFO_QUIC_T099: case CONNECTION_INFO_QUIC_999: case CONNECTION_INFO_QUIC_DRAFT_25: case CONNECTION_INFO_QUIC_DRAFT_27: case CONNECTION_INFO_QUIC_DRAFT_28: case CONNECTION_INFO_QUIC_DRAFT_29: case CONNECTION_INFO_QUIC_T051: case CONNECTION_INFO_QUIC_RFC_V1: case CONNECTION_INFO_QUIC_2_DRAFT_1: return CONNECTION_INFO_COARSE_QUIC; case CONNECTION_INFO_UNKNOWN: return CONNECTION_INFO_COARSE_OTHER; case NUM_OF_CONNECTION_INFOS: NOTREACHED(); return CONNECTION_INFO_COARSE_OTHER; } NOTREACHED(); return CONNECTION_INFO_COARSE_OTHER; } HttpResponseInfo::HttpResponseInfo() = default; HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs) = default; HttpResponseInfo::~HttpResponseInfo() = default; HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) = default; bool HttpResponseInfo::InitFromPickle(const base::Pickle& pickle, bool* response_truncated) { base::PickleIterator iter(pickle); // Read flags and verify version int flags; if (!iter.ReadInt(&flags)) return false; int version = flags & RESPONSE_INFO_VERSION_MASK; if (version < RESPONSE_INFO_MINIMUM_VERSION || version > RESPONSE_INFO_VERSION) { DLOG(ERROR) << "unexpected response info version: " << version; return false; } // Read request-time int64_t time_val; if (!iter.ReadInt64(&time_val)) return false; request_time = Time::FromInternalValue(time_val); was_cached = true; // Set status to show cache resurrection. // Read response-time if (!iter.ReadInt64(&time_val)) return false; response_time = Time::FromInternalValue(time_val); // Read response-headers headers = base::MakeRefCounted(&iter); if (headers->response_code() == -1) return false; // Read ssl-info if (flags & RESPONSE_INFO_HAS_CERT) { ssl_info.cert = X509Certificate::CreateFromPickle(&iter); if (!ssl_info.cert.get()) return false; } if (flags & RESPONSE_INFO_HAS_CERT_STATUS) { CertStatus cert_status; if (!iter.ReadUInt32(&cert_status)) return false; ssl_info.cert_status = cert_status; } if (flags & RESPONSE_INFO_HAS_SECURITY_BITS) { // The security_bits field has been removed from ssl_info. For backwards // compatibility, we should still read the value out of iter. int security_bits; if (!iter.ReadInt(&security_bits)) return false; } if (flags & RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS) { int connection_status; if (!iter.ReadInt(&connection_status)) return false; // SSLv3 is gone, so drop cached entries that were loaded over SSLv3. if (SSLConnectionStatusToVersion(connection_status) == SSL_CONNECTION_VERSION_SSL3) { return false; } ssl_info.connection_status = connection_status; } // Signed Certificate Timestamps are no longer persisted to the cache, so // ignore them when reading them out. if (flags & RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS) { int num_scts; if (!iter.ReadInt(&num_scts)) return false; for (int i = 0; i < num_scts; ++i) { scoped_refptr sct( ct::SignedCertificateTimestamp::CreateFromPickle(&iter)); uint16_t status; if (!sct.get() || !iter.ReadUInt16(&status)) return false; } } // Read vary-data if (flags & RESPONSE_INFO_HAS_VARY_DATA) { if (!vary_data.InitFromPickle(&iter)) return false; } // Read socket_address. std::string socket_address_host; if (!iter.ReadString(&socket_address_host)) return false; // If the host was written, we always expect the port to follow. uint16_t socket_address_port; if (!iter.ReadUInt16(&socket_address_port)) return false; IPAddress ip_address; if (ip_address.AssignFromIPLiteral(socket_address_host)) { remote_endpoint = IPEndPoint(ip_address, socket_address_port); } else if (ParseURLHostnameToAddress(socket_address_host, &ip_address)) { remote_endpoint = IPEndPoint(ip_address, socket_address_port); } // Read protocol-version. if (flags & RESPONSE_INFO_HAS_ALPN_NEGOTIATED_PROTOCOL) { if (!iter.ReadString(&alpn_negotiated_protocol)) return false; } // Read connection info. if (flags & RESPONSE_INFO_HAS_CONNECTION_INFO) { int value; if (!iter.ReadInt(&value)) return false; if (value > static_cast(CONNECTION_INFO_UNKNOWN) && value < static_cast(NUM_OF_CONNECTION_INFOS)) { connection_info = static_cast(value); } } // Read key_exchange_group if (flags & RESPONSE_INFO_HAS_KEY_EXCHANGE_GROUP) { int key_exchange_group; if (!iter.ReadInt(&key_exchange_group)) return false; // Historically, the key_exchange_group field was key_exchange_info which // conflated a number of different values based on the cipher suite, so some // values must be discarded. See https://crbug.com/639421. if (KeyExchangeGroupIsValid(ssl_info.connection_status)) ssl_info.key_exchange_group = key_exchange_group; } // Read staleness time. if (flags & RESPONSE_INFO_HAS_STALENESS) { if (!iter.ReadInt64(&time_val)) return false; stale_revalidate_timeout = base::Time() + base::Microseconds(time_val); } was_fetched_via_spdy = (flags & RESPONSE_INFO_WAS_SPDY) != 0; was_alpn_negotiated = (flags & RESPONSE_INFO_WAS_ALPN) != 0; was_fetched_via_proxy = (flags & RESPONSE_INFO_WAS_PROXY) != 0; *response_truncated = (flags & RESPONSE_INFO_TRUNCATED) != 0; did_use_http_auth = (flags & RESPONSE_INFO_USE_HTTP_AUTHENTICATION) != 0; unused_since_prefetch = (flags & RESPONSE_INFO_UNUSED_SINCE_PREFETCH) != 0; restricted_prefetch = (flags & RESPONSE_INFO_RESTRICTED_PREFETCH) != 0; single_keyed_cache_entry_unusable = (flags & RESPONSE_INFO_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE) != 0; ssl_info.pkp_bypassed = (flags & RESPONSE_INFO_PKP_BYPASSED) != 0; // Read peer_signature_algorithm. if (flags & RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM) { int peer_signature_algorithm; if (!iter.ReadInt(&peer_signature_algorithm) || !base::IsValueInRangeForNumericType( peer_signature_algorithm)) { return false; } ssl_info.peer_signature_algorithm = base::checked_cast(peer_signature_algorithm); } // Read DNS aliases. if (flags & RESPONSE_INFO_HAS_DNS_ALIASES) { int num_aliases; if (!iter.ReadInt(&num_aliases)) return false; std::string alias; for (int i = 0; i < num_aliases; i++) { if (!iter.ReadString(&alias)) return false; dns_aliases.insert(alias); } } ssl_info.encrypted_client_hello = (flags & RESPONSE_INFO_ENCRYPTED_CLIENT_HELLO) != 0; return true; } void HttpResponseInfo::Persist(base::Pickle* pickle, bool skip_transient_headers, bool response_truncated) const { int flags = RESPONSE_INFO_VERSION; if (ssl_info.is_valid()) { flags |= RESPONSE_INFO_HAS_CERT; flags |= RESPONSE_INFO_HAS_CERT_STATUS; if (ssl_info.key_exchange_group != 0) flags |= RESPONSE_INFO_HAS_KEY_EXCHANGE_GROUP; if (ssl_info.connection_status != 0) flags |= RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS; if (ssl_info.peer_signature_algorithm != 0) flags |= RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM; } if (vary_data.is_valid()) flags |= RESPONSE_INFO_HAS_VARY_DATA; if (response_truncated) flags |= RESPONSE_INFO_TRUNCATED; if (was_fetched_via_spdy) flags |= RESPONSE_INFO_WAS_SPDY; if (was_alpn_negotiated) { flags |= RESPONSE_INFO_WAS_ALPN; flags |= RESPONSE_INFO_HAS_ALPN_NEGOTIATED_PROTOCOL; } if (was_fetched_via_proxy) flags |= RESPONSE_INFO_WAS_PROXY; if (connection_info != CONNECTION_INFO_UNKNOWN) flags |= RESPONSE_INFO_HAS_CONNECTION_INFO; if (did_use_http_auth) flags |= RESPONSE_INFO_USE_HTTP_AUTHENTICATION; if (unused_since_prefetch) flags |= RESPONSE_INFO_UNUSED_SINCE_PREFETCH; if (restricted_prefetch) flags |= RESPONSE_INFO_RESTRICTED_PREFETCH; if (single_keyed_cache_entry_unusable) flags |= RESPONSE_INFO_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE; if (ssl_info.pkp_bypassed) flags |= RESPONSE_INFO_PKP_BYPASSED; if (!stale_revalidate_timeout.is_null()) flags |= RESPONSE_INFO_HAS_STALENESS; if (!dns_aliases.empty()) flags |= RESPONSE_INFO_HAS_DNS_ALIASES; if (ssl_info.encrypted_client_hello) flags |= RESPONSE_INFO_ENCRYPTED_CLIENT_HELLO; pickle->WriteInt(flags); pickle->WriteInt64(request_time.ToInternalValue()); pickle->WriteInt64(response_time.ToInternalValue()); HttpResponseHeaders::PersistOptions persist_options = HttpResponseHeaders::PERSIST_RAW; if (skip_transient_headers) { persist_options = HttpResponseHeaders::PERSIST_SANS_COOKIES | HttpResponseHeaders::PERSIST_SANS_CHALLENGES | HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP | HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE | HttpResponseHeaders::PERSIST_SANS_RANGES | HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE; } headers->Persist(pickle, persist_options); if (ssl_info.is_valid()) { ssl_info.cert->Persist(pickle); pickle->WriteUInt32(ssl_info.cert_status); if (ssl_info.connection_status != 0) pickle->WriteInt(ssl_info.connection_status); } if (vary_data.is_valid()) vary_data.Persist(pickle); pickle->WriteString(remote_endpoint.ToStringWithoutPort()); pickle->WriteUInt16(remote_endpoint.port()); if (was_alpn_negotiated) pickle->WriteString(alpn_negotiated_protocol); if (connection_info != CONNECTION_INFO_UNKNOWN) pickle->WriteInt(static_cast(connection_info)); if (ssl_info.is_valid() && ssl_info.key_exchange_group != 0) pickle->WriteInt(ssl_info.key_exchange_group); if (flags & RESPONSE_INFO_HAS_STALENESS) { pickle->WriteInt64( (stale_revalidate_timeout - base::Time()).InMicroseconds()); } if (ssl_info.is_valid() && ssl_info.peer_signature_algorithm != 0) pickle->WriteInt(ssl_info.peer_signature_algorithm); if (!dns_aliases.empty()) { pickle->WriteInt(dns_aliases.size()); for (const auto& alias : dns_aliases) pickle->WriteString(alias); } } bool HttpResponseInfo::DidUseQuic() const { switch (connection_info) { case CONNECTION_INFO_UNKNOWN: case CONNECTION_INFO_HTTP1_1: case CONNECTION_INFO_DEPRECATED_SPDY2: case CONNECTION_INFO_DEPRECATED_SPDY3: case CONNECTION_INFO_HTTP2: case CONNECTION_INFO_DEPRECATED_HTTP2_14: case CONNECTION_INFO_DEPRECATED_HTTP2_15: case CONNECTION_INFO_HTTP0_9: case CONNECTION_INFO_HTTP1_0: return false; case CONNECTION_INFO_QUIC_UNKNOWN_VERSION: case CONNECTION_INFO_QUIC_32: case CONNECTION_INFO_QUIC_33: case CONNECTION_INFO_QUIC_34: case CONNECTION_INFO_QUIC_35: case CONNECTION_INFO_QUIC_36: case CONNECTION_INFO_QUIC_37: case CONNECTION_INFO_QUIC_38: case CONNECTION_INFO_QUIC_39: case CONNECTION_INFO_QUIC_40: case CONNECTION_INFO_QUIC_41: case CONNECTION_INFO_QUIC_42: case CONNECTION_INFO_QUIC_43: case CONNECTION_INFO_QUIC_44: case CONNECTION_INFO_QUIC_45: case CONNECTION_INFO_QUIC_46: case CONNECTION_INFO_QUIC_47: case CONNECTION_INFO_QUIC_Q048: case CONNECTION_INFO_QUIC_T048: case CONNECTION_INFO_QUIC_Q049: case CONNECTION_INFO_QUIC_T049: case CONNECTION_INFO_QUIC_Q050: case CONNECTION_INFO_QUIC_T050: case CONNECTION_INFO_QUIC_Q099: case CONNECTION_INFO_QUIC_T099: case CONNECTION_INFO_QUIC_999: case CONNECTION_INFO_QUIC_DRAFT_25: case CONNECTION_INFO_QUIC_DRAFT_27: case CONNECTION_INFO_QUIC_DRAFT_28: case CONNECTION_INFO_QUIC_DRAFT_29: case CONNECTION_INFO_QUIC_T051: case CONNECTION_INFO_QUIC_RFC_V1: case CONNECTION_INFO_QUIC_2_DRAFT_1: return true; case NUM_OF_CONNECTION_INFOS: NOTREACHED(); return false; } NOTREACHED(); return false; } // static std::string HttpResponseInfo::ConnectionInfoToString( ConnectionInfo connection_info) { switch (connection_info) { case CONNECTION_INFO_UNKNOWN: return "unknown"; case CONNECTION_INFO_HTTP1_1: return "http/1.1"; case CONNECTION_INFO_DEPRECATED_SPDY2: NOTREACHED(); return ""; case CONNECTION_INFO_DEPRECATED_SPDY3: return "spdy/3"; // Since ConnectionInfo is persisted to disk, deprecated values have to be // handled. Note that h2-14 and h2-15 are essentially wire compatible with // h2. // Intentional fallthrough. case CONNECTION_INFO_DEPRECATED_HTTP2_14: case CONNECTION_INFO_DEPRECATED_HTTP2_15: case CONNECTION_INFO_HTTP2: return "h2"; case CONNECTION_INFO_QUIC_UNKNOWN_VERSION: return "http/2+quic"; case CONNECTION_INFO_QUIC_32: return "http/2+quic/32"; case CONNECTION_INFO_QUIC_33: return "http/2+quic/33"; case CONNECTION_INFO_QUIC_34: return "http/2+quic/34"; case CONNECTION_INFO_QUIC_35: return "http/2+quic/35"; case CONNECTION_INFO_QUIC_36: return "http/2+quic/36"; case CONNECTION_INFO_QUIC_37: return "http/2+quic/37"; case CONNECTION_INFO_QUIC_38: return "http/2+quic/38"; case CONNECTION_INFO_QUIC_39: return "http/2+quic/39"; case CONNECTION_INFO_QUIC_40: return "http/2+quic/40"; case CONNECTION_INFO_QUIC_41: return "http/2+quic/41"; case CONNECTION_INFO_QUIC_42: return "http/2+quic/42"; case CONNECTION_INFO_QUIC_43: return "http/2+quic/43"; case CONNECTION_INFO_QUIC_44: return "http/2+quic/44"; case CONNECTION_INFO_QUIC_45: return "http/2+quic/45"; case CONNECTION_INFO_QUIC_46: return "http/2+quic/46"; case CONNECTION_INFO_QUIC_47: return "http/2+quic/47"; case CONNECTION_INFO_QUIC_Q048: return "h3-Q048"; case CONNECTION_INFO_QUIC_T048: return "h3-T048"; case CONNECTION_INFO_QUIC_Q049: return "h3-Q049"; case CONNECTION_INFO_QUIC_T049: return "h3-T049"; case CONNECTION_INFO_QUIC_Q050: return "h3-Q050"; case CONNECTION_INFO_QUIC_T050: return "h3-T050"; case CONNECTION_INFO_QUIC_Q099: return "h3-Q099"; case CONNECTION_INFO_QUIC_DRAFT_25: return "h3-25"; case CONNECTION_INFO_QUIC_DRAFT_27: return "h3-27"; case CONNECTION_INFO_QUIC_DRAFT_28: return "h3-28"; case CONNECTION_INFO_QUIC_DRAFT_29: return "h3-29"; case CONNECTION_INFO_QUIC_T099: return "h3-T099"; case CONNECTION_INFO_HTTP0_9: return "http/0.9"; case CONNECTION_INFO_HTTP1_0: return "http/1.0"; case CONNECTION_INFO_QUIC_999: return "http2+quic/999"; case CONNECTION_INFO_QUIC_T051: return "h3-T051"; case CONNECTION_INFO_QUIC_RFC_V1: return "h3"; case CONNECTION_INFO_QUIC_2_DRAFT_1: return "h3/quic2draft01"; case NUM_OF_CONNECTION_INFOS: break; } NOTREACHED(); return ""; } } // namespace net