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 <string>
39#include <strings.h>
40#include <cstdio>
41#include <sys/param.h>
42#include <pwd.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45
46extern "C" {
47#include "krb5.h"
48#ifdef HAVE_ET_COM_ERR_H
49#include "et/com_err.h"
50#else
51#include "com_err.h"
52#endif
53}
54
55#include "XrdVersion.hh"
56
58#include "XrdNet/XrdNetUtils.hh"
60#include "XrdOuc/XrdOucEnv.hh"
63#include "XrdSys/XrdSysPwd.hh"
66
67/******************************************************************************/
68/* D e f i n e s */
69/******************************************************************************/
70
71#define krb_etxt(x) (char *)error_message(x)
72
73#define XrdSecPROTOIDENT "krb5"
74#define XrdSecPROTOIDLEN sizeof(XrdSecPROTOIDENT)
75#define XrdSecNOIPCHK 0x0001
76#define XrdSecEXPTKN 0x0002
77#define XrdSecINITTKN 0x0004
78#define XrdSecDEBUG 0x1000
79
80#define XrdSecMAXPATHLEN 4096
81
82#define CLDBG(x) if (client_options & XrdSecDEBUG) std::cerr <<"Seckrb5: " <<x <<std::endl;
83#define CLPRT(x) std::cerr <<"Seckrb5: " <<x <<std::endl;
84
85typedef krb5_error_code krb_rc;
86
87/******************************************************************************/
88/* X r d S e c P r o t o c o l k r b 5 C l a s s */
89/******************************************************************************/
90
92{
93public:
94friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor
95
97 XrdSecParameters **parms,
98 XrdOucErrInfo *einfo=0);
99
101 XrdOucErrInfo *einfo=0);
102
103static char *getPrincipal() {return Principal;}
104
105static int Init(XrdOucErrInfo *einfo, char *KP=0, char *kfn=0);
106
107static void setOpts(int opts) {options = opts;}
108static void setClientOpts(int opts) {client_options = opts;}
109static void setParms(char *param) {Parms = param;}
110static void setExpFile(char *expfile)
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 }
119
120 XrdSecProtocolkrb5(const char *KP,
121 const char *hname,
122 XrdNetAddrInfo &endPoint)
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 }
136
137 void Delete();
138
139private:
140
141 ~XrdSecProtocolkrb5() {} // Delete() does it all
142
143static int Fatal(XrdOucErrInfo *erp, int rc, const char *msg1,
144 const char *KP=0, int krc=0, bool isClient=false);
145static int get_krbCreds(char *KP, krb5_creds **krb_creds);
146 void SetAddr(krb5_address &ipadd);
147
148static XrdSysMutex krbContext; // Server
149static XrdSysMutex krbClientContext;// Client
150static int options; // Server
151static int client_options;// Client
152static krb5_context krb_context; // Server
153static krb5_context krb_client_context; // Client
154static krb5_ccache krb_client_ccache; // Client
155static krb5_ccache krb_ccache; // Server
156static krb5_keytab krb_keytab; // Server
157static krb5_principal krb_principal; // Server
158
159static char *Principal; // Server's principal name
160static char *Parms; // Server parameters
161
162static char ExpFile[XrdSecMAXPATHLEN]; // Server: (template for)
163 // file to export token
164int exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp);
165int get_krbFwdCreds(char *KP, krb5_data *outdata);
166
167XrdNetAddrInfo epAddr;
168char CName[256]; // Kerberos limit
169char *Service; // Target principal for client
170char Step; // Indicates at which step we are
171krb5_auth_context AuthContext; // Authetication context
172krb5_auth_context AuthClientContext; // Authetication context
173krb5_ticket *Ticket; // Ticket associated to client authentication
174krb5_creds *Creds; // Client: credentials
175};
176
177/******************************************************************************/
178/* S t a t i c D a t a */
179/******************************************************************************/
180
181XrdSysMutex XrdSecProtocolkrb5::krbContext; // Server
182XrdSysMutex XrdSecProtocolkrb5::krbClientContext; // Client
183
184int XrdSecProtocolkrb5::client_options = 0;// Client
185int XrdSecProtocolkrb5::options = 0; // Server
186krb5_context XrdSecProtocolkrb5::krb_context; // Server
187krb5_context XrdSecProtocolkrb5::krb_client_context; // Client
188krb5_ccache XrdSecProtocolkrb5::krb_client_ccache; // Client
189krb5_ccache XrdSecProtocolkrb5::krb_ccache; // Server
190krb5_keytab XrdSecProtocolkrb5::krb_keytab = NULL; // Server
191krb5_principal XrdSecProtocolkrb5::krb_principal; // Server
192
193char *XrdSecProtocolkrb5::Principal = 0; // Server
194char *XrdSecProtocolkrb5::Parms = 0; // Server
195
196char XrdSecProtocolkrb5::ExpFile[XrdSecMAXPATHLEN] = "/tmp/krb5cc_<uid>";
197
198/******************************************************************************/
199/* D e l e t e */
200/******************************************************************************/
201
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}
213
214/******************************************************************************/
215/* g e t C r e d e n t i a l s */
216/******************************************************************************/
217
219 XrdOucErrInfo *error)
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}
408
409/******************************************************************************/
410/* S e r v e r O r i e n t e d M e t h o d s */
411/******************************************************************************/
412/******************************************************************************/
413/* A u t h e n t i c a t e */
414/******************************************************************************/
415
417 XrdSecParameters **parms,
418 XrdOucErrInfo *error)
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}
560
561/******************************************************************************/
562/* I n i t i a l i z a t i o n M e t h o d s */
563/******************************************************************************/
564/******************************************************************************/
565/* I n i t */
566/******************************************************************************/
567
568int XrdSecProtocolkrb5::Init(XrdOucErrInfo *erp, char *KP, char *kfn)
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}
635
636/******************************************************************************/
637/* P r i v a t e M e t h o d s */
638/******************************************************************************/
639/******************************************************************************/
640/* F a t a l */
641/******************************************************************************/
642
643int XrdSecProtocolkrb5::Fatal(XrdOucErrInfo *erp, int rc, const char *msg,
644 const char *KP, int krc, bool isClient)
645{
646 const char *msgv[8];
647 int k, i = 0;
648
649 msgv[i++] = "Seckrb5: "; //0
650 msgv[i++] = msg; //1
651 if (krc) {msgv[i++] = "; "; //2
652 msgv[i++] = krb_etxt(krc); //3
653 }
654 if (KP) {const char* who = (isClient ? "(client=" : "(server=");
655 msgv[i++] = who; //4
656 msgv[i++] = KP; //5
657 msgv[i++] = ")."; //6
658 }
659 if (erp) erp->setErrInfo(rc, msgv, i);
660 else {for (k = 0; k < i; k++) std::cerr <<msgv[k];
661 std::cerr <<std::endl;
662 }
663
664 return -1;
665}
666
667/******************************************************************************/
668/* g e t _ k r b C r e d s */
669/******************************************************************************/
670
671// Warning! The krbClientContext lock must be held prior to calling this routine
672
673int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds)
674{
675 krb_rc rc;
676 krb5_principal the_principal;
677 krb5_creds mycreds;
678
679// Clear my credentials
680//
681 memset((char *)&mycreds, 0, sizeof(mycreds));
682
683// Setup the "principal/instance@realm"
684//
685 if ((rc = krb5_parse_name(krb_client_context,KP,&the_principal)))
686 {CLDBG("get_krbCreds: Cannot parse service name;" <<krb_etxt(rc));
687 return rc;
688 }
689
690// Copy the current target principal into the credentials
691//
692 if ((rc = krb5_copy_principal(krb_client_context, the_principal, &mycreds.server)))
693 {CLDBG("get_krbCreds: err copying principal to creds; " <<krb_etxt(rc));
694 krb5_free_principal(krb_client_context, the_principal);
695 return rc;
696 }
697
698// Get our principal name
699//
700 if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &mycreds.client)))
701 {CLDBG("get_krbCreds: err copying client name to creds; " <<krb_etxt(rc));
702 krb5_free_cred_contents(krb_client_context, &mycreds);
703 krb5_free_principal(krb_client_context, the_principal);
704 return rc;
705 }
706
707// Now get the credentials (free our local info)
708//
709 rc = krb5_get_credentials(krb_client_context, 0, krb_client_ccache, &mycreds, krb_creds);
710 krb5_free_cred_contents(krb_client_context, &mycreds);
711 krb5_free_principal(krb_client_context, the_principal);
712
713// Check if all went well
714//
715 if (rc) {CLDBG("get_krbCreds: unable to get creds; " <<krb_etxt(rc));}
716 return rc;
717}
718
719/******************************************************************************/
720/* g e t _ k r b F w d C r e d s */
721/******************************************************************************/
722
723int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata)
724{
725 int rc;
726 krb5_principal client, server;
727
728// Fill-in our principal
729//
730 if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &client)))
731 {CLDBG("get_krbFwdCreds: err filling client principal; " <<krb_etxt(rc));
732 return rc;
733 }
734
735// Fill-in target (service) principal
736//
737 if ((rc = krb5_parse_name(krb_client_context, KP, &server)))
738 {CLDBG("get_krbFwdCreds: Cannot parse service principal;" <<krb_etxt(rc));
739 return rc;
740 }
741
742// Set the timestamp in the authentication context
743//
744 if ((rc = krb5_auth_con_setflags(krb_client_context, AuthClientContext,
745 KRB5_AUTH_CONTEXT_RET_TIME)))
746 {CLDBG("Unable to set KRB5_AUTH_CONTEXT_RET_TIME"
747 " in the authentication context" << krb_etxt(rc));
748 return rc;
749 }
750
751// Acquire a TGT for use at a remote host system
752//
753 if ((rc = krb5_fwd_tgt_creds(krb_client_context, AuthClientContext, 0 /*host*/,
754 client, server, krb_client_ccache, true,
755 outdata)))
756 {CLDBG("get_krbFwdCreds: err getting forwarded ticket;" <<krb_etxt(rc));
757 return rc;
758 }
759
760// Done
761//
762 return rc;
763}
764
765/******************************************************************************/
766/* e x p _ k r b T k n */
767/******************************************************************************/
768
769int XrdSecProtocolkrb5::exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp)
770{
771 krb5_address ipadd;
772 int rc = 0;
773
774// Create the cache filename, expanding the keywords, if needed
775//
776 char ccfile[XrdSecMAXPATHLEN];
777 strcpy(ccfile, XrdSecProtocolkrb5::ExpFile);
778 int nlen = strlen(ccfile);
779 char *pusr = (char *) strstr(&ccfile[0], "<user>");
780 if (pusr)
781 {int ln = strlen(CName);
782 if (ln != 6) {
783 // Adjust the space
784 int lm = strlen(ccfile) - (int)(pusr + 6 - &ccfile[0]);
785 memmove(pusr+ln, pusr+6, lm);
786 }
787 // Copy the name
788 memcpy(pusr, CName, ln);
789 // Adjust the length
790 nlen += (ln - 6);
791 }
792 char *puid = (char *) strstr(&ccfile[0], "<uid>");
793 struct passwd *pw;
794 XrdSysPwd thePwd(CName, &pw);
795 if (puid)
796 {char cuid[20] = {0};
797 if (pw)
798 sprintf(cuid, "%d", pw->pw_uid);
799 int ln = strlen(cuid);
800 if (ln != 5) {
801 // Adjust the space
802 int lm = strlen(ccfile) - (int)(puid + 5 - &ccfile[0]);
803 memmove(puid+ln, pusr+5, lm);
804 }
805 // Copy the name
806 memcpy(puid, cuid, ln);
807 // Adjust the length
808 nlen += (ln - 5);
809 }
810
811// Terminate to the new length
812//
813 ccfile[nlen] = 0;
814
815// Point the received creds
816//
817 krbContext.Lock();
818 krb5_data forwardCreds;
819 forwardCreds.data = &cred->buffer[XrdSecPROTOIDLEN];
820 forwardCreds.length = cred->size -XrdSecPROTOIDLEN;
821
822// Get the replay cache
823//
824 krb5_rcache rcache;
825 if ((rc = krb5_get_server_rcache(krb_context,
826 krb5_princ_component(krb_context, krb_principal, 0),
827 &rcache)))
828 return rc;
829 if ((rc = krb5_auth_con_setrcache(krb_context, AuthContext, rcache)))
830 return rc;
831
832// Fill-in remote address
833//
834 SetAddr(ipadd);
835 if ((rc = krb5_auth_con_setaddrs(krb_context, AuthContext, 0, &ipadd)))
836 return rc;
837
838// Readout the credentials
839//
840 krb5_creds **creds = 0;
841 if ((rc = krb5_rd_cred(krb_context, AuthContext,
842 &forwardCreds, &creds, 0)))
843 return rc;
844
845// Resolve cache name
846 krb5_ccache cache = 0;
847 if ((rc = krb5_cc_resolve(krb_context, ccfile, &cache)))
848 return rc;
849
850// Init cache
851//
852 if ((rc = krb5_cc_initialize(krb_context, cache,
853 Ticket->enc_part2->client)))
854 return rc;
855
856// Store credentials in cache
857//
858 if ((rc = krb5_cc_store_cred(krb_context, cache, *creds)))
859 return rc;
860
861// Close cache
862 if ((rc = krb5_cc_close(krb_context, cache)))
863 return rc;
864
865// Change permission and ownership of the file
866//
867 if (chmod(ccfile, 0600) == -1)
868 return Fatal(erp, errno, "Unable to change file permissions;", ccfile, 0);
869
870// Done
871//
872 return 0;
873}
874
875/******************************************************************************/
876/* S e t A d d r */
877/******************************************************************************/
878
879void XrdSecProtocolkrb5::SetAddr(krb5_address &ipadd)
880{
881// The below is a hack but that's how it is actually done!
882//
883 if (epAddr.Family() == AF_INET6)
884 {struct sockaddr_in6 *ip = (struct sockaddr_in6 *)epAddr.SockAddr();
885 ipadd.addrtype = ADDRTYPE_INET6;
886 ipadd.length = sizeof(ip->sin6_addr);
887 ipadd.contents = (krb5_octet *)&ip->sin6_addr;
888 } else {
889 struct sockaddr_in *ip = (struct sockaddr_in *)epAddr.SockAddr();
890 ipadd.addrtype = ADDRTYPE_INET;
891 ipadd.length = sizeof(ip->sin_addr);
892 ipadd.contents = (krb5_octet *)&ip->sin_addr;
893 }
894}
895
896/******************************************************************************/
897/* X r d S e c p r o t o c o l k r b 5 I n i t */
898/******************************************************************************/
899
900extern "C"
901{
902char *XrdSecProtocolkrb5Init(const char mode,
903 const char *parms,
904 XrdOucErrInfo *erp)
905{
906 char *op, *KPrincipal=0, *Keytab=0, *ExpFile=0;
907 char parmbuff[1024];
908 XrdOucTokenizer inParms(parmbuff);
909 int options = XrdSecNOIPCHK;
910 static bool serverinitialized = false;
911
912// For client-side one-time initialization, we only need to set debug flag and
913// initialize the kerberos context and cache location.
914//
915 if ((mode == 'c') || (serverinitialized))
916 {
917 int opts = 0;
918 if (getenv("XrdSecDEBUG")) opts |= XrdSecDEBUG;
919 if (getenv("XrdSecKRB5INITTKN")) opts |= XrdSecINITTKN;
921 return (XrdSecProtocolkrb5::Init(erp) ? (char *)0 : (char *)"");
922 }
923
924 if (!serverinitialized) {
925 serverinitialized = true;
926 }
927
928// Duplicate the parms
929//
930 if (parms) strlcpy(parmbuff, parms, sizeof(parmbuff));
931 else {char *msg = (char *)"Seckrb5: Kerberos parameters not specified.";
932 if (erp) erp->setErrInfo(EINVAL, msg);
933 else std::cerr <<msg <<std::endl;
934 return (char *)0;
935 }
936
937// Expected parameters: [<keytab>] [-ipchk] [-exptkn[:filetemplate]] <principal>
938//
939 if (inParms.GetLine())
940 {if ((op = inParms.GetToken()) && *op == '/')
941 {Keytab = op; op = inParms.GetToken();}
942 if (op && !strcmp(op, "-ipchk"))
943 {options &= ~XrdSecNOIPCHK;
944 op = inParms.GetToken();
945 }
946 if (op && !strncmp(op, "-exptkn", 7))
947 {options |= XrdSecEXPTKN;
948 if (op[7] == ':') ExpFile = op+8;
949 op = inParms.GetToken();
950 }
951 KPrincipal = strdup(op);
952 }
953
954 if (ExpFile)
955 fprintf(stderr,"Template for exports: %s\n", ExpFile);
956 else
957 fprintf(stderr,"Template for exports not set\n");
958
959// Now make sure that we have all the right info
960//
961 if (!KPrincipal)
962 {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
963 if (erp) erp->setErrInfo(EINVAL, msg);
964 else std::cerr <<msg <<std::endl;
965 return (char *)0;
966 }
967
968// Expand possible keywords in the principal
969//
970 int plen = strlen(KPrincipal);
971 int lkey = strlen("<host>");
972 char *phost = (char *) strstr(&KPrincipal[0], "<host>");
973 if (phost)
974 {char *hn = XrdNetUtils::MyHostName();
975 if (hn)
976 {int lhn = strlen(hn);
977 if (lhn != lkey) {
978 // Allocate, if needed
979 int lnew = plen - lkey + lhn;
980 if (lnew > plen) {
981 KPrincipal = (char *) realloc(KPrincipal, lnew+1);
982 KPrincipal[lnew] = 0;
983 phost = (char *) strstr(&KPrincipal[0], "<host>");
984 }
985 // Adjust the space
986 int lm = plen - (int)(phost + lkey - &KPrincipal[0]);
987 memmove(phost + lhn, phost + lkey, lm);
988 }
989 // Copy the name
990 memcpy(phost, hn, lhn);
991 // Cleanup
992 free(hn);
993 }
994 }
995
996// Now initialize the server
997//
998 options |= XrdSecDEBUG;
1001 if (!XrdSecProtocolkrb5::Init(erp, KPrincipal, Keytab))
1002 {free(KPrincipal);
1003 int lpars = strlen(XrdSecProtocolkrb5::getPrincipal());
1004 if (options & XrdSecEXPTKN)
1005 lpars += strlen(",fwd");
1006 char *params = (char *)malloc(lpars+1);
1007 if (params)
1008 {memset(params,0,lpars+1);
1009 strcpy(params,XrdSecProtocolkrb5::getPrincipal());
1010 if (options & XrdSecEXPTKN)
1011 strcat(params,",fwd");
1013 return params;
1014 }
1015 return (char *)0;
1016 }
1017
1018// Failure
1019//
1020 free(KPrincipal);
1021 return (char *)0;
1022}
1023}
1024
1025/******************************************************************************/
1026/* X r d S e c P r o t o c o l k r b 5 O b j e c t */
1027/******************************************************************************/
1028
1029extern "C"
1030{
1032 const char *hostname,
1033 XrdNetAddrInfo &endPoint,
1034 const char *parms,
1035 XrdOucErrInfo *erp)
1036{
1037 XrdSecProtocolkrb5 *prot;
1038 char *KPrincipal=0;
1039
1040// If this is a client call, then we need to get the target principal from the
1041// parms (which must be the first and only token). For servers, we use the
1042// context we established at initialization time.
1043//
1044 if (mode == 'c')
1045 {if ((KPrincipal = (char *)parms)) while(*KPrincipal == ' ') KPrincipal++;
1046 if (!KPrincipal || !*KPrincipal)
1047 {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
1048 if (erp) erp->setErrInfo(EINVAL, msg);
1049 else std::cerr <<msg <<std::endl;
1050 return (XrdSecProtocol *)0;
1051 }
1052 }
1053
1054// Get a new protocol object
1055//
1056 if (!(prot = new XrdSecProtocolkrb5(KPrincipal, hostname, endPoint)))
1057 {char *msg = (char *)"Seckrb5: Insufficient memory for protocol.";
1058 if (erp) erp->setErrInfo(ENOMEM, msg);
1059 else std::cerr <<msg <<std::endl;
1060 return (XrdSecProtocol *)0;
1061 }
1062
1063// All done
1064//
1065 return prot;
1066}
1067
1068void
1069 __eprintf (const char *string, const char *expression,
1070 unsigned int line, const char *filename)
1071 {
1072 fprintf (stderr, string, expression, line, filename);
1073 fflush (stderr);
1074 abort ();
1075 }
1076}
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 XrdSecNOIPCHK
#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)
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)
XrdSecEntity Entity
XrdSecProtocol(const char *pName)
Constructor.
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
char * buffer
Pointer to the buffer.
int size
Size of the buffer or length of data in the buffer.