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(), rc);
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(), rc);
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 return -1;
872 }
873
874 } else {
875 // Client is plain http. If we have a secret key then we reject it
876 if (secretkey) {
877 TRACEI(ALL, " Rejecting plain http with no valid token as we have a secretkey.");
878 return -1;
879 }
880 }
881
882 } else {
883 // Client is plain http. If we have a secret key then we reject it
884 if (secretkey) {
885 TRACEI(ALL, " Rejecting plain http with no valid token as we have a secretkey.");
886 return -1;
887 }
888 }
889
890 ssldone = true;
891 }
892
893
894
895 // Now we have everything that is needed to try the login
896 // Remember that if there is an exthandler then it has the responsibility
897 // for authorization in the paths that it manages
898 if (!Bridge && !FindMatchingExtHandler(CurrentReq)) {
899 if (SecEntity.name)
900 Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, SecEntity.name, ishttps ? "https" : "http");
901 else
902 Bridge = XrdXrootd::Bridge::Login(&CurrentReq, Link, &SecEntity, "unknown", ishttps ? "https" : "http");
903
904 if (!Bridge) {
905 TRACEI(REQ, " Authorization failed.");
906 return -1;
907 }
908 if (m_maxdelay > 0) Bridge->SetWait(m_maxdelay, false);
909
910 // Let the bridge process the login, and then reinvoke us
911 DoingLogin = true;
912 return 0;
913 }
914
915 // Compute and send the response. This may involve further reading from the socket
916 rc = CurrentReq.ProcessHTTPReq();
917 if (rc < 0)
918 CurrentReq.reset();
919
920
921
922 TRACEI(REQ, "Process is exiting rc:" << rc);
923 return rc;
924}
925/******************************************************************************/
926/* R e c y c l e */
927/******************************************************************************/
928
929#undef TRACELINK
930#define TRACELINK Link
931
932void XrdHttpProtocol::Recycle(XrdLink *lp, int csec, const char *reason) {
933
934 // Release all appendages
935 //
936
937 Cleanup();
938
939
940 // Set fields to starting point (debugging mostly)
941 //
942 Reset();
943
944 // Push ourselves on the stack
945 //
946 ProtStack.Push(&ProtLink);
947}
948
949int XrdHttpProtocol::Stats(char *buff, int blen, int do_sync) {
950 // Synchronize statistics if need be
951 //
952 // if (do_sync) {
953 //
954 // SI->statsMutex.Lock();
955 // SI->readCnt += numReads;
956 // cumReads += numReads;
957 // numReads = 0;
958 // SI->prerCnt += numReadP;
959 // cumReadP += numReadP;
960 // numReadP = 0;
961 // SI->rvecCnt += numReadV;
962 // cumReadV += numReadV;
963 // numReadV = 0;
964 // SI->rsegCnt += numSegsV;
965 // cumSegsV += numSegsV;
966 // numSegsV = 0;
967 // SI->writeCnt += numWrites;
968 // cumWrites += numWrites;
969 // numWrites = 0;
970 // SI->statsMutex.UnLock();
971 // }
972 //
973 // // Now return the statistics
974 // //
975 // return SI->Stats(buff, blen, do_sync);
976
977 return 0;
978}
979
980/******************************************************************************/
981/* C o n f i g */
982/******************************************************************************/
983
984#define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
985//#define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, ConfigFN, myEnv)
986#define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
987
988#define HTTPS_ALERT(x,y,z) httpsspec = true;\
989 if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
990 eDest.Say("Config http." x " overrides the xrd." y " directive.")
991
992int XrdHttpProtocol::Config(const char *ConfigFN, XrdOucEnv *myEnv) {
993 XrdOucEnv cfgEnv;
994 XrdOucStream Config(&eDest, getenv("XRDINSTANCE"), &cfgEnv, "=====> ");
995 std::vector<extHInfo> extHIVec;
996 char *var;
997 int cfgFD, GoNo, NoGo = 0, ismine;
998
999 var = nullptr;
1000 XrdOucEnv::Import("XRD_READV_LIMITS", var);
1002
1003 pmarkHandle = (XrdNetPMark* ) myEnv->GetPtr("XrdNetPMark*");
1004
1006 auto nonIanaChecksums = cksumHandler.getNonIANAConfiguredCksums();
1007 if(nonIanaChecksums.size()) {
1008 std::stringstream warningMsgSS;
1009 warningMsgSS << "Config warning: the following checksum algorithms are not IANA compliant: [";
1010 std::string unknownCksumString;
1011 for(auto unknownCksum: nonIanaChecksums) {
1012 unknownCksumString += unknownCksum + ",";
1013 }
1014 unknownCksumString.erase(unknownCksumString.size() - 1);
1015 warningMsgSS << unknownCksumString << "]" << ". They therefore cannot be queried by a user via HTTP." ;
1016 eDest.Say(warningMsgSS.str().c_str());
1017 }
1018
1019 // Initialize our custom BIO type.
1020 if (!m_bio_type) {
1021
1022 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1023 m_bio_type = (26|0x0400|0x0100);
1024 m_bio_method = static_cast<BIO_METHOD*>(OPENSSL_malloc(sizeof(BIO_METHOD)));
1025
1026 if (m_bio_method) {
1027 memset(m_bio_method, '\0', sizeof(BIO_METHOD));
1028 m_bio_method->type = m_bio_type;
1034 }
1035 #else
1036 // OpenSSL 1.1 has an internal counter for generating unique types.
1037 // We'll switch to that when widely available.
1038 m_bio_type = BIO_get_new_index();
1039 m_bio_method = BIO_meth_new(m_bio_type, "xrdhttp-bio-method");
1040
1041 if (m_bio_method) {
1042 BIO_meth_set_write(m_bio_method, BIO_XrdLink_write);
1043 BIO_meth_set_read(m_bio_method, BIO_XrdLink_read);
1044 BIO_meth_set_create(m_bio_method, BIO_XrdLink_create);
1045 BIO_meth_set_destroy(m_bio_method, BIO_XrdLink_destroy);
1046 BIO_meth_set_ctrl(m_bio_method, BIO_XrdLink_ctrl);
1047 }
1048
1049 #endif
1050 }
1051
1052 // If we have a tls context record whether it configured for verification
1053 // so that we can provide meaningful error and warning messages.
1054 //
1055 xrdctxVer = xrdctx && xrdctx->x509Verify();
1056
1057 // Open and attach the config file
1058 //
1059 if ((cfgFD = open(ConfigFN, O_RDONLY, 0)) < 0)
1060 return eDest.Emsg("Config", errno, "open config file", ConfigFN);
1061 Config.Attach(cfgFD);
1062 static const char *cvec[] = { "*** http protocol config:", 0 };
1063 Config.Capture(cvec);
1064
1065 // Process items
1066 //
1067 while ((var = Config.GetMyFirstWord())) {
1068 if ((ismine = !strncmp("http.", var, 5)) && var[5]) var += 5;
1069
1070 if (ismine) {
1071 if TS_Xeq("trace", xtrace);
1072 else if TS_Xeq("cert", xsslcert);
1073 else if TS_Xeq("key", xsslkey);
1074 else if TS_Xeq("cadir", xsslcadir);
1075 else if TS_Xeq("cipherfilter", xsslcipherfilter);
1076 else if TS_Xeq("gridmap", xgmap);
1077 else if TS_Xeq("cafile", xsslcafile);
1078 else if TS_Xeq("secretkey", xsecretkey);
1079 else if TS_Xeq("desthttps", xdesthttps);
1080 else if TS_Xeq("secxtractor", xsecxtractor);
1081 else if TS_Xeq("cors", xcors);
1082 else if TS_Xeq3("exthandler", xexthandler);
1083 else if TS_Xeq("selfhttps2http", xselfhttps2http);
1084 else if TS_Xeq("embeddedstatic", xembeddedstatic);
1085 else if TS_Xeq("listingredir", xlistredir);
1086 else if TS_Xeq("staticredir", xstaticredir);
1087 else if TS_Xeq("staticpreload", xstaticpreload);
1088 else if TS_Xeq("staticheader", xstaticheader);
1089 else if TS_Xeq("listingdeny", xlistdeny);
1090 else if TS_Xeq("header2cgi", xheader2cgi);
1091 else if TS_Xeq("httpsmode", xhttpsmode);
1092 else if TS_Xeq("tlsreuse", xtlsreuse);
1093 else if TS_Xeq("auth", xauth);
1094 else if TS_Xeq("tlsclientauth", xtlsclientauth);
1095 else if TS_Xeq("maxdelay", xmaxdelay);
1096 else {
1097 eDest.Say("Config warning: ignoring unknown directive '", var, "'.");
1098 Config.Echo();
1099 continue;
1100 }
1101 if (GoNo) {
1102 Config.Echo();
1103 NoGo = 1;
1104 }
1105 }
1106 }
1107
1108// To minimize message confusion down, if an error occurred during config
1109// parsing, just bail out now with a confirming message.
1110//
1111 if (NoGo)
1112 {eDest.Say("Config failure: one or more directives are flawed!");
1113 return 1;
1114 }
1115
1116// Some headers must always be converted to CGI key=value pairs
1117//
1118 hdr2cgimap["Cache-Control"] = "cache-control";
1119
1120// Test if XrdEC is loaded
1121 if (getenv("XRDCL_EC")) usingEC = true;
1122
1123// Pre-compute the static headers
1124//
1125 const auto default_verb = m_staticheader_map.find("");
1126 std::string default_static_headers;
1127 if (default_verb != m_staticheader_map.end()) {
1128 for (const auto &header_entry : default_verb->second) {
1129 default_static_headers += header_entry.first + ": " + header_entry.second + "\r\n";
1130 }
1131 }
1132 m_staticheaders[""] = default_static_headers;
1133 for (const auto &item : m_staticheader_map) {
1134 if (item.first.empty()) {
1135 continue; // Skip default case; already handled
1136 }
1137 auto headers = default_static_headers;
1138 for (const auto &header_entry : item.second) {
1139 headers += header_entry.first + ": " + header_entry.second + "\r\n";
1140 }
1141
1142 m_staticheaders[item.first] = headers;
1143 }
1144
1145// Test if this is a caching server
1146//
1147 if (myEnv->Get("XrdCache")) hasCache = true;
1148
1149 // Load CORS plugin if configured
1150 if(xrdcorsLibPath.size()) {
1151 if(LoadCorsHandler(&eDest, xrdcorsLibPath.c_str()) != 0) {
1152 return 1;
1153 }
1154 if (xrdcors->Configure(ConfigFN, &eDest) != 0) {
1155 return 1;
1156 }
1157 }
1158
1159// If https was disabled, then issue a warning message if xrdtls configured
1160// of it's disabled because httpsmode was auto and xrdtls was not configured.
1161// If we get past this point then we know https is a plausible option but we
1162// can still fail if we cannot supply any missing but required options.
1163//
1164 if (httpsmode == hsmOff || (httpsmode == hsmAuto && !xrdctx && !httpsspec))
1165 {const char *why = (httpsmode == hsmOff ? "has been disabled!"
1166 : "was not configured.");
1167 const char *what = Configed();
1168
1169 eDest.Say("Config warning: HTTPS functionality ", why);
1170 httpsmode = hsmOff;
1171
1172 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1173 if (what)
1174 {eDest.Say("Config failure: ", what, " HTTPS but it ", why);
1175 NoGo = 1;
1176 }
1177 return NoGo;
1178 }
1179
1180// Warn if a private key was specified without a cert as this has no meaning
1181// even as an auto overide as they must be paired.
1182//
1183 if (sslkey && !sslcert)
1184 {eDest.Say("Config warning: specifying http.key without http.cert "
1185 "is meaningless; ignoring key!");
1186 free(sslkey); sslkey = 0;
1187 }
1188
1189// If the mode is manual then we need to have at least a cert.
1190//
1191 if (httpsmode == hsmMan)
1192 {if (!sslcert)
1193 {eDest.Say("Config failure: 'httpsmode manual' requires atleast a "
1194 "a cert specification!");
1195 return 1;
1196 }
1197 }
1198
1199// If it's auto d through all possibilities. It's either auto with xrdtls
1200// configured or manual which needs at least a cert specification. For auto
1201// configuration we will only issue a warning if overrides were specified.
1202//
1203 if (httpsmode == hsmAuto && xrdctx)
1204 {const XrdTlsContext::CTX_Params *cP = xrdctx->GetParams();
1205 const char *what1 = 0, *what2 = 0, *what3 = 0;
1206
1207 if (!sslcert && cP->cert.size())
1208 {sslcert = strdup(cP->cert.c_str());
1209 if (cP->pkey.size()) sslkey = strdup(cP->pkey.c_str());
1210 what1 = "xrd.tls to supply 'cert' and 'key'.";
1211 }
1212 if (!sslcadir && cP->cadir.size())
1213 {sslcadir = strdup(cP->cadir.c_str());
1214 what2 = "xrd.tlsca to supply 'cadir'.";
1215 }
1216 if (!sslcafile && cP->cafile.size())
1217 {sslcafile = strdup(cP->cafile.c_str());
1218 what2 = (what2 ? "xrd.tlsca to supply 'cadir' and 'cafile'."
1219 : "xrd.tlsca to supply 'cafile'.");
1220 }
1223 what3 = "xrd.tlsca to supply 'refresh' interval.";
1224 }
1225 if (!httpsspec && what1) eDest.Say("Config Using ", what1);
1226 if (!httpsspec && what2) eDest.Say("Config Using ", what2);
1227 if (!httpsspec && what3) eDest.Say("Config Using ", what3);
1228 }
1229
1230// If a gridmap or secxtractor is present then we must be able to verify certs
1231//
1232 if (!(sslcadir || sslcafile))
1233 {const char *what = Configed();
1234 const char *why = (httpsspec ? "a cadir or cafile was not specified!"
1235 : "'xrd.tlsca noverify' was specified!");
1236 if (what)
1237 {eDest.Say("Config failure: ", what, " cert verification but ", why);
1238 return 1;
1239 }
1240 }
1241 httpsmode = hsmOn;
1242
1243// Oddly we need to create an error bio at this point
1244//
1245 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1246
1247// Now we can configure HTTPS. We will not reuse the passed context as we will
1248// be setting our own options specific to out implementation. One day we will.
1249//
1250 const char *how = "completed.";
1251 eDest.Say("++++++ HTTPS initialization started.");
1252 if (!InitTLS()) {NoGo = 1; how = "failed.";}
1253 eDest.Say("------ HTTPS initialization ", how);
1254 if (NoGo) return NoGo;
1255
1256// We can now load all the external handlers
1257//
1258 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv)) return 1;
1259
1260// At this point, we can actually initialize security plugins
1261//
1262 return (InitSecurity() ? NoGo : 1);
1263}
1264
1265/******************************************************************************/
1266/* C o n f i g e d */
1267/******************************************************************************/
1268
1269const char *XrdHttpProtocol::Configed()
1270{
1271 if (secxtractor && gridmap) return "gridmap and secxtractor require";
1272 if (secxtractor) return "secxtractor requires";
1273 if (gridmap) return "gridmap requires";
1274 return 0;
1275}
1276
1277/******************************************************************************/
1278/* B u f f g e t L i n e */
1279/******************************************************************************/
1280
1282
1283int XrdHttpProtocol::BuffgetLine(XrdOucString &dest) {
1284
1285 dest = "";
1286 char save;
1287
1288 // Easy case
1289 if (myBuffEnd >= myBuffStart) {
1290 int l = 0;
1291 for (char *p = myBuffStart; p < myBuffEnd; p++) {
1292 l++;
1293 if (*p == '\n') {
1294 save = *(p+1);
1295 *(p+1) = '\0';
1296 dest.assign(myBuffStart, 0, l-1);
1297 *(p+1) = save;
1298
1299 //strncpy(dest, myBuffStart, l);
1300 //dest[l] = '\0';
1301 BuffConsume(l);
1302
1303 //if (dest[l-1] == '\n') dest[l - 1] = '\0';
1304 return l;
1305 }
1306
1307 }
1308
1309 return 0;
1310 } else {
1311 // More complex case... we have to do it in two segments
1312
1313 // Segment 1: myBuffStart->myBuff->buff+myBuff->bsize
1314 int l = 0;
1315 for (char *p = myBuffStart; p < myBuff->buff + myBuff->bsize; p++) {
1316 l++;
1317 if ((*p == '\n') || (*p == '\0')) {
1318 save = *(p+1);
1319 *(p+1) = '\0';
1320 dest.assign(myBuffStart, 0, l-1);
1321 *(p+1) = save;
1322
1323 //strncpy(dest, myBuffStart, l);
1324
1325 BuffConsume(l);
1326
1327 //if (dest[l-1] == '\n') dest[l - 1] = '\0';
1328 return l;
1329 }
1330
1331 }
1332
1333 // We did not find the \n, let's keep on searching in the 2nd segment
1334 // Segment 2: myBuff->buff --> myBuffEnd
1335 l = 0;
1336 for (char *p = myBuff->buff; p < myBuffEnd; p++) {
1337 l++;
1338 if ((*p == '\n') || (*p == '\0')) {
1339 save = *(p+1);
1340 *(p+1) = '\0';
1341 // Remember the 1st segment
1342 int l1 = myBuff->buff + myBuff->bsize - myBuffStart;
1343
1344 dest.assign(myBuffStart, 0, l1-1);
1345 //strncpy(dest, myBuffStart, l1);
1346 BuffConsume(l1);
1347
1348 dest.insert(myBuffStart, l1, l-1);
1349 //strncpy(dest + l1, myBuffStart, l);
1350 //dest[l + l1] = '\0';
1351 BuffConsume(l);
1352
1353 *(p+1) = save;
1354
1355 //if (dest[l + l1 - 1] == '\n') dest[l + l1 - 1] = '\0';
1356 return l + l1;
1357 }
1358
1359 }
1360
1361
1362
1363 }
1364
1365 return 0;
1366}
1367
1368/******************************************************************************/
1369/* g e t D a t a O n e S h o t */
1370/******************************************************************************/
1371
1372int XrdHttpProtocol::getDataOneShot(int blen, bool wait) {
1373 int rlen, maxread;
1374
1375 // Get up to blen bytes from the connection. Put them into mybuff.
1376 // This primitive, for the way it is used, is not supposed to block if wait=false
1377
1378 // Returns:
1379 // 2: no space left in buffer
1380 // 1: timeout
1381 // -1: error
1382 // 0: everything read correctly
1383
1384
1385
1386 // Check for buffer overflow first
1387 maxread = std::min(blen, BuffAvailable());
1388 TRACE(DEBUG, "getDataOneShot BuffAvailable: " << BuffAvailable() << " maxread: " << maxread);
1389
1390 if (!maxread)
1391 return 2;
1392
1393 if (ishttps) {
1394 int sslavail = maxread;
1395
1396 if (!wait) {
1397 int l = SSL_pending(ssl);
1398 if (l > 0)
1399 sslavail = std::min(maxread, SSL_pending(ssl));
1400 }
1401
1402 if (sslavail < 0) {
1403 Link->setEtext("link SSL_pending error");
1404 ERR_print_errors(sslbio_err);
1405 return -1;
1406 }
1407
1408 TRACE(DEBUG, "getDataOneShot sslavail: " << sslavail);
1409 if (sslavail <= 0) return 0;
1410
1411 if (myBuffEnd - myBuff->buff >= myBuff->bsize) {
1412 TRACE(DEBUG, "getDataOneShot Buffer panic");
1413 myBuffEnd = myBuff->buff;
1414 }
1415
1416 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1417 if (rlen <= 0) {
1418 Link->setEtext("link SSL read error");
1419 ERR_print_errors(sslbio_err);
1420 return -1;
1421 }
1422
1423
1424 } else {
1425
1426 if (myBuffEnd - myBuff->buff >= myBuff->bsize) {
1427 TRACE(DEBUG, "getDataOneShot Buffer panic");
1428 myBuffEnd = myBuff->buff;
1429 }
1430
1431 if (wait)
1432 rlen = Link->Recv(myBuffEnd, maxread, readWait);
1433 else
1434 rlen = Link->Recv(myBuffEnd, maxread);
1435
1436
1437 if (rlen == 0) {
1438 Link->setEtext("link read error or closed");
1439 return -1;
1440 }
1441
1442 if (rlen < 0) {
1443 Link->setEtext("link timeout or other error");
1444 return -1;
1445 }
1446 }
1447
1448 myBuffEnd += rlen;
1449
1450 TRACE(REQ, "read " << rlen << " of " << blen << " bytes");
1451
1452 return 0;
1453}
1454
1456
1457int XrdHttpProtocol::BuffAvailable() {
1458 int r;
1459
1460 if (myBuffEnd >= myBuffStart)
1461 r = myBuff->buff + myBuff->bsize - myBuffEnd;
1462 else
1463 r = myBuffStart - myBuffEnd;
1464
1465 if ((r < 0) || (r > myBuff->bsize)) {
1466 TRACE(REQ, "internal error, myBuffAvailable: " << r << " myBuff->bsize " << myBuff->bsize);
1467 abort();
1468 }
1469
1470 return r;
1471}
1472
1473/******************************************************************************/
1474/* B u f f U s e d */
1475/******************************************************************************/
1476
1478
1479int XrdHttpProtocol::BuffUsed() {
1480 int r;
1481
1482 if (myBuffEnd >= myBuffStart)
1483 r = myBuffEnd - myBuffStart;
1484 else
1485
1486 r = myBuff->bsize - (myBuffStart - myBuffEnd);
1487
1488 if ((r < 0) || (r > myBuff->bsize)) {
1489 TRACE(REQ, "internal error, myBuffUsed: " << r << " myBuff->bsize " << myBuff->bsize);
1490 abort();
1491 }
1492
1493 return r;
1494}
1495
1496/******************************************************************************/
1497/* B u f f F r e e */
1498/******************************************************************************/
1499
1501
1502int XrdHttpProtocol::BuffFree() {
1503 return (myBuff->bsize - BuffUsed());
1504}
1505
1506/******************************************************************************/
1507/* B u f f C o n s u m e */
1508/******************************************************************************/
1509
1510void XrdHttpProtocol::BuffConsume(int blen) {
1511
1512 if (blen > myBuff->bsize) {
1513 TRACE(REQ, "internal error, BuffConsume(" << blen << ") smaller than buffsize");
1514 abort();
1515 }
1516
1517 if (blen > BuffUsed()) {
1518 TRACE(REQ, "internal error, BuffConsume(" << blen << ") larger than BuffUsed:" << BuffUsed());
1519 abort();
1520 }
1521
1522 myBuffStart = myBuffStart + blen;
1523
1524 if (myBuffStart >= myBuff->buff + myBuff->bsize)
1525 myBuffStart -= myBuff->bsize;
1526
1527 if (myBuffEnd >= myBuff->buff + myBuff->bsize)
1528 myBuffEnd -= myBuff->bsize;
1529
1530 if (BuffUsed() == 0)
1531 myBuffStart = myBuffEnd = myBuff->buff;
1532}
1533
1534/******************************************************************************/
1535/* B u f f g e t D a t a */
1536/******************************************************************************/
1537
1546int XrdHttpProtocol::BuffgetData(int blen, char **data, bool wait) {
1547 int rlen;
1548
1549 TRACE(DEBUG, "BuffgetData: requested " << blen << " bytes");
1550
1551
1552 if (wait) {
1553 // If there's not enough data in the buffer then wait on the socket until it comes
1554 if (blen > BuffUsed()) {
1555 TRACE(REQ, "BuffgetData: need to read " << blen - BuffUsed() << " bytes");
1556 if ( getDataOneShot(blen - BuffUsed(), true) )
1557 // The wanted data could not be read. Either timeout of connection closed
1558 return 0;
1559 }
1560 } else {
1561 // Get a peek at the socket, without waiting, if we have no data in the buffer
1562 if ( !BuffUsed() ) {
1563 if ( getDataOneShot(blen, false) )
1564 // The wanted data could not be read. Either timeout of connection closed
1565 return -1;
1566 }
1567 }
1568
1569 // And now make available the data taken from the buffer. Note that the buffer
1570 // may be empty...
1571 if (myBuffStart <= myBuffEnd) {
1572 rlen = std::min( (long) blen, (long)(myBuffEnd - myBuffStart) );
1573
1574 } else
1575 rlen = std::min( (long) blen, (long)(myBuff->buff + myBuff->bsize - myBuffStart) );
1576
1577 *data = myBuffStart;
1578 BuffConsume(rlen);
1579 return rlen;
1580}
1581
1582/******************************************************************************/
1583/* S e n d D a t a */
1584/******************************************************************************/
1585
1587
1588int XrdHttpProtocol::SendData(const char *body, int bodylen) {
1589
1590 int r;
1591
1592 if (body && bodylen) {
1593 TRACE(REQ, "Sending " << bodylen << " bytes");
1594 if (ishttps) {
1595 r = SSL_write(ssl, body, bodylen);
1596 if (r <= 0) {
1597 ERR_print_errors(sslbio_err);
1598 return -1;
1599 }
1600
1601 } else {
1602 r = Link->Send(body, bodylen);
1603 if (r <= 0) return -1;
1604 }
1605 }
1606
1607 return 0;
1608}
1609
1610/******************************************************************************/
1611/* S t a r t S i m p l e R e s p */
1612/******************************************************************************/
1613
1614int XrdHttpProtocol::StartSimpleResp(int code, const char *desc,
1615 const char *header_to_add,
1616 long long bodylen, bool keepalive) {
1617 static const std::unordered_map<int, std::string> statusTexts = {
1618 {100, "Continue"},
1619 {200, "OK"},
1620 {201, "Created"},
1621 {206, "Partial Content"},
1622 {302, "Redirect"},
1623 {307, "Temporary Redirect"},
1624 {400, "Bad Request"},
1625 {401, "Unauthorized"},
1626 {403, "Forbidden"},
1627 {404, "Not Found"},
1628 {405, "Method Not Allowed"},
1629 {409, "Conflict"},
1630 {416, "Range Not Satisfiable"},
1631 {423, "Locked"},
1632 {500, "Internal Server Error"},
1633 {502, "Bad Gateway"},
1634 {504, "Gateway Timeout"},
1635 {507, "Insufficient Storage"}};
1636
1637 std::stringstream ss;
1638 const std::string crlf = "\r\n";
1639
1640 ss << "HTTP/1.1 " << code << " ";
1641
1642 if (desc) {
1643 ss << desc;
1644 } else {
1645 auto it = statusTexts.find(code);
1646 if (it != statusTexts.end()) {
1647 ss << it->second;
1648 } else {
1649 ss << "Unknown";
1650 }
1651 }
1652 ss << crlf;
1653
1654 if (keepalive && (code != 100))
1655 ss << "Connection: Keep-Alive" << crlf;
1656 else
1657 ss << "Connection: Close" << crlf;
1658
1659 ss << "Server: XrootD/" << XrdVSTRING << crlf;
1660
1661 const auto iter = m_staticheaders.find(CurrentReq.requestverb);
1662 if (iter != m_staticheaders.end()) {
1663 ss << iter->second;
1664 } else {
1665 ss << m_staticheaders[""];
1666 }
1667
1668 if(xrdcors) {
1669 auto corsAllowOrigin = xrdcors->getCORSAllowOriginHeader(CurrentReq.m_origin);
1670 if(corsAllowOrigin) {
1671 ss << *corsAllowOrigin << crlf;
1672 }
1673 }
1674
1675 if ((bodylen >= 0) && (code != 100))
1676 ss << "Content-Length: " << bodylen << crlf;
1677
1678 if (header_to_add && (header_to_add[0] != '\0')) ss << header_to_add << crlf;
1679
1680 ss << crlf;
1681
1682 const std::string &outhdr = ss.str();
1683 TRACEI(RSP, "Sending resp: " << code << " header len:" << outhdr.size());
1684 if (SendData(outhdr.c_str(), outhdr.size()))
1685 return -1;
1686
1687 return 0;
1688}
1689
1690/******************************************************************************/
1691/* S t a r t C h u n k e d R e s p */
1692/******************************************************************************/
1693
1694int XrdHttpProtocol::StartChunkedResp(int code, const char *desc, const char *header_to_add, long long bodylen, bool keepalive) {
1695 const std::string crlf = "\r\n";
1696 std::stringstream ss;
1697
1698 if (header_to_add && (header_to_add[0] != '\0')) {
1699 ss << header_to_add << crlf;
1700 }
1701
1702 ss << "Transfer-Encoding: chunked";
1703 TRACEI(RSP, "Starting chunked response");
1704 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1705}
1706
1707/******************************************************************************/
1708/* C h u n k R e s p */
1709/******************************************************************************/
1710
1711int XrdHttpProtocol::ChunkResp(const char *body, long long bodylen) {
1712 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1713 if (ChunkRespHeader(content_length))
1714 return -1;
1715
1716 if (body && SendData(body, content_length))
1717 return -1;
1718
1719 return ChunkRespFooter();
1720}
1721
1722/******************************************************************************/
1723/* C h u n k R e s p H e a d e r */
1724/******************************************************************************/
1725
1726int XrdHttpProtocol::ChunkRespHeader(long long bodylen) {
1727 const std::string crlf = "\r\n";
1728 std::stringstream ss;
1729
1730 ss << std::hex << bodylen << std::dec << crlf;
1731
1732 const std::string &chunkhdr = ss.str();
1733 TRACEI(RSP, "Sending encoded chunk of size " << bodylen);
1734 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1735}
1736
1737/******************************************************************************/
1738/* C h u n k R e s p F o o t e r */
1739/******************************************************************************/
1740
1741int XrdHttpProtocol::ChunkRespFooter() {
1742 const std::string crlf = "\r\n";
1743 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1744}
1745
1746/******************************************************************************/
1747/* S e n d S i m p l e R e s p */
1748/******************************************************************************/
1749
1753
1754int XrdHttpProtocol::SendSimpleResp(int code, const char *desc, const char *header_to_add, const char *body, long long bodylen, bool keepalive) {
1755
1756 long long content_length = bodylen;
1757 if (bodylen <= 0) {
1758 content_length = body ? strlen(body) : 0;
1759 }
1760
1761 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1762 return -1;
1763
1764 //
1765 // Send the data
1766 //
1767 if (body)
1768 return SendData(body, content_length);
1769
1770 return 0;
1771}
1772
1773/******************************************************************************/
1774/* C o n f i g u r e */
1775/******************************************************************************/
1776
1778 /*
1779 Function: Establish configuration at load time.
1780
1781 Input: None.
1782
1783 Output: 0 upon success or !0 otherwise.
1784 */
1785
1786 char *rdf;
1787
1788 // Copy out the special info we want to use at top level
1789 //
1790 eDest.logger(pi->eDest->logger());
1791 XrdHttpTrace.SetLogger(pi->eDest->logger());
1792 // SI = new XrdXrootdStats(pi->Stats);
1793 Sched = pi->Sched;
1794 BPool = pi->BPool;
1795 xrd_cslist = getenv("XRD_CSLIST");
1796
1797 Port = pi->Port;
1798
1799 // Copy out the current TLS context
1800 //
1801 xrdctx = pi->tlsCtx;
1802
1803 {
1804 char buf[16];
1805 sprintf(buf, "%d", Port);
1806 Port_str = strdup(buf);
1807 }
1808
1809 // Now process and configuration parameters
1810 //
1811 rdf = (parms && *parms ? parms : pi->ConfigFN);
1812 if (rdf && Config(rdf, pi->theEnv)) return 0;
1813 if (pi->DebugON) XrdHttpTrace.What = TRACE_ALL;
1814
1815 // Set the redirect flag if we are a pure redirector
1817 if ((rdf = getenv("XRDROLE"))) {
1818 eDest.Emsg("Config", "XRDROLE: ", rdf);
1819
1820 if (!strcasecmp(rdf, "manager") || !strcasecmp(rdf, "supervisor")) {
1822 eDest.Emsg("Config", "Configured as HTTP(s) redirector.");
1823 } else {
1824
1825 eDest.Emsg("Config", "Configured as HTTP(s) data server.");
1826 }
1827
1828 } else {
1829 eDest.Emsg("Config", "No XRDROLE specified.");
1830 }
1831
1832 // Schedule protocol object cleanup
1833 //
1834 ProtStack.Set(pi->Sched, &XrdHttpTrace,
1835 (XrdHttpTrace.What & TRACE_MEM ? TRACE_MEM : 0));
1836 ProtStack.Set((pi->ConnMax / 3 ? pi->ConnMax / 3 : 30), 60 * 60);
1837
1838 // Return success
1839 //
1840
1841 return 1;
1842}
1843
1844/******************************************************************************/
1845/* p a r s e H e a d e r 2 C G I */
1846/******************************************************************************/
1847int XrdHttpProtocol::parseHeader2CGI(XrdOucStream &Config, XrdSysError & err,std::map<std::string, std::string> &header2cgi) {
1848 char *val, keybuf[1024], parmbuf[1024];
1849 char *parm;
1850
1851 // Get the header key
1852 val = Config.GetWord();
1853 if (!val || !val[0]) {
1854 err.Emsg("Config", "No headerkey specified.");
1855 return 1;
1856 } else {
1857
1858 // Trim the beginning, in place
1859 while ( *val && !isalnum(*val) ) val++;
1860 strcpy(keybuf, val);
1861
1862 // Trim the end, in place
1863 char *pp;
1864 pp = keybuf + strlen(keybuf) - 1;
1865 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1866 *pp = '\0';
1867 pp--;
1868 }
1869
1870 parm = Config.GetWord();
1871
1872 // Avoids segfault in case a key is given without value
1873 if(!parm || !parm[0]) {
1874 err.Emsg("Config", "No header2cgi value specified. key: '", keybuf, "'");
1875 return 1;
1876 }
1877
1878 // Trim the beginning, in place
1879 while ( *parm && !isalnum(*parm) ) parm++;
1880 strcpy(parmbuf, parm);
1881
1882 // Trim the end, in place
1883 pp = parmbuf + strlen(parmbuf) - 1;
1884 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1885 *pp = '\0';
1886 pp--;
1887 }
1888
1889 // Add this mapping to the map that will be used
1890 try {
1891 header2cgi[keybuf] = parmbuf;
1892 } catch ( ... ) {
1893 err.Emsg("Config", "Can't insert new header2cgi rule. key: '", keybuf, "'");
1894 return 1;
1895 }
1896
1897 }
1898 return 0;
1899}
1900
1901
1902/******************************************************************************/
1903/* I n i t T L S */
1904/******************************************************************************/
1905
1906bool XrdHttpProtocol::InitTLS() {
1907
1908 std::string eMsg;
1911
1912// Create a new TLS context
1913//
1914 if (sslverifydepth > 255) sslverifydepth = 255;
1916 //TLS_SET_REFINT will set the refresh interval in minutes, hence the division by 60
1919
1920// Make sure the context was created
1921//
1922 if (!xrdctx->isOK())
1923 {eDest.Say("Config failure: ", eMsg.c_str());
1924 return false;
1925 }
1926
1927// Setup session cache (this is controversial). The default is off but many
1928// programs expect it being enabled and break when it is disabled. In such
1929// cases it should be enabled. This is, of course, a big OpenSSL mess.
1930//
1931 static const char *sess_ctx_id = "XrdHTTPSessionCtx";
1932 unsigned int n =(unsigned int)(strlen(sess_ctx_id)+1);
1933 xrdctx->SessionCache(tlsCache, sess_ctx_id, n);
1934
1935// Set special ciphers if so specified.
1936//
1938 {eDest.Say("Config failure: ", "Unable to set allowable https ciphers!");
1939 return false;
1940 }
1941
1942// Enable or disable the config in the context
1944
1945// All done
1946//
1947 return true;
1948}
1949
1950/******************************************************************************/
1951/* C l e a n u p */
1952/******************************************************************************/
1953
1954void XrdHttpProtocol::Cleanup() {
1955
1956 TRACE(ALL, " Cleanup");
1957
1958 if (BPool && myBuff) {
1959 BuffConsume(BuffUsed());
1960 BPool->Release(myBuff);
1961 myBuff = 0;
1962 }
1963
1964 if (ssl) {
1965 // Shutdown the SSL/TLS connection
1966 // This triggers a bidirectional shutdown of the connection; the bidirectional
1967 // shutdown is useful to ensure that the client receives the server response;
1968 // a one-sided shutdown can result in the server sending a TCP reset packet, zapping
1969 // the contents of the TCP socket buffer on the client side. The HTTP 1.1 RFC has a
1970 // description of why this is important:
1971 // https://datatracker.ietf.org/doc/html/rfc9112#name-tls-connection-closure
1972 // Once we get the clean SSL shutdown message back from the client, we know that
1973 // the client has received the response and we can safely close the connection.
1974 int ret = SSL_shutdown(ssl);
1975 if (ret != 1) {
1976 if(ret == 0) {
1977 // ret == 0, the unidirectional shutdown was successful; wait for the acknowledgement.
1978 ret = SSL_shutdown(ssl);
1979 if (ret != 1) {
1980 TRACE(ALL, "SSL server failed to receive the SSL shutdown message from the client");
1981 ERR_print_errors(sslbio_err);
1982 }
1983 } else {
1984 //ret < 0, an error really happened.
1985 TRACE(ALL, "SSL server failed to send the shutdown message to the client");
1986 ERR_print_errors(sslbio_err);
1987 }
1988 }
1989
1990 if (secxtractor)
1991 secxtractor->FreeSSL(ssl);
1992
1993 SSL_free(ssl);
1994
1995 }
1996
1997
1998 ssl = 0;
1999 sbio = 0;
2000
2001 if (SecEntity.caps) free(SecEntity.caps);
2002 if (SecEntity.grps) free(SecEntity.grps);
2003 if (SecEntity.endorsements) free(SecEntity.endorsements);
2004 if (SecEntity.vorg) free(SecEntity.vorg);
2005 if (SecEntity.role) free(SecEntity.role);
2006 if (SecEntity.name) free(SecEntity.name);
2007 if (SecEntity.host) free(SecEntity.host);
2008 if (SecEntity.moninfo) free(SecEntity.moninfo);
2009
2010 SecEntity.Reset();
2011
2012 if (Addr_str) free(Addr_str);
2013 Addr_str = 0;
2014}
2015
2016/******************************************************************************/
2017/* R e s e t */
2018/******************************************************************************/
2019
2020void XrdHttpProtocol::Reset() {
2021
2022 TRACE(ALL, " Reset");
2023 Link = 0;
2024 CurrentReq.reset();
2025 CurrentReq.reqstate = 0;
2026
2027 if (myBuff) {
2028 BPool->Release(myBuff);
2029 myBuff = 0;
2030 }
2031 myBuffStart = myBuffEnd = 0;
2032
2033 DoingLogin = false;
2034 DoneSetInfo = false;
2035
2036 ResumeBytes = 0;
2037 Resume = 0;
2038
2039 //
2040 // numReads = 0;
2041 // numReadP = 0;
2042 // numReadV = 0;
2043 // numSegsV = 0;
2044 // numWrites = 0;
2045 // numFiles = 0;
2046 // cumReads = 0;
2047 // cumReadV = 0;
2048 // cumSegsV = 0;
2049 // cumWrites = 0;
2050 // totReadP = 0;
2051
2052 SecEntity.Reset();
2054 ishttps = false;
2055 ssldone = false;
2056
2057 Bridge = 0;
2058 ssl = 0;
2059 sbio = 0;
2060
2061}
2062
2063/******************************************************************************/
2064/* x h t t p s m o d e */
2065/******************************************************************************/
2066
2067/* Function: xhttpsmode
2068
2069 Purpose: To parse the directive: httpsmode {auto | disable | manual}
2070
2071 auto configure https if configured in xrd framework.
2072 disable do not configure https no matter what
2073 manual configure https and ignore the xrd framework
2074
2075 Output: 0 upon success or !0 upon failure.
2076 */
2077
2078int XrdHttpProtocol::xhttpsmode(XrdOucStream & Config) {
2079 char *val;
2080
2081 // Get the val
2082 //
2083 val = Config.GetWord();
2084 if (!val || !val[0]) {
2085 eDest.Emsg("Config", "httpsmode parameter not specified");
2086 return 1;
2087 }
2088
2089 // Record the val
2090 //
2091 if (!strcmp(val, "auto")) httpsmode = hsmAuto;
2092 else if (!strcmp(val, "disable")) httpsmode = hsmOff;
2093 else if (!strcmp(val, "manual")) httpsmode = hsmMan;
2094 else {eDest.Emsg("Config", "invalid httpsmode parameter - ", val);
2095 return 1;
2096 }
2097 return 0;
2098}
2099
2100/******************************************************************************/
2101/* x s s l v e r i f y d e p t h */
2102/******************************************************************************/
2103
2104/* Function: xsslverifydepth
2105
2106 Purpose: To parse the directive: sslverifydepth <depth>
2107
2108 <depth> the max depth of the ssl cert verification
2109
2110 Output: 0 upon success or !0 upon failure.
2111 */
2112
2113int XrdHttpProtocol::xsslverifydepth(XrdOucStream & Config) {
2114 char *val;
2115
2116 // Get the val
2117 //
2118 val = Config.GetWord();
2119 if (!val || !val[0]) {
2120 eDest.Emsg("Config", "sslverifydepth value not specified");
2121 return 1;
2122 }
2123
2124 // Record the val
2125 //
2126 sslverifydepth = atoi(val);
2127
2128 if (xrdctxVer){ HTTPS_ALERT("verifydepth","tlsca",false); }
2129 return 0;
2130}
2131
2132/******************************************************************************/
2133/* x s s l c e r t */
2134/******************************************************************************/
2135
2136/* Function: xsslcert
2137
2138 Purpose: To parse the directive: sslcert <path>
2139
2140 <path> the path of the server certificate to be used.
2141
2142 Output: 0 upon success or !0 upon failure.
2143 */
2144
2145int XrdHttpProtocol::xsslcert(XrdOucStream & Config) {
2146 char *val;
2147
2148 // Get the path
2149 //
2150 val = Config.GetWord();
2151 if (!val || !val[0]) {
2152 eDest.Emsg("Config", "HTTP X509 certificate not specified");
2153 return 1;
2154 }
2155
2156 // Record the path
2157 //
2158 if (sslcert) free(sslcert);
2159 sslcert = strdup(val);
2160
2161 // If we have an xrd context issue reminder
2162 //
2163 HTTPS_ALERT("cert","tls",true);
2164 return 0;
2165}
2166
2167/******************************************************************************/
2168/* x s s l k e y */
2169/******************************************************************************/
2170
2171/* Function: xsslkey
2172
2173 Purpose: To parse the directive: sslkey <path>
2174
2175 <path> the path of the server key to be used.
2176
2177 Output: 0 upon success or !0 upon failure.
2178 */
2179
2180int XrdHttpProtocol::xsslkey(XrdOucStream & Config) {
2181 char *val;
2182
2183 // Get the path
2184 //
2185 val = Config.GetWord();
2186 if (!val || !val[0]) {
2187 eDest.Emsg("Config", "HTTP X509 key not specified");
2188 return 1;
2189 }
2190
2191 // Record the path
2192 //
2193 if (sslkey) free(sslkey);
2194 sslkey = strdup(val);
2195
2196 HTTPS_ALERT("key","tls",true);
2197 return 0;
2198}
2199
2200/******************************************************************************/
2201/* x g m a p */
2202/******************************************************************************/
2203
2204/* Function: xgmap
2205
2206 Purpose: To parse the directive: gridmap [required] [compatNameGeneration] <path>
2207
2208 required optional parameter which if present treats any grimap errors
2209 as fatal.
2210 <path> the path of the gridmap file to be used. Normally it's
2211 /etc/grid-security/gridmap. No mapfile means no translation
2212 required. Pointing to a non existing mapfile is an error.
2213
2214 Output: 0 upon success or !0 upon failure.
2215 */
2216
2217int XrdHttpProtocol::xgmap(XrdOucStream & Config) {
2218 char *val;
2219
2220 // Get the path
2221 //
2222 val = Config.GetWord();
2223 if (!val || !val[0]) {
2224 eDest.Emsg("Config", "HTTP X509 gridmap file location not specified");
2225 return 1;
2226 }
2227
2228 // Handle optional parameter "required"
2229 //
2230 if (!strncmp(val, "required", 8)) {
2231 isRequiredGridmap = true;
2232 val = Config.GetWord();
2233
2234 if (!val || !val[0]) {
2235 eDest.Emsg("Config", "HTTP X509 gridmap file missing after [required] "
2236 "parameter");
2237 return 1;
2238 }
2239 }
2240
2241 // Handle optional parameter "compatNameGeneration"
2242 //
2243 if (!strcmp(val, "compatNameGeneration")) {
2244 compatNameGeneration = true;
2245 val = Config.GetWord();
2246 if (!val || !val[0]) {
2247 eDest.Emsg("Config", "HTTP X509 gridmap file missing after "
2248 "[compatNameGeneration] parameter");
2249 return 1;
2250 }
2251 }
2252
2253
2254 // Record the path
2255 //
2256 if (gridmap) free(gridmap);
2257 gridmap = strdup(val);
2258 return 0;
2259}
2260
2261/******************************************************************************/
2262/* x s s l c a f i l e */
2263/******************************************************************************/
2264
2265/* Function: xsslcafile
2266
2267 Purpose: To parse the directive: sslcafile <path>
2268
2269 <path> the path of the server key to be used.
2270
2271 Output: 0 upon success or !0 upon failure.
2272 */
2273
2274int XrdHttpProtocol::xsslcafile(XrdOucStream & Config) {
2275 char *val;
2276
2277 // Get the path
2278 //
2279 val = Config.GetWord();
2280 if (!val || !val[0]) {
2281 eDest.Emsg("Config", "HTTP X509 CAfile not specified");
2282 return 1;
2283 }
2284
2285 // Record the path
2286 //
2287 if (sslcafile) free(sslcafile);
2288 sslcafile = strdup(val);
2289
2290 if (xrdctxVer){ HTTPS_ALERT("cafile","tlsca",false); }
2291 return 0;
2292}
2293
2294/******************************************************************************/
2295/* x s e c r e t k e y */
2296/******************************************************************************/
2297
2298/* Function: xsecretkey
2299
2300 Purpose: To parse the directive: xsecretkey <key>
2301
2302 <key> the key to be used
2303
2304 Output: 0 upon success or !0 upon failure.
2305 */
2306
2307int XrdHttpProtocol::xsecretkey(XrdOucStream & Config) {
2308 char *val;
2309 bool inFile = false;
2310
2311 // Get the path
2312 //
2313 val = Config.GetWord();
2314 if (!val || !val[0]) {
2315 eDest.Emsg("Config", "Shared secret key not specified");
2316 return 1;
2317 }
2318
2319
2320 // If the token starts with a slash, then we interpret it as
2321 // the path to a file that contains the secretkey
2322 // otherwise, the token itself is the secretkey
2323 if (val[0] == '/') {
2324 struct stat st;
2325 inFile = true;
2326 int fd = open(val, O_RDONLY);
2327
2328 if ( fd == -1 ) {
2329 eDest.Emsg("Config", errno, "open shared secret key file", val);
2330 return 1;
2331 }
2332
2333 if ( fstat(fd, &st) != 0 ) {
2334 eDest.Emsg("Config", errno, "fstat shared secret key file", val);
2335 close(fd);
2336 return 1;
2337 }
2338
2339 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2340 eDest.Emsg("Config",
2341 "For your own security, the shared secret key file cannot be world readable or group writable '", val, "'");
2342 close(fd);
2343 return 1;
2344 }
2345
2346 FILE *fp = fdopen(fd, "r");
2347
2348 if ( fp == nullptr ) {
2349 eDest.Emsg("Config", errno, "fdopen shared secret key file", val);
2350 close(fd);
2351 return 1;
2352 }
2353
2354 char line[1024];
2355 while( fgets(line, 1024, fp) ) {
2356 char *pp;
2357
2358 // Trim the end
2359 pp = line + strlen(line) - 1;
2360 while ( (pp >= line) && (!isalnum(*pp)) ) {
2361 *pp = '\0';
2362 pp--;
2363 }
2364
2365 // Trim the beginning
2366 pp = line;
2367 while ( *pp && !isalnum(*pp) ) pp++;
2368
2369 if ( strlen(pp) >= 32 ) {
2370 eDest.Say("Config", "Secret key loaded.");
2371 // Record the path
2372 if (secretkey) free(secretkey);
2373 secretkey = strdup(pp);
2374
2375 fclose(fp);
2376 return 0;
2377 }
2378
2379 }
2380
2381 fclose(fp);
2382 eDest.Emsg("Config", "Cannot find useful secretkey in file '", val, "'");
2383 return 1;
2384
2385 }
2386
2387 if ( strlen(val) < 32 ) {
2388 eDest.Emsg("Config", "Secret key is too short");
2389 return 1;
2390 }
2391
2392 // Record the path
2393 if (secretkey) free(secretkey);
2394 secretkey = strdup(val);
2395 if (!inFile) Config.noEcho();
2396
2397 return 0;
2398}
2399
2400/******************************************************************************/
2401/* x l i s t d e n y */
2402/******************************************************************************/
2403
2404/* Function: xlistdeny
2405
2406 Purpose: To parse the directive: listingdeny <yes|no|0|1>
2407
2408 <val> makes this redirector deny listings with an error
2409
2410 Output: 0 upon success or !0 upon failure.
2411 */
2412
2413int XrdHttpProtocol::xlistdeny(XrdOucStream & Config) {
2414 char *val;
2415
2416 // Get the path
2417 //
2418 val = Config.GetWord();
2419 if (!val || !val[0]) {
2420 eDest.Emsg("Config", "listingdeny flag not specified");
2421 return 1;
2422 }
2423
2424 // Record the value
2425 //
2426 listdeny = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2427
2428
2429 return 0;
2430}
2431
2432/******************************************************************************/
2433/* x l i s t r e d i r */
2434/******************************************************************************/
2435
2436/* Function: xlistredir
2437
2438 Purpose: To parse the directive: listingredir <Url>
2439
2440 <Url> http/https server to redirect to in the case of listing
2441
2442 Output: 0 upon success or !0 upon failure.
2443 */
2444
2445int XrdHttpProtocol::xlistredir(XrdOucStream & Config) {
2446 char *val;
2447
2448 // Get the path
2449 //
2450 val = Config.GetWord();
2451 if (!val || !val[0]) {
2452 eDest.Emsg("Config", "listingredir flag not specified");
2453 return 1;
2454 }
2455
2456 // Record the value
2457 //
2458 if (listredir) free(listredir);
2459 listredir = strdup(val);
2460
2461
2462 return 0;
2463}
2464
2465/******************************************************************************/
2466/* x s s l d e s t h t t p s */
2467/******************************************************************************/
2468
2469/* Function: xdesthttps
2470
2471 Purpose: To parse the directive: desthttps <yes|no|0|1>
2472
2473 <val> makes this redirector produce http or https redirection targets
2474
2475 Output: 0 upon success or !0 upon failure.
2476 */
2477
2478int XrdHttpProtocol::xdesthttps(XrdOucStream & Config) {
2479 char *val;
2480
2481 // Get the path
2482 //
2483 val = Config.GetWord();
2484 if (!val || !val[0]) {
2485 eDest.Emsg("Config", "desthttps flag not specified");
2486 return 1;
2487 }
2488
2489 // Record the value
2490 //
2491 isdesthttps = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2492
2493
2494 return 0;
2495}
2496
2497/******************************************************************************/
2498/* x e m b e d d e d s t a t i c */
2499/******************************************************************************/
2500
2501/* Function: xembeddedstatic
2502
2503 Purpose: To parse the directive: embeddedstatic <yes|no|0|1|true|false>
2504
2505 <val> this server will redirect HTTPS to itself using HTTP+token
2506
2507 Output: 0 upon success or !0 upon failure.
2508 */
2509
2510int XrdHttpProtocol::xembeddedstatic(XrdOucStream & Config) {
2511 char *val;
2512
2513 // Get the path
2514 //
2515 val = Config.GetWord();
2516 if (!val || !val[0]) {
2517 eDest.Emsg("Config", "embeddedstatic flag not specified");
2518 return 1;
2519 }
2520
2521 // Record the value
2522 //
2523 embeddedstatic = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2524
2525
2526 return 0;
2527}
2528
2529/******************************************************************************/
2530/* x r e d i r s t a t i c */
2531/******************************************************************************/
2532
2533/* Function: xstaticredir
2534
2535 Purpose: To parse the directive: staticredir <Url>
2536
2537 <Url> http/https server to redirect to in the case of /static
2538
2539 Output: 0 upon success or !0 upon failure.
2540 */
2541
2542int XrdHttpProtocol::xstaticredir(XrdOucStream & Config) {
2543 char *val;
2544
2545 // Get the path
2546 //
2547 val = Config.GetWord();
2548 if (!val || !val[0]) {
2549 eDest.Emsg("Config", "staticredir url not specified");
2550 return 1;
2551 }
2552
2553 // Record the value
2554 //
2555 if (staticredir) free(staticredir);
2556 staticredir = strdup(val);
2557
2558 return 0;
2559}
2560
2561/******************************************************************************/
2562/* x p r e l o a d s t a t i c */
2563/******************************************************************************/
2564
2565/* Function: xpreloadstatic
2566
2567 Purpose: To parse the directive: preloadstatic <http url path> <local file>
2568
2569 <http url path> http/http path whose response we are preloading
2570 e.g. /static/mycss.css
2571 NOTE: this must start with /static
2572
2573
2574 Output: 0 upon success or !0 upon failure.
2575 */
2576
2577int XrdHttpProtocol::xstaticpreload(XrdOucStream & Config) {
2578 char *val, *k, key[1024];
2579
2580 // Get the key
2581 //
2582 k = Config.GetWord();
2583 if (!k || !k[0]) {
2584 eDest.Emsg("Config", "preloadstatic urlpath not specified");
2585 return 1;
2586 }
2587
2588 strcpy(key, k);
2589
2590 // Get the val
2591 //
2592 val = Config.GetWord();
2593 if (!val || !val[0]) {
2594 eDest.Emsg("Config", "preloadstatic filename not specified");
2595 return 1;
2596 }
2597
2598 // Try to load the file into memory
2599 int fp = open(val, O_RDONLY);
2600 if( fp < 0 ) {
2601 eDest.Emsg("Config", errno, "open preloadstatic filename", val);
2602 return 1;
2603 }
2604
2606 // Max 64Kb ok?
2607 nfo->data = (char *)malloc(65536);
2608 nfo->len = read(fp, (void *)nfo->data, 65536);
2609 close(fp);
2610
2611 if (nfo->len <= 0) {
2612 eDest.Emsg("Config", errno, "read from preloadstatic filename", val);
2613 return 1;
2614 }
2615
2616 if (nfo->len >= 65536) {
2617 eDest.Emsg("Config", "Truncated preloadstatic filename. Max is 64 KB '", val, "'");
2618 return 1;
2619 }
2620
2621 // Record the value
2622 //
2623 if (!staticpreload)
2624 staticpreload = new XrdOucHash<StaticPreloadInfo>;
2625
2626 staticpreload->Rep((const char *)key, nfo);
2627 return 0;
2628}
2629
2630/******************************************************************************/
2631/* x s t a t i c h e a d e r */
2632/******************************************************************************/
2633
2634//
2635// xstaticheader parses the http.staticheader director with the following syntax:
2636//
2637// http.staticheader [-verb=[GET|HEAD|...]]* header [value]
2638//
2639// When set, this will cause XrdHttp to always return the specified header and
2640// value.
2641//
2642// Setting this option multiple times is additive (multiple headers may be set).
2643// Omitting the value will cause the static header setting to be unset.
2644//
2645// Omitting the -verb argument will cause it the header to be set unconditionally
2646// for all requests.
2647int XrdHttpProtocol::xstaticheader(XrdOucStream & Config) {
2648 auto val = Config.GetWord();
2649 std::vector<std::string> verbs;
2650 while (true) {
2651 if (!val || !val[0]) {
2652 eDest.Emsg("Config", "http.staticheader requires the header to be specified");
2653 return 1;
2654 }
2655
2656 std::string match_verb;
2657 std::string_view val_str(val);
2658 if (val_str.substr(0, 6) == "-verb=") {
2659 verbs.emplace_back(val_str.substr(6));
2660 } else if (val_str == "-") {
2661 eDest.Emsg("Config", "http.staticheader is ignoring unknown flag: ", val_str.data());
2662 } else {
2663 break;
2664 }
2665
2666 val = Config.GetWord();
2667 }
2668 if (verbs.empty()) {
2669 verbs.emplace_back();
2670 }
2671
2672 std::string header = val;
2673
2674 val = Config.GetWord();
2675 std::string header_value;
2676 if (val && val[0]) {
2677 header_value = val;
2678 }
2679
2680 for (const auto &verb : verbs) {
2681 auto iter = m_staticheader_map.find(verb);
2682 if (iter == m_staticheader_map.end()) {
2683 if (!header_value.empty())
2684 m_staticheader_map.insert(iter, {verb, {{header, header_value}}});
2685 } else if (header_value.empty()) {
2686 iter->second.clear();
2687 } else {
2688 iter->second.emplace_back(header, header_value);
2689 }
2690 }
2691
2692 return 0;
2693}
2694
2695
2696/******************************************************************************/
2697/* x s e l f h t t p s 2 h t t p */
2698/******************************************************************************/
2699
2700/* Function: selfhttps2http
2701
2702 Purpose: To parse the directive: selfhttps2http <yes|no|0|1>
2703
2704 <val> this server will redirect HTTPS to itself using HTTP+token
2705
2706 Output: 0 upon success or !0 upon failure.
2707 */
2708
2709int XrdHttpProtocol::xselfhttps2http(XrdOucStream & Config) {
2710 char *val;
2711
2712 // Get the path
2713 //
2714 val = Config.GetWord();
2715 if (!val || !val[0]) {
2716 eDest.Emsg("Config", "selfhttps2http flag not specified");
2717 return 1;
2718 }
2719
2720 // Record the value
2721 //
2722 selfhttps2http = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2723
2724
2725 return 0;
2726}
2727
2728/******************************************************************************/
2729/* x s e c x t r a c t o r */
2730/******************************************************************************/
2731
2732/* Function: xsecxtractor
2733
2734 Purpose: To parse the directive: secxtractor [required] <path> <params>
2735
2736 required optional parameter which if present treats any secxtractor
2737 errors as fatal.
2738 <path> the path of the plugin to be loaded
2739 <params> parameters passed to the secxtractor library
2740
2741 Output: 0 upon success or !0 upon failure.
2742 */
2743
2744int XrdHttpProtocol::xsecxtractor(XrdOucStream& Config) {
2745 char *val;
2746
2747 // Get the path
2748 //
2749 val = Config.GetWord();
2750 if (!val || !val[0]) {
2751 eDest.Emsg("Config", "No security extractor plugin specified.");
2752 return 1;
2753 } else {
2754 // Handle optional parameter [required]
2755 //
2756 if (!strncmp(val, "required", 8)) {
2757 isRequiredXtractor = true;
2758 val = Config.GetWord();
2759
2760 if (!val || !val[0]) {
2761 eDest.Emsg("Config", "No security extractor plugin after [required] "
2762 "parameter");
2763 return 1;
2764 }
2765 }
2766
2767 char libName[4096];
2768 strlcpy(libName, val, sizeof(libName));
2769 libName[sizeof(libName) - 1] = '\0';
2770 char libParms[4096];
2771
2772 if (!Config.GetRest(libParms, 4095)) {
2773 eDest.Emsg("Config", "secxtractor config params longer than 4k");
2774 return 1;
2775 }
2776
2777 // Try to load the plugin (if available) that extracts info from the
2778 // user cert/proxy
2779 if (LoadSecXtractor(&eDest, libName, libParms)) {
2780 return 1;
2781 }
2782 }
2783
2784 return 0;
2785}
2786
2787int XrdHttpProtocol::xcors(XrdOucStream& Config) {
2788 char * val;
2789 // Get the path
2790 val = Config.GetWord();
2791 if (!val || !val[0]) {
2792 eDest.Emsg("Config", "No CORS plugin specified.");
2793 return 1;
2794 }
2795 xrdcorsLibPath = val;
2796 return 0;
2797}
2798
2799/******************************************************************************/
2800/* x e x t h a n d l e r */
2801/******************************************************************************/
2802
2803/* Function: xexthandler
2804 *
2805 * Purpose: To parse the directive: exthandler <name> <path> <initparm>
2806 *
2807 * <name> a unique name (max 16chars) to be given to this
2808 * instance, e.g 'myhandler1'
2809 * <path> the path of the plugin to be loaded
2810 * <initparm> a string parameter (e.g. a config file) that is
2811 * passed to the initialization of the plugin
2812 *
2813 * Output: 0 upon success or !0 upon failure.
2814 */
2815
2816int XrdHttpProtocol::xexthandler(XrdOucStream &Config,
2817 std::vector<extHInfo> &hiVec) {
2818 char *val, path[1024], namebuf[1024];
2819 char *parm;
2820 // By default, every external handler need TLS configured to be loaded
2821 bool noTlsOK = false;
2822
2823 // Get the name
2824 //
2825 val = Config.GetWord();
2826 if (!val || !val[0]) {
2827 eDest.Emsg("Config", "No instance name specified for an http external handler plugin.");
2828 return 1;
2829 }
2830 if (strlen(val) >= 16) {
2831 eDest.Emsg("Config", "Instance name too long for an http external handler plugin.");
2832 return 1;
2833 }
2834 strncpy(namebuf, val, sizeof(namebuf));
2835 namebuf[ sizeof(namebuf)-1 ] = '\0';
2836
2837 // Get the +notls option if it was provided
2838 val = Config.GetWord();
2839
2840 if(val && !strcmp("+notls",val)) {
2841 noTlsOK = true;
2842 val = Config.GetWord();
2843 }
2844
2845 // Get the path
2846 //
2847 if (!val || !val[0]) {
2848 eDest.Emsg("Config", "No http external handler plugin specified.");
2849 return 1;
2850 }
2851 if (strlen(val) >= (int)sizeof(path)) {
2852 eDest.Emsg("Config", "Path too long for an http external handler plugin.");
2853 return 1;
2854 }
2855
2856 strcpy(path, val);
2857
2858 // Everything else is a free string
2859 //
2860 parm = Config.GetWord();
2861
2862 // Verify whether this is a duplicate (we never supported replacements)
2863 //
2864 for (int i = 0; i < (int)hiVec.size(); i++)
2865 {if (hiVec[i].extHName == namebuf) {
2866 eDest.Emsg("Config", "Instance name already present for "
2867 "http external handler plugin",
2868 hiVec[i].extHPath.c_str());
2869 return 1;
2870 }
2871 }
2872
2873 // Verify that we don't have more already than we are allowed to have
2874 //
2875 if (hiVec.size() >= MAX_XRDHTTPEXTHANDLERS) {
2876 eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
2877 return 1;
2878 }
2879
2880 // Create an info struct and push it on the list of ext handlers to load
2881 //
2882 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm : ""), noTlsOK));
2883
2884 return 0;
2885}
2886
2887/******************************************************************************/
2888/* x h e a d e r 2 c g i */
2889/******************************************************************************/
2890
2891/* Function: xheader2cgi
2892 *
2893 * Purpose: To parse the directive: header2cgi <headerkey> <cgikey>
2894 *
2895 * <headerkey> the name of an incoming HTTP header
2896 * to be transformed
2897 * <cgikey> the name to be given when adding it to the cgi info
2898 * that is kept only internally
2899 *
2900 * Output: 0 upon success or !0 upon failure.
2901 */
2902
2903int XrdHttpProtocol::xheader2cgi(XrdOucStream & Config) {
2904 return parseHeader2CGI(Config,eDest,hdr2cgimap);
2905}
2906
2907/******************************************************************************/
2908/* x s s l c a d i r */
2909/******************************************************************************/
2910
2911/* Function: xsslcadir
2912
2913 Purpose: To parse the directive: sslcadir <path>
2914
2915 <path> the path of the server key to be used.
2916
2917 Output: 0 upon success or !0 upon failure.
2918 */
2919
2920int XrdHttpProtocol::xsslcadir(XrdOucStream & Config) {
2921 char *val;
2922
2923 // Get the path
2924 //
2925 val = Config.GetWord();
2926 if (!val || !val[0]) {
2927 eDest.Emsg("Config", "HTTP X509 CAdir not specified");
2928 return 1;
2929 }
2930
2931 // Record the path
2932 //
2933 if (sslcadir) free(sslcadir);
2934 sslcadir = strdup(val);
2935
2936 if (xrdctxVer){ HTTPS_ALERT("cadir","tlsca",false); }
2937 return 0;
2938}
2939
2940/******************************************************************************/
2941/* x s s l c i p h e r f i l t e r */
2942/******************************************************************************/
2943
2944/* Function: xsslcipherfilter
2945
2946 Purpose: To parse the directive: cipherfilter <filter>
2947
2948 <filter> the filter string to be used when generating
2949 the SSL cipher list
2950
2951 Output: 0 upon success or !0 upon failure.
2952 */
2953
2954int XrdHttpProtocol::xsslcipherfilter(XrdOucStream & Config) {
2955 char *val;
2956
2957 // Get the filter string
2958 //
2959 val = Config.GetWord();
2960 if (!val || !val[0]) {
2961 eDest.Emsg("Config", "SSL cipherlist filter string not specified");
2962 return 1;
2963 }
2964
2965 // Record the filter string
2966 //
2968 sslcipherfilter = strdup(val);
2969
2970 return 0;
2971}
2972
2973/******************************************************************************/
2974/* x t l s r e u s e */
2975/******************************************************************************/
2976
2977/* Function: xtlsreuse
2978
2979 Purpose: To parse the directive: tlsreuse {on | off}
2980
2981 Output: 0 upon success or 1 upon failure.
2982 */
2983
2984int XrdHttpProtocol::xtlsreuse(XrdOucStream & Config) {
2985
2986 char *val;
2987
2988// Get the argument
2989//
2990 val = Config.GetWord();
2991 if (!val || !val[0])
2992 {eDest.Emsg("Config", "tlsreuse argument not specified"); return 1;}
2993
2994// If it's off, we set it off
2995//
2996 if (!strcmp(val, "off"))
2998 return 0;
2999 }
3000
3001// If it's on we set it on.
3002//
3003 if (!strcmp(val, "on"))
3005 return 0;
3006 }
3007
3008// Bad argument
3009//
3010 eDest.Emsg("config", "invalid tlsreuse parameter -", val);
3011 return 1;
3012}
3013
3014int XrdHttpProtocol::xtlsclientauth(XrdOucStream &Config) {
3015 auto val = Config.GetWord();
3016 if (!val || !val[0])
3017 {eDest.Emsg("Config", "tlsclientauth argument not specified"); return 1;}
3018
3019 if (!strcmp(val, "off"))
3020 {tlsClientAuth = false;
3021 return 0;
3022 }
3023 if (!strcmp(val, "on"))
3024 {tlsClientAuth = true;
3025 return 0;
3026 }
3027
3028 eDest.Emsg("config", "invalid tlsclientauth parameter -", val);
3029 return 1;
3030}
3031
3032int XrdHttpProtocol::xauth(XrdOucStream &Config) {
3033 char *val = Config.GetWord();
3034 if(val) {
3035 if(!strcmp("tpc",val)) {
3036 if(!(val = Config.GetWord())) {
3037 eDest.Emsg("Config", "http.auth tpc value not specified."); return 1;
3038 } else {
3039 if(!strcmp("fcreds",val)) {
3040 tpcForwardCreds = true;
3041 } else {
3042 eDest.Emsg("Config", "http.auth tpc value is invalid"); return 1;
3043 }
3044 }
3045 } else {
3046 eDest.Emsg("Config", "http.auth value is invalid"); return 1;
3047 }
3048 }
3049 return 0;
3050}
3051
3052int XrdHttpProtocol::xmaxdelay(XrdOucStream &Config) {
3053 char *val = Config.GetWord();
3054 if(val) {
3055 int maxdelay;
3056 if (XrdOuca2x::a2tm(eDest, "http.maxdelay", val, &maxdelay, 1)) return 1;
3057 m_maxdelay = maxdelay;
3058 } else {
3059 eDest.Emsg("Config", "http.maxdelay requires an argument in seconds (default is 30). Example: http.maxdelay 30");
3060 return 1;
3061 }
3062 return 0;
3063}
3064
3065/******************************************************************************/
3066/* x t r a c e */
3067/******************************************************************************/
3068
3069/* Function: xtrace
3070
3071 Purpose: To parse the directive: trace <events>
3072
3073 <events> the blank separated list of events to trace. Trace
3074 directives are cumulative.
3075
3076 Output: 0 upon success or 1 upon failure.
3077 */
3078
3079int XrdHttpProtocol::xtrace(XrdOucStream & Config) {
3080
3081 char *val;
3082
3083 static struct traceopts {
3084 const char *opname;
3085 int opval;
3086 } tropts[] = {
3087 {"all", TRACE_ALL},
3088 {"auth", TRACE_AUTH},
3089 {"debug", TRACE_DEBUG},
3090 {"mem", TRACE_MEM},
3091 {"redirect", TRACE_REDIR},
3092 {"request", TRACE_REQ},
3093 {"response", TRACE_RSP}
3094 };
3095 int i, neg, trval = 0, numopts = sizeof (tropts) / sizeof (struct traceopts);
3096
3097 if (!(val = Config.GetWord())) {
3098 eDest.Emsg("config", "trace option not specified");
3099 return 1;
3100 }
3101 while (val) {
3102 if (!strcmp(val, "off")) trval = 0;
3103 else {
3104 if ((neg = (val[0] == '-' && val[1]))) val++;
3105 for (i = 0; i < numopts; i++) {
3106 if (!strcmp(val, tropts[i].opname)) {
3107 if (neg) trval &= ~tropts[i].opval;
3108 else trval |= tropts[i].opval;
3109 break;
3110 }
3111 }
3112 if (i >= numopts)
3113 eDest.Emsg("config", "invalid trace option", val);
3114 }
3115 val = Config.GetWord();
3116 }
3117 XrdHttpTrace.What = trval;
3118 return 0;
3119}
3120
3121int XrdHttpProtocol::doStat(char *fname) {
3122 int l;
3123 bool b;
3124 CurrentReq.filesize = 0;
3125 CurrentReq.fileflags = 0;
3126 CurrentReq.filemodtime = 0;
3127
3128 memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3129 CurrentReq.xrdreq.stat.requestid = htons(kXR_stat);
3130 memset(CurrentReq.xrdreq.stat.reserved, 0,
3131 sizeof (CurrentReq.xrdreq.stat.reserved));
3132 l = strlen(fname) + 1;
3133 CurrentReq.xrdreq.stat.dlen = htonl(l);
3134
3135 if (!Bridge) return -1;
3136 b = Bridge->Run((char *) &CurrentReq.xrdreq, fname, l);
3137 if (!b) {
3138 return -1;
3139 }
3140
3141
3142 return 0;
3143}
3144
3145/******************************************************************************/
3146/* d o C h k s u m */
3147/******************************************************************************/
3148
3150 size_t length;
3151 memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3152 CurrentReq.xrdreq.query.requestid = htons(kXR_query);
3153 CurrentReq.xrdreq.query.infotype = htons(kXR_Qcksum);
3154 memset(CurrentReq.xrdreq.query.reserved1, '\0', sizeof(CurrentReq.xrdreq.query.reserved1));
3155 memset(CurrentReq.xrdreq.query.fhandle, '\0', sizeof(CurrentReq.xrdreq.query.fhandle));
3156 memset(CurrentReq.xrdreq.query.reserved2, '\0', sizeof(CurrentReq.xrdreq.query.reserved2));
3157 length = fname.length() + 1;
3158 CurrentReq.xrdreq.query.dlen = htonl(length);
3159
3160 if (!Bridge) return -1;
3161
3162 return Bridge->Run(reinterpret_cast<char *>(&CurrentReq.xrdreq), const_cast<char *>(fname.c_str()), length) ? 0 : -1;
3163}
3164
3165
3166static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION);
3167
3168// Loads the SecXtractor plugin, if available
3169int XrdHttpProtocol::LoadSecXtractor(XrdSysError *myeDest, const char *libName,
3170 const char *libParms) {
3171
3172
3173 // We don't want to load it more than once
3174 if (secxtractor) return 1;
3175
3176 XrdOucPinLoader myLib(myeDest, &compiledVer, "secxtractorlib", libName);
3178
3179 // Get the entry point of the object creator
3180 //
3181 ep = (XrdHttpSecXtractor *(*)(XrdHttpSecXtractorArgs))(myLib.Resolve("XrdHttpGetSecXtractor"));
3182 if (ep && (secxtractor = ep(myeDest, NULL, libParms))) return 0;
3183 myLib.Unload();
3184 return 1;
3185}
3186/******************************************************************************/
3187/* L o a d E x t H a n d l e r */
3188/******************************************************************************/
3189
3190int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec, const char *cFN, XrdOucEnv &myEnv) {
3191 for (int i = 0; i < (int) hiVec.size(); i++) {
3192 if(hiVec[i].extHNoTlsOK) {
3193 // The external plugin does not need TLS to be loaded
3194 if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3195 hiVec[i].extHParm.c_str(), &myEnv,
3196 hiVec[i].extHName.c_str()))
3197 return 1;
3198 }
3199 }
3200 return 0;
3201}
3202
3203int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3204 const char *cFN, XrdOucEnv &myEnv) {
3205
3206 // Add the pointer to the cadir and the cakey to the environment.
3207 //
3208 if (sslcadir) myEnv.Put("http.cadir", sslcadir);
3209 if (sslcafile) myEnv.Put("http.cafile", sslcafile);
3210 if (sslcert) myEnv.Put("http.cert", sslcert);
3211 if (sslkey) myEnv.Put("http.key" , sslkey);
3212
3213 // Load all of the specified external handlers.
3214 //
3215 for (int i = 0; i < (int)hiVec.size(); i++) {
3216 // Only load the external handlers that were not already loaded
3217 // by LoadExtHandlerNoTls(...)
3218 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3219 if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3220 hiVec[i].extHParm.c_str(), &myEnv,
3221 hiVec[i].extHName.c_str())) return 1;
3222 }
3223 }
3224 return 0;
3225}
3226
3227// Loads the external handler plugin, if available
3228int XrdHttpProtocol::LoadExtHandler(XrdSysError *myeDest, const char *libName,
3229 const char *configFN, const char *libParms,
3230 XrdOucEnv *myEnv, const char *instName) {
3231
3232
3233 // This function will avoid loading doubles. No idea why this happens
3234 if (ExtHandlerLoaded(instName)) {
3235 eDest.Emsg("Config", "Instance name already present for an http external handler plugin.");
3236 return 1;
3237 }
3238 if (exthandlercnt >= MAX_XRDHTTPEXTHANDLERS) {
3239 eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
3240 return 1;
3241 }
3242
3243 XrdOucPinLoader myLib(myeDest, &compiledVer, "exthandlerlib", libName);
3244 XrdHttpExtHandler *(*ep)(XrdHttpExtHandlerArgs);
3245
3246 // Get the entry point of the object creator
3247 //
3248 ep = (XrdHttpExtHandler *(*)(XrdHttpExtHandlerArgs))(myLib.Resolve("XrdHttpGetExtHandler"));
3249
3250 XrdHttpExtHandler *newhandler;
3251 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3252
3253 // Handler has been loaded, it's the last one in the list
3254 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3255 exthandler[exthandlercnt].name[15] = '\0';
3256 exthandler[exthandlercnt++].ptr = newhandler;
3257
3258 return 0;
3259 }
3260
3261 myLib.Unload();
3262 return 1;
3263}
3264
3265
3266int XrdHttpProtocol::LoadCorsHandler(XrdSysError *eDest, const char *libname) {
3267 if(xrdcors) return 1;
3268 XrdOucPinLoader corsLib(eDest, &compiledVer, "corslib",libname);
3269 XrdHttpCors *(*ep)(XrdHttpCorsGetHandlerArgs);
3270 ep = (XrdHttpCors *(*)(XrdHttpCorsGetHandlerArgs))(corsLib.Resolve("XrdHttpCorsGetHandler"));
3271 if(ep && (xrdcors = ep())) return 0;
3272 corsLib.Unload();
3273 return 1;
3274}
3275
3276// Tells if we have already loaded a certain exthandler. Try to
3277// privilege speed, as this func may be invoked pretty often
3278bool XrdHttpProtocol::ExtHandlerLoaded(const char *handlername) {
3279 for (int i = 0; i < exthandlercnt; i++) {
3280 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3281 return true;
3282 }
3283 }
3284 return false;
3285}
3286
3287// Locates a matching external handler for a given request, if available. Try to
3288// privilege speed, as this func is invoked for every incoming request
3289XrdHttpExtHandler * XrdHttpProtocol::FindMatchingExtHandler(const XrdHttpReq &req) {
3290
3291 for (int i = 0; i < exthandlercnt; i++) {
3292 if (exthandler[i].ptr->MatchesPath(req.requestverb.c_str(), req.resource.c_str())) {
3293 return exthandler[i].ptr;
3294 }
3295 }
3296 return NULL;
3297}
#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)
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:204
char * Get(const char *varname)
Definition XrdOucEnv.hh:69
void * GetPtr(const char *varname)
Definition XrdOucEnv.cc:263
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.