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