diff -rubwBEN 3.4/configure.ac 3.4-mm/configure.ac
--- 3.4/configure.ac	2013-08-24 19:10:13.310836377 +0100
+++ 3.4-mm/configure.ac	2013-08-24 17:52:47.001721169 +0100
@@ -1906,10 +1906,7 @@
     AC_DEFINE(HAVE_KRB5_GET_ERROR_MESSAGE,1,
       [Define to 1 if you have krb5_get_error_message]),)
   AC_CHECK_DECLS(krb5_kt_free_entry,,,[#include <krb5.h>])
-  AC_CHECK_TYPE(krb5_pac,
-    AC_DEFINE(HAVE_KRB5_PAC,1,
-	[Define to 1 if you have krb5_pac]),,
-	[#include <krb5.h>])
+  AC_CHECK_DECLS(krb5_pac,,,[#include <krb5.h>])
   AC_CHECK_LIB(krb5,krb5_kt_free_entry,
     AC_DEFINE(HAVE_KRB5_KT_FREE_ENTRY,1,
       [Define to 1 if you have krb5_kt_free_entry]),)
diff -rubwBEN 3.4/helpers/external_acl/kerberos_ldap_group/kerberos_ldap_group.cc 3.4-mm/helpers/external_acl/kerberos_ldap_group/kerberos_ldap_group.cc
--- 3.4/helpers/external_acl/kerberos_ldap_group/kerberos_ldap_group.cc	2013-08-12 00:01:44.458703000 +0100
+++ 3.4-mm/helpers/external_acl/kerberos_ldap_group/kerberos_ldap_group.cc	2013-08-25 01:11:05.941373265 +0100
@@ -413,8 +413,14 @@
                 log((char *) "%s| %s: INFO: Got User: %s Netbios Name: %s\n", LogTime(), PROGRAM, up, np);
             domain = get_netbios_name(&margs, netbios);
             user = nuser;
+            if (up) { 
             xfree(up);
+		up=NULL;
+	    }
+	    if (np) {
             xfree(np);
+	        np=NULL;
+	    }
         } else if (domain) {
             strup(domain);
             *domain = '\0';
@@ -436,8 +442,14 @@
         else
             log((char *) "%s| %s: INFO: Got User: %s Domain: %s\n", LogTime(), PROGRAM, up, domain ? dp : "NULL");
 
+        if (up) {
         xfree(up);
+	    up=NULL;
+	}
+	if (dp) {
         xfree(dp);
+	    dp=NULL;
+	}
         if (!strcmp(user, "QQ") && domain && !strcmp(domain, "QQ")) {
             clean_args(&margs);
             exit(-1);
diff -rubwBEN 3.4/helpers/external_acl/kerberos_ldap_group/support_krb5.cc 3.4-mm/helpers/external_acl/kerberos_ldap_group/support_krb5.cc
--- 3.4/helpers/external_acl/kerberos_ldap_group/support_krb5.cc	2013-08-12 00:01:44.458703000 +0100
+++ 3.4-mm/helpers/external_acl/kerberos_ldap_group/support_krb5.cc	2013-08-25 16:10:21.140711113 +0100
@@ -184,10 +184,10 @@
     if (!principal_name) {
         debug((char *) "%s| %s: DEBUG: Did not find a principal in keytab for domain %s.\n", LogTime(), PROGRAM, domain);
         debug((char *) "%s| %s: DEBUG: Try to get principal of trusted domain.\n", LogTime(), PROGRAM);
-        creds = (krb5_creds *) xmalloc(sizeof(*creds));
-        memset(creds, 0, sizeof(*creds));
 
         for (i = 0; i < nprinc; ++i) {
+            creds = (krb5_creds *) xmalloc(sizeof(*creds));
+            memset(creds, 0, sizeof(*creds));
             /*
              * get credentials
              */
@@ -205,8 +205,10 @@
             snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain);
             creds->client = principal_list[i];
             code = krb5_parse_name(kparam.context, service, &creds->server);
-            if (service)
+            if (service) {
                 xfree(service);
+		service = NULL;
+	    }
             code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0);
 #endif
             if (code) {
@@ -233,8 +235,10 @@
             snprintf(service, strlen("krbtgt") + strlen(domain) + strlen(krb5_princ_realm(kparam.context, principal_list[i])->data) + 3, "krbtgt/%s@%s", domain, krb5_princ_realm(kparam.context, principal_list[i])->data);
 #endif
             code = krb5_parse_name(kparam.context, service, &creds->server);
-            if (service)
+            if (service) {
                 xfree(service);
+		service=NULL;
+	    }
             if (code) {
                 error((char *) "%s| %s: ERROR: Error while initialising TGT credentials : %s\n", LogTime(), PROGRAM, error_message(code));
                 goto loop_end;
@@ -253,8 +257,18 @@
             if (principal_name)
                 xfree(principal_name);
             principal_name = NULL;
+	    if (tgt_creds)
+		krb5_free_creds(kparam.context, tgt_creds);
+	    tgt_creds = NULL;
+	    if (creds)
+		krb5_free_creds(kparam.context, creds);
+            creds = NULL;
+
         }
 
+        if (principal_name)
+            xfree(principal_name);
+        principal_name = NULL;
         if (tgt_creds)
             krb5_free_creds(kparam.context, tgt_creds);
         tgt_creds = NULL;
@@ -287,8 +301,10 @@
         snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain);
         creds->client = principal;
         code = krb5_parse_name(kparam.context, service, &creds->server);
-        if (service)
+        if (service) {
             xfree(service);
+	    service = NULL;
+	}
         code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0);
 #endif
         if (code) {
@@ -318,10 +334,13 @@
         krb5_kt_close(kparam.context, keytab);
     if (keytab_name)
         xfree(keytab_name);
+    keytab_name = NULL;
     if (principal_name)
         xfree(principal_name);
+    principal_name = NULL;
     if (mem_cache)
         xfree(mem_cache);
+    mem_cache = NULL;
     if (principal)
         krb5_free_principal(kparam.context, principal);
     for (i = 0; i < nprinc; ++i) {
@@ -330,6 +349,7 @@
     }
     if (principal_list)
         xfree(principal_list);
+    principal_list = NULL;
     if (creds)
         krb5_free_creds(kparam.context, creds);
 
diff -rubwBEN 3.4/helpers/external_acl/kerberos_ldap_group/support_ldap.cc 3.4-mm/helpers/external_acl/kerberos_ldap_group/support_ldap.cc
--- 3.4/helpers/external_acl/kerberos_ldap_group/support_ldap.cc	2013-08-12 00:01:44.458703000 +0100
+++ 3.4-mm/helpers/external_acl/kerberos_ldap_group/support_ldap.cc	2013-08-25 14:15:16.466539908 +0100
@@ -588,15 +588,15 @@
                     if ((values = ldap_get_values_len(ld, msg, attr)) != NULL) {
                         for (il = 0; values[il] != NULL; ++il) {
 
-                            attr_value = (char **) xrealloc(attr_value, (il + 1) * sizeof(char *));
+                            attr_value = (char **) xrealloc(attr_value, (max_attr + 1) * sizeof(char *));
                             if (!attr_value)
                                 break;
 
-                            attr_value[il] = (char *) xmalloc(values[il]->bv_len + 1);
-                            memcpy(attr_value[il], values[il]->bv_val, values[il]->bv_len);
-                            attr_value[il][values[il]->bv_len] = 0;
+                            attr_value[max_attr] = (char *) xmalloc(values[il]->bv_len + 1);
+                            memcpy(attr_value[max_attr], values[il]->bv_val, values[il]->bv_len);
+                            attr_value[max_attr][values[il]->bv_len] = 0;
+                            max_attr++;
                         }
-                        max_attr = il;
                     }
                     ber_bvecfree(values);
                 }
@@ -940,7 +940,10 @@
 
         }
         nhosts = free_hostname_list(&hlist, nhosts);
+	if (bindp) {
         xfree(bindp);
+	    bindp = NULL;
+	}
         if (margs->lbind) {
             bindp = xstrdup(margs->lbind);
         } else {
@@ -1180,10 +1183,13 @@
         krb5_cleanup();
 #endif
     if (lcreds) {
+	if (lcreds->dn)
         xfree(lcreds->dn);
+	if (lcreds->pw)
         xfree(lcreds->pw);
         xfree(lcreds);
     }
+    if (bindp)
     xfree(bindp);
     return (retval);
 }
diff -rubwBEN 3.4/helpers/negotiate_auth/kerberos/Makefile.am 3.4-mm/helpers/negotiate_auth/kerberos/Makefile.am
--- 3.4/helpers/negotiate_auth/kerberos/Makefile.am	2013-08-12 00:01:44.458703000 +0100
+++ 3.4-mm/helpers/negotiate_auth/kerberos/Makefile.am	2013-08-23 13:16:29.677167792 +0100
@@ -7,7 +7,7 @@
 
 AM_CPPFLAGS = $(INCLUDES) -I$(srcdir)
 
-negotiate_kerberos_auth_SOURCES = negotiate_kerberos_auth.cc
+negotiate_kerberos_auth_SOURCES = negotiate_kerberos_auth.cc negotiate_kerberos_pac.cc
 negotiate_kerberos_auth_LDFLAGS = 
 negotiate_kerberos_auth_LDADD = \
 	$(top_builddir)/lib/libmiscencoding.la \
diff -rubwBEN 3.4/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc 3.4-mm/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc
--- 3.4/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc	2013-08-12 00:01:44.458703000 +0100
+++ 3.4-mm/helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc	2013-08-24 22:20:20.064119213 +0100
@@ -36,79 +36,7 @@
 
 #if HAVE_GSSAPI
 
-#if HAVE_STRING_H
-#include <string.h>
-#endif
-#if HAVE_STDOI_H
-#include <stdio.h>
-#endif
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_TIME_H
-#include <time.h>
-#endif
-
-#include "util.h"
-#include "base64.h"
-
-#if HAVE_GSSAPI_GSSAPI_H
-#include <gssapi/gssapi.h>
-#elif HAVE_GSSAPI_H
-#include <gssapi.h>
-#endif
-
-#if !HAVE_HEIMDAL_KERBEROS
-#if HAVE_GSSAPI_GSSAPI_KRB5_H
-#include <gssapi/gssapi_krb5.h>
-#endif
-#if HAVE_GSSAPI_GSSAPI_GENERIC_H
-#include <gssapi/gssapi_generic.h>
-#endif
-#if HAVE_GSSAPI_GSSAPI_EXT_H
-#include <gssapi/gssapi_ext.h>
-#endif
-#endif
-
-#ifndef gss_nt_service_name
-#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
-#endif
-
-#define PROGRAM "negotiate_kerberos_auth"
-
-#ifndef MAX_AUTHTOKEN_LEN
-#define MAX_AUTHTOKEN_LEN   65535
-#endif
-#ifndef SQUID_KERB_AUTH_VERSION
-#define SQUID_KERB_AUTH_VERSION "3.0.4sq"
-#endif
-
-int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
-                  const char *function, int log);
-char *gethost_name(void);
-static const char *LogTime(void);
-
-static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
-
-static const char *
-LogTime()
-{
-    struct tm *tm;
-    struct timeval now;
-    static time_t last_t = 0;
-    static char buf[128];
-
-    gettimeofday(&now, NULL);
-    if (now.tv_sec != last_t) {
-        tm = localtime((time_t *) & now.tv_sec);
-        strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
-        last_t = now.tv_sec;
-    }
-    return buf;
-}
+#include "negotiate_kerberos.h"
 
 char *
 gethost_name(void)
@@ -213,6 +141,19 @@
     char buf[MAX_AUTHTOKEN_LEN];
     char *c, *p;
     char *user = NULL;
+    char *rfc_user = NULL;
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_KRB5_PAC
+    char ad_groups[MAX_PAC_GROUP_SIZE];
+    char *ag=NULL;
+    krb5_context context = NULL;
+    krb5_error_code ret;
+    krb5_pac pac;
+#if HAVE_HEIMDAL_KERBEROS
+    gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
+#else
+    gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
+#endif
+#endif
     int length = 0;
     static int err = 0;
     int opt, log = 0, norealm = 0;
@@ -284,6 +225,8 @@
         snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2,
                  "%s@%s", service_name, host_name);
         service.length = strlen((char *) service.value);
+        xfree(host_name);
+        host_name = NULL;
     }
 
     while (1) {
@@ -351,10 +294,6 @@
                 xfree(token);
                 token = NULL;
             }
-            if (host_name) {
-                xfree(host_name);
-                host_name = NULL;
-            }
             fprintf(stdout, "BH quit command\n");
             exit(0);
         }
@@ -458,8 +397,44 @@
             if (norealm && (p = strchr(user, '@')) != NULL) {
                 *p = '\0';
             }
+
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_KRB5_PAC
+	    ret = krb5_init_context(&context);
+	    if (!check_k5_err(context, "krb5_init_context", ret)) {
+#define ADWIN2KPAC 128
+#if HAVE_HEIMDAL_KERBEROS
+		major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status, 
+				gss_context, ADWIN2KPAC, &data_set);
+		if (!check_gss_err(major_status, minor_status, 
+				"gsskrb5_extract_authz_data_from_sec_context()", log)) {
+		    ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac);
+		    gss_release_buffer(&minor_status, &data_set);
+		    if (!check_k5_err(context, "krb5_pac_parse", ret)) {
+			ag = get_ad_groups((char *)&ad_groups, context, pac);
+			krb5_pac_free(context, pac);
+		    }
+		    krb5_free_context(context);
+		}
+#else
+		type_id.value = (void *)"mspac";
+		type_id.length = strlen((char *)type_id.value);
+
+		major_status = gss_map_name_to_any(&minor_status, client_name, 1, &type_id, (gss_any_t *)&pac);
+		if (!check_gss_err(major_status, minor_status, "gss_map_name_to_any()", log)) {
+		    ag = get_ad_groups((char *)&ad_groups,context, pac);
+		}
+		(void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
+		krb5_free_context(context);
+#endif
+	    }
+	    if (ag) {
+		debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM, ag);
+	    }
+#endif
             fprintf(stdout, "AF %s %s\n", token, user);
-            debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token, rfc1738_escape(user));
+	    rfc_user = rfc1738_escape(user);
+            debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token, rfc_user);
+/*	    xfree(rfc_user); */
             if (log)
                 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
                         PROGRAM, rfc1738_escape(user));
diff -rubwBEN 3.4/helpers/negotiate_auth/kerberos/negotiate_kerberos.h 3.4-mm/helpers/negotiate_auth/kerberos/negotiate_kerberos.h
--- 3.4/helpers/negotiate_auth/kerberos/negotiate_kerberos.h	1970-01-01 01:00:00.000000000 +0100
+++ 3.4-mm/helpers/negotiate_auth/kerberos/negotiate_kerberos.h	2013-08-24 19:11:59.141839001 +0100
@@ -0,0 +1,154 @@
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2013 Markus Moeller. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *   As a special exemption, M Moeller gives permission to link this program
+ *   with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
+ *   the resulting executable, without including the source code for
+ *   the Libraries in the source distribution.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "util.h"
+#include "base64.h"
+
+#if HAVE_KRB5_H
+#if HAVE_BROKEN_SOLARIS_KRB5_H
+#warn "Warning! You have a broken Solaris <krb5.h> system header"
+#warn "http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6837512"
+#if defined(__cplusplus)
+#define KRB5INT_BEGIN_DECLS     extern "C" {
+#define KRB5INT_END_DECLS
+KRB5INT_BEGIN_DECLS
+#endif
+#endif /* HAVE_BROKEN_SOLARIS_KRB5_H */
+#if HAVE_BROKEN_HEIMDAL_KRB5_H
+extern "C" {
+#include <krb5.h>
+}
+#else
+#include <krb5.h>
+#endif
+#endif /* HAVE_KRB5_H */
+
+#if HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#elif HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+
+#if !HAVE_HEIMDAL_KERBEROS
+#if HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif
+#if HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+#if HAVE_GSSAPI_GSSAPI_EXT_H
+#include <gssapi/gssapi_ext.h>
+#endif
+#else
+#if HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif
+#endif
+
+#ifndef gss_nt_service_name
+#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
+#endif
+
+#define PROGRAM "negotiate_kerberos_auth"
+
+#ifndef MAX_AUTHTOKEN_LEN
+#define MAX_AUTHTOKEN_LEN   65535
+#endif
+#ifndef SQUID_KERB_AUTH_VERSION
+#define SQUID_KERB_AUTH_VERSION "3.0.4sq"
+#endif
+
+typedef struct {
+    uint16_t length;
+    uint16_t maxlength;
+    uint32_t pointer;
+} RPC_UNICODE_STRING;
+
+char *gethost_name(void);
+
+static const char *LogTime(void);
+
+static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
+
+static const char *
+LogTime()
+{
+    struct tm *tm;
+    struct timeval now;
+    static time_t last_t = 0;
+    static char buf[128];
+
+    gettimeofday(&now, NULL);
+    if (now.tv_sec != last_t) {
+        tm = localtime((time_t *) & now.tv_sec);
+        strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
+        last_t = now.tv_sec;
+    }
+    return buf;
+}
+
+int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
+                  const char *function, int log);
+
+int check_k5_err(krb5_context context, const char *msg, krb5_error_code code);
+
+char *gethost_name(void);
+
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_KRB5_PAC
+#define MAX_PAC_GROUP_SIZE 200*60
+void align(int n);
+void getustr(RPC_UNICODE_STRING *string);
+char **getgids(char **Rids, uint32_t GroupIds, uint32_t GroupCount);
+char *getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t  GroupCount);
+char *getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount, uint32_t GroupCount);
+uint64_t get6byt_be(void);
+uint32_t get4byt(void);
+uint16_t get2byt(void);
+uint8_t get1byt(void);
+char *xstrcpy( char *src, const char*dst);
+char *xstrcat( char *src, const char*dst);
+int checkustr(RPC_UNICODE_STRING *string);
+char *get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac);
+#endif
diff -rubwBEN 3.4/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc 3.4-mm/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc
--- 3.4/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc	1970-01-01 01:00:00.000000000 +0100
+++ 3.4-mm/helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc	2013-08-24 22:39:51.018148248 +0100
@@ -0,0 +1,450 @@
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *   As a special exemption, M Moeller gives permission to link this program
+ *   with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
+ *   the resulting executable, without including the source code for
+ *   the Libraries in the source distribution.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "squid.h"
+#include "rfc1738.h"
+#include "compat/getaddrinfo.h"
+#include "compat/getnameinfo.h"
+
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_KRB5_PAC
+
+#include "negotiate_kerberos.h"
+
+static int bpos;
+static krb5_data *ad_data;
+static unsigned char *p;
+
+int
+check_k5_err(krb5_context context, const char *function, krb5_error_code code)
+{
+    const char *errmsg;
+
+    if (code) {
+	errmsg = krb5_get_error_message(context, code);
+        debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, errmsg);
+	fprintf(stderr, "%s| %s: ERROR: %s: %s\n", LogTime(), PROGRAM, function, errmsg);
+	krb5_free_error_message(context, errmsg);
+    }
+    return code;
+}
+
+void
+align(int n){
+    if ( bpos % n != 0 ) {
+	int al;
+	al = (bpos/n);
+	bpos = bpos+(bpos-n*al);
+    }
+}
+
+void
+getustr(RPC_UNICODE_STRING *string) {
+
+    string->length = (p[bpos]<<0) | (p[bpos+1]<<8);
+    string->maxlength = (p[bpos+2]<<0) | (p[bpos+2+1]<<8);
+    string->pointer = (p[bpos+4]<<0) | (p[bpos+4+1]<<8) | (p[bpos+4+2]<<16) | (p[bpos+4+3]<<24);
+    bpos = bpos+8;
+
+}
+
+uint64_t
+get6byt_be(void) {
+    uint64_t var;
+
+    var = ((int64_t)p[bpos+5]<<0) | ((int64_t)p[bpos+4]<<8) | ((int64_t)p[bpos+3]<<16) | ((int64_t)p[bpos+2]<<24) | ((int64_t)p[bpos+1]<<32) | ((int64_t)p[bpos]<<40);
+    bpos = bpos+6;
+
+    return var;
+}
+
+uint32_t
+get4byt(void) {
+    uint32_t var;
+
+    var=(p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+    bpos = bpos+4;
+
+    return var;
+}
+
+uint16_t 
+get2byt(void) {
+    uint16_t var;
+
+    var=(p[bpos]<<0) | (p[bpos+1]<<8); 
+    bpos = bpos+2;
+
+    return var;
+}
+
+uint8_t
+get1byt(void) {
+    uint8_t var;
+
+    var=(p[bpos]<<0);
+    bpos = bpos+1;
+
+    return var;
+}
+
+char *
+xstrcpy( char *src, const char*dst) {
+    if (dst) {
+	if (strlen(dst)>MAX_PAC_GROUP_SIZE)
+	    return NULL;
+	else
+	    return strcpy(src,dst);
+    } else
+	return src;
+}
+
+char *
+xstrcat( char *src, const char*dst) {
+    if (dst) {
+        if (strlen(src)+strlen(dst)+1>MAX_PAC_GROUP_SIZE)
+            return NULL;
+        else
+            return strcat(src,dst);
+    } else
+	return src;
+}
+
+
+int
+checkustr(RPC_UNICODE_STRING *string) {
+    uint32_t size,off,len;
+
+    if (string->pointer != 0){
+	align(4);
+	size = (p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+	bpos = bpos+4;
+	off = (p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+	bpos = bpos+4;
+	len = (p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+	bpos = bpos+4;
+	if (len > size || off != 0 || 
+	    string->length > string->maxlength || len != string->length/2) {
+	    debug((char *) "%s| %s: ERROR: RPC_UNICODE_STRING encoding error => size: %d len: %d/%d maxlength: %d offset: %d\n", 
+		    LogTime(), PROGRAM, size, len, string->length, string->maxlength, off);
+	    return -1;
+	}
+	/* UNICODE string */
+	bpos = bpos+string->length;
+    }
+    return 0;
+}
+
+char **
+getgids(char **Rids, uint32_t GroupIds, uint32_t  GroupCount) {
+    if (GroupIds!= 0){
+        uint32_t ngroup;
+        uint32_t sauth;
+        int l;
+
+        align(4);
+        ngroup = get4byt();
+        if ( ngroup != GroupCount) {
+            debug((char *) "%s| %s: ERROR: Group encoding error => GroupCount: %d Array size: %d\n",
+                                LogTime(), PROGRAM, GroupCount, ngroup);
+            return NULL;
+        }
+        debug((char *) "%s| %s: INFO: Found %d rids\n", LogTime(), PROGRAM, GroupCount);
+
+	Rids=(char **)xcalloc(GroupCount*sizeof(char*),1);
+        for ( l=0; l<(int)GroupCount; l++) {
+	    Rids[l]=(char *)xcalloc(4*sizeof(char),1);
+            memcpy((void *)Rids[l],(void *)&p[bpos],4); 
+            sauth = get4byt();
+	    debug((char *) "%s| %s: Info: Got rid: %u\n", LogTime(), PROGRAM, sauth);
+	    /* attribute */
+            bpos = bpos+4; 
+        }
+    }
+    return Rids;
+}
+
+char *
+getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t  GroupCount) {
+    if (DomainLogonId!= 0) {
+        uint32_t nauth;
+        uint8_t rev;
+        uint64_t idauth;
+        uint32_t sauth;
+	char dli[256];
+	char *ag;
+        int length;
+        int l;
+
+        align(4);
+
+        nauth = get4byt();
+	
+	/* prepend rids with DomainID */
+        length=1+1+6+nauth*4;
+	for (l=0; l<(int)GroupCount;l++){
+	    ag=(char *)xcalloc((length+4)*sizeof(char),1);
+	    memcpy((void *)ag,(const void*)&p[bpos],1);
+	    memcpy((void *)&ag[1],(const void*)&p[bpos+1],1);
+	    ag[1] = ag[1]+1;
+	    memcpy((void *)&ag[2],(const void*)&p[bpos+2],6+nauth*4);
+	    memcpy((void *)&ag[length],(const void*)Rids[l],4);
+	    if (l==0) {
+		if (!xstrcpy(ad_groups,"group=")) {
+		    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+				LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		}
+	    } else {
+		if (!xstrcat(ad_groups," group=")) {
+                    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+                                LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		}
+	    }
+	    if (!xstrcat(ad_groups,base64_encode_bin(ag, length+4))) {
+                    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+                                LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+	    }
+	    xfree(ag);
+	    xfree(Rids[l]);
+	}
+	xfree(Rids);
+
+	/* mainly for debug only */
+        rev = get1byt();
+        bpos = bpos + 1; /*nsub*/
+        idauth = get6byt_be();
+
+	snprintf(dli,sizeof(dli),"S-%d-%lu",rev,(long unsigned int)idauth);	
+        for ( l=0;l<(int)nauth;l++ ) {
+            sauth = get4byt();
+	    snprintf((char *)&dli[strlen(dli)],sizeof(dli)-strlen(dli),"-%u",sauth);	
+        }
+        debug((char *) "%s| %s: INFO: Got DomainLogonId %s\n", LogTime(), PROGRAM, dli); 
+    }
+    return ad_groups;
+}
+
+char *
+getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount, uint32_t GroupCount) {
+    if (ExtraSids!= 0){
+        uint32_t ngroup;
+	uint32_t *pa;
+	char *ag;
+        int length;
+        int l;
+
+        align(4);
+        ngroup = get4byt();
+        if ( ngroup != SidCount) {
+            debug((char *) "%s| %s: ERROR: Group encoding error => SidCount: %d Array size: %d\n",
+                                LogTime(), PROGRAM, SidCount, ngroup);
+	    return NULL;
+        }
+        debug((char *) "%s| %s: INFO: Found %d ExtraSIDs\n", LogTime(), PROGRAM, SidCount);
+
+	pa=(uint32_t *)xmalloc(SidCount*sizeof(uint32_t));
+        for ( l=0; l < (int)SidCount; l++ ){
+            pa[l] = get4byt();
+            bpos = bpos+4; /* attr */
+        }
+        
+        for ( l=0; l<(int)SidCount; l++ ){
+	    char es[256];
+            uint32_t nauth;
+            uint8_t rev;
+            uint64_t idauth;
+            uint32_t sauth;
+            int k;
+            
+	    if (pa[l] != 0){
+		nauth = get4byt();
+
+		length = 1+1+6+nauth*4;
+		ag = (char *)xcalloc((length)*sizeof(char),1);
+		memcpy((void *)ag,(const void*)&p[bpos],length);
+		if (!ad_groups) {
+		    if (!xstrcpy(ad_groups,"group=")) {
+			debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+                                	LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		    }
+		} else {
+		    if (!xstrcat(ad_groups," group=")) {
+			debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
+					LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		    }
+		}
+		if (!xstrcat(ad_groups,base64_encode_bin(ag, length))) {
+                    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
+                                LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		}
+		xfree(ag);
+
+		rev = get1byt();
+		bpos = bpos + 1; /* nsub */
+		idauth = get6byt_be();
+
+		snprintf(es,sizeof(es),"S-%d-%lu",rev,(long unsigned int)idauth);	
+		for ( k=0;k<(int)nauth;k++ ) {
+		    sauth = get4byt();
+		    snprintf((char *)&es[strlen(es)],sizeof(es)-strlen(es),"-%u",sauth);	
+		}
+		debug((char *) "%s| %s: INFO: Got ExtraSid %s\n", LogTime(), PROGRAM, es); 
+	    }
+        }
+	xfree(pa);
+    }
+    return ad_groups;
+}
+
+char *
+get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac){
+    krb5_error_code ret;
+    RPC_UNICODE_STRING EffectiveName;
+    RPC_UNICODE_STRING FullName;
+    RPC_UNICODE_STRING LogonScript;
+    RPC_UNICODE_STRING ProfilePath;
+    RPC_UNICODE_STRING HomeDirectory;
+    RPC_UNICODE_STRING HomeDirectoryDrive;
+    RPC_UNICODE_STRING LogonServer;
+    RPC_UNICODE_STRING LogonDomainName;
+    uint32_t GroupCount=0;
+    uint32_t GroupIds=0;
+    uint32_t LogonDomainId=0;
+    uint32_t SidCount=0;
+    uint32_t ExtraSids=0;
+    /*
+    uint32_t ResourceGroupDomainSid=0;
+    uint32_t ResourceGroupCount=0;
+    uint32_t ResourceGroupIds=0;
+    */
+    char **Rids=NULL;
+    int l=0;
+
+    ad_data = (krb5_data *)xmalloc(sizeof(krb5_data));
+
+#define KERB_LOGON_INFO 1
+    ret = krb5_pac_get_buffer(context, pac, KERB_LOGON_INFO, ad_data);
+    if (check_k5_err(context, "krb5_pac_get_buffer", ret)) 
+	goto k5clean;
+    
+    p = (unsigned char *)ad_data->data;
+
+    debug((char *) "%s| %s: INFO: Got PAC data of lengh %d\n", 
+		    LogTime(), PROGRAM, (int)ad_data->length);
+
+    /* Skip 16 bytes icommon RPC header
+     * Skip 4 bytes RPC unique pointer referent
+     * http://msdn.microsoft.com/en-gb/library/cc237933.aspx
+     */
+    /* Some data are pointers to data which follows the main KRB5 LOGON structure => 
+     *         So need to read the data 
+     * some logical consistency checks are done when analysineg the pointer data
+     */
+    bpos = 20;
+    /* 8 bytes LogonTime
+     * 8 bytes LogoffTime
+     * 8 bytes KickOffTime
+     * 8 bytes PasswordLastSet
+     * 8 bytes PasswordCanChange
+     * 8 bytes PasswordMustChange
+     */
+    bpos = bpos+48;
+    getustr(&EffectiveName);
+    getustr(&FullName);
+    getustr(&LogonScript);
+    getustr(&ProfilePath);
+    getustr(&HomeDirectory);
+    getustr(&HomeDirectoryDrive);
+    /* 2 bytes LogonCount
+     * 2 bytes BadPasswordCount
+     * 4 bytes UserID
+     * 4 bytes PrimaryGroupId
+     */
+    bpos = bpos+12;
+    GroupCount = get4byt();
+    GroupIds = get4byt();
+    /* 4 bytes UserFlags
+     * 16 bytes UserSessionKey
+     */
+    bpos = bpos+20;
+    getustr(&LogonServer);
+    getustr(&LogonDomainName);
+    LogonDomainId = get4byt();
+    /* 8 bytes Reserved1
+     * 4 bytes UserAccountControl
+     * 4 bytes SubAuthStatus
+     * 8 bytes LastSuccessfullLogon
+     * 8 bytes LastFailedLogon
+     * 4 bytes FailedLogonCount
+     * 4 bytes Reserved2
+     */
+    bpos = bpos+40;
+    SidCount = get4byt();
+    ExtraSids = get4byt();
+    /* 4 bytes ResourceGroupDomainSid
+     * 4 bytes ResourceGroupCount
+     * 4 bytes ResourceGroupIds
+     */
+    bpos = bpos+12;
+    /* 
+     * Read all data from structure => Now check pointers
+     */
+    if (checkustr(&EffectiveName)<0)
+	goto k5clean;
+    if (checkustr(&FullName)<0)
+	goto k5clean;
+    if (checkustr(&LogonScript)<0)
+	goto k5clean;
+    if (checkustr(&ProfilePath)<0)
+	goto k5clean;
+    if (checkustr(&HomeDirectory)<0)
+	goto k5clean;
+    if (checkustr(&HomeDirectoryDrive)<0)
+	goto k5clean;
+    Rids = getgids(Rids,GroupIds,GroupCount);
+    if (checkustr(&LogonServer)<0)
+	goto k5clean;
+    if (checkustr(&LogonDomainName)<0)
+	goto k5clean;
+    ad_groups = getdomaingids(ad_groups,LogonDomainId,Rids,GroupCount);
+    if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount,GroupCount))==NULL)
+        goto k5clean;
+    
+    debug((char *) "%s| %s: INFO: Read %d of %d bytes \n", LogTime(), PROGRAM, bpos, (int)ad_data->length);
+    krb5_free_data(context, ad_data);
+    return ad_groups; 
+k5clean:
+    for ( l=0; l<(int)GroupCount; l++) {
+	if (Rids[l])
+	    xfree(Rids[l]);
+    }
+    xfree(Rids);
+    krb5_free_data(context, ad_data);
+    return NULL;
+}
+#endif
diff -rubwBEN 3.4/helpers/negotiate_auth/kerberos/test_negotiate_auth.sh 3.4-mm/helpers/negotiate_auth/kerberos/test_negotiate_auth.sh
--- 3.4/helpers/negotiate_auth/kerberos/test_negotiate_auth.sh	1970-01-01 01:00:00.000000000 +0100
+++ 3.4-mm/helpers/negotiate_auth/kerberos/test_negotiate_auth.sh	2013-08-24 18:33:52.091782292 +0100
@@ -0,0 +1,7 @@
+#!/bin/bash
+if [[ -z "$1" ]]; then
+	echo "Need squid hostname"
+	exit 0
+fi
+dir=`dirname $0`
+$dir/negotiate_kerberos_auth_test $1 | awk '{sub(/Token:/,"YR"); print $0}END{print "QQ"}' | $dir/negotiate_kerberos_auth -d
diff -rubwBEN 3.4/negotiate_kerberos_auth.cc 3.4-mm/negotiate_kerberos_auth.cc
--- 3.4/negotiate_kerberos_auth.cc	1970-01-01 01:00:00.000000000 +0100
+++ 3.4-mm/negotiate_kerberos_auth.cc	2013-08-24 14:45:18.571442258 +0100
@@ -0,0 +1,529 @@
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *   As a special exemption, M Moeller gives permission to link this program
+ *   with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
+ *   the resulting executable, without including the source code for
+ *   the Libraries in the source distribution.
+ *
+ * -----------------------------------------------------------------------------
+ */
+/*
+ * Hosted at http://sourceforge.net/projects/squidkerbauth
+ */
+#include "squid.h"
+#include "rfc1738.h"
+#include "compat/getaddrinfo.h"
+#include "compat/getnameinfo.h"
+
+#if HAVE_GSSAPI
+
+#include "negotiate_kerberos.h"
+
+char *
+gethost_name(void)
+{
+    /*
+     * char hostname[sysconf(_SC_HOST_NAME_MAX)];
+     */
+    char hostname[1024];
+    struct addrinfo *hres = NULL, *hres_list;
+    int rc, count;
+
+    rc = gethostname(hostname, sizeof(hostname)-1);
+    if (rc) {
+        fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
+                LogTime(), PROGRAM, hostname);
+        return NULL;
+    }
+    rc = getaddrinfo(hostname, NULL, NULL, &hres);
+    if (rc != 0) {
+        fprintf(stderr,
+                "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
+                LogTime(), PROGRAM, gai_strerror(rc));
+        return NULL;
+    }
+    hres_list = hres;
+    count = 0;
+    while (hres_list) {
+        ++count;
+        hres_list = hres_list->ai_next;
+    }
+    rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
+                     sizeof(hostname), NULL, 0, 0);
+    if (rc != 0) {
+        fprintf(stderr,
+                "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
+                LogTime(), PROGRAM, gai_strerror(rc));
+        freeaddrinfo(hres);
+        return NULL;
+    }
+    freeaddrinfo(hres);
+    hostname[sizeof(hostname)-1] = '\0';
+    return (xstrdup(hostname));
+}
+
+int
+check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
+              const char *function, int log)
+{
+    if (GSS_ERROR(major_status)) {
+        OM_uint32 maj_stat, min_stat;
+        OM_uint32 msg_ctx = 0;
+        gss_buffer_desc status_string;
+        char buf[1024];
+        size_t len;
+
+        len = 0;
+        msg_ctx = 0;
+        do {
+            /* convert major status code (GSS-API error) to text */
+            maj_stat = gss_display_status(&min_stat, major_status,
+                                          GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
+            if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
+                if (sizeof(buf) > len + status_string.length + 1) {
+                    snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
+                    len += status_string.length;
+                }
+            } else
+                msg_ctx = 0;
+            gss_release_buffer(&min_stat, &status_string);
+        } while (msg_ctx);
+        if (sizeof(buf) > len + 2) {
+            snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
+            len += 2;
+        }
+        msg_ctx = 0;
+        do {
+            /* convert minor status code (underlying routine error) to text */
+            maj_stat = gss_display_status(&min_stat, minor_status,
+                                          GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
+            if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
+                if (sizeof(buf) > len + status_string.length) {
+                    snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
+                    len += status_string.length;
+                }
+            } else
+                msg_ctx = 0;
+            gss_release_buffer(&min_stat, &status_string);
+        } while (msg_ctx);
+        debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
+        fprintf(stdout, "BH %s failed: %s\n", function, buf);
+        if (log)
+            fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
+                    PROGRAM);
+        return (1);
+    }
+    return (0);
+}
+
+int
+main(int argc, char *const argv[])
+{
+    char buf[MAX_AUTHTOKEN_LEN];
+    char *c, *p;
+    char *user = NULL;
+    char *rfc_user = NULL;
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_DECL_KRB5_PAC
+    char ad_groups[MAX_PAC_GROUP_SIZE];
+    char *ag=NULL;
+    krb5_context context = NULL;
+    krb5_error_code ret;
+    krb5_pac pac;
+#if HAVE_HEIMDAL_KERBEROS
+    gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
+#else
+    gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
+#endif
+#endif
+    int length = 0;
+    static int err = 0;
+    int opt, log = 0, norealm = 0;
+    OM_uint32 ret_flags = 0, spnego_flag = 0;
+    char *service_name = (char *) "HTTP", *host_name = NULL;
+    char *token = NULL;
+    char *service_principal = NULL;
+    OM_uint32 major_status, minor_status;
+    gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
+    gss_name_t client_name = GSS_C_NO_NAME;
+    gss_name_t server_name = GSS_C_NO_NAME;
+    gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
+    gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+    const unsigned char *kerberosToken = NULL;
+    const unsigned char *spnegoToken = NULL;
+    size_t spnegoTokenLength = 0;
+
+    setbuf(stdout, NULL);
+    setbuf(stdin, NULL);
+
+    while (-1 != (opt = getopt(argc, argv, "dirs:h"))) {
+        switch (opt) {
+        case 'd':
+            debug_enabled = 1;
+            break;
+        case 'i':
+            log = 1;
+            break;
+        case 'r':
+            norealm = 1;
+            break;
+        case 's':
+            service_principal = xstrdup(optarg);
+            break;
+        case 'h':
+            fprintf(stderr, "Usage: \n");
+            fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n");
+            fprintf(stderr, "-d full debug\n");
+            fprintf(stderr, "-i informational messages\n");
+            fprintf(stderr, "-r remove realm from username\n");
+            fprintf(stderr, "-s service principal name\n");
+            fprintf(stderr, "-h help\n");
+            fprintf(stderr,
+                    "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
+            fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
+            exit(0);
+        default:
+            fprintf(stderr, "%s| %s: WARNING: unknown option: -%c.\n", LogTime(),
+                    PROGRAM, opt);
+        }
+    }
+
+    debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
+    if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) {
+        service.value = service_principal;
+        service.length = strlen((char *) service.value);
+    } else {
+        host_name = gethost_name();
+        if (!host_name) {
+            fprintf(stderr,
+                    "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
+                    LogTime(), PROGRAM);
+            fprintf(stdout, "BH hostname error\n");
+            exit(-1);
+        }
+        service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2);
+        snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2,
+                 "%s@%s", service_name, host_name);
+        service.length = strlen((char *) service.value);
+        xfree(host_name);
+        host_name = NULL;
+    }
+
+    while (1) {
+        if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
+            if (ferror(stdin)) {
+                debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
+                      LogTime(), PROGRAM, ferror(stdin),
+                      strerror(ferror(stdin)));
+
+                fprintf(stdout, "BH input error\n");
+                exit(1);	/* BIIG buffer */
+            }
+            fprintf(stdout, "BH input error\n");
+            exit(0);
+        }
+        c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
+        if (c) {
+            *c = '\0';
+            length = c - buf;
+        } else {
+            err = 1;
+        }
+        if (err) {
+            debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM);
+            fprintf(stdout, "BH Oversized message\n");
+            err = 0;
+            continue;
+        }
+        debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM, buf, length);
+
+        if (buf[0] == '\0') {
+            debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM);
+            fprintf(stdout, "BH Invalid request\n");
+            continue;
+        }
+        if (strlen(buf) < 2) {
+            debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
+            fprintf(stdout, "BH Invalid request\n");
+            continue;
+        }
+        if (!strncmp(buf, "QQ", 2)) {
+            gss_release_buffer(&minor_status, &input_token);
+            gss_release_buffer(&minor_status, &output_token);
+            gss_release_buffer(&minor_status, &service);
+            gss_release_cred(&minor_status, &server_creds);
+            if (server_name)
+                gss_release_name(&minor_status, &server_name);
+            if (client_name)
+                gss_release_name(&minor_status, &client_name);
+            if (gss_context != GSS_C_NO_CONTEXT)
+                gss_delete_sec_context(&minor_status, &gss_context, NULL);
+            if (kerberosToken) {
+                /* Allocated by parseNegTokenInit, but no matching free function exists.. */
+                if (!spnego_flag)
+                    xfree((char *) kerberosToken);
+                kerberosToken = NULL;
+            }
+            if (spnego_flag) {
+                /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
+                if (spnegoToken)
+                    xfree((char *) spnegoToken);
+                spnegoToken = NULL;
+            }
+            if (token) {
+                xfree(token);
+                token = NULL;
+            }
+            fprintf(stdout, "BH quit command\n");
+            exit(0);
+        }
+        if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
+            debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
+            fprintf(stdout, "BH Invalid request\n");
+            continue;
+        }
+        if (!strncmp(buf, "YR", 2)) {
+            if (gss_context != GSS_C_NO_CONTEXT)
+                gss_delete_sec_context(&minor_status, &gss_context, NULL);
+            gss_context = GSS_C_NO_CONTEXT;
+        }
+        if (strlen(buf) <= 3) {
+            debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf);
+            fprintf(stdout, "BH Invalid negotiate request\n");
+            continue;
+        }
+        input_token.length = base64_decode_len(buf+3);
+        debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length: %d).\n",
+              LogTime(), PROGRAM, buf + 3, (int) input_token.length);
+        input_token.value = xmalloc(input_token.length);
+
+        input_token.length = base64_decode((char *) input_token.value, input_token.length, buf+3);
+
+        if ((input_token.length >= sizeof ntlmProtocol + 1) &&
+                (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
+            debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
+                  LogTime(), PROGRAM,
+                  (int) *((unsigned char *) input_token.value +
+                          sizeof ntlmProtocol));
+            fprintf(stdout, "BH received type %d NTLM token\n",
+                    (int) *((unsigned char *) input_token.value +
+                            sizeof ntlmProtocol));
+            goto cleanup;
+        }
+        if (service_principal) {
+            if (strcasecmp(service_principal, "GSS_C_NO_NAME")) {
+                major_status = gss_import_name(&minor_status, &service,
+                                               (gss_OID) GSS_C_NULL_OID, &server_name);
+
+            } else {
+                server_name = GSS_C_NO_NAME;
+                major_status = GSS_S_COMPLETE;
+            }
+        } else {
+            major_status = gss_import_name(&minor_status, &service,
+                                           gss_nt_service_name, &server_name);
+        }
+
+        if (check_gss_err(major_status, minor_status, "gss_import_name()", log))
+            goto cleanup;
+
+        major_status =
+            gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
+                             GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL);
+        if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log))
+            goto cleanup;
+
+        major_status = gss_accept_sec_context(&minor_status,
+                                              &gss_context,
+                                              server_creds,
+                                              &input_token,
+                                              GSS_C_NO_CHANNEL_BINDINGS,
+                                              &client_name, NULL, &output_token, &ret_flags, NULL, NULL);
+
+        if (output_token.length) {
+            spnegoToken = (const unsigned char *) output_token.value;
+            spnegoTokenLength = output_token.length;
+            token = (char *) xmalloc(base64_encode_len(spnegoTokenLength));
+            if (token == NULL) {
+                debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
+                fprintf(stdout, "BH Not enough memory\n");
+                goto cleanup;
+            }
+            base64_encode_str(token, base64_encode_len(spnegoTokenLength),
+                              (const char *) spnegoToken, spnegoTokenLength);
+
+            if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
+                goto cleanup;
+            if (major_status & GSS_S_CONTINUE_NEEDED) {
+                debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
+                fprintf(stdout, "TT %s\n", token);
+                goto cleanup;
+            }
+            gss_release_buffer(&minor_status, &output_token);
+            major_status =
+                gss_display_name(&minor_status, client_name, &output_token,
+                                 NULL);
+
+            if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
+                goto cleanup;
+            user = (char *) xmalloc(output_token.length + 1);
+            if (user == NULL) {
+                debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
+                fprintf(stdout, "BH Not enough memory\n");
+                goto cleanup;
+            }
+            memcpy(user, output_token.value, output_token.length);
+            user[output_token.length] = '\0';
+            if (norealm && (p = strchr(user, '@')) != NULL) {
+                *p = '\0';
+            }
+
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_DECL_KRB5_PAC
+	    ret = krb5_init_context(&context);
+	    if (!check_k5_err(context, "krb5_init_context", ret)) {
+#define ADWIN2KPAC 128
+#if HAVE_HEIMDAL_KERBEROS
+		major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status, 
+				gss_context, ADWIN2KPAC, &data_set);
+		if (!check_gss_err(major_status, minor_status, 
+				"gsskrb5_extract_authz_data_from_sec_context()", log)) {
+		    ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac);
+		    gss_release_buffer(&minor_status, &data_set);
+		    if (!check_k5_err(context, "krb5_pac_parse", ret)) {
+			ag = get_ad_groups((char *)&ad_groups, context, pac);
+			krb5_pac_free(context, pac);
+		    }
+		    krb5_free_context(context);
+		}
+#else
+		type_id.value = (void *)"mspac";
+		type_id.length = strlen((char *)type_id.value);
+
+		major_status = gss_map_name_to_any(&minor_status, client_name, 1, &type_id, (gss_any_t *)&pac);
+		if (!check_gss_err(major_status, minor_status, "gss_map_name_to_any()", log)) {
+		    ag = get_ad_groups((char *)&ad_groups,context, pac);
+		}
+		(void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
+		krb5_free_context(context);
+#endif
+	    }
+	    if (ag) {
+		debug((char *) "%s| %s: DEBUG: Groups %s\n", LogTime(), PROGRAM, ag);
+	    }
+#endif
+            fprintf(stdout, "AF %s %s\n", token, user);
+	    rfc_user = rfc1738_escape(user);
+            debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token, rfc_user);
+	    xfree(rfc_user);
+            if (log)
+                fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
+                        PROGRAM, rfc1738_escape(user));
+            goto cleanup;
+        } else {
+            if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
+                goto cleanup;
+            if (major_status & GSS_S_CONTINUE_NEEDED) {
+                debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
+                fprintf(stdout, "NA %s\n", token);
+                goto cleanup;
+            }
+            gss_release_buffer(&minor_status, &output_token);
+            major_status =
+                gss_display_name(&minor_status, client_name, &output_token,
+                                 NULL);
+
+            if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
+                goto cleanup;
+            /*
+             *  Return dummy token AA. May need an extra return tag then AF
+             */
+            user = (char *) xmalloc(output_token.length + 1);
+            if (user == NULL) {
+                debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
+                fprintf(stdout, "BH Not enough memory\n");
+                goto cleanup;
+            }
+            memcpy(user, output_token.value, output_token.length);
+            user[output_token.length] = '\0';
+            if (norealm && (p = strchr(user, '@')) != NULL) {
+                *p = '\0';
+            }
+            fprintf(stdout, "AF %s %s\n", "AA==", user);
+            debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", rfc1738_escape(user));
+            if (log)
+                fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
+                        PROGRAM, rfc1738_escape(user));
+
+        }
+cleanup:
+        gss_release_buffer(&minor_status, &input_token);
+        gss_release_buffer(&minor_status, &output_token);
+        gss_release_cred(&minor_status, &server_creds);
+        if (server_name)
+            gss_release_name(&minor_status, &server_name);
+        if (client_name)
+            gss_release_name(&minor_status, &client_name);
+        if (kerberosToken) {
+            /* Allocated by parseNegTokenInit, but no matching free function exists.. */
+            if (!spnego_flag)
+                xfree((char *) kerberosToken);
+            kerberosToken = NULL;
+        }
+        if (spnego_flag) {
+            /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
+            if (spnegoToken)
+                xfree((char *) spnegoToken);
+            spnegoToken = NULL;
+        }
+        if (token) {
+            xfree(token);
+            token = NULL;
+        }
+        if (user) {
+            xfree(user);
+            user = NULL;
+        }
+        continue;
+    }
+}
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef MAX_AUTHTOKEN_LEN
+#define MAX_AUTHTOKEN_LEN   65535
+#endif
+int
+main(int argc, char *const argv[])
+{
+    setbuf(stdout, NULL);
+    setbuf(stdin, NULL);
+    char buf[MAX_AUTHTOKEN_LEN];
+    while (1) {
+        if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
+            fprintf(stdout, "BH input error\n");
+            exit(0);
+        }
+        fprintf(stdout, "BH Kerberos authentication not supported\n");
+    }
+}
+#endif /* HAVE_GSSAPI */
diff -rubwBEN 3.4/negotiate_kerberos.h 3.4-mm/negotiate_kerberos.h
--- 3.4/negotiate_kerberos.h	1970-01-01 01:00:00.000000000 +0100
+++ 3.4-mm/negotiate_kerberos.h	2013-08-24 14:45:04.670441914 +0100
@@ -0,0 +1,154 @@
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2013 Markus Moeller. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *   As a special exemption, M Moeller gives permission to link this program
+ *   with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
+ *   the resulting executable, without including the source code for
+ *   the Libraries in the source distribution.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "util.h"
+#include "base64.h"
+
+#if HAVE_KRB5_H
+#if HAVE_BROKEN_SOLARIS_KRB5_H
+#warn "Warning! You have a broken Solaris <krb5.h> system header"
+#warn "http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6837512"
+#if defined(__cplusplus)
+#define KRB5INT_BEGIN_DECLS     extern "C" {
+#define KRB5INT_END_DECLS
+KRB5INT_BEGIN_DECLS
+#endif
+#endif /* HAVE_BROKEN_SOLARIS_KRB5_H */
+#if HAVE_BROKEN_HEIMDAL_KRB5_H
+extern "C" {
+#include <krb5.h>
+}
+#else
+#include <krb5.h>
+#endif
+#endif /* HAVE_KRB5_H */
+
+#if HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#elif HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+
+#if !HAVE_HEIMDAL_KERBEROS
+#if HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif
+#if HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+#if HAVE_GSSAPI_GSSAPI_EXT_H
+#include <gssapi/gssapi_ext.h>
+#endif
+#else
+#if HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif
+#endif
+
+#ifndef gss_nt_service_name
+#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
+#endif
+
+#define PROGRAM "negotiate_kerberos_auth"
+
+#ifndef MAX_AUTHTOKEN_LEN
+#define MAX_AUTHTOKEN_LEN   65535
+#endif
+#ifndef SQUID_KERB_AUTH_VERSION
+#define SQUID_KERB_AUTH_VERSION "3.0.4sq"
+#endif
+
+typedef struct {
+    uint16_t length;
+    uint16_t maxlength;
+    uint32_t pointer;
+} RPC_UNICODE_STRING;
+
+char *gethost_name(void);
+
+static const char *LogTime(void);
+
+static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
+
+static const char *
+LogTime()
+{
+    struct tm *tm;
+    struct timeval now;
+    static time_t last_t = 0;
+    static char buf[128];
+
+    gettimeofday(&now, NULL);
+    if (now.tv_sec != last_t) {
+        tm = localtime((time_t *) & now.tv_sec);
+        strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
+        last_t = now.tv_sec;
+    }
+    return buf;
+}
+
+int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
+                  const char *function, int log);
+
+int check_k5_err(krb5_context context, const char *msg, krb5_error_code code);
+
+char *gethost_name(void);
+
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_DECL_KRB5_PAC
+#define MAX_PAC_GROUP_SIZE 200*60
+void align(int n);
+void getustr(RPC_UNICODE_STRING *string);
+char **getgids(char **Rids, uint32_t GroupIds, uint32_t GroupCount);
+char *getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t  GroupCount);
+char *getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount, uint32_t GroupCount);
+uint64_t get6byt_be(void);
+uint32_t get4byt(void);
+uint16_t get2byt(void);
+uint8_t get1byt(void);
+char *xstrcpy( char *src, const char*dst);
+char *xstrcat( char *src, const char*dst);
+int checkustr(RPC_UNICODE_STRING *string);
+char *get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac);
+#endif
diff -rubwBEN 3.4/negotiate_kerberos_pac.cc 3.4-mm/negotiate_kerberos_pac.cc
--- 3.4/negotiate_kerberos_pac.cc	1970-01-01 01:00:00.000000000 +0100
+++ 3.4-mm/negotiate_kerberos_pac.cc	2013-08-24 14:45:13.722442138 +0100
@@ -0,0 +1,450 @@
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *   As a special exemption, M Moeller gives permission to link this program
+ *   with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
+ *   the resulting executable, without including the source code for
+ *   the Libraries in the source distribution.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "squid.h"
+#include "rfc1738.h"
+#include "compat/getaddrinfo.h"
+#include "compat/getnameinfo.h"
+
+#if (defined(HAVE_GSSKRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT) || defined(HAVE_GSS_MAP_NAME_TO_ANY)) && HAVE_DECL_KRB5_PAC
+
+#include "negotiate_kerberos.h"
+
+static int bpos;
+static krb5_data *ad_data;
+static unsigned char *p;
+
+int
+check_k5_err(krb5_context context, const char *function, krb5_error_code code)
+{
+    const char *errmsg;
+
+    if (code) {
+	errmsg = krb5_get_error_message(context, code);
+        debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, errmsg);
+	fprintf(stderr, "%s| %s: ERROR: %s: %s\n", LogTime(), PROGRAM, function, errmsg);
+	krb5_free_error_message(context, errmsg);
+    }
+    return code;
+}
+
+void
+align(int n){
+    if ( bpos % n != 0 ) {
+	int al;
+	al = (bpos/n);
+	bpos = bpos+(bpos-n*al);
+    }
+}
+
+void
+getustr(RPC_UNICODE_STRING *string) {
+
+    string->length = (p[bpos]<<0) | (p[bpos+1]<<8);
+    string->maxlength = (p[bpos+2]<<0) | (p[bpos+2+1]<<8);
+    string->pointer = (p[bpos+4]<<0) | (p[bpos+4+1]<<8) | (p[bpos+4+2]<<16) | (p[bpos+4+3]<<24);
+    bpos = bpos+8;
+
+}
+
+uint64_t
+get6byt_be(void) {
+    uint64_t var;
+
+    var = ((int64_t)p[bpos+5]<<0) | ((int64_t)p[bpos+4]<<8) | ((int64_t)p[bpos+3]<<16) | ((int64_t)p[bpos+2]<<24) | ((int64_t)p[bpos+1]<<32) | ((int64_t)p[bpos]<<40);
+    bpos = bpos+6;
+
+    return var;
+}
+
+uint32_t
+get4byt(void) {
+    uint32_t var;
+
+    var=(p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+    bpos = bpos+4;
+
+    return var;
+}
+
+uint16_t 
+get2byt(void) {
+    uint16_t var;
+
+    var=(p[bpos]<<0) | (p[bpos+1]<<8); 
+    bpos = bpos+2;
+
+    return var;
+}
+
+uint8_t
+get1byt(void) {
+    uint8_t var;
+
+    var=(p[bpos]<<0);
+    bpos = bpos+1;
+
+    return var;
+}
+
+char *
+xstrcpy( char *src, const char*dst) {
+    if (dst) {
+	if (strlen(dst)>MAX_PAC_GROUP_SIZE)
+	    return NULL;
+	else
+	    return strcpy(src,dst);
+    } else
+	return src;
+}
+
+char *
+xstrcat( char *src, const char*dst) {
+    if (dst) {
+        if (strlen(src)+strlen(dst)+1>MAX_PAC_GROUP_SIZE)
+            return NULL;
+        else
+            return strcat(src,dst);
+    } else
+	return src;
+}
+
+
+int
+checkustr(RPC_UNICODE_STRING *string) {
+    uint32_t size,off,len;
+
+    if (string->pointer != 0){
+	align(4);
+	size = (p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+	bpos = bpos+4;
+	off = (p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+	bpos = bpos+4;
+	len = (p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24);
+	bpos = bpos+4;
+	if (len > size || off != 0 || 
+	    string->length > string->maxlength || len != string->length/2) {
+	    debug((char *) "%s| %s: ERROR: RPC_UNICODE_STRING encoding error => size: %d len: %d/%d maxlength: %d offset: %d\n", 
+		    LogTime(), PROGRAM, size, len, string->length, string->maxlength, off);
+	    return -1;
+	}
+	/* UNICODE string */
+	bpos = bpos+string->length;
+    }
+    return 0;
+}
+
+char **
+getgids(char **Rids, uint32_t GroupIds, uint32_t  GroupCount) {
+    if (GroupIds!= 0){
+        uint32_t ngroup;
+        uint32_t sauth;
+        int l;
+
+        align(4);
+        ngroup = get4byt();
+        if ( ngroup != GroupCount) {
+            debug((char *) "%s| %s: ERROR: Group encoding error => GroupCount: %d Array size: %d\n",
+                                LogTime(), PROGRAM, GroupCount, ngroup);
+            return NULL;
+        }
+        debug((char *) "%s| %s: INFO: Found %d rids\n", LogTime(), PROGRAM, GroupCount);
+
+	Rids=(char **)xcalloc(GroupCount*sizeof(char*),1);
+        for ( l=0; l<(int)GroupCount; l++) {
+	    Rids[l]=(char *)xcalloc(4*sizeof(char),1);
+            memcpy((void *)Rids[l],(void *)&p[bpos],4); 
+            sauth = get4byt();
+	    debug((char *) "%s| %s: Info: Got rid: %u\n", LogTime(), PROGRAM, sauth);
+	    /* attribute */
+            bpos = bpos+4; 
+        }
+    }
+    return Rids;
+}
+
+char *
+getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t  GroupCount) {
+    if (DomainLogonId!= 0) {
+        uint32_t nauth;
+        uint8_t rev;
+        uint64_t idauth;
+        uint32_t sauth;
+	char dli[256];
+	char *ag;
+        int length;
+        int l;
+
+        align(4);
+
+        nauth = get4byt();
+	
+	/* prepend rids with DomainID */
+        length=1+1+6+nauth*4;
+	for (l=0; l<(int)GroupCount;l++){
+	    ag=(char *)xcalloc((length+4)*sizeof(char),1);
+	    memcpy((void *)ag,(const void*)&p[bpos],1);
+	    memcpy((void *)&ag[1],(const void*)&p[bpos+1],1);
+	    ag[1] = ag[1]+1;
+	    memcpy((void *)&ag[2],(const void*)&p[bpos+2],6+nauth*4);
+	    memcpy((void *)&ag[length],(const void*)Rids[l],4);
+	    if (l==0) {
+		if (!xstrcpy(ad_groups,"group=")) {
+		    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+				LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		}
+	    } else {
+		if (!xstrcat(ad_groups," group=")) {
+                    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+                                LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		}
+	    }
+	    if (!xstrcat(ad_groups,base64_encode_bin(ag, length+4))) {
+                    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+                                LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+	    }
+	    xfree(ag);
+	    xfree(Rids[l]);
+	}
+	xfree(Rids);
+
+	/* mainly for debug only */
+        rev = get1byt();
+        bpos = bpos + 1; /*nsub*/
+        idauth = get6byt_be();
+
+	snprintf(dli,sizeof(dli),"S-%d-%lu",rev,(long unsigned int)idauth);	
+        for ( l=0;l<(int)nauth;l++ ) {
+            sauth = get4byt();
+	    snprintf((char *)&dli[strlen(dli)],sizeof(dli)-strlen(dli),"-%u",sauth);	
+        }
+        debug((char *) "%s| %s: INFO: Got DomainLogonId %s\n", LogTime(), PROGRAM, dli); 
+    }
+    return ad_groups;
+}
+
+char *
+getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount, uint32_t GroupCount) {
+    if (ExtraSids!= 0){
+        uint32_t ngroup;
+	uint32_t *pa;
+	char *ag;
+        int length;
+        int l;
+
+        align(4);
+        ngroup = get4byt();
+        if ( ngroup != SidCount) {
+            debug((char *) "%s| %s: ERROR: Group encoding error => SidCount: %d Array size: %d\n",
+                                LogTime(), PROGRAM, SidCount, ngroup);
+	    return NULL;
+        }
+        debug((char *) "%s| %s: INFO: Found %d ExtraSIDs\n", LogTime(), PROGRAM, SidCount);
+
+	pa=(uint32_t *)xmalloc(SidCount*sizeof(uint32_t));
+        for ( l=0; l < (int)SidCount; l++ ){
+            pa[l] = get4byt();
+            bpos = bpos+4; /* attr */
+        }
+        
+        for ( l=0; l<(int)SidCount; l++ ){
+	    char es[256];
+            uint32_t nauth;
+            uint8_t rev;
+            uint64_t idauth;
+            uint32_t sauth;
+            int k;
+            
+	    if (pa[l] != 0){
+		nauth = get4byt();
+
+		length = 1+1+6+nauth*4;
+		ag = (char *)xcalloc((length)*sizeof(char),1);
+		memcpy((void *)ag,(const void*)&p[bpos],length);
+		if (!ad_groups) {
+		    if (!xstrcpy(ad_groups,"group=")) {
+			debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", 
+                                	LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		    }
+		} else {
+		    if (!xstrcat(ad_groups," group=")) {
+			debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
+					LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		    }
+		}
+		if (!xstrcat(ad_groups,base64_encode_bin(ag, length+4))) {
+                    debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
+                                LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+		}
+		xfree(ag);
+
+		rev = get1byt();
+		bpos = bpos + 1; /* nsub */
+		idauth = get6byt_be();
+
+		snprintf(es,sizeof(es),"S-%d-%lu",rev,(long unsigned int)idauth);	
+		for ( k=0;k<(int)nauth;k++ ) {
+		    sauth = get4byt();
+		    snprintf((char *)&es[strlen(es)],sizeof(es)-strlen(es),"-%u",sauth);	
+		}
+		debug((char *) "%s| %s: INFO: Got ExtraSid %s\n", LogTime(), PROGRAM, es); 
+	    }
+        }
+	xfree(pa);
+    }
+    return ad_groups;
+}
+
+char *
+get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac){
+    krb5_error_code ret;
+    RPC_UNICODE_STRING EffectiveName;
+    RPC_UNICODE_STRING FullName;
+    RPC_UNICODE_STRING LogonScript;
+    RPC_UNICODE_STRING ProfilePath;
+    RPC_UNICODE_STRING HomeDirectory;
+    RPC_UNICODE_STRING HomeDirectoryDrive;
+    RPC_UNICODE_STRING LogonServer;
+    RPC_UNICODE_STRING LogonDomainName;
+    uint32_t GroupCount=0;
+    uint32_t GroupIds=0;
+    uint32_t LogonDomainId=0;
+    uint32_t SidCount=0;
+    uint32_t ExtraSids=0;
+    /*
+    uint32_t ResourceGroupDomainSid=0;
+    uint32_t ResourceGroupCount=0;
+    uint32_t ResourceGroupIds=0;
+    */
+    char **Rids=NULL;
+    int l=0;
+
+    ad_data = (krb5_data *)xmalloc(sizeof(krb5_data));
+
+#define KERB_LOGON_INFO 1
+    ret = krb5_pac_get_buffer(context, pac, KERB_LOGON_INFO, ad_data);
+    if (check_k5_err(context, "krb5_pac_get_buffer", ret)) 
+	goto k5clean;
+    
+    p = (unsigned char *)ad_data->data;
+
+    debug((char *) "%s| %s: INFO: Got PAC data of lengh %d\n", 
+		    LogTime(), PROGRAM, (int)ad_data->length);
+
+    /* Skip 16 bytes icommon RPC header
+     * Skip 4 bytes RPC unique pointer referent
+     * http://msdn.microsoft.com/en-gb/library/cc237933.aspx
+     */
+    /* Some data are pointers to data which follows the main KRB5 LOGON structure => 
+     *         So need to read the data 
+     * some logical consistency checks are done when analysineg the pointer data
+     */
+    bpos = 20;
+    /* 8 bytes LogonTime
+     * 8 bytes LogoffTime
+     * 8 bytes KickOffTime
+     * 8 bytes PasswordLastSet
+     * 8 bytes PasswordCanChange
+     * 8 bytes PasswordMustChange
+     */
+    bpos = bpos+48;
+    getustr(&EffectiveName);
+    getustr(&FullName);
+    getustr(&LogonScript);
+    getustr(&ProfilePath);
+    getustr(&HomeDirectory);
+    getustr(&HomeDirectoryDrive);
+    /* 2 bytes LogonCount
+     * 2 bytes BadPasswordCount
+     * 4 bytes UserID
+     * 4 bytes PrimaryGroupId
+     */
+    bpos = bpos+12;
+    GroupCount = get4byt();
+    GroupIds = get4byt();
+    /* 4 bytes UserFlags
+     * 16 bytes UserSessionKey
+     */
+    bpos = bpos+20;
+    getustr(&LogonServer);
+    getustr(&LogonDomainName);
+    LogonDomainId = get4byt();
+    /* 8 bytes Reserved1
+     * 4 bytes UserAccountControl
+     * 4 bytes SubAuthStatus
+     * 8 bytes LastSuccessfullLogon
+     * 8 bytes LastFailedLogon
+     * 4 bytes FailedLogonCount
+     * 4 bytes Reserved2
+     */
+    bpos = bpos+40;
+    SidCount = get4byt();
+    ExtraSids = get4byt();
+    /* 4 bytes ResourceGroupDomainSid
+     * 4 bytes ResourceGroupCount
+     * 4 bytes ResourceGroupIds
+     */
+    bpos = bpos+12;
+    /* 
+     * Read all data from structure => Now check pointers
+     */
+    if (checkustr(&EffectiveName)<0)
+	goto k5clean;
+    if (checkustr(&FullName)<0)
+	goto k5clean;
+    if (checkustr(&LogonScript)<0)
+	goto k5clean;
+    if (checkustr(&ProfilePath)<0)
+	goto k5clean;
+    if (checkustr(&HomeDirectory)<0)
+	goto k5clean;
+    if (checkustr(&HomeDirectoryDrive)<0)
+	goto k5clean;
+    Rids = getgids(Rids,GroupIds,GroupCount);
+    if (checkustr(&LogonServer)<0)
+	goto k5clean;
+    if (checkustr(&LogonDomainName)<0)
+	goto k5clean;
+    ad_groups = getdomaingids(ad_groups,LogonDomainId,Rids,GroupCount);
+    if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount,GroupCount))==NULL)
+        goto k5clean;
+    
+    debug((char *) "%s| %s: INFO: Read %d of %d bytes \n", LogTime(), PROGRAM, bpos, (int)ad_data->length);
+    krb5_free_data(context, ad_data);
+    return ad_groups; 
+k5clean:
+    for ( l=0; l<(int)GroupCount; l++) {
+	if (Rids[l])
+	    xfree(Rids[l]);
+    }
+    xfree(Rids);
+    krb5_free_data(context, ad_data);
+    return NULL;
+}
+#endif
diff -rubwBEN 3.4/src/peer_proxy_negotiate_auth.cc 3.4-mm/src/peer_proxy_negotiate_auth.cc
--- 3.4/src/peer_proxy_negotiate_auth.cc	2013-08-12 00:01:44.458703000 +0100
+++ 3.4-mm/src/peer_proxy_negotiate_auth.cc	2013-08-24 17:18:50.208670666 +0100
@@ -210,21 +210,29 @@
         static krb5_keytab_entry entry;
         static krb5_kt_cursor cursor;
         static krb5_creds *creds = NULL;
-#if HAVE_HEIMDAL_KERBEROS
+#if defined(HAVE_HEIMDAL_KERBEROS) && !defined(HAVE_KRB5_GET_RENEWED_CREDS)
         static krb5_creds creds2;
 #endif
         static krb5_principal principal = NULL;
         static krb5_deltat skew;
 
+#if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+        krb5_get_init_creds_opt *options;
+#else
         krb5_get_init_creds_opt options;
+#endif
         krb5_error_code code = 0;
         krb5_deltat rlife;
 #if HAVE_PROFILE_H && HAVE_KRB5_GET_PROFILE && HAVE_PROFILE_GET_INTEGER && HAVE_PROFILE_RELEASE
         profile_t profile;
 #endif
-#if HAVE_HEIMDAL_KERBEROS
+#if defined(HAVE_HEIMDAL_KERBEROS) && !defined(HAVE_KRB5_GET_RENEWED_CREDS)
         krb5_kdc_flags flags;
-        krb5_realm *client_realm;
+#if HAVE_KRB5_PRINCIPAL_GET_REALM
+        const char *client_realm;
+#else
+        krb5_realm client_realm;
+#endif
 #endif
         char *mem_cache;
 
@@ -236,7 +244,7 @@
                 (creds->times.endtime - time(0) > skew) &&
                 (creds->times.renew_till - time(0) > 2 * skew)) {
             if (creds->times.endtime - time(0) < 2 * skew) {
-#if !HAVE_HEIMDAL_KERBEROS
+#if HAVE_KRB5_GET_RENEWED_CREDS
                 /* renew ticket */
                 code =
                     krb5_get_renewed_creds(kparam.context, creds, principal,
@@ -256,10 +264,15 @@
                            << error_message(code));
                     return (1);
                 }
+#if HAVE_KRB5_PRINCIPAL_GET_REALM
+                client_realm = krb5_principal_get_realm(kparam.context, principal);
+#else
                 client_realm = krb5_princ_realm(kparam.context, creds2.client);
+#endif
                 code =
                     krb5_make_principal(kparam.context, &creds2.server,
-                                        *client_realm, KRB5_TGS_NAME, *client_realm, NULL);
+                                        (krb5_const_realm)&client_realm, KRB5_TGS_NAME, 
+				 	(krb5_const_realm)&client_realm, NULL);
                 if (code) {
                     debugs(11, 5,
                            HERE << "Error while getting krbtgt principal : " <<
@@ -400,7 +413,11 @@
 
             creds = (krb5_creds *) xmalloc(sizeof(*creds));
             memset(creds, 0, sizeof(*creds));
+#if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+	    krb5_get_init_creds_opt_alloc(kparam.context, &options);
+#else
             krb5_get_init_creds_opt_init(&options);
+#endif
             code = krb5_string_to_deltat((char *) MAX_RENEW_TIME, &rlife);
             if (code != 0 || rlife == 0) {
                 debugs(11, 5,
@@ -408,8 +425,18 @@
                        " : " << error_message(code));
                 return (1);
             }
+#if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+            krb5_get_init_creds_opt_set_renew_life(options, rlife);
+            code =
+                krb5_get_init_creds_keytab(kparam.context, creds, principal,
+                                           keytab, 0, NULL, options);
+#if HAVE_KRB5_GET_INIT_CREDS_FREE_CONTEXT
+	krb5_get_init_creds_opt_free(kparam.context, options);
+#else
+	krb5_get_init_creds_opt_free(options);
+#endif
+#else
             krb5_get_init_creds_opt_set_renew_life(&options, rlife);
-
             code =
                 krb5_get_init_creds_keytab(kparam.context, creds, principal,
                                            keytab, 0, NULL, &options);
@@ -413,6 +440,7 @@
             code =
                 krb5_get_init_creds_keytab(kparam.context, creds, principal,
                                            keytab, 0, NULL, &options);
+#endif
             if (code) {
                 debugs(11, 5,
                        HERE <<
diff -rubwBEN 3.4/test_negotiate_auth.sh 3.4-mm/test_negotiate_auth.sh
--- 3.4/test_negotiate_auth.sh	1970-01-01 01:00:00.000000000 +0100
+++ 3.4-mm/test_negotiate_auth.sh	2013-08-24 14:45:29.159442521 +0100
@@ -0,0 +1,7 @@
+#!/bin/bash
+if [[ -z "$1" ]]; then
+	echo "Need squid hostname"
+	exit 0
+fi
+dir=`dirname $0`
+$dir/negotiate_kerberos_auth_test $1 | awk '{sub(/Token:/,"YR"); print $0}END{print "QQ"}' | $dir/negotiate_kerberos_auth -d

