XRootD
Loading...
Searching...
No Matches
XrdSecProtocolkrb5.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d S e c P r o t o c o l k r b 5 . c c */
4/* */
5/* (c) 2003 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/* Modifications: */
10/* - January 2007: add support for forwarded tickets */
11/* (author: G. Ganis, CERN) */
12/* */
13/* This file is part of the XRootD software suite. */
14/* */
15/* XRootD is free software: you can redistribute it and/or modify it under */
16/* the terms of the GNU Lesser General Public License as published by the */
17/* Free Software Foundation, either version 3 of the License, or (at your */
18/* option) any later version. */
19/* */
20/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
21/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
22/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
23/* License for more details. */
24/* */
25/* You should have received a copy of the GNU Lesser General Public License */
26/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
27/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
28/* */
29/* The copyright holder's institutional names and contributor's names may not */
30/* be used to endorse or promote products derived from this software without */
31/* specific prior written permission of the institution or contributor. */
32/******************************************************************************/
33
34#include <unistd.h>
35#include <cctype>
36#include <cerrno>
37#include <cstdlib>
38#include <strings.h>
39#include <cstdio>
40#include <sys/param.h>
41#include <pwd.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44
45extern "C" {
46#include "krb5.h"
47#ifdef HAVE_ET_COM_ERR_H
48#include "et/com_err.h"
49#else
50#include "com_err.h"
51#endif
52}
53
54#include "XrdVersion.hh"
55
57#include "XrdNet/XrdNetUtils.hh"
59#include "XrdOuc/XrdOucEnv.hh"
62#include "XrdSys/XrdSysPwd.hh"
65
66/******************************************************************************/
67/* D e f i n e s */
68/******************************************************************************/
69
70#define krb_etxt(x) (char *)error_message(x)
71
72#define XrdSecPROTOIDENT "krb5"
73#define XrdSecPROTOIDLEN sizeof(XrdSecPROTOIDENT)
74#define XrdSecNOIPCHK 0x0001
75#define XrdSecEXPTKN 0x0002
76#define XrdSecINITTKN 0x0004
77#define XrdSecDEBUG 0x1000
78
79#define XrdSecMAXPATHLEN 4096
80
81#define CLDBG(x) if (client_options & XrdSecDEBUG) std::cerr <<"Seckrb5: " <<x <<std::endl;
82#define CLPRT(x) std::cerr <<"Seckrb5: " <<x <<std::endl;
83
84typedef krb5_error_code krb_rc;
85
86/******************************************************************************/
87/* X r d S e c P r o t o c o l k r b 5 C l a s s */
88/******************************************************************************/
89
91{
92public:
93friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor
94
96 XrdSecParameters **parms,
97 XrdOucErrInfo *einfo=0);
98
100 XrdOucErrInfo *einfo=0);
101
102static char *getPrincipal() {return Principal;}
103
104static int Init(XrdOucErrInfo *einfo, char *KP=0, char *kfn=0);
105
106static void setOpts(int opts) {options = opts;}
107static void setClientOpts(int opts) {client_options = opts;}
108static void setParms(char *param) {Parms = param;}
109static void setExpFile(char *expfile)
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 }
118
119 XrdSecProtocolkrb5(const char *KP,
120 const char *hname,
121 XrdNetAddrInfo &endPoint)
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 }
135
136 void Delete();
137
138private:
139
140 ~XrdSecProtocolkrb5() {} // Delete() does it all
141
142static int Fatal(XrdOucErrInfo *erp,int rc,const char *msg1,char *KP=0,int krc=0);
143static int get_krbCreds(char *KP, krb5_creds **krb_creds);
144 void SetAddr(krb5_address &ipadd);
145
146static XrdSysMutex krbContext; // Server
147static XrdSysMutex krbClientContext;// Client
148static int options; // Server
149static int client_options;// Client
150static krb5_context krb_context; // Server
151static krb5_context krb_client_context; // Client
152static krb5_ccache krb_client_ccache; // Client
153static krb5_ccache krb_ccache; // Server
154static krb5_keytab krb_keytab; // Server
155static krb5_principal krb_principal; // Server
156
157static char *Principal; // Server's principal name
158static char *Parms; // Server parameters
159
160static char ExpFile[XrdSecMAXPATHLEN]; // Server: (template for)
161 // file to export token
162int exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp);
163int get_krbFwdCreds(char *KP, krb5_data *outdata);
164
165XrdNetAddrInfo epAddr;
166char CName[256]; // Kerberos limit
167char *Service; // Target principal for client
168char Step; // Indicates at which step we are
169krb5_auth_context AuthContext; // Authetication context
170krb5_auth_context AuthClientContext; // Authetication context
171krb5_ticket *Ticket; // Ticket associated to client authentication
172krb5_creds *Creds; // Client: credentials
173};
174
175/******************************************************************************/
176/* S t a t i c D a t a */
177/******************************************************************************/
178
179XrdSysMutex XrdSecProtocolkrb5::krbContext; // Server
180XrdSysMutex XrdSecProtocolkrb5::krbClientContext; // Client
181
182int XrdSecProtocolkrb5::client_options = 0;// Client
183int XrdSecProtocolkrb5::options = 0; // Server
184krb5_context XrdSecProtocolkrb5::krb_context; // Server
185krb5_context XrdSecProtocolkrb5::krb_client_context; // Client
186krb5_ccache XrdSecProtocolkrb5::krb_client_ccache; // Client
187krb5_ccache XrdSecProtocolkrb5::krb_ccache; // Server
188krb5_keytab XrdSecProtocolkrb5::krb_keytab = NULL; // Server
189krb5_principal XrdSecProtocolkrb5::krb_principal; // Server
190
191char *XrdSecProtocolkrb5::Principal = 0; // Server
192char *XrdSecProtocolkrb5::Parms = 0; // Server
193
194char XrdSecProtocolkrb5::ExpFile[XrdSecMAXPATHLEN] = "/tmp/krb5cc_<uid>";
195
196/******************************************************************************/
197/* D e l e t e */
198/******************************************************************************/
199
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}
211
212/******************************************************************************/
213/* g e t C r e d e n t i a l s */
214/******************************************************************************/
215
217 XrdOucErrInfo *error)
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}
406
407/******************************************************************************/
408/* S e r v e r O r i e n t e d M e t h o d s */
409/******************************************************************************/
410/******************************************************************************/
411/* A u t h e n t i c a t e */
412/******************************************************************************/
413
415 XrdSecParameters **parms,
416 XrdOucErrInfo *error)
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}
536
537/******************************************************************************/
538/* I n i t i a l i z a t i o n M e t h o d s */
539/******************************************************************************/
540/******************************************************************************/
541/* I n i t */
542/******************************************************************************/
543
544int XrdSecProtocolkrb5::Init(XrdOucErrInfo *erp, char *KP, char *kfn)
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}
611
612/******************************************************************************/
613/* P r i v a t e M e t h o d s */
614/******************************************************************************/
615/******************************************************************************/
616/* F a t a l */
617/******************************************************************************/
618
619int XrdSecProtocolkrb5::Fatal(XrdOucErrInfo *erp, int rc, const char *msg,
620 char *KP, int krc)
621{
622 const char *msgv[8];
623 int k, i = 0;
624
625 msgv[i++] = "Seckrb5: "; //0
626 msgv[i++] = msg; //1
627 if (krc) {msgv[i++] = "; "; //2
628 msgv[i++] = krb_etxt(krc); //3
629 }
630 if (KP) {msgv[i++] = " (p="; //4
631 msgv[i++] = KP; //5
632 msgv[i++] = ")."; //6
633 }
634 if (erp) erp->setErrInfo(rc, msgv, i);
635 else {for (k = 0; k < i; k++) std::cerr <<msgv[k];
636 std::cerr <<std::endl;
637 }
638
639 return -1;
640}
641
642/******************************************************************************/
643/* g e t _ k r b C r e d s */
644/******************************************************************************/
645
646// Warning! The krbClientContext lock must be held prior to calling this routine
647
648int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds)
649{
650 krb_rc rc;
651 krb5_principal the_principal;
652 krb5_creds mycreds;
653
654// Clear my credentials
655//
656 memset((char *)&mycreds, 0, sizeof(mycreds));
657
658// Setup the "principal/instance@realm"
659//
660 if ((rc = krb5_parse_name(krb_client_context,KP,&the_principal)))
661 {CLDBG("get_krbCreds: Cannot parse service name;" <<krb_etxt(rc));
662 return rc;
663 }
664
665// Copy the current target principal into the credentials
666//
667 if ((rc = krb5_copy_principal(krb_client_context, the_principal, &mycreds.server)))
668 {CLDBG("get_krbCreds: err copying principal to creds; " <<krb_etxt(rc));
669 krb5_free_principal(krb_client_context, the_principal);
670 return rc;
671 }
672
673// Get our principal name
674//
675 if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &mycreds.client)))
676 {CLDBG("get_krbCreds: err copying client name to creds; " <<krb_etxt(rc));
677 krb5_free_cred_contents(krb_client_context, &mycreds);
678 krb5_free_principal(krb_client_context, the_principal);
679 return rc;
680 }
681
682// Now get the credentials (free our local info)
683//
684 rc = krb5_get_credentials(krb_client_context, 0, krb_client_ccache, &mycreds, krb_creds);
685 krb5_free_cred_contents(krb_client_context, &mycreds);
686 krb5_free_principal(krb_client_context, the_principal);
687
688// Check if all went well
689//
690 if (rc) {CLDBG("get_krbCreds: unable to get creds; " <<krb_etxt(rc));}
691 return rc;
692}
693
694/******************************************************************************/
695/* g e t _ k r b F w d C r e d s */
696/******************************************************************************/
697
698int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata)
699{
700 int rc;
701 krb5_principal client, server;
702
703// Fill-in our principal
704//
705 if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &client)))
706 {CLDBG("get_krbFwdCreds: err filling client principal; " <<krb_etxt(rc));
707 return rc;
708 }
709
710// Fill-in target (service) principal
711//
712 if ((rc = krb5_parse_name(krb_client_context, KP, &server)))
713 {CLDBG("get_krbFwdCreds: Cannot parse service principal;" <<krb_etxt(rc));
714 return rc;
715 }
716
717// Set the timestamp in the authentication context
718//
719 if ((rc = krb5_auth_con_setflags(krb_client_context, AuthClientContext,
720 KRB5_AUTH_CONTEXT_RET_TIME)))
721 {CLDBG("Unable to set KRB5_AUTH_CONTEXT_RET_TIME"
722 " in the authentication context" << krb_etxt(rc));
723 return rc;
724 }
725
726// Acquire a TGT for use at a remote host system
727//
728 if ((rc = krb5_fwd_tgt_creds(krb_client_context, AuthClientContext, 0 /*host*/,
729 client, server, krb_client_ccache, true,
730 outdata)))
731 {CLDBG("get_krbFwdCreds: err getting forwarded ticket;" <<krb_etxt(rc));
732 return rc;
733 }
734
735// Done
736//
737 return rc;
738}
739
740/******************************************************************************/
741/* e x p _ k r b T k n */
742/******************************************************************************/
743
744int XrdSecProtocolkrb5::exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp)
745{
746 krb5_address ipadd;
747 int rc = 0;
748
749// Create the cache filename, expanding the keywords, if needed
750//
751 char ccfile[XrdSecMAXPATHLEN];
752 strcpy(ccfile, XrdSecProtocolkrb5::ExpFile);
753 int nlen = strlen(ccfile);
754 char *pusr = (char *) strstr(&ccfile[0], "<user>");
755 if (pusr)
756 {int ln = strlen(CName);
757 if (ln != 6) {
758 // Adjust the space
759 int lm = strlen(ccfile) - (int)(pusr + 6 - &ccfile[0]);
760 memmove(pusr+ln, pusr+6, lm);
761 }
762 // Copy the name
763 memcpy(pusr, CName, ln);
764 // Adjust the length
765 nlen += (ln - 6);
766 }
767 char *puid = (char *) strstr(&ccfile[0], "<uid>");
768 struct passwd *pw;
769 XrdSysPwd thePwd(CName, &pw);
770 if (puid)
771 {char cuid[20] = {0};
772 if (pw)
773 sprintf(cuid, "%d", pw->pw_uid);
774 int ln = strlen(cuid);
775 if (ln != 5) {
776 // Adjust the space
777 int lm = strlen(ccfile) - (int)(puid + 5 - &ccfile[0]);
778 memmove(puid+ln, pusr+5, lm);
779 }
780 // Copy the name
781 memcpy(puid, cuid, ln);
782 // Adjust the length
783 nlen += (ln - 5);
784 }
785
786// Terminate to the new length
787//
788 ccfile[nlen] = 0;
789
790// Point the received creds
791//
792 krbContext.Lock();
793 krb5_data forwardCreds;
794 forwardCreds.data = &cred->buffer[XrdSecPROTOIDLEN];
795 forwardCreds.length = cred->size -XrdSecPROTOIDLEN;
796
797// Get the replay cache
798//
799 krb5_rcache rcache;
800 if ((rc = krb5_get_server_rcache(krb_context,
801 krb5_princ_component(krb_context, krb_principal, 0),
802 &rcache)))
803 return rc;
804 if ((rc = krb5_auth_con_setrcache(krb_context, AuthContext, rcache)))
805 return rc;
806
807// Fill-in remote address
808//
809 SetAddr(ipadd);
810 if ((rc = krb5_auth_con_setaddrs(krb_context, AuthContext, 0, &ipadd)))
811 return rc;
812
813// Readout the credentials
814//
815 krb5_creds **creds = 0;
816 if ((rc = krb5_rd_cred(krb_context, AuthContext,
817 &forwardCreds, &creds, 0)))
818 return rc;
819
820// Resolve cache name
821 krb5_ccache cache = 0;
822 if ((rc = krb5_cc_resolve(krb_context, ccfile, &cache)))
823 return rc;
824
825// Init cache
826//
827 if ((rc = krb5_cc_initialize(krb_context, cache,
828 Ticket->enc_part2->client)))
829 return rc;
830
831// Store credentials in cache
832//
833 if ((rc = krb5_cc_store_cred(krb_context, cache, *creds)))
834 return rc;
835
836// Close cache
837 if ((rc = krb5_cc_close(krb_context, cache)))
838 return rc;
839
840// Change permission and ownership of the file
841//
842 if (chmod(ccfile, 0600) == -1)
843 return Fatal(erp, errno, "Unable to change file permissions;", ccfile, 0);
844
845// Done
846//
847 return 0;
848}
849
850/******************************************************************************/
851/* S e t A d d r */
852/******************************************************************************/
853
854void XrdSecProtocolkrb5::SetAddr(krb5_address &ipadd)
855{
856// The below is a hack but that's how it is actually done!
857//
858 if (epAddr.Family() == AF_INET6)
859 {struct sockaddr_in6 *ip = (struct sockaddr_in6 *)epAddr.SockAddr();
860 ipadd.addrtype = ADDRTYPE_INET6;
861 ipadd.length = sizeof(ip->sin6_addr);
862 ipadd.contents = (krb5_octet *)&ip->sin6_addr;
863 } else {
864 struct sockaddr_in *ip = (struct sockaddr_in *)epAddr.SockAddr();
865 ipadd.addrtype = ADDRTYPE_INET;
866 ipadd.length = sizeof(ip->sin_addr);
867 ipadd.contents = (krb5_octet *)&ip->sin_addr;
868 }
869}
870
871/******************************************************************************/
872/* X r d S e c p r o t o c o l k r b 5 I n i t */
873/******************************************************************************/
874
875extern "C"
876{
877char *XrdSecProtocolkrb5Init(const char mode,
878 const char *parms,
879 XrdOucErrInfo *erp)
880{
881 char *op, *KPrincipal=0, *Keytab=0, *ExpFile=0;
882 char parmbuff[1024];
883 XrdOucTokenizer inParms(parmbuff);
884 int options = XrdSecNOIPCHK;
885 static bool serverinitialized = false;
886
887// For client-side one-time initialization, we only need to set debug flag and
888// initialize the kerberos context and cache location.
889//
890 if ((mode == 'c') || (serverinitialized))
891 {
892 int opts = 0;
893 if (getenv("XrdSecDEBUG")) opts |= XrdSecDEBUG;
894 if (getenv("XrdSecKRB5INITTKN")) opts |= XrdSecINITTKN;
896 return (XrdSecProtocolkrb5::Init(erp) ? (char *)0 : (char *)"");
897 }
898
899 if (!serverinitialized) {
900 serverinitialized = true;
901 }
902
903// Duplicate the parms
904//
905 if (parms) strlcpy(parmbuff, parms, sizeof(parmbuff));
906 else {char *msg = (char *)"Seckrb5: Kerberos parameters not specified.";
907 if (erp) erp->setErrInfo(EINVAL, msg);
908 else std::cerr <<msg <<std::endl;
909 return (char *)0;
910 }
911
912// Expected parameters: [<keytab>] [-ipchk] [-exptkn[:filetemplate]] <principal>
913//
914 if (inParms.GetLine())
915 {if ((op = inParms.GetToken()) && *op == '/')
916 {Keytab = op; op = inParms.GetToken();}
917 if (op && !strcmp(op, "-ipchk"))
918 {options &= ~XrdSecNOIPCHK;
919 op = inParms.GetToken();
920 }
921 if (op && !strncmp(op, "-exptkn", 7))
922 {options |= XrdSecEXPTKN;
923 if (op[7] == ':') ExpFile = op+8;
924 op = inParms.GetToken();
925 }
926 KPrincipal = strdup(op);
927 }
928
929 if (ExpFile)
930 fprintf(stderr,"Template for exports: %s\n", ExpFile);
931 else
932 fprintf(stderr,"Template for exports not set\n");
933
934// Now make sure that we have all the right info
935//
936 if (!KPrincipal)
937 {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
938 if (erp) erp->setErrInfo(EINVAL, msg);
939 else std::cerr <<msg <<std::endl;
940 return (char *)0;
941 }
942
943// Expand possible keywords in the principal
944//
945 int plen = strlen(KPrincipal);
946 int lkey = strlen("<host>");
947 char *phost = (char *) strstr(&KPrincipal[0], "<host>");
948 if (phost)
949 {char *hn = XrdNetUtils::MyHostName();
950 if (hn)
951 {int lhn = strlen(hn);
952 if (lhn != lkey) {
953 // Allocate, if needed
954 int lnew = plen - lkey + lhn;
955 if (lnew > plen) {
956 KPrincipal = (char *) realloc(KPrincipal, lnew+1);
957 KPrincipal[lnew] = 0;
958 phost = (char *) strstr(&KPrincipal[0], "<host>");
959 }
960 // Adjust the space
961 int lm = plen - (int)(phost + lkey - &KPrincipal[0]);
962 memmove(phost + lhn, phost + lkey, lm);
963 }
964 // Copy the name
965 memcpy(phost, hn, lhn);
966 // Cleanup
967 free(hn);
968 }
969 }
970
971// Now initialize the server
972//
973 options |= XrdSecDEBUG;
976 if (!XrdSecProtocolkrb5::Init(erp, KPrincipal, Keytab))
977 {free(KPrincipal);
978 int lpars = strlen(XrdSecProtocolkrb5::getPrincipal());
979 if (options & XrdSecEXPTKN)
980 lpars += strlen(",fwd");
981 char *params = (char *)malloc(lpars+1);
982 if (params)
983 {memset(params,0,lpars+1);
984 strcpy(params,XrdSecProtocolkrb5::getPrincipal());
985 if (options & XrdSecEXPTKN)
986 strcat(params,",fwd");
988 return params;
989 }
990 return (char *)0;
991 }
992
993// Failure
994//
995 free(KPrincipal);
996 return (char *)0;
997}
998}
999
1000/******************************************************************************/
1001/* X r d S e c P r o t o c o l k r b 5 O b j e c t */
1002/******************************************************************************/
1003
1004extern "C"
1005{
1007 const char *hostname,
1008 XrdNetAddrInfo &endPoint,
1009 const char *parms,
1010 XrdOucErrInfo *erp)
1011{
1012 XrdSecProtocolkrb5 *prot;
1013 char *KPrincipal=0;
1014
1015// If this is a client call, then we need to get the target principal from the
1016// parms (which must be the first and only token). For servers, we use the
1017// context we established at initialization time.
1018//
1019 if (mode == 'c')
1020 {if ((KPrincipal = (char *)parms)) while(*KPrincipal == ' ') KPrincipal++;
1021 if (!KPrincipal || !*KPrincipal)
1022 {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
1023 if (erp) erp->setErrInfo(EINVAL, msg);
1024 else std::cerr <<msg <<std::endl;
1025 return (XrdSecProtocol *)0;
1026 }
1027 }
1028
1029// Get a new protocol object
1030//
1031 if (!(prot = new XrdSecProtocolkrb5(KPrincipal, hostname, endPoint)))
1032 {char *msg = (char *)"Seckrb5: Insufficient memory for protocol.";
1033 if (erp) erp->setErrInfo(ENOMEM, msg);
1034 else std::cerr <<msg <<std::endl;
1035 return (XrdSecProtocol *)0;
1036 }
1037
1038// All done
1039//
1040 return prot;
1041}
1042
1043void
1044 __eprintf (const char *string, const char *expression,
1045 unsigned int line, const char *filename)
1046 {
1047 fprintf (stderr, string, expression, line, filename);
1048 fflush (stderr);
1049 abort ();
1050 }
1051}
XrdVERSIONINFO(XrdClGetPlugIn, XrdClGetPlugIn) extern "C"
void Fatal(const char *op, const char *target)
Definition XrdCrc32c.cc:58
int fflush(FILE *stream)
#define access(a, b)
Definition XrdPosix.hh:39
XrdSecBuffer XrdSecParameters
XrdSecBuffer XrdSecCredentials
#define XrdSecDEBUG
#define CLDBG(x)
char * XrdSecProtocolkrb5Init(const char mode, const char *parms, XrdOucErrInfo *erp)
#define krb_etxt(x)
#define CLPRT(x)
#define XrdSecPROTOIDLEN
#define XrdSecMAXPATHLEN
#define XrdSecEXPTKN
#define XrdSecPROTOIDENT
krb5_error_code krb_rc
#define XrdSecINITTKN
XrdSecProtocol * XrdSecProtocolkrb5Object(const char mode, const char *hostname, XrdNetAddrInfo &endPoint, const char *parms, XrdOucErrInfo *erp)
#define XrdSecNOIPCHK
void __eprintf(const char *string, const char *expression, unsigned int line, const char *filename)
struct myOpts opts
int emsg(int rc, char *msg)
size_t strlcpy(char *dst, const char *src, size_t sz)
const sockaddr * SockAddr()
int Family() const
static char * MyHostName(const char *eName="*unknown*", const char **eText=0)
char * Get(const char *varname)
Definition XrdOucEnv.hh:69
XrdOucEnv * getEnv()
int setErrInfo(int code, const char *emsg)
char * GetToken(char **rest=0, int lowcase=0)
XrdNetAddrInfo * addrInfo
Entity's connection details.
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
char * name
Entity's name.
char * host
Entity's host name dnr dependent.
XrdSecEntity Entity
int Authenticate(XrdSecCredentials *cred, XrdSecParameters **parms, XrdOucErrInfo *einfo=0)
XrdSecProtocolkrb5(const char *KP, const char *hname, XrdNetAddrInfo &endPoint)
void Delete()
Delete the protocol object. DO NOT use C++ delete() on this object.
static char * getPrincipal()
static void setOpts(int opts)
static void setClientOpts(int opts)
static void setExpFile(char *expfile)
static void setParms(char *param)
static int Init(XrdOucErrInfo *einfo, char *KP=0, char *kfn=0)
XrdSecCredentials * getCredentials(XrdSecParameters *parm=0, XrdOucErrInfo *einfo=0)
friend class XrdSecProtocolDummy
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.