Transport.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 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"
12 #include "tools/squidclient/Ping.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 
26 int conn = -1;
27 
28 void
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 
52 bool
53 Transport::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, 0, '\1'},
61  {"https", no_argument, 0, '\3'},
62  {"trusted-ca", required_argument, 0, 'A'},
63  {"cert", required_argument, 0, 'C'},
64  {"host", required_argument, 0, 'h'},
65  {"local", required_argument, 0, 'l'},
66  {"port", required_argument, 0, 'p'},
67  {"params", required_argument, 0, 'P'},
68  {0, 0, 0, 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 
137 static int
139 {
140  static struct addrinfo *AI = NULL;
141  addr.getAddrInfo(AI);
142  int res = bind(sock, AI->ai_addr, AI->ai_addrlen);
144  return res;
145 }
146 
147 static void
149 {
150  struct addrinfo *AI = NULL;
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 
160  if ( !iaddr.GetHostByName(Transport::Config.localHost) ) {
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 */
168  if ( !iaddr.GetHostByName(Transport::Config.hostname) ) {
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 
192  if ( !iaddr.GetHostByName(Transport::Config.hostname) ) {
193  std::cerr << "ERROR: Cannot resolve " << Transport::Config.hostname << ": Host unknown." << std::endl;
194  exit(1);
195  }
196  }
197 
198  iaddr.port(Transport::Config.port);
199 }
200 
202 static int
204 {
205  static struct addrinfo *AI = NULL;
206  addr.getAddrInfo(AI);
207  int res = connect(sock, AI->ai_addr, AI->ai_addrlen);
210  return res;
211 }
212 
213 bool
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 
237 ssize_t
238 Transport::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 
261 ssize_t
262 Transport::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 
284 void
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  */
295 static int
296 verifyByCA(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 
327 static int
328 verifyTlsCertificate(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
345 static void
346 gnutlsDebugHandler(int level, const char *msg)
347 {
348  debugVerbose(level, "GnuTLS: " << msg);
349 }
350 #endif
351 
352 void
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
402 static bool
403 doTlsHandshake(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 
431 static bool
433 {
434  const char *err = NULL;
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.
448 static 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
467 static bool
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 
487 bool
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 #endif
507  return true;
508 }
509 
510 void
512 {
513 #if USE_GNUTLS
514  if (!Config.tlsEnabled)
515  return;
516 
517  debugVerbose(3, "Shutting down TLS library...");
518 
519  // release any existing session and credentials
520  gnutls_deinit(Config.session);
521  gnutls_anon_free_client_credentials(Config.anonCredentials);
522  gnutls_certificate_free_credentials(Config.certCredentials);
523 
524  // NP: gnutls init is re-entrant and lock-counted with deinit but not thread safe.
525  gnutls_global_deinit();
526  Config.tlsEnabled = false;
527 #endif
528 }
529 
int ioTimeout
I/O operation timeout.
Definition: Transport.h:50
static bool tryTlsCertificate(const char *hostname)
Definition: Transport.cc:468
unsigned short port() const
Definition: Address.cc:778
bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex)
Definition: Transport.cc:53
Parameters scParams
global squidcleint parameters
Definition: squidclient.cc:66
gnutls_certificate_credentials_t certCredentials
Definition: Transport.h:84
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
int type
Definition: errorpage.cc:152
const char * params
Definition: Transport.h:71
void InitTls()
Initialize TLS library environment when necessary.
Definition: Transport.cc:353
void CloseConnection()
close the current connection
Definition: Transport.cc:285
int i
Definition: membanger.c:49
uint16_t port
port on the server to contact
Definition: Transport.h:59
TheConfig Config
Definition: Transport.cc:23
static void gnutlsDebugHandler(int level, const char *msg)
Definition: Transport.cc:346
int conn
the current server connection FD
Definition: Transport.cc:26
bool MaybeStartTls(const char *hostname)
Definition: Transport.cc:488
const char * hostname
the destination server host name to contact
Definition: Transport.h:56
gnutls_anon_client_credentials_t anonCredentials
anonymous client credentials
Definition: Transport.h:81
bool tlsEnabled
whether to enable TLS on the server connnection
Definition: Transport.h:62
void usage()
display Transport Options command line help to stderr
Definition: Transport.cc:29
char * toUrl(char *buf, unsigned int len) const
Definition: Address.cc:884
int verbosityLevel
Definition: Parameters.h:27
const char * xstrerr(int error)
Definition: xstrerror.cc:83
int opterr
Definition: getopt.c:47
parameters controlling outgoing connection
Definition: Transport.h:24
ssize_t Write(const void *buf, size_t len)
Definition: Transport.cc:238
std::list< std::string > caFiles
Definition: Transport.h:77
bool Connect()
locate and connect to the configured server
Definition: Transport.cc:214
void * addr
Definition: membanger.c:46
static bool tryTlsAnonymous()
Definition: Transport.cc:449
void setEmpty()
Fast reset of the stored content to what would be after default constructor.
Definition: Address.cc:184
#define debugVerbose(LEVEL, MESSAGE)
display debug messages at varying verbosity levels
Definition: Parameters.h:31
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition: Address.cc:599
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
void TimerStart()
start timing a new transaction
Definition: Ping.cc:79
#define CACHE_HTTP_PORT
Definition: squid.h:21
void const char * buf
Definition: stub_helper.cc:16
int optind
Definition: getopt.c:48
ssize_t Read(void *buf, size_t len)
Definition: Transport.cc:262
bool GetHostByName(const char *s)
Definition: Address.cc:372
std::list< std::string > certFiles
Definition: Transport.h:74
socklen_t ai_addrlen
const char * localHost
the local hostname to bind as for outgoing IP
Definition: Transport.h:53
static int verifyByCA(gnutls_session_t session)
Definition: Transport.cc:296
gnutls_session_t session
TLS session state.
Definition: Transport.h:87
#define IPV6_OFF
Definition: tools.h:19
struct sockaddr * ai_addr
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
#define IPV6_SPECIAL_V4MAPPING
Definition: tools.h:21
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:23
static bool doTlsHandshake(const char *type)
Definition: Transport.cc:403
void ShutdownTls()
De-initialize TLS library environment when necessary.
Definition: Transport.cc:511
static int verifyTlsCertificate(gnutls_session_t session)
Definition: Transport.cc:328
#define IPV6_SPECIAL_SPLITSTACK
Definition: tools.h:22
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 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 const char * shortOpStr
Definition: main.cc:423
char * optarg
Definition: getopt.c:51
#define NULL
Definition: types.h:166
static bool loadTlsParameters()
Definition: Transport.cc:432
static void resolveDestination(Ip::Address &iaddr)
Definition: Transport.cc:148
bool tlsAnonymous
whether to do anonymous TLS (non-authenticated)
Definition: Transport.h:65

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors