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 <krb5.h>
+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 <krb5.h>
+#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 <krb5.h>
+}
+#else
+#include <krb5.h>
+#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 <gssapi.h>
+#elif HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif /* HAVE_GSSAPI_H */
+
+#if !HAVE_HEIMDAL_KERBEROS
+#if HAVE_GSSAPI_GSSAPI_EXT_H
+#include <gssapi/gssapi_ext.h>
+#endif /* HAVE_GSSAPI_GSSAPI_EXT_H */
+#if HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */
+#if HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#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 <krb5.h>
+}
+#else
+#include <krb5.h>
+#endif
+#endif /* HAVE_KRB5_H */
+
+#ifdef HAVE_GSSAPI_H
+#include <gssapi.h>
+#elif defined(HAVE_GSSAPI_GSSAPI_H)
+#include <gssapi/gssapi.h>
+#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 <gssapi/gssapi_ext.h>
+#endif /* HAVE_GSSAPI_GSSAPI_EXT_H */
+#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */
+#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#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 <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#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 <et/com_err.h>
+#elif defined(HAVE_COM_ERR_H)
+#include <com_err.h>
+#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) <<endc;
+        return kerberosError;
+  }
+
+  memcpy((void *)gssBuf,(const void *)gssOutputToken.value,(size_t)gssOutputToken.length);
+
+  PrintBase64(os, (const char *)gssBuf, gssOutputToken.length);
+  gss_release_buffer(&gssMinorStatus, &gssInputToken);
+  gss_release_buffer(&gssMinorStatus, &gssOutputToken);
+
+  if (gssEnvironment.gssMajorStatus == GSS_S_CONTINUE_NEEDED) {
+//        If mutual authentication is wished use value from Proxy-Authentication-Info header
+//        from a 200 OK.
+//        Comment << "NegoKerberosAuthPrintT3 warning: CONTINUE NEEDED" << endc;
+        return kerberosSentT1;
+  }
+
+  return kerberosDone;
+}
+
+//
+// Do initial ticket creation
+//
+
+NtlmAuthState CltGssXact::NegoKerberosAuthPrintT1(ostream & os, String userp,
+			String passwdp, String server, String krbCfgPath, bool clearKrbCc,
+			String krbSPN)
+{
+  gss_buffer_desc gssService;
+  String theGssHostName; 
+  String theGssServiceName; 
+  
+  gssService.length = 0;
+  gssService.value = NULL;
+
+  // Initialise kerberos
+  krbEnvironment.krbUserp = userp; 
+  krbEnvironment.krbPasswdp = passwdp; 
+  krbEnvironment.krbServerName = server; 
+  krbEnvironment.krbCfgPath = krbCfgPath; 
+  krbEnvironment.krbSPN = krbSPN; 
+  krbEnvironment.clearKrbCc = clearKrbCc; 
+
+  if (krbEnvironment.krbSetup() == kerberosError)
+	return kerberosError;
+
+  // Cleanup old values
+  gssEnvironment.gssCleanup();
+
+  // Create GSS Service name 
+  theGssHostName = server(0,server.find(':'));
+
+  if (krbSPN.len() > 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

