24 #include "XrdVersion.hh"
52 #include <openssl/err.h>
53 #include <openssl/ssl.h>
55 #include <arpa/inet.h>
62 #define XRHTTP_TK_GRACETIME 600
106 BIO *XrdHttpProtocol::sslbio_err = 0;
108 bool XrdHttpProtocol::isRequiredXtractor =
false;
112 int XrdHttpProtocol::exthandlercnt = 0;
115 bool XrdHttpProtocol::usingEC = false;
116 bool XrdHttpProtocol::hasCache= false;
137 const char *TraceID =
"Protocol";
164 "xrootd protocol anchor");
174 :
XrdProtocol(
"HTTP protocol handler"), ProtLink(this),
175 SecEntity(
""), CurrentReq(this, ReadRangeConfig) {
200 char mybuf[16], mybuf2[1024];
203 bool myishttps =
false;
207 if ((dlen = lp->
Peek(mybuf, (
int) sizeof (mybuf),
hailWait)) < (
int)
sizeof (mybuf)) {
208 if (dlen <= 0) lp->
setEtext(
"handshake not received");
211 mybuf[dlen - 1] =
'\0';
219 for (
int i = 0; i < dlen; i++) {
221 sprintf(mybuf3,
"%.02d ", mybuf[i]);
222 strcat(mybuf2, mybuf3);
229 for (
int i = 0; i < dlen - 1; i++)
230 if (!isprint(mybuf[i]) && (mybuf[i] !=
'\r') && (mybuf[i] !=
'\n')) {
232 TRACEI(
DEBUG,
"This does not look like http at pos " << i);
237 if ((!ismine) && (dlen >= 4)) {
238 char check[4] = {00, 00, 00, 00};
239 if (memcmp(mybuf, check, 4)) {
246 TRACEI(ALL,
"This may look like https, but https is not configured");
253 TRACEI(
DEBUG,
"This does not look like https. Protocol not matched.");
261 TRACEI(REQ,
"Protocol matched. https: " << myishttps);
264 hp->ishttps = myishttps;
279 hp->myBuffStart = hp->myBuffEnd = hp->myBuff->
buff;
287 char *XrdHttpProtocol::GetClientIPStr() {
290 if (!
Link)
return strdup(
"unknown");
292 if (!ai)
return strdup(
"unknown");
309 int ret = lp->
Send(data, datal);
310 BIO_clear_retry_flags(bio);
312 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
313 BIO_set_retry_write(bio);
327 int ret = lp->
Recv(data, datal);
328 BIO_clear_retry_flags(bio);
330 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
331 BIO_set_retry_read(bio);
338 BIO_set_init(bio, 0);
339 BIO_set_data(bio, NULL);
340 BIO_set_flags(bio, 0);
346 if (bio == NULL)
return 0;
347 if (BIO_get_shutdown(bio)) {
348 if (BIO_get_data(bio)) {
351 BIO_set_init(bio, 0);
352 BIO_set_flags(bio, 0);
361 case BIO_CTRL_GET_CLOSE:
362 ret = BIO_get_shutdown(bio);
364 case BIO_CTRL_SET_CLOSE:
365 BIO_set_shutdown(bio, (
int)num);
378 BIO *XrdHttpProtocol::CreateBIO(
XrdLink *lp)
385 BIO_set_shutdown(ret, 0);
386 BIO_set_data(ret, lp);
387 BIO_set_init(ret, 1);
396 #define TRACELINK Link
408 if (!myBuff || !myBuff->
buff || !myBuff->
bsize) {
409 TRACE(ALL,
" Process. No buffer available. Internal error.");
415 char *nfo = GetClientIPStr();
417 TRACEI(REQ,
" Setting host: " << nfo);
426 if (ishttps && !ssldone) {
429 sbio = CreateBIO(
Link);
430 BIO_set_nbio(sbio, 1);
436 ERR_print_errors(sslbio_err);
445 SSL_set_bio(ssl, sbio, sbio);
452 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_RCVTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
453 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_SNDTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
456 int res = SSL_accept(ssl);
458 if ((res == -1) && (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ)) {
459 TRACEI(
DEBUG,
" SSL_accept wants to read more bytes... err:" << SSL_get_error(ssl, res));
464 ERR_print_errors(sslbio_err);
473 BIO_set_nbio(sbio, 0);
499 if ((rc = getDataOneShot(BuffAvailable())) < 0) {
505 if (BuffUsed() < ResumeBytes)
return 1;
513 if (mon_info.size() >= 1024) {
514 TRACEI(ALL,
"User agent string too long");
516 TRACEI(ALL,
"Internal logic error: Bridge is null after login");
525 SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
540 while ((rc = BuffgetLine(tmpline)) > 0) {
541 std::string traceLine = tmpline.
c_str();
545 TRACE(
DEBUG,
" rc:" << rc <<
" got hdr line: " << traceLine);
546 if ((rc == 2) && (tmpline.
length() == 2) && (tmpline[0] ==
'\r') && (tmpline[1] ==
'\n')) {
549 TRACE(
DEBUG,
" rc:" << rc <<
" detected header end.");
556 TRACE(
DEBUG,
" Parsing first line: " << traceLine.c_str());
559 TRACE(
DEBUG,
" Parsing of first line failed with " << result);
565 TRACE(
DEBUG,
" Parsing of header line failed with " << result)
566 SendSimpleResp(400,NULL,NULL,
"Malformed header line. Hint: ensure the line finishes with \"\\r\\n\"", 0,
false);
577 TRACEI(REQ,
" rc:" << rc <<
"Header not yet complete.");
582 if ((rc <= 0) && (BuffUsed() >= 16384)) {
583 TRACEI(ALL,
"Corrupted header detected, or line too long. Disconnecting client.");
602 time_t timenow = time(0);
620 TRACEI(REQ,
" rc:" << rc <<
" self-redirecting to http with security token.");
627 struct sockaddr_storage sa;
628 socklen_t sl =
sizeof(sa);
635 switch (sa.ss_family) {
637 if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
644 if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
646 Addr_str = (
char *)malloc(strlen(buf)+3);
654 TRACEI(REQ,
" Can't recognize the address family of the local host.");
662 TRACEI(REQ,
" rc:"<<rc<<
" self-redirecting to http with security token: '"
663 << dest.
c_str() <<
"'");
667 SendSimpleResp(302, NULL, (
char *) dest.
c_str(), 0, 0,
true);
672 TRACEI(REQ,
" rc:" << rc <<
" Can't perform self-redirection.");
676 TRACEI(ALL,
" Could not calculate self-redirection hash");
682 if (!ishttps && !ssldone) {
692 if (t) tim = atoi(t);
694 TRACEI(REQ,
" xrdhttptime not specified. Authentication failed.");
698 TRACEI(REQ,
" Token expired. Authentication failed.");
783 TRACEI(REQ,
" Invalid tk '" << tk <<
"' != '" << hash <<
"'(calculated). Authentication failed.");
790 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
798 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
818 TRACEI(REQ,
" Authorization failed.");
835 TRACEI(REQ,
"Process is exiting rc:" << rc);
843 #define TRACELINK Link
897 #define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
899 #define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
901 #define HTTPS_ALERT(x,y,z) httpsspec = true;\
902 if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
903 eDest.Say("Config http." x " overrides the xrd." y " directive.")
905 int XrdHttpProtocol::Config(
const char *ConfigFN,
XrdOucEnv *myEnv) {
908 std::vector<extHInfo> extHIVec;
910 int cfgFD, GoNo, NoGo = 0, ismine;
927 eDest.
Emsg(
"httpMon", rc,
"create stats thread");
935 if(nonIanaChecksums.size()) {
936 std::stringstream warningMsgSS;
937 warningMsgSS <<
"Config warning: the following checksum algorithms are not IANA compliant: [";
938 std::string unknownCksumString;
939 for(
auto unknownCksum: nonIanaChecksums) {
940 unknownCksumString += unknownCksum +
",";
942 unknownCksumString.erase(unknownCksumString.size() - 1);
943 warningMsgSS << unknownCksumString <<
"]" <<
". They therefore cannot be queried by a user via HTTP." ;
944 eDest.
Say(warningMsgSS.str().c_str());
970 if ((cfgFD =
open(ConfigFN, O_RDONLY, 0)) < 0)
971 return eDest.
Emsg(
"Config", errno,
"open config file", ConfigFN);
973 static const char *cvec[] = {
"*** http protocol config:", 0 };
978 while ((var =
Config.GetMyFirstWord())) {
979 if ((ismine = !strncmp(
"http.", var, 5)) && var[5]) var += 5;
982 if TS_Xeq(
"trace", xtrace);
983 else if TS_Xeq(
"cert", xsslcert);
984 else if TS_Xeq(
"key", xsslkey);
985 else if TS_Xeq(
"cadir", xsslcadir);
986 else if TS_Xeq(
"cipherfilter", xsslcipherfilter);
987 else if TS_Xeq(
"gridmap", xgmap);
988 else if TS_Xeq(
"cafile", xsslcafile);
989 else if TS_Xeq(
"secretkey", xsecretkey);
990 else if TS_Xeq(
"desthttps", xdesthttps);
991 else if TS_Xeq(
"secxtractor", xsecxtractor);
992 else if TS_Xeq(
"cors", xcors);
993 else if TS_Xeq3(
"exthandler", xexthandler);
994 else if TS_Xeq(
"selfhttps2http", xselfhttps2http);
995 else if TS_Xeq(
"embeddedstatic", xembeddedstatic);
996 else if TS_Xeq(
"listingredir", xlistredir);
997 else if TS_Xeq(
"staticredir", xstaticredir);
998 else if TS_Xeq(
"staticpreload", xstaticpreload);
999 else if TS_Xeq(
"staticheader", xstaticheader);
1000 else if TS_Xeq(
"listingdeny", xlistdeny);
1001 else if TS_Xeq(
"listing", xlisting);
1002 else if TS_Xeq(
"header2cgi", xheader2cgi);
1003 else if TS_Xeq(
"httpsmode", xhttpsmode);
1004 else if TS_Xeq(
"tlsreuse", xtlsreuse);
1005 else if TS_Xeq(
"auth", xauth);
1006 else if TS_Xeq(
"tlsclientauth", xtlsclientauth);
1007 else if TS_Xeq(
"maxdelay", xmaxdelay);
1009 eDest.
Say(
"Config warning: ignoring unknown directive '", var,
"'.");
1024 {
eDest.
Say(
"Config failure: one or more directives are flawed!");
1030 hdr2cgimap[
"Cache-Control"] =
"cache-control";
1033 if (getenv(
"XRDCL_EC"))
usingEC =
true;
1038 std::string default_static_headers;
1040 for (
const auto &header_entry : default_verb->second) {
1041 default_static_headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1046 if (item.first.empty()) {
1049 auto headers = default_static_headers;
1050 for (
const auto &header_entry : item.second) {
1051 headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1059 if (myEnv->
Get(
"XrdCache")) hasCache =
true;
1078 :
"was not configured.");
1079 const char *what = Configed();
1081 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1084 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1086 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1096 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1097 "is meaningless; ignoring key!");
1105 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1106 "a cert specification!");
1117 const char *what1 = 0, *what2 = 0, *what3 = 0;
1122 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1126 what2 =
"xrd.tlsca to supply 'cadir'.";
1130 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1131 :
"xrd.tlsca to supply 'cafile'.");
1135 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1149 {
const char *what = Configed();
1150 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1151 :
"'xrd.tlsca noverify' was specified!");
1153 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1161 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1166 const char *how =
"completed.";
1167 eDest.
Say(
"++++++ HTTPS initialization started.");
1168 if (!
InitTLS()) {NoGo = 1; how =
"failed.";}
1169 eDest.
Say(
"------ HTTPS initialization ", how);
1170 if (NoGo)
return NoGo;
1174 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1178 return (InitSecurity() ? NoGo : 1);
1185 const char *XrdHttpProtocol::Configed()
1187 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1188 if (secxtractor)
return "secxtractor requires";
1189 if (
gridmap)
return "gridmap requires";
1205 if (myBuffEnd >= myBuffStart) {
1207 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1212 dest.
assign(myBuffStart, 0, l-1);
1231 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1233 if ((*p ==
'\n') || (*p ==
'\0')) {
1236 dest.
assign(myBuffStart, 0, l-1);
1252 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1254 if ((*p ==
'\n') || (*p ==
'\0')) {
1258 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1260 dest.
assign(myBuffStart, 0, l1-1);
1264 dest.
insert(myBuffStart, l1, l-1);
1288 int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1303 maxread = std::min(blen, BuffAvailable());
1304 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1310 int sslavail = maxread;
1313 int l = SSL_pending(ssl);
1315 sslavail = std::min(maxread, SSL_pending(ssl));
1320 ERR_print_errors(sslbio_err);
1324 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1325 if (sslavail <= 0)
return 0;
1327 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1329 myBuffEnd = myBuff->
buff;
1332 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1335 ERR_print_errors(sslbio_err);
1342 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1344 myBuffEnd = myBuff->
buff;
1350 rlen =
Link->
Recv(myBuffEnd, maxread);
1366 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1373 int XrdHttpProtocol::BuffAvailable() {
1376 if (myBuffEnd >= myBuffStart)
1377 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1379 r = myBuffStart - myBuffEnd;
1381 if ((r < 0) || (r > myBuff->
bsize)) {
1382 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1395 int XrdHttpProtocol::BuffUsed() {
1398 if (myBuffEnd >= myBuffStart)
1399 r = myBuffEnd - myBuffStart;
1402 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1404 if ((r < 0) || (r > myBuff->
bsize)) {
1405 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1418 int XrdHttpProtocol::BuffFree() {
1419 return (myBuff->
bsize - BuffUsed());
1426 void XrdHttpProtocol::BuffConsume(
int blen) {
1428 if (blen > myBuff->
bsize) {
1429 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1433 if (blen > BuffUsed()) {
1434 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1438 myBuffStart = myBuffStart + blen;
1440 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1441 myBuffStart -= myBuff->
bsize;
1443 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1444 myBuffEnd -= myBuff->
bsize;
1446 if (BuffUsed() == 0)
1447 myBuffStart = myBuffEnd = myBuff->
buff;
1462 int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1465 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1470 if (blen > BuffUsed()) {
1471 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1472 if ( getDataOneShot(blen - BuffUsed(),
true) )
1478 if ( !BuffUsed() ) {
1479 if ( getDataOneShot(blen,
false) )
1487 if (myBuffStart <= myBuffEnd) {
1488 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1491 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1493 *data = myBuffStart;
1504 int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1508 if (body && bodylen) {
1509 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1511 r = SSL_write(ssl, body, bodylen);
1513 ERR_print_errors(sslbio_err);
1525 return r <= 0 ? -1 : 0;
1532 int XrdHttpProtocol::StartSimpleResp(
int code,
const char *desc,
1533 const char *header_to_add,
1534 long long bodylen,
bool keepalive) {
1535 std::stringstream ss;
1536 const std::string crlf =
"\r\n";
1538 ss <<
"HTTP/1.1 " << code <<
" ";
1547 if (keepalive && (code != 100))
1548 ss <<
"Connection: Keep-Alive" << crlf;
1550 ss <<
"Connection: Close" << crlf;
1552 ss <<
"Server: XRootD" << crlf;
1563 if(corsAllowOrigin) {
1564 ss << *corsAllowOrigin << crlf;
1568 if ((bodylen >= 0) && (code != 100))
1569 ss <<
"Content-Length: " << bodylen << crlf;
1571 if (header_to_add && (header_to_add[0] !=
'\0')) ss << header_to_add << crlf;
1575 const std::string &outhdr = ss.str();
1576 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1577 if (SendData(outhdr.c_str(), outhdr.size()))
1587 int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1588 const std::string crlf =
"\r\n";
1589 std::stringstream ss;
1593 if (header_to_add && (header_to_add[0] !=
'\0')) {
1594 ss << header_to_add << crlf;
1597 ss <<
"Transfer-Encoding: chunked";
1598 TRACEI(RSP,
"Starting chunked response");
1600 int r = StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1609 int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1610 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1611 long long header_len = (bodylen < 0) ? 0 : content_length;
1615 if (ChunkRespHeader(header_len)) {
1620 if (body && SendData(body, content_length)){
1625 int r = ChunkRespFooter();
1627 if (content_length == 0 || bodylen == -1) {
1642 int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1643 const std::string crlf =
"\r\n";
1644 std::stringstream ss;
1648 const std::string &chunkhdr = ss.str();
1649 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1650 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1657 int XrdHttpProtocol::ChunkRespFooter() {
1658 const std::string crlf =
"\r\n";
1659 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1670 int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1676 long long content_length = bodylen;
1678 content_length = body ? strlen(body) : 0;
1681 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0) {
1688 if (body) r = SendData(body, content_length);
1726 sprintf(buf,
"%d",
Port);
1732 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1738 if ((rdf = getenv(
"XRDROLE"))) {
1741 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1743 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1746 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1750 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1769 char *val, keybuf[1024], parmbuf[1024];
1771 bool strip_on_redirect =
false;
1775 if (!val || !val[0]) {
1776 err.
Emsg(
"Config",
"No headerkey specified.");
1781 while ( *val && !isalnum(*val) ) val++;
1782 strcpy(keybuf, val);
1786 pp = keybuf + strlen(keybuf) - 1;
1787 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1795 if(!parm || !parm[0]) {
1796 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1801 while ( *parm && !isalnum(*parm) ) parm++;
1802 strcpy(parmbuf, parm);
1805 pp = parmbuf + strlen(parmbuf) - 1;
1806 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1812 char *nextWord =
Config.GetWord();
1813 if (nextWord && nextWord[0] && !strcasecmp(nextWord,
"strip-on-redirect")) {
1814 strip_on_redirect =
true;
1819 header2cgi[keybuf] = parmbuf;
1820 if (strip_on_redirect) {
1824 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1837 bool XrdHttpProtocol::InitTLS() {
1866 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1867 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1873 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1889 void XrdHttpProtocol::Cleanup() {
1891 TRACE(ALL,
" Cleanup");
1893 if (
BPool && myBuff) {
1894 BuffConsume(BuffUsed());
1909 int ret = SSL_shutdown(ssl);
1913 ret = SSL_shutdown(ssl);
1915 TRACE(ALL,
"SSL server failed to receive the SSL shutdown message from the client");
1916 ERR_print_errors(sslbio_err);
1920 TRACE(ALL,
"SSL server failed to send the shutdown message to the client");
1921 ERR_print_errors(sslbio_err);
1955 void XrdHttpProtocol::Reset() {
1957 TRACE(ALL,
" Reset");
1966 myBuffStart = myBuffEnd = 0;
1969 DoneSetInfo =
false;
2019 if (!val || !val[0]) {
2020 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2029 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2054 if (!val || !val[0]) {
2055 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2086 if (!val || !val[0]) {
2087 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2121 if (!val || !val[0]) {
2122 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2158 if (!val || !val[0]) {
2159 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2165 if (!strncmp(val,
"required", 8)) {
2169 if (!val || !val[0]) {
2170 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2178 if (!strcmp(val,
"compatNameGeneration")) {
2181 if (!val || !val[0]) {
2182 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2183 "[compatNameGeneration] parameter");
2215 if (!val || !val[0]) {
2216 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2244 bool inFile =
false;
2249 if (!val || !val[0]) {
2250 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2258 if (val[0] ==
'/') {
2261 int fd =
open(val, O_RDONLY);
2264 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2268 if (
fstat(fd, &st) != 0 ) {
2269 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2274 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2276 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2281 FILE *fp = fdopen(fd,
"r");
2283 if ( fp ==
nullptr ) {
2284 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2290 while( fgets(line, 1024, fp) ) {
2294 pp = line + strlen(line) - 1;
2295 while ( (pp >= line) && (!isalnum(*pp)) ) {
2302 while ( *pp && !isalnum(*pp) ) pp++;
2304 if ( strlen(pp) >= 32 ) {
2305 eDest.
Say(
"Config",
"Secret key loaded.");
2317 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2322 if ( strlen(val) < 32 ) {
2323 eDest.
Emsg(
"Config",
"Secret key is too short");
2330 if (!inFile)
Config.noEcho();
2354 if (!val || !val[0]) {
2355 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2361 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2385 if (!val || !val[0]) {
2386 eDest.
Emsg(
"Config",
"listing flag not specified");
2390 int denyCmp = strncasecmp(val,
"deny",4);
2391 if (denyCmp && strncasecmp(val,
"allow",5)) {
2392 eDest.
Emsg(
"Config",
"http.listing option only accepts \"allow\" or \"deny\"");
2422 if (!val || !val[0]) {
2423 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2455 if (!val || !val[0]) {
2456 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2462 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2487 if (!val || !val[0]) {
2488 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2494 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2519 if (!val || !val[0]) {
2520 eDest.
Emsg(
"Config",
"staticredir url not specified");
2549 char *val, *k, key[1024];
2555 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2564 if (!val || !val[0]) {
2565 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2570 int fp =
open(val, O_RDONLY);
2572 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2576 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2578 nfo->data = (
char *)malloc(65536);
2579 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2582 if (nfo->len <= 0) {
2583 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2587 if (nfo->len >= 65536) {
2588 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2619 auto val =
Config.GetWord();
2620 std::vector<std::string> verbs;
2622 if (!val || !val[0]) {
2623 eDest.
Emsg(
"Config",
"http.staticheader requires the header to be specified");
2627 std::string match_verb;
2628 std::string_view val_str(val);
2629 if (val_str.substr(0, 6) ==
"-verb=") {
2630 verbs.emplace_back(val_str.substr(6));
2631 }
else if (val_str ==
"-") {
2632 eDest.
Emsg(
"Config",
"http.staticheader is ignoring unknown flag: ", val_str.data());
2639 if (verbs.empty()) {
2640 verbs.emplace_back();
2643 std::string header = val;
2646 std::string header_value;
2647 if (val && val[0]) {
2651 for (
const auto &verb : verbs) {
2654 if (!header_value.empty())
2656 }
else if (header_value.empty()) {
2657 iter->second.clear();
2659 iter->second.emplace_back(header, header_value);
2686 if (!val || !val[0]) {
2687 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2693 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2721 if (!val || !val[0]) {
2722 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2727 if (!strncmp(val,
"required", 8)) {
2728 isRequiredXtractor =
true;
2731 if (!val || !val[0]) {
2732 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2739 strlcpy(libName, val,
sizeof(libName));
2740 libName[
sizeof(libName) - 1] =
'\0';
2741 char libParms[4096];
2743 if (!
Config.GetRest(libParms, 4095)) {
2744 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2750 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2762 if (!val || !val[0]) {
2763 eDest.
Emsg(
"Config",
"No CORS plugin specified.");
2788 std::vector<extHInfo> &hiVec) {
2789 char *val, path[1024], namebuf[1024];
2792 bool noTlsOK =
false;
2797 if (!val || !val[0]) {
2798 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2801 if (strlen(val) >= 16) {
2802 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2805 strncpy(namebuf, val,
sizeof(namebuf));
2806 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2811 if(val && !strcmp(
"+notls",val)) {
2818 if (!val || !val[0]) {
2819 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2822 if (strlen(val) >= (int)
sizeof(path)) {
2823 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2835 for (
int i = 0; i < (int)hiVec.size(); i++)
2836 {
if (hiVec[i].extHName == namebuf) {
2837 eDest.
Emsg(
"Config",
"Instance name already present for "
2838 "http external handler plugin",
2839 hiVec[i].extHPath.c_str());
2847 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2853 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2897 if (!val || !val[0]) {
2898 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2931 if (!val || !val[0]) {
2932 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2962 if (!val || !val[0])
2963 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2967 if (!strcmp(val,
"off"))
2974 if (!strcmp(val,
"on"))
2981 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
2986 auto val =
Config.GetWord();
2987 if (!val || !val[0])
2988 {
eDest.
Emsg(
"Config",
"tlsclientauth argument not specified");
return 1;}
2990 if (!strcmp(val,
"off"))
2994 if (!strcmp(val,
"on"))
2999 eDest.
Emsg(
"config",
"invalid tlsclientauth parameter -", val);
3004 char *val =
Config.GetWord();
3006 if(!strcmp(
"tpc",val)) {
3007 if(!(val =
Config.GetWord())) {
3008 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
3010 if(!strcmp(
"fcreds",val)) {
3013 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
3017 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
3024 char *val =
Config.GetWord();
3030 eDest.
Emsg(
"Config",
"http.maxdelay requires an argument in seconds (default is 30). Example: http.maxdelay 30");
3054 static struct traceopts {
3066 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3068 if (!(val =
Config.GetWord())) {
3069 eDest.
Emsg(
"config",
"trace option not specified");
3073 if (!strcmp(val,
"off")) trval = 0;
3075 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3076 for (i = 0; i < numopts; i++) {
3077 if (!strcmp(val, tropts[i].opname)) {
3078 if (neg) trval &= ~tropts[i].opval;
3079 else trval |= tropts[i].opval;
3084 eDest.
Emsg(
"config",
"invalid trace option", val);
3103 l = strlen(fname) + 1;
3128 length = fname.
length() + 1;
3140 int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3141 const char *libParms) {
3145 if (secxtractor)
return 1;
3147 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3153 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3161 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3162 for (
int i = 0; i < (int) hiVec.size(); i++) {
3163 if(hiVec[i].extHNoTlsOK) {
3165 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3166 hiVec[i].extHParm.c_str(), &myEnv,
3167 hiVec[i].extHName.c_str()))
3174 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3188 for (
int i = 0; i < (int)hiVec.size(); i++) {
3191 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3192 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3193 hiVec[i].extHParm.c_str(), &myEnv,
3194 hiVec[i].extHName.c_str()))
return 1;
3201 int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3202 const char *configFN,
const char *libParms,
3203 XrdOucEnv *myEnv,
const char *instName) {
3207 if (ExtHandlerLoaded(instName)) {
3208 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3212 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3216 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3224 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3227 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3228 exthandler[exthandlercnt].name[15] =
'\0';
3229 exthandler[exthandlercnt++].ptr = newhandler;
3239 int XrdHttpProtocol::LoadCorsHandler(
XrdSysError *
eDest,
const char *libname) {
3244 if(ep && (
xrdcors = ep()))
return 0;
3251 bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3252 for (
int i = 0; i < exthandlercnt; i++) {
3253 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3264 for (
int i = 0; i < exthandlercnt; i++) {
3266 return exthandler[i].ptr;
struct ClientSetRequest set
struct ClientQueryRequest query
struct ClientStatRequest stat
#define XrdHttpCorsGetHandlerArgs
#define XrdHttpExtHandlerArgs
XrdSysError eDest(0, "HttpMon")
static int BIO_XrdLink_create(BIO *bio)
const char * XrdHttpSecEntityTident
#define HTTPS_ALERT(x, y, z)
static long BIO_XrdLink_ctrl(BIO *bio, int cmd, long num, void *ptr)
XrdSysTrace XrdHttpTrace("http")
int BIO_XrdLink_write(BIO *bio, const char *data, int datal)
static int BIO_XrdLink_destroy(BIO *bio)
#define XRHTTP_TK_GRACETIME
static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION)
static int BIO_XrdLink_read(BIO *bio, char *data, int datal)
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 void Record(XrdHttpReq &req, int code)
static void Initialize(XrdSysLogger *logP, XrdXrootdGStream *gStream, XrdMonRoll *mrollP)
static void * Start(void *)
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 allowMissingCRL
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 std::unordered_set< std::string > strp_cgi_params
CGI parameters (names) to strip from redirect URLs.
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.
XResponseType xrdresp
The last response data we got.
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.
void setHttpStatusCode(int code)
int parseLine(char *line, int len)
Parse the header.
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
std::chrono::steady_clock::time_point startTime
int getInitialStatusCode()
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)
void PutInt(const char *varname, long value)
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)
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=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 uint64_t crlAM
Allow CA validation when CRL is missing (CRL soft-fail)
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, time_t timeout=0)
Factory for creating CloseImpl objects.
std::string cafile
-> ca cert file.
uint64_t opts
Options as passed to the constructor.
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.