r4460: Add a new GENSEC module: gensec_gssapi
authorAndrew Bartlett <abartlet@samba.org>
Sat, 1 Jan 2005 00:19:08 +0000 (00:19 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:07:53 +0000 (13:07 -0500)
(disabled by default, set parametric option: gensec:gssapi=yes to enable).

This module backs directly onto GSSAPI, and allows us to sign and seal
GSSAPI/Krb5 connections in particular.  This avoids me reinventing the
entire GSSAPI wheel.

Currently a lot of things are left as default - we will soon start
specifiying OIDs as well as passwords (it uses the keytab only at the
moment).  Tested with our LDAP-* torture tests against Win2k3.

My hope is to use this module to access the new SPNEGO implementation
in Heimdal, to avoid having to standards-verify our own.

Andrew Bartlett

source/libcli/auth/gensec.mk
source/libcli/auth/gensec_gssapi.c [new file with mode: 0644]
source/libcli/auth/spnego.c
source/libcli/ldap/ldap_client.c
source/param/loadparm.c

index 66abfd10b7ed8ae869ade2bdfd6b197ca59b5671..b66006ce4fcc465019feb7211402c6545848dff0 100644 (file)
@@ -23,6 +23,16 @@ REQUIRED_SUBSYSTEMS = NDR_KRB5PAC EXT_LIB_KRB5
 # End MODULE gensec_krb5
 ################################################
 
+################################################
+# Start MODULE gensec_gssapi
+[MODULE::gensec_gssapi]
+SUBSYSTEM = GENSEC
+INIT_FUNCTION = gensec_gssapi_init
+INIT_OBJ_FILES = libcli/auth/gensec_gssapi.o 
+REQUIRED_SUBSYSTEMS = EXT_LIB_KRB5
+# End MODULE gensec_gssapi
+################################################
+
 ################################################
 # Start MODULE gensec_spnego
 [MODULE::gensec_spnego]
diff --git a/source/libcli/auth/gensec_gssapi.c b/source/libcli/auth/gensec_gssapi.c
new file mode 100644 (file)
index 0000000..c41c3fb
--- /dev/null
@@ -0,0 +1,336 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Kerberos backend for GENSEC
+   
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
+
+   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"
+#include "system/kerberos.h"
+#include "system/time.h"
+#include "libcli/auth/kerberos.h"
+#include "auth/auth.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+struct gensec_gssapi_state {
+       gss_ctx_id_t gssapi_context;
+       struct gss_channel_bindings_struct *input_chan_bindings;
+       gss_name_t server_name;
+       gss_name_t client_name;
+       int want_flags, got_flags;
+};
+static int gensec_gssapi_destory(void *ptr) 
+{
+       struct gensec_gssapi_state *gensec_gssapi_state = ptr;
+       OM_uint32 maj_stat, min_stat;
+
+       if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) {
+               maj_stat = gss_delete_sec_context (&min_stat,
+                                                  &gensec_gssapi_state->gssapi_context,
+                                                  GSS_C_NO_BUFFER);
+       }
+
+       if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) {
+               maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->server_name);
+       }
+       if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) {
+               maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name);
+       }
+       return 0;
+}
+
+static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
+{
+       struct gensec_gssapi_state *gensec_gssapi_state;
+
+       gensec_gssapi_state = talloc_p(gensec_security, struct gensec_gssapi_state);
+       if (!gensec_gssapi_state) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       gensec_security->private_data = gensec_gssapi_state;
+
+       gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT;
+       gensec_gssapi_state->server_name = GSS_C_NO_NAME;
+       gensec_gssapi_state->client_name = GSS_C_NO_NAME;
+
+       talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destory); 
+
+       /* TODO: Fill in channel bindings */
+       gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
+       
+       gensec_gssapi_state->want_flags = 0;
+       gensec_gssapi_state->got_flags = 0;
+
+       if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
+               /* GSSAPI won't give us the session keys */
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
+               gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG;
+       }
+       if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
+               gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security)
+{
+       NTSTATUS nt_status;
+       struct gensec_gssapi_state *gensec_gssapi_state;
+
+       nt_status = gensec_gssapi_start(gensec_security);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       gensec_gssapi_state = gensec_security->private_data;
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security)
+{
+       struct gensec_gssapi_state *gensec_gssapi_state;
+       NTSTATUS nt_status;
+       gss_buffer_desc name_token;
+       OM_uint32 maj_stat, min_stat;
+
+       nt_status = gensec_gssapi_start(gensec_security);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       gensec_gssapi_state = gensec_security->private_data;
+
+       name_token.value = talloc_asprintf(gensec_gssapi_state, "%s@%s", gensec_get_target_service(gensec_security), 
+                                          gensec_get_target_hostname(gensec_security));
+       DEBUG(0, ("name: %s\n", (char *)name_token.value));
+       name_token.length = strlen(name_token.value);
+
+       maj_stat = gss_import_name (&min_stat,
+                                   &name_token,
+                                   GSS_C_NT_HOSTBASED_SERVICE,
+                                   &gensec_gssapi_state->server_name);
+
+
+       if (maj_stat) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       return NT_STATUS_OK;
+}
+
+
+
+/**
+ * Next state function for the GSSAPI GENSEC mechanism
+ * 
+ * @param gensec_gssapi_state GSSAPI State
+ * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
+ * @param in The request, as a DATA_BLOB
+ * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
+ * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
+ *                or NT_STATUS_OK if the user is authenticated. 
+ */
+
+static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, 
+                                  TALLOC_CTX *out_mem_ctx, 
+                                  const DATA_BLOB in, DATA_BLOB *out) 
+{
+       struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       OM_uint32 maj_stat, min_stat;
+       OM_uint32 min_stat2;
+       gss_buffer_desc input_token, output_token;
+
+       input_token.length = in.length;
+       input_token.value = in.data;
+
+       switch (gensec_security->gensec_role) {
+       case GENSEC_CLIENT:
+       {
+               maj_stat = gss_init_sec_context(&min_stat, 
+                                               GSS_C_NO_CREDENTIAL, 
+                                               &gensec_gssapi_state->gssapi_context, 
+                                               gensec_gssapi_state->server_name, 
+                                               GSS_C_NO_OID, 
+                                               gensec_gssapi_state->want_flags, 
+                                               0, 
+                                               gensec_gssapi_state->input_chan_bindings,
+                                               &input_token, 
+                                               NULL, 
+                                               &output_token, 
+                                               &gensec_gssapi_state->got_flags, /* ret flags */
+                                               NULL);
+               break;
+       }
+       case GENSEC_SERVER:
+       {
+               maj_stat = gss_accept_sec_context(&min_stat, 
+                                                 gensec_gssapi_state->gssapi_context, 
+                                                 GSS_C_NO_CREDENTIAL, 
+                                                 &input_token, 
+                                                 gensec_gssapi_state->input_chan_bindings,
+                                                 &gensec_gssapi_state->client_name, 
+                                                 NULL /* mech oid */,
+                                                 &output_token, 
+                                                 &gensec_gssapi_state->got_flags, 
+                                                 NULL, 
+                                                 NULL);
+               break;
+       }
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
+               
+       }
+
+       *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
+       gss_release_buffer(&min_stat2, &output_token);
+
+       if (maj_stat == GSS_S_COMPLETE) {
+               return NT_STATUS_OK;
+       } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+               return NT_STATUS_MORE_PROCESSING_REQUIRED;
+       } else {
+               gss_buffer_desc msg1, msg2;
+               OM_uint32 msg_ctx = 0;
+               
+               msg1.value = NULL;
+               msg2.value = NULL;
+               gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE,
+                                  GSS_C_NULL_OID, &msg_ctx, &msg1);
+               gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE,
+                                  GSS_C_NULL_OID, &msg_ctx, &msg2);
+               DEBUG(1, ("gensec_gssapi_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value));
+               gss_release_buffer(&min_stat2, &msg1);
+               gss_release_buffer(&min_stat2, &msg2);
+
+               return nt_status;
+       }
+       
+}
+
+static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, 
+                                  TALLOC_CTX *mem_ctx, 
+                                  const DATA_BLOB *in, 
+                                  DATA_BLOB *out)
+{
+       struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
+       OM_uint32 maj_stat, min_stat;
+       gss_buffer_desc input_token, output_token;
+       int conf_state;
+       input_token.length = in->length;
+       input_token.value = in->data;
+       
+       maj_stat = gss_wrap(&min_stat, 
+                           gensec_gssapi_state->gssapi_context, 
+                           gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
+                           GSS_C_QOP_DEFAULT,
+                           &input_token,
+                           &conf_state,
+                           &output_token);
+       if (GSS_ERROR(maj_stat)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
+
+       gss_release_buffer(&min_stat, &output_token);
+
+       if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
+           && !conf_state) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, 
+                                    TALLOC_CTX *mem_ctx, 
+                                    const DATA_BLOB *in, 
+                                    DATA_BLOB *out)
+{
+       struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
+       OM_uint32 maj_stat, min_stat;
+       gss_buffer_desc input_token, output_token;
+       int conf_state;
+       gss_qop_t qop_state;
+       input_token.length = in->length;
+       input_token.value = in->data;
+       
+       maj_stat = gss_unwrap(&min_stat, 
+                             gensec_gssapi_state->gssapi_context, 
+                             &input_token,
+                             &output_token, 
+                             &conf_state,
+                             &qop_state);
+       if (GSS_ERROR(maj_stat)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
+
+       gss_release_buffer(&min_stat, &output_token);
+       
+       if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
+           && !conf_state) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       return NT_STATUS_OK;
+}
+
+static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, 
+                                      uint32 feature) 
+{
+       struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
+       if (feature & GENSEC_FEATURE_SIGN) {
+               return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG;
+       }
+       if (feature & GENSEC_FEATURE_SEAL) {
+               return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
+       }
+       return False;
+}
+
+static const struct gensec_security_ops gensec_gssapi_security_ops = {
+       .name           = "gssapi",
+       .oid            = GENSEC_OID_KERBEROS5,
+       .client_start   = gensec_gssapi_client_start,
+       .server_start   = gensec_gssapi_server_start,
+       .update         = gensec_gssapi_update,
+       .wrap           = gensec_gssapi_wrap,
+       .unwrap         = gensec_gssapi_unwrap,
+       .have_feature   = gensec_gssapi_have_feature
+
+};
+
+NTSTATUS gensec_gssapi_init(void)
+{
+       NTSTATUS ret;
+
+       ret = gensec_register(&gensec_gssapi_security_ops);
+       if (!NT_STATUS_IS_OK(ret)) {
+               DEBUG(0,("Failed to register '%s' gensec backend!\n",
+                       gensec_gssapi_security_ops.name));
+               return ret;
+       }
+
+       return ret;
+}
index f13bbc11b47df4e063f987758ba93cdeb383a70f..8b4be6eb751f17d38ee349a10ecbbc364ee33ba4 100644 (file)
@@ -42,6 +42,7 @@ struct spnego_state {
        enum spnego_message_type expected_packet;
        enum spnego_state_position state_position;
        struct gensec_security *sub_sec_security;
+       BOOL no_response_expected;
 };
 
 
@@ -57,6 +58,7 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi
        spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
        spnego_state->state_position = SPNEGO_CLIENT_START;
        spnego_state->sub_sec_security = NULL;
+       spnego_state->no_response_expected = False;
 
        gensec_security->private_data = spnego_state;
        return NT_STATUS_OK;
@@ -74,6 +76,7 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi
        spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
        spnego_state->state_position = SPNEGO_SERVER_START;
        spnego_state->sub_sec_security = NULL;
+       spnego_state->no_response_expected = False;
 
        gensec_security->private_data = spnego_state;
        return NT_STATUS_OK;
@@ -374,7 +377,7 @@ static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec
        }
        nt_status = gensec_update(spnego_state->sub_sec_security,
                                  out_mem_ctx, in, &unwrapped_out);
-       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+       if (NT_STATUS_IS_OK(nt_status) || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                struct spnego_data spnego_out;
                spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
                spnego_out.negTokenInit.mechTypes = mechTypes;
@@ -390,7 +393,12 @@ static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec
                /* set next state */
                spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
                spnego_state->state_position = SPNEGO_CLIENT_TARG;
-               return nt_status;
+               
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       spnego_state->no_response_expected = True;
+               }
+
+               return NT_STATUS_MORE_PROCESSING_REQUIRED;
        } 
        talloc_free(spnego_state->sub_sec_security);
        spnego_state->sub_sec_security = NULL;
@@ -601,6 +609,10 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                /* set next state */
                spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
                spnego_state->state_position = SPNEGO_CLIENT_TARG;
+
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       spnego_state->no_response_expected = True;
+               }
                
                return NT_STATUS_MORE_PROCESSING_REQUIRED;
        }
@@ -672,10 +684,14 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                        return NT_STATUS_ACCESS_DENIED;
                }
 
-               nt_status = gensec_update(spnego_state->sub_sec_security,
-                                         out_mem_ctx, 
-                                         spnego.negTokenTarg.responseToken, 
-                                         &unwrapped_out);
+               if (spnego_state->no_response_expected) {
+                       nt_status = NT_STATUS_OK;
+               } else {
+                       nt_status = gensec_update(spnego_state->sub_sec_security,
+                                                 out_mem_ctx, 
+                                                 spnego.negTokenTarg.responseToken, 
+                                                 &unwrapped_out);
+               } 
                
                
                if (NT_STATUS_IS_OK(nt_status) 
index 77356cbe70f84932608a747e71fe9281ccffa569..9ca9e4b5c4255a84ff87522bd55af7bdeaf2d22a 100644 (file)
@@ -459,9 +459,13 @@ int ldap_bind_sasl(struct ldap_connection *conn, const char *username, const cha
                        break;
                }
 
-               status = gensec_update(conn->gensec, mem_ctx,
-                                      response->r.BindResponse.SASL.secblob,
-                                      &output);
+               if (!NT_STATUS_IS_OK(status)) {
+                       status = gensec_update(conn->gensec, mem_ctx,
+                                              response->r.BindResponse.SASL.secblob,
+                                              &output);
+               } else {
+                       output.length = 0;
+               }
 
                talloc_free(response);
        }
index 70caf62ff2f03302fbb165f9549dfa5ee92e861f..524511d1b54eff6786d07e687f8e47060e6ffffc 100644 (file)
@@ -3075,6 +3075,7 @@ BOOL lp_load(const char *pszFname, BOOL global_only, BOOL save_defaults,
                lp_do_parameter(-1, "wins server", "127.0.0.1");
        }
 
+       lp_do_parameter(-1, "gensec:gssapi", "False");
        lp_do_parameter(-1, "gensec:krb5", "False");
        lp_do_parameter(-1, "gensec:ms_krb5", "False");