]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
negotiate_kerberos_auth: MEMORY keytab and replay cache support
authorMarkus Moeller <huaraz@moeller.plus.com>
Fri, 19 Dec 2014 22:16:42 +0000 (14:16 -0800)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 19 Dec 2014 22:16:42 +0000 (14:16 -0800)
1) Checks for MEMORY: keytab support and reads the keytab from disk into
   MEMORY to improve performance (i.e. read keytab only at startup and
   nerver again)

2) Add option for replay cache type. Allows to set replay cache to none
   to improve performance ( may reduce security a bit )

3) Add option for replay cache directory.  If /var/tmp is not the best
   location you can choose a different location.

acinclude/krb5.m4
helpers/negotiate_auth/kerberos/negotiate_kerberos.h
helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.8
helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc
helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc
helpers/negotiate_auth/kerberos/test_negotiate_auth.sh

index 5aa8a99d54423c3000a9cadacca05b0efd084dd7..f8776803924fc6ac962353d65687857ce19c3087 100644 (file)
@@ -114,6 +114,33 @@ int main(int argc, char *argv[])
   ])
 ])
 
+dnl check whether the kerberos context has a memory keytab. Sets
+dnl squid_cv_memory_keytab if that's the case.
+AC_DEFUN([SQUID_CHECK_KRB5_CONTEXT_MEMORY_KEYTAB],[
+  AC_CACHE_CHECK([for memory keytab], squid_cv_memory_keytab, [
+    AC_RUN_IFELSE([
+      AC_LANG_SOURCE([[
+#if HAVE_BROKEN_SOLARIS_KRB5_H
+#if defined(__cplusplus)
+#define KRB5INT_BEGIN_DECLS     extern "C" {
+#define KRB5INT_END_DECLS
+KRB5INT_BEGIN_DECLS
+#endif
+#endif
+#include <krb5.h>
+int main(int argc, char *argv[])
+{
+    krb5_context context;
+    krb5_keytab kt;
+
+    krb5_init_context(&context);
+    return krb5_kt_resolve(context, "MEMORY:test_keytab", &kt);
+}
+      ]])
+    ], [ squid_cv_memory_keytab=yes ], [ squid_cv_memory_keytab=no ], [:])
+  ])
+])
+
 
 dnl checks that gssapi is ok, and sets squid_cv_working_gssapi accordingly
 AC_DEFUN([SQUID_CHECK_WORKING_GSSAPI], [
@@ -331,6 +358,10 @@ AC_DEFUN([SQUID_CHECK_KRB5_FUNCS],[
   SQUID_DEFINE_BOOL(HAVE_KRB5_MEMORY_CACHE,$squid_cv_memory_cache,
        [Define if kerberos has MEMORY: cache support])
 
+  SQUID_CHECK_KRB5_CONTEXT_MEMORY_KEYTAB
+  SQUID_DEFINE_BOOL(HAVE_KRB5_MEMORY_KEYTAB,$squid_cv_memory_keytab,
+       [Define if kerberos has MEMORY: keytab support])
+
   SQUID_CHECK_WORKING_GSSAPI
   SQUID_DEFINE_BOOL(HAVE_GSSAPI,$squid_cv_working_gssapi,[GSSAPI support])
 
index f3ae4e25a208f8c5ff21452e2dbe2a62d7975adb..85208cdcc06247b914fe131114cb78d7c93b02a8 100644 (file)
@@ -116,13 +116,13 @@ static const unsigned char ntlmProtocol[] = {'N', 'T', 'L', 'M', 'S', 'S', 'P',
 inline 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) {
+        struct tm *tm;
         tm = localtime((time_t *) & now.tv_sec);
         strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
         last_t = now.tv_sec;
index 27d21bd6ef01e5ba674276cc2650545f024dabd5..b31b046c5e26abb56dc7a6f32327d712398a2737 100644 (file)
@@ -7,7 +7,7 @@ Version 3.0.4sq
 .
 .SH SYNOPSIS
 .if !'po4a'hide' .B negotiate_kerberos_auth
-.if !'po4a'hide' .B [\-h] [\-d] [\-i] [\-r] [\-s Service-Principal-Name] 
+.if !'po4a'hide' .B [\-h] [\-d] [\-i] [\-r] [\-s Service\-Principal\-Name] [\-k Keytab\-Name] [\-c Replay\-Cache\-Directory] [\-t Replay\-Cache\-Type] 
 .
 .SH DESCRIPTION
 .B negotiate_kerberos_auth
@@ -28,8 +28,17 @@ Write informational messages to stderr.
 .if !'po4a'hide' .B \-r 
 Remove realm from username before returning the username to squid.
 .if !'po4a'hide' .TP 12
-.if !'po4a'hide' .B \-s Service-Principal-name
+.if !'po4a'hide' .B \-s Service\-Principal\-name
 Provide Service Principal Name.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-k Keytab\-Name
+Provide Kerberos Keytab Name (Default: /etc/krb5.keytab)
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-c Replay\-Cache\-Directory
+Provide Replay Cache Directory (Default: /var/tmp)
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-t Replay\-Cache\-Type
+Provide Replay Cache Type (Default: dfl) 
 .
 .SH CONFIGURATION
 .PP
@@ -52,8 +61,9 @@ helper in
 The following squid startup file modification may be required:
 
 Add the following lines to the squid startup script to point squid to a keytab file which
-contains the HTTP/fqdn service principal for the default Kerberos domain. The fqdn must be
-the proxy name set in IE or firefox. You can not use an IP address.
+contains the HTTP/fqdn service principal for the default Kerberos domain. The keytab name can
+also be provided by the \-k <keytab name> option. The fqdn must be the proxy name set in IE
+ or firefox. You can not use an IP address.
 
 KRB5_KTNAME=/etc/squid/HTTP.keytab
 export KRB5_KTNAME
@@ -62,23 +72,23 @@ If you use a different Kerberos domain than the machine itself is in you can poi
 the seperate Kerberos config file by setting the following environmnet variable in the startup
 script.
 
-KRB5_CONFIG=/etc/krb5-squid.conf
+KRB5_CONFIG=/etc/krb5\-squid.conf
 export KRB5_CONFIG
 
 Kerberos can keep a replay cache to detect the reuse of Kerberos tickets (usually only possible 
 in a 5 minute window) . If squid is under high load with Negotiate(Kerberos) proxy authentication 
 requests the replay cache checks can create high CPU load. If the environment does not require 
 high security the replay cache check can be disabled for MIT based Kerberos implementations by 
-adding the following to the startup script 
+adding the below to the startup script or use the \-t none option.
 
 KRB5RCACHETYPE=none
 export KRB5RCACHETYPE
 
 If negotiate_kerberos_auth doesn't determine for some reason the right service principal you can provide
-it with -s HTTP/fqdn.
+it with \-s HTTP/fqdn.
 
 If you serve multiple Kerberos realms add a HTTP/fqdn@REALM service principal per realm to the
-HTTP.keytab file and use the -s GSS_C_NO_NAME option with negotiate_kerberos_auth.
+HTTP.keytab file and use the \-s GSS_C_NO_NAME option with negotiate_kerberos_auth.
 
 .
 .SH AUTHOR
@@ -90,7 +100,7 @@ This manual was written by
 .
 .SH COPYRIGHT
 .PP
- * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
+ * Copyright (C) 1996\-2014 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -103,34 +113,34 @@ Distributed under the GNU General Public License (GNU GPL) version 2 or later (G
 .SH QUESTIONS
 Questions on the usage of this program can be sent to the
 .I Squid Users mailing list
-.if !'po4a'hide' <squid-users@squid-cache.org>
+.if !'po4a'hide' <squid\-users@squid\-cache.org>
 .
 .SH REPORTING BUGS
 Bug reports need to be made in English.
-See http://wiki.squid-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report.
+See http://wiki.squid\-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report.
 .PP
-Report bugs or bug fixes using http://bugs.squid-cache.org/
+Report bugs or bug fixes using http://bugs.squid\-cache.org/
 .PP
 Report serious security bugs to
-.I Squid Bugs <squid-bugs@squid-cache.org>
+.I Squid Bugs <squid\-bugs@squid\-cache.org>
 .PP
 Report ideas for new improvements to the
 .I Squid Developers mailing list
-.if !'po4a'hide' <squid-dev@squid-cache.org>
+.if !'po4a'hide' <squid\-dev@squid\-cache.org>
 .
 .SH SEE ALSO
 .if !'po4a'hide' .BR squid "(8) "
 .if !'po4a'hide' .BR ext_kerberos_ldap_group_acl "(8) "
 .br
-.BR RFC4559 " - SPNEGO-based Kerberos and NTLM HTTP Authentication in Microsoft Windows,"
+.BR RFC4559 " \- SPNEGO\-based Kerberos and NTLM HTTP Authentication in Microsoft Windows,"
 .br
-.BR RFC2478 " - The Simple and Protected GSS-API Negotiation Mechanism,"
+.BR RFC2478 " \- The Simple and Protected GSS\-API Negotiation Mechanism,"
 .br
-.BR RFC1964 " - The Kerberos Version 5 GSS-API Mechanism,"
+.BR RFC1964 " \- The Kerberos Version 5 GSS\-API Mechanism,"
 .br
 The Squid FAQ wiki
-.if !'po4a'hide' http://wiki.squid-cache.org/SquidFaq
+.if !'po4a'hide' http://wiki.squid\-cache.org/SquidFaq
 .br
 The Squid Configuration Manual
-.if !'po4a'hide' http://www.squid-cache.org/Doc/config/
-.if !'po4a'hide' http://wiki.squid-cache.org/ConfigExamples/Authenticate/Kerberos
+.if !'po4a'hide' http://www.squid\-cache.org/Doc/config/
+.if !'po4a'hide' http://wiki.squid\-cache.org/ConfigExamples/Authenticate/Kerberos
index f92a6b9cef43fa22f7b8239e8f65b35a3a4e2da0..93aa1d13394ce6d40f964d15ae2c9c47279a3fb1 100644 (file)
 
 #include "negotiate_kerberos.h"
 
+#if HAVE_SYS_STAT_H
+#include "sys/stat.h"
+#endif
+#if HAVE_UNISTD_H
+#include "unistd.h"
+#endif
+
+#if HAVE_KRB5_MEMORY_KEYTAB
+typedef struct _krb5_kt_list {
+    struct _krb5_kt_list *next;
+    krb5_keytab_entry *entry;
+} *krb5_kt_list;
+krb5_kt_list ktlist = NULL;
+
+krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list kt_list);
+krb5_error_code krb5_write_keytab(krb5_context context,
+                                  krb5_kt_list kt_list,
+                                  char *name);
+krb5_error_code krb5_read_keytab(krb5_context context,
+                                 char *name,
+                                 krb5_kt_list *kt_list);
+#endif /* HAVE_KRB5_MEMORY_KEYTAB */
+
+#if HAVE_PAC_SUPPORT || HAVE_KRB5_MEMORY_KEYTAB
+int
+check_k5_err(krb5_context context, const char *function, krb5_error_code code)
+{
+
+    if (code) {
+        const char *errmsg;
+        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);
+#if HAVE_KRB5_FREE_ERROR_MESSAGE
+        krb5_free_error_message(context, errmsg);
+#elif HAVE_KRB5_FREE_ERROR_STRING
+        krb5_free_error_string(context, (char *)errmsg);
+#else
+        xfree(errmsg);
+#endif
+    }
+    return code;
+}
+#endif
+
 char *
 gethost_name(void)
 {
@@ -56,12 +101,15 @@ gethost_name(void)
 
     rc = gethostname(hostname, sizeof(hostname)-1);
     if (rc) {
+        debug((char *) "%s| %s: ERROR: resolving hostname '%s' failed\n", LogTime(), PROGRAM, hostname);
         fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
                 LogTime(), PROGRAM, hostname);
         return NULL;
     }
     rc = getaddrinfo(hostname, NULL, NULL, &hres);
     if (rc != 0) {
+        debug((char *) "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
+              LogTime(), PROGRAM, gai_strerror(rc));
         fprintf(stderr,
                 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
                 LogTime(), PROGRAM, gai_strerror(rc));
@@ -76,6 +124,8 @@ gethost_name(void)
     rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
                      sizeof(hostname), NULL, 0, 0);
     if (rc != 0) {
+        debug((char *) "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
+              LogTime(), PROGRAM, gai_strerror(rc));
         fprintf(stderr,
                 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
                 LogTime(), PROGRAM, gai_strerror(rc));
@@ -142,6 +192,138 @@ check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
     return (0);
 }
 
+#if HAVE_KRB5_MEMORY_KEYTAB
+/*
+ * Free a kt_list
+ */
+krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list list)
+{
+    krb5_kt_list lp = list;
+
+    while (lp) {
+#if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY )
+        krb5_error_code  retval = krb5_kt_free_entry(context, lp->entry);
+#else
+        krb5_error_code  retval = krb5_free_keytab_entry_contents(context, lp->entry);
+#endif
+        safe_free(lp->entry);
+        if (check_k5_err(context, "krb5_kt_free_entry", retval))
+            return retval;
+        krb5_kt_list prev = lp;
+        lp = lp->next;
+        xfree(prev);
+    }
+    return 0;
+}
+/*
+ * Read in a keytab and append it to list.  If list starts as NULL,
+ * allocate a new one if necessary.
+ */
+krb5_error_code krb5_read_keytab(krb5_context context, char *name, krb5_kt_list *list)
+{
+    krb5_kt_list lp = NULL, tail = NULL, back = NULL;
+    krb5_keytab kt;
+    krb5_keytab_entry *entry;
+    krb5_kt_cursor cursor;
+    krb5_error_code retval = 0;
+
+    if (*list) {
+        /* point lp at the tail of the list */
+        for (lp = *list; lp->next; lp = lp->next);
+        back = lp;
+    }
+    retval = krb5_kt_resolve(context, name, &kt);
+    if (check_k5_err(context, "krb5_kt_resolve", retval))
+        return retval;
+    retval = krb5_kt_start_seq_get(context, kt, &cursor);
+    if (check_k5_err(context, "krb5_kt_start_seq_get", retval))
+        goto close_kt;
+    for (;;) {
+        entry = (krb5_keytab_entry *)xcalloc(1, sizeof (krb5_keytab_entry));
+        if (!entry) {
+            retval = ENOMEM;
+            debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
+                  LogTime(), PROGRAM, strerror(retval));
+            fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
+                    LogTime(), PROGRAM, strerror(retval));
+            break;
+        }
+        memset(entry, 0, sizeof (*entry));
+        retval = krb5_kt_next_entry(context, kt, entry, &cursor);
+        if (check_k5_err(context, "krb5_kt_next_entry", retval))
+            break;
+
+        if (!lp) {              /* if list is empty, start one */
+            lp = (krb5_kt_list)xmalloc(sizeof (*lp));
+            if (!lp) {
+                retval = ENOMEM;
+                debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
+                      LogTime(), PROGRAM, strerror(retval));
+                fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
+                        LogTime(), PROGRAM, strerror(retval));
+                break;
+            }
+        } else {
+            lp->next = (krb5_kt_list)xmalloc(sizeof (*lp));
+            if (!lp->next) {
+                retval = ENOMEM;
+                debug((char *) "%s| %s: ERROR: krb5_read_keytab failed: %s\n",
+                      LogTime(), PROGRAM, strerror(retval));
+                fprintf(stderr, "%s| %s: ERROR: krb5_read_keytab: %s\n",
+                        LogTime(), PROGRAM, strerror(retval));
+                break;
+            }
+            lp = lp->next;
+        }
+        if (!tail)
+            tail = lp;
+        lp->next = NULL;
+        lp->entry = entry;
+    }
+    xfree(entry);
+    if (retval) {
+        if (retval == KRB5_KT_END)
+            retval = 0;
+        else {
+            krb5_free_kt_list(context, tail);
+            tail = NULL;
+            if (back)
+                back->next = NULL;
+        }
+    }
+    if (!*list)
+        *list = tail;
+    krb5_kt_end_seq_get(context, kt, &cursor);
+close_kt:
+    krb5_kt_close(context, kt);
+    return retval;
+}
+
+/*
+ * Takes a kt_list and writes it to the named keytab.
+ */
+krb5_error_code krb5_write_keytab(krb5_context context, krb5_kt_list list, char *name)
+{
+    krb5_keytab kt;
+    char ktname[MAXPATHLEN+sizeof("MEMORY:")+1];
+    krb5_error_code retval = 0;
+
+    snprintf(ktname, sizeof(ktname), "%s", name);
+    retval = krb5_kt_resolve(context, ktname, &kt);
+    if (retval)
+        return retval;
+    for (krb5_kt_list lp = list; lp; lp = lp->next) {
+        retval = krb5_kt_add_entry(context, kt, lp->entry);
+        if (retval)
+            break;
+    }
+    /*
+     *     krb5_kt_close(context, kt);
+     */
+    return retval;
+}
+#endif /* HAVE_KRB5_MEMORY_KEYTAB */
+
 int
 main(int argc, char *const argv[])
 {
@@ -152,14 +334,16 @@ main(int argc, char *const argv[])
 #if HAVE_PAC_SUPPORT
     char ad_groups[MAX_PAC_GROUP_SIZE];
     char *ag=NULL;
-    krb5_context context = NULL;
-    krb5_error_code ret;
     krb5_pac pac;
 #if USE_HEIMDAL_KRB5
     gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
 #else
     gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
 #endif
+#endif
+#if HAVE_PAC_SUPPORT || HAVE_KRB5_MEMORY_KEYTAB
+    krb5_context context = NULL;
+    krb5_error_code ret;
 #endif
     long length = 0;
     static int err = 0;
@@ -168,6 +352,15 @@ main(int argc, char *const argv[])
     char *service_name = (char *) "HTTP", *host_name = NULL;
     char *token = NULL;
     char *service_principal = NULL;
+    char *keytab_name = NULL;
+    char *keytab_name_env = NULL;
+#if HAVE_KRB5_MEMORY_KEYTAB
+    char *memory_keytab_name = NULL;
+#endif
+    char *rcache_type = NULL;
+    char *rcache_type_env = NULL;
+    char *rcache_dir = NULL;
+    char *rcache_dir_env = 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;
@@ -183,7 +376,7 @@ main(int argc, char *const argv[])
     setbuf(stdout, NULL);
     setbuf(stdin, NULL);
 
-    while (-1 != (opt = getopt(argc, argv, "dirs:h"))) {
+    while (-1 != (opt = getopt(argc, argv, "dirs:k:c:t:"))) {
         switch (opt) {
         case 'd':
             debug_enabled = 1;
@@ -194,29 +387,114 @@ main(int argc, char *const argv[])
         case 'r':
             norealm = 1;
             break;
+        case 'k':
+#if HAVE_SYS_STAT_H
+            struct stat fstat;
+            char *ktp;
+#endif
+           if (optarg)
+                keytab_name = xstrdup(optarg);
+           else {
+                fprintf(stderr, "ERROR: keytab file not given\n");
+               exit(1);
+           }
+            /*
+             * Some sanity checks
+             */
+#if HAVE_SYS_STAT_H
+            if ((ktp=strchr(keytab_name,':')))
+                ktp++;
+            else
+                ktp=keytab_name;
+            if (stat((const char*)ktp, &fstat)) {
+                if (ENOENT == errno)
+                    fprintf(stderr, "ERROR: keytab file %s does not exist\n",keytab_name);
+                else
+                    fprintf(stderr, "ERROR: Error %s during stat of keytab file %s\n",strerror(errno),keytab_name);
+                exit(1);
+            } else if (!S_ISREG(fstat.st_mode)) {
+                fprintf(stderr, "ERROR: keytab file %s is not a file\n",keytab_name);
+                exit(1);
+            }
+#endif
+#if HAVE_UNISTD_H
+            if (access(ktp, R_OK)) {
+                fprintf(stderr, "ERROR: keytab file %s is not accessible\n",keytab_name);
+                exit(1);
+            }
+#endif
+            break;
+        case 'c':
+#if HAVE_SYS_STAT_H
+            struct stat dstat;
+#endif
+           if (optarg)
+                rcache_dir = xstrdup(optarg);
+           else {
+                fprintf(stderr, "ERROR: replay cache directory not given\n");
+               exit(1);
+           }
+            /*
+             * Some sanity checks
+             */
+#if HAVE_SYS_STAT_H
+            if (stat((const char*)rcache_dir, &dstat)) {
+                if (ENOENT == errno)
+                    fprintf(stderr, "ERROR: replay cache directory %s does not exist\n",rcache_dir);
+                else
+                    fprintf(stderr, "ERROR: Error %s during stat of replay cache directory %s\n",strerror(errno),rcache_dir);
+                exit(1);
+            } else if (!S_ISDIR(dstat.st_mode)) {
+                fprintf(stderr, "ERROR: replay cache directory %s is not a directory\n",rcache_dir);
+                exit(1);
+            }
+#endif
+#if HAVE_UNISTD_H
+            if (access(rcache_dir, W_OK)) {
+                fprintf(stderr, "ERROR: replay cache directory %s is not accessible\n",rcache_dir);
+                exit(1);
+            }
+#endif
+            break;
+        case 't':
+           if (optarg)
+                rcache_type = xstrdup(optarg);
+           else {
+                fprintf(stderr, "ERROR: replay cache type not given\n");
+               exit(1);
+           }
+            break;
         case 's':
-            service_principal = xstrdup(optarg);
+           if (optarg)
+                service_principal = xstrdup(optarg);
+           else {
+                fprintf(stderr, "ERROR: service principal not given\n");
+               exit(1);
+           }
             break;
-        case 'h':
+        default:
             fprintf(stderr, "Usage: \n");
-            fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n");
+            fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\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, "-k keytab name\n");
+            fprintf(stderr, "-c replay cache directory\n");
+            fprintf(stderr, "-t replay cache type\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")) {
+        if (!strstr(service_principal,"HTTP/")) {
+            debug((char *) "%s| %s: WARN: service_principal %s does not start with HTTP/\n",
+                  LogTime(), PROGRAM, service_principal);
+        }
         service.value = service_principal;
         service.length = strlen((char *) service.value);
     } else {
@@ -235,6 +513,69 @@ main(int argc, char *const argv[])
         xfree(host_name);
     }
 
+    if (rcache_type) {
+        rcache_type_env = (char *) xmalloc(strlen("KRB5RCACHETYPE=")+strlen(rcache_type)+1);
+        strcpy(rcache_type_env, "KRB5RCACHETYPE=");
+        strcat(rcache_type_env, rcache_type);
+        putenv(rcache_type_env);
+        debug((char *) "%s| %s: INFO: Setting replay cache type to %s\n",
+              LogTime(), PROGRAM, rcache_type);
+    }
+
+    if (rcache_dir) {
+        rcache_dir_env = (char *) xmalloc(strlen("KRB5RCACHEDIR=")+strlen(rcache_dir)+1);
+        strcpy(rcache_dir_env, "KRB5RCACHEDIR=");
+        strcat(rcache_dir_env, rcache_dir);
+        putenv(rcache_dir_env);
+        debug((char *) "%s| %s: INFO: Setting replay cache directory to %s\n",
+              LogTime(), PROGRAM, rcache_dir);
+    }
+
+    if (keytab_name) {
+        keytab_name_env = (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(keytab_name)+1);
+        strcpy(keytab_name_env, "KRB5_KTNAME=");
+        strcat(keytab_name_env, keytab_name);
+        putenv(keytab_name_env);
+    } else {
+        keytab_name_env = getenv("KRB5_KTNAME");
+        if (!keytab_name_env)
+            keytab_name = xstrdup("/etc/krb5.keytab");
+        else
+            keytab_name = xstrdup(keytab_name_env);
+    }
+    debug((char *) "%s| %s: INFO: Setting keytab to %s\n", LogTime(), PROGRAM, keytab_name);
+#if HAVE_KRB5_MEMORY_KEYTAB
+    ret = krb5_init_context(&context);
+    if (!check_k5_err(context, "krb5_init_context", ret)) {
+        memory_keytab_name = (char *)xmalloc(strlen("MEMORY:negotiate_kerberos_auth_")+16);
+        snprintf(memory_keytab_name, strlen("MEMORY:negotiate_kerberos_auth_")+16,
+                 "MEMORY:negotiate_kerberos_auth_%d", (unsigned int) getpid());
+        ret = krb5_read_keytab(context, keytab_name, &ktlist);
+        if (check_k5_err(context, "krb5_read_keytab", ret)) {
+            debug((char *) "%s| %s: ERROR: Reading keytab %s into list failed\n",
+                  LogTime(), PROGRAM, keytab_name);
+        } else {
+            ret = krb5_write_keytab(context, ktlist, memory_keytab_name);
+            if (check_k5_err(context, "krb5_write_keytab", ret)) {
+                debug((char *) "%s| %s: ERROR: Writing list into keytab %s\n",
+                      LogTime(), PROGRAM, memory_keytab_name);
+            } else {
+                keytab_name_env = (char *) xmalloc(strlen("KRB5_KTNAME=")+strlen(memory_keytab_name)+1);
+                strcpy(keytab_name_env, "KRB5_KTNAME=");
+                strcat(keytab_name_env, memory_keytab_name);
+                putenv(keytab_name_env);
+                xfree(keytab_name);
+                keytab_name = xstrdup(memory_keytab_name);
+                debug((char *) "%s| %s: INFO: Changed keytab to %s\n",
+                      LogTime(), PROGRAM, memory_keytab_name);
+            }
+        }
+    }
+    krb5_free_context(context);
+#endif
+#ifdef HAVE_HEIMDAL_KERBEROS
+    gsskrb5_register_acceptor_identity(keytab_name);
+#endif
     while (1) {
         if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
             if (ferror(stdin)) {
@@ -478,7 +819,7 @@ main(int argc, char *const argv[])
 #else
             fprintf(stdout, "AF %s %s\n", "AA==", rfc_user);
 #endif
-           debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", rfc_user);
+            debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", rfc_user);
             if (log)
                 fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
                         PROGRAM, rfc_user);
index d88549fb1df034e4d69fcce721a35d87bd0a915a..3b8ebc4fcbd5f1b0ba2838d46745b013a0063a13 100644 (file)
@@ -48,25 +48,8 @@ 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);
-#if HAVE_KRB5_FREE_ERROR_MESSAGE
-        krb5_free_error_message(context, errmsg);
-#elif HAVE_KRB5_FREE_ERROR_STRING
-        krb5_free_error_string(context, (char *)errmsg);
-#else
-        xfree(errmsg);
-#endif
-    }
-    return code;
-}
+extern int
+check_k5_err(krb5_context context, const char *function, krb5_error_code code);
 
 void
 align(int n)
@@ -134,7 +117,7 @@ get1byt(void)
 }
 
 char *
-xstrcpy( char *src, const char *dst)
+pstrcpy( char *src, const char *dst)
 {
     if (dst) {
         if (strlen(dst)>MAX_PAC_GROUP_SIZE)
@@ -146,7 +129,7 @@ xstrcpy( char *src, const char *dst)
 }
 
 char *
-xstrcat( char *src, const char *dst)
+pstrcat( char *src, const char *dst)
 {
     if (dst) {
         if (strlen(src)+strlen(dst)+1>MAX_PAC_GROUP_SIZE)
@@ -239,17 +222,17 @@ getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t Gro
             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=")) {
+                if (!pstrcpy(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=")) {
+                if (!pstrcat(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, (int)(length+4)))) {
+            if (!pstrcat(ad_groups,base64_encode_bin(ag, (int)(length+4)))) {
                 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
                       LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
             }
@@ -311,17 +294,17 @@ getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount)
                 ag = (char *)xcalloc((length)*sizeof(char),1);
                 memcpy((void *)ag,(const void*)&p[bpos],length);
                 if (!ad_groups) {
-                    if (!xstrcpy(ad_groups,"group=")) {
+                    if (!pstrcpy(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=")) {
+                    if (!pstrcat(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, (int)length))) {
+                if (!pstrcat(ad_groups,base64_encode_bin(ag, (int)length))) {
                     debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
                           LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
                 }
index 8885106e47651c6ca9d216133c11865bd0ca8e92..a36cc84c8f3027ad06f850b982b83258f516b34f 100755 (executable)
@@ -7,9 +7,14 @@
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
-if [[ -z "$1" ]]; then
+if test -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
+if test ! -f $dir/squid.keytab ; then
+       echo "Expect $dir/squid.keytab"
+       exit 0
+fi
+# $dir/negotiate_kerberos_auth_test $1 3 | awk '{sub(/Token:/,"YR"); print $0}END{print "QQ"}' | valgrind --log-file=$dir/negotiate_kerberos_auth.val --leak-check=full --show-reachable=yes -v $dir/negotiate_kerberos_auth -d -t none -k squid.keytab
+$dir/negotiate_kerberos_auth_test $1 3 | awk '{sub(/Token:/,"YR"); print $0}END{print "QQ"}' | $dir/negotiate_kerberos_auth -d -t none -k $dir/squid.keytab -s GSS_C_NO_NAME