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