From 59f6c2a7c5b9da250c586680946bdfc3a0245dcd Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Tue, 19 Jun 2018 17:04:25 -0700 Subject: [PATCH] [Server] Enable use of delegated credentials for 3rd party copy. --- docs/man/xrdcp.1 | 14 ++- src/XrdApps/XrdCpConfig.cc | 16 +++- src/XrdApps/XrdCpConfig.hh | 3 + src/XrdOfs/XrdOfs.cc | 3 +- src/XrdOfs/XrdOfs.hh | 2 + src/XrdOfs/XrdOfsConfig.cc | 155 +++++++++++++++++++++++++++++-- src/XrdOfs/XrdOfsTPC.cc | 167 ++++++++++++++++++++++++++++++---- src/XrdOfs/XrdOfsTPC.hh | 16 +++- src/XrdOfs/XrdOfsTPCInfo.cc | 29 ++++++ src/XrdOfs/XrdOfsTPCInfo.hh | 32 +++++-- src/XrdOfs/XrdOfsTPCProg.cc | 117 ++++++++++++++++++++++-- src/XrdOfs/XrdOfsTPCProg.hh | 2 +- src/XrdOuc/XrdOucProg.cc | 52 +++++++++-- src/XrdOuc/XrdOucProg.hh | 8 ++ src/XrdOuc/XrdOucStream.cc | 9 ++ src/XrdXrootd/XrdXrootdXeq.cc | 5 + 16 files changed, 565 insertions(+), 65 deletions(-) diff --git a/docs/man/xrdcp.1 b/docs/man/xrdcp.1 index da5b97fa4bf..fb3dec11b37 100644 --- a/docs/man/xrdcp.1 +++ b/docs/man/xrdcp.1 @@ -11,8 +11,8 @@ xrdcp - copy files [\fB--nopbar\fR] [\fB--posc\fR] [\fB--proxy \fIipaddr\fB:\fIport\fR] [\fB--recursive\fR] [\fB--retry\fR \fItime\fR] [\fB--server\fR] [\fB--silent\fR] [\fB--sources\fR \fInum\fR] [\fB--streams\fR \fInum\fR] -[\fB--tpc\fR \fIfirst\fR|\fIonly\fR] [\fB--verbose\fR] [\fB--version\fR] -[\fB--xrate\fR \fIrate\fR] [\fB--zip\fR \fIfile\fR] +[\fB--tpc\fR [\fBdelegate\fR] \fBfirst\fR|\fBonly\fR] [\fB--verbose\fR] +[\fB--version\fR] [\fB--xrate\fR \fIrate\fR] [\fB--zip\fR \fIfile\fR] \fIlegacy options\fR: [\fB-adler\fR] [\fB-DS\fR\fIparm string\fR] [\fB-DI\fR\fIparm number\fR] [\fB-md5\fR] [\fB-np\fR] [\fB-OD\fR\fIcgi\fR] [\fB-OS\fR\fIcgi\fR] [\fB-x\fR] @@ -109,14 +109,18 @@ uses \fInum\fR additional parallel streams to do the transfer. The maximum value is 15. The default is 0 (i.e., use only the main stream). .RE -\fB--tpc\fR \fIfirst|only\fR +\fB--tpc\fR [\fBdelegate\fR] \fBfirst\fR|\fBonly\fR .RS 5 copies the file from remote server to remote server using third-party-copy protocol (i.e., data flows from server to server). The source and destination servers must support third party copies. Additional security restrictions may apply and may cause the copy to fail if they cannot be satisfied. -Argument \fI'first'\fR tries tpc and if it fails, does a normal copy; -while \fI'only'\fR fails the copy unless tpc succeeds. +Argument '\fBfirst\fR' tries tpc and if it fails, does a normal copy; +while '\fBonly\fR' fails the copy unless tpc succeeds. When '\fBdelegate\fR' is +specified, the copy delegates the command issuer's credentials to the target +server which uses those credentials to authenticate with the source server. +Delegation is ignored if the target server is not configured to use delegated +credentials. Currently, only gsi credentials can be delegated. .RE \fB-v\fR | \fB--verbose\fR diff --git a/src/XrdApps/XrdCpConfig.cc b/src/XrdApps/XrdCpConfig.cc index 2f266791a54..fc17cfb73bc 100644 --- a/src/XrdApps/XrdCpConfig.cc +++ b/src/XrdApps/XrdCpConfig.cc @@ -265,6 +265,14 @@ do{while(optind < Argc && Legacy(optind)) {} if (!a2i(optarg, &nStrm, 1, 15)) Usage(22); break; case OpTpc: OpSpec |= DoTpc; + if (!strcmp("delegate", optarg)) + {OpSpec|= DoTpcDlgt; + if (optind >= Argc) + {UMSG("Missing tpc qualifier after " + "'delegate'"); + } + optarg = Argv[optind++]; + } if (!strcmp("only", optarg)) OpSpec|= DoTpcOnly; else if (strcmp("first", optarg)) {optind--; @@ -864,10 +872,10 @@ void XrdCpConfig::Usage(int rc) static const char *Options= "\n" "Options: [--cksum ] [--debug ] [--coerce] [--dynamic-src]\n" " [--force] [--help] [--infiles ] [--license] [--nopbar]\n" - " [--path] [--posc] [--proxy :] [--recursive]\n" - " [--retry ] [--server] [--silent] [--sources ] [--streams ]\n" - " [--tpc {first|only}] [--verbose] [--version] [--xrate ]\n" - " [--parallel ] [--zip ]"; + " [--path] [--parallel ] [--posc] [--proxy :]\n" + " [--recursive] [--retry ] [--server] [--silent] [--sources ]\n" + " [--streams ] [--tpc [delegate] {first|only}] [--verbose]\n" + " [--version] [--xrate ] [--zip ]"; static const char *Syntax2= "\n" ": [[x]root://[:]/] | -"; diff --git a/src/XrdApps/XrdCpConfig.hh b/src/XrdApps/XrdCpConfig.hh index 0c3047a10fe..68bb78a5cac 100644 --- a/src/XrdApps/XrdCpConfig.hh +++ b/src/XrdApps/XrdCpConfig.hh @@ -144,6 +144,7 @@ static const int DoStreams = 0x00010000; // -S | --streams static const int OpTpc = 'T'; static const int DoTpc = 0x00020000; // -T | --tpc {first | only} static const int DoTpcOnly = 0x00100000; // -T | --tpc only +static const int DoTpcDlgt = 0x00800000; // -T | --tpc delegate ... static const int OpVerbose = 'v'; static const int DoVerbose = 0x00040000; // -v | --verbose @@ -159,6 +160,8 @@ static const int DoParallel = 0x00200000; // --parallel static const int OpDynaSrc = 'Z'; static const int DoDynaSrc = 0x00400000; // --dynamic-src +// const int DoTpcDlgt = 0x00800000; // Marker to show bit used + static const int OpZip = 'z'; static const int DoZip = 0x01000000;// --zip diff --git a/src/XrdOfs/XrdOfs.cc b/src/XrdOfs/XrdOfs.cc index 1be808dea42..c6a63b195b9 100644 --- a/src/XrdOfs/XrdOfs.cc +++ b/src/XrdOfs/XrdOfs.cc @@ -562,7 +562,8 @@ int XrdOfsFile::open(const char *path, // In } // If this is a third party copy and we are the destination, then validate -// specification at this point and setup to transfer. +// specification at this point and setup to transfer. Note that if the +// call fails and auto removal is enabled, the file we created will be deleted. // if (tpcKey && isRW) {char pfnbuff[MAXPATHLEN+8]; const char *pfnP; diff --git a/src/XrdOfs/XrdOfs.hh b/src/XrdOfs/XrdOfs.hh index 57aaf3bce98..a52989e264a 100644 --- a/src/XrdOfs/XrdOfs.hh +++ b/src/XrdOfs/XrdOfs.hh @@ -402,6 +402,8 @@ XrdSysMutex ocMutex; // Global mutex for open/close int ConfigDispFwd(char *buff, struct fwdOpt &Fwd); int ConfigPosc(XrdSysError &Eroute); int ConfigRedir(XrdSysError &Eroute, XrdOucEnv *EnvInfo); +int ConfigTPC(XrdSysError &Eroute); +char *ConfigTPCDir(XrdSysError &Eroute, const char *xPath); const char *Fname(const char *); int Forward(int &Result, XrdOucErrInfo &Resp, struct fwdOpt &Fwd, const char *arg1=0, const char *arg2=0, diff --git a/src/XrdOfs/XrdOfsConfig.cc b/src/XrdOfs/XrdOfsConfig.cc index ad643b03631..9fe32c6e662 100644 --- a/src/XrdOfs/XrdOfsConfig.cc +++ b/src/XrdOfs/XrdOfsConfig.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include "XrdVersion.hh" @@ -57,6 +58,7 @@ #include "XrdOuc/XrdOucEnv.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysHeaders.hh" +#include "XrdOuc/XrdOucNSWalk.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucTrace.hh" #include "XrdOuc/XrdOucUtils.hh" @@ -82,6 +84,13 @@ extern XrdOss *XrdOfsOss; XrdVERSIONINFO(XrdOfs,XrdOfs); +namespace +{ +XrdOfsTPC::iParm Parms; // TPC parameters + +int SetMode(const char *path, mode_t mode) {return chmod(path, mode);} +} + /******************************************************************************/ /* d e f i n e s */ /******************************************************************************/ @@ -235,7 +244,7 @@ int XrdOfs::Configure(XrdSysError &Eroute, XrdOucEnv *EnvInfo) { // Configure third party copy but only if we are not a manager // if ((Options & ThirdPC) && !(Options & isManager)) - if (!XrdOfsTPC::Start()) NoGo = 1; + NoGo |= ConfigTPC(Eroute); // We need to do pre-initialization for event recording as the oss needs some // environmental information from that initialization to initialize the frm, @@ -572,6 +581,101 @@ int XrdOfs::ConfigRedir(XrdSysError &Eroute, XrdOucEnv *EnvInfo) return 0; } +/******************************************************************************/ +/* C o n f i g T P C */ +/******************************************************************************/ + +int XrdOfs::ConfigTPC(XrdSysError &Eroute) +{ + +// Check if we need to configure rge credentials directory +// + if (Parms.fCreds) + {char *cpath = Parms.cpath; + if (!(Parms.cpath = ConfigTPCDir(Eroute, cpath))) return 1; + free(cpath); + } + +// Initialize the TPC object +// + XrdOfsTPC::Init(Parms); + +// Start TPC operations +// + return (XrdOfsTPC::Start() ? 0 : 1); +} + +/******************************************************************************/ +/* C o n f i g T P C D i r */ +/******************************************************************************/ + +char *XrdOfs::ConfigTPCDir(XrdSysError &Eroute, const char *xPath) +{ + + const int AMode = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH; // 775 + const int BMode = S_IRWXU| S_IRGRP|S_IXGRP; // 750 + const char *iName; + char pBuff[MAXPATHLEN], *aPath; + int rc; + +// Construct the proper path to stored credentials +// + iName = XrdOucUtils::InstName(-1); + if (xPath) aPath = XrdOucUtils::genPath(xPath, iName, ".ofs/.tpccreds/"); + else {if (!(aPath = getenv("XRDADMINPATH"))) + {XrdOucUtils::genPath(pBuff, MAXPATHLEN, "/tmp", iName); + aPath = pBuff; + } + aPath = XrdOucUtils::genPath(aPath, (char *)0, ".ofs/.tpccreds/"); + } + +// Make sure directory path exists +// + if ((rc = XrdOucUtils::makePath(aPath, AMode))) + {Eroute.Emsg("Config", rc, "create TPC path", aPath); + free(aPath); + return 0; + } + +// Protect the last component +// + if (SetMode(aPath, BMode)) + {Eroute.Emsg("Config", errno, "protect TPC path", aPath); + free(aPath); + return 0; + } + +// list the contents of teh directory +// + XrdOucNSWalk nsWalk(&Eroute, aPath, 0, XrdOucNSWalk::retFile); + XrdOucNSWalk::NSEnt *nsX, *nsP = nsWalk.Index(rc); + if (rc) + {Eroute.Emsg("Config", rc, "list TPC path", aPath); + free(aPath); + return 0; + } + +// Remove directory contents of all files +// + bool isBad = false; + while((nsX = nsP)) + {nsP = nsP->Next; + if (unlink(nsX->Path)) + {Eroute.Emsg("Config", errno, "remove TPC creds", nsX->Path); + isBad = true; + } + delete nsX; + } + +// Check if all went well +// + if (isBad) {free(aPath); return 0;} + +// All done +// + return aPath; +} + /******************************************************************************/ /* C o n f i g X e q */ /******************************************************************************/ @@ -1195,9 +1299,12 @@ int XrdOfs::xrole(XrdOucStream &Config, XrdSysError &Eroute) Purpose: To parse the directive: tpc [cksum ] [ttl []] [logok] [xfr ] [allow ] [require {all|client|dest} [+]] - [restrict ] [streams ] + [restrict ] + [streams [,]] [echo] [scan {stderr | stdout}] [autorm] [pgm [parms]] + [fcreds [?] =] + [fcpath ] parms: [dn ] [group ] [host ] [vo ] @@ -1208,7 +1315,8 @@ int XrdOfs::xrole(XrdOucStream &Config, XrdSysError &Eroute) allow only allow destinations that match the specified authentication specification. maximum number of simultaneous transfers. - the number of TCP streams to use for the copy. + the default number of TCP streams to use for the copy. + The maximum number of TCP streams to use for the copy/ require that the client, destination, or both (i.e. all) use the specified authentication protocol. Additional require statements may be specified to add additional @@ -1221,13 +1329,20 @@ int XrdOfs::xrole(XrdOucStream &Config, XrdSysError &Eroute) default is to scan both. pgm specifies the transfer command with optional paramaters. It must be the last parameter on the line. + fcreds Forward destination credentials for protocol . The + request fails if thee are no credentials for . If a + question mark preceeds then if the client has not + forwarded its credentials, the server's credentials are + used. Otherwise, the copy fails. + = the name of the envar to be set with the path to the + credentials to be forwarded. + fcpath where creds are stored (default /.ofs/.tpccreds). Output: 0 upon success or !0 upon failure. */ int XrdOfs::xtpc(XrdOucStream &Config, XrdSysError &Eroute) { - XrdOfsTPC::iParm Parms; char *val, pgm[1024]; int reqType; *pgm = 0; @@ -1305,13 +1420,41 @@ int XrdOfs::xtpc(XrdOucStream &Config, XrdSysError &Eroute) if (!strcmp(val, "streams")) {if (!(val = Config.GetWord())) {Eroute.Emsg("Config","tpc streams value not specified"); return 1;} - if (XrdOuca2x::a2i(Eroute,"tpc streams",val,&Parms.Strm,1)) return 1; + char *comma = index(val,','); + if (comma) + {*comma++ = 0; + if (!(*comma)) + {Eroute.Emsg("Config","tpc streams max value missing"); return 1;} + if (XrdOuca2x::a2i(Eroute,"tpc max streams",comma,&Parms.SMax,0,15)) + return 1; + } + if (XrdOuca2x::a2i(Eroute,"tpc streams",val,&Parms.Strm,0,15)) return 1; + continue; + } + if (!strcmp(val, "fcreds")) + {char aBuff[64]; + Parms.fCreds = 1; + if (!(val = Config.GetWord()) || (*val == '?' && *(val+1) == '\0')) + {Eroute.Emsg("Config","tpc fcreds auth not specified"); return 1;} + if (strlen(val) >= sizeof(aBuff)) + {Eroute.Emsg("Config","invalid fcreds auth -", val); return 1;} + strcpy(aBuff, val); + if (!(val = Config.GetWord()) || *val != '=' || *(val+1) == 0) + {Eroute.Emsg("Config","tpc fcreds envar not specified"); return 1;} + const char *emsg = XrdOfsTPC::AddAuth(aBuff,val+1); + if (emsg) {Eroute.Emsg("Config",emsg,"-", val); return 1;} + continue; + } + if (!strcmp(val, "fcpath")) + {if (Parms.cpath) {free(Parms.cpath); Parms.cpath = 0;} + if (!(val = Config.GetWord())) + {Eroute.Emsg("Config","tpc fcpath arg not specified"); return 1;} + Parms.cpath = strdup(val); continue; } Eroute.Say("Config warning: ignoring invalid tpc option '",val,"'."); } - XrdOfsTPC::Init(Parms); Options |= ThirdPC; return 0; } diff --git a/src/XrdOfs/XrdOfsTPC.cc b/src/XrdOfs/XrdOfsTPC.cc index 23317898ece..dd5f74b6154 100644 --- a/src/XrdOfs/XrdOfsTPC.cc +++ b/src/XrdOfs/XrdOfsTPC.cc @@ -30,6 +30,7 @@ #include #include +#include #include #include "XrdAcc/XrdAccAccess.hh" @@ -42,6 +43,7 @@ #include "XrdOfs/XrdOfsTPCJob.hh" #include "XrdOfs/XrdOfsTPCProg.hh" #include "XrdOfs/XrdOfsTrace.hh" +#include "XrdOss/XrdOss.hh" #include "XrdOuc/XrdOucCallBack.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucProg.hh" @@ -60,20 +62,31 @@ extern XrdSysError OfsEroute; extern XrdOfsStats OfsStats; extern XrdOucTrace OfsTrace; +extern XrdOss *XrdOfsOss; namespace XrdOfsTPCParms { +static const int fcMax = 8; + +struct fcTb {char *aVar; + char aProt[XrdSecPROTOIDSIZE]; + bool aOpt; + bool aGSI; + } fcAuth[fcMax]; char *XfrProg = 0; char *cksType = 0; +const char *gsiPKH = "-----BEGIN PRIVATE KEY-----\n"; +int tcpSTRM = 0; +int tcpSMax = 15; int LogOK = 0; -int nStrms = 0; int xfrMax = 9; int tpcOK = 0; int encTPC = 0; int errMon =-3; +int fcNum = 0; bool doEcho = false; bool autoRM = false; -}; +} using namespace XrdOfsTPCParms; @@ -137,9 +150,56 @@ XrdOfsTPCAllow *XrdOfsTPC::ALList = 0; XrdAccAuthorize *XrdOfsTPC::fsAuth = 0; +char *XrdOfsTPC::cPath = 0; + int XrdOfsTPC::maxTTL =15; int XrdOfsTPC::dflTTL = 7; +/******************************************************************************/ +/* A d d A u t h */ +/******************************************************************************/ + +const char *XrdOfsTPC::AddAuth(const char *auth, const char *avar) +{ + bool aOpt, aGSI; + +// Check if credentials are optional +// + if (*auth != '?') aOpt = false; + else {aOpt = true; + auth++; + } + aGSI = strcmp("gsi", auth) == 0; + +// Verify that the authname is not too long +// + if (strlen(auth) >= XrdSecPROTOIDSIZE) return "Invalid auth"; + +// Check if auth is already in the table +// + for (int i = 0; i < fcNum; i++) + if (!strcmp(auth, fcAuth[i].aProt)) + {if (fcAuth[i].aVar) free(fcAuth[i].aVar); + fcAuth[i].aVar = strdup(avar); + fcAuth[i].aOpt = aOpt; + fcAuth[i].aGSI = aGSI; + return 0; + } + +// Check if we have room to add an auth +// + if (fcNum >= fcMax) return "Too many fcred auths"; + +// Add an auth +// + strcpy(fcAuth[fcNum].aProt, auth); + fcAuth[fcNum].aVar = strdup(avar); + fcAuth[fcNum].aOpt = aOpt; + fcAuth[fcNum].aGSI = aGSI; + fcNum++; + return 0; +} + /******************************************************************************/ /* A l l o w */ /******************************************************************************/ @@ -250,6 +310,21 @@ int XrdOfsTPC::Authorize(XrdOfsTPC **pTPC, return SFS_OK; } +/******************************************************************************/ +/* Private: D e a t h */ +/******************************************************************************/ + +int XrdOfsTPC::Death(XrdOfsTPC::Facts &Args, const char *eMsg, int eCode, int nomsg) +{ +// If automatc removal is wanted, remove the file. +// + if (autoRM && Args.Pfn) XrdOfsOss->Unlink(Args.Lfn); + +// Return error information +// + return Fatal(Args, eMsg, eCode, nomsg); +} + /******************************************************************************/ /* Private: F a t a l */ /******************************************************************************/ @@ -336,6 +411,8 @@ int XrdOfsTPC::getTTL(XrdOucEnv *Env) void XrdOfsTPC::Init(XrdOfsTPC::iParm &Parms) { + std::string aStr; + // Set program if specified // if (Parms.Pgm) @@ -350,16 +427,37 @@ void XrdOfsTPC::Init(XrdOfsTPC::iParm &Parms) cksType = Parms.Ckst; } +// Create credential forwarding template, if cred path specified. It is +// gauranteed to end with a slash (it better be). +// + if (Parms.cpath && Parms.fCreds) cPath = strdup(Parms.cpath); + else cPath = 0; + +// Check for streams option +// + if (Parms.Strm > 15) Parms.Strm = 15; + // Set all other static values // if (Parms.Dflttl > 0) dflTTL = Parms.Dflttl; if (Parms.Maxttl > 0) maxTTL = Parms.Maxttl; if (Parms.Logok >= 0) LogOK = Parms.Logok; - if (Parms.Strm > 0) nStrms = Parms.Strm; + if (Parms.Strm > 0) tcpSTRM= Parms.Strm; + if (Parms.SMax > 0) tcpSMax= Parms.SMax; if (Parms.Xmax > 0) xfrMax = Parms.Xmax; if (Parms.Grab < 0) errMon = Parms.Grab; if (Parms.xEcho >= 0) doEcho = Parms.xEcho != 0; if (Parms.autoRM >= 0) autoRM = Parms.autoRM != 0; + +// Record all delegated auths +// + for (int i = 0; i < fcNum; i++) + {aStr += ' '; aStr += fcAuth[i].aProt;} + +// Export the delegated auths +// + if (aStr.length()) + XrdOucEnv::Export("XRDTPCDLG", strdup(aStr.c_str()+1)); } /******************************************************************************/ @@ -450,13 +548,7 @@ int XrdOfsTPC::Start() // If there is no copy program then we use the default one // - if (!XfrProg) - {char pgmBuff[256], sBuff[32]; - if (nStrms) sprintf(sBuff, " -S %d", nStrms); - else *sBuff = 0; - snprintf(pgmBuff,sizeof(pgmBuff),"xrdcp --server%s",sBuff); - XfrProg = strdup(pgmBuff); - } + if (!XfrProg) XfrProg = strdup("xrdcp --server"); // Allocate copy program objects // @@ -483,35 +575,60 @@ int XrdOfsTPC::Validate(XrdOfsTPC **theTPC, XrdOfsTPC::Facts &Args) const char *tpcLfn = Args.Env->Get(XrdOucTPC::tpcLfn); const char *tpcSrc = Args.Env->Get(XrdOucTPC::tpcSrc); const char *tpcCks = Args.Env->Get(XrdOucTPC::tpcCks); - const char *theCGI; - char Buff[512], myURL[4096]; + const char *tpcStr = Args.Env->Get(XrdOucTPC::tpcStr); + const char *theCGI, *enVar = 0; + char Buff[512], myURL[4096], sVal = 0; int n, doRN = 0, myURLen = sizeof(myURL); short lfnLoc[2]; // Determine if we can handle any TPC requests // - if (!tpcOK || !Args.Usr) return Fatal(Args, "tpc not supported", ENOTSUP); + if (!tpcOK || !Args.Usr) return Death(Args, "tpc not supported", ENOTSUP); + +// If we will be forwarding credentials, then verify that we have some +// + for (int i = 0; i < fcNum; i++) + {if (!strcmp(Args.Usr->prot, fcAuth[i].aProt)) + {if (Args.Usr->creds == 0 || Args.Usr->credslen < 1 + || (fcAuth[i].aGSI && !strstr(Args.Usr->creds, gsiPKH))) + {if (!fcAuth[i].aOpt) + return Death(Args,"no delegated credentials for tpc",EACCES); + } else enVar = fcAuth[i].aVar; + break; + } + } // This is a request by a writer to get data from another party. Make sure // the source has been specified. // - if (!tpcSrc) return Fatal(Args, "tpc source not specified", EINVAL); - if (!Args.Pfn) return Fatal(Args, "tpc pfn not specified", EINVAL); + if (!tpcSrc) return Death(Args, "tpc source not specified", EINVAL); + if (!Args.Pfn) return Death(Args, "tpc pfn not specified", EINVAL); // If the lfn, if present, it must be absolute. // if (!tpcLfn) tpcLfn = Args.Lfn; - else if (*tpcLfn != '/') return Fatal(Args,"source lfn not absolute",EINVAL); + else if (*tpcLfn != '/') return Death(Args,"source lfn not absolute",EINVAL); else doRN = (strcmp(Args.Lfn, tpcLfn) != 0); +// Validate number of streams and adjust accordingly +// + if (tpcStr) + {char *eP; + long nStrm = strtol(tpcStr, &eP, 10); + if (nStrm < 0 || !(*eP)) + return Death(Args, "tpc streams value is invalid", EINVAL); + if (nStrm > tcpSMax) nStrm = tcpSMax; + sVal = static_cast(nStrm); + } else sVal = static_cast(tcpSTRM); + // Generate the origin id // - if (!genOrg(Args.Usr, Buff, sizeof(Buff))) return Fatal(Args, Buff, EINVAL); + if (!genOrg(Args.Usr, Buff, sizeof(Buff))) return Death(Args, Buff, EINVAL); // Construct the source url (it may be very big) // n = snprintf(myURL, myURLen, "xroot://%s/%s?", tpcSrc, tpcLfn); - if (n >= int(sizeof(myURL))) return Fatal(Args, "url too long", EINVAL); + if (n >= int(sizeof(myURL))) return Death(Args, "url too long", EINVAL); // Set lfn location in the URL but only if we need to do a rename // @@ -519,17 +636,27 @@ int XrdOfsTPC::Validate(XrdOfsTPC **theTPC, XrdOfsTPC::Facts &Args) else lfnLoc[1] = lfnLoc[0] = 0; theCGI = XrdOucTPC::cgiD2Src(Args.Key, Buff, myURL+n, myURLen-n); - if (*theCGI == '!') return Fatal(Args, theCGI+1, EINVAL); + if (*theCGI == '!') return Death(Args, theCGI+1, EINVAL); // Create a pseudo tpc object that will contain the information we need to // actually peform this copy. // if (!(myTPC = new XrdOfsTPCJob(myURL, Args.Usr->tident, Args.Lfn, Args.Pfn, tpcCks, lfnLoc))) - return Fatal(Args, "insufficient memory", ENOMEM); + return Death(Args, "insufficient memory", ENOMEM); + +// Set credentials for the job if we need to +// + if (enVar && Args.Usr->credslen > 0) + myTPC->Info.SetCreds(enVar, Args.Usr->creds, Args.Usr->credslen); + +// Set number of streams to use +// + if (sVal) myTPC->Info.SetStreams(sVal); // All done // + myTPC->Info.isDest(); *theTPC = (XrdOfsTPC *)myTPC; return SFS_OK; } diff --git a/src/XrdOfs/XrdOfsTPC.hh b/src/XrdOfs/XrdOfsTPC.hh index 28c92a76841..bbea483f297 100644 --- a/src/XrdOfs/XrdOfsTPC.hh +++ b/src/XrdOfs/XrdOfsTPC.hh @@ -64,26 +64,36 @@ struct Facts Usr(vEnt), eRR(vInf), Env(vEnv) {} }; +static +const char *AddAuth(const char *auth, const char *avar); + static void Allow(char *vDN, char *vGN, char *vHN, char *vVO); static int Authorize(XrdOfsTPC **theTPC, Facts &Args, int isPLE=0); +static +const char *credPath() {return cPath;} + virtual void Del() {} struct iParm {char *Pgm; char *Ckst; + char *cpath; + int fCreds; int Dflttl; int Maxttl; int Logok; int Strm; + int SMax; int Xmax; int Grab; int xEcho; int autoRM; - iParm() : Pgm(0), Ckst(0), Dflttl(-1), Maxttl(-1), - Logok(-1), Strm(-1), Xmax(-1), Grab(0), + iParm() : Pgm(0), Ckst(0), cpath(0), fCreds(0), + Dflttl(-1), Maxttl(-1), + Logok(-1), Strm(-1), SMax(64), Xmax(-1), Grab(0), xEcho(-1), autoRM(-1) {} }; @@ -117,6 +127,7 @@ XrdOfsTPCInfo Info; protected: +static int Death(Facts &Args, const char *eMsg, int eCode, int nomsg=0); static int Fatal(Facts &Args, const char *eMsg, int eCode, int nomsg=0); static int genOrg(const XrdSecEntity *client, char *Buff, int Blen); static int getTTL(XrdOucEnv *Env); @@ -127,6 +138,7 @@ static XrdAccAuthorize *fsAuth; static XrdOucTList *AuthDst; static XrdOucTList *AuthOrg; +static char *cPath; static XrdOfsTPCAllow *ALList; static XrdOucPListAnchor *RPList; diff --git a/src/XrdOfs/XrdOfsTPCInfo.cc b/src/XrdOfs/XrdOfsTPCInfo.cc index c2a97b3b6b1..60224eb6bc9 100644 --- a/src/XrdOfs/XrdOfsTPCInfo.cc +++ b/src/XrdOfs/XrdOfsTPCInfo.cc @@ -35,6 +35,7 @@ #include "XrdNet/XrdNetAddr.hh" #include "XrdOfs/XrdOfsStats.hh" #include "XrdOfs/XrdOfsTPCInfo.hh" +#include "XrdOss/XrdOss.hh" #include "XrdOuc/XrdOucErrInfo.hh" #include "XrdSfs/XrdSfsInterface.hh" #include "XrdSys/XrdSysError.hh" @@ -45,6 +46,34 @@ extern XrdSysError OfsEroute; extern XrdOfsStats OfsStats; +extern XrdOss *XrdOfsOss; + +namespace XrdOfsTPCParms +{ +extern bool autoRM; +} + +/******************************************************************************/ +/* D e s t r u c t o r */ +/******************************************************************************/ + +XrdOfsTPCInfo::~XrdOfsTPCInfo() +{ +// Check if we should remove the file +// + if (isDST && !isAOK && XrdOfsTPCParms::autoRM && Lfn) + XrdOfsOss->Unlink(Lfn); + +// Delete all appendages +// + if (Key) {free(Key); Key = 0;} + if (Org) {free(Org); Org = 0;} + if (Lfn) {free(Lfn); Lfn = 0;} + if (Dst) {free(Dst); Dst = 0;} + if (Cks) {free(Cks); Cks = 0;} + if (Crd) {free(Crd); Crd = 0; Csz = 0;} + if (cbP) delete cbP; +} /******************************************************************************/ /* F a i l */ diff --git a/src/XrdOfs/XrdOfsTPCInfo.hh b/src/XrdOfs/XrdOfsTPCInfo.hh index 797cc893cff..52939688f97 100644 --- a/src/XrdOfs/XrdOfsTPCInfo.hh +++ b/src/XrdOfs/XrdOfsTPCInfo.hh @@ -46,6 +46,8 @@ void Engage() {inWtR = true;} // Must be called w/ a serialization lock! int Fail(XrdOucErrInfo *eRR, const char *eMsg, int eCode); +void isDest() {isDST = true;} + int Match(const char *cKey, const char *cOrg, const char *xLfn, const char *xDst); @@ -57,6 +59,17 @@ const char *Set(const char *cKey, const char *cOrg, int SetCB(XrdOucErrInfo *eRR); +void SetCreds(const char *evar, const char *creds, int crdsz) + {Env = evar; + Crd = (char *)malloc(crdsz); + memcpy(Crd, creds, crdsz); + Csz = crdsz; + } + +void SetStreams(char sval) {Str = sval;} + +void Success() {isAOK = true;} + XrdOfsTPCInfo(const char *vKey=0, const char *vOrg=0, const char *vLfn=0, const char *vDst=0, const char *vCks=0) : cbP(0), @@ -64,15 +77,12 @@ int SetCB(XrdOucErrInfo *eRR); Key(vKey ? strdup(vKey) :0), Org(vOrg ? strdup(vOrg) :0), Lfn(vLfn ? strdup(vLfn) :0), - Dst(vDst ? strdup(vDst) :0), inWtR(false) {} + Dst(vDst ? strdup(vDst) :0), + Env(0), Crd(0), Csz(0), Str(0), + inWtR(false), isDST(false), isAOK(false) + {} - ~XrdOfsTPCInfo() {if (Key) {free(Key); Key = 0;} - if (Org) {free(Org); Org = 0;} - if (Lfn) {free(Lfn); Lfn = 0;} - if (Dst) {free(Dst); Dst = 0;} - if (Cks) {free(Cks); Cks = 0;} - if (cbP) delete cbP; - } + ~XrdOfsTPCInfo(); XrdOucCallBack *cbP; // Callback object char *Cks; // Checksum information (only at dest) @@ -80,6 +90,12 @@ char *Key; // Rendezvous key or src URL char *Org; // Rendezvous origin char *Lfn; // Rendezvous path or dest LFN char *Dst; // Rendezvous dest or dest PFN +const char *Env; // -> creds envar name +char *Crd; // Credentials to be forwarded dst->src +int Csz; // Size of credentials +char Str; // Number of streams to use bool inWtR; // Traget in waitresp status, async reply is valid. +bool isDST; // This info is about the destination file (PFN) +bool isAOK; // The copy succeeded }; #endif diff --git a/src/XrdOfs/XrdOfsTPCProg.cc b/src/XrdOfs/XrdOfsTPCProg.cc index e7d3337e5db..186800d1576 100644 --- a/src/XrdOfs/XrdOfsTPCProg.cc +++ b/src/XrdOfs/XrdOfsTPCProg.cc @@ -40,6 +40,7 @@ #include "XrdOuc/XrdOucProg.hh" #include "XrdOuc/XrdOucTrace.hh" #include "XrdSys/XrdSysError.hh" +#include "XrdSys/XrdSysFD.hh" #include "XrdSys/XrdSysHeaders.hh" /******************************************************************************/ @@ -80,6 +81,40 @@ void *XrdOfsTPCProgRun(void *pp) return (void *)0; } +/******************************************************************************/ +/* L o c a l C l a s s e s */ +/******************************************************************************/ + +namespace +{ +class credFile +{ +public: + +char *Path; +char pEnv[MAXPATHLEN+65]; + + credFile(XrdOfsTPCJob *jP) + {if (jP->Info.Csz > 0 && jP->Info.Crd && jP->Info.Env) + {int n; + csMutex.Lock(); n = cSeq++; csMutex.UnLock(); + snprintf(pEnv, sizeof(pEnv), "%s=%s%s#%d.creds", + jP->Info.Env, jP->credPath(), jP->Info.Org, n); + Path = index(pEnv,'=')+1; + } else Path = 0; + } + + ~credFile() {if (Path) unlink(Path);} + +private: +static XrdSysMutex csMutex; +static int cSeq; +}; + +XrdSysMutex credFile::csMutex; +int credFile::cSeq = 0; +} + /******************************************************************************/ /* C o n s t r u c t o r */ /******************************************************************************/ @@ -91,6 +126,39 @@ XrdOfsTPCProg::XrdOfsTPCProg(XrdOfsTPCProg *Prev, int num, int errMon) {snprintf(Pname, sizeof(Pname), "TPC job %d: ", num); Pname[sizeof(Pname)-1] = 0; } + +/******************************************************************************/ +/* E x p o r t C r e d s */ +/******************************************************************************/ + +int XrdOfsTPCProg::ExportCreds(const char *path) +{ +static const int oOpts = (O_CREAT | O_TRUNC | O_WRONLY); +static const mode_t oMode = (S_IRUSR | S_IWUSR); + +int fd, rc; + +// Open the file as if it were new +// + fd = XrdSysFD_Open(path, oOpts, oMode); + if (fd < 0) + {rc = errno; + OfsEroute.Emsg("TPC", rc, "create credentials file", path); + return -rc; + } + +// Write out the credentials +// + if (write(fd, Job->Info.Crd, Job->Info.Csz) < 0) + {rc = errno; + OfsEroute.Emsg("TPC", rc, "write credentials file", path); + } else rc = 0; + +// Close the file and return (we ignore close errors) +// + close(fd); + return rc; +} /******************************************************************************/ /* I n i t */ @@ -171,9 +239,17 @@ XrdOfsTPCProg *XrdOfsTPCProg::Start(XrdOfsTPCJob *jP, int &rc) int XrdOfsTPCProg::Xeq() { EPNAME("Xeq"); - const char *cksOpt; - char *lP, *Colon, *cksVal, *tident = Job->Info.Org; - int rc; + credFile cFile(Job); + const char *Args[6], *eVec[2], **envArg; + char *lP, *Colon, *cksVal, sBuff[8], *tident = Job->Info.Org; + int rc, aNum = 0; + +// If we have credentials, write them out to a file +// + if (cFile.Path && (rc = ExportCreds(cFile.Path))) + {strcpy(eRec, "Copy failed; unable to pass credentials."); + return rc; + } // Echo out what we are doing if so desired // @@ -187,11 +263,35 @@ int XrdOfsTPCProg::Xeq() // Determine checksum option // cksVal = (Job->Info.Cks ? Job->Info.Cks : XrdOfsTPCParms::cksType); - cksOpt = (cksVal ? "-C" : 0); + if (cksVal) + {Args[aNum++] = "-C"; + Args[aNum++] = cksVal; + } + +// Set streams option if need be +// + if (Job->Info.Str) + {sprintf(sBuff, "%d", static_cast(Job->Info.Str)); + Args[aNum++] = "-S"; + Args[aNum++] = sBuff; + } + +// Set remaining arguments +// + Args[aNum++] = Job->Info.Key; + Args[aNum++] = Job->Info.Dst; + +// Determine if credentials are being passed +// + if (cFile.Path) + {eVec[0] = cFile.pEnv; + eVec[1] = 0; + envArg = eVec; + } else envArg = 0; // Start the job. // - if ((rc = Prog.Run(&JobStream,cksOpt,cksVal,Job->Info.Key,Job->Info.Dst))) + if ((rc = Prog.Run(&JobStream, Args, aNum, envArg))) {strcpy(eRec, "Copy failed; unable to start job."); OfsEroute.Emsg("TPC", Job->Info.Org, Job->Info.Lfn, eRec); return rc; @@ -218,12 +318,13 @@ int XrdOfsTPCProg::Xeq() // if (rc && !(*eRec)) sprintf(eRec, "Copy failed with return code %d", rc); -// Log failures and optionally remove the file +// Log failures and optionally remove the file (Info would do that as well +// but much later on, so we do it now). // if (rc) {OfsEroute.Emsg("TPC", Job->Info.Org, Job->Info.Lfn, eRec); - if (autoRM) XrdOfsOss->Unlink(Job->Info.Dst, XRDOSS_isPFN); - } + if (autoRM) XrdOfsOss->Unlink(Job->Info.Lfn); + } else Job->Info.Success(); // All done // diff --git a/src/XrdOfs/XrdOfsTPCProg.hh b/src/XrdOfs/XrdOfsTPCProg.hh index 2e6f5b330f1..60ec846d755 100644 --- a/src/XrdOfs/XrdOfsTPCProg.hh +++ b/src/XrdOfs/XrdOfsTPCProg.hh @@ -56,7 +56,7 @@ XrdOfsTPCProg *Start(XrdOfsTPCJob *jP, int &rc); ~XrdOfsTPCProg() {} private: - + int ExportCreds(const char *path); static XrdSysMutex pgmMutex; static XrdOfsTPCProg *pgmIdle; diff --git a/src/XrdOuc/XrdOucProg.cc b/src/XrdOuc/XrdOucProg.cc index e7eb57c477e..04159ffc67d 100644 --- a/src/XrdOuc/XrdOucProg.cc +++ b/src/XrdOuc/XrdOucProg.cc @@ -39,6 +39,7 @@ #include "XrdSys/XrdWin32.hh" #endif +#include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucProg.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdSys/XrdSysError.hh" @@ -94,9 +95,17 @@ int XrdOucProg::Feed(const char *data[], const int dlen[]) /******************************************************************************/ /* R u n */ /******************************************************************************/ + +#define runWithVec(strmP, theRC)\ + const char *argV[4]; int argC = 0;\ + if (arg1) argV[argC++] = arg1;\ + if (arg2) argV[argC++] = arg2;\ + if (arg3) argV[argC++] = arg3;\ + if (arg4) argV[argC++] = arg4;\ + theRC = Run(strmP, argV, argC) -int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2, - const char *arg3, const char *arg4) +int XrdOucProg::Run(XrdOucStream *Sp, const char *argV[], int argC, + const char *envV[]) { const int maxArgs = sizeof(Arg)/sizeof(Arg[0])+4; char *myArgs[maxArgs+1]; @@ -115,10 +124,8 @@ int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2, // Append additional arguments as needed // - if (arg1 && j < maxArgs) myArgs[j++] = (char *)arg1; - if (arg2 && j < maxArgs) myArgs[j++] = (char *)arg2; - if (arg3 && j < maxArgs) myArgs[j++] = (char *)arg3; - if (arg4 && j < maxArgs) myArgs[j++] = (char *)arg4; + for (int i = 0; i < argC && j < maxArgs; i++) + if (argV[i]) myArgs[j++] = (char *)argV[i]; // Make sure we don't have too many // @@ -132,9 +139,18 @@ int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2, // if (myProc) return (*myProc)(Sp, myArgs, j); -// Execute the command +// Execute the command, possibly setting an environment. +// + if (envV) + {XrdOucEnv progEnv, *oldEnv = Sp->SetEnv(&progEnv); + progEnv.PutPtr("XrdEnvars**", (void *)envV); + rc = Sp->Exec(myArgs, 1, theEFD); + Sp->SetEnv(oldEnv); + } else rc = Sp->Exec(myArgs, 1, theEFD); + +// Diagnose any errors // - if (Sp->Exec(myArgs, 1, theEFD)) + if (rc) {rc = Sp->LastError(); if (eDest) eDest->Emsg("Run", rc, "execute", Arg[0]); return -rc; @@ -147,6 +163,19 @@ int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2, /******************************************************************************/ +int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2, + const char *arg3, const char *arg4) +{ + int rc; + +// Execute the command +// + runWithVec(Sp, rc); + return rc; +} + +/******************************************************************************/ + int XrdOucProg::Run(const char *arg1, const char *arg2, const char *arg3, const char *arg4) { @@ -156,7 +185,8 @@ int XrdOucProg::Run(const char *arg1, const char *arg2, // Execute the command // - if ((rc = Run(&cmd, arg1, arg2, arg3, arg4))) return rc; + runWithVec(&cmd, rc); + if (rc) return rc; // Drain all output // @@ -167,6 +197,7 @@ int XrdOucProg::Run(const char *arg1, const char *arg2, // return RunDone(cmd); } + /******************************************************************************/ int XrdOucProg::Run(char *outBuff, int outBsz, @@ -179,7 +210,8 @@ int XrdOucProg::Run(char *outBuff, int outBsz, // Execute the command // - if ((rc = Run(&cmd, arg1, arg2, arg3, arg4))) return rc; + runWithVec(&cmd, rc); + if (rc) return rc; // Drain the first line to the output buffer // diff --git a/src/XrdOuc/XrdOucProg.hh b/src/XrdOuc/XrdOucProg.hh index 57a95151954..3425ce09c20 100644 --- a/src/XrdOuc/XrdOucProg.hh +++ b/src/XrdOuc/XrdOucProg.hh @@ -67,6 +67,14 @@ int Feed(const char *data) {return Feed(data, (int)strlen(data));} // XrdOucStream *getStream() {return myStream;} +// Run executes the command that was passed via Setup(). You may pass +// additional arguments to be appended to the existing ones. The +// method also allows envars to be set in forked process via envV vector +// which contains strings "var=val" and terminaes with a null pointer. +// +int Run(XrdOucStream *Sp, const char *argV[], int argc=0, + const char *envV[]=0); + // Run executes the command that was passed via Setup(). You may pass // up to four additional arguments that will be added to the end of any // existing arguments. The ending status code of the program is returned. diff --git a/src/XrdOuc/XrdOucStream.cc b/src/XrdOuc/XrdOucStream.cc index 4009b0d0ea0..85a4ee46ff0 100644 --- a/src/XrdOuc/XrdOucStream.cc +++ b/src/XrdOuc/XrdOucStream.cc @@ -368,6 +368,15 @@ int XrdOucStream::Exec(char **parm, int inrd, int efd) } else close(Child_log); } + // Check if we need to set any envornment variables + // + if (myEnv) + {char **envP; + int i = 0; + if ((envP = (char **)myEnv->GetPtr("XrdEnvars**"))) + while(envP[i]) {putenv(envP[i]); i++;} + } + // Set our process group (the parent should have done this by now) then // invoke the command never to return // diff --git a/src/XrdXrootd/XrdXrootdXeq.cc b/src/XrdXrootd/XrdXrootdXeq.cc index d2b53ce5f4b..9928986b5f7 100644 --- a/src/XrdXrootd/XrdXrootdXeq.cc +++ b/src/XrdXrootd/XrdXrootdXeq.cc @@ -1700,6 +1700,11 @@ int XrdXrootdProtocol::do_Qconf() n = snprintf(bp, bleft, "%s\n", (tpcval ? tpcval : "tpc")); bp += n; bleft -= n; } + else if (!strcmp("tpcdlg", val)) + {char *tpcval = getenv("XRDTPCDLG"); + n = snprintf(bp, bleft, "%s\n", (tpcval ? tpcval : "tpcdlg")); + bp += n; bleft -= n; + } else if (!strcmp("wan_port", val) && WANPort) {n = snprintf(bp, bleft, "%d\n", WANPort); bp += n; bleft -= n;