------------------------------------------------------------ revno: 13700 revision-id: squid3@treenet.co.nz-20141220171025-klg12ilx88lold88 parent: squid3@treenet.co.nz-20141220160508-flie60i044v5l558 author: Markus Moeller committer: Amos Jeffries branch nick: 3.5 timestamp: Sat 2014-12-20 09:10:25 -0800 message: negotiate_kerberos_auth: MEMORY keytab and replay cache support 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. ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: squid3@treenet.co.nz-20141220171025-klg12ilx88lold88 # target_branch: http://bzr.squid-cache.org/bzr/squid3/3.5 # testament_sha1: bca1a2073d117d62f5acd3467a107715934b871c # timestamp: 2014-12-20 17:50:52 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/3.5 # base_revision_id: squid3@treenet.co.nz-20141220160508-\ # flie60i044v5l558 # # Begin patch === modified file 'acinclude/krb5.m4' --- acinclude/krb5.m4 2014-09-17 13:21:07 +0000 +++ acinclude/krb5.m4 2014-12-20 17:10:25 +0000 @@ -114,6 +114,33 @@ ]) ]) +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 +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 @@ 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]) === modified file 'helpers/negotiate_auth/kerberos/negotiate_kerberos.h' --- helpers/negotiate_auth/kerberos/negotiate_kerberos.h 2014-09-13 13:31:49 +0000 +++ helpers/negotiate_auth/kerberos/negotiate_kerberos.h 2014-12-20 17:10:25 +0000 @@ -116,13 +116,13 @@ 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; === modified file 'helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.8' --- helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.8 2014-10-31 09:12:50 +0000 +++ helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.8 2014-12-20 17:10:25 +0000 @@ -7,7 +7,7 @@ . .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 @@ .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 @@ 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 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 @@ 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 @@ . .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 @@ .SH QUESTIONS Questions on the usage of this program can be sent to the .I Squid Users mailing list -.if !'po4a'hide' +.if !'po4a'hide' . .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 +.I Squid Bugs .PP Report ideas for new improvements to the .I Squid Developers mailing list -.if !'po4a'hide' +.if !'po4a'hide' . .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 -.BR RFC2478 " - The Simple and Protected GSS-API Negotiation Mechanism," -.br -.BR RFC1964 " - The Kerberos Version 5 GSS-API Mechanism," +.BR RFC4559 " \- SPNEGO\-based Kerberos and NTLM HTTP Authentication in Microsoft Windows," +.br +.BR RFC2478 " \- The Simple and Protected GSS\-API Negotiation Mechanism," +.br +.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 === modified file 'helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc' --- helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc 2014-09-13 13:31:49 +0000 +++ helpers/negotiate_auth/kerberos/negotiate_kerberos_auth.cc 2014-12-20 17:10:25 +0000 @@ -44,6 +44,51 @@ #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 @@ 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 @@ 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 @@ 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,8 +334,6 @@ #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; @@ -161,6 +341,10 @@ 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; int opt, log = 0, norealm = 0; @@ -168,6 +352,15 @@ 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 @@ 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 @@ 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 @@ 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)) { === modified file 'helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc' --- helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc 2014-09-17 13:21:07 +0000 +++ helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc 2014-12-20 17:10:25 +0000 @@ -48,25 +48,8 @@ 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 @@ } 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 @@ } 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 @@ 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 @@ 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); } === modified file 'helpers/negotiate_auth/kerberos/test_negotiate_auth.sh' --- helpers/negotiate_auth/kerberos/test_negotiate_auth.sh 2014-09-13 13:31:49 +0000 +++ helpers/negotiate_auth/kerberos/test_negotiate_auth.sh 2014-12-20 17:10:25 +0000 @@ -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