assert(checklist != NULL && checklist->request != NULL);
const char *serverName = nullptr;
- SBuf serverNameKeeper; // because c_str() is not constant
+ SBuf clientSniKeeper; // because c_str() is not constant
if (ConnStateData *conn = checklist->conn()) {
-
- if (conn->serverBump()) {
- if (X509 *peer_cert = conn->serverBump()->serverCert.get())
- return Ssl::matchX509CommonNames(peer_cert, (void *)data, check_cert_domain<MatchType>);
- }
-
- if (conn->sslCommonName().isEmpty()) {
+ const char *clientRequestedServerName = nullptr;
+ clientSniKeeper = conn->tlsClientSni();
+ if (clientSniKeeper.isEmpty()) {
const char *host = checklist->request->url.host();
if (host && *host) // paranoid first condition: host() is never nil
- serverName = host;
- } else {
- serverNameKeeper = conn->sslCommonName();
- serverName = serverNameKeeper.c_str();
+ clientRequestedServerName = host;
+ } else
+ clientRequestedServerName = clientSniKeeper.c_str();
+
+ if (useConsensus) {
+ X509 *peer_cert = conn->serverBump() ? conn->serverBump()->serverCert.get() : nullptr;
+ // use the client requested name if it matches the server
+ // certificate or if the certificate is not available
+ if (!peer_cert || Ssl::checkX509ServerValidity(peer_cert, clientRequestedServerName))
+ serverName = clientRequestedServerName;
+ } else if (useClientRequested)
+ serverName = clientRequestedServerName;
+ else { // either no options or useServerProvided
+ if (X509 *peer_cert = (conn->serverBump() ? conn->serverBump()->serverCert.get() : nullptr))
+ return Ssl::matchX509CommonNames(peer_cert, (void *)data, check_cert_domain<MatchType>);
+ if (!useServerProvided)
+ serverName = clientRequestedServerName;
}
}
return data->match(serverName);
}
+const Acl::Options &
+ACLServerNameStrategy::options()
+{
+ static const Acl::BooleanOption ClientRequested;
+ static const Acl::BooleanOption ServerProvided;
+ static const Acl::BooleanOption Consensus;
+ static const Acl::Options MyOptions = {
+ {"--client-requested", &ClientRequested},
+ {"--server-provided", &ServerProvided},
+ {"--consensus", &Consensus}
+ };
+
+ ClientRequested.linkWith(&useClientRequested);
+ ServerProvided.linkWith(&useServerProvided);
+ Consensus.linkWith(&useConsensus);
+ return MyOptions;
+}
+
+bool
+ACLServerNameStrategy::valid() const
+{
+ int optionCount = 0;
+
+ if (useClientRequested)
+ optionCount++;
+ if (useServerProvided)
+ optionCount++;
+ if (useConsensus)
+ optionCount++;
+
+ if (optionCount > 1) {
+ debugs(28, DBG_CRITICAL, "ERROR: Multiple options given for the server_name ACL");
+ return false;
+ }
+ return true;
+}
/* ACLStrategy API */
virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *);
virtual bool requiresRequest() const {return true;}
+ virtual const Acl::Options &options();
+ virtual bool valid() const;
+private:
+ Acl::BooleanOptionValue useClientRequested; ///< Ignore server-supplied names
+ Acl::BooleanOptionValue useServerProvided; ///< Ignore client-supplied names
+ Acl::BooleanOptionValue useConsensus; ///< Ignore mismatching names
};
#endif /* SQUID_ACLSERVERNAME_H */
# SslBump2: After getting SSL Client Hello info.
# SslBump3: After getting SSL Server Hello info.
- acl aclname ssl::server_name .foo.com ...
+ acl aclname ssl::server_name [option] .foo.com ...
# matches server name obtained from various sources [fast]
#
- # The server name is obtained during Ssl-Bump steps from such sources
- # as CONNECT request URI, client SNI, and SSL server certificate CN.
- # During each Ssl-Bump step, Squid may improve its understanding of a
- # "true server name". Unlike dstdomain, this ACL does not perform
- # DNS lookups.
- # The "none" name can be used to match transactions where Squid
+ # The ACL computes server name(s) using such information sources as
+ # CONNECT request URI, TLS client SNI, and TLS server certificate
+ # subject (CN and SubjectAltName). The computed server name(s) usually
+ # change with each SslBump step, as more info becomes available:
+ # * SNI is used as the server name instead of the request URI,
+ # * subject name(s) from the server certificate (CN and
+ # SubjectAltName) are used as the server names instead of SNI.
+ #
+ # When the ACL computes multiple server names, matching any single
+ # computed name is sufficient for the ACL to match.
+ #
+ # The "none" name can be used to match transactions where the ACL
# could not compute the server name using any information source
- # already available at the ACL evaluation time.
+ # that was both available and allowed to be used by the ACL options at
+ # the ACL evaluation time.
+ #
+ # Unlike dstdomain, this ACL does not perform DNS lookups.
+ #
+ # An ACL option below may be used to restrict what information
+ # sources are used to extract the server names from:
+ #
+ # --client-requested
+ # The server name is SNI regardless of what the server says.
+ # --server-provided
+ # The server name(s) are the certificate subject name(s), regardless
+ # of what the client has requested. If the server certificate is
+ # unavailable, then the name is "none".
+ # --consensus
+ # The server name is either SNI (if SNI matches at least one of the
+ # certificate subject names) or "none" (otherwise). When the server
+ # certificate is unavailable, the consensus server name is SNI.
+ #
+ # Combining multiple options in one ACL is a fatal configuration
+ # error.
+ #
+ # For all options: If no SNI is available, then the CONNECT request
+ # target (a.k.a. URI) is used instead of SNI (for an intercepted
+ # connection, this target is the destination IP address).
acl aclname ssl::server_name_regex [-i] \.foo\.com ...
# regex matches server name obtained from various sources [fast]
In all other cases, a single dash ("-") is
logged.
- ssl::>sni SSL client SNI sent to Squid. Available only
- after the peek, stare, or splice SSL bumping
- actions.
+ ssl::>sni SSL client SNI sent to Squid.
ssl::>cert_subject
The Subject field of the received client
clientConnection->tlsNegotiations()->retrieveParsedInfo(details);
if (details && !details->serverName.isEmpty()) {
resetSslCommonName(details->serverName.c_str());
- if (sslServerBump)
- sslServerBump->clientSni = details->serverName;
+ tlsClientSni_ = details->serverName;
}
// We should disable read/write handlers
const unsigned short connectPort = clientConnection->local.port();
#if USE_OPENSSL
- if (serverBump() && !serverBump()->clientSni.isEmpty())
- connectHost.assign(serverBump()->clientSni);
+ if (!tlsClientSni_.isEmpty())
+ connectHost.assign(tlsClientSni_);
else
#endif
{
}
const SBuf &sslCommonName() const {return sslCommonName_;}
void resetSslCommonName(const char *name) {sslCommonName_ = name;}
+ const SBuf &tlsClientSni() const { return tlsClientSni_; }
/// Fill the certAdaptParams with the required data for certificate adaptation
/// and create the key for storing/retrieve the certificate to/from the cache
void buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties);
/// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
String sslConnectHostOrIp; ///< The SSL server host name as passed in the CONNECT request
SBuf sslCommonName_; ///< CN name for SSL certificate generation
+
+ /// TLS client delivered SNI value. Empty string if none has been received.
+ SBuf tlsClientSni_;
String sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
/// HTTPS server cert. fetching state for bump-ssl-server-first
case LFT_SSL_CLIENT_SNI:
if (al->request && al->request->clientConnectionManager.valid()) {
- if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
- if (!srvBump->clientSni.isEmpty())
- out = srvBump->clientSni.c_str();
+ if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
+ if (!conn->tlsClientSni().isEmpty()) {
+ sb = conn->tlsClientSni();
+ out = sb.c_str();
+ }
}
}
break;
Ssl::BumpMode step3; ///< The SSL bump mode at step3
} act; ///< bumping actions at various bumping steps
Ssl::BumpStep step; ///< The SSL bumping step
- SBuf clientSni; ///< the SSL client SNI name
private:
Security::SessionPointer serverSession; ///< The TLS session object on server side.