This adds gss-spnego to ntlm_auth. It contains some new spnego support
authorVolker Lendecke <vlendec@samba.org>
Tue, 29 Jul 2003 15:00:38 +0000 (15:00 +0000)
committerVolker Lendecke <vlendec@samba.org>
Tue, 29 Jul 2003 15:00:38 +0000 (15:00 +0000)
from Jim McDonough. It is to enable cyrus sasl to provide the
gss-spnego support. For a preliminary patch to cyrus sasl see

http://samba.sernet.de/cyrus-gss-spnego.diff

Volker
(This used to be commit 45cef8f66e46abe4a25fd2b803a7d1051c1c6602)

source3/Makefile.in
source3/include/includes.h
source3/include/spnego.h [new file with mode: 0644]
source3/libsmb/spnego.c [new file with mode: 0644]
source3/utils/ntlm_auth.c

index 56ca81fd5c9a3178a84430b64848c465ca8a7ab4..c4d1b306a89e22571278e394b460552aed148c8c 100644 (file)
@@ -615,7 +615,8 @@ POPT_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
 
 TDBBACKUP_OBJ = tdb/tdbbackup.o tdb/tdbback.o $(TDBBASE_OBJ)
 
-NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ)
+NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \
+               libsmb/asn1.o libsmb/spnego.o
 
 ######################################################################
 # now the rules...
index d900d7feb9f230c7b8057f9d6fb122e59bff8de4..eb7f73b9d35c68b364a9bbde03dc1f397019f704 100644 (file)
@@ -835,6 +835,8 @@ extern int errno;
 
 #include "nsswitch/winbind_client.h"
 
+#include "spnego.h"
+
 /*
  * Type for wide character dirent structure.
  * Only d_name is defined by POSIX.
diff --git a/source3/include/spnego.h b/source3/include/spnego.h
new file mode 100644 (file)
index 0000000..8bb13bd
--- /dev/null
@@ -0,0 +1,65 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   RFC2478 Compliant SPNEGO implementation
+
+   Copyright (C) Jim McDonough <jmcd@us.ibm.com>   2003
+
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef SAMBA_SPNEGO_H
+#define SAMBA_SPNEGO_H
+
+#define SPNEGO_DELEG_FLAG    0x01
+#define SPNEGO_MUTUAL_FLAG   0x02
+#define SPNEGO_REPLAY_FLAG   0x04
+#define SPNEGO_SEQUENCE_FLAG 0x08
+#define SPNEGO_ANON_FLAG     0x10
+#define SPNEGO_CONF_FLAG     0x20
+#define SPNEGO_INTEG_FLAG    0x40
+#define SPNEGO_REQ_FLAG      0x80
+
+#define SPNEGO_NEG_TOKEN_INIT 0
+#define SPNEGO_NEG_TOKEN_TARG 1
+
+typedef enum _spnego_negResult {
+       SPNEGO_ACCEPT_COMPLETED = 0,
+       SPNEGO_ACCEPT_INCOMPLETE = 1,
+       SPNEGO_REJECT = 2
+} negResult_t;
+
+typedef struct spnego_negTokenInit {
+       char **mechTypes;
+       int reqFlags;
+       DATA_BLOB mechToken;
+       DATA_BLOB mechListMIC;
+} negTokenInit_t;
+
+typedef struct spnego_negTokenTarg {
+       uint8 negResult;
+       char *supportedMech;
+       DATA_BLOB responseToken;
+       DATA_BLOB mechListMIC;
+} negTokenTarg_t;
+
+typedef struct spnego_spnego {
+       int type;
+       negTokenInit_t negTokenInit;
+       negTokenTarg_t negTokenTarg;
+} SPNEGO_DATA;
+
+#endif
diff --git a/source3/libsmb/spnego.c b/source3/libsmb/spnego.c
new file mode 100644 (file)
index 0000000..078191f
--- /dev/null
@@ -0,0 +1,292 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   RFC2478 Compliant SPNEGO implementation
+
+   Copyright (C) Jim McDonough <jmcd@us.ibm.com>   2003
+
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
+{
+       ZERO_STRUCTP(token);
+
+       asn1_start_tag(asn1, ASN1_CONTEXT(0));
+       asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+       while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+               int i;
+
+               switch (asn1->data[asn1->ofs]) {
+               /* Read mechTypes */
+               case ASN1_CONTEXT(0):
+                       asn1_start_tag(asn1, ASN1_CONTEXT(0));
+                       asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+                       token->mechTypes = malloc(sizeof(*token->mechTypes));
+                       for (i = 0; !asn1->has_error &&
+                                    0 < asn1_tag_remaining(asn1); i++) {
+                               token->mechTypes = 
+                                       realloc(token->mechTypes, (i + 1) *
+                                               sizeof(*token->mechTypes));
+                               asn1_read_OID(asn1, token->mechTypes + i);
+                       }
+                       token->mechTypes[i] = NULL;
+                       
+                       asn1_end_tag(asn1);
+                       asn1_end_tag(asn1);
+                       break;
+               /* Read reqFlags */
+               case ASN1_CONTEXT(1):
+                       asn1_start_tag(asn1, ASN1_CONTEXT(1));
+                       asn1_read_Integer(asn1, &token->reqFlags);
+                       token->reqFlags |= SPNEGO_REQ_FLAG;
+                       asn1_end_tag(asn1);
+                       break;
+                /* Read mechToken */
+               case ASN1_CONTEXT(2):
+                       asn1_start_tag(asn1, ASN1_CONTEXT(2));
+                       asn1_read_OctetString(asn1, &token->mechToken);
+                       asn1_end_tag(asn1);
+                       break;
+               /* Read mecListMIC */
+               case ASN1_CONTEXT(3):
+                       asn1_start_tag(asn1, ASN1_CONTEXT(3));
+                       if (!asn1_read_OctetString(asn1, &token->mechListMIC)) {
+                               char *mechListMIC;
+                               asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+                               asn1_push_tag(asn1, ASN1_CONTEXT(0));
+                               asn1_read_GeneralString(asn1, &mechListMIC);
+                               asn1_pop_tag(asn1);
+                               asn1_pop_tag(asn1);
+
+                               token->mechListMIC =
+                                       data_blob(mechListMIC, strlen(mechListMIC));
+                               SAFE_FREE(mechListMIC);
+                       }
+                       asn1_end_tag(asn1);
+                       break;
+               default:
+                       asn1->has_error = True;
+                       break;
+               }
+       }
+
+       asn1_end_tag(asn1);
+       asn1_end_tag(asn1);
+
+       return !asn1->has_error;
+}
+
+static BOOL write_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
+{
+       asn1_push_tag(asn1, ASN1_CONTEXT(0));
+       asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+       /* Write mechTypes */
+       if (token->mechTypes && *token->mechTypes) {
+               int i;
+
+               asn1_push_tag(asn1, ASN1_CONTEXT(0));
+               asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+               for (i = 0; token->mechTypes[i]; i++) {
+                       asn1_write_OID(asn1, token->mechTypes[i]);
+               }
+               asn1_pop_tag(asn1);
+               asn1_pop_tag(asn1);
+       }
+
+       /* write reqFlags */
+       if (token->reqFlags & SPNEGO_REQ_FLAG) {
+               int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
+
+               asn1_push_tag(asn1, ASN1_CONTEXT(1));
+               asn1_write_Integer(asn1, flags);
+               asn1_pop_tag(asn1);
+       }
+
+       /* write mechToken */
+       if (token->mechToken.data) {
+               asn1_push_tag(asn1, ASN1_CONTEXT(2));
+               asn1_write_OctetString(asn1, token->mechToken.data,
+                                      token->mechToken.length);
+               asn1_pop_tag(asn1);
+       }
+
+       /* write mechListMIC */
+       if (token->mechListMIC.data) {
+               asn1_push_tag(asn1, ASN1_CONTEXT(3));
+               asn1_write_OctetString(asn1, token->mechListMIC.data,
+                                      token->mechListMIC.length);
+               asn1_pop_tag(asn1);
+       }
+
+       asn1_pop_tag(asn1);
+       asn1_pop_tag(asn1);
+
+       return !asn1->has_error;
+}
+
+static BOOL read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
+{
+       ZERO_STRUCTP(token);
+
+       asn1_start_tag(asn1, ASN1_CONTEXT(1));
+       asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+       while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+               switch (asn1->data[asn1->ofs]) {
+               case ASN1_CONTEXT(0):
+                       /* this is listed as being non-optional by RFC2478 but
+                          Windows doesn't always send it... */
+                       asn1_start_tag(asn1, ASN1_CONTEXT(0));
+                       asn1_start_tag(asn1, ASN1_ENUMERATED);
+                       asn1_read_uint8(asn1, &token->negResult);
+                       asn1_end_tag(asn1);
+                       asn1_end_tag(asn1);
+                       break;
+               case ASN1_CONTEXT(1):
+                       asn1_start_tag(asn1, ASN1_CONTEXT(1));
+                       asn1_read_OID(asn1, &token->supportedMech);
+                       asn1_end_tag(asn1);
+                       break;
+               case ASN1_CONTEXT(2):
+                       asn1_start_tag(asn1, ASN1_CONTEXT(2));
+                       asn1_read_OctetString(asn1, &token->responseToken);
+                       asn1_end_tag(asn1);
+                       break;
+               case ASN1_CONTEXT(3):
+                       asn1_start_tag(asn1, ASN1_CONTEXT(3));
+                       asn1_read_OctetString(asn1, &token->mechListMIC);
+                       asn1_end_tag(asn1);
+                       break;
+               default:
+                       asn1->has_error = True;
+                       break;
+               }
+       }
+
+       asn1_end_tag(asn1);
+       asn1_end_tag(asn1);
+
+       return !asn1->has_error;
+}
+
+static BOOL write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
+{
+       asn1_push_tag(asn1, ASN1_CONTEXT(1));
+       asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+       asn1_push_tag(asn1, ASN1_CONTEXT(0));
+       asn1_write_enumerated(asn1, token->negResult);
+       asn1_pop_tag(asn1);
+
+       if (token->supportedMech) {
+               asn1_push_tag(asn1, ASN1_CONTEXT(1));
+               asn1_write_OID(asn1, token->supportedMech);
+               asn1_pop_tag(asn1);
+       }
+
+       if (token->responseToken.data) {
+               asn1_push_tag(asn1, ASN1_CONTEXT(2));
+               asn1_write_OctetString(asn1, token->responseToken.data,
+                                      token->responseToken.length);
+               asn1_pop_tag(asn1);
+       }
+
+       if (token->mechListMIC.data) {
+               asn1_push_tag(asn1, ASN1_CONTEXT(3));
+               asn1_write_OctetString(asn1, token->mechListMIC.data,
+                                     token->mechListMIC.length);
+               asn1_pop_tag(asn1);
+       }
+
+       asn1_pop_tag(asn1);
+       asn1_pop_tag(asn1);
+
+       return !asn1->has_error;
+}
+
+ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
+{
+       ASN1_DATA asn1;
+       ssize_t ret = -1;
+
+       ZERO_STRUCTP(token);
+       ZERO_STRUCT(asn1);
+       asn1_load(&asn1, data);
+
+       switch (asn1.data[asn1.ofs]) {
+       case ASN1_APPLICATION(0):
+               asn1_start_tag(&asn1, ASN1_APPLICATION(0));
+               asn1_check_OID(&asn1, OID_SPNEGO);
+               if (read_negTokenInit(&asn1, &token->negTokenInit)) {
+                       token->type = SPNEGO_NEG_TOKEN_INIT;
+               }
+               asn1_end_tag(&asn1);
+               break;
+       case ASN1_CONTEXT(1):
+               if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
+                       token->type = SPNEGO_NEG_TOKEN_TARG;
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (!asn1.has_error) ret = asn1.ofs;
+       asn1_free(&asn1);
+
+       return ret;
+}
+
+ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
+{
+       ASN1_DATA asn1;
+       ssize_t ret = -1;
+
+       ZERO_STRUCT(asn1);
+
+       switch (spnego->type) {
+       case SPNEGO_NEG_TOKEN_INIT:
+               asn1_push_tag(&asn1, ASN1_APPLICATION(0));
+               asn1_write_OID(&asn1, OID_SPNEGO);
+               write_negTokenInit(&asn1, &spnego->negTokenInit);
+               asn1_pop_tag(&asn1);
+               break;
+       case SPNEGO_NEG_TOKEN_TARG:
+               write_negTokenTarg(&asn1, &spnego->negTokenTarg);
+               break;
+       default:
+               asn1.has_error = True;
+               break;
+       }
+
+       if (!asn1.has_error) {
+               *blob = data_blob(asn1.data, asn1.length);
+               ret = asn1.ofs;
+       }
+       asn1_free(&asn1);
+
+       return ret;
+}
+
index a84f9b30f5d98df80d3196eb6dfe995b5ea3f7a7..fcb2cb4335ff803a42353b94fd3a424088af8097 100644 (file)
@@ -32,7 +32,8 @@
 enum squid_mode {
        SQUID_2_4_BASIC,
        SQUID_2_5_BASIC,
-       SQUID_2_5_NTLMSSP
+       SQUID_2_5_NTLMSSP,
+       GSS_SPNEGO
 };
        
 
@@ -342,6 +343,225 @@ static void manage_squid_basic_request(enum squid_mode squid_mode,
        }
 }
 
+static void offer_gss_spnego_mechs(void) {
+
+       DATA_BLOB token;
+       ASN1_DATA asn1;
+       SPNEGO_DATA spnego;
+       const char *OIDs[] = {OID_NTLMSSP, NULL};
+       ssize_t len;
+       char *reply_base64;
+
+       /* Server negTokenInit (mech offerings) */
+       ZERO_STRUCT(spnego);
+       spnego.type = SPNEGO_NEG_TOKEN_INIT;
+       spnego.negTokenInit.mechTypes = OIDs;
+
+       ZERO_STRUCT(asn1);
+       asn1_push_tag(&asn1, ASN1_SEQUENCE(0));
+       asn1_push_tag(&asn1, ASN1_CONTEXT(0));
+       asn1_write_GeneralString(&asn1, "NONE");
+       asn1_pop_tag(&asn1);
+       asn1_pop_tag(&asn1);
+       spnego.negTokenInit.mechListMIC = data_blob(asn1.data, asn1.length);
+       asn1_free(&asn1);
+
+       len = write_spnego_data(&token, &spnego);
+       data_blob_free(&spnego.negTokenInit.mechListMIC);
+
+       if (len == -1) {
+               DEBUG(1, ("Could not write SPNEGO data blob\n"));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
+       reply_base64 = base64_encode_data_blob(token);
+       x_fprintf(x_stdout, "TT %s\n", reply_base64);
+
+       SAFE_FREE(reply_base64);
+       data_blob_free(&token);
+       DEBUG(10, ("sent SPNEGO negTokenInit\n"));
+       return;
+}
+
+static void manage_gss_spnego_request(enum squid_mode squid_mode,
+                                     char *buf, int length) 
+{
+       static NTLMSSP_STATE *ntlmssp_state = NULL;
+       SPNEGO_DATA spnego;
+       DATA_BLOB request, token;
+       NTSTATUS status;
+       char *reply_base64;
+       pstring reply;
+       ssize_t len;
+
+       if (strlen(buf) < 2) {
+
+               if (ntlmssp_state != NULL) {
+                       DEBUG(1, ("Request for initial SPNEGO request where "
+                                 "we already have a state\n"));
+                       x_fprintf(x_stdout, "BH\n");
+                       return;
+               }
+
+               DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
+       if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) {
+
+               /* Initial request, get the negTokenInit offering
+                   mechanisms */
+
+               offer_gss_spnego_mechs();
+               return;
+       }
+
+       /* All subsequent requests are "KK" (Knock, Knock ;)) and have
+          a blob. This might be negTokenInit or negTokenTarg */
+
+       if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) {
+               DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
+       request = base64_decode_data_blob(buf + 3);
+
+       len = read_spnego_data(request, &spnego);
+
+       if (len == -1) {
+               DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
+       if ( (spnego.type != SPNEGO_NEG_TOKEN_INIT) &&
+            (spnego.type != SPNEGO_NEG_TOKEN_TARG) ) {
+
+               DEBUG(1, ("Got an invalid SPNEGO token!\n"));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
+       if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
+
+               /* Second request from Client. This is where the
+                  client offers its mechanism to use. We currently
+                  only support NTLMSSP, the decision for Kerberos
+                  would be taken here. */
+
+               if ( (spnego.negTokenInit.mechTypes == NULL) ||
+                    (spnego.negTokenInit.mechTypes[0] == NULL) ) {
+                       DEBUG(1, ("Client did not offer any mechanism"));
+                       x_fprintf(x_stdout, "BH\n");
+                       return;
+               }
+
+               if ( strcmp(spnego.negTokenInit.mechTypes[0], OID_NTLMSSP) != 0 ) {
+                       DEBUG(1, ("Client did not choose NTLMSSP but %s\n",
+                                 spnego.negTokenInit.mechTypes[0]));
+                       x_fprintf(x_stdout, "BH\n");
+                       return;
+               }
+
+               if ( spnego.negTokenInit.mechToken.data == NULL ) {
+                       DEBUG(1, ("Client did not provide  NTLMSSP data\n"));
+                       x_fprintf(x_stdout, "BH\n");
+                       return;
+               }
+
+               if ( ntlmssp_state != NULL ) {
+                       DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
+                                 "already got one\n"));
+                       x_fprintf(x_stdout, "BH\n");
+
+                       ntlmssp_server_end(&ntlmssp_state);
+                       return;
+               }
+
+               ntlmssp_server_start(&ntlmssp_state);
+               ntlmssp_state->check_password = winbind_pw_check;
+               ntlmssp_state->get_domain = get_winbind_domain;
+               ntlmssp_state->get_global_myname = get_winbind_netbios_name;
+
+               DEBUG(10, ("got NTLMSSP packet:\n"));
+               dump_data(10, spnego.negTokenInit.mechToken.data,
+                         spnego.negTokenInit.mechToken.length);
+
+               ZERO_STRUCT(spnego);
+               spnego.type = SPNEGO_NEG_TOKEN_TARG;
+               spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+               spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
+
+               status = ntlmssp_server_update(ntlmssp_state,
+                                              spnego.negTokenInit.mechToken,
+                                              &spnego.negTokenTarg.responseToken);
+
+       } else {
+
+               /* spnego.type == SPNEGO_NEG_TOKEN_TARG */
+
+               DATA_BLOB response;
+
+               if (spnego.negTokenTarg.responseToken.data == NULL) {
+                       DEBUG(1, ("Got a negTokenArg without a responseToken!\n"));
+                       x_fprintf(x_stdout, "BH\n");
+                       return;
+               }
+
+               status = ntlmssp_server_update(ntlmssp_state,
+                                              spnego.negTokenTarg.responseToken,
+                                              &response);
+
+               data_blob_free(&spnego.negTokenTarg.responseToken);
+
+               spnego.negTokenTarg.responseToken = response;
+
+       }
+
+       if ( !NT_STATUS_IS_OK(status) && 
+            !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ) {
+
+               /* Neither ok nor more work to do, so reject */
+
+               x_fprintf(x_stdout, "NA %s\n", nt_errstr(status));
+               DEBUG(10, ("NTLMSSP %s\n", nt_errstr(status)));
+               return;
+       }
+
+       pstr_sprintf(reply, "TT");
+
+       if (NT_STATUS_IS_OK(status)) {
+               pstr_sprintf(reply, "AF %s\\%s",
+                            ntlmssp_state->domain, ntlmssp_state->user);
+
+               spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+
+               DEBUG(10, ("NTLMSSP OK!\n"));
+       }
+
+       len = write_spnego_data(&token, &spnego);
+
+       if (len == -1) {
+               DEBUG(1, ("Could not write SPNEGO data blob\n"));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
+       reply_base64 = base64_encode_data_blob(token);
+       x_fprintf(x_stdout, "%s %s\n", reply, reply_base64);
+       SAFE_FREE(reply_base64);
+       data_blob_free(&token);
+
+       if (NT_STATUS_IS_OK(status)) {
+               ntlmssp_server_end(&ntlmssp_state);
+       }
+
+       return;
+}
+
 static void manage_squid_request(enum squid_mode squid_mode) 
 {
        char buf[SQUID_BUFFER_SIZE+1];
@@ -383,6 +603,8 @@ static void manage_squid_request(enum squid_mode squid_mode)
                manage_squid_basic_request(squid_mode, buf, length);
        } else if (squid_mode == SQUID_2_5_NTLMSSP) {
                manage_squid_ntlmssp_request(squid_mode, buf, length);
+       } else if (squid_mode == GSS_SPNEGO) {
+               manage_gss_spnego_request(squid_mode, buf, length);
        }
 }
 
@@ -1334,6 +1556,8 @@ enum {
                        squid_stream(SQUID_2_5_BASIC);
                } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
                        squid_stream(SQUID_2_4_BASIC);
+               } else if (strcmp(helper_protocol, "gss-spnego")== 0) {
+                       squid_stream(GSS_SPNEGO);
                } else {
                        x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
                        exit(1);