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