24 #include "XrdVersion.hh"
51 #include <openssl/err.h>
52 #include <openssl/ssl.h>
54 #include <arpa/inet.h>
61 #define XRHTTP_TK_GRACETIME 600
103 BIO *XrdHttpProtocol::sslbio_err = 0;
105 bool XrdHttpProtocol::isRequiredXtractor =
false;
109 int XrdHttpProtocol::exthandlercnt = 0;
112 bool XrdHttpProtocol::usingEC = false;
113 bool XrdHttpProtocol::hasCache= false;
134 const char *TraceID =
"Protocol";
161 "xrootd protocol anchor");
167 #if OPENSSL_VERSION_NUMBER < 0x10100000L
174 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
189 bio->shutdown = shut;
192 return bio->shutdown;
204 :
XrdProtocol(
"HTTP protocol handler"), ProtLink(this),
205 SecEntity(
""), CurrentReq(this, ReadRangeConfig) {
230 char mybuf[16], mybuf2[1024];
233 bool myishttps =
false;
237 if ((dlen = lp->
Peek(mybuf, (
int) sizeof (mybuf),
hailWait)) < (
int)
sizeof (mybuf)) {
238 if (dlen <= 0) lp->
setEtext(
"handshake not received");
241 mybuf[dlen - 1] =
'\0';
249 for (
int i = 0; i < dlen; i++) {
251 sprintf(mybuf3,
"%.02d ", mybuf[i]);
252 strcat(mybuf2, mybuf3);
259 for (
int i = 0; i < dlen - 1; i++)
260 if (!isprint(mybuf[i]) && (mybuf[i] !=
'\r') && (mybuf[i] !=
'\n')) {
262 TRACEI(
DEBUG,
"This does not look like http at pos " << i);
267 if ((!ismine) && (dlen >= 4)) {
268 char check[4] = {00, 00, 00, 00};
269 if (memcmp(mybuf, check, 4)) {
276 TRACEI(ALL,
"This may look like https, but https is not configured");
283 TRACEI(
DEBUG,
"This does not look like https. Protocol not matched.");
291 TRACEI(REQ,
"Protocol matched. https: " << myishttps);
294 hp->ishttps = myishttps;
309 hp->myBuffStart = hp->myBuffEnd = hp->myBuff->
buff;
317 char *XrdHttpProtocol::GetClientIPStr() {
320 if (!
Link)
return strdup(
"unknown");
322 if (!ai)
return strdup(
"unknown");
330 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
341 int ret = lp->
Send(data, datal);
342 BIO_clear_retry_flags(bio);
345 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
346 BIO_set_retry_write(bio);
362 int ret = lp->
Send(data, datal);
363 BIO_clear_retry_flags(bio);
365 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
366 BIO_set_retry_write(bio);
373 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
384 int ret = lp->
Recv(data, datal);
385 BIO_clear_retry_flags(bio);
388 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
389 BIO_set_retry_read(bio);
404 int ret = lp->
Recv(data, datal);
405 BIO_clear_retry_flags(bio);
407 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
408 BIO_set_retry_read(bio);
424 #if OPENSSL_VERSION_NUMBER < 0x10100000L
436 if (bio == NULL)
return 0;
452 case BIO_CTRL_GET_CLOSE:
455 case BIO_CTRL_SET_CLOSE:
470 BIO *XrdHttpProtocol::CreateBIO(
XrdLink *lp)
489 #define TRACELINK Link
497 if (!myBuff || !myBuff->
buff || !myBuff->
bsize) {
498 TRACE(ALL,
" Process. No buffer available. Internal error.");
504 char *nfo = GetClientIPStr();
506 TRACEI(REQ,
" Setting host: " << nfo);
515 if (ishttps && !ssldone) {
518 sbio = CreateBIO(
Link);
519 BIO_set_nbio(sbio, 1);
525 ERR_print_errors(sslbio_err);
534 SSL_set_bio(ssl, sbio, sbio);
541 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_RCVTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
542 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_SNDTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
545 int res = SSL_accept(ssl);
547 if ((res == -1) && (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ)) {
548 TRACEI(
DEBUG,
" SSL_accept wants to read more bytes... err:" << SSL_get_error(ssl, res));
553 ERR_print_errors(sslbio_err);
562 BIO_set_nbio(sbio, 0);
588 if ((rc = getDataOneShot(BuffAvailable())) < 0) {
594 if (BuffUsed() < ResumeBytes)
return 1;
602 if (mon_info.size() >= 1024) {
603 TRACEI(ALL,
"User agent string too long");
605 TRACEI(ALL,
"Internal logic error: Bridge is null after login");
614 SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
629 while ((rc = BuffgetLine(tmpline)) > 0) {
630 std::string traceLine = tmpline.
c_str();
634 TRACE(
DEBUG,
" rc:" << rc <<
" got hdr line: " << traceLine);
635 if ((rc == 2) && (tmpline.
length() > 1) && (tmpline[rc - 1] ==
'\n')) {
637 TRACE(
DEBUG,
" rc:" << rc <<
" detected header end.");
643 TRACE(
DEBUG,
" Parsing first line: " << traceLine.c_str());
646 TRACE(
DEBUG,
" Parsing of first line failed with " << result);
652 TRACE(
DEBUG,
" Parsing of header line failed with " << result)
653 SendSimpleResp(400,NULL,NULL,
"Malformed header line. Hint: ensure the line finishes with \"\\r\\n\"", 0,
false);
664 TRACEI(REQ,
" rc:" << rc <<
"Header not yet complete.");
669 if ((rc <= 0) && (BuffUsed() >= 16384)) {
670 TRACEI(ALL,
"Corrupted header detected, or line too long. Disconnecting client.");
689 time_t timenow = time(0);
707 TRACEI(REQ,
" rc:" << rc <<
" self-redirecting to http with security token.");
714 struct sockaddr_storage sa;
715 socklen_t sl =
sizeof(sa);
722 switch (sa.ss_family) {
724 if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
731 if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
733 Addr_str = (
char *)malloc(strlen(buf)+3);
741 TRACEI(REQ,
" Can't recognize the address family of the local host.");
749 TRACEI(REQ,
" rc:"<<rc<<
" self-redirecting to http with security token: '"
750 << dest.
c_str() <<
"'");
754 SendSimpleResp(302, NULL, (
char *) dest.
c_str(), 0, 0,
true);
759 TRACEI(REQ,
" rc:" << rc <<
" Can't perform self-redirection.");
763 TRACEI(ALL,
" Could not calculate self-redirection hash");
769 if (!ishttps && !ssldone) {
779 if (t) tim = atoi(t);
781 TRACEI(REQ,
" xrdhttptime not specified. Authentication failed.");
785 TRACEI(REQ,
" Token expired. Authentication failed.");
870 TRACEI(REQ,
" Invalid tk '" << tk <<
"' != '" << hash <<
"'(calculated). Authentication failed.");
877 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
885 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
905 TRACEI(REQ,
" Authorization failed.");
922 TRACEI(REQ,
"Process is exiting rc:" << rc);
930 #define TRACELINK Link
984 #define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
986 #define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
988 #define HTTPS_ALERT(x,y,z) httpsspec = true;\
989 if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
990 eDest.Say("Config http." x " overrides the xrd." y " directive.")
992 int XrdHttpProtocol::Config(
const char *ConfigFN,
XrdOucEnv *myEnv) {
995 std::vector<extHInfo> extHIVec;
997 int cfgFD, GoNo, NoGo = 0, ismine;
1007 if(nonIanaChecksums.size()) {
1008 std::stringstream warningMsgSS;
1009 warningMsgSS <<
"Config warning: the following checksum algorithms are not IANA compliant: [";
1010 std::string unknownCksumString;
1011 for(
auto unknownCksum: nonIanaChecksums) {
1012 unknownCksumString += unknownCksum +
",";
1014 unknownCksumString.erase(unknownCksumString.size() - 1);
1015 warningMsgSS << unknownCksumString <<
"]" <<
". They therefore cannot be queried by a user via HTTP." ;
1016 eDest.
Say(warningMsgSS.str().c_str());
1022 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1024 m_bio_method =
static_cast<BIO_METHOD*
>(OPENSSL_malloc(
sizeof(BIO_METHOD)));
1059 if ((cfgFD =
open(ConfigFN, O_RDONLY, 0)) < 0)
1060 return eDest.
Emsg(
"Config", errno,
"open config file", ConfigFN);
1062 static const char *cvec[] = {
"*** http protocol config:", 0 };
1067 while ((var =
Config.GetMyFirstWord())) {
1068 if ((ismine = !strncmp(
"http.", var, 5)) && var[5]) var += 5;
1071 if TS_Xeq(
"trace", xtrace);
1072 else if TS_Xeq(
"cert", xsslcert);
1073 else if TS_Xeq(
"key", xsslkey);
1074 else if TS_Xeq(
"cadir", xsslcadir);
1075 else if TS_Xeq(
"cipherfilter", xsslcipherfilter);
1076 else if TS_Xeq(
"gridmap", xgmap);
1077 else if TS_Xeq(
"cafile", xsslcafile);
1078 else if TS_Xeq(
"secretkey", xsecretkey);
1079 else if TS_Xeq(
"desthttps", xdesthttps);
1080 else if TS_Xeq(
"secxtractor", xsecxtractor);
1081 else if TS_Xeq(
"cors", xcors);
1082 else if TS_Xeq3(
"exthandler", xexthandler);
1083 else if TS_Xeq(
"selfhttps2http", xselfhttps2http);
1084 else if TS_Xeq(
"embeddedstatic", xembeddedstatic);
1085 else if TS_Xeq(
"listingredir", xlistredir);
1086 else if TS_Xeq(
"staticredir", xstaticredir);
1087 else if TS_Xeq(
"staticpreload", xstaticpreload);
1088 else if TS_Xeq(
"staticheader", xstaticheader);
1089 else if TS_Xeq(
"listingdeny", xlistdeny);
1090 else if TS_Xeq(
"header2cgi", xheader2cgi);
1091 else if TS_Xeq(
"httpsmode", xhttpsmode);
1092 else if TS_Xeq(
"tlsreuse", xtlsreuse);
1093 else if TS_Xeq(
"auth", xauth);
1094 else if TS_Xeq(
"tlsclientauth", xtlsclientauth);
1095 else if TS_Xeq(
"maxdelay", xmaxdelay);
1097 eDest.
Say(
"Config warning: ignoring unknown directive '", var,
"'.");
1112 {
eDest.
Say(
"Config failure: one or more directives are flawed!");
1118 hdr2cgimap[
"Cache-Control"] =
"cache-control";
1121 if (getenv(
"XRDCL_EC"))
usingEC =
true;
1126 std::string default_static_headers;
1128 for (
const auto &header_entry : default_verb->second) {
1129 default_static_headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1134 if (item.first.empty()) {
1137 auto headers = default_static_headers;
1138 for (
const auto &header_entry : item.second) {
1139 headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1147 if (myEnv->
Get(
"XrdCache")) hasCache =
true;
1166 :
"was not configured.");
1167 const char *what = Configed();
1169 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1172 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1174 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1184 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1185 "is meaningless; ignoring key!");
1193 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1194 "a cert specification!");
1205 const char *what1 = 0, *what2 = 0, *what3 = 0;
1210 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1214 what2 =
"xrd.tlsca to supply 'cadir'.";
1218 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1219 :
"xrd.tlsca to supply 'cafile'.");
1223 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1233 {
const char *what = Configed();
1234 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1235 :
"'xrd.tlsca noverify' was specified!");
1237 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1245 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1250 const char *how =
"completed.";
1251 eDest.
Say(
"++++++ HTTPS initialization started.");
1252 if (!
InitTLS()) {NoGo = 1; how =
"failed.";}
1253 eDest.
Say(
"------ HTTPS initialization ", how);
1254 if (NoGo)
return NoGo;
1258 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1262 return (InitSecurity() ? NoGo : 1);
1269 const char *XrdHttpProtocol::Configed()
1271 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1272 if (secxtractor)
return "secxtractor requires";
1273 if (
gridmap)
return "gridmap requires";
1289 if (myBuffEnd >= myBuffStart) {
1291 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1296 dest.
assign(myBuffStart, 0, l-1);
1315 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1317 if ((*p ==
'\n') || (*p ==
'\0')) {
1320 dest.
assign(myBuffStart, 0, l-1);
1336 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1338 if ((*p ==
'\n') || (*p ==
'\0')) {
1342 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1344 dest.
assign(myBuffStart, 0, l1-1);
1348 dest.
insert(myBuffStart, l1, l-1);
1372 int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1387 maxread = std::min(blen, BuffAvailable());
1388 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1394 int sslavail = maxread;
1397 int l = SSL_pending(ssl);
1399 sslavail = std::min(maxread, SSL_pending(ssl));
1404 ERR_print_errors(sslbio_err);
1408 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1409 if (sslavail <= 0)
return 0;
1411 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1413 myBuffEnd = myBuff->
buff;
1416 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1419 ERR_print_errors(sslbio_err);
1426 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1428 myBuffEnd = myBuff->
buff;
1434 rlen =
Link->
Recv(myBuffEnd, maxread);
1450 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1457 int XrdHttpProtocol::BuffAvailable() {
1460 if (myBuffEnd >= myBuffStart)
1461 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1463 r = myBuffStart - myBuffEnd;
1465 if ((r < 0) || (r > myBuff->
bsize)) {
1466 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1479 int XrdHttpProtocol::BuffUsed() {
1482 if (myBuffEnd >= myBuffStart)
1483 r = myBuffEnd - myBuffStart;
1486 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1488 if ((r < 0) || (r > myBuff->
bsize)) {
1489 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1502 int XrdHttpProtocol::BuffFree() {
1503 return (myBuff->
bsize - BuffUsed());
1510 void XrdHttpProtocol::BuffConsume(
int blen) {
1512 if (blen > myBuff->
bsize) {
1513 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1517 if (blen > BuffUsed()) {
1518 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1522 myBuffStart = myBuffStart + blen;
1524 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1525 myBuffStart -= myBuff->
bsize;
1527 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1528 myBuffEnd -= myBuff->
bsize;
1530 if (BuffUsed() == 0)
1531 myBuffStart = myBuffEnd = myBuff->
buff;
1546 int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1549 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1554 if (blen > BuffUsed()) {
1555 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1556 if ( getDataOneShot(blen - BuffUsed(),
true) )
1562 if ( !BuffUsed() ) {
1563 if ( getDataOneShot(blen,
false) )
1571 if (myBuffStart <= myBuffEnd) {
1572 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1575 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1577 *data = myBuffStart;
1588 int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1592 if (body && bodylen) {
1593 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1595 r = SSL_write(ssl, body, bodylen);
1597 ERR_print_errors(sslbio_err);
1603 if (r <= 0)
return -1;
1614 int XrdHttpProtocol::StartSimpleResp(
int code,
const char *desc,
1615 const char *header_to_add,
1616 long long bodylen,
bool keepalive) {
1617 static const std::unordered_map<int, std::string> statusTexts = {
1621 {206,
"Partial Content"},
1623 {307,
"Temporary Redirect"},
1624 {400,
"Bad Request"},
1625 {401,
"Unauthorized"},
1628 {405,
"Method Not Allowed"},
1630 {416,
"Range Not Satisfiable"},
1632 {500,
"Internal Server Error"},
1633 {502,
"Bad Gateway"},
1634 {504,
"Gateway Timeout"},
1635 {507,
"Insufficient Storage"}};
1637 std::stringstream ss;
1638 const std::string crlf =
"\r\n";
1640 ss <<
"HTTP/1.1 " << code <<
" ";
1645 auto it = statusTexts.find(code);
1646 if (it != statusTexts.end()) {
1654 if (keepalive && (code != 100))
1655 ss <<
"Connection: Keep-Alive" << crlf;
1657 ss <<
"Connection: Close" << crlf;
1659 ss <<
"Server: XrootD/" << XrdVSTRING << crlf;
1670 if(corsAllowOrigin) {
1671 ss << *corsAllowOrigin << crlf;
1675 if ((bodylen >= 0) && (code != 100))
1676 ss <<
"Content-Length: " << bodylen << crlf;
1678 if (header_to_add && (header_to_add[0] !=
'\0')) ss << header_to_add << crlf;
1682 const std::string &outhdr = ss.str();
1683 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1684 if (SendData(outhdr.c_str(), outhdr.size()))
1694 int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1695 const std::string crlf =
"\r\n";
1696 std::stringstream ss;
1698 if (header_to_add && (header_to_add[0] !=
'\0')) {
1699 ss << header_to_add << crlf;
1702 ss <<
"Transfer-Encoding: chunked";
1703 TRACEI(RSP,
"Starting chunked response");
1704 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1711 int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1712 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1713 if (ChunkRespHeader(content_length))
1716 if (body && SendData(body, content_length))
1719 return ChunkRespFooter();
1726 int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1727 const std::string crlf =
"\r\n";
1728 std::stringstream ss;
1732 const std::string &chunkhdr = ss.str();
1733 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1734 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1741 int XrdHttpProtocol::ChunkRespFooter() {
1742 const std::string crlf =
"\r\n";
1743 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1754 int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1756 long long content_length = bodylen;
1758 content_length = body ? strlen(body) : 0;
1761 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1768 return SendData(body, content_length);
1805 sprintf(buf,
"%d",
Port);
1811 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1817 if ((rdf = getenv(
"XRDROLE"))) {
1820 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1822 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1825 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1829 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1848 char *val, keybuf[1024], parmbuf[1024];
1853 if (!val || !val[0]) {
1854 err.
Emsg(
"Config",
"No headerkey specified.");
1859 while ( *val && !isalnum(*val) ) val++;
1860 strcpy(keybuf, val);
1864 pp = keybuf + strlen(keybuf) - 1;
1865 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1873 if(!parm || !parm[0]) {
1874 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1879 while ( *parm && !isalnum(*parm) ) parm++;
1880 strcpy(parmbuf, parm);
1883 pp = parmbuf + strlen(parmbuf) - 1;
1884 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1891 header2cgi[keybuf] = parmbuf;
1893 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1906 bool XrdHttpProtocol::InitTLS() {
1931 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1932 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1938 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1954 void XrdHttpProtocol::Cleanup() {
1956 TRACE(ALL,
" Cleanup");
1958 if (
BPool && myBuff) {
1959 BuffConsume(BuffUsed());
1974 int ret = SSL_shutdown(ssl);
1978 ret = SSL_shutdown(ssl);
1980 TRACE(ALL,
"SSL server failed to receive the SSL shutdown message from the client");
1981 ERR_print_errors(sslbio_err);
1985 TRACE(ALL,
"SSL server failed to send the shutdown message to the client");
1986 ERR_print_errors(sslbio_err);
2020 void XrdHttpProtocol::Reset() {
2022 TRACE(ALL,
" Reset");
2031 myBuffStart = myBuffEnd = 0;
2034 DoneSetInfo =
false;
2084 if (!val || !val[0]) {
2085 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2094 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2119 if (!val || !val[0]) {
2120 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2151 if (!val || !val[0]) {
2152 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2186 if (!val || !val[0]) {
2187 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2223 if (!val || !val[0]) {
2224 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2230 if (!strncmp(val,
"required", 8)) {
2234 if (!val || !val[0]) {
2235 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2243 if (!strcmp(val,
"compatNameGeneration")) {
2246 if (!val || !val[0]) {
2247 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2248 "[compatNameGeneration] parameter");
2280 if (!val || !val[0]) {
2281 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2309 bool inFile =
false;
2314 if (!val || !val[0]) {
2315 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2323 if (val[0] ==
'/') {
2326 int fd =
open(val, O_RDONLY);
2329 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2333 if (
fstat(fd, &st) != 0 ) {
2334 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2339 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2341 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2346 FILE *fp = fdopen(fd,
"r");
2348 if ( fp ==
nullptr ) {
2349 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2355 while( fgets(line, 1024, fp) ) {
2359 pp = line + strlen(line) - 1;
2360 while ( (pp >= line) && (!isalnum(*pp)) ) {
2367 while ( *pp && !isalnum(*pp) ) pp++;
2369 if ( strlen(pp) >= 32 ) {
2370 eDest.
Say(
"Config",
"Secret key loaded.");
2382 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2387 if ( strlen(val) < 32 ) {
2388 eDest.
Emsg(
"Config",
"Secret key is too short");
2395 if (!inFile)
Config.noEcho();
2419 if (!val || !val[0]) {
2420 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2426 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2451 if (!val || !val[0]) {
2452 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2484 if (!val || !val[0]) {
2485 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2491 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2516 if (!val || !val[0]) {
2517 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2523 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2548 if (!val || !val[0]) {
2549 eDest.
Emsg(
"Config",
"staticredir url not specified");
2578 char *val, *k, key[1024];
2584 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2593 if (!val || !val[0]) {
2594 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2599 int fp =
open(val, O_RDONLY);
2601 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2605 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2607 nfo->data = (
char *)malloc(65536);
2608 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2611 if (nfo->len <= 0) {
2612 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2616 if (nfo->len >= 65536) {
2617 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2648 auto val =
Config.GetWord();
2649 std::vector<std::string> verbs;
2651 if (!val || !val[0]) {
2652 eDest.
Emsg(
"Config",
"http.staticheader requires the header to be specified");
2656 std::string match_verb;
2657 std::string_view val_str(val);
2658 if (val_str.substr(0, 6) ==
"-verb=") {
2659 verbs.emplace_back(val_str.substr(6));
2660 }
else if (val_str ==
"-") {
2661 eDest.
Emsg(
"Config",
"http.staticheader is ignoring unknown flag: ", val_str.data());
2668 if (verbs.empty()) {
2669 verbs.emplace_back();
2672 std::string header = val;
2675 std::string header_value;
2676 if (val && val[0]) {
2680 for (
const auto &verb : verbs) {
2683 if (!header_value.empty())
2685 }
else if (header_value.empty()) {
2686 iter->second.clear();
2688 iter->second.emplace_back(header, header_value);
2715 if (!val || !val[0]) {
2716 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2722 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2750 if (!val || !val[0]) {
2751 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2756 if (!strncmp(val,
"required", 8)) {
2757 isRequiredXtractor =
true;
2760 if (!val || !val[0]) {
2761 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2768 strlcpy(libName, val,
sizeof(libName));
2769 libName[
sizeof(libName) - 1] =
'\0';
2770 char libParms[4096];
2772 if (!
Config.GetRest(libParms, 4095)) {
2773 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2779 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2791 if (!val || !val[0]) {
2792 eDest.
Emsg(
"Config",
"No CORS plugin specified.");
2817 std::vector<extHInfo> &hiVec) {
2818 char *val, path[1024], namebuf[1024];
2821 bool noTlsOK =
false;
2826 if (!val || !val[0]) {
2827 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2830 if (strlen(val) >= 16) {
2831 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2834 strncpy(namebuf, val,
sizeof(namebuf));
2835 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2840 if(val && !strcmp(
"+notls",val)) {
2847 if (!val || !val[0]) {
2848 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2851 if (strlen(val) >= (int)
sizeof(path)) {
2852 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2864 for (
int i = 0; i < (int)hiVec.size(); i++)
2865 {
if (hiVec[i].extHName == namebuf) {
2866 eDest.
Emsg(
"Config",
"Instance name already present for "
2867 "http external handler plugin",
2868 hiVec[i].extHPath.c_str());
2876 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2882 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2926 if (!val || !val[0]) {
2927 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2960 if (!val || !val[0]) {
2961 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2991 if (!val || !val[0])
2992 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2996 if (!strcmp(val,
"off"))
3003 if (!strcmp(val,
"on"))
3010 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
3015 auto val =
Config.GetWord();
3016 if (!val || !val[0])
3017 {
eDest.
Emsg(
"Config",
"tlsclientauth argument not specified");
return 1;}
3019 if (!strcmp(val,
"off"))
3023 if (!strcmp(val,
"on"))
3028 eDest.
Emsg(
"config",
"invalid tlsclientauth parameter -", val);
3033 char *val =
Config.GetWord();
3035 if(!strcmp(
"tpc",val)) {
3036 if(!(val =
Config.GetWord())) {
3037 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
3039 if(!strcmp(
"fcreds",val)) {
3042 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
3046 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
3053 char *val =
Config.GetWord();
3059 eDest.
Emsg(
"Config",
"http.maxdelay requires an argument in seconds (default is 30). Example: http.maxdelay 30");
3083 static struct traceopts {
3095 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3097 if (!(val =
Config.GetWord())) {
3098 eDest.
Emsg(
"config",
"trace option not specified");
3102 if (!strcmp(val,
"off")) trval = 0;
3104 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3105 for (i = 0; i < numopts; i++) {
3106 if (!strcmp(val, tropts[i].opname)) {
3107 if (neg) trval &= ~tropts[i].opval;
3108 else trval |= tropts[i].opval;
3113 eDest.
Emsg(
"config",
"invalid trace option", val);
3132 l = strlen(fname) + 1;
3157 length = fname.
length() + 1;
3169 int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3170 const char *libParms) {
3174 if (secxtractor)
return 1;
3176 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3182 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3190 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3191 for (
int i = 0; i < (int) hiVec.size(); i++) {
3192 if(hiVec[i].extHNoTlsOK) {
3194 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3195 hiVec[i].extHParm.c_str(), &myEnv,
3196 hiVec[i].extHName.c_str()))
3203 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3215 for (
int i = 0; i < (int)hiVec.size(); i++) {
3218 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3219 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3220 hiVec[i].extHParm.c_str(), &myEnv,
3221 hiVec[i].extHName.c_str()))
return 1;
3228 int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3229 const char *configFN,
const char *libParms,
3230 XrdOucEnv *myEnv,
const char *instName) {
3234 if (ExtHandlerLoaded(instName)) {
3235 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3239 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3243 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3251 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3254 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3255 exthandler[exthandlercnt].name[15] =
'\0';
3256 exthandler[exthandlercnt++].ptr = newhandler;
3266 int XrdHttpProtocol::LoadCorsHandler(
XrdSysError *
eDest,
const char *libname) {
3271 if(ep && (
xrdcors = ep()))
return 0;
3278 bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3279 for (
int i = 0; i < exthandlercnt; i++) {
3280 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3291 for (
int i = 0; i < exthandlercnt; i++) {
3293 return exthandler[i].ptr;
struct ClientSetRequest set
struct ClientQueryRequest query
struct ClientStatRequest stat
static XrdSysError eDest(0,"crypto_")
#define XrdHttpCorsGetHandlerArgs
#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")
void * BIO_get_data(BIO *bio)
static int BIO_XrdLink_read(BIO *bio, char *data, size_t datal, size_t *read)
void BIO_set_data(BIO *bio, void *ptr)
static int BIO_XrdLink_destroy(BIO *bio)
#define XRHTTP_TK_GRACETIME
static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION)
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
int compareHash(const char *h1, const char *h2)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
Utility functions for XrdHTTP.
std::string decode_str(const std::string &str)
std::string obfuscateAuth(const std::string &input)
ssize_t read(int fildes, void *buf, size_t nbyte)
#define TLS_SET_VDEPTH(cOpts, vdv)
#define TLS_SET_REFINT(cOpts, refi)
void Release(XrdBuffer *bp)
XrdBuffer * Obtain(int bsz)
const std::vector< std::string > & getNonIANAConfiguredCksums() const
void configure(const char *csList)
virtual std::optional< std::string > getCORSAllowOriginHeader(const std::string &origin)=0
virtual int Configure(const char *configFN, XrdSysError *errP)=0
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 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 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.
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.
XrdHttpProtocol operator=(const XrdHttpProtocol &rhs)
static XrdHttpCors * xrdcors
static bool compatNameGeneration
static std::string xrdcorsLibPath
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 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 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 std::unordered_map< std::string, std::string > m_staticheaders
static int crlRefIntervalSec
CRL thread refresh interval.
static XrdHttpReadRangeHandler::Configuration ReadRangeConfig
configuration for the read range handler
static XrdSecService * CIA
static XrdBuffManager * BPool
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.
ReqType request
The request we got.
XrdOucEnv * opaque
The opaque data, after parsing.
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)
ClientRequest xrdreq
The last issued xrd request, often pending.
const std::string & userAgent() const
virtual int InitSSL(SSL *, char *)
virtual int FreeSSL(SSL *)
int setEtext(const char *text)
int Peek(char *buff, int blen, int timeout=-1)
int Recv(char *buff, int blen)
const XrdNetAddr * NetAddr() const
XrdNetAddrInfo * AddrInfo()
int Send(const char *buff, int blen)
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 Set(int inQMax, time_t agemax=1800)
void Push(XrdObject< T > *Node)
static bool Import(const char *var, char *&val)
void * GetPtr(const char *varname)
char * Get(const char *varname)
void Put(const char *varname, const char *value)
void insert(const int i, int start=-1)
const char * c_str() const
void assign(const char *s, int j, int k=-1)
static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1)
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)
void SetTlsClientAuth(bool setting)
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
virtual void SetWait(int wtime, bool notify=false)=0
CloseImpl< false > Close(Ctx< File > file, uint16_t timeout=0)
Factory for creating CloseImpl objects.
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.