XRootD
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 
45 extern "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 
56 #include "XrdNet/XrdNetAddrInfo.hh"
57 #include "XrdNet/XrdNetUtils.hh"
58 #include "XrdOuc/XrdOucErrInfo.hh"
59 #include "XrdOuc/XrdOucEnv.hh"
60 #include "XrdSys/XrdSysHeaders.hh"
61 #include "XrdSys/XrdSysPthread.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 
84 typedef 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 {
92 public:
93 friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor
94 
96  XrdSecParameters **parms,
97  XrdOucErrInfo *einfo=0);
98 
100  XrdOucErrInfo *einfo=0);
101 
102 static char *getPrincipal() {return Principal;}
103 
104 static int Init(XrdOucErrInfo *einfo, char *KP=0, char *kfn=0);
105 
106 static void setOpts(int opts) {options = opts;}
107 static void setClientOpts(int opts) {client_options = opts;}
108 static void setParms(char *param) {Parms = param;}
109 static 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 
138 private:
139 
140  ~XrdSecProtocolkrb5() {} // Delete() does it all
141 
142 static int Fatal(XrdOucErrInfo *erp,int rc,const char *msg1,char *KP=0,int krc=0);
143 static int get_krbCreds(char *KP, krb5_creds **krb_creds);
144  void SetAddr(krb5_address &ipadd);
145 
146 static XrdSysMutex krbContext; // Server
147 static XrdSysMutex krbClientContext;// Client
148 static int options; // Server
149 static int client_options;// Client
150 static krb5_context krb_context; // Server
151 static krb5_context krb_client_context; // Client
152 static krb5_ccache krb_client_ccache; // Client
153 static krb5_ccache krb_ccache; // Server
154 static krb5_keytab krb_keytab; // Server
155 static krb5_principal krb_principal; // Server
156 
157 static char *Principal; // Server's principal name
158 static char *Parms; // Server parameters
159 
160 static char ExpFile[XrdSecMAXPATHLEN]; // Server: (template for)
161  // file to export token
162 int exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp);
163 int get_krbFwdCreds(char *KP, krb5_data *outdata);
164 
165 XrdNetAddrInfo epAddr;
166 char CName[256]; // Kerberos limit
167 char *Service; // Target principal for client
168 char Step; // Indicates at which step we are
169 krb5_auth_context AuthContext; // Authetication context
170 krb5_auth_context AuthClientContext; // Authetication context
171 krb5_ticket *Ticket; // Ticket associated to client authentication
172 krb5_creds *Creds; // Client: credentials
173 };
174 
175 /******************************************************************************/
176 /* S t a t i c D a t a */
177 /******************************************************************************/
178 
179 XrdSysMutex XrdSecProtocolkrb5::krbContext; // Server
180 XrdSysMutex XrdSecProtocolkrb5::krbClientContext; // Client
181 
182 int XrdSecProtocolkrb5::client_options = 0;// Client
183 int XrdSecProtocolkrb5::options = 0; // Server
184 krb5_context XrdSecProtocolkrb5::krb_context; // Server
185 krb5_context XrdSecProtocolkrb5::krb_client_context; // Client
186 krb5_ccache XrdSecProtocolkrb5::krb_client_ccache; // Client
187 krb5_ccache XrdSecProtocolkrb5::krb_ccache; // Server
188 krb5_keytab XrdSecProtocolkrb5::krb_keytab = NULL; // Server
189 krb5_principal XrdSecProtocolkrb5::krb_principal; // Server
190 
191 char *XrdSecProtocolkrb5::Principal = 0; // Server
192 char *XrdSecProtocolkrb5::Parms = 0; // Server
193 
194 char 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 
544 int 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 
619 int 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 
648 int 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 
698 int 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 
744 int 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 
854 void 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 
875 extern "C"
876 {
877 char *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 
1004 extern "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 
1043 void
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 access(const char *path, int amode)
int fflush(FILE *stream)
XrdSecBuffer XrdSecParameters
XrdSecBuffer XrdSecCredentials
XrdSecProtocol * XrdSecProtocolkrb5Object(const char mode, const char *hostname, XrdNetAddrInfo &endPoint, const char *parms, XrdOucErrInfo *erp)
char * XrdSecProtocolkrb5Init(const char mode, const char *parms, XrdOucErrInfo *erp)
#define XrdSecDEBUG
#define CLDBG(x)
#define krb_etxt(x)
#define CLPRT(x)
#define XrdSecPROTOIDLEN
#define XrdSecMAXPATHLEN
#define XrdSecEXPTKN
#define XrdSecPROTOIDENT
krb5_error_code krb_rc
#define XrdSecINITTKN
#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)
Definition: XrdNetUtils.cc:666
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69
int setErrInfo(int code, const char *emsg)
XrdOucEnv * getEnv()
char * GetToken(char **rest=0, int lowcase=0)
XrdNetAddrInfo * addrInfo
Entity's connection details.
Definition: XrdSecEntity.hh:80
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5)
Definition: XrdSecEntity.hh:67
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
char * host
Entity's host name dnr dependent.
Definition: XrdSecEntity.hh:70
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 void setOpts(int opts)
static void setClientOpts(int opts)
static char * getPrincipal()
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
XrdSsiService * Service
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.