/
ServerName.cc
150 lines (128 loc) · 4.73 KB
/
ServerName.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
* Copyright (C) 1996-2022 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
* Please see the COPYING and CONTRIBUTORS files for details.
*/
/* DEBUG: section 28 Access Control */
#include "squid.h"
#include "acl/DomainData.h"
#include "acl/FilledChecklist.h"
#include "acl/RegexData.h"
#include "acl/ServerName.h"
#include "client_side.h"
#include "fde.h"
#include "http/Stream.h"
#include "HttpRequest.h"
#include "ipcache.h"
#include "SquidString.h"
#include "ssl/bio.h"
#include "ssl/ServerBump.h"
#include "ssl/support.h"
// Compare function for tree search algorithms
static int
aclHostDomainCompare( char *const &a, char * const &b)
{
const char *h = static_cast<const char *>(a);
const char *d = static_cast<const char *>(b);
debugs(28, 7, "Match:" << h << " <> " << d);
return matchDomainName(h, d, mdnHonorWildcards);
}
bool
ACLServerNameData::match(const char *host)
{
if (host == nullptr)
return 0;
debugs(28, 3, "checking '" << host << "'");
char *h = const_cast<char *>(host);
char const * const * result = domains->find(h, aclHostDomainCompare);
debugs(28, 3, "'" << host << "' " << (result ? "found" : "NOT found"));
return (result != nullptr);
}
/// A helper function to be used with Ssl::matchX509CommonNames().
/// \retval 0 when the name (cn or an alternate name) matches acl data
/// \retval 1 when the name does not match
template<class MatchType>
int
check_cert_domain( void *check_data, ASN1_STRING *cn_data)
{
char cn[1024];
ACLData<MatchType> * data = (ACLData<MatchType> *)check_data;
if (cn_data->length > (int)sizeof(cn) - 1)
return 1; // ignore data that does not fit our buffer
char *s = reinterpret_cast<char *>(cn_data->data);
char *d = cn;
for (int i = 0; i < cn_data->length; ++i, ++d, ++s) {
if (*s == '\0')
return 1; // always a domain mismatch. contains 0x00
*d = *s;
}
cn[cn_data->length] = '\0';
debugs(28, 4, "Verifying certificate name/subjectAltName " << cn);
if (data->match(cn))
return 0;
return 1;
}
int
ACLServerNameStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
{
assert(checklist != nullptr && checklist->request != nullptr);
const char *serverName = nullptr;
SBuf clientSniKeeper; // because c_str() is not constant
if (ConnStateData *conn = checklist->conn()) {
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
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;
}
}
if (!serverName)
serverName = "none";
return data->match(serverName);
}
const Acl::Options &
ACLServerNameStrategy::options()
{
static const Acl::BooleanOption ClientRequested("--client-requested");
static const Acl::BooleanOption ServerProvided("--server-provided");
static const Acl::BooleanOption Consensus("--consensus");
static const Acl::Options MyOptions = { &ClientRequested, &ServerProvided, &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;
}