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 ((ec = krb5_unparse_name(krb_context,
514 (krb5_const_principal)Ticket->enc_part2->client,
515 (char **)&cpName)))
516 {char mBuff[1024];
517 snprintf(mBuff, sizeof(mBuff),
518 "[principal unparse failed; %s]", krb_etxt(ec));
519 cPrincipal = mBuff;
520 } else {
521 cPrincipal = cpName;
522 krb5_free_unparsed_name(krb_context, cpName);
523 }
524 }
525 }
526
527// Make sure the name is null-terminated
528//
529 CName[sizeof(CName)-1] = '\0';
530
531// If requested, ask the client for a forwardable token
532 int hsrc = 0;
533 if (!rc && XrdSecProtocolkrb5::options & XrdSecEXPTKN) {
534 // We just ask for more; the client knows what to send over
535 hsrc = 1;
536 // We need to fill-in a fake buffer
537 int len = strlen("fwdtgt") + 1;
538 char *buf = (char *) malloc(len);
539 memcpy(buf, "fwdtgt", len-1);
540 buf[len-1] = 0;
541 *parms = new XrdSecParameters(buf, len);
542 }
543
544// Release any allocated storage at this point and unlock mutex
545//
546 krbContext.UnLock();
547
548// Diagnose any errors
549//
550 if (rc && iferror)
551 return Fatal(error, EACCES, iferror,
552 (isCP ? cPrincipal.c_str() : Principal), rc, isCP);
553
554// All done
555//
556 return hsrc;
557}
558
559/******************************************************************************/
560/* I n i t i a l i z a t i o n M e t h o d s */
561/******************************************************************************/
562/******************************************************************************/
563/* I n i t */
564/******************************************************************************/
565
566int XrdSecProtocolkrb5::Init(XrdOucErrInfo *erp, char *KP, char *kfn)
567{
568 krb_rc rc;
569 char buff[2048];
570
571// Create a kerberos context. There is one such context per protocol object.
572//
573
574// If we have no principal then this is a client-side call: initializations are done
575// in getCredentials to allow for multiple client principals
576//
577 if (!KP) return 0;
578
579 if ((rc = krb5_init_context(&krb_context)))
580 return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc);
581
582// Obtain the default cache location
583//
584 if ((rc = krb5_cc_default(krb_context, &krb_ccache)))
585 return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc);
586
587// Try to resolve the keyfile name
588//
589 if (kfn && *kfn)
590 {if ((rc = krb5_kt_resolve(krb_context, kfn, &krb_keytab)))
591 {snprintf(buff, sizeof(buff), "Unable to find keytab '%s';", kfn);
592 return Fatal(erp, ESRCH, buff, Principal, rc);
593 }
594 } else {
595 krb5_kt_default(krb_context, &krb_keytab);
596 }
597
598// Keytab name
599//
600 char krb_kt_name[1024];
601 if ((rc = krb5_kt_get_name(krb_context, krb_keytab, &krb_kt_name[0], 1024)))
602 {snprintf(buff, sizeof(buff), "Unable to get keytab name;");
603 return Fatal(erp, ESRCH, buff, Principal, rc);
604 }
605
606// Check if we can read access the keytab file
607//
608 krb5_kt_cursor ktc;
609 if ((rc = krb5_kt_start_seq_get(krb_context, krb_keytab, &ktc)))
610 {snprintf(buff, sizeof(buff), "Unable to start sequence on the keytab file %s", krb_kt_name);
611 return Fatal(erp, EPERM, buff, Principal, rc);
612 }
613 if ((rc = krb5_kt_end_seq_get(krb_context, krb_keytab, &ktc)))
614 {snprintf(buff, sizeof(buff), "WARNING: unable to end sequence on the keytab file %s", krb_kt_name);
615 CLPRT(buff);
616 }
617
618// Now, extract the "principal/instance@realm" from the stream
619//
620 if ((rc = krb5_parse_name(krb_context,KP,&krb_principal)))
621 return Fatal(erp, EINVAL, "Cannot parse service principal name", KP, rc);
622
623// Establish the correct principal to use
624//
625 if ((rc = krb5_unparse_name(krb_context,(krb5_const_principal)krb_principal,
626 (char **)&Principal)))
627 return Fatal(erp, EINVAL, "Unable to unparse service principal;", KP, rc);
628
629// All done
630//
631 return 0;
632}
633
634/******************************************************************************/
635/* P r i v a t e M e t h o d s */
636/******************************************************************************/
637/******************************************************************************/
638/* F a t a l */
639/******************************************************************************/
640
641int XrdSecProtocolkrb5::Fatal(XrdOucErrInfo *erp, int rc, const char *msg,
642 const char *KP, int krc, bool isClient)
643{
644 const char *msgv[8];
645 int k, i = 0;
646
647 msgv[i++] = "Seckrb5: "; //0
648 msgv[i++] = msg; //1
649 if (krc) {msgv[i++] = "; "; //2
650 msgv[i++] = krb_etxt(krc); //3
651 }
652 if (KP) {const char* who = (isClient ? "(client=" : "(server=");
653 msgv[i++] = who; //4
654 msgv[i++] = KP; //5
655 msgv[i++] = ")."; //6
656 }
657 if (erp) erp->setErrInfo(rc, msgv, i);
658 else {for (k = 0; k < i; k++) std::cerr <<msgv[k];
659 std::cerr <<std::endl;
660 }
661
662 return -1;
663}
664
665/******************************************************************************/
666/* g e t _ k r b C r e d s */
667/******************************************************************************/
668
669// Warning! The krbClientContext lock must be held prior to calling this routine
670
671int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds)
672{
673 krb_rc rc;
674 krb5_principal the_principal;
675 krb5_creds mycreds;
676
677// Clear my credentials
678//
679 memset((char *)&mycreds, 0, sizeof(mycreds));
680
681// Setup the "principal/instance@realm"
682//
683 if ((rc = krb5_parse_name(krb_client_context,KP,&the_principal)))
684 {CLDBG("get_krbCreds: Cannot parse service name;" <<krb_etxt(rc));
685 return rc;
686 }
687
688// Copy the current target principal into the credentials
689//
690 if ((rc = krb5_copy_principal(krb_client_context, the_principal, &mycreds.server)))
691 {CLDBG("get_krbCreds: err copying principal to creds; " <<krb_etxt(rc));
692 krb5_free_principal(krb_client_context, the_principal);
693 return rc;
694 }
695
696// Get our principal name
697//
698 if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &mycreds.client)))
699 {CLDBG("get_krbCreds: err copying client name to creds; " <<krb_etxt(rc));
700 krb5_free_cred_contents(krb_client_context, &mycreds);
701 krb5_free_principal(krb_client_context, the_principal);
702 return rc;
703 }
704
705// Now get the credentials (free our local info)
706//
707 rc = krb5_get_credentials(krb_client_context, 0, krb_client_ccache, &mycreds, krb_creds);
708 krb5_free_cred_contents(krb_client_context, &mycreds);
709 krb5_free_principal(krb_client_context, the_principal);
710
711// Check if all went well
712//
713 if (rc) {CLDBG("get_krbCreds: unable to get creds; " <<krb_etxt(rc));}
714 return rc;
715}
716
717/******************************************************************************/
718/* g e t _ k r b F w d C r e d s */
719/******************************************************************************/
720
721int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata)
722{
723 int rc;
724 krb5_principal client, server;
725
726// Fill-in our principal
727//
728 if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &client)))
729 {CLDBG("get_krbFwdCreds: err filling client principal; " <<krb_etxt(rc));
730 return rc;
731 }
732
733// Fill-in target (service) principal
734//
735 if ((rc = krb5_parse_name(krb_client_context, KP, &server)))
736 {CLDBG("get_krbFwdCreds: Cannot parse service principal;" <<krb_etxt(rc));
737 return rc;
738 }
739
740// Set the timestamp in the authentication context
741//
742 if ((rc = krb5_auth_con_setflags(krb_client_context, AuthClientContext,
743 KRB5_AUTH_CONTEXT_RET_TIME)))
744 {CLDBG("Unable to set KRB5_AUTH_CONTEXT_RET_TIME"
745 " in the authentication context" << krb_etxt(rc));
746 return rc;
747 }
748
749// Acquire a TGT for use at a remote host system
750//
751 if ((rc = krb5_fwd_tgt_creds(krb_client_context, AuthClientContext, 0 /*host*/,
752 client, server, krb_client_ccache, true,
753 outdata)))
754 {CLDBG("get_krbFwdCreds: err getting forwarded ticket;" <<krb_etxt(rc));
755 return rc;
756 }
757
758// Done
759//
760 return rc;
761}
762
763/******************************************************************************/
764/* e x p _ k r b T k n */
765/******************************************************************************/
766
767int XrdSecProtocolkrb5::exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp)
768{
769 krb5_address ipadd;
770 int rc = 0;
771
772// Create the cache filename, expanding the keywords, if needed
773//
774 char ccfile[XrdSecMAXPATHLEN];
775 strcpy(ccfile, XrdSecProtocolkrb5::ExpFile);
776 int nlen = strlen(ccfile);
777 char *pusr = (char *) strstr(&ccfile[0], "<user>");
778 if (pusr)
779 {int ln = strlen(CName);
780 if (ln != 6) {
781 // Adjust the space
782 int lm = strlen(ccfile) - (int)(pusr + 6 - &ccfile[0]);
783 memmove(pusr+ln, pusr+6, lm);
784 }
785 // Copy the name
786 memcpy(pusr, CName, ln);
787 // Adjust the length
788 nlen += (ln - 6);
789 }
790 char *puid = (char *) strstr(&ccfile[0], "<uid>");
791 struct passwd *pw;
792 XrdSysPwd thePwd(CName, &pw);
793 if (puid)
794 {char cuid[20] = {0};
795 if (pw)
796 sprintf(cuid, "%d", pw->pw_uid);
797 int ln = strlen(cuid);
798 if (ln != 5) {
799 // Adjust the space
800 int lm = strlen(ccfile) - (int)(puid + 5 - &ccfile[0]);
801 memmove(puid+ln, pusr+5, lm);
802 }
803 // Copy the name
804 memcpy(puid, cuid, ln);
805 // Adjust the length
806 nlen += (ln - 5);
807 }
808
809// Terminate to the new length
810//
811 ccfile[nlen] = 0;
812
813// Point the received creds
814//
815 krbContext.Lock();
816 krb5_data forwardCreds;
817 forwardCreds.data = &cred->buffer[XrdSecPROTOIDLEN];
818 forwardCreds.length = cred->size -XrdSecPROTOIDLEN;
819
820// Get the replay cache
821//
822 krb5_rcache rcache;
823 if ((rc = krb5_get_server_rcache(krb_context,
824 krb5_princ_component(krb_context, krb_principal, 0),
825 &rcache)))
826 return rc;
827 if ((rc = krb5_auth_con_setrcache(krb_context, AuthContext, rcache)))
828 return rc;
829
830// Fill-in remote address
831//
832 SetAddr(ipadd);
833 if ((rc = krb5_auth_con_setaddrs(krb_context, AuthContext, 0, &ipadd)))
834 return rc;
835
836// Readout the credentials
837//
838 krb5_creds **creds = 0;
839 if ((rc = krb5_rd_cred(krb_context, AuthContext,
840 &forwardCreds, &creds, 0)))
841 return rc;
842
843// Resolve cache name
844 krb5_ccache cache = 0;
845 if ((rc = krb5_cc_resolve(krb_context, ccfile, &cache)))
846 return rc;
847
848// Init cache
849//
850 if ((rc = krb5_cc_initialize(krb_context, cache,
851 Ticket->enc_part2->client)))
852 return rc;
853
854// Store credentials in cache
855//
856 if ((rc = krb5_cc_store_cred(krb_context, cache, *creds)))
857 return rc;
858
859// Close cache
860 if ((rc = krb5_cc_close(krb_context, cache)))
861 return rc;
862
863// Change permission and ownership of the file
864//
865 if (chmod(ccfile, 0600) == -1)
866 return Fatal(erp, errno, "Unable to change file permissions;", ccfile, 0);
867
868// Done
869//
870 return 0;
871}
872
873/******************************************************************************/
874/* S e t A d d r */
875/******************************************************************************/
876
877void XrdSecProtocolkrb5::SetAddr(krb5_address &ipadd)
878{
879// The below is a hack but that's how it is actually done!
880//
881 if (epAddr.Family() == AF_INET6)
882 {struct sockaddr_in6 *ip = (struct sockaddr_in6 *)epAddr.SockAddr();
883 ipadd.addrtype = ADDRTYPE_INET6;
884 ipadd.length = sizeof(ip->sin6_addr);
885 ipadd.contents = (krb5_octet *)&ip->sin6_addr;
886 } else {
887 struct sockaddr_in *ip = (struct sockaddr_in *)epAddr.SockAddr();
888 ipadd.addrtype = ADDRTYPE_INET;
889 ipadd.length = sizeof(ip->sin_addr);
890 ipadd.contents = (krb5_octet *)&ip->sin_addr;
891 }
892}
893
894/******************************************************************************/
895/* X r d S e c p r o t o c o l k r b 5 I n i t */
896/******************************************************************************/
897
898extern "C"
899{
900char *XrdSecProtocolkrb5Init(const char mode,
901 const char *parms,
902 XrdOucErrInfo *erp)
903{
904 char *op, *KPrincipal=0, *Keytab=0, *ExpFile=0;
905 char parmbuff[1024];
906 XrdOucTokenizer inParms(parmbuff);
907 int options = XrdSecNOIPCHK;
908 static bool serverinitialized = false;
909
910// For client-side one-time initialization, we only need to set debug flag and
911// initialize the kerberos context and cache location.
912//
913 if ((mode == 'c') || (serverinitialized))
914 {
915 int opts = 0;
916 if (getenv("XrdSecDEBUG")) opts |= XrdSecDEBUG;
917 if (getenv("XrdSecKRB5INITTKN")) opts |= XrdSecINITTKN;
919 return (XrdSecProtocolkrb5::Init(erp) ? (char *)0 : (char *)"");
920 }
921
922 if (!serverinitialized) {
923 serverinitialized = true;
924 }
925
926// Duplicate the parms
927//
928 if (parms) strlcpy(parmbuff, parms, sizeof(parmbuff));
929 else {char *msg = (char *)"Seckrb5: Kerberos parameters not specified.";
930 if (erp) erp->setErrInfo(EINVAL, msg);
931 else std::cerr <<msg <<std::endl;
932 return (char *)0;
933 }
934
935// Expected parameters: [<keytab>] [-ipchk] [-exptkn[:filetemplate]] <principal>
936//
937 if (inParms.GetLine())
938 {if ((op = inParms.GetToken()) && *op == '/')
939 {Keytab = op; op = inParms.GetToken();}
940 if (op && !strcmp(op, "-ipchk"))
941 {options &= ~XrdSecNOIPCHK;
942 op = inParms.GetToken();
943 }
944 if (op && !strncmp(op, "-exptkn", 7))
945 {options |= XrdSecEXPTKN;
946 if (op[7] == ':') ExpFile = op+8;
947 op = inParms.GetToken();
948 }
949 KPrincipal = strdup(op);
950 }
951
952 if (ExpFile)
953 fprintf(stderr,"Template for exports: %s\n", ExpFile);
954 else
955 fprintf(stderr,"Template for exports not set\n");
956
957// Now make sure that we have all the right info
958//
959 if (!KPrincipal)
960 {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
961 if (erp) erp->setErrInfo(EINVAL, msg);
962 else std::cerr <<msg <<std::endl;
963 return (char *)0;
964 }
965
966// Expand possible keywords in the principal
967//
968 int plen = strlen(KPrincipal);
969 int lkey = strlen("<host>");
970 char *phost = (char *) strstr(&KPrincipal[0], "<host>");
971 if (phost)
972 {char *hn = XrdNetUtils::MyHostName();
973 if (hn)
974 {int lhn = strlen(hn);
975 if (lhn != lkey) {
976 // Allocate, if needed
977 int lnew = plen - lkey + lhn;
978 if (lnew > plen) {
979 KPrincipal = (char *) realloc(KPrincipal, lnew+1);
980 KPrincipal[lnew] = 0;
981 phost = (char *) strstr(&KPrincipal[0], "<host>");
982 }
983 // Adjust the space
984 int lm = plen - (int)(phost + lkey - &KPrincipal[0]);
985 memmove(phost + lhn, phost + lkey, lm);
986 }
987 // Copy the name
988 memcpy(phost, hn, lhn);
989 // Cleanup
990 free(hn);
991 }
992 }
993
994// Now initialize the server
995//
996 options |= XrdSecDEBUG;
999 if (!XrdSecProtocolkrb5::Init(erp, KPrincipal, Keytab))
1000 {free(KPrincipal);
1001 int lpars = strlen(XrdSecProtocolkrb5::getPrincipal());
1002 if (options & XrdSecEXPTKN)
1003 lpars += strlen(",fwd");
1004 char *params = (char *)malloc(lpars+1);
1005 if (params)
1006 {memset(params,0,lpars+1);
1007 strcpy(params,XrdSecProtocolkrb5::getPrincipal());
1008 if (options & XrdSecEXPTKN)
1009 strcat(params,",fwd");
1011 return params;
1012 }
1013 return (char *)0;
1014 }
1015
1016// Failure
1017//
1018 free(KPrincipal);
1019 return (char *)0;
1020}
1021}
1022
1023/******************************************************************************/
1024/* X r d S e c P r o t o c o l k r b 5 O b j e c t */
1025/******************************************************************************/
1026
1027extern "C"
1028{
1030 const char *hostname,
1031 XrdNetAddrInfo &endPoint,
1032 const char *parms,
1033 XrdOucErrInfo *erp)
1034{
1035 XrdSecProtocolkrb5 *prot;
1036 char *KPrincipal=0;
1037
1038// If this is a client call, then we need to get the target principal from the
1039// parms (which must be the first and only token). For servers, we use the
1040// context we established at initialization time.
1041//
1042 if (mode == 'c')
1043 {if ((KPrincipal = (char *)parms)) while(*KPrincipal == ' ') KPrincipal++;
1044 if (!KPrincipal || !*KPrincipal)
1045 {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
1046 if (erp) erp->setErrInfo(EINVAL, msg);
1047 else std::cerr <<msg <<std::endl;
1048 return (XrdSecProtocol *)0;
1049 }
1050 }
1051
1052// Get a new protocol object
1053//
1054 if (!(prot = new XrdSecProtocolkrb5(KPrincipal, hostname, endPoint)))
1055 {char *msg = (char *)"Seckrb5: Insufficient memory for protocol.";
1056 if (erp) erp->setErrInfo(ENOMEM, msg);
1057 else std::cerr <<msg <<std::endl;
1058 return (XrdSecProtocol *)0;
1059 }
1060
1061// All done
1062//
1063 return prot;
1064}
1065
1066void
1067 __eprintf (const char *string, const char *expression,
1068 unsigned int line, const char *filename)
1069 {
1070 fprintf (stderr, string, expression, line, filename);
1071 fflush (stderr);
1072 abort ();
1073 }
1074}
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.