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