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