XRootD
XrdCmsSecurity.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d C m s S e c u r i t y . c c */
4 /* */
5 /* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <cstdlib>
32 #include <string>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 
37 #include "XrdVersion.hh"
38 
39 #include "XProtocol/YProtocol.hh"
40 
41 #include "Xrd/XrdLink.hh"
42 
43 #include "XrdCms/XrdCmsSecurity.hh"
44 #include "XrdCms/XrdCmsTalk.hh"
45 #include "XrdCms/XrdCmsTrace.hh"
46 #include "XrdCms/XrdCmsVnId.hh"
47 
48 #include "XrdNet/XrdNetAddrInfo.hh"
49 #include "XrdOuc/XrdOucEnv.hh"
50 #include "XrdOuc/XrdOucErrInfo.hh"
52 #include "XrdOuc/XrdOucTList.hh"
54 #include "XrdSys/XrdSysError.hh"
55 #include "XrdSys/XrdSysFD.hh"
56 #include "XrdSys/XrdSysPthread.hh"
57 
58 using namespace XrdCms;
59 
60 /******************************************************************************/
61 /* S t a t i c S y m b o l s */
62 /******************************************************************************/
63 
64 namespace
65 {
66 static XrdSecGetProt_t getProtocol = 0;
67 
68 static const int nidMax = 64;
69 }
70 
71 XrdSecService *XrdCmsSecurity::DHS = 0;
72 
73 /******************************************************************************/
74 /* A u t h e n t i c a t e */
75 /******************************************************************************/
76 
77 int XrdCmsSecurity::Authenticate(XrdLink *Link, const char *Token, int Toksz)
78 {
79  CmsRRHdr myHdr = {0, kYR_xauth, 0, 0};
80  XrdSecCredentials cred;
81  XrdSecProtocol *AuthProt = 0;
82  XrdSecParameters *parm = 0;
84  const char *eText = 0;
85  char *authName, authBuff[4096];
86  int rc, myDlen, abLen = sizeof(authBuff);
87 
88 // Send a request for authentication
89 //
90  if ((eText = XrdCmsTalk::Request(Link, myHdr, (char *)Token, Toksz+1)))
91  {Say.Emsg("Auth",Link->Host(),"authentication failed;",eText);
92  return 0;
93  }
94 
95 // Perform standard authentication
96 //
97 do {
98 
99 // Get the response header and verify the request code
100 //
101  if ((eText = XrdCmsTalk::Attend(Link,myHdr,authBuff,abLen,myDlen))) break;
102  if (myHdr.rrCode != kYR_xauth) {eText = "invalid auth response"; break;}
103  cred.size = myDlen; cred.buffer = authBuff;
104 
105 // If we do not yet have a protocol, get one
106 //
107  if (!AuthProt)
108  {if (!DHS || !(AuthProt=DHS->getProtocol(Link->Host(),
109  *(Link->AddrInfo()),&cred,eMsg)))
110  {eText = eMsg.getErrText(rc); break;}
111  }
112 
113 // Perform the authentication
114 //
115  AuthProt->Entity.addrInfo = Link->AddrInfo();
116  if (!(rc = AuthProt->Authenticate(&cred, &parm, &eMsg))
117  && DHS->PostProcess(AuthProt->Entity, eMsg)) break;
118  if (rc < 0) {eText = eMsg.getErrText(rc); break;}
119  if (parm)
120  {eText = XrdCmsTalk::Request(Link, myHdr, parm->buffer, parm->size);
121  delete parm;
122  if (eText) break;
123  } else {eText = "auth interface violation"; break;}
124 
125 } while(1);
126 
127 // Check if we succeeded
128 //
129  if (!eText)
130  {if (!(authName = AuthProt->Entity.name)) eText = "entity name missing";
131  else {Link->setID(authName,0);
132  Say.Emsg("Auth",Link->Host(),"authenticated as", authName);
133  }
134  }
135 
136 // Check if we failed
137 //
138  if (eText) Say.Emsg("Auth",Link->Host(),"authentication failed;",eText);
139 
140 // Perform final steps here
141 //
142  if (AuthProt) AuthProt->Delete();
143  return (eText == 0);
144 }
145 
146 /******************************************************************************/
147 /* c h k V n I d */
148 /******************************************************************************/
149 
150 char *XrdCmsSecurity::chkVnId(XrdSysError &eDest, const char *vnid,
151  const char *what)
152 {
153  int n = strlen(vnid);
154 
155 // Check if it is too long
156 //
157  if (n > nidMax)
158  {eDest.Emsg("Config", what, "a too long vnid -", vnid);
159  return 0;
160  }
161 
162 // Check for a null vnid
163 //
164  if (!n || !(*vnid))
165  {eDest.Emsg("Config", what, "a null vnid.");
166  return 0;
167  }
168 
169 // Make sure the vnid does not contain invalid characters
170 //
171  const char *cP = vnid;
172  while(*cP && (isalnum(*cP) || ispunct(*cP)) && *cP != '&' && *cP != ' ') cP++;
173  if (*cP)
174  {eDest.Emsg("Config", what, "an invalid vnid -", vnid);
175  return 0;
176  }
177 
178 // All is well
179 //
180  return strdup(vnid);
181 }
182 
183 /******************************************************************************/
184 /* C o n f i g u r e */
185 /******************************************************************************/
186 
187 int XrdCmsSecurity::Configure(const char *Lib, const char *Cfn)
188 {
189  static XrdSysMutex myMutex;
190  XrdSysMutexHelper hlpMtx(&myMutex);
191 
192 // If we aleady have a security interface, return (may happen in client)
193 //
194  if (!Cfn && getProtocol) return 1;
195 
196 // Get the server object and protocol creator
197 //
198  if (!(DHS = XrdSecLoadSecService(&Say, Cfn, (strcmp(Lib,"default") ? Lib:0),
199  &getProtocol)))
200  {Say.Emsg("Config","Unable to create security service object via",Lib);
201  return 0;
202  }
203 
204 // All done
205 //
206  return 1;
207 }
208 
209 /******************************************************************************/
210 /* g e t V n I d */
211 /******************************************************************************/
212 
213 char *XrdCmsSecurity::getVnId(XrdSysError &eDest, const char *cfgFN,
214  const char *nidarg, const char *nidparm,
215  char nidType)
216 {
217  static XrdVERSIONINFODEF (myVer, XrdNID, XrdVNUMBER, XrdVERSION);
218  std::string (*ep)(XrdCmsgetVnIdArgs);
219  std::string nidName;
220 
221 // Read the vnid from a file is so directed
222 //
223  if (*nidarg == '<')
224  {char buff[nidMax+8];
225  int nfd = XrdSysFD_Open(nidarg+1, O_RDONLY);
226  if (nfd < 0)
227  {eDest.Emsg("Config", errno, "open vnid file", nidarg+1);
228  return 0;
229  }
230  int n = read(nfd, buff, sizeof(buff)-1);
231  if (n < 0)
232  {eDest.Emsg("Config", errno, "read vnid file", nidarg+1);
233  close(nfd);
234  return 0;
235  }
236  close(nfd);
237  while(n && buff[n-1] == '\n') n--;
238  buff[n] = 0;
239  return chkVnId(eDest, buff, "vnid file contains");
240  }
241 
242 // Check if the actual vnid is being specified
243 //
244  if (*nidarg == '=') return chkVnId(eDest, nidarg+1, "vnid value is");
245 
246 // Make sure a plugin is being passed
247 //
248  if (*nidarg != '@')
249  {eDest.Emsg("Config", "vnid specification is invalid -", nidarg);
250  return 0;
251  }
252 
253 // Get the entry point of the node ID creator
254 //
255  XrdOucPinLoader nidLib(&eDest, &myVer, "vnid", nidarg+1);
256  ep = (std::string (*)(XrdCmsgetVnIdArgs))(nidLib.Resolve("XrdCmsgetVnId"));
257  if (!ep) return 0;
258 
259 // Get the node ID
260 //
261  nidName = ep(eDest, std::string(cfgFN), std::string(nidparm ? nidparm : ""),
262  nidType, nidMax);
263 
264 // Unload the plugin (we don't need it anymore)
265 //
266  nidLib.Unload();
267 
268 // Verify that the node ID meets specs
269 //
270  return chkVnId(eDest, nidName.c_str(), "vnid plugin returned");
271 }
272 
273 /******************************************************************************/
274 /* g e t T o k e n */
275 /******************************************************************************/
276 
277 const char *XrdCmsSecurity::getToken(int &size, XrdNetAddrInfo *endPoint)
278 {
279 
280 // If not configured, return a null to indicate no authentication required
281 //
282  if (!DHS) {size = 0; return 0;}
283 
284 // Return actual token
285 //
286  return DHS->getParms(size, endPoint);
287 }
288 
289 /******************************************************************************/
290 /* I d e n t i f y */
291 /******************************************************************************/
292 
294  char *authBuff, int abLen)
295 {
296  CmsRRHdr outHdr = {0, kYR_xauth, 0, 0};
297  const char *hName = Link->Host();
298  XrdSecCredentials *cred;
299  XrdSecProtocol *AuthProt = 0;
300  XrdSecParameters AuthParm, *AuthP = 0;
302  const char *eText = 0;
303  int rc, myDlen;
304 
305 // Verify that we are configured
306 //
307  if (!getProtocol && !Configure("libXrdSec.so"))
308  {Say.Emsg("Auth", hName ,"authentication configuration failed.");
309  return 0;
310  }
311 
312 // Obtain the protocol
313 //
314  AuthParm.buffer = (char *)authBuff; AuthParm.size = strlen(authBuff);
315  if (!(AuthProt = getProtocol(hName,*(Link->AddrInfo()),AuthParm,&eMsg)))
316  {Say.Emsg("Auth", hName, "getProtocol() failed;", eMsg.getErrText(rc));
317  return 0;
318  }
319 
320 // Perform standard authentication
321 //
322 do {
323 
324 // Get credentials
325 //
326  if (!(cred = AuthProt->getCredentials(AuthP, &eMsg)))
327  {eText = eMsg.getErrText(rc); break;}
328 
329 // Send credentials to the server
330 //
331  eText = XrdCmsTalk::Request(Link, outHdr, cred->buffer, cred->size);
332  delete cred;
333  if (eText) break;
334 
335 // Get the response header and prepare for next iteration if need be
336 //
337  if ((eText = XrdCmsTalk::Attend(Link,inHdr,authBuff,abLen,myDlen))) break;
338  AuthParm.size = myDlen; AuthParm.buffer = authBuff; AuthP = &AuthParm;
339 
340 } while(inHdr.rrCode == kYR_xauth);
341 
342 // Check if we failed
343 //
344  if (eText) Say.Emsg("Auth", hName, "authentication failed;", eText);
345 
346 // Perform final steps here
347 //
348  if (AuthProt) AuthProt->Delete();
349  return (eText == 0);
350 }
351 
352 /******************************************************************************/
353 /* s e t S e c F u n c */
354 /******************************************************************************/
355 
356 void XrdCmsSecurity::setSecFunc(void *secfP)
357  {getProtocol = (XrdSecGetProt_t)secfP;}
358 
359 /******************************************************************************/
360 /* s e t S y s t e m I D */
361 /******************************************************************************/
362 
363 char *XrdCmsSecurity::setSystemID(XrdOucTList *tp, const char *iVNID,
364  const char *iTag, char iType)
365 {
366  XrdOucTList *tpF;
367  char sidbuff[8192], *sidend = sidbuff+sizeof(sidbuff)-32;
368  char *cP, *sp = sidbuff;
369  char *fMan, *fp, *xp;
370  int n;
371 
372 // Extract out the instance name (we must have one)
373 //
374  const char *instP = getenv("XRDINSTANCE");
375  if (instP) instP = index(instP, ' ');
376  if (!instP) return (char *)"!envar XRDINSTANCE undefined.";
377  while(*instP && *instP == ' ') instP++;
378  if (!(*instP)) return (char *)"!envar XRDINSTANCE invalid.";
379 
380 // The system ID starts with the semi-unique name of this node unless it's
381 // a vnetid, in which case it's unique withn this cluster. Note that vnetid's
382 // always start with an asterisk. It does not otherwise.
383 //
384  if (iVNID)
385  {*sp++ = '*'; *sp++ = iType; *sp++ = '-';
386  strcpy(sp, iVNID);
387  sp += strlen(iVNID);
388  } else {
389  *sp++ = iType; *sp++ = '-';
390  strcpy(sp, instP);
391  sp += strlen(instP);
392  }
393 
394 // Export the vnid
395 //
396  *sp = 0;
397  XrdOucEnv::Export("XRDCMSVNID", sidbuff);
398  *sp++ = ' '; cP = sp;
399 
400 // Insert tag if we have one
401 //
402  if (iTag) sp += sprintf(sp, "%s.", iTag);
403 
404 // Develop a unique cluster name for this cluster
405 //
406  if (!tp) sp += sprintf(sp, "%s", instP);
407  else {tpF = tp;
408  fMan = tp->text + strlen(tp->text) - 1;
409  while((tp = tp->next))
410  {fp = fMan; xp = tp->text + strlen(tp->text) - 1;
411  do {if (*fp != *xp) break;
412  xp--;
413  } while(fp-- != tpF->text);
414  if ((n = xp - tp->text + 1) > 0)
415  {sp += sprintf(sp, "%d", tp->val);
416  if (sp+n >= sidend) return (char *)0;
417  strncpy(sp, tp->text, n); sp += n;
418  }
419  }
420  sp += sprintf(sp, "%d", tpF->val);
421  n = strlen(tpF->text);
422  if (sp+n >= sidend) return (char *)0;
423  strcpy(sp, tpF->text); sp += n;
424  }
425 
426 // Set envar to hold the cluster name
427 //
428  *sp = '\0';
429  XrdOucEnv::Export("XRDCMSCLUSTERID", cP);
430 
431 // Export the full virtual network ID
432 //
433  XrdOucEnv::Export("XRDCMSSYSID", sidbuff);
434 
435 // Return the system ID
436 //
437  return strdup(sidbuff);
438 }
#define XrdCmsgetVnIdArgs
Definition: XrdCmsVnId.hh:70
static XrdSysError eDest(0,"crypto_")
ssize_t read(int fildes, void *buf, size_t nbyte)
#define close(a)
Definition: XrdPosix.hh:43
XrdSecProtocol *(* XrdSecGetProt_t)(const char *hostname, XrdNetAddrInfo &endPoint, XrdSecParameters &sectoken, XrdOucErrInfo *einfo)
Typedef to simplify the encoding of methods returning XrdSecProtocol.
XrdSecService * XrdSecLoadSecService(XrdSysError *eDest, const char *cfn, const char *seclib, XrdSecGetProt_t *getP, XrdSecProtector **proP)
#define eMsg(x)
static const char * getToken(int &size, XrdNetAddrInfo *endPoint)
static char * getVnId(XrdSysError &eDest, const char *cfgFN, const char *nidlib, const char *nidparm, char nidType)
static char * setSystemID(XrdOucTList *tp, const char *iVNID, const char *iTag, char iType)
static int Authenticate(XrdLink *Link, const char *Token, int tlen)
static int Configure(const char *Lib, const char *Cfn=0)
static int Identify(XrdLink *Link, XrdCms::CmsRRHdr &inHdr, char *authBuff, int abLen)
static void setSecFunc(void *secfP)
static const char * Attend(XrdLink *Link, XrdCms::CmsRRHdr &Hdr, char *buff, int blen, int &rlen, int tmo=5000)
Definition: XrdCmsTalk.cc:46
static const char * Request(XrdLink *Link, XrdCms::CmsRRHdr &Hdr, char *buff, int blen)
Definition: XrdCmsTalk.cc:100
static int Export(const char *Var, const char *Val)
Definition: XrdOucEnv.cc:188
void * Resolve(const char *symbl, int mcnt=1)
void Unload(bool dodel=false)
XrdOucTList * next
Definition: XrdOucTList.hh:45
char * text
Definition: XrdOucTList.hh:46
XrdNetAddrInfo * addrInfo
Entity's connection details.
Definition: XrdSecEntity.hh:80
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
XrdSecEntity Entity
virtual XrdSecCredentials * getCredentials(XrdSecParameters *parm=0, XrdOucErrInfo *einfo=0)=0
virtual void Delete()=0
Delete the protocol object. DO NOT use C++ delete() on this object.
virtual int Authenticate(XrdSecCredentials *cred, XrdSecParameters **parms, XrdOucErrInfo *einfo=0)=0
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
XrdVERSIONINFODEF(myVersion, cmsclient, XrdVNUMBER, XrdVERSION)
XrdSysError Say
kXR_char rrCode
Definition: YProtocol.hh:84
@ kYR_xauth
Definition: YProtocol.hh:117
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.