diff -ruBEN polygraph-4.0.11/configure.in polygraph-4.0.11-mm/configure.in --- polygraph-4.0.11/configure.in 2010-11-06 08:35:57.000000000 +0000 +++ polygraph-4.0.11-mm/configure.in 2011-02-12 13:53:42.000000000 +0000 @@ -386,6 +386,149 @@ sleep 1; fi +AC_ARG_WITH(krb5-config, + AC_HELP_STRING([--with-krb5-config=PATH], + [specify path to krb5-config (default=detect)]), [ + case "$withval" in + yes) unset krb5confpath ;; + no) krb5confpath=no ;; + *) krb5confpath=$withval ;; + esac +]) +if test x"$krb5confpath" != "xno"; then + if test "x$krb5confpath" != "x"; then + if ! test -x "$krb5confpath"; then + AC_MSG_WARN([krb5-config '$krb5confpath' not executable, ignoring]) + AC_CHECK_PROG(ac_krb5_config, krb5-config, yes, no) + krb5confpath=krb5-config + fi + krb5_config_path=`dirname $krb5confpath` + AC_CHECK_PROG(ac_krb5_config, krb5-config, yes, no, $krb5_config_path) + else + AC_CHECK_PROG(ac_krb5_config,krb5-config,yes,no) + krb5confpath=krb5-config + fi +fi +if test "x$ac_krb5_config" = "xyes" ; then + ac_heimdal="`$krb5confpath --version 2>/dev/null | grep -i heimdal`" + if test "x$ac_heimdal" != "x" ; then + AC_DEFINE(HAVE_HEIMDAL_KERBEROS,1,[Define to 1 if you have Heimdal Kerberos]) + else + AC_DEFINE(HAVE_MIT_KERBEROS,1,[Define to 1 if you have MIT Kerberos]) + fi + KRB5INCS="`$krb5confpath --cflags krb5 2>/dev/null`" + KRB5LIBS="`$krb5confpath --libs krb5 2>/dev/null`" + KRB5INCS="`$krb5confpath --cflags gssapi 2>/dev/null` $KRB5INCS" + KRB5LIBS="`$krb5confpath --libs gssapi 2>/dev/null` $KRB5LIBS" + CPPFLAGS="$CPPFLAGS $KRB5INCS" + LIBS="$LIBS $KRB5LIBS" + AC_CHECK_HEADERS(gssapi.h gssapi/gssapi.h gssapi/gssapi_krb5.h) + if test "x$ac_heimdal" = "x" ; then + AC_CHECK_HEADERS(gssapi/gssapi_generic.h) + fi + + AC_CACHE_CHECK([for broken Heimdal krb5.h],squid_cv_broken_heimdal_krb5_h, [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +int +main(void) +{ + krb5_context context; + + krb5_init_context(&context); + + return 0; +} +]])], [ squid_cv_broken_heimdal_krb5_h=no ], [ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#if defined(__cplusplus) +extern "C" { +#endif +#include +#if defined(__cplusplus) +} +#endif +int +main(void) +{ + krb5_context context; + + krb5_init_context(&context); + + return 0; +} +]])], [ squid_cv_broken_heimdal_krb5_h=yes ], [ squid_cv_broken_heimdal_krb5_h=no ]) + ]) + ]) + + if test "x$squid_cv_broken_heimdal_krb5_h" = "xyes"; then + AC_DEFINE(HAVE_BROKEN_HEIMDAL_KRB5_H, 1, [Define to 1 if Heimdal krb5.h is broken for C++]) + fi + AC_CHECK_HEADERS(krb5.h com_err.h et/com_err.h) + + dnl do compile check + AC_MSG_CHECKING([for working krb5]) + AC_TRY_RUN([ +#if HAVE_KRB5_H +#if HAVE_BROKEN_HEIMDAL_KRB5_H +extern "C" { +#include +} +#else +#include +#endif +#endif + +int +main(void) +{ + krb5_context context; + + krb5_init_context(&context); + + return 0; +} +], [unset no_krb5 + AC_DEFINE(HAVE_KRB5, 1, [KRB5 support]) + AC_MSG_RESULT(yes)], + AC_MSG_RESULT(no)) + + dnl do compile check + AC_MSG_CHECKING([for working gssapi]) + AC_TRY_RUN([ +#if HAVE_GSSAPI_H +#include +#elif HAVE_GSSAPI_GSSAPI_H +#include +#endif /* HAVE_GSSAPI_H */ + +#if !HAVE_HEIMDAL_KERBEROS +#if HAVE_GSSAPI_GSSAPI_EXT_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_EXT_H */ +#if HAVE_GSSAPI_GSSAPI_KRB5_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */ +#if HAVE_GSSAPI_GSSAPI_GENERIC_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_GENERIC_H */ +#endif /* HAVE_HEIMDAL_KERBEROS */ + +int +main(void) +{ + OM_uint32 val; + gss_OID_set set; + + gss_create_empty_oid_set(&val, &set); + + return 0; +} +], [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GSSAPI, 1, [GSSAPI support])], + [AC_MSG_RESULT(no)]) +fi + dnl check whether loganalizers/comparator should be built AC_MSG_CHECKING(whether to build comparator) AC_ARG_ENABLE(comparator, diff -ruBEN polygraph-4.0.11/src/client/CltCfg.cc polygraph-4.0.11-mm/src/client/CltCfg.cc --- polygraph-4.0.11/src/client/CltCfg.cc 2010-02-18 08:25:00.000000000 +0000 +++ polygraph-4.0.11-mm/src/client/CltCfg.cc 2011-02-12 20:38:14.000000000 +0000 @@ -50,7 +50,10 @@ theBusyPeriod(0), theIdlePeriodDur(0), theFtpProxyCycleCnt(0), theHttpProxyCycleCnt(0), thePipelineDepth(0), - theRecurRatio(-1), theSpnegoAuthRatio(-1), theEmbedRecurRatio(-1), + theRecurRatio(-1), theSpnegoAuthRatio(-1), preferKerberosAuth(false), + clearKerberosCache(false), theKerberosConfigPath(0), + theKerberosProxySPN(0), theKerberosServerSPN(0), + theEmbedRecurRatio(-1), theAbortProb(0), theAuthError(0), theWaitXactLmt(-1), theIcpPort(-1), theCredentialCycleCnt(0), theForeignWorld(0), @@ -81,7 +84,6 @@ delete thePopModel; delete theBusyPeriod; delete theContainerTags; - delete theAcceptedContentCodings; delete theForeignWorld; delete theAcceptedContentCodings; delete theWarmupPlan; @@ -103,6 +105,11 @@ theRobot->recurRatio(theRecurRatio); theRobot->spnegoRatio(theSpnegoAuthRatio); + theRobot->kerberos(preferKerberosAuth); + theRobot->kerberosClearCache(clearKerberosCache); + theKerberosConfigPath = theRobot->kerberosConfigPath(); + theKerberosProxySPN = theRobot->kerberosProxySPN(); + theKerberosServerSPN = theRobot->kerberosServerSPN(); theRobot->embedRecurRatio(theEmbedRecurRatio); theRobot->abortProb(theAbortProb); theRobot->authError(theAuthError); diff -ruBEN polygraph-4.0.11/src/client/CltCfg.h polygraph-4.0.11-mm/src/client/CltCfg.h --- polygraph-4.0.11/src/client/CltCfg.h 2010-01-22 23:57:25.000000000 +0000 +++ polygraph-4.0.11-mm/src/client/CltCfg.h 2011-02-12 20:38:09.000000000 +0000 @@ -105,6 +105,11 @@ double thePublicRatio; // how often to request a "public" object double theRecurRatio; // how often to re-visit an old object double theSpnegoAuthRatio; // how often negotiate/spnego auth is preferred over NTLMSSP + bool preferKerberosAuth; // is negotiate/kerberos auth preferred over negotiate/spnego + bool clearKerberosCache; // clear cache before new session + String theKerberosConfigPath; // is the Kerberos configuration file location (krb5.conf) + String theKerberosProxySPN; // is the Kerberos service principal name for the proxy + String theKerberosServerSPN; // is the Kerberos service principal name for the server double theEmbedRecurRatio; // how often to re-visit an old embedded obj double theAbortProb; // prob of aborting a transaction double theAuthError; // prob of an authentication error diff -ruBEN polygraph-4.0.11/src/client/GssKrbCtx.h polygraph-4.0.11-mm/src/client/GssKrbCtx.h --- polygraph-4.0.11/src/client/GssKrbCtx.h 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.0.11-mm/src/client/GssKrbCtx.h 2011-02-13 00:30:41.000000000 +0000 @@ -0,0 +1,78 @@ +#ifndef __HTTP_GSSKRBCTX_H +#define __HTTP_GSSKRBCTX_H + +#ifdef HAVE_KRB5_H +#ifdef HAVE_BROKEN_HEIMDAL_KRB5_H +extern "C" { +#include +} +#else +#include +#endif +#endif /* HAVE_KRB5_H */ + +#ifdef HAVE_GSSAPI_H +#include +#elif defined(HAVE_GSSAPI_GSSAPI_H) +#include +#endif /* HAVE_GSSAPI_H */ + +#ifndef gss_nt_service_name +#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE +#endif + +#if !defined(HAVE_HEIMDAL_KERBEROS) +#ifdef HAVE_GSSAPI_GSSAPI_EXT_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_EXT_H */ +#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */ +#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H +#include +#endif /* HAVE_GSSAPI_GSSAPI_GENERIC_H */ +#endif /* !HAVE_HEIMDAL_KERBEROS */ + +class GssKrbXact { + public: + GssKrbXact(); + OM_uint32 gssMajorStatus; + OM_uint32 gssMinorStatus; + gss_name_t gssServerName; + void gssCleanup(); + int checkGssError(String gssFunctionName); + NtlmAuthState NegoKerberosAuthPrint(ostream &os, String gssBase64Token); + static int pton(const char *p); + + class KrbEnv { + public: + KrbEnv(); + ~KrbEnv(); + String krbUserp; + String krbPasswdp; + String krbServerName; + String krbCfgPath; + String krbSPN; + bool clearKrbCc; + NtlmAuthState krbSetup(); + + private: + krb5_context theKrbCtx; + krb5_principal theKrbClt; + krb5_ccache theKrbCc; + krb5_creds theKrbCreds; + krb5_error_code theKrbErrorCode; + struct krbCtxStruct { + String theKrbCfgPath; + krb5_context theKrbCtx; + struct krbCtxStruct *theNextKrbCtx; + } *theKrbCtxStruct; + NtlmAuthState krbCleanup(); + int krbCheckError(String theKrbFunctionName); + }; + friend class KrbEnv; + + private: + gss_ctx_id_t theGssContext; +}; +#endif diff -ruBEN polygraph-4.0.11/src/client/HttpCltXact.cc polygraph-4.0.11-mm/src/client/HttpCltXact.cc --- polygraph-4.0.11/src/client/HttpCltXact.cc 2010-10-07 22:55:44.000000000 +0100 +++ polygraph-4.0.11-mm/src/client/HttpCltXact.cc 2011-02-12 20:38:37.000000000 +0000 @@ -16,6 +16,7 @@ #include "client/MarkupBodyParser.h" #include "client/MultiPartParser.h" #include "client/NtlmAuth.h" +#include "client/KerberosAuth.h" #include "client/SingleCxm.h" #include "client/PipelinedCxm.h" #include "client/PrivCache.h" @@ -587,16 +588,17 @@ void HttpCltXact::makeProxyAuthHdr(ostream &os) { const HttpAuthScheme scheme(theOwner->proxyAuthScheme(theOid)); Connection::NtlmAuth &ntlmState(theConn->theProxyNtlmState); - makeAuthHdr(hfpProxyAuthorization, scheme, ntlmState, os); + makeAuthHdr(true, hfpProxyAuthorization, scheme, ntlmState, os); } void HttpCltXact::makeOriginAuthHdr(ostream &os) { const HttpAuthScheme scheme(theOwner->originAuthScheme(theOid)); Connection::NtlmAuth &ntlmState(theConn->theOriginNtlmState); - makeAuthHdr(hfpAuthorization, scheme, ntlmState, os); + makeAuthHdr(false, hfpAuthorization, scheme, ntlmState, os); } -void HttpCltXact::makeAuthHdr(const String &header, const HttpAuthScheme scheme, Connection::NtlmAuth &ntlmState, ostream &os) { +void HttpCltXact::makeAuthHdr(const bool proxyAuth, const String &header, const HttpAuthScheme scheme, + Connection::NtlmAuth &ntlmState, ostream &os) { theOid.authCred(false); switch (ntlmState.state) { case ntlmNone: { // Initiating auth @@ -608,9 +610,37 @@ } else if (scheme == authNegotiate) { makeAuthorization(header, scheme, os); - NegoNtlmAuthPrintT1(os, ntlmState.useSpnegoNtlm); - os << crlf; - ntlmState.state = ntlmSentT1; + if (!theOwner->cfg()->preferKerberosAuth) { // Spnego + NegoNtlmAuthPrintT1(os, ntlmState.useSpnegoNtlm); + os << crlf; + ntlmState.state = ntlmSentT1; + } else { // Kerberos otherwise + if (theOwner->credentialsFor(theOid, theCred)) { + Area aUser = theCred.name(); + String sUser(aUser.data(), aUser.size()); + Area aPass = theCred.password(); + String sPass(aPass.data(), aPass.size()); + if (proxyAuth) { + ntlmState.state = + CltGssXact::NegoKerberosAuthPrintT1(os, sUser, + sPass, theOwner->proxy(theOid).addrA(), + theOwner->cfg()->theKerberosConfigPath, + theOwner->cfg()->clearKerberosCache, + theOwner->cfg()->theKerberosProxySPN); + } else {// Oid2UrlHost(theOid).addrA() should have the name but + // has only the IP. need to look for right lookup call + ntlmState.state = + CltGssXact::NegoKerberosAuthPrintT1(os, sUser, + sPass, Oid2UrlHost(theOid).addrA(), + theOwner->cfg()->theKerberosConfigPath, + theOwner->cfg()->clearKerberosCache, + theOwner->cfg()->theKerberosServerSPN); + } + os << crlf; + } else { + ntlmState.state = kerberosError; + } + } } else if (scheme == authBasic) { if (theOwner->credentialsFor(theOid, theCred)) { @@ -624,6 +654,15 @@ } break; } + case kerberosSentT1: { + if (!header.cmp(hfpProxyAuthorization)) { + ntlmState.state = CltGssXact::NegoKerberosAuthPrintT3(os, ntlmState.hdrRcvdT2); + } else { + ntlmState.state = CltGssXact::NegoKerberosAuthPrintT3(os, ntlmState.hdrRcvdT2); + } + os << crlf; + break; + } case ntlmSentT1: { if (theOwner->credentialsFor(theOid, theCred)) { Area aUser = theCred.name(); @@ -647,7 +686,12 @@ } break; } + case kerberosDone: case ntlmDone: + case kerberosError: { + // do nothing + break; + } case ntlmError: { // do nothing break; @@ -941,21 +985,26 @@ authError = startAuth(authNtlm); } else if (challenge.scheme == authNegotiate) { - ntlmState.useSpnegoNtlm = isSpnegoNtlm(challenge.params.cstr()); - if (ntlmState.useSpnegoNtlm && theOwner->cfg()->theSpnegoAuthRatio >= 0) { - static RndGen rng; - ntlmState.useSpnegoNtlm = rng.event(theOwner->cfg()->theSpnegoAuthRatio); - } + if (!theOwner->cfg()->preferKerberosAuth) { // Spnego + ntlmState.useSpnegoNtlm = isSpnegoNtlm(challenge.params.cstr()); + if (ntlmState.useSpnegoNtlm && theOwner->cfg()->theSpnegoAuthRatio >= 0) { + static RndGen rng; + ntlmState.useSpnegoNtlm = rng.event(theOwner->cfg()->theSpnegoAuthRatio); + } - // According to rfc4559 spnego-based negotiate auth is not supposed - // to be used by proxies (but can be passed transparently to servers), - // so we can't really say whether we need to retry here or what. Need - // to test on available clients and proxies. - doRetry = ntlmState.useSpnegoNtlm; + // According to rfc4559 spnego-based negotiate auth is not supposed + // to be used by proxies (but can be passed transparently to servers), + // so we can't really say whether we need to retry here or what. Need + // to test on available clients and proxies. + doRetry = ntlmState.useSpnegoNtlm; - // sent nothing, in case of NTLMSSP proxy should signal disconnect, - // Negotiate authentication will resume once we reconnect - authError = startAuth(authNegotiate); + // sent nothing, in case of NTLMSSP proxy should signal disconnect, + // Negotiate authentication will resume once we reconnect + authError = startAuth(authNegotiate); + } else { // Kerberos otherwise + doRetry = true; + authError = startAuth(authNegotiate); + } } else if (!theCred.image()) { // sent nothing, Basic auth @@ -968,6 +1017,11 @@ // else auth correctly denied due to invalid credentials break; } + case kerberosSentT1: { + ntlmState.hdrRcvdT2 = challenge.params; + doRetry = true; // continue processing in HopByHops + break; + } case ntlmSentT1: { ntlmState.hdrRcvdT2 = challenge.params; doRetry = true; // continue processing in HopByHops @@ -998,6 +1052,14 @@ authError = proxyAuth ? errProxyAuthAllowed : errOriginAuthAllowed; break; } + case kerberosSentT1: { // as expected + ntlmState.state = kerberosDone; + break; + } + case kerberosDone: { + // do nothing, everything is ok + break; + } case ntlmDone: { // do nothing, everything is ok break; diff -ruBEN polygraph-4.0.11/src/client/HttpCltXact.h polygraph-4.0.11-mm/src/client/HttpCltXact.h --- polygraph-4.0.11/src/client/HttpCltXact.h 2010-01-22 23:57:25.000000000 +0000 +++ polygraph-4.0.11-mm/src/client/HttpCltXact.h 2011-02-12 13:53:42.000000000 +0000 @@ -79,7 +79,7 @@ void makeCookies(ostream &os); void makeProxyAuthHdr(ostream &os); void makeOriginAuthHdr(ostream &os); - void makeAuthHdr(const String &header, const HttpAuthScheme scheme, Connection::NtlmAuth &ntlmState, ostream &os); + void makeAuthHdr(const bool proxyAuth, const String &header, const HttpAuthScheme scheme, Connection::NtlmAuth &ntlmState, ostream &os); void makeAuthorization(const String &header, const HttpAuthScheme scheme, ostream &os); void firstHandSync(); diff -ruBEN polygraph-4.0.11/src/client/KerberosAuth.cc polygraph-4.0.11-mm/src/client/KerberosAuth.cc --- polygraph-4.0.11/src/client/KerberosAuth.cc 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.0.11-mm/src/client/KerberosAuth.cc 2011-02-13 01:05:17.000000000 +0000 @@ -0,0 +1,438 @@ +/* +*/ + +#include "base/polygraph.h" +#include "runtime/LogComment.h" +#include "runtime/NtlmAuthState.h" +#include "xstd/InAddress.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "client/KerberosAuth.h" +#include "xstd/gadgets.h" + +#if defined(HAVE_KRB5) && defined(HAVE_GSSAPI) + +#include "client/GssKrbCtx.h" + +#ifdef HAVE_ET_COM_ERR_H +#include +#elif defined(HAVE_COM_ERR_H) +#include +#else +#define error_message(code) krb5_get_err_text(theKrbCtx, code) +#endif + +#ifndef gss_mech_spnego +static gss_OID_desc _gss_mech_spnego = +{6, (void *) "\x2b\x06\x01\x05\x05\x02"}; +gss_OID gss_mech_spnego = &_gss_mech_spnego; +#endif + +int GssKrbXact::pton(const char *p) { + if (*p == '[') { + struct in6_addr inp; + // + // XXX ugly memory copy to remove [ ] + // + char copy[64]; + strncpy(copy, p+1, 64); + strtok(copy, "]"); + return inet_pton(AF_INET6, copy, &inp); + } else { + struct in_addr inp; + return inet_pton(AF_INET, p, &inp); + } +} + +static GssKrbXact gssEnvironment; +static GssKrbXact::KrbEnv krbEnvironment; + +// +// Setup Kerberos context for each Kerberos configuration file +// +GssKrbXact::KrbEnv::KrbEnv() { + krbUserp = NULL; + krbPasswdp = NULL; + krbServerName = NULL; + krbCfgPath = NULL; + krbSPN = NULL; + clearKrbCc = false; + theKrbCtx = NULL; + theKrbClt = NULL ; + theKrbCc = NULL; + theKrbErrorCode = 0; + theKrbCtxStruct = NULL; +} + +GssKrbXact::KrbEnv::~KrbEnv() { + krbCleanup(); +} + +NtlmAuthState GssKrbXact::KrbEnv::krbSetup() +{ + struct krbCtxStruct *kc; + static krb5_get_init_creds_opt options; + krb5_creds theKrbMemoryCreds; + krb5_deltat theKrbDeltaTime = 0; + String theKrbMaxTinme; + String theKrbMemoryCache; + String theKrbHostName; + String theKrbTgt; + + if (!krbServerName || !krbUserp || !krbPasswdp ) + return kerberosError; + + memset(&theKrbCreds, 0, sizeof(theKrbCreds)); + + kc = theKrbCtxStruct; + while (kc) { + if (!kc->theKrbCfgPath.cmp(krbCfgPath)) { + setenv("KRB5_CONFIG", krbCfgPath.cstr(), 1); + theKrbCtx = kc->theKrbCtx; + break; + } + kc = kc->theNextKrbCtx; + } + if (!kc) { + kc = new krbCtxStruct; + kc->theKrbCfgPath = krbCfgPath; + + setenv("KRB5_CONFIG", krbCfgPath.cstr(), 1); + theKrbErrorCode = krb5_init_context(&kc->theKrbCtx); + if (krbCheckError("krb5_init_context()")) + return kerberosError; + + // Setup ticket options - only once required + if (!theKrbCtxStruct) { + // Get a ticket valid for 7 days if kdc allows ir + theKrbMaxTinme = "7d"; + krb5_get_init_creds_opt_init(&options); + theKrbErrorCode = krb5_string_to_deltat((char *)theKrbMaxTinme.cstr(), &theKrbDeltaTime); + if (krbCheckError("krb5_string_to_deltat()")) + return kerberosError; + krb5_get_init_creds_opt_set_tkt_life(&options,theKrbDeltaTime); + } + + kc->theNextKrbCtx = theKrbCtxStruct; + theKrbCtxStruct = kc; + theKrbCtx = kc->theKrbCtx; + } + + theKrbHostName = krbServerName(0,krbServerName.find(':')); + + theKrbMemoryCache = "MEMORY:" + krbCfgPath + "_" + krbUserp; + setenv("KRB5CCNAME", theKrbMemoryCache.cstr(), 1); + + theKrbErrorCode = krb5_cc_resolve(theKrbCtx, theKrbMemoryCache.cstr(), &theKrbCc); + if (krbCheckError("krb5_cc_resolve()")) + return krbCleanup(); +// +// Check if cache has already valid credentails to avoid delay through +// krb5_get_init_creds_password call +// + + memset(&theKrbMemoryCreds, 0, sizeof(theKrbMemoryCreds)); + + theKrbTgt = krbSPN; + if (theKrbTgt.len() <= 0) { + if (pton(theKrbHostName.cstr())) { + Comment << "Warning hostname " << theKrbHostName << " is in IPV4/IPV6 address form" << endc; + } + theKrbTgt = "HTTP/" + theKrbHostName; + } + theKrbErrorCode = krb5_parse_name(theKrbCtx, theKrbTgt.cstr(), &theKrbMemoryCreds.server); + if (krbCheckError("krb5_parse_name(theKrbTgt)")) + return krbCleanup(); + + theKrbErrorCode = krb5_parse_name(theKrbCtx, krbUserp.cstr(), &theKrbMemoryCreds.client); + if (krbCheckError("krb5_parse_name(userp)")) + return krbCleanup(); + + theKrbErrorCode = krb5_cc_retrieve_cred(theKrbCtx, theKrbCc, KRB5_TC_MATCH_SRV_NAMEONLY, + &theKrbMemoryCreds, &theKrbCreds); + krb5_free_cred_contents(theKrbCtx, &theKrbMemoryCreds); + +// +// get new credentials if not in cache or (nearly) expired +// + if (clearKrbCc || theKrbErrorCode || theKrbCreds.times.endtime - time(0) < 300 ) { + krb5_free_cred_contents(theKrbCtx, &theKrbCreds); + + theKrbErrorCode = krb5_parse_name(theKrbCtx, krbUserp.cstr(), &theKrbClt); + if (krbCheckError("krb5_parse_name(userp(2))")) + return krbCleanup(); + + theKrbErrorCode = krb5_get_init_creds_password(theKrbCtx, &theKrbCreds, theKrbClt, + (char *)krbPasswdp.cstr(), NULL, 0, 0, NULL, &options); + if (krbCheckError("krb5_get_init_creds_password(" + krbUserp + ")")) + return krbCleanup(); + + theKrbErrorCode = krb5_cc_initialize(theKrbCtx, theKrbCc, theKrbClt); + if (krbCheckError("krb5_cc_initialize()")) + return krbCleanup(); + + // store credentials in memory cache for later re-use + theKrbErrorCode = krb5_cc_store_cred(theKrbCtx, theKrbCc, &theKrbCreds); + if (krbCheckError("krb5_cc_store_cred()")) + return krbCleanup(); + + } + + krbCleanup(); + return kerberosDone; +} + +int GssKrbXact::KrbEnv::krbCheckError(String theKrbFunctionName) { + if (theKrbErrorCode) { +// Does not work - error_message is defined in com_err but can not be found +// Comment << function << "failed with error: " << error_message(theKrbErrorCode) << endc; + Comment << theKrbFunctionName << "failed with error: " << theKrbErrorCode << endc; + return (1); + } + return (0); +} + +NtlmAuthState GssKrbXact::KrbEnv::krbCleanup() +{ + if (theKrbCtx) { + if (theKrbClt) { + krb5_free_principal(theKrbCtx, theKrbClt); + theKrbClt = NULL; + } + if (theKrbCc) { + krb5_cc_close(theKrbCtx, theKrbCc); + theKrbCc = NULL; + } + krb5_free_cred_contents(theKrbCtx, &theKrbCreds); + } + return kerberosError; +} + +// +// Setup GSS context +// + +GssKrbXact::GssKrbXact() { + theGssContext = GSS_C_NO_CONTEXT; + gssServerName = GSS_C_NO_NAME; +} + +void GssKrbXact::gssCleanup() { + + if (theGssContext != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&gssMinorStatus, &theGssContext, NULL); + if(gssServerName != GSS_C_NO_NAME) + gss_release_name(&gssMinorStatus, &gssServerName); + + theGssContext = GSS_C_NO_CONTEXT; + gssServerName = GSS_C_NO_NAME; +} + +int GssKrbXact::checkGssError(String gssFunctionName) +{ + if (GSS_ERROR(gssMajorStatus)) { + OM_uint32 gssMaj, gssMin; + OM_uint32 gssMsgCtx = 0; + gss_buffer_desc gssStatus; + String gssErrorString = NULL; + char *gssMsg; + + gssMsgCtx = 0; + do { /* convert major status code (GSS-API error) to text */ + gssMaj = gss_display_status(&gssMin, gssMajorStatus, + GSS_C_GSS_CODE, GSS_C_NULL_OID, &gssMsgCtx, &gssStatus); + if (gssMaj == GSS_S_COMPLETE && gssStatus.length > 0) { + gssMsg=(char *)malloc(gssStatus.length+1); + memcpy(gssMsg,gssStatus.value,gssStatus.length); + gssMsg[gssStatus.length]='\0'; + gssErrorString = gssErrorString + gssMsg; + free(gssMsg); + } else { + gssMsgCtx = 0; + } + gss_release_buffer(&gssMin, &gssStatus); + } while (gssMsgCtx); + + gssErrorString = gssErrorString + ". "; + gssMsgCtx = 0; + do { /* convert minor status code (underlying routine error) to text */ + gssMaj = gss_display_status(&gssMin, gssMinorStatus, + GSS_C_MECH_CODE, GSS_C_NULL_OID, &gssMsgCtx, &gssStatus); + if (gssMaj == GSS_S_COMPLETE && gssStatus.length > 0) { + gssMsg=(char *)malloc(gssStatus.length+1); + memcpy(gssMsg,gssStatus.value,gssStatus.length); + gssMsg[gssStatus.length]='\0'; + gssErrorString = gssErrorString + gssMsg; + free(gssMsg); + } else { + gssMsgCtx = 0; + } + gss_release_buffer(&gssMin, &gssStatus); + } while (gssMsgCtx); + + Comment << gssFunctionName << " failed with error: " << gssErrorString << endc; + return (1); + } + return (0); +} + +// +// Do the real work +// + +NtlmAuthState GssKrbXact::NegoKerberosAuthPrint(ostream & os, String gssBase64Token) +{ + gss_buffer_desc gssInputToken; + gss_buffer_desc gssOutputToken; + + size_t size; + unsigned char gssBuf[8096]; /* enough, unless the authorisation data is very + long */ + gssInputToken.length = 0; + gssInputToken.value = NULL; + gssOutputToken.length = 0; + gssOutputToken.value = NULL; + + if (gssBase64Token) { + const char * header = gssBase64Token.cstr(); + + /* skip initial whitespaces */ + while(*header && isspace((unsigned char)*header)) + header++; + + if (!*header) + return kerberosError; + + size = DecodeBase64(header, strlen(header), (char*)gssBuf, sizeof(gssBuf)/sizeof(gssBuf[0])); + + gssInputToken.length = size; + memcpy(gssInputToken.value,gssBuf,size); + } + + gssEnvironment.gssMajorStatus = gss_init_sec_context(&gssEnvironment.gssMinorStatus, + GSS_C_NO_CREDENTIAL, &gssEnvironment.theGssContext, gssEnvironment.gssServerName, + gss_mech_spnego, + 0, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &gssInputToken, NULL, &gssOutputToken, NULL, NULL); + + if (gssEnvironment.checkGssError("gss_init_sec_context(" + krbEnvironment.krbUserp + ")")) + return kerberosError; + + if (gssOutputToken.length > sizeof(gssBuf)) { + Comment << "Token length " << gssOutputToken.length << " > buffer length " << sizeof(gssBuf) < 0) { + gssService.value = malloc(krbSPN.len()); + memcpy(gssService.value,krbSPN.cstr(),krbSPN.len()); + gssService.length = krbSPN.len(); + + // Import GSS Service + gssEnvironment.gssMajorStatus = gss_import_name(&gssEnvironment.gssMinorStatus, + &gssService, (gss_OID) GSS_C_NULL_OID, &gssEnvironment.gssServerName); + } else { + theGssServiceName = "HTTP@" + theGssHostName; + gssService.value = malloc(theGssServiceName.len()); + memcpy(gssService.value,theGssServiceName.cstr(),theGssServiceName.len()); + gssService.length = theGssServiceName.len(); + + // Import GSS Service + gssEnvironment.gssMajorStatus = gss_import_name(&gssEnvironment.gssMinorStatus, + &gssService, gss_nt_service_name, &gssEnvironment.gssServerName); + } + + gss_release_buffer(&gssEnvironment.gssMinorStatus, &gssService); + + if (gssEnvironment.checkGssError("gss_import_name(" + theGssServiceName + ")")) { + gssEnvironment.gssCleanup(); + return kerberosError; + } + + return gssEnvironment.NegoKerberosAuthPrint(os, NULL); + +} + +// +// Process reply and create new response +// + +NtlmAuthState CltGssXact::NegoKerberosAuthPrintT3(ostream & os, String gssBase64Token) +{ + return gssEnvironment.NegoKerberosAuthPrint(os, gssBase64Token); +} + +#else // No Kerberos support + +NtlmAuthState CltGssXact::NegoKerberosAuthPrintT1(ostream & os, String userp, String passwdp, + String server, String path, bool clear) +{ + Assert(false); + return kerberosError; +} + +NtlmAuthState CltGssXact::NegoKerberosAuthPrintT3(ostream & os, String base64Token) +{ + Assert(false); + return kerberosError; +} +#endif diff -ruBEN polygraph-4.0.11/src/client/KerberosAuth.h polygraph-4.0.11-mm/src/client/KerberosAuth.h --- polygraph-4.0.11/src/client/KerberosAuth.h 1970-01-01 01:00:00.000000000 +0100 +++ polygraph-4.0.11-mm/src/client/KerberosAuth.h 2011-02-12 20:39:31.000000000 +0000 @@ -0,0 +1,13 @@ +#ifndef __HTTP_KERBEROS_AUTH_H +#define __HTTP_KERBEROS_AUTH_H + +class CltGssXact { + public: + static NtlmAuthState NegoKerberosAuthPrintT1(ostream &os, String userp, String passwdp, + String server, String kerberosConfigPath, + bool clearKerberosCache, String kerberosSPN); + static NtlmAuthState NegoKerberosAuthPrintT3(ostream &os, String base64Token); + +}; + +#endif diff -ruBEN polygraph-4.0.11/src/client/Makefile.am polygraph-4.0.11-mm/src/client/Makefile.am --- polygraph-4.0.11/src/client/Makefile.am 2010-02-13 16:48:22.000000000 +0000 +++ polygraph-4.0.11-mm/src/client/Makefile.am 2011-02-12 13:53:42.000000000 +0000 @@ -93,6 +93,9 @@ SpnegoCodec.cc \ NtlmAuth.h \ NtlmAuth.cc \ + GssKrbCtx.h \ + KerberosAuth.h \ + KerberosAuth.cc \ RangeCfg.h \ RangeCfg.cc \ SingleRangeCfg.h \ diff -ruBEN polygraph-4.0.11/src/pgl/RobotSym.cc polygraph-4.0.11-mm/src/pgl/RobotSym.cc --- polygraph-4.0.11/src/pgl/RobotSym.cc 2010-01-22 23:57:25.000000000 +0000 +++ polygraph-4.0.11-mm/src/pgl/RobotSym.cc 2011-02-12 20:37:50.000000000 +0000 @@ -49,6 +49,11 @@ static String strHttpProxies = "http_proxies"; static String strRecurrence = "recurrence"; static String strSpnegoAuthRatio = "spnego_auth_ratio"; +static String strKerberosAuth = "kerberos_auth"; +static String strKerberosClearCache = "kerberos_clear_cache"; +static String strKerberosConfigPath = "kerberos_config_path"; +static String strKerberosProxySPN = "kerberos_proxy_spn"; +static String strKerberosServerSPN = "kerberos_server_spn"; static String strReq_inter_arrival = "req_inter_arrival"; static String strReq_methods = "req_methods"; static String strContainerTags = "container_tags"; @@ -84,6 +89,11 @@ theRec->bAdd(strTime_distr, strReq_inter_arrival, 0); theRec->bAdd(NumSym::TheType, strRecurrence, 0); theRec->bAdd(NumSym::TheType, strSpnegoAuthRatio, 0); + theRec->bAdd(BoolSym::TheType, strKerberosAuth, 0); + theRec->bAdd(BoolSym::TheType, strKerberosClearCache, 0); + theRec->bAdd(StringSym::TheType, strKerberosConfigPath, 0); + theRec->bAdd(StringSym::TheType, strKerberosProxySPN, 0); + theRec->bAdd(StringSym::TheType, strKerberosServerSPN, 0); theRec->bAdd(NumSym::TheType, strEmbed_recur, 0); theRec->bAdd(strStringArr, strInterests, 0); theRec->bAdd(strStringArr, strReq_types, 0); @@ -166,6 +176,27 @@ bool RobotSym::spnegoRatio(double &ratio) const { return getDouble(strSpnegoAuthRatio, ratio); } + +bool RobotSym::kerberos(bool &set) const { + return getBool(strKerberosAuth, set); +} + +bool RobotSym::kerberosClearCache(bool &set) const { + return getBool(strKerberosClearCache, set); +} + +String RobotSym::kerberosConfigPath() const { + return getString(strKerberosConfigPath); +} + +String RobotSym::kerberosProxySPN() const { + return getString(strKerberosProxySPN); +} + +String RobotSym::kerberosServerSPN() const { + return getString(strKerberosServerSPN); +} + bool RobotSym::embedRecurRatio(double &ratio) const { return getDouble(strEmbed_recur, ratio); } diff -ruBEN polygraph-4.0.11/src/pgl/RobotSym.h polygraph-4.0.11-mm/src/pgl/RobotSym.h --- polygraph-4.0.11/src/pgl/RobotSym.h 2010-01-22 23:57:25.000000000 +0000 +++ polygraph-4.0.11-mm/src/pgl/RobotSym.h 2011-02-12 20:37:45.000000000 +0000 @@ -31,6 +31,11 @@ bool reqInterArrival(RndDistr *&iad) const; bool recurRatio(double &ratio) const; bool spnegoRatio(double &ratio) const; + bool kerberos(bool &set) const; + bool kerberosClearCache(bool &set) const; + String kerberosConfigPath() const; + String kerberosProxySPN() const; + String kerberosServerSPN() const; bool embedRecurRatio(double &ratio) const; bool uniqueUrls(bool &set) const; bool openConnLimit(int &lmt) const; diff -ruBEN polygraph-4.0.11/src/runtime/NtlmAuthState.h polygraph-4.0.11-mm/src/runtime/NtlmAuthState.h --- polygraph-4.0.11/src/runtime/NtlmAuthState.h 2007-05-12 01:04:16.000000000 +0100 +++ polygraph-4.0.11-mm/src/runtime/NtlmAuthState.h 2011-02-12 13:53:42.000000000 +0000 @@ -7,6 +7,6 @@ #define POLYGRAPH__RUNTIME_NTLM_AUTH_STATE_H typedef enum { ntlmNone, // No NTML is used or not initiated - ntlmSentT1, ntlmSentT3, ntlmDone, ntlmError } NtlmAuthState; + ntlmSentT1, ntlmSentT3, ntlmDone, ntlmError ,kerberosSentT1, kerberosDone, kerberosError} NtlmAuthState; #endif