]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
ef57eb7b | 2 | * Copyright (C) 1996-2016 The Squid Software Foundation and contributors |
bbc27441 AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
f7f3304a | 9 | #include "squid.h" |
24438ec5 | 10 | #include "../helper.h" |
502bcc7b | 11 | #include "anyp/PortCfg.h" |
b3f7fd88 | 12 | #include "fs_io.h" |
24438ec5 | 13 | #include "helper/Reply.h" |
602d9612 | 14 | #include "SquidConfig.h" |
f9b6ff6e | 15 | #include "SquidString.h" |
95d2589c | 16 | #include "SquidTime.h" |
14798e73 | 17 | #include "ssl/cert_validate_message.h" |
602d9612 A |
18 | #include "ssl/Config.h" |
19 | #include "ssl/helper.h" | |
fc54b8d2 | 20 | #include "wordlist.h" |
95d2589c | 21 | |
0e208dad | 22 | Ssl::CertValidationHelper::LruCache *Ssl::CertValidationHelper::HelperCache = nullptr; |
14798e73 | 23 | |
4a77bb4e | 24 | #if USE_SSL_CRTD |
95d2589c CT |
25 | Ssl::Helper * Ssl::Helper::GetInstance() |
26 | { | |
27 | static Ssl::Helper sslHelper; | |
28 | return &sslHelper; | |
29 | } | |
30 | ||
fad2588a | 31 | Ssl::Helper::Helper() : ssl_crtd(NULL) |
95d2589c | 32 | { |
95d2589c CT |
33 | } |
34 | ||
35 | Ssl::Helper::~Helper() | |
36 | { | |
37 | Shutdown(); | |
38 | } | |
39 | ||
40 | void Ssl::Helper::Init() | |
41 | { | |
586089cd CT |
42 | assert(ssl_crtd == NULL); |
43 | ||
f0763147 AJ |
44 | // we need to start ssl_crtd only if some port(s) need to bump SSL *and* generate certificates |
45 | // TODO: generate host certificates for SNI enabled accel ports | |
24e6c8f1 | 46 | bool found = false; |
fa720bfb | 47 | for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next) |
f0763147 | 48 | found = s->flags.tunnelSslBumping && s->generateHostCertificates; |
24e6c8f1 | 49 | if (!found) |
586089cd CT |
50 | return; |
51 | ||
051da40c | 52 | ssl_crtd = new helper(Ssl::TheConfig.ssl_crtd); |
1af735c7 | 53 | ssl_crtd->childs.updateLimits(Ssl::TheConfig.ssl_crtdChildren); |
95d2589c | 54 | ssl_crtd->ipc_type = IPC_STREAM; |
0af9303a CT |
55 | // The crtd messages may contain the eol ('\n') character. We are |
56 | // going to use the '\1' char as the end-of-message mark. | |
57 | ssl_crtd->eom = '\1'; | |
95d2589c CT |
58 | assert(ssl_crtd->cmdline == NULL); |
59 | { | |
60 | char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd); | |
61 | char *tmp_begin = tmp; | |
ba5d55c1 | 62 | char *token = NULL; |
95d2589c CT |
63 | while ((token = strwordtok(NULL, &tmp))) { |
64 | wordlistAdd(&ssl_crtd->cmdline, token); | |
95d2589c CT |
65 | } |
66 | safe_free(tmp_begin); | |
67 | } | |
95d2589c CT |
68 | helperOpenServers(ssl_crtd); |
69 | } | |
70 | ||
71 | void Ssl::Helper::Shutdown() | |
72 | { | |
73 | if (!ssl_crtd) | |
74 | return; | |
75 | helperShutdown(ssl_crtd); | |
76 | wordlistDestroy(&ssl_crtd->cmdline); | |
95d2589c CT |
77 | delete ssl_crtd; |
78 | ssl_crtd = NULL; | |
79 | } | |
80 | ||
81 | void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data) | |
82 | { | |
586089cd | 83 | assert(ssl_crtd); |
95d2589c | 84 | |
6825b101 CT |
85 | std::string msg = message.compose(); |
86 | msg += '\n'; | |
87 | if (!ssl_crtd->trySubmit(msg.c_str(), callback, data)) { | |
ddc77a2e | 88 | ::Helper::Reply failReply(::Helper::BrokenHelper); |
fd7f26ea | 89 | failReply.notes.add("message", "error 45 Temporary network problem, please retry later"); |
12f7e09b | 90 | callback(data, failReply); |
95d2589c CT |
91 | return; |
92 | } | |
95d2589c | 93 | } |
4a77bb4e | 94 | #endif //USE_SSL_CRTD |
2cef0ca6 | 95 | |
2cef0ca6 AR |
96 | Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance() |
97 | { | |
98 | static Ssl::CertValidationHelper sslHelper; | |
99 | if (!Ssl::TheConfig.ssl_crt_validator) | |
100 | return NULL; | |
101 | return &sslHelper; | |
102 | } | |
103 | ||
104 | Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL) | |
105 | { | |
106 | } | |
107 | ||
108 | Ssl::CertValidationHelper::~CertValidationHelper() | |
109 | { | |
110 | Shutdown(); | |
111 | } | |
112 | ||
113 | void Ssl::CertValidationHelper::Init() | |
114 | { | |
115 | assert(ssl_crt_validator == NULL); | |
116 | ||
117 | // we need to start ssl_crtd only if some port(s) need to bump SSL | |
118 | bool found = false; | |
fa720bfb | 119 | for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next) |
6a25a046 | 120 | found = s->flags.tunnelSslBumping; |
2cef0ca6 AR |
121 | if (!found) |
122 | return; | |
123 | ||
124 | ssl_crt_validator = new helper("ssl_crt_validator"); | |
125 | ssl_crt_validator->childs.updateLimits(Ssl::TheConfig.ssl_crt_validator_Children); | |
126 | ssl_crt_validator->ipc_type = IPC_STREAM; | |
127 | // The crtd messages may contain the eol ('\n') character. We are | |
128 | // going to use the '\1' char as the end-of-message mark. | |
129 | ssl_crt_validator->eom = '\1'; | |
130 | assert(ssl_crt_validator->cmdline == NULL); | |
14798e73 CT |
131 | |
132 | int ttl = 60; | |
133 | size_t cache = 2048; | |
2cef0ca6 AR |
134 | { |
135 | char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator); | |
136 | char *tmp_begin = tmp; | |
137 | char * token = NULL; | |
14798e73 | 138 | bool parseParams = true; |
2cef0ca6 | 139 | while ((token = strwordtok(NULL, &tmp))) { |
14798e73 CT |
140 | if (parseParams) { |
141 | if (strncmp(token, "ttl=", 4) == 0) { | |
142 | ttl = atoi(token + 4); | |
143 | continue; | |
144 | } else if (strncmp(token, "cache=", 6) == 0) { | |
145 | cache = atoi(token + 6); | |
146 | continue; | |
147 | } else | |
148 | parseParams = false; | |
149 | } | |
2cef0ca6 AR |
150 | wordlistAdd(&ssl_crt_validator->cmdline, token); |
151 | } | |
4a77bb4e | 152 | xfree(tmp_begin); |
2cef0ca6 AR |
153 | } |
154 | helperOpenServers(ssl_crt_validator); | |
14798e73 CT |
155 | |
156 | //WARNING: initializing static member in an object initialization method | |
157 | assert(HelperCache == NULL); | |
0e208dad | 158 | HelperCache = new Ssl::CertValidationHelper::LruCache(ttl, cache); |
2cef0ca6 AR |
159 | } |
160 | ||
161 | void Ssl::CertValidationHelper::Shutdown() | |
162 | { | |
163 | if (!ssl_crt_validator) | |
164 | return; | |
165 | helperShutdown(ssl_crt_validator); | |
166 | wordlistDestroy(&ssl_crt_validator->cmdline); | |
167 | delete ssl_crt_validator; | |
168 | ssl_crt_validator = NULL; | |
4c304fb9 | 169 | |
14798e73 | 170 | // CertValidationHelper::HelperCache is a static member, it is not good policy to |
4c304fb9 | 171 | // reset it here. Will work because the current Ssl::CertValidationHelper is |
14798e73 CT |
172 | // always the same static object. |
173 | delete HelperCache; | |
174 | HelperCache = NULL; | |
175 | } | |
176 | ||
5c2f68b7 AJ |
177 | class submitData |
178 | { | |
179 | CBDATA_CLASS(submitData); | |
180 | ||
181 | public: | |
14798e73 | 182 | std::string query; |
0e208dad | 183 | AsyncCall::Pointer callback; |
14798e73 | 184 | SSL *ssl; |
14798e73 CT |
185 | }; |
186 | CBDATA_CLASS_INIT(submitData); | |
187 | ||
188 | static void | |
24438ec5 | 189 | sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply) |
14798e73 CT |
190 | { |
191 | Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY); | |
0e208dad | 192 | Ssl::CertValidationResponse::Pointer validationResponse = new Ssl::CertValidationResponse; |
14798e73 CT |
193 | std::string error; |
194 | ||
195 | submitData *crtdvdData = static_cast<submitData *>(data); | |
196 | STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl); | |
2428ce02 | 197 | if (reply.result == ::Helper::BrokenHelper) { |
14798e73 | 198 | debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content()); |
2428ce02 | 199 | validationResponse->resultCode = ::Helper::BrokenHelper; |
ddc77a2e CT |
200 | } else if (!reply.other().hasContent()) { |
201 | debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper returned NULL response"); | |
202 | validationResponse->resultCode = ::Helper::BrokenHelper; | |
14798e73 | 203 | } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK || |
4c304fb9 | 204 | !replyMsg.parseResponse(*validationResponse, peerCerts, error) ) { |
14798e73 CT |
205 | debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect"); |
206 | debugs(83, DBG_IMPORTANT, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody()); | |
2428ce02 | 207 | validationResponse->resultCode = ::Helper::BrokenHelper; |
4c304fb9 | 208 | } else |
14798e73 CT |
209 | validationResponse->resultCode = reply.result; |
210 | ||
0e208dad CT |
211 | Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(crtdvdData->callback->getDialer()); |
212 | Must(dialer); | |
213 | dialer->arg1 = validationResponse; | |
214 | ScheduleCallHere(crtdvdData->callback); | |
14798e73 CT |
215 | |
216 | if (Ssl::CertValidationHelper::HelperCache && | |
2428ce02 | 217 | (validationResponse->resultCode == ::Helper::Okay || validationResponse->resultCode == ::Helper::Error)) { |
0e208dad | 218 | Ssl::CertValidationResponse::Pointer *item = new Ssl::CertValidationResponse::Pointer(validationResponse); |
f235c94b CT |
219 | if (!Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), item)) |
220 | delete item; | |
0e208dad | 221 | } |
14798e73 | 222 | |
14798e73 CT |
223 | SSL_free(crtdvdData->ssl); |
224 | delete crtdvdData; | |
2cef0ca6 AR |
225 | } |
226 | ||
0e208dad | 227 | void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &callback) |
2cef0ca6 | 228 | { |
2cef0ca6 AR |
229 | assert(ssl_crt_validator); |
230 | ||
14798e73 CT |
231 | Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST); |
232 | message.setCode(Ssl::CertValidationMsg::code_cert_validate); | |
233 | message.composeRequest(request); | |
234 | debugs(83, 5, "SSL crtvd request: " << message.compose().c_str()); | |
235 | ||
236 | submitData *crtdvdData = new submitData; | |
237 | crtdvdData->query = message.compose(); | |
238 | crtdvdData->query += '\n'; | |
239 | crtdvdData->callback = callback; | |
14798e73 CT |
240 | crtdvdData->ssl = request.ssl; |
241 | CRYPTO_add(&crtdvdData->ssl->references,1,CRYPTO_LOCK_SSL); | |
0e208dad | 242 | Ssl::CertValidationResponse::Pointer const*validationResponse; |
14798e73 CT |
243 | |
244 | if (CertValidationHelper::HelperCache && | |
4c304fb9 | 245 | (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) { |
0e208dad CT |
246 | |
247 | CertValidationHelper::CbDialer *dialer = dynamic_cast<CertValidationHelper::CbDialer*>(callback->getDialer()); | |
bac324a9 | 248 | Must(dialer); |
0e208dad CT |
249 | dialer->arg1 = *validationResponse; |
250 | ScheduleCallHere(callback); | |
14798e73 CT |
251 | SSL_free(crtdvdData->ssl); |
252 | delete crtdvdData; | |
253 | return; | |
254 | } | |
6825b101 CT |
255 | |
256 | if (!ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData)) { | |
0e208dad CT |
257 | Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse;; |
258 | resp->resultCode = ::Helper::BrokenHelper; | |
259 | Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(callback->getDialer()); | |
bac324a9 | 260 | Must(dialer); |
0e208dad CT |
261 | dialer->arg1 = resp; |
262 | ScheduleCallHere(callback); | |
6825b101 | 263 | |
6825b101 CT |
264 | SSL_free(crtdvdData->ssl); |
265 | delete crtdvdData; | |
266 | return; | |
267 | } | |
2cef0ca6 | 268 | } |
f53969cc | 269 |