From 463c6169afba296db2e7941f27391296be4218be Mon Sep 17 00:00:00 2001 From: Fabrizio Furano Date: Wed, 27 Sep 2017 16:22:24 +0200 Subject: [PATCH 1/4] First implementation of the header2cgi directive --- src/XrdHttp/XrdHttpProtocol.cc | 61 ++++++++++++++++++++++++++++++++++ src/XrdHttp/XrdHttpProtocol.hh | 6 +++- src/XrdHttp/XrdHttpReq.cc | 37 ++++++++++++++++++++- src/XrdHttp/XrdHttpReq.hh | 4 ++- src/XrdHttp/XrdHttpUtils.cc | 2 +- src/XrdHttp/XrdHttpUtils.hh | 2 +- 6 files changed, 107 insertions(+), 5 deletions(-) diff --git a/src/XrdHttp/XrdHttpProtocol.cc b/src/XrdHttp/XrdHttpProtocol.cc index 2b1e74fe7a0..1bb06ffc320 100644 --- a/src/XrdHttp/XrdHttpProtocol.cc +++ b/src/XrdHttp/XrdHttpProtocol.cc @@ -2224,7 +2224,68 @@ int XrdHttpProtocol::xexthandler(XrdOucStream & Config, const char *ConfigFN, Xr +/* Function: xheader2cgi + * + * Purpose: To parse the directive: header2cgi + * + * the name of an incoming HTTP header + * to be transformed + * the name to be given when adding it to the cgi info + * that is kept only internally + * + * Output: 0 upon success or !0 upon failure. + */ +int XrdHttpProtocol::xheader2cgi(XrdOucStream & Config) { + char *val, keybuf[1024], parmbuf[1024]; + char *parm; + + // Get the path + // + val = Config.GetWord(); + if (!val || !val[0]) { + eDest.Emsg("Config", "No headerkey specified."); + return 1; + } else { + + // Trim the beginning, in place + while ( *val && !isalnum(*val) ) val++; + strcpy(keybuf, val); + + // Trim the end, in place + char *pp; + pp = keybuf + strlen(keybuf) - 1; + while ( (pp >= keybuf) && (!isalnum(*pp)) ) { + *pp = '\0'; + pp--; + } + + parm = Config.GetWord(); + + // Trim the beginning, in place + while ( *parm && !isalnum(*parm) ) parm++; + strcpy(parmbuf, parm); + + // Trim the end, in place + pp = parmbuf + strlen(parmbuf) - 1; + while ( (pp >= parmbuf) && (!isalnum(*pp)) ) { + *pp = '\0'; + pp--; + } + + // Add this mapping to the map that will be used + try { + hdr2cgimap[keybuf] = parmbuf; + } catch ( ... ) { + eDest.Emsg("Config", "Can't insert new header2cgi rule. key: '", keybuf, "'"); + return 1; + } + + } + + + return 0; +} diff --git a/src/XrdHttp/XrdHttpProtocol.hh b/src/XrdHttp/XrdHttpProtocol.hh index 4731112d5d5..51c0efe4a35 100644 --- a/src/XrdHttp/XrdHttpProtocol.hh +++ b/src/XrdHttp/XrdHttpProtocol.hh @@ -167,7 +167,8 @@ private: static int xsslcafile(XrdOucStream &Config); static int xsslverifydepth(XrdOucStream &Config); static int xsecretkey(XrdOucStream &Config); - + static int xheader2cgi(XrdOucStream &Config); + static XrdHttpSecXtractor *secxtractor; static XrdHttpExtHandler *exthandler; @@ -343,5 +344,8 @@ protected: /// Our role static kXR_int32 myRole; + /// Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption + static std::map< std::string, std::string > hdr2cgimap; + }; #endif diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index 3168d92a5ed..f79c1b69a66 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -192,6 +192,23 @@ int XrdHttpReq::parseLine(char *line, int len) { } else if (!strcmp(key, "Expect") && strstr(val, "100-continue")) { sendcontinue = true; + } else { + // Some headers need to be translated into "local" cgi info + std::map< std:: string, std:: string > ::iterator it = prot->hdr2cgimap.find(key); + if (it != prot->hdr2cgimap.end()) { + std:: string s; + s.assign(val, line+len-val); + trim(s); + + if (hdr2cgistr.length() > 0) { + hdr2cgistr.append("&"); + } + hdr2cgistr.append(key); + hdr2cgistr.append("="); + hdr2cgistr.append(it->second); + + + } } // We memorize the heaers also as a string @@ -767,7 +784,24 @@ int XrdHttpReq::ProcessHTTPReq() { } - + /// If we have to add extra header information, add it here. + if (!hdr2cgistr.empty()) { + const char *p = strchr(resourceplusopaque.c_str(), '?'); + if (p) { + resourceplusopaque.append("&"); + } else { + resourceplusopaque.append("?"); + } + + char *q = quote(hdr2cgistr.c_str()); + resourceplusopaque.append(q); + free(q); + + // Once we've appended the authorization to the full resource+opaque string, + // reset the authz to empty: this way, any operation that triggers repeated ProcessHTTPReq + // calls won't also trigger multiple copies of the authz. + hdr2cgistr = ""; + } // // Here we process the request locally @@ -2232,6 +2266,7 @@ void XrdHttpReq::reset() { host = ""; destination = ""; + hdr2cgistr = ""; iovP = 0; iovN = 0; diff --git a/src/XrdHttp/XrdHttpReq.hh b/src/XrdHttp/XrdHttpReq.hh index 91898076d47..68d051bf04f 100644 --- a/src/XrdHttp/XrdHttpReq.hh +++ b/src/XrdHttp/XrdHttpReq.hh @@ -195,7 +195,9 @@ public: /// The destination field specified in the req std::string destination; - + /// Additional opaque info that may come from the hdr2cgi directive + std::string hdr2cgistr; + // // Area for coordinating request and responses to/from the bridge // diff --git a/src/XrdHttp/XrdHttpUtils.cc b/src/XrdHttp/XrdHttpUtils.cc index 94b6f096607..bfcac258392 100644 --- a/src/XrdHttp/XrdHttpUtils.cc +++ b/src/XrdHttp/XrdHttpUtils.cc @@ -318,7 +318,7 @@ char *unquote(char *str) { // Quote a string and return a new one -char *quote(char *str) { +char *quote(const char *str) { int l = strlen(str); char *r = (char *) malloc(l*3 + 1); r[0] = '\0'; diff --git a/src/XrdHttp/XrdHttpUtils.hh b/src/XrdHttp/XrdHttpUtils.hh index cc15be94728..ab5acbcf80d 100644 --- a/src/XrdHttp/XrdHttpUtils.hh +++ b/src/XrdHttp/XrdHttpUtils.hh @@ -73,7 +73,7 @@ int compareHash( // Create a new quoted string -char *quote(char *str); +char *quote(const char *str); // unquote a string and return a new one char *unquote(char *str); From 0bce8e1c62a90dc8d5b7f2c19cf89fd501ac1363 Mon Sep 17 00:00:00 2001 From: Fabrizio Furano Date: Wed, 27 Sep 2017 16:59:40 +0200 Subject: [PATCH 2/4] Log when Authorization headers are appended to the opaque info. --- src/XrdHttp/XrdHttpReq.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index f79c1b69a66..4314e3ab518 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -795,6 +795,7 @@ int XrdHttpReq::ProcessHTTPReq() { char *q = quote(hdr2cgistr.c_str()); resourceplusopaque.append(q); + TRACEI(DEBUG, "Appended Authorization header to opaque info: '" << hdr2cgistr << "'"); free(q); // Once we've appended the authorization to the full resource+opaque string, From 92902e186883ea765e65771a8ce3552f7df272aa Mon Sep 17 00:00:00 2001 From: Fabrizio Furano Date: Tue, 3 Oct 2017 16:52:49 +0200 Subject: [PATCH 3/4] Little fixes --- src/XrdHttp/XrdHttpProtocol.cc | 2 ++ src/XrdHttp/XrdHttpReq.cc | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/XrdHttp/XrdHttpProtocol.cc b/src/XrdHttp/XrdHttpProtocol.cc index 1bb06ffc320..a2935c2ac37 100644 --- a/src/XrdHttp/XrdHttpProtocol.cc +++ b/src/XrdHttp/XrdHttpProtocol.cc @@ -94,6 +94,7 @@ BIO *XrdHttpProtocol::sslbio_err = 0; XrdCryptoFactory *XrdHttpProtocol::myCryptoFactory = 0; XrdHttpSecXtractor *XrdHttpProtocol::secxtractor = 0; XrdHttpExtHandler *XrdHttpProtocol::exthandler = 0; +std::map< std::string, std::string > XrdHttpProtocol::hdr2cgimap; static const unsigned char *s_server_session_id_context = (const unsigned char *) "XrdHTTPSessionCtx"; static int s_server_session_id_context_len = 18; @@ -868,6 +869,7 @@ int XrdHttpProtocol::Config(const char *ConfigFN, XrdOucEnv *myEnv) { else if TS_Xeq("staticredir", xstaticredir); else if TS_Xeq("staticpreload", xstaticpreload); else if TS_Xeq("listingdeny", xlistdeny); + else if TS_Xeq("header2cgi", xheader2cgi); else { eDest.Say("Config warning: ignoring unknown directive '", var, "'."); Config.Echo(); diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index 4314e3ab518..5d1ee5b3527 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -193,7 +193,7 @@ int XrdHttpReq::parseLine(char *line, int len) { } else if (!strcmp(key, "Expect") && strstr(val, "100-continue")) { sendcontinue = true; } else { - // Some headers need to be translated into "local" cgi info + // Some headers need to be translated into "local" cgi info. In theory they should already be quoted std::map< std:: string, std:: string > ::iterator it = prot->hdr2cgimap.find(key); if (it != prot->hdr2cgimap.end()) { std:: string s; @@ -203,9 +203,9 @@ int XrdHttpReq::parseLine(char *line, int len) { if (hdr2cgistr.length() > 0) { hdr2cgistr.append("&"); } - hdr2cgistr.append(key); - hdr2cgistr.append("="); hdr2cgistr.append(it->second); + hdr2cgistr.append("="); + hdr2cgistr.append(s); } From f2306b27c00cc71341c33e258849055b1f2218af Mon Sep 17 00:00:00 2001 From: Fabrizio Furano Date: Wed, 4 Oct 2017 10:50:53 +0200 Subject: [PATCH 4/4] Fix log line --- src/XrdHttp/XrdHttpReq.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index 5d1ee5b3527..108a354698b 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -795,7 +795,7 @@ int XrdHttpReq::ProcessHTTPReq() { char *q = quote(hdr2cgistr.c_str()); resourceplusopaque.append(q); - TRACEI(DEBUG, "Appended Authorization header to opaque info: '" << hdr2cgistr << "'"); + TRACEI(DEBUG, "Appended header fields to opaque info: '" << hdr2cgistr << "'"); free(q); // Once we've appended the authorization to the full resource+opaque string,