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),
198SecEntity(""), CurrentReq(this, ReadRangeConfig) {
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)) {
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
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));
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
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)
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 &&
681 char hash[512];
682 time_t timenow = time(0);
683
684
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);
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 = unquote(nfo);
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 = unquote(nfo);
804 TRACEI(REQ, " Setting host: " << SecEntity.host);
805 }
806
807 nfo = CurrentReq.opaque->Get("xrdhttpdn");
808 if (nfo) {
809 TRACEI(DEBUG, " Setting dn: " << nfo);
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 = unquote(nfo);
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 = unquote(nfo);
825 TRACEI(REQ, " Setting grps: " << SecEntity.grps);
826 }
827
828 nfo = CurrentReq.opaque->Get("xrdhttpendorsements");
829 if (nfo) {
830 TRACEI(DEBUG, " Setting endorsements: " << nfo);
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 = unquote(nfo);
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 = unquote(nfo);
851 TRACEI(REQ, " Setting creds: " << SecEntity.creds);
852 }
853 }
854
855 char hash[512];
856
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
909 if (rc < 0)
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 //
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 //
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)
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, const char *header_to_add, long long bodylen, bool keepalive) {
1594 std::stringstream ss;
1595 const std::string crlf = "\r\n";
1596
1597 ss << "HTTP/1.1 " << code << " ";
1598 if (desc) {
1599 ss << desc;
1600 } else {
1601 if (code == 200) ss << "OK";
1602 else if (code == 100) ss << "Continue";
1603 else if (code == 206) ss << "Partial Content";
1604 else if (code == 302) ss << "Redirect";
1605 else if (code == 307) ss << "Temporary Redirect";
1606 else if (code == 400) ss << "Bad Request";
1607 else if (code == 403) ss << "Forbidden";
1608 else if (code == 404) ss << "Not Found";
1609 else if (code == 405) ss << "Method Not Allowed";
1610 else if (code == 416) ss << "Range Not Satisfiable";
1611 else if (code == 500) ss << "Internal Server Error";
1612 else if (code == 502) ss << "Bad Gateway";
1613 else if (code == 504) ss << "Gateway Timeout";
1614 else ss << "Unknown";
1615 }
1616 ss << crlf;
1617 if (keepalive && (code != 100))
1618 ss << "Connection: Keep-Alive" << crlf;
1619 else
1620 ss << "Connection: Close" << crlf;
1621
1622 ss << "Server: XrootD/" << XrdVSTRING << crlf;
1623
1624 const auto iter = m_staticheaders.find(CurrentReq.requestverb);
1625 if (iter != m_staticheaders.end()) {
1626 ss << iter->second;
1627 } else {
1628 ss << m_staticheaders[""];
1629 }
1630
1631 if ((bodylen >= 0) && (code != 100))
1632 ss << "Content-Length: " << bodylen << crlf;
1633
1634 if (header_to_add && (header_to_add[0] != '\0'))
1635 ss << header_to_add << crlf;
1636
1637 ss << crlf;
1638
1639 const std::string &outhdr = ss.str();
1640 TRACEI(RSP, "Sending resp: " << code << " header len:" << outhdr.size());
1641 if (SendData(outhdr.c_str(), outhdr.size()))
1642 return -1;
1643
1644 return 0;
1645}
1646
1647/******************************************************************************/
1648/* S t a r t C h u n k e d R e s p */
1649/******************************************************************************/
1650
1651int XrdHttpProtocol::StartChunkedResp(int code, const char *desc, const char *header_to_add, long long bodylen, bool keepalive) {
1652 const std::string crlf = "\r\n";
1653 std::stringstream ss;
1654
1655 if (header_to_add && (header_to_add[0] != '\0')) {
1656 ss << header_to_add << crlf;
1657 }
1658
1659 ss << "Transfer-Encoding: chunked";
1660 TRACEI(RSP, "Starting chunked response");
1661 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1662}
1663
1664/******************************************************************************/
1665/* C h u n k R e s p */
1666/******************************************************************************/
1667
1668int XrdHttpProtocol::ChunkResp(const char *body, long long bodylen) {
1669 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1670 if (ChunkRespHeader(content_length))
1671 return -1;
1672
1673 if (body && SendData(body, content_length))
1674 return -1;
1675
1676 return ChunkRespFooter();
1677}
1678
1679/******************************************************************************/
1680/* C h u n k R e s p H e a d e r */
1681/******************************************************************************/
1682
1683int XrdHttpProtocol::ChunkRespHeader(long long bodylen) {
1684 const std::string crlf = "\r\n";
1685 std::stringstream ss;
1686
1687 ss << std::hex << bodylen << std::dec << crlf;
1688
1689 const std::string &chunkhdr = ss.str();
1690 TRACEI(RSP, "Sending encoded chunk of size " << bodylen);
1691 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1692}
1693
1694/******************************************************************************/
1695/* C h u n k R e s p F o o t e r */
1696/******************************************************************************/
1697
1698int XrdHttpProtocol::ChunkRespFooter() {
1699 const std::string crlf = "\r\n";
1700 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1701}
1702
1703/******************************************************************************/
1704/* S e n d S i m p l e R e s p */
1705/******************************************************************************/
1706
1710
1711int XrdHttpProtocol::SendSimpleResp(int code, const char *desc, const char *header_to_add, const char *body, long long bodylen, bool keepalive) {
1712
1713 long long content_length = bodylen;
1714 if (bodylen <= 0) {
1715 content_length = body ? strlen(body) : 0;
1716 }
1717
1718 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1719 return -1;
1720
1721 //
1722 // Send the data
1723 //
1724 if (body)
1725 return SendData(body, content_length);
1726
1727 return 0;
1728}
1729
1730/******************************************************************************/
1731/* C o n f i g u r e */
1732/******************************************************************************/
1733
1735 /*
1736 Function: Establish configuration at load time.
1737
1738 Input: None.
1739
1740 Output: 0 upon success or !0 otherwise.
1741 */
1742
1743 char *rdf;
1744
1745 // Copy out the special info we want to use at top level
1746 //
1747 eDest.logger(pi->eDest->logger());
1749 // SI = new XrdXrootdStats(pi->Stats);
1750 Sched = pi->Sched;
1751 BPool = pi->BPool;
1752 xrd_cslist = getenv("XRD_CSLIST");
1753
1754 Port = pi->Port;
1755
1756 // Copy out the current TLS context
1757 //
1758 xrdctx = pi->tlsCtx;
1759
1760 {
1761 char buf[16];
1762 sprintf(buf, "%d", Port);
1763 Port_str = strdup(buf);
1764 }
1765
1766 // Now process and configuration parameters
1767 //
1768 rdf = (parms && *parms ? parms : pi->ConfigFN);
1769 if (rdf && Config(rdf, pi->theEnv)) return 0;
1771
1772 // Set the redirect flag if we are a pure redirector
1774 if ((rdf = getenv("XRDROLE"))) {
1775 eDest.Emsg("Config", "XRDROLE: ", rdf);
1776
1777 if (!strcasecmp(rdf, "manager") || !strcasecmp(rdf, "supervisor")) {
1779 eDest.Emsg("Config", "Configured as HTTP(s) redirector.");
1780 } else {
1781
1782 eDest.Emsg("Config", "Configured as HTTP(s) data server.");
1783 }
1784
1785 } else {
1786 eDest.Emsg("Config", "No XRDROLE specified.");
1787 }
1788
1789 // Schedule protocol object cleanup
1790 //
1793 ProtStack.Set((pi->ConnMax / 3 ? pi->ConnMax / 3 : 30), 60 * 60);
1794
1795 // Return success
1796 //
1797
1798 return 1;
1799}
1800
1801/******************************************************************************/
1802/* p a r s e H e a d e r 2 C G I */
1803/******************************************************************************/
1804int XrdHttpProtocol::parseHeader2CGI(XrdOucStream &Config, XrdSysError & err,std::map<std::string, std::string> &header2cgi) {
1805 char *val, keybuf[1024], parmbuf[1024];
1806 char *parm;
1807
1808 // Get the header key
1809 val = Config.GetWord();
1810 if (!val || !val[0]) {
1811 err.Emsg("Config", "No headerkey specified.");
1812 return 1;
1813 } else {
1814
1815 // Trim the beginning, in place
1816 while ( *val && !isalnum(*val) ) val++;
1817 strcpy(keybuf, val);
1818
1819 // Trim the end, in place
1820 char *pp;
1821 pp = keybuf + strlen(keybuf) - 1;
1822 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1823 *pp = '\0';
1824 pp--;
1825 }
1826
1827 parm = Config.GetWord();
1828
1829 // Avoids segfault in case a key is given without value
1830 if(!parm || !parm[0]) {
1831 err.Emsg("Config", "No header2cgi value specified. key: '", keybuf, "'");
1832 return 1;
1833 }
1834
1835 // Trim the beginning, in place
1836 while ( *parm && !isalnum(*parm) ) parm++;
1837 strcpy(parmbuf, parm);
1838
1839 // Trim the end, in place
1840 pp = parmbuf + strlen(parmbuf) - 1;
1841 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1842 *pp = '\0';
1843 pp--;
1844 }
1845
1846 // Add this mapping to the map that will be used
1847 try {
1848 header2cgi[keybuf] = parmbuf;
1849 } catch ( ... ) {
1850 err.Emsg("Config", "Can't insert new header2cgi rule. key: '", keybuf, "'");
1851 return 1;
1852 }
1853
1854 }
1855 return 0;
1856}
1857
1858
1859/******************************************************************************/
1860/* I n i t T L S */
1861/******************************************************************************/
1862
1863bool XrdHttpProtocol::InitTLS() {
1864
1865 std::string eMsg;
1868
1869// Create a new TLS context
1870//
1871 if (sslverifydepth > 255) sslverifydepth = 255;
1873 //TLS_SET_REFINT will set the refresh interval in minutes, hence the division by 60
1876
1877// Make sure the context was created
1878//
1879 if (!xrdctx->isOK())
1880 {eDest.Say("Config failure: ", eMsg.c_str());
1881 return false;
1882 }
1883
1884// Setup session cache (this is controversial). The default is off but many
1885// programs expect it being enabled and break when it is disabled. In such
1886// cases it should be enabled. This is, of course, a big OpenSSL mess.
1887//
1888 static const char *sess_ctx_id = "XrdHTTPSessionCtx";
1889 unsigned int n =(unsigned int)(strlen(sess_ctx_id)+1);
1890 xrdctx->SessionCache(tlsCache, sess_ctx_id, n);
1891
1892// Set special ciphers if so specified.
1893//
1895 {eDest.Say("Config failure: ", "Unable to set allowable https ciphers!");
1896 return false;
1897 }
1898
1899// All done
1900//
1901 return true;
1902}
1903
1904/******************************************************************************/
1905/* C l e a n u p */
1906/******************************************************************************/
1907
1908void XrdHttpProtocol::Cleanup() {
1909
1910 TRACE(ALL, " Cleanup");
1911
1912 if (BPool && myBuff) {
1913 BuffConsume(BuffUsed());
1914 BPool->Release(myBuff);
1915 myBuff = 0;
1916 }
1917
1918 if (ssl) {
1919 // Shutdown the SSL/TLS connection
1920 // https://www.openssl.org/docs/man1.0.2/man3/SSL_shutdown.html
1921 // We don't need a bidirectional shutdown as
1922 // when we are here, the connection will not be re-used.
1923 // In the case SSL_shutdown returns 0,
1924 // "the output of SSL_get_error(3) may be misleading, as an erroneous SSL_ERROR_SYSCALL may be flagged even though no error occurred."
1925 // we will then just flush the thread's queue.
1926 // In the case an error really happened, we print the error that happened
1927 int ret = SSL_shutdown(ssl);
1928 if (ret != 1) {
1929 if(ret == 0) {
1930 // Clean this thread's error queue for the old openssl versions
1931 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1932 ERR_remove_thread_state(nullptr);
1933 #endif
1934 } else {
1935 //ret < 0, an error really happened.
1936 TRACE(ALL, " SSL_shutdown failed");
1937 ERR_print_errors(sslbio_err);
1938 }
1939 }
1940
1941 if (secxtractor)
1942 secxtractor->FreeSSL(ssl);
1943
1944 SSL_free(ssl);
1945
1946 }
1947
1948
1949 ssl = 0;
1950 sbio = 0;
1951
1952 if (SecEntity.caps) free(SecEntity.caps);
1953 if (SecEntity.grps) free(SecEntity.grps);
1955 if (SecEntity.vorg) free(SecEntity.vorg);
1956 if (SecEntity.role) free(SecEntity.role);
1957 if (SecEntity.name) free(SecEntity.name);
1958 if (SecEntity.host) free(SecEntity.host);
1960
1961 SecEntity.Reset();
1962
1963 if (Addr_str) free(Addr_str);
1964 Addr_str = 0;
1965}
1966
1967/******************************************************************************/
1968/* R e s e t */
1969/******************************************************************************/
1970
1971void XrdHttpProtocol::Reset() {
1972
1973 TRACE(ALL, " Reset");
1974 Link = 0;
1975 CurrentReq.reset();
1976 CurrentReq.reqstate = 0;
1977
1978 if (myBuff) {
1979 BPool->Release(myBuff);
1980 myBuff = 0;
1981 }
1982 myBuffStart = myBuffEnd = 0;
1983
1984 DoingLogin = false;
1985 DoneSetInfo = false;
1986
1987 ResumeBytes = 0;
1988 Resume = 0;
1989
1990 //
1991 // numReads = 0;
1992 // numReadP = 0;
1993 // numReadV = 0;
1994 // numSegsV = 0;
1995 // numWrites = 0;
1996 // numFiles = 0;
1997 // cumReads = 0;
1998 // cumReadV = 0;
1999 // cumSegsV = 0;
2000 // cumWrites = 0;
2001 // totReadP = 0;
2002
2003 SecEntity.Reset();
2005 ishttps = false;
2006 ssldone = false;
2007
2008 Bridge = 0;
2009 ssl = 0;
2010 sbio = 0;
2011
2012}
2013
2014/******************************************************************************/
2015/* x h t t p s m o d e */
2016/******************************************************************************/
2017
2018/* Function: xhttpsmode
2019
2020 Purpose: To parse the directive: httpsmode {auto | disable | manual}
2021
2022 auto configure https if configured in xrd framework.
2023 disable do not configure https no matter what
2024 manual configure https and ignore the xrd framework
2025
2026 Output: 0 upon success or !0 upon failure.
2027 */
2028
2029int XrdHttpProtocol::xhttpsmode(XrdOucStream & Config) {
2030 char *val;
2031
2032 // Get the val
2033 //
2034 val = Config.GetWord();
2035 if (!val || !val[0]) {
2036 eDest.Emsg("Config", "httpsmode parameter not specified");
2037 return 1;
2038 }
2039
2040 // Record the val
2041 //
2042 if (!strcmp(val, "auto")) httpsmode = hsmAuto;
2043 else if (!strcmp(val, "disable")) httpsmode = hsmOff;
2044 else if (!strcmp(val, "manual")) httpsmode = hsmMan;
2045 else {eDest.Emsg("Config", "invalid httpsmode parameter - ", val);
2046 return 1;
2047 }
2048 return 0;
2049}
2050
2051/******************************************************************************/
2052/* x s s l v e r i f y d e p t h */
2053/******************************************************************************/
2054
2055/* Function: xsslverifydepth
2056
2057 Purpose: To parse the directive: sslverifydepth <depth>
2058
2059 <depth> the max depth of the ssl cert verification
2060
2061 Output: 0 upon success or !0 upon failure.
2062 */
2063
2064int XrdHttpProtocol::xsslverifydepth(XrdOucStream & Config) {
2065 char *val;
2066
2067 // Get the val
2068 //
2069 val = Config.GetWord();
2070 if (!val || !val[0]) {
2071 eDest.Emsg("Config", "sslverifydepth value not specified");
2072 return 1;
2073 }
2074
2075 // Record the val
2076 //
2077 sslverifydepth = atoi(val);
2078
2079 if (xrdctxVer){ HTTPS_ALERT("verifydepth","tlsca",false); }
2080 return 0;
2081}
2082
2083/******************************************************************************/
2084/* x s s l c e r t */
2085/******************************************************************************/
2086
2087/* Function: xsslcert
2088
2089 Purpose: To parse the directive: sslcert <path>
2090
2091 <path> the path of the server certificate to be used.
2092
2093 Output: 0 upon success or !0 upon failure.
2094 */
2095
2096int XrdHttpProtocol::xsslcert(XrdOucStream & Config) {
2097 char *val;
2098
2099 // Get the path
2100 //
2101 val = Config.GetWord();
2102 if (!val || !val[0]) {
2103 eDest.Emsg("Config", "HTTP X509 certificate not specified");
2104 return 1;
2105 }
2106
2107 // Record the path
2108 //
2109 if (sslcert) free(sslcert);
2110 sslcert = strdup(val);
2111
2112 // If we have an xrd context issue reminder
2113 //
2114 HTTPS_ALERT("cert","tls",true);
2115 return 0;
2116}
2117
2118/******************************************************************************/
2119/* x s s l k e y */
2120/******************************************************************************/
2121
2122/* Function: xsslkey
2123
2124 Purpose: To parse the directive: sslkey <path>
2125
2126 <path> the path of the server key to be used.
2127
2128 Output: 0 upon success or !0 upon failure.
2129 */
2130
2131int XrdHttpProtocol::xsslkey(XrdOucStream & Config) {
2132 char *val;
2133
2134 // Get the path
2135 //
2136 val = Config.GetWord();
2137 if (!val || !val[0]) {
2138 eDest.Emsg("Config", "HTTP X509 key not specified");
2139 return 1;
2140 }
2141
2142 // Record the path
2143 //
2144 if (sslkey) free(sslkey);
2145 sslkey = strdup(val);
2146
2147 HTTPS_ALERT("key","tls",true);
2148 return 0;
2149}
2150
2151/******************************************************************************/
2152/* x g m a p */
2153/******************************************************************************/
2154
2155/* Function: xgmap
2156
2157 Purpose: To parse the directive: gridmap [required] [compatNameGeneration] <path>
2158
2159 required optional parameter which if present treats any grimap errors
2160 as fatal.
2161 <path> the path of the gridmap file to be used. Normally it's
2162 /etc/grid-security/gridmap. No mapfile means no translation
2163 required. Pointing to a non existing mapfile is an error.
2164
2165 Output: 0 upon success or !0 upon failure.
2166 */
2167
2168int XrdHttpProtocol::xgmap(XrdOucStream & Config) {
2169 char *val;
2170
2171 // Get the path
2172 //
2173 val = Config.GetWord();
2174 if (!val || !val[0]) {
2175 eDest.Emsg("Config", "HTTP X509 gridmap file location not specified");
2176 return 1;
2177 }
2178
2179 // Handle optional parameter "required"
2180 //
2181 if (!strncmp(val, "required", 8)) {
2182 isRequiredGridmap = true;
2183 val = Config.GetWord();
2184
2185 if (!val || !val[0]) {
2186 eDest.Emsg("Config", "HTTP X509 gridmap file missing after [required] "
2187 "parameter");
2188 return 1;
2189 }
2190 }
2191
2192 // Handle optional parameter "compatNameGeneration"
2193 //
2194 if (!strcmp(val, "compatNameGeneration")) {
2195 compatNameGeneration = true;
2196 val = Config.GetWord();
2197 if (!val || !val[0]) {
2198 eDest.Emsg("Config", "HTTP X509 gridmap file missing after "
2199 "[compatNameGeneration] parameter");
2200 return 1;
2201 }
2202 }
2203
2204
2205 // Record the path
2206 //
2207 if (gridmap) free(gridmap);
2208 gridmap = strdup(val);
2209 return 0;
2210}
2211
2212/******************************************************************************/
2213/* x s s l c a f i l e */
2214/******************************************************************************/
2215
2216/* Function: xsslcafile
2217
2218 Purpose: To parse the directive: sslcafile <path>
2219
2220 <path> the path of the server key to be used.
2221
2222 Output: 0 upon success or !0 upon failure.
2223 */
2224
2225int XrdHttpProtocol::xsslcafile(XrdOucStream & Config) {
2226 char *val;
2227
2228 // Get the path
2229 //
2230 val = Config.GetWord();
2231 if (!val || !val[0]) {
2232 eDest.Emsg("Config", "HTTP X509 CAfile not specified");
2233 return 1;
2234 }
2235
2236 // Record the path
2237 //
2238 if (sslcafile) free(sslcafile);
2239 sslcafile = strdup(val);
2240
2241 if (xrdctxVer){ HTTPS_ALERT("cafile","tlsca",false); }
2242 return 0;
2243}
2244
2245/******************************************************************************/
2246/* x s e c r e t k e y */
2247/******************************************************************************/
2248
2249/* Function: xsecretkey
2250
2251 Purpose: To parse the directive: xsecretkey <key>
2252
2253 <key> the key to be used
2254
2255 Output: 0 upon success or !0 upon failure.
2256 */
2257
2258int XrdHttpProtocol::xsecretkey(XrdOucStream & Config) {
2259 char *val;
2260 bool inFile = false;
2261
2262 // Get the path
2263 //
2264 val = Config.GetWord();
2265 if (!val || !val[0]) {
2266 eDest.Emsg("Config", "Shared secret key not specified");
2267 return 1;
2268 }
2269
2270
2271 // If the token starts with a slash, then we interpret it as
2272 // the path to a file that contains the secretkey
2273 // otherwise, the token itself is the secretkey
2274 if (val[0] == '/') {
2275 struct stat st;
2276 inFile = true;
2277 int fd = open(val, O_RDONLY);
2278
2279 if ( fd == -1 ) {
2280 eDest.Emsg("Config", errno, "open shared secret key file", val);
2281 return 1;
2282 }
2283
2284 if ( fstat(fd, &st) != 0 ) {
2285 eDest.Emsg("Config", errno, "fstat shared secret key file", val);
2286 close(fd);
2287 return 1;
2288 }
2289
2290 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2291 eDest.Emsg("Config",
2292 "For your own security, the shared secret key file cannot be world readable or group writable '", val, "'");
2293 close(fd);
2294 return 1;
2295 }
2296
2297 FILE *fp = fdopen(fd, "r");
2298
2299 if ( fp == nullptr ) {
2300 eDest.Emsg("Config", errno, "fdopen shared secret key file", val);
2301 close(fd);
2302 return 1;
2303 }
2304
2305 char line[1024];
2306 while( fgets(line, 1024, fp) ) {
2307 char *pp;
2308
2309 // Trim the end
2310 pp = line + strlen(line) - 1;
2311 while ( (pp >= line) && (!isalnum(*pp)) ) {
2312 *pp = '\0';
2313 pp--;
2314 }
2315
2316 // Trim the beginning
2317 pp = line;
2318 while ( *pp && !isalnum(*pp) ) pp++;
2319
2320 if ( strlen(pp) >= 32 ) {
2321 eDest.Say("Config", "Secret key loaded.");
2322 // Record the path
2323 if (secretkey) free(secretkey);
2324 secretkey = strdup(pp);
2325
2326 fclose(fp);
2327 return 0;
2328 }
2329
2330 }
2331
2332 fclose(fp);
2333 eDest.Emsg("Config", "Cannot find useful secretkey in file '", val, "'");
2334 return 1;
2335
2336 }
2337
2338 if ( strlen(val) < 32 ) {
2339 eDest.Emsg("Config", "Secret key is too short");
2340 return 1;
2341 }
2342
2343 // Record the path
2344 if (secretkey) free(secretkey);
2345 secretkey = strdup(val);
2346 if (!inFile) Config.noEcho();
2347
2348 return 0;
2349}
2350
2351/******************************************************************************/
2352/* x l i s t d e n y */
2353/******************************************************************************/
2354
2355/* Function: xlistdeny
2356
2357 Purpose: To parse the directive: listingdeny <yes|no|0|1>
2358
2359 <val> makes this redirector deny listings with an error
2360
2361 Output: 0 upon success or !0 upon failure.
2362 */
2363
2364int XrdHttpProtocol::xlistdeny(XrdOucStream & Config) {
2365 char *val;
2366
2367 // Get the path
2368 //
2369 val = Config.GetWord();
2370 if (!val || !val[0]) {
2371 eDest.Emsg("Config", "listingdeny flag not specified");
2372 return 1;
2373 }
2374
2375 // Record the value
2376 //
2377 listdeny = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2378
2379
2380 return 0;
2381}
2382
2383/******************************************************************************/
2384/* x l i s t r e d i r */
2385/******************************************************************************/
2386
2387/* Function: xlistredir
2388
2389 Purpose: To parse the directive: listingredir <Url>
2390
2391 <Url> http/https server to redirect to in the case of listing
2392
2393 Output: 0 upon success or !0 upon failure.
2394 */
2395
2396int XrdHttpProtocol::xlistredir(XrdOucStream & Config) {
2397 char *val;
2398
2399 // Get the path
2400 //
2401 val = Config.GetWord();
2402 if (!val || !val[0]) {
2403 eDest.Emsg("Config", "listingredir flag not specified");
2404 return 1;
2405 }
2406
2407 // Record the value
2408 //
2409 if (listredir) free(listredir);
2410 listredir = strdup(val);
2411
2412
2413 return 0;
2414}
2415
2416/******************************************************************************/
2417/* x s s l d e s t h t t p s */
2418/******************************************************************************/
2419
2420/* Function: xdesthttps
2421
2422 Purpose: To parse the directive: desthttps <yes|no|0|1>
2423
2424 <val> makes this redirector produce http or https redirection targets
2425
2426 Output: 0 upon success or !0 upon failure.
2427 */
2428
2429int XrdHttpProtocol::xdesthttps(XrdOucStream & Config) {
2430 char *val;
2431
2432 // Get the path
2433 //
2434 val = Config.GetWord();
2435 if (!val || !val[0]) {
2436 eDest.Emsg("Config", "desthttps flag not specified");
2437 return 1;
2438 }
2439
2440 // Record the value
2441 //
2442 isdesthttps = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2443
2444
2445 return 0;
2446}
2447
2448/******************************************************************************/
2449/* x e m b e d d e d s t a t i c */
2450/******************************************************************************/
2451
2452/* Function: xembeddedstatic
2453
2454 Purpose: To parse the directive: embeddedstatic <yes|no|0|1|true|false>
2455
2456 <val> this server will redirect HTTPS to itself using HTTP+token
2457
2458 Output: 0 upon success or !0 upon failure.
2459 */
2460
2461int XrdHttpProtocol::xembeddedstatic(XrdOucStream & Config) {
2462 char *val;
2463
2464 // Get the path
2465 //
2466 val = Config.GetWord();
2467 if (!val || !val[0]) {
2468 eDest.Emsg("Config", "embeddedstatic flag not specified");
2469 return 1;
2470 }
2471
2472 // Record the value
2473 //
2474 embeddedstatic = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2475
2476
2477 return 0;
2478}
2479
2480/******************************************************************************/
2481/* x r e d i r s t a t i c */
2482/******************************************************************************/
2483
2484/* Function: xstaticredir
2485
2486 Purpose: To parse the directive: staticredir <Url>
2487
2488 <Url> http/https server to redirect to in the case of /static
2489
2490 Output: 0 upon success or !0 upon failure.
2491 */
2492
2493int XrdHttpProtocol::xstaticredir(XrdOucStream & Config) {
2494 char *val;
2495
2496 // Get the path
2497 //
2498 val = Config.GetWord();
2499 if (!val || !val[0]) {
2500 eDest.Emsg("Config", "staticredir url not specified");
2501 return 1;
2502 }
2503
2504 // Record the value
2505 //
2506 if (staticredir) free(staticredir);
2507 staticredir = strdup(val);
2508
2509 return 0;
2510}
2511
2512/******************************************************************************/
2513/* x p r e l o a d s t a t i c */
2514/******************************************************************************/
2515
2516/* Function: xpreloadstatic
2517
2518 Purpose: To parse the directive: preloadstatic <http url path> <local file>
2519
2520 <http url path> http/http path whose response we are preloading
2521 e.g. /static/mycss.css
2522 NOTE: this must start with /static
2523
2524
2525 Output: 0 upon success or !0 upon failure.
2526 */
2527
2528int XrdHttpProtocol::xstaticpreload(XrdOucStream & Config) {
2529 char *val, *k, key[1024];
2530
2531 // Get the key
2532 //
2533 k = Config.GetWord();
2534 if (!k || !k[0]) {
2535 eDest.Emsg("Config", "preloadstatic urlpath not specified");
2536 return 1;
2537 }
2538
2539 strcpy(key, k);
2540
2541 // Get the val
2542 //
2543 val = Config.GetWord();
2544 if (!val || !val[0]) {
2545 eDest.Emsg("Config", "preloadstatic filename not specified");
2546 return 1;
2547 }
2548
2549 // Try to load the file into memory
2550 int fp = open(val, O_RDONLY);
2551 if( fp < 0 ) {
2552 eDest.Emsg("Config", errno, "open preloadstatic filename", val);
2553 return 1;
2554 }
2555
2556 StaticPreloadInfo *nfo = new StaticPreloadInfo;
2557 // Max 64Kb ok?
2558 nfo->data = (char *)malloc(65536);
2559 nfo->len = read(fp, (void *)nfo->data, 65536);
2560 close(fp);
2561
2562 if (nfo->len <= 0) {
2563 eDest.Emsg("Config", errno, "read from preloadstatic filename", val);
2564 return 1;
2565 }
2566
2567 if (nfo->len >= 65536) {
2568 eDest.Emsg("Config", "Truncated preloadstatic filename. Max is 64 KB '", val, "'");
2569 return 1;
2570 }
2571
2572 // Record the value
2573 //
2574 if (!staticpreload)
2576
2577 staticpreload->Rep((const char *)key, nfo);
2578 return 0;
2579}
2580
2581/******************************************************************************/
2582/* x s t a t i c h e a d e r */
2583/******************************************************************************/
2584
2585//
2586// xstaticheader parses the http.staticheader director with the following syntax:
2587//
2588// http.staticheader [-verb=[GET|HEAD|...]]* header [value]
2589//
2590// When set, this will cause XrdHttp to always return the specified header and
2591// value.
2592//
2593// Setting this option multiple times is additive (multiple headers may be set).
2594// Omitting the value will cause the static header setting to be unset.
2595//
2596// Omitting the -verb argument will cause it the header to be set unconditionally
2597// for all requests.
2598int XrdHttpProtocol::xstaticheader(XrdOucStream & Config) {
2599 auto val = Config.GetWord();
2600 std::vector<std::string> verbs;
2601 while (true) {
2602 if (!val || !val[0]) {
2603 eDest.Emsg("Config", "http.staticheader requires the header to be specified");
2604 return 1;
2605 }
2606
2607 std::string match_verb;
2608 std::string_view val_str(val);
2609 if (val_str.substr(0, 6) == "-verb=") {
2610 verbs.emplace_back(val_str.substr(6));
2611 } else if (val_str == "-") {
2612 eDest.Emsg("Config", "http.staticheader is ignoring unknown flag: ", val_str.data());
2613 } else {
2614 break;
2615 }
2616
2617 val = Config.GetWord();
2618 }
2619 if (verbs.empty()) {
2620 verbs.emplace_back();
2621 }
2622
2623 std::string header = val;
2624
2625 val = Config.GetWord();
2626 std::string header_value;
2627 if (val && val[0]) {
2628 header_value = val;
2629 }
2630
2631 for (const auto &verb : verbs) {
2632 auto iter = m_staticheader_map.find(verb);
2633 if (iter == m_staticheader_map.end() && !header_value.empty()) {
2634 m_staticheader_map.insert(iter, {verb, {{header, header_value}}});
2635 } else if (header_value.empty()) {
2636 iter->second.clear();
2637 } else {
2638 iter->second.emplace_back(header, header_value);
2639 }
2640 }
2641
2642 return 0;
2643}
2644
2645
2646/******************************************************************************/
2647/* x s e l f h t t p s 2 h t t p */
2648/******************************************************************************/
2649
2650/* Function: selfhttps2http
2651
2652 Purpose: To parse the directive: selfhttps2http <yes|no|0|1>
2653
2654 <val> this server will redirect HTTPS to itself using HTTP+token
2655
2656 Output: 0 upon success or !0 upon failure.
2657 */
2658
2659int XrdHttpProtocol::xselfhttps2http(XrdOucStream & Config) {
2660 char *val;
2661
2662 // Get the path
2663 //
2664 val = Config.GetWord();
2665 if (!val || !val[0]) {
2666 eDest.Emsg("Config", "selfhttps2http flag not specified");
2667 return 1;
2668 }
2669
2670 // Record the value
2671 //
2672 selfhttps2http = (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcmp(val, "1"));
2673
2674
2675 return 0;
2676}
2677
2678/******************************************************************************/
2679/* x s e c x t r a c t o r */
2680/******************************************************************************/
2681
2682/* Function: xsecxtractor
2683
2684 Purpose: To parse the directive: secxtractor [required] <path> <params>
2685
2686 required optional parameter which if present treats any secxtractor
2687 errors as fatal.
2688 <path> the path of the plugin to be loaded
2689 <params> parameters passed to the secxtractor library
2690
2691 Output: 0 upon success or !0 upon failure.
2692 */
2693
2694int XrdHttpProtocol::xsecxtractor(XrdOucStream& Config) {
2695 char *val;
2696
2697 // Get the path
2698 //
2699 val = Config.GetWord();
2700 if (!val || !val[0]) {
2701 eDest.Emsg("Config", "No security extractor plugin specified.");
2702 return 1;
2703 } else {
2704 // Handle optional parameter [required]
2705 //
2706 if (!strncmp(val, "required", 8)) {
2707 isRequiredXtractor = true;
2708 val = Config.GetWord();
2709
2710 if (!val || !val[0]) {
2711 eDest.Emsg("Config", "No security extractor plugin after [required] "
2712 "parameter");
2713 return 1;
2714 }
2715 }
2716
2717 char libName[4096];
2718 strlcpy(libName, val, sizeof(libName));
2719 libName[sizeof(libName) - 1] = '\0';
2720 char libParms[4096];
2721
2722 if (!Config.GetRest(libParms, 4095)) {
2723 eDest.Emsg("Config", "secxtractor config params longer than 4k");
2724 return 1;
2725 }
2726
2727 // Try to load the plugin (if available) that extracts info from the
2728 // user cert/proxy
2729 if (LoadSecXtractor(&eDest, libName, libParms)) {
2730 return 1;
2731 }
2732 }
2733
2734 return 0;
2735}
2736
2737/******************************************************************************/
2738/* x e x t h a n d l e r */
2739/******************************************************************************/
2740
2741/* Function: xexthandler
2742 *
2743 * Purpose: To parse the directive: exthandler <name> <path> <initparm>
2744 *
2745 * <name> a unique name (max 16chars) to be given to this
2746 * instance, e.g 'myhandler1'
2747 * <path> the path of the plugin to be loaded
2748 * <initparm> a string parameter (e.g. a config file) that is
2749 * passed to the initialization of the plugin
2750 *
2751 * Output: 0 upon success or !0 upon failure.
2752 */
2753
2754int XrdHttpProtocol::xexthandler(XrdOucStream &Config,
2755 std::vector<extHInfo> &hiVec) {
2756 char *val, path[1024], namebuf[1024];
2757 char *parm;
2758 // By default, every external handler need TLS configured to be loaded
2759 bool noTlsOK = false;
2760
2761 // Get the name
2762 //
2763 val = Config.GetWord();
2764 if (!val || !val[0]) {
2765 eDest.Emsg("Config", "No instance name specified for an http external handler plugin.");
2766 return 1;
2767 }
2768 if (strlen(val) >= 16) {
2769 eDest.Emsg("Config", "Instance name too long for an http external handler plugin.");
2770 return 1;
2771 }
2772 strncpy(namebuf, val, sizeof(namebuf));
2773 namebuf[ sizeof(namebuf)-1 ] = '\0';
2774
2775 // Get the +notls option if it was provided
2776 val = Config.GetWord();
2777
2778 if(val && !strcmp("+notls",val)) {
2779 noTlsOK = true;
2780 val = Config.GetWord();
2781 }
2782
2783 // Get the path
2784 //
2785 if (!val || !val[0]) {
2786 eDest.Emsg("Config", "No http external handler plugin specified.");
2787 return 1;
2788 }
2789 if (strlen(val) >= (int)sizeof(path)) {
2790 eDest.Emsg("Config", "Path too long for an http external handler plugin.");
2791 return 1;
2792 }
2793
2794 strcpy(path, val);
2795
2796 // Everything else is a free string
2797 //
2798 parm = Config.GetWord();
2799
2800 // Verify whether this is a duplicate (we never supported replacements)
2801 //
2802 for (int i = 0; i < (int)hiVec.size(); i++)
2803 {if (hiVec[i].extHName == namebuf) {
2804 eDest.Emsg("Config", "Instance name already present for "
2805 "http external handler plugin",
2806 hiVec[i].extHPath.c_str());
2807 return 1;
2808 }
2809 }
2810
2811 // Verify that we don't have more already than we are allowed to have
2812 //
2813 if (hiVec.size() >= MAX_XRDHTTPEXTHANDLERS) {
2814 eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
2815 return 1;
2816 }
2817
2818 // Create an info struct and push it on the list of ext handlers to load
2819 //
2820 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm : ""), noTlsOK));
2821
2822 return 0;
2823}
2824
2825/******************************************************************************/
2826/* x h e a d e r 2 c g i */
2827/******************************************************************************/
2828
2829/* Function: xheader2cgi
2830 *
2831 * Purpose: To parse the directive: header2cgi <headerkey> <cgikey>
2832 *
2833 * <headerkey> the name of an incoming HTTP header
2834 * to be transformed
2835 * <cgikey> the name to be given when adding it to the cgi info
2836 * that is kept only internally
2837 *
2838 * Output: 0 upon success or !0 upon failure.
2839 */
2840
2841int XrdHttpProtocol::xheader2cgi(XrdOucStream & Config) {
2842 return parseHeader2CGI(Config,eDest,hdr2cgimap);
2843}
2844
2845/******************************************************************************/
2846/* x s s l c a d i r */
2847/******************************************************************************/
2848
2849/* Function: xsslcadir
2850
2851 Purpose: To parse the directive: sslcadir <path>
2852
2853 <path> the path of the server key to be used.
2854
2855 Output: 0 upon success or !0 upon failure.
2856 */
2857
2858int XrdHttpProtocol::xsslcadir(XrdOucStream & Config) {
2859 char *val;
2860
2861 // Get the path
2862 //
2863 val = Config.GetWord();
2864 if (!val || !val[0]) {
2865 eDest.Emsg("Config", "HTTP X509 CAdir not specified");
2866 return 1;
2867 }
2868
2869 // Record the path
2870 //
2871 if (sslcadir) free(sslcadir);
2872 sslcadir = strdup(val);
2873
2874 if (xrdctxVer){ HTTPS_ALERT("cadir","tlsca",false); }
2875 return 0;
2876}
2877
2878/******************************************************************************/
2879/* x s s l c i p h e r f i l t e r */
2880/******************************************************************************/
2881
2882/* Function: xsslcipherfilter
2883
2884 Purpose: To parse the directive: cipherfilter <filter>
2885
2886 <filter> the filter string to be used when generating
2887 the SSL cipher list
2888
2889 Output: 0 upon success or !0 upon failure.
2890 */
2891
2892int XrdHttpProtocol::xsslcipherfilter(XrdOucStream & Config) {
2893 char *val;
2894
2895 // Get the filter string
2896 //
2897 val = Config.GetWord();
2898 if (!val || !val[0]) {
2899 eDest.Emsg("Config", "SSL cipherlist filter string not specified");
2900 return 1;
2901 }
2902
2903 // Record the filter string
2904 //
2906 sslcipherfilter = strdup(val);
2907
2908 return 0;
2909}
2910
2911/******************************************************************************/
2912/* x t l s r e u s e */
2913/******************************************************************************/
2914
2915/* Function: xtlsreuse
2916
2917 Purpose: To parse the directive: tlsreuse {on | off}
2918
2919 Output: 0 upon success or 1 upon failure.
2920 */
2921
2922int XrdHttpProtocol::xtlsreuse(XrdOucStream & Config) {
2923
2924 char *val;
2925
2926// Get the argument
2927//
2928 val = Config.GetWord();
2929 if (!val || !val[0])
2930 {eDest.Emsg("Config", "tlsreuse argument not specified"); return 1;}
2931
2932// If it's off, we set it off
2933//
2934 if (!strcmp(val, "off"))
2936 return 0;
2937 }
2938
2939// If it's on we set it on.
2940//
2941 if (!strcmp(val, "on"))
2943 return 0;
2944 }
2945
2946// Bad argument
2947//
2948 eDest.Emsg("config", "invalid tlsreuse parameter -", val);
2949 return 1;
2950}
2951
2952int XrdHttpProtocol::xauth(XrdOucStream &Config) {
2953 char *val = Config.GetWord();
2954 if(val) {
2955 if(!strcmp("tpc",val)) {
2956 if(!(val = Config.GetWord())) {
2957 eDest.Emsg("Config", "http.auth tpc value not specified."); return 1;
2958 } else {
2959 if(!strcmp("fcreds",val)) {
2960 tpcForwardCreds = true;
2961 } else {
2962 eDest.Emsg("Config", "http.auth tpc value is invalid"); return 1;
2963 }
2964 }
2965 } else {
2966 eDest.Emsg("Config", "http.auth value is invalid"); return 1;
2967 }
2968 }
2969 return 0;
2970}
2971
2972/******************************************************************************/
2973/* x t r a c e */
2974/******************************************************************************/
2975
2976/* Function: xtrace
2977
2978 Purpose: To parse the directive: trace <events>
2979
2980 <events> the blank separated list of events to trace. Trace
2981 directives are cumulative.
2982
2983 Output: 0 upon success or 1 upon failure.
2984 */
2985
2986int XrdHttpProtocol::xtrace(XrdOucStream & Config) {
2987
2988 char *val;
2989
2990 static struct traceopts {
2991 const char *opname;
2992 int opval;
2993 } tropts[] = {
2994 {"all", TRACE_ALL},
2995 {"auth", TRACE_AUTH},
2996 {"debug", TRACE_DEBUG},
2997 {"mem", TRACE_MEM},
2998 {"redirect", TRACE_REDIR},
2999 {"request", TRACE_REQ},
3000 {"response", TRACE_RSP}
3001 };
3002 int i, neg, trval = 0, numopts = sizeof (tropts) / sizeof (struct traceopts);
3003
3004 if (!(val = Config.GetWord())) {
3005 eDest.Emsg("config", "trace option not specified");
3006 return 1;
3007 }
3008 while (val) {
3009 if (!strcmp(val, "off")) trval = 0;
3010 else {
3011 if ((neg = (val[0] == '-' && val[1]))) val++;
3012 for (i = 0; i < numopts; i++) {
3013 if (!strcmp(val, tropts[i].opname)) {
3014 if (neg) trval &= ~tropts[i].opval;
3015 else trval |= tropts[i].opval;
3016 break;
3017 }
3018 }
3019 if (i >= numopts)
3020 eDest.Emsg("config", "invalid trace option", val);
3021 }
3022 val = Config.GetWord();
3023 }
3024 XrdHttpTrace.What = trval;
3025 return 0;
3026}
3027
3028int XrdHttpProtocol::doStat(char *fname) {
3029 int l;
3030 bool b;
3031 CurrentReq.filesize = 0;
3034
3035 memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3037 memset(CurrentReq.xrdreq.stat.reserved, 0,
3038 sizeof (CurrentReq.xrdreq.stat.reserved));
3039 l = strlen(fname) + 1;
3040 CurrentReq.xrdreq.stat.dlen = htonl(l);
3041
3042 if (!Bridge) return -1;
3043 b = Bridge->Run((char *) &CurrentReq.xrdreq, fname, l);
3044 if (!b) {
3045 return -1;
3046 }
3047
3048
3049 return 0;
3050}
3051
3052/******************************************************************************/
3053/* d o C h k s u m */
3054/******************************************************************************/
3055
3057 size_t length;
3058 memset(&CurrentReq.xrdreq, 0, sizeof (ClientRequest));
3064 length = fname.length() + 1;
3065 CurrentReq.xrdreq.query.dlen = htonl(length);
3066
3067 if (!Bridge) return -1;
3068
3069 return Bridge->Run(reinterpret_cast<char *>(&CurrentReq.xrdreq), const_cast<char *>(fname.c_str()), length) ? 0 : -1;
3070}
3071
3072
3073static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION);
3074
3075// Loads the SecXtractor plugin, if available
3076int XrdHttpProtocol::LoadSecXtractor(XrdSysError *myeDest, const char *libName,
3077 const char *libParms) {
3078
3079
3080 // We don't want to load it more than once
3081 if (secxtractor) return 1;
3082
3083 XrdOucPinLoader myLib(myeDest, &compiledVer, "secxtractorlib", libName);
3085
3086 // Get the entry point of the object creator
3087 //
3088 ep = (XrdHttpSecXtractor *(*)(XrdHttpSecXtractorArgs))(myLib.Resolve("XrdHttpGetSecXtractor"));
3089 if (ep && (secxtractor = ep(myeDest, NULL, libParms))) return 0;
3090 myLib.Unload();
3091 return 1;
3092}
3093/******************************************************************************/
3094/* L o a d E x t H a n d l e r */
3095/******************************************************************************/
3096
3097int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec, const char *cFN, XrdOucEnv &myEnv) {
3098 for (int i = 0; i < (int) hiVec.size(); i++) {
3099 if(hiVec[i].extHNoTlsOK) {
3100 // The external plugin does not need TLS to be loaded
3101 if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3102 hiVec[i].extHParm.c_str(), &myEnv,
3103 hiVec[i].extHName.c_str()))
3104 return 1;
3105 }
3106 }
3107 return 0;
3108}
3109
3110int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3111 const char *cFN, XrdOucEnv &myEnv) {
3112
3113 // Add the pointer to the cadir and the cakey to the environment.
3114 //
3115 if (sslcadir) myEnv.Put("http.cadir", sslcadir);
3116 if (sslcafile) myEnv.Put("http.cafile", sslcafile);
3117 if (sslcert) myEnv.Put("http.cert", sslcert);
3118 if (sslkey) myEnv.Put("http.key" , sslkey);
3119
3120 // Load all of the specified external handlers.
3121 //
3122 for (int i = 0; i < (int)hiVec.size(); i++) {
3123 // Only load the external handlers that were not already loaded
3124 // by LoadExtHandlerNoTls(...)
3125 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3126 if (LoadExtHandler(&eDest, hiVec[i].extHPath.c_str(), cFN,
3127 hiVec[i].extHParm.c_str(), &myEnv,
3128 hiVec[i].extHName.c_str())) return 1;
3129 }
3130 }
3131 return 0;
3132}
3133
3134// Loads the external handler plugin, if available
3135int XrdHttpProtocol::LoadExtHandler(XrdSysError *myeDest, const char *libName,
3136 const char *configFN, const char *libParms,
3137 XrdOucEnv *myEnv, const char *instName) {
3138
3139
3140 // This function will avoid loading doubles. No idea why this happens
3141 if (ExtHandlerLoaded(instName)) {
3142 eDest.Emsg("Config", "Instance name already present for an http external handler plugin.");
3143 return 1;
3144 }
3145 if (exthandlercnt >= MAX_XRDHTTPEXTHANDLERS) {
3146 eDest.Emsg("Config", "Cannot load one more exthandler. Max is 4");
3147 return 1;
3148 }
3149
3150 XrdOucPinLoader myLib(myeDest, &compiledVer, "exthandlerlib", libName);
3152
3153 // Get the entry point of the object creator
3154 //
3155 ep = (XrdHttpExtHandler *(*)(XrdHttpExtHandlerArgs))(myLib.Resolve("XrdHttpGetExtHandler"));
3156
3157 XrdHttpExtHandler *newhandler;
3158 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3159
3160 // Handler has been loaded, it's the last one in the list
3161 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3162 exthandler[exthandlercnt].name[15] = '\0';
3163 exthandler[exthandlercnt++].ptr = newhandler;
3164
3165 return 0;
3166 }
3167
3168 myLib.Unload();
3169 return 1;
3170}
3171
3172
3173
3174// Tells if we have already loaded a certain exthandler. Try to
3175// privilege speed, as this func may be invoked pretty often
3176bool XrdHttpProtocol::ExtHandlerLoaded(const char *handlername) {
3177 for (int i = 0; i < exthandlercnt; i++) {
3178 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3179 return true;
3180 }
3181 }
3182 return false;
3183}
3184
3185// Locates a matching external handler for a given request, if available. Try to
3186// privilege speed, as this func is invoked for every incoming request
3187XrdHttpExtHandler * XrdHttpProtocol::FindMatchingExtHandler(const XrdHttpReq &req) {
3188
3189 for (int i = 0; i < exthandlercnt; i++) {
3190 if (exthandler[i].ptr->MatchesPath(req.requestverb.c_str(), req.resource.c_str())) {
3191 return exthandler[i].ptr;
3192 }
3193 }
3194 return NULL;
3195}
#define kXR_isManager
kXR_unt16 requestid
Definition XProtocol.hh:630
kXR_char reserved1[2]
Definition XProtocol.hh:632
struct ClientSetRequest set
Definition XProtocol.hh:871
kXR_char reserved[11]
Definition XProtocol.hh:770
kXR_char reserved2[8]
Definition XProtocol.hh:634
kXR_char fhandle[4]
Definition XProtocol.hh:633
@ kXR_query
Definition XProtocol.hh:113
@ kXR_set
Definition XProtocol.hh:130
@ kXR_stat
Definition XProtocol.hh:129
kXR_unt16 requestid
Definition XProtocol.hh:719
#define kXR_isServer
struct ClientQueryRequest query
Definition XProtocol.hh:866
kXR_unt16 requestid
Definition XProtocol.hh:768
struct ClientStatRequest stat
Definition XProtocol.hh:873
kXR_char modifier
Definition XProtocol.hh:721
@ kXR_Qcksum
Definition XProtocol.hh:617
kXR_char reserved[15]
Definition XProtocol.hh:720
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
#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)
char * unquote(char *str)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
Utility functions for XrdHTTP.
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
void Release(XrdBuffer *bp)
Definition XrdBuffer.cc:221
XrdBuffer * Obtain(int bsz)
Definition XrdBuffer.cc:140
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)
int reqstate
State machine to talk to the bridge.
XrdOucString resource
The resource specified by the request, stripped of opaque data.
bool headerok
Tells if we have finished reading the header.
const std::string & userAgent() const
std::string requestverb
ReqType request
The request we got.
int ProcessHTTPReq()
XrdOucEnv * opaque
The opaque data, after parsing.
long filemodtime
int parseFirstLine(char *line, int len)
Parse the first line of the header.
int parseLine(char *line, int len)
Parse the header.
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
long long filesize
ClientRequest xrdreq
The last issued xrd request, often pending.
virtual void reset()
virtual int InitSSL(SSL *, char *)
virtual int FreeSSL(SSL *)
static const int noPort
Do not add port number.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
void SetDialect(const char *dP)
void SetTLS(bool val)
void Set(int inQMax, time_t agemax=1800)
Definition XrdObject.icc:90
void Push(XrdObject< T > *Node)
Definition XrdObject.hh:101
T * Pop()
Definition XrdObject.hh:93
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
T * Rep(const char *KeyVal, T *KeyData, const int LifeTime=0, XrdOucHash_Options opt=Hash_default)
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
char * vorg
Entity's virtual organization(s)
int credslen
Length of the 'creds' data.
XrdNetAddrInfo * addrInfo
Entity's connection details.
const char * tident
Trace identifier always preset.
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
char * caps
Entity's capabilities.
char * creds
Raw entity credentials or cert.
char * grps
Entity's group name(s)
void Reset(const char *spV=0)
char * name
Entity's name.
char * role
Entity's role(s)
char * endorsements
Protocol specific endorsements.
void Display(XrdSysError &mDest)
char * moninfo
Information for monitoring.
char * host
Entity's host name dnr dependent.
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
XrdSysLogger * logger(XrdSysLogger *lp=0)
void SetLogger(XrdSysLogger *logp)
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.
const CTX_Params * GetParams()
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)
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0
XrdCmsConfig Config
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.