Transport.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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
9#include "squid.h"
10#include "ip/Address.h"
11#include "ip/tools.h"
14
15#if HAVE_GETOPT_H
16#include <getopt.h>
17#endif
18#if HAVE_GNUTLS_X509_H
19#include <gnutls/x509.h>
20#endif
21#include <iostream>
22
24
26int conn = -1;
27
28void
30{
31 std::cerr << "Connection Settings" << std::endl
32 << " -h | --host host Send message to server on 'host'. Default is localhost." << std::endl
33 << " -l | --local host Specify a local IP address to bind to. Default is none." << std::endl
34 << " -p | --port port Port number on server to contact. Default is " << CACHE_HTTP_PORT << "." << std::endl
35 << " -T timeout Timeout in seconds for read/write operations" << std::endl
36#if USE_GNUTLS
37 << " --https Use TLS/SSL on the HTTP connection" << std::endl
38 << std::endl
39 << " TLS options:" << std::endl
40 << " --anonymous-tls Use Anonymous TLS. Sets default parameters:" << std::endl
41 << " \"PERFORMANCE:+ANON-ECDH:+ANON-DH\"" << std::endl
42 << " --params=\"...\" Use the given parameters." << std::endl
43 << " --cert=FILE Path to a PEM file holding the client X.509 certificate chain." << std::endl
44 << " May be repeated if there are multiple certificates to use for the server." << std::endl
45 << " --trusted-ca=PATH Path to a PEM file holding trusted CA certificate(s)." << std::endl
46 << " May be repeated." << std::endl
47 << " Example path: \"/etc/ssl/certs/ca-certificates.crt\"" << std::endl
48#endif
49 << std::endl;
50}
51
52bool
53Transport::TheConfig::parseCommandOpts(int argc, char *argv[], int c, int &optIndex)
54{
55 bool tls = false;
56 const char *shortOpStr = "h:l:p:T:?";
57
58 // options for controlling squidclient transport connection
59 static struct option longOptions[] = {
60 {"anonymous-tls",no_argument, nullptr, '\1'},
61 {"https", no_argument, nullptr, '\3'},
62 {"trusted-ca", required_argument, nullptr, 'A'},
63 {"cert", required_argument, nullptr, 'C'},
64 {"host", required_argument, nullptr, 'h'},
65 {"local", required_argument, nullptr, 'l'},
66 {"port", required_argument, nullptr, 'p'},
67 {"params", required_argument, nullptr, 'P'},
68 {nullptr, 0, nullptr, 0}
69 };
70
71 int saved_opterr = opterr;
72 opterr = 0; // suppress errors from getopt
73 do {
74 switch (c) {
75 case '\1':
76 tls = true;
77 tlsAnonymous = true;
78 params = "PERFORMANCE:+ANON-ECDH:+ANON-DH";
79 break;
80
81 case '\3':
82 tls = true;
83 break;
84
85 case 'A':
86 tls = true;
87 caFiles.push_back(std::string(optarg));
88 break;
89
90 case 'C':
91 tls = true;
92 certFiles.push_back(std::string(optarg));
93 break;
94
95 case 'h':
96 hostname = optarg;
97 break;
98
99 case 'l':
100 localHost = optarg;
101 break;
102
103 case 'p': /* port number */
104 sscanf(optarg, "%hd", &port);
105 if (port < 1)
106 port = CACHE_HTTP_PORT; /* default */
107 break;
108
109 case 'P':
110 tls = true;
111 params = optarg;
112 break;
113
114 case 'T':
115 ioTimeout = atoi(optarg);
116 break;
117
118 default:
119 if (tls)
121
122 // rewind and let the caller handle unknown options
123 --optind;
124 opterr = saved_opterr;
125 return true;
126 }
127 } while ((c = getopt_long(argc, argv, shortOpStr, longOptions, &optIndex)) != -1);
128
129 if (tls)
131
132 opterr = saved_opterr;
133 return false;
134}
135
137static int
138client_comm_bind(int sock, const Ip::Address &addr)
139{
140 static struct addrinfo *AI = nullptr;
141 addr.getAddrInfo(AI);
142 int res = bind(sock, AI->ai_addr, AI->ai_addrlen);
144 return res;
145}
146
147static void
149{
150 struct addrinfo *AI = nullptr;
151
152 debugVerbose(2, "Transport detected: IPv4" <<
153 ((Ip::EnableIpv6 & IPV6_SPECIAL_V4MAPPING) ? "-mapped " : "") <<
154 (Ip::EnableIpv6 == IPV6_OFF ? "-only" : " and IPv6") <<
155 ((Ip::EnableIpv6 & IPV6_SPECIAL_SPLITSTACK) ? " split-stack" : ""));
156
157 if (Transport::Config.localHost) {
158 debugVerbose(2, "Resolving " << Transport::Config.localHost << " ...");
159
161 std::cerr << "ERROR: Cannot resolve " << Transport::Config.localHost << ": Host unknown." << std::endl;
162 exit(1);
163 }
164 } else {
165 debugVerbose(2, "Resolving " << Transport::Config.hostname << " ...");
166 /* Process the remote host name to locate the Protocol required
167 in case we are being asked to link to another version of squid */
169 std::cerr << "ERROR: Cannot resolve " << Transport::Config.hostname << ": Host unknown." << std::endl;
170 exit(1);
171 }
172 }
173
174 iaddr.getAddrInfo(AI);
175 if ((conn = socket(AI->ai_family, AI->ai_socktype, 0)) < 0) {
176 std::cerr << "ERROR: could not open socket to " << iaddr << std::endl;
178 exit(1);
179 }
181
182 if (Transport::Config.localHost) {
183 if (client_comm_bind(conn, iaddr) < 0) {
184 std::cerr << "ERROR: could not bind socket to " << iaddr << std::endl;
185 exit(1);
186 }
187
188 iaddr.setEmpty();
189
190 debugVerbose(2, "Resolving... " << Transport::Config.hostname);
191
193 std::cerr << "ERROR: Cannot resolve " << Transport::Config.hostname << ": Host unknown." << std::endl;
194 exit(1);
195 }
196 }
197
199}
200
202static int
203client_comm_connect(int sock, const Ip::Address &addr)
204{
205 static struct addrinfo *AI = nullptr;
206 addr.getAddrInfo(AI);
207 int res = connect(sock, AI->ai_addr, AI->ai_addrlen);
210 return res;
211}
212
213bool
215{
216 Ip::Address iaddr;
217 resolveDestination(iaddr);
218
219 debugVerbose(2, "Connecting... " << Config.hostname << " (" << iaddr << ")");
220
221 if (client_comm_connect(conn, iaddr) < 0) {
222 char hostnameBuf[MAX_IPSTRLEN];
223 iaddr.toUrl(hostnameBuf, MAX_IPSTRLEN);
224 std::cerr << "ERROR: Cannot connect to " << hostnameBuf
225 << (!errno ?": Host unknown." : "") << std::endl;
226 exit(1);
227 }
228 debugVerbose(2, "Connected to: " << Config.hostname << " (" << iaddr << ")");
229
230 // do any TLS setup that might be needed
232 return false;
233
234 return true;
235}
236
237ssize_t
238Transport::Write(const void *buf, size_t len)
239{
240 if (conn < 0)
241 return -1;
242
243 if (Config.tlsEnabled) {
244#if USE_GNUTLS
245 gnutls_record_send(Config.session, buf, len);
246 return len;
247#else
248 return 0;
249#endif
250 } else {
251
252#if _SQUID_WINDOWS_
253 return send(conn, buf, len, 0);
254#else
255 alarm(Config.ioTimeout);
256 return write(conn, buf, len);
257#endif
258 }
259}
260
261ssize_t
262Transport::Read(void *buf, size_t len)
263{
264 if (conn < 0)
265 return -1;
266
267 if (Config.tlsEnabled) {
268#if USE_GNUTLS
269 return gnutls_record_recv(Config.session, buf, len);
270#else
271 return 0;
272#endif
273 } else {
274
275#if _SQUID_WINDOWS_
276 return recv(conn, buf, len, 0);
277#else
278 alarm(Config.ioTimeout);
279 return read(conn, buf, len);
280#endif
281 }
282}
283
284void
286{
287 (void) close(conn);
288 conn = -1;
289}
290
291#if USE_GNUTLS
292/* This function will verify the peer's certificate, and check
293 * if the hostname matches, as well as the activation, expiration dates.
294 */
295static int
296verifyByCA(gnutls_session_t session)
297{
298 /* read hostname */
299 const char *hostname = static_cast<const char*>(gnutls_session_get_ptr(session));
300
301 /* This verification function uses the trusted CAs in the credentials
302 * structure. So you must have installed one or more CA certificates.
303 */
304 unsigned int status;
305 if (gnutls_certificate_verify_peers3(session, hostname, &status) < 0) {
306 std::cerr << "VERIFY peers failure";
307 return GNUTLS_E_CERTIFICATE_ERROR;
308 }
309
310 gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
311 gnutls_datum_t out;
312 if (gnutls_certificate_verification_status_print(status, type, &out, 0) < 0) {
313 std::cerr << "VERIFY status failure";
314 return GNUTLS_E_CERTIFICATE_ERROR;
315 }
316
317 std::cerr << "VERIFY DATUM: " << out.data << std::endl;
318 gnutls_free(out.data);
319
320 if (status != 0) /* Certificate is not trusted */
321 return GNUTLS_E_CERTIFICATE_ERROR;
322
323 /* notify gnutls to continue handshake normally */
324 return GNUTLS_E_SUCCESS;
325}
326
327static int
328verifyTlsCertificate(gnutls_session_t session)
329{
330 // XXX: 1) try to verify using DANE -> Secure Authenticated Connection
331
332 // 2) try to verify using CA
333 if (verifyByCA(session) == GNUTLS_E_SUCCESS) {
334 std::cerr << "SUCCESS: CA verified Encrypted Connection" << std::endl;
335 return GNUTLS_E_SUCCESS;
336 }
337
338 // 3) fails both is insecure, but show the results anyway.
339 std::cerr << "WARNING: Insecure Connection" << std::endl;
340 return GNUTLS_E_SUCCESS;
341}
342#endif
343
344#if USE_GNUTLS
345static void
346gnutlsDebugHandler(int level, const char *msg)
347{
348 debugVerbose(level, "GnuTLS: " << msg);
349}
350#endif
351
352void
354{
355#if USE_GNUTLS
356 debugVerbose(3, "Initializing TLS library...");
357 // NP: gnutls init is re-entrant and lock-counted with deinit but not thread safe.
358 if (gnutls_global_init() != GNUTLS_E_SUCCESS) {
359 int xerrno = errno;
360 std::cerr << "FATAL ERROR: TLS Initialize failed: " << xstrerr(xerrno) << std::endl;
361 exit(1);
362 }
363
364 Config.tlsEnabled = true;
365
366#if USE_GNUTLS
367 gnutls_global_set_log_function(&gnutlsDebugHandler);
368 gnutls_global_set_log_level(scParams.verbosityLevel);
369#endif
370
371 // Initialize for anonymous TLS
372 gnutls_anon_allocate_client_credentials(&Config.anonCredentials);
373
374 // Initialize for X.509 certificate exchange
375 gnutls_certificate_allocate_credentials(&Config.certCredentials);
376 for (std::list<std::string>::const_iterator i = Config.caFiles.begin(); i != Config.caFiles.end(); ++i) {
377 int x = gnutls_certificate_set_x509_trust_file(Config.certCredentials, (*i).c_str(), GNUTLS_X509_FMT_PEM);
378 if (x < 0) {
379 debugVerbose(3, "WARNING: Failed to load Certificate Authorities from " << *i);
380 } else {
381 debugVerbose(3, "Loaded " << x << " Certificate Authorities from " << *i);
382 }
383 }
384 gnutls_certificate_set_verify_function(Config.certCredentials, verifyTlsCertificate);
385
386 for (std::list<std::string>::const_iterator i = Config.certFiles.begin(); i != Config.certFiles.end(); ++i) {
387 if (gnutls_certificate_set_x509_key_file(Transport::Config.certCredentials, (*i).c_str(), (*i).c_str(), GNUTLS_X509_FMT_PEM) != GNUTLS_E_SUCCESS) {
388 debugVerbose(3, "WARNING: Failed to load Certificate from " << *i);
389 } else {
390 debugVerbose(3, "Loaded Certificate from " << *i);
391 }
392 }
393
394#else
395 std::cerr << "ERROR: TLS support not available." << std::endl;
396#endif
397}
398
399#if USE_GNUTLS
400
401// perform the actual handshake exchange with remote server
402static bool
403doTlsHandshake(const char *type)
404{
405 // setup the connection for TLS
406 gnutls_transport_set_int(Transport::Config.session, conn);
407 gnutls_handshake_set_timeout(Transport::Config.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
408
409 debugVerbose(2, type << " TLS handshake ... ");
410
411 int ret = 0;
412 do {
413 ret = gnutls_handshake(Transport::Config.session);
414 } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
415
416 if (ret < 0) {
417 std::cerr << "ERROR: " << type << " TLS Handshake failed (" << ret << ") "
418 << gnutls_alert_get_name(gnutls_alert_get(Transport::Config.session))
419 << std::endl;
420 gnutls_perror(ret);
421 gnutls_deinit(Transport::Config.session);
422 return false;
423 }
424
425 char *desc = gnutls_session_get_desc(Transport::Config.session);
426 debugVerbose(3, "TLS Session info: " << std::endl << desc << std::endl);
427 gnutls_free(desc);
428 return true;
429}
430
431static bool
433{
434 const char *err = nullptr;
435 int x;
436 if ((x = gnutls_priority_set_direct(Transport::Config.session, Transport::Config.params, &err)) != GNUTLS_E_SUCCESS) {
437 if (x == GNUTLS_E_INVALID_REQUEST)
438 std::cerr << "ERROR: Syntax error at: " << err << std::endl;
439 gnutls_perror(x);
440 return false;
441 }
442 return true;
443}
444
445// attempt an anonymous TLS handshake
446// this encrypts the connection but does not secure it
447// so many public servers do not support this handshake type.
448static bool
450{
451 if (!loadTlsParameters())
452 return false;
453
454 // put the anonymous credentials to the current session
455 int x;
456 if ((x = gnutls_credentials_set(Transport::Config.session, GNUTLS_CRD_ANON, Transport::Config.anonCredentials)) != GNUTLS_E_SUCCESS) {
457 std::cerr << "ERROR: Anonymous TLS credentials setup failed (" << x << ") " << std::endl;
458 gnutls_perror(x);
459 return false;
460 }
461
462 return doTlsHandshake("Anonymous");
463}
464
465// attempt a X.509 certificate exchange
466// this both encrypts and authenticates the connection
467static bool
468tryTlsCertificate(const char *hostname)
469{
470 gnutls_session_set_ptr(Transport::Config.session, (void *) hostname);
471 gnutls_server_name_set(Transport::Config.session, GNUTLS_NAME_DNS, hostname, strlen(hostname));
472
473 if (!loadTlsParameters())
474 return false;
475
476 // put the X.509 credentials to the current session
477 gnutls_credentials_set(Transport::Config.session, GNUTLS_CRD_CERTIFICATE, Transport::Config.certCredentials);
478
479 // setup the connection for TLS
480 gnutls_transport_set_int(Transport::Config.session, conn);
481 gnutls_handshake_set_timeout(Transport::Config.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
482
483 return doTlsHandshake("X.509");
484}
485#endif
486
487bool
488Transport::MaybeStartTls(const char *hostname)
489{
490#if USE_GNUTLS
491 if (Config.tlsEnabled) {
492
493 // Initialize TLS session
494 gnutls_init(&Transport::Config.session, GNUTLS_CLIENT);
495
496 if (Transport::Config.tlsAnonymous && !tryTlsAnonymous()) {
497 gnutls_deinit(Config.session);
498 return false;
499 }
500
501 if (!tryTlsCertificate(hostname)) {
502 gnutls_deinit(Config.session);
503 return false;
504 }
505 }
506#else
507 (void)hostname;
508#endif
509 return true;
510}
511
512void
514{
515#if USE_GNUTLS
516 if (!Config.tlsEnabled)
517 return;
518
519 debugVerbose(3, "Shutting down TLS library...");
520
521 // release any existing session and credentials
522 gnutls_deinit(Config.session);
523 gnutls_anon_free_client_credentials(Config.anonCredentials);
524 gnutls_certificate_free_credentials(Config.certCredentials);
525
526 // NP: gnutls init is re-entrant and lock-counted with deinit but not thread safe.
527 gnutls_global_deinit();
528 Config.tlsEnabled = false;
529#endif
530}
531
Parameters scParams
global squidcleint parameters
Definition: squidclient.cc:66
#define debugVerbose(LEVEL, MESSAGE)
display debug messages at varying verbosity levels
Definition: Parameters.h:31
static int client_comm_connect(int sock, const Ip::Address &addr)
Set up the destination socket address for message to send to.
Definition: Transport.cc:203
static bool tryTlsCertificate(const char *hostname)
Definition: Transport.cc:468
static int client_comm_bind(int sock, const Ip::Address &addr)
Set up the source socket address from which to send.
Definition: Transport.cc:138
static int verifyByCA(gnutls_session_t session)
Definition: Transport.cc:296
int conn
the current server connection FD
Definition: Transport.cc:26
static bool tryTlsAnonymous()
Definition: Transport.cc:449
static void gnutlsDebugHandler(int level, const char *msg)
Definition: Transport.cc:346
static void resolveDestination(Ip::Address &iaddr)
Definition: Transport.cc:148
static bool loadTlsParameters()
Definition: Transport.cc:432
static bool doTlsHandshake(const char *type)
Definition: Transport.cc:403
static int verifyTlsCertificate(gnutls_session_t session)
Definition: Transport.cc:328
void setEmpty()
Fast reset of the stored content to what would be after default constructor.
Definition: Address.cc:184
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition: Address.cc:599
bool GetHostByName(const char *s)
Definition: Address.cc:372
char * toUrl(char *buf, unsigned int len) const
Definition: Address.cc:874
unsigned short port() const
Definition: Address.cc:778
int verbosityLevel
Definition: Parameters.h:27
parameters controlling outgoing connection
Definition: Transport.h:25
const char * localHost
the local hostname to bind as for outgoing IP
Definition: Transport.h:53
gnutls_session_t session
TLS session state.
Definition: Transport.h:87
int ioTimeout
I/O operation timeout.
Definition: Transport.h:50
void usage()
display Transport Options command line help to stderr
Definition: Transport.cc:29
gnutls_certificate_credentials_t certCredentials
Definition: Transport.h:84
bool tlsEnabled
whether to enable TLS on the server connection
Definition: Transport.h:62
gnutls_anon_client_credentials_t anonCredentials
anonymous client credentials
Definition: Transport.h:81
bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex)
Definition: Transport.cc:53
std::list< std::string > certFiles
Definition: Transport.h:74
const char * hostname
the destination server host name to contact
Definition: Transport.h:56
std::list< std::string > caFiles
Definition: Transport.h:77
static int port
Definition: ldap_backend.cc:70
int optind
Definition: getopt.c:48
char * optarg
Definition: getopt.c:51
int opterr
Definition: getopt.c:47
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
#define IPV6_SPECIAL_SPLITSTACK
Definition: tools.h:22
#define IPV6_SPECIAL_V4MAPPING
Definition: tools.h:21
#define IPV6_OFF
Definition: tools.h:19
static const char * shortOpStr
Definition: main.cc:393
void TimerStart()
start timing a new transaction
Definition: Ping.cc:79
ssize_t Read(void *buf, size_t len)
Definition: Transport.cc:262
bool MaybeStartTls(const char *hostname)
Definition: Transport.cc:488
TheConfig Config
Definition: Transport.cc:23
void ShutdownTls()
De-initialize TLS library environment when necessary.
Definition: Transport.cc:513
ssize_t Write(const void *buf, size_t len)
Definition: Transport.cc:238
void InitTls()
Initialize TLS library environment when necessary.
Definition: Transport.cc:353
bool Connect()
locate and connect to the configured server
Definition: Transport.cc:214
void CloseConnection()
close the current connection
Definition: Transport.cc:285
#define CACHE_HTTP_PORT
Definition: squid.h:16
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors