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.");
871 SendSimpleResp(400,
nullptr,
nullptr,
"Authentication failed: invalid token", 0,
false);
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_Xeq(
"cors", xcors);
1083 else if TS_Xeq3(
"exthandler", xexthandler);
1084 else if TS_Xeq(
"selfhttps2http", xselfhttps2http);
1085 else if TS_Xeq(
"embeddedstatic", xembeddedstatic);
1086 else if TS_Xeq(
"listingredir", xlistredir);
1087 else if TS_Xeq(
"staticredir", xstaticredir);
1088 else if TS_Xeq(
"staticpreload", xstaticpreload);
1089 else if TS_Xeq(
"staticheader", xstaticheader);
1090 else if TS_Xeq(
"listingdeny", xlistdeny);
1091 else if TS_Xeq(
"header2cgi", xheader2cgi);
1092 else if TS_Xeq(
"httpsmode", xhttpsmode);
1093 else if TS_Xeq(
"tlsreuse", xtlsreuse);
1094 else if TS_Xeq(
"auth", xauth);
1095 else if TS_Xeq(
"tlsclientauth", xtlsclientauth);
1096 else if TS_Xeq(
"maxdelay", xmaxdelay);
1098 eDest.
Say(
"Config warning: ignoring unknown directive '", var,
"'.");
1113 {
eDest.
Say(
"Config failure: one or more directives are flawed!");
1119 hdr2cgimap[
"Cache-Control"] =
"cache-control";
1122 if (getenv(
"XRDCL_EC"))
usingEC =
true;
1127 std::string default_static_headers;
1129 for (
const auto &header_entry : default_verb->second) {
1130 default_static_headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1135 if (item.first.empty()) {
1138 auto headers = default_static_headers;
1139 for (
const auto &header_entry : item.second) {
1140 headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1148 if (myEnv->
Get(
"XrdCache")) hasCache =
true;
1167 :
"was not configured.");
1168 const char *what = Configed();
1170 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1173 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1175 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1185 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1186 "is meaningless; ignoring key!");
1194 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1195 "a cert specification!");
1206 const char *what1 = 0, *what2 = 0, *what3 = 0;
1211 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1215 what2 =
"xrd.tlsca to supply 'cadir'.";
1219 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1220 :
"xrd.tlsca to supply 'cafile'.");
1224 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1234 {
const char *what = Configed();
1235 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1236 :
"'xrd.tlsca noverify' was specified!");
1238 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1246 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1251 const char *how =
"completed.";
1252 eDest.
Say(
"++++++ HTTPS initialization started.");
1253 if (!
InitTLS()) {NoGo = 1; how =
"failed.";}
1254 eDest.
Say(
"------ HTTPS initialization ", how);
1255 if (NoGo)
return NoGo;
1259 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1263 return (InitSecurity() ? NoGo : 1);
1270 const char *XrdHttpProtocol::Configed()
1272 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1273 if (secxtractor)
return "secxtractor requires";
1274 if (
gridmap)
return "gridmap requires";
1290 if (myBuffEnd >= myBuffStart) {
1292 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1297 dest.
assign(myBuffStart, 0, l-1);
1316 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1318 if ((*p ==
'\n') || (*p ==
'\0')) {
1321 dest.
assign(myBuffStart, 0, l-1);
1337 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1339 if ((*p ==
'\n') || (*p ==
'\0')) {
1343 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1345 dest.
assign(myBuffStart, 0, l1-1);
1349 dest.
insert(myBuffStart, l1, l-1);
1373 int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1388 maxread = std::min(blen, BuffAvailable());
1389 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1395 int sslavail = maxread;
1398 int l = SSL_pending(ssl);
1400 sslavail = std::min(maxread, SSL_pending(ssl));
1405 ERR_print_errors(sslbio_err);
1409 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1410 if (sslavail <= 0)
return 0;
1412 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1414 myBuffEnd = myBuff->
buff;
1417 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1420 ERR_print_errors(sslbio_err);
1427 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1429 myBuffEnd = myBuff->
buff;
1435 rlen =
Link->
Recv(myBuffEnd, maxread);
1451 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1458 int XrdHttpProtocol::BuffAvailable() {
1461 if (myBuffEnd >= myBuffStart)
1462 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1464 r = myBuffStart - myBuffEnd;
1466 if ((r < 0) || (r > myBuff->
bsize)) {
1467 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1480 int XrdHttpProtocol::BuffUsed() {
1483 if (myBuffEnd >= myBuffStart)
1484 r = myBuffEnd - myBuffStart;
1487 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1489 if ((r < 0) || (r > myBuff->
bsize)) {
1490 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1503 int XrdHttpProtocol::BuffFree() {
1504 return (myBuff->
bsize - BuffUsed());
1511 void XrdHttpProtocol::BuffConsume(
int blen) {
1513 if (blen > myBuff->
bsize) {
1514 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1518 if (blen > BuffUsed()) {
1519 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1523 myBuffStart = myBuffStart + blen;
1525 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1526 myBuffStart -= myBuff->
bsize;
1528 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1529 myBuffEnd -= myBuff->
bsize;
1531 if (BuffUsed() == 0)
1532 myBuffStart = myBuffEnd = myBuff->
buff;
1547 int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1550 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1555 if (blen > BuffUsed()) {
1556 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1557 if ( getDataOneShot(blen - BuffUsed(),
true) )
1563 if ( !BuffUsed() ) {
1564 if ( getDataOneShot(blen,
false) )
1572 if (myBuffStart <= myBuffEnd) {
1573 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1576 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1578 *data = myBuffStart;
1589 int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1593 if (body && bodylen) {
1594 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1596 r = SSL_write(ssl, body, bodylen);
1598 ERR_print_errors(sslbio_err);
1604 if (r <= 0)
return -1;
1615 int XrdHttpProtocol::StartSimpleResp(
int code,
const char *desc,
1616 const char *header_to_add,
1617 long long bodylen,
bool keepalive) {
1618 std::stringstream ss;
1619 const std::string crlf =
"\r\n";
1621 ss <<
"HTTP/1.1 " << code <<
" ";
1630 if (keepalive && (code != 100))
1631 ss <<
"Connection: Keep-Alive" << crlf;
1633 ss <<
"Connection: Close" << crlf;
1635 ss <<
"Server: XrootD/" << XrdVSTRING << crlf;
1646 if(corsAllowOrigin) {
1647 ss << *corsAllowOrigin << crlf;
1651 if ((bodylen >= 0) && (code != 100))
1652 ss <<
"Content-Length: " << bodylen << crlf;
1654 if (header_to_add && (header_to_add[0] !=
'\0')) ss << header_to_add << crlf;
1658 const std::string &outhdr = ss.str();
1659 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1660 if (SendData(outhdr.c_str(), outhdr.size()))
1670 int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1671 const std::string crlf =
"\r\n";
1672 std::stringstream ss;
1674 if (header_to_add && (header_to_add[0] !=
'\0')) {
1675 ss << header_to_add << crlf;
1678 ss <<
"Transfer-Encoding: chunked";
1679 TRACEI(RSP,
"Starting chunked response");
1680 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1687 int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1688 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1689 if (ChunkRespHeader(content_length))
1692 if (body && SendData(body, content_length))
1695 return ChunkRespFooter();
1702 int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1703 const std::string crlf =
"\r\n";
1704 std::stringstream ss;
1708 const std::string &chunkhdr = ss.str();
1709 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1710 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1717 int XrdHttpProtocol::ChunkRespFooter() {
1718 const std::string crlf =
"\r\n";
1719 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1730 int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1732 long long content_length = bodylen;
1734 content_length = body ? strlen(body) : 0;
1737 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1744 return SendData(body, content_length);
1781 sprintf(buf,
"%d",
Port);
1787 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1793 if ((rdf = getenv(
"XRDROLE"))) {
1796 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1798 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1801 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1805 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1824 char *val, keybuf[1024], parmbuf[1024];
1829 if (!val || !val[0]) {
1830 err.
Emsg(
"Config",
"No headerkey specified.");
1835 while ( *val && !isalnum(*val) ) val++;
1836 strcpy(keybuf, val);
1840 pp = keybuf + strlen(keybuf) - 1;
1841 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1849 if(!parm || !parm[0]) {
1850 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1855 while ( *parm && !isalnum(*parm) ) parm++;
1856 strcpy(parmbuf, parm);
1859 pp = parmbuf + strlen(parmbuf) - 1;
1860 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1867 header2cgi[keybuf] = parmbuf;
1869 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1882 bool XrdHttpProtocol::InitTLS() {
1907 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1908 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1914 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1930 void XrdHttpProtocol::Cleanup() {
1932 TRACE(ALL,
" Cleanup");
1934 if (
BPool && myBuff) {
1935 BuffConsume(BuffUsed());
1950 int ret = SSL_shutdown(ssl);
1954 ret = SSL_shutdown(ssl);
1956 TRACE(ALL,
"SSL server failed to receive the SSL shutdown message from the client");
1957 ERR_print_errors(sslbio_err);
1961 TRACE(ALL,
"SSL server failed to send the shutdown message to the client");
1962 ERR_print_errors(sslbio_err);
1996 void XrdHttpProtocol::Reset() {
1998 TRACE(ALL,
" Reset");
2007 myBuffStart = myBuffEnd = 0;
2010 DoneSetInfo =
false;
2060 if (!val || !val[0]) {
2061 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2070 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2095 if (!val || !val[0]) {
2096 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2127 if (!val || !val[0]) {
2128 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2162 if (!val || !val[0]) {
2163 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2199 if (!val || !val[0]) {
2200 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2206 if (!strncmp(val,
"required", 8)) {
2210 if (!val || !val[0]) {
2211 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2219 if (!strcmp(val,
"compatNameGeneration")) {
2222 if (!val || !val[0]) {
2223 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2224 "[compatNameGeneration] parameter");
2256 if (!val || !val[0]) {
2257 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2285 bool inFile =
false;
2290 if (!val || !val[0]) {
2291 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2299 if (val[0] ==
'/') {
2302 int fd =
open(val, O_RDONLY);
2305 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2309 if (
fstat(fd, &st) != 0 ) {
2310 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2315 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2317 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2322 FILE *fp = fdopen(fd,
"r");
2324 if ( fp ==
nullptr ) {
2325 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2331 while( fgets(line, 1024, fp) ) {
2335 pp = line + strlen(line) - 1;
2336 while ( (pp >= line) && (!isalnum(*pp)) ) {
2343 while ( *pp && !isalnum(*pp) ) pp++;
2345 if ( strlen(pp) >= 32 ) {
2346 eDest.
Say(
"Config",
"Secret key loaded.");
2358 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2363 if ( strlen(val) < 32 ) {
2364 eDest.
Emsg(
"Config",
"Secret key is too short");
2371 if (!inFile)
Config.noEcho();
2395 if (!val || !val[0]) {
2396 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2402 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2427 if (!val || !val[0]) {
2428 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2460 if (!val || !val[0]) {
2461 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2467 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2492 if (!val || !val[0]) {
2493 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2499 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2524 if (!val || !val[0]) {
2525 eDest.
Emsg(
"Config",
"staticredir url not specified");
2554 char *val, *k, key[1024];
2560 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2569 if (!val || !val[0]) {
2570 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2575 int fp =
open(val, O_RDONLY);
2577 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2581 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2583 nfo->data = (
char *)malloc(65536);
2584 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2587 if (nfo->len <= 0) {
2588 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2592 if (nfo->len >= 65536) {
2593 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2624 auto val =
Config.GetWord();
2625 std::vector<std::string> verbs;
2627 if (!val || !val[0]) {
2628 eDest.
Emsg(
"Config",
"http.staticheader requires the header to be specified");
2632 std::string match_verb;
2633 std::string_view val_str(val);
2634 if (val_str.substr(0, 6) ==
"-verb=") {
2635 verbs.emplace_back(val_str.substr(6));
2636 }
else if (val_str ==
"-") {
2637 eDest.
Emsg(
"Config",
"http.staticheader is ignoring unknown flag: ", val_str.data());
2644 if (verbs.empty()) {
2645 verbs.emplace_back();
2648 std::string header = val;
2651 std::string header_value;
2652 if (val && val[0]) {
2656 for (
const auto &verb : verbs) {
2659 if (!header_value.empty())
2661 }
else if (header_value.empty()) {
2662 iter->second.clear();
2664 iter->second.emplace_back(header, header_value);
2691 if (!val || !val[0]) {
2692 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2698 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2726 if (!val || !val[0]) {
2727 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2732 if (!strncmp(val,
"required", 8)) {
2733 isRequiredXtractor =
true;
2736 if (!val || !val[0]) {
2737 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2744 strlcpy(libName, val,
sizeof(libName));
2745 libName[
sizeof(libName) - 1] =
'\0';
2746 char libParms[4096];
2748 if (!
Config.GetRest(libParms, 4095)) {
2749 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2755 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2767 if (!val || !val[0]) {
2768 eDest.
Emsg(
"Config",
"No CORS plugin specified.");
2793 std::vector<extHInfo> &hiVec) {
2794 char *val, path[1024], namebuf[1024];
2797 bool noTlsOK =
false;
2802 if (!val || !val[0]) {
2803 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2806 if (strlen(val) >= 16) {
2807 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2810 strncpy(namebuf, val,
sizeof(namebuf));
2811 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2816 if(val && !strcmp(
"+notls",val)) {
2823 if (!val || !val[0]) {
2824 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2827 if (strlen(val) >= (int)
sizeof(path)) {
2828 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2840 for (
int i = 0; i < (int)hiVec.size(); i++)
2841 {
if (hiVec[i].extHName == namebuf) {
2842 eDest.
Emsg(
"Config",
"Instance name already present for "
2843 "http external handler plugin",
2844 hiVec[i].extHPath.c_str());
2852 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2858 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2902 if (!val || !val[0]) {
2903 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2936 if (!val || !val[0]) {
2937 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2967 if (!val || !val[0])
2968 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2972 if (!strcmp(val,
"off"))
2979 if (!strcmp(val,
"on"))
2986 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
2991 auto val =
Config.GetWord();
2992 if (!val || !val[0])
2993 {
eDest.
Emsg(
"Config",
"tlsclientauth argument not specified");
return 1;}
2995 if (!strcmp(val,
"off"))
2999 if (!strcmp(val,
"on"))
3004 eDest.
Emsg(
"config",
"invalid tlsclientauth parameter -", val);
3009 char *val =
Config.GetWord();
3011 if(!strcmp(
"tpc",val)) {
3012 if(!(val =
Config.GetWord())) {
3013 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
3015 if(!strcmp(
"fcreds",val)) {
3018 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
3022 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
3029 char *val =
Config.GetWord();
3035 eDest.
Emsg(
"Config",
"http.maxdelay requires an argument in seconds (default is 30). Example: http.maxdelay 30");
3059 static struct traceopts {
3071 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3073 if (!(val =
Config.GetWord())) {
3074 eDest.
Emsg(
"config",
"trace option not specified");
3078 if (!strcmp(val,
"off")) trval = 0;
3080 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3081 for (i = 0; i < numopts; i++) {
3082 if (!strcmp(val, tropts[i].opname)) {
3083 if (neg) trval &= ~tropts[i].opval;
3084 else trval |= tropts[i].opval;
3089 eDest.
Emsg(
"config",
"invalid trace option", val);
3108 l = strlen(fname) + 1;
3133 length = fname.
length() + 1;
3145 int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3146 const char *libParms) {
3150 if (secxtractor)
return 1;
3152 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3158 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3166 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3167 for (
int i = 0; i < (int) hiVec.size(); i++) {
3168 if(hiVec[i].extHNoTlsOK) {
3170 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3171 hiVec[i].extHParm.c_str(), &myEnv,
3172 hiVec[i].extHName.c_str()))
3179 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3191 for (
int i = 0; i < (int)hiVec.size(); i++) {
3194 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3195 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3196 hiVec[i].extHParm.c_str(), &myEnv,
3197 hiVec[i].extHName.c_str()))
return 1;
3204 int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3205 const char *configFN,
const char *libParms,
3206 XrdOucEnv *myEnv,
const char *instName) {
3210 if (ExtHandlerLoaded(instName)) {
3211 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3215 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3219 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3227 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3230 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3231 exthandler[exthandlercnt].name[15] =
'\0';
3232 exthandler[exthandlercnt++].ptr = newhandler;
3242 int XrdHttpProtocol::LoadCorsHandler(
XrdSysError *
eDest,
const char *libname) {
3247 if(ep && (
xrdcors = ep()))
return 0;
3254 bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3255 for (
int i = 0; i < exthandlercnt; i++) {
3256 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3267 for (
int i = 0; i < exthandlercnt; i++) {
3269 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)
std::string httpStatusToString(int status)
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.