/******************************************************************************/ /* */ /* X r d C m s S e c u r i t y . c c */ /* */ /* (c) 2007 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. */ /******************************************************************************/ #include #include #include #include #include #include "XrdVersion.hh" #include "XProtocol/YProtocol.hh" #include "Xrd/XrdLink.hh" #include "XrdCms/XrdCmsSecurity.hh" #include "XrdCms/XrdCmsTalk.hh" #include "XrdCms/XrdCmsTrace.hh" #include "XrdCms/XrdCmsVnId.hh" #include "XrdNet/XrdNetAddrInfo.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucErrInfo.hh" #include "XrdOuc/XrdOucPinLoader.hh" #include "XrdOuc/XrdOucTList.hh" #include "XrdSec/XrdSecLoadSecurity.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysFD.hh" #include "XrdSys/XrdSysPthread.hh" using namespace XrdCms; /******************************************************************************/ /* S t a t i c S y m b o l s */ /******************************************************************************/ namespace { static XrdSecGetProt_t getProtocol = 0; static const int nidMax = 64; } XrdSecService *XrdCmsSecurity::DHS = 0; /******************************************************************************/ /* A u t h e n t i c a t e */ /******************************************************************************/ int XrdCmsSecurity::Authenticate(XrdLink *Link, const char *Token, int Toksz) { CmsRRHdr myHdr = {0, kYR_xauth, 0, 0}; XrdSecCredentials cred; XrdSecProtocol *AuthProt = 0; XrdSecParameters *parm = 0; XrdOucErrInfo eMsg; const char *eText = 0; char *authName, authBuff[4096]; int rc, myDlen, abLen = sizeof(authBuff); // Send a request for authentication // if ((eText = XrdCmsTalk::Request(Link, myHdr, (char *)Token, Toksz+1))) {Say.Emsg("Auth",Link->Host(),"authentication failed;",eText); return 0; } // Perform standard authentication // do { // Get the response header and verify the request code // if ((eText = XrdCmsTalk::Attend(Link,myHdr,authBuff,abLen,myDlen))) break; if (myHdr.rrCode != kYR_xauth) {eText = "invalid auth response"; break;} cred.size = myDlen; cred.buffer = authBuff; // If we do not yet have a protocol, get one // if (!AuthProt) {if (!DHS || !(AuthProt=DHS->getProtocol(Link->Host(), *(Link->AddrInfo()),&cred,&eMsg))) {eText = eMsg.getErrText(rc); break;} } // Perform the authentication // AuthProt->Entity.addrInfo = Link->AddrInfo(); if (!(rc = AuthProt->Authenticate(&cred, &parm, &eMsg))) break; if (rc < 0) {eText = eMsg.getErrText(rc); break;} if (parm) {eText = XrdCmsTalk::Request(Link, myHdr, parm->buffer, parm->size); delete parm; if (eText) break; } else {eText = "auth interface violation"; break;} } while(1); // Check if we succeeded // if (!eText) {if (!(authName = AuthProt->Entity.name)) eText = "entity name missing"; else {Link->setID(authName,0); Say.Emsg("Auth",Link->Host(),"authenticated as", authName); } } // Check if we failed // if (eText) Say.Emsg("Auth",Link->Host(),"authentication failed;",eText); // Perform final steps here // if (AuthProt) AuthProt->Delete(); return (eText == 0); } /******************************************************************************/ /* c h k V n I d */ /******************************************************************************/ char *XrdCmsSecurity::chkVnId(XrdSysError &eDest, const char *vnid, const char *what) { int n = strlen(vnid); // Check if it is too long // if (n > nidMax) {eDest.Emsg("Config", what, "a too long vnid -", vnid); return 0; } // Check for a null vnid // if (!n || !(*vnid)) {eDest.Emsg("Config", what, "a null vnid."); return 0; } // Make sure the vnid does not contain invalid characters // const char *cP = vnid; while(*cP && (isalnum(*cP) || ispunct(*cP)) && *cP != '&' && *cP != ' ') cP++; if (*cP) {eDest.Emsg("Config", what, "an invalid vnid -", vnid); return 0; } // All is well // return strdup(vnid); } /******************************************************************************/ /* C o n f i g u r e */ /******************************************************************************/ int XrdCmsSecurity::Configure(const char *Lib, const char *Cfn) { static XrdSysMutex myMutex; XrdSysMutexHelper hlpMtx(&myMutex); // If we aleady have a security interface, return (may happen in client) // if (!Cfn && getProtocol) return 1; // Get the server object and protocol creator // if (!(DHS = XrdSecLoadSecService(&Say, Cfn, (strcmp(Lib,"default") ? Lib:0), &getProtocol))) {Say.Emsg("Config","Unable to create security service object via",Lib); return 0; } // All done // return 1; } /******************************************************************************/ /* g e t V n I d */ /******************************************************************************/ char *XrdCmsSecurity::getVnId(XrdSysError &eDest, const char *cfgFN, const char *nidarg, const char *nidparm, char nidType) { static XrdVERSIONINFODEF (myVer, XrdNID, XrdVNUMBER, XrdVERSION); std::string (*ep)(XrdCmsgetVnIdArgs); std::string nidName; // Read the vnid from a file is so directed // if (*nidarg == '<') {char buff[nidMax+8]; int nfd = XrdSysFD_Open(nidarg+1, O_RDONLY); if (nfd < 0) {eDest.Emsg("Config", errno, "open vnid file", nidarg+1); return 0; } int n = read(nfd, buff, sizeof(buff)-1); if (n < 0) {eDest.Emsg("Config", errno, "read vnid file", nidarg+1); close(nfd); return 0; } close(nfd); while(n && buff[n-1] == '\n') n--; buff[n] = 0; return chkVnId(eDest, buff, "vnid file contains"); } // Check if the actual vnid is being specified // if (*nidarg == '=') return chkVnId(eDest, nidarg+1, "vnid value is"); // Make sure a plugin is being passed // if (*nidarg != '@') {eDest.Emsg("Config", "vnid specification is invalid -", nidarg); return 0; } // Get the entry point of the node ID creator // XrdOucPinLoader nidLib(&eDest, &myVer, "vnid", nidarg+1); ep = (std::string (*)(XrdCmsgetVnIdArgs))(nidLib.Resolve("XrdCmsgetVnId")); if (!ep) return 0; // Get the node ID // nidName = ep(eDest, std::string(cfgFN), std::string(nidparm ? nidparm : ""), nidType, nidMax); // Unload the plugin (we don't need it anymore) // nidLib.Unload(); // Verify that the node ID meets specs // return chkVnId(eDest, nidName.c_str(), "vnid plugin returned"); } /******************************************************************************/ /* g e t T o k e n */ /******************************************************************************/ const char *XrdCmsSecurity::getToken(int &size, XrdNetAddrInfo *endPoint) { // If not configured, return a null to indicate no authentication required // if (!DHS) {size = 0; return 0;} // Return actual token // return DHS->getParms(size, endPoint); } /******************************************************************************/ /* I d e n t i f y */ /******************************************************************************/ int XrdCmsSecurity::Identify(XrdLink *Link, XrdCms::CmsRRHdr &inHdr, char *authBuff, int abLen) { CmsRRHdr outHdr = {0, kYR_xauth, 0, 0}; const char *hName = Link->Host(); XrdSecCredentials *cred; XrdSecProtocol *AuthProt = 0; XrdSecParameters AuthParm, *AuthP = 0; XrdOucErrInfo eMsg; const char *eText = 0; int rc, myDlen; // Verify that we are configured // if (!getProtocol && !Configure("libXrdSec.so")) {Say.Emsg("Auth", hName ,"authentication configuration failed."); return 0; } // Obtain the protocol // AuthParm.buffer = (char *)authBuff; AuthParm.size = strlen(authBuff); if (!(AuthProt = getProtocol(hName,*(Link->AddrInfo()),AuthParm,&eMsg))) {Say.Emsg("Auth", hName, "getProtocol() failed;", eMsg.getErrText(rc)); return 0; } // Perform standard authentication // do { // Get credentials // if (!(cred = AuthProt->getCredentials(AuthP, &eMsg))) {eText = eMsg.getErrText(rc); break;} // Send credentials to the server // eText = XrdCmsTalk::Request(Link, outHdr, cred->buffer, cred->size); delete cred; if (eText) break; // Get the response header and prepare for next iteration if need be // if ((eText = XrdCmsTalk::Attend(Link,inHdr,authBuff,abLen,myDlen))) break; AuthParm.size = myDlen; AuthParm.buffer = authBuff; AuthP = &AuthParm; } while(inHdr.rrCode == kYR_xauth); // Check if we failed // if (eText) Say.Emsg("Auth", hName, "authentication failed;", eText); // Perform final steps here // if (AuthProt) AuthProt->Delete(); return (eText == 0); } /******************************************************************************/ /* s e t S e c F u n c */ /******************************************************************************/ void XrdCmsSecurity::setSecFunc(void *secfP) {getProtocol = (XrdSecGetProt_t)secfP;} /******************************************************************************/ /* s e t S y s t e m I D */ /******************************************************************************/ char *XrdCmsSecurity::setSystemID(XrdOucTList *tp, const char *iVNID, const char *iTag, char iType) { XrdOucTList *tpF; char sidbuff[8192], *sidend = sidbuff+sizeof(sidbuff)-32; char *cP, *sp = sidbuff; char *fMan, *fp, *xp; int n; // Extract out the instance name (we must have one) // const char *instP = getenv("XRDINSTANCE"); if (instP) instP = index(instP, ' '); if (!instP) return (char *)"!envar XRDINSTANCE undefined."; while(*instP && *instP == ' ') instP++; if (!(*instP)) return (char *)"!envar XRDINSTANCE invalid."; // The system ID starts with the semi-unique name of this node unless it's // a vnetid, in which case it's unique withn this cluster. Note that vnetid's // always start with an asterisk. It does not otherwise. // if (iVNID) {*sp++ = '*'; *sp++ = iType; *sp++ = '-'; strcpy(sp, iVNID); sp += strlen(iVNID); } else { *sp++ = iType; *sp++ = '-'; strcpy(sp, instP); sp += strlen(instP); } // Export the vnid // *sp = 0; XrdOucEnv::Export("XRDCMSVNID", sidbuff); *sp++ = ' '; cP = sp; // Insert tag if we have one // if (iTag) sp += sprintf(sp, "%s.", iTag); // Develop a unique cluster name for this cluster // if (!tp) sp += sprintf(sp, "%s", instP); else {tpF = tp; fMan = tp->text + strlen(tp->text) - 1; while((tp = tp->next)) {fp = fMan; xp = tp->text + strlen(tp->text) - 1; do {if (*fp != *xp) break; xp--; } while(fp-- != tpF->text); if ((n = xp - tp->text + 1) > 0) {sp += sprintf(sp, "%d", tp->val); if (sp+n >= sidend) return (char *)0; strncpy(sp, tp->text, n); sp += n; } } sp += sprintf(sp, "%d", tpF->val); n = strlen(tpF->text); if (sp+n >= sidend) return (char *)0; strcpy(sp, tpF->text); sp += n; } // Set envar to hold the cluster name // *sp = '\0'; XrdOucEnv::Export("XRDCMSCLUSTERID", cP); // Export the full virtual network ID // XrdOucEnv::Export("XRDCMSSYSID", sidbuff); // Return the system ID // return strdup(sidbuff); }