24 #include "XrdVersion.hh"
50 #include <openssl/err.h>
51 #include <openssl/ssl.h>
53 #include <arpa/inet.h>
60 #define XRHTTP_TK_GRACETIME 600
102 BIO *XrdHttpProtocol::sslbio_err = 0;
104 bool XrdHttpProtocol::isRequiredXtractor =
false;
106 int XrdHttpProtocol::exthandlercnt = 0;
109 bool XrdHttpProtocol::usingEC = false;
110 bool XrdHttpProtocol::hasCache= false;
131 const char *TraceID =
"Protocol";
158 "xrootd protocol anchor");
164 #if OPENSSL_VERSION_NUMBER < 0x10100000L
171 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
186 bio->shutdown = shut;
189 return bio->shutdown;
201 :
XrdProtocol(
"HTTP protocol handler"), ProtLink(this),
202 SecEntity(
""), CurrentReq(this, ReadRangeConfig) {
227 char mybuf[16], mybuf2[1024];
230 bool myishttps =
false;
234 if ((dlen = lp->
Peek(mybuf, (
int) sizeof (mybuf),
hailWait)) < (
int)
sizeof (mybuf)) {
235 if (dlen <= 0) lp->
setEtext(
"handshake not received");
238 mybuf[dlen - 1] =
'\0';
246 for (
int i = 0; i < dlen; i++) {
248 sprintf(mybuf3,
"%.02d ", mybuf[i]);
249 strcat(mybuf2, mybuf3);
256 for (
int i = 0; i < dlen - 1; i++)
257 if (!isprint(mybuf[i]) && (mybuf[i] !=
'\r') && (mybuf[i] !=
'\n')) {
259 TRACEI(
DEBUG,
"This does not look like http at pos " << i);
264 if ((!ismine) && (dlen >= 4)) {
265 char check[4] = {00, 00, 00, 00};
266 if (memcmp(mybuf, check, 4)) {
273 TRACEI(ALL,
"This may look like https, but https is not configured");
280 TRACEI(
DEBUG,
"This does not look like https. Protocol not matched.");
288 TRACEI(REQ,
"Protocol matched. https: " << myishttps);
291 hp->ishttps = myishttps;
306 hp->myBuffStart = hp->myBuffEnd = hp->myBuff->
buff;
314 char *XrdHttpProtocol::GetClientIPStr() {
317 if (!
Link)
return strdup(
"unknown");
319 if (!ai)
return strdup(
"unknown");
327 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
338 int ret = lp->
Send(data, datal);
339 BIO_clear_retry_flags(bio);
342 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
343 BIO_set_retry_write(bio);
359 int ret = lp->
Send(data, datal);
360 BIO_clear_retry_flags(bio);
362 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
363 BIO_set_retry_write(bio);
370 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
381 int ret = lp->
Recv(data, datal);
382 BIO_clear_retry_flags(bio);
385 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
386 BIO_set_retry_read(bio);
401 int ret = lp->
Recv(data, datal);
402 BIO_clear_retry_flags(bio);
404 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
405 BIO_set_retry_read(bio);
421 #if OPENSSL_VERSION_NUMBER < 0x10100000L
433 if (bio == NULL)
return 0;
449 case BIO_CTRL_GET_CLOSE:
452 case BIO_CTRL_SET_CLOSE:
467 BIO *XrdHttpProtocol::CreateBIO(
XrdLink *lp)
486 #define TRACELINK Link
494 if (!myBuff || !myBuff->
buff || !myBuff->
bsize) {
495 TRACE(ALL,
" Process. No buffer available. Internal error.");
501 char *nfo = GetClientIPStr();
503 TRACEI(REQ,
" Setting host: " << nfo);
512 if (ishttps && !ssldone) {
515 sbio = CreateBIO(
Link);
516 BIO_set_nbio(sbio, 1);
518 TRACE(ALL,
"Failed to configure the TLS client authentication; invalid configuration");
526 ERR_print_errors(sslbio_err);
535 SSL_set_bio(ssl, sbio, sbio);
542 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_RCVTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
543 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_SNDTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
546 int res = SSL_accept(ssl);
548 if ((res == -1) && (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ)) {
549 TRACEI(
DEBUG,
" SSL_accept wants to read more bytes... err:" << SSL_get_error(ssl, res));
554 ERR_print_errors(sslbio_err);
563 BIO_set_nbio(sbio, 0);
568 if (
tlsClientAuth == XrdTlsContext::ClientAuthSetting::kOn && HandleAuthentication(
Link)) {
589 if ((rc = getDataOneShot(BuffAvailable())) < 0) {
595 if (BuffUsed() < ResumeBytes)
return 1;
603 if (mon_info.size() >= 1024) {
604 TRACEI(ALL,
"User agent string too long");
606 TRACEI(ALL,
"Internal logic error: Bridge is null after login");
615 SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
630 while ((rc = BuffgetLine(tmpline)) > 0) {
631 std::string traceLine = tmpline.
c_str();
635 TRACE(
DEBUG,
" rc:" << rc <<
" got hdr line: " << traceLine);
636 if ((rc == 2) && (tmpline.
length() > 1) && (tmpline[rc - 1] ==
'\n')) {
638 TRACE(
DEBUG,
" rc:" << rc <<
" detected header end.");
644 TRACE(
DEBUG,
" Parsing first line: " << traceLine.c_str());
647 TRACE(
DEBUG,
" Parsing of first line failed with " << result);
653 TRACE(
DEBUG,
" Parsing of header line failed with " << result)
654 SendSimpleResp(400,NULL,NULL,
"Malformed header line. Hint: ensure the line finishes with \"\\r\\n\"", 0,
false);
665 TRACEI(REQ,
" rc:" << rc <<
"Header not yet complete.");
670 if ((rc <= 0) && (BuffUsed() >= 16384)) {
671 TRACEI(ALL,
"Corrupted header detected, or line too long. Disconnecting client.");
690 time_t timenow = time(0);
708 TRACEI(REQ,
" rc:" << rc <<
" self-redirecting to http with security token.");
715 struct sockaddr_storage sa;
716 socklen_t sl =
sizeof(sa);
723 switch (sa.ss_family) {
725 if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
732 if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
734 Addr_str = (
char *)malloc(strlen(buf)+3);
742 TRACEI(REQ,
" Can't recognize the address family of the local host.");
750 TRACEI(REQ,
" rc:"<<rc<<
" self-redirecting to http with security token: '"
751 << dest.
c_str() <<
"'");
755 SendSimpleResp(302, NULL, (
char *) dest.
c_str(), 0, 0,
true);
760 TRACEI(REQ,
" rc:" << rc <<
" Can't perform self-redirection.");
764 TRACEI(ALL,
" Could not calculate self-redirection hash");
770 if (!ishttps && !ssldone) {
780 if (t) tim = atoi(t);
782 TRACEI(REQ,
" xrdhttptime not specified. Authentication failed.");
786 TRACEI(REQ,
" Token expired. Authentication failed.");
871 TRACEI(REQ,
" Invalid tk '" << tk <<
"' != '" << hash <<
"'(calculated). Authentication failed.");
878 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
886 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
906 TRACEI(REQ,
" Authorization failed.");
923 TRACEI(REQ,
"Process is exiting rc:" << rc);
931 #define TRACELINK Link
985 #define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
987 #define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
989 #define HTTPS_ALERT(x,y,z) httpsspec = true;\
990 if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
991 eDest.Say("Config http." x " overrides the xrd." y " directive.")
993 int XrdHttpProtocol::Config(
const char *ConfigFN,
XrdOucEnv *myEnv) {
996 std::vector<extHInfo> extHIVec;
998 int cfgFD, GoNo, NoGo = 0, ismine;
1008 if(nonIanaChecksums.size()) {
1009 std::stringstream warningMsgSS;
1010 warningMsgSS <<
"Config warning: the following checksum algorithms are not IANA compliant: [";
1011 std::string unknownCksumString;
1012 for(
auto unknownCksum: nonIanaChecksums) {
1013 unknownCksumString += unknownCksum +
",";
1015 unknownCksumString.erase(unknownCksumString.size() - 1);
1016 warningMsgSS << unknownCksumString <<
"]" <<
". They therefore cannot be queried by a user via HTTP." ;
1017 eDest.
Say(warningMsgSS.str().c_str());
1023 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1025 m_bio_method =
static_cast<BIO_METHOD*
>(OPENSSL_malloc(
sizeof(BIO_METHOD)));
1060 if ((cfgFD =
open(ConfigFN, O_RDONLY, 0)) < 0)
1061 return eDest.
Emsg(
"Config", errno,
"open config file", ConfigFN);
1063 static const char *cvec[] = {
"*** http protocol config:", 0 };
1068 while ((var =
Config.GetMyFirstWord())) {
1069 if ((ismine = !strncmp(
"http.", var, 5)) && var[5]) var += 5;
1072 if TS_Xeq(
"trace", xtrace);
1073 else if TS_Xeq(
"cert", xsslcert);
1074 else if TS_Xeq(
"key", xsslkey);
1075 else if TS_Xeq(
"cadir", xsslcadir);
1076 else if TS_Xeq(
"cipherfilter", xsslcipherfilter);
1077 else if TS_Xeq(
"gridmap", xgmap);
1078 else if TS_Xeq(
"cafile", xsslcafile);
1079 else if TS_Xeq(
"secretkey", xsecretkey);
1080 else if TS_Xeq(
"desthttps", xdesthttps);
1081 else if TS_Xeq(
"secxtractor", xsecxtractor);
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;
1156 :
"was not configured.");
1157 const char *what = Configed();
1159 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1162 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1164 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1174 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1175 "is meaningless; ignoring key!");
1183 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1184 "a cert specification!");
1195 const char *what1 = 0, *what2 = 0, *what3 = 0;
1200 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1204 what2 =
"xrd.tlsca to supply 'cadir'.";
1208 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1209 :
"xrd.tlsca to supply 'cafile'.");
1213 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1223 {
const char *what = Configed();
1224 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1225 :
"'xrd.tlsca noverify' was specified!");
1227 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1235 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1240 const char *how =
"completed.";
1241 eDest.
Say(
"++++++ HTTPS initialization started.");
1242 if (!
InitTLS()) {NoGo = 1; how =
"failed.";}
1243 eDest.
Say(
"------ HTTPS initialization ", how);
1244 if (NoGo)
return NoGo;
1248 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1252 return (InitSecurity() ? NoGo : 1);
1259 const char *XrdHttpProtocol::Configed()
1261 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1262 if (secxtractor)
return "secxtractor requires";
1263 if (
gridmap)
return "gridmap requires";
1279 if (myBuffEnd >= myBuffStart) {
1281 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1286 dest.
assign(myBuffStart, 0, l-1);
1305 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1307 if ((*p ==
'\n') || (*p ==
'\0')) {
1310 dest.
assign(myBuffStart, 0, l-1);
1326 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1328 if ((*p ==
'\n') || (*p ==
'\0')) {
1332 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1334 dest.
assign(myBuffStart, 0, l1-1);
1338 dest.
insert(myBuffStart, l1, l-1);
1362 int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1377 maxread = std::min(blen, BuffAvailable());
1378 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1384 int sslavail = maxread;
1387 int l = SSL_pending(ssl);
1389 sslavail = std::min(maxread, SSL_pending(ssl));
1394 ERR_print_errors(sslbio_err);
1398 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1399 if (sslavail <= 0)
return 0;
1401 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1403 myBuffEnd = myBuff->
buff;
1406 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1409 ERR_print_errors(sslbio_err);
1416 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1418 myBuffEnd = myBuff->
buff;
1424 rlen =
Link->
Recv(myBuffEnd, maxread);
1440 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1447 int XrdHttpProtocol::BuffAvailable() {
1450 if (myBuffEnd >= myBuffStart)
1451 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1453 r = myBuffStart - myBuffEnd;
1455 if ((r < 0) || (r > myBuff->
bsize)) {
1456 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1469 int XrdHttpProtocol::BuffUsed() {
1472 if (myBuffEnd >= myBuffStart)
1473 r = myBuffEnd - myBuffStart;
1476 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1478 if ((r < 0) || (r > myBuff->
bsize)) {
1479 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1492 int XrdHttpProtocol::BuffFree() {
1493 return (myBuff->
bsize - BuffUsed());
1500 void XrdHttpProtocol::BuffConsume(
int blen) {
1502 if (blen > myBuff->
bsize) {
1503 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1507 if (blen > BuffUsed()) {
1508 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1512 myBuffStart = myBuffStart + blen;
1514 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1515 myBuffStart -= myBuff->
bsize;
1517 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1518 myBuffEnd -= myBuff->
bsize;
1520 if (BuffUsed() == 0)
1521 myBuffStart = myBuffEnd = myBuff->
buff;
1536 int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1539 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1544 if (blen > BuffUsed()) {
1545 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1546 if ( getDataOneShot(blen - BuffUsed(),
true) )
1552 if ( !BuffUsed() ) {
1553 if ( getDataOneShot(blen,
false) )
1561 if (myBuffStart <= myBuffEnd) {
1562 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1565 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1567 *data = myBuffStart;
1578 int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1582 if (body && bodylen) {
1583 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1585 r = SSL_write(ssl, body, bodylen);
1587 ERR_print_errors(sslbio_err);
1593 if (r <= 0)
return -1;
1604 int XrdHttpProtocol::StartSimpleResp(
int code,
const char *desc,
1605 const char *header_to_add,
1606 long long bodylen,
bool keepalive) {
1607 static const std::unordered_map<int, std::string> statusTexts = {
1611 {206,
"Partial Content"},
1613 {307,
"Temporary Redirect"},
1614 {400,
"Bad Request"},
1615 {401,
"Unauthorized"},
1618 {405,
"Method Not Allowed"},
1620 {416,
"Range Not Satisfiable"},
1622 {500,
"Internal Server Error"},
1623 {502,
"Bad Gateway"},
1624 {504,
"Gateway Timeout"},
1625 {507,
"Insufficient Storage"}};
1627 std::stringstream ss;
1628 const std::string crlf =
"\r\n";
1630 ss <<
"HTTP/1.1 " << code <<
" ";
1635 auto it = statusTexts.find(code);
1636 if (it != statusTexts.end()) {
1644 if (keepalive && (code != 100))
1645 ss <<
"Connection: Keep-Alive" << crlf;
1647 ss <<
"Connection: Close" << crlf;
1649 ss <<
"Server: XrootD/" << XrdVSTRING << crlf;
1658 if ((bodylen >= 0) && (code != 100))
1659 ss <<
"Content-Length: " << bodylen << crlf;
1661 if (header_to_add && (header_to_add[0] !=
'\0')) ss << header_to_add << crlf;
1665 const std::string &outhdr = ss.str();
1666 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1667 if (SendData(outhdr.c_str(), outhdr.size()))
1677 int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1678 const std::string crlf =
"\r\n";
1679 std::stringstream ss;
1681 if (header_to_add && (header_to_add[0] !=
'\0')) {
1682 ss << header_to_add << crlf;
1685 ss <<
"Transfer-Encoding: chunked";
1686 TRACEI(RSP,
"Starting chunked response");
1687 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1694 int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1695 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1696 if (ChunkRespHeader(content_length))
1699 if (body && SendData(body, content_length))
1702 return ChunkRespFooter();
1709 int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1710 const std::string crlf =
"\r\n";
1711 std::stringstream ss;
1715 const std::string &chunkhdr = ss.str();
1716 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1717 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1724 int XrdHttpProtocol::ChunkRespFooter() {
1725 const std::string crlf =
"\r\n";
1726 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1737 int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1739 long long content_length = bodylen;
1741 content_length = body ? strlen(body) : 0;
1744 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1751 return SendData(body, content_length);
1788 sprintf(buf,
"%d",
Port);
1794 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1800 if ((rdf = getenv(
"XRDROLE"))) {
1803 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1805 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1808 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1812 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1831 char *val, keybuf[1024], parmbuf[1024];
1836 if (!val || !val[0]) {
1837 err.
Emsg(
"Config",
"No headerkey specified.");
1842 while ( *val && !isalnum(*val) ) val++;
1843 strcpy(keybuf, val);
1847 pp = keybuf + strlen(keybuf) - 1;
1848 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1856 if(!parm || !parm[0]) {
1857 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1862 while ( *parm && !isalnum(*parm) ) parm++;
1863 strcpy(parmbuf, parm);
1866 pp = parmbuf + strlen(parmbuf) - 1;
1867 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1874 header2cgi[keybuf] = parmbuf;
1876 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1889 bool XrdHttpProtocol::InitTLS() {
1914 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1915 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1921 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1934 void XrdHttpProtocol::Cleanup() {
1936 TRACE(ALL,
" Cleanup");
1938 if (
BPool && myBuff) {
1939 BuffConsume(BuffUsed());
1954 int ret = SSL_shutdown(ssl);
1958 ret = SSL_shutdown(ssl);
1960 TRACE(ALL,
"SSL server failed to receive the SSL shutdown message from the client");
1961 ERR_print_errors(sslbio_err);
1965 TRACE(ALL,
"SSL server failed to send the shutdown message to the client");
1966 ERR_print_errors(sslbio_err);
2000 void XrdHttpProtocol::Reset() {
2002 TRACE(ALL,
" Reset");
2011 myBuffStart = myBuffEnd = 0;
2014 DoneSetInfo =
false;
2064 if (!val || !val[0]) {
2065 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2074 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2099 if (!val || !val[0]) {
2100 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2131 if (!val || !val[0]) {
2132 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2166 if (!val || !val[0]) {
2167 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2203 if (!val || !val[0]) {
2204 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2210 if (!strncmp(val,
"required", 8)) {
2214 if (!val || !val[0]) {
2215 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2223 if (!strcmp(val,
"compatNameGeneration")) {
2226 if (!val || !val[0]) {
2227 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2228 "[compatNameGeneration] parameter");
2260 if (!val || !val[0]) {
2261 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2289 bool inFile =
false;
2294 if (!val || !val[0]) {
2295 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2303 if (val[0] ==
'/') {
2306 int fd =
open(val, O_RDONLY);
2309 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2313 if (
fstat(fd, &st) != 0 ) {
2314 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2319 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2321 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2326 FILE *fp = fdopen(fd,
"r");
2328 if ( fp ==
nullptr ) {
2329 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2335 while( fgets(line, 1024, fp) ) {
2339 pp = line + strlen(line) - 1;
2340 while ( (pp >= line) && (!isalnum(*pp)) ) {
2347 while ( *pp && !isalnum(*pp) ) pp++;
2349 if ( strlen(pp) >= 32 ) {
2350 eDest.
Say(
"Config",
"Secret key loaded.");
2362 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2367 if ( strlen(val) < 32 ) {
2368 eDest.
Emsg(
"Config",
"Secret key is too short");
2375 if (!inFile)
Config.noEcho();
2399 if (!val || !val[0]) {
2400 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2406 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2431 if (!val || !val[0]) {
2432 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2464 if (!val || !val[0]) {
2465 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2471 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2496 if (!val || !val[0]) {
2497 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2503 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2528 if (!val || !val[0]) {
2529 eDest.
Emsg(
"Config",
"staticredir url not specified");
2558 char *val, *k, key[1024];
2564 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2573 if (!val || !val[0]) {
2574 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2579 int fp =
open(val, O_RDONLY);
2581 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2585 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2587 nfo->data = (
char *)malloc(65536);
2588 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2591 if (nfo->len <= 0) {
2592 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2596 if (nfo->len >= 65536) {
2597 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2628 auto val =
Config.GetWord();
2629 std::vector<std::string> verbs;
2631 if (!val || !val[0]) {
2632 eDest.
Emsg(
"Config",
"http.staticheader requires the header to be specified");
2636 std::string match_verb;
2637 std::string_view val_str(val);
2638 if (val_str.substr(0, 6) ==
"-verb=") {
2639 verbs.emplace_back(val_str.substr(6));
2640 }
else if (val_str ==
"-") {
2641 eDest.
Emsg(
"Config",
"http.staticheader is ignoring unknown flag: ", val_str.data());
2648 if (verbs.empty()) {
2649 verbs.emplace_back();
2652 std::string header = val;
2655 std::string header_value;
2656 if (val && val[0]) {
2660 for (
const auto &verb : verbs) {
2663 if (!header_value.empty())
2665 }
else if (header_value.empty()) {
2666 iter->second.clear();
2668 iter->second.emplace_back(header, header_value);
2695 if (!val || !val[0]) {
2696 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2702 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2730 if (!val || !val[0]) {
2731 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2736 if (!strncmp(val,
"required", 8)) {
2737 isRequiredXtractor =
true;
2740 if (!val || !val[0]) {
2741 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2748 strlcpy(libName, val,
sizeof(libName));
2749 libName[
sizeof(libName) - 1] =
'\0';
2750 char libParms[4096];
2752 if (!
Config.GetRest(libParms, 4095)) {
2753 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2759 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2785 std::vector<extHInfo> &hiVec) {
2786 char *val, path[1024], namebuf[1024];
2789 bool noTlsOK =
false;
2794 if (!val || !val[0]) {
2795 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2798 if (strlen(val) >= 16) {
2799 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2802 strncpy(namebuf, val,
sizeof(namebuf));
2803 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2808 if(val && !strcmp(
"+notls",val)) {
2815 if (!val || !val[0]) {
2816 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2819 if (strlen(val) >= (int)
sizeof(path)) {
2820 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2832 for (
int i = 0; i < (int)hiVec.size(); i++)
2833 {
if (hiVec[i].extHName == namebuf) {
2834 eDest.
Emsg(
"Config",
"Instance name already present for "
2835 "http external handler plugin",
2836 hiVec[i].extHPath.c_str());
2844 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2850 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2894 if (!val || !val[0]) {
2895 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2928 if (!val || !val[0]) {
2929 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2959 if (!val || !val[0])
2960 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2964 if (!strcmp(val,
"off"))
2971 if (!strcmp(val,
"on"))
2978 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
2983 auto val =
Config.GetWord();
2984 if (!val || !val[0])
2985 {
eDest.
Emsg(
"Config",
"tlsclientauth argument not specified");
return 1;}
2987 if (!strcmp(val,
"off"))
2991 if (!strcmp(val,
"on"))
2996 eDest.
Emsg(
"config",
"invalid tlsclientauth parameter -", val);
3001 char *val =
Config.GetWord();
3003 if(!strcmp(
"tpc",val)) {
3004 if(!(val =
Config.GetWord())) {
3005 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
3007 if(!strcmp(
"fcreds",val)) {
3010 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
3014 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
3021 char *val =
Config.GetWord();
3027 eDest.
Emsg(
"Config",
"http.maxdelay requires an argument in seconds (default is 30). Example: http.maxdelay 30");
3051 static struct traceopts {
3063 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3065 if (!(val =
Config.GetWord())) {
3066 eDest.
Emsg(
"config",
"trace option not specified");
3070 if (!strcmp(val,
"off")) trval = 0;
3072 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3073 for (i = 0; i < numopts; i++) {
3074 if (!strcmp(val, tropts[i].opname)) {
3075 if (neg) trval &= ~tropts[i].opval;
3076 else trval |= tropts[i].opval;
3081 eDest.
Emsg(
"config",
"invalid trace option", val);
3100 l = strlen(fname) + 1;
3125 length = fname.
length() + 1;
3137 int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3138 const char *libParms) {
3142 if (secxtractor)
return 1;
3144 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3150 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3158 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3159 for (
int i = 0; i < (int) hiVec.size(); i++) {
3160 if(hiVec[i].extHNoTlsOK) {
3162 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3163 hiVec[i].extHParm.c_str(), &myEnv,
3164 hiVec[i].extHName.c_str()))
3171 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3183 for (
int i = 0; i < (int)hiVec.size(); i++) {
3186 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3187 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3188 hiVec[i].extHParm.c_str(), &myEnv,
3189 hiVec[i].extHName.c_str()))
return 1;
3196 int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3197 const char *configFN,
const char *libParms,
3198 XrdOucEnv *myEnv,
const char *instName) {
3202 if (ExtHandlerLoaded(instName)) {
3203 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3207 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3211 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3219 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3222 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3223 exthandler[exthandlercnt].name[15] =
'\0';
3224 exthandler[exthandlercnt++].ptr = newhandler;
3237 bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3238 for (
int i = 0; i < exthandlercnt; i++) {
3239 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3250 for (
int i = 0; i < exthandlercnt; i++) {
3252 return exthandler[i].ptr;
struct ClientSetRequest set
struct ClientQueryRequest query
struct ClientStatRequest stat
#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)
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 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 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.
bool SetTlsClientAuth(ClientAuthSetting setting)
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
virtual void SetWait(int wtime, bool notify=false)=0
CloseImpl< false > Close(Ctx< File > file, uint16_t timeout=0)
Factory for creating CloseImpl objects.
XrdTlsContext::ClientAuthSetting tlsClientAuth
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.