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; +} +}