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 91 of file XrdSecProtocolkrb5.cc.

Constructor & Destructor Documentation

◆ XrdSecProtocolkrb5()

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

Definition at line 120 of file XrdSecProtocolkrb5.cc.

124  {Service = (KP ? strdup(KP) : 0);
125  Entity.host = strdup(hname);
126  epAddr = endPoint;
127  Entity.addrInfo = &epAddr;
128  CName[0] = '?'; CName[1] = '\0';
129  Entity.name = CName;
130  Step = 0;
131  AuthContext = 0;
132  AuthClientContext = 0;
133  Ticket = 0;
134  Creds = 0;
135  }
#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 416 of file XrdSecProtocolkrb5.cc.

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

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

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 218 of file XrdSecProtocolkrb5.cc.

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

103 {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 568 of file XrdSecProtocolkrb5.cc.

569 {
570  krb_rc rc;
571  char buff[2048];
572 
573 // Create a kerberos context. There is one such context per protocol object.
574 //
575 
576 // If we have no principal then this is a client-side call: initializations are done
577 // in getCredentials to allow for multiple client principals
578 //
579  if (!KP) return 0;
580 
581  if ((rc = krb5_init_context(&krb_context)))
582  return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc);
583 
584 // Obtain the default cache location
585 //
586  if ((rc = krb5_cc_default(krb_context, &krb_ccache)))
587  return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc);
588 
589 // Try to resolve the keyfile name
590 //
591  if (kfn && *kfn)
592  {if ((rc = krb5_kt_resolve(krb_context, kfn, &krb_keytab)))
593  {snprintf(buff, sizeof(buff), "Unable to find keytab '%s';", kfn);
594  return Fatal(erp, ESRCH, buff, Principal, rc);
595  }
596  } else {
597  krb5_kt_default(krb_context, &krb_keytab);
598  }
599 
600 // Keytab name
601 //
602  char krb_kt_name[1024];
603  if ((rc = krb5_kt_get_name(krb_context, krb_keytab, &krb_kt_name[0], 1024)))
604  {snprintf(buff, sizeof(buff), "Unable to get keytab name;");
605  return Fatal(erp, ESRCH, buff, Principal, rc);
606  }
607 
608 // Check if we can read access the keytab file
609 //
610  krb5_kt_cursor ktc;
611  if ((rc = krb5_kt_start_seq_get(krb_context, krb_keytab, &ktc)))
612  {snprintf(buff, sizeof(buff), "Unable to start sequence on the keytab file %s", krb_kt_name);
613  return Fatal(erp, EPERM, buff, Principal, rc);
614  }
615  if ((rc = krb5_kt_end_seq_get(krb_context, krb_keytab, &ktc)))
616  {snprintf(buff, sizeof(buff), "WARNING: unable to end sequence on the keytab file %s", krb_kt_name);
617  CLPRT(buff);
618  }
619 
620 // Now, extract the "principal/instance@realm" from the stream
621 //
622  if ((rc = krb5_parse_name(krb_context,KP,&krb_principal)))
623  return Fatal(erp, EINVAL, "Cannot parse service principal name", KP, rc);
624 
625 // Establish the correct principal to use
626 //
627  if ((rc = krb5_unparse_name(krb_context,(krb5_const_principal)krb_principal,
628  (char **)&Principal)))
629  return Fatal(erp, EINVAL, "Unable to unparse service principal;", KP, rc);
630 
631 // All done
632 //
633  return 0;
634 }

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 108 of file XrdSecProtocolkrb5.cc.

108 {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 110 of file XrdSecProtocolkrb5.cc.

111  {if (expfile)
112  {int lt = strlen(expfile);
113  lt = (lt >= XrdSecMAXPATHLEN) ?
114  XrdSecMAXPATHLEN -1 : lt;
115  memcpy(ExpFile, expfile, lt);
116  ExpFile[lt] = 0;
117  }
118  }
#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 107 of file XrdSecProtocolkrb5.cc.

107 {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 109 of file XrdSecProtocolkrb5.cc.

109 {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 94 of file XrdSecProtocolkrb5.cc.


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