24#include "XrdVersion.hh"
48#include <openssl/err.h>
49#include <openssl/ssl.h>
58#define XRHTTP_TK_GRACETIME 600
99BIO *XrdHttpProtocol::sslbio_err = 0;
101bool XrdHttpProtocol::isRequiredXtractor =
false;
103int XrdHttpProtocol::exthandlercnt = 0;
106bool XrdHttpProtocol::usingEC = false;
107bool XrdHttpProtocol::hasCache= false;
128const char *TraceID =
"Protocol";
154 "xrootd protocol anchor");
160#if OPENSSL_VERSION_NUMBER < 0x10100000L
167#if OPENSSL_VERSION_NUMBER < 0x1000105fL
182 bio->shutdown = shut;
185 return bio->shutdown;
197:
XrdProtocol(
"HTTP protocol handler"), ProtLink(this),
198SecEntity(
""), CurrentReq(this, ReadRangeConfig) {
223 char mybuf[16], mybuf2[1024];
226 bool myishttps =
false;
230 if ((dlen = lp->
Peek(mybuf, (
int) sizeof (mybuf),
hailWait)) < (
int)
sizeof (mybuf)) {
231 if (dlen <= 0) lp->
setEtext(
"handshake not received");
234 mybuf[dlen - 1] =
'\0';
242 for (
int i = 0; i < dlen; i++) {
244 sprintf(mybuf3,
"%.02d ", mybuf[i]);
245 strcat(mybuf2, mybuf3);
252 for (
int i = 0; i < dlen - 1; i++)
253 if (!isprint(mybuf[i]) && (mybuf[i] !=
'\r') && (mybuf[i] !=
'\n')) {
255 TRACEI(
DEBUG,
"This does not look like http at pos " << i);
260 if ((!ismine) && (dlen >= 4)) {
261 char check[4] = {00, 00, 00, 00};
262 if (memcmp(mybuf, check, 4)) {
269 TRACEI(ALL,
"This may look like https, but https is not configured");
276 TRACEI(
DEBUG,
"This does not look like https. Protocol not matched.");
284 TRACEI(REQ,
"Protocol matched. https: " << myishttps);
287 hp->ishttps = myishttps;
302 hp->myBuffStart = hp->myBuffEnd = hp->myBuff->
buff;
310char *XrdHttpProtocol::GetClientIPStr() {
313 if (!
Link)
return strdup(
"unknown");
315 if (!ai)
return strdup(
"unknown");
323#if OPENSSL_VERSION_NUMBER < 0x1000105fL
334 int ret = lp->
Send(data, datal);
335 BIO_clear_retry_flags(bio);
338 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
339 BIO_set_retry_write(bio);
355 int ret = lp->
Send(data, datal);
356 BIO_clear_retry_flags(bio);
358 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
359 BIO_set_retry_write(bio);
366#if OPENSSL_VERSION_NUMBER < 0x1000105fL
377 int ret = lp->
Recv(data, datal);
378 BIO_clear_retry_flags(bio);
381 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
382 BIO_set_retry_read(bio);
397 int ret = lp->
Recv(data, datal);
398 BIO_clear_retry_flags(bio);
400 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
401 BIO_set_retry_read(bio);
417#if OPENSSL_VERSION_NUMBER < 0x10100000L
429 if (bio == NULL)
return 0;
445 case BIO_CTRL_GET_CLOSE:
448 case BIO_CTRL_SET_CLOSE:
463BIO *XrdHttpProtocol::CreateBIO(
XrdLink *lp)
482#define TRACELINK Link
490 if (!myBuff || !myBuff->
buff || !myBuff->
bsize) {
491 TRACE(ALL,
" Process. No buffer available. Internal error.");
497 char *nfo = GetClientIPStr();
499 TRACEI(REQ,
" Setting host: " << nfo);
508 if (ishttps && !ssldone) {
511 sbio = CreateBIO(
Link);
512 BIO_set_nbio(sbio, 1);
518 ERR_print_errors(sslbio_err);
527 SSL_set_bio(ssl, sbio, sbio);
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));
538 int res = SSL_accept(ssl);
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));
546 ERR_print_errors(sslbio_err);
555 BIO_set_nbio(sbio, 0);
560 if (HandleAuthentication(
Link)) {
581 if ((rc = getDataOneShot(BuffAvailable())) < 0) {
587 if (BuffUsed() < ResumeBytes)
return 1;
595 if (mon_info.size() >= 1024) {
596 TRACEI(ALL,
"User agent string too long");
598 TRACEI(ALL,
"Internal logic error: Bridge is null after login");
607 SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
622 while ((rc = BuffgetLine(tmpline)) > 0) {
623 std::string traceLine = tmpline.
c_str();
627 TRACE(
DEBUG,
" rc:" << rc <<
" got hdr line: " << traceLine);
628 if ((rc == 2) && (tmpline.
length() > 1) && (tmpline[rc - 1] ==
'\n')) {
630 TRACE(
DEBUG,
" rc:" << rc <<
" detected header end.");
636 TRACE(
DEBUG,
" Parsing first line: " << traceLine.c_str());
639 TRACE(
DEBUG,
" Parsing of first line failed with " << result);
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);
657 TRACEI(REQ,
" rc:" << rc <<
"Header not yet complete.");
662 if ((rc <= 0) && (BuffUsed() >= 16384)) {
663 TRACEI(ALL,
"Corrupted header detected, or line too long. Disconnecting client.");
682 time_t timenow = time(0);
700 TRACEI(REQ,
" rc:" << rc <<
" self-redirecting to http with security token.");
707 struct sockaddr_storage sa;
708 socklen_t sl =
sizeof(sa);
715 switch (sa.ss_family) {
717 if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
724 if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
726 Addr_str = (
char *)malloc(strlen(buf)+3);
734 TRACEI(REQ,
" Can't recognize the address family of the local host.");
742 TRACEI(REQ,
" rc:"<<rc<<
" self-redirecting to http with security token: '"
743 << dest.
c_str() <<
"'");
747 SendSimpleResp(302, NULL, (
char *) dest.
c_str(), 0, 0,
true);
752 TRACEI(REQ,
" rc:" << rc <<
" Can't perform self-redirection.");
756 TRACEI(ALL,
" Could not calculate self-redirection hash");
762 if (!ishttps && !ssldone) {
772 if (t) tim = atoi(t);
774 TRACEI(REQ,
" xrdhttptime not specified. Authentication failed.");
778 TRACEI(REQ,
" Token expired. Authentication failed.");
863 TRACEI(REQ,
" Invalid tk '" << tk <<
"' != '" << hash <<
"'(calculated). Authentication failed.");
870 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
878 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
898 TRACEI(REQ,
" Authorization failed.");
914 TRACEI(REQ,
"Process is exiting rc:" << rc);
922#define TRACELINK Link
976#define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
978#define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
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.")
984int XrdHttpProtocol::Config(
const char *ConfigFN,
XrdOucEnv *myEnv) {
987 std::vector<extHInfo> extHIVec;
989 int cfgFD, GoNo, NoGo = 0, ismine;
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 +
",";
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());
1014 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1016 m_bio_method =
static_cast<BIO_METHOD*
>(OPENSSL_malloc(
sizeof(BIO_METHOD)));
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);
1059 while ((var = Config.GetMyFirstWord())) {
1060 if ((ismine = !strncmp(
"http.", var, 5)) && var[5]) var += 5;
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);
1086 eDest.
Say(
"Config warning: ignoring unknown directive '", var,
"'.");
1101 {
eDest.
Say(
"Config failure: one or more directives are flawed!");
1107 hdr2cgimap[
"Cache-Control"] =
"cache-control";
1110 if (getenv(
"XRDCL_EC"))
usingEC =
true;
1115 std::string default_static_headers;
1117 for (
const auto &header_entry : default_verb->second) {
1118 default_static_headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1123 if (item.first.empty()) {
1126 auto headers = default_static_headers;
1127 for (
const auto &header_entry : item.second) {
1128 headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1136 if (myEnv->
Get(
"XrdCache")) hasCache =
true;
1145 :
"was not configured.");
1146 const char *what = Configed();
1148 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1151 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1153 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1163 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1164 "is meaningless; ignoring key!");
1172 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1173 "a cert specification!");
1184 const char *what1 = 0, *what2 = 0, *what3 = 0;
1189 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1193 what2 =
"xrd.tlsca to supply 'cadir'.";
1197 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1198 :
"xrd.tlsca to supply 'cafile'.");
1202 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1212 {
const char *what = Configed();
1213 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1214 :
"'xrd.tlsca noverify' was specified!");
1216 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1224 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
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;
1237 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1241 return (InitSecurity() ? NoGo : 1);
1248const char *XrdHttpProtocol::Configed()
1250 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1251 if (secxtractor)
return "secxtractor requires";
1252 if (
gridmap)
return "gridmap requires";
1268 if (myBuffEnd >= myBuffStart) {
1270 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1275 dest.
assign(myBuffStart, 0, l-1);
1294 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1296 if ((*p ==
'\n') || (*p ==
'\0')) {
1299 dest.
assign(myBuffStart, 0, l-1);
1315 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1317 if ((*p ==
'\n') || (*p ==
'\0')) {
1321 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1323 dest.
assign(myBuffStart, 0, l1-1);
1327 dest.
insert(myBuffStart, l1, l-1);
1351int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1366 maxread = std::min(blen, BuffAvailable());
1367 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1373 int sslavail = maxread;
1376 int l = SSL_pending(ssl);
1378 sslavail = std::min(maxread, SSL_pending(ssl));
1383 ERR_print_errors(sslbio_err);
1387 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1388 if (sslavail <= 0)
return 0;
1390 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1392 myBuffEnd = myBuff->
buff;
1395 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1398 ERR_print_errors(sslbio_err);
1405 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1407 myBuffEnd = myBuff->
buff;
1413 rlen =
Link->
Recv(myBuffEnd, maxread);
1429 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1436int XrdHttpProtocol::BuffAvailable() {
1439 if (myBuffEnd >= myBuffStart)
1440 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1442 r = myBuffStart - myBuffEnd;
1444 if ((r < 0) || (r > myBuff->
bsize)) {
1445 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1458int XrdHttpProtocol::BuffUsed() {
1461 if (myBuffEnd >= myBuffStart)
1462 r = myBuffEnd - myBuffStart;
1465 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1467 if ((r < 0) || (r > myBuff->
bsize)) {
1468 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1481int XrdHttpProtocol::BuffFree() {
1482 return (myBuff->
bsize - BuffUsed());
1489void XrdHttpProtocol::BuffConsume(
int blen) {
1491 if (blen > myBuff->
bsize) {
1492 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1496 if (blen > BuffUsed()) {
1497 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1501 myBuffStart = myBuffStart + blen;
1503 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1504 myBuffStart -= myBuff->
bsize;
1506 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1507 myBuffEnd -= myBuff->
bsize;
1509 if (BuffUsed() == 0)
1510 myBuffStart = myBuffEnd = myBuff->
buff;
1525int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1528 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1533 if (blen > BuffUsed()) {
1534 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1535 if ( getDataOneShot(blen - BuffUsed(),
true) )
1541 if ( !BuffUsed() ) {
1542 if ( getDataOneShot(blen,
false) )
1550 if (myBuffStart <= myBuffEnd) {
1551 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1554 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1556 *data = myBuffStart;
1567int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1571 if (body && bodylen) {
1572 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1574 r = SSL_write(ssl, body, bodylen);
1576 ERR_print_errors(sslbio_err);
1582 if (r <= 0)
return -1;
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";
1597 ss <<
"HTTP/1.1 " << code <<
" ";
1601 if (code == 200) ss <<
"OK";
1602 else if (code == 100) ss <<
"Continue";
1603 else if (code == 201) ss <<
"Created";
1604 else if (code == 206) ss <<
"Partial Content";
1605 else if (code == 302) ss <<
"Redirect";
1606 else if (code == 307) ss <<
"Temporary Redirect";
1607 else if (code == 400) ss <<
"Bad Request";
1608 else if (code == 401) ss <<
"Unauthorized";
1609 else if (code == 403) ss <<
"Forbidden";
1610 else if (code == 404) ss <<
"Not Found";
1611 else if (code == 405) ss <<
"Method Not Allowed";
1612 else if (code == 409) ss <<
"Conflict";
1613 else if (code == 416) ss <<
"Range Not Satisfiable";
1614 else if (code == 423) ss <<
"Locked";
1615 else if (code == 500) ss <<
"Internal Server Error";
1616 else if (code == 502) ss <<
"Bad Gateway";
1617 else if (code == 504) ss <<
"Gateway Timeout";
1618 else ss <<
"Unknown";
1621 if (keepalive && (code != 100))
1622 ss <<
"Connection: Keep-Alive" << crlf;
1624 ss <<
"Connection: Close" << crlf;
1626 ss <<
"Server: XrootD/" << XrdVSTRING << crlf;
1635 if ((bodylen >= 0) && (code != 100))
1636 ss <<
"Content-Length: " << bodylen << crlf;
1638 if (header_to_add && (header_to_add[0] !=
'\0'))
1639 ss << header_to_add << crlf;
1643 const std::string &outhdr = ss.str();
1644 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1645 if (SendData(outhdr.c_str(), outhdr.size()))
1655int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1656 const std::string crlf =
"\r\n";
1657 std::stringstream ss;
1659 if (header_to_add && (header_to_add[0] !=
'\0')) {
1660 ss << header_to_add << crlf;
1663 ss <<
"Transfer-Encoding: chunked";
1664 TRACEI(RSP,
"Starting chunked response");
1665 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1672int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1673 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1674 if (ChunkRespHeader(content_length))
1677 if (body && SendData(body, content_length))
1680 return ChunkRespFooter();
1687int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1688 const std::string crlf =
"\r\n";
1689 std::stringstream ss;
1691 ss << std::hex << bodylen << std::dec << crlf;
1693 const std::string &chunkhdr = ss.str();
1694 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1695 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1702int XrdHttpProtocol::ChunkRespFooter() {
1703 const std::string crlf =
"\r\n";
1704 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1715int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1717 long long content_length = bodylen;
1719 content_length = body ? strlen(body) : 0;
1722 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1729 return SendData(body, content_length);
1766 sprintf(buf,
"%d",
Port);
1772 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1773 if (rdf && Config(rdf, pi->
theEnv))
return 0;
1778 if ((rdf = getenv(
"XRDROLE"))) {
1781 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1783 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1786 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1790 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1809 char *val, keybuf[1024], parmbuf[1024];
1813 val = Config.GetWord();
1814 if (!val || !val[0]) {
1815 err.
Emsg(
"Config",
"No headerkey specified.");
1820 while ( *val && !isalnum(*val) ) val++;
1821 strcpy(keybuf, val);
1825 pp = keybuf + strlen(keybuf) - 1;
1826 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1831 parm = Config.GetWord();
1834 if(!parm || !parm[0]) {
1835 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1840 while ( *parm && !isalnum(*parm) ) parm++;
1841 strcpy(parmbuf, parm);
1844 pp = parmbuf + strlen(parmbuf) - 1;
1845 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1852 header2cgi[keybuf] = parmbuf;
1854 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1867bool XrdHttpProtocol::InitTLS() {
1892 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1893 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1899 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1912void XrdHttpProtocol::Cleanup() {
1914 TRACE(ALL,
" Cleanup");
1916 if (
BPool && myBuff) {
1917 BuffConsume(BuffUsed());
1931 int ret = SSL_shutdown(ssl);
1935 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1936 ERR_remove_thread_state(
nullptr);
1940 TRACE(ALL,
" SSL_shutdown failed");
1941 ERR_print_errors(sslbio_err);
1975void XrdHttpProtocol::Reset() {
1977 TRACE(ALL,
" Reset");
1986 myBuffStart = myBuffEnd = 0;
1989 DoneSetInfo =
false;
2033int XrdHttpProtocol::xhttpsmode(
XrdOucStream & Config) {
2039 if (!val || !val[0]) {
2040 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2049 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2068int XrdHttpProtocol::xsslverifydepth(
XrdOucStream & Config) {
2074 if (!val || !val[0]) {
2075 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2106 if (!val || !val[0]) {
2107 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2141 if (!val || !val[0]) {
2142 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2178 if (!val || !val[0]) {
2179 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2185 if (!strncmp(val,
"required", 8)) {
2189 if (!val || !val[0]) {
2190 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2198 if (!strcmp(val,
"compatNameGeneration")) {
2201 if (!val || !val[0]) {
2202 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2203 "[compatNameGeneration] parameter");
2229int XrdHttpProtocol::xsslcafile(
XrdOucStream & Config) {
2235 if (!val || !val[0]) {
2236 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2262int XrdHttpProtocol::xsecretkey(
XrdOucStream & Config) {
2264 bool inFile =
false;
2269 if (!val || !val[0]) {
2270 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2278 if (val[0] ==
'/') {
2281 int fd =
open(val, O_RDONLY);
2284 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2288 if (
fstat(fd, &st) != 0 ) {
2289 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2294 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2296 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2301 FILE *fp = fdopen(fd,
"r");
2303 if ( fp ==
nullptr ) {
2304 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2310 while( fgets(line, 1024, fp) ) {
2314 pp = line + strlen(line) - 1;
2315 while ( (pp >= line) && (!isalnum(*pp)) ) {
2322 while ( *pp && !isalnum(*pp) ) pp++;
2324 if ( strlen(pp) >= 32 ) {
2325 eDest.
Say(
"Config",
"Secret key loaded.");
2337 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2342 if ( strlen(val) < 32 ) {
2343 eDest.
Emsg(
"Config",
"Secret key is too short");
2350 if (!inFile)
Config.noEcho();
2374 if (!val || !val[0]) {
2375 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2381 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2400int XrdHttpProtocol::xlistredir(
XrdOucStream & Config) {
2406 if (!val || !val[0]) {
2407 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2433int XrdHttpProtocol::xdesthttps(
XrdOucStream & Config) {
2439 if (!val || !val[0]) {
2440 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2446 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2465int XrdHttpProtocol::xembeddedstatic(
XrdOucStream & Config) {
2471 if (!val || !val[0]) {
2472 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2478 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2497int XrdHttpProtocol::xstaticredir(
XrdOucStream & Config) {
2503 if (!val || !val[0]) {
2504 eDest.
Emsg(
"Config",
"staticredir url not specified");
2532int XrdHttpProtocol::xstaticpreload(
XrdOucStream & Config) {
2533 char *val, *k, key[1024];
2539 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2548 if (!val || !val[0]) {
2549 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2554 int fp =
open(val, O_RDONLY);
2556 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2560 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2562 nfo->data = (
char *)malloc(65536);
2563 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2566 if (nfo->len <= 0) {
2567 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2571 if (nfo->len >= 65536) {
2572 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2602int XrdHttpProtocol::xstaticheader(
XrdOucStream & Config) {
2603 auto val =
Config.GetWord();
2604 std::vector<std::string> verbs;
2606 if (!val || !val[0]) {
2607 eDest.
Emsg(
"Config",
"http.staticheader requires the header to be specified");
2611 std::string match_verb;
2612 std::string_view val_str(val);
2613 if (val_str.substr(0, 6) ==
"-verb=") {
2614 verbs.emplace_back(val_str.substr(6));
2615 }
else if (val_str ==
"-") {
2616 eDest.
Emsg(
"Config",
"http.staticheader is ignoring unknown flag: ", val_str.data());
2623 if (verbs.empty()) {
2624 verbs.emplace_back();
2627 std::string header = val;
2630 std::string header_value;
2631 if (val && val[0]) {
2635 for (
const auto &verb : verbs) {
2638 if (!header_value.empty())
2640 }
else if (header_value.empty()) {
2641 iter->second.clear();
2643 iter->second.emplace_back(header, header_value);
2664int XrdHttpProtocol::xselfhttps2http(
XrdOucStream & Config) {
2670 if (!val || !val[0]) {
2671 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2677 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2699int XrdHttpProtocol::xsecxtractor(
XrdOucStream& Config) {
2705 if (!val || !val[0]) {
2706 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2711 if (!strncmp(val,
"required", 8)) {
2712 isRequiredXtractor =
true;
2715 if (!val || !val[0]) {
2716 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2723 strlcpy(libName, val,
sizeof(libName));
2724 libName[
sizeof(libName) - 1] =
'\0';
2725 char libParms[4096];
2727 if (!
Config.GetRest(libParms, 4095)) {
2728 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2734 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2760 std::vector<extHInfo> &hiVec) {
2761 char *val, path[1024], namebuf[1024];
2764 bool noTlsOK =
false;
2769 if (!val || !val[0]) {
2770 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2773 if (strlen(val) >= 16) {
2774 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2777 strncpy(namebuf, val,
sizeof(namebuf));
2778 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2783 if(val && !strcmp(
"+notls",val)) {
2790 if (!val || !val[0]) {
2791 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2794 if (strlen(val) >= (int)
sizeof(path)) {
2795 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2807 for (
int i = 0; i < (int)hiVec.size(); i++)
2808 {
if (hiVec[i].extHName == namebuf) {
2809 eDest.
Emsg(
"Config",
"Instance name already present for "
2810 "http external handler plugin",
2811 hiVec[i].extHPath.c_str());
2819 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2825 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2846int XrdHttpProtocol::xheader2cgi(
XrdOucStream & Config) {
2869 if (!val || !val[0]) {
2870 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2897int XrdHttpProtocol::xsslcipherfilter(
XrdOucStream & Config) {
2903 if (!val || !val[0]) {
2904 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2934 if (!val || !val[0])
2935 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2939 if (!strcmp(val,
"off"))
2946 if (!strcmp(val,
"on"))
2953 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
2958 char *val =
Config.GetWord();
2960 if(!strcmp(
"tpc",val)) {
2961 if(!(val =
Config.GetWord())) {
2962 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
2964 if(!strcmp(
"fcreds",val)) {
2967 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
2971 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
2995 static struct traceopts {
3007 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3009 if (!(val =
Config.GetWord())) {
3010 eDest.
Emsg(
"config",
"trace option not specified");
3014 if (!strcmp(val,
"off")) trval = 0;
3016 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3017 for (i = 0; i < numopts; i++) {
3018 if (!strcmp(val, tropts[i].opname)) {
3019 if (neg) trval &= ~tropts[i].opval;
3020 else trval |= tropts[i].opval;
3025 eDest.
Emsg(
"config",
"invalid trace option", val);
3044 l = strlen(fname) + 1;
3069 length = fname.
length() + 1;
3081int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3082 const char *libParms) {
3086 if (secxtractor)
return 1;
3088 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3094 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3102int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3103 for (
int i = 0; i < (int) hiVec.size(); i++) {
3104 if(hiVec[i].extHNoTlsOK) {
3106 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3107 hiVec[i].extHParm.c_str(), &myEnv,
3108 hiVec[i].extHName.c_str()))
3115int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3127 for (
int i = 0; i < (int)hiVec.size(); i++) {
3130 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3131 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3132 hiVec[i].extHParm.c_str(), &myEnv,
3133 hiVec[i].extHName.c_str()))
return 1;
3140int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3141 const char *configFN,
const char *libParms,
3142 XrdOucEnv *myEnv,
const char *instName) {
3146 if (ExtHandlerLoaded(instName)) {
3147 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3151 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3155 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3163 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3166 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3167 exthandler[exthandlercnt].name[15] =
'\0';
3168 exthandler[exthandlercnt++].ptr = newhandler;
3181bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3182 for (
int i = 0; i < exthandlercnt; i++) {
3183 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3194 for (
int i = 0; i < exthandlercnt; i++) {
3196 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")
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_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
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)
#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.
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.
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 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
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.
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)
char * Get(const char *varname)
void * GetPtr(const char *varname)
void Put(const char *varname, const char *value)
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)
const char * c_str() const
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
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.