/******************************************************************************/
/* */
/* 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();
}