XRootD
Loading...
Searching...
No Matches
XrdHttpProtocol.cc
Go to the documentation of this file.
1//------------------------------------------------------------------------------
2// This file is part of XrdHTTP: A pragmatic implementation of the
3// HTTP/WebDAV protocol for the Xrootd framework
4//
5// Copyright (c) 2013 by European Organization for Nuclear Research (CERN)
6// Author: Fabrizio Furano <furano@cern.ch>
7// File Date: Nov 2012
8//------------------------------------------------------------------------------
9// XRootD is free software: you can redistribute it and/or modify
10// it under the terms of the GNU Lesser General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// XRootD is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17// GNU General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public License
20// along with XRootD. If not, see <http://www.gnu.org/licenses/>.
21//------------------------------------------------------------------------------
22
23
24#include "XrdVersion.hh"
25
26#include "Xrd/XrdBuffer.hh"
27#include "Xrd/XrdLink.hh"
29#include "XrdOuc/XrdOuca2x.hh"
31#include "XrdOuc/XrdOucEnv.hh"
32#include "XrdOuc/XrdOucGMap.hh"
33#include "XrdSys/XrdSysE2T.hh"
34#include "XrdSys/XrdSysTimer.hh"
36#include "XrdHttpTrace.hh"
37#include "XrdHttpProtocol.hh"
38
39#include <sys/stat.h>
40#include "XrdHttpUtils.hh"
41#include "XrdHttpSecXtractor.hh"
42#include "XrdHttpExtHandler.hh"
43
44#include "XrdTls/XrdTls.hh"
46#include "XrdOuc/XrdOucUtils.hh"
49
50#include <charconv>
51#include <openssl/err.h>
52#include <openssl/ssl.h>
53#include <vector>
54#include <arpa/inet.h>
55#include <sstream>
56#include <cctype>
57#include <sys/stat.h>
58#include <fcntl.h>
59#include <algorithm>
60
61#define XRHTTP_TK_GRACETIME 600
62
63
64/******************************************************************************/
65/* G l o b a l s */
66/******************************************************************************/
67
68// It seems that eos needs this to be present
69const char *XrdHttpSecEntityTident = "http";
70
71//
72// Static stuff
73//
74
76int XrdHttpProtocol::readWait = 300000;
77int XrdHttpProtocol::Port = 1094;
79
80//XrdXrootdStats *XrdHttpProtocol::SI = 0;
87bool XrdHttpProtocol::listdeny = false;
92
98
103BIO *XrdHttpProtocol::sslbio_err = 0;
104XrdHttpSecXtractor *XrdHttpProtocol::secxtractor = 0;
105bool XrdHttpProtocol::isRequiredXtractor = false;
106struct XrdHttpProtocol::XrdHttpExtHandlerInfo XrdHttpProtocol::exthandler[MAX_XRDHTTPEXTHANDLERS];
109int XrdHttpProtocol::exthandlercnt = 0;
110std::map< std::string, std::string > XrdHttpProtocol::hdr2cgimap;
111
112bool XrdHttpProtocol::usingEC = false;
113bool XrdHttpProtocol::hasCache= false;
114
115XrdScheduler *XrdHttpProtocol::Sched = 0; // System scheduler
116XrdBuffManager *XrdHttpProtocol::BPool = 0; // Buffer manager
117XrdSysError XrdHttpProtocol::eDest = 0; // Error message handler
118XrdSecService *XrdHttpProtocol::CIA = 0; // Authentication Server
119int XrdHttpProtocol::m_bio_type = 0; // BIO type identifier for our custom BIO.
120BIO_METHOD *XrdHttpProtocol::m_bio_method = NULL; // BIO method constructor.
121char *XrdHttpProtocol::xrd_cslist = nullptr;
126
127decltype(XrdHttpProtocol::m_staticheader_map) XrdHttpProtocol::m_staticheader_map;
128decltype(XrdHttpProtocol::m_staticheaders) XrdHttpProtocol::m_staticheaders;
129
131
132namespace
133{
134const char *TraceID = "Protocol";
135}
136
138{
140
141static const int hsmAuto = -1;
142static const int hsmOff = 0;
143static const int hsmMan = 1;
144static const int hsmOn = 1; // Dual purpose but use a meaningful varname
145
148bool tlsClientAuth = true;
149bool httpsspec = false;
150bool xrdctxVer = false;
151}
152
153using namespace XrdHttpProtoInfo;
154
155/******************************************************************************/
156/* P r o t o c o l M a n a g e m e n t S t a c k s */
157/******************************************************************************/
158
161 "xrootd protocol anchor");
162
163
164/******************************************************************************/
165/* U g l y O p e n S S L w o r k a r o u n d s */
166/******************************************************************************/
167#if OPENSSL_VERSION_NUMBER < 0x10100000L
168void *BIO_get_data(BIO *bio) {
169 return bio->ptr;
170}
171void BIO_set_data(BIO *bio, void *ptr) {
172 bio->ptr = ptr;
173}
174#if OPENSSL_VERSION_NUMBER < 0x1000105fL
175int BIO_get_flags(BIO *bio) {
176 return bio->flags;
177}
178#endif
179void BIO_set_flags(BIO *bio, int flags) {
180 bio->flags = flags;
181}
182int BIO_get_init(BIO *bio) {
183 return bio->init;
184}
185void BIO_set_init(BIO *bio, int init) {
186 bio->init = init;
187}
188void BIO_set_shutdown(BIO *bio, int shut) {
189 bio->shutdown = shut;
190}
191int BIO_get_shutdown(BIO *bio) {
192 return bio->shutdown;
193}
194
195#endif
196/******************************************************************************/
197/* X r d H T T P P r o t o c o l C l a s s */
198/******************************************************************************/
199/******************************************************************************/
200/* C o n s t r u c t o r */
201/******************************************************************************/
202
204: XrdProtocol("HTTP protocol handler"), ProtLink(this),
206 myBuff = 0;
207 Addr_str = 0;
208 Reset();
209 ishttps = imhttps;
210
211}
212
213/******************************************************************************/
214/* A s s i g n m e n t O p e r a t o r */
215
216/******************************************************************************/
217
219
220 return *this;
221}
222
223/******************************************************************************/
224/* M a t c h */
225/******************************************************************************/
226
227#define TRACELINK lp
228
230 char mybuf[16], mybuf2[1024];
231 XrdHttpProtocol *hp;
232 int dlen;
233 bool myishttps = false;
234
235 // Peek at the first 20 bytes of data
236 //
237 if ((dlen = lp->Peek(mybuf, (int) sizeof (mybuf), hailWait)) < (int) sizeof (mybuf)) {
238 if (dlen <= 0) lp->setEtext("handshake not received");
239 return (XrdProtocol *) 0;
240 }
241 mybuf[dlen - 1] = '\0';
242
243 // Trace the data
244 //
245
246 TRACEI(DEBUG, "received dlen: " << dlen);
247 //TRACEI(REQ, "received buf: " << mybuf);
248 mybuf2[0] = '\0';
249 for (int i = 0; i < dlen; i++) {
250 char mybuf3[16];
251 sprintf(mybuf3, "%.02d ", mybuf[i]);
252 strcat(mybuf2, mybuf3);
253
254 }
255 TRACEI(DEBUG, "received dump: " << mybuf2);
256
257 // Decide if it looks http or not. For now we are happy if all the received characters are alphanumeric
258 bool ismine = true;
259 for (int i = 0; i < dlen - 1; i++)
260 if (!isprint(mybuf[i]) && (mybuf[i] != '\r') && (mybuf[i] != '\n')) {
261 ismine = false;
262 TRACEI(DEBUG, "This does not look like http at pos " << i);
263 break;
264 }
265
266 // If it does not look http then look if it looks like https
267 if ((!ismine) && (dlen >= 4)) {
268 char check[4] = {00, 00, 00, 00};
269 if (memcmp(mybuf, check, 4)) {
270
271 if (httpsmode) {
272 ismine = true;
273 myishttps = true;
274 TRACEI(DEBUG, "This may look like https");
275 } else {
276 TRACEI(ALL, "This may look like https, but https is not configured");
277 }
278
279 }
280 }
281
282 if (!ismine) {
283 TRACEI(DEBUG, "This does not look like https. Protocol not matched.");
284 return (XrdProtocol *) 0;
285 }
286
287 // It does look http or https...
288 // Get a protocol object off the stack (if none, allocate a new one)
289 //
290
291 TRACEI(REQ, "Protocol matched. https: " << myishttps);
292 if (!(hp = ProtStack.Pop())) hp = new XrdHttpProtocol(myishttps);
293 else
294 hp->ishttps = myishttps;
295
296 // We now have to do some work arounds to tell the underlying framework
297 // that is is https without invoking TLS on the actual link. Eventually,
298 // we should just use the link's TLS native implementation.
299 //
300 hp->SecEntity.addrInfo = lp->AddrInfo();
301 XrdNetAddr *netP = const_cast<XrdNetAddr*>(lp->NetAddr());
302 netP->SetDialect("https");
303 netP->SetTLS(true);
304
305 // Allocate 1MB buffer from pool
306 if (!hp->myBuff) {
307 hp->myBuff = BPool->Obtain(1024 * 1024);
308 }
309 hp->myBuffStart = hp->myBuffEnd = hp->myBuff->buff;
310
311 // Bind the protocol to the link and return the protocol
312 //
313 hp->Link = lp;
314 return (XrdProtocol *) hp;
315}
316
317char *XrdHttpProtocol::GetClientIPStr() {
318 char buf[256];
319 buf[0] = '\0';
320 if (!Link) return strdup("unknown");
322 if (!ai) return strdup("unknown");
323
324 if (!Link->AddrInfo()->Format(buf, 255, XrdNetAddrInfo::fmtAddr, XrdNetAddrInfo::noPort)) return strdup("unknown");
325
326 return strdup(buf);
327}
328
329// Various routines for handling XrdLink as BIO objects within OpenSSL.
330#if OPENSSL_VERSION_NUMBER < 0x1000105fL
331int BIO_XrdLink_write(BIO *bio, const char *data, size_t datal, size_t *written)
332{
333 if (!data || !bio) {
334 *written = 0;
335 return 0;
336 }
337
338 XrdLink *lp=static_cast<XrdLink *>(BIO_get_data(bio));
339
340 errno = 0;
341 int ret = lp->Send(data, datal);
342 BIO_clear_retry_flags(bio);
343 if (ret <= 0) {
344 *written = 0;
345 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
346 BIO_set_retry_write(bio);
347 return ret;
348 }
349 *written = ret;
350 return 1;
351}
352#else
353int BIO_XrdLink_write(BIO *bio, const char *data, int datal)
354{
355 if (!data || !bio) {
356 errno = ENOMEM;
357 return -1;
358 }
359
360 errno = 0;
361 XrdLink *lp = static_cast<XrdLink *>(BIO_get_data(bio));
362 int ret = lp->Send(data, datal);
363 BIO_clear_retry_flags(bio);
364 if (ret <= 0) {
365 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
366 BIO_set_retry_write(bio);
367 }
368 return ret;
369}
370#endif
371
372
373#if OPENSSL_VERSION_NUMBER < 0x1000105fL
374static int BIO_XrdLink_read(BIO *bio, char *data, size_t datal, size_t *read)
375{
376 if (!data || !bio) {
377 *read = 0;
378 return 0;
379 }
380
381 errno = 0;
382
383 XrdLink *lp = static_cast<XrdLink *>(BIO_get_data(bio));
384 int ret = lp->Recv(data, datal);
385 BIO_clear_retry_flags(bio);
386 if (ret <= 0) {
387 *read = 0;
388 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
389 BIO_set_retry_read(bio);
390 return ret;
391 }
392 *read = ret;
393}
394#else
395static int BIO_XrdLink_read(BIO *bio, char *data, int datal)
396{
397 if (!data || !bio) {
398 errno = ENOMEM;
399 return -1;
400 }
401
402 errno = 0;
403 XrdLink *lp = static_cast<XrdLink *>(BIO_get_data(bio));
404 int ret = lp->Recv(data, datal);
405 BIO_clear_retry_flags(bio);
406 if (ret <= 0) {
407 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
408 BIO_set_retry_read(bio);
409 }
410 return ret;
411}
412#endif
413
414
415static int BIO_XrdLink_create(BIO *bio)
416{
417
418
419 BIO_set_init(bio, 0);
420 //BIO_set_next(bio, 0);
421 BIO_set_data(bio, NULL);
422 BIO_set_flags(bio, 0);
423
424#if OPENSSL_VERSION_NUMBER < 0x10100000L
425
426 bio->num = 0;
427
428#endif
429
430 return 1;
431}
432
433
434static int BIO_XrdLink_destroy(BIO *bio)
435{
436 if (bio == NULL) return 0;
437 if (BIO_get_shutdown(bio)) {
438 if (BIO_get_data(bio)) {
439 static_cast<XrdLink*>(BIO_get_data(bio))->Close();
440 }
441 BIO_set_init(bio, 0);
442 BIO_set_flags(bio, 0);
443 }
444 return 1;
445}
446
447
448static long BIO_XrdLink_ctrl(BIO *bio, int cmd, long num, void * ptr)
449{
450 long ret = 1;
451 switch (cmd) {
452 case BIO_CTRL_GET_CLOSE:
453 ret = BIO_get_shutdown(bio);
454 break;
455 case BIO_CTRL_SET_CLOSE:
456 BIO_set_shutdown(bio, (int)num);
457 break;
458 case BIO_CTRL_DUP:
459 case BIO_CTRL_FLUSH:
460 ret = 1;
461 break;
462 default:
463 ret = 0;
464 break;
465 }
466 return ret;
467}
468
469
470BIO *XrdHttpProtocol::CreateBIO(XrdLink *lp)
471{
472 if (m_bio_method == NULL)
473 return NULL;
474
475 BIO *ret = BIO_new(m_bio_method);
476
477 BIO_set_shutdown(ret, 0);
478 BIO_set_data(ret, lp);
479 BIO_set_init(ret, 1);
480 return ret;
481}
482
483
484/******************************************************************************/
485/* P r o c e s s */
486/******************************************************************************/
487
488#undef TRACELINK
489#define TRACELINK Link
490
491int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here
492{
493 int rc = 0;
494
495 TRACEI(DEBUG, " Process. lp:"<<(void *)lp<<" reqstate: "<<CurrentReq.reqstate);
496
497 if (!myBuff || !myBuff->buff || !myBuff->bsize) {
498 TRACE(ALL, " Process. No buffer available. Internal error.");
499 return -1;
500 }
501
502
503 if (!SecEntity.host) {
504 char *nfo = GetClientIPStr();
505 if (nfo) {
506 TRACEI(REQ, " Setting host: " << nfo);
507 SecEntity.host = nfo;
508 strcpy(SecEntity.prot, "http");
509 }
510 }
511
512
513
514 // If https then check independently for the ssl handshake
515 if (ishttps && !ssldone) {
516
517 if (!ssl) {
518 sbio = CreateBIO(Link);
519 BIO_set_nbio(sbio, 1);
520 ssl = (SSL*)xrdctx->Session();
521 }
522
523 if (!ssl) {
524 TRACEI(DEBUG, " SSL_new returned NULL");
525 ERR_print_errors(sslbio_err);
526 return -1;
527 }
528
529 // If a secxtractor has been loaded
530 // maybe it wants to add its own initialization bits
531 if (secxtractor)
532 secxtractor->InitSSL(ssl, sslcadir);
533
534 SSL_set_bio(ssl, sbio, sbio);
535 //SSL_set_connect_state(ssl);
536
537 //SSL_set_fd(ssl, Link->FDnum());
538 struct timeval tv;
539 tv.tv_sec = 10;
540 tv.tv_usec = 0;
541 setsockopt(Link->FDnum(), SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
542 setsockopt(Link->FDnum(), SOL_SOCKET, SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
543
544 TRACEI(DEBUG, " Entering SSL_accept...");
545 int res = SSL_accept(ssl);
546 TRACEI(DEBUG, " SSL_accept returned :" << res);
547 if ((res == -1) && (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ)) {
548 TRACEI(DEBUG, " SSL_accept wants to read more bytes... err:" << SSL_get_error(ssl, res));
549 return 1;
550 }
551
552 if(res <= 0) {
553 ERR_print_errors(sslbio_err);
554 if (res < 0) {
555
556 SSL_free(ssl);
557 ssl = 0;
558 return -1;
559 }
560 }
561
562 BIO_set_nbio(sbio, 0);
563
564 strcpy(SecEntity.prot, "https");
565
566 // Get the voms string and auth information
567 if (tlsClientAuth && HandleAuthentication(Link)) {
568 SSL_free(ssl);
569 ssl = 0;
570 return -1;
571 }
572
573 ssldone = true;
574 if (TRACING(TRACE_AUTH)) {
575 SecEntity.Display(eDest);
576 }
577 }
578
579
580
581 if (!DoingLogin) {
582 // Re-invocations triggered by the bridge have lp==0
583 // In this case we keep track of a different request state
584 if (lp) {
585
586 // This is an invocation that was triggered by a socket event
587 // Read all the data that is available, throw it into the buffer
588 if ((rc = getDataOneShot(BuffAvailable())) < 0) {
589 // Error -> exit
590 return -1;
591 }
592
593 // If we need more bytes, let's wait for another invokation
594 if (BuffUsed() < ResumeBytes) return 1;
595
596
597 } else
598 CurrentReq.reqstate++;
599 } else if (!DoneSetInfo && !CurrentReq.userAgent().empty()) { // DoingLogin is true, meaning the login finished.
600 std::string mon_info = "monitor info " + CurrentReq.userAgent();
601 DoneSetInfo = true;
602 if (mon_info.size() >= 1024) {
603 TRACEI(ALL, "User agent string too long");
604 } else if (!Bridge) {
605 TRACEI(ALL, "Internal logic error: Bridge is null after login");
606 } else {
607 TRACEI(DEBUG, "Setting " << mon_info);
608 memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
609 CurrentReq.xrdreq.set.requestid = htons(kXR_set);
610 CurrentReq.xrdreq.set.modifier = '\0';
611 memset(CurrentReq.xrdreq.set.reserved, '\0', sizeof(CurrentReq.xrdreq.set.reserved));
612 CurrentReq.xrdreq.set.dlen = htonl(mon_info.size());
613 if (!Bridge->Run((char *) &CurrentReq.xrdreq, (char *) mon_info.c_str(), mon_info.size())) {
614 SendSimpleResp(500, nullptr, nullptr, "Could not set user agent.", 0, false);
615 return -1;
616 }
617 return 0;
618 }
619 } else {
620 DoingLogin = false;
621 }
622
623 // Read the next request header, that is, read until a double CRLF is found
624
625
626 if (!CurrentReq.headerok) {
627
628 // Read as many lines as possible into the buffer. An empty line breaks
629 while ((rc = BuffgetLine(tmpline)) > 0) {
630 std::string traceLine = tmpline.c_str();
631 if (TRACING(TRACE_DEBUG)) {
632 traceLine = obfuscateAuth(traceLine);
633 }
634 TRACE(DEBUG, " rc:" << rc << " got hdr line: " << traceLine);
635 if ((rc == 2) && (tmpline.length() > 1) && (tmpline[rc - 1] == '\n')) {
636 CurrentReq.headerok = true;
637 TRACE(DEBUG, " rc:" << rc << " detected header end.");
638 break;
639 }
640
641
642 if (CurrentReq.request == CurrentReq.rtUnset) {
643 TRACE(DEBUG, " Parsing first line: " << traceLine.c_str());
644 int result = CurrentReq.parseFirstLine((char *)tmpline.c_str(), tmpline.length());
645 if (result < 0) {
646 TRACE(DEBUG, " Parsing of first line failed with " << result);
647 return -1;
648 }
649 } else {
650 int result = CurrentReq.parseLine((char *) tmpline.c_str(), tmpline.length());
651 if(result < 0) {
652 TRACE(DEBUG, " Parsing of header line failed with " << result)
653 SendSimpleResp(400,NULL,NULL,"Malformed header line. Hint: ensure the line finishes with \"\\r\\n\"", 0, false);
654 return -1;
655 }
656 }
657
658
659 }
660
661 // Here we have CurrentReq loaded with the header, or its relevant fields
662
663 if (!CurrentReq.headerok) {
664 TRACEI(REQ, " rc:" << rc << "Header not yet complete.");
665
666 // Here a subtle error condition. IF we failed reading a line AND the buffer
667 // has a reasonable amount of data available THEN we consider the header
668 // as corrupted and shutdown the client
669 if ((rc <= 0) && (BuffUsed() >= 16384)) {
670 TRACEI(ALL, "Corrupted header detected, or line too long. Disconnecting client.");
671 return -1;
672 }
673
674
675 if (CurrentReq.reqstate > 0)
676 CurrentReq.reqstate--;
677 // Waiting for more data
678 return 1;
679 }
680
681 }
682
683 // If we are in self-redirect mode, then let's do it
684 // Do selfredirect only with 'simple' requests, otherwise poor clients may misbehave
685 if (ishttps && ssldone && selfhttps2http &&
686 ( (CurrentReq.request == XrdHttpReq::rtGET) || (CurrentReq.request == XrdHttpReq::rtPUT) ||
687 (CurrentReq.request == XrdHttpReq::rtPROPFIND)) ) {
688 char hash[512];
689 time_t timenow = time(0);
690
691
692 calcHashes(hash, CurrentReq.resource.c_str(), (kXR_int16) CurrentReq.request,
693 &SecEntity,
694 timenow,
695 secretkey);
696
697
698
699 if (hash[0]) {
700
701 // Workaround... delete the previous opaque information
702 if (CurrentReq.opaque) {
703 delete CurrentReq.opaque;
704 CurrentReq.opaque = 0;
705 }
706
707 TRACEI(REQ, " rc:" << rc << " self-redirecting to http with security token.");
708
709 XrdOucString dest = "Location: http://";
710 // Here I should put the IP addr of the server
711
712 // We have to recompute it here because we don't know to which
713 // interface the client had connected to
714 struct sockaddr_storage sa;
715 socklen_t sl = sizeof(sa);
716 getsockname(this->Link->AddrInfo()->SockFD(), (struct sockaddr*)&sa, &sl);
717
718 // now get it back and print it
719 char buf[256];
720 bool ok = false;
721
722 switch (sa.ss_family) {
723 case AF_INET:
724 if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
725 if (Addr_str) free(Addr_str);
726 Addr_str = strdup(buf);
727 ok = true;
728 }
729 break;
730 case AF_INET6:
731 if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
732 if (Addr_str) free(Addr_str);
733 Addr_str = (char *)malloc(strlen(buf)+3);
734 strcpy(Addr_str, "[");
735 strcat(Addr_str, buf);
736 strcat(Addr_str, "]");
737 ok = true;
738 }
739 break;
740 default:
741 TRACEI(REQ, " Can't recognize the address family of the local host.");
742 }
743
744 if (ok) {
745 dest += Addr_str;
746 dest += ":";
747 dest += Port_str;
748 dest += CurrentReq.resource.c_str();
749 TRACEI(REQ," rc:"<<rc<<" self-redirecting to http with security token: '"
750 << dest.c_str() << "'");
751
752
753 CurrentReq.appendOpaque(dest, &SecEntity, hash, timenow);
754 SendSimpleResp(302, NULL, (char *) dest.c_str(), 0, 0, true);
755 CurrentReq.reset();
756 return -1;
757 }
758
759 TRACEI(REQ, " rc:" << rc << " Can't perform self-redirection.");
760
761 }
762 else {
763 TRACEI(ALL, " Could not calculate self-redirection hash");
764 }
765 }
766
767 // If this is not https, then extract the signed information from the url
768 // and fill the SecEntity structure as if we were using https
769 if (!ishttps && !ssldone) {
770
771
772 if (CurrentReq.opaque) {
773 char * tk = CurrentReq.opaque->Get("xrdhttptk");
774 // If there is a hash then we use it as authn info
775 if (tk) {
776
777 time_t tim = 0;
778 char * t = CurrentReq.opaque->Get("xrdhttptime");
779 if (t) tim = atoi(t);
780 if (!t) {
781 TRACEI(REQ, " xrdhttptime not specified. Authentication failed.");
782 return -1;
783 }
784 if (abs(time(0) - tim) > XRHTTP_TK_GRACETIME) {
785 TRACEI(REQ, " Token expired. Authentication failed.");
786 return -1;
787 }
788
789 // Fill the Secentity from the fields in the URL:name, vo, host
790 char *nfo;
791
792 nfo = CurrentReq.opaque->Get("xrdhttpvorg");
793 if (nfo) {
794 TRACEI(DEBUG, " Setting vorg: " << nfo);
795 SecEntity.vorg = strdup(nfo);
796 TRACEI(REQ, " Setting vorg: " << SecEntity.vorg);
797 }
798
799 nfo = CurrentReq.opaque->Get("xrdhttpname");
800 if (nfo) {
801 TRACEI(DEBUG, " Setting name: " << nfo);
802 SecEntity.name = strdup(decode_str(nfo).c_str());
803 TRACEI(REQ, " Setting name: " << SecEntity.name);
804 }
805
806 nfo = CurrentReq.opaque->Get("xrdhttphost");
807 if (nfo) {
808 TRACEI(DEBUG, " Setting host: " << nfo);
809 if (SecEntity.host) free(SecEntity.host);
810 SecEntity.host = strdup(decode_str(nfo).c_str());
811 TRACEI(REQ, " Setting host: " << SecEntity.host);
812 }
813
814 nfo = CurrentReq.opaque->Get("xrdhttpdn");
815 if (nfo) {
816 TRACEI(DEBUG, " Setting dn: " << nfo);
817 SecEntity.moninfo = strdup(decode_str(nfo).c_str());
818 TRACEI(REQ, " Setting dn: " << SecEntity.moninfo);
819 }
820
821 nfo = CurrentReq.opaque->Get("xrdhttprole");
822 if (nfo) {
823 TRACEI(DEBUG, " Setting role: " << nfo);
824 SecEntity.role = strdup(decode_str(nfo).c_str());
825 TRACEI(REQ, " Setting role: " << SecEntity.role);
826 }
827
828 nfo = CurrentReq.opaque->Get("xrdhttpgrps");
829 if (nfo) {
830 TRACEI(DEBUG, " Setting grps: " << nfo);
831 SecEntity.grps = strdup(decode_str(nfo).c_str());
832 TRACEI(REQ, " Setting grps: " << SecEntity.grps);
833 }
834
835 nfo = CurrentReq.opaque->Get("xrdhttpendorsements");
836 if (nfo) {
837 TRACEI(DEBUG, " Setting endorsements: " << nfo);
838 SecEntity.endorsements = strdup(decode_str(nfo).c_str());
839 TRACEI(REQ, " Setting endorsements: " << SecEntity.endorsements);
840 }
841
842 nfo = CurrentReq.opaque->Get("xrdhttpcredslen");
843 if (nfo) {
844 TRACEI(DEBUG, " Setting credslen: " << nfo);
845 char *s1 = strdup(decode_str(nfo).c_str());
846 if (s1 && s1[0]) {
847 SecEntity.credslen = atoi(s1);
848 TRACEI(REQ, " Setting credslen: " << SecEntity.credslen);
849 }
850 if (s1) free(s1);
851 }
852
853 if (SecEntity.credslen) {
854 nfo = CurrentReq.opaque->Get("xrdhttpcreds");
855 if (nfo) {
856 TRACEI(DEBUG, " Setting creds: " << nfo);
857 SecEntity.creds = strdup(decode_str(nfo).c_str());
858 TRACEI(REQ, " Setting creds: " << SecEntity.creds);
859 }
860 }
861
862 char hash[512];
863
864 calcHashes(hash, CurrentReq.resource.c_str(), (kXR_int16) CurrentReq.request,
865 &SecEntity,
866 tim,
867 secretkey);
868
869 if (compareHash(hash, tk)) {
870 TRACEI(REQ, " Invalid tk '" << tk << "' != '" << hash << "'(calculated). Authentication failed.");
871 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 std::stringstream ss;
1618 const std::string crlf = "\r\n";
1619
1620 ss << "HTTP/1.1 " << code << " ";
1621
1622 if (desc) {
1623 ss << desc;
1624 } else {
1625 ss << httpStatusToString(code);
1626 }
1627 ss << crlf;
1628
1629 if (keepalive && (code != 100))
1630 ss << "Connection: Keep-Alive" << crlf;
1631 else
1632 ss << "Connection: Close" << crlf;
1633
1634 ss << "Server: XrootD/" << XrdVSTRING << crlf;
1635
1636 const auto iter = m_staticheaders.find(CurrentReq.requestverb);
1637 if (iter != m_staticheaders.end()) {
1638 ss << iter->second;
1639 } else {
1640 ss << m_staticheaders[""];
1641 }
1642
1643 if(xrdcors) {
1644 auto corsAllowOrigin = xrdcors->getCORSAllowOriginHeader(CurrentReq.m_origin);
1645 if(corsAllowOrigin) {
1646 ss << *corsAllowOrigin << crlf;
1647 }
1648 }
1649
1650 if ((bodylen >= 0) && (code != 100))
1651 ss << "Content-Length: " << bodylen << crlf;
1652
1653 if (header_to_add && (header_to_add[0] != '\0')) ss << header_to_add << crlf;
1654
1655 ss << crlf;
1656
1657 const std::string &outhdr = ss.str();
1658 TRACEI(RSP, "Sending resp: " << code << " header len:" << outhdr.size());
1659 if (SendData(outhdr.c_str(), outhdr.size()))
1660 return -1;
1661
1662 return 0;
1663}
1664
1665/******************************************************************************/
1666/* S t a r t C h u n k e d R e s p */
1667/******************************************************************************/
1668
1669int XrdHttpProtocol::StartChunkedResp(int code, const char *desc, const char *header_to_add, long long bodylen, bool keepalive) {
1670 const std::string crlf = "\r\n";
1671 std::stringstream ss;
1672
1673 if (header_to_add && (header_to_add[0] != '\0')) {
1674 ss << header_to_add << crlf;
1675 }
1676
1677 ss << "Transfer-Encoding: chunked";
1678 TRACEI(RSP, "Starting chunked response");
1679 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1680}
1681
1682/******************************************************************************/
1683/* C h u n k R e s p */
1684/******************************************************************************/
1685
1686int XrdHttpProtocol::ChunkResp(const char *body, long long bodylen) {
1687 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1688 if (ChunkRespHeader(content_length))
1689 return -1;
1690
1691 if (body && SendData(body, content_length))
1692 return -1;
1693
1694 return ChunkRespFooter();
1695}
1696
1697/******************************************************************************/
1698/* C h u n k R e s p H e a d e r */
1699/******************************************************************************/
1700
1701int XrdHttpProtocol::ChunkRespHeader(long long bodylen) {
1702 const std::string crlf = "\r\n";
1703 std::stringstream ss;
1704
1705 ss << std::hex << bodylen << std::dec << crlf;
1706
1707 const std::string &chunkhdr = ss.str();
1708 TRACEI(RSP, "Sending encoded chunk of size " << bodylen);
1709 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1710}
1711
1712/******************************************************************************/
1713/* C h u n k R e s p F o o t e r */
1714/******************************************************************************/
1715
1716int XrdHttpProtocol::ChunkRespFooter() {
1717 const std::string crlf = "\r\n";
1718 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1719}
1720
1721/******************************************************************************/
1722/* S e n d S i m p l e R e s p */
1723/******************************************************************************/
1724
1728
1729int XrdHttpProtocol::SendSimpleResp(int code, const char *desc, const char *header_to_add, const char *body, long long bodylen, bool keepalive) {
1730
1731 long long content_length = bodylen;
1732 if (bodylen <= 0) {
1733 content_length = body ? strlen(body) : 0;
1734 }
1735
1736 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1737 return -1;
1738
1739 //
1740 // Send the data
1741 //
1742 if (body)
1743 return SendData(body, content_length);
1744
1745 return 0;
1746}
1747
1748/******************************************************************************/
1749/* C o n f i g u r e */
1750/******************************************************************************/
1751
1753 /*
1754 Function: Establish configuration at load time.
1755
1756 Input: None.
1757
1758 Output: 0 upon success or !0 otherwise.
1759 */
1760
1761 char *rdf;
1762
1763 // Copy out the special info we want to use at top level
1764 //
1765 eDest.logger(pi->eDest->logger());
1766 XrdHttpTrace.SetLogger(pi->eDest->logger());
1767 // SI = new XrdXrootdStats(pi->Stats);
1768 Sched = pi->Sched;
1769 BPool = pi->BPool;
1770 xrd_cslist = getenv("XRD_CSLIST");
1771
1772 Port = pi->Port;
1773
1774 // Copy out the current TLS context
1775 //
1776 xrdctx = pi->tlsCtx;
1777
1778 {
1779 char buf[16];
1780 sprintf(buf, "%d", Port);
1781 Port_str = strdup(buf);
1782 }
1783
1784 // Now process and configuration parameters
1785 //
1786 rdf = (parms && *parms ? parms : pi->ConfigFN);
1787 if (rdf && Config(rdf, pi->theEnv)) return 0;
1788 if (pi->DebugON) XrdHttpTrace.What = TRACE_ALL;
1789
1790 // Set the redirect flag if we are a pure redirector
1792 if ((rdf = getenv("XRDROLE"))) {
1793 eDest.Emsg("Config", "XRDROLE: ", rdf);
1794
1795 if (!strcasecmp(rdf, "manager") || !strcasecmp(rdf, "supervisor")) {
1797 eDest.Emsg("Config", "Configured as HTTP(s) redirector.");
1798 } else {
1799
1800 eDest.Emsg("Config", "Configured as HTTP(s) data server.");
1801 }
1802
1803 } else {
1804 eDest.Emsg("Config", "No XRDROLE specified.");
1805 }
1806
1807 // Schedule protocol object cleanup
1808 //
1809 ProtStack.Set(pi->Sched, &XrdHttpTrace,
1810 (XrdHttpTrace.What & TRACE_MEM ? TRACE_MEM : 0));
1811 ProtStack.Set((pi->ConnMax / 3 ? pi->ConnMax / 3 : 30), 60 * 60);
1812
1813 // Return success
1814 //
1815
1816 return 1;
1817}
1818
1819/******************************************************************************/
1820/* p a r s e H e a d e r 2 C G I */
1821/******************************************************************************/
1822int XrdHttpProtocol::parseHeader2CGI(XrdOucStream &Config, XrdSysError & err,std::map<std::string, std::string> &header2cgi) {
1823 char *val, keybuf[1024], parmbuf[1024];
1824 char *parm;
1825
1826 // Get the header key
1827 val = Config.GetWord();
1828 if (!val || !val[0]) {
1829 err.Emsg("Config", "No headerkey specified.");
1830 return 1;
1831 } else {
1832
1833 // Trim the beginning, in place
1834 while ( *val && !isalnum(*val) ) val++;
1835 strcpy(keybuf, val);
1836
1837 // Trim the end, in place
1838 char *pp;
1839 pp = keybuf + strlen(keybuf) - 1;
1840 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1841 *pp = '\0';
1842 pp--;
1843 }
1844
1845 parm = Config.GetWord();
1846
1847 // Avoids segfault in case a key is given without value
1848 if(!parm || !parm[0]) {
1849 err.Emsg("Config", "No header2cgi value specified. key: '", keybuf, "'");
1850 return 1;
1851 }
1852
1853 // Trim the beginning, in place
1854 while ( *parm && !isalnum(*parm) ) parm++;
1855 strcpy(parmbuf, parm);
1856
1857 // Trim the end, in place
1858 pp = parmbuf + strlen(parmbuf) - 1;
1859 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1860 *pp = '\0';
1861 pp--;
1862 }
1863
1864 // Add this mapping to the map that will be used
1865 try {
1866 header2cgi[keybuf] = parmbuf;
1867 } catch ( ... ) {
1868 err.Emsg("Config", "Can't insert new header2cgi rule. key: '", keybuf, "'");
1869 return 1;
1870 }
1871
1872 }
1873 return 0;
1874}
1875
1876
1877/******************************************************************************/
1878/* I n i t T L S */
1879/******************************************************************************/
1880
1881bool XrdHttpProtocol::InitTLS() {
1882
1883 std::string eMsg;
1886
1887// Create a new TLS context
1888//
1889 if (sslverifydepth > 255) sslverifydepth = 255;
1891 //TLS_SET_REFINT will set the refresh interval in minutes, hence the division by 60
1894
1895// Make sure the context was created
1896//
1897 if (!xrdctx->isOK())
1898 {eDest.Say("Config failure: ", eMsg.c_str());
1899 return false;
1900 }
1901
1902// Setup session cache (this is controversial). The default is off but many
1903// programs expect it being enabled and break when it is disabled. In such
1904// cases it should be enabled. This is, of course, a big OpenSSL mess.
1905//
1906 static const char *sess_ctx_id = "XrdHTTPSessionCtx";
1907 unsigned int n =(unsigned int)(strlen(sess_ctx_id)+1);
1908 xrdctx->SessionCache(tlsCache, sess_ctx_id, n);
1909
1910// Set special ciphers if so specified.
1911//
1913 {eDest.Say("Config failure: ", "Unable to set allowable https ciphers!");
1914 return false;
1915 }
1916
1917// Enable or disable the config in the context
1919
1920// All done
1921//
1922 return true;
1923}
1924
1925/******************************************************************************/
1926/* C l e a n u p */
1927/******************************************************************************/
1928
1929void XrdHttpProtocol::Cleanup() {
1930
1931 TRACE(ALL, " Cleanup");
1932
1933 if (BPool && myBuff) {
1934 BuffConsume(BuffUsed());
1935 BPool->Release(myBuff);
1936 myBuff = 0;
1937 }
1938
1939 if (ssl) {
1940 // Shutdown the SSL/TLS connection
1941 // This triggers a bidirectional shutdown of the connection; the bidirectional
1942 // shutdown is useful to ensure that the client receives the server response;
1943 // a one-sided shutdown can result in the server sending a TCP reset packet, zapping
1944 // the contents of the TCP socket buffer on the client side. The HTTP 1.1 RFC has a
1945 // description of why this is important:
1946 // https://datatracker.ietf.org/doc/html/rfc9112#name-tls-connection-closure
1947 // Once we get the clean SSL shutdown message back from the client, we know that
1948 // the client has received the response and we can safely close the connection.
1949 int ret = SSL_shutdown(ssl);
1950 if (ret != 1) {
1951 if(ret == 0) {
1952 // ret == 0, the unidirectional shutdown was successful; wait for the acknowledgement.
1953 ret = SSL_shutdown(ssl);
1954 if (ret != 1) {
1955 TRACE(ALL, "SSL server failed to receive the SSL shutdown message from the client");
1956 ERR_print_errors(sslbio_err);
1957 }
1958 } else {
1959 //ret < 0, an error really happened.
1960 TRACE(ALL, "SSL server failed to send the shutdown message to the client");
1961 ERR_print_errors(sslbio_err);
1962 }
1963 }
1964
1965 if (secxtractor)
1966 secxtractor->FreeSSL(ssl);
1967
1968 SSL_free(ssl);
1969
1970 }
1971
1972
1973 ssl = 0;
1974 sbio = 0;
1975
1976 if (SecEntity.caps) free(SecEntity.caps);
1977 if (SecEntity.grps) free(SecEntity.grps);
1978 if (SecEntity.endorsements) free(SecEntity.endorsements);
1979 if (SecEntity.vorg) free(SecEntity.vorg);
1980 if (SecEntity.role) free(SecEntity.role);
1981 if (SecEntity.name) free(SecEntity.name);
1982 if (SecEntity.host) free(SecEntity.host);
1983 if (SecEntity.moninfo) free(SecEntity.moninfo);
1984
1985 SecEntity.Reset();
1986
1987 if (Addr_str) free(Addr_str);
1988 Addr_str = 0;
1989}
1990
1991/******************************************************************************/
1992/* R e s e t */
1993/******************************************************************************/
1994
1995void XrdHttpProtocol::Reset() {
1996
1997 TRACE(ALL, " Reset");
1998 Link = 0;
1999 CurrentReq.reset();
2000 CurrentReq.reqstate = 0;
2001
2002 if (myBuff) {
2003 BPool->Release(myBuff);
2004 myBuff = 0;
2005 }
2006 myBuffStart = myBuffEnd = 0;
2007
2008 DoingLogin = false;
2009 DoneSetInfo = false;
2010
2011 ResumeBytes = 0;
2012 Resume = 0;
2013
2014 //
2015 // numReads = 0;
2016 // numReadP = 0;
2017 // numReadV = 0;
2018 // numSegsV = 0;
2019 // numWrites = 0;
2020 // numFiles = 0;
2021 // cumReads = 0;
2022 // cumReadV = 0;
2023 // cumSegsV = 0;
2024 // cumWrites = 0;
2025 // totReadP = 0;
2026
2027 SecEntity.Reset();
2029 ishttps = false;
2030 ssldone = false;
2031
2032 Bridge = 0;
2033 ssl = 0;
2034 sbio = 0;
2035
2036}
2037
2038/******************************************************************************/
2039/* x h t t p s m o d e */
2040/******************************************************************************/
2041
2042/* Function: xhttpsmode
2043
2044 Purpose: To parse the directive: httpsmode {auto | disable | manual}
2045
2046 auto configure https if configured in xrd framework.
2047 disable do not configure https no matter what
2048 manual configure https and ignore the xrd framework
2049
2050 Output: 0 upon success or !0 upon failure.
2051 */
2052
2053int XrdHttpProtocol::xhttpsmode(XrdOucStream & Config) {
2054 char *val;
2055
2056 // Get the val
2057 //
2058 val = Config.GetWord();
2059 if (!val || !val[0]) {
2060 eDest.Emsg("Config", "httpsmode parameter not specified");
2061 return 1;
2062 }
2063
2064 // Record the val
2065 //
2066 if (!strcmp(val, "auto")) httpsmode = hsmAuto;
2067 else if (!strcmp(val, "disable")) httpsmode = hsmOff;
2068 else if (!strcmp(val, "manual")) httpsmode = hsmMan;
2069 else {eDest.Emsg("Config", "invalid httpsmode parameter - ", val);
2070 return 1;
2071 }
2072 return 0;
2073}
2074
2075/******************************************************************************/
2076/* x s s l v e r i f y d e p t h */
2077/******************************************************************************/
2078
2079/* Function: xsslverifydepth
2080
2081 Purpose: To parse the directive: sslverifydepth <depth>
2082
2083 <depth> the max depth of the ssl cert verification
2084
2085 Output: 0 upon success or !0 upon failure.
2086 */
2087
2088int XrdHttpProtocol::xsslverifydepth(XrdOucStream & Config) {
2089 char *val;
2090
2091 // Get the val
2092 //
2093 val = Config.GetWord();
2094 if (!val || !val[0]) {
2095 eDest.Emsg("Config", "sslverifydepth value not specified");
2096 return 1;
2097 }
2098
2099 // Record the val
2100 //
2101 sslverifydepth = atoi(val);
2102
2103 if (xrdctxVer){ HTTPS_ALERT("verifydepth","tlsca",false); }
2104 return 0;
2105}
2106
2107/******************************************************************************/
2108/* x s s l c e r t */
2109/******************************************************************************/
2110
2111/* Function: xsslcert
2112
2113 Purpose: To parse the directive: sslcert <path>
2114
2115 <path> the path of the server certificate to be used.
2116
2117 Output: 0 upon success or !0 upon failure.
2118 */
2119
2120int XrdHttpProtocol::xsslcert(XrdOucStream & Config) {
2121 char *val;
2122
2123 // Get the path
2124 //
2125 val = Config.GetWord();
2126 if (!val || !val[0]) {
2127 eDest.Emsg("Config", "HTTP X509 certificate not specified");
2128 return 1;
2129 }
2130
2131 // Record the path
2132 //
2133 if (sslcert) free(sslcert);
2134 sslcert = strdup(val);
2135
2136 // If we have an xrd context issue reminder
2137 //
2138 HTTPS_ALERT("cert","tls",true);
2139 return 0;
2140}
2141
2142/******************************************************************************/
2143/* x s s l k e y */
2144/******************************************************************************/
2145
2146/* Function: xsslkey
2147
2148 Purpose: To parse the directive: sslkey <path>
2149
2150 <path> the path of the server key to be used.
2151
2152 Output: 0 upon success or !0 upon failure.
2153 */
2154
2155int XrdHttpProtocol::xsslkey(XrdOucStream & Config) {
2156 char *val;
2157
2158 // Get the path
2159 //
2160 val = Config.GetWord();
2161 if (!val || !val[0]) {
2162 eDest.Emsg("Config", "HTTP X509 key not specified");
2163 return 1;
2164 }
2165
2166 // Record the path
2167 //
2168 if (sslkey) free(sslkey);
2169 sslkey = strdup(val);
2170
2171 HTTPS_ALERT("key","tls",true);
2172 return 0;
2173}
2174
2175/******************************************************************************/
2176/* x g m a p */
2177/******************************************************************************/
2178
2179/* Function: xgmap
2180
2181 Purpose: To parse the directive: gridmap [required] [compatNameGeneration] <path>
2182
2183 required optional parameter which if present treats any grimap errors
2184 as fatal.
2185 <path> the path of the gridmap file to be used. Normally it's
2186 /etc/grid-security/gridmap. No mapfile means no translation
2187 required. Pointing to a non existing mapfile is an error.
2188
2189 Output: 0 upon success or !0 upon failure.
2190 */
2191
2192int XrdHttpProtocol::xgmap(XrdOucStream & Config) {
2193 char *val;
2194
2195 // Get the path
2196 //
2197 val = Config.GetWord();
2198 if (!val || !val[0]) {
2199 eDest.Emsg("Config", "HTTP X509 gridmap file location not specified");
2200 return 1;
2201 }
2202
2203 // Handle optional parameter "required"
2204 //
2205 if (!strncmp(val, "required", 8)) {
2206 isRequiredGridmap = true;
2207 val = Config.GetWord();
2208
2209 if (!val || !val[0]) {
2210 eDest.Emsg("Config", "HTTP X509 gridmap file missing after [required] "
2211 "parameter");
2212 return 1;
2213 }
2214 }
2215
2216 // Handle optional parameter "compatNameGeneration"
2217 //
2218 if (!strcmp(val, "compatNameGeneration")) {
2219 compatNameGeneration = true;
2220 val = Config.GetWord();
2221 if (!val || !val[0]) {
2222 eDest.Emsg("Config", "HTTP X509 gridmap file missing after "
2223 "[compatNameGeneration] parameter");
2224 return 1;
2225 }
2226 }
2227
2228
2229 // Record the path
2230 //
2231 if (gridmap) free(gridmap);
2232 gridmap = strdup(val);
2233 return 0;
2234}
2235
2236/******************************************************************************/
2237/* x s s l c a f i l e */
2238/******************************************************************************/
2239
2240/* Function: xsslcafile
2241
2242 Purpose: To parse the directive: sslcafile <path>
2243
2244 <path> the path of the server key to be used.
2245
2246 Output: 0 upon success or !0 upon failure.
2247 */
2248
2249int XrdHttpProtocol::xsslcafile(XrdOucStream & Config) {
2250 char *val;
2251
2252 // Get the path
2253 //
2254 val = Config.GetWord();
2255 if (!val || !val[0]) {
2256 eDest.Emsg("Config", "HTTP X509 CAfile not specified");
2257 return 1;
2258 }
2259
2260 // Record the path
2261 //
2262 if (sslcafile) free(sslcafile);
2263 sslcafile = strdup(val);
2264
2265 if (xrdctxVer){ HTTPS_ALERT("cafile","tlsca",false); }
2266 return 0;
2267}
2268
2269/******************************************************************************/
2270/* x s e c r e t k e y */
2271/******************************************************************************/
2272
2273/* Function: xsecretkey
2274
2275 Purpose: To parse the directive: xsecretkey <key>
2276
2277 <key> the key to be used
2278
2279 Output: 0 upon success or !0 upon failure.
2280 */
2281
2282int XrdHttpProtocol::xsecretkey(XrdOucStream & Config) {
2283 char *val;
2284 bool inFile = false;
2285
2286 // Get the path
2287 //
2288 val = Config.GetWord();
2289 if (!val || !val[0]) {
2290 eDest.Emsg("Config", "Shared secret key not specified");
2291 return 1;
2292 }
2293
2294
2295 // If the token starts with a slash, then we interpret it as
2296 // the path to a file that contains the secretkey
2297 // otherwise, the token itself is the secretkey
2298 if (val[0] == '/') {
2299 struct stat st;
2300 inFile = true;
2301 int fd = open(val, O_RDONLY);
2302
2303 if ( fd == -1 ) {
2304 eDest.Emsg("Config", errno, "open shared secret key file", val);
2305 return 1;
2306 }
2307
2308 if ( fstat(fd, &st) != 0 ) {
2309 eDest.Emsg("Config", errno, "fstat shared secret key file", val);
2310 close(fd);
2311 return 1;
2312 }
2313
2314 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2315 eDest.Emsg("Config",
2316 "For your own security, the shared secret key file cannot be world readable or group writable '", val, "'");
2317 close(fd);
2318 return 1;
2319 }
2320
2321 FILE *fp = fdopen(fd, "r");
2322
2323 if ( fp == nullptr ) {
2324 eDest.Emsg("Config", errno, "fdopen shared secret key file", val);
2325 close(fd);
2326 return 1;
2327 }
2328
2329 char line[1024];
2330 while( fgets(line, 1024, fp) ) {
2331 char *pp;
2332
2333 // Trim the end
2334 pp = line + strlen(line) - 1;
2335 while ( (pp >= line) && (!isalnum(*pp)) ) {
2336 *pp = '\0';
2337 pp--;
2338 }
2339
2340 // Trim the beginning
2341 pp = line;
2342 while ( *pp && !isalnum(*pp) ) pp++;
2343
2344 if ( strlen(pp) >= 32 ) {
2345 eDest.Say("Config", "Secret key loaded.");
2346 // Record the path
2347 if (secretkey) free(secretkey);
2348 secretkey = strdup(pp);
2349
2350 fclose(fp);
2351 return 0;
2352 }
2353
2354 }
2355
2356 fclose(fp);
2357 eDest.Emsg("Config", "Cannot find useful secretkey in file '", val, "'");
2358 return 1;
2359
2360 }
2361
2362 if ( strlen(val) < 32 ) {
2363 eDest.Emsg("Config", "Secret key is too short");
2364 return 1;
2365 }
2366
2367 // Record the path
2368 if (secretkey) free(secretkey);
2369 secretkey = strdup(val);
2370 if (!inFile) Config.noEcho();
2371
2372 return 0;
2373}
2374
2375/******************************************************************************/
2376/* x l i s t d e n y */
2377/******************************************************************************/
2378
2379/* Function: xlistdeny
2380
2381 Purpose: To parse the directive: listingdeny <yes|no|0|1>
2382
2383 <val> makes this redirector deny listings with an error
2384
2385 Output: 0 upon success or !0 upon failure.
2386 */
2387
2388int XrdHttpProtocol::xlistdeny(XrdOucStream & Config) {
2389 char *val;
2390
2391 // Get the path
2392 //
2393 val = Config.GetWord();
2394 if (!val || !val[0]) {
2395 eDest.Emsg("Config", "listingdeny flag not specified");
2396 return 1;
2397 }
2398
2399 // Record the value
2400 //
2401 listdeny = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2402
2403
2404 return 0;
2405}
2406
2407/******************************************************************************/
2408/* x l i s t r e d i r */
2409/******************************************************************************/
2410
2411/* Function: xlistredir
2412
2413 Purpose: To parse the directive: listingredir <Url>
2414
2415 <Url> http/https server to redirect to in the case of listing
2416
2417 Output: 0 upon success or !0 upon failure.
2418 */
2419
2420int XrdHttpProtocol::xlistredir(XrdOucStream & Config) {
2421 char *val;
2422
2423 // Get the path
2424 //
2425 val = Config.GetWord();
2426 if (!val || !val[0]) {
2427 eDest.Emsg("Config", "listingredir flag not specified");
2428 return 1;
2429 }
2430
2431 // Record the value
2432 //
2433 if (listredir) free(listredir);
2434 listredir = strdup(val);
2435
2436
2437 return 0;
2438}
2439
2440/******************************************************************************/
2441/* x s s l d e s t h t t p s */
2442/******************************************************************************/
2443
2444/* Function: xdesthttps
2445
2446 Purpose: To parse the directive: desthttps <yes|no|0|1>
2447
2448 <val> makes this redirector produce http or https redirection targets
2449
2450 Output: 0 upon success or !0 upon failure.
2451 */
2452
2453int XrdHttpProtocol::xdesthttps(XrdOucStream & Config) {
2454 char *val;
2455
2456 // Get the path
2457 //
2458 val = Config.GetWord();
2459 if (!val || !val[0]) {
2460 eDest.Emsg("Config", "desthttps flag not specified");
2461 return 1;
2462 }
2463
2464 // Record the value
2465 //
2466 isdesthttps = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2467
2468
2469 return 0;
2470}
2471
2472/******************************************************************************/
2473/* x e m b e d d e d s t a t i c */
2474/******************************************************************************/
2475
2476/* Function: xembeddedstatic
2477
2478 Purpose: To parse the directive: embeddedstatic <yes|no|0|1|true|false>
2479
2480 <val> this server will redirect HTTPS to itself using HTTP+token
2481
2482 Output: 0 upon success or !0 upon failure.
2483 */
2484
2485int XrdHttpProtocol::xembeddedstatic(XrdOucStream & Config) {
2486 char *val;
2487
2488 // Get the path
2489 //
2490 val = Config.GetWord();
2491 if (!val || !val[0]) {
2492 eDest.Emsg("Config", "embeddedstatic flag not specified");
2493 return 1;
2494 }
2495
2496 // Record the value
2497 //
2498 embeddedstatic = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2499
2500
2501 return 0;
2502}
2503
2504/******************************************************************************/
2505/* x r e d i r s t a t i c */
2506/******************************************************************************/
2507
2508/* Function: xstaticredir
2509
2510 Purpose: To parse the directive: staticredir <Url>
2511
2512 <Url> http/https server to redirect to in the case of /static
2513
2514 Output: 0 upon success or !0 upon failure.
2515 */
2516
2517int XrdHttpProtocol::xstaticredir(XrdOucStream & Config) {
2518 char *val;
2519
2520 // Get the path
2521 //
2522 val = Config.GetWord();
2523 if (!val || !val[0]) {
2524 eDest.Emsg("Config", "staticredir url not specified");
2525 return 1;
2526 }
2527
2528 // Record the value
2529 //
2530 if (staticredir) free(staticredir);
2531 staticredir = strdup(val);
2532
2533 return 0;
2534}
2535
2536/******************************************************************************/
2537/* x p r e l o a d s t a t i c */
2538/******************************************************************************/
2539
2540/* Function: xpreloadstatic
2541
2542 Purpose: To parse the directive: preloadstatic <http url path> <local file>
2543
2544 <http url path> http/http path whose response we are preloading
2545 e.g. /static/mycss.css
2546 NOTE: this must start with /static
2547
2548
2549 Output: 0 upon success or !0 upon failure.
2550 */
2551
2552int XrdHttpProtocol::xstaticpreload(XrdOucStream & Config) {
2553 char *val, *k, key[1024];
2554
2555 // Get the key
2556 //
2557 k = Config.GetWord();
2558 if (!k || !k[0]) {
2559 eDest.Emsg("Config", "preloadstatic urlpath not specified");
2560 return 1;
2561 }
2562
2563 strcpy(key, k);
2564
2565 // Get the val
2566 //
2567 val = Config.GetWord();
2568 if (!val || !val[0]) {
2569 eDest.Emsg("Config", "preloadstatic filename not specified");
2570 return 1;
2571 }
2572
2573 // Try to load the file into memory
2574 int fp = open(val, O_RDONLY);
2575 if( fp < 0 ) {
2576 eDest.Emsg("Config", errno, "open preloadstatic filename", val);
2577 return 1;
2578 }
2579
2581 // Max 64Kb ok?
2582 nfo->data = (char *)malloc(65536);
2583 nfo->len = read(fp, (void *)nfo->data, 65536);
2584 close(fp);
2585
2586 if (nfo->len <= 0) {
2587 eDest.Emsg("Config", errno, "read from preloadstatic filename", val);
2588 return 1;
2589 }
2590
2591 if (nfo->len >= 65536) {
2592 eDest.Emsg("Config", "Truncated preloadstatic filename. Max is 64 KB '", val, "'");
2593 return 1;
2594 }
2595
2596 // Record the value
2597 //
2598 if (!staticpreload)
2599 staticpreload = new XrdOucHash<StaticPreloadInfo>;
2600
2601 staticpreload->Rep((const char *)key, nfo);
2602 return 0;
2603}
2604
2605/******************************************************************************/
2606/* x s t a t i c h e a d e r */
2607/******************************************************************************/
2608
2609//
2610// xstaticheader parses the http.staticheader director with the following syntax:
2611//
2612// http.staticheader [-verb=[GET|HEAD|...]]* header [value]
2613//
2614// When set, this will cause XrdHttp to always return the specified header and
2615// value.
2616//
2617// Setting this option multiple times is additive (multiple headers may be set).
2618// Omitting the value will cause the static header setting to be unset.
2619//
2620// Omitting the -verb argument will cause it the header to be set unconditionally
2621// for all requests.
2622int XrdHttpProtocol::xstaticheader(XrdOucStream & Config) {
2623 auto val = Config.GetWord();
2624 std::vector<std::string> verbs;
2625 while (true) {
2626 if (!val || !val[0]) {
2627 eDest.Emsg("Config", "http.staticheader requires the header to be specified");
2628 return 1;
2629 }
2630
2631 std::string match_verb;
2632 std::string_view val_str(val);
2633 if (val_str.substr(0, 6) == "-verb=") {
2634 verbs.emplace_back(val_str.substr(6));
2635 } else if (val_str == "-") {
2636 eDest.Emsg("Config", "http.staticheader is ignoring unknown flag: ", val_str.data());
2637 } else {
2638 break;
2639 }
2640
2641 val = Config.GetWord();
2642 }
2643 if (verbs.empty()) {
2644 verbs.emplace_back();
2645 }
2646
2647 std::string header = val;
2648
2649 val = Config.GetWord();
2650 std::string header_value;
2651 if (val && val[0]) {
2652 header_value = val;
2653 }
2654
2655 for (const auto &verb : verbs) {
2656 auto iter = m_staticheader_map.find(verb);
2657 if (iter == m_staticheader_map.end()) {
2658 if (!header_value.empty())
2659 m_staticheader_map.insert(iter, {verb, {{header, header_value}}});
2660 } else if (header_value.empty()) {
2661 iter->second.clear();
2662 } else {
2663 iter->second.emplace_back(header, header_value);
2664 }
2665 }
2666
2667 return 0;
2668}
2669
2670
2671/******************************************************************************/
2672/* x s e l f h t t p s 2 h t t p */
2673/******************************************************************************/
2674
2675/* Function: selfhttps2http
2676
2677 Purpose: To parse the directive: selfhttps2http <yes|no|0|1>
2678
2679 <val> this server will redirect HTTPS to itself using HTTP+token
2680
2681 Output: 0 upon success or !0 upon failure.
2682 */
2683
2684int XrdHttpProtocol::xselfhttps2http(XrdOucStream & Config) {
2685 char *val;
2686
2687 // Get the path
2688 //
2689 val = Config.GetWord();
2690 if (!val || !val[0]) {
2691 eDest.Emsg("Config", "selfhttps2http flag not specified");
2692 return 1;
2693 }
2694
2695 // Record the value
2696 //
2697 selfhttps2http = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2698
2699
2700 return 0;
2701}
2702
2703/******************************************************************************/
2704/* x s e c x t r a c t o r */
2705/******************************************************************************/
2706
2707/* Function: xsecxtractor
2708
2709 Purpose: To parse the directive: secxtractor [required] <path> <params>
2710
2711 required optional parameter which if present treats any secxtractor
2712 errors as fatal.
2713 <path> the path of the plugin to be loaded
2714 <params> parameters passed to the secxtractor library
2715
2716 Output: 0 upon success or !0 upon failure.
2717 */
2718
2719int XrdHttpProtocol::xsecxtractor(XrdOucStream& Config) {
2720 char *val;
2721
2722 // Get the path
2723 //
2724 val = Config.GetWord();
2725 if (!val || !val[0]) {
2726 eDest.Emsg("Config", "No security extractor plugin specified.");
2727 return 1;
2728 } else {
2729 // Handle optional parameter [required]
2730 //
2731 if (!strncmp(val, "required", 8)) {
2732 isRequiredXtractor = true;
2733 val = Config.GetWord();
2734
2735 if (!val || !val[0]) {
2736 eDest.Emsg("Config", "No security extractor plugin after [required] "
2737 "parameter");
2738 return 1;
2739 }
2740 }
2741
2742 char libName[4096];
2743 strlcpy(libName, val, sizeof(libName));
2744 libName[sizeof(libName) - 1] = '\0';
2745 char libParms[4096];
2746
2747 if (!Config.GetRest(libParms, 4095)) {
2748 eDest.Emsg("Config", "secxtractor config params longer than 4k");
2749 return 1;
2750 }
2751
2752 // Try to load the plugin (if available) that extracts info from the
2753 // user cert/proxy
2754 if (LoadSecXtractor(&eDest, libName, libParms)) {
2755 return 1;
2756 }
2757 }
2758
2759 return 0;
2760}
2761
2762int XrdHttpProtocol::xcors(XrdOucStream& Config) {
2763 char * val;
2764 // Get the path
2765 val = Config.GetWord();
2766 if (!val || !val[0]) {
2767 eDest.Emsg("Config", "No CORS plugin specified.");
2768 return 1;
2769 }
2770 xrdcorsLibPath = val;
2771 return 0;
2772}
2773
2774/******************************************************************************/
2775/* x e x t h a n d l e r */
2776/******************************************************************************/
2777
2778/* Function: xexthandler
2779 *
2780 * Purpose: To parse the directive: exthandler <name> <path> <initparm>
2781 *
2782 * <name> a unique name (max 16chars) to be given to this
2783 * instance, e.g 'myhandler1'
2784 * <path> the path of the plugin to be loaded
2785 * <initparm> a string parameter (e.g. a config file) that is
2786 * passed to the initialization of the plugin
2787 *
2788 * Output: 0 upon success or !0 upon failure.
2789 */
2790
2791int XrdHttpProtocol::xexthandler(XrdOucStream &Config,
2792 std::vector<extHInfo> &hiVec) {
2793 char *val, path[1024], namebuf[1024];
2794 char *parm;
2795 // By default, every external handler need TLS configured to be loaded
2796 bool noTlsOK = false;
2797
2798 // Get the name
2799 //
2800 val = Config.GetWord();
2801 if (!val || !val[0]) {
2802 eDest.Emsg("Config", "No instance name specified for an http external handler plugin.");
2803 return 1;
2804 }
2805 if (strlen(val) >= 16) {
2806 eDest.Emsg("Config", "Instance name too long for an http external handler plugin.");
2807 return 1;
2808 }
2809 strncpy(namebuf, val, sizeof(namebuf));
2810 namebuf[ sizeof(namebuf)-1 ] = '\0';
2811
2812 // Get the +notls option if it was provided
2813 val = Config.GetWord();
2814
2815 if(val && !strcmp("+notls",val)) {
2816 noTlsOK = true;
2817 val = Config.GetWord();
2818 }
2819
2820 // Get the path
2821 //
2822 if (!val || !val[0]) {
2823 eDest.Emsg("Config", "No http external handler plugin specified.");
2824 return 1;
2825 }
2826 if (strlen(val) >= (int)sizeof(path)) {
2827 eDest.Emsg("Config", "Path too long for an http external handler plugin.");
2828 return 1;
2829 }
2830
2831 strcpy(path, val);
2832
2833 // Everything else is a free string
2834 //
2835 parm = Config.GetWord();
2836
2837 // Verify whether this is a duplicate (we never supported replacements)
2838 //
2839 for (int i = 0; i < (int)hiVec.size(); i++)
2840 {if (hiVec[i].extHName == namebuf) {
2841 eDest.Emsg("Config", "Instance name already present for "
2842 "http external handler plugin",
2843 hiVec[i].extHPath.c_str());
2844 return 1;
2845 }
2846 }
2847
2848 // Verify that we don't have more already than we are allowed to have
2849 //
2850 if (hiVec.size() >= MAX_XRDHTTPEXTHANDLERS) {
2851 eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
2852 return 1;
2853 }
2854
2855 // Create an info struct and push it on the list of ext handlers to load
2856 //
2857 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm : ""), noTlsOK));
2858
2859 return 0;
2860}
2861
2862/******************************************************************************/
2863/* x h e a d e r 2 c g i */
2864/******************************************************************************/
2865
2866/* Function: xheader2cgi
2867 *
2868 * Purpose: To parse the directive: header2cgi <headerkey> <cgikey>
2869 *
2870 * <headerkey> the name of an incoming HTTP header
2871 * to be transformed
2872 * <cgikey> the name to be given when adding it to the cgi info
2873 * that is kept only internally
2874 *
2875 * Output: 0 upon success or !0 upon failure.
2876 */
2877
2878int XrdHttpProtocol::xheader2cgi(XrdOucStream & Config) {
2879 return parseHeader2CGI(Config,eDest,hdr2cgimap);
2880}
2881
2882/******************************************************************************/
2883/* x s s l c a d i r */
2884/******************************************************************************/
2885
2886/* Function: xsslcadir
2887
2888 Purpose: To parse the directive: sslcadir <path>
2889
2890 <path> the path of the server key to be used.
2891
2892 Output: 0 upon success or !0 upon failure.
2893 */
2894
2895int XrdHttpProtocol::xsslcadir(XrdOucStream & Config) {
2896 char *val;
2897
2898 // Get the path
2899 //
2900 val = Config.GetWord();
2901 if (!val || !val[0]) {
2902 eDest.Emsg("Config", "HTTP X509 CAdir not specified");
2903 return 1;
2904 }
2905
2906 // Record the path
2907 //
2908 if (sslcadir) free(sslcadir);
2909 sslcadir = strdup(val);
2910
2911 if (xrdctxVer){ HTTPS_ALERT("cadir","tlsca",false); }
2912 return 0;
2913}
2914
2915/******************************************************************************/
2916/* x s s l c i p h e r f i l t e r */
2917/******************************************************************************/
2918
2919/* Function: xsslcipherfilter
2920
2921 Purpose: To parse the directive: cipherfilter <filter>
2922
2923 <filter> the filter string to be used when generating
2924 the SSL cipher list
2925
2926 Output: 0 upon success or !0 upon failure.
2927 */
2928
2929int XrdHttpProtocol::xsslcipherfilter(XrdOucStream & Config) {
2930 char *val;
2931
2932 // Get the filter string
2933 //
2934 val = Config.GetWord();
2935 if (!val || !val[0]) {
2936 eDest.Emsg("Config", "SSL cipherlist filter string not specified");
2937 return 1;
2938 }
2939
2940 // Record the filter string
2941 //
2943 sslcipherfilter = strdup(val);
2944
2945 return 0;
2946}
2947
2948/******************************************************************************/
2949/* x t l s r e u s e */
2950/******************************************************************************/
2951
2952/* Function: xtlsreuse
2953
2954 Purpose: To parse the directive: tlsreuse {on | off}
2955
2956 Output: 0 upon success or 1 upon failure.
2957 */
2958
2959int XrdHttpProtocol::xtlsreuse(XrdOucStream & Config) {
2960
2961 char *val;
2962
2963// Get the argument
2964//
2965 val = Config.GetWord();
2966 if (!val || !val[0])
2967 {eDest.Emsg("Config", "tlsreuse argument not specified"); return 1;}
2968
2969// If it's off, we set it off
2970//
2971 if (!strcmp(val, "off"))
2973 return 0;
2974 }
2975
2976// If it's on we set it on.
2977//
2978 if (!strcmp(val, "on"))
2980 return 0;
2981 }
2982
2983// Bad argument
2984//
2985 eDest.Emsg("config", "invalid tlsreuse parameter -", val);
2986 return 1;
2987}
2988
2989int XrdHttpProtocol::xtlsclientauth(XrdOucStream &Config) {
2990 auto val = Config.GetWord();
2991 if (!val || !val[0])
2992 {eDest.Emsg("Config", "tlsclientauth argument not specified"); return 1;}
2993
2994 if (!strcmp(val, "off"))
2995 {tlsClientAuth = false;
2996 return 0;
2997 }
2998 if (!strcmp(val, "on"))
2999 {tlsClientAuth = true;
3000 return 0;
3001 }
3002
3003 eDest.Emsg("config", "invalid tlsclientauth parameter -", val);
3004 return 1;
3005}
3006
3007int XrdHttpProtocol::xauth(XrdOucStream &Config) {
3008 char *val = Config.GetWord();
3009 if(val) {
3010 if(!strcmp("tpc",val)) {
3011 if(!(val = Config.GetWord())) {
3012 eDest.Emsg("Config", "http.auth tpc value not specified."); return 1;
3013 } else {
3014 if(!strcmp("fcreds",val)) {
3015 tpcForwardCreds = true;
3016 } else {
3017 eDest.Emsg("Config", "http.auth tpc value is invalid"); return 1;
3018 }
3019 }
3020 } else {
3021 eDest.Emsg("Config", "http.auth value is invalid"); return 1;
3022 }
3023 }
3024 return 0;
3025}
3026
3027int XrdHttpProtocol::xmaxdelay(XrdOucStream &Config) {
3028 char *val = Config.GetWord();
3029 if(val) {
3030 int maxdelay;
3031 if (XrdOuca2x::a2tm(eDest, "http.maxdelay", val, &maxdelay, 1)) return 1;
3032 m_maxdelay = maxdelay;
3033 } else {
3034 eDest.Emsg("Config", "http.maxdelay requires an argument in seconds (default is 30). Example: http.maxdelay 30");
3035 return 1;
3036 }
3037 return 0;
3038}
3039
3040/******************************************************************************/
3041/* x t r a c e */
3042/******************************************************************************/
3043
3044/* Function: xtrace
3045
3046 Purpose: To parse the directive: trace <events>
3047
3048 <events> the blank separated list of events to trace. Trace
3049 directives are cumulative.
3050
3051 Output: 0 upon success or 1 upon failure.
3052 */
3053
3054int XrdHttpProtocol::xtrace(XrdOucStream & Config) {
3055
3056 char *val;
3057
3058 static struct traceopts {
3059 const char *opname;
3060 int opval;
3061 } tropts[] = {
3062 {"all", TRACE_ALL},
3063 {"auth", TRACE_AUTH},
3064 {"debug", TRACE_DEBUG},
3065 {"mem", TRACE_MEM},
3066 {"redirect", TRACE_REDIR},
3067 {"request", TRACE_REQ},
3068 {"response", TRACE_RSP}
3069 };
3070 int i, neg, trval = 0, numopts = sizeof (tropts) / sizeof (struct traceopts);
3071
3072 if (!(val = Config.GetWord())) {
3073 eDest.Emsg("config", "trace option not specified");
3074 return 1;
3075 }
3076 while (val) {
3077 if (!strcmp(val, "off")) trval = 0;
3078 else {
3079 if ((neg = (val[0] == '-' && val[1]))) val++;
3080 for (i = 0; i < numopts; i++) {
3081 if (!strcmp(val, tropts[i].opname)) {
3082 if (neg) trval &= ~tropts[i].opval;
3083 else trval |= tropts[i].opval;
3084 break;
3085 }
3086 }
3087 if (i >= numopts)
3088 eDest.Emsg("config", "invalid trace option", val);
3089 }
3090 val = Config.GetWord();
3091 }
3092 XrdHttpTrace.What = trval;
3093 return 0;
3094}
3095
3096int XrdHttpProtocol::doStat(char *fname) {
3097 int l;
3098 bool b;
3099 CurrentReq.filesize = 0;
3100 CurrentReq.fileflags = 0;
3101 CurrentReq.filemodtime = 0;
3102
3103 memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3104 CurrentReq.xrdreq.stat.requestid = htons(kXR_stat);
3105 memset(CurrentReq.xrdreq.stat.reserved, 0,
3106 sizeof (CurrentReq.xrdreq.stat.reserved));
3107 l = strlen(fname) + 1;
3108 CurrentReq.xrdreq.stat.dlen = htonl(l);
3109
3110 if (!Bridge) return -1;
3111 b = Bridge->Run((char *) &CurrentReq.xrdreq, fname, l);
3112 if (!b) {
3113 return -1;
3114 }
3115
3116
3117 return 0;
3118}
3119
3120/******************************************************************************/
3121/* d o C h k s u m */
3122/******************************************************************************/
3123
3125 size_t length;
3126 memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3127 CurrentReq.xrdreq.query.requestid = htons(kXR_query);
3128 CurrentReq.xrdreq.query.infotype = htons(kXR_Qcksum);
3129 memset(CurrentReq.xrdreq.query.reserved1, '\0', sizeof(CurrentReq.xrdreq.query.reserved1));
3130 memset(CurrentReq.xrdreq.query.fhandle, '\0', sizeof(CurrentReq.xrdreq.query.fhandle));
3131 memset(CurrentReq.xrdreq.query.reserved2, '\0', sizeof(CurrentReq.xrdreq.query.reserved2));
3132 length = fname.length() + 1;
3133 CurrentReq.xrdreq.query.dlen = htonl(length);
3134
3135 if (!Bridge) return -1;
3136
3137 return Bridge->Run(reinterpret_cast<char *>(&CurrentReq.xrdreq), const_cast<char *>(fname.c_str()), length) ? 0 : -1;
3138}
3139
3140
3141static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION);
3142
3143// Loads the SecXtractor plugin, if available
3144int XrdHttpProtocol::LoadSecXtractor(XrdSysError *myeDest, const char *libName,
3145 const char *libParms) {
3146
3147
3148 // We don't want to load it more than once
3149 if (secxtractor) return 1;
3150
3151 XrdOucPinLoader myLib(myeDest, &compiledVer, "secxtractorlib", libName);
3153
3154 // Get the entry point of the object creator
3155 //
3156 ep = (XrdHttpSecXtractor *(*)(XrdHttpSecXtractorArgs))(myLib.Resolve("XrdHttpGetSecXtractor"));
3157 if (ep && (secxtractor = ep(myeDest, NULL, libParms))) return 0;
3158 myLib.Unload();
3159 return 1;
3160}
3161/******************************************************************************/
3162/* L o a d E x t H a n d l e r */
3163/******************************************************************************/
3164
3165int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec, const char *cFN, XrdOucEnv &myEnv) {
3166 for (int i = 0; i < (int) hiVec.size(); i++) {
3167 if(hiVec[i].extHNoTlsOK) {
3168 // The external plugin does not need TLS to be loaded
3169 if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3170 hiVec[i].extHParm.c_str(), &myEnv,
3171 hiVec[i].extHName.c_str()))
3172 return 1;
3173 }
3174 }
3175 return 0;
3176}
3177
3178int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3179 const char *cFN, XrdOucEnv &myEnv) {
3180
3181 // Add the pointer to the cadir and the cakey to the environment.
3182 //
3183 if (sslcadir) myEnv.Put("http.cadir", sslcadir);
3184 if (sslcafile) myEnv.Put("http.cafile", sslcafile);
3185 if (sslcert) myEnv.Put("http.cert", sslcert);
3186 if (sslkey) myEnv.Put("http.key" , sslkey);
3187
3188 // Load all of the specified external handlers.
3189 //
3190 for (int i = 0; i < (int)hiVec.size(); i++) {
3191 // Only load the external handlers that were not already loaded
3192 // by LoadExtHandlerNoTls(...)
3193 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3194 if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3195 hiVec[i].extHParm.c_str(), &myEnv,
3196 hiVec[i].extHName.c_str())) return 1;
3197 }
3198 }
3199 return 0;
3200}
3201
3202// Loads the external handler plugin, if available
3203int XrdHttpProtocol::LoadExtHandler(XrdSysError *myeDest, const char *libName,
3204 const char *configFN, const char *libParms,
3205 XrdOucEnv *myEnv, const char *instName) {
3206
3207
3208 // This function will avoid loading doubles. No idea why this happens
3209 if (ExtHandlerLoaded(instName)) {
3210 eDest.Emsg("Config", "Instance name already present for an http external handler plugin.");
3211 return 1;
3212 }
3213 if (exthandlercnt >= MAX_XRDHTTPEXTHANDLERS) {
3214 eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
3215 return 1;
3216 }
3217
3218 XrdOucPinLoader myLib(myeDest, &compiledVer, "exthandlerlib", libName);
3219 XrdHttpExtHandler *(*ep)(XrdHttpExtHandlerArgs);
3220
3221 // Get the entry point of the object creator
3222 //
3223 ep = (XrdHttpExtHandler *(*)(XrdHttpExtHandlerArgs))(myLib.Resolve("XrdHttpGetExtHandler"));
3224
3225 XrdHttpExtHandler *newhandler;
3226 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3227
3228 // Handler has been loaded, it's the last one in the list
3229 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3230 exthandler[exthandlercnt].name[15] = '\0';
3231 exthandler[exthandlercnt++].ptr = newhandler;
3232
3233 return 0;
3234 }
3235
3236 myLib.Unload();
3237 return 1;
3238}
3239
3240
3241int XrdHttpProtocol::LoadCorsHandler(XrdSysError *eDest, const char *libname) {
3242 if(xrdcors) return 1;
3243 XrdOucPinLoader corsLib(eDest, &compiledVer, "corslib",libname);
3244 XrdHttpCors *(*ep)(XrdHttpCorsGetHandlerArgs);
3245 ep = (XrdHttpCors *(*)(XrdHttpCorsGetHandlerArgs))(corsLib.Resolve("XrdHttpCorsGetHandler"));
3246 if(ep && (xrdcors = ep())) return 0;
3247 corsLib.Unload();
3248 return 1;
3249}
3250
3251// Tells if we have already loaded a certain exthandler. Try to
3252// privilege speed, as this func may be invoked pretty often
3253bool XrdHttpProtocol::ExtHandlerLoaded(const char *handlername) {
3254 for (int i = 0; i < exthandlercnt; i++) {
3255 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3256 return true;
3257 }
3258 }
3259 return false;
3260}
3261
3262// Locates a matching external handler for a given request, if available. Try to
3263// privilege speed, as this func is invoked for every incoming request
3264XrdHttpExtHandler * XrdHttpProtocol::FindMatchingExtHandler(const XrdHttpReq &req) {
3265
3266 for (int i = 0; i < exthandlercnt; i++) {
3267 if (exthandler[i].ptr->MatchesPath(req.requestverb.c_str(), req.resource.c_str())) {
3268 return exthandler[i].ptr;
3269 }
3270 }
3271 return NULL;
3272}
#define kXR_isManager
@ kXR_query
Definition XProtocol.hh:113
@ kXR_set
Definition XProtocol.hh:130
@ kXR_stat
Definition XProtocol.hh:129
#define kXR_isServer
@ kXR_Qcksum
Definition XProtocol.hh:617
int kXR_int32
Definition XPtypes.hh:89
short kXR_int16
Definition XPtypes.hh:66
#define DEBUG(x)
#define TS_Xeq(x, m)
Definition XrdConfig.cc:160
static XrdSysError eDest(0,"crypto_")
bool usingEC
#define XrdHttpCorsGetHandlerArgs
#define XrdHttpExtHandlerArgs
int BIO_get_init(BIO *bio)
int BIO_get_shutdown(BIO *bio)
int BIO_get_flags(BIO *bio)
static int BIO_XrdLink_create(BIO *bio)
const char * XrdHttpSecEntityTident
void BIO_set_init(BIO *bio, int init)
int BIO_XrdLink_write(BIO *bio, const char *data, size_t datal, size_t *written)
#define HTTPS_ALERT(x, y, z)
static long BIO_XrdLink_ctrl(BIO *bio, int cmd, long num, void *ptr)
void BIO_set_shutdown(BIO *bio, int shut)
XrdSysTrace XrdHttpTrace("http")
static int BIO_XrdLink_read(BIO *bio, char *data, size_t datal, size_t *read)
void BIO_set_data(BIO *bio, void *ptr)
#define TS_Xeq3(x, m)
static int BIO_XrdLink_destroy(BIO *bio)
#define XRHTTP_TK_GRACETIME
static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION)
void * BIO_get_data(BIO *bio)
void BIO_set_flags(BIO *bio, int flags)
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
#define MAX_XRDHTTPEXTHANDLERS
#define XrdHttpSecXtractorArgs
Trace definitions.
#define TRACE_AUTH
#define TRACE_REQ
#define TRACE_RSP
#define TRACE_REDIR
int compareHash(const char *h1, const char *h2)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
std::string httpStatusToString(int status)
Utility functions for XrdHTTP.
std::string decode_str(const std::string &str)
std::string obfuscateAuth(const std::string &input)
int fclose(FILE *stream)
#define close(a)
Definition XrdPosix.hh:48
#define fstat(a, b)
Definition XrdPosix.hh:62
#define open
Definition XrdPosix.hh:76
#define stat(a, b)
Definition XrdPosix.hh:101
#define read(a, b, c)
Definition XrdPosix.hh:82
#define eMsg(x)
struct myOpts opts
size_t strlcpy(char *dst, const char *src, size_t sz)
#define TLS_SET_VDEPTH(cOpts, vdv)
#define TLS_SET_REFINT(cOpts, refi)
#define TRACE_DEBUG
Definition XrdTrace.hh:36
#define TRACE_MEM
Definition XrdTrace.hh:38
#define TRACE(act, x)
Definition XrdTrace.hh:63
#define TRACE_ALL
Definition XrdTrace.hh:35
#define TRACING(x)
Definition XrdTrace.hh:70
#define TRACEI(act, x)
Definition XrdTrace.hh:66
char * buff
Definition XrdBuffer.hh:45
const std::vector< std::string > & getNonIANAConfiguredCksums() const
void configure(const char *csList)
virtual int Configure(const char *configFN, XrdSysError *errP)=0
static char * secretkey
The key used to calculate the url hashes.
static BIO_METHOD * m_bio_method
C-style vptr table for our custom BIO objects.
static char * gridmap
Gridmap file location. The same used by XrdSecGsi.
static XrdScheduler * Sched
static kXR_int32 myRole
Our role.
static char * sslcafile
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
static char * Port_str
Our port, as a string.
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdSysError eDest
static bool selfhttps2http
If client is HTTPS, self-redirect with HTTP+token.
static XrdHttpChecksumHandler cksumHandler
static int hailWait
Timeout for reading the handshake.
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static char * xrd_cslist
The list of checksums that were configured via the xrd.cksum parameter on the server config file.
static char * sslcipherfilter
static int m_bio_type
Type identifier for our custom BIO objects.
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
static char * sslcert
OpenSSL stuff.
XrdLink * Link
The link we are bound to.
static char * sslkey
int doStat(char *fname)
Perform a Stat request.
XrdObject< XrdHttpProtocol > ProtLink
static int readWait
Timeout for reading data.
void Recycle(XrdLink *lp, int consec, const char *reason)
Recycle this instance.
static std::unordered_map< std::string, std::vector< std::pair< std::string, std::string > > > m_staticheader_map
The static headers to always return; map is from verb to a list of (header, val) pairs.
static char * sslcadir
XrdHttpProtocol operator=(const XrdHttpProtocol &rhs)
static XrdHttpCors * xrdcors
static bool compatNameGeneration
static std::string xrdcorsLibPath
static bool isdesthttps
True if the redirections must be towards https targets.
static XrdObjectQ< XrdHttpProtocol > ProtStack
XrdProtocol * Match(XrdLink *lp)
Tells if the oustanding bytes on the socket match this protocol implementation.
static bool isRequiredGridmap
static char * listredir
Url to redirect to in the case a listing is requested.
int Stats(char *buff, int blen, int do_sync=0)
Get activity stats.
static int crlRefIntervalSec
CRL thread refresh interval.
static int Port
Our port.
static XrdHttpReadRangeHandler::Configuration ReadRangeConfig
configuration for the read range handler
static XrdSecService * CIA
static XrdBuffManager * BPool
static std::unordered_map< std::string, std::string > m_staticheaders
static bool tpcForwardCreds
If set to true, the HTTP TPC transfers will forward the credentials to redirected hosts.
int Process(XrdLink *lp)
Process data incoming from the socket.
XrdHttpProtocol(const XrdHttpProtocol &)=default
Ctor, dtors and copy ctor.
static bool listdeny
If true, any form of listing is denied.
static int parseHeader2CGI(XrdOucStream &Config, XrdSysError &err, std::map< std::string, std::string > &header2cgi)
Use this function to parse header2cgi configurations.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
static int sslverifydepth
Depth of verification of a certificate chain.
static int Configure(char *parms, XrdProtocol_Config *pi)
Read and apply the configuration.
static int Configure(XrdSysError &Eroute, const char *const parms, Configuration &cfg)
XrdOucString resource
The resource specified by the request, stripped of opaque data.
std::string requestverb
static const int noPort
Do not add port number.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
void SetDialect(const char *dP)
void SetTLS(bool val)
static bool Import(const char *var, char *&val)
Definition XrdOucEnv.cc: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.