/******************************************************************************/ /* */ /* X r d S e c P r o t o c o l g s i . c c */ /* */ /* (c) 2005 G. Ganis / CERN */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /* */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "XrdVersion.hh" #include "XrdNet/XrdNetAddr.hh" #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysLogger.hh" #include "XrdSys/XrdSysError.hh" #include "XrdOuc/XrdOucPinLoader.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdSut/XrdSutAux.hh" #include "XrdCrypto/XrdCryptoMsgDigest.hh" #include "XrdCrypto/XrdCryptoX509Chain.hh" #include "XrdCrypto/XrdCryptoX509Req.hh" #include "XrdSecgsi/XrdSecProtocolgsi.hh" /******************************************************************************/ /* T r a c i n g I n i t O p t i o n s */ /******************************************************************************/ #ifndef NODEBUG #define POPTS(t,y) {if (t) {t->Beg(epname); cerr <End();}} #else #define POPTS(t,y) #endif /******************************************************************************/ /* S t a t i c D a t a */ /******************************************************************************/ static String Prefix = "xrd"; static String ProtoID = XrdSecPROTOIDENT; static const kXR_int32 Version = XrdSecgsiVERSION; static const char *gsiClientSteps[] = { "kXGC_none", "kXGC_certreq", "kXGC_cert", "kXGC_sigpxy", "kXGC_reserved" }; static const char *gsiServerSteps[] = { "kXGS_none", "kXGS_init", "kXGS_cert", "kXGS_pxyreq", "kXGS_reserved" }; static const char *gGSErrStr[] = { "ErrParseBuffer", // 10000 "ErrDecodeBuffer", // 10001 "ErrLoadCrypto", // 10002 "ErrBadProtocol", // 10003 "ErrCreateBucket", // 10004 "ErrDuplicateBucket", // 10005 "ErrCreateBuffer", // 10006 "ErrSerialBuffer", // 10007 "ErrGenCipher", // 10008 "ErrExportPuK", // 10009 "ErrEncRndmTag", // 10010 "ErrBadRndmTag", // 10011 "ErrNoRndmTag", // 10012 "ErrNoCipher", // 10013 "ErrNoCreds", // 10014 "ErrBadOpt", // 10015 "ErrMarshal", // 10016 "ErrUnmarshal", // 10017 "ErrSaveCreds", // 10018 "ErrNoBuffer", // 10019 "ErrRefCipher", // 10020 "ErrNoPublic", // 10021 "ErrAddBucket", // 10022 "ErrFinCipher", // 10023 "ErrInit", // 10024 "ErrBadCreds", // 10025 "ErrError" // 10026 }; // One day in secs static const int kOneDay = 86400; // Default proxy location template static const char *gUsrPxyDef = "/tmp/x509up_u"; // Tag for pad support static const char *gNoPadTag = "nopad"; // static const char *gPadTag = "&pad"; /******************************************************************************/ /* S t a t i c C l a s s D a t a */ /******************************************************************************/ XrdSysMutex XrdSecProtocolgsi::gsiContext; String XrdSecProtocolgsi::CAdir = "/etc/grid-security/certificates/"; String XrdSecProtocolgsi::CRLdir = "/etc/grid-security/certificates/"; String XrdSecProtocolgsi::DefCRLext= ".r0"; String XrdSecProtocolgsi::GMAPFile = "/etc/grid-security/grid-mapfile"; String XrdSecProtocolgsi::SrvCert = "/etc/grid-security/xrd/xrdcert.pem"; String XrdSecProtocolgsi::SrvKey = "/etc/grid-security/xrd/xrdkey.pem"; String XrdSecProtocolgsi::UsrProxy; String XrdSecProtocolgsi::UsrCert = "/.globus/usercert.pem"; String XrdSecProtocolgsi::UsrKey = "/.globus/userkey.pem"; String XrdSecProtocolgsi::PxyValid = "12:00"; int XrdSecProtocolgsi::DepLength= 0; int XrdSecProtocolgsi::DefBits = 512; int XrdSecProtocolgsi::CACheck = 1; int XrdSecProtocolgsi::CRLCheck = 1; int XrdSecProtocolgsi::CRLDownload = 0; int XrdSecProtocolgsi::CRLRefresh = 86400; int XrdSecProtocolgsi::GMAPOpt = 1; bool XrdSecProtocolgsi::GMAPuseDNname = 0; String XrdSecProtocolgsi::DefCrypto= "ssl"; String XrdSecProtocolgsi::DefCipher= "aes-128-cbc:bf-cbc:des-ede3-cbc"; String XrdSecProtocolgsi::DefMD = "sha1:md5"; String XrdSecProtocolgsi::DefError = "invalid credentials "; int XrdSecProtocolgsi::PxyReqOpts = 0; int XrdSecProtocolgsi::AuthzPxyWhat = -1; int XrdSecProtocolgsi::AuthzPxyWhere = -1; XrdSecgsiGMAP_t XrdSecProtocolgsi::GMAPFun = 0; XrdSecgsiAuthz_t XrdSecProtocolgsi::AuthzFun = 0; XrdSecgsiAuthzKey_t XrdSecProtocolgsi::AuthzKey = 0; int XrdSecProtocolgsi::AuthzCertFmt = -1; int XrdSecProtocolgsi::GMAPCacheTimeOut = -1; int XrdSecProtocolgsi::AuthzCacheTimeOut = 43200; // 12h, default String XrdSecProtocolgsi::SrvAllowedNames; int XrdSecProtocolgsi::VOMSAttrOpt = 1; XrdSecgsiAuthz_t XrdSecProtocolgsi::VOMSFun = 0; int XrdSecProtocolgsi::VOMSCertFmt = -1; int XrdSecProtocolgsi::MonInfoOpt = 0; bool XrdSecProtocolgsi::HashCompatibility = 1; bool XrdSecProtocolgsi::TrustDNS = true; // // Crypto related info int XrdSecProtocolgsi::ncrypt = 0; // Number of factories XrdCryptoFactory *XrdSecProtocolgsi::cryptF[XrdCryptoMax] = {0}; // their hooks int XrdSecProtocolgsi::cryptID[XrdCryptoMax] = {0}; // their IDs String XrdSecProtocolgsi::cryptName[XrdCryptoMax] = {0}; // their names XrdCryptoCipher *XrdSecProtocolgsi::refcip[XrdCryptoMax] = {0}; // ref for session ciphers // // Caches XrdSutCache XrdSecProtocolgsi::cacheCA; // Server certificates info cache (default size 144) XrdSutCache XrdSecProtocolgsi::cacheCert(8,13); // Server certificates info cache (Fibonacci-based sizes) XrdSutCache XrdSecProtocolgsi::cachePxy(8,13); // Client proxies cache (Fibonacci-based sizes) XrdSutCache XrdSecProtocolgsi::cacheGMAPFun; // Entries mapped by GMAPFun (default size 144) XrdSutCache XrdSecProtocolgsi::cacheAuthzFun; // Entities filled by AuthzFun (default size 144) // // Services XrdOucGMap *XrdSecProtocolgsi::servGMap = 0; // Grid map service // // CA and CRL stacks GSIStack XrdSecProtocolgsi::stackCA; // Stack of CA in use GSIStack XrdSecProtocolgsi::stackCRL; // Stack of CRL in use // // GMAP control vars time_t XrdSecProtocolgsi::lastGMAPCheck = -1; // Time of last check XrdSysMutex XrdSecProtocolgsi::mutexGMAP; // Mutex to control GMAP reloads // // Running options / settings int XrdSecProtocolgsi::Debug = 0; // [CS] Debug level bool XrdSecProtocolgsi::Server = 1; // [CS] If server mode int XrdSecProtocolgsi::TimeSkew = 300; // [CS] Allowed skew in secs for time stamps // // Debug an tracing XrdSysError XrdSecProtocolgsi::eDest(0, "secgsi_"); XrdSysLogger XrdSecProtocolgsi::Logger; XrdOucTrace *XrdSecProtocolgsi::GSITrace = 0; XrdOucTrace *gsiTrace = 0; /******************************************************************************/ /* S t a t i c F u n c t i o n s */ /******************************************************************************/ //_____________________________________________________________________________ static const char *ClientStepStr(int kclt) { // Return string with client step static const char *ukn = "Unknown"; kclt = (kclt < 0) ? 0 : kclt; kclt = (kclt > kXGC_reserved) ? 0 : kclt; kclt = (kclt >= kXGC_certreq) ? (kclt - kXGC_certreq + 1) : kclt; if (kclt < 0 || kclt > (kXGC_reserved - kXGC_certreq + 1)) return ukn; else return gsiClientSteps[kclt]; } //_____________________________________________________________________________ static const char *ServerStepStr(int ksrv) { // Return string with server step static const char *ukn = "Unknown"; ksrv = (ksrv < 0) ? 0 : ksrv; ksrv = (ksrv > kXGS_reserved) ? 0 : ksrv; ksrv = (ksrv >= kXGS_init) ? (ksrv - kXGS_init + 1) : ksrv; if (ksrv < 0 || ksrv > (kXGS_reserved - kXGS_init + 1)) return ukn; else return gsiServerSteps[ksrv]; } /******************************************************************************/ /* D u m p o f H a n d s h a k e v a r i a b l e s */ /******************************************************************************/ //_____________________________________________________________________________ void gsiHSVars::Dump(XrdSecProtocolgsi *p) { // Dump content EPNAME("HSVars::Dump"); PRINT("----------------------------------------------------------------"); PRINT("protocol instance: "<TimeStamp = time(0); // Local handshake variables hs->Tty = (isatty(0) == 0 || isatty(1) == 0) ? 0 : 1; } else { PRINT("could not create handshake vars object"); } // Set host name and address // The hostname is critical for the GSI protocol; it must match the potential // names on the remote EEC. We default to the hostname requested by the user to // the client (or proxy). However, as we may have been redirected to an IP // address instead of an actual hostname, we must fallback to a reverse DNS lookup. // As of time of testing (June 2018), EOS will redirect to an IP address to handle // metadata commands and rely on the reverse DNS lookup for GSI security to function. // Hence, this fallback likely needs to be kept for some time. // // We provide servers a switch and clients an environment variable to override all // usage of DNS (processed on XrdSecProtocolgsiInit). // Default is to fallback to DNS lookups in limited // cases for backward compatibility. expectedHost = NULL; if (TrustDNS) { if (!hname || !XrdNetAddrInfo::isHostName(hname)) { Entity.host = strdup(endPoint.Name("")); } else { // At this point, hname still may possibly be a non-qualified domain name. // If there is a '.' character, then we assume it is a qualified domain name -- // otherwise, we use DNS. // // NOTE: We can definitively test whether this is a qualified domain name by // simply appending a '.' to `hname` and performing a lookup. However, this // causes DNS to be used by every lookup - meaning we rely on the security // of DNS for all cases; we want to avoid this. if (strchr(hname, '.')) { // We have a valid hostname; proceed. Entity.host = strdup(hname); } else { XrdNetAddr xrd_addr; char canonname[256]; if (!xrd_addr.Set(hname) || (xrd_addr.Format(canonname, 256, XrdNetAddrInfo::fmtName, XrdNetAddrInfo::noPort) <= 0)) { Entity.host = strdup(hname); } else { Entity.host = strdup(canonname); } } } } else { // We have been told via environment variable to not trust DNS; use the exact // hostname provided by the user. // char dnBuff[256]; // getdomainname(dnBuff, sizeof(dnBuff)); Entity.host = strdup(hname); expectedHost = strdup(hname); } epAddr = endPoint; Entity.addrInfo = &epAddr; // Init session variables sessionCF = 0; sessionKey = 0; bucketKey = 0; sessionMD = 0; sessionKsig = 0; sessionKver = 0; sessionKver = 0; proxyChain = 0; useIV = false; // // Notify, if required DEBUG("constructing: host: "<< Entity.host); DEBUG("p: "<Parms = new XrdSutBuffer(p.c_str(), p.length()); } } // We are done String vers = Version; vers.insert('.',vers.length()-2); vers.insert('.',vers.length()-5); DEBUG("object created: v"< -1) ? opt.debug : Debug; // We must have the tracing object at this point // (initialized in XrdSecProtocolgsiInit) if (!gsiTrace) { ErrF(erp,kGSErrInit,"tracing object (gsiTrace) not initialized! cannot continue"); return Parms; } // Set debug mask ... also for auxilliary libs int trace = 0, traceSut = 0, traceCrypto = 0; if (Debug >= 3) { trace = cryptoTRACE_Dump; traceSut = sutTRACE_Dump; traceCrypto = cryptoTRACE_Dump; GSITrace->What = TRACE_ALL; } else if (Debug >= 2) { trace = cryptoTRACE_Debug; traceSut = sutTRACE_Debug; traceCrypto = cryptoTRACE_Debug; GSITrace->What = TRACE_Debug; GSITrace->What |= TRACE_Authen; } else if (Debug >= 1) { trace = cryptoTRACE_Debug; traceSut = sutTRACE_Notify; traceCrypto = cryptoTRACE_Notify; GSITrace->What = TRACE_Debug; } // ... also for auxilliary libs XrdSutSetTrace(traceSut); XrdCryptoSetTrace(traceCrypto); // Name hashing algorithm compatibility if (opt.hashcomp == 0) HashCompatibility = 0; // // Operation mode Server = (opt.mode == 's'); // // CA verification level // // 0 do not verify // 1 verify if self-signed; warn if not // 2 verify in all cases; fail if not possible // if (opt.ca >= 0 && opt.ca <= 2) CACheck = opt.ca; DEBUG("option CACheck: "< 0) { if (XrdSutExpand(dp) == 0) { if (stat(dp.c_str(),&st) == -1) { if (errno == ENOENT) { ErrF(erp,kGSErrError,"CA directory non existing:",dp.c_str()); PRINT(erp->getErrText()); } else { ErrF(erp,kGSErrError,"cannot stat CA directory:",dp.c_str()); PRINT(erp->getErrText()); } } else { if (!(dp.endswith('/'))) dp += '/'; if (!(CAtmp.endswith(','))) CAtmp += ','; CAtmp += dp; } } else { PRINT("Warning: could not expand: "< 0) CAdir = CAtmp; } DEBUG("using CA dir(s): "<= 10) { CRLDownload = 1; opt.crl %= 10; } if (opt.crl >= 0 && opt.crl <= 3) CRLCheck = opt.crl; DEBUG("option CRLCheck: "< 0) { if (XrdSutExpand(dp) == 0) { if (stat(dp.c_str(),&st) == -1) { if (errno == ENOENT) { ErrF(erp,kGSErrError,"CRL directory non existing:",dp.c_str()); PRINT(erp->getErrText()); } else { ErrF(erp,kGSErrError,"cannot stat CRL directory:",dp.c_str()); PRINT(erp->getErrText()); } } else { if (!(dp.endswith('/'))) dp += '/'; if (!(CRLtmp.endswith(','))) CRLtmp += ','; CRLtmp += dp; } } else { PRINT("Warning: could not expand: "< 0) CRLdir = CRLtmp; } else { // Use CAdir CRLdir = CAdir; } if (CRLCheck > 0) DEBUG("using CRL dir(s): "< 0 && ncpt[0] != '-') { // Try loading if ((cf = XrdCryptoFactory::GetCryptoFactory(ncpt.c_str()))) { // Add it to the list cryptF[ncrypt] = cf; cryptID[ncrypt] = cf->ID(); cryptName[ncrypt].insert(cf->Name(),0,strlen(cf->Name())+1); cf->SetTrace(trace); cf->Notify(); // Ref cipher if (!(refcip[ncrypt] = cf->Cipher(0,0,0))) { PRINT("ref cipher for module "<= XrdCryptoMax) { PRINT("max number of crypto modules (" << XrdCryptoMax <<") reached "); break; } if (cryptlist.length()) cryptlist += ":"; cryptlist += ncpt; if (!cf->HasPaddingSupport()) cryptlist += gNoPadTag; } } else { PRINT("cannot instantiate crypto factory "<getErrText()); return Parms; } // // List of supported / wanted ciphers if (opt.cipher) DefCipher = opt.cipher; // make sure we support all of them String cip = ""; int from = 0; while ((from = DefCipher.tokenize(cip, from, ':')) != -1) { if (cip.length() > 0) { int i = 0; for (; i < ncrypt; i++) { if (!(cryptF[i]->SupportedCipher(cip.c_str()))) { // Not supported: drop from the list DEBUG("cipher type not supported ("< 0) { int i = 0; for (; i < ncrypt; i++) { if (!(cryptF[i]->SupportedMsgDigest(md.c_str()))) { // Not supported: drop from the list PRINT("MD type not supported ("<getErrText()); return Parms; } DEBUG("CA list: "<= 10) { GMAPuseDNname = 1; opt.ogmap %= 10; } if (opt.ogmap >= 0 && opt.ogmap <= 2) GMAPOpt = opt.ogmap; DEBUG("user mapping file option: "< 0) { // Initialize the GMap service // String pars; if (Debug) pars += "dbg|"; if (opt.gmapto > 0) { pars += "to="; pars += (int)opt.gmapto; } if (!(servGMap = XrdOucgetGMap(&eDest, GMAPFile.c_str(), pars.c_str()))) { if (GMAPOpt > 1) { ErrF(erp,kGSErrError,"error loading grid map file:",GMAPFile.c_str()); PRINT(erp->getErrText()); return Parms; } else { NOTIFY("Grid map file: "< 0) { if (!(GMAPFun = LoadGMAPFun((const char *) opt.gmapfun, (const char *) opt.gmapfunparms))) { ErrF(erp, kGSErrError, "GMAP plug-in could not be loaded", opt.gmapfun); PRINT(erp->getErrText()); return Parms; } else { hasgmapfun = 1; } } // // Disable GMAP if neither a grid mapfile nor a GMAP function are available if (!hasgmap && !hasgmapfun) { if (GMAPOpt > 1) { ErrF(erp,kGSErrError,"User mapping required, but neither a grid mapfile" " nor a mapping function are available"); PRINT(erp->getErrText()); return Parms; } GMAPOpt = 0; } // // Authorization function bool hasauthzfun = 0; if (opt.authzfun) { if (!(AuthzFun = LoadAuthzFun((const char *) opt.authzfun, (const char *) opt.authzfunparms, AuthzCertFmt))) { ErrF(erp, kGSErrError, "Authz plug-in could not be loaded", opt.authzfun); PRINT(erp->getErrText()); return Parms; } else { hasauthzfun = 1; // Notify certificate format if (AuthzCertFmt >= 0 && AuthzCertFmt <= 1) { const char *ccfmt[] = { "raw", "PEM base64" }; DEBUG("authzfun: proxy certificate format: "< 0) { AuthzCacheTimeOut = opt.authzto; DEBUG("grid-map cache entries expire after "< 0 && !hasauthzfun && opt.gmapto > 0) { GMAPCacheTimeOut = opt.gmapto; DEBUG("grid-map cache entries expire after "< 0) { AuthzPxyWhat = opt.authzpxy / 10; AuthzPxyWhere = opt.authzpxy % 10; // Some notification const char *capxy_what = (AuthzPxyWhat == 1) ? "'last proxy only'" : "'full proxy chain'"; const char *capxy_where = (AuthzPxyWhere == 1) ? "XrdSecEntity.creds" : "XrdSecEntity.endorsements"; DEBUG("Export proxy for authorization in '"<= 0) ? opt.vomsat : VOMSAttrOpt; // // Alternative VOMS extraction function if (opt.vomsfun) { if (!(VOMSFun = LoadVOMSFun((const char *) opt.vomsfun, (const char *) opt.vomsfunparms, VOMSCertFmt))) { ErrF(erp, kGSErrError, "VOMS plug-in could not be loaded", opt.vomsfun); PRINT(erp->getErrText()); return Parms; } else { // We at least check VOMS attributes if we have a function ... if (VOMSAttrOpt < 1) VOMSAttrOpt = 1; // Notify certificate format if (VOMSCertFmt >= 0 && VOMSCertFmt <= 1) { const char *ccfmt[] = { "raw", "PEM base64" }; DEBUG("vomsfun: proxy certificate format: "<,c:,ca: Parms = new char[cryptlist.length()+3+12+certcalist.length()+5]; if (Parms) { sprintf(Parms,"v:%d,c:%s,ca:%s", Version,cryptlist.c_str(),certcalist.c_str()); } else { ErrF(erp,kGSErrInit,"no system resources for 'Parms'"); PRINT(erp->getErrText()); } // Some notification DEBUG("available crypto modules: "< struct passwd *pw = getpwuid(getuid()); if (!pw) { NOTIFY("WARNING: cannot get user information (uid:"<pw_uid); } // Define user certificate file if (opt.cert) { String TmpCert = opt.cert; if (XrdSutExpand(TmpCert) == 0) { UsrCert = TmpCert; } else { PRINT("Could not expand: "< DefBits) DefBits = opt.bits; // // Delegate proxy options if (opt.dlgpxy > 0) { PxyReqOpts |= kOptsSigReq; if (opt.dlgpxy == 2) { PxyReqOpts |= kOptsFwdPxy; } else { PxyReqOpts |= kOptsDlgPxy; } } // // Define valid CNs for the server certificates; default is null, which means that // the server CN must be in the form "*/" if (opt.srvnames) SrvAllowedNames = opt.srvnames; // // Notify TRACE(Authen, "using certificate file: "< 0) { SafeFree(Entity.creds); } else { Entity.creds = 0; } Entity.credslen = 0; SafeFree(Entity.moninfo); // Cleanup the handshake variables, if still there SafeDelete(hs); // Cleanup any other instance specific to this protocol SafeDelete(sessionKey); // Session Key (result of the handshake) SafeDelete(bucketKey); // Bucket with the key in export form SafeDelete(sessionMD); // Message Digest instance SafeDelete(sessionKsig); // RSA key to sign SafeDelete(sessionKver); // RSA key to verify if (proxyChain) proxyChain->Cleanup(1); SafeDelete(proxyChain); // Chain with delegated proxies SafeDelete(expectedHost); delete this; } /******************************************************************************/ /* E n c r y p t i o n R e l a t e d M e t h o d s */ /******************************************************************************/ //_____________________________________________________________________________ int XrdSecProtocolgsi::Encrypt(const char *inbuf, // Data to be encrypted int inlen, // Length of data in inbuff XrdSecBuffer **outbuf) // Returns encrypted data { // Encrypt data in inbuff and place it in outbuff. // // Returns: < 0 Failed, the return value is -errno of the reason. Typically, // -EINVAL - one or more arguments are invalid. // -ENOTSUP - encryption not supported by the protocol // -EOVERFLOW - outbuff is too small to hold result // -ENOENT - Context not initialized // = 0 Success, outbuff contains a pointer to the encrypted data. // EPNAME("Encrypt"); // We must have a key if (!sessionKey) return -ENOENT; // And something to encrypt if (!inbuf || inlen <= 0 || !outbuf) return -EINVAL; // Regenerate IV int liv = 0; char *iv = 0; if (useIV) { iv = sessionKey->RefreshIV(liv); sessionKey->SetIV(liv, iv); } // Get output buffer char *buf = (char *)malloc(sessionKey->EncOutLength(inlen) + liv); if (!buf) return -ENOMEM; // IV at beginning memcpy(buf, iv, liv); // Encrypt int len = sessionKey->Encrypt(inbuf, inlen, buf + liv); if (len <= 0) { SafeFree(buf); return -EINVAL; } // Create and fill output buffer *outbuf = new XrdSecBuffer(buf, len); // We are done DEBUG("encrypted buffer has "<MaxIVLength() : 0; int sz = inlen - liv; // Get output buffer char *buf = (char *)malloc(sessionKey->DecOutLength(sz) + liv); if (!buf) return -ENOMEM; // Get and set IV if (useIV) { char *iv = new char[liv]; memcpy(iv, inbuf, liv); sessionKey->SetIV(liv, iv); delete[] iv; } // Decrypt int len = sessionKey->Decrypt(inbuf + liv, sz, buf); if (len <= 0) { SafeFree(buf); return -EINVAL; } // Create and fill output buffer *outbuf = new XrdSecBuffer(buf, len); // We are done DEBUG("decrypted buffer has "<Reset(0); // Calculate digest sessionMD->Update(inbuf, inlen); sessionMD->Final(); // Output length int lmax = sessionKsig->GetOutlen(sessionMD->Length()); char *buf = (char *)malloc(lmax); if (!buf) return -ENOMEM; // Sign int len = sessionKsig->EncryptPrivate(sessionMD->Buffer(), sessionMD->Length(), buf, lmax); if (len <= 0) { SafeFree(buf); return -EINVAL; } // Create and fill output buffer *outbuf = new XrdSecBuffer(buf, len); // We are done DEBUG("signature has "< 0 Failed to verify, signature does not match inbuff data. // EPNAME("Verify"); // We must have a PKI and a digest if (!sessionKver || !sessionMD) return -ENOENT; // And something to verify if (!inbuf || inlen <= 0 || !sigbuf || siglen <= 0) return -EINVAL; // Reset digest sessionMD->Reset(0); // Calculate digest sessionMD->Update(inbuf, inlen); sessionMD->Final(); // Output length int lmax = sessionKver->GetOutlen(siglen); char *buf = new char[lmax]; if (!buf) return -ENOMEM; // Decrypt signature int len = sessionKver->DecryptPublic(sigbuf, siglen, buf, lmax); if (len <= 0) { delete[] buf; return -EINVAL; } // Verify signature bool bad = 1; if (len == sessionMD->Length()) { if (!strncmp(buf, sessionMD->Buffer(), len)) { // Signature matches bad = 0; DEBUG("signature successfully verified"); } } // Cleanup if (buf) delete[] buf; // We are done return ((bad) ? 1 : 0); } //_____________________________________________________________________________ int XrdSecProtocolgsi::getKey(char *kbuf, int klen) { // Get the current encryption key // // Returns: < 0 Failed, returned value if -errno (see Encrypt) // >= 0 The size of the encyption key. The supplied buffer of length // size hold the key. If the buffer address is 0, only the // size of the key is returned. // EPNAME("getKey"); // Check if we have to serialize the key if (!bucketKey) { // We must have a key for that if (!sessionKey) // Invalid call return -ENOENT; // Create bucket bucketKey = sessionKey->AsBucket(); } // Prepare output now, if we have any if (bucketKey) { // If are asked only the size, we are done if (kbuf == 0) return bucketKey->size; // Check the size of the buffer if (klen < bucketKey->size) // Too small return -EOVERFLOW; // Copy the buffer memcpy(kbuf, bucketKey->buffer, bucketKey->size); // We are done DEBUG("session key exported"); return bucketKey->size; } // Key exists but we could export it in bucket format return -ENOMEM; } //_____________________________________________________________________________ int XrdSecProtocolgsi::setKey(char *kbuf, int klen) { // Set the current encryption key // // Returns: < 0 Failed, returned value if -errno (see Encrypt) // 0 The new key has been set. // EPNAME("setKey"); // Make sur that we can initialize the new key if (!kbuf || klen <= 0) // Invalid inputs return -EINVAL; if (!sessionCF) // Invalid context return -ENOENT; // Put the buffer key into a bucket XrdSutBucket *bck = new XrdSutBucket(); if (!bck) // Cannot get buffer: out-of-resources? return -ENOMEM; // Set key buffer bck->SetBuf(kbuf, klen); // Init a new cipher from the bucket XrdCryptoCipher *newKey = sessionCF->Cipher(bck); if (!newKey) { SafeDelete(bck); return -ENOMEM; } // Delete current key SafeDelete(sessionKey); // Set the new key sessionKey = newKey; // Cleanup SafeDelete(bck); // Ok DEBUG("session key update"); return 0; } /******************************************************************************/ /* C l i e n t O r i e n t e d F u n c t i o n s */ /******************************************************************************/ /******************************************************************************/ /* g e t C r e d e n t i a l s */ /******************************************************************************/ XrdSecCredentials *XrdSecProtocolgsi::getCredentials(XrdSecParameters *parm, XrdOucErrInfo *ei) { // Query client for the password; remote username and host // are specified in 'parm'. File '.rootnetrc' is checked. EPNAME("getCredentials"); // If we are a server the only reason to be here is to get the forwarded // or saved client credentials if (srvMode) { XrdSecCredentials *creds = 0; if (proxyChain) { // Export the proxy chain into a bucket XrdCryptoX509ExportChain_t ExportChain = sessionCF->X509ExportChain(); if (ExportChain) { XrdSutBucket *bck = (*ExportChain)(proxyChain, 1); if (bck) { // We need to duplicate it because XrdSecCredentials uses // {malloc, free} instead of {new, delete} char *nbuf = (char *) malloc(bck->size); if (nbuf) { memcpy(nbuf, bck->buffer, bck->size); // Import the buffer in a XrdSecCredentials object creds = new XrdSecCredentials(nbuf, bck->size); } delete bck; } } } return creds; } // Handshake vars container must be initialized at this point if (!hs) return ErrC(ei,0,0,0,kGSErrError, "handshake var container missing","getCredentials"); // // Nothing to do if buffer is empty if ((!parm && !hs->Parms) || (parm && (!(parm->buffer) || parm->size <= 0))) { if (hs->Iter == 0) return ErrC(ei,0,0,0,kGSErrNoBuffer,"missing parameters","getCredentials"); else return (XrdSecCredentials *)0; } // We support passing the user {proxy, cert, key} paths via Url parameter char *upp = (ei && ei->getEnv()) ? ei->getEnv()->Get("xrd.gsiusrpxy") : 0; if (upp) UsrProxy = upp; upp = (ei && ei->getEnv()) ? ei->getEnv()->Get("xrd.gsiusrcrt") : 0; if (upp) UsrCert = upp; upp = (ei && ei->getEnv()) ? ei->getEnv()->Get("xrd.gsiusrkey") : 0; if (upp) UsrKey = upp; // Count interations (hs->Iter)++; // Update time stamp hs->TimeStamp = time(0); // Local vars int step = 0; int nextstep = 0; const char *stepstr = 0; char *bpub = 0; int lpub = 0; String CryptoMod = ""; String Host = ""; String RemID = ""; String Emsg; String specID = ""; String issuerHash = ""; // Buffer / Bucket related XrdSutBuffer *bpar = 0; // Global buffer XrdSutBuffer *bmai = 0; // Main buffer XrdSutBucket *bck = 0; // Generic bucket // // Decode received buffer bpar = hs->Parms; if (!bpar && !(bpar = new XrdSutBuffer((const char *)parm->buffer,parm->size))) return ErrC(ei,0,0,0,kGSErrDecodeBuffer,"global",stepstr); // Ownership has been transferred hs->Parms = 0; // // Check protocol ID name if (strcmp(bpar->GetProtocol(),XrdSecPROTOIDENT)) return ErrC(ei,bpar,bmai,0,kGSErrBadProtocol,stepstr); // // The step indicates what we are supposed to do if (!(step = bpar->GetStep())) { // The first, fake, step step = kXGS_init; bpar->SetStep(step); } stepstr = ServerStepStr(step); // Dump, if requested XrdOucString bmsg; if (QTRACE(Dump)) { bmsg.form("IN: bpar: %s", stepstr); bpar->Dump(bmsg.c_str()); } // // Parse input buffer if (ParseClientInput(bpar, &bmai, Emsg) == -1) { DEBUG(Emsg<<" CF: "<Dump(bmsg.c_str()); } } // // Version DEBUG("version run by server: "<< hs->RemVers); // // Check random challenge if (!CheckRtag(bmai, Emsg)) return ErrC(ei,bpar,bmai,0,kGSErrBadRndmTag,Emsg.c_str(),stepstr); // // Login name if any String user(Entity.name); if (user.length() <= 0) user = getenv("XrdSecUSER"); // // Now action depens on the step nextstep = kXGC_none; XrdCryptoX509 *c = 0; switch (step) { case kXGS_init: // // Add bucket with cryptomod to the global list // (This must be always visible from now on) CryptoMod = hs->CryptoMod; if (hs->RemVers >= XrdSecgsiVersDHsigned && !(hs->HasPad)) CryptoMod += gNoPadTag; if (bpar->AddBucket(CryptoMod,kXRS_cryptomod) != 0) return ErrC(ei,bpar,bmai,0, kGSErrCreateBucket,XrdSutBuckStr(kXRS_cryptomod),stepstr); // // Add bucket with our version to the main list if (bpar->MarshalBucket(kXRS_version,(kXR_int32)(Version)) != 0) return ErrC(ei,bpar,bmai,0, kGSErrCreateBucket, XrdSutBuckStr(kXRS_version),"global",stepstr); // // Add our issuer hash c = hs->PxyChain->Begin(); if (c->type == XrdCryptoX509::kCA) { issuerHash = c->SubjectHash(); if (HashCompatibility && c->SubjectHash(1)) { issuerHash += "|"; issuerHash += c->SubjectHash(1); } } else { issuerHash = c->IssuerHash(); if (HashCompatibility && c->IssuerHash(1) && strcmp(c->IssuerHash(1),c->IssuerHash())) { issuerHash += "|"; issuerHash += c->IssuerHash(1); } } while ((c = hs->PxyChain->Next()) != 0) { if (c->type != XrdCryptoX509::kCA) break; issuerHash = c->SubjectHash(); if (HashCompatibility && c->SubjectHash(1) && strcmp(c->IssuerHash(1),c->IssuerHash())) { issuerHash += "|"; issuerHash += c->SubjectHash(1); } } DEBUG("Client issuer hash: " << issuerHash); if (bpar->AddBucket(issuerHash,kXRS_issuer_hash) != 0) return ErrC(ei,bpar,bmai,0, kGSErrCreateBucket, XrdSutBuckStr(kXRS_issuer_hash),stepstr); // // Add bucket with our delegate proxy options if (hs->RemVers >= 10100) { if (bpar->MarshalBucket(kXRS_clnt_opts,(kXR_int32)(hs->Options)) != 0) return ErrC(ei,bpar,bmai,0, kGSErrCreateBucket, XrdSutBuckStr(kXRS_clnt_opts),"global",stepstr); } // nextstep = kXGC_certreq; break; case kXGS_cert: // // We must have a session cipher at this point if (!(sessionKey)) return ErrC(ei,bpar,bmai,0, kGSErrNoCipher,"session cipher",stepstr); // // Extract buffer with public info for the cipher agreement if (!(bpub = sessionKey->Public(lpub))) return ErrC(ei,bpar,bmai,0, kGSErrNoPublic,"session",stepstr); // // If server supports decoding of signed DH, do sign them if (hs->RemVers >= XrdSecgsiVersDHsigned) { bck = new XrdSutBucket(bpub,lpub,kXRS_cipher); if (sessionKsig) { // Encrypt client DH public parameters with client private key if (sessionKsig->EncryptPrivate(*bck) <= 0) return ErrC(ei,bpar,bmai,0, kGSErrExportPuK, "encrypting client DH public parameters",stepstr); } else { return ErrC(ei,bpar,bmai,0, kGSErrExportPuK, "client signing key undefined!",stepstr); } // // Add it to the global list if (bpar->AddBucket(bck) != 0) return ErrC(ei,bpar,bmai,0, kGSErrAddBucket, "main",stepstr); // // Export client public key XrdOucString cpub; if (sessionKsig->ExportPublic(cpub) < 0) return ErrC(ei,bpar,bmai,0, kGSErrExportPuK, "exporting client public key",stepstr); // Add it to the global list if (bpar->UpdateBucket(cpub.c_str(),cpub.length(),kXRS_puk) != 0) return ErrC(ei,bpar,bmai,0, kGSErrAddBucket, XrdSutBuckStr(kXRS_puk),"global",stepstr); } else { // // Add it to the global list if (bpar->UpdateBucket(bpub,lpub,kXRS_puk) != 0) return ErrC(ei,bpar,bmai,0, kGSErrAddBucket, XrdSutBuckStr(kXRS_puk),"global",stepstr); delete[] bpub; // bpub is being duplicated inside of 'UpdateBucket' } // // Add the proxy certificate bmai->AddBucket(hs->Cbck); // // Add login name if any, needed while chosing where to export the proxies if (user.length() > 0) { if (bmai->AddBucket(user, kXRS_user) != 0) return ErrC(ei,bpar,bmai,0, kGSErrCreateBucket, XrdSutBuckStr(kXRS_user),stepstr); } // nextstep = kXGC_cert; break; case kXGS_pxyreq: // // If something went wrong, send explanation if (Emsg.length() > 0) { if (bmai->AddBucket(Emsg,kXRS_message) != 0) return ErrC(ei,bpar,bmai,0, kGSErrCreateBucket, XrdSutBuckStr(kXRS_message),stepstr); } // // Add login name if any, needed while chosing where to export the proxies if (user.length() > 0) { if (bmai->AddBucket(user, kXRS_user) != 0) return ErrC(ei,bpar,bmai,0, kGSErrCreateBucket, XrdSutBuckStr(kXRS_user),stepstr); } // // The relevant buckets should already be in the buffers nextstep = kXGC_sigpxy; break; default: return ErrC(ei,bpar,bmai,0, kGSErrBadOpt,stepstr); } // // Serialize and encrypt if (AddSerialized('c', nextstep, hs->ID, bpar, bmai, kXRS_main, sessionKey) != 0) { return ErrC(ei,bpar,bmai,0, kGSErrSerialBuffer,"main",stepstr); } // // Serialize the global buffer char *bser = 0; int nser = bpar->Serialized(&bser,'f'); if (QTRACE(Authen)) { bmsg.form("OUT: bpar: %s", ClientStepStr(bpar->GetStep())); bpar->Dump(bmsg.c_str()); bmsg.form("OUT: bmai: %s", ClientStepStr(bpar->GetStep())); bmai->Dump(bmsg.c_str()); } // // We may release the buffers now REL2(bpar,bmai); // // Return serialized buffer if (nser > 0) { DEBUG("returned " << nser <<" bytes of credentials"); return new XrdSecCredentials(bser, nser); } else { NOTIFY("problems with final serialization"); return (XrdSecCredentials *)0; } } /******************************************************************************/ /* S e r v e r O r i e n t e d M e t h o d s */ /******************************************************************************/ //_____________________________________________________________________________ static bool AuthzFunCheck(XrdSutCacheEntry *e, void *a) { int st_ref = (*((XrdSutCacheArg_t *)a)).arg1; time_t ts_ref = (time_t)(*((XrdSutCacheArg_t *)a)).arg2; long to_ref = (*((XrdSutCacheArg_t *)a)).arg3; int st_exp = (*((XrdSutCacheArg_t *)a)).arg4; if (e && (e->status == st_ref)) { // Check expiration, if required bool expired = 0; if (to_ref > 0 && (ts_ref - e->mtime) > to_ref) expired = 1; int notafter = *((int *) e->buf2.buf); if (to_ref > notafter) expired = 1; if (expired) { // Invalidate the entry, if the case e->status = st_exp; } else { return true; } } return false; } /******************************************************************************/ /* A u t h e n t i c a t e */ /******************************************************************************/ int XrdSecProtocolgsi::Authenticate(XrdSecCredentials *cred, XrdSecParameters **parms, XrdOucErrInfo *ei) { // // Check if we have any credentials or if no credentials really needed. // In either case, use host name as client name EPNAME("Authenticate"); // // If cred buffer is two small or empty assume host protocol if (cred->size <= (int)XrdSecPROTOIDLEN || !cred->buffer) { strncpy(Entity.prot, "host", sizeof(Entity.prot)); return 0; } // Handshake vars conatiner must be initialized at this point if (!hs) return ErrS(Entity.tident,ei,0,0,0,kGSErrError, "handshake var container missing", "protocol initialization problems"); // Update time stamp hs->TimeStamp = time(0); // // ID of this handshaking if (hs->ID.length() <= 0) hs->ID = Entity.tident; DEBUG("handshaking ID: " << hs->ID); // Local vars int kS_rc = kgST_more; int step = 0; int nextstep = 0; char *bpub = 0; int lpub = 0; const char *stepstr = 0; String Message; String CryptList; String Ciphers; String Host; String SrvPuKExp; String Salt; String RndmTag; String ClntMsg(256); // Buffer related XrdSutBuffer *bpar = 0; // Global buffer XrdSutBuffer *bmai = 0; // Main buffer XrdSutBucket *bck = 0; // Generic bucket // Proxy export related XrdOucString spxy; XrdSutBucket *bpxy = 0; // // Decode received buffer if (!(bpar = new XrdSutBuffer((const char *)cred->buffer,cred->size))) return ErrS(hs->ID,ei,0,0,0,kGSErrDecodeBuffer,"global",stepstr); // // Check protocol ID name if (strcmp(bpar->GetProtocol(),XrdSecPROTOIDENT)) return ErrS(hs->ID,ei,bpar,bmai,0,kGSErrBadProtocol,stepstr); // // The step indicates what we are supposed to do step = bpar->GetStep(); stepstr = ClientStepStr(step); // Dump, if requested XrdOucString bmsg; if (QTRACE(Dump)) { bmsg.form("IN: bpar: %s", stepstr); bpar->Dump(bmsg.c_str()); } // // Parse input buffer if (ParseServerInput(bpar, &bmai, ClntMsg) == -1) { DEBUG(ClntMsg); return ErrS(hs->ID,ei,bpar,bmai,0,kGSErrParseBuffer,ClntMsg.c_str(),stepstr); } // // Version DEBUG("version run by client: "<< hs->RemVers); DEBUG("options req by client: "<< hs->Options); // // Dump, if requested if (QTRACE(Dump)) { if (bmai) { bmsg.form("IN: bmai: %s", stepstr); bmai->Dump(bmsg.c_str()); } } // // Check random challenge if (!CheckRtag(bmai, ClntMsg)) return ErrS(hs->ID,ei,bpar,bmai,0,kGSErrBadRndmTag,stepstr,ClntMsg.c_str()); // Extract the VOMS attrbutes, if required XrdCryptoX509ExportChain_t X509ExportChain = (sessionCF) ? sessionCF->X509ExportChain() : 0; if (!X509ExportChain) { // Error return ErrS(hs->ID,ei,0,0,0,kGSErrError, "crypto factory function for chain export not found"); } // // Now action depens on the step switch (step) { case kXGC_certreq: // // Client required us to send our certificate and cipher DH public parameters: // add first this last one. // Extract buffer with public info for the cipher agreement if (!(bpub = hs->Rcip->Public(lpub))) return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrNoPublic, "session",stepstr); // If client supports decoding of signed DH, do sign them if (hs->RemVers >= XrdSecgsiVersDHsigned) { bck = new XrdSutBucket(bpub,lpub,kXRS_cipher); if (sessionKsig) { // // Encrypt server DH public parameters with server key if (sessionKsig->EncryptPrivate(*bck) <= 0) return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrExportPuK, "encrypting server DH public parameters",stepstr); } else { return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrExportPuK, "server signing key undefined!",stepstr); } } else { // Previous naming bck = new XrdSutBucket(bpub,lpub,kXRS_puk); } // // Add it to the global list if (bpar->AddBucket(bck) != 0) return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrAddBucket, "main",stepstr); // // Add bucket with list of supported ciphers if (bpar->AddBucket(DefCipher,kXRS_cipher_alg) != 0) return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrAddBucket,XrdSutBuckStr(kXRS_cipher_alg),stepstr); // // Add bucket with list of supported MDs if (bpar->AddBucket(DefMD,kXRS_md_alg) != 0) return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrAddBucket,XrdSutBuckStr(kXRS_md_alg),stepstr); // // Add the server certificate bpar->AddBucket(hs->Cbck); // We are done for the moment nextstep = kXGS_cert; break; case kXGC_cert: // // Client sent its own credentials: their are checked in // ParseServerInput, so if we are here they are OK kS_rc = kgST_ok; nextstep = kXGS_none; if (GMAPOpt > 0) { // Get name from gridmap String name; QueryGMAP(hs->Chain, hs->TimeStamp, name); DEBUG("username(s) associated with this DN: "<GetBucket(kXRS_user))) { bck->ToString(user); bmai->Deactivate(kXRS_user); } DEBUG("target user: "< 0) { // Check if the wanted username is authorized String u; int from = 0; bool ok = 0; while ((from = name.tokenize(u, from, ',')) != -1) { if (user == u) { ok = 1; break; } } if (ok) { name = u; DEBUG("DN mapping: requested user is authorized: name is '"<Chain->EEChash()) { Entity.name = strdup(hs->Chain->EEChash()); } else if (GMAPuseDNname && hs->Chain->EECname()) { Entity.name = strdup(hs->Chain->EECname()); } else { PRINT("WARNING: DN missing: corruption? "); } } // Add the DN as default moninfo if requested (the authz plugin may change this) if (MonInfoOpt > 0) { Entity.moninfo = strdup(hs->Chain->EECname()); } if (VOMSAttrOpt > 0) { if (VOMSFun) { // Fill the information needed by the external function if (VOMSCertFmt == 1) { // PEM base64 bpxy = (*X509ExportChain)(hs->Chain, true); bpxy->ToString(spxy); delete bpxy; Entity.creds = strdup(spxy.c_str()); Entity.credslen = spxy.length(); } else { // Raw (opaque) format, to be used with XrdCrypto Entity.creds = (char *) hs->Chain; Entity.credslen = 0; } if ((*VOMSFun)(Entity) != 0 && VOMSAttrOpt == 2) { // Error kS_rc = kgST_error; PRINT("ERROR: the VOMS extraction plug-in reported a failure for this handshake"); break; } } else { // Lite version (no validations whatsover) if (ExtractVOMS(hs->Chain, Entity) != 0 && VOMSAttrOpt == 2) { // Error kS_rc = kgST_error; PRINT("ERROR: VOMS attributes required but not found (default lite-extraction technology)"); break; } } NOTIFY("VOMS: Entity.vorg: "<< (Entity.vorg ? Entity.vorg : "")); NOTIFY("VOMS: Entity.grps: "<< (Entity.grps ? Entity.grps : "")); NOTIFY("VOMS: Entity.role: "<< (Entity.role ? Entity.role : "")); NOTIFY("VOMS: Entity.endorsements: "<< (Entity.endorsements ? Entity.endorsements : "")); } // Here prepare/extract the information for authorization spxy = ""; bpxy = 0; if (AuthzFun && AuthzKey) { // Fill the information needed by the external function if (AuthzCertFmt == 1) { // May have been already done if (!Entity.creds || (Entity.creds && Entity.credslen == 0)) { // PEM base64 bpxy = (*X509ExportChain)(hs->Chain, true); bpxy->ToString(spxy); Entity.creds = strdup(spxy.c_str()); Entity.credslen = spxy.length(); // If not empty Entity.creds is a pointer to hs->Chain and // we need not to free it } } else { // May have been already done if (Entity.creds && Entity.credslen > 0) { // Entity.creds is in PEM form, we need to free it free(Entity.creds); // Raw (opaque) format, to be used with XrdCrypto Entity.creds = (char *) hs->Chain; Entity.credslen = 0; } } // Get the key char *key = 0; int lkey = 0; if ((lkey = (*AuthzKey)(Entity, &key)) < 0) { // Fatal error kS_rc = kgST_error; PRINT("ERROR: unable to get the key associated to this user"); break; } const char *dn = (const char *)key; time_t now = hs->TimeStamp; // We may have it in the cache XrdSutCERef ceref; bool rdlock = false; XrdSutCacheArg_t arg = {kCE_ok, now, AuthzCacheTimeOut, kCE_disabled}; XrdSutCacheEntry *cent = cacheAuthzFun.Get(dn, rdlock, AuthzFunCheck, (void *) &arg); if (!cent) { // Fatal error kS_rc = kgST_error; PRINT("ERROR: unable to get cache entry for dn: "<rwmtx)); if (!rdlock) { if (cent->buf1.buf) FreeEntity((XrdSecEntity *) cent->buf1.buf); SafeDelete(cent->buf1.buf); SafeDelete(cent->buf2.buf); } if (cent->status != kCE_ok) { int authzrc = 0; if ((authzrc = (*AuthzFun)(Entity)) != 0) { // Error kS_rc = kgST_error; PRINT("ERROR: the authorization plug-in reported a failure for this handshake"); SafeDelete(key); ceref.UnLock(); break; } else { cent->status = kCE_ok; // Save a copy of the relevant Entity fields XrdSecEntity *se = new XrdSecEntity(); int slen = 0; CopyEntity(&Entity, se, &slen); FreeEntity((XrdSecEntity *) cent->buf1.buf); SafeDelete(cent->buf1.buf); cent->buf1.buf = (char *) se; cent->buf1.len = slen; // Proxy expiration time int notafter = hs->Chain->End() ? hs->Chain->End()->NotAfter() : -1; cent->buf2.buf = (char *) new int(notafter); cent->buf2.len = sizeof(int); // Fill up the rest cent->cnt = 0; cent->mtime = now; // creation time // Notify DEBUG("Saved Entity to cacheAuthzFun ("<buf1.buf, &Entity, &slen); // Notify DEBUG("Got Entity from cacheAuthzFun ("<= 0) { if (bpxy && AuthzPxyWhat == 1) { SafeDelete(bpxy); spxy = ""; SafeFree(Entity.creds); Entity.credslen = 0; } if (!bpxy) { if (AuthzPxyWhat == 1 && hs->Chain->End()) { bpxy = hs->Chain->End()->Export(); } else { bpxy = (*X509ExportChain)(hs->Chain, true); } bpxy->ToString(spxy); } if (AuthzPxyWhere == 1) { Entity.creds = strdup(spxy.c_str()); Entity.credslen = spxy.length(); } else { // This should be deprecated Entity.endorsements = strdup(spxy.c_str()); } delete bpxy; NOTIFY("Entity.endorsements: "<<(void *)Entity.endorsements); NOTIFY("Entity.creds: "<<(void *)Entity.creds); NOTIFY("Entity.credslen: "<RemVers >= 10100) { if (hs->PxyChain) { // The client is going to send over info for delegation kS_rc = kgST_more; nextstep = kXGS_pxyreq; } } break; case kXGC_sigpxy: // // Nothing to do after this kS_rc = kgST_ok; nextstep = kXGS_none; // // If something went wrong, print explanation if (ClntMsg.length() > 0) { PRINT(ClntMsg); } break; default: return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrBadOpt, stepstr); } if (kS_rc == kgST_more) { // // Add message to client if (ClntMsg.length() > 0) if (bmai->AddBucket(ClntMsg,kXRS_message) != 0) { NOTIFY("problems adding bucket with message for client"); } // // Serialize, encrypt and add to the global list if (AddSerialized('s', nextstep, hs->ID, bpar, bmai, kXRS_main, sessionKey) != 0) { return ErrS(hs->ID,ei,bpar,bmai,0, kGSErrSerialBuffer, "main / session cipher",stepstr); } // // Serialize the global buffer char *bser = 0; int nser = bpar->Serialized(&bser,'f'); // // Dump, if requested if (QTRACE(Authen)) { bmsg.form("OUT: bpar: %s", ServerStepStr(bpar->GetStep())); bpar->Dump(bmsg.c_str()); bmsg.form("OUT: bmai: %s", ServerStepStr(bpar->GetStep())); bmai->Dump(bmsg.c_str()); } // // Create buffer for client *parms = new XrdSecParameters(bser,nser); } else { // // Cleanup handshake vars SafeDelete(hs); } // // We may release the buffers now REL2(bpar,bmai); // // All done return kS_rc; } /******************************************************************************/ /* C o p y E n t i ty */ /******************************************************************************/ void XrdSecProtocolgsi::CopyEntity(XrdSecEntity *in, XrdSecEntity *out, int *lout) { // Copy relevant fields of 'in' into 'out'; return length of 'out' if (!in || !out) return; int slen = sizeof(XrdSecEntity); if (in->name) { out->name = strdup(in->name); slen += strlen(in->name); } if (in->host) { out->host = strdup(in->host); slen += strlen(in->host); } if (in->vorg) { out->vorg = strdup(in->vorg); slen += strlen(in->vorg); } if (in->role) { out->role = strdup(in->role); slen += strlen(in->role); } if (in->grps) { out->grps = strdup(in->grps); slen += strlen(in->grps); } if (in->creds && in->credslen > 0) { out->creds = strdup(in->creds); slen += in->credslen; out->credslen = in->credslen; } if (in->endorsements) { out->endorsements = strdup(in->endorsements); slen += strlen(in->endorsements); } if (in->moninfo) { out->moninfo = strdup(in->moninfo); slen += strlen(in->moninfo); } // Save length, if required if (lout) *lout = slen; // Done return; } /******************************************************************************/ /* F r e e E n t i ty */ /******************************************************************************/ void XrdSecProtocolgsi::FreeEntity(XrdSecEntity *in) { // Free relevant fields of 'in'; if (!in) return; if (in->name) SafeFree(in->name); if (in->host) SafeFree(in->host); if (in->vorg) SafeFree(in->vorg); if (in->role) SafeFree(in->role); if (in->grps) SafeFree(in->grps); if (in->creds && in->credslen > 0) { SafeFree(in->creds); in->credslen = 0; } if (in->endorsements) SafeFree(in->endorsements); if (in->moninfo) SafeFree(in->moninfo); // Done return; } /******************************************************************************/ /* E x t r a c t V O M S */ /******************************************************************************/ int XrdSecProtocolgsi::ExtractVOMS(X509Chain *c, XrdSecEntity &ent) { // Get the VOMS attributes from proxy file(s) in chain 'c' (either the proxy // or the limited proxy) and fill the relevant fields in 'ent' EPNAME("ExtractVOMS"); if (!c) return -1; XrdCryptoX509 *xp = c->End(); if (!xp) return -1; // Extractor XrdCryptoX509GetVOMSAttr_t X509GetVOMSAttr = sessionCF->X509GetVOMSAttr(); if (!X509GetVOMSAttr) return -1; // Extract the information XrdOucString vatts; int rc = 0; if ((rc = (*X509GetVOMSAttr)(xp, vatts)) != 0) { if (strstr(xp->Subject(), "CN=limited proxy")) { xp = c->SearchBySubject(xp->Issuer()); rc = (*X509GetVOMSAttr)(xp, vatts); } if (rc != 0) { if (rc > 0) { NOTIFY("No VOMS attributes in proxy chain"); } else { PRINT("ERROR: problem extracting VOMS attributes"); } return -1; } } int from = 0; XrdOucString vat; while ((from = vatts.tokenize(vat, from, ',')) != -1) { XrdOucString vo, role, grp; if (vat.length() > 0) { // The attribute is in the form // /VO[/group[/subgroup(s)]][/Role=role][/Capability=cap] int isl = vat.find('/', 1); if (isl != STR_NPOS) vo.assign(vat, 1, isl - 1); int igr = vat.find("/Role=", 1); if (igr != STR_NPOS) grp.assign(vat, 0, igr - 1); int irl = vat.find("Role="); if (irl != STR_NPOS) { role.assign(vat, irl + 5); isl = role.find('/'); role.erase(isl); } if (ent.vorg) { if (vo != (const char *) ent.vorg) { DEBUG("WARNING: found a second VO ('"< 0) ent.vorg = strdup(vo.c_str()); } if (grp.length() > 0 && (!ent.grps || grp.length() > int(strlen(ent.grps)))) { SafeFree(ent.grps); ent.grps = strdup(grp.c_str()); } if (role.length() > 0 && role != "NULL" && !ent.role) { ent.role = strdup(role.c_str()); } } } // Save the whole string in endorsements SafeFree(ent.endorsements); if (vatts.length() > 0) ent.endorsements = strdup(vatts.c_str()); // Notify if did not find the main info (the VO ...) if (!ent.vorg) PRINT("WARNING: no VO found! (VOMS attributes: '"< 0) POPTS(t, " CRL refresh time: "<< crlrefresh); if (mode == 'c') { POPTS(t, " Certificate: " << (cert ? cert : XrdSecProtocolgsi::UsrCert)); POPTS(t, " Key: " << (key ? key : XrdSecProtocolgsi::UsrKey)); POPTS(t, " Proxy file: " << XrdSecProtocolgsi::UsrProxy); POPTS(t, " Proxy validity: " << (valid ? valid : XrdSecProtocolgsi::PxyValid)); POPTS(t, " Proxy dep length: " << deplen); POPTS(t, " Proxy bits: " << bits); POPTS(t, " Proxy sign option: "<< sigpxy); POPTS(t, " Proxy delegation option: "<< dlgpxy); POPTS(t, " Allowed server names: "<< (srvnames ? srvnames : "[*/][/*]")); } else { POPTS(t, " Certificate: " << (cert ? cert : XrdSecProtocolgsi::SrvCert)); POPTS(t, " Key: " << (key ? key : XrdSecProtocolgsi::SrvKey)); POPTS(t, " Proxy delegation option: "<< dlgpxy); if (dlgpxy > 1) POPTS(t, " Template for exported proxy: "<< (exppxy ? exppxy : gUsrPxyDef)); POPTS(t, " GRIDmap file: " << (gridmap ? gridmap : XrdSecProtocolgsi::GMAPFile)); POPTS(t, " GRIDmap option: "<< ogmap); POPTS(t, " GRIDmap cache entries expiration (secs): "<< gmapto); if (gmapfun) { POPTS(t, " DN mapping function: " << gmapfun); if (gmapfunparms) POPTS(t, " DN mapping function parms: " << gmapfunparms); } else { if (gmapfunparms) POPTS(t, " DN mapping function parms: ignored (no mapping function defined)"); } if (authzfun) { POPTS(t, " Authorization function: " << authzfun); if (authzfunparms) POPTS(t, " Authorization function parms: " << authzfunparms); POPTS(t, " Authorization cache entries expiration (secs): " << authzto); } else { if (authzfunparms) POPTS(t, " Authorization function parms: ignored (no authz function defined)"); } POPTS(t, " Client proxy availability in XrdSecEntity.endorsement: "<< authzpxy); POPTS(t, " VOMS option: "<< vomsat); if (vomsfun) { POPTS(t, " VOMS extraction function: " << vomsfun); if (vomsfunparms) POPTS(t, " VOMS extraction function parms: " << vomsfunparms); } else { if (vomsfunparms) POPTS(t, " VOMS extraction function parms: ignored (no VOMS extraction function defined)"); } POPTS(t, " MonInfo option: "<< moninfo); if (!hashcomp) POPTS(t, " Name hashing algorithm compatibility OFF"); } // Crypto options POPTS(t, " Crypto modules: "<< (clist ? clist : XrdSecProtocolgsi::DefCrypto)); POPTS(t, " Ciphers: "<< (cipher ? cipher : XrdSecProtocolgsi::DefCipher)); POPTS(t, " MDigests: "<< (md ? md : XrdSecProtocolgsi::DefMD)); if (trustdns) { POPTS(t, " Trusting DNS for hostname checking"); } else { POPTS(t, " Untrusting DNS for hostname checking"); } POPTS(t, "*** ------------------------------------------------------------ ***"); } /******************************************************************************/ /* X r d S e c P r o t o c o l g s i I n i t */ /******************************************************************************/ extern "C" { char *XrdSecProtocolgsiInit(const char mode, const char *parms, XrdOucErrInfo *erp) { // One-time protocol initialization, filling the static flags and options // of the protocol. // For clients (mode == 'c') we use values in envs. // For servers (mode == 's') the command line options are passed through // parms. EPNAME("ProtocolgsiInit"); gsiOptions opts; char *rc = (char *)""; char *cenv = 0; // Initiate error logging and tracing gsiTrace = XrdSecProtocolgsi::EnableTracing(); // // Clients first if (mode == 'c') { // // Decode envs: // "XrdSecDEBUG" debug flag ("0","1","2","3") // "XrdSecGSICADIR" full path to an alternative path // containing the CA info // [/etc/grid-security/certificates] // "XrdSecGSICRLDIR" full path to an alternative path // containing the CRL info // [/etc/grid-security/certificates] // "XrdSecGSICRLEXT" default extension of CRL files [.r0] // "XrdSecGSIUSERCERT" full path to an alternative file // containing the user certificate // [$HOME/.globus/usercert.pem] // "XrdSecGSIUSERKEY" full path to an alternative file // containing the user key // [$HOME/.globus/userkey.pem] // "XrdSecGSIUSERPROXY" full path to an alternative file // containing the user proxy // [/tmp/x509up_u] // "XrdSecGSIPROXYVALID" validity of proxies in the // grid-proxy-init format // ["12:00", i.e. 12 hours] // "XrdSecGSIPROXYDEPLEN" depth of signature path for proxies; // use -1 for unlimited [0] // "XrdSecGSIPROXYKEYBITS" bits in PKI for proxies [512] // "XrdSecGSICACHECK" CA check level [1]: // 0 do not verify; // 1 verify if self-signed, warn if not; // 2 verify in all cases, fail if not possible // "XrdSecGSICRLCHECK" CRL check level [2]: // 0 don't care; // 1 use if available; // 2 require, // 3 require non-expired CRL // "XrdSecGSIDELEGPROXY" Forwarding of credentials option: // 0 deny; 1 sign request created // by server; 2 forward local proxy // (include private key) [1] // "XrdSecGSISRVNAMES" Server names allowed: if the server CN // does not match any of these, or it is // explicitely denied by these, or it is // not in the form "*/", the // handshake fails. // "XrdSecGSIUSEDEFAULTHASH" If this variable is set only the default // name hashing algorithm is used // opts.mode = mode; // debug cenv = getenv("XrdSecDEBUG"); if (cenv) {if (cenv[0] >= 49 && cenv[0] <= 51) opts.debug = atoi(cenv); else {PRINT("unsupported debug value from env XrdSecDEBUG: "<] // [-c:[-]ssl[:[-]] // [-crldir:] // [-crlext:] // [-cert:] // [-key:] // [-cipher:] // [-md:] // [-ca:] // [-crl:] // [-crlrefresh:] // [-gridmap:] // [-gmapfun:] // [-gmapfunparms:] // [-authzfun:] // [-authzfunparms:] // [-authzto:] // [-gmapto:] // [-gmapopt:] // [-dlgpxy:] // [-exppxy:] // [-authzpxy] // [-vomsat:] // [-vomsfun:] // [-vomsfunparms:] // [-defaulthash] // [-trustdns:<0|1>] // int debug = -1; String clist = ""; String certdir = ""; String crldir = ""; String crlext = ""; String cert = ""; String key = ""; String cipher = ""; String md = ""; String gridmap = ""; String gmapfun = ""; String gmapfunparms = ""; String authzfun = ""; String authzfunparms = ""; String vomsfun = ""; String vomsfunparms = ""; String exppxy = ""; int ca = 1; int crl = 1; int crlrefresh = 86400; int ogmap = 1; int gmapto = 600; int authzto = -1; int dlgpxy = 0; int authzpxy = 0; int vomsat = 1; int moninfo = 0; int hashcomp = 1; int trustdns = 1; char *op = 0; while (inParms.GetLine()) { while ((op = inParms.GetToken())) { if (!strncmp(op, "-d:",3)) { debug = atoi(op+3); } else if (!strncmp(op, "-c:",3)) { clist = (const char *)(op+3); } else if (!strncmp(op, "-certdir:",9)) { certdir = (const char *)(op+9); } else if (!strncmp(op, "-crldir:",8)) { crldir = (const char *)(op+8); } else if (!strncmp(op, "-crlext:",8)) { crlext = (const char *)(op+8); } else if (!strncmp(op, "-cert:",6)) { cert = (const char *)(op+6); } else if (!strncmp(op, "-key:",5)) { key = (const char *)(op+5); } else if (!strncmp(op, "-cipher:",8)) { cipher = (const char *)(op+8); } else if (!strncmp(op, "-md:",4)) { md = (const char *)(op+4); } else if (!strncmp(op, "-ca:",4)) { ca = atoi(op+4); } else if (!strncmp(op, "-crl:",5)) { crl = atoi(op+5); } else if (!strncmp(op, "-crlrefresh:",12)) { crlrefresh = atoi(op+12); } else if (!strncmp(op, "-gmapopt:",9)) { ogmap = atoi(op+9); } else if (!strncmp(op, "-gridmap:",9)) { gridmap = (const char *)(op+9); } else if (!strncmp(op, "-gmapfun:",9)) { gmapfun = (const char *)(op+9); } else if (!strncmp(op, "-gmapfunparms:",14)) { gmapfunparms = (const char *)(op+14); } else if (!strncmp(op, "-authzfun:",10)) { authzfun = (const char *)(op+10); } else if (!strncmp(op, "-authzfunparms:",15)) { authzfunparms = (const char *)(op+15); } else if (!strncmp(op, "-authzto:",9)) { authzto = atoi(op+9); } else if (!strncmp(op, "-gmapto:",8)) { gmapto = atoi(op+8); } else if (!strncmp(op, "-dlgpxy:",8)) { dlgpxy = atoi(op+8); } else if (!strncmp(op, "-exppxy:",8)) { exppxy = (const char *)(op+8); } else if (!strncmp(op, "-authzpxy:",10)) { authzpxy = atoi(op+10); } else if (!strncmp(op, "-authzpxy",9)) { authzpxy = 11; } else if (!strncmp(op, "-vomsat:",8)) { vomsat = atoi(op+8); } else if (!strncmp(op, "-vomsfun:",9)) { vomsfun = (const char *)(op+9); } else if (!strncmp(op, "-vomsfunparms:",14)) { vomsfunparms = (const char *)(op+14); } else if (!strcmp(op, "-moninfo")) { moninfo = 1; } else if (!strncmp(op, "-moninfo:",9)) { moninfo = atoi(op+9); } else if (!strcmp(op, "-defaulthash")) { hashcomp = 0; } else if (!strncmp(op, "-trustdns:",10)) { trustdns = atoi(op+10); } else { PRINT("ignoring unknown switch: "< -1) ? debug : opts.debug; opts.mode = 's'; opts.ca = ca; opts.crl = crl; opts.crlrefresh = crlrefresh; opts.ogmap = ogmap; opts.gmapto = gmapto; opts.authzto = authzto; opts.dlgpxy = (dlgpxy >= 0 && dlgpxy <= 1) ? dlgpxy : 0; opts.authzpxy = authzpxy; opts.vomsat = vomsat; opts.moninfo = moninfo; opts.hashcomp = hashcomp; opts.trustdns = (trustdns <= 0) ? false : true; if (clist.length() > 0) opts.clist = (char *)clist.c_str(); if (certdir.length() > 0) opts.certdir = (char *)certdir.c_str(); if (crldir.length() > 0) opts.crldir = (char *)crldir.c_str(); if (crlext.length() > 0) opts.crlext = (char *)crlext.c_str(); if (cert.length() > 0) opts.cert = (char *)cert.c_str(); if (key.length() > 0) opts.key = (char *)key.c_str(); if (cipher.length() > 0) opts.cipher = (char *)cipher.c_str(); if (md.length() > 0) opts.md = (char *)md.c_str(); if (gridmap.length() > 0) opts.gridmap = (char *)gridmap.c_str(); if (gmapfun.length() > 0) opts.gmapfun = (char *)gmapfun.c_str(); if (gmapfunparms.length() > 0) opts.gmapfunparms = (char *)gmapfunparms.c_str(); if (authzfun.length() > 0) opts.authzfun = (char *)authzfun.c_str(); if (authzfunparms.length() > 0) opts.authzfunparms = (char *)authzfunparms.c_str(); if (exppxy.length() > 0) opts.exppxy = (char *)exppxy.c_str(); if (vomsfun.length() > 0) opts.vomsfun = (char *)vomsfun.c_str(); if (vomsfunparms.length() > 0) opts.vomsfunparms = (char *)vomsfunparms.c_str(); // Notify init options, if required opts.Print(gsiTrace); // // Setup the plug-in with the chosen options return XrdSecProtocolgsi::Init(opts,erp); } // Notify init options, if required opts.Print(gsiTrace); // // Setup the plug-in with the defaults return XrdSecProtocolgsi::Init(opts,erp); }} /******************************************************************************/ /* X r d S e c P r o t o c o l g s i O b j e c t */ /******************************************************************************/ XrdVERSIONINFO(XrdSecProtocolgsiObject,secgsi); namespace {XrdVersionInfo *gsiVersion = &XrdVERSIONINFOVAR(XrdSecProtocolgsiObject);} extern "C" { XrdSecProtocol *XrdSecProtocolgsiObject(const char mode, const char *hostname, XrdNetAddrInfo &endPoint, const char *parms, XrdOucErrInfo *erp) { XrdSecProtocolgsi *prot; int options = XrdSecNOIPCHK; // // Get a new protocol object if (!(prot = new XrdSecProtocolgsi(options, hostname, endPoint, parms))) { const char *msg = "Secgsi: Insufficient memory for protocol."; if (erp) erp->setErrInfo(ENOMEM, msg); else cerr < 0) { bls->SetStep(step); buf->SetStep(step); hs->LastStep = step; } // // If a random tag has been sent and we have a session cipher, // we sign it XrdSutBucket *brt = buf->GetBucket(kXRS_rtag); if (brt && sessionKsig) { // // Encrypt random tag with session cipher if (sessionKsig->EncryptPrivate(*brt) <= 0) { PRINT("error encrypting random tag"); return -1; } // // Update type brt->type = kXRS_signed_rtag; } // // Add an random challenge: if a next exchange is required this will // allow to prove authenticity of counter part // // Generate new random tag and create a bucket if (!(opt == 'c' && step == kXGC_sigpxy)) { String RndmTag; XrdSutRndm::GetRndmTag(RndmTag); // // Get bucket brt = 0; if (!(brt = new XrdSutBucket(RndmTag,kXRS_rtag))) { PRINT("error creating random tag bucket"); return -1; } buf->AddBucket(brt); } // // Get cache entry if (!hs->Cref) { PRINT("cache entry not found: protocol error"); return -1; } // // Add random tag to the cache and update timestamp hs->Cref->buf1.SetBuf(brt->buffer,brt->size); hs->Cref->mtime = (kXR_int32)hs->TimeStamp; // // Now serialize the buffer ... char *bser = 0; int nser = buf->Serialized(&bser); // // Update bucket with this content XrdSutBucket *bck = 0;; if (!(bck = bls->GetBucket(type))) { // or create new bucket, if not existing if (!(bck = new XrdSutBucket(bser,nser,type))) { PRINT("error creating bucket " <<" - type: "<AddBucket(bck); } else { bck->Update(bser,nser); } // // Encrypted the bucket if (cip) { if (cip->Encrypt(*bck, useIV) == 0) { PRINT("error encrypting bucket - cipher " <<" - type: "<GetStep(); // Do the right action switch (step) { case kXGS_init: // Process message if (ClientDoInit(br, bm, cmsg) != 0) return -1; break; case kXGS_cert: // Process message if (ClientDoCert(br, bm, cmsg) != 0) return -1; break; case kXGS_pxyreq: // Process message if (ClientDoPxyreq(br, bm, cmsg) != 0) return -1; break; default: cmsg = "protocol error: unknown action: "; cmsg += step; return -1; break; } // We are done return 0; } //_________________________________________________________________________ int XrdSecProtocolgsi::ClientDoInit(XrdSutBuffer *br, XrdSutBuffer **bm, String &emsg) { // Client side: process a kXGS_init message. // Return 0 on success, -1 on error. If the case, a message is returned // in cmsg. EPNAME("ClientDoInit"); // // Create the main buffer as a copy of the buffer received if (!((*bm) = new XrdSutBuffer(br->GetProtocol(),br->GetOptions()))) { emsg = "error instantiating main buffer"; return -1; } // // Extract server version from options String opts = br->GetOptions(); int ii = opts.find("v:"); if (ii >= 0) { String sver(opts,ii+2); sver.erase(sver.find(',')); hs->RemVers = atoi(sver.c_str()); } else { hs->RemVers = Version; emsg = "server version information not found in options:" " assume same as local"; } // Set use IV depending on the remote version useIV = false; if (hs->RemVers >= XrdSecgsiVersDHsigned) { // Supports setting a unique IV in enc/dec operations useIV = true; } // // Create cache if (!(hs->Cref = new XrdSutPFEntry("c"))) { emsg = "error creating cache"; return -1; } // // Save server version in cache hs->Cref->status = hs->RemVers; // // Set options hs->Options = PxyReqOpts; // // Extract list of crypto modules String clist; ii = opts.find("c:"); if (ii >= 0) { clist.assign(opts, ii+2); clist.erase(clist.find(',')); } else { NOTIFY("Crypto list missing: protocol error? (use defaults)"); clist = DefCrypto; } // Parse the list loading the first we can if (ParseCrypto(clist) != 0) { emsg = "cannot find / load crypto requested modules :"; emsg += clist; return -1; } // // Extract server certificate CA hashes String srvca; ii = opts.find("ca:"); if (ii >= 0) { srvca.assign(opts, ii+3); srvca.erase(srvca.find(',')); } // Parse the list loading the first we can if (ParseCAlist(srvca) != 0) { emsg = "unknown CA: cannot verify server certificate"; hs->Chain = 0; return -1; } // // Resolve place-holders in cert, key and proxy file paths, if any if (XrdSutResolve(UsrCert, Entity.host, Entity.vorg, Entity.grps, Entity.name) != 0) { PRINT("Problems resolving templates in "<PxyChain, sessionKsig, hs->Cbck }; if (QueryProxy(1, &cachePxy, "Proxy:0", sessionCF, hs->TimeStamp, &pi, &po) != 0) { emsg = "error getting user proxies"; hs->Chain = 0; return -1; } // Save the result hs->PxyChain = po.chain; hs->Cbck = new XrdSutBucket(*((XrdSutBucket *)(po.cbck))); if (!(sessionKsig = sessionCF->RSA(*(po.ksig)))) { emsg = "could not get a copy of the signing key:"; hs->Chain = 0; return -1; } // // And we are done; return 0; } //_________________________________________________________________________ int XrdSecProtocolgsi::ClientDoCert(XrdSutBuffer *br, XrdSutBuffer **bm, String &emsg) { // Client side: process a kXGS_cert message. // Return 0 on success, -1 on error. If the case, a message is returned // in cmsg. EPNAME("ClientDoCert"); XrdSutBucket *bck = 0; // // make sure the cache is still there if (!hs->Cref) { emsg = "cache entry not found"; hs->Chain = 0; return -1; } // // make sure is not too old int reftime = hs->TimeStamp - TimeSkew; if (hs->Cref->mtime < reftime) { emsg = "cache entry expired"; // Remove: should not be checked a second time SafeDelete(hs->Cref); hs->Chain = 0; return -1; } // // Get from cache version run by server hs->RemVers = hs->Cref->status; // // Extract list of cipher algorithms supported by the server String cip = ""; if ((bck = br->GetBucket(kXRS_cipher_alg))) { String ciplist; bck->ToString(ciplist); // Parse the list int from = 0; while ((from = ciplist.tokenize(cip, from, ':')) != -1) { if (cip.length() > 0) if (sessionCF->SupportedCipher(cip.c_str())) break; cip = ""; } // Must have a common cipher algorithm if (cip.length() <= 0) { emsg = "no common cipher algorithm"; hs->Chain = 0; return -1; } } else { NOTIFY("WARNING: list of ciphers supported by server missing" " - using default"); } // // Extract server certificate if (!(bck = br->GetBucket(kXRS_x509))) { emsg = "server certificate missing"; hs->Chain = 0; return -1; } // // Finalize chain: get a copy of it (we do not touch the reference) hs->Chain = new X509Chain(hs->Chain); if (!(hs->Chain)) { emsg = "cannot duplicate reference chain"; return -1; } // The new chain must be deleted at destruction hs->Options |= kOptsDelChn; // Get hook to parsing function XrdCryptoX509ParseBucket_t ParseBucket = sessionCF->X509ParseBucket(); if (!ParseBucket) { emsg = "cannot attach to ParseBucket function!"; return -1; } // Parse bucket int nci = (*ParseBucket)(bck, hs->Chain); if (nci != 1) { emsg += nci; emsg += " vs 1 expected)"; return -1; } // // Verify the chain x509ChainVerifyOpt_t vopt = {0,static_cast(hs->TimeStamp),-1,hs->Crl}; XrdCryptoX509Chain::EX509ChainErr ecode = XrdCryptoX509Chain::kNone; if (!(hs->Chain->Verify(ecode, &vopt))) { emsg = "certificate chain verification failed: "; emsg += hs->Chain->LastError(); return -1; } // // Verify server identity using RFC2818 method // // First we check the SAN. If the check succeeds then we are all done. // Otherwise, if there is no SAN extension or if trustDNS is in effect, // we check if the common name matches. // DEBUG("Checking cert is for host " <Chain->End()->MatchesSAN(Entity.host, hasSAN)) {if (hasSAN && !TrustDNS) {emsg = "Unable to verify server hostname '"; emsg += wantHost; emsg+= "' using SAN extension; common name fallback disallowed."; return -1; } // If the common name check fails, TrustDNS allows fallback if (!ServerCertNameOK(hs->Chain->End()->Subject(),Entity.host,emsg)) {if (!TrustDNS || Entity.addrInfo == 0 || expectedHost) {emsg = "Unable to verify server hostname '"; emsg += wantHost; emsg+= "' using common name; DNS fallback prohibited."; return -1; } // Use DNS to resolve possible alias name const char *name = Entity.addrInfo->Name(); if (name == NULL) {emsg = "Unable to verify server hostname '"; emsg += wantHost; emsg+= "'; DNS fallback translation failed."; return -1; } DEBUG("TrustDNS: checking if cert is for host " <Chain->End()->Subject(),name,emsg) || (hasSAN && !hs->Chain->End()->MatchesSAN(name,hasSAN)); if (!hostOK) return -1; } } // If we used the DNS then we must prohibit proxy delegation of any kind // if (usedDNS) {if (hs->Options & (kOptsFwdPxy | kOptsSigReq)) {hs->Options &= ~(kOptsFwdPxy | kOptsSigReq); std::cerr <<"secgsi: proxy delegation forbidden when trusting DNS!\n" <RSA(*(hs->Chain->End()->PKI())); if (!sessionKver || !sessionKver->IsValid()) { emsg = "server certificate contains an invalid key"; return -1; } // Move next part to here, after sessionKver set, in order to // verify the signature of DH parameters // // If client supports decoding of signed DH, do sign them if (hs->RemVers >= XrdSecgsiVersDHsigned) { // Extract server public part for session cipher if (!(bck = br->GetBucket(kXRS_cipher))) { emsg = "server public part for session cipher missing"; hs->Chain = 0; return -1; } // Encrypt server DH public parameters with server key if (sessionKver->DecryptPublic(*bck) <= 0) { emsg = "decrypting server DH public parameters"; return -1; } } else { // Extract server public part for session cipher if (!(bck = br->GetBucket(kXRS_puk))) { emsg = "server public part for session cipher missing"; hs->Chain = 0; return -1; } // If the server doesn't provide signed DH parameter, disable proxy delegation if (hs->Options & (kOptsFwdPxy | kOptsSigReq)) { hs->Options &= ~(kOptsFwdPxy | kOptsSigReq); PRINT("no signed DH parameters from " << Entity.host << ". Will not delegate x509 proxy to it"); } } // // Initialize session cipher SafeDelete(sessionKey); if (!(sessionKey = sessionCF->Cipher(hs->HasPad, 0,bck->buffer,bck->size,cip.c_str())) || !(sessionKey->IsValid())) { PRINT("could not instantiate session cipher " "using cipher public info from server"); emsg = "could not instantiate session cipher "; return -1; } // // Communicate the cipher name to server if (hs->RemVers >= XrdSecgsiVersDHsigned) { // Including the length of the IV if supported String cipiv; String::form(cipiv, "%s#%d", cip.c_str(), sessionKey->MaxIVLength()); br->UpdateBucket(cipiv, kXRS_cipher_alg); } else { br->UpdateBucket(cip, kXRS_cipher_alg); } // Deactivate what not needed any longer if (hs->RemVers >= XrdSecgsiVersDHsigned) { br->Deactivate(kXRS_cipher); } else { br->Deactivate(kXRS_puk); } br->Deactivate(kXRS_x509); // // Extract list of MD algorithms supported by the server String md = ""; if ((bck = br->GetBucket(kXRS_md_alg))) { String mdlist; bck->ToString(mdlist); // Parse the list int from = 0; while ((from = mdlist.tokenize(md, from, ':')) != -1) { if (md.length() > 0) if (sessionCF->SupportedMsgDigest(md.c_str())) break; md = ""; } } else { NOTIFY("WARNING: list of digests supported by server missing" " - using default"); md = "sha256"; } if (!(sessionMD = sessionCF->MsgDigest(md.c_str()))) { emsg = "could not instantiate digest object"; return -1; } // Communicate choice to server br->UpdateBucket(md, kXRS_md_alg); // // Extract the main buffer (it contains the random challenge // and will contain our credentials encrypted) XrdSutBucket *bckm = 0; if (!(bckm = br->GetBucket(kXRS_main))) { emsg = "main buffer missing"; return -1; } // // Deserialize main buffer if (!((*bm) = new XrdSutBuffer(bckm->buffer,bckm->size))) { emsg = "error deserializing main buffer"; return -1; } // // And we are done; return 0; } //_________________________________________________________________________ int XrdSecProtocolgsi::ClientDoPxyreq(XrdSutBuffer *br, XrdSutBuffer **bm, String &emsg) { // Client side: process a kXGS_pxyreq message. // Return 0 on success, -1 on error. If the case, a message is returned // in cmsg. XrdSutBucket *bck = 0; // // Extract the main buffer (it contains the random challenge // and will contain our credentials encrypted) XrdSutBucket *bckm = 0; if (!(bckm = br->GetBucket(kXRS_main))) { emsg = "main buffer missing"; return -1; } // // Decrypt the main buffer with the session cipher, if available if (sessionKey) { if (!(sessionKey->Decrypt(*bckm, useIV))) { emsg = "error with session cipher"; return -1; } } // // Deserialize main buffer if (!((*bm) = new XrdSutBuffer(bckm->buffer,bckm->size))) { emsg = "error deserializing main buffer"; return -1; } // // Check if we are ready to proces this if ((hs->Options & kOptsFwdPxy)) { // We have to send the private key of our proxy XrdCryptoX509 *pxy = 0; XrdCryptoRSA *kpxy = 0; if (!(hs->PxyChain) || !(pxy = hs->PxyChain->End()) || !(kpxy = pxy->PKI())) { emsg = "local proxy info missing or corrupted"; return 0; } // Send back the signed request as bucket String pri; if (kpxy->ExportPrivate(pri) != 0) { emsg = "problems exporting private key"; return 0; } // Add it to the main list if ((*bm)->AddBucket(pri, kXRS_x509) != 0) { emsg = "problem adding bucket with private key to main buffer"; return 0; } } else { // Proxy request: check if we are allowed to sign it if (!(hs->Options & kOptsSigReq)) { emsg = "Not allowed to sign proxy requests"; return 0; } // Get the request if (!(bck = (*bm)->GetBucket(kXRS_x509_req))) { emsg = "bucket with proxy request missing"; return 0; } XrdCryptoX509Req *req = sessionCF->X509Req(bck); if (!req) { emsg = "could not resolve proxy request"; return 0; } req->SetVersion(hs->RemVers); // Get our proxy and its private key XrdCryptoX509 *pxy = 0; XrdCryptoRSA *kpxy = 0; if (!(hs->PxyChain) || !(pxy = hs->PxyChain->End()) || !(kpxy = pxy->PKI())) { emsg = "local proxy info missing or corrupted"; return 0; } // Sign the request XrdCryptoX509SignProxyReq_t X509SignProxyReq = (sessionCF) ? sessionCF->X509SignProxyReq() : 0; if (!X509SignProxyReq) { emsg = "problems getting method to sign request"; return 0; } XrdCryptoX509 *npxy = 0; if ((*X509SignProxyReq)(pxy, kpxy, req, &npxy) != 0) { emsg = "problems signing the request"; return 0; } delete req; (*bm)->Deactivate(kXRS_x509_req); // Send back the signed request as bucket if ((bck = npxy->Export())) { // Add it to the main list if ((*bm)->AddBucket(bck) != 0) { emsg = "problem adding signed request to main buffer"; return 0; } } delete npxy; // has been allocated in *X509SignProxyReq } // // And we are done; return 0; } //_________________________________________________________________________ int XrdSecProtocolgsi::ParseServerInput(XrdSutBuffer *br, XrdSutBuffer **bm, String &cmsg) { // Parse received buffer b, extracting and decrypting the main // buffer *bm and extracting the session // cipher, random tag buckets and user name, if any. // Results used to fill the local handshake variables EPNAME("ParseServerInput"); // Space for pointer to main buffer must be already allocated if (!br || !bm) { PRINT("invalid inputs ("<GetStep(); // Do the right action switch (step) { case kXGC_certreq: // Process message if (ServerDoCertreq(br, bm, cmsg) != 0) return -1; break; case kXGC_cert: // Process message if (ServerDoCert(br, bm, cmsg) != 0) return -1; break; case kXGC_sigpxy: // Process message if (ServerDoSigpxy(br, bm, cmsg) != 0) return -1; break; default: cmsg = "protocol error: unknown action: "; cmsg += step; return -1; break; } // // We are done return 0; } //_________________________________________________________________________ int XrdSecProtocolgsi::ServerDoCertreq(XrdSutBuffer *br, XrdSutBuffer **bm, String &cmsg) { // Server side: process a kXGC_certreq message. // Return 0 on success, -1 on error. If the case, a message is returned // in cmsg. XrdSutCERef ceref; XrdSutBucket *bck = 0; XrdSutBucket *bckm = 0; // // Get version run by client, if there if (br->UnmarshalBucket(kXRS_version,hs->RemVers) != 0) { hs->RemVers = Version; cmsg = "client version information not found in options:" " assume same as local"; } else { br->Deactivate(kXRS_version); } // Reset use IV; will be set in next round depending on the remote version useIV = false; // // Extract the main buffer if (!(bckm = br->GetBucket(kXRS_main))) { cmsg = "main buffer missing"; return -1; } // // Extract bucket with crypto module if (!(bck = br->GetBucket(kXRS_cryptomod))) { cmsg = "crypto module specification missing"; return -1; } String cmod; bck->ToString(cmod); // Parse the list loading the first we can if (ParseCrypto(cmod) != 0) { cmsg = "cannot find / load crypto requested module :"; cmsg += cmod; return -1; } // // Extract bucket with client issuer hash if (!(bck = br->GetBucket(kXRS_issuer_hash))) { cmsg = "client issuer hash missing"; return -1; } String cahash; bck->ToString(cahash); // // Check if we know it if (ParseCAlist(cahash) != 0) { cmsg = "unknown CA: cannot verify client credentials"; return -1; } // Find our certificate in cache String cadum; XrdSutCacheEntry *cent = GetSrvCertEnt(ceref, sessionCF, hs->TimeStamp, cadum); if (!cent) { cmsg = "cannot find certificate: corruption?"; return -1; } // Fill some relevant handshake variables sessionKsig = sessionCF->RSA(*((XrdCryptoRSA *)(cent->buf2.buf))); hs->Cbck = new XrdSutBucket(*((XrdSutBucket *)(cent->buf3.buf))); ceref.UnLock(); // Create a handshake cache if (!(hs->Cref = new XrdSutPFEntry(hs->ID.c_str()))) { cmsg = "cannot create cache entry"; return -1; } // // Deserialize main buffer if (!((*bm) = new XrdSutBuffer(bckm->buffer,bckm->size))) { cmsg = "error deserializing main buffer"; return -1; } // Deactivate what not need any longer br->Deactivate(kXRS_issuer_hash); // // Get options, if any if (br->UnmarshalBucket(kXRS_clnt_opts, hs->Options) == 0) br->Deactivate(kXRS_clnt_opts); // We are done return 0; } //_________________________________________________________________________ int XrdSecProtocolgsi::ServerDoCert(XrdSutBuffer *br, XrdSutBuffer **bm, String &cmsg) { // Server side: process a kXGC_cert message. // Return 0 on success, -1 on error. If the case, a message is returned // in cmsg. EPNAME("ServerDoCert"); XrdSutBucket *bck = 0; XrdSutBucket *bckm = 0; // // Extract the main buffer if (!(bckm = br->GetBucket(kXRS_main))) { cmsg = "main buffer missing"; return -1; } // // Extract cipher algorithm chosen by the client int lenIV = 0; String cip = ""; if ((bck = br->GetBucket(kXRS_cipher_alg))) { bck->ToString(cip); // Extract IV length, if any int piv = cip.find('#'); if (piv >= 0) { String siv(cip, piv+1); if (siv.isdigit()) lenIV = siv.atoi(); cip.erase(piv); } // Parse the list if (DefCipher.find(cip) == -1) { cmsg = "unsupported cipher chosen by the client"; hs->Chain = 0; return -1; } // Deactivate the bucket br->Deactivate(kXRS_cipher_alg); } else { NOTIFY("WARNING: client choice for cipher missing" " - using default"); } XrdOucString cpub; if (hs->RemVers >= XrdSecgsiVersDHsigned) { // Supports setting a unique IV in enc/dec operations useIV = true; // First get the client public key if (!(bck = br->GetBucket(kXRS_puk))) { cmsg = "bucket with client public key missing"; return -1; } bck->ToString(cpub); sessionKver = sessionCF->RSA(cpub.c_str(), cpub.length()); if (!sessionKver || !sessionKver->IsValid()) { cmsg = "bucket with client public key contains an invalid key"; return -1; } // Get the client DH parameters if (!(bck = br->GetBucket(kXRS_cipher))) { cmsg = "bucket with client DH parameters missing"; return -1; } // Decrypt client DH public parameters with client key if (sessionKver->DecryptPublic(*bck) <= 0) { cmsg = "decrypting client DH public parameters"; return -1; } } else { // Get the client DH parameters if (!(bck = br->GetBucket(kXRS_puk))) { cmsg = "bucket with client DH parameters missing"; return -1; } // If the client doesn't provide signed DH parameter, disable proxy delegation if ((PxyReqOpts & kOptsSrvReq) || hs->Options & (kOptsDlgPxy | kOptsSigReq | kOptsFwdPxy)) PRINT("no signed DH parameters from client:" << Entity.tident << " : will not delegate x509 proxy to it"); if ((PxyReqOpts & kOptsSrvReq)) PxyReqOpts &= ~kOptsSrvReq; if (hs->Options & (kOptsDlgPxy | kOptsSigReq | kOptsFwdPxy)) hs->Options &= ~(kOptsDlgPxy | kOptsSigReq | kOptsFwdPxy); } // Get the session cipher if (bck) { // // Cleanup SafeDelete(sessionKey); // // Prepare cipher agreement: make sure we have the reference cipher if (!hs->Rcip) { cmsg = "reference cipher missing"; hs->Chain = 0; return -1; } sessionKey = hs->Rcip; // // Instantiate the session cipher if (!(sessionKey->Finalize(hs->HasPad,bck->buffer,bck->size,cip.c_str()))) { cmsg = "cannot finalize session cipher"; hs->Chain = 0; return -1; } // Set IV length, if any if (lenIV > 0) sessionKey->SetIV(lenIV, (const char *)0); } else { cmsg = "bucket with DH parameters not found or invalid: cannot finalize session cipher"; return -1; } // // We need it only once if (hs->RemVers >= XrdSecgsiVersDHsigned) br->Deactivate(kXRS_cipher); br->Deactivate(kXRS_puk); // // Decrypt the main buffer with the session cipher, if available if (sessionKey) { if (!(sessionKey->Decrypt(*bckm, useIV))) { cmsg = "error decrypting main buffer with session cipher"; hs->Chain = 0; return -1; } } // // Deserialize main buffer if (!((*bm) = new XrdSutBuffer(bckm->buffer,bckm->size))) { cmsg = "error deserializing main buffer"; hs->Chain = 0; return -1; } // // Get version run by client, if there if (hs->RemVers == -1) { if ((*bm)->UnmarshalBucket(kXRS_version,hs->RemVers) != 0) { hs->RemVers = Version; cmsg = "client version information not found in options:" " assume same as local"; } else { (*bm)->Deactivate(kXRS_version); } } // // Get cache entry if (!hs->Cref) { cmsg = "session cache has gone"; hs->Chain = 0; return -1; } // // make sure cache is not too old int reftime = hs->TimeStamp - TimeSkew; if (hs->Cref->mtime < reftime) { cmsg = "cache entry expired"; SafeDelete(hs->Cref); hs->Chain = 0; return -1; } // // Extract the client certificate if (!(bck = (*bm)->GetBucket(kXRS_x509))) { cmsg = "client certificate missing"; SafeDelete(hs->Cref); hs->Chain = 0; return -1; } // // Finalize chain: get a copy of it (we do not touch the reference) hs->Chain = new X509Chain(hs->Chain); if (!(hs->Chain)) { cmsg = "cannot duplicate reference chain"; return -1; } // The new chain must be deleted at destruction hs->Options |= kOptsDelChn; // Get hook to parsing function XrdCryptoX509ParseBucket_t ParseBucket = sessionCF->X509ParseBucket(); if (!ParseBucket) { cmsg = "cannot attach to ParseBucket function!"; return -1; } // Parse bucket int nci = (*ParseBucket)(bck, hs->Chain); if (nci < 2) { cmsg = "wrong number of certificates in received bucket ("; cmsg += nci; cmsg += " > 1 expected)"; return -1; } // // Verify the chain x509ChainVerifyOpt_t vopt = {0,static_cast(hs->TimeStamp),-1,hs->Crl}; XrdCryptoX509Chain::EX509ChainErr ecode = XrdCryptoX509Chain::kNone; if (!(hs->Chain->Verify(ecode, &vopt))) { cmsg = "certificate chain verification failed: "; cmsg += hs->Chain->LastError(); return -1; } // // Extract the client public key from the certificate XrdCryptoRSA *ckey = sessionCF->RSA(*(hs->Chain->End()->PKI())); if (!ckey || !ckey->IsValid()) { cmsg = "client certificate contains an invalid key"; return -1; } if (hs->RemVers >= XrdSecgsiVersDHsigned) { // For new clients, make sure it is the same we got from the bucket XrdOucString cpubcert; if ((ckey->ExportPublic(cpubcert) < 0)) { cmsg = "exporting client public key"; return -1; } if (cpubcert != cpub) { cmsg = "client public key does not match the one from the bucket!"; return -1; } delete ckey; } else { // For old clients, set the client public key from the certificate sessionKver = ckey; } // Deactivate certificate buffer (*bm)->Deactivate(kXRS_x509); // // Check if there will be delegated proxies; these can be through // normal request+signature, or just forwarded by the client. // In both cases we need to save the proxy chain. If we need a // request, we have to prepare it and send it back to the client. // Get hook to parsing function XrdCryptoX509CreateProxyReq_t X509CreateProxyReq = sessionCF->X509CreateProxyReq(); if (!X509CreateProxyReq) { cmsg = "cannot attach to X509CreateProxyReq function!"; return -1; } bool needReq = ((PxyReqOpts & kOptsSrvReq) && (hs->Options & kOptsSigReq)) || (hs->Options & kOptsDlgPxy); if (needReq || (hs->Options & kOptsFwdPxy)) { // Create a new proxy chain hs->PxyChain = new X509Chain(); // Add the current proxy if ((*ParseBucket)(bck, hs->PxyChain) > 1) { // Reorder it hs->PxyChain->Reorder(); if (needReq) { // Create the request XrdCryptoX509Req *rPXp = (XrdCryptoX509Req *) &(hs->RemVers); XrdCryptoRSA *krPXp = 0; if ((*X509CreateProxyReq)(hs->PxyChain->End(), &rPXp, &krPXp) == 0) { // Save key in the cache hs->Cref->buf4.buf = (char *)krPXp; // Prepare export bucket for request XrdSutBucket *bckr = rPXp->Export(); // Add it to the main list if ((*bm)->AddBucket(bckr) != 0) { SafeDelete(hs->PxyChain); NOTIFY("WARNING: proxy req: problem adding bucket to main buffer"); } delete rPXp; } else { SafeDelete(hs->PxyChain); NOTIFY("WARNING: proxy req: problem creating request"); } } } else { SafeDelete(hs->PxyChain); NOTIFY("WARNING: proxy req: wrong number of certificates"); } } // // Extract the MD algorithm chosen by the client String md = ""; if ((bck = br->GetBucket(kXRS_md_alg))) { String mdlist; bck->ToString(md); // Parse the list if (DefMD.find(md) == -1) { cmsg = "unsupported MD chosen by the client"; return -1; } // Deactivate br->Deactivate(kXRS_md_alg); } else { NOTIFY("WARNING: client choice for digests missing" " - using default"); md = "md5"; } if (!(sessionMD = sessionCF->MsgDigest(md.c_str()))) { cmsg = "could not instantiate digest object"; return -1; } // We are done return 0; } //_________________________________________________________________________ int XrdSecProtocolgsi::ServerDoSigpxy(XrdSutBuffer *br, XrdSutBuffer **bm, String &cmsg) { // Server side: process a kXGC_sigpxy message. // Return 0 on success, -1 on error. If the case, a message is returned // in cmsg. EPNAME("ServerDoSigpxy"); XrdSutBucket *bck = 0; XrdSutBucket *bckm = 0; // // Extract the main buffer if (!(bckm = br->GetBucket(kXRS_main))) { cmsg = "main buffer missing"; return 0; } // // Decrypt the main buffer with the session cipher, if available if (sessionKey) { if (!(sessionKey->Decrypt(*bckm, useIV))) { cmsg = "error decrypting main buffer with session cipher"; return 0; } } // // Deserialize main buffer if (!((*bm) = new XrdSutBuffer(bckm->buffer,bckm->size))) { cmsg = "error deserializing main buffer"; return 0; } // Get the bucket if (!(bck = (*bm)->GetBucket(kXRS_x509))) { cmsg = "buffer with requested info missing"; // Is there a message from the client? if (!(bck = (*bm)->GetBucket(kXRS_message))) { // Yes: decode it and print it String m; bck->ToString(m); DEBUG("msg from client: "<PxyChain; if (!pxyc) { cmsg = "the proxy chain is gone"; return 0; } // Action depend on the type of message if ((hs->Options & kOptsFwdPxy)) { // The bucket contains a private key to be added to the proxy // public key XrdCryptoRSA *kpx = pxyc->End()->PKI(); if (kpx->ImportPrivate(bck->buffer, bck->size) != 0) { cmsg = "problems importing private key"; return 0; } } else { // The bucket contains our request signed by the client // The full key is in the cache if (!hs->Cref) { cmsg = "session cache has gone"; return 0; } // Get the signed certificate XrdCryptoX509 *npx = sessionCF->X509(bck); if (!npx) { cmsg = "could not resolve signed request"; return 0; } // Set full PKI XrdCryptoRSA *knpx = (XrdCryptoRSA *)(hs->Cref->buf4.buf); npx->SetPKI((XrdCryptoX509data)(knpx->Opaque())); // Add the new proxy ecert to the chain pxyc->PushBack(npx); } // Save the chain in the instance proxyChain = pxyc; hs->PxyChain = 0; // Notify if (QTRACE(Authen)) { proxyChain->Dump(); } // Check if the proxy chain is to become the actual credentials // if ((PxyReqOpts & kOptsPxCred)) { XrdCryptoX509ExportChain_t c2mem = (sessionCF) ? sessionCF->X509ExportChain() : 0; if (!c2mem) { cmsg = "chain exporter not found; proxy chain not exported"; return 0; } XrdOucString spxy; XrdSutBucket *bpxy = (*c2mem)(proxyChain, true); bpxy->ToString(spxy); if (Entity.credslen > 0) SafeFree(Entity.creds); Entity.creds = strdup(spxy.c_str()); Entity.credslen = spxy.length(); DEBUG("proxy chain exported in Entity.creds (" << Entity.credslen << " bytes)"); DEBUG("\n\n" << spxy.c_str() << "\n\n"); delete bpxy; return 0; } // // Extract user login name, if any String user; if ((bck = (*bm)->GetBucket(kXRS_user))) { bck->ToString(user); (*bm)->Deactivate(kXRS_user); } if (user.length() <= 0) user = Entity.name; // Dump to file if required if ((PxyReqOpts & kOptsPxFile)) { if (user.length() > 0) { String pxfile = UsrProxy, name; struct passwd *pw = getpwnam(user.c_str()); if (pw) { name = pw->pw_name; } else { // Get Hash of the subject XrdCryptoX509 *c = proxyChain->SearchBySubject(proxyChain->EECname()); if (c) { name = c->SubjectHash(); } else { cmsg = "proxy chain not dumped to file: could not find subject hash"; return 0; } } if (XrdSutResolve(pxfile, Entity.host, Entity.vorg, Entity.grps, name.c_str()) != 0) { PRINT("Problems resolving templates in "< placeholder if (pw && pxfile.find("") != STR_NPOS) { String suid; suid += (int) pw->pw_uid; pxfile.replace("", suid.c_str()); } // Get the function XrdCryptoX509ChainToFile_t ctofile = sessionCF->X509ChainToFile(); if ((*ctofile)(proxyChain,pxfile.c_str()) != 0) { cmsg = "problems dumping proxy chain to file "; cmsg += pxfile; return 0; } PRINT("proxy chain dumped to "<< pxfile); } else { cmsg = "proxy chain not dumped to file: entity name undefined"; return 0; } } // We are done return 0; } //__________________________________________________________________ void XrdSecProtocolgsi::ErrF(XrdOucErrInfo *einfo, kXR_int32 ecode, const char *msg1, const char *msg2, const char *msg3) { // Filling the error structure EPNAME("ErrF"); char *msgv[12]; int k, i = 0, sz = strlen("Secgsi"); // // Code message, if any int cm = (ecode >= kGSErrParseBuffer && ecode <= kGSErrError) ? (ecode-kGSErrParseBuffer) : -1; const char *cmsg = (cm > -1) ? gGSErrStr[cm] : 0; // // Build error message array msgv[i++] = (char *)"Secgsi"; //0 if (cmsg) {msgv[i++] = (char *)": "; //1 msgv[i++] = (char *)cmsg; //2 sz += strlen(msgv[i-1]) + 2; } if (msg1) {msgv[i++] = (char *)": "; //3 msgv[i++] = (char *)msg1; //4 sz += strlen(msgv[i-1]) + 2; } if (msg2) {msgv[i++] = (char *)": "; //5 msgv[i++] = (char *)msg2; //6 sz += strlen(msgv[i-1]) + 2; } if (msg3) {msgv[i++] = (char *)": "; //7 msgv[i++] = (char *)msg3; //8 sz += strlen(msgv[i-1]) + 2; } // save it (or print it) if (einfo) { einfo->setErrInfo(ecode, (const char **)msgv, i); } if (QTRACE(Debug)) { char *bout = new char[sz+10]; if (bout) { bout[0] = 0; for (k = 0; k < i; k++) strcat(bout, msgv[k]); DEBUG(bout); } else { for (k = 0; k < i; k++) DEBUG(msgv[k]); } } } //__________________________________________________________________ XrdSecCredentials *XrdSecProtocolgsi::ErrC(XrdOucErrInfo *einfo, XrdSutBuffer *b1, XrdSutBuffer *b2, XrdSutBuffer *b3, kXR_int32 ecode, const char *msg1, const char *msg2, const char *msg3) { // Error logging client method // Fill the error structure ErrF(einfo, ecode, msg1, msg2, msg3); // Release buffers REL3(b1,b2,b3); // We are done return (XrdSecCredentials *)0; } //__________________________________________________________________ int XrdSecProtocolgsi::ErrS(String ID, XrdOucErrInfo *einfo, XrdSutBuffer *b1, XrdSutBuffer *b2, XrdSutBuffer *b3, kXR_int32 ecode, const char *msg1, const char *msg2, const char *msg3) { // Error logging server method // Fill the error structure ErrF(einfo, ecode, msg1, msg2, msg3); // Release buffers REL3(b1,b2,b3); // We are done return kgST_error; } //______________________________________________________________________________ bool XrdSecProtocolgsi::CheckRtag(XrdSutBuffer *bm, String &emsg) { // Check random tag signature if it was sent with previous packet EPNAME("CheckRtag"); // Make sure we got a buffer if (!bm) { emsg = "Buffer not defined"; return 0; } // // If we sent out a random tag check its signature if (hs->Cref && hs->Cref->buf1.len > 0) { XrdSutBucket *brt = 0; if ((brt = bm->GetBucket(kXRS_signed_rtag))) { // Make sure we got the right key to decrypt if (!(sessionKver)) { emsg = "Session cipher undefined"; return 0; } // Decrypt it with the counter part public key if (sessionKver->DecryptPublic(*brt) <= 0) { emsg = "error decrypting random tag with public key"; return 0; } } else { emsg = "random tag missing - protocol error"; return 0; } // // Random tag cross-check: content if (memcmp(brt->buffer,hs->Cref->buf1.buf,hs->Cref->buf1.len)) { emsg = "random tag content mismatch"; SafeDelete(hs->Cref); // Remove: should not be checked a second time return 0; } // // Reset the cache entry but we will not use the info a second time memset(hs->Cref->buf1.buf,0,hs->Cref->buf1.len); hs->Cref->buf1.SetBuf(); // // Flag successful check hs->RtagOK = 1; bm->Deactivate(kXRS_signed_rtag); DEBUG("Random tag successfully checked"); } else { DEBUG("Nothing to check"); } // We are done return 1; } //______________________________________________________________________________ XrdCryptoX509Crl *XrdSecProtocolgsi::LoadCRL(XrdCryptoX509 *xca, const char *subjhash, XrdCryptoFactory *CF, int dwld, int &errcrl) { // Scan crldir for a valid CRL certificate associated to CA whose // certificate is xca. If 'dwld' is true try to download the CRL from // the relevant URI, if any. // If the CRL is found and is valid according // to the chosen option, return its content in a X509Crl object. // Return 0 in any other case EPNAME("LoadCRL"); XrdCryptoX509Crl *crl = 0; errcrl = 0; // make sure we got what we need if (!xca || !CF) { PRINT("Invalid inputs"); errcrl = -1; return crl; } // Get the CA hash String cahash(subjhash); int hashalg = 0; if (strcmp(subjhash, xca->SubjectHash())) hashalg = 1; // Drop the extension (".0") String caroot(cahash, 0, cahash.find(".0")-1); // The dir String crlext = XrdSecProtocolgsi::DefCRLext; String crldir; int from = 0; while ((from = CRLdir.tokenize(crldir, from, ',')) != -1) { if (crldir.length() <= 0) continue; // Add the default CRL extension and the dir String crlfile = crldir + caroot; crlfile += crlext; DEBUG("target file: "<X509Crl(crlfile.c_str()))) { if ((errcrl = VerifyCRL(crl, xca, crldir, CF, hashalg)) == 0) return crl; } SafeDelete(crl); } // If not required, we are done if (CRLCheck < 2 || (dwld == 0)) { // Done return crl; } // If in 'required' mode, we will also try to load the CRL from the // information found in the CA certificate or in the certificate directory. // To avoid this overload, the CRL information should be installed offline, e.g. with // utils/getCRLcert errcrl = 0; // Try to retrieve it from the URI in the CA certificate, if any if ((crl = CF->X509Crl(xca))) { if ((errcrl = VerifyCRL(crl, xca, crldir, CF, hashalg)) == 0) return crl; SafeDelete(crl); } // Finally try the ".crl_url" file from = 0; while ((from = CRLdir.tokenize(crldir, from, ',')) != -1) { if (crldir.length() <= 0) continue; SafeDelete(crl); String crlurl = crldir + caroot; crlurl += ".crl_url"; DEBUG("target file: "<X509Crl(line, 1))) { if ((errcrl = VerifyCRL(crl, xca, crldir, CF, hashalg)) == 0) return crl; SafeDelete(crl); } } } // We need to parse the full dirs: make some cleanup first from = 0; while ((from = CRLdir.tokenize(crldir, from, ',')) != -1) { if (crldir.length() <= 0) continue; SafeDelete(crl); // Open directory DIR *dd = opendir(crldir.c_str()); if (!dd) { PRINT("could not open directory: "<d_name)) continue; // File name contain the root CA hash if (!strstr(dent->d_name,caroot.c_str())) continue; // candidate name String crlfile = crldir + dent->d_name; DEBUG("analysing entry "<X509Crl(crlfile.c_str()))) { if ((errcrl = VerifyCRL(crl, xca, crldir, CF, hashalg)) == 0) break; SafeDelete(crl); } } // Close dir closedir(dd); // Are we done? if (crl) break; } // We are done return crl; } //______________________________________________________________________________ int XrdSecProtocolgsi::VerifyCRL(XrdCryptoX509Crl *crl, XrdCryptoX509 *xca, String crldir, XrdCryptoFactory *CF, int hashalg) { EPNAME("VerifyCRL"); int rc = 0; // Make sure they have the same issuer if (!strcmp(xca->SubjectHash(hashalg), crl->IssuerHash(hashalg))) { // Signing certificate file String casigfile = crldir + crl->IssuerHash(hashalg); DEBUG("CA signing certificate file = "<X509(casigfile.c_str()))) { if (CRLCheck >= 2) { PRINT("CA certificate to verify the signature ("<IssuerHash(hashalg)<< ") could not be loaded - exit"); } else { DEBUG("CA certificate to verify the signature could not be loaded - verification skipped"); } rc = -3; } else { // Verify signature if (crl->Verify(xcasig)) { // Ok, we are done if (CRLCheck >= 3 && crl && crl->IsExpired()) { rc = -5; NOTIFY("CRL is expired (CRLCheck: "<SubjectHash(hashalg)<< " does not match CRL issuer "<IssuerHash(hashalg)<<"! "); } return rc; } //______________________________________________________________________________ String XrdSecProtocolgsi::GetCApath(const char *cahash) { // Look in the paths defined by CAdir for the certificate file related to // 'cahash', in the form /.0 String path; String ent; int from = 0; while ((from = CAdir.tokenize(ent, from, ',')) != -1) { if (ent.length() > 0) { path = ent; if (!path.endswith('/')) path += "/"; path += cahash; if (!path.endswith(".0")) path += ".0"; if (!access(path.c_str(), R_OK)) break; } path = ""; } // Done return path; } //______________________________________________________________________________ bool XrdSecProtocolgsi::VerifyCA(int opt, X509Chain *cca, XrdCryptoFactory *CF) { // Verify the CA in 'cca' according to 'opt': // opt = 2 full check // 1 only if self-signed // 0 no check EPNAME("VerifyCA"); bool verified = 0; XrdCryptoX509Chain::ECAStatus st = XrdCryptoX509Chain::kUnknown; cca->SetStatusCA(st); // We nust have got a chain if (!cca) { PRINT("Invalid input "); return 0; } // Get the parse function XrdCryptoX509ParseFile_t ParseFile = CF->X509ParseFile(); if (!ParseFile) { PRINT("Cannot attach to the ParseFile function"); return 0; } // Point to the certificate XrdCryptoX509 *xc = cca->Begin(); if (!xc) { PRINT("Cannot attach to first certificate in chain"); return 0; } // Make sure it is valid if (!(xc->IsValid())) { PRINT("CA certificate is expired ("<SubjectHash()<<", not_before: "<NotBefore()<<" secs UTC )"); return 0; } // Is it self-signed ? bool self = (!strcmp(xc->IssuerHash(), xc->SubjectHash())) ? 1 : 0; if (!self) { String inam; if (opt == 2) { // We are requested to verify it bool notdone = 1; // We need to load the issuer(s) CA(s) XrdCryptoX509 *xd = xc; while (notdone) { X509Chain *ch = 0; int ncis = -1; for (int ha = 0; ha < 2; ha++) { inam = GetCApath(xd->IssuerHash(ha)); if (inam.length() <= 0) continue; ch = new X509Chain(); ncis = (*ParseFile)(inam.c_str(), ch); if (ncis >= 1) break; SafeDelete(ch); } if (ncis < 1) break; XrdCryptoX509 *xi = ch->Begin(); while (xi) { if (!strcmp(xd->IssuerHash(), xi->SubjectHash())) break; xi = ch->Next(); } if (xi) { // Add the certificate to the requested CA chain ch->Remove(xi); cca->PutInFront(xi); SafeDelete(ch); // We may be over if (!strcmp(xi->IssuerHash(), xi->SubjectHash())) { notdone = 0; break; } else { // This becomes the daughter xd = xi; } } else { break; } } if (!notdone) { // Verify the chain X509Chain::EX509ChainErr e; x509ChainVerifyOpt_t vopt = {kOptsCheckSubCA, 0, -1, 0}; if (!(verified = cca->Verify(e, &vopt))) PRINT("CA certificate not self-signed: verification failed for '"<SubjectHash()<<"': error: "<< cca->X509ChainError(e)); } else { PRINT("CA certificate not self-signed: cannot verify integrity ("<SubjectHash()<<")"); } } else { // Fill CA information cca->CheckCA(0); // Set OK in any case verified = 1; // Notify if some sort of check was required if (opt == 1) { NOTIFY("Warning: CA certificate not self-signed and" " integrity not checked: assuming OK ("<SubjectHash()<<")"); } } } else { if (CACheck > 0) { // Check self-signature if (!(verified = cca->CheckCA())) PRINT("CA certificate self-signed: integrity check failed ("<SubjectHash()<<")"); } else { // Set OK in any case verified = 1; // Notify if some sort of check was required NOTIFY("Warning: CA certificate self-signed but" " integrity not checked: assuming OK ("<SubjectHash()<<")"); } } // Set the status in the chain st = (verified) ? XrdCryptoX509Chain::kValid : st; cca->SetStatusCA(st); // Done return verified; } //_____________________________________________________________________________ static bool GetCACheck(XrdSutCacheEntry *e, void *a) { EPNAME("GetCACheck"); int crl_check = (*((XrdSutCacheArg_t *)a)).arg1; int crl_refresh = (*((XrdSutCacheArg_t *)a)).arg2; time_t ts_ref = (time_t)(*((XrdSutCacheArg_t *)a)).arg3; if (!e) return false; X509Chain *chain = 0; // If we had already something, check it, as we may be done bool goodca = 0; if ((chain = (X509Chain *)(e->buf1.buf))) { // Check the validity of the certificates in the chain; if a certificate became invalid, // we need to reload a valid one for the same CA. if (chain->CheckValidity() == 0) { goodca = 1; } else { PRINT("CA entry for '"<name<<"' needs refreshing: clean the related entry cache first"); return false; } } if (goodca) { XrdCryptoX509Crl *crl = (XrdCryptoX509Crl *)(e->buf2.buf); bool goodcrl = 1; if ((crl_check == 2 && !crl) || (crl_check == 3 && crl->IsExpired())) goodcrl = 0; if (crl_refresh > 0 && ((ts_ref - e->mtime) > crl_refresh)) goodcrl = 0; if (goodcrl) { return true; } else if (crl) { PRINT("CRL entry for '"<name<<"' needs refreshing: clean the related entry cache first ("</.0 . // If 'hs' is defined, store pointers to chain and crl into 'hs'. // Return 0 if ok, -1 if not available, -2 if CRL not ok EPNAME("GetCA"); XrdSutCERef ceref; int rc = 0; // We nust have got a CA hash if (!cahash || !cf) { PRINT("Invalid input "); return -1; } // Timestamp time_t timestamp = (hs) ? hs->TimeStamp : time(0); // The tag String tag(cahash,20); tag += ':'; tag += cf->ID(); DEBUG("Querying cache for tag: "<rwmtx)); // Point to the content X509Chain *chain = (X509Chain *)(cent->buf1.buf); XrdCryptoX509Crl *crl = (XrdCryptoX509Crl *)(cent->buf2.buf); // If invalid we fail if (cent->status == kCE_inactive) { // Cleanup and remove existing invalid entries if (chain) stackCA.Del(chain); if (crl) stackCRL.Del(crl); PRINT("unable to get a valid entry from cache for " << tag); return -1; } // Check if we are done if (rdlock) { // Save chain if (hs) hs->Chain = chain; stackCA.Add(chain); // Save crl if (crl) { if (hs) hs->Crl = crl; // Add to the stack for proper cleaning of invalidated CRLs stackCRL.Add(crl); } return 0; } // Cleanup and remove existing invalid entries if (chain) stackCA.Del(chain); if (crl) stackCRL.Del(crl); chain = 0; crl = 0; cent->buf1.buf = 0; cent->buf2.buf = 0; // If not, prepare the file name String fnam = GetCApath(cahash); DEBUG("trying to load CA certificate from "<Chain) ? 0 : 1; chain = (createchain) ? new X509Chain() : hs->Chain; if (!chain) { PRINT("could not attach-to or create new GSI chain"); rc = -1; } // Get the parse function XrdCryptoX509ParseFile_t ParseFile = cf->X509ParseFile(); if (rc == 0 && ParseFile) { int nci = (createchain) ? (*ParseFile)(fnam.c_str(), chain) : 1; bool ok = 0, verified = 0; if (nci == 1) { // Verify the CA verified = VerifyCA(CACheck, chain, cf); XrdCryptoX509Crl *crl = 0; if (verified) { // Get CRL, if required ok = 1; if (CRLCheck > 0) { int errcrl = 0; if ((crl = LoadCRL(chain->EffCA(), cahash, cf, CRLDownload, errcrl))) { // Good CA DEBUG("CRL successfully loaded"); } else { String em = "missing or expired: ignoring"; if ((CRLCheck == 1 && errcrl != 0 && errcrl != -5) || (CRLCheck >= 2 && errcrl != 0)) { ok = 0; em = "invalid: failing"; } else if (CRLCheck >= 2) { ok = 0; em = "missing or expired: failing"; } NOTIFY("CRL is "<buf1.buf = (char *)(chain); cent->buf1.len = 0; // Just a flag stackCA.Add(chain); if (crl) { cent->buf2.buf = (char *)(crl); cent->buf2.len = 0; // Just a flag stackCRL.Add(crl); } cent->mtime = timestamp; cent->status = kCE_ok; cent->cnt = 0; // Fill output, if required if (hs) { hs->Chain = chain; hs->Crl = crl; if (strcmp(cahash, chain->Begin()->SubjectHash())) hs->HashAlg = 1; } } else { SafeDelete(crl); SafeDelete(chain); rc = -2; } } else { SafeDelete(chain); NOTIFY("certificate not found or invalid (nci: "<key, &st) != 0) { DEBUG("cannot access private key file: "<key); return 1; } if (!S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || (st.st_mode & (S_IWGRP | S_IWOTH)) != 0 || (st.st_mode & (S_IRGRP | S_IROTH)) != 0) { DEBUG("wrong permissions for file: "<key<< " (should be 0600)"); return 1; } // // Validity int valid = (pi->valid) ? XrdSutParseTime(pi->valid, 1) : -1; // // Options XrdProxyOpt_t pxopt = {pi->bits, // bits in key valid, // duration validity in secs pi->deplen}; // signature path depth // // Init now XrdCryptoX509CreateProxy_t X509CreateProxy = cf->X509CreateProxy(); if (!X509CreateProxy) { PRINT("cannot attach to X509CreateProxy function!"); return 1; } rc = (*X509CreateProxy)(pi->cert, pi->key, &pxopt, ch, kp, pi->out); #else // command string String cmd(kMAXBUFLEN); // Check if GLOBUS_LOCATION is defined if (getenv("GLOBUS_LOCATION")) cmd = "source $GLOBUS_LOCATION/etc/globus-user-env.sh;"; // Add main command cmd += " grid-proxy-init"; // Add user cert cmd += " -cert "; cmd += pi->cert; // Add user key cmd += " -key "; cmd += pi->key; // Add CA dir (no support for multi-dirs) String cdir(pi->certdir); cdir.erase(cdir.find(',')); cmd += " -certdir "; cmd += cdir; // Add validity if (pi->valid) { cmd += " -valid "; cmd += pi->valid; } // Add number of bits in key if (pi->bits > 512) { cmd += " -bits "; cmd += pi->bits; } // Add depth of signature path if (pi->deplen > -1) { cmd += " -path-length "; cmd += pi->deplen; } // Add output proxy coordinates if (pi->out) { cmd += " -out "; cmd += pi->out; } // Notify DEBUG("executing: " << cmd); // Execute rc = system(cmd.c_str()); DEBUG("return code: "<< rc << " (0x"<<(int *)rc<<")"); #endif // We are done return rc; } //__________________________________________________________________________ int XrdSecProtocolgsi::ParseCAlist(String calist) { // Parse received ca list, find the first available CA in the list // and return a chain initialized with such a CA. // If nothing found return 0. EPNAME("ParseCAlist"); // Check inputs if (calist.length() <= 0) { PRINT("nothing to parse"); return -1; } DEBUG("parsing list: "<Chain = 0; String cahash = ""; // Parse list if (calist.length()) { int from = 0; while ((from = calist.tokenize(cahash, from, '|')) != -1) { // Check this hash if (cahash.length()) { // Make sure the extension ".0" if there, as external implementations may not // include it if (!cahash.endswith(".0")) cahash += ".0"; // Get the CA chain if (GetCA(cahash.c_str(), sessionCF, hs) == 0) return 0; } } } // We did not find it return -1; } //__________________________________________________________________________ int XrdSecProtocolgsi::ParseCrypto(String clist) { // Parse crypto list clist, extracting the first available module // and getting a related local cipher and a related reference // cipher to be used to agree the session cipher; the local lists // crypto info is updated, if needed // The results are used to fill the handshake part of the protocol // instance. EPNAME("ParseCrypto"); // Check inputs if (clist.length() <= 0) { NOTIFY("empty list: nothing to parse"); return -1; } DEBUG("parsing list: "<CryptoMod = ""; // Parse list int from = 0; while ((from = clist.tokenize(hs->CryptoMod, from, '|')) != -1) { // Check this module if (hs->CryptoMod.length() > 0) { DEBUG("found module: "<CryptoMod); // Padding support? bool otherHasPad = true; if (hs->RemVers >= XrdSecgsiVersDHsigned) { if (hs->CryptoMod.endswith(gNoPadTag)) { otherHasPad = false; hs->CryptoMod.replace(gNoPadTag, ""); } } else { otherHasPad = false; } // Load the crypto factory if ((sessionCF = XrdCryptoFactory::GetCryptoFactory(hs->CryptoMod.c_str()))) { sessionCF->SetTrace(GSITrace->What); if (QTRACE(Debug)) sessionCF->Notify(); if (otherHasPad && sessionCF->HasPaddingSupport()) hs->HasPad = 1; int fid = sessionCF->ID(); int i = 0; // Retrieve the index in local table while (i < ncrypt) { if (cryptID[i] == fid) break; i++; } if (i >= ncrypt) { if (ncrypt == XrdCryptoMax) { DEBUG("max number of crypto slots reached - do nothing"); return 0; } else { // Add new entry cryptF[i] = sessionCF; cryptID[i] = fid; ncrypt++; } } // On servers the ref cipher should be defined at this point hs->Rcip = sessionCF->Cipher(hs->HasPad, 0,0,0); // we are done return 0; } } } // Nothing found return -1; } //_____________________________________________________________________________ static bool QueryProxyCheck(XrdSutCacheEntry *e, void *a) { time_t ts_ref = (time_t)(*((XrdSutCacheArg_t *)a)).arg1; if (e && e->buf1.buf) { X509Chain *chain = (X509Chain *)(e->buf1.buf); if (chain->CheckValidity(1, ts_ref) == 0) return true; } return false; } //__________________________________________________________________________ int XrdSecProtocolgsi::QueryProxy(bool checkcache, XrdSutCache *cache, const char *tag, XrdCryptoFactory *cf, time_t timestamp, ProxyIn_t *pi, ProxyOut_t *po) { // Query users proxies, initializing if needed EPNAME("QueryProxy"); XrdSutCERef ceref; bool hasproxy = 0; // We may already loaded valid proxies bool rdlock = false; XrdSutCacheArg_t arg = {timestamp, -1, -1, -1}; XrdSutCacheEntry *cent = cache->Get(tag, rdlock, QueryProxyCheck, (void *) &arg); if (!cent) { PRINT("cannot get cache entry for: "<rwmtx)); if (checkcache && rdlock) { po->chain = (X509Chain *)(cent->buf1.buf); po->ksig = (XrdCryptoRSA *)(cent->buf2.buf); po->cbck = (XrdSutBucket *)(cent->buf3.buf); // We are done ceref.UnLock(); return 0; } // Cleanup the chain po->chain = (X509Chain *)(cent->buf1.buf); if (po->chain) po->chain->Cleanup(); SafeDelete(po->chain); // Cleanup cache entry cent->buf1.buf = 0; cent->buf1.len = 0; // The key is deleted by the certificate destructor // Just reset the buffer cent->buf2.buf = 0; cent->buf2.len = 0; // and the related bucket if (cent->buf3.buf) delete (XrdSutBucket *)(cent->buf3.buf); cent->buf3.buf = 0; cent->buf3.len = 0; // // We do not have good proxies, try load (user may have initialized // them in the meanwhile) // Create a new chain first, if needed if (!(po->chain)) po->chain = new X509Chain(); if (!(po->chain)) { PRINT("cannot create new chain!"); return -1; } int ntry = 3; bool parsefile = 1; bool exportbucket = 0; XrdCryptoX509ParseFile_t ParseFile = 0; XrdCryptoX509ParseBucket_t ParseBucket = 0; while (!hasproxy && ntry > 0) { // Try init as last option if (ntry == 1) { // Cleanup the chain po->chain->Cleanup(); if (InitProxy(pi, cf, po->chain, &(po->ksig)) != 0) { NOTIFY("problems initializing proxy via external shell"); ntry--; continue; } // We need to explicitely export the proxy in a bucket exportbucket = 1; #ifndef HASGRIDPROXYINIT // Chain is already loaded if we used the internal function // to initialize the proxies parsefile = 0; timestamp = time(0); #endif } ntry--; // // A proxy chain may have been passed via XrdSecCREDS: check that first if (ntry == 2) { char *cbuf = getenv("XrdSecCREDS"); if (cbuf) { // Import into a bucket XrdSutBucket xbck(0, 0, kXRS_x509); // Fill bucket xbck.SetBuf(cbuf, strlen(cbuf)); // Parse the bucket if (!(ParseBucket = cf->X509ParseBucket())) { PRINT("cannot attach to ParseBucket function!"); continue; } int nci = (*ParseBucket)(&xbck, po->chain); if (nci < 2) { NOTIFY("proxy bucket must have at least two certificates" " (found: "<X509ParseFile())) { PRINT("cannot attach to ParseFile function!"); continue; } } // Parse the proxy file int nci = (*ParseFile)(pi->out, po->chain); if (nci < 2) { DEBUG("proxy files must have at least two certificates" " (found: "< 1) ? 1 : 0; po->chain->CheckCA(checkselfsigned); exportbucket = 1; } } // Check validity in time if (po->chain->CheckValidity(1, timestamp) != 0) { NOTIFY("proxy files contains expired certificates"); continue; } // Reorder chain if (po->chain->Reorder() != 0) { NOTIFY("proxy files contains inconsistent certificates"); continue; } // Check key po->ksig = po->chain->End()->PKI(); if (po->ksig->status != XrdCryptoRSA::kComplete) { NOTIFY("proxy files contain invalid key pair"); continue; } XrdCryptoX509ExportChain_t ExportChain = cf->X509ExportChain(); if (!ExportChain) { PRINT("cannot attach to ExportChain function!"); continue; } // Create bucket for export if (exportbucket) { po->cbck = (*ExportChain)(po->chain, 0); if (!(po->cbck)) { PRINT("could not create bucket for export"); continue; } } // Save info in cache cent->mtime = po->chain->End()->NotAfter(); // the expiring time cent->status = kCE_special; // distinguish from normal certs cent->cnt = 0; // The chain cent->buf1.buf = (char *)(po->chain); cent->buf1.len = 0; // Just a flag // The key cent->buf2.buf = (char *)(po->chain->End()->PKI()); cent->buf2.len = 0; // Just a flag // The export bucket cent->buf3.buf = (char *)(po->cbck); cent->buf3.len = 0; // Just a flag // Set the positive flag hasproxy = 1; } // Always unlock ceref.UnLock(); // We are done if (!hasproxy) { // Some cleanup po->chain->Cleanup(); SafeDelete(po->chain); SafeDelete(po->cbck); return -1; } return 0; } //_____________________________________________________________________________ static bool QueryGMAPCheck(XrdSutCacheEntry *e, void *a) { int st_ref = (*((XrdSutCacheArg_t *)a)).arg1; time_t ts_ref = (time_t)(*((XrdSutCacheArg_t *)a)).arg2; long to_ref = (*((XrdSutCacheArg_t *)a)).arg3; if (e) { // Check expiration, if required if ((e->status != st_ref) || ((e->status == st_ref) && (to_ref > 0) && ((ts_ref - e->mtime) > to_ref))) { return false; } else { return true; } } return false; } //__________________________________________________________________________ void XrdSecProtocolgsi::QueryGMAP(XrdCryptoX509Chain *chain, int now, String &usrs) { // Resolve usernames associated with this proxy. The lookup is typically // based on the 'dn' (either in the grid mapfile or via the 'GMAPFun' plugin) but // it can also be based on the full proxy via the AuthzFun plugin. // For 'grid mapfile' and 'GMAPFun' the result is kept valid for a certain amount // of time, hashed on the 'dn'. // On return, an empty string in 'usrs' indicates failure. // Note that 'usrs' can be a comma-separated list of usernames. EPNAME("QueryGMAP"); // List of user names attached to the entity usrs = ""; // The chain must be defined if (!chain) { PRINT("input chain undefined!"); return; } // Now we check the DN-mapping function and eventually the gridmap file. // The result can be cached for a while. const char *dn = chain->EECname(); if (GMAPFun) { XrdSutCERef ceref; bool rdlock = false; XrdSutCacheArg_t arg = {kCE_ok, now, GMAPCacheTimeOut, -1}; XrdSutCacheEntry *cent = cacheGMAPFun.Get(dn, rdlock, QueryGMAPCheck, (void *) &arg); if (!cent) { PRINT("unable to get a valid entry from cache for dn: " << dn); return; } ceref.Set(&(cent->rwmtx)); // Check if we need to get/update the content if (!rdlock) { // Run the search via the external function char *name = (*GMAPFun)(dn, now); if (name) { cent->status = kCE_ok; // Add username SafeDelArray(cent->buf1.buf); cent->buf1.buf = name; cent->buf1.len = strlen(name); } // Fill up the rest cent->cnt = 0; cent->mtime = now; // creation time } // Retrieve result form cache usrs = cent->buf1.buf; // We are done with the cache ceref.UnLock(); } // Check the map file, if any // if (servGMap) { char u[65]; if (servGMap->dn2user(dn, u, sizeof(u), now) == 0) { if (usrs.length() > 0) usrs += ","; usrs += (const char *)u; } } // Done return; } //_____________________________________________________________________________ XrdSecgsiGMAP_t XrdSecProtocolgsi::LoadGMAPFun(const char *plugin, const char *parms) { // Load the DN-Username mapping function from the specified plug-in EPNAME("LoadGMAPFun"); char errBuff[2048]; // Make sure the input config file is defined if (!plugin || strlen(plugin) <= 0) { PRINT("plug-in file undefined"); return (XrdSecgsiGMAP_t)0; } // Create the plug-in instance XrdOucPinLoader gmapLib(errBuff,sizeof(errBuff),gsiVersion,"gmaplib",plugin); // Use global symbols? bool useglobals = 0; XrdOucString params, ps(parms), p; int from = 0; while ((from = ps.tokenize(p, from, '|')) != -1) { if (p == "useglobals") { useglobals = 1; } else { if (params.length() > 0) params += " "; params += p; } } DEBUG("params: '"<< params<<"'; useglobals: "< 0) params += " "; params += p; } } DEBUG("params: '"<< params<<"'; useglobals: "< 0) params += " "; params += p; } } DEBUG("params: '"<< params<<"'; useglobals: "<[/*]" if (hname) { size_t ih = srvcn.find("/"); if (ih != std::string::npos) { srvcn.erasefromstart(ih + 1); } allowed = XrdCryptoX509::MatchHostnames(srvcn.c_str(), hname); // Update the error msg, if the case if (!allowed) { if (emsg.length() <= 0) { emsg = "server certificate CN '"; emsg += srvcn; emsg += "' does not match the expected format(s):"; } String defcn("[*/]"); defcn += hname; defcn += "[/*]"; emsg += " '"; emsg += defcn; emsg += "' (default)"; } } // Take into account specific requests, if any if (SrvAllowedNames.length() > 0) { // The SrvAllowedNames string contains the allowed formats separated by a '|'. // The specifications can contain the or placeholders which // are replaced by hname; they can also contain the '*' wildcard, in // which case XrdOucString::matches is used. A '-' before the specification // will deny the matching CN's; the last matching wins. String allowedfmts(SrvAllowedNames); allowedfmts.replace("", hname); allowedfmts.replace("", hname); int from = 0; String fmt; while ((from = allowedfmts.tokenize(fmt, from, '|')) != -1) { // Check if this should be denied bool deny = 0; if (fmt.beginswith("-")) { deny = 1; fmt.erasefromstart(1); } if (srvcn.matches(fmt.c_str()) > 0) allowed = (deny) ? 0 : 1; } // Update the error msg, if the case if (!allowed) { if (emsg.length() <= 0) { emsg = "server certificate CN '"; emsg += srvcn; emsg += "' does not match the expected format:"; } emsg += " '"; emsg += SrvAllowedNames; emsg += "' (exceptions)"; } } // Reset error msg, if the match was successful if (allowed) emsg = ""; else emsg += "; exceptions are controlled by the env XrdSecGSISRVNAMES"; // Done return allowed; } //_____________________________________________________________________________ static bool GetSrvCertEntCheck(XrdSutCacheEntry *e, void *a) { int st_ref = (*((XrdSutCacheArg_t *)a)).arg1; time_t ts_ref = (time_t)(*((XrdSutCacheArg_t *)a)).arg2; if (e) { if (e->status > st_ref) { if (e->mtime >= ts_ref) return true; } } return false; } //_____________________________________________________________________________ XrdSutCacheEntry *XrdSecProtocolgsi::GetSrvCertEnt(XrdSutCERef &ceref, XrdCryptoFactory *cf, time_t timestamp, String &certcalist) { // Get cache entry for server certificate. This function checks the cache // and loads or re-loads the certificate form the specified files if required. // make sure we got what we need EPNAME("GetSrvCertEnt"); if (!cf) { PRINT("Invalid inputs"); return (XrdSutCacheEntry *)0; } bool rdlock = false; XrdSutCacheArg_t arg = {kCE_allowed, timestamp, -1, -1}; XrdSutCacheEntry *cent = cacheCert.Get(cf->Name(), rdlock, GetSrvCertEntCheck, (void *) &arg); if (!cent) { PRINT("unable to get a valid entry from cache for " << cf->Name()); return (XrdSutCacheEntry *)0; } ceref.Set(&(cent->rwmtx)); // Are we done ? if (rdlock) return cent; if (cent->buf1.buf) PRINT("entry has expired: trying to renew ..."); // Try get one or renew-it if (cent->status == kCE_special) { // Try init proxies ProxyIn_t pi = {SrvCert.c_str(), SrvKey.c_str(), CAdir.c_str(), UsrProxy.c_str(), PxyValid.c_str(), 0, 512}; X509Chain *ch = 0; XrdCryptoRSA *k = 0; XrdSutBucket *b = 0; ProxyOut_t po = {ch, k, b }; // We lock inside ceref.UnLock(false); if (QueryProxy(0, &cacheCert, cf->Name(), cf, timestamp, &pi, &po) != 0) { PRINT("proxy expired and cannot be renewed"); return (XrdSutCacheEntry *)0; } // When successful we return read-locked (this flow needs checking; but it is not mainstream) ceref.ReadLock(); return cent; } // Reset the entry delete (XrdCryptoX509 *) cent->buf1.buf; // Destroys also xsrv->PKI() pointed in cent->buf2.buf delete (XrdSutBucket *) cent->buf3.buf; cent->buf1.buf = 0; cent->buf2.buf = 0; cent->buf3.buf = 0; // // Get the IDs of the file: we need them to acquire the right privileges when opening // the certificate uid_t gsi_uid = geteuid(); gid_t gsi_gid = getegid(); struct stat st; if (!stat(SrvKey.c_str(), &st)) { if (st.st_uid != gsi_uid || st.st_gid != gsi_gid) { gsi_uid = st.st_uid; gsi_gid = st.st_gid; } } // Check normal certificates XrdCryptoX509 *xsrv = cf->X509(SrvCert.c_str(), SrvKey.c_str()); if (xsrv) { // Must be of EEC type if (xsrv->type != XrdCryptoX509::kEEC) { PRINT("problems loading srv cert: not EEC but: "<Type()); SafeDelete(xsrv); ceref.UnLock(); return (XrdSutCacheEntry *)0; } // Must be valid if (!(xsrv->IsValid())) { PRINT("problems loading srv cert: invalid"); SafeDelete(xsrv); ceref.UnLock(); return (XrdSutCacheEntry *)0; } // PKI must have been successfully initialized if (!xsrv->PKI() || xsrv->PKI()->status != XrdCryptoRSA::kComplete) { PRINT("problems loading srv cert: invalid PKI"); SafeDelete(xsrv); ceref.UnLock(); return (XrdSutCacheEntry *)0; } // Must be exportable XrdSutBucket *xbck = xsrv->Export(); if (!xbck) { PRINT("problems loading srv cert: cannot export into bucket"); SafeDelete(xsrv); ceref.UnLock(); return (XrdSutCacheEntry *)0; } // We must have the issuing CA certificate int rcgetca = 0; if ((rcgetca = GetCA(xsrv->IssuerHash(), cf)) != 0) { String emsg(xsrv->IssuerHash()); // Try different name hash, if it makes sense if (strcmp(xsrv->IssuerHash(1), xsrv->IssuerHash(0))) { if ((rcgetca = GetCA(xsrv->IssuerHash(1), cf)) != 0) { emsg += "|"; emsg += xsrv->IssuerHash(1); } } if (rcgetca != 0) { // We do not have it, really if (rcgetca == -1) { PRINT("do not have certificate for the issuing CA '"<status = kCE_ok; cent->cnt = 0; cent->mtime = xsrv->NotAfter(); // expiration time // Save pointer to certificate (destroys also xsrv->PKI()) if (cent->buf1.buf) delete (XrdCryptoX509 *) cent->buf1.buf; cent->buf1.buf = (char *)xsrv; cent->buf1.len = 0; // just a flag // Save pointer to key cent->buf2.buf = 0; cent->buf2.buf = (char *)(xsrv->PKI()); cent->buf2.len = 0; // just a flag // Save pointer to bucket if (cent->buf3.buf) delete (XrdSutBucket *) cent->buf3.buf; cent->buf3.buf = (char *)(xbck); cent->buf3.len = 0; // just a flag // Save CA hash in list to communicate to clients if (certcalist.find(xsrv->IssuerHash()) == STR_NPOS) { if (certcalist.length() > 0) certcalist += "|"; certcalist += xsrv->IssuerHash(); } // Save also old CA hash in list to communicate to clients, if relevant if (HashCompatibility && xsrv->IssuerHash(1) && strcmp(xsrv->IssuerHash(1),xsrv->IssuerHash())) { if (certcalist.find(xsrv->IssuerHash(1)) == STR_NPOS) { if (certcalist.length() > 0) certcalist += "|"; certcalist += xsrv->IssuerHash(1); } } } else { PRINT("failed to load certificate from files ("<< SrvCert <<","<