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;
124 const char *TraceID =
"Protocol";
152 "xrootd protocol anchor");
158 #if OPENSSL_VERSION_NUMBER < 0x10100000L
165 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
180 bio->shutdown = shut;
183 return bio->shutdown;
195 :
XrdProtocol(
"HTTP protocol handler"), ProtLink(this),
196 SecEntity(
""), CurrentReq(this, ReadRangeConfig) {
221 char mybuf[16], mybuf2[1024];
224 bool myishttps =
false;
228 if ((dlen = lp->
Peek(mybuf, (
int) sizeof (mybuf),
hailWait)) < (
int)
sizeof (mybuf)) {
229 if (dlen <= 0) lp->
setEtext(
"handshake not received");
232 mybuf[dlen - 1] =
'\0';
240 for (
int i = 0; i < dlen; i++) {
242 sprintf(mybuf3,
"%.02d ", mybuf[i]);
243 strcat(mybuf2, mybuf3);
250 for (
int i = 0; i < dlen - 1; i++)
251 if (!isprint(mybuf[i]) && (mybuf[i] !=
'\r') && (mybuf[i] !=
'\n')) {
253 TRACEI(
DEBUG,
"This does not look like http at pos " << i);
258 if ((!ismine) && (dlen >= 4)) {
259 char check[4] = {00, 00, 00, 00};
260 if (memcmp(mybuf, check, 4)) {
267 TRACEI(ALL,
"This may look like https, but https is not configured");
274 TRACEI(
DEBUG,
"This does not look like https. Protocol not matched.");
282 TRACEI(REQ,
"Protocol matched. https: " << myishttps);
285 hp->ishttps = myishttps;
300 hp->myBuffStart = hp->myBuffEnd = hp->myBuff->
buff;
308 char *XrdHttpProtocol::GetClientIPStr() {
311 if (!
Link)
return strdup(
"unknown");
313 if (!ai)
return strdup(
"unknown");
321 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
332 int ret = lp->
Send(data, datal);
333 BIO_clear_retry_flags(bio);
336 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
337 BIO_set_retry_write(bio);
353 int ret = lp->
Send(data, datal);
354 BIO_clear_retry_flags(bio);
356 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
357 BIO_set_retry_write(bio);
364 #if OPENSSL_VERSION_NUMBER < 0x1000105fL
375 int ret = lp->
Recv(data, datal);
376 BIO_clear_retry_flags(bio);
379 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
380 BIO_set_retry_read(bio);
395 int ret = lp->
Recv(data, datal);
396 BIO_clear_retry_flags(bio);
398 if ((errno == EINTR) || (errno == EINPROGRESS) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
399 BIO_set_retry_read(bio);
415 #if OPENSSL_VERSION_NUMBER < 0x10100000L
427 if (bio == NULL)
return 0;
443 case BIO_CTRL_GET_CLOSE:
446 case BIO_CTRL_SET_CLOSE:
464 setsockopt(link->FDnum(), SOL_SOCKET, SO_RCVTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
465 setsockopt(link->FDnum(), SOL_SOCKET, SO_SNDTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
478 BIO *XrdHttpProtocol::CreateBIO(
XrdLink *lp)
497 #define TRACELINK Link
505 if (!myBuff || !myBuff->
buff || !myBuff->
bsize) {
506 TRACE(ALL,
" Process. No buffer available. Internal error.");
512 char *nfo = GetClientIPStr();
514 TRACEI(REQ,
" Setting host: " << nfo);
523 if (ishttps && !ssldone) {
526 sbio = CreateBIO(
Link);
527 BIO_set_nbio(sbio, 1);
530 postheaderauth =
false;
531 postheaderwait =
false;
532 postheaderauthdone =
false;
537 ERR_print_errors(sslbio_err);
546 SSL_set_bio(ssl, sbio, sbio);
553 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_RCVTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
554 setsockopt(
Link->
FDnum(), SOL_SOCKET, SO_SNDTIMEO, (
struct timeval *)&tv,
sizeof(
struct timeval));
557 int res = SSL_accept(ssl);
559 if ((res == -1) && (SSL_get_error(ssl, res) == SSL_ERROR_WANT_READ)) {
560 TRACEI(
DEBUG,
" SSL_accept wants to read more bytes... err:" << SSL_get_error(ssl, res));
565 ERR_print_errors(sslbio_err);
574 BIO_set_nbio(sbio, 0);
579 if (
tlsClientAuth == XrdTlsContext::ClientAuthSetting::kOn && HandleAuthentication(
Link)) {
600 if ((rc = getDataOneShot(BuffAvailable())) < 0) {
606 if (BuffUsed() < ResumeBytes)
return 1;
611 }
else if (!DoneSetInfo && !postheaderwait && !postheaderauth && !
CurrentReq.
userAgent().empty()) {
614 if (mon_info.size() >= 1024) {
615 TRACEI(ALL,
"User agent string too long");
617 TRACEI(ALL,
"Internal logic error: Bridge is null after login");
626 SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
631 }
else if (!postheaderwait) {
641 while ((rc = BuffgetLine(tmpline)) > 0) {
642 std::string traceLine = tmpline.
c_str();
646 TRACE(
DEBUG,
" rc:" << rc <<
" got hdr line: " << traceLine);
647 if ((rc == 2) && (tmpline.
length() > 1) && (tmpline[rc - 1] ==
'\n')) {
649 TRACE(
DEBUG,
" rc:" << rc <<
" detected header end.");
655 TRACE(
DEBUG,
" Parsing first line: " << traceLine.c_str());
658 TRACE(
DEBUG,
" Parsing of first line failed with " << result);
662 #if OPENSSL_VERSION_NUMBER >= 0x10100010L
665 if (!postheaderauthdone &&
tlsClientAuth == XrdTlsContext::ClientAuthSetting::kDefer)
668 {postheaderwait =
true;
679 TRACE(
DEBUG,
" Parsing of header line failed with " << result)
680 SendSimpleResp(400,NULL,NULL,
"Malformed header line. Hint: ensure the line finishes with \"\\r\\n\"", 0,
false);
691 TRACEI(REQ,
" rc:" << rc <<
"Header not yet complete.");
696 if ((rc <= 0) && (BuffUsed() >= 16384)) {
697 TRACEI(ALL,
"Corrupted header detected, or line too long. Disconnecting client.");
711 #if OPENSSL_VERSION_NUMBER >= 0x10100010L
712 if (postheaderwait) {
713 postheaderwait =
false;
714 if (SSL_verify_client_post_handshake(ssl) != 1) {
717 TRACEI(ALL,
"Unable to request client X.509 authentication");
718 ERR_print_errors(sslbio_err);
722 auto res = SSL_write_ex(ssl,
nullptr, 0, &write_size);
724 TRACEI(
DEBUG,
" SSL post-handshake auth failed; err:" << SSL_get_error(ssl, res));
725 ERR_print_errors(sslbio_err);
726 SendSimpleResp(500,
nullptr,
nullptr,
"Failed post-handshake authentication", 0,
false);
729 TRACEI(
DEBUG,
" SSL post-handshake auth finished successfully");
730 postheaderauth =
true;
735 if (postheaderauth) {
736 postheaderauth =
false;
737 postheaderauthdone =
true;
739 TRACEI(REQ,
"Reading out response to post-handshake authentication");
740 BIO_set_nbio(sbio, 1);
741 auto res = SSL_peek_ex(ssl,
nullptr, 0, &readbytes);
742 if ((res <= 0) && SSL_get_error(ssl, res) != SSL_ERROR_WANT_READ) {
743 SendSimpleResp(500,
nullptr,
nullptr,
"Failed to process authentication frames", 0,
false);
746 BIO_set_nbio(sbio, 0);
747 if (HandleAuthentication(
Link)) {
748 SendSimpleResp(500,
nullptr,
nullptr,
"Failed to extract authentication information from handshake", 0,
false);
760 time_t timenow = time(0);
778 TRACEI(REQ,
" rc:" << rc <<
" self-redirecting to http with security token.");
785 struct sockaddr_storage sa;
786 socklen_t sl =
sizeof(sa);
793 switch (sa.ss_family) {
795 if (inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), buf, INET_ADDRSTRLEN)) {
802 if (inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), buf, INET6_ADDRSTRLEN)) {
804 Addr_str = (
char *)malloc(strlen(buf)+3);
812 TRACEI(REQ,
" Can't recognize the address family of the local host.");
820 TRACEI(REQ,
" rc:"<<rc<<
" self-redirecting to http with security token: '"
821 << dest.
c_str() <<
"'");
825 SendSimpleResp(302, NULL, (
char *) dest.
c_str(), 0, 0,
true);
830 TRACEI(REQ,
" rc:" << rc <<
" Can't perform self-redirection.");
834 TRACEI(ALL,
" Could not calculate self-redirection hash");
840 if (!ishttps && !ssldone) {
850 if (t) tim = atoi(t);
852 TRACEI(REQ,
" xrdhttptime not specified. Authentication failed.");
856 TRACEI(REQ,
" Token expired. Authentication failed.");
941 TRACEI(REQ,
" Invalid tk '" << tk <<
"' != '" << hash <<
"'(calculated). Authentication failed.");
948 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
956 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
976 TRACEI(REQ,
" Authorization failed.");
992 TRACEI(REQ,
"Process is exiting rc:" << rc);
1000 #define TRACELINK Link
1054 #define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
1056 #define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
1058 #define HTTPS_ALERT(x,y,z) httpsspec = true;\
1059 if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
1060 eDest.Say("Config http." x " overrides the xrd." y " directive.")
1062 int XrdHttpProtocol::Config(
const char *ConfigFN,
XrdOucEnv *myEnv) {
1065 std::vector<extHInfo> extHIVec;
1067 int cfgFD, GoNo, NoGo = 0, ismine;
1077 if(nonIanaChecksums.size()) {
1078 std::stringstream warningMsgSS;
1079 warningMsgSS <<
"Config warning: the following checksum algorithms are not IANA compliant: [";
1080 std::string unknownCksumString;
1081 for(
auto unknownCksum: nonIanaChecksums) {
1082 unknownCksumString += unknownCksum +
",";
1084 unknownCksumString.erase(unknownCksumString.size() - 1);
1085 warningMsgSS << unknownCksumString <<
"]" <<
". They therefore cannot be queried by a user via HTTP." ;
1086 eDest.
Say(warningMsgSS.str().c_str());
1092 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1094 m_bio_method =
static_cast<BIO_METHOD*
>(OPENSSL_malloc(
sizeof(BIO_METHOD)));
1129 if ((cfgFD =
open(ConfigFN, O_RDONLY, 0)) < 0)
1130 return eDest.
Emsg(
"Config", errno,
"open config file", ConfigFN);
1132 static const char *cvec[] = {
"*** http protocol config:", 0 };
1137 while ((var =
Config.GetMyFirstWord())) {
1138 if ((ismine = !strncmp(
"http.", var, 5)) && var[5]) var += 5;
1141 if TS_Xeq(
"trace", xtrace);
1142 else if TS_Xeq(
"cert", xsslcert);
1143 else if TS_Xeq(
"key", xsslkey);
1144 else if TS_Xeq(
"cadir", xsslcadir);
1145 else if TS_Xeq(
"cipherfilter", xsslcipherfilter);
1146 else if TS_Xeq(
"gridmap", xgmap);
1147 else if TS_Xeq(
"cafile", xsslcafile);
1148 else if TS_Xeq(
"secretkey", xsecretkey);
1149 else if TS_Xeq(
"desthttps", xdesthttps);
1150 else if TS_Xeq(
"secxtractor", xsecxtractor);
1151 else if TS_Xeq3(
"exthandler", xexthandler);
1152 else if TS_Xeq(
"selfhttps2http", xselfhttps2http);
1153 else if TS_Xeq(
"embeddedstatic", xembeddedstatic);
1154 else if TS_Xeq(
"listingredir", xlistredir);
1155 else if TS_Xeq(
"staticredir", xstaticredir);
1156 else if TS_Xeq(
"staticpreload", xstaticpreload);
1157 else if TS_Xeq(
"listingdeny", xlistdeny);
1158 else if TS_Xeq(
"header2cgi", xheader2cgi);
1159 else if TS_Xeq(
"httpsmode", xhttpsmode);
1160 else if TS_Xeq(
"tlsreuse", xtlsreuse);
1161 else if TS_Xeq(
"auth", xauth);
1162 else if TS_Xeq(
"tlsclientauth", xtlsclientauth);
1163 else if TS_Xeq(
"tlsrequiredprefix", xtlsrequiredprefix);
1165 eDest.
Say(
"Config warning: ignoring unknown directive '", var,
"'.");
1180 {
eDest.
Say(
"Config failure: one or more directives are flawed!");
1186 hdr2cgimap[
"Cache-Control"] =
"cache-control";
1189 if (getenv(
"XRDCL_EC"))
usingEC =
true;
1198 :
"was not configured.");
1199 const char *what = Configed();
1201 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1204 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1206 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1216 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1217 "is meaningless; ignoring key!");
1225 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1226 "a cert specification!");
1237 const char *what1 = 0, *what2 = 0, *what3 = 0;
1242 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1246 what2 =
"xrd.tlsca to supply 'cadir'.";
1250 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1251 :
"xrd.tlsca to supply 'cafile'.");
1255 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1265 {
const char *what = Configed();
1266 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1267 :
"'xrd.tlsca noverify' was specified!");
1269 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1277 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1282 const char *how =
"completed.";
1283 eDest.
Say(
"++++++ HTTPS initialization started.");
1284 if (!
InitTLS()) {NoGo = 1; how =
"failed.";}
1285 eDest.
Say(
"------ HTTPS initialization ", how);
1286 if (NoGo)
return NoGo;
1290 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1294 return (InitSecurity() ? NoGo : 1);
1301 const char *XrdHttpProtocol::Configed()
1303 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1304 if (secxtractor)
return "secxtractor requires";
1305 if (
gridmap)
return "gridmap requires";
1321 if (myBuffEnd >= myBuffStart) {
1323 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1328 dest.
assign(myBuffStart, 0, l-1);
1347 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1349 if ((*p ==
'\n') || (*p ==
'\0')) {
1352 dest.
assign(myBuffStart, 0, l-1);
1368 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1370 if ((*p ==
'\n') || (*p ==
'\0')) {
1374 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1376 dest.
assign(myBuffStart, 0, l1-1);
1380 dest.
insert(myBuffStart, l1, l-1);
1404 int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1419 maxread = std::min(blen, BuffAvailable());
1420 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1426 int sslavail = maxread;
1429 int l = SSL_pending(ssl);
1431 sslavail = std::min(maxread, SSL_pending(ssl));
1436 ERR_print_errors(sslbio_err);
1440 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1441 if (sslavail <= 0)
return 0;
1443 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1445 myBuffEnd = myBuff->
buff;
1448 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1451 ERR_print_errors(sslbio_err);
1458 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1460 myBuffEnd = myBuff->
buff;
1466 rlen =
Link->
Recv(myBuffEnd, maxread);
1482 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1489 int XrdHttpProtocol::BuffAvailable() {
1492 if (myBuffEnd >= myBuffStart)
1493 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1495 r = myBuffStart - myBuffEnd;
1497 if ((r < 0) || (r > myBuff->
bsize)) {
1498 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1511 int XrdHttpProtocol::BuffUsed() {
1514 if (myBuffEnd >= myBuffStart)
1515 r = myBuffEnd - myBuffStart;
1518 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1520 if ((r < 0) || (r > myBuff->
bsize)) {
1521 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1534 int XrdHttpProtocol::BuffFree() {
1535 return (myBuff->
bsize - BuffUsed());
1542 void XrdHttpProtocol::BuffConsume(
int blen) {
1544 if (blen > myBuff->
bsize) {
1545 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1549 if (blen > BuffUsed()) {
1550 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1554 myBuffStart = myBuffStart + blen;
1556 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1557 myBuffStart -= myBuff->
bsize;
1559 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1560 myBuffEnd -= myBuff->
bsize;
1562 if (BuffUsed() == 0)
1563 myBuffStart = myBuffEnd = myBuff->
buff;
1578 int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1581 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1586 if (blen > BuffUsed()) {
1587 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1588 if ( getDataOneShot(blen - BuffUsed(),
true) )
1594 if ( !BuffUsed() ) {
1595 if ( getDataOneShot(blen,
false) )
1603 if (myBuffStart <= myBuffEnd) {
1604 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1607 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1609 *data = myBuffStart;
1620 int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1624 if (body && bodylen) {
1625 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1627 r = SSL_write(ssl, body, bodylen);
1629 ERR_print_errors(sslbio_err);
1635 if (r <= 0)
return -1;
1646 int XrdHttpProtocol::StartSimpleResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1647 std::stringstream ss;
1648 const std::string crlf =
"\r\n";
1650 ss <<
"HTTP/1.1 " << code <<
" ";
1654 if (code == 200) ss <<
"OK";
1655 else if (code == 100) ss <<
"Continue";
1656 else if (code == 206) ss <<
"Partial Content";
1657 else if (code == 302) ss <<
"Redirect";
1658 else if (code == 307) ss <<
"Temporary Redirect";
1659 else if (code == 400) ss <<
"Bad Request";
1660 else if (code == 403) ss <<
"Forbidden";
1661 else if (code == 404) ss <<
"Not Found";
1662 else if (code == 405) ss <<
"Method Not Allowed";
1663 else if (code == 416) ss <<
"Range Not Satisfiable";
1664 else if (code == 500) ss <<
"Internal Server Error";
1665 else if (code == 504) ss <<
"Gateway Timeout";
1666 else ss <<
"Unknown";
1669 if (keepalive && (code != 100))
1670 ss <<
"Connection: Keep-Alive" << crlf;
1672 ss <<
"Connection: Close" << crlf;
1674 ss <<
"Server: XrootD/" << XrdVSTRING << crlf;
1676 if ((bodylen >= 0) && (code != 100))
1677 ss <<
"Content-Length: " << bodylen << crlf;
1679 if (header_to_add && (header_to_add[0] !=
'\0'))
1680 ss << header_to_add << crlf;
1684 const std::string &outhdr = ss.str();
1685 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1686 if (SendData(outhdr.c_str(), outhdr.size()))
1696 int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1697 const std::string crlf =
"\r\n";
1698 std::stringstream ss;
1700 if (header_to_add && (header_to_add[0] !=
'\0')) {
1701 ss << header_to_add << crlf;
1704 ss <<
"Transfer-Encoding: chunked";
1705 TRACEI(RSP,
"Starting chunked response");
1706 return StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1713 int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1714 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1715 if (ChunkRespHeader(content_length))
1718 if (body && SendData(body, content_length))
1721 return ChunkRespFooter();
1728 int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1729 const std::string crlf =
"\r\n";
1730 std::stringstream ss;
1734 const std::string &chunkhdr = ss.str();
1735 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1736 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1743 int XrdHttpProtocol::ChunkRespFooter() {
1744 const std::string crlf =
"\r\n";
1745 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1756 int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1758 long long content_length = bodylen;
1760 content_length = body ? strlen(body) : 0;
1763 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0)
1770 return SendData(body, content_length);
1807 sprintf(buf,
"%d",
Port);
1813 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1819 if ((rdf = getenv(
"XRDROLE"))) {
1822 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1824 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1827 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1831 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1850 char *val, keybuf[1024], parmbuf[1024];
1855 if (!val || !val[0]) {
1856 err.
Emsg(
"Config",
"No headerkey specified.");
1861 while ( *val && !isalnum(*val) ) val++;
1862 strcpy(keybuf, val);
1866 pp = keybuf + strlen(keybuf) - 1;
1867 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1875 if(!parm || !parm[0]) {
1876 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1881 while ( *parm && !isalnum(*parm) ) parm++;
1882 strcpy(parmbuf, parm);
1885 pp = parmbuf + strlen(parmbuf) - 1;
1886 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1893 header2cgi[keybuf] = parmbuf;
1895 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1908 bool XrdHttpProtocol::InitTLS() {
1933 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1934 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1940 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1953 void XrdHttpProtocol::Cleanup() {
1955 TRACE(ALL,
" Cleanup");
1957 if (
BPool && myBuff) {
1958 BuffConsume(BuffUsed());
1972 int ret = SSL_shutdown(ssl);
1976 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1977 ERR_remove_thread_state(
nullptr);
1981 TRACE(ALL,
" SSL_shutdown failed");
1982 ERR_print_errors(sslbio_err);
2016 void XrdHttpProtocol::Reset() {
2018 TRACE(ALL,
" Reset");
2027 myBuffStart = myBuffEnd = 0;
2030 DoneSetInfo =
false;
2031 postheaderauth =
false;
2032 postheaderwait =
false;
2082 if (!val || !val[0]) {
2083 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2092 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2117 if (!val || !val[0]) {
2118 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2149 if (!val || !val[0]) {
2150 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2184 if (!val || !val[0]) {
2185 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2221 if (!val || !val[0]) {
2222 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2228 if (!strncmp(val,
"required", 8)) {
2232 if (!val || !val[0]) {
2233 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2241 if (!strcmp(val,
"compatNameGeneration")) {
2244 if (!val || !val[0]) {
2245 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2246 "[compatNameGeneration] parameter");
2278 if (!val || !val[0]) {
2279 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2307 bool inFile =
false;
2312 if (!val || !val[0]) {
2313 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2321 if (val[0] ==
'/') {
2324 int fd =
open(val, O_RDONLY);
2327 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2331 if (
fstat(fd, &st) != 0 ) {
2332 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2337 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2339 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2344 FILE *fp = fdopen(fd,
"r");
2346 if ( fp ==
nullptr ) {
2347 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2353 while( fgets(line, 1024, fp) ) {
2357 pp = line + strlen(line) - 1;
2358 while ( (pp >= line) && (!isalnum(*pp)) ) {
2365 while ( *pp && !isalnum(*pp) ) pp++;
2367 if ( strlen(pp) >= 32 ) {
2368 eDest.
Say(
"Config",
"Secret key loaded.");
2380 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2385 if ( strlen(val) < 32 ) {
2386 eDest.
Emsg(
"Config",
"Secret key is too short");
2393 if (!inFile)
Config.noEcho();
2417 if (!val || !val[0]) {
2418 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2424 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2449 if (!val || !val[0]) {
2450 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2482 if (!val || !val[0]) {
2483 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2489 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2514 if (!val || !val[0]) {
2515 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2521 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2546 if (!val || !val[0]) {
2547 eDest.
Emsg(
"Config",
"staticredir url not specified");
2576 char *val, *k, key[1024];
2582 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2591 if (!val || !val[0]) {
2592 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2597 int fp =
open(val, O_RDONLY);
2599 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2603 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2605 nfo->data = (
char *)malloc(65536);
2606 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2609 if (nfo->len <= 0) {
2610 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2614 if (nfo->len >= 65536) {
2615 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2647 if (!val || !val[0]) {
2648 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2654 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2682 if (!val || !val[0]) {
2683 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2688 if (!strncmp(val,
"required", 8)) {
2689 isRequiredXtractor =
true;
2692 if (!val || !val[0]) {
2693 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2700 strlcpy(libName, val,
sizeof(libName));
2701 libName[
sizeof(libName) - 1] =
'\0';
2702 char libParms[4096];
2704 if (!
Config.GetRest(libParms, 4095)) {
2705 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2711 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2737 std::vector<extHInfo> &hiVec) {
2738 char *val, path[1024], namebuf[1024];
2741 bool noTlsOK =
false;
2746 if (!val || !val[0]) {
2747 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2750 if (strlen(val) >= 16) {
2751 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2754 strncpy(namebuf, val,
sizeof(namebuf));
2755 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2760 if(val && !strcmp(
"+notls",val)) {
2767 if (!val || !val[0]) {
2768 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2771 if (strlen(val) >= (int)
sizeof(path)) {
2772 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2784 for (
int i = 0; i < (int)hiVec.size(); i++)
2785 {
if (hiVec[i].extHName == namebuf) {
2786 eDest.
Emsg(
"Config",
"Instance name already present for "
2787 "http external handler plugin",
2788 hiVec[i].extHPath.c_str());
2796 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2802 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2846 if (!val || !val[0]) {
2847 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2880 if (!val || !val[0]) {
2881 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2911 if (!val || !val[0])
2912 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2916 if (!strcmp(val,
"off"))
2923 if (!strcmp(val,
"on"))
2930 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
2935 auto val =
Config.GetWord();
2936 if (!val || !val[0])
2937 {
eDest.
Emsg(
"Config",
"tlsclientauth argument not specified");
return 1;}
2939 if (!strcmp(val,
"off"))
2943 if (!strcmp(val,
"on"))
2947 if (!strcmp(val,
"defer"))
2949 #if OPENSSL_VERSION_NUMBER >= 0x10100010L
2953 eDest.
Emsg(
"config",
"http.tlsclientauth defer is not supported on this platform");
2958 eDest.
Emsg(
"config",
"invalid tlsclientauth parameter -", val);
2963 auto val =
Config.GetWord();
2964 if (!val || !val[0])
2965 {
eDest.
Emsg(
"Config",
"tlsrequiredprefix argument not specified");
return 1;}
2968 {
eDest.
Emsg(
"Config",
"http.tlsrequiredprefix argument must be an absolute path");
return 1;}
2975 char *val =
Config.GetWord();
2977 if(!strcmp(
"tpc",val)) {
2978 if(!(val =
Config.GetWord())) {
2979 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
2981 if(!strcmp(
"fcreds",val)) {
2984 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
2988 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
3012 static struct traceopts {
3024 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3026 if (!(val =
Config.GetWord())) {
3027 eDest.
Emsg(
"config",
"trace option not specified");
3031 if (!strcmp(val,
"off")) trval = 0;
3033 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3034 for (i = 0; i < numopts; i++) {
3035 if (!strcmp(val, tropts[i].opname)) {
3036 if (neg) trval &= ~tropts[i].opval;
3037 else trval |= tropts[i].opval;
3042 eDest.
Emsg(
"config",
"invalid trace option", val);
3061 l = strlen(fname) + 1;
3086 length = fname.
length() + 1;
3098 int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3099 const char *libParms) {
3103 if (secxtractor)
return 1;
3105 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3111 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3119 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3120 for (
int i = 0; i < (int) hiVec.size(); i++) {
3121 if(hiVec[i].extHNoTlsOK) {
3123 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3124 hiVec[i].extHParm.c_str(), &myEnv,
3125 hiVec[i].extHName.c_str()))
3132 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3144 for (
int i = 0; i < (int)hiVec.size(); i++) {
3147 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3148 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3149 hiVec[i].extHParm.c_str(), &myEnv,
3150 hiVec[i].extHName.c_str()))
return 1;
3157 int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3158 const char *configFN,
const char *libParms,
3159 XrdOucEnv *myEnv,
const char *instName) {
3163 if (ExtHandlerLoaded(instName)) {
3164 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3168 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3172 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3180 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3183 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3184 exthandler[exthandlercnt].name[15] =
'\0';
3185 exthandler[exthandlercnt++].ptr = newhandler;
3198 bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3199 for (
int i = 0; i < exthandlercnt; i++) {
3200 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3211 for (
int i = 0; i < exthandlercnt; i++) {
3213 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 bool isRequiredGridmap
static char * listredir
Url to redirect to in the case a listing is requested.
int Stats(char *buff, int blen, int do_sync=0)
Get activity stats.
static int crlRefIntervalSec
CRL thread refresh interval.
static XrdHttpReadRangeHandler::Configuration ReadRangeConfig
configuration for the read range handler
static XrdSecService * CIA
static XrdBuffManager * BPool
static 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)
void SetTlsClientAuth(ClientAuthSetting setting)
int SessionCache(int opts=scNone, const char *id=0, int idlen=0)
static const int DEFAULT_CRL_REF_INT_SEC
Default CRL refresh interval in seconds.
static const uint64_t servr
This is a server context.
static const uint64_t rfCRL
Turn on the CRL refresh thread.
static const uint64_t logVF
Log verify failures.
static const uint64_t artON
Auto retry Handshake.
const CTX_Params * GetParams()
static const int scOff
Turn off cache.
bool SetContextCiphers(const char *ciphers)
static const int scSrvr
Turn on cache server mode (default)
static Bridge * Login(Result *rsltP, XrdLink *linkP, XrdSecEntity *seceP, const char *nameP, const char *protP)
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0
CloseImpl< false > Close(Ctx< File > file, uint16_t timeout=0)
Factory for creating CloseImpl objects.
std::vector< std::string > tlsAuthRequestPrefixes
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.