XrdSecProtocolkrb4.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 4 . 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 /******************************************************************************/
00010 
00011 #include <unistd.h>
00012 #include <ctype.h>
00013 #include <errno.h>
00014 #include <stdlib.h>
00015 #include <strings.h>
00016 #include <stdio.h>
00017 #include <sys/param.h>
00018 
00019 #include "XrdOuc/XrdOucErrInfo.hh"
00020 #include "XrdSys/XrdSysHeaders.hh"
00021 #include "XrdSys/XrdSysPthread.hh"
00022 #include "XrdOuc/XrdOucTokenizer.hh"
00023 #include "XrdSec/XrdSecInterface.hh"
00024 
00025 #include "kerberosIV/krb.h"
00026 typedef  int krb_rc;
00027 
00028 /******************************************************************************/
00029 /*                               D e f i n e s                                */
00030 /******************************************************************************/
00031   
00032 #define XrdSecPROTOIDENT    "krb4"
00033 #define XrdSecPROTOIDLEN    sizeof(XrdSecPROTOIDENT)
00034 #define XrdSecNOIPCHK       0x0001
00035 #define XrdSecDEBUG         0x1000
00036 
00037 #define CLDBG(x) if (options & XrdSecDEBUG) cerr <<"sec_krb4: " <<x <<endl;
00038 
00039 /******************************************************************************/
00040 /*              X r d S e c P r o t o c o l k r b 4   C l a s s               */
00041 /******************************************************************************/
00042 
00043 class XrdSecProtocolkrb4 : public XrdSecProtocol
00044 {
00045 public:
00046 friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor
00047 
00048 
00049         int                Authenticate  (XrdSecCredentials *cred,
00050                                           XrdSecParameters **parms,
00051                                           XrdOucErrInfo     *einfo=0);
00052 
00053         XrdSecCredentials *getCredentials(XrdSecParameters  *parm=0,
00054                                           XrdOucErrInfo     *einfo=0);
00055 
00056 static  char              *getPrincipal() {return Principal;}
00057 
00058 static  int                Init_Server(XrdOucErrInfo *einfo, 
00059                                        char *KP=0, char *kfn=0);
00060 
00061 static  void               setOpts(int opts) {options = opts;}
00062 
00063         XrdSecProtocolkrb4(const char                *KP,
00064                            const char                *hname,
00065                            const struct sockaddr     *ipadd)
00066                           : XrdSecProtocol(XrdSecPROTOIDENT)
00067                           {Service = (KP ? strdup(KP) : 0);
00068                            Entity.host = strdup(hname);
00069                            memcpy(&hostaddr, ipadd, sizeof(hostaddr));
00070                            CName[0] = '?'; CName[1] = '\0';
00071                            Entity.name = CName;
00072                           }
00073 
00074         void              Delete();
00075 
00076 private:
00077 
00078        ~XrdSecProtocolkrb4() {} // Delete() does it all
00079 
00080 
00081 static char *Append(char *dst, const char *src);
00082 static int Fatal(XrdOucErrInfo *erp,int rc,const char *msg1,char *KP=0,int krc=0);
00083 static int   get_SIR(XrdOucErrInfo *erp, const char *sh, char *sbuff, char *ibuff,
00084               char *rbuff);
00085 
00086 static XrdSysMutex        krbContext;           // Client or server
00087 static int                options;              // Client or server
00088 static char               mySname[SNAME_SZ+1];  // Server
00089 static char               myIname[INST_SZ+1];   // Server
00090 static char               myRname[REALM_SZ+1];  // Server
00091 
00092 static char              *keyfile;       // Server-side only
00093 static char              *Principal;     // Server's principal name
00094 
00095 struct sockaddr           hostaddr;      // Client-side only
00096 char                      CName[256];    // Kerberos limit
00097 char                     *Service;       // Target principal for client
00098 };
00099 
00100 /******************************************************************************/
00101 /*                           S t a t i c   D a t a                            */
00102 /******************************************************************************/
00103   
00104 XrdSysMutex         XrdSecProtocolkrb4::krbContext;          // Client or server
00105 int                 XrdSecProtocolkrb4::options = 0;         // Client or Server
00106 
00107 char                XrdSecProtocolkrb4::mySname[SNAME_SZ+1]; // Server
00108 char                XrdSecProtocolkrb4::myIname[INST_SZ+1];  // Server
00109 char                XrdSecProtocolkrb4::myRname[REALM_SZ+1]; // Server
00110 char               *XrdSecProtocolkrb4::keyfile   = 0;       // Server
00111 char               *XrdSecProtocolkrb4::Principal = 0;       // Server
00112 
00113 /******************************************************************************/
00114 /*                                D e l e t e                                 */
00115 /******************************************************************************/
00116   
00117 void XrdSecProtocolkrb4::Delete()
00118 {
00119      if (Entity.host) free(Entity.host);
00120      if (Service)     free(Service);
00121      delete this;
00122 }
00123 
00124 /******************************************************************************/
00125 /*             C l i e n t   O r i e n t e d   F u n c t i o n s              */
00126 /******************************************************************************/
00127 /******************************************************************************/
00128 /*                        g e t C r e d e n t i a l s                         */
00129 /******************************************************************************/
00130 
00131 
00132 XrdSecCredentials *XrdSecProtocolkrb4::getCredentials(XrdSecParameters *noparm,
00133                                                       XrdOucErrInfo    *error)
00134 {
00135    const long cksum = 0L;
00136    struct ktext katix;       /* Kerberos data */
00137    krb_rc rc;
00138    char *buff;
00139    char  sname[SNAME_SZ+1];
00140    char  iname[INST_SZ+1];
00141    char  rname[REALM_SZ+1];
00142 
00143 // Extract the "Principal.instance@realm" from the Service name
00144 //
00145    if (!Service)
00146       {Fatal(error, EINVAL, "krb4 service Principal name not specified.");
00147        return (XrdSecCredentials *)0;
00148       }
00149    if (get_SIR(error, Service, sname, iname, rname) < 0) 
00150       return (XrdSecCredentials *)0;
00151    CLDBG("sname='" <<sname <<"' iname='" <<iname <<"' rname='" <<rname <<"'");
00152 
00153 // Supply null credentials if so needed for this protocol
00154 //
00155    if (!sname[0])
00156       {CLDBG("Null credentials supplied.");
00157        return new XrdSecCredentials(0,0);
00158       }
00159 
00160 // Supply kerberos-style credentials
00161 //
00162    krbContext.Lock();
00163    rc = krb_mk_req(&katix, sname, iname, rname, cksum);
00164    krbContext.UnLock();
00165 
00166 // Check if all succeeded. If so, copy the ticket into the buffer. We wish
00167 // we could place the ticket directly into the buffer but architectural
00168 // differences won't allow us that optimization.
00169 // Because of some typedef stupidity, we are now reserving the 1st 8 bytes
00170 // of the credentials buffer for identifying information.
00171 //
00172    if (rc == KSUCCESS)
00173       {int bsz = XrdSecPROTOIDLEN+katix.length;
00174        if (!(buff = (char *)malloc(bsz)))
00175           {Fatal(error,ENOMEM,"Insufficient memory for credentials.",Service);
00176            return (XrdSecCredentials *)0;
00177           }
00178        strcpy(buff, XrdSecPROTOIDENT);
00179        memcpy((void *)(buff+XrdSecPROTOIDLEN),
00180               (const void *)katix.dat, (size_t)katix.length);
00181        CLDBG("Returned " <<bsz <<" bytes of credentials; p=" <<sname);
00182        return new XrdSecCredentials(buff, bsz);
00183       }
00184 
00185 // Diagnose the failure
00186 //
00187    {char ebuff[1024];
00188     snprintf(ebuff, sizeof(ebuff)-1, "Unable to get credentials from %s;",
00189              Service);
00190     ebuff[sizeof(ebuff)-1] = '\0';
00191     Fatal(error, EACCES, ebuff, Service, rc);
00192     return (XrdSecCredentials *)0;
00193    }
00194 }
00195 
00196 /******************************************************************************/
00197 /*               S e r v e r   O r i e n t e d   M e t h o d s                */
00198 /******************************************************************************/
00199 /******************************************************************************/
00200 /*                          A u t h e n t i c a t e                           */
00201 /******************************************************************************/
00202 
00203 int XrdSecProtocolkrb4::Authenticate(XrdSecCredentials *cred,
00204                                      XrdSecParameters **parms,
00205                                      XrdOucErrInfo     *error)
00206 {
00207    struct ktext katix;       /* Kerberos data */
00208    struct auth_dat pid;
00209    krb_rc rc;
00210    char *idp;
00211    unsigned int ipaddr;  // Should be 32 bits in all supported data models
00212 
00213 // Check if we have any credentials or if no credentials really needed.
00214 // In either case, use host name as client name
00215 //
00216    if (cred->size <= (int)XrdSecPROTOIDLEN || !cred->buffer)
00217       {strncpy(Entity.prot, "host", sizeof(Entity.prot));
00218        Entity.name[0] = '?'; Entity.name[1] = '\0';
00219        return 0;
00220       }
00221 
00222 // Check if this is a recognized protocol
00223 //
00224    if (strcmp(cred->buffer, XrdSecPROTOIDENT))
00225       {char emsg[256];
00226        snprintf(emsg, sizeof(emsg),
00227                 "Authentication protocol id mismatch (%.4s != %.4s).",
00228                 XrdSecPROTOIDENT,  cred->buffer);
00229        Fatal(error, EINVAL, emsg);
00230        return -1;
00231       }
00232 
00233 // Indicate who we are
00234 //
00235    strncpy(Entity.prot, XrdSecPROTOIDENT, sizeof(Entity.prot));
00236 
00237 // Create a kerberos style ticket (need to do that, unfortunately)
00238 //
00239    katix.length = cred->size-XrdSecPROTOIDLEN;
00240    memcpy((void *)katix.dat, (const void *)&cred->buffer[XrdSecPROTOIDLEN],
00241                                    (size_t) katix.length);
00242 
00243 // Prepare to check the ip address. This is rather poor since K4 "knows"
00244 // that IP addresses are 4 bytes. Well, by the time IPV6 comes along, K4
00245 // will be history (it's almost there now :-).
00246 //
00247    if (options & XrdSecNOIPCHK) ipaddr = 0;
00248       else {sockaddr_in *ip = (sockaddr_in *)&hostaddr;
00249             memcpy((void *)&ipaddr,(void *)&ip->sin_addr.s_addr,sizeof(ipaddr));
00250            }
00251 
00252 // Decode the credentials
00253 //
00254    krbContext.Lock();
00255    rc = krb_rd_req(&katix, mySname, myIname, ipaddr, &pid, keyfile);
00256    krbContext.UnLock();
00257 
00258 // Diagnose any errors
00259 //
00260    if (rc != KSUCCESS)
00261       {Fatal(error,rc,"Unable to authenticate credentials;",Principal,rc);
00262        return -1;
00263       }
00264 
00265 // Construct the user's name (use of the fact that names are < 256 chars)
00266 //
00267    idp = Append(CName, pid.pname);
00268    if (pid.pinst[0])
00269       {*idp = '.'; idp++; idp = Append(idp, pid.pinst);}
00270    if (pid.prealm[0] && strcasecmp(pid.prealm, myRname))
00271       {*idp = '@'; idp++; idp = Append(idp, pid.prealm);}
00272 
00273 // All done
00274 //
00275    return 0;
00276 }
00277 
00278 /******************************************************************************/
00279 /*       P r o t o c o l   I n i t i a l i z a t i o n   M e t h o d s        */
00280 /******************************************************************************/
00281 /******************************************************************************/
00282 /*                           I n i t _ S e r v e r                            */
00283 /******************************************************************************/
00284   
00285 int XrdSecProtocolkrb4::Init_Server(XrdOucErrInfo *erp, char *KP, char *kfn)
00286 {
00287    int plen;
00288 
00289 // Now, extract the "Principal.instance@realm" from the stream
00290 //
00291    if (!KP)
00292       return Fatal(erp, EINVAL, "krb4 service Principal name not specified.");
00293    if (get_SIR(erp, KP, mySname, myIname, myRname) < 0) return -1;
00294    CLDBG("sname='" <<mySname <<"' iname='" <<myIname <<"' rname='" <<myRname <<"'");
00295 
00296 // Construct appropriate Principal name
00297 //
00298    plen = strlen(mySname) + strlen(myIname) + strlen(myRname) + 3;
00299    if (!(Principal = (char *)malloc(plen)))
00300       {Principal = (char *)"?";
00301        return Fatal(erp, ENOMEM, "insufficient storage", KP);
00302       }
00303    if (*myIname) sprintf((char *)Principal, "%s.%s@%s",mySname,myIname,myRname);
00304       else       sprintf((char *)Principal, "%s@%s",   mySname,myRname);
00305 
00306 // If we have been passed a keyfile name, use it.
00307 //
00308    if (kfn && *kfn) keyfile = strdup(kfn);
00309       else keyfile = (char *)"";
00310 
00311 // All done
00312 //
00313    return 0;
00314 }
00315   
00316 /******************************************************************************/
00317 /*                       P r i v a t e   M e t h o d s                        */
00318 /******************************************************************************/
00319 /******************************************************************************/
00320 /*                                A p p e n d                                 */
00321 /******************************************************************************/
00322   
00323  char *XrdSecProtocolkrb4::Append(char *dst, const char *src)
00324 {
00325       while(*src) {*dst = *src; dst++; src++;}
00326       *dst = '\0';
00327       return dst;
00328 }
00329 
00330 /******************************************************************************/
00331 /*                                 F a t a l                                  */
00332 /******************************************************************************/
00333 
00334 int XrdSecProtocolkrb4::Fatal(XrdOucErrInfo *erp, int rc, const char *msg,
00335                              char *KP, int krc)
00336 {
00337    const char *msgv[8];
00338    int k, i = 0;
00339 
00340               msgv[i++] = "Seckrb4: ";     //0
00341               msgv[i++] = msg;             //1
00342    if (krc)  {msgv[i++] = "; ";            //2
00343               msgv[i++] = krb_err_txt[rc]; //3
00344              }
00345    if (KP)   {msgv[i++] = " (p=";          //4
00346               msgv[i++] = KP;              //5
00347               msgv[i++] = ").";            //6
00348              }
00349    if (erp) erp->setErrInfo(rc, msgv, i);
00350       else {for (k = 0; k < i; k++) cerr <<msgv[k];
00351             cerr <<endl;
00352            }
00353 
00354    return -1;
00355 }
00356 
00357 /******************************************************************************/
00358 /*                               g e t _ S I R                                */
00359 /******************************************************************************/
00360   
00361 int XrdSecProtocolkrb4::get_SIR(XrdOucErrInfo *erp, const char *sh,
00362                                        char *sbuff, char *ibuff, char *rbuff)
00363 {
00364     int h, i, j, k;
00365 
00366     k = strlen(sh);
00367     if (k > MAX_K_NAME_SZ) 
00368        return Fatal(erp, EINVAL, "service name is to long", (char *)sh);
00369 
00370     for (j = 0; j < k && sh[j] != '@'; j++) {};
00371     if (j > k) j = k;
00372        else {if (j == k-1) 
00373                 return Fatal(erp,EINVAL,"realm name missing after '@'",(char *)sh);
00374              if (k-j > REALM_SZ) 
00375                 return Fatal(erp, EINVAL, "realm name is to long",(char *)sh);
00376             }
00377 
00378     for (i = 0; i < j && sh[i] != '.'; i++) {};
00379     if (i < j) {if (j-i >= INST_SZ) 
00380                    return Fatal(erp, EINVAL, "instance is too long",(char *)sh);
00381                 if (i+1 == j) 
00382                    return Fatal(erp,EINVAL,"instance name missing after '.'",(char *)sh);
00383                }
00384 
00385     if (i == SNAME_SZ) 
00386        return Fatal(erp, EINVAL, "service name is too long", (char *)sh);
00387     if (!i) return Fatal(erp, EINVAL, "service name not specified.");
00388 
00389     strncpy(sbuff, sh, i); sbuff[i] = '\0';
00390     if ( (h = j - i - 1) <= 0) ibuff[0] = '\0';
00391        else {strncpy(ibuff, &sh[i+1], h); ibuff[h] = '\0';}
00392     if ( (h = k - j - 1) <= 0) krb_get_lrealm(rbuff, 1);
00393        else {strncpy(rbuff, &sh[j+1], h); rbuff[h] = '\0';}
00394 
00395     return 1;
00396 }
00397  
00398 /******************************************************************************/
00399 /*                X r d S e c p r o t o c o l k r b 4 I n i t                 */
00400 /******************************************************************************/
00401   
00402 extern "C"
00403 {
00404 char  *XrdSecProtocolkrb4Init(const char     mode,
00405                               const char    *parms,
00406                               XrdOucErrInfo *erp)
00407 {
00408    char *op, *KPrincipal=0, *Keytab=0;
00409    char parmbuff[1024];
00410    XrdOucTokenizer inParms(parmbuff);
00411    int options = XrdSecNOIPCHK;
00412 
00413 // For client-side one-time initialization, we only need to set debug flag and
00414 // initialize the kerberos context and cache location.
00415 //
00416    if (mode == 'c')
00417       {if (getenv("XrdSecDEBUG")) XrdSecProtocolkrb4::setOpts(XrdSecDEBUG);
00418        return (char *)"";
00419       }
00420 
00421 // Duplicate the parms
00422 //
00423    if (parms) strlcpy(parmbuff, parms, sizeof(parmbuff));
00424       else {char *msg = (char *)"Seckrb4: Kerberos parameters not specified.";
00425             if (erp) erp->setErrInfo(EINVAL, msg);
00426                else cerr <<msg <<endl;
00427             return (char *)0;
00428            }
00429 
00430 // Expected parameters: [<keytab>] [-ipchk] <principal>
00431 //
00432    if (inParms.GetLine())
00433       {if ((op = inParms.GetToken()) && *op == '/')
00434           {Keytab = op; op = inParms.GetToken();}
00435            if (op && !strcmp(op, "-ipchk"))
00436               {options &= ~XrdSecNOIPCHK;
00437                op = inParms.GetToken();
00438               }
00439            KPrincipal = op;
00440       }
00441 
00442 // Now make sure that we have all the right info
00443 //
00444    if (!KPrincipal)
00445       {char *msg = (char *)"Seckrb4: Kerberos principal not specified.";
00446        if (erp) erp->setErrInfo(EINVAL, msg);
00447           else cerr <<msg <<endl;
00448        return (char *)0;
00449       }
00450 
00451 // Now initialize the server
00452 //
00453    XrdSecProtocolkrb4::setOpts(options);
00454    return (XrdSecProtocolkrb4::Init_Server(erp, KPrincipal, Keytab)
00455            ? (char *)0 : XrdSecProtocolkrb4::getPrincipal());
00456 }
00457 }
00458 
00459 /******************************************************************************/
00460 /*              X r d S e c P r o t o c o l k r b 4 O b j e c t               */
00461 /******************************************************************************/
00462   
00463 extern "C"
00464 {
00465 XrdSecProtocol *XrdSecProtocolkrb4Object(const char              mode,
00466                                          const char             *hostname,
00467                                          const struct sockaddr  &netaddr,
00468                                          const char             *parms,
00469                                                XrdOucErrInfo    *erp)
00470 {
00471    XrdSecProtocolkrb4 *prot;
00472    char *KPrincipal=0;
00473 
00474 // If this is a client call, then we need to get the target principal from the
00475 // parms (which must be the first and only token). For servers, we use the
00476 // context we established at initialization time.
00477 //
00478    if (mode == 'c')
00479       {if ((KPrincipal = (char *)parms)) while(*KPrincipal == ' ') KPrincipal++;
00480        if (!KPrincipal || !*KPrincipal)
00481           {char *msg = (char *)"Seckrb4: Kerberos principal not specified.";
00482            if (erp) erp->setErrInfo(EINVAL, msg);
00483               else cerr <<msg <<endl;
00484            return (XrdSecProtocol *)0;
00485           }
00486       }
00487 
00488 // Get a new protocol object
00489 //
00490    if (!(prot = new XrdSecProtocolkrb4(KPrincipal, hostname, &netaddr)))
00491       {char *msg = (char *)"Seckrb4: Insufficient memory for protocol.";
00492        if (erp) erp->setErrInfo(ENOMEM, msg);
00493           else cerr <<msg <<endl;
00494        return (XrdSecProtocol *)0;
00495       }
00496 
00497 // All done
00498 //
00499    return prot;
00500 }
00501 }

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