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.");
784 SendSimpleResp(400,
nullptr,
nullptr,
"Authentication failed: invalid token", 0,
false);
791 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
799 TRACEI(ALL,
" Rejecting plain http with no valid token as we have a secretkey.");
819 TRACEI(REQ,
" Authorization failed.");
836 TRACEI(REQ,
"Process is exiting rc:" << rc);
844 #define TRACELINK Link
898 #define TS_Xeq(x,m) (!strcmp(x,var)) GoNo = m(Config)
900 #define TS_Xeq3(x,m) (!strcmp(x,var)) GoNo = m(Config, extHIVec)
902 #define HTTPS_ALERT(x,y,z) httpsspec = true;\
903 if (xrdctx && httpsmode == hsmAuto && (z || xrdctx->x509Verify())) \
904 eDest.Say("Config http." x " overrides the xrd." y " directive.")
906 int XrdHttpProtocol::Config(
const char *ConfigFN,
XrdOucEnv *myEnv) {
909 std::vector<extHInfo> extHIVec;
911 int cfgFD, GoNo, NoGo = 0, ismine;
928 eDest.
Emsg(
"httpMon", rc,
"create stats thread");
936 if(nonIanaChecksums.size()) {
937 std::stringstream warningMsgSS;
938 warningMsgSS <<
"Config warning: the following checksum algorithms are not IANA compliant: [";
939 std::string unknownCksumString;
940 for(
auto unknownCksum: nonIanaChecksums) {
941 unknownCksumString += unknownCksum +
",";
943 unknownCksumString.erase(unknownCksumString.size() - 1);
944 warningMsgSS << unknownCksumString <<
"]" <<
". They therefore cannot be queried by a user via HTTP." ;
945 eDest.
Say(warningMsgSS.str().c_str());
971 if ((cfgFD =
open(ConfigFN, O_RDONLY, 0)) < 0)
972 return eDest.
Emsg(
"Config", errno,
"open config file", ConfigFN);
974 static const char *cvec[] = {
"*** http protocol config:", 0 };
979 while ((var =
Config.GetMyFirstWord())) {
980 if ((ismine = !strncmp(
"http.", var, 5)) && var[5]) var += 5;
983 if TS_Xeq(
"trace", xtrace);
984 else if TS_Xeq(
"cert", xsslcert);
985 else if TS_Xeq(
"key", xsslkey);
986 else if TS_Xeq(
"cadir", xsslcadir);
987 else if TS_Xeq(
"cipherfilter", xsslcipherfilter);
988 else if TS_Xeq(
"gridmap", xgmap);
989 else if TS_Xeq(
"cafile", xsslcafile);
990 else if TS_Xeq(
"secretkey", xsecretkey);
991 else if TS_Xeq(
"desthttps", xdesthttps);
992 else if TS_Xeq(
"secxtractor", xsecxtractor);
993 else if TS_Xeq(
"cors", xcors);
994 else if TS_Xeq3(
"exthandler", xexthandler);
995 else if TS_Xeq(
"selfhttps2http", xselfhttps2http);
996 else if TS_Xeq(
"embeddedstatic", xembeddedstatic);
997 else if TS_Xeq(
"listingredir", xlistredir);
998 else if TS_Xeq(
"staticredir", xstaticredir);
999 else if TS_Xeq(
"staticpreload", xstaticpreload);
1000 else if TS_Xeq(
"staticheader", xstaticheader);
1001 else if TS_Xeq(
"listingdeny", xlistdeny);
1002 else if TS_Xeq(
"listing", xlisting);
1003 else if TS_Xeq(
"header2cgi", xheader2cgi);
1004 else if TS_Xeq(
"httpsmode", xhttpsmode);
1005 else if TS_Xeq(
"tlsreuse", xtlsreuse);
1006 else if TS_Xeq(
"auth", xauth);
1007 else if TS_Xeq(
"tlsclientauth", xtlsclientauth);
1008 else if TS_Xeq(
"maxdelay", xmaxdelay);
1010 eDest.
Say(
"Config warning: ignoring unknown directive '", var,
"'.");
1025 {
eDest.
Say(
"Config failure: one or more directives are flawed!");
1031 hdr2cgimap[
"Cache-Control"] =
"cache-control";
1034 if (getenv(
"XRDCL_EC"))
usingEC =
true;
1039 std::string default_static_headers;
1041 for (
const auto &header_entry : default_verb->second) {
1042 default_static_headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1047 if (item.first.empty()) {
1050 auto headers = default_static_headers;
1051 for (
const auto &header_entry : item.second) {
1052 headers += header_entry.first +
": " + header_entry.second +
"\r\n";
1060 if (myEnv->
Get(
"XrdCache")) hasCache =
true;
1079 :
"was not configured.");
1080 const char *what = Configed();
1082 eDest.
Say(
"Config warning: HTTPS functionality ", why);
1085 LoadExtHandlerNoTls(extHIVec, ConfigFN, *myEnv);
1087 {
eDest.
Say(
"Config failure: ", what,
" HTTPS but it ", why);
1097 {
eDest.
Say(
"Config warning: specifying http.key without http.cert "
1098 "is meaningless; ignoring key!");
1106 {
eDest.
Say(
"Config failure: 'httpsmode manual' requires atleast a "
1107 "a cert specification!");
1118 const char *what1 = 0, *what2 = 0, *what3 = 0;
1123 what1 =
"xrd.tls to supply 'cert' and 'key'.";
1127 what2 =
"xrd.tlsca to supply 'cadir'.";
1131 what2 = (what2 ?
"xrd.tlsca to supply 'cadir' and 'cafile'."
1132 :
"xrd.tlsca to supply 'cafile'.");
1136 what3 =
"xrd.tlsca to supply 'refresh' interval.";
1150 {
const char *what = Configed();
1151 const char *why = (
httpsspec ?
"a cadir or cafile was not specified!"
1152 :
"'xrd.tlsca noverify' was specified!");
1154 {
eDest.
Say(
"Config failure: ", what,
" cert verification but ", why);
1162 sslbio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
1167 const char *how =
"completed.";
1168 eDest.
Say(
"++++++ HTTPS initialization started.");
1169 if (!
InitTLS()) {NoGo = 1; how =
"failed.";}
1170 eDest.
Say(
"------ HTTPS initialization ", how);
1171 if (NoGo)
return NoGo;
1175 if (LoadExtHandler(extHIVec, ConfigFN, *myEnv))
return 1;
1179 return (InitSecurity() ? NoGo : 1);
1186 const char *XrdHttpProtocol::Configed()
1188 if (secxtractor &&
gridmap)
return "gridmap and secxtractor require";
1189 if (secxtractor)
return "secxtractor requires";
1190 if (
gridmap)
return "gridmap requires";
1206 if (myBuffEnd >= myBuffStart) {
1208 for (
char *p = myBuffStart; p < myBuffEnd; p++) {
1213 dest.
assign(myBuffStart, 0, l-1);
1232 for (
char *p = myBuffStart; p < myBuff->
buff + myBuff->
bsize; p++) {
1234 if ((*p ==
'\n') || (*p ==
'\0')) {
1237 dest.
assign(myBuffStart, 0, l-1);
1253 for (
char *p = myBuff->
buff; p < myBuffEnd; p++) {
1255 if ((*p ==
'\n') || (*p ==
'\0')) {
1259 int l1 = myBuff->
buff + myBuff->
bsize - myBuffStart;
1261 dest.
assign(myBuffStart, 0, l1-1);
1265 dest.
insert(myBuffStart, l1, l-1);
1289 int XrdHttpProtocol::getDataOneShot(
int blen,
bool wait) {
1304 maxread = std::min(blen, BuffAvailable());
1305 TRACE(
DEBUG,
"getDataOneShot BuffAvailable: " << BuffAvailable() <<
" maxread: " << maxread);
1311 int sslavail = maxread;
1314 int l = SSL_pending(ssl);
1316 sslavail = std::min(maxread, SSL_pending(ssl));
1321 ERR_print_errors(sslbio_err);
1325 TRACE(
DEBUG,
"getDataOneShot sslavail: " << sslavail);
1326 if (sslavail <= 0)
return 0;
1328 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1330 myBuffEnd = myBuff->
buff;
1333 rlen = SSL_read(ssl, myBuffEnd, sslavail);
1336 ERR_print_errors(sslbio_err);
1343 if (myBuffEnd - myBuff->
buff >= myBuff->
bsize) {
1345 myBuffEnd = myBuff->
buff;
1351 rlen =
Link->
Recv(myBuffEnd, maxread);
1367 TRACE(REQ,
"read " << rlen <<
" of " << blen <<
" bytes");
1374 int XrdHttpProtocol::BuffAvailable() {
1377 if (myBuffEnd >= myBuffStart)
1378 r = myBuff->
buff + myBuff->
bsize - myBuffEnd;
1380 r = myBuffStart - myBuffEnd;
1382 if ((r < 0) || (r > myBuff->
bsize)) {
1383 TRACE(REQ,
"internal error, myBuffAvailable: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1396 int XrdHttpProtocol::BuffUsed() {
1399 if (myBuffEnd >= myBuffStart)
1400 r = myBuffEnd - myBuffStart;
1403 r = myBuff->
bsize - (myBuffStart - myBuffEnd);
1405 if ((r < 0) || (r > myBuff->
bsize)) {
1406 TRACE(REQ,
"internal error, myBuffUsed: " << r <<
" myBuff->bsize " << myBuff->
bsize);
1419 int XrdHttpProtocol::BuffFree() {
1420 return (myBuff->
bsize - BuffUsed());
1427 void XrdHttpProtocol::BuffConsume(
int blen) {
1429 if (blen > myBuff->
bsize) {
1430 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") smaller than buffsize");
1434 if (blen > BuffUsed()) {
1435 TRACE(REQ,
"internal error, BuffConsume(" << blen <<
") larger than BuffUsed:" << BuffUsed());
1439 myBuffStart = myBuffStart + blen;
1441 if (myBuffStart >= myBuff->
buff + myBuff->
bsize)
1442 myBuffStart -= myBuff->
bsize;
1444 if (myBuffEnd >= myBuff->
buff + myBuff->
bsize)
1445 myBuffEnd -= myBuff->
bsize;
1447 if (BuffUsed() == 0)
1448 myBuffStart = myBuffEnd = myBuff->
buff;
1463 int XrdHttpProtocol::BuffgetData(
int blen,
char **data,
bool wait) {
1466 TRACE(
DEBUG,
"BuffgetData: requested " << blen <<
" bytes");
1471 if (blen > BuffUsed()) {
1472 TRACE(REQ,
"BuffgetData: need to read " << blen - BuffUsed() <<
" bytes");
1473 if ( getDataOneShot(blen - BuffUsed(),
true) )
1479 if ( !BuffUsed() ) {
1480 if ( getDataOneShot(blen,
false) )
1488 if (myBuffStart <= myBuffEnd) {
1489 rlen = std::min( (
long) blen, (
long)(myBuffEnd - myBuffStart) );
1492 rlen = std::min( (
long) blen, (
long)(myBuff->
buff + myBuff->
bsize - myBuffStart) );
1494 *data = myBuffStart;
1505 int XrdHttpProtocol::SendData(
const char *body,
int bodylen) {
1509 if (body && bodylen) {
1510 TRACE(REQ,
"Sending " << bodylen <<
" bytes");
1512 r = SSL_write(ssl, body, bodylen);
1514 ERR_print_errors(sslbio_err);
1526 return r <= 0 ? -1 : 0;
1533 int XrdHttpProtocol::StartSimpleResp(
int code,
const char *desc,
1534 const char *header_to_add,
1535 long long bodylen,
bool keepalive) {
1536 std::stringstream ss;
1537 const std::string crlf =
"\r\n";
1539 ss <<
"HTTP/1.1 " << code <<
" ";
1548 if (keepalive && (code != 100))
1549 ss <<
"Connection: Keep-Alive" << crlf;
1551 ss <<
"Connection: Close" << crlf;
1553 ss <<
"Server: XRootD" << crlf;
1564 if(corsAllowOrigin) {
1565 ss << *corsAllowOrigin << crlf;
1569 if ((bodylen >= 0) && (code != 100))
1570 ss <<
"Content-Length: " << bodylen << crlf;
1572 if (header_to_add && (header_to_add[0] !=
'\0')) ss << header_to_add << crlf;
1576 const std::string &outhdr = ss.str();
1577 TRACEI(RSP,
"Sending resp: " << code <<
" header len:" << outhdr.size());
1578 if (SendData(outhdr.c_str(), outhdr.size()))
1588 int XrdHttpProtocol::StartChunkedResp(
int code,
const char *desc,
const char *header_to_add,
long long bodylen,
bool keepalive) {
1589 const std::string crlf =
"\r\n";
1590 std::stringstream ss;
1594 if (header_to_add && (header_to_add[0] !=
'\0')) {
1595 ss << header_to_add << crlf;
1598 ss <<
"Transfer-Encoding: chunked";
1599 TRACEI(RSP,
"Starting chunked response");
1601 int r = StartSimpleResp(code, desc, ss.str().c_str(), bodylen, keepalive);
1610 int XrdHttpProtocol::ChunkResp(
const char *body,
long long bodylen) {
1611 long long content_length = (bodylen <= 0) ? (body ? strlen(body) : 0) : bodylen;
1612 long long header_len = (bodylen < 0) ? 0 : content_length;
1616 if (ChunkRespHeader(header_len)) {
1621 if (body && SendData(body, content_length)){
1626 int r = ChunkRespFooter();
1628 if (content_length == 0 || bodylen == -1) {
1643 int XrdHttpProtocol::ChunkRespHeader(
long long bodylen) {
1644 const std::string crlf =
"\r\n";
1645 std::stringstream ss;
1649 const std::string &chunkhdr = ss.str();
1650 TRACEI(RSP,
"Sending encoded chunk of size " << bodylen);
1651 return (SendData(chunkhdr.c_str(), chunkhdr.size())) ? -1 : 0;
1658 int XrdHttpProtocol::ChunkRespFooter() {
1659 const std::string crlf =
"\r\n";
1660 return (SendData(crlf.c_str(), crlf.size())) ? -1 : 0;
1671 int XrdHttpProtocol::SendSimpleResp(
int code,
const char *desc,
const char *header_to_add,
const char *body,
long long bodylen,
bool keepalive) {
1677 long long content_length = bodylen;
1679 content_length = body ? strlen(body) : 0;
1682 if (StartSimpleResp(code, desc, header_to_add, content_length, keepalive) < 0) {
1689 if (body) r = SendData(body, content_length);
1727 sprintf(buf,
"%d",
Port);
1733 rdf = (parms && *parms ? parms : pi->
ConfigFN);
1739 if ((rdf = getenv(
"XRDROLE"))) {
1742 if (!strcasecmp(rdf,
"manager") || !strcasecmp(rdf,
"supervisor")) {
1744 eDest.
Emsg(
"Config",
"Configured as HTTP(s) redirector.");
1747 eDest.
Emsg(
"Config",
"Configured as HTTP(s) data server.");
1751 eDest.
Emsg(
"Config",
"No XRDROLE specified.");
1770 char *val, keybuf[1024], parmbuf[1024];
1772 bool strip_on_redirect =
false;
1776 if (!val || !val[0]) {
1777 err.
Emsg(
"Config",
"No headerkey specified.");
1782 while ( *val && !isalnum(*val) ) val++;
1783 strcpy(keybuf, val);
1787 pp = keybuf + strlen(keybuf) - 1;
1788 while ( (pp >= keybuf) && (!isalnum(*pp)) ) {
1796 if(!parm || !parm[0]) {
1797 err.
Emsg(
"Config",
"No header2cgi value specified. key: '", keybuf,
"'");
1802 while ( *parm && !isalnum(*parm) ) parm++;
1803 strcpy(parmbuf, parm);
1806 pp = parmbuf + strlen(parmbuf) - 1;
1807 while ( (pp >= parmbuf) && (!isalnum(*pp)) ) {
1813 char *nextWord =
Config.GetWord();
1814 if (nextWord && nextWord[0] && !strcasecmp(nextWord,
"strip-on-redirect")) {
1815 strip_on_redirect =
true;
1820 header2cgi[keybuf] = parmbuf;
1821 if (strip_on_redirect) {
1825 err.
Emsg(
"Config",
"Can't insert new header2cgi rule. key: '", keybuf,
"'");
1838 bool XrdHttpProtocol::InitTLS() {
1867 static const char *sess_ctx_id =
"XrdHTTPSessionCtx";
1868 unsigned int n =(
unsigned int)(strlen(sess_ctx_id)+1);
1874 {
eDest.
Say(
"Config failure: ",
"Unable to set allowable https ciphers!");
1890 void XrdHttpProtocol::Cleanup() {
1892 TRACE(ALL,
" Cleanup");
1894 if (
BPool && myBuff) {
1895 BuffConsume(BuffUsed());
1910 int ret = SSL_shutdown(ssl);
1914 ret = SSL_shutdown(ssl);
1916 TRACE(ALL,
"SSL server failed to receive the SSL shutdown message from the client");
1917 ERR_print_errors(sslbio_err);
1921 TRACE(ALL,
"SSL server failed to send the shutdown message to the client");
1922 ERR_print_errors(sslbio_err);
1956 void XrdHttpProtocol::Reset() {
1958 TRACE(ALL,
" Reset");
1967 myBuffStart = myBuffEnd = 0;
1970 DoneSetInfo =
false;
2020 if (!val || !val[0]) {
2021 eDest.
Emsg(
"Config",
"httpsmode parameter not specified");
2030 else {
eDest.
Emsg(
"Config",
"invalid httpsmode parameter - ", val);
2055 if (!val || !val[0]) {
2056 eDest.
Emsg(
"Config",
"sslverifydepth value not specified");
2087 if (!val || !val[0]) {
2088 eDest.
Emsg(
"Config",
"HTTP X509 certificate not specified");
2122 if (!val || !val[0]) {
2123 eDest.
Emsg(
"Config",
"HTTP X509 key not specified");
2159 if (!val || !val[0]) {
2160 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file location not specified");
2166 if (!strncmp(val,
"required", 8)) {
2170 if (!val || !val[0]) {
2171 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after [required] "
2179 if (!strcmp(val,
"compatNameGeneration")) {
2182 if (!val || !val[0]) {
2183 eDest.
Emsg(
"Config",
"HTTP X509 gridmap file missing after "
2184 "[compatNameGeneration] parameter");
2216 if (!val || !val[0]) {
2217 eDest.
Emsg(
"Config",
"HTTP X509 CAfile not specified");
2245 bool inFile =
false;
2250 if (!val || !val[0]) {
2251 eDest.
Emsg(
"Config",
"Shared secret key not specified");
2259 if (val[0] ==
'/') {
2262 int fd =
open(val, O_RDONLY);
2265 eDest.
Emsg(
"Config", errno,
"open shared secret key file", val);
2269 if (
fstat(fd, &st) != 0 ) {
2270 eDest.
Emsg(
"Config", errno,
"fstat shared secret key file", val);
2275 if ( st.st_mode & S_IWOTH & S_IWGRP & S_IROTH) {
2277 "For your own security, the shared secret key file cannot be world readable or group writable '", val,
"'");
2282 FILE *fp = fdopen(fd,
"r");
2284 if ( fp ==
nullptr ) {
2285 eDest.
Emsg(
"Config", errno,
"fdopen shared secret key file", val);
2291 while( fgets(line, 1024, fp) ) {
2295 pp = line + strlen(line) - 1;
2296 while ( (pp >= line) && (!isalnum(*pp)) ) {
2303 while ( *pp && !isalnum(*pp) ) pp++;
2305 if ( strlen(pp) >= 32 ) {
2306 eDest.
Say(
"Config",
"Secret key loaded.");
2318 eDest.
Emsg(
"Config",
"Cannot find useful secretkey in file '", val,
"'");
2323 if ( strlen(val) < 32 ) {
2324 eDest.
Emsg(
"Config",
"Secret key is too short");
2331 if (!inFile)
Config.noEcho();
2355 if (!val || !val[0]) {
2356 eDest.
Emsg(
"Config",
"listingdeny flag not specified");
2362 listdeny = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2386 if (!val || !val[0]) {
2387 eDest.
Emsg(
"Config",
"listing flag not specified");
2391 int denyCmp = strncasecmp(val,
"deny",4);
2392 if (denyCmp && strncasecmp(val,
"allow",5)) {
2393 eDest.
Emsg(
"Config",
"http.listing option only accepts \"allow\" or \"deny\"");
2423 if (!val || !val[0]) {
2424 eDest.
Emsg(
"Config",
"listingredir flag not specified");
2456 if (!val || !val[0]) {
2457 eDest.
Emsg(
"Config",
"desthttps flag not specified");
2463 isdesthttps = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2488 if (!val || !val[0]) {
2489 eDest.
Emsg(
"Config",
"embeddedstatic flag not specified");
2495 embeddedstatic = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2520 if (!val || !val[0]) {
2521 eDest.
Emsg(
"Config",
"staticredir url not specified");
2550 char *val, *k, key[1024];
2556 eDest.
Emsg(
"Config",
"preloadstatic urlpath not specified");
2565 if (!val || !val[0]) {
2566 eDest.
Emsg(
"Config",
"preloadstatic filename not specified");
2571 int fp =
open(val, O_RDONLY);
2573 eDest.
Emsg(
"Config", errno,
"open preloadstatic filename", val);
2577 StaticPreloadInfo *nfo =
new StaticPreloadInfo;
2579 nfo->data = (
char *)malloc(65536);
2580 nfo->len =
read(fp, (
void *)nfo->data, 65536);
2583 if (nfo->len <= 0) {
2584 eDest.
Emsg(
"Config", errno,
"read from preloadstatic filename", val);
2588 if (nfo->len >= 65536) {
2589 eDest.
Emsg(
"Config",
"Truncated preloadstatic filename. Max is 64 KB '", val,
"'");
2620 auto val =
Config.GetWord();
2621 std::vector<std::string> verbs;
2623 if (!val || !val[0]) {
2624 eDest.
Emsg(
"Config",
"http.staticheader requires the header to be specified");
2628 std::string match_verb;
2629 std::string_view val_str(val);
2630 if (val_str.substr(0, 6) ==
"-verb=") {
2631 verbs.emplace_back(val_str.substr(6));
2632 }
else if (val_str ==
"-") {
2633 eDest.
Emsg(
"Config",
"http.staticheader is ignoring unknown flag: ", val_str.data());
2640 if (verbs.empty()) {
2641 verbs.emplace_back();
2644 std::string header = val;
2647 std::string header_value;
2648 if (val && val[0]) {
2652 for (
const auto &verb : verbs) {
2655 if (!header_value.empty())
2657 }
else if (header_value.empty()) {
2658 iter->second.clear();
2660 iter->second.emplace_back(header, header_value);
2687 if (!val || !val[0]) {
2688 eDest.
Emsg(
"Config",
"selfhttps2http flag not specified");
2694 selfhttps2http = (!strcasecmp(val,
"true") || !strcasecmp(val,
"yes") || !strcmp(val,
"1"));
2722 if (!val || !val[0]) {
2723 eDest.
Emsg(
"Config",
"No security extractor plugin specified.");
2728 if (!strncmp(val,
"required", 8)) {
2729 isRequiredXtractor =
true;
2732 if (!val || !val[0]) {
2733 eDest.
Emsg(
"Config",
"No security extractor plugin after [required] "
2740 strlcpy(libName, val,
sizeof(libName));
2741 libName[
sizeof(libName) - 1] =
'\0';
2742 char libParms[4096];
2744 if (!
Config.GetRest(libParms, 4095)) {
2745 eDest.
Emsg(
"Config",
"secxtractor config params longer than 4k");
2751 if (LoadSecXtractor(&
eDest, libName, libParms)) {
2763 if (!val || !val[0]) {
2764 eDest.
Emsg(
"Config",
"No CORS plugin specified.");
2789 std::vector<extHInfo> &hiVec) {
2790 char *val, path[1024], namebuf[1024];
2793 bool noTlsOK =
false;
2798 if (!val || !val[0]) {
2799 eDest.
Emsg(
"Config",
"No instance name specified for an http external handler plugin.");
2802 if (strlen(val) >= 16) {
2803 eDest.
Emsg(
"Config",
"Instance name too long for an http external handler plugin.");
2806 strncpy(namebuf, val,
sizeof(namebuf));
2807 namebuf[
sizeof(namebuf)-1 ] =
'\0';
2812 if(val && !strcmp(
"+notls",val)) {
2819 if (!val || !val[0]) {
2820 eDest.
Emsg(
"Config",
"No http external handler plugin specified.");
2823 if (strlen(val) >= (int)
sizeof(path)) {
2824 eDest.
Emsg(
"Config",
"Path too long for an http external handler plugin.");
2836 for (
int i = 0; i < (int)hiVec.size(); i++)
2837 {
if (hiVec[i].extHName == namebuf) {
2838 eDest.
Emsg(
"Config",
"Instance name already present for "
2839 "http external handler plugin",
2840 hiVec[i].extHPath.c_str());
2848 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
2854 hiVec.push_back(extHInfo(namebuf, path, (parm ? parm :
""), noTlsOK));
2898 if (!val || !val[0]) {
2899 eDest.
Emsg(
"Config",
"HTTP X509 CAdir not specified");
2932 if (!val || !val[0]) {
2933 eDest.
Emsg(
"Config",
"SSL cipherlist filter string not specified");
2963 if (!val || !val[0])
2964 {
eDest.
Emsg(
"Config",
"tlsreuse argument not specified");
return 1;}
2968 if (!strcmp(val,
"off"))
2975 if (!strcmp(val,
"on"))
2982 eDest.
Emsg(
"config",
"invalid tlsreuse parameter -", val);
2987 auto val =
Config.GetWord();
2988 if (!val || !val[0])
2989 {
eDest.
Emsg(
"Config",
"tlsclientauth argument not specified");
return 1;}
2991 if (!strcmp(val,
"off"))
2995 if (!strcmp(val,
"on"))
3000 eDest.
Emsg(
"config",
"invalid tlsclientauth parameter -", val);
3005 char *val =
Config.GetWord();
3007 if(!strcmp(
"tpc",val)) {
3008 if(!(val =
Config.GetWord())) {
3009 eDest.
Emsg(
"Config",
"http.auth tpc value not specified.");
return 1;
3011 if(!strcmp(
"fcreds",val)) {
3014 eDest.
Emsg(
"Config",
"http.auth tpc value is invalid");
return 1;
3018 eDest.
Emsg(
"Config",
"http.auth value is invalid");
return 1;
3025 char *val =
Config.GetWord();
3031 eDest.
Emsg(
"Config",
"http.maxdelay requires an argument in seconds (default is 30). Example: http.maxdelay 30");
3055 static struct traceopts {
3067 int i, neg, trval = 0, numopts =
sizeof (tropts) /
sizeof (
struct traceopts);
3069 if (!(val =
Config.GetWord())) {
3070 eDest.
Emsg(
"config",
"trace option not specified");
3074 if (!strcmp(val,
"off")) trval = 0;
3076 if ((neg = (val[0] ==
'-' && val[1]))) val++;
3077 for (i = 0; i < numopts; i++) {
3078 if (!strcmp(val, tropts[i].opname)) {
3079 if (neg) trval &= ~tropts[i].opval;
3080 else trval |= tropts[i].opval;
3085 eDest.
Emsg(
"config",
"invalid trace option", val);
3104 l = strlen(fname) + 1;
3129 length = fname.
length() + 1;
3141 int XrdHttpProtocol::LoadSecXtractor(
XrdSysError *myeDest,
const char *libName,
3142 const char *libParms) {
3146 if (secxtractor)
return 1;
3148 XrdOucPinLoader myLib(myeDest, &compiledVer,
"secxtractorlib", libName);
3154 if (ep && (secxtractor = ep(myeDest, NULL, libParms)))
return 0;
3162 int XrdHttpProtocol::LoadExtHandlerNoTls(std::vector<extHInfo> &hiVec,
const char *cFN,
XrdOucEnv &myEnv) {
3163 for (
int i = 0; i < (int) hiVec.size(); i++) {
3164 if(hiVec[i].extHNoTlsOK) {
3166 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3167 hiVec[i].extHParm.c_str(), &myEnv,
3168 hiVec[i].extHName.c_str()))
3175 int XrdHttpProtocol::LoadExtHandler(std::vector<extHInfo> &hiVec,
3189 for (
int i = 0; i < (int)hiVec.size(); i++) {
3192 if(!ExtHandlerLoaded(hiVec[i].extHName.c_str())) {
3193 if (LoadExtHandler(&
eDest, hiVec[i].extHPath.c_str(), cFN,
3194 hiVec[i].extHParm.c_str(), &myEnv,
3195 hiVec[i].extHName.c_str()))
return 1;
3202 int XrdHttpProtocol::LoadExtHandler(
XrdSysError *myeDest,
const char *libName,
3203 const char *configFN,
const char *libParms,
3204 XrdOucEnv *myEnv,
const char *instName) {
3208 if (ExtHandlerLoaded(instName)) {
3209 eDest.
Emsg(
"Config",
"Instance name already present for an http external handler plugin.");
3213 eDest.
Emsg(
"Config",
"Cannot load one more exthandler. Max is 4");
3217 XrdOucPinLoader myLib(myeDest, &compiledVer,
"exthandlerlib", libName);
3225 if (ep && (newhandler = ep(myeDest, configFN, libParms, myEnv))) {
3228 strncpy( exthandler[exthandlercnt].name, instName, 16 );
3229 exthandler[exthandlercnt].name[15] =
'\0';
3230 exthandler[exthandlercnt++].ptr = newhandler;
3240 int XrdHttpProtocol::LoadCorsHandler(
XrdSysError *
eDest,
const char *libname) {
3245 if(ep && (
xrdcors = ep()))
return 0;
3252 bool XrdHttpProtocol::ExtHandlerLoaded(
const char *handlername) {
3253 for (
int i = 0; i < exthandlercnt; i++) {
3254 if ( !strncmp(exthandler[i].name, handlername, 15) ) {
3265 for (
int i = 0; i < exthandlercnt; i++) {
3267 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.