diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c4c4c892a70..0432cfd48fa 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -23,7 +23,6 @@ include( XrdCrypto )
include( XrdSec )
include( XrdXml )
include( XrdHeaders )
-#include( XrdSecztn )
if( BUILD_CRYPTO )
include( XrdSecgsi )
@@ -75,6 +74,7 @@ if( NOT XRDCL_ONLY )
if( BUILD_SCITOKENS )
include( XrdSciTokens )
+ include( XrdSecztn )
endif()
endif()
diff --git a/src/XrdSecztn.cmake b/src/XrdSecztn.cmake
new file mode 100644
index 00000000000..89bed38adb3
--- /dev/null
+++ b/src/XrdSecztn.cmake
@@ -0,0 +1,30 @@
+#include_directories( ${KERBEROS5_INCLUDE_DIR} )
+include( XRootDCommon )
+
+#-------------------------------------------------------------------------------
+# The XrdSecztn module
+#-------------------------------------------------------------------------------
+set( LIB_XRD_SEC_ZTN XrdSecztn-${PLUGIN_VERSION} )
+
+add_library(
+ ${LIB_XRD_SEC_ZTN}
+ MODULE
+ XrdSecztn/XrdSecProtocolztn.cc )
+
+target_link_libraries(
+ ${LIB_XRD_SEC_ZTN}
+# XrdUtils
+ )
+
+set_target_properties(
+ ${LIB_XRD_SEC_ZTN}
+ PROPERTIES
+ INTERFACE_LINK_LIBRARIES ""
+ LINK_INTERFACE_LIBRARIES "" )
+
+#-------------------------------------------------------------------------------
+# Install
+#-------------------------------------------------------------------------------
+install(
+ TARGETS ${LIB_XRD_SEC_ZTN}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
diff --git a/src/XrdSecztn/XrdSecProtocolztn.cc b/src/XrdSecztn/XrdSecProtocolztn.cc
new file mode 100644
index 00000000000..99b36d84edb
--- /dev/null
+++ b/src/XrdSecztn/XrdSecProtocolztn.cc
@@ -0,0 +1,845 @@
+/******************************************************************************/
+/* */
+/* X r d S e c P r o t o c o l z t n . c c */
+/* */
+/* (c) 2020 by the Board of Trustees of the Leland Stanford, Jr., University */
+/* All Rights Reserved */
+/* Produced by Andrew Hanushevsky for Stanford University under contract */
+/* DE-AC02-76-SFO0515 with the Department of Energy */
+/* */
+/* This file is part of the XRootD software suite. */
+/* */
+/* XRootD is free software: you can redistribute it and/or modify it under */
+/* the terms of the GNU Lesser General Public License as published by the */
+/* Free Software Foundation, either version 3 of the License, or (at your */
+/* option) any later version. */
+/* */
+/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
+/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
+/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
+/* License for more details. */
+/* */
+/* You should have received a copy of the GNU Lesser General Public License */
+/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
+/* COPYING (GPL license). If not, see . */
+/* */
+/* The copyright holder's institutional names and contributor's names may not */
+/* be used to endorse or promote products derived from this software without */
+/* specific prior written permission of the institution or contributor. */
+/******************************************************************************/
+
+#define __STDC_FORMAT_MACROS 1
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "XrdVersion.hh"
+
+#include "XrdNet/XrdNetAddrInfo.hh"
+#include "XrdOuc/XrdOucErrInfo.hh"
+#include "XrdOuc/XrdOucPinLoader.hh"
+#include "XrdOuc/XrdOucString.hh"
+#include "XrdOuc/XrdOucTokenizer.hh"
+#include "XrdSciTokens/XrdSciTokensHelper.hh"
+#include "XrdSys/XrdSysE2T.hh"
+#include "XrdSys/XrdSysHeaders.hh"
+#include "XrdSec/XrdSecInterface.hh"
+
+#ifndef EAUTH
+#define EAUTH EBADE
+#endif
+/******************************************************************************/
+/* V e r s i o n I n f o r m a t i o n */
+/******************************************************************************/
+
+XrdVERSIONINFO(XrdSecProtocolztnObject,secztn);
+
+/******************************************************************************/
+/* L o c a l F u n c t i o n s */
+/******************************************************************************/
+/******************************************************************************/
+/* F a t a l */
+/******************************************************************************/
+
+namespace
+{
+XrdSecCredentials *Fatal(XrdOucErrInfo *erp, const char *eMsg, int rc,
+ bool hdr=true)
+{
+ if (!erp) std::cerr <<"Secztn: " <setErrInfo(rc, eVec, 2);
+ }
+ return 0;
+}
+}
+
+/******************************************************************************/
+/* g e t L i n k a g e */
+/******************************************************************************/
+
+namespace
+{
+ XrdSciTokensHelper **sth_Linkage = 0;
+ char *sth_piName = 0;
+
+bool getLinkage(XrdOucErrInfo *erp, const char *piName)
+{
+ char eMsgBuff[2048];
+ XrdVersionInfo *myVer = &XrdVERSIONINFOVAR(XrdSecProtocolztnObject);
+ XrdOucPinLoader myLib(eMsgBuff, sizeof(eMsgBuff), myVer,
+ "ztn.tokenlib", piName);
+
+// Get the address of the pointer to the helper we need
+//
+ sth_Linkage = (XrdSciTokensHelper **)(myLib.Resolve("SciTokensHelper"));
+
+// If we succeeded, record the name of the plugin and return success
+//
+ if (sth_Linkage)
+ {sth_piName = strdup(piName);
+ return true;
+ }
+
+// We failed to find this for one reason or another
+//
+ erp->setErrInfo(ESRCH, eMsgBuff);
+ return false;
+ }
+}
+
+/******************************************************************************/
+/* v e c V a r s */
+/******************************************************************************/
+
+namespace
+{
+bool vecVars(const char *vSpec, std::vector &Vec)
+{
+ XrdOucString Val;
+ char *comma, *varP;
+ int n;
+
+// Make sure we have somthing here and it has no embedded spaces
+//
+ if (!(n = strlen(vSpec)) || index(vSpec, ' ')) return false;
+
+// Copy envar list as we need to modify it
+//
+ varP = (char *)alloca(n+1);
+ strcpy(varP, vSpec);
+
+// Copy out the envar list
+//
+ do {comma = index(varP, ',');
+ if (comma) *comma = 0;
+ if (*varP)
+ {if (*varP != '/' || index(varP, '%') == rindex(varP, '%'))
+ {Val = varP;
+ Vec.push_back(Val);
+ }
+ }
+ varP = comma + 1;
+ } while(comma);
+
+// All done
+//
+ return Vec.size() != 0;
+}
+}
+
+/******************************************************************************/
+/* L o c a l D e f i n i t i o n s */
+/******************************************************************************/
+
+namespace
+{
+int MaxTokSize = 4096;
+
+// Option flags
+//
+static const uint64_t srvVNum = 0x00000000000000ffULL;
+static const uint64_t useFirst = 0x0000000000000100ULL;
+static const uint64_t useLast = 0x0000000000000200ULL;
+static const uint64_t srvRTOK = 0x0000000000000800ULL;
+}
+
+/******************************************************************************/
+/* X r d S e c P r o t o c o l z t n C l a s s */
+/******************************************************************************/
+
+class XrdSecProtocolztn : public XrdSecProtocol
+{
+public:
+
+ int Authenticate (XrdSecCredentials *cred,
+ XrdSecParameters **parms,
+ XrdOucErrInfo *einfo=0);
+
+ void Delete() {delete this;}
+
+ XrdSecCredentials *getCredentials(XrdSecParameters *parms,
+ XrdOucErrInfo *einfo=0);
+
+ bool needTLS() {return true;}
+
+// Client-side constructor
+//
+ XrdSecProtocolztn(const char *parms, XrdOucErrInfo *erp, bool &aOK);
+
+
+// Server-side constructor
+//
+ XrdSecProtocolztn(const char *hname, XrdNetAddrInfo &endPoint,
+ XrdSciTokensHelper *sthp)
+ : XrdSecProtocol("ztn"), sthP(sthp), tokName(""),
+ maxTSize(MaxTokSize), cont(false), rtGet(false)
+ {Entity.host = strdup(hname);
+ Entity.name = (char *)"anon";
+ Entity.addrInfo = &endPoint;
+ }
+
+ ~XrdSecProtocolztn() {if (Entity.host) free(Entity.host);
+ if (Entity.name) free(Entity.name);
+ } // via Delete()
+private:
+
+struct TokenHdr
+ {char id[4];
+ char opr;
+ char num;
+
+ static const char SndAI = 'S';
+ static const char IsTkn = 'T';
+
+ void Fill(char opc, int n=1) {strcpy(id, "ztn"); opr = opc; num = n;}
+ };
+
+struct TokenResp
+ {TokenHdr hdr;
+ uint16_t len;
+ char tkn[1]; // Sized to actual token length
+ };
+
+XrdSecCredentials *findToken(XrdOucErrInfo *erp,
+ std::vector &Vec, bool &isbad);
+XrdSecCredentials *getToken(XrdOucErrInfo *erp, XrdSecParameters *parms);
+XrdSecCredentials *readFail(XrdOucErrInfo *erp, const char *path, int rc);
+XrdSecCredentials *readToken(XrdOucErrInfo *erp, const char *path, bool &isbad);
+XrdSecCredentials *retToken(XrdOucErrInfo *erp, const char *tkn, int tsz);
+int SendAI(XrdOucErrInfo *erp, XrdSecParameters **parms);
+const char *Strip(const char *bTok, int &sz);
+
+std::vector srvrLoc;
+
+XrdSciTokensHelper *sthP;
+const char *tokName;
+uint64_t ztnInfo;
+int maxTSize;
+bool cont;
+bool rtGet;
+};
+
+/******************************************************************************/
+/* C l i e n t O r i e n t e d F u n c t i o n s */
+/******************************************************************************/
+/******************************************************************************/
+/* C o n s t r u c t o r */
+/******************************************************************************/
+
+XrdSecProtocolztn::XrdSecProtocolztn(const char *parms, XrdOucErrInfo *erp,
+ bool &aOK)
+ : XrdSecProtocol("ztn"), sthP(0),
+ tokName(""), ztnInfo(0), maxTSize(0),
+ cont(false), rtGet(false)
+{
+ char *endP;
+
+// Assume we will fail
+//
+ aOK = false;
+
+// If there are no parameters then fail as the server must supply them
+//
+ if (!parms || !(*parms))
+ {Fatal(erp, "Client parameters not specified.", EINVAL);
+ return;
+ }
+
+// Server supplied parms: ::[[,[,...]]]
+
+// The first parameter is the options and version number.
+//
+ ztnInfo = strtoll(parms, &endP, 10);
+ if (*endP != ':')
+ {Fatal(erp, "Malformed client parameters.", EINVAL);
+ return;
+ }
+ parms = endP+1;
+
+// The second parameter is the maximum token size
+//
+ maxTSize = strtol(parms, &endP, 10);
+ if (maxTSize <= 0 || *endP != ':')
+ {Fatal(erp, "Invalid or missing maxtsz parameter.", EINVAL);
+ return;
+ }
+ endP++;
+
+// Check if we have an envar list. If none ignore search settings as we do
+// not yet support -rt (i.e. realtime token generation).
+//
+ if (!(*endP) || !vecVars(endP, srvrLoc)) ztnInfo &= ~(useFirst | useLast);
+
+// All done here
+//
+ aOK = true;
+}
+
+/******************************************************************************/
+/* Private: f i n d T o k e n */
+/******************************************************************************/
+
+XrdSecCredentials *XrdSecProtocolztn::findToken(XrdOucErrInfo *erp,
+ std::vector &Vec,
+ bool &isbad)
+{
+ XrdSecCredentials *resp;
+ const char *aTok, *bTok;
+ int sz;
+
+// Look through all of the possible envars
+//
+ for (int i = 0; i < (int)Vec.size(); i++)
+ {tokName = Vec[i].c_str();
+
+ if (Vec[i].beginswith('/') == 1)
+ {char tokPath[MAXPATHLEN+8];
+ snprintf(tokPath, sizeof(tokPath), tokName,
+ Vec[i].length(), int(geteuid()));
+ resp = readToken(erp, tokPath, isbad);
+ if (resp || isbad) return resp;
+ continue;
+ }
+
+ if (!(aTok = getenv(Vec[i].c_str())) || !*(aTok)) continue;
+
+ if (Vec[i].endswith("_DIR"))
+ {char tokPath[MAXPATHLEN+8];
+ snprintf(tokPath,sizeof(tokPath),"%s/bt_u%d",aTok,int(geteuid()));
+ resp = readToken(erp, tokPath, isbad);
+ if (resp || isbad) return resp;
+ continue;
+ }
+
+ if (Vec[i].endswith("_FILE"))
+ {if ((resp = readToken(erp, aTok, isbad)) || isbad) return resp;
+ continue;
+ }
+
+ if ((bTok = Strip(aTok, sz))) return retToken(erp, bTok, sz);
+ }
+
+// Nothing found
+//
+ isbad = false;
+ return 0;
+}
+
+/******************************************************************************/
+/* g e t C r e d e n t i a l s */
+/******************************************************************************/
+
+XrdSecCredentials *XrdSecProtocolztn::getCredentials(XrdSecParameters *parms,
+ XrdOucErrInfo *error)
+{
+ static const char *dfltLoc[] = {"BEARER_TOKEN", "BEARER_TOKEN_FILE",
+ "XDG_RUNTIME_DIR", "/tmp/bt_u%d"};
+ static const char **dfltLocEnd = dfltLoc + sizeof(dfltLoc)/sizeof(char*);
+ static std::vector dfltVec(dfltLoc, dfltLocEnd);
+
+ XrdSecCredentials *resp;
+ bool isbad;
+
+// If this is a continuation, then handle as such
+//
+ if (cont) return getToken(error, parms);
+
+// Check if we need to use server supplied envars first
+//
+ if (ztnInfo & useFirst)
+ {resp = findToken(error, srvrLoc, isbad);
+ if (resp || isbad) return resp;
+ }
+
+// Handle the default search if allowed
+//
+ if (!((ztnInfo & useFirst) && (ztnInfo & useLast)))
+ {resp = findToken(error, dfltVec, isbad);
+ if (resp || isbad) return resp;
+ }
+
+// Check if we need to use server supplied envars first
+//
+ if (!(ztnInfo & useFirst) && (ztnInfo & useLast))
+ {resp = findToken(error, srvrLoc, isbad);
+ if (resp || isbad) return resp;
+ }
+
+// We do not have a envar value then ask the server for a list of
+// token issuers so we can get one, if allowed. Otherwise, it's an error.
+//
+ if (rtGet)
+ {TokenHdr *tHdr = (TokenHdr *)malloc(sizeof(TokenHdr));
+ tHdr->Fill(TokenHdr::SndAI, 0);
+ cont = true;
+ return new XrdSecCredentials((char *)tHdr, sizeof(TokenHdr));
+ }
+ Fatal(error, "No token found; runtime fetch disallowed.", ENOPROTOOPT);
+ return 0;
+}
+
+/******************************************************************************/
+/* Private: g e t T o k e n */
+/******************************************************************************/
+
+XrdSecCredentials *XrdSecProtocolztn::getToken(XrdOucErrInfo *erp,
+ XrdSecParameters *parms)
+{
+// We currently do not support dynamic token creation
+//
+ return Fatal(erp, "Realtime token creation not supported.", ENOTSUP);
+}
+
+/******************************************************************************/
+/* Private: r e a d F a i l */
+/******************************************************************************/
+
+XrdSecCredentials *XrdSecProtocolztn::readFail(XrdOucErrInfo *erp,
+ const char *path, int rc)
+{
+ const char *mVec[7];
+ int k = 6;
+
+ mVec[0] = "Secztn: Unable to find token via ";
+ mVec[1] = tokName;
+ mVec[2] = "=";
+ mVec[3] = path;
+ mVec[4] = "; ";
+ mVec[5] = XrdSysE2T(rc);
+ if (rc == EPERM) mVec[k++] = " because of excessive permissions";
+
+
+ if (erp) erp->setErrInfo(rc, mVec, k);
+ else {for (int k = 0; k < 6; k++) std::cerr < maxTSize) return readFail(erp, path, EMSGSIZE);
+ buff = (char *)alloca(Stat.st_size);
+
+// Open the token file
+//
+ if ((tokFD = open(path, O_RDONLY)) < 0)
+ return readFail(erp, path, errno);
+
+// Read in the token
+//
+ if ((rdLen = read(tokFD, buff, Stat.st_size)) != Stat.st_size)
+ {int rc = (rdLen < 0 ? errno : EIO);
+ close(tokFD);
+ return readFail(erp, path, rc);
+ }
+ close(tokFD);
+
+// Strip the token
+//
+ if (!(bTok = Strip(buff, sz)))
+ {isbad = false;
+ return 0;
+ }
+
+// Return response
+//
+ return retToken(erp, buff, Stat.st_size);
+}
+
+/******************************************************************************/
+/* Private: r e t T o k e n */
+/******************************************************************************/
+
+XrdSecCredentials *XrdSecProtocolztn::retToken(XrdOucErrInfo *erp,
+ const char *tkn, int tsz)
+{
+ TokenResp *tResp;
+ int rspLen = sizeof(TokenResp) + tsz + 1;
+
+// Make sure token is not too big
+//
+ if (tsz >= maxTSize) return Fatal(erp, "Token is too big", EMSGSIZE);
+
+// Get sufficient storage to assemble the full response
+//
+ tResp = (TokenResp *)malloc(rspLen);
+ if (!tResp)
+ {Fatal(erp, "Insufficient memory.", ENOMEM);
+ return 0;
+ }
+
+// Fill out the response
+//
+ tResp->hdr.Fill(TokenHdr::IsTkn);
+ tResp->len = htons(tsz+1);
+ memcpy(tResp->tkn, tkn, tsz);
+ *((tResp->tkn)+tsz) = 0;
+
+// Now return it
+//
+ return new XrdSecCredentials((char *)tResp, rspLen);
+}
+
+/******************************************************************************/
+/* Private: S t r i p */
+/******************************************************************************/
+
+const char *XrdSecProtocolztn::Strip(const char *bTok, int &sz)
+{
+ int j, k, n = strlen(bTok);
+
+// Make sure we have at least one character here
+//
+ if (!n) return 0;
+
+// Find first non-whitespace character
+//
+ for (j = 0; j < n; j++) if (!isspace(static_cast(bTok[j]))) break;
+
+// Make sure we have at least one character
+//
+ if (j >= n) return 0;
+
+// Find last non-whitespace character
+//
+ for (k = n-1; k > j; k--) if (!isspace(static_cast(bTok[k]))) break;
+
+// Compute length and allocate enough storage to copy the token
+//
+ if (k <= j) return 0;
+
+// Compute length and return pointer to the token
+//
+ sz = k - j + 1;
+ return bTok + j;
+}
+
+/******************************************************************************/
+/* S e r v e r O r i e n t e d M e t h o d s */
+/******************************************************************************/
+/******************************************************************************/
+/* A u t h e n t i c a t e */
+/******************************************************************************/
+
+int XrdSecProtocolztn::Authenticate(XrdSecCredentials *cred,
+ XrdSecParameters **parms,
+ XrdOucErrInfo *erp)
+{
+ TokenResp *tResp;
+
+// Check if we have any credentials or if no credentials really needed.
+// In either case, use host name as client name
+//
+ if (cred->size < (int)sizeof(TokenHdr) || !cred->buffer)
+ {Fatal(erp, "Invalid ztn credentials", EINVAL, false);
+ return -1;
+ }
+ tResp = (TokenResp *)cred->buffer;
+
+// Check if this is our protocol
+//
+ if (strcmp(tResp->hdr.id, "ztn"))
+ {char msg[256];
+ snprintf(msg, sizeof(msg),
+ "Authentication protocol id mismatch ('ztn' != '%.4s').",
+ tResp->hdr.id);
+ Fatal(erp, msg, EINVAL, false);
+ return -1;
+ }
+
+// Check if caller wants the list of authorized issuers
+//
+ if (tResp->hdr.opr == TokenHdr::SndAI) return SendAI(erp, parms);
+
+// If this is not a token response then this is an error
+//
+ if (tResp->hdr.opr != TokenHdr::IsTkn)
+ {Fatal(erp, "Invalid ztn response code", EINVAL, false);
+ return -1;
+ }
+
+// Make sure the response is consistent
+//
+ int tLen = ntohs(tResp->len);
+ if (tResp->hdr.num != 1 || tLen < 1
+ || (int(sizeof(TokenResp) + tLen)) > cred->size
+ || !(tResp->tkn[0]) || *(tResp->tkn+(tLen-1)))
+ {Fatal(erp, "'ztn' token response malformed", EINVAL, false);
+ return -1;
+ }
+
+// Validate the token
+//
+ std::string msgRC;
+ if (Entity.name) {free(Entity.name); Entity.name = 0;}
+ if (sthP->Validate(tResp->tkn, msgRC, &Entity))
+ {if (!Entity.name) Entity.name = strdup("anon");
+ return 0;
+ }
+
+// Validation failed, generate message and return failure
+//
+ msgRC.insert(0, "ztn validation failed; ");
+ Fatal(erp, msgRC.c_str(), EAUTH, false);
+ return -1;
+}
+
+/******************************************************************************/
+/* Private: S e n d A I */
+/******************************************************************************/
+
+int XrdSecProtocolztn::SendAI(XrdOucErrInfo *erp, XrdSecParameters **parms)
+{
+ Fatal(erp, "Authorized issuer request not supported", ENOTSUP);
+ return -1;
+}
+
+/******************************************************************************/
+/* I n i t i a l i z a t i o n F u n c t i o n s */
+/******************************************************************************/
+/******************************************************************************/
+/* X r d S e c P r o t o c o l z t n I n i t */
+/******************************************************************************/
+
+extern "C"
+{
+char *XrdSecProtocolztnInit(const char mode,
+ const char *parms,
+ XrdOucErrInfo *erp)
+{
+ static char nilstr = 0;
+ XrdOucString accPlugin("libXrdAccSciTokens.so");
+
+// This only makes sense for server initialization
+//
+ if (mode == 'c') return &nilstr;
+
+// If there are no parameters, return the defaults
+//
+ if (!parms || !(*parms))
+ {char buff[256];
+ if (!getLinkage(erp, accPlugin.c_str())) return 0;
+ snprintf(buff, sizeof(buff), "TLS:0:%d:", MaxTokSize);
+ return strdup(buff);
+ }
+
+// Copy the parameters as we will need modify them
+//
+ std::vector useVec;
+ XrdOucString cfgParms(parms);
+ XrdOucTokenizer cfg(const_cast(cfgParms.c_str()));
+ char *endP, *val;
+ uint64_t opts = 0;
+
+// Setup to parse parameters
+//
+ cfg.GetLine();
+
+// Parse the parameters: -maxsz -rt -tokenlib
+// -use [first|last|only] { | none}
+//
+ while((val = cfg.GetToken()))
+ { if (!strcmp(val, "-maxsz"))
+ {if (!(val = cfg.GetToken()))
+ {Fatal(erp, "-maxsz argument missing", EINVAL);
+ return 0;
+ }
+ MaxTokSize = strtol(val, &endP, 10);
+ if (*endP == 'k' || *endP == 'K')
+ {MaxTokSize *= 1024; endP++;}
+ if (MaxTokSize <= 0 || *endP)
+ {Fatal(erp, "-maxsz argument is invalid", EINVAL);
+ return 0;
+ }
+ }
+ else if (!strcmp(val, "-rt")) opts |= srvRTOK;
+
+ else if (!strcmp(val, "-tokenlib"))
+ {if (!(val = cfg.GetToken()))
+ {Fatal(erp, "-acclib plugin path missing", EINVAL);
+ return 0;
+ }
+ accPlugin = val;
+ }
+
+ else if (!strcmp(val, "-use"))
+ {if (!(val = cfg.GetToken()))
+ {Fatal(erp, "-envar argument missing", EINVAL);
+ return 0;
+ }
+ opts &= ~(useFirst | useLast);
+ if (!strcmp(val, "first")) opts |= useFirst;
+ else if (!strcmp(val, "last")) opts |= useLast;
+ else if (!strcmp(val, "only")) opts |= (useFirst | useLast);
+
+ if (opts & (useFirst | useLast) && !(val = cfg.GetToken()))
+ {Fatal(erp, "-use argument missing", EINVAL);
+ return 0;
+ }
+
+ useVec.clear();
+ if (strcmp(val, "none") && !vecVars(val, useVec))
+ {Fatal(erp, "-use argument list malformed", EINVAL);
+ return 0;
+ }
+ }
+
+ else {XrdOucString eTxt("Invalid parameter - "); eTxt += val;
+ Fatal(erp, eTxt.c_str(), EINVAL);
+ return 0;
+ }
+ }
+
+// If there are no envars to use then realtime fetch must be enabled
+//
+ if (useVec.size() == 0 && (opts & useFirst) && (opts & useLast)
+ && !(opts & srvRTOK))
+ {Fatal(erp, "'-envar only none' without '-rt' disallowed.", EINVAL);
+ return 0;
+ }
+
+// We currently do not support -rt so complain is specified
+//
+ if (opts & srvRTOK)
+ {Fatal(erp, "'-rt' option currently unsupported", ENOTSUP);
+ return 0;
+ }
+
+// We rely on the token authorization plugin to validate tokens. Load it to
+// get the validation object pointer. This will be filled in later but we
+// want to know that it's actually present.
+//
+ if (!getLinkage(erp, accPlugin.c_str())) return 0;
+
+// Assemble the parameter line and return it
+//
+ char buff[256];
+ snprintf(buff, sizeof(buff), "TLS:%" PRIu64 ":%d:", opts, MaxTokSize);
+ cfgParms = buff;
+ for (int i = 0; i < (int)useVec.size(); i++)
+ {cfgParms += useVec[i];
+ if (i+1 < (int)useVec.size()) cfgParms += ',';
+ }
+ return strdup(cfgParms.c_str());
+}
+}
+
+/******************************************************************************/
+/* X r d S e c P r o t o c o l z t n O b j e c t */
+/******************************************************************************/
+
+extern "C"
+{
+XrdSecProtocol *XrdSecProtocolztnObject(const char mode,
+ const char *hostname,
+ XrdNetAddrInfo &endPoint,
+ const char *parms,
+ XrdOucErrInfo *erp)
+{
+ XrdSecProtocolztn *protP;
+
+// Whether this is a client of server, the connection must be using TLS.
+//
+ if (!endPoint.isUsingTLS())
+ {Fatal(erp,"security protocol 'ztn' disallowed for non-TLS connections.",
+ ENOTSUP, false);
+ return 0;
+ }
+
+// Get a protocol object appropriate for the mode
+//
+ if (mode == 'c')
+ {bool aOK;
+ protP = new XrdSecProtocolztn(parms, erp, aOK);
+ if (aOK) return protP;
+ delete protP;
+ return 0;
+ }
+
+// In server mode we need to make sure the token plugin was actually
+// loaded and initialized as we need a pointer to the helper.
+//
+ XrdSciTokensHelper *sthP= *sth_Linkage;
+ if (!sthP)
+ {char msg[1024];
+ snprintf(msg,sizeof(msg),"ztn required plugin (%s) has not been loaded!",
+ sth_piName);
+ Fatal(erp, msg, EIDRM,false);
+ return 0;
+ }
+
+// Get an authentication object and return it
+//
+ if (!(protP = new XrdSecProtocolztn(hostname, endPoint, sthP)))
+ Fatal(erp, "insufficient memory for protocol.", ENOMEM, false);
+
+// All done
+//
+ return protP;
+}
+}