XRootD
XrdSecProtocolkrb5 Class Reference
+ Inheritance diagram for XrdSecProtocolkrb5:
+ Collaboration diagram for XrdSecProtocolkrb5:

Public Member Functions

 XrdSecProtocolkrb5 (const char *KP, const char *hname, XrdNetAddrInfo &endPoint)
 
int Authenticate (XrdSecCredentials *cred, XrdSecParameters **parms, XrdOucErrInfo *einfo=0)
 
void Delete ()
 Delete the protocol object. DO NOT use C++ delete() on this object. More...
 
XrdSecCredentialsgetCredentials (XrdSecParameters *parm=0, XrdOucErrInfo *einfo=0)
 
- Public Member Functions inherited from XrdSecProtocol
 XrdSecProtocol (const char *pName)
 Constructor. More...
 
virtual int Decrypt (const char *inbuff, int inlen, XrdSecBuffer **outbuff)
 
virtual int Encrypt (const char *inbuff, int inlen, XrdSecBuffer **outbuff)
 
virtual int getKey (char *buff=0, int size=0)
 
virtual bool needTLS ()
 Check if this protocol requires TLS to properly function. More...
 
virtual int setKey (char *buff, int size)
 
virtual int Sign (const char *inbuff, int inlen, XrdSecBuffer **outbuff)
 
virtual int Verify (const char *inbuff, int inlen, const char *sigbuff, int siglen)
 

Static Public Member Functions

static char * getPrincipal ()
 
static int Init (XrdOucErrInfo *einfo, char *KP=0, char *kfn=0)
 
static void setClientOpts (int opts)
 
static void setExpFile (char *expfile)
 
static void setOpts (int opts)
 
static void setParms (char *param)
 

Friends

class XrdSecProtocolDummy
 

Additional Inherited Members

- Public Attributes inherited from XrdSecProtocol
XrdSecEntity Entity
 
- Protected Member Functions inherited from XrdSecProtocol
virtual ~XrdSecProtocol ()
 Destructor (prevents use of direct delete). More...
 

Detailed Description

Definition at line 90 of file XrdSecProtocolkrb5.cc.

Constructor & Destructor Documentation

◆ XrdSecProtocolkrb5()

XrdSecProtocolkrb5::XrdSecProtocolkrb5 ( const char *  KP,
const char *  hname,
XrdNetAddrInfo endPoint 
)
inline

Definition at line 119 of file XrdSecProtocolkrb5.cc.

123  {Service = (KP ? strdup(KP) : 0);
124  Entity.host = strdup(hname);
125  epAddr = endPoint;
126  Entity.addrInfo = &epAddr;
127  CName[0] = '?'; CName[1] = '\0';
128  Entity.name = CName;
129  Step = 0;
130  AuthContext = 0;
131  AuthClientContext = 0;
132  Ticket = 0;
133  Creds = 0;
134  }
#define XrdSecPROTOIDENT
XrdNetAddrInfo * addrInfo
Entity's connection details.
Definition: XrdSecEntity.hh:80
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
char * host
Entity's host name dnr dependent.
Definition: XrdSecEntity.hh:70
XrdSecEntity Entity
XrdSecProtocol(const char *pName)
Constructor.

References XrdSecEntity::addrInfo, XrdSecProtocol::Entity, XrdSecEntity::host, XrdSecEntity::name, and XrdSsi::Service.

Member Function Documentation

◆ Authenticate()

int XrdSecProtocolkrb5::Authenticate ( XrdSecCredentials cred,
XrdSecParameters **  parms,
XrdOucErrInfo einfo = 0 
)
virtual

Authenticate a client.

Parameters
credCredentials supplied by the client.
parmsPlace where the address of additional authentication data is to be placed for another autrhentication handshake.
einfoThe error information object where error messages should be placed. The messages are returned to the client. Should einfo be null, messages should be written to stderr.
Returns
> 0 -> parms present (more authentication needed) = 0 -> Entity present (authentication suceeded) < 0 -> einfo present (error has occurred)

Implements XrdSecProtocol.

Definition at line 414 of file XrdSecProtocolkrb5.cc.

417 {
418  krb5_data inbuf; /* Kerberos data */
419  krb5_address ipadd;
420  krb_rc rc = 0;
421  char *iferror = 0;
422 
423 // Check if we have any credentials or if no credentials really needed.
424 // In either case, use host name as client name
425 //
426  if (cred->size <= (int)XrdSecPROTOIDLEN || !cred->buffer)
427  {strncpy(Entity.prot, "host", sizeof(Entity.prot));
428  return 0;
429  }
430 
431 // Check if this is a recognized protocol
432 //
433  if (strcmp(cred->buffer, XrdSecPROTOIDENT))
434  {char emsg[256];
435  snprintf(emsg, sizeof(emsg),
436  "Authentication protocol id mismatch (%.4s != %.4s).",
437  XrdSecPROTOIDENT, cred->buffer);
438  Fatal(error, EINVAL, emsg, Principal);
439  return -1;
440  }
441 
442  CLDBG("protocol check");
443 
444  char printit[4096];
445  sprintf(printit,"Step is %d",Step);
446  CLDBG(printit);
447 // If this is not the first call the buffer contains a forwarded token:
448 // we save it into a file and return signalling the end of the hand-shake
449 //
450  if (Step > 0)
451  {if ((rc = exp_krbTkn(cred, error)))
452  iferror = (char *)"Unable to export the token to file";
453  if (rc && iferror) {
454  krbContext.UnLock();
455  return Fatal(error, EINVAL, iferror, Principal, rc);
456  }
457  krbContext.UnLock();
458 
459  return 0;
460  }
461 
462  CLDBG("protocol check");
463 
464 // Increment the step
465 //
466  Step += 1;
467 
468 // Indicate who we are
469 //
470  strncpy(Entity.prot, XrdSecPROTOIDENT, sizeof(Entity.prot));
471 
472 // Create a kerberos style ticket and obtain the kerberos mutex
473 //
474 
475  CLDBG("Context Lock");
476 
477  inbuf.length = cred->size -XrdSecPROTOIDLEN;
478  inbuf.data = &cred->buffer[XrdSecPROTOIDLEN];
479 
480  krbContext.Lock();
481 
482 // Check if whether the IP address in the credentials must match that of
483 // the incoming host.
484 //
485  CLDBG("Context Locked");
486  if (!(XrdSecProtocolkrb5::options & XrdSecNOIPCHK))
487  {SetAddr(ipadd);
488  iferror = (char *)"Unable to validate ip address;";
489  if (!(rc=krb5_auth_con_init(krb_context, &AuthContext)))
490  rc=krb5_auth_con_setaddrs(krb_context, AuthContext, NULL, &ipadd);
491  }
492 
493 // Decode the credentials and extract client's name
494 //
495  if (!rc)
496  {if ((rc = krb5_rd_req(krb_context, &AuthContext, &inbuf,
497  (krb5_const_principal)krb_principal,
498  krb_keytab, NULL, &Ticket)))
499  iferror = (char *)"Unable to authenticate credentials;";
500  else if ((rc = krb5_aname_to_localname(krb_context,
501  Ticket->enc_part2->client,
502  sizeof(CName)-1, CName)))
503  iferror = (char *)"Unable to extract client name;";
504  }
505 
506 // Make sure the name is null-terminated
507 //
508  CName[sizeof(CName)-1] = '\0';
509 
510 // If requested, ask the client for a forwardable token
511  int hsrc = 0;
512  if (!rc && XrdSecProtocolkrb5::options & XrdSecEXPTKN) {
513  // We just ask for more; the client knows what to send over
514  hsrc = 1;
515  // We need to fill-in a fake buffer
516  int len = strlen("fwdtgt") + 1;
517  char *buf = (char *) malloc(len);
518  memcpy(buf, "fwdtgt", len-1);
519  buf[len-1] = 0;
520  *parms = new XrdSecParameters(buf, len);
521  }
522 
523 // Release any allocated storage at this point and unlock mutex
524 //
525  krbContext.UnLock();
526 
527 // Diagnose any errors
528 //
529  if (rc && iferror)
530  return Fatal(error, EACCES, iferror, Principal, rc);
531 
532 // All done
533 //
534  return hsrc;
535 }
XrdSecBuffer XrdSecParameters
#define CLDBG(x)
#define XrdSecPROTOIDLEN
#define XrdSecEXPTKN
krb5_error_code krb_rc
#define XrdSecNOIPCHK
int emsg(int rc, char *msg)
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
Definition: XrdSecEntity.hh:67
char * buffer
Pointer to the buffer.
int size
Size of the buffer or length of data in the buffer.

References XrdSecBuffer::buffer, CLDBG, emsg(), XrdSecProtocol::Entity, Fatal(), XrdSysMutex::Lock(), XrdSecEntity::prot, XrdSecBuffer::size, XrdSysMutex::UnLock(), XrdSecEXPTKN, XrdSecNOIPCHK, XrdSecPROTOIDENT, and XrdSecPROTOIDLEN.

+ Here is the call graph for this function:

◆ Delete()

void XrdSecProtocolkrb5::Delete ( )
virtual

Delete the protocol object. DO NOT use C++ delete() on this object.

Implements XrdSecProtocol.

Definition at line 200 of file XrdSecProtocolkrb5.cc.

201 {
202  if (Parms) {free(Parms); Parms = 0;}
203  if (Creds) krb5_free_creds(krb_context, Creds);
204  if (Ticket) krb5_free_ticket(krb_context, Ticket);
205  if (AuthContext) krb5_auth_con_free(krb_context, AuthContext);
206  if (AuthClientContext) krb5_auth_con_free(krb_client_context, AuthClientContext);
207  if (Entity.host) free(Entity.host);
208  if (Service) free(Service);
209  delete this;
210 }

References XrdSecProtocol::Entity, XrdSecEntity::host, and XrdSsi::Service.

◆ getCredentials()

XrdSecCredentials * XrdSecProtocolkrb5::getCredentials ( XrdSecParameters parm = 0,
XrdOucErrInfo einfo = 0 
)
virtual

Generate client credentials to be used in the authentication process.

Parameters
parmPointer to the information returned by the server either in the initial login response or the authmore response.
einfoThe error information object where error messages should be placed. The messages are returned to the client. Should einfo be null, messages should be written to stderr.
Returns
Success: Pointer to credentials to sent to the server. The caller is responsible for deleting the object. Failure: Null pointer with einfo, if supplied, containing the reason for the failure.

Implements XrdSecProtocol.

Definition at line 216 of file XrdSecProtocolkrb5.cc.

218 {
219  char *buff;
220  int bsz;
221  krb_rc rc;
222  krb5_data outbuf;
223  CLDBG("getCredentials");
224 // Supply null credentials if so needed for this protocol
225 //
226  if (!Service)
227  {CLDBG("Null credentials supplied.");
228  return new XrdSecCredentials(0,0);
229  }
230 
231  CLDBG("context lock");
232  krbClientContext.Lock();
233  CLDBG("context locked");
234 
235 // We support passing the credential cache path via Url parameter
236 //
237  char *ccn = (error && error->getEnv()) ? error->getEnv()->Get("xrd.k5ccname") : 0;
238  const char *kccn = ccn ? (const char *)ccn : getenv("KRB5CCNAME");
239  char ccname[128];
240  if (!kccn)
241  {snprintf(ccname, 128, "/tmp/krb5cc_%d", geteuid());
242  if (access(ccname, R_OK) == 0)
243  {kccn = ccname;}
244  }
245  CLDBG((kccn ? kccn : "credentials cache unset"));
246 
247 // Initialize the context and get the cache default.
248 //
249  if ((rc = krb5_init_context(&krb_client_context)))
250  {krbClientContext.UnLock();
251  Fatal(error, ENOPROTOOPT, "Kerberos initialization failed", Service, rc);
252  return (XrdSecCredentials *)0;
253  }
254 
255  CLDBG("init context");
256 
257 // Set the name of the default credentials cache for the Kerberos context
258 //
259  if ((rc = krb5_cc_set_default_name(krb_client_context, kccn)))
260  {krbClientContext.UnLock();
261  Fatal(error, ENOPROTOOPT, "Kerberos default credentials cache setting failed", Service, rc);
262  return (XrdSecCredentials *)0;
263  }
264 
265  CLDBG("cc set default name");
266 
267 // Obtain the default cache location
268 //
269  if ((rc = krb5_cc_default(krb_client_context, &krb_client_ccache)))
270  {krbClientContext.UnLock();
271  Fatal(error, ENOPROTOOPT, "Unable to locate cred cache", Service, rc);
272  return (XrdSecCredentials *)0;
273  }
274 
275  CLDBG("cc default");
276 // Check if the server asked for a forwardable ticket
277 //
278  char *pfwd = 0;
279  if ((pfwd = (char *) strstr(Service,",fwd")))
280  {
281  client_options |= XrdSecEXPTKN;
282  *pfwd = 0;
283  }
284 
285 // Clear outgoing ticket and lock the kerberos context
286 //
287  outbuf.length = 0; outbuf.data = 0;
288 
289 // If this is not the first call, we are asked to send over a delegated ticket:
290 // we must create it first
291 // we save it into a file and return signalling the end of the hand-shake
292 //
293 
294  if (Step > 0)
295  {if ((rc = get_krbFwdCreds(Service, &outbuf)))
296  {krbClientContext.UnLock();
297  Fatal(error, ESRCH, "Unable to get forwarded credentials", Service, rc);
298  return (XrdSecCredentials *)0;
299  } else
300  {bsz = XrdSecPROTOIDLEN+outbuf.length;
301  if (!(buff = (char *)malloc(bsz)))
302  {krbClientContext.UnLock();
303  Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service);
304  return (XrdSecCredentials *)0;
305  }
306  strcpy(buff, XrdSecPROTOIDENT);
307  memcpy((void *)(buff+XrdSecPROTOIDLEN),
308  (const void *)outbuf.data, (size_t)outbuf.length);
309  CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service);
310  if (outbuf.data) free(outbuf.data);
311  krbClientContext.UnLock();
312  return new XrdSecCredentials(buff, bsz);
313  }
314  }
315 
316 // Increment the step
317 //
318  Step += 1;
319 
320 // Get a service ticket for this principal
321 //
322  bool caninittkn = (isatty(0) == 0 || isatty(1) == 0) ? 0 : 1;
323  const char *reinitcmd = (client_options & XrdSecEXPTKN) ? "kinit -f" : "kinit";
324  bool notdone = 1;
325  bool reinitdone = 0;
326  while (notdone)
327  {if ((rc = (krb_rc)get_krbCreds(Service, &Creds)))
328  { if (!(client_options & XrdSecINITTKN) || reinitdone || !caninittkn)
329  {krbClientContext.UnLock();
330  const char *m = (!(client_options & XrdSecINITTKN)) ?
331  "No or invalid credentials" : "Unable to get credentials";
332  Fatal(error, ESRCH, m, Service, rc);
333  return (XrdSecCredentials *)0;
334  } else {// Need to re-init
335  CLPRT("Ticket missing or invalid: re-init ");
336  rc = system(reinitcmd);
337  CLDBG("getCredentials: return code from '"<<reinitcmd<<
338  "': "<< rc);
339  reinitdone = 1;
340  continue;
341  }
342  }
343  if (client_options & XrdSecEXPTKN)
344  {// Make sure the ticket is forwardable
345  if (!(Creds->ticket_flags & TKT_FLG_FORWARDABLE))
346  { if ((client_options & XrdSecINITTKN) && !reinitdone && caninittkn)
347  { // Need to re-init
348  CLPRT("Existing ticket is not forwardable: re-init ");
349  rc = system(reinitcmd);
350  CLDBG("getCredentials: return code from '"<<reinitcmd<<
351  "': "<< rc);
352  reinitdone = 1;
353  continue;
354  } else {
355  krbClientContext.UnLock();
356  Fatal(error, ESRCH, "Existing ticket is not forwardable: cannot continue",
357  Service, rc);
358  return (XrdSecCredentials *)0;
359  }
360  }
361  }
362  // We are done
363  notdone = 0;
364  }
365 
366 // Set the RET_TIME flag in the authentication context
367 //
368  if ((rc = krb5_auth_con_init(krb_client_context, &AuthClientContext)))
369  {krbClientContext.UnLock();
370  Fatal(error, ESRCH, "Unable to init a new auth context", Service, rc);
371  return (XrdSecCredentials *)0;
372  }
373 
374 // Generate a kerberos-style authentication message
375 //
376  rc = krb5_mk_req_extended(krb_client_context, &AuthClientContext,
377  AP_OPTS_USE_SESSION_KEY,(krb5_data *)0, Creds,&outbuf);
378 
379 // Check if all succeeded. If so, copy the ticket into the buffer. We wish
380 // we could place the ticket directly into the buffer but architectural
381 // differences won't allow us that optimization.
382 //
383  if (!rc)
384  {bsz = XrdSecPROTOIDLEN+outbuf.length;
385  if (!(buff = (char *)malloc(bsz)))
386  {krbClientContext.UnLock();
387  Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service);
388  return (XrdSecCredentials *)0;
389  }
390  strcpy(buff, XrdSecPROTOIDENT);
391  memcpy((void *)(buff+XrdSecPROTOIDLEN),
392  (const void *)outbuf.data, (size_t)outbuf.length);
393  CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service);
394  if (outbuf.data) free(outbuf.data);
395  krbClientContext.UnLock();
396  return new XrdSecCredentials(buff, bsz);
397  }
398 
399 // Diagnose the failure
400 //
401  if (outbuf.data) free(outbuf.data);
402  krbClientContext.UnLock();
403  Fatal(error, EACCES, "Unable to get credentials", Service, rc);
404  return (XrdSecCredentials *)0;
405 }
int access(const char *path, int amode)
XrdSecBuffer XrdSecCredentials
#define CLPRT(x)
#define XrdSecINITTKN
Generic structure to pass security information back and forth.

References access(), CLDBG, CLPRT, Fatal(), XrdOucEnv::Get(), XrdOucErrInfo::getEnv(), XrdSysMutex::Lock(), XrdSsi::Service, XrdSysMutex::UnLock(), XrdSecEXPTKN, XrdSecINITTKN, XrdSecPROTOIDENT, and XrdSecPROTOIDLEN.

+ Here is the call graph for this function:

◆ getPrincipal()

static char* XrdSecProtocolkrb5::getPrincipal ( )
inlinestatic

Definition at line 102 of file XrdSecProtocolkrb5.cc.

102 {return Principal;}

Referenced by XrdSecProtocolkrb5Init().

+ Here is the caller graph for this function:

◆ Init()

int XrdSecProtocolkrb5::Init ( XrdOucErrInfo einfo,
char *  KP = 0,
char *  kfn = 0 
)
static

Definition at line 544 of file XrdSecProtocolkrb5.cc.

545 {
546  krb_rc rc;
547  char buff[2048];
548 
549 // Create a kerberos context. There is one such context per protocol object.
550 //
551 
552 // If we have no principal then this is a client-side call: initializations are done
553 // in getCredentials to allow for multiple client principals
554 //
555  if (!KP) return 0;
556 
557  if ((rc = krb5_init_context(&krb_context)))
558  return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc);
559 
560 // Obtain the default cache location
561 //
562  if ((rc = krb5_cc_default(krb_context, &krb_ccache)))
563  return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc);
564 
565 // Try to resolve the keyfile name
566 //
567  if (kfn && *kfn)
568  {if ((rc = krb5_kt_resolve(krb_context, kfn, &krb_keytab)))
569  {snprintf(buff, sizeof(buff), "Unable to find keytab '%s';", kfn);
570  return Fatal(erp, ESRCH, buff, Principal, rc);
571  }
572  } else {
573  krb5_kt_default(krb_context, &krb_keytab);
574  }
575 
576 // Keytab name
577 //
578  char krb_kt_name[1024];
579  if ((rc = krb5_kt_get_name(krb_context, krb_keytab, &krb_kt_name[0], 1024)))
580  {snprintf(buff, sizeof(buff), "Unable to get keytab name;");
581  return Fatal(erp, ESRCH, buff, Principal, rc);
582  }
583 
584 // Check if we can read access the keytab file
585 //
586  krb5_kt_cursor ktc;
587  if ((rc = krb5_kt_start_seq_get(krb_context, krb_keytab, &ktc)))
588  {snprintf(buff, sizeof(buff), "Unable to start sequence on the keytab file %s", krb_kt_name);
589  return Fatal(erp, EPERM, buff, Principal, rc);
590  }
591  if ((rc = krb5_kt_end_seq_get(krb_context, krb_keytab, &ktc)))
592  {snprintf(buff, sizeof(buff), "WARNING: unable to end sequence on the keytab file %s", krb_kt_name);
593  CLPRT(buff);
594  }
595 
596 // Now, extract the "principal/instance@realm" from the stream
597 //
598  if ((rc = krb5_parse_name(krb_context,KP,&krb_principal)))
599  return Fatal(erp, EINVAL, "Cannot parse service name", KP, rc);
600 
601 // Establish the correct principal to use
602 //
603  if ((rc = krb5_unparse_name(krb_context,(krb5_const_principal)krb_principal,
604  (char **)&Principal)))
605  return Fatal(erp, EINVAL, "Unable to unparse principal;", KP, rc);
606 
607 // All done
608 //
609  return 0;
610 }

References CLPRT, and Fatal().

Referenced by XrdSecProtocolkrb5Init().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ setClientOpts()

static void XrdSecProtocolkrb5::setClientOpts ( int  opts)
inlinestatic

Definition at line 107 of file XrdSecProtocolkrb5.cc.

107 {client_options = opts;}
struct myOpts opts

References opts.

Referenced by XrdSecProtocolkrb5Init().

+ Here is the caller graph for this function:

◆ setExpFile()

static void XrdSecProtocolkrb5::setExpFile ( char *  expfile)
inlinestatic

Definition at line 109 of file XrdSecProtocolkrb5.cc.

110  {if (expfile)
111  {int lt = strlen(expfile);
112  lt = (lt >= XrdSecMAXPATHLEN) ?
113  XrdSecMAXPATHLEN -1 : lt;
114  memcpy(ExpFile, expfile, lt);
115  ExpFile[lt] = 0;
116  }
117  }
#define XrdSecMAXPATHLEN

References XrdSecMAXPATHLEN.

Referenced by XrdSecProtocolkrb5Init().

+ Here is the caller graph for this function:

◆ setOpts()

static void XrdSecProtocolkrb5::setOpts ( int  opts)
inlinestatic

Definition at line 106 of file XrdSecProtocolkrb5.cc.

106 {options = opts;}

References opts.

Referenced by XrdSecProtocolkrb5Init().

+ Here is the caller graph for this function:

◆ setParms()

static void XrdSecProtocolkrb5::setParms ( char *  param)
inlinestatic

Definition at line 108 of file XrdSecProtocolkrb5.cc.

108 {Parms = param;}

Referenced by XrdSecProtocolkrb5Init().

+ Here is the caller graph for this function:

Friends And Related Function Documentation

◆ XrdSecProtocolDummy

friend class XrdSecProtocolDummy
friend

Definition at line 93 of file XrdSecProtocolkrb5.cc.


The documentation for this class was generated from the following file: