XRootD
XrdSecTLayer.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d S e c T L a y e r . c c */
4 /* */
5 /* */
6 /* (c) 2008 by the Board of Trustees of the Leland Stanford, Jr., University */
7 /* All Rights Reserved */
8 /* Produced by Andrew Hanushevsky for Stanford University under contract */
9 /* DE-AC02-76-SFO0515 with the Department of Energy */
10 /* */
11 /* This file is part of the XRootD software suite. */
12 /* */
13 /* XRootD is free software: you can redistribute it and/or modify it under */
14 /* the terms of the GNU Lesser General Public License as published by the */
15 /* Free Software Foundation, either version 3 of the License, or (at your */
16 /* option) any later version. */
17 /* */
18 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
19 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
20 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
21 /* License for more details. */
22 /* */
23 /* You should have received a copy of the GNU Lesser General Public License */
24 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
25 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
26 /* */
27 /* The copyright holder's institutional names and contributor's names may not */
28 /* be used to endorse or promote products derived from this software without */
29 /* specific prior written permission of the institution or contributor. */
30 /******************************************************************************/
31 
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <fcntl.h>
35 #include <poll.h>
36 #include <cstdio>
37 #include <unistd.h>
38 
39 #include "XrdOuc/XrdOucErrInfo.hh"
40 #include "XrdSec/XrdSecTLayer.hh"
41 #include "XrdSys/XrdSysE2T.hh"
42 #include "XrdSys/XrdSysFD.hh"
43 #include "XrdSys/XrdSysHeaders.hh"
44 
45 /******************************************************************************/
46 /* S t a t i c V a l u e s */
47 /******************************************************************************/
48 
49 // Some compilers are incapable of optimizing away inline static const char's.
50 //
51 const char XrdSecTLayer::TLayerRR::endData;
52 const char XrdSecTLayer::TLayerRR::xfrData;
53 
54 /******************************************************************************/
55 /* C o n s t r u c t o r */
56 /******************************************************************************/
57 
58 XrdSecTLayer::XrdSecTLayer(const char *pName, Initiator who1st)
59  : XrdSecProtocol(pName),
60  secTid(0), mySem(0), Starter(who1st), myFD(-1), urFD(-1),
61  Tmax(275), Tcur(0), eCode(0), eText(0)
62 {
63 
64 // Do the standard stuff
65 //
66  memset((void *)&Hdr, 0, sizeof(Hdr));
67  strncpy(Hdr.protName,pName,sizeof(Hdr.protName)-1);
68 }
69 
70 /******************************************************************************/
71 /* C l i e n t O r i e n t e d M e t h o d s */
72 /******************************************************************************/
73 /******************************************************************************/
74 /* g e t C r e d e n t i a l s */
75 /******************************************************************************/
76 
78  XrdOucErrInfo *einfo)
79 {
80  char Buff[dataSz];
81  int Blen = 0, wrLen = 0;
82  char *bP, Req = TLayerRR::xfrData;
83 
84 // If this is the first time call, perform boot-up sequence and start the flow
85 //
86  eDest = einfo;
87  if (!parm)
88  {if (!bootUp(isClient)) return 0;
89  if (Starter == isServer)
90  {Hdr.protCode = TLayerRR::xfrData;
91  bP = (char *)malloc(hdrSz);
92  memcpy(bP, (char *)&Hdr, hdrSz);
93  return new XrdSecCredentials(bP, hdrSz);
94  }
95  } else {
96  if (parm->size < hdrSz)
97  {secError("Invalid parms length", EPROTO);
98  return 0;
99  }
100  Req = ((TLayerRR *)parm->buffer)->protCode;
101  wrLen= parm->size - hdrSz;
102  }
103 
104 // Perform required action
105 // xfrData -> xfrData | endData if socket gets closed
106 // endData -> endData if socket still open else protocol error
107 //
108  switch(Req)
109  {case TLayerRR::xfrData:
110  if (wrLen > 0 && write(myFD, parm->buffer+hdrSz, wrLen) < 0)
111  {secError("Socket write failed", errno); return 0;}
112  Blen = Read(myFD, Buff, dataSz);
113  if (Blen < 0 && (Blen != -EPIPE) && (Blen != -ECONNRESET))
114  {secError("Socket read failed", -Blen); return 0;}
115  break;
116  case TLayerRR::endData:
117  if (myFD < 0) {secError("Protocol violation", EPROTO); return 0;}
118  Blen = -1;
119  break;
120  default: secError("Unknown parms request", EINVAL); return 0;
121  }
122 
123 // Set correct protocol code based on value in Blen. On the client side we
124 // check for proper completion upon socket close or when we get endData.
125 // Note that we apply self-pacing here as well since either side can pace,
126 //
127  if (Blen < 0) {if (!secDone()) return 0;
128  Blen = 0; Hdr.protCode = TLayerRR::endData;}
129  else if (Blen || wrLen) {Tcur = 0; Hdr.protCode = TLayerRR::xfrData;}
130  else if (++Tcur <= Tmax) Hdr.protCode = TLayerRR::xfrData;
131  else {Tcur = 0; Hdr.protCode = TLayerRR::endData;}
132 
133 // Return the credentials
134 //
135  bP = (char *)malloc(hdrSz+Blen);
136  memcpy(bP, (char *)&Hdr, hdrSz);
137  if (Blen) memcpy(bP+hdrSz, Buff, Blen);
138  return new XrdSecCredentials(bP, hdrSz+Blen);
139 }
140 
141 /******************************************************************************/
142 /* S e r v e r O r i e n t e d M e t h o d s */
143 /******************************************************************************/
144 
146  XrdSecParameters **parms,
147  XrdOucErrInfo *einfo)
148 {
149  char Buff[dataSz];
150  int Blen = 0, wrLen;
151  char *bP, Req;
152 
153 // If this is the first time call, perform boot-up sequence and start the flow
154 //
155  eDest = einfo;
156  if (myFD < 0 && !bootUp(isServer)) return -1;
157 
158 // Get the request code
159 //
160  if (cred->size < hdrSz) {secError("Invalid credentials",EBADMSG); return -1;}
161  Req = ((TLayerRR *)cred->buffer)->protCode;
162  wrLen= cred->size - hdrSz;
163 
164 // Perform required action
165 // xfrData -> xfrData | endData if socket gets closed
166 // endData -> noresponse
167 //
168  switch(Req)
169  {case TLayerRR::xfrData:
170  if (wrLen > 0 && write(myFD, cred->buffer+hdrSz, wrLen) < 0)
171  {secError("Socket write failed", errno); return -1;}
172  Blen = Read(myFD, Buff, dataSz);
173  if (Blen < 0 && (Blen != -EPIPE) && (Blen != -ECONNRESET))
174  {secError("Socket read failed", -Blen); return 0;}
175  break;
176  case TLayerRR::endData: return (secDone() ? 0 : -1);
177  default: secError("Unknown parms request", EINVAL); return -1;
178  }
179 
180 // Set correct protocol code based on value in Blen and wrLen. Note that if
181 // both are zero then we decrease the pace count and bail if it reaches zero.
182 // Otherwise, we reset the pace count to it initial value. On the server side,
183 // we defer the socket drain until we receive a endData notification.
184 //
185  if (Blen < 0) {Blen = 0; Hdr.protCode = TLayerRR::endData;}
186  else if (Blen || wrLen) {Tcur = 0; Hdr.protCode = TLayerRR::xfrData;}
187  else if (++Tcur <= Tmax) Hdr.protCode = TLayerRR::xfrData;
188  else {Tcur = 0; Hdr.protCode = TLayerRR::endData;}
189 
190 // Return the credentials
191 //
192  bP = (char *)malloc(hdrSz+Blen);
193  memcpy(bP, (char *)&Hdr, hdrSz);
194  if (Blen) memcpy(bP+hdrSz, Buff, Blen);
195  *parms = new XrdSecParameters(bP, hdrSz+Blen);
196 
197  return 1;
198 }
199 
200 
201 /******************************************************************************/
202 /* P r i v a t e M e t h o d s */
203 /******************************************************************************/
204 /******************************************************************************/
205 /* b o o t U p */
206 /******************************************************************************/
207 
208 void *XrdSecTLayerBootUp(void *carg)
209  {XrdSecTLayer *tP = (XrdSecTLayer *)carg;
210  tP->secXeq();
211  return (void *)0;
212  }
213 
214 /******************************************************************************/
215 
216 int XrdSecTLayer::bootUp(Initiator whoami)
217 {
218  int sv[2];
219 
220 // Create a socket pair
221 //
222  if (XrdSysFD_Socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
223  {secError("Unable to create socket pair", errno); return 0;}
224  myFD = sv[0]; urFD = sv[1];
225  Responder = whoami;
226 
227 // Start a thread to handle the socket interaction
228 //
230  {int rc = errno;
231  close(myFD); myFD = -1;
232  close(urFD); urFD = -1;
233  secError("Unable to create thread", rc);
234  return 0;
235  }
236 
237 // All done
238 //
239  return 1;
240 }
241 
242 /******************************************************************************/
243 /* R e a d */
244 /******************************************************************************/
245 
246 int XrdSecTLayer::Read(int FD, char *Buff, int rdLen)
247 {
248  struct pollfd polltab = {FD, POLLIN|POLLRDNORM|POLLHUP, 0};
249  int retc, xWt, Tlen = 0;
250 
251 // Read the data. We will employ a self-pacing read schedule where the
252 // assumptioon is that should a read produce zero bytes after a small amount
253 // of time then the data supplier needs additional data (i.e., writes) before
254 // it can supply data to be read. This occurs because certain transport layer
255 // protocols issue several writes in a row to complete the client/server
256 // interaction. We cannot use a fixed schedule for this because streams may
257 // coalesce adjacent writes, sigh.
258 
259 // Compute the actual poll wait time
260 //
261  if (Tcur) xWt = (Tcur+10)/10;
262  else xWt = 1;
263 
264 // Now do the interaction
265 //
266  do {do {retc = poll(&polltab, 1, xWt);} while(retc < 0 && errno == EINTR);
267  if (retc <= 0) return (retc ? -errno : Tlen);
268  do {retc = read(FD, Buff, rdLen);} while(retc < 0 && errno == EINTR);
269  if (retc <= 0) return (retc ? -errno : (Tlen ? Tlen : -EPIPE));
270  Tlen += retc; Buff += retc; rdLen -= retc; xWt = 1;
271  } while(rdLen > 0);
272 
273  return Tlen;
274 }
275 
276 /******************************************************************************/
277 /* s e c D o n e */
278 /******************************************************************************/
279 
280 int XrdSecTLayer::secDone()
281 {
282 
283 // First close the socket and wait for thread completion
284 //
285  secDrain();
286 
287 // Next, check if everything went well
288 //
289  if (!eCode) return 1;
290 
291 // Diagnose the problem and return failure
292 //
293  secError((eText ? eText : "?"), eCode, 0);
294  return 0;
295 }
296 
297 /******************************************************************************/
298 /* s e c D r a i n */
299 /******************************************************************************/
300 
301 void XrdSecTLayer::secDrain()
302 {
303  if (myFD >= 0)
304  {close(myFD); myFD = -1;
305  mySem.Wait();
306  }
307 }
308 
309 /******************************************************************************/
310 /* s e c E r r n o */
311 /******************************************************************************/
312 
313 const char *XrdSecTLayer::secErrno(int rc, char *buff)
314 {
315  sprintf(buff, "err %d", rc);
316  return buff;
317 }
318 
319 /******************************************************************************/
320 /* s e c E r r o r */
321 /******************************************************************************/
322 
323 void XrdSecTLayer::secError(const char *Msg, int rc, int iserrno)
324 {
325  char buff[32];
326  const char *tlist[] = {"XrdSecProtocol", Hdr.protName, ": ", Msg, "; ",
327  (iserrno ? XrdSysE2T(rc) : secErrno(rc,buff))
328  };
329  int i, n = sizeof(tlist)/sizeof(const char *);
330 
331  if (eDest) eDest->setErrInfo(rc, tlist, n);
332  else {for (i = 0; i < n; i++) std::cerr <<tlist[i]; std::cerr <<std::endl;}
333 
334  secDrain();
335 }
336 
337 /******************************************************************************/
338 /* s e c X e q */
339 /******************************************************************************/
340 
342 {
343  XrdOucErrInfo einfo;
344  const char *Msg;
345 
346 // Initiate the protocol
347 //
348  if (Responder == XrdSecTLayer::isClient) secClient(urFD, &einfo);
349  else secServer(urFD, &einfo);
350 // Extract out the completion code
351 //
352  Msg = einfo.getErrText(eCode);
353  if (eText) {free(eText); eText = 0;}
354  if (eCode) eText = strdup(Msg ? Msg : "Authentication failed");
355 
356 // Indicate we are done
357 //
358  if (urFD>0) close(urFD);
359  urFD = -1;
360  mySem.Post();
361 }
static XrdSysError eDest(0,"crypto_")
ssize_t write(int fildes, const void *buf, size_t nbyte)
ssize_t read(int fildes, void *buf, size_t nbyte)
#define close(a)
Definition: XrdPosix.hh:43
XrdSecBuffer XrdSecParameters
XrdSecBuffer XrdSecCredentials
void * XrdSecTLayerBootUp(void *carg)
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
#define XRDSYSTHREAD_HOLD
const char * getErrText()
virtual void secClient(int theFD, XrdOucErrInfo *einfo)=0
virtual int Authenticate(XrdSecCredentials *cred, XrdSecParameters **parms, XrdOucErrInfo *einfo=0)
pthread_t secTid
virtual XrdSecCredentials * getCredentials(XrdSecParameters *parm=0, XrdOucErrInfo *einfo=0)
Definition: XrdSecTLayer.cc:77
virtual void secServer(int theFD, XrdOucErrInfo *einfo)=0
XrdSecTLayer(const char *pName, Initiator who1st=isClient)
Definition: XrdSecTLayer.cc:58
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=0)
Generic structure to pass security information back and forth.
char * buffer
Pointer to the buffer.
int size
Size of the buffer or length of data in the buffer.