24 #include "XrdVersion.hh"
48 #include <openssl/err.h>
49 #include <openssl/ssl.h>
51 #include <arpa/inet.h>
58 #define XRHTTP_TK_GRACETIME 600
99 BIO *XrdHttpProtocol::sslbio_err = 0;
101 bool XrdHttpProtocol::isRequiredXtractor =
false;
103 int XrdHttpProtocol::exthandlercnt = 0;
106 bool XrdHttpProtocol::usingEC = false;
127 const 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),
198 SecEntity(
""), 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;
310 char *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:
463 BIO *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);
514 TRACE(ALL,
"Failed to configure the TLS client authentication; invalid configuration");
522 ERR_print_errors(sslbio_err);
531 SSL_set_bio(ssl, sbio, sbio);
538 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_RCVTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
539 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_SNDTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
542 int res = SSL_accept(ssl);
544 if ((res == -1) && (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ)) {
545 TRACEI(
DEBUG,
" SSL_accept wants to read more bytes... err:" << SSL_get_error(ssl, res));
550 ERR_print_errors(sslbio_err);
559 BIO_set_nbio(sbio, 0);
564 if (
tlsClientAuth == XrdTlsContext::ClientAuthSetting::kOn && HandleAuthentication(
Link)) {
585 if ((rc = getDataOneShot(BuffAvailable())) < 0) {
591 if (BuffUsed() < ResumeBytes)
return 1;
599 if (mon_info.size() >= 1024) {
600 TRACEI(ALL,
"User agent string too long");
602 TRACEI(ALL,
"Internal logic error: Bridge is null after login");
611 SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
626 while ((rc = BuffgetLine(tmpline)) > 0) {
627 std::string traceLine = tmpline.
c_str();
631 TRACE(
DEBUG,
" rc:" << rc <<
" got hdr line: " << traceLine);
632 if ((rc == 2) && (tmpline.
length() > 1) && (tmpline[rc - 1] ==
'\n')) {
634 TRACE(
DEBUG,
" rc:" << rc <<
" detected header end.");
640 TRACE(
DEBUG,
" Parsing first line: " << traceLine.c_str());
643 TRACE(
DEBUG,
" Parsing of first line failed with " << result);
649 TRACE(
DEBUG,
" Parsing of header line failed with " << result)
650 SendSimpleResp(400,NULL,NULL,
"Malformed header line. Hint: ensure the line finishes with \"\\r\\n\"", 0,
false);
661 TRACEI(REQ,
" rc:" << rc <<
"Header not yet complete.");
666 if ((rc <= 0) && (BuffUsed() >= 16384)) {
667 TRACEI(ALL,
"Corrupted header detected, or line too long. Disconnecting client.");
686 time_t timenow = time(0);
704 TRACEI(REQ,
" rc:" << rc <<
" self-redirecting to http with security token.");
711 struct sockaddr_storage sa;
712 socklen_t sl =
sizeof(sa);
719 switch (sa.ss_family) {
721 if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
728 if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
730 Addr_str = (
char *)malloc(strlen(buf)+3);
738 TRACEI(REQ,
" Can't recognize the address family of the local host.");
746 TRACEI(REQ,
" rc:"<<rc<<
" self-redirecting to http with security token: '"
747 << dest.
c_str() <<
"'");
751 SendSimpleResp(302, NULL, (
char *) dest.
c_str(), 0, 0,
true);
756 TRACEI(REQ,
" rc:" << rc <<
" Can't perform self-redirection.");
760 TRACEI(ALL,
" Could not calculate self-redirection hash");
766 if (!ishttps && !ssldone) {
776 if (t) tim = atoi(t);
778 TRACEI(REQ,
" xrdhttptime not specified. Authentication failed.");
782 TRACEI(REQ,
" Token expired. Authentication failed.");
867 TRACEI(REQ,
" Invalid tk '" << tk <<
"' != '" << hash <<
"'(calculated). Authentication failed.");
874 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
882 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
902 TRACEI(REQ,
" Authorization failed.");
918 TRACEI(REQ,
"Process is exiting rc:" << rc);
926 #define TRACELINK Link
980 #define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
982 #define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
984 #define HTTPS_ALERT(x,y,z) httpsspec = true;\
985 if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
986 eDest.Say("Config http." x " overrides the xrd." y " directive.")
988 int XrdHttpProtocol::Config(
const char *ConfigFN,
XrdOucEnv *myEnv) {
991 std::vector<extHInfo> extHIVec;
993 int cfgFD, GoNo, NoGo = 0, ismine;
1003 if(nonIanaChecksums.size()) {
1004 std::stringstream warningMsgSS;
1005 warningMsgSS <<
"Config warning: the following checksum algorithms are not IANA compliant: [";
1006 std::string unknownCksumString;
1007 for(
auto unknownCksum: nonIanaChecksums) {
1008 unknownCksumString += unknownCksum +
",";
1010 unknownCksumString.erase(unknownCksumString.size() - 1);
1011 warningMsgSS << unknownCksumString <<
"]" <<
". They therefore cannot be queried by a user via HTTP." ;
1012 eDest.
Say(warningMsgSS.str().c_str());
1018 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1020 m_bio_method =
static_cast<BIO_METHOD*
>(OPENSSL_malloc(
sizeof(BIO_METHOD)));
1055 if ((cfgFD =
open(ConfigFN, O_RDONLY, 0)) < 0)
1056 return eDest.
Emsg(
"Config", errno,
"open config file", ConfigFN);
1058 static const char *cvec[] = {
"*** http protocol config:", 0 };
1063 while ((var =
Config.GetMyFirstWord())) {
1064 if ((ismine = !strncmp(
"http.", var, 5)) && var[5]) var += 5;
1067 if TS_Xeq(
"trace", xtrace);
1068 else if TS_Xeq(
"cert", xsslcert);
1069 else if TS_Xeq(
"key", xsslkey);
1070 else if TS_Xeq(
"cadir", xsslcadir);
1071 else if TS_Xeq(
"cipherfilter", xsslcipherfilter);
1072 else if TS_Xeq(
"gridmap", xgmap);
1073 else if TS_Xeq(
"cafile", xsslcafile);
1074 else if TS_Xeq(
"secretkey", xsecretkey);
1075 else if TS_Xeq(
"desthttps", xdesthttps);
1076 else if TS_Xeq(
"secxtractor", xsecxtractor);
1077 else if TS_Xeq3(
"exthandler", xexthandler);
1078 else if TS_Xeq(
"selfhttps2http", xselfhttps2http);
1079 else if TS_Xeq(
"embeddedstatic", xembeddedstatic);
1080 else if TS_Xeq(
"listingredir", xlistredir);
1081 else if TS_Xeq(
"staticredir", xstaticredir);
1082 else if TS_Xeq(
"staticpreload", xstaticpreload);
1083 else if TS_Xeq(
"staticheader", xstaticheader);
1084 else if TS_Xeq(
"listingdeny", xlistdeny);
1085 else if TS_Xeq(
"header2cgi", xheader2cgi);
1086 else if TS_Xeq(
"httpsmode", xhttpsmode);
1087 else if TS_Xeq(
"tlsreuse", xtlsreuse);
1088 else if TS_Xeq(
"auth", xauth);
1089 else if TS_Xeq(
"tlsclientauth", xtlsclientauth);
1091 eDest.
Say(
"Config warning: ignoring unknown directive '", var,
"'.");
1106 {
eDest.
Say(
"Config failure: one or more directives are flawed!");
1112 hdr2cgimap[
"Cache-Control"] =
"cache-control";
1115 if (getenv(
"XRDCL_EC"))
usingEC =
true;
1120 std::string default_static_headers;
1122 for (
const auto &header_entry : default_verb->second) {
1123 default_static_headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1127 auto headers = default_static_headers;
1128 for (
const auto &header_entry : item.second) {
1129 headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1142 :
"was not configured.");
1143 const char *what = Configed();
1145 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1148 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1150 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1160 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1161 "is meaningless; ignoring key!");
1169 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1170 "a cert specification!");
1181 const char *what1 = 0, *what2 = 0, *what3 = 0;
1186 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1190 what2 =
"xrd.tlsca to supply 'cadir'.";
1194 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1195 :
"xrd.tlsca to supply 'cafile'.");
1199 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1209 {
const char *what = Configed();
1210 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1211 :
"'xrd.tlsca noverify' was specified!");
1213 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1221 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1226 const char *how =
"completed.";
1227 eDest.
Say(
"++++++ HTTPS initialization started.");
1228 if (!
InitTLS()) {NoGo = 1; how =
"failed.";}
1229 eDest.
Say(
"------ HTTPS initialization ", how);
1230 if (NoGo)
return NoGo;
1234 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1238 return (InitSecurity() ? NoGo : 1);
1245 const char *XrdHttpProtocol::Configed()
1247 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1248 if (secxtractor)
return "secxtractor requires";
1249 if (
gridmap)
return "gridmap requires";
1265 if (myBuffEnd >= myBuffStart) {
1267 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1272 dest.
assign(myBuffStart, 0, l-1);
1291 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1293 if ((*p ==
'\n') || (*p ==
'\0')) {
1296 dest.
assign(myBuffStart, 0, l-1);
1312 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1314 if ((*p ==
'\n') || (*p ==
'\0')) {
1318 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1320 dest.
assign(myBuffStart, 0, l1-1);
1324 dest.
insert(myBuffStart, l1, l-1);
1348 int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1363 maxread = std::min(blen, BuffAvailable());
1364 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1370 int sslavail = maxread;
1373 int l = SSL_pending(ssl);
1375 sslavail = std::min(maxread, SSL_pending(ssl));
1380 ERR_print_errors(sslbio_err);
1384 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1385 if (sslavail <= 0)
return 0;
1387 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1389 myBuffEnd = myBuff->
buff;
1392 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1395 ERR_print_errors(sslbio_err);
1402 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1404 myBuffEnd = myBuff->
buff;
1410 rlen =
Link->
Recv(myBuffEnd, maxread);
1426 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1433 int XrdHttpProtocol::BuffAvailable() {
1436 if (myBuffEnd >= myBuffStart)
1437 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1439 r = myBuffStart - myBuffEnd;
1441 if ((r < 0) || (r > myBuff->
bsize)) {
1442 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1455 int XrdHttpProtocol::BuffUsed() {
1458 if (myBuffEnd >= myBuffStart)
1459 r = myBuffEnd - myBuffStart;
1462 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1464 if ((r < 0) || (r > myBuff->
bsize)) {
1465 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1478 int XrdHttpProtocol::BuffFree() {
1479 return (myBuff->
bsize - BuffUsed());
1486 void XrdHttpProtocol::BuffConsume(
int blen) {
1488 if (blen > myBuff->
bsize) {
1489 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1493 if (blen > BuffUsed()) {
1494 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1498 myBuffStart = myBuffStart + blen;
1500 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1501 myBuffStart -= myBuff->
bsize;
1503 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1504 myBuffEnd -= myBuff->
bsize;
1506 if (BuffUsed() == 0)
1507 myBuffStart = myBuffEnd = myBuff->
buff;
1522 int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1525 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1530 if (blen > BuffUsed()) {
1531 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1532 if ( getDataOneShot(blen - BuffUsed(),
true) )
1538 if ( !BuffUsed() ) {
1539 if ( getDataOneShot(blen,
false) )
1547 if (myBuffStart <= myBuffEnd) {
1548 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1551 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1553 *data = myBuffStart;
1564 int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1568 if (body && bodylen) {
1569 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1571 r = SSL_write(ssl, body, bodylen);
1573 ERR_print_errors(sslbio_err);
1579 if (r <= 0)
return -1;
1590 int XrdHttpProtocol::StartSimpleResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1591 std::stringstream ss;
1592 const std::string crlf =
"\r\n";
1594 ss <<
"HTTP/1.1 " << code <<
" ";
1598 if (code == 200) ss <<
"OK";
1599 else if (code == 100) ss <<
"Continue";
1600 else if (code == 206) ss <<
"Partial Content";
1601 else if (code == 302) ss <<
"Redirect";
1602 else if (code == 307) ss <<
"Temporary Redirect";
1603 else if (code == 400) ss <<
"Bad Request";
1604 else if (code == 403) ss <<
"Forbidden";
1605 else if (code == 404) ss <<
"Not Found";
1606 else if (code == 405) ss <<
"Method Not Allowed";
1607 else if (code == 416) ss <<
"Range Not Satisfiable";
1608 else if (code == 500) ss <<
"Internal Server Error";
1609 else if (code == 504) ss <<
"Gateway Timeout";
1610 else ss <<
"Unknown";
1613 if (keepalive && (code != 100))
1614 ss <<
"Connection: Keep-Alive" << crlf;
1616 ss <<
"Connection: Close" << crlf;
1618 ss <<
"Server: XrootD/" << XrdVSTRING << crlf;
1625 if ((bodylen >= 0) && (code != 100))
1626 ss <<
"Content-Length: " << bodylen << crlf;
1628 if (header_to_add && (header_to_add[0] !=
'\0'))
1629 ss << header_to_add << crlf;
1633 const std::string &outhdr = ss.str();
1634 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1635 if (SendData(outhdr.c_str(), outhdr.size()))
1645 int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1646 const std::string crlf =
"\r\n";
1647 std::stringstream ss;
1649 if (header_to_add && (header_to_add[0] !=
'\0')) {
1650 ss << header_to_add << crlf;
1653 ss <<
"Transfer-Encoding: chunked";
1654 TRACEI(RSP,
"Starting chunked response");
1655 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1662 int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1663 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1664 if (ChunkRespHeader(content_length))
1667 if (body && SendData(body, content_length))
1670 return ChunkRespFooter();
1677 int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1678 const std::string crlf =
"\r\n";
1679 std::stringstream ss;
1683 const std::string &chunkhdr = ss.str();
1684 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1685 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1692 int XrdHttpProtocol::ChunkRespFooter() {
1693 const std::string crlf =
"\r\n";
1694 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1705 int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1707 long long content_length = bodylen;
1709 content_length = body ? strlen(body) : 0;
1712 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1719 return SendData(body, content_length);
1756 sprintf(buf,
"%d",
Port);
1762 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1768 if ((rdf = getenv(
"XRDROLE"))) {
1771 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1773 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1776 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1780 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1799 char *val, keybuf[1024], parmbuf[1024];
1804 if (!val || !val[0]) {
1805 err.
Emsg(
"Config",
"No headerkey specified.");
1810 while ( *val && !isalnum(*val) ) val++;
1811 strcpy(keybuf, val);
1815 pp = keybuf + strlen(keybuf) - 1;
1816 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1824 if(!parm || !parm[0]) {
1825 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1830 while ( *parm && !isalnum(*parm) ) parm++;
1831 strcpy(parmbuf, parm);
1834 pp = parmbuf + strlen(parmbuf) - 1;
1835 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1842 header2cgi[keybuf] = parmbuf;
1844 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1857 bool XrdHttpProtocol::InitTLS() {
1882 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1883 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1889 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1902 void XrdHttpProtocol::Cleanup() {
1904 TRACE(ALL,
" Cleanup");
1906 if (
BPool && myBuff) {
1907 BuffConsume(BuffUsed());
1921 int ret = SSL_shutdown(ssl);
1925 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1926 ERR_remove_thread_state(
nullptr);
1930 TRACE(ALL,
" SSL_shutdown failed");
1931 ERR_print_errors(sslbio_err);
1965 void XrdHttpProtocol::Reset() {
1967 TRACE(ALL,
" Reset");
1976 myBuffStart = myBuffEnd = 0;
1979 DoneSetInfo =
false;
2029 if (!val || !val[0]) {
2030 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2039 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2064 if (!val || !val[0]) {
2065 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2096 if (!val || !val[0]) {
2097 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2131 if (!val || !val[0]) {
2132 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2168 if (!val || !val[0]) {
2169 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2175 if (!strncmp(val,
"required", 8)) {
2179 if (!val || !val[0]) {
2180 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2188 if (!strcmp(val,
"compatNameGeneration")) {
2191 if (!val || !val[0]) {
2192 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2193 "[compatNameGeneration] parameter");
2225 if (!val || !val[0]) {
2226 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2254 bool inFile =
false;
2259 if (!val || !val[0]) {
2260 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2268 if (val[0] ==
'/') {
2271 int fd =
open(val, O_RDONLY);
2274 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2278 if (
fstat(fd, &st) != 0 ) {
2279 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2284 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2286 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2291 FILE *fp = fdopen(fd,
"r");
2293 if ( fp ==
nullptr ) {
2294 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2300 while( fgets(line, 1024, fp) ) {
2304 pp = line + strlen(line) - 1;
2305 while ( (pp >= line) && (!isalnum(*pp)) ) {
2312 while ( *pp && !isalnum(*pp) ) pp++;
2314 if ( strlen(pp) >= 32 ) {
2315 eDest.
Say(
"Config",
"Secret key loaded.");
2327 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2332 if ( strlen(val) < 32 ) {
2333 eDest.
Emsg(
"Config",
"Secret key is too short");
2340 if (!inFile)
Config.noEcho();
2364 if (!val || !val[0]) {
2365 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2371 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2396 if (!val || !val[0]) {
2397 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2429 if (!val || !val[0]) {
2430 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2436 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2461 if (!val || !val[0]) {
2462 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2468 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2493 if (!val || !val[0]) {
2494 eDest.
Emsg(
"Config",
"staticredir url not specified");
2523 char *val, *k, key[1024];
2529 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2538 if (!val || !val[0]) {
2539 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2544 int fp =
open(val, O_RDONLY);
2546 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2550 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2552 nfo->data = (
char *)malloc(65536);
2553 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2556 if (nfo->len <= 0) {
2557 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2561 if (nfo->len >= 65536) {
2562 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2593 auto val =
Config.GetWord();
2594 std::vector<std::string> verbs;
2596 if (!val || !val[0]) {
2597 eDest.
Emsg(
"Config",
"http.staticheader requires the header to be set to be specified");
2601 std::string match_verb;
2602 std::string_view val_str(val);
2603 if (val_str.substr(0, 6) ==
"-verb=") {
2604 verbs.emplace_back(val_str.substr(6));
2605 }
else if (val_str ==
"-") {
2606 eDest.
Emsg(
"Config",
"http.staticheader is ignoring unknown flag: ", val_str.data());
2613 if (verbs.empty()) {
2614 verbs.emplace_back();
2617 std::string header = val;
2620 std::string header_value;
2621 if (val && val[0]) {
2625 for (
const auto &verb : verbs) {
2629 }
else if (header_value.empty()) {
2630 iter->second.clear();
2632 iter->second.emplace_back(header, header_value);
2659 if (!val || !val[0]) {
2660 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2666 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2694 if (!val || !val[0]) {
2695 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2700 if (!strncmp(val,
"required", 8)) {
2701 isRequiredXtractor =
true;
2704 if (!val || !val[0]) {
2705 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2712 strlcpy(libName, val,
sizeof(libName));
2713 libName[
sizeof(libName) - 1] =
'\0';
2714 char libParms[4096];
2716 if (!
Config.GetRest(libParms, 4095)) {
2717 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2723 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2749 std::vector<extHInfo> &hiVec) {
2750 char *val, path[1024], namebuf[1024];
2753 bool noTlsOK =
false;
2758 if (!val || !val[0]) {
2759 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2762 if (strlen(val) >= 16) {
2763 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2766 strncpy(namebuf, val,
sizeof(namebuf));
2767 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2772 if(val && !strcmp(
"+notls",val)) {
2779 if (!val || !val[0]) {
2780 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2783 if (strlen(val) >= (int)
sizeof(path)) {
2784 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2796 for (
int i = 0; i < (int)hiVec.size(); i++)
2797 {
if (hiVec[i].extHName == namebuf) {
2798 eDest.
Emsg(
"Config",
"Instance name already present for "
2799 "http external handler plugin",
2800 hiVec[i].extHPath.c_str());
2808 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2814 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2858 if (!val || !val[0]) {
2859 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2892 if (!val || !val[0]) {
2893 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2923 if (!val || !val[0])
2924 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2928 if (!strcmp(val,
"off"))
2935 if (!strcmp(val,
"on"))
2942 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
2947 auto val =
Config.GetWord();
2948 if (!val || !val[0])
2949 {
eDest.
Emsg(
"Config",
"tlsclientauth argument not specified");
return 1;}
2951 if (!strcmp(val,
"off"))
2955 if (!strcmp(val,
"on"))
2960 eDest.
Emsg(
"config",
"invalid tlsclientauth parameter -", val);
2965 char *val =
Config.GetWord();
2967 if(!strcmp(
"tpc",val)) {
2968 if(!(val =
Config.GetWord())) {
2969 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
2971 if(!strcmp(
"fcreds",val)) {
2974 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
2978 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
3002 static struct traceopts {
3014 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3016 if (!(val =
Config.GetWord())) {
3017 eDest.
Emsg(
"config",
"trace option not specified");
3021 if (!strcmp(val,
"off")) trval = 0;
3023 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3024 for (i = 0; i < numopts; i++) {
3025 if (!strcmp(val, tropts[i].opname)) {
3026 if (neg) trval &= ~tropts[i].opval;
3027 else trval |= tropts[i].opval;
3032 eDest.
Emsg(
"config",
"invalid trace option", val);
3051 l = strlen(fname) + 1;
3076 length = fname.
length() + 1;
3088 int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3089 const char *libParms) {
3093 if (secxtractor)
return 1;
3095 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3101 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3109 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3110 for (
int i = 0; i < (int) hiVec.size(); i++) {
3111 if(hiVec[i].extHNoTlsOK) {
3113 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3114 hiVec[i].extHParm.c_str(), &myEnv,
3115 hiVec[i].extHName.c_str()))
3122 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3134 for (
int i = 0; i < (int)hiVec.size(); i++) {
3137 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3138 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3139 hiVec[i].extHParm.c_str(), &myEnv,
3140 hiVec[i].extHName.c_str()))
return 1;
3147 int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3148 const char *configFN,
const char *libParms,
3149 XrdOucEnv *myEnv,
const char *instName) {
3153 if (ExtHandlerLoaded(instName)) {
3154 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3158 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3162 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3170 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3173 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3174 exthandler[exthandlercnt].name[15] =
'\0';
3175 exthandler[exthandlercnt++].ptr = newhandler;
3188 bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3189 for (
int i = 0; i < exthandlercnt; i++) {
3190 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3201 for (
int i = 0; i < exthandlercnt; i++) {
3203 return exthandler[i].ptr;
struct ClientSetRequest set
struct ClientQueryRequest query
struct ClientStatRequest stat
#define XrdHttpExtHandlerArgs
int BIO_get_init(BIO *bio)
int BIO_get_shutdown(BIO *bio)
int BIO_get_flags(BIO *bio)
static int BIO_XrdLink_create(BIO *bio)
const char * XrdHttpSecEntityTident
void BIO_set_init(BIO *bio, int init)
int BIO_XrdLink_write(BIO *bio, const char *data, size_t datal, size_t *written)
#define HTTPS_ALERT(x, y, z)
static long BIO_XrdLink_ctrl(BIO *bio, int cmd, long num, void *ptr)
void BIO_set_shutdown(BIO *bio, int shut)
XrdSysTrace XrdHttpTrace("http")
void * BIO_get_data(BIO *bio)
static int BIO_XrdLink_read(BIO *bio, char *data, size_t datal, size_t *read)
void BIO_set_data(BIO *bio, void *ptr)
static int BIO_XrdLink_destroy(BIO *bio)
#define XRHTTP_TK_GRACETIME
static XrdVERSIONINFODEF(compiledVer, XrdHttpProtocolTest, XrdVNUMBER, XrdVERSION)
void BIO_set_flags(BIO *bio, int flags)
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
#define MAX_XRDHTTPEXTHANDLERS
#define XrdHttpSecXtractorArgs
int compareHash(const char *h1, const char *h2)
char * unquote(char *str)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
Utility functions for XrdHTTP.
std::string obfuscateAuth(const std::string &input)
int stat(const char *path, struct stat *buf)
int open(const char *path, int oflag,...)
int fstat(int fildes, struct stat *buf)
ssize_t read(int fildes, void *buf, size_t nbyte)
#define TLS_SET_VDEPTH(cOpts, vdv)
#define TLS_SET_REFINT(cOpts, refi)
void Release(XrdBuffer *bp)
XrdBuffer * Obtain(int bsz)
const std::vector< std::string > & getNonIANAConfiguredCksums() const
void configure(const char *csList)
static char * secretkey
The key used to calculate the url hashes.
static BIO_METHOD * m_bio_method
C-style vptr table for our custom BIO objects.
static char * gridmap
Gridmap file location. The same used by XrdSecGsi.
static XrdScheduler * Sched
static kXR_int32 myRole
Our role.
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
static char * Port_str
Our port, as a string.
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static bool selfhttps2http
If client is HTTPS, self-redirect with HTTP+token.
static XrdHttpChecksumHandler cksumHandler
static int hailWait
Timeout for reading the handshake.
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static char * xrd_cslist
The list of checksums that were configured via the xrd.cksum parameter on the server config file.
static char * sslcipherfilter
static int m_bio_type
Type identifier for our custom BIO objects.
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
static char * sslcert
OpenSSL stuff.
XrdLink * Link
The link we are bound to.
int doStat(char *fname)
Perform a Stat request.
XrdObject< XrdHttpProtocol > ProtLink
static int readWait
Timeout for reading data.
void Recycle(XrdLink *lp, int consec, const char *reason)
Recycle this instance.
XrdHttpProtocol operator=(const XrdHttpProtocol &rhs)
static bool compatNameGeneration
static bool isdesthttps
True if the redirections must be towards https targets.
static XrdObjectQ< XrdHttpProtocol > ProtStack
XrdProtocol * Match(XrdLink *lp)
Tells if the oustanding bytes on the socket match this protocol implementation.
static std::unordered_map< std::string, std::vector< std::pair< std::string, std::string > > > m_staticheader_map
The static headers to always return; map is from verb to a list of (header, val) pairs.
static bool isRequiredGridmap
static char * listredir
Url to redirect to in the case a listing is requested.
int Stats(char *buff, int blen, int do_sync=0)
Get activity stats.
static std::unordered_map< std::string, std::string > m_staticheaders
static int crlRefIntervalSec
CRL thread refresh interval.
static XrdHttpReadRangeHandler::Configuration ReadRangeConfig
configuration for the read range handler
static XrdSecService * CIA
static XrdBuffManager * BPool
static bool tpcForwardCreds
If set to true, the HTTP TPC transfers will forward the credentials to redirected hosts.
int Process(XrdLink *lp)
Process data incoming from the socket.
XrdHttpProtocol(const XrdHttpProtocol &)=default
Ctor, dtors and copy ctor.
static bool listdeny
If true, any form of listing is denied.
static int parseHeader2CGI(XrdOucStream &Config, XrdSysError &err, std::map< std::string, std::string > &header2cgi)
Use this function to parse header2cgi configurations.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
static int sslverifydepth
Depth of verification of a certificate chain.
static int Configure(char *parms, XrdProtocol_Config *pi)
Read and apply the configuration.
static int Configure(XrdSysError &Eroute, const char *const parms, Configuration &cfg)
int reqstate
State machine to talk to the bridge.
XrdOucString resource
The resource specified by the request, stripped of opaque data.
bool headerok
Tells if we have finished reading the header.
ReqType request
The request we got.
XrdOucEnv * opaque
The opaque data, after parsing.
int parseFirstLine(char *line, int len)
Parse the first line of the header.
int parseLine(char *line, int len)
Parse the header.
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
ClientRequest xrdreq
The last issued xrd request, often pending.
const std::string & userAgent() const
virtual int InitSSL(SSL *, char *)
virtual int FreeSSL(SSL *)
int setEtext(const char *text)
int Peek(char *buff, int blen, int timeout=-1)
int Recv(char *buff, int blen)
const XrdNetAddr * NetAddr() const
XrdNetAddrInfo * AddrInfo()
int Send(const char *buff, int blen)
static const int noPort
Do not add port number.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
void SetDialect(const char *dP)
void Set(int inQMax, time_t agemax=1800)
void Push(XrdObject< T > *Node)
static bool Import(const char *var, char *&val)
void * GetPtr(const char *varname)
char * Get(const char *varname)
void Put(const char *varname, const char *value)
void insert(const int i, int start=-1)
const char * c_str() const
void assign(const char *s, int j, int k=-1)
char * vorg
Entity's virtual organization(s)
int credslen
Length of the 'creds' data.
XrdNetAddrInfo * addrInfo
Entity's connection details.
const char * tident
Trace identifier always preset.
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
char * caps
Entity's capabilities.
char * creds
Raw entity credentials or cert.
char * grps
Entity's group name(s)
void Reset(const char *spV=0)
char * name
Entity's name.
char * role
Entity's role(s)
char * endorsements
Protocol specific endorsements.
void Display(XrdSysError &mDest)
char * moninfo
Information for monitoring.
char * host
Entity's host name dnr dependent.
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
XrdSysLogger * logger(XrdSysLogger *lp=0)
void SetLogger(XrdSysLogger *logp)
int SessionCache(int opts=scNone, const char *id=0, int idlen=0)
static const int DEFAULT_CRL_REF_INT_SEC
Default CRL refresh interval in seconds.
static const uint64_t servr
This is a server context.
static const uint64_t rfCRL
Turn on the CRL refresh thread.
bool SetTlsClientAuth(ClientAuthSetting setting)
static const uint64_t logVF
Log verify failures.
static const uint64_t artON
Auto retry Handshake.
const CTX_Params * GetParams()
static const int scOff
Turn off cache.
bool SetContextCiphers(const char *ciphers)
static const int scSrvr
Turn on cache server mode (default)
static Bridge * Login(Result *rsltP, XrdLink *linkP, XrdSecEntity *seceP, const char *nameP, const char *protP)
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0
CloseImpl< false > Close(Ctx< File > file, uint16_t timeout=0)
Factory for creating CloseImpl objects.
XrdTlsContext::ClientAuthSetting tlsClientAuth
std::string cafile
-> ca cert file.
std::string cadir
-> ca cert directory.
int crlRT
crl refresh interval time in seconds
std::string pkey
-> private key path.
std::string cert
-> certificate path.