Skip to content

Commit

Permalink
Update to xrootd 5.7.1 (SOFTWARE-5975), update defer client auth patc…
Browse files Browse the repository at this point in the history
…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
matyasselmeci committed Sep 6, 2024
1 parent 3af7745 commit eea2aa5
Show file tree
Hide file tree
Showing 4 changed files with 342 additions and 42 deletions.
34 changes: 0 additions & 34 deletions xrootd/osg/2303-file-pointer-leak.patch

This file was deleted.

333 changes: 333 additions & 0 deletions xrootd/osg/bbockelm-defer_clientauth_v5_v2.patch
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.
//!
Loading

0 comments on commit eea2aa5

Please sign in to comment.