XRootD
Loading...
Searching...
No Matches
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.
 
XrdSecCredentialsgetCredentials (XrdSecParameters *parm=0, XrdOucErrInfo *einfo=0)
 
- Public Member Functions inherited from XrdSecProtocol
 XrdSecProtocol (const char *pName)
 Constructor.
 
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.
 
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).
 

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.
char * name
Entity's name.
char * host
Entity's host name dnr dependent.
XrdSecEntity Entity

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

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 succeeded) < 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)
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, and XrdSecEntity::host.

◆ 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}
#define access(a, b)
Definition XrdPosix.hh:44
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(), 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 Symbol 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: