XRootD
XrdHttpProtocol.cc
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // This file is part of XrdHTTP: A pragmatic implementation of the
3 // HTTP/WebDAV protocol for the Xrootd framework
4 //
5 // Copyright (c) 2013 by European Organization for Nuclear Research (CERN)
6 // Author: Fabrizio Furano <furano@cern.ch>
7 // File Date: Nov 2012
8 //------------------------------------------------------------------------------
9 // XRootD is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Lesser General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // XRootD is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public License
20 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
21 //------------------------------------------------------------------------------
22 
23 
24 #include "XrdVersion.hh"
25 
26 #include "Xrd/XrdBuffer.hh"
27 #include "Xrd/XrdLink.hh"
28 #include "XProtocol/XProtocol.hh"
29 #include "XrdOuc/XrdOucStream.hh"
30 #include "XrdOuc/XrdOucEnv.hh"
31 #include "XrdOuc/XrdOucGMap.hh"
32 #include "XrdSys/XrdSysE2T.hh"
33 #include "XrdSys/XrdSysTimer.hh"
35 #include "XrdHttpTrace.hh"
36 #include "XrdHttpProtocol.hh"
37 
38 #include <sys/stat.h>
39 #include "XrdHttpUtils.hh"
40 #include "XrdHttpSecXtractor.hh"
41 #include "XrdHttpExtHandler.hh"
42 
43 #include "XrdTls/XrdTls.hh"
44 #include "XrdTls/XrdTlsContext.hh"
45 #include "XrdOuc/XrdOucUtils.hh"
47 
48 #include <openssl/err.h>
49 #include <openssl/ssl.h>
50 #include <vector>
51 #include <arpa/inet.h>
52 #include <sstream>
53 #include <cctype>
54 #include <sys/stat.h>
55 #include <fcntl.h>
56 #include <algorithm>
57 
58 #define XRHTTP_TK_GRACETIME 600
59 
60 
61 /******************************************************************************/
62 /* G l o b a l s */
63 /******************************************************************************/
64 
65 // It seems that eos needs this to be present
66 const char *XrdHttpSecEntityTident = "http";
67 
68 //
69 // Static stuff
70 //
71 
72 int XrdHttpProtocol::hailWait = 60000;
73 int XrdHttpProtocol::readWait = 300000;
74 int XrdHttpProtocol::Port = 1094;
76 
77 //XrdXrootdStats *XrdHttpProtocol::SI = 0;
78 char *XrdHttpProtocol::sslcert = 0;
79 char *XrdHttpProtocol::sslkey = 0;
84 bool XrdHttpProtocol::listdeny = false;
88 
91 bool XrdHttpProtocol::isdesthttps = false;
94 
95 char *XrdHttpProtocol::gridmap = 0;
99 BIO *XrdHttpProtocol::sslbio_err = 0;
100 XrdHttpSecXtractor *XrdHttpProtocol::secxtractor = 0;
101 bool XrdHttpProtocol::isRequiredXtractor = false;
102 struct XrdHttpProtocol::XrdHttpExtHandlerInfo XrdHttpProtocol::exthandler[MAX_XRDHTTPEXTHANDLERS];
103 int XrdHttpProtocol::exthandlercnt = 0;
104 std::map< std::string, std::string > XrdHttpProtocol::hdr2cgimap;
105 
106 bool XrdHttpProtocol::usingEC = false;
107 bool XrdHttpProtocol::hasCache= false;
108 
109 XrdScheduler *XrdHttpProtocol::Sched = 0; // System scheduler
110 XrdBuffManager *XrdHttpProtocol::BPool = 0; // Buffer manager
111 XrdSysError XrdHttpProtocol::eDest = 0; // Error message handler
112 XrdSecService *XrdHttpProtocol::CIA = 0; // Authentication Server
113 int XrdHttpProtocol::m_bio_type = 0; // BIO type identifier for our custom BIO.
114 BIO_METHOD *XrdHttpProtocol::m_bio_method = NULL; // BIO method constructor.
115 char *XrdHttpProtocol::xrd_cslist = nullptr;
120 
121 decltype(XrdHttpProtocol::m_staticheader_map) XrdHttpProtocol::m_staticheader_map;
122 decltype(XrdHttpProtocol::m_staticheaders) XrdHttpProtocol::m_staticheaders;
123 
125 
126 namespace
127 {
128 const char *TraceID = "Protocol";
129 }
130 
132 {
134 
135 static const int hsmAuto = -1;
136 static const int hsmOff = 0;
137 static const int hsmMan = 1;
138 static const int hsmOn = 1; // Dual purpose but use a meaningful varname
139 
142 XrdTlsContext::ClientAuthSetting tlsClientAuth = XrdTlsContext::ClientAuthSetting::kOn;
143 bool httpsspec = false;
144 bool xrdctxVer = false;
145 }
146 
147 using namespace XrdHttpProtoInfo;
148 
149 /******************************************************************************/
150 /* P r o t o c o l M a n a g e m e n t S t a c k s */
151 /******************************************************************************/
152 
154 XrdHttpProtocol::ProtStack("ProtStack",
155  "xrootd protocol anchor");
156 
157 
158 /******************************************************************************/
159 /* U g l y O p e n S S L w o r k a r o u n d s */
160 /******************************************************************************/
161 #if OPENSSL_VERSION_NUMBER < 0x10100000L
162 void *BIO_get_data(BIO *bio) {
163  return bio->ptr;
164 }
165 void BIO_set_data(BIO *bio, void *ptr) {
166  bio->ptr = ptr;
167 }
168 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
169 int BIO_get_flags(BIO *bio) {
170  return bio->flags;
171 }
172 #endif
173 void BIO_set_flags(BIO *bio, int flags) {
174  bio->flags = flags;
175 }
176 int BIO_get_init(BIO *bio) {
177  return bio->init;
178 }
179 void BIO_set_init(BIO *bio, int init) {
180  bio->init = init;
181 }
182 void BIO_set_shutdown(BIO *bio, int shut) {
183  bio->shutdown = shut;
184 }
185 int BIO_get_shutdown(BIO *bio) {
186  return bio->shutdown;
187 }
188 
189 #endif
190 /******************************************************************************/
191 /* X r d H T T P P r o t o c o l C l a s s */
192 /******************************************************************************/
193 /******************************************************************************/
194 /* C o n s t r u c t o r */
195 /******************************************************************************/
196 
198 : XrdProtocol("HTTP protocol handler"), ProtLink(this),
199 SecEntity(""), CurrentReq(this, ReadRangeConfig) {
200  myBuff = 0;
201  Addr_str = 0;
202  Reset();
203  ishttps = imhttps;
204 
205 }
206 
207 /******************************************************************************/
208 /* A s s i g n m e n t O p e r a t o r */
209 
210 /******************************************************************************/
211 
213 
214  return *this;
215 }
216 
217 /******************************************************************************/
218 /* M a t c h */
219 /******************************************************************************/
220 
221 #define TRACELINK lp
222 
224  char mybuf[16], mybuf2[1024];
225  XrdHttpProtocol *hp;
226  int dlen;
227  bool myishttps = false;
228 
229  // Peek at the first 20 bytes of data
230  //
231  if ((dlen = lp->Peek(mybuf, (int) sizeof (mybuf), hailWait)) < (int) sizeof (mybuf)) {
232  if (dlen <= 0) lp->setEtext("handshake not received");
233  return (XrdProtocol *) 0;
234  }
235  mybuf[dlen - 1] = '\0';
236 
237  // Trace the data
238  //
239 
240  TRACEI(DEBUG, "received dlen: " << dlen);
241  //TRACEI(REQ, "received buf: " << mybuf);
242  mybuf2[0] = '\0';
243  for (int i = 0; i < dlen; i++) {
244  char mybuf3[16];
245  sprintf(mybuf3, "%.02d ", mybuf[i]);
246  strcat(mybuf2, mybuf3);
247 
248  }
249  TRACEI(DEBUG, "received dump: " << mybuf2);
250 
251  // Decide if it looks http or not. For now we are happy if all the received characters are alphanumeric
252  bool ismine = true;
253  for (int i = 0; i < dlen - 1; i++)
254  if (!isprint(mybuf[i]) && (mybuf[i] != '\r') && (mybuf[i] != '\n')) {
255  ismine = false;
256  TRACEI(DEBUG, "This does not look like http at pos " << i);
257  break;
258  }
259 
260  // If it does not look http then look if it looks like https
261  if ((!ismine) && (dlen >= 4)) {
262  char check[4] = {00, 00, 00, 00};
263  if (memcmp(mybuf, check, 4)) {
264 
265  if (httpsmode) {
266  ismine = true;
267  myishttps = true;
268  TRACEI(DEBUG, "This may look like https");
269  } else {
270  TRACEI(ALL, "This may look like https, but https is not configured");
271  }
272 
273  }
274  }
275 
276  if (!ismine) {
277  TRACEI(DEBUG, "This does not look like https. Protocol not matched.");
278  return (XrdProtocol *) 0;
279  }
280 
281  // It does look http or https...
282  // Get a protocol object off the stack (if none, allocate a new one)
283  //
284 
285  TRACEI(REQ, "Protocol matched. https: " << myishttps);
286  if (!(hp = ProtStack.Pop())) hp = new XrdHttpProtocol(myishttps);
287  else
288  hp->ishttps = myishttps;
289 
290  // We now have to do some work arounds to tell the underlying framework
291  // that is is https without invoking TLS on the actual link. Eventually,
292  // we should just use the link's TLS native implementation.
293  //
294  hp->SecEntity.addrInfo = lp->AddrInfo();
295  XrdNetAddr *netP = const_cast<XrdNetAddr*>(lp->NetAddr());
296  netP->SetDialect("https");
297  netP->SetTLS(true);
298 
299  // Allocate 1MB buffer from pool
300  if (!hp->myBuff) {
301  hp->myBuff = BPool->Obtain(1024 * 1024);
302  }
303  hp->myBuffStart = hp->myBuffEnd = hp->myBuff->buff;
304 
305  // Bind the protocol to the link and return the protocol
306  //
307  hp->Link = lp;
308  return (XrdProtocol *) hp;
309 }
310 
311 char *XrdHttpProtocol::GetClientIPStr() {
312  char buf[256];
313  buf[0] = '\0';
314  if (!Link) return strdup("unknown");
315  XrdNetAddrInfo *ai = Link->AddrInfo();
316  if (!ai) return strdup("unknown");
317 
318  if (!Link->AddrInfo()->Format(buf, 255, XrdNetAddrInfo::fmtAddr, XrdNetAddrInfo::noPort)) return strdup("unknown");
319 
320  return strdup(buf);
321 }
322 
323 // Various routines for handling XrdLink as BIO objects within OpenSSL.
324 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
325 int BIO_XrdLink_write(BIO *bio, const char *data, size_t datal, size_t *written)
326 {
327  if (!data || !bio) {
328  *written = 0;
329  return 0;
330  }
331 
332  XrdLink *lp=static_cast<XrdLink *>(BIO_get_data(bio));
333 
334  errno = 0;
335  int ret = lp->Send(data, datal);
336  BIO_clear_retry_flags(bio);
337  if (ret <= 0) {
338  *written = 0;
339  if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
340  BIO_set_retry_write(bio);
341  return ret;
342  }
343  *written = ret;
344  return 1;
345 }
346 #else
347 int BIO_XrdLink_write(BIO *bio, const char *data, int datal)
348 {
349  if (!data || !bio) {
350  errno = ENOMEM;
351  return -1;
352  }
353 
354  errno = 0;
355  XrdLink *lp = static_cast<XrdLink *>(BIO_get_data(bio));
356  int ret = lp->Send(data, datal);
357  BIO_clear_retry_flags(bio);
358  if (ret <= 0) {
359  if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
360  BIO_set_retry_write(bio);
361  }
362  return ret;
363 }
364 #endif
365 
366 
367 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
368 static int BIO_XrdLink_read(BIO *bio, char *data, size_t datal, size_t *read)
369 {
370  if (!data || !bio) {
371  *read = 0;
372  return 0;
373  }
374 
375  errno = 0;
376 
377  XrdLink *lp = static_cast<XrdLink *>(BIO_get_data(bio));
378  int ret = lp->Recv(data, datal);
379  BIO_clear_retry_flags(bio);
380  if (ret <= 0) {
381  *read = 0;
382  if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
383  BIO_set_retry_read(bio);
384  return ret;
385  }
386  *read = ret;
387 }
388 #else
389 static int BIO_XrdLink_read(BIO *bio, char *data, int datal)
390 {
391  if (!data || !bio) {
392  errno = ENOMEM;
393  return -1;
394  }
395 
396  errno = 0;
397  XrdLink *lp = static_cast<XrdLink *>(BIO_get_data(bio));
398  int ret = lp->Recv(data, datal);
399  BIO_clear_retry_flags(bio);
400  if (ret <= 0) {
401  if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
402  BIO_set_retry_read(bio);
403  }
404  return ret;
405 }
406 #endif
407 
408 
409 static int BIO_XrdLink_create(BIO *bio)
410 {
411 
412 
413  BIO_set_init(bio, 0);
414  //BIO_set_next(bio, 0);
415  BIO_set_data(bio, NULL);
416  BIO_set_flags(bio, 0);
417 
418 #if OPENSSL_VERSION_NUMBER < 0x10100000L
419 
420  bio->num = 0;
421 
422 #endif
423 
424  return 1;
425 }
426 
427 
428 static int BIO_XrdLink_destroy(BIO *bio)
429 {
430  if (bio == NULL) return 0;
431  if (BIO_get_shutdown(bio)) {
432  if (BIO_get_data(bio)) {
433  static_cast<XrdLink*>(BIO_get_data(bio))->Close();
434  }
435  BIO_set_init(bio, 0);
436  BIO_set_flags(bio, 0);
437  }
438  return 1;
439 }
440 
441 
442 static long BIO_XrdLink_ctrl(BIO *bio, int cmd, long num, void * ptr)
443 {
444  long ret = 1;
445  switch (cmd) {
446  case BIO_CTRL_GET_CLOSE:
447  ret = BIO_get_shutdown(bio);
448  break;
449  case BIO_CTRL_SET_CLOSE:
450  BIO_set_shutdown(bio, (int)num);
451  break;
452  case BIO_CTRL_DUP:
453  case BIO_CTRL_FLUSH:
454  ret = 1;
455  break;
456  default:
457  ret = 0;
458  break;
459  }
460  return ret;
461 }
462 
463 
464 BIO *XrdHttpProtocol::CreateBIO(XrdLink *lp)
465 {
466  if (m_bio_method == NULL)
467  return NULL;
468 
469  BIO *ret = BIO_new(m_bio_method);
470 
471  BIO_set_shutdown(ret, 0);
472  BIO_set_data(ret, lp);
473  BIO_set_init(ret, 1);
474  return ret;
475 }
476 
477 
478 /******************************************************************************/
479 /* P r o c e s s */
480 /******************************************************************************/
481 
482 #undef TRACELINK
483 #define TRACELINK Link
484 
485 int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here
486 {
487  int rc = 0;
488 
489  TRACEI(DEBUG, " Process. lp:"<<(void *)lp<<" reqstate: "<<CurrentReq.reqstate);
490 
491  if (!myBuff || !myBuff->buff || !myBuff->bsize) {
492  TRACE(ALL, " Process. No buffer available. Internal error.");
493  return -1;
494  }
495 
496 
497  if (!SecEntity.host) {
498  char *nfo = GetClientIPStr();
499  if (nfo) {
500  TRACEI(REQ, " Setting host: " << nfo);
501  SecEntity.host = nfo;
502  strcpy(SecEntity.prot, "http");
503  }
504  }
505 
506 
507 
508  // If https then check independently for the ssl handshake
509  if (ishttps && !ssldone) {
510 
511  if (!ssl) {
512  sbio = CreateBIO(Link);
513  BIO_set_nbio(sbio, 1);
515  TRACE(ALL, "Failed to configure the TLS client authentication; invalid configuration");
516  return -1;
517  }
518  ssl = (SSL*)xrdctx->Session();
519  }
520 
521  if (!ssl) {
522  TRACEI(DEBUG, " SSL_new returned NULL");
523  ERR_print_errors(sslbio_err);
524  return -1;
525  }
526 
527  // If a secxtractor has been loaded
528  // maybe it wants to add its own initialization bits
529  if (secxtractor)
530  secxtractor->InitSSL(ssl, sslcadir);
531 
532  SSL_set_bio(ssl, sbio, sbio);
533  //SSL_set_connect_state(ssl);
534 
535  //SSL_set_fd(ssl, Link->FDnum());
536  struct timeval tv;
537  tv.tv_sec = 10;
538  tv.tv_usec = 0;
539  setsockopt(Link->FDnum(), SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
540  setsockopt(Link->FDnum(), SOL_SOCKET, SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
541 
542  TRACEI(DEBUG, " Entering SSL_accept...");
543  int res = SSL_accept(ssl);
544  TRACEI(DEBUG, " SSL_accept returned :" << res);
545  if ((res == -1) && (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ)) {
546  TRACEI(DEBUG, " SSL_accept wants to read more bytes... err:" << SSL_get_error(ssl, res));
547  return 1;
548  }
549 
550  if(res <= 0) {
551  ERR_print_errors(sslbio_err);
552  if (res < 0) {
553 
554  SSL_free(ssl);
555  ssl = 0;
556  return -1;
557  }
558  }
559 
560  BIO_set_nbio(sbio, 0);
561 
562  strcpy(SecEntity.prot, "https");
563 
564  // Get the voms string and auth information
565  if (tlsClientAuth == XrdTlsContext::ClientAuthSetting::kOn && HandleAuthentication(Link)) {
566  SSL_free(ssl);
567  ssl = 0;
568  return -1;
569  }
570 
571  ssldone = true;
572  if (TRACING(TRACE_AUTH)) {
574  }
575  }
576 
577 
578 
579  if (!DoingLogin) {
580  // Re-invocations triggered by the bridge have lp==0
581  // In this case we keep track of a different request state
582  if (lp) {
583 
584  // This is an invocation that was triggered by a socket event
585  // Read all the data that is available, throw it into the buffer
586  if ((rc = getDataOneShot(BuffAvailable())) < 0) {
587  // Error -> exit
588  return -1;
589  }
590 
591  // If we need more bytes, let's wait for another invokation
592  if (BuffUsed() < ResumeBytes) return 1;
593 
594 
595  } else
597  } else if (!DoneSetInfo && !CurrentReq.userAgent().empty()) { // DoingLogin is true, meaning the login finished.
598  std::string mon_info = "monitor info " + CurrentReq.userAgent();
599  DoneSetInfo = true;
600  if (mon_info.size() >= 1024) {
601  TRACEI(ALL, "User agent string too long");
602  } else if (!Bridge) {
603  TRACEI(ALL, "Internal logic error: Bridge is null after login");
604  } else {
605  TRACEI(DEBUG, "Setting " << mon_info);
606  memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
608  CurrentReq.xrdreq.set.modifier = '\0';
609  memset(CurrentReq.xrdreq.set.reserved, '\0', sizeof(CurrentReq.xrdreq.set.reserved));
610  CurrentReq.xrdreq.set.dlen = htonl(mon_info.size());
611  if (!Bridge->Run((char *) &CurrentReq.xrdreq, (char *) mon_info.c_str(), mon_info.size())) {
612  SendSimpleResp(500, nullptr, nullptr, "Could not set user agent.", 0, false);
613  return -1;
614  }
615  return 0;
616  }
617  } else {
618  DoingLogin = false;
619  }
620 
621  // Read the next request header, that is, read until a double CRLF is found
622 
623 
624  if (!CurrentReq.headerok) {
625 
626  // Read as many lines as possible into the buffer. An empty line breaks
627  while ((rc = BuffgetLine(tmpline)) > 0) {
628  std::string traceLine = tmpline.c_str();
629  if (TRACING(TRACE_DEBUG)) {
630  traceLine = obfuscateAuth(traceLine);
631  }
632  TRACE(DEBUG, " rc:" << rc << " got hdr line: " << traceLine);
633  if ((rc == 2) && (tmpline.length() > 1) && (tmpline[rc - 1] == '\n')) {
634  CurrentReq.headerok = true;
635  TRACE(DEBUG, " rc:" << rc << " detected header end.");
636  break;
637  }
638 
639 
641  TRACE(DEBUG, " Parsing first line: " << traceLine.c_str());
642  int result = CurrentReq.parseFirstLine((char *)tmpline.c_str(), rc);
643  if (result < 0) {
644  TRACE(DEBUG, " Parsing of first line failed with " << result);
645  return -1;
646  }
647  } else {
648  int result = CurrentReq.parseLine((char *) tmpline.c_str(), rc);
649  if(result < 0) {
650  TRACE(DEBUG, " Parsing of header line failed with " << result)
651  SendSimpleResp(400,NULL,NULL,"Malformed header line. Hint: ensure the line finishes with \"\\r\\n\"", 0, false);
652  return -1;
653  }
654  }
655 
656 
657  }
658 
659  // Here we have CurrentReq loaded with the header, or its relevant fields
660 
661  if (!CurrentReq.headerok) {
662  TRACEI(REQ, " rc:" << rc << "Header not yet complete.");
663 
664  // Here a subtle error condition. IF we failed reading a line AND the buffer
665  // has a reasonable amount of data available THEN we consider the header
666  // as corrupted and shutdown the client
667  if ((rc <= 0) && (BuffUsed() >= 16384)) {
668  TRACEI(ALL, "Corrupted header detected, or line too long. Disconnecting client.");
669  return -1;
670  }
671 
672 
673  if (CurrentReq.reqstate > 0)
675  // Waiting for more data
676  return 1;
677  }
678 
679  }
680 
681  // If we are in self-redirect mode, then let's do it
682  // Do selfredirect only with 'simple' requests, otherwise poor clients may misbehave
683  if (ishttps && ssldone && selfhttps2http &&
686  char hash[512];
687  time_t timenow = time(0);
688 
689 
691  &SecEntity,
692  timenow,
693  secretkey);
694 
695 
696 
697  if (hash[0]) {
698 
699  // Workaround... delete the previous opaque information
700  if (CurrentReq.opaque) {
701  delete CurrentReq.opaque;
702  CurrentReq.opaque = 0;
703  }
704 
705  TRACEI(REQ, " rc:" << rc << " self-redirecting to http with security token.");
706 
707  XrdOucString dest = "Location: http://";
708  // Here I should put the IP addr of the server
709 
710  // We have to recompute it here because we don't know to which
711  // interface the client had connected to
712  struct sockaddr_storage sa;
713  socklen_t sl = sizeof(sa);
714  getsockname(this->Link->AddrInfo()->SockFD(), (struct sockaddr*)&sa, &sl);
715 
716  // now get it back and print it
717  char buf[256];
718  bool ok = false;
719 
720  switch (sa.ss_family) {
721  case AF_INET:
722  if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
723  if (Addr_str) free(Addr_str);
724  Addr_str = strdup(buf);
725  ok = true;
726  }
727  break;
728  case AF_INET6:
729  if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
730  if (Addr_str) free(Addr_str);
731  Addr_str = (char *)malloc(strlen(buf)+3);
732  strcpy(Addr_str, "[");
733  strcat(Addr_str, buf);
734  strcat(Addr_str, "]");
735  ok = true;
736  }
737  break;
738  default:
739  TRACEI(REQ, " Can't recognize the address family of the local host.");
740  }
741 
742  if (ok) {
743  dest += Addr_str;
744  dest += ":";
745  dest += Port_str;
746  dest += CurrentReq.resource.c_str();
747  TRACEI(REQ," rc:"<<rc<<" self-redirecting to http with security token: '"
748  << dest.c_str() << "'");
749 
750 
751  CurrentReq.appendOpaque(dest, &SecEntity, hash, timenow);
752  SendSimpleResp(302, NULL, (char *) dest.c_str(), 0, 0, true);
753  CurrentReq.reset();
754  return -1;
755  }
756 
757  TRACEI(REQ, " rc:" << rc << " Can't perform self-redirection.");
758 
759  }
760  else {
761  TRACEI(ALL, " Could not calculate self-redirection hash");
762  }
763  }
764 
765  // If this is not https, then extract the signed information from the url
766  // and fill the SecEntity structure as if we were using https
767  if (!ishttps && !ssldone) {
768 
769 
770  if (CurrentReq.opaque) {
771  char * tk = CurrentReq.opaque->Get("xrdhttptk");
772  // If there is a hash then we use it as authn info
773  if (tk) {
774 
775  time_t tim = 0;
776  char * t = CurrentReq.opaque->Get("xrdhttptime");
777  if (t) tim = atoi(t);
778  if (!t) {
779  TRACEI(REQ, " xrdhttptime not specified. Authentication failed.");
780  return -1;
781  }
782  if (abs(time(0) - tim) > XRHTTP_TK_GRACETIME) {
783  TRACEI(REQ, " Token expired. Authentication failed.");
784  return -1;
785  }
786 
787  // Fill the Secentity from the fields in the URL:name, vo, host
788  char *nfo;
789 
790  nfo = CurrentReq.opaque->Get("xrdhttpvorg");
791  if (nfo) {
792  TRACEI(DEBUG, " Setting vorg: " << nfo);
793  SecEntity.vorg = strdup(nfo);
794  TRACEI(REQ, " Setting vorg: " << SecEntity.vorg);
795  }
796 
797  nfo = CurrentReq.opaque->Get("xrdhttpname");
798  if (nfo) {
799  TRACEI(DEBUG, " Setting name: " << nfo);
800  SecEntity.name = unquote(nfo);
801  TRACEI(REQ, " Setting name: " << SecEntity.name);
802  }
803 
804  nfo = CurrentReq.opaque->Get("xrdhttphost");
805  if (nfo) {
806  TRACEI(DEBUG, " Setting host: " << nfo);
807  if (SecEntity.host) free(SecEntity.host);
808  SecEntity.host = unquote(nfo);
809  TRACEI(REQ, " Setting host: " << SecEntity.host);
810  }
811 
812  nfo = CurrentReq.opaque->Get("xrdhttpdn");
813  if (nfo) {
814  TRACEI(DEBUG, " Setting dn: " << nfo);
815  SecEntity.moninfo = unquote(nfo);
816  TRACEI(REQ, " Setting dn: " << SecEntity.moninfo);
817  }
818 
819  nfo = CurrentReq.opaque->Get("xrdhttprole");
820  if (nfo) {
821  TRACEI(DEBUG, " Setting role: " << nfo);
822  SecEntity.role = unquote(nfo);
823  TRACEI(REQ, " Setting role: " << SecEntity.role);
824  }
825 
826  nfo = CurrentReq.opaque->Get("xrdhttpgrps");
827  if (nfo) {
828  TRACEI(DEBUG, " Setting grps: " << nfo);
829  SecEntity.grps = unquote(nfo);
830  TRACEI(REQ, " Setting grps: " << SecEntity.grps);
831  }
832 
833  nfo = CurrentReq.opaque->Get("xrdhttpendorsements");
834  if (nfo) {
835  TRACEI(DEBUG, " Setting endorsements: " << nfo);
837  TRACEI(REQ, " Setting endorsements: " << SecEntity.endorsements);
838  }
839 
840  nfo = CurrentReq.opaque->Get("xrdhttpcredslen");
841  if (nfo) {
842  TRACEI(DEBUG, " Setting credslen: " << nfo);
843  char *s1 = unquote(nfo);
844  if (s1 && s1[0]) {
845  SecEntity.credslen = atoi(s1);
846  TRACEI(REQ, " Setting credslen: " << SecEntity.credslen);
847  }
848  if (s1) free(s1);
849  }
850 
851  if (SecEntity.credslen) {
852  nfo = CurrentReq.opaque->Get("xrdhttpcreds");
853  if (nfo) {
854  TRACEI(DEBUG, " Setting creds: " << nfo);
855  SecEntity.creds = unquote(nfo);
856  TRACEI(REQ, " Setting creds: " << SecEntity.creds);
857  }
858  }
859 
860  char hash[512];
861 
863  &SecEntity,
864  tim,
865  secretkey);
866 
867  if (compareHash(hash, tk)) {
868  TRACEI(REQ, " Invalid tk '" << tk << "' != '" << hash << "'(calculated). Authentication failed.");
869  return -1;
870  }
871 
872  } else {
873  // Client is plain http. If we have a secret key then we reject it
874  if (secretkey) {
875  TRACEI(ALL, " Rejecting plain http with no valid token as we have a secretkey.");
876  return -1;
877  }
878  }
879 
880  } else {
881  // Client is plain http. If we have a secret key then we reject it
882  if (secretkey) {
883  TRACEI(ALL, " Rejecting plain http with no valid token as we have a secretkey.");
884  return -1;
885  }
886  }
887 
888  ssldone = true;
889  }
890 
891 
892 
893  // Now we have everything that is needed to try the login
894  // Remember that if there is an exthandler then it has the responsibility
895  // for authorization in the paths that it manages
896  if (!Bridge && !FindMatchingExtHandler(CurrentReq)) {
897  if (SecEntity.name)
898  Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, SecEntity.name, ishttps ? "https" : "http");
899  else
900  Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, "unknown", ishttps ? "https" : "http");
901 
902  if (!Bridge) {
903  TRACEI(REQ, " Authorization failed.");
904  return -1;
905  }
906 
907  // Let the bridge process the login, and then reinvoke us
908  DoingLogin = true;
909  return 0;
910  }
911 
912  // Compute and send the response. This may involve further reading from the socket
913  rc = CurrentReq.ProcessHTTPReq();
914  if (rc < 0)
915  CurrentReq.reset();
916 
917 
918 
919  TRACEI(REQ, "Process is exiting rc:" << rc);
920  return rc;
921 }
922 /******************************************************************************/
923 /* R e c y c l e */
924 /******************************************************************************/
925 
926 #undef TRACELINK
927 #define TRACELINK Link
928 
929 void XrdHttpProtocol::Recycle(XrdLink *lp, int csec, const char *reason) {
930 
931  // Release all appendages
932  //
933 
934  Cleanup();
935 
936 
937  // Set fields to starting point (debugging mostly)
938  //
939  Reset();
940 
941  // Push ourselves on the stack
942  //
944 }
945 
946 int XrdHttpProtocol::Stats(char *buff, int blen, int do_sync) {
947  // Synchronize statistics if need be
948  //
949  // if (do_sync) {
950  //
951  // SI->statsMutex.Lock();
952  // SI->readCnt += numReads;
953  // cumReads += numReads;
954  // numReads = 0;
955  // SI->prerCnt += numReadP;
956  // cumReadP += numReadP;
957  // numReadP = 0;
958  // SI->rvecCnt += numReadV;
959  // cumReadV += numReadV;
960  // numReadV = 0;
961  // SI->rsegCnt += numSegsV;
962  // cumSegsV += numSegsV;
963  // numSegsV = 0;
964  // SI->writeCnt += numWrites;
965  // cumWrites += numWrites;
966  // numWrites = 0;
967  // SI->statsMutex.UnLock();
968  // }
969  //
970  // // Now return the statistics
971  // //
972  // return SI->Stats(buff, blen, do_sync);
973 
974  return 0;
975 }
976 
977 /******************************************************************************/
978 /* C o n f i g */
979 /******************************************************************************/
980 
981 #define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
982 //#define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, ConfigFN, myEnv)
983 #define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
984 
985 #define HTTPS_ALERT(x,y,z) httpsspec = true;\
986  if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
987  eDest.Say("Config http." x " overrides the xrd." y " directive.")
988 
989 int XrdHttpProtocol::Config(const char *ConfigFN, XrdOucEnv *myEnv) {
990  XrdOucEnv cfgEnv;
991  XrdOucStream Config(&eDest, getenv("XRDINSTANCE"), &cfgEnv, "=====> ");
992  std::vector<extHInfo> extHIVec;
993  char *var;
994  int cfgFD, GoNo, NoGo = 0, ismine;
995 
996  var = nullptr;
997  XrdOucEnv::Import("XRD_READV_LIMITS", var);
999 
1000  pmarkHandle = (XrdNetPMark* ) myEnv->GetPtr("XrdNetPMark*");
1001 
1003  auto nonIanaChecksums = cksumHandler.getNonIANAConfiguredCksums();
1004  if(nonIanaChecksums.size()) {
1005  std::stringstream warningMsgSS;
1006  warningMsgSS << "Config warning: the following checksum algorithms are not IANA compliant: [";
1007  std::string unknownCksumString;
1008  for(auto unknownCksum: nonIanaChecksums) {
1009  unknownCksumString += unknownCksum + ",";
1010  }
1011  unknownCksumString.erase(unknownCksumString.size() - 1);
1012  warningMsgSS << unknownCksumString << "]" << ". They therefore cannot be queried by a user via HTTP." ;
1013  eDest.Say(warningMsgSS.str().c_str());
1014  }
1015 
1016  // Initialize our custom BIO type.
1017  if (!m_bio_type) {
1018 
1019  #if OPENSSL_VERSION_NUMBER < 0x10100000L
1020  m_bio_type = (26|0x0400|0x0100);
1021  m_bio_method = static_cast<BIO_METHOD*>(OPENSSL_malloc(sizeof(BIO_METHOD)));
1022 
1023  if (m_bio_method) {
1024  memset(m_bio_method, '\0', sizeof(BIO_METHOD));
1025  m_bio_method->type = m_bio_type;
1026  m_bio_method->bwrite = BIO_XrdLink_write;
1027  m_bio_method->bread = BIO_XrdLink_read;
1028  m_bio_method->create = BIO_XrdLink_create;
1029  m_bio_method->destroy = BIO_XrdLink_destroy;
1031  }
1032  #else
1033  // OpenSSL 1.1 has an internal counter for generating unique types.
1034  // We'll switch to that when widely available.
1035  m_bio_type = BIO_get_new_index();
1036  m_bio_method = BIO_meth_new(m_bio_type, "xrdhttp-bio-method");
1037 
1038  if (m_bio_method) {
1039  BIO_meth_set_write(m_bio_method, BIO_XrdLink_write);
1040  BIO_meth_set_read(m_bio_method, BIO_XrdLink_read);
1041  BIO_meth_set_create(m_bio_method, BIO_XrdLink_create);
1042  BIO_meth_set_destroy(m_bio_method, BIO_XrdLink_destroy);
1043  BIO_meth_set_ctrl(m_bio_method, BIO_XrdLink_ctrl);
1044  }
1045 
1046  #endif
1047  }
1048 
1049  // If we have a tls context record whether it configured for verification
1050  // so that we can provide meaningful error and warning messages.
1051  //
1053 
1054  // Open and attach the config file
1055  //
1056  if ((cfgFD = open(ConfigFN, O_RDONLY, 0)) < 0)
1057  return eDest.Emsg("Config", errno, "open config file", ConfigFN);
1058  Config.Attach(cfgFD);
1059  static const char *cvec[] = { "*** http protocol config:", 0 };
1060  Config.Capture(cvec);
1061 
1062  // Process items
1063  //
1064  while ((var = Config.GetMyFirstWord())) {
1065  if ((ismine = !strncmp("http.", var, 5)) && var[5]) var += 5;
1066 
1067  if (ismine) {
1068  if TS_Xeq("trace", xtrace);
1069  else if TS_Xeq("cert", xsslcert);
1070  else if TS_Xeq("key", xsslkey);
1071  else if TS_Xeq("cadir", xsslcadir);
1072  else if TS_Xeq("cipherfilter", xsslcipherfilter);
1073  else if TS_Xeq("gridmap", xgmap);
1074  else if TS_Xeq("cafile", xsslcafile);
1075  else if TS_Xeq("secretkey", xsecretkey);
1076  else if TS_Xeq("desthttps", xdesthttps);
1077  else if TS_Xeq("secxtractor", xsecxtractor);
1078  else if TS_Xeq3("exthandler", xexthandler);
1079  else if TS_Xeq("selfhttps2http", xselfhttps2http);
1080  else if TS_Xeq("embeddedstatic", xembeddedstatic);
1081  else if TS_Xeq("listingredir", xlistredir);
1082  else if TS_Xeq("staticredir", xstaticredir);
1083  else if TS_Xeq("staticpreload", xstaticpreload);
1084  else if TS_Xeq("staticheader", xstaticheader);
1085  else if TS_Xeq("listingdeny", xlistdeny);
1086  else if TS_Xeq("header2cgi", xheader2cgi);
1087  else if TS_Xeq("httpsmode", xhttpsmode);
1088  else if TS_Xeq("tlsreuse", xtlsreuse);
1089  else if TS_Xeq("auth", xauth);
1090  else if TS_Xeq("tlsclientauth", xtlsclientauth);
1091  else {
1092  eDest.Say("Config warning: ignoring unknown directive '", var, "'.");
1093  Config.Echo();
1094  continue;
1095  }
1096  if (GoNo) {
1097  Config.Echo();
1098  NoGo = 1;
1099  }
1100  }
1101  }
1102 
1103 // To minimize message confusion down, if an error occurred during config
1104 // parsing, just bail out now with a confirming message.
1105 //
1106  if (NoGo)
1107  {eDest.Say("Config failure: one or more directives are flawed!");
1108  return 1;
1109  }
1110 
1111 // Some headers must always be converted to CGI key=value pairs
1112 //
1113  hdr2cgimap["Cache-Control"] = "cache-control";
1114 
1115 // Test if XrdEC is loaded
1116  if (getenv("XRDCL_EC")) usingEC = true;
1117 
1118 // Pre-compute the static headers
1119 //
1120  const auto default_verb = m_staticheader_map.find("");
1121  std::string default_static_headers;
1122  if (default_verb != m_staticheader_map.end()) {
1123  for (const auto &header_entry : default_verb->second) {
1124  default_static_headers += header_entry.first + ": " + header_entry.second + "\r\n";
1125  }
1126  }
1127  m_staticheaders[""] = default_static_headers;
1128  for (const auto &item : m_staticheader_map) {
1129  if (item.first.empty()) {
1130  continue; // Skip default case; already handled
1131  }
1132  auto headers = default_static_headers;
1133  for (const auto &header_entry : item.second) {
1134  headers += header_entry.first + ": " + header_entry.second + "\r\n";
1135  }
1136 
1137  m_staticheaders[item.first] = headers;
1138  }
1139 
1140 // Test if this is a caching server
1141 //
1142  if (myEnv->Get("XrdCache")) hasCache = true;
1143 
1144 // If https was disabled, then issue a warning message if xrdtls configured
1145 // of it's disabled because httpsmode was auto and xrdtls was not configured.
1146 // If we get past this point then we know https is a plausible option but we
1147 // can still fail if we cannot supply any missing but required options.
1148 //
1149  if (httpsmode == hsmOff || (httpsmode == hsmAuto && !xrdctx && !httpsspec))
1150  {const char *why = (httpsmode == hsmOff ? "has been disabled!"
1151  : "was not configured.");
1152  const char *what = Configed();
1153 
1154  eDest.Say("Config warning: HTTPS functionality ", why);
1155  httpsmode = hsmOff;
1156 
1157  LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1158  if (what)
1159  {eDest.Say("Config failure: ", what, " HTTPS but it ", why);
1160  NoGo = 1;
1161  }
1162  return NoGo;
1163  }
1164 
1165 // Warn if a private key was specified without a cert as this has no meaning
1166 // even as an auto overide as they must be paired.
1167 //
1168  if (sslkey && !sslcert)
1169  {eDest.Say("Config warning: specifying http.key without http.cert "
1170  "is meaningless; ignoring key!");
1171  free(sslkey); sslkey = 0;
1172  }
1173 
1174 // If the mode is manual then we need to have at least a cert.
1175 //
1176  if (httpsmode == hsmMan)
1177  {if (!sslcert)
1178  {eDest.Say("Config failure: 'httpsmode manual' requires atleast a "
1179  "a cert specification!");
1180  return 1;
1181  }
1182  }
1183 
1184 // If it's auto d through all possibilities. It's either auto with xrdtls
1185 // configured or manual which needs at least a cert specification. For auto
1186 // configuration we will only issue a warning if overrides were specified.
1187 //
1188  if (httpsmode == hsmAuto && xrdctx)
1189  {const XrdTlsContext::CTX_Params *cP = xrdctx->GetParams();
1190  const char *what1 = 0, *what2 = 0, *what3 = 0;
1191 
1192  if (!sslcert && cP->cert.size())
1193  {sslcert = strdup(cP->cert.c_str());
1194  if (cP->pkey.size()) sslkey = strdup(cP->pkey.c_str());
1195  what1 = "xrd.tls to supply 'cert' and 'key'.";
1196  }
1197  if (!sslcadir && cP->cadir.size())
1198  {sslcadir = strdup(cP->cadir.c_str());
1199  what2 = "xrd.tlsca to supply 'cadir'.";
1200  }
1201  if (!sslcafile && cP->cafile.size())
1202  {sslcafile = strdup(cP->cafile.c_str());
1203  what2 = (what2 ? "xrd.tlsca to supply 'cadir' and 'cafile'."
1204  : "xrd.tlsca to supply 'cafile'.");
1205  }
1207  crlRefIntervalSec = cP->crlRT;
1208  what3 = "xrd.tlsca to supply 'refresh' interval.";
1209  }
1210  if (!httpsspec && what1) eDest.Say("Config Using ", what1);
1211  if (!httpsspec && what2) eDest.Say("Config Using ", what2);
1212  if (!httpsspec && what3) eDest.Say("Config Using ", what3);
1213  }
1214 
1215 // If a gridmap or secxtractor is present then we must be able to verify certs
1216 //
1217  if (!(sslcadir || sslcafile))
1218  {const char *what = Configed();
1219  const char *why = (httpsspec ? "a cadir or cafile was not specified!"
1220  : "'xrd.tlsca noverify' was specified!");
1221  if (what)
1222  {eDest.Say("Config failure: ", what, " cert verification but ", why);
1223  return 1;
1224  }
1225  }
1226  httpsmode = hsmOn;
1227 
1228 // Oddly we need to create an error bio at this point
1229 //
1230  sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1231 
1232 // Now we can configure HTTPS. We will not reuse the passed context as we will
1233 // be setting our own options specific to out implementation. One day we will.
1234 //
1235  const char *how = "completed.";
1236  eDest.Say("++++++ HTTPS initialization started.");
1237  if (!InitTLS()) {NoGo = 1; how = "failed.";}
1238  eDest.Say("------ HTTPS initialization ", how);
1239  if (NoGo) return NoGo;
1240 
1241 // We can now load all the external handlers
1242 //
1243  if (LoadExtHandler(extHIVec, ConfigFN, *myEnv)) return 1;
1244 
1245 // At this point, we can actually initialize security plugins
1246 //
1247  return (InitSecurity() ? NoGo : 1);
1248 }
1249 
1250 /******************************************************************************/
1251 /* C o n f i g e d */
1252 /******************************************************************************/
1253 
1254 const char *XrdHttpProtocol::Configed()
1255 {
1256  if (secxtractor && gridmap) return "gridmap and secxtractor require";
1257  if (secxtractor) return "secxtractor requires";
1258  if (gridmap) return "gridmap requires";
1259  return 0;
1260 }
1261 
1262 /******************************************************************************/
1263 /* B u f f g e t L i n e */
1264 /******************************************************************************/
1265 
1267 
1268 int XrdHttpProtocol::BuffgetLine(XrdOucString &dest) {
1269 
1270  dest = "";
1271  char save;
1272 
1273  // Easy case
1274  if (myBuffEnd >= myBuffStart) {
1275  int l = 0;
1276  for (char *p = myBuffStart; p < myBuffEnd; p++) {
1277  l++;
1278  if (*p == '\n') {
1279  save = *(p+1);
1280  *(p+1) = '\0';
1281  dest.assign(myBuffStart, 0, l-1);
1282  *(p+1) = save;
1283 
1284  //strncpy(dest, myBuffStart, l);
1285  //dest[l] = '\0';
1286  BuffConsume(l);
1287 
1288  //if (dest[l-1] == '\n') dest[l - 1] = '\0';
1289  return l;
1290  }
1291 
1292  }
1293 
1294  return 0;
1295  } else {
1296  // More complex case... we have to do it in two segments
1297 
1298  // Segment 1: myBuffStart->myBuff->buff+myBuff->bsize
1299  int l = 0;
1300  for (char *p = myBuffStart; p < myBuff->buff + myBuff->bsize; p++) {
1301  l++;
1302  if ((*p == '\n') || (*p == '\0')) {
1303  save = *(p+1);
1304  *(p+1) = '\0';
1305  dest.assign(myBuffStart, 0, l-1);
1306  *(p+1) = save;
1307 
1308  //strncpy(dest, myBuffStart, l);
1309 
1310  BuffConsume(l);
1311 
1312  //if (dest[l-1] == '\n') dest[l - 1] = '\0';
1313  return l;
1314  }
1315 
1316  }
1317 
1318  // We did not find the \n, let's keep on searching in the 2nd segment
1319  // Segment 2: myBuff->buff --> myBuffEnd
1320  l = 0;
1321  for (char *p = myBuff->buff; p < myBuffEnd; p++) {
1322  l++;
1323  if ((*p == '\n') || (*p == '\0')) {
1324  save = *(p+1);
1325  *(p+1) = '\0';
1326  // Remember the 1st segment
1327  int l1 = myBuff->buff + myBuff->bsize - myBuffStart;
1328 
1329  dest.assign(myBuffStart, 0, l1-1);
1330  //strncpy(dest, myBuffStart, l1);
1331  BuffConsume(l1);
1332 
1333  dest.insert(myBuffStart, l1, l-1);
1334  //strncpy(dest + l1, myBuffStart, l);
1335  //dest[l + l1] = '\0';
1336  BuffConsume(l);
1337 
1338  *(p+1) = save;
1339 
1340  //if (dest[l + l1 - 1] == '\n') dest[l + l1 - 1] = '\0';
1341  return l + l1;
1342  }
1343 
1344  }
1345 
1346 
1347 
1348  }
1349 
1350  return 0;
1351 }
1352 
1353 /******************************************************************************/
1354 /* g e t D a t a O n e S h o t */
1355 /******************************************************************************/
1356 
1357 int XrdHttpProtocol::getDataOneShot(int blen, bool wait) {
1358  int rlen, maxread;
1359 
1360  // Get up to blen bytes from the connection. Put them into mybuff.
1361  // This primitive, for the way it is used, is not supposed to block if wait=false
1362 
1363  // Returns:
1364  // 2: no space left in buffer
1365  // 1: timeout
1366  // -1: error
1367  // 0: everything read correctly
1368 
1369 
1370 
1371  // Check for buffer overflow first
1372  maxread = std::min(blen, BuffAvailable());
1373  TRACE(DEBUG, "getDataOneShot BuffAvailable: " << BuffAvailable() << " maxread: " << maxread);
1374 
1375  if (!maxread)
1376  return 2;
1377 
1378  if (ishttps) {
1379  int sslavail = maxread;
1380 
1381  if (!wait) {
1382  int l = SSL_pending(ssl);
1383  if (l > 0)
1384  sslavail = std::min(maxread, SSL_pending(ssl));
1385  }
1386 
1387  if (sslavail < 0) {
1388  Link->setEtext("link SSL_pending error");
1389  ERR_print_errors(sslbio_err);
1390  return -1;
1391  }
1392 
1393  TRACE(DEBUG, "getDataOneShot sslavail: " << sslavail);
1394  if (sslavail <= 0) return 0;
1395 
1396  if (myBuffEnd - myBuff->buff >= myBuff->bsize) {
1397  TRACE(DEBUG, "getDataOneShot Buffer panic");
1398  myBuffEnd = myBuff->buff;
1399  }
1400 
1401  rlen = SSL_read(ssl, myBuffEnd, sslavail);
1402  if (rlen <= 0) {
1403  Link->setEtext("link SSL read error");
1404  ERR_print_errors(sslbio_err);
1405  return -1;
1406  }
1407 
1408 
1409  } else {
1410 
1411  if (myBuffEnd - myBuff->buff >= myBuff->bsize) {
1412  TRACE(DEBUG, "getDataOneShot Buffer panic");
1413  myBuffEnd = myBuff->buff;
1414  }
1415 
1416  if (wait)
1417  rlen = Link->Recv(myBuffEnd, maxread, readWait);
1418  else
1419  rlen = Link->Recv(myBuffEnd, maxread);
1420 
1421 
1422  if (rlen == 0) {
1423  Link->setEtext("link read error or closed");
1424  return -1;
1425  }
1426 
1427  if (rlen < 0) {
1428  Link->setEtext("link timeout or other error");
1429  return -1;
1430  }
1431  }
1432 
1433  myBuffEnd += rlen;
1434 
1435  TRACE(REQ, "read " << rlen << " of " << blen << " bytes");
1436 
1437  return 0;
1438 }
1439 
1441 
1442 int XrdHttpProtocol::BuffAvailable() {
1443  int r;
1444 
1445  if (myBuffEnd >= myBuffStart)
1446  r = myBuff->buff + myBuff->bsize - myBuffEnd;
1447  else
1448  r = myBuffStart - myBuffEnd;
1449 
1450  if ((r < 0) || (r > myBuff->bsize)) {
1451  TRACE(REQ, "internal error, myBuffAvailable: " << r << " myBuff->bsize " << myBuff->bsize);
1452  abort();
1453  }
1454 
1455  return r;
1456 }
1457 
1458 /******************************************************************************/
1459 /* B u f f U s e d */
1460 /******************************************************************************/
1461 
1463 
1464 int XrdHttpProtocol::BuffUsed() {
1465  int r;
1466 
1467  if (myBuffEnd >= myBuffStart)
1468  r = myBuffEnd - myBuffStart;
1469  else
1470 
1471  r = myBuff->bsize - (myBuffStart - myBuffEnd);
1472 
1473  if ((r < 0) || (r > myBuff->bsize)) {
1474  TRACE(REQ, "internal error, myBuffUsed: " << r << " myBuff->bsize " << myBuff->bsize);
1475  abort();
1476  }
1477 
1478  return r;
1479 }
1480 
1481 /******************************************************************************/
1482 /* B u f f F r e e */
1483 /******************************************************************************/
1484 
1486 
1487 int XrdHttpProtocol::BuffFree() {
1488  return (myBuff->bsize - BuffUsed());
1489 }
1490 
1491 /******************************************************************************/
1492 /* B u f f C o n s u m e */
1493 /******************************************************************************/
1494 
1495 void XrdHttpProtocol::BuffConsume(int blen) {
1496 
1497  if (blen > myBuff->bsize) {
1498  TRACE(REQ, "internal error, BuffConsume(" << blen << ") smaller than buffsize");
1499  abort();
1500  }
1501 
1502  if (blen > BuffUsed()) {
1503  TRACE(REQ, "internal error, BuffConsume(" << blen << ") larger than BuffUsed:" << BuffUsed());
1504  abort();
1505  }
1506 
1507  myBuffStart = myBuffStart + blen;
1508 
1509  if (myBuffStart >= myBuff->buff + myBuff->bsize)
1510  myBuffStart -= myBuff->bsize;
1511 
1512  if (myBuffEnd >= myBuff->buff + myBuff->bsize)
1513  myBuffEnd -= myBuff->bsize;
1514 
1515  if (BuffUsed() == 0)
1516  myBuffStart = myBuffEnd = myBuff->buff;
1517 }
1518 
1519 /******************************************************************************/
1520 /* B u f f g e t D a t a */
1521 /******************************************************************************/
1522 
1531 int XrdHttpProtocol::BuffgetData(int blen, char **data, bool wait) {
1532  int rlen;
1533 
1534  TRACE(DEBUG, "BuffgetData: requested " << blen << " bytes");
1535 
1536 
1537  if (wait) {
1538  // If there's not enough data in the buffer then wait on the socket until it comes
1539  if (blen > BuffUsed()) {
1540  TRACE(REQ, "BuffgetData: need to read " << blen - BuffUsed() << " bytes");
1541  if ( getDataOneShot(blen - BuffUsed(), true) )
1542  // The wanted data could not be read. Either timeout of connection closed
1543  return 0;
1544  }
1545  } else {
1546  // Get a peek at the socket, without waiting, if we have no data in the buffer
1547  if ( !BuffUsed() ) {
1548  if ( getDataOneShot(blen, false) )
1549  // The wanted data could not be read. Either timeout of connection closed
1550  return -1;
1551  }
1552  }
1553 
1554  // And now make available the data taken from the buffer. Note that the buffer
1555  // may be empty...
1556  if (myBuffStart <= myBuffEnd) {
1557  rlen = std::min( (long) blen, (long)(myBuffEnd - myBuffStart) );
1558 
1559  } else
1560  rlen = std::min( (long) blen, (long)(myBuff->buff + myBuff->bsize - myBuffStart) );
1561 
1562  *data = myBuffStart;
1563  BuffConsume(rlen);
1564  return rlen;
1565 }
1566 
1567 /******************************************************************************/
1568 /* S e n d D a t a */
1569 /******************************************************************************/
1570 
1572 
1573 int XrdHttpProtocol::SendData(const char *body, int bodylen) {
1574 
1575  int r;
1576 
1577  if (body && bodylen) {
1578  TRACE(REQ, "Sending " << bodylen << " bytes");
1579  if (ishttps) {
1580  r = SSL_write(ssl, body, bodylen);
1581  if (r <= 0) {
1582  ERR_print_errors(sslbio_err);
1583  return -1;
1584  }
1585 
1586  } else {
1587  r = Link->Send(body, bodylen);
1588  if (r <= 0) return -1;
1589  }
1590  }
1591 
1592  return 0;
1593 }
1594 
1595 /******************************************************************************/
1596 /* S t a r t S i m p l e R e s p */
1597 /******************************************************************************/
1598 
1599 int XrdHttpProtocol::StartSimpleResp(int code, const char *desc, const char *header_to_add, long long bodylen, bool keepalive) {
1600  std::stringstream ss;
1601  const std::string crlf = "\r\n";
1602 
1603  ss << "HTTP/1.1 " << code << " ";
1604  if (desc) {
1605  ss << desc;
1606  } else {
1607  if (code == 200) ss << "OK";
1608  else if (code == 100) ss << "Continue";
1609  else if (code == 206) ss << "Partial Content";
1610  else if (code == 302) ss << "Redirect";
1611  else if (code == 307) ss << "Temporary Redirect";
1612  else if (code == 400) ss << "Bad Request";
1613  else if (code == 403) ss << "Forbidden";
1614  else if (code == 404) ss << "Not Found";
1615  else if (code == 405) ss << "Method Not Allowed";
1616  else if (code == 416) ss << "Range Not Satisfiable";
1617  else if (code == 500) ss << "Internal Server Error";
1618  else if (code == 502) ss << "Bad Gateway";
1619  else if (code == 504) ss << "Gateway Timeout";
1620  else ss << "Unknown";
1621  }
1622  ss << crlf;
1623  if (keepalive && (code != 100))
1624  ss << "Connection: Keep-Alive" << crlf;
1625  else
1626  ss << "Connection: Close" << crlf;
1627 
1628  ss << "Server: XrootD/" << XrdVSTRING << crlf;
1629 
1630  const auto iter = m_staticheaders.find(CurrentReq.requestverb);
1631  if (iter != m_staticheaders.end()) {
1632  ss << iter->second;
1633  } else {
1634  ss << m_staticheaders[""];
1635  }
1636 
1637  if ((bodylen >= 0) && (code != 100))
1638  ss << "Content-Length: " << bodylen << crlf;
1639 
1640  if (header_to_add && (header_to_add[0] != '\0'))
1641  ss << header_to_add << crlf;
1642 
1643  ss << crlf;
1644 
1645  const std::string &outhdr = ss.str();
1646  TRACEI(RSP, "Sending resp: " << code << " header len:" << outhdr.size());
1647  if (SendData(outhdr.c_str(), outhdr.size()))
1648  return -1;
1649 
1650  return 0;
1651 }
1652 
1653 /******************************************************************************/
1654 /* S t a r t C h u n k e d R e s p */
1655 /******************************************************************************/
1656 
1657 int XrdHttpProtocol::StartChunkedResp(int code, const char *desc, const char *header_to_add, long long bodylen, bool keepalive) {
1658  const std::string crlf = "\r\n";
1659  std::stringstream ss;
1660 
1661  if (header_to_add && (header_to_add[0] != '\0')) {
1662  ss << header_to_add << crlf;
1663  }
1664 
1665  ss << "Transfer-Encoding: chunked";
1666  TRACEI(RSP, "Starting chunked response");
1667  return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1668 }
1669 
1670 /******************************************************************************/
1671 /* C h u n k R e s p */
1672 /******************************************************************************/
1673 
1674 int XrdHttpProtocol::ChunkResp(const char *body, long long bodylen) {
1675  long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1676  if (ChunkRespHeader(content_length))
1677  return -1;
1678 
1679  if (body && SendData(body, content_length))
1680  return -1;
1681 
1682  return ChunkRespFooter();
1683 }
1684 
1685 /******************************************************************************/
1686 /* C h u n k R e s p H e a d e r */
1687 /******************************************************************************/
1688 
1689 int XrdHttpProtocol::ChunkRespHeader(long long bodylen) {
1690  const std::string crlf = "\r\n";
1691  std::stringstream ss;
1692 
1693  ss << std::hex << bodylen << std::dec << crlf;
1694 
1695  const std::string &chunkhdr = ss.str();
1696  TRACEI(RSP, "Sending encoded chunk of size " << bodylen);
1697  return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1698 }
1699 
1700 /******************************************************************************/
1701 /* C h u n k R e s p F o o t e r */
1702 /******************************************************************************/
1703 
1704 int XrdHttpProtocol::ChunkRespFooter() {
1705  const std::string crlf = "\r\n";
1706  return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1707 }
1708 
1709 /******************************************************************************/
1710 /* S e n d S i m p l e R e s p */
1711 /******************************************************************************/
1712 
1716 
1717 int XrdHttpProtocol::SendSimpleResp(int code, const char *desc, const char *header_to_add, const char *body, long long bodylen, bool keepalive) {
1718 
1719  long long content_length = bodylen;
1720  if (bodylen <= 0) {
1721  content_length = body ? strlen(body) : 0;
1722  }
1723 
1724  if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1725  return -1;
1726 
1727  //
1728  // Send the data
1729  //
1730  if (body)
1731  return SendData(body, content_length);
1732 
1733  return 0;
1734 }
1735 
1736 /******************************************************************************/
1737 /* C o n f i g u r e */
1738 /******************************************************************************/
1739 
1741  /*
1742  Function: Establish configuration at load time.
1743 
1744  Input: None.
1745 
1746  Output: 0 upon success or !0 otherwise.
1747  */
1748 
1749  char *rdf;
1750 
1751  // Copy out the special info we want to use at top level
1752  //
1753  eDest.logger(pi->eDest->logger());
1755  // SI = new XrdXrootdStats(pi->Stats);
1756  Sched = pi->Sched;
1757  BPool = pi->BPool;
1758  xrd_cslist = getenv("XRD_CSLIST");
1759 
1760  Port = pi->Port;
1761 
1762  // Copy out the current TLS context
1763  //
1764  xrdctx = pi->tlsCtx;
1765 
1766  {
1767  char buf[16];
1768  sprintf(buf, "%d", Port);
1769  Port_str = strdup(buf);
1770  }
1771 
1772  // Now process and configuration parameters
1773  //
1774  rdf = (parms && *parms ? parms : pi->ConfigFN);
1775  if (rdf && Config(rdf, pi->theEnv)) return 0;
1776  if (pi->DebugON) XrdHttpTrace.What = TRACE_ALL;
1777 
1778  // Set the redirect flag if we are a pure redirector
1779  myRole = kXR_isServer;
1780  if ((rdf = getenv("XRDROLE"))) {
1781  eDest.Emsg("Config", "XRDROLE: ", rdf);
1782 
1783  if (!strcasecmp(rdf, "manager") || !strcasecmp(rdf, "supervisor")) {
1785  eDest.Emsg("Config", "Configured as HTTP(s) redirector.");
1786  } else {
1787 
1788  eDest.Emsg("Config", "Configured as HTTP(s) data server.");
1789  }
1790 
1791  } else {
1792  eDest.Emsg("Config", "No XRDROLE specified.");
1793  }
1794 
1795  // Schedule protocol object cleanup
1796  //
1798  (XrdHttpTrace.What & TRACE_MEM ? TRACE_MEM : 0));
1799  ProtStack.Set((pi->ConnMax / 3 ? pi->ConnMax / 3 : 30), 60 * 60);
1800 
1801  // Return success
1802  //
1803 
1804  return 1;
1805 }
1806 
1807 /******************************************************************************/
1808 /* p a r s e H e a d e r 2 C G I */
1809 /******************************************************************************/
1810 int XrdHttpProtocol::parseHeader2CGI(XrdOucStream &Config, XrdSysError & err,std::map<std::string, std::string> &header2cgi) {
1811  char *val, keybuf[1024], parmbuf[1024];
1812  char *parm;
1813 
1814  // Get the header key
1815  val = Config.GetWord();
1816  if (!val || !val[0]) {
1817  err.Emsg("Config", "No headerkey specified.");
1818  return 1;
1819  } else {
1820 
1821  // Trim the beginning, in place
1822  while ( *val && !isalnum(*val) ) val++;
1823  strcpy(keybuf, val);
1824 
1825  // Trim the end, in place
1826  char *pp;
1827  pp = keybuf + strlen(keybuf) - 1;
1828  while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1829  *pp = '\0';
1830  pp--;
1831  }
1832 
1833  parm = Config.GetWord();
1834 
1835  // Avoids segfault in case a key is given without value
1836  if(!parm || !parm[0]) {
1837  err.Emsg("Config", "No header2cgi value specified. key: '", keybuf, "'");
1838  return 1;
1839  }
1840 
1841  // Trim the beginning, in place
1842  while ( *parm && !isalnum(*parm) ) parm++;
1843  strcpy(parmbuf, parm);
1844 
1845  // Trim the end, in place
1846  pp = parmbuf + strlen(parmbuf) - 1;
1847  while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1848  *pp = '\0';
1849  pp--;
1850  }
1851 
1852  // Add this mapping to the map that will be used
1853  try {
1854  header2cgi[keybuf] = parmbuf;
1855  } catch ( ... ) {
1856  err.Emsg("Config", "Can't insert new header2cgi rule. key: '", keybuf, "'");
1857  return 1;
1858  }
1859 
1860  }
1861  return 0;
1862 }
1863 
1864 
1865 /******************************************************************************/
1866 /* I n i t T L S */
1867 /******************************************************************************/
1868 
1869 bool XrdHttpProtocol::InitTLS() {
1870 
1871  std::string eMsg;
1874 
1875 // Create a new TLS context
1876 //
1877  if (sslverifydepth > 255) sslverifydepth = 255;
1879  //TLS_SET_REFINT will set the refresh interval in minutes, hence the division by 60
1882 
1883 // Make sure the context was created
1884 //
1885  if (!xrdctx->isOK())
1886  {eDest.Say("Config failure: ", eMsg.c_str());
1887  return false;
1888  }
1889 
1890 // Setup session cache (this is controversial). The default is off but many
1891 // programs expect it being enabled and break when it is disabled. In such
1892 // cases it should be enabled. This is, of course, a big OpenSSL mess.
1893 //
1894  static const char *sess_ctx_id = "XrdHTTPSessionCtx";
1895  unsigned int n =(unsigned int)(strlen(sess_ctx_id)+1);
1896  xrdctx->SessionCache(tlsCache, sess_ctx_id, n);
1897 
1898 // Set special ciphers if so specified.
1899 //
1901  {eDest.Say("Config failure: ", "Unable to set allowable https ciphers!");
1902  return false;
1903  }
1904 
1905 // All done
1906 //
1907  return true;
1908 }
1909 
1910 /******************************************************************************/
1911 /* C l e a n u p */
1912 /******************************************************************************/
1913 
1914 void XrdHttpProtocol::Cleanup() {
1915 
1916  TRACE(ALL, " Cleanup");
1917 
1918  if (BPool && myBuff) {
1919  BuffConsume(BuffUsed());
1920  BPool->Release(myBuff);
1921  myBuff = 0;
1922  }
1923 
1924  if (ssl) {
1925  // Shutdown the SSL/TLS connection
1926  // https://www.openssl.org/docs/man1.0.2/man3/SSL_shutdown.html
1927  // We don't need a bidirectional shutdown as
1928  // when we are here, the connection will not be re-used.
1929  // In the case SSL_shutdown returns 0,
1930  // "the output of SSL_get_error(3) may be misleading, as an erroneous SSL_ERROR_SYSCALL may be flagged even though no error occurred."
1931  // we will then just flush the thread's queue.
1932  // In the case an error really happened, we print the error that happened
1933  int ret = SSL_shutdown(ssl);
1934  if (ret != 1) {
1935  if(ret == 0) {
1936  // Clean this thread's error queue for the old openssl versions
1937  #if OPENSSL_VERSION_NUMBER < 0x10100000L
1938  ERR_remove_thread_state(nullptr);
1939  #endif
1940  } else {
1941  //ret < 0, an error really happened.
1942  TRACE(ALL, " SSL_shutdown failed");
1943  ERR_print_errors(sslbio_err);
1944  }
1945  }
1946 
1947  if (secxtractor)
1948  secxtractor->FreeSSL(ssl);
1949 
1950  SSL_free(ssl);
1951 
1952  }
1953 
1954 
1955  ssl = 0;
1956  sbio = 0;
1957 
1958  if (SecEntity.caps) free(SecEntity.caps);
1959  if (SecEntity.grps) free(SecEntity.grps);
1961  if (SecEntity.vorg) free(SecEntity.vorg);
1962  if (SecEntity.role) free(SecEntity.role);
1963  if (SecEntity.name) free(SecEntity.name);
1964  if (SecEntity.host) free(SecEntity.host);
1965  if (SecEntity.moninfo) free(SecEntity.moninfo);
1966 
1967  SecEntity.Reset();
1968 
1969  if (Addr_str) free(Addr_str);
1970  Addr_str = 0;
1971 }
1972 
1973 /******************************************************************************/
1974 /* R e s e t */
1975 /******************************************************************************/
1976 
1977 void XrdHttpProtocol::Reset() {
1978 
1979  TRACE(ALL, " Reset");
1980  Link = 0;
1981  CurrentReq.reset();
1982  CurrentReq.reqstate = 0;
1983 
1984  if (myBuff) {
1985  BPool->Release(myBuff);
1986  myBuff = 0;
1987  }
1988  myBuffStart = myBuffEnd = 0;
1989 
1990  DoingLogin = false;
1991  DoneSetInfo = false;
1992 
1993  ResumeBytes = 0;
1994  Resume = 0;
1995 
1996  //
1997  // numReads = 0;
1998  // numReadP = 0;
1999  // numReadV = 0;
2000  // numSegsV = 0;
2001  // numWrites = 0;
2002  // numFiles = 0;
2003  // cumReads = 0;
2004  // cumReadV = 0;
2005  // cumSegsV = 0;
2006  // cumWrites = 0;
2007  // totReadP = 0;
2008 
2009  SecEntity.Reset();
2011  ishttps = false;
2012  ssldone = false;
2013 
2014  Bridge = 0;
2015  ssl = 0;
2016  sbio = 0;
2017 
2018 }
2019 
2020 /******************************************************************************/
2021 /* x h t t p s m o d e */
2022 /******************************************************************************/
2023 
2024 /* Function: xhttpsmode
2025 
2026  Purpose: To parse the directive: httpsmode {auto | disable | manual}
2027 
2028  auto configure https if configured in xrd framework.
2029  disable do not configure https no matter what
2030  manual configure https and ignore the xrd framework
2031 
2032  Output: 0 upon success or !0 upon failure.
2033  */
2034 
2035 int XrdHttpProtocol::xhttpsmode(XrdOucStream & Config) {
2036  char *val;
2037 
2038  // Get the val
2039  //
2040  val = Config.GetWord();
2041  if (!val || !val[0]) {
2042  eDest.Emsg("Config", "httpsmode parameter not specified");
2043  return 1;
2044  }
2045 
2046  // Record the val
2047  //
2048  if (!strcmp(val, "auto")) httpsmode = hsmAuto;
2049  else if (!strcmp(val, "disable")) httpsmode = hsmOff;
2050  else if (!strcmp(val, "manual")) httpsmode = hsmMan;
2051  else {eDest.Emsg("Config", "invalid httpsmode parameter - ", val);
2052  return 1;
2053  }
2054  return 0;
2055 }
2056 
2057 /******************************************************************************/
2058 /* x s s l v e r i f y d e p t h */
2059 /******************************************************************************/
2060 
2061 /* Function: xsslverifydepth
2062 
2063  Purpose: To parse the directive: sslverifydepth <depth>
2064 
2065  <depth> the max depth of the ssl cert verification
2066 
2067  Output: 0 upon success or !0 upon failure.
2068  */
2069 
2070 int XrdHttpProtocol::xsslverifydepth(XrdOucStream & Config) {
2071  char *val;
2072 
2073  // Get the val
2074  //
2075  val = Config.GetWord();
2076  if (!val || !val[0]) {
2077  eDest.Emsg("Config", "sslverifydepth value not specified");
2078  return 1;
2079  }
2080 
2081  // Record the val
2082  //
2083  sslverifydepth = atoi(val);
2084 
2085  if (xrdctxVer){ HTTPS_ALERT("verifydepth","tlsca",false); }
2086  return 0;
2087 }
2088 
2089 /******************************************************************************/
2090 /* x s s l c e r t */
2091 /******************************************************************************/
2092 
2093 /* Function: xsslcert
2094 
2095  Purpose: To parse the directive: sslcert <path>
2096 
2097  <path> the path of the server certificate to be used.
2098 
2099  Output: 0 upon success or !0 upon failure.
2100  */
2101 
2102 int XrdHttpProtocol::xsslcert(XrdOucStream & Config) {
2103  char *val;
2104 
2105  // Get the path
2106  //
2107  val = Config.GetWord();
2108  if (!val || !val[0]) {
2109  eDest.Emsg("Config", "HTTP X509 certificate not specified");
2110  return 1;
2111  }
2112 
2113  // Record the path
2114  //
2115  if (sslcert) free(sslcert);
2116  sslcert = strdup(val);
2117 
2118  // If we have an xrd context issue reminder
2119  //
2120  HTTPS_ALERT("cert","tls",true);
2121  return 0;
2122 }
2123 
2124 /******************************************************************************/
2125 /* x s s l k e y */
2126 /******************************************************************************/
2127 
2128 /* Function: xsslkey
2129 
2130  Purpose: To parse the directive: sslkey <path>
2131 
2132  <path> the path of the server key to be used.
2133 
2134  Output: 0 upon success or !0 upon failure.
2135  */
2136 
2137 int XrdHttpProtocol::xsslkey(XrdOucStream & Config) {
2138  char *val;
2139 
2140  // Get the path
2141  //
2142  val = Config.GetWord();
2143  if (!val || !val[0]) {
2144  eDest.Emsg("Config", "HTTP X509 key not specified");
2145  return 1;
2146  }
2147 
2148  // Record the path
2149  //
2150  if (sslkey) free(sslkey);
2151  sslkey = strdup(val);
2152 
2153  HTTPS_ALERT("key","tls",true);
2154  return 0;
2155 }
2156 
2157 /******************************************************************************/
2158 /* x g m a p */
2159 /******************************************************************************/
2160 
2161 /* Function: xgmap
2162 
2163  Purpose: To parse the directive: gridmap [required] [compatNameGeneration] <path>
2164 
2165  required optional parameter which if present treats any grimap errors
2166  as fatal.
2167  <path> the path of the gridmap file to be used. Normally it's
2168  /etc/grid-security/gridmap. No mapfile means no translation
2169  required. Pointing to a non existing mapfile is an error.
2170 
2171  Output: 0 upon success or !0 upon failure.
2172  */
2173 
2174 int XrdHttpProtocol::xgmap(XrdOucStream & Config) {
2175  char *val;
2176 
2177  // Get the path
2178  //
2179  val = Config.GetWord();
2180  if (!val || !val[0]) {
2181  eDest.Emsg("Config", "HTTP X509 gridmap file location not specified");
2182  return 1;
2183  }
2184 
2185  // Handle optional parameter "required"
2186  //
2187  if (!strncmp(val, "required", 8)) {
2188  isRequiredGridmap = true;
2189  val = Config.GetWord();
2190 
2191  if (!val || !val[0]) {
2192  eDest.Emsg("Config", "HTTP X509 gridmap file missing after [required] "
2193  "parameter");
2194  return 1;
2195  }
2196  }
2197 
2198  // Handle optional parameter "compatNameGeneration"
2199  //
2200  if (!strcmp(val, "compatNameGeneration")) {
2201  compatNameGeneration = true;
2202  val = Config.GetWord();
2203  if (!val || !val[0]) {
2204  eDest.Emsg("Config", "HTTP X509 gridmap file missing after "
2205  "[compatNameGeneration] parameter");
2206  return 1;
2207  }
2208  }
2209 
2210 
2211  // Record the path
2212  //
2213  if (gridmap) free(gridmap);
2214  gridmap = strdup(val);
2215  return 0;
2216 }
2217 
2218 /******************************************************************************/
2219 /* x s s l c a f i l e */
2220 /******************************************************************************/
2221 
2222 /* Function: xsslcafile
2223 
2224  Purpose: To parse the directive: sslcafile <path>
2225 
2226  <path> the path of the server key to be used.
2227 
2228  Output: 0 upon success or !0 upon failure.
2229  */
2230 
2231 int XrdHttpProtocol::xsslcafile(XrdOucStream & Config) {
2232  char *val;
2233 
2234  // Get the path
2235  //
2236  val = Config.GetWord();
2237  if (!val || !val[0]) {
2238  eDest.Emsg("Config", "HTTP X509 CAfile not specified");
2239  return 1;
2240  }
2241 
2242  // Record the path
2243  //
2244  if (sslcafile) free(sslcafile);
2245  sslcafile = strdup(val);
2246 
2247  if (xrdctxVer){ HTTPS_ALERT("cafile","tlsca",false); }
2248  return 0;
2249 }
2250 
2251 /******************************************************************************/
2252 /* x s e c r e t k e y */
2253 /******************************************************************************/
2254 
2255 /* Function: xsecretkey
2256 
2257  Purpose: To parse the directive: xsecretkey <key>
2258 
2259  <key> the key to be used
2260 
2261  Output: 0 upon success or !0 upon failure.
2262  */
2263 
2264 int XrdHttpProtocol::xsecretkey(XrdOucStream & Config) {
2265  char *val;
2266  bool inFile = false;
2267 
2268  // Get the path
2269  //
2270  val = Config.GetWord();
2271  if (!val || !val[0]) {
2272  eDest.Emsg("Config", "Shared secret key not specified");
2273  return 1;
2274  }
2275 
2276 
2277  // If the token starts with a slash, then we interpret it as
2278  // the path to a file that contains the secretkey
2279  // otherwise, the token itself is the secretkey
2280  if (val[0] == '/') {
2281  struct stat st;
2282  inFile = true;
2283  int fd = open(val, O_RDONLY);
2284 
2285  if ( fd == -1 ) {
2286  eDest.Emsg("Config", errno, "open shared secret key file", val);
2287  return 1;
2288  }
2289 
2290  if ( fstat(fd, &st) != 0 ) {
2291  eDest.Emsg("Config", errno, "fstat shared secret key file", val);
2292  close(fd);
2293  return 1;
2294  }
2295 
2296  if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2297  eDest.Emsg("Config",
2298  "For your own security, the shared secret key file cannot be world readable or group writable '", val, "'");
2299  close(fd);
2300  return 1;
2301  }
2302 
2303  FILE *fp = fdopen(fd, "r");
2304 
2305  if ( fp == nullptr ) {
2306  eDest.Emsg("Config", errno, "fdopen shared secret key file", val);
2307  close(fd);
2308  return 1;
2309  }
2310 
2311  char line[1024];
2312  while( fgets(line, 1024, fp) ) {
2313  char *pp;
2314 
2315  // Trim the end
2316  pp = line + strlen(line) - 1;
2317  while ( (pp >= line) && (!isalnum(*pp)) ) {
2318  *pp = '\0';
2319  pp--;
2320  }
2321 
2322  // Trim the beginning
2323  pp = line;
2324  while ( *pp && !isalnum(*pp) ) pp++;
2325 
2326  if ( strlen(pp) >= 32 ) {
2327  eDest.Say("Config", "Secret key loaded.");
2328  // Record the path
2329  if (secretkey) free(secretkey);
2330  secretkey = strdup(pp);
2331 
2332  fclose(fp);
2333  return 0;
2334  }
2335 
2336  }
2337 
2338  fclose(fp);
2339  eDest.Emsg("Config", "Cannot find useful secretkey in file '", val, "'");
2340  return 1;
2341 
2342  }
2343 
2344  if ( strlen(val) < 32 ) {
2345  eDest.Emsg("Config", "Secret key is too short");
2346  return 1;
2347  }
2348 
2349  // Record the path
2350  if (secretkey) free(secretkey);
2351  secretkey = strdup(val);
2352  if (!inFile) Config.noEcho();
2353 
2354  return 0;
2355 }
2356 
2357 /******************************************************************************/
2358 /* x l i s t d e n y */
2359 /******************************************************************************/
2360 
2361 /* Function: xlistdeny
2362 
2363  Purpose: To parse the directive: listingdeny <yes|no|0|1>
2364 
2365  <val> makes this redirector deny listings with an error
2366 
2367  Output: 0 upon success or !0 upon failure.
2368  */
2369 
2370 int XrdHttpProtocol::xlistdeny(XrdOucStream & Config) {
2371  char *val;
2372 
2373  // Get the path
2374  //
2375  val = Config.GetWord();
2376  if (!val || !val[0]) {
2377  eDest.Emsg("Config", "listingdeny flag not specified");
2378  return 1;
2379  }
2380 
2381  // Record the value
2382  //
2383  listdeny = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2384 
2385 
2386  return 0;
2387 }
2388 
2389 /******************************************************************************/
2390 /* x l i s t r e d i r */
2391 /******************************************************************************/
2392 
2393 /* Function: xlistredir
2394 
2395  Purpose: To parse the directive: listingredir <Url>
2396 
2397  <Url> http/https server to redirect to in the case of listing
2398 
2399  Output: 0 upon success or !0 upon failure.
2400  */
2401 
2402 int XrdHttpProtocol::xlistredir(XrdOucStream & Config) {
2403  char *val;
2404 
2405  // Get the path
2406  //
2407  val = Config.GetWord();
2408  if (!val || !val[0]) {
2409  eDest.Emsg("Config", "listingredir flag not specified");
2410  return 1;
2411  }
2412 
2413  // Record the value
2414  //
2415  if (listredir) free(listredir);
2416  listredir = strdup(val);
2417 
2418 
2419  return 0;
2420 }
2421 
2422 /******************************************************************************/
2423 /* x s s l d e s t h t t p s */
2424 /******************************************************************************/
2425 
2426 /* Function: xdesthttps
2427 
2428  Purpose: To parse the directive: desthttps <yes|no|0|1>
2429 
2430  <val> makes this redirector produce http or https redirection targets
2431 
2432  Output: 0 upon success or !0 upon failure.
2433  */
2434 
2435 int XrdHttpProtocol::xdesthttps(XrdOucStream & Config) {
2436  char *val;
2437 
2438  // Get the path
2439  //
2440  val = Config.GetWord();
2441  if (!val || !val[0]) {
2442  eDest.Emsg("Config", "desthttps flag not specified");
2443  return 1;
2444  }
2445 
2446  // Record the value
2447  //
2448  isdesthttps = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2449 
2450 
2451  return 0;
2452 }
2453 
2454 /******************************************************************************/
2455 /* x e m b e d d e d s t a t i c */
2456 /******************************************************************************/
2457 
2458 /* Function: xembeddedstatic
2459 
2460  Purpose: To parse the directive: embeddedstatic <yes|no|0|1|true|false>
2461 
2462  <val> this server will redirect HTTPS to itself using HTTP+token
2463 
2464  Output: 0 upon success or !0 upon failure.
2465  */
2466 
2467 int XrdHttpProtocol::xembeddedstatic(XrdOucStream & Config) {
2468  char *val;
2469 
2470  // Get the path
2471  //
2472  val = Config.GetWord();
2473  if (!val || !val[0]) {
2474  eDest.Emsg("Config", "embeddedstatic flag not specified");
2475  return 1;
2476  }
2477 
2478  // Record the value
2479  //
2480  embeddedstatic = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2481 
2482 
2483  return 0;
2484 }
2485 
2486 /******************************************************************************/
2487 /* x r e d i r s t a t i c */
2488 /******************************************************************************/
2489 
2490 /* Function: xstaticredir
2491 
2492  Purpose: To parse the directive: staticredir <Url>
2493 
2494  <Url> http/https server to redirect to in the case of /static
2495 
2496  Output: 0 upon success or !0 upon failure.
2497  */
2498 
2499 int XrdHttpProtocol::xstaticredir(XrdOucStream & Config) {
2500  char *val;
2501 
2502  // Get the path
2503  //
2504  val = Config.GetWord();
2505  if (!val || !val[0]) {
2506  eDest.Emsg("Config", "staticredir url not specified");
2507  return 1;
2508  }
2509 
2510  // Record the value
2511  //
2512  if (staticredir) free(staticredir);
2513  staticredir = strdup(val);
2514 
2515  return 0;
2516 }
2517 
2518 /******************************************************************************/
2519 /* x p r e l o a d s t a t i c */
2520 /******************************************************************************/
2521 
2522 /* Function: xpreloadstatic
2523 
2524  Purpose: To parse the directive: preloadstatic <http url path> <local file>
2525 
2526  <http url path> http/http path whose response we are preloading
2527  e.g. /static/mycss.css
2528  NOTE: this must start with /static
2529 
2530 
2531  Output: 0 upon success or !0 upon failure.
2532  */
2533 
2534 int XrdHttpProtocol::xstaticpreload(XrdOucStream & Config) {
2535  char *val, *k, key[1024];
2536 
2537  // Get the key
2538  //
2539  k = Config.GetWord();
2540  if (!k || !k[0]) {
2541  eDest.Emsg("Config", "preloadstatic urlpath not specified");
2542  return 1;
2543  }
2544 
2545  strcpy(key, k);
2546 
2547  // Get the val
2548  //
2549  val = Config.GetWord();
2550  if (!val || !val[0]) {
2551  eDest.Emsg("Config", "preloadstatic filename not specified");
2552  return 1;
2553  }
2554 
2555  // Try to load the file into memory
2556  int fp = open(val, O_RDONLY);
2557  if( fp < 0 ) {
2558  eDest.Emsg("Config", errno, "open preloadstatic filename", val);
2559  return 1;
2560  }
2561 
2562  StaticPreloadInfo *nfo = new StaticPreloadInfo;
2563  // Max 64Kb ok?
2564  nfo->data = (char *)malloc(65536);
2565  nfo->len = read(fp, (void *)nfo->data, 65536);
2566  close(fp);
2567 
2568  if (nfo->len <= 0) {
2569  eDest.Emsg("Config", errno, "read from preloadstatic filename", val);
2570  return 1;
2571  }
2572 
2573  if (nfo->len >= 65536) {
2574  eDest.Emsg("Config", "Truncated preloadstatic filename. Max is 64 KB '", val, "'");
2575  return 1;
2576  }
2577 
2578  // Record the value
2579  //
2580  if (!staticpreload)
2582 
2583  staticpreload->Rep((const char *)key, nfo);
2584  return 0;
2585 }
2586 
2587 /******************************************************************************/
2588 /* x s t a t i c h e a d e r */
2589 /******************************************************************************/
2590 
2591 //
2592 // xstaticheader parses the http.staticheader director with the following syntax:
2593 //
2594 // http.staticheader [-verb=[GET|HEAD|...]]* header [value]
2595 //
2596 // When set, this will cause XrdHttp to always return the specified header and
2597 // value.
2598 //
2599 // Setting this option multiple times is additive (multiple headers may be set).
2600 // Omitting the value will cause the static header setting to be unset.
2601 //
2602 // Omitting the -verb argument will cause it the header to be set unconditionally
2603 // for all requests.
2604 int XrdHttpProtocol::xstaticheader(XrdOucStream & Config) {
2605  auto val = Config.GetWord();
2606  std::vector<std::string> verbs;
2607  while (true) {
2608  if (!val || !val[0]) {
2609  eDest.Emsg("Config", "http.staticheader requires the header to be specified");
2610  return 1;
2611  }
2612 
2613  std::string match_verb;
2614  std::string_view val_str(val);
2615  if (val_str.substr(0, 6) == "-verb=") {
2616  verbs.emplace_back(val_str.substr(6));
2617  } else if (val_str == "-") {
2618  eDest.Emsg("Config", "http.staticheader is ignoring unknown flag: ", val_str.data());
2619  } else {
2620  break;
2621  }
2622 
2623  val = Config.GetWord();
2624  }
2625  if (verbs.empty()) {
2626  verbs.emplace_back();
2627  }
2628 
2629  std::string header = val;
2630 
2631  val = Config.GetWord();
2632  std::string header_value;
2633  if (val && val[0]) {
2634  header_value = val;
2635  }
2636 
2637  for (const auto &verb : verbs) {
2638  auto iter = m_staticheader_map.find(verb);
2639  if (iter == m_staticheader_map.end() && !header_value.empty()) {
2640  m_staticheader_map.insert(iter, {verb, {{header, header_value}}});
2641  } else if (header_value.empty()) {
2642  iter->second.clear();
2643  } else {
2644  iter->second.emplace_back(header, header_value);
2645  }
2646  }
2647 
2648  return 0;
2649 }
2650 
2651 
2652 /******************************************************************************/
2653 /* x s e l f h t t p s 2 h t t p */
2654 /******************************************************************************/
2655 
2656 /* Function: selfhttps2http
2657 
2658  Purpose: To parse the directive: selfhttps2http <yes|no|0|1>
2659 
2660  <val> this server will redirect HTTPS to itself using HTTP+token
2661 
2662  Output: 0 upon success or !0 upon failure.
2663  */
2664 
2665 int XrdHttpProtocol::xselfhttps2http(XrdOucStream & Config) {
2666  char *val;
2667 
2668  // Get the path
2669  //
2670  val = Config.GetWord();
2671  if (!val || !val[0]) {
2672  eDest.Emsg("Config", "selfhttps2http flag not specified");
2673  return 1;
2674  }
2675 
2676  // Record the value
2677  //
2678  selfhttps2http = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2679 
2680 
2681  return 0;
2682 }
2683 
2684 /******************************************************************************/
2685 /* x s e c x t r a c t o r */
2686 /******************************************************************************/
2687 
2688 /* Function: xsecxtractor
2689 
2690  Purpose: To parse the directive: secxtractor [required] <path> <params>
2691 
2692  required optional parameter which if present treats any secxtractor
2693  errors as fatal.
2694  <path> the path of the plugin to be loaded
2695  <params> parameters passed to the secxtractor library
2696 
2697  Output: 0 upon success or !0 upon failure.
2698  */
2699 
2700 int XrdHttpProtocol::xsecxtractor(XrdOucStream& Config) {
2701  char *val;
2702 
2703  // Get the path
2704  //
2705  val = Config.GetWord();
2706  if (!val || !val[0]) {
2707  eDest.Emsg("Config", "No security extractor plugin specified.");
2708  return 1;
2709  } else {
2710  // Handle optional parameter [required]
2711  //
2712  if (!strncmp(val, "required", 8)) {
2713  isRequiredXtractor = true;
2714  val = Config.GetWord();
2715 
2716  if (!val || !val[0]) {
2717  eDest.Emsg("Config", "No security extractor plugin after [required] "
2718  "parameter");
2719  return 1;
2720  }
2721  }
2722 
2723  char libName[4096];
2724  strlcpy(libName, val, sizeof(libName));
2725  libName[sizeof(libName) - 1] = '\0';
2726  char libParms[4096];
2727 
2728  if (!Config.GetRest(libParms, 4095)) {
2729  eDest.Emsg("Config", "secxtractor config params longer than 4k");
2730  return 1;
2731  }
2732 
2733  // Try to load the plugin (if available) that extracts info from the
2734  // user cert/proxy
2735  if (LoadSecXtractor(&eDest, libName, libParms)) {
2736  return 1;
2737  }
2738  }
2739 
2740  return 0;
2741 }
2742 
2743 /******************************************************************************/
2744 /* x e x t h a n d l e r */
2745 /******************************************************************************/
2746 
2747 /* Function: xexthandler
2748  *
2749  * Purpose: To parse the directive: exthandler <name> <path> <initparm>
2750  *
2751  * <name> a unique name (max 16chars) to be given to this
2752  * instance, e.g 'myhandler1'
2753  * <path> the path of the plugin to be loaded
2754  * <initparm> a string parameter (e.g. a config file) that is
2755  * passed to the initialization of the plugin
2756  *
2757  * Output: 0 upon success or !0 upon failure.
2758  */
2759 
2760 int XrdHttpProtocol::xexthandler(XrdOucStream &Config,
2761  std::vector<extHInfo> &hiVec) {
2762  char *val, path[1024], namebuf[1024];
2763  char *parm;
2764  // By default, every external handler need TLS configured to be loaded
2765  bool noTlsOK = false;
2766 
2767  // Get the name
2768  //
2769  val = Config.GetWord();
2770  if (!val || !val[0]) {
2771  eDest.Emsg("Config", "No instance name specified for an http external handler plugin.");
2772  return 1;
2773  }
2774  if (strlen(val) >= 16) {
2775  eDest.Emsg("Config", "Instance name too long for an http external handler plugin.");
2776  return 1;
2777  }
2778  strncpy(namebuf, val, sizeof(namebuf));
2779  namebuf[ sizeof(namebuf)-1 ] = '\0';
2780 
2781  // Get the +notls option if it was provided
2782  val = Config.GetWord();
2783 
2784  if(val && !strcmp("+notls",val)) {
2785  noTlsOK = true;
2786  val = Config.GetWord();
2787  }
2788 
2789  // Get the path
2790  //
2791  if (!val || !val[0]) {
2792  eDest.Emsg("Config", "No http external handler plugin specified.");
2793  return 1;
2794  }
2795  if (strlen(val) >= (int)sizeof(path)) {
2796  eDest.Emsg("Config", "Path too long for an http external handler plugin.");
2797  return 1;
2798  }
2799 
2800  strcpy(path, val);
2801 
2802  // Everything else is a free string
2803  //
2804  parm = Config.GetWord();
2805 
2806  // Verify whether this is a duplicate (we never supported replacements)
2807  //
2808  for (int i = 0; i < (int)hiVec.size(); i++)
2809  {if (hiVec[i].extHName == namebuf) {
2810  eDest.Emsg("Config", "Instance name already present for "
2811  "http external handler plugin",
2812  hiVec[i].extHPath.c_str());
2813  return 1;
2814  }
2815  }
2816 
2817  // Verify that we don't have more already than we are allowed to have
2818  //
2819  if (hiVec.size() >= MAX_XRDHTTPEXTHANDLERS) {
2820  eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
2821  return 1;
2822  }
2823 
2824  // Create an info struct and push it on the list of ext handlers to load
2825  //
2826  hiVec.push_back(extHInfo(namebuf, path, (parm ? parm : ""), noTlsOK));
2827 
2828  return 0;
2829 }
2830 
2831 /******************************************************************************/
2832 /* x h e a d e r 2 c g i */
2833 /******************************************************************************/
2834 
2835 /* Function: xheader2cgi
2836  *
2837  * Purpose: To parse the directive: header2cgi <headerkey> <cgikey>
2838  *
2839  * <headerkey> the name of an incoming HTTP header
2840  * to be transformed
2841  * <cgikey> the name to be given when adding it to the cgi info
2842  * that is kept only internally
2843  *
2844  * Output: 0 upon success or !0 upon failure.
2845  */
2846 
2847 int XrdHttpProtocol::xheader2cgi(XrdOucStream & Config) {
2849 }
2850 
2851 /******************************************************************************/
2852 /* x s s l c a d i r */
2853 /******************************************************************************/
2854 
2855 /* Function: xsslcadir
2856 
2857  Purpose: To parse the directive: sslcadir <path>
2858 
2859  <path> the path of the server key to be used.
2860 
2861  Output: 0 upon success or !0 upon failure.
2862  */
2863 
2864 int XrdHttpProtocol::xsslcadir(XrdOucStream & Config) {
2865  char *val;
2866 
2867  // Get the path
2868  //
2869  val = Config.GetWord();
2870  if (!val || !val[0]) {
2871  eDest.Emsg("Config", "HTTP X509 CAdir not specified");
2872  return 1;
2873  }
2874 
2875  // Record the path
2876  //
2877  if (sslcadir) free(sslcadir);
2878  sslcadir = strdup(val);
2879 
2880  if (xrdctxVer){ HTTPS_ALERT("cadir","tlsca",false); }
2881  return 0;
2882 }
2883 
2884 /******************************************************************************/
2885 /* x s s l c i p h e r f i l t e r */
2886 /******************************************************************************/
2887 
2888 /* Function: xsslcipherfilter
2889 
2890  Purpose: To parse the directive: cipherfilter <filter>
2891 
2892  <filter> the filter string to be used when generating
2893  the SSL cipher list
2894 
2895  Output: 0 upon success or !0 upon failure.
2896  */
2897 
2898 int XrdHttpProtocol::xsslcipherfilter(XrdOucStream & Config) {
2899  char *val;
2900 
2901  // Get the filter string
2902  //
2903  val = Config.GetWord();
2904  if (!val || !val[0]) {
2905  eDest.Emsg("Config", "SSL cipherlist filter string not specified");
2906  return 1;
2907  }
2908 
2909  // Record the filter string
2910  //
2911  if (sslcipherfilter) free(sslcipherfilter);
2912  sslcipherfilter = strdup(val);
2913 
2914  return 0;
2915 }
2916 
2917 /******************************************************************************/
2918 /* x t l s r e u s e */
2919 /******************************************************************************/
2920 
2921 /* Function: xtlsreuse
2922 
2923  Purpose: To parse the directive: tlsreuse {on | off}
2924 
2925  Output: 0 upon success or 1 upon failure.
2926  */
2927 
2928 int XrdHttpProtocol::xtlsreuse(XrdOucStream & Config) {
2929 
2930  char *val;
2931 
2932 // Get the argument
2933 //
2934  val = Config.GetWord();
2935  if (!val || !val[0])
2936  {eDest.Emsg("Config", "tlsreuse argument not specified"); return 1;}
2937 
2938 // If it's off, we set it off
2939 //
2940  if (!strcmp(val, "off"))
2942  return 0;
2943  }
2944 
2945 // If it's on we set it on.
2946 //
2947  if (!strcmp(val, "on"))
2949  return 0;
2950  }
2951 
2952 // Bad argument
2953 //
2954  eDest.Emsg("config", "invalid tlsreuse parameter -", val);
2955  return 1;
2956 }
2957 
2958 int XrdHttpProtocol::xtlsclientauth(XrdOucStream &Config) {
2959  auto val = Config.GetWord();
2960  if (!val || !val[0])
2961  {eDest.Emsg("Config", "tlsclientauth argument not specified"); return 1;}
2962 
2963  if (!strcmp(val, "off"))
2964  {tlsClientAuth = XrdTlsContext::ClientAuthSetting::kOff;
2965  return 0;
2966  }
2967  if (!strcmp(val, "on"))
2968  {tlsClientAuth = XrdTlsContext::ClientAuthSetting::kOn;
2969  return 0;
2970  }
2971 
2972  eDest.Emsg("config", "invalid tlsclientauth parameter -", val);
2973  return 1;
2974 }
2975 
2976 int XrdHttpProtocol::xauth(XrdOucStream &Config) {
2977  char *val = Config.GetWord();
2978  if(val) {
2979  if(!strcmp("tpc",val)) {
2980  if(!(val = Config.GetWord())) {
2981  eDest.Emsg("Config", "http.auth tpc value not specified."); return 1;
2982  } else {
2983  if(!strcmp("fcreds",val)) {
2984  tpcForwardCreds = true;
2985  } else {
2986  eDest.Emsg("Config", "http.auth tpc value is invalid"); return 1;
2987  }
2988  }
2989  } else {
2990  eDest.Emsg("Config", "http.auth value is invalid"); return 1;
2991  }
2992  }
2993  return 0;
2994 }
2995 
2996 /******************************************************************************/
2997 /* x t r a c e */
2998 /******************************************************************************/
2999 
3000 /* Function: xtrace
3001 
3002  Purpose: To parse the directive: trace <events>
3003 
3004  <events> the blank separated list of events to trace. Trace
3005  directives are cumulative.
3006 
3007  Output: 0 upon success or 1 upon failure.
3008  */
3009 
3010 int XrdHttpProtocol::xtrace(XrdOucStream & Config) {
3011 
3012  char *val;
3013 
3014  static struct traceopts {
3015  const char *opname;
3016  int opval;
3017  } tropts[] = {
3018  {"all", TRACE_ALL},
3019  {"auth", TRACE_AUTH},
3020  {"debug", TRACE_DEBUG},
3021  {"mem", TRACE_MEM},
3022  {"redirect", TRACE_REDIR},
3023  {"request", TRACE_REQ},
3024  {"response", TRACE_RSP}
3025  };
3026  int i, neg, trval = 0, numopts = sizeof (tropts) / sizeof (struct traceopts);
3027 
3028  if (!(val = Config.GetWord())) {
3029  eDest.Emsg("config", "trace option not specified");
3030  return 1;
3031  }
3032  while (val) {
3033  if (!strcmp(val, "off")) trval = 0;
3034  else {
3035  if ((neg = (val[0] == '-' && val[1]))) val++;
3036  for (i = 0; i < numopts; i++) {
3037  if (!strcmp(val, tropts[i].opname)) {
3038  if (neg) trval &= ~tropts[i].opval;
3039  else trval |= tropts[i].opval;
3040  break;
3041  }
3042  }
3043  if (i >= numopts)
3044  eDest.Emsg("config", "invalid trace option", val);
3045  }
3046  val = Config.GetWord();
3047  }
3048  XrdHttpTrace.What = trval;
3049  return 0;
3050 }
3051 
3052 int XrdHttpProtocol::doStat(char *fname) {
3053  int l;
3054  bool b;
3055  CurrentReq.filesize = 0;
3056  CurrentReq.fileflags = 0;
3057  CurrentReq.filemodtime = 0;
3058 
3059  memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3061  memset(CurrentReq.xrdreq.stat.reserved, 0,
3062  sizeof (CurrentReq.xrdreq.stat.reserved));
3063  l = strlen(fname) + 1;
3064  CurrentReq.xrdreq.stat.dlen = htonl(l);
3065 
3066  if (!Bridge) return -1;
3067  b = Bridge->Run((char *) &CurrentReq.xrdreq, fname, l);
3068  if (!b) {
3069  return -1;
3070  }
3071 
3072 
3073  return 0;
3074 }
3075 
3076 /******************************************************************************/
3077 /* d o C h k s u m */
3078 /******************************************************************************/
3079 
3081  size_t length;
3082  memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3086  memset(CurrentReq.xrdreq.query.fhandle, '\0', sizeof(CurrentReq.xrdreq.query.fhandle));
3088  length = fname.length() + 1;
3089  CurrentReq.xrdreq.query.dlen = htonl(length);
3090 
3091  if (!Bridge) return -1;
3092 
3093  return Bridge->Run(reinterpret_cast<char *>(&CurrentReq.xrdreq), const_cast<char *>(fname.c_str()), length) ? 0 : -1;
3094 }
3095 
3096 
3097 static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION);
3098 
3099 // Loads the SecXtractor plugin, if available
3100 int XrdHttpProtocol::LoadSecXtractor(XrdSysError *myeDest, const char *libName,
3101  const char *libParms) {
3102 
3103 
3104  // We don't want to load it more than once
3105  if (secxtractor) return 1;
3106 
3107  XrdOucPinLoader myLib(myeDest, &compiledVer, "secxtractorlib", libName);
3109 
3110  // Get the entry point of the object creator
3111  //
3112  ep = (XrdHttpSecXtractor *(*)(XrdHttpSecXtractorArgs))(myLib.Resolve("XrdHttpGetSecXtractor"));
3113  if (ep && (secxtractor = ep(myeDest, NULL, libParms))) return 0;
3114  myLib.Unload();
3115  return 1;
3116 }
3117 /******************************************************************************/
3118 /* L o a d E x t H a n d l e r */
3119 /******************************************************************************/
3120 
3121 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec, const char *cFN, XrdOucEnv &myEnv) {
3122  for (int i = 0; i < (int) hiVec.size(); i++) {
3123  if(hiVec[i].extHNoTlsOK) {
3124  // The external plugin does not need TLS to be loaded
3125  if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3126  hiVec[i].extHParm.c_str(), &myEnv,
3127  hiVec[i].extHName.c_str()))
3128  return 1;
3129  }
3130  }
3131  return 0;
3132 }
3133 
3134 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3135  const char *cFN, XrdOucEnv &myEnv) {
3136 
3137  // Add the pointer to the cadir and the cakey to the environment.
3138  //
3139  if (sslcadir) myEnv.Put("http.cadir", sslcadir);
3140  if (sslcafile) myEnv.Put("http.cafile", sslcafile);
3141  if (sslcert) myEnv.Put("http.cert", sslcert);
3142  if (sslkey) myEnv.Put("http.key" , sslkey);
3143 
3144  // Load all of the specified external handlers.
3145  //
3146  for (int i = 0; i < (int)hiVec.size(); i++) {
3147  // Only load the external handlers that were not already loaded
3148  // by LoadExtHandlerNoTls(...)
3149  if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3150  if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3151  hiVec[i].extHParm.c_str(), &myEnv,
3152  hiVec[i].extHName.c_str())) return 1;
3153  }
3154  }
3155  return 0;
3156 }
3157 
3158 // Loads the external handler plugin, if available
3159 int XrdHttpProtocol::LoadExtHandler(XrdSysError *myeDest, const char *libName,
3160  const char *configFN, const char *libParms,
3161  XrdOucEnv *myEnv, const char *instName) {
3162 
3163 
3164  // This function will avoid loading doubles. No idea why this happens
3165  if (ExtHandlerLoaded(instName)) {
3166  eDest.Emsg("Config", "Instance name already present for an http external handler plugin.");
3167  return 1;
3168  }
3169  if (exthandlercnt >= MAX_XRDHTTPEXTHANDLERS) {
3170  eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
3171  return 1;
3172  }
3173 
3174  XrdOucPinLoader myLib(myeDest, &compiledVer, "exthandlerlib", libName);
3176 
3177  // Get the entry point of the object creator
3178  //
3179  ep = (XrdHttpExtHandler *(*)(XrdHttpExtHandlerArgs))(myLib.Resolve("XrdHttpGetExtHandler"));
3180 
3181  XrdHttpExtHandler *newhandler;
3182  if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3183 
3184  // Handler has been loaded, it's the last one in the list
3185  strncpy( exthandler[exthandlercnt].name, instName, 16 );
3186  exthandler[exthandlercnt].name[15] = '\0';
3187  exthandler[exthandlercnt++].ptr = newhandler;
3188 
3189  return 0;
3190  }
3191 
3192  myLib.Unload();
3193  return 1;
3194 }
3195 
3196 
3197 
3198 // Tells if we have already loaded a certain exthandler. Try to
3199 // privilege speed, as this func may be invoked pretty often
3200 bool XrdHttpProtocol::ExtHandlerLoaded(const char *handlername) {
3201  for (int i = 0; i < exthandlercnt; i++) {
3202  if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3203  return true;
3204  }
3205  }
3206  return false;
3207 }
3208 
3209 // Locates a matching external handler for a given request, if available. Try to
3210 // privilege speed, as this func is invoked for every incoming request
3211 XrdHttpExtHandler * XrdHttpProtocol::FindMatchingExtHandler(const XrdHttpReq &req) {
3212 
3213  for (int i = 0; i < exthandlercnt; i++) {
3214  if (exthandler[i].ptr->MatchesPath(req.requestverb.c_str(), req.resource.c_str())) {
3215  return exthandler[i].ptr;
3216  }
3217  }
3218  return NULL;
3219 }
#define kXR_isManager
Definition: XProtocol.hh:1156
kXR_unt16 requestid
Definition: XProtocol.hh:630
kXR_char reserved1[2]
Definition: XProtocol.hh:632
struct ClientSetRequest set
Definition: XProtocol.hh:871
kXR_char reserved[11]
Definition: XProtocol.hh:770
kXR_unt16 infotype
Definition: XProtocol.hh:631
kXR_char reserved2[8]
Definition: XProtocol.hh:634
kXR_char fhandle[4]
Definition: XProtocol.hh:633
@ kXR_query
Definition: XProtocol.hh:113
@ kXR_set
Definition: XProtocol.hh:130
@ kXR_stat
Definition: XProtocol.hh:129
kXR_unt16 requestid
Definition: XProtocol.hh:719
#define kXR_isServer
Definition: XProtocol.hh:1157
struct ClientQueryRequest query
Definition: XProtocol.hh:866
kXR_unt16 requestid
Definition: XProtocol.hh:768
struct ClientStatRequest stat
Definition: XProtocol.hh:873
kXR_int32 dlen
Definition: XProtocol.hh:722
kXR_int32 dlen
Definition: XProtocol.hh:772
kXR_char modifier
Definition: XProtocol.hh:721
@ kXR_Qcksum
Definition: XProtocol.hh:617
kXR_char reserved[15]
Definition: XProtocol.hh:720
int kXR_int32
Definition: XPtypes.hh:89
short kXR_int16
Definition: XPtypes.hh:66
#define DEBUG(x)
Definition: XrdBwmTrace.hh:54
#define XrdHttpExtHandlerArgs
int BIO_get_init(BIO *bio)
int BIO_get_shutdown(BIO *bio)
int BIO_get_flags(BIO *bio)
static int BIO_XrdLink_create(BIO *bio)
const char * XrdHttpSecEntityTident
void BIO_set_init(BIO *bio, int init)
int BIO_XrdLink_write(BIO *bio, const char *data, size_t datal, size_t *written)
#define HTTPS_ALERT(x, y, z)
static long BIO_XrdLink_ctrl(BIO *bio, int cmd, long num, void *ptr)
void BIO_set_shutdown(BIO *bio, int shut)
#define TS_Xeq(x, m)
XrdSysTrace XrdHttpTrace("http")
void * BIO_get_data(BIO *bio)
static int BIO_XrdLink_read(BIO *bio, char *data, size_t datal, size_t *read)
void BIO_set_data(BIO *bio, void *ptr)
#define TS_Xeq3(x, m)
static int BIO_XrdLink_destroy(BIO *bio)
#define XRHTTP_TK_GRACETIME
static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION)
void BIO_set_flags(BIO *bio, int flags)
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
#define MAX_XRDHTTPEXTHANDLERS
#define XrdHttpSecXtractorArgs
Trace definitions.
#define TRACE_AUTH
Definition: XrdHttpTrace.hh:48
#define TRACE_REQ
Definition: XrdHttpTrace.hh:51
#define TRACE_RSP
Definition: XrdHttpTrace.hh:53
#define TRACE_REDIR
Definition: XrdHttpTrace.hh:52
int compareHash(const char *h1, const char *h2)
char * unquote(char *str)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
Utility functions for XrdHTTP.
std::string obfuscateAuth(const std::string &input)
int stat(const char *path, struct stat *buf)
int open(const char *path, int oflag,...)
int fstat(int fildes, struct stat *buf)
int fclose(FILE *stream)
ssize_t read(int fildes, void *buf, size_t nbyte)
#define close(a)
Definition: XrdPosix.hh:48
#define eMsg(x)
struct myOpts opts
size_t strlcpy(char *dst, const char *src, size_t sz)
#define TLS_SET_VDEPTH(cOpts, vdv)
#define TLS_SET_REFINT(cOpts, refi)
#define TRACE_DEBUG
Definition: XrdTrace.hh:36
#define TRACE_MEM
Definition: XrdTrace.hh:38
#define TRACE(act, x)
Definition: XrdTrace.hh:63
#define TRACE_ALL
Definition: XrdTrace.hh:35
#define TRACING(x)
Definition: XrdTrace.hh:70
#define TRACEI(act, x)
Definition: XrdTrace.hh:66
void Release(XrdBuffer *bp)
Definition: XrdBuffer.cc:221
XrdBuffer * Obtain(int bsz)
Definition: XrdBuffer.cc:140
int bsize
Definition: XrdBuffer.hh:46
char * buff
Definition: XrdBuffer.hh:45
const std::vector< std::string > & getNonIANAConfiguredCksums() const
void configure(const char *csList)
static char * secretkey
The key used to calculate the url hashes.
static BIO_METHOD * m_bio_method
C-style vptr table for our custom BIO objects.
static char * gridmap
Gridmap file location. The same used by XrdSecGsi.
static XrdScheduler * Sched
static kXR_int32 myRole
Our role.
static char * sslcafile
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
static char * Port_str
Our port, as a string.
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdSysError eDest
static bool selfhttps2http
If client is HTTPS, self-redirect with HTTP+token.
static XrdHttpChecksumHandler cksumHandler
static int hailWait
Timeout for reading the handshake.
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static char * xrd_cslist
The list of checksums that were configured via the xrd.cksum parameter on the server config file.
static char * sslcipherfilter
static int m_bio_type
Type identifier for our custom BIO objects.
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
static char * sslcert
OpenSSL stuff.
XrdLink * Link
The link we are bound to.
static char * sslkey
int doStat(char *fname)
Perform a Stat request.
XrdObject< XrdHttpProtocol > ProtLink
static int readWait
Timeout for reading data.
void Recycle(XrdLink *lp, int consec, const char *reason)
Recycle this instance.
static char * sslcadir
XrdHttpProtocol operator=(const XrdHttpProtocol &rhs)
static bool compatNameGeneration
static bool isdesthttps
True if the redirections must be towards https targets.
static XrdObjectQ< XrdHttpProtocol > ProtStack
XrdProtocol * Match(XrdLink *lp)
Tells if the oustanding bytes on the socket match this protocol implementation.
static std::unordered_map< std::string, std::vector< std::pair< std::string, std::string > > > m_staticheader_map
The static headers to always return; map is from verb to a list of (header, val) pairs.
static bool isRequiredGridmap
static char * listredir
Url to redirect to in the case a listing is requested.
int Stats(char *buff, int blen, int do_sync=0)
Get activity stats.
static std::unordered_map< std::string, std::string > m_staticheaders
XrdHttpReq CurrentReq
static int crlRefIntervalSec
CRL thread refresh interval.
static int Port
Our port.
static XrdHttpReadRangeHandler::Configuration ReadRangeConfig
configuration for the read range handler
static XrdSecService * CIA
static XrdBuffManager * BPool
static bool tpcForwardCreds
If set to true, the HTTP TPC transfers will forward the credentials to redirected hosts.
int Process(XrdLink *lp)
Process data incoming from the socket.
XrdHttpProtocol(const XrdHttpProtocol &)=default
Ctor, dtors and copy ctor.
static bool listdeny
If true, any form of listing is denied.
static int parseHeader2CGI(XrdOucStream &Config, XrdSysError &err, std::map< std::string, std::string > &header2cgi)
Use this function to parse header2cgi configurations.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
static int sslverifydepth
Depth of verification of a certificate chain.
static int Configure(char *parms, XrdProtocol_Config *pi)
Read and apply the configuration.
static int Configure(XrdSysError &Eroute, const char *const parms, Configuration &cfg)
int reqstate
State machine to talk to the bridge.
Definition: XrdHttpReq.hh:331
XrdOucString resource
The resource specified by the request, stripped of opaque data.
Definition: XrdHttpReq.hh:249
bool headerok
Tells if we have finished reading the header.
Definition: XrdHttpReq.hh:257
std::string requestverb
Definition: XrdHttpReq.hh:242
ReqType request
The request we got.
Definition: XrdHttpReq.hh:241
int ProcessHTTPReq()
Definition: XrdHttpReq.cc:939
XrdOucEnv * opaque
The opaque data, after parsing.
Definition: XrdHttpReq.hh:251
long fileflags
Definition: XrdHttpReq.hh:321
long filemodtime
Definition: XrdHttpReq.hh:322
int parseFirstLine(char *line, int len)
Parse the first line of the header.
Definition: XrdHttpReq.cc:261
int parseLine(char *line, int len)
Parse the header.
Definition: XrdHttpReq.cc:116
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
Definition: XrdHttpReq.cc:637
long long filesize
Definition: XrdHttpReq.hh:320
ClientRequest xrdreq
The last issued xrd request, often pending.
Definition: XrdHttpReq.hh:305
const std::string & userAgent() const
Definition: XrdHttpReq.hh:215
virtual void reset()
Definition: XrdHttpReq.cc:2815
virtual int InitSSL(SSL *, char *)
virtual int FreeSSL(SSL *)
static const int noPort
Do not add port number.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
void SetDialect(const char *dP)
Definition: XrdNetAddr.hh:205
void SetTLS(bool val)
Definition: XrdNetAddr.cc:590
void Set(int inQMax, time_t agemax=1800)
Definition: XrdObject.icc:90
void Push(XrdObject< T > *Node)
Definition: XrdObject.hh:101
T * Pop()
Definition: XrdObject.hh:93
static bool Import(const char *var, char *&val)
Definition: XrdOucEnv.cc:222
void * GetPtr(const char *varname)
Definition: XrdOucEnv.cc:281
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69
void Put(const char *varname, const char *value)
Definition: XrdOucEnv.hh:85
void insert(const int i, int start=-1)
const char * c_str() const
void assign(const char *s, int j, int k=-1)
int length() const
XrdBuffManager * BPool
Definition: XrdProtocol.hh:63
XrdScheduler * Sched
Definition: XrdProtocol.hh:64
XrdTlsContext * tlsCtx
Definition: XrdProtocol.hh:99
XrdSysError * eDest
Definition: XrdProtocol.hh:61
XrdOucEnv * theEnv
Definition: XrdProtocol.hh:66
char * vorg
Entity's virtual organization(s)
Definition: XrdSecEntity.hh:71
int credslen
Length of the 'creds' data.
Definition: XrdSecEntity.hh:78
XrdNetAddrInfo * addrInfo
Entity's connection details.
Definition: XrdSecEntity.hh:80
const char * tident
Trace identifier always preset.
Definition: XrdSecEntity.hh:81
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
Definition: XrdSecEntity.hh:67
char * caps
Entity's capabilities.
Definition: XrdSecEntity.hh:74
char * creds
Raw entity credentials or cert.
Definition: XrdSecEntity.hh:77
char * grps
Entity's group name(s)
Definition: XrdSecEntity.hh:73
void Reset(const char *spV=0)
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
char * role
Entity's role(s)
Definition: XrdSecEntity.hh:72
char * endorsements
Protocol specific endorsements.
Definition: XrdSecEntity.hh:75
void Display(XrdSysError &mDest)
Definition: XrdSecEntity.cc:58
char * moninfo
Information for monitoring.
Definition: XrdSecEntity.hh:76
char * host
Entity's host name dnr dependent.
Definition: XrdSecEntity.hh:70
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
Definition: XrdSysError.cc:141
XrdSysLogger * logger(XrdSysLogger *lp=0)
Definition: XrdSysError.hh:141
void SetLogger(XrdSysLogger *logp)
Definition: XrdSysTrace.cc:65
int SessionCache(int opts=scNone, const char *id=0, int idlen=0)
static const int DEFAULT_CRL_REF_INT_SEC
Default CRL refresh interval in seconds.
static const uint64_t servr
This is a server context.
static const uint64_t rfCRL
Turn on the CRL refresh thread.
bool SetTlsClientAuth(ClientAuthSetting setting)
static const uint64_t logVF
Log verify failures.
static const uint64_t artON
Auto retry Handshake.
void * Session()
const CTX_Params * GetParams()
static const int scOff
Turn off cache.
bool SetContextCiphers(const char *ciphers)
static const int scSrvr
Turn on cache server mode (default)
static Bridge * Login(Result *rsltP, XrdLink *linkP, XrdSecEntity *seceP, const char *nameP, const char *protP)
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0
bool InitTLS()
Definition: XrdClTls.cc:96
CloseImpl< false > Close(Ctx< File > file, uint16_t timeout=0)
Factory for creating CloseImpl objects.
XrdCmsConfig Config
static const int hsmOff
XrdTlsContext::ClientAuthSetting tlsClientAuth
static const int hsmMan
static const int hsmOn
static const int hsmAuto
XrdTlsContext * xrdctx
@ dec
Definition: XrdSysTrace.hh:42
@ hex
Definition: XrdSysTrace.hh:42
std::string cafile
-> ca cert file.
std::string cadir
-> ca cert directory.
int crlRT
crl refresh interval time in seconds
std::string pkey
-> private key path.
std::string cert
-> certificate path.