/******************************************************************************/ /* */ /* X r d S e c T L a y e r . c c */ /* */ /* */ /* (c) 2008 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 #include #include "XrdOuc/XrdOucErrInfo.hh" #include "XrdSec/XrdSecTLayer.hh" #include "XrdSys/XrdSysFD.hh" #include "XrdSys/XrdSysHeaders.hh" /******************************************************************************/ /* S t a t i c V a l u e s */ /******************************************************************************/ // Some compilers are incapable of optimizing away inline static const char's. // const char XrdSecTLayer::TLayerRR::endData; const char XrdSecTLayer::TLayerRR::xfrData; /******************************************************************************/ /* C o n s t r u c t o r */ /******************************************************************************/ XrdSecTLayer::XrdSecTLayer(const char *pName, Initiator who1st) : XrdSecProtocol(pName), secTid(0), mySem(0), Starter(who1st), myFD(-1), urFD(-1), Tmax(275), Tcur(0), eCode(0), eText(0) { // Do the standard stuff // memset((void *)&Hdr, 0, sizeof(Hdr)); strncpy(Hdr.protName,pName,sizeof(Hdr.protName)-1); } /******************************************************************************/ /* C l i e n t O r i e n t e d M e t h o d s */ /******************************************************************************/ /******************************************************************************/ /* g e t C r e d e n t i a l s */ /******************************************************************************/ XrdSecCredentials *XrdSecTLayer::getCredentials(XrdSecParameters *parm, XrdOucErrInfo *einfo) { char Buff[dataSz]; int Blen = 0, wrLen = 0; char *bP, Req = TLayerRR::xfrData; // If this is the first time call, perform boot-up sequence and start the flow // eDest = einfo; if (!parm) {if (!bootUp(isClient)) return 0; if (Starter == isServer) {Hdr.protCode = TLayerRR::xfrData; bP = (char *)malloc(hdrSz); memcpy(bP, (char *)&Hdr, hdrSz); return new XrdSecCredentials(bP, hdrSz); } } else { if (parm->size < hdrSz) {secError("Invalid parms length", EPROTO); return 0; } Req = ((TLayerRR *)parm->buffer)->protCode; wrLen= parm->size - hdrSz; } // Perform required action // xfrData -> xfrData | endData if socket gets closed // endData -> endData if socket still open else protocol error // switch(Req) {case TLayerRR::xfrData: if (wrLen > 0 && write(myFD, parm->buffer+hdrSz, wrLen) < 0) {secError("Socket write failed", errno); return 0;} Blen = Read(myFD, Buff, dataSz); if (Blen < 0 && (Blen != -EPIPE) && (Blen != -ECONNRESET)) {secError("Socket read failed", -Blen); return 0;} break; case TLayerRR::endData: if (myFD < 0) {secError("Protocol violation", EPROTO); return 0;} Blen = -1; break; default: secError("Unknown parms request", EINVAL); return 0; } // Set correct protocol code based on value in Blen. On the client side we // check for proper completion upon socket close or when we get endData. // Note that we apply self-pacing here as well since either side can pace, // if (Blen < 0) {if (!secDone()) return 0; Blen = 0; Hdr.protCode = TLayerRR::endData;} else if (Blen || wrLen) {Tcur = 0; Hdr.protCode = TLayerRR::xfrData;} else if (++Tcur <= Tmax) Hdr.protCode = TLayerRR::xfrData; else {Tcur = 0; Hdr.protCode = TLayerRR::endData;} // Return the credentials // bP = (char *)malloc(hdrSz+Blen); memcpy(bP, (char *)&Hdr, hdrSz); if (Blen) memcpy(bP+hdrSz, Buff, Blen); return new XrdSecCredentials(bP, hdrSz+Blen); } /******************************************************************************/ /* S e r v e r O r i e n t e d M e t h o d s */ /******************************************************************************/ int XrdSecTLayer::Authenticate (XrdSecCredentials *cred, XrdSecParameters **parms, XrdOucErrInfo *einfo) { char Buff[dataSz]; int Blen = 0, wrLen; char *bP, Req; // If this is the first time call, perform boot-up sequence and start the flow // eDest = einfo; if (myFD < 0 && !bootUp(isServer)) return -1; // Get the request code // if (cred->size < hdrSz) {secError("Invalid credentials",EBADMSG); return -1;} Req = ((TLayerRR *)cred->buffer)->protCode; wrLen= cred->size - hdrSz; // Perform required action // xfrData -> xfrData | endData if socket gets closed // endData -> noresponse // switch(Req) {case TLayerRR::xfrData: if (wrLen > 0 && write(myFD, cred->buffer+hdrSz, wrLen) < 0) {secError("Socket write failed", errno); return -1;} Blen = Read(myFD, Buff, dataSz); if (Blen < 0 && (Blen != -EPIPE) && (Blen != -ECONNRESET)) {secError("Socket read failed", -Blen); return 0;} break; case TLayerRR::endData: return (secDone() ? 0 : -1); default: secError("Unknown parms request", EINVAL); return -1; } // Set correct protocol code based on value in Blen and wrLen. Note that if // both are zero then we decrease the pace count and bail if it reaches zero. // Otherwise, we reset the pace count to it initial value. On the server side, // we defer the socket drain until we receive a endData notification. // if (Blen < 0) {Blen = 0; Hdr.protCode = TLayerRR::endData;} else if (Blen || wrLen) {Tcur = 0; Hdr.protCode = TLayerRR::xfrData;} else if (++Tcur <= Tmax) Hdr.protCode = TLayerRR::xfrData; else {Tcur = 0; Hdr.protCode = TLayerRR::endData;} // Return the credentials // bP = (char *)malloc(hdrSz+Blen); memcpy(bP, (char *)&Hdr, hdrSz); if (Blen) memcpy(bP+hdrSz, Buff, Blen); *parms = new XrdSecParameters(bP, hdrSz+Blen); return 1; } /******************************************************************************/ /* P r i v a t e M e t h o d s */ /******************************************************************************/ /******************************************************************************/ /* b o o t U p */ /******************************************************************************/ void *XrdSecTLayerBootUp(void *carg) {XrdSecTLayer *tP = (XrdSecTLayer *)carg; tP->secXeq(); return (void *)0; } /******************************************************************************/ int XrdSecTLayer::bootUp(Initiator whoami) { int sv[2]; // Create a socket pair // if (XrdSysFD_Socketpair(AF_UNIX, SOCK_STREAM, 0, sv)) {secError("Unable to create socket pair", errno); return 0;} myFD = sv[0]; urFD = sv[1]; Responder = whoami; // Start a thread to handle the socket interaction // if (XrdSysThread::Run(&secTid,XrdSecTLayerBootUp,(void *)this, XRDSYSTHREAD_HOLD)) {int rc = errno; close(myFD); myFD = -1; close(urFD); urFD = -1; secError("Unable to create thread", rc); return 0; } // All done // return 1; } /******************************************************************************/ /* R e a d */ /******************************************************************************/ int XrdSecTLayer::Read(int FD, char *Buff, int rdLen) { struct pollfd polltab = {FD, POLLIN|POLLRDNORM|POLLHUP, 0}; int retc, xWt, Tlen = 0; // Read the data. We will employ a self-pacing read schedule where the // assumptioon is that should a read produce zero bytes after a small amount // of time then the data supplier needs additional data (i.e., writes) before // it can supply data to be read. This occurs because certain transport layer // protocols issue several writes in a row to complete the client/server // interaction. We cannot use a fixed schedule for this because streams may // coalesce adjacent writes, sigh. // Compute the actual poll wait time // if (Tcur) xWt = (Tcur+10)/10; else xWt = 1; // Now do the interaction // do {do {retc = poll(&polltab, 1, xWt);} while(retc < 0 && errno == EINTR); if (retc <= 0) return (retc ? -errno : Tlen); do {retc = read(FD, Buff, rdLen);} while(retc < 0 && errno == EINTR); if (retc <= 0) return (retc ? -errno : (Tlen ? Tlen : -EPIPE)); Tlen += retc; Buff += retc; rdLen -= retc; xWt = 1; } while(rdLen > 0); return Tlen; } /******************************************************************************/ /* s e c D o n e */ /******************************************************************************/ int XrdSecTLayer::secDone() { // First close the socket and wait for thread completion // secDrain(); // Next, check if everything went well // if (!eCode) return 1; // Diagnose the problem and return failure // secError((eText ? eText : "?"), eCode, 0); return 0; } /******************************************************************************/ /* s e c D r a i n */ /******************************************************************************/ void XrdSecTLayer::secDrain() { if (myFD >= 0) {close(myFD); myFD = -1; mySem.Wait(); } } /******************************************************************************/ /* s e c E r r n o */ /******************************************************************************/ const char *XrdSecTLayer::secErrno(int rc, char *buff) { sprintf(buff, "err %d", rc); return buff; } /******************************************************************************/ /* s e c E r r o r */ /******************************************************************************/ void XrdSecTLayer::secError(const char *Msg, int rc, int iserrno) { char buff[32]; const char *tlist[] = {"XrdSecProtocol", Hdr.protName, ": ", Msg, "; ", (iserrno ? strerror(rc) : secErrno(rc,buff)) }; int i, n = sizeof(tlist)/sizeof(const char *); if (eDest) eDest->setErrInfo(rc, tlist, n); else {for (i = 0; i < n; i++) cerr <0) close(urFD); urFD = -1; mySem.Post(); }