-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update to xrootd 5.7.1 (SOFTWARE-5975), update defer client auth patc…
…h (SOFTWARE-5968) git-svn-id: https://vdt.cs.wisc.edu/svn/native/redhat/branches/23-main@28020 4e558342-562e-0410-864c-e07659590f8c
- Loading branch information
1 parent
3af7745
commit eea2aa5
Showing
4 changed files
with
342 additions
and
42 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,333 @@ | ||
src/XrdHttp/XrdHttpProtocol.cc | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- | ||
src/XrdHttp/XrdHttpProtocol.hh | 14 ++++++++++++++ | ||
src/XrdTls/XrdTlsContext.cc | 25 +++++++++++++++++++++++++ | ||
src/XrdTls/XrdTlsContext.hh | 23 +++++++++++++++++++++++ | ||
4 files changed, 191 insertions(+), 3 deletions(-) | ||
|
||
diff --git c/src/XrdHttp/XrdHttpProtocol.cc w/src/XrdHttp/XrdHttpProtocol.cc | ||
index 4c9b4a733..76c97b0de 100644 | ||
--- c/src/XrdHttp/XrdHttpProtocol.cc | ||
+++ w/src/XrdHttp/XrdHttpProtocol.cc | ||
@@ -135,6 +135,8 @@ static const int hsmOn = 1; // Dual purpose but use a meaningful varname | ||
|
||
int httpsmode = hsmAuto; | ||
int tlsCache = XrdTlsContext::scOff; | ||
+XrdTlsContext::ClientAuthSetting tlsClientAuth = XrdTlsContext::ClientAuthSetting::kOn; | ||
+std::vector<std::string> tlsAuthRequestPrefixes; | ||
bool httpsspec = false; | ||
bool xrdctxVer = false; | ||
} | ||
@@ -448,6 +450,23 @@ static long BIO_XrdLink_ctrl(BIO *bio, int cmd, long num, void * ptr) | ||
case BIO_CTRL_FLUSH: | ||
ret = 1; | ||
break; | ||
+ case BIO_C_SET_NBIO: | ||
+ { | ||
+ auto link = static_cast<XrdLink*>(BIO_get_data(bio)); | ||
+ if (link) { | ||
+ struct timeval tv; | ||
+ tv.tv_sec = 10; | ||
+ tv.tv_usec = 0; | ||
+ if (num) { | ||
+ tv.tv_sec = 0; | ||
+ tv.tv_usec = 1; | ||
+ } | ||
+ setsockopt(link->FDnum(), SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); | ||
+ setsockopt(link->FDnum(), SOL_SOCKET, SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); | ||
+ } | ||
+ ret = 1; | ||
+ break; | ||
+ } | ||
default: | ||
ret = 0; | ||
break; | ||
@@ -506,7 +525,11 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here | ||
if (!ssl) { | ||
sbio = CreateBIO(Link); | ||
BIO_set_nbio(sbio, 1); | ||
+ xrdctx->SetTlsClientAuth(tlsClientAuth); | ||
ssl = (SSL*)xrdctx->Session(); | ||
+ postheaderauth = false; | ||
+ postheaderwait = false; | ||
+ postheaderauthdone = false; | ||
} | ||
|
||
if (!ssl) { | ||
@@ -553,7 +576,7 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here | ||
strcpy(SecEntity.prot, "https"); | ||
|
||
// Get the voms string and auth information | ||
- if (HandleAuthentication(Link)) { | ||
+ if (tlsClientAuth == XrdTlsContext::ClientAuthSetting::kOn && HandleAuthentication(Link)) { | ||
SSL_free(ssl); | ||
ssl = 0; | ||
return -1; | ||
@@ -585,7 +608,7 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here | ||
|
||
} else | ||
CurrentReq.reqstate++; | ||
- } else if (!DoneSetInfo && !CurrentReq.userAgent().empty()) { // DoingLogin is true, meaning the login finished. | ||
+ } else if (!DoneSetInfo && !postheaderwait && !postheaderauth && !CurrentReq.userAgent().empty()) { // DoingLogin is true, meaning the login finished. | ||
std::string mon_info = "monitor info " + CurrentReq.userAgent(); | ||
DoneSetInfo = true; | ||
if (mon_info.size() >= 1024) { | ||
@@ -605,7 +628,7 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here | ||
} | ||
return 0; | ||
} | ||
- } else { | ||
+ } else if (!postheaderwait) { | ||
DoingLogin = false; | ||
} | ||
|
||
@@ -635,6 +658,21 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here | ||
TRACE(DEBUG, " Parsing of first line failed with " << result); | ||
return -1; | ||
} | ||
+ | ||
+#if OPENSSL_VERSION_NUMBER >= 0x10100010L | ||
+ // We permit TLS client auth to be deferred until after the request path is sent. | ||
+ // If this is a path requiring client auth, then do that now. | ||
+ if (!postheaderauthdone && tlsClientAuth == XrdTlsContext::ClientAuthSetting::kDefer) | ||
+ {for (const auto &prefix : tlsAuthRequestPrefixes) { | ||
+ {if (!strncmp(prefix.c_str(), CurrentReq.resource.c_str(), prefix.length())) | ||
+ {postheaderwait = true; | ||
+ DoingLogin = true; | ||
+ break; | ||
+ } | ||
+ } | ||
+ } | ||
+ } | ||
+#endif | ||
} else { | ||
int result = CurrentReq.parseLine((char *) tmpline.c_str(), rc); | ||
if(result < 0) { | ||
@@ -669,6 +707,50 @@ int XrdHttpProtocol::Process(XrdLink *lp) // We ignore the argument here | ||
|
||
} | ||
|
||
+ | ||
+#if OPENSSL_VERSION_NUMBER >= 0x10100010L | ||
+ if (postheaderwait) { | ||
+ postheaderwait = false; | ||
+ if (SSL_verify_client_post_handshake(ssl) != 1) { | ||
+ // This is hit if the remote client doesn't support the post-handshake authentication | ||
+ // (curl, Mac OSX) or TLS v1.3 (RHEL7). | ||
+ TRACEI(ALL, "Unable to request client X.509 authentication"); | ||
+ ERR_print_errors(sslbio_err); | ||
+ } else { | ||
+ // We must invoke an empty write to trigger the authentication request in the TLS layer. | ||
+ size_t write_size; | ||
+ auto res = SSL_write_ex(ssl, nullptr, 0, &write_size); | ||
+ if (res <= 0) { | ||
+ TRACEI(DEBUG, " SSL post-handshake auth failed; err:" << SSL_get_error(ssl, res)); | ||
+ ERR_print_errors(sslbio_err); | ||
+ SendSimpleResp(500, nullptr, nullptr, "Failed post-handshake authentication", 0, false); | ||
+ return -1; | ||
+ } else { | ||
+ TRACEI(DEBUG, " SSL post-handshake auth finished successfully"); | ||
+ postheaderauth = true; | ||
+ return 1; | ||
+ } | ||
+ } | ||
+ } | ||
+ if (postheaderauth) { | ||
+ postheaderauth = false; | ||
+ postheaderauthdone = true; | ||
+ size_t readbytes; | ||
+ TRACEI(REQ, "Reading out response to post-handshake authentication"); | ||
+ BIO_set_nbio(sbio, 1); | ||
+ auto res = SSL_peek_ex(ssl, nullptr, 0, &readbytes); | ||
+ if ((res <= 0) && SSL_get_error(ssl, res) != SSL_ERROR_WANT_READ) { | ||
+ SendSimpleResp(500, nullptr, nullptr, "Failed to process authentication frames", 0, false); | ||
+ return -1; | ||
+ } | ||
+ BIO_set_nbio(sbio, 0); | ||
+ if (HandleAuthentication(Link)) { | ||
+ SendSimpleResp(500, nullptr, nullptr, "Failed to extract authentication information from handshake", 0, false); | ||
+ return -1; | ||
+ } | ||
+ } | ||
+#endif | ||
+ | ||
// If we are in self-redirect mode, then let's do it | ||
// Do selfredirect only with 'simple' requests, otherwise poor clients may misbehave | ||
if (ishttps && ssldone && selfhttps2http && | ||
@@ -1077,6 +1159,8 @@ int XrdHttpProtocol::Config(const char *ConfigFN, XrdOucEnv *myEnv) { | ||
else if TS_Xeq("httpsmode", xhttpsmode); | ||
else if TS_Xeq("tlsreuse", xtlsreuse); | ||
else if TS_Xeq("auth", xauth); | ||
+ else if TS_Xeq("tlsclientauth", xtlsclientauth); | ||
+ else if TS_Xeq("tlsrequiredprefix", xtlsrequiredprefix); | ||
else { | ||
eDest.Say("Config warning: ignoring unknown directive '", var, "'."); | ||
Config.Echo(); | ||
@@ -1944,6 +2028,8 @@ void XrdHttpProtocol::Reset() { | ||
|
||
DoingLogin = false; | ||
DoneSetInfo = false; | ||
+ postheaderauth = false; | ||
+ postheaderwait = false; | ||
|
||
ResumeBytes = 0; | ||
Resume = 0; | ||
@@ -2834,6 +2920,46 @@ int XrdHttpProtocol::xtlsreuse(XrdOucStream & Config) { | ||
return 1; | ||
} | ||
|
||
+int XrdHttpProtocol::xtlsclientauth(XrdOucStream &Config) { | ||
+ auto val = Config.GetWord(); | ||
+ if (!val || !val[0]) | ||
+ {eDest.Emsg("Config", "tlsclientauth argument not specified"); return 1;} | ||
+ | ||
+ if (!strcmp(val, "off")) | ||
+ {tlsClientAuth = XrdTlsContext::ClientAuthSetting::kOff; | ||
+ return 0; | ||
+ } | ||
+ if (!strcmp(val, "on")) | ||
+ {tlsClientAuth = XrdTlsContext::ClientAuthSetting::kOn; | ||
+ return 0; | ||
+ } | ||
+ if (!strcmp(val, "defer")) | ||
+ { | ||
+#if OPENSSL_VERSION_NUMBER >= 0x10100010L | ||
+ tlsClientAuth = XrdTlsContext::ClientAuthSetting::kDefer; | ||
+ return 0; | ||
+#else | ||
+ eDest.Emsg("config", "http.tlsclientauth defer is not supported on this platform"); | ||
+ return 1; | ||
+#endif | ||
+ } | ||
+ | ||
+ eDest.Emsg("config", "invalid tlsclientauth parameter -", val); | ||
+ return 1; | ||
+} | ||
+ | ||
+int XrdHttpProtocol::xtlsrequiredprefix(XrdOucStream &Config) { | ||
+ auto val = Config.GetWord(); | ||
+ if (!val || !val[0]) | ||
+ {eDest.Emsg("Config", "tlsrequiredprefix argument not specified"); return 1;} | ||
+ | ||
+ if (val[0] != '/') | ||
+ {eDest.Emsg("Config", "http.tlsrequiredprefix argument must be an absolute path"); return 1;} | ||
+ | ||
+ tlsAuthRequestPrefixes.push_back(val); | ||
+ return 0; | ||
+} | ||
+ | ||
int XrdHttpProtocol::xauth(XrdOucStream &Config) { | ||
char *val = Config.GetWord(); | ||
if(val) { | ||
diff --git c/src/XrdHttp/XrdHttpProtocol.hh w/src/XrdHttp/XrdHttpProtocol.hh | ||
index ece9f9cb8..78fd24b45 100644 | ||
--- c/src/XrdHttp/XrdHttpProtocol.hh | ||
+++ w/src/XrdHttp/XrdHttpProtocol.hh | ||
@@ -223,6 +223,8 @@ class XrdHttpProtocol : public XrdProtocol { | ||
static int xhttpsmode(XrdOucStream &Config); | ||
static int xtlsreuse(XrdOucStream &Config); | ||
static int xauth(XrdOucStream &Config); | ||
+ static int xtlsclientauth(XrdOucStream &Config); | ||
+ static int xtlsrequiredprefix(XrdOucStream &Config); | ||
|
||
static bool isRequiredXtractor; // If true treat secxtractor errors as fatal | ||
static XrdHttpSecXtractor *secxtractor; | ||
@@ -327,6 +329,18 @@ class XrdHttpProtocol : public XrdProtocol { | ||
/// Flag to tell if the https handshake has finished, in the case of an https | ||
/// connection being established | ||
bool ssldone; | ||
+ | ||
+ /// Flag indicating we should send a request for client TLS authentication | ||
+ /// after the status line has finished processing. | ||
+ bool postheaderwait; | ||
+ | ||
+ /// Flag indicating we should wait for a response to the post-header authentication | ||
+ /// request (after status line have been processed). | ||
+ bool postheaderauth; | ||
+ | ||
+ /// Flag indicating the deferred auth has completed | ||
+ bool postheaderauthdone; | ||
+ | ||
static XrdCryptoFactory *myCryptoFactory; | ||
|
||
protected: | ||
diff --git c/src/XrdTls/XrdTlsContext.cc w/src/XrdTls/XrdTlsContext.cc | ||
index adf569cb1..c57f8ccfd 100644 | ||
--- c/src/XrdTls/XrdTlsContext.cc | ||
+++ w/src/XrdTls/XrdTlsContext.cc | ||
@@ -70,6 +70,7 @@ struct XrdTlsContextImpl | ||
time_t lastCertModTime = 0; | ||
int sessionCacheOpts = -1; | ||
std::string sessionCacheId; | ||
+ uint64_t opts{0}; | ||
}; | ||
|
||
/******************************************************************************/ | ||
@@ -591,6 +592,8 @@ XrdTlsContext::XrdTlsContext(const char *cert, const char *key, | ||
SSL_CTX **ctxLoc; | ||
} ctx_tracker(&pImpl->ctx); | ||
|
||
+ pImpl->opts = opts; | ||
+ | ||
static const uint64_t sslOpts = SSL_OP_ALL | ||
| SSL_OP_NO_SSLv2 | ||
| SSL_OP_NO_SSLv3 | ||
@@ -1136,3 +1139,25 @@ bool XrdTlsContext::newHostCertificateDetected() { | ||
} | ||
return false; | ||
} | ||
+ | ||
+void XrdTlsContext::SetTlsClientAuth(ClientAuthSetting setting) { | ||
+ | ||
+ bool LogVF = (pImpl->opts & logVF) != 0; | ||
+ switch (setting) { | ||
+ case kOn: | ||
+ SSL_CTX_set_verify(pImpl->ctx, SSL_VERIFY_PEER, (LogVF ? VerCB : 0)); | ||
+ break; | ||
+ case kOff: | ||
+ SSL_CTX_set_verify(pImpl->ctx, SSL_VERIFY_NONE, 0); | ||
+ break; | ||
+ case kDefer: | ||
+#if OPENSSL_VERSION_NUMBER < 0x10100010L | ||
+ // Post-handhsake auth was added in OpenSSL version 1.1.1; for older version, | ||
+ // simply switch to always request client certificates. | ||
+ SSL_CTX_set_verify(pImpl->ctx, SSL_VERIFY_PEER, (LogVF ? VerCB : 0)); | ||
+#else | ||
+ SSL_CTX_set_verify(pImpl->ctx, SSL_VERIFY_PEER | SSL_VERIFY_POST_HANDSHAKE, (LogVF ? VerCB : 0)); | ||
+#endif | ||
+ break; | ||
+ } | ||
+} | ||
diff --git c/src/XrdTls/XrdTlsContext.hh w/src/XrdTls/XrdTlsContext.hh | ||
index e6b61b7b8..d86de9d46 100644 | ||
--- c/src/XrdTls/XrdTlsContext.hh | ||
+++ w/src/XrdTls/XrdTlsContext.hh | ||
@@ -173,6 +173,29 @@ void SetDefaultCiphers(const char *ciphers); | ||
|
||
bool SetCrlRefresh(int refsec=-1); | ||
|
||
+enum ClientAuthSetting { | ||
+ kOn, | ||
+ kOff, | ||
+ kDefer | ||
+}; | ||
+ | ||
+//------------------------------------------------------------------------ | ||
+//! Indicate how the server should handle TLS client authentication. | ||
+//! | ||
+//! @param setting kOn: All clients will be asked to send a TLS client | ||
+//! certificate | ||
+//! kOff: No clients will be asked to send a TLS client | ||
+//! certificate; | ||
+//! kDefer: Only ask for a TLS client certificate | ||
+//! explicitly post-authentication. | ||
+//! | ||
+//! Note the TLS connection will not fail if the client is asked for a cert | ||
+//! but none are provided. | ||
+//! | ||
+//------------------------------------------------------------------------ | ||
+ | ||
+ void SetTlsClientAuth(ClientAuthSetting setting); | ||
+ | ||
//------------------------------------------------------------------------ | ||
//! Check if certificates are being verified. | ||
//! |
Oops, something went wrong.