Put the internal gensec_gssapi state into a header.
[kai/samba.git] / source4 / auth / gensec / gensec_gssapi.c
index 1541c88e0784714741e6bf97b12511f0860a1f6d..20576256c283cbea0106d1b1fc6a9858aa32361e 100644 (file)
 #include "auth/session_proto.h"
 #include <gssapi/gssapi.h>
 #include <gssapi/gssapi_krb5.h>
-
-enum gensec_gssapi_sasl_state 
-{
-       STAGE_GSS_NEG,
-       STAGE_SASL_SSF_NEG,
-       STAGE_SASL_SSF_ACCEPT,
-       STAGE_DONE
-};
-
-#define NEG_SEAL 0x4
-#define NEG_SIGN 0x2
-#define NEG_NONE 0x1
-
-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;
-       OM_uint32 want_flags, got_flags;
-       gss_OID gss_oid;
-
-       DATA_BLOB session_key;
-       DATA_BLOB pac;
-
-       struct smb_krb5_context *smb_krb5_context;
-       struct gssapi_creds_container *client_cred;
-       struct gssapi_creds_container *server_cred;
-
-       gss_cred_id_t delegated_cred_handle;
-
-       bool sasl; /* We have two different mechs in this file: One
-                   * for SASL wrapped GSSAPI and another for normal
-                   * GSSAPI */
-       enum gensec_gssapi_sasl_state sasl_state;
-       uint8_t sasl_protection; /* What was negotiated at the SASL
-                                 * layer, independent of the GSSAPI
-                                 * layer... */
-
-       size_t max_wrap_buf_size;
-       int gss_exchange_count;
-       size_t sig_size;
-};
+#include "auth/gensec/gensec_gssapi.h"
 
 static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security);
 static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security);
@@ -143,9 +102,45 @@ static int gensec_gssapi_destructor(struct gensec_gssapi_state *gensec_gssapi_st
        if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) {
                maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name);
        }
+
+       if (gensec_gssapi_state->lucid) {
+               gss_krb5_free_lucid_sec_context(&min_stat, gensec_gssapi_state->lucid);
+       }
+
        return 0;
 }
 
+static NTSTATUS gensec_gssapi_init_lucid(struct gensec_gssapi_state *gensec_gssapi_state)
+{
+       OM_uint32 maj_stat, min_stat;
+
+       if (gensec_gssapi_state->lucid) {
+               return NT_STATUS_OK;
+       }
+
+       maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
+                                                    &gensec_gssapi_state->gssapi_context,
+                                                    1,
+                                                    (void **)&gensec_gssapi_state->lucid);
+       if (maj_stat != GSS_S_COMPLETE) {
+               DEBUG(0,("gensec_gssapi_init_lucid: %s\n",
+                       gssapi_error_string(gensec_gssapi_state,
+                                           maj_stat, min_stat,
+                                           gensec_gssapi_state->gss_oid)));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       if (gensec_gssapi_state->lucid->version != 1) {
+               DEBUG(0,("gensec_gssapi_init_lucid: lucid version[%d] != 1\n",
+                       gensec_gssapi_state->lucid->version));
+               gss_krb5_free_lucid_sec_context(&min_stat, gensec_gssapi_state->lucid);
+               gensec_gssapi_state->lucid = NULL;
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return NT_STATUS_OK;
+}
+
 static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
 {
        struct gensec_gssapi_state *gensec_gssapi_state;
@@ -169,6 +164,7 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
        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;
+       gensec_gssapi_state->lucid = NULL;
 
        /* TODO: Fill in channel bindings */
        gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
@@ -1083,10 +1079,10 @@ static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_securi
 
        if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
                input_message.length = pdu_length;
-               input_message.value = whole_pdu;
+               input_message.value = discard_const(whole_pdu);
        } else {
                input_message.length = length;
-               input_message.value = data;
+               input_message.value = discard_const(data);
        }
 
        input_token.length = sig->length;
@@ -1139,6 +1135,31 @@ static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security,
        if (feature & GENSEC_FEATURE_DCE_STYLE) {
                return gensec_gssapi_state->got_flags & GSS_C_DCE_STYLE;
        }
+       if (feature & GENSEC_FEATURE_NEW_SPNEGO) {
+               NTSTATUS status;
+
+               if (!(gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG)) {
+                       return false;
+               }
+
+               if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "force_new_spnego", false)) {
+                       return true;
+               }
+               if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "disable_new_spnego", false)) {
+                       return false;
+               }
+
+               status = gensec_gssapi_init_lucid(gensec_gssapi_state);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return false;
+               }
+
+               if (gensec_gssapi_state->lucid->protocol == 1) {
+                       return true;
+               }
+
+               return false;
+       }
        /* We can always do async (rather than strict request/reply) packets.  */
        if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
                return true;
@@ -1165,20 +1186,24 @@ static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_securit
                return NT_STATUS_OK;
        }
 
-       maj_stat = gsskrb5_get_initiator_subkey(&min_stat, 
-                                               gensec_gssapi_state->gssapi_context,
-                                               &subkey);
+       maj_stat = gsskrb5_get_subkey(&min_stat,
+                                     gensec_gssapi_state->gssapi_context,
+                                     &subkey);
        if (maj_stat != 0) {
                DEBUG(1, ("NO session key for this mech\n"));
                return NT_STATUS_NO_USER_SESSION_KEY;
        }
        
-       DEBUG(10, ("Got KRB5 session key of length %d\n",  
-                  (int)KRB5_KEY_LENGTH(subkey)));
-       gensec_gssapi_state->session_key = data_blob_talloc(gensec_gssapi_state, 
-                                                           KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey));
+       DEBUG(10, ("Got KRB5 session key of length %d%s\n",
+                  (int)KRB5_KEY_LENGTH(subkey),
+                  (gensec_gssapi_state->sasl_state == STAGE_DONE)?" (done)":""));
+       *session_key = data_blob_talloc(gensec_gssapi_state,
+                                       KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey));
        krb5_free_keyblock(gensec_gssapi_state->smb_krb5_context->krb5_context, subkey);
-       *session_key = gensec_gssapi_state->session_key;
+       if (gensec_gssapi_state->sasl_state == STAGE_DONE) {
+               /* only cache in the done stage */
+               gensec_gssapi_state->session_key = *session_key;
+       }
        dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
 
        return NT_STATUS_OK;
@@ -1386,8 +1411,7 @@ size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t da
 {
        struct gensec_gssapi_state *gensec_gssapi_state
                = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
-       OM_uint32 maj_stat, min_stat;
-       gss_krb5_lucid_context_v1_t *lucid = NULL;
+       NTSTATUS status;
 
        if (gensec_gssapi_state->sig_size) {
                return gensec_gssapi_state->sig_size;
@@ -1399,29 +1423,23 @@ size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t da
                gensec_gssapi_state->sig_size = 37;
        }
 
-       maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
-                                                    &gensec_gssapi_state->gssapi_context,
-                                                    1, (void **)&lucid);
-       if (maj_stat != GSS_S_COMPLETE) {
-               return gensec_gssapi_state->sig_size;
-       }
-
-       if (lucid->version != 1) {
+       status = gensec_gssapi_init_lucid(gensec_gssapi_state);
+       if (!NT_STATUS_IS_OK(status)) {
                return gensec_gssapi_state->sig_size;
        }
 
-       if (lucid->protocol == 1) {
+       if (gensec_gssapi_state->lucid->protocol == 1) {
                if (gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG) {
                        /*
                         * TODO: windows uses 76 here, but we don't know
                         *       gss_wrap works with aes keys yet
                         */
-                       gensec_gssapi_state->sig_size = 60;
+                       gensec_gssapi_state->sig_size = 76;
                } else {
                        gensec_gssapi_state->sig_size = 28;
                }
-       } else if (lucid->protocol == 0) {
-               switch (lucid->rfc1964_kd.ctx_key.type) {
+       } else if (gensec_gssapi_state->lucid->protocol == 0) {
+               switch (gensec_gssapi_state->lucid->rfc1964_kd.ctx_key.type) {
                case KEYTYPE_DES:
                case KEYTYPE_ARCFOUR:
                case KEYTYPE_ARCFOUR_56:
@@ -1441,8 +1459,6 @@ size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t da
                }
        }
 
-       gss_krb5_free_lucid_sec_context(&min_stat, lucid);
-
        return gensec_gssapi_state->sig_size;
 }