XrdSecProtocolkrb5.cc

Go to the documentation of this file.
00001 /******************************************************************************/
00002 /*                                                                            */
00003 /*                 X r d S e c P r o t o c o l k r b 5 . c c                  */
00004 /*                                                                            */
00005 /* (c) 2003 by the Board of Trustees of the Leland Stanford, Jr., University  */
00006 /*                            All Rights Reserved                             */
00007 /*   Produced by Andrew Hanushevsky for Stanford University under contract    */
00008 /*              DE-AC03-76-SFO0515 with the Department of Energy              */
00009 /*   Modifications:                                                           */
00010 /*    - January 2007: add support for forwarded tickets                       */
00011 /*                   (author: G. Ganis, CERN)                                 */
00012 /******************************************************************************/
00013 
00014 #include <unistd.h>
00015 #include <ctype.h>
00016 #include <errno.h>
00017 #include <stdlib.h>
00018 #include <strings.h>
00019 #include <stdio.h>
00020 #include <sys/param.h>
00021 #include <pwd.h>
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 
00025 
00026 extern "C" {
00027 #include "krb5.h"
00028 #ifdef HAVE_ET_COM_ERR_H
00029 #include "et/com_err.h"
00030 #else
00031 #include "com_err.h"
00032 #endif
00033 }
00034 
00035 #include "XrdNet/XrdNetDNS.hh"
00036 #include "XrdOuc/XrdOucErrInfo.hh"
00037 #include "XrdSys/XrdSysHeaders.hh"
00038 #include "XrdSys/XrdSysPthread.hh"
00039 #include "XrdOuc/XrdOucTokenizer.hh"
00040 #include "XrdSec/XrdSecInterface.hh"
00041 #include "XrdSys/XrdSysPriv.hh"
00042   
00043 /******************************************************************************/
00044 /*                               D e f i n e s                                */
00045 /******************************************************************************/
00046 
00047 #define krb_etxt(x) (char *)error_message(x)
00048   
00049 #define XrdSecPROTOIDENT    "krb5"
00050 #define XrdSecPROTOIDLEN    sizeof(XrdSecPROTOIDENT)
00051 #define XrdSecNOIPCHK       0x0001
00052 #define XrdSecEXPTKN        0x0002
00053 #define XrdSecINITTKN       0x0004
00054 #define XrdSecDEBUG         0x1000
00055 
00056 #define XrdSecMAXPATHLEN      4096
00057 
00058 #define CLDBG(x) if (client_options & XrdSecDEBUG) cerr <<"Seckrb5: " <<x <<endl;
00059 #define CLPRT(x) cerr <<"Seckrb5: " <<x <<endl;
00060 
00061 typedef  krb5_error_code krb_rc;
00062 
00063 /******************************************************************************/
00064 /*              X r d S e c P r o t o c o l k r b 5   C l a s s               */
00065 /******************************************************************************/
00066 
00067 class XrdSecProtocolkrb5 : public XrdSecProtocol
00068 {
00069 public:
00070 friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor
00071 
00072         int                Authenticate  (XrdSecCredentials *cred,
00073                                           XrdSecParameters **parms,
00074                                           XrdOucErrInfo     *einfo=0);
00075 
00076         XrdSecCredentials *getCredentials(XrdSecParameters  *parm=0,
00077                                           XrdOucErrInfo     *einfo=0);
00078 
00079 static  char              *getPrincipal() {return Principal;}
00080 
00081 static  int                Init(XrdOucErrInfo *einfo, char *KP=0, char *kfn=0);
00082 
00083 static  void               setOpts(int opts) {options = opts;}
00084 static  void               setClientOpts(int opts) {client_options = opts;}
00085 static  void               setParms(char *param) {Parms = param;}
00086 static  void               setExpFile(char *expfile)
00087                                      {if (expfile)
00088                                          {int lt = strlen(expfile);
00089                                           lt = (lt >= XrdSecMAXPATHLEN) ?
00090                                                       XrdSecMAXPATHLEN -1 : lt;
00091                                           memcpy(ExpFile, expfile, lt);
00092                                           ExpFile[lt] = 0;
00093                                          }
00094                                      }
00095 
00096         XrdSecProtocolkrb5(const char                *KP,
00097                            const char                *hname,
00098                            const struct sockaddr     *ipadd)
00099                           : XrdSecProtocol(XrdSecPROTOIDENT)
00100                           {Service = (KP ? strdup(KP) : 0);
00101                            Entity.host = strdup(hname);
00102                            memcpy(&hostaddr, ipadd, sizeof(hostaddr));
00103                            CName[0] = '?'; CName[1] = '\0';
00104                            Entity.name = CName;
00105                            Step = 0;
00106                            AuthContext = 0;
00107                            AuthClientContext = 0;
00108                            Ticket = 0;
00109                            Creds = 0;
00110                           }
00111 
00112         void              Delete();
00113 
00114 private:
00115 
00116        ~XrdSecProtocolkrb5() {} // Delete() does it all
00117 
00118 static int Fatal(XrdOucErrInfo *erp,int rc,const char *msg1,char *KP=0,int krc=0);
00119 static int get_krbCreds(char *KP, krb5_creds **krb_creds);
00120 
00121 static XrdSysMutex        krbContext;    // Server
00122 static XrdSysMutex        krbClientContext;// Client
00123 static int                options;       // Server
00124 static int                client_options;// Client
00125 static krb5_context       krb_context;   // Server
00126 static krb5_context       krb_client_context;   // Client 
00127 static krb5_ccache        krb_client_ccache;    // Client 
00128 static krb5_ccache        krb_ccache;    // Server
00129 static krb5_keytab        krb_keytab;    // Server
00130 static uid_t              krb_kt_uid;// Server
00131 static gid_t              krb_kt_gid;// Server
00132 static krb5_principal     krb_principal; // Server
00133 
00134 static char              *Principal;     // Server's principal name
00135 static char              *Parms;         // Server parameters
00136 
00137 static char               ExpFile[XrdSecMAXPATHLEN]; // Server: (template for)
00138                                                      // file to export token
00139 int exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp);
00140 int get_krbFwdCreds(char *KP, krb5_data *outdata);
00141 
00142 struct sockaddr           hostaddr;      // Client-side only
00143 char                      CName[256];    // Kerberos limit
00144 char                     *Service;       // Target principal for client
00145 char                      Step;          // Indicates at which step we are
00146 krb5_auth_context         AuthContext;   // Authetication context
00147 krb5_auth_context         AuthClientContext;   // Authetication context
00148 krb5_ticket              *Ticket;        // Ticket associated to client authentication
00149 krb5_creds               *Creds;         // Client: credentials
00150 };
00151 
00152 /******************************************************************************/
00153 /*                           S t a t i c   D a t a                            */
00154 /******************************************************************************/
00155   
00156 XrdSysMutex         XrdSecProtocolkrb5::krbContext;        // Server
00157 XrdSysMutex         XrdSecProtocolkrb5::krbClientContext;  // Client
00158 
00159 int                 XrdSecProtocolkrb5::client_options = 0;// Client
00160 int                 XrdSecProtocolkrb5::options = 0;       // Server
00161 krb5_context        XrdSecProtocolkrb5::krb_context;       // Server
00162 krb5_context        XrdSecProtocolkrb5::krb_client_context;       // Client 
00163 krb5_ccache         XrdSecProtocolkrb5::krb_client_ccache; // Client
00164 krb5_ccache         XrdSecProtocolkrb5::krb_ccache;        // Server
00165 krb5_keytab         XrdSecProtocolkrb5::krb_keytab = NULL; // Server
00166 uid_t               XrdSecProtocolkrb5::krb_kt_uid = 0;    // Server
00167 gid_t               XrdSecProtocolkrb5::krb_kt_gid = 0;    // Server
00168 krb5_principal      XrdSecProtocolkrb5::krb_principal;     // Server
00169 
00170 char               *XrdSecProtocolkrb5::Principal = 0;     // Server
00171 char               *XrdSecProtocolkrb5::Parms = 0;         // Server
00172 
00173 char                XrdSecProtocolkrb5::ExpFile[XrdSecMAXPATHLEN] = "/tmp/krb5cc_<uid>";
00174 
00175 /******************************************************************************/
00176 /*                                D e l e t e                                 */
00177 /******************************************************************************/
00178   
00179 void XrdSecProtocolkrb5::Delete()
00180 {
00181      if (Parms)       free(Parms); Parms = 0;
00182      if (Creds)       krb5_free_creds(krb_context, Creds);
00183      if (Ticket)      krb5_free_ticket(krb_context, Ticket);
00184      if (AuthContext) krb5_auth_con_free(krb_context, AuthContext);
00185      if (AuthClientContext) krb5_auth_con_free(krb_client_context, AuthClientContext);
00186      if (Entity.host) free(Entity.host);
00187      if (Service)     free(Service);
00188      delete this;
00189 }
00190 
00191 /******************************************************************************/
00192 /*                        g e t C r e d e n t i a l s                         */
00193 /******************************************************************************/
00194 
00195 XrdSecCredentials *XrdSecProtocolkrb5::getCredentials(XrdSecParameters *noparm,
00196                                                       XrdOucErrInfo    *error)
00197 {
00198    char *buff;
00199    int bsz;
00200    krb_rc rc;
00201    krb5_data         outbuf;
00202    CLDBG("getCredentials");
00203 // Supply null credentials if so needed for this protocol
00204 //
00205    if (!Service)
00206       {CLDBG("Null credentials supplied.");
00207        return new XrdSecCredentials(0,0);
00208       }
00209 
00210 // Set KRB5CCNAME to its dfault value, if not done
00211 //
00212    if (!getenv("KRB5CCNAME")) {
00213       char ccname[128];
00214       sprintf(ccname, "KRB5CCNAME=FILE:/tmp/krb5cc_%d", geteuid());
00215       putenv(strdup(ccname));
00216    }
00217    CLDBG(getenv("KRB5CCNAME"));
00218          
00219 // Initialize the context and get the cache default.
00220 //
00221    if ((rc = krb5_init_context(&krb_client_context)))
00222       {Fatal(error, ENOPROTOOPT, "Kerberos initialization failed", Service, rc);
00223        return (XrdSecCredentials *)0;
00224       }
00225 
00226    CLDBG("init context");
00227 // Obtain the default cache location
00228 //
00229    if ((rc = krb5_cc_default(krb_client_context, &krb_client_ccache)))
00230       {Fatal(error, ENOPROTOOPT, "Unable to locate cred cache", Service, rc);
00231        return (XrdSecCredentials *)0;
00232       }
00233 
00234    CLDBG("cc cache default");
00235 // Check if the server asked for a forwardable ticket
00236 //
00237    char *pfwd = 0;
00238    if ((pfwd = (char *) strstr(Service,",fwd")))
00239       {
00240          client_options |= XrdSecEXPTKN;
00241          *pfwd = 0;
00242       }
00243 
00244 // Clear outgoing ticket and lock the kerberos context
00245 //
00246    outbuf.length = 0; outbuf.data = 0;
00247 
00248    CLDBG("context lock");
00249    krbClientContext.Lock();
00250    CLDBG("context locked");
00251 
00252 // If this is not the first call, we are asked to send over a delegated ticket:
00253 // we must create it first
00254 // we save it into a file and return signalling the end of the hand-shake
00255 //
00256 
00257    if (Step > 0)
00258       {if ((rc = get_krbFwdCreds(Service, &outbuf)))
00259           {krbClientContext.UnLock();
00260            Fatal(error, ESRCH, "Unable to get forwarded credentials", Service, rc);
00261            return (XrdSecCredentials *)0;
00262           } else
00263             {bsz = XrdSecPROTOIDLEN+outbuf.length;
00264              if (!(buff = (char *)malloc(bsz)))
00265                 {krbClientContext.UnLock();
00266                  Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service);
00267                  return (XrdSecCredentials *)0;
00268                 }
00269              strcpy(buff, XrdSecPROTOIDENT);
00270              memcpy((void *)(buff+XrdSecPROTOIDLEN),
00271                             (const void *)outbuf.data, (size_t)outbuf.length);
00272              CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service);
00273              if (outbuf.data)  free(outbuf.data);
00274              krbClientContext.UnLock();
00275              return new XrdSecCredentials(buff, bsz);
00276             }
00277       }
00278 
00279 // Increment the step
00280 //
00281    Step += 1;
00282 
00283 // Get a service ticket for this principal
00284 //
00285    const char *reinitcmd = (client_options & XrdSecEXPTKN) ? "kinit -f" : "kinit";
00286    bool notdone = 1;
00287    bool reinitdone = 0;
00288    while (notdone)
00289       {if ((rc = (krb_rc)get_krbCreds(Service, &Creds)))
00290           { if (!(client_options & XrdSecINITTKN) || reinitdone)
00291                {krbClientContext.UnLock();
00292                 const char *m = (!(client_options & XrdSecINITTKN)) ?
00293                                 "No or invalid credentials" : "Unable to get credentials";
00294                 Fatal(error, ESRCH, m, Service, rc);
00295                 return (XrdSecCredentials *)0;
00296                } else {// Need to re-init
00297                        CLPRT("Ticket missing or invalid: re-init ");
00298                        rc = system(reinitcmd);
00299                        CLDBG("getCredentials: return code from '"<<reinitcmd<<
00300                              "': "<< rc);
00301                        reinitdone = 1;
00302                        continue;
00303                       }
00304           }
00305        if (client_options & XrdSecEXPTKN)
00306           {// Make sure the ticket is forwardable
00307            if (!(Creds->ticket_flags & TKT_FLG_FORWARDABLE))
00308               { if ((client_options & XrdSecINITTKN) && !reinitdone)
00309                    { // Need to re-init
00310                     CLPRT("Existing ticket is not forwardable: re-init ");
00311                     rc = system(reinitcmd);
00312                     CLDBG("getCredentials: return code from '"<<reinitcmd<<
00313                           "': "<< rc);
00314                     reinitdone = 1;
00315                     continue;
00316                    } else {
00317                     krbClientContext.UnLock();
00318                     Fatal(error, ESRCH, "Existing ticket is not forwardable: cannot continue",
00319                                         Service, rc);
00320                     return (XrdSecCredentials *)0;
00321                    }
00322               }
00323           }
00324        // We are done
00325        notdone = 0;
00326       }
00327 
00328 // Set the RET_TIME flag in the authentication context 
00329 //
00330    if ((rc = krb5_auth_con_init(krb_client_context, &AuthClientContext)))
00331       {krbClientContext.UnLock();
00332        Fatal(error, ESRCH, "Unable to init a new auth context", Service, rc);
00333        return (XrdSecCredentials *)0;
00334       }
00335 
00336 // Generate a kerberos-style authentication message
00337 //
00338    rc = krb5_mk_req_extended(krb_client_context, &AuthClientContext,
00339              AP_OPTS_USE_SESSION_KEY,(krb5_data *)0, Creds,&outbuf);
00340 
00341 // Check if all succeeded. If so, copy the ticket into the buffer. We wish
00342 // we could place the ticket directly into the buffer but architectural
00343 // differences won't allow us that optimization.
00344 //
00345    if (!rc)
00346       {bsz = XrdSecPROTOIDLEN+outbuf.length;
00347        if (!(buff = (char *)malloc(bsz)))
00348           {krbClientContext.UnLock();
00349            Fatal(error, ENOMEM, "Insufficient memory for credentials.", Service);
00350            return (XrdSecCredentials *)0;
00351           }
00352        strcpy(buff, XrdSecPROTOIDENT);
00353        memcpy((void *)(buff+XrdSecPROTOIDLEN),
00354               (const void *)outbuf.data, (size_t)outbuf.length);
00355        CLDBG("Returned " <<bsz <<" bytes of creds; p=" <<Service);
00356        if (outbuf.data)  free(outbuf.data);
00357        krbClientContext.UnLock();
00358        return new XrdSecCredentials(buff, bsz);
00359       }
00360 
00361 // Diagnose the failure
00362 //
00363    if (outbuf.data)  free(outbuf.data);
00364    krbClientContext.UnLock();
00365    Fatal(error, EACCES, "Unable to get credentials", Service, rc);
00366    return (XrdSecCredentials *)0;
00367 }
00368 
00369 /******************************************************************************/
00370 /*               S e r v e r   O r i e n t e d   M e t h o d s                */
00371 /******************************************************************************/
00372 /******************************************************************************/
00373 /*                          A u t h e n t i c a t e                           */
00374 /******************************************************************************/
00375 
00376 int XrdSecProtocolkrb5::Authenticate(XrdSecCredentials *cred,
00377                                      XrdSecParameters **parms,
00378                                      XrdOucErrInfo     *error)
00379 {
00380    krb5_data         inbuf;                 /* Kerberos data */
00381    krb5_address      ipadd;
00382    krb_rc rc = 0;
00383    char *iferror = 0;
00384 
00385 // Check if we have any credentials or if no credentials really needed.
00386 // In either case, use host name as client name
00387 //
00388    if (cred->size <= (int)XrdSecPROTOIDLEN || !cred->buffer)
00389       {strncpy(Entity.prot, "host", sizeof(Entity.prot));
00390        return 0;
00391       }
00392 
00393 // Check if this is a recognized protocol
00394 //
00395    if (strcmp(cred->buffer, XrdSecPROTOIDENT))
00396       {char emsg[256];
00397        snprintf(emsg, sizeof(emsg),
00398                 "Authentication protocol id mismatch (%.4s != %.4s).",
00399                 XrdSecPROTOIDENT,  cred->buffer);
00400        Fatal(error, EINVAL, emsg, Principal);
00401        return -1;
00402       }
00403 
00404    CLDBG("protocol check");
00405 
00406    char printit[4096];
00407    sprintf(printit,"Step is %d",Step);
00408    CLDBG(printit);
00409 // If this is not the first call the buffer contains a forwarded token:
00410 // we save it into a file and return signalling the end of the hand-shake
00411 //
00412    if (Step > 0)
00413       {if ((rc = exp_krbTkn(cred, error)))
00414           iferror = (char *)"Unable to export the token to file";
00415        if (rc && iferror) {
00416           krbContext.UnLock();
00417           return Fatal(error, EINVAL, iferror, Principal, rc);
00418        }
00419        krbContext.UnLock();
00420 
00421        return 0;
00422       }
00423    
00424    CLDBG("protocol check");
00425 
00426 // Increment the step
00427 //
00428    Step += 1;
00429 
00430 // Indicate who we are
00431 //
00432    strncpy(Entity.prot, XrdSecPROTOIDENT, sizeof(Entity.prot));
00433 
00434 // Create a kerberos style ticket and obtain the kerberos mutex
00435 //
00436 
00437    CLDBG("Context Lock");
00438 
00439    inbuf.length = cred->size -XrdSecPROTOIDLEN;
00440    inbuf.data   = &cred->buffer[XrdSecPROTOIDLEN];
00441 
00442    krbContext.Lock();
00443 
00444 // Check if whether the IP address in the credentials must match that of
00445 // the incomming host.
00446 //
00447    CLDBG("Context Locked");
00448    if (!(XrdSecProtocolkrb5::options & XrdSecNOIPCHK))
00449       {struct sockaddr_in *ip = (struct sockaddr_in *)&hostaddr;
00450       // The above is a hack but K5 does it this way
00451        ipadd.addrtype = ADDRTYPE_INET;
00452        ipadd.length = sizeof(ip->sin_addr);
00453        ipadd.contents = (krb5_octet *)&ip->sin_addr;
00454        iferror = (char *)"Unable to validate ip address;";
00455        if (!(rc=krb5_auth_con_init(krb_context, &AuthContext)))
00456              rc=krb5_auth_con_setaddrs(krb_context, AuthContext, NULL, &ipadd);
00457       }
00458 
00459 // Decode the credentials and extract client's name
00460 //
00461    {  XrdSysPrivGuard pGuard(krb_kt_uid, krb_kt_gid);
00462       if (pGuard.Valid())
00463          {if (!rc)
00464              {if ((rc = krb5_rd_req(krb_context, &AuthContext, &inbuf,
00465                                   (krb5_const_principal)krb_principal,
00466                                    krb_keytab, NULL, &Ticket)))
00467                  iferror = (char *)"Unable to authenticate credentials;";
00468               else if ((rc = krb5_aname_to_localname(krb_context,
00469                                           Ticket->enc_part2->client,
00470                                           sizeof(CName)-1, CName)))
00471                      iferror = (char *)"Unable to extract client name;";
00472              }
00473       } else
00474          iferror = (char *)"Unable to acquire privileges to read the keytab;";
00475    }
00476 // Make sure the name is null-terminated
00477 //
00478    CName[sizeof(CName)-1] = '\0';
00479 
00480 // If requested, ask the client for a forwardable token
00481    int hsrc = 0;
00482    if (!rc && XrdSecProtocolkrb5::options & XrdSecEXPTKN) {
00483       // We just ask for more; the client knows what to send over
00484       hsrc = 1;
00485       // We need to fill-in a fake buffer
00486       int len =  strlen("fwdtgt") + 1;
00487       char *buf = (char *) malloc(len);
00488       memcpy(buf, "fwdtgt", len-1);
00489       buf[len-1] = 0;
00490       *parms = new XrdSecParameters(buf, len);
00491    }
00492 
00493 // Release any allocated storage at this point and unlock mutex
00494 //
00495    krbContext.UnLock();
00496 
00497 // Diagnose any errors
00498 //
00499    if (rc && iferror)
00500       return Fatal(error, EACCES, iferror, Principal, rc);
00501 
00502 // All done
00503 //
00504    return hsrc;
00505 }
00506 
00507 /******************************************************************************/
00508 /*                I n i t i a l i z a t i o n   M e t h o d s                 */
00509 /******************************************************************************/
00510 /******************************************************************************/
00511 /*                                  I n i t                                   */
00512 /******************************************************************************/
00513 
00514 int XrdSecProtocolkrb5::Init(XrdOucErrInfo *erp, char *KP, char *kfn)
00515 {
00516    krb_rc rc;
00517    char buff[1024];
00518 
00519 // Create a kerberos context. There is one such context per protocol object.
00520 //
00521 
00522 // If we have no principal then this is a client-side call
00523 //
00524    if (!KP) {
00525      if ((rc = krb5_init_context(&krb_client_context)))
00526        return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc);
00527 
00528      // Obtain the default cache location
00529      //
00530      if ((rc = krb5_cc_default(krb_client_context, &krb_client_ccache)))
00531        return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc);
00532      
00533      return 0;
00534    }
00535 
00536    if ((rc = krb5_init_context(&krb_context)))
00537       return Fatal(erp, ENOPROTOOPT, "Kerberos initialization failed", KP, rc);
00538 
00539 // Obtain the default cache location
00540 //
00541    if ((rc = krb5_cc_default(krb_context, &krb_ccache)))
00542       return Fatal(erp, ENOPROTOOPT, "Unable to locate cred cache", KP, rc);
00543 
00544 // Try to resolve the keyfile name
00545 //
00546    if (kfn && *kfn)
00547       {if ((rc = krb5_kt_resolve(krb_context, kfn, &krb_keytab)))
00548           {snprintf(buff, sizeof(buff), "Unable to find keytab '%s';", kfn);
00549            return Fatal(erp, ESRCH, buff, Principal, rc);
00550           }
00551       } else {
00552        krb5_kt_default(krb_context, &krb_keytab);
00553       }
00554 
00555 // Keytab name
00556 //
00557    char krb_kt_name[1024];
00558    if ((rc = krb5_kt_get_name(krb_context, krb_keytab, &krb_kt_name[0], 1024)))
00559       {snprintf(buff, sizeof(buff), "Unable to get keytab name;");
00560        return Fatal(erp, ESRCH, buff, Principal, rc);
00561       }
00562 
00563 // Find out if need to acquire privileges to open and read the keytab file and
00564 // set the required uid,gid accordingly
00565 //
00566    krb_kt_uid = 0;
00567    krb_kt_gid = 0;
00568    char *pf = 0;
00569    if ((pf = (char *) strstr(krb_kt_name, "FILE:")))
00570       {pf += strlen("FILE:");
00571        if (strlen(pf) > 0)
00572           {struct stat st;
00573            if (!stat(pf, &st))
00574               {if (st.st_uid != geteuid() || st.st_gid != getegid())
00575                   {krb_kt_uid = st.st_uid;
00576                    krb_kt_gid = st.st_gid;
00577                   }
00578               }
00579           }
00580       }
00581 
00582 // Now, extract the "principal/instance@realm" from the stream
00583 //
00584    if ((rc = krb5_parse_name(krb_context,KP,&krb_principal)))
00585      return Fatal(erp, EINVAL, "Cannot parse service name", KP, rc);
00586 
00587 // Establish the correct principal to use
00588 //
00589    if ((rc = krb5_unparse_name(krb_context,(krb5_const_principal)krb_principal,
00590                               (char **)&Principal)))
00591       return Fatal(erp, EINVAL, "Unable to unparse principal;", KP, rc);
00592 
00593 // All done
00594 //
00595    return 0;
00596 }
00597 
00598 /******************************************************************************/
00599 /*                       P r i v a t e   M e t h o d s                        */
00600 /******************************************************************************/
00601 /******************************************************************************/
00602 /*                                 F a t a l                                  */
00603 /******************************************************************************/
00604 
00605 int XrdSecProtocolkrb5::Fatal(XrdOucErrInfo *erp, int rc, const char *msg,
00606                              char *KP, int krc)
00607 {
00608    const char *msgv[8];
00609    int k, i = 0;
00610 
00611               msgv[i++] = "Seckrb5: ";    //0
00612               msgv[i++] = msg;            //1
00613    if (krc)  {msgv[i++] = "; ";           //2
00614               msgv[i++] =  krb_etxt(krc); //3
00615              }
00616    if (KP)   {msgv[i++] = " (p=";         //4
00617               msgv[i++] = KP;             //5
00618               msgv[i++] = ").";           //6
00619              }
00620    if (erp) erp->setErrInfo(rc, msgv, i);
00621       else {for (k = 0; k < i; k++) cerr <<msgv[k];
00622             cerr <<endl;
00623            }
00624 
00625    return -1;
00626 }
00627 
00628 /******************************************************************************/
00629 /*                          g e t _ k r b C r e d s                           */
00630 /******************************************************************************/
00631 
00632 // Warning! The krbClientContext lock must be held prior to calling this routine
00633 
00634 int XrdSecProtocolkrb5::get_krbCreds(char *KP, krb5_creds **krb_creds)
00635 {
00636     krb_rc rc;
00637     krb5_principal the_principal;
00638     krb5_creds mycreds;
00639 
00640 // Clear my credentials
00641 //
00642    memset((char *)&mycreds, 0, sizeof(mycreds));
00643 
00644 // Setup the "principal/instance@realm"
00645 //
00646    if ((rc = krb5_parse_name(krb_client_context,KP,&the_principal)))
00647       {CLDBG("get_krbCreds: Cannot parse service name;" <<krb_etxt(rc));
00648        return rc;
00649       }
00650 
00651 // Copy the current target principal into the credentials
00652 //
00653    if ((rc = krb5_copy_principal(krb_client_context, the_principal, &mycreds.server)))
00654       {CLDBG("get_krbCreds: err copying principal to creds; " <<krb_etxt(rc));
00655        return rc;
00656       }
00657 
00658 // Get our principal name
00659 //
00660    if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &mycreds.client)))
00661       {krb5_free_cred_contents(krb_client_context, &mycreds);
00662        CLDBG("get_krbCreds: err copying client name to creds; " <<krb_etxt(rc));
00663        return rc;
00664       }
00665 
00666 // Now get the credentials (free our local info)
00667 //
00668    rc = krb5_get_credentials(krb_client_context, 0, krb_client_ccache, &mycreds,  krb_creds);
00669    krb5_free_cred_contents(krb_client_context, &mycreds);
00670 
00671 // Check if all went well
00672 //
00673    if (rc) {CLDBG("get_krbCreds: unable to get creds; " <<krb_etxt(rc));}
00674    return rc;
00675 }
00676 
00677 /******************************************************************************/
00678 /*                        g e t _ k r b F w d C r e d s                       */
00679 /******************************************************************************/
00680 
00681 int XrdSecProtocolkrb5::get_krbFwdCreds(char *KP, krb5_data *outdata)
00682 {
00683     int rc;
00684     krb5_principal client, server;
00685 
00686 // Fill-in our principal
00687 //
00688    if ((rc = krb5_cc_get_principal(krb_client_context, krb_client_ccache, &client)))
00689       {CLDBG("get_krbFwdCreds: err filling client principal; " <<krb_etxt(rc));
00690        return rc;
00691       }
00692 
00693 // Fill-in target (service) principal
00694 //
00695    if ((rc = krb5_parse_name(krb_client_context, KP, &server)))
00696       {CLDBG("get_krbFwdCreds: Cannot parse service principal;" <<krb_etxt(rc));
00697        return rc;
00698       }
00699 
00700 // Set the timestamp in the authentication context
00701 //
00702    if ((rc = krb5_auth_con_setflags(krb_client_context, AuthClientContext,
00703                                    KRB5_AUTH_CONTEXT_RET_TIME)))
00704       {CLDBG("Unable to set KRB5_AUTH_CONTEXT_RET_TIME"
00705                            " in the authentication context" << krb_etxt(rc));
00706        return rc;
00707       }
00708 
00709 // Acquire a TGT for use at a remote host system
00710 //
00711    if ((rc = krb5_fwd_tgt_creds(krb_client_context, AuthClientContext, 0 /*host*/,
00712                                      client, server, krb_client_ccache, true,
00713                                      outdata)))
00714       {CLDBG("get_krbFwdCreds: err getting forwarded ticket;" <<krb_etxt(rc));
00715        return rc;
00716       }
00717 
00718 // Done
00719 //
00720    return rc;
00721 }
00722 
00723 /******************************************************************************/
00724 /*                          e x p _ k r b T k n                               */
00725 /******************************************************************************/
00726 
00727 int XrdSecProtocolkrb5::exp_krbTkn(XrdSecCredentials *cred, XrdOucErrInfo *erp)
00728 {
00729     int rc = 0;
00730 
00731 // Create the cache filename, expanding the keywords, if needed
00732 //
00733     char ccfile[XrdSecMAXPATHLEN];
00734     strcpy(ccfile, XrdSecProtocolkrb5::ExpFile);
00735     int nlen = strlen(ccfile);
00736     char *pusr = (char *) strstr(&ccfile[0], "<user>");
00737     if (pusr)
00738        {int ln = strlen(CName);
00739         if (ln != 6) {
00740            // Adjust the space
00741            int lm = strlen(ccfile) - (int)(pusr + 6 - &ccfile[0]); 
00742            memmove(pusr+ln, pusr+6, lm);
00743         }
00744         // Copy the name
00745         memcpy(pusr, CName, ln);
00746         // Adjust the length
00747         nlen += (ln - 6);
00748         }
00749     char *puid = (char *) strstr(&ccfile[0], "<uid>");
00750     struct passwd *pw = getpwnam(CName);
00751     if (puid)
00752        {char cuid[20] = {0};
00753         if (pw)
00754            sprintf(cuid, "%d", pw->pw_uid);
00755         int ln = strlen(cuid);
00756         if (ln != 5) {
00757            // Adjust the space
00758            int lm = strlen(ccfile) - (int)(puid + 5 - &ccfile[0]); 
00759            memmove(puid+ln, pusr+5, lm);
00760         }
00761         // Copy the name
00762         memcpy(puid, cuid, ln);
00763         // Adjust the length
00764         nlen += (ln - 5);
00765         }
00766 
00767 // Terminate to the new length
00768 //
00769     ccfile[nlen] = 0;
00770 
00771 // Point the received creds
00772 //
00773     krbContext.Lock();
00774     krb5_data forwardCreds;
00775     forwardCreds.data = &cred->buffer[XrdSecPROTOIDLEN];
00776     forwardCreds.length = cred->size -XrdSecPROTOIDLEN;
00777 
00778 // Get the replay cache
00779 //
00780     krb5_rcache rcache;
00781     if ((rc = krb5_get_server_rcache(krb_context,
00782                                      krb5_princ_component(krb_context, krb_principal, 0),
00783                                      &rcache)))
00784        return rc;
00785     if ((rc = krb5_auth_con_setrcache(krb_context, AuthContext, rcache)))
00786        return rc;
00787 
00788 // Fill-in remote address
00789 //
00790     if ((rc = krb5_auth_con_setaddrs(krb_context, AuthContext, 0, (krb5_address *)&hostaddr)))
00791        return rc;
00792 
00793 // Readout the credentials
00794 //
00795     krb5_creds **creds = 0;
00796     if ((rc = krb5_rd_cred(krb_context, AuthContext,
00797                            &forwardCreds, &creds, 0)))
00798        return rc;
00799 
00800 // Resolve cache name
00801     krb5_ccache cache = 0;
00802     if ((rc = krb5_cc_resolve(krb_context, ccfile, &cache)))
00803        return rc;
00804 
00805 // Need privileges from now on
00806 //
00807     XrdSysPrivGuard pGuard((uid_t)0, (gid_t)0);
00808     if (!pGuard.Valid())
00809        return Fatal(erp, EINVAL, "Unable to acquire privileges;", ccfile, 0);
00810 
00811 // Init cache
00812 //
00813     if ((rc = krb5_cc_initialize(krb_context, cache,
00814                                  Ticket->enc_part2->client)))
00815        return rc;
00816 
00817 // Store credentials in cache
00818 //
00819     if ((rc = krb5_cc_store_cred(krb_context, cache, *creds)))
00820        return rc;
00821 
00822 // Close cache
00823     if ((rc = krb5_cc_close(krb_context, cache)))
00824        return rc;
00825 
00826 // Change permission and ownership of the file
00827 //
00828     if (chown(ccfile, pw->pw_uid, pw->pw_gid) == -1)
00829        return Fatal(erp, errno, "Unable to change file ownership;", ccfile, 0);
00830     if (chmod(ccfile, 0600) == -1)
00831        return Fatal(erp, errno, "Unable to change file permissions;", ccfile, 0);
00832 
00833 // Done
00834 //
00835    return 0;
00836 }
00837  
00838 /******************************************************************************/
00839 /*                X r d S e c p r o t o c o l k r b 5 I n i t                 */
00840 /******************************************************************************/
00841   
00842 extern "C"
00843 {
00844 char  *XrdSecProtocolkrb5Init(const char     mode,
00845                               const char    *parms,
00846                               XrdOucErrInfo *erp)
00847 {
00848    char *op, *KPrincipal=0, *Keytab=0, *ExpFile=0;
00849    char parmbuff[1024];
00850    XrdOucTokenizer inParms(parmbuff);
00851    int options = XrdSecNOIPCHK;
00852    static bool serverinitialized = false;
00853 
00854 // For client-side one-time initialization, we only need to set debug flag and
00855 // initialize the kerberos context and cache location.
00856 //
00857    if ((mode == 'c') || (serverinitialized))
00858       {
00859        int opts = 0;
00860        if (getenv("XrdSecDEBUG")) opts |= XrdSecDEBUG;
00861        if (getenv("XrdSecKRB5INITTKN")) opts |= XrdSecINITTKN;
00862        XrdSecProtocolkrb5::setClientOpts(opts);
00863        return (XrdSecProtocolkrb5::Init(erp) ? (char *)0 : (char *)"");
00864       }
00865 
00866    if (!serverinitialized) {
00867      serverinitialized = true;
00868    }
00869 
00870 // Duplicate the parms
00871 //
00872    if (parms) strlcpy(parmbuff, parms, sizeof(parmbuff));
00873       else {char *msg = (char *)"Seckrb5: Kerberos parameters not specified.";
00874             if (erp) erp->setErrInfo(EINVAL, msg);
00875                else cerr <<msg <<endl;
00876             return (char *)0;
00877            }
00878 
00879 // Expected parameters: [<keytab>] [-ipchk] [-exptkn[:filetemplate]] <principal>
00880 //
00881    if (inParms.GetLine())
00882       {if ((op = inParms.GetToken()) && *op == '/')
00883           {Keytab = op; op = inParms.GetToken();}
00884            if (op && !strcmp(op, "-ipchk"))
00885               {options &= ~XrdSecNOIPCHK;
00886                op = inParms.GetToken();
00887               }
00888            if (op && !strncmp(op, "-exptkn", 7))
00889               {options |= XrdSecEXPTKN;
00890                if (op[7] == ':') ExpFile = op+8;
00891                op = inParms.GetToken();
00892               }
00893            KPrincipal = strdup(op);
00894       }
00895 
00896     if (ExpFile)
00897        fprintf(stderr,"Template for exports: %s\n", ExpFile);
00898     else
00899        fprintf(stderr,"Template for exports not set\n");
00900 
00901 // Now make sure that we have all the right info
00902 //
00903    if (!KPrincipal)
00904       {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
00905        if (erp) erp->setErrInfo(EINVAL, msg);
00906           else cerr <<msg <<endl;
00907        return (char *)0;
00908       }
00909 
00910 // Expand possible keywords in the principal
00911 //
00912     int plen = strlen(KPrincipal);
00913     int lkey = strlen("<host>");
00914     char *phost = (char *) strstr(&KPrincipal[0], "<host>");
00915     if (phost)
00916        {char *hn = XrdNetDNS::getHostName();
00917         if (hn)
00918            {int lhn = strlen(hn);
00919             if (lhn != lkey) {
00920               // Allocate, if needed
00921               int lnew = plen - lkey + lhn;
00922               if (lnew > plen) {
00923                  KPrincipal = (char *) realloc(KPrincipal, lnew+1);
00924                  KPrincipal[lnew] = 0;
00925                  phost = (char *) strstr(&KPrincipal[0], "<host>");
00926               }
00927               // Adjust the space
00928               int lm = plen - (int)(phost + lkey - &KPrincipal[0]); 
00929               memmove(phost + lhn, phost + lkey, lm);
00930             }
00931             // Copy the name
00932             memcpy(phost, hn, lhn);
00933             // Cleanup
00934             free(hn);
00935            }
00936        }
00937 
00938 // Now initialize the server
00939 //
00940    options |= XrdSecDEBUG;
00941    XrdSecProtocolkrb5::setExpFile(ExpFile);
00942    XrdSecProtocolkrb5::setOpts(options);
00943    if (!XrdSecProtocolkrb5::Init(erp, KPrincipal, Keytab))
00944       {free(KPrincipal);
00945        int lpars = strlen(XrdSecProtocolkrb5::getPrincipal());
00946        if (options & XrdSecEXPTKN)
00947           lpars += strlen(",fwd");
00948        char *params = (char *)malloc(lpars+1);
00949        if (params)
00950           {memset(params,0,lpars+1);
00951            strcpy(params,XrdSecProtocolkrb5::getPrincipal());
00952            if (options & XrdSecEXPTKN)
00953               strcat(params,",fwd");
00954            XrdSecProtocolkrb5::setParms(params);
00955            return params;
00956           }
00957       }
00958 
00959 // Failure
00960 //
00961    free(KPrincipal);
00962    return (char *)0;
00963 }
00964 }
00965 
00966 /******************************************************************************/
00967 /*              X r d S e c P r o t o c o l k r b 5 O b j e c t               */
00968 /******************************************************************************/
00969   
00970 extern "C"
00971 {
00972 XrdSecProtocol *XrdSecProtocolkrb5Object(const char              mode,
00973                                          const char             *hostname,
00974                                          const struct sockaddr  &netaddr,
00975                                          const char             *parms,
00976                                                XrdOucErrInfo    *erp)
00977 {
00978    XrdSecProtocolkrb5 *prot;
00979    char *KPrincipal=0;
00980 
00981 // If this is a client call, then we need to get the target principal from the
00982 // parms (which must be the first and only token). For servers, we use the
00983 // context we established at initialization time.
00984 //
00985    if (mode == 'c')
00986       {if ((KPrincipal = (char *)parms)) while(*KPrincipal == ' ') KPrincipal++;
00987        if (!KPrincipal || !*KPrincipal)
00988           {char *msg = (char *)"Seckrb5: Kerberos principal not specified.";
00989            if (erp) erp->setErrInfo(EINVAL, msg);
00990               else cerr <<msg <<endl;
00991            return (XrdSecProtocol *)0;
00992           }
00993       }
00994 
00995 // Get a new protocol object
00996 //
00997    if (!(prot = new XrdSecProtocolkrb5(KPrincipal, hostname, &netaddr)))
00998       {char *msg = (char *)"Seckrb5: Insufficient memory for protocol.";
00999        if (erp) erp->setErrInfo(ENOMEM, msg);
01000           else cerr <<msg <<endl;
01001        return (XrdSecProtocol *)0;
01002       }
01003 
01004 // All done
01005 //
01006    return prot;
01007 }
01008 
01009 void
01010       __eprintf (const char *string, const char *expression,
01011                  unsigned int line, const char *filename)
01012       {
01013         fprintf (stderr, string, expression, line, filename);
01014         fflush (stderr);
01015         abort ();
01016       }
01017 }

Generated on Tue Jul 5 14:47:00 2011 for ROOT_528-00b_version by  doxygen 1.5.1