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