Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into 4-0-abartlet
authorAndrew Bartlett <abartlet@samba.org>
Tue, 12 Aug 2008 23:47:18 +0000 (09:47 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 12 Aug 2008 23:47:18 +0000 (09:47 +1000)
source/auth/gensec/gensec.h
source/auth/gensec/gensec_gssapi.c
source/auth/gensec/spnego.c
source/auth/gensec/spnego_parse.c

index 2a89e67ed2ae46a3b7125b901049ebe4c162d13a..2830297ffe4043fc31ae661aac1f5d90b27279c0 100644 (file)
@@ -53,6 +53,7 @@ struct gensec_target {
 #define GENSEC_FEATURE_ASYNC_REPLIES   0x00000010
 #define GENSEC_FEATURE_DATAGRAM_MODE   0x00000020
 #define GENSEC_FEATURE_SIGN_PKT_HEADER 0x00000040
+#define GENSEC_FEATURE_NEW_SPNEGO      0x00000080
 
 /* GENSEC mode */
 enum gensec_role
index ff4a23e7fc615bf5159c780d782d6d0178ffbb70..0df40dc82fbc75e6a6588ec75c1ad12e42bf40c7 100644 (file)
@@ -65,6 +65,7 @@ struct gensec_gssapi_state {
        struct smb_krb5_context *smb_krb5_context;
        struct gssapi_creds_container *client_cred;
        struct gssapi_creds_container *server_cred;
+       gss_krb5_lucid_context_v1_t *lucid;
 
        gss_cred_id_t delegated_cred_handle;
 
@@ -143,9 +144,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 +206,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 +1121,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 +1177,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;
@@ -1386,8 +1449,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,18 +1461,12 @@ 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
@@ -1420,8 +1476,8 @@ size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t da
                } 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 +1497,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;
 }
 
index 1544326bb1c06c45f0a105dcf5d40cda440d1c61..1855e0583d71af366fadd22b4f150ff15fc42076 100644 (file)
@@ -5,6 +5,7 @@
    
    Copyright (C) Jim McDonough <jmcd@us.ibm.com>      2003
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+   Copyright (C) Stefan Metzmacher <metze@samba.org>  2004-2008
 
    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
@@ -44,6 +45,8 @@ struct spnego_state {
        bool no_response_expected;
 
        const char *neg_oid;
+
+       DATA_BLOB mech_types;
 };
 
 
@@ -60,6 +63,7 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi
        spnego_state->state_position = SPNEGO_CLIENT_START;
        spnego_state->sub_sec_security = NULL;
        spnego_state->no_response_expected = false;
+       spnego_state->mech_types = data_blob(NULL, 0);
 
        gensec_security->private_data = spnego_state;
        return NT_STATUS_OK;
@@ -78,6 +82,7 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi
        spnego_state->state_position = SPNEGO_SERVER_START;
        spnego_state->sub_sec_security = NULL;
        spnego_state->no_response_expected = false;
+       spnego_state->mech_types = data_blob(NULL, 0);
 
        gensec_security->private_data = spnego_state;
        return NT_STATUS_OK;
@@ -392,12 +397,22 @@ static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_
        int i;
        NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
        DATA_BLOB null_data_blob = data_blob(NULL,0);
+       bool ok;
 
        const struct gensec_security_ops_wrapper *all_sec
                = gensec_security_by_oid_list(gensec_security, 
                                              out_mem_ctx, 
                                              mechType,
                                              GENSEC_OID_SPNEGO);
+
+       ok = spnego_write_mech_types(spnego_state,
+                                    mechType,
+                                    &spnego_state->mech_types);
+       if (!ok) {
+               DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
        if (spnego_state->state_position == SPNEGO_SERVER_START) {
                for (i=0; all_sec && all_sec[i].op; i++) {
                        /* optomisitic token */
@@ -556,6 +571,9 @@ static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec
                                              GENSEC_OID_SPNEGO);
        for (i=0; all_sec && all_sec[i].op; i++) {
                struct spnego_data spnego_out;
+               const char **send_mech_types;
+               bool ok;
+
                nt_status = gensec_subcontext_start(spnego_state,
                                                    gensec_security,
                                                    &spnego_state->sub_sec_security);
@@ -591,10 +609,20 @@ static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec
                }
 
                spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
-               
+
+               send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
+                                                                       &all_sec[i]);
+
+               ok = spnego_write_mech_types(spnego_state,
+                                            send_mech_types,
+                                            &spnego_state->mech_types);
+               if (!ok) {
+                       DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
+                       return NT_STATUS_NO_MEMORY;
+               }
+
                /* List the remaining mechs as options */
-               spnego_out.negTokenInit.mechTypes = gensec_security_oids_from_ops_wrapped(out_mem_ctx, 
-                                                                                         &all_sec[i]);
+               spnego_out.negTokenInit.mechTypes = send_mech_types;
                spnego_out.negTokenInit.reqFlags = 0;
                
                if (spnego_state->state_position == SPNEGO_SERVER_START) {
@@ -644,7 +672,9 @@ static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec
                                                  struct spnego_state *spnego_state,
                                                  TALLOC_CTX *out_mem_ctx, 
                                                  NTSTATUS nt_status,
-                                                 const DATA_BLOB unwrapped_out, DATA_BLOB *out) 
+                                                 const DATA_BLOB unwrapped_out,
+                                                 DATA_BLOB mech_list_mic,
+                                                 DATA_BLOB *out)
 {
        struct spnego_data spnego_out;
        DATA_BLOB null_data_blob = data_blob(NULL, 0);
@@ -664,6 +694,7 @@ static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec
                        spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
                }
                spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+               spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
                spnego_state->state_position = SPNEGO_DONE;
        } else {
                spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
@@ -687,6 +718,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
 {
        struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
        DATA_BLOB null_data_blob = data_blob(NULL, 0);
+       DATA_BLOB mech_list_mic = data_blob(NULL, 0);
        DATA_BLOB unwrapped_out = data_blob(NULL, 0);
        struct spnego_data spnego_out;
        struct spnego_data spnego;
@@ -737,7 +769,8 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                                                                      spnego_state,
                                                                      out_mem_ctx,
                                                                      nt_status,
-                                                                     unwrapped_out, 
+                                                                     unwrapped_out,
+                                                                     null_data_blob,
                                                                      out);
                        
                        spnego_free_data(&spnego);
@@ -829,6 +862,8 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
        case SPNEGO_SERVER_TARG:
        {
                NTSTATUS nt_status;
+               bool new_spnego = false;
+
                if (!in.length) {
                        return NT_STATUS_INVALID_PARAMETER;
                }
@@ -860,12 +895,40 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                                          out_mem_ctx, 
                                          spnego.negTokenTarg.responseToken,
                                          &unwrapped_out);
+               if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
+                       new_spnego = true;
+                       nt_status = gensec_check_packet(spnego_state->sub_sec_security,
+                                                       out_mem_ctx,
+                                                       spnego_state->mech_types.data,
+                                                       spnego_state->mech_types.length,
+                                                       spnego_state->mech_types.data,
+                                                       spnego_state->mech_types.length,
+                                                       &spnego.negTokenTarg.mechListMIC);
+                       if (!NT_STATUS_IS_OK(nt_status)) {
+                               DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
+                                       nt_errstr(nt_status)));
+                       }
+               }
+               if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
+                       nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
+                                                      out_mem_ctx,
+                                                      spnego_state->mech_types.data,
+                                                      spnego_state->mech_types.length,
+                                                      spnego_state->mech_types.data,
+                                                      spnego_state->mech_types.length,
+                                                      &mech_list_mic);
+                       if (!NT_STATUS_IS_OK(nt_status)) {
+                               DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
+                                       nt_errstr(nt_status)));
+                       }
+               }
 
                nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
                                                              spnego_state,
                                                              out_mem_ctx, 
                                                              nt_status,
-                                                             unwrapped_out, 
+                                                             unwrapped_out,
+                                                             mech_list_mic,
                                                              out);
                
                spnego_free_data(&spnego);
@@ -940,12 +1003,44 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                        } else {
                                nt_status = NT_STATUS_OK;
                        }
+                       if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
+                               nt_status = gensec_check_packet(spnego_state->sub_sec_security,
+                                                               out_mem_ctx,
+                                                               spnego_state->mech_types.data,
+                                                               spnego_state->mech_types.length,
+                                                               spnego_state->mech_types.data,
+                                                               spnego_state->mech_types.length,
+                                                               &spnego.negTokenTarg.mechListMIC);
+                               if (!NT_STATUS_IS_OK(nt_status)) {
+                                       DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
+                                               nt_errstr(nt_status)));
+                               }
+                       }
                } else {
+                       bool new_spnego = false;
+
                        nt_status = gensec_update(spnego_state->sub_sec_security,
                                                  out_mem_ctx, 
                                                  spnego.negTokenTarg.responseToken, 
                                                  &unwrapped_out);
 
+                       if (NT_STATUS_IS_OK(nt_status)) {
+                               new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
+                                                                GENSEC_FEATURE_NEW_SPNEGO);
+                       }
+                       if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
+                               nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
+                                                              out_mem_ctx,
+                                                              spnego_state->mech_types.data,
+                                                              spnego_state->mech_types.length,
+                                                              spnego_state->mech_types.data,
+                                                              spnego_state->mech_types.length,
+                                                              &mech_list_mic);
+                               if (!NT_STATUS_IS_OK(nt_status)) {
+                                       DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
+                                               nt_errstr(nt_status)));
+                               }
+                       }
                        if (NT_STATUS_IS_OK(nt_status)) {
                                spnego_state->no_response_expected = true;
                        }
@@ -967,7 +1062,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                        spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
                        spnego_out.negTokenTarg.supportedMech = NULL;
                        spnego_out.negTokenTarg.responseToken = unwrapped_out;
-                       spnego_out.negTokenTarg.mechListMIC = null_data_blob;
+                       spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
                        
                        if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
                                DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
index 8012a83ba8d59149e9d49355f90e89bb5696828a..5ea8cf71002b9d386b3909e5d8a85e3242da6062 100644 (file)
@@ -374,3 +374,35 @@ out:
        return ret;
 }
 
+bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
+                            const char **mech_types,
+                            DATA_BLOB *blob)
+{
+       struct asn1_data *asn1 = asn1_init(mem_ctx);
+
+       /* Write mechTypes */
+       if (mech_types && *mech_types) {
+               int i;
+
+               asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+               for (i = 0; mech_types[i]; i++) {
+                       asn1_write_OID(asn1, mech_types[i]);
+               }
+               asn1_pop_tag(asn1);
+       }
+
+       if (asn1->has_error) {
+               asn1_free(asn1);
+               return false;
+       }
+
+       *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
+       if (blob->length != asn1->length) {
+               asn1_free(asn1);
+               return false;
+       }
+
+       asn1_free(asn1);
+
+       return true;
+}