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 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.
char * name
Entity's name.
char * host
Entity's host name dnr dependent.
XrdSecEntity Entity
XrdSecProtocol(const char *pName)
Constructor.

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