r6727: One more step down the long march to the 'Kerberos domain join'.
authorAndrew Bartlett <abartlet@samba.org>
Wed, 11 May 2005 12:03:48 +0000 (12:03 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:16:38 +0000 (13:16 -0500)
This patch allows a suitably patched Heimdal GSSAPI library (detected
in configure) to supply to us the session keys, and further compleats
the gensec_gssapi module.  This is tested for CIFS, but fails for LDAP
at this point (that is what I'll work on next).

We currently fill out the 'session info' from the SAM, like
gensec_krb5 does, but both will need to use the PAC extraction
functions in the near future.

Andrew Bartlett

source/auth/gensec/gensec.mk
source/auth/gensec/gensec_gssapi.c
source/auth/kerberos/kerberos.m4

index 02823fc11f50a6dba0d582d75a10af4a045622b9..2057674c5e4fea7aaa7553ed1d4a1cbce25952c1 100644 (file)
@@ -14,7 +14,8 @@ REQUIRED_SUBSYSTEMS = \
 SUBSYSTEM = GENSEC
 INIT_FUNCTION = gensec_krb5_init
 INIT_OBJ_FILES = auth/gensec/gensec_krb5.o 
-REQUIRED_SUBSYSTEMS = NDR_KRB5PAC KERBEROS EXT_LIB_KRB5 
+REQUIRED_SUBSYSTEMS = NDR_KRB5PAC KERBEROS EXT_LIB_KRB5 REQUIRED_SUBSYSTEMS = AUTH
+
 # End MODULE gensec_krb5
 ################################################
 
@@ -24,7 +25,7 @@ REQUIRED_SUBSYSTEMS = NDR_KRB5PAC KERBEROS EXT_LIB_KRB5
 SUBSYSTEM = GENSEC
 INIT_FUNCTION = gensec_gssapi_init
 INIT_OBJ_FILES = auth/gensec/gensec_gssapi.o 
-REQUIRED_SUBSYSTEMS = EXT_LIB_KRB5
+REQUIRED_SUBSYSTEMS = EXT_LIB_KRB5 AUTH
 # End MODULE gensec_gssapi
 ################################################
 
index c974b93952ddda36381d3a28180bb1a70981fab8..b051e9cb44cfa52fd4012b30e886d5dca249f263 100644 (file)
@@ -4,6 +4,7 @@
    Kerberos backend for GENSEC
    
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
+   Copyright (C) Stefan Metzmacher <metze@samba.org> 2005
 
    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
 
 #include "includes.h"
 #include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
 #include "auth/auth.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_AUTH
 
+static const gss_OID_desc gensec_gss_krb5_mechanism_oid_desc =
+        {9, (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
+
+static const gss_OID_desc gensec_gss_spnego_mechanism_oid_desc =
+        {6, (void *)discard_const_p(char, "\x2b\x06\x01\x05\x05\x02")};
+
 struct gensec_gssapi_state {
        gss_ctx_id_t gssapi_context;
        struct gss_channel_bindings_struct *input_chan_bindings;
@@ -35,6 +44,9 @@ struct gensec_gssapi_state {
        gss_name_t client_name;
        OM_uint32 want_flags, got_flags;
        const gss_OID_desc *gss_oid;
+
+       DATA_BLOB session_key;
+       DATA_BLOB pac;
 };
 static int gensec_gssapi_destory(void *ptr) 
 {
@@ -79,9 +91,14 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
        gensec_gssapi_state->want_flags = 0;
        gensec_gssapi_state->got_flags = 0;
 
+       gensec_gssapi_state->session_key = data_blob(NULL, 0);
+       gensec_gssapi_state->pac = data_blob(NULL, 0);
+
        if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
-               /* GSSAPI won't give us the session keys */
+#ifndef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
+               /* GSSAPI won't give us the session keys, without the right hook */
                return NT_STATUS_INVALID_PARAMETER;
+#endif
        }
        if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
                gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG;
@@ -89,17 +106,17 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
        if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
                gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG;
        }
+       if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
+               gensec_gssapi_state->want_flags |= GSS_C_DCE_STYLE;
+       }
 
        if (strcmp(gensec_security->ops->oid, GENSEC_OID_KERBEROS5) == 0) {
-               static const gss_OID_desc gensec_gss_krb5_mechanism_oid_desc =
-                       {9, (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
-
                gensec_gssapi_state->gss_oid = &gensec_gss_krb5_mechanism_oid_desc;
        } else if (strcmp(gensec_security->ops->oid, GENSEC_OID_SPNEGO) == 0) {
-               static const gss_OID_desc gensec_gss_spnego_mechanism_oid_desc =
-                       {6, (void *)discard_const_p(char, "\x2b\x06\x01\x05\x05\x02")};
                gensec_gssapi_state->gss_oid = &gensec_gss_spnego_mechanism_oid_desc;
        } else {
+               DEBUG(1, ("gensec_gssapi incorrectly configured - cannot determine gss OID from %s\n", 
+                         gensec_security->ops->oid));
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -313,6 +330,198 @@ static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security,
        return NT_STATUS_OK;
 }
 
+static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security) 
+{
+       /* not const but work for DCERPC packets and arcfour */
+       return 45;
+}
+
+static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, 
+                                         TALLOC_CTX *mem_ctx, 
+                                         uint8_t *data, size_t length, 
+                                         const uint8_t *whole_pdu, size_t pdu_length, 
+                                         DATA_BLOB *sig)
+{
+       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;
+       ssize_t sig_length = 0;
+
+       input_token.length = length;
+       input_token.value = 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;
+       }
+
+       if (output_token.length < length) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       sig_length = 45;
+
+       memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
+       *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
+
+DEBUG(0,("gensec_gssapi_seal_packet: siglen: %d inlen: %d, wrap_len: %d\n", sig->length, length, output_token.length - sig_length));
+dump_data(0,sig->data, sig->length);
+dump_data(0,data, length);
+dump_data(0,((uint8_t *)output_token.value) + sig_length, output_token.length - sig_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_unseal_packet(struct gensec_security *gensec_security, 
+                                           TALLOC_CTX *mem_ctx, 
+                                           uint8_t *data, size_t length, 
+                                           const uint8_t *whole_pdu, size_t pdu_length,
+                                           const DATA_BLOB *sig)
+{
+       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;
+       DATA_BLOB in;
+
+DEBUG(0,("gensec_gssapi_unseal_packet: siglen: %d\n", sig->length));
+dump_data(0,sig->data, sig->length);
+
+       in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
+
+       memcpy(in.data, sig->data, sig->length);
+       memcpy(in.data + sig->length, data, length);
+
+       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;
+       }
+
+       if (output_token.length != length) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       memcpy(data, output_token.value, 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_sign_packet(struct gensec_security *gensec_security, 
+                                         TALLOC_CTX *mem_ctx, 
+                                         const uint8_t *data, size_t length, 
+                                         const uint8_t *whole_pdu, size_t pdu_length, 
+                                         DATA_BLOB *sig)
+{
+       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;
+       ssize_t sig_length = 0;
+
+       input_token.length = length;
+       input_token.value = discard_const_p(uint8_t *, data);
+
+       maj_stat = gss_wrap(&min_stat, 
+                           gensec_gssapi_state->gssapi_context,
+                           0,
+                           GSS_C_QOP_DEFAULT,
+                           &input_token,
+                           &conf_state,
+                           &output_token);
+       if (GSS_ERROR(maj_stat)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (output_token.length < length) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       sig_length = 45;
+
+       /*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/
+       *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
+
+DEBUG(0,("gensec_gssapi_sign_packet: siglen: %d\n", sig->length));
+dump_data(0,sig->data, sig->length);
+
+       gss_release_buffer(&min_stat, &output_token);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, 
+                                          TALLOC_CTX *mem_ctx, 
+                                          const uint8_t *data, size_t length, 
+                                          const uint8_t *whole_pdu, size_t pdu_length, 
+                                          const DATA_BLOB *sig)
+{
+       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;
+       DATA_BLOB in;
+
+DEBUG(0,("gensec_gssapi_check_packet: siglen: %d\n", sig->length));
+dump_data(0,sig->data, sig->length);
+
+       in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
+
+       memcpy(in.data, sig->data, sig->length);
+       memcpy(in.data + sig->length, data, length);
+
+       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;
+       }
+
+       if (output_token.length != length) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       /*memcpy(data, output_token.value, length);*/
+
+       gss_release_buffer(&min_stat, &output_token);
+
+       return NT_STATUS_OK;
+}
+
 static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, 
                                       uint32_t feature) 
 {
@@ -323,9 +532,125 @@ static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security,
        if (feature & GENSEC_FEATURE_SEAL) {
                return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
        }
+       if (feature & GENSEC_FEATURE_SESSION_KEY) {
+#ifdef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
+               if ((gensec_gssapi_state->gss_oid->length == gensec_gss_krb5_mechanism_oid_desc.length)
+                   && (memcmp(gensec_gssapi_state->gss_oid->elements, gensec_gss_krb5_mechanism_oid_desc.elements, gensec_gssapi_state->gss_oid->length) == 0)) {
+                       return True;
+               }
+       }
+#endif 
        return False;
 }
 
+static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, 
+                                         DATA_BLOB *session_key) 
+{
+       struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
+       
+       if (gensec_gssapi_state->session_key.data) {
+               *session_key = gensec_gssapi_state->session_key;
+               return NT_STATUS_OK;
+       }
+
+#ifdef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
+       if ((gensec_gssapi_state->gss_oid->length == gensec_gss_krb5_mechanism_oid_desc.length)
+           && (memcmp(gensec_gssapi_state->gss_oid->elements, gensec_gss_krb5_mechanism_oid_desc.elements, gensec_gssapi_state->gss_oid->length) == 0)) {
+               OM_uint32 maj_stat, min_stat;
+               gss_buffer_desc skey;
+               
+               maj_stat = gsskrb5_get_initiator_subkey(&min_stat, 
+                                                       gensec_gssapi_state->gssapi_context, 
+                                                       &skey);
+               
+               if (maj_stat == 0) {
+                       DEBUG(10, ("Got KRB5 session key of length %d\n",  skey.length));
+                       gensec_gssapi_state->session_key = data_blob_talloc(gensec_gssapi_state, 
+                                                                           skey.value, skey.length);
+                       *session_key = gensec_gssapi_state->session_key;
+                       dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
+                       
+                       gss_release_buffer(&min_stat, &skey);
+                       return NT_STATUS_OK;
+               }
+               return NT_STATUS_NO_USER_SESSION_KEY;
+       }
+#endif
+       
+       DEBUG(1, ("NO session key for this mech\n"));
+       return NT_STATUS_NO_USER_SESSION_KEY;
+}
+
+static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
+                                        struct auth_session_info **_session_info) 
+{
+       NTSTATUS nt_status;
+       struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
+       struct auth_serversupplied_info *server_info = NULL;
+       struct auth_session_info *session_info = NULL;
+       char *p;
+       char *principal;
+       const char *account_name;
+       const char *realm;
+       OM_uint32 maj_stat, min_stat;
+       gss_buffer_desc name_token;
+       
+       maj_stat = gss_display_name (&min_stat,
+                                    gensec_gssapi_state->client_name,
+                                    &name_token,
+                                    NULL);
+       if (maj_stat) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       principal = talloc_strndup(gensec_gssapi_state, name_token.value, name_token.length);
+
+       gss_release_buffer(&min_stat, &name_token);
+
+       NT_STATUS_HAVE_NO_MEMORY(principal);
+
+       p = strchr(principal, '@');
+       if (p) {
+               *p = '\0';
+               p++;
+               realm = p;
+       } else {
+               realm = lp_realm();
+       }
+       account_name = principal;
+
+       /* IF we have the PAC - otherwise we need to get this
+        * data from elsewere - local ldb, or (TODO) lookup of some
+        * kind... 
+        *
+        * when heimdal can generate the PAC, we should fail if there's
+        * no PAC present
+        */
+
+       {
+               DATA_BLOB user_sess_key = data_blob(NULL, 0);
+               DATA_BLOB lm_sess_key = data_blob(NULL, 0);
+               /* TODO: should we pass the krb5 session key in here? */
+               nt_status = sam_get_server_info(gensec_gssapi_state, account_name, realm,
+                                               user_sess_key, lm_sess_key,
+                                               &server_info);
+               talloc_free(principal);
+               NT_STATUS_NOT_OK_RETURN(nt_status);
+       }
+
+       /* references the server_info into the session_info */
+       nt_status = auth_generate_session_info(gensec_gssapi_state, server_info, &session_info);
+       talloc_free(server_info);
+       NT_STATUS_NOT_OK_RETURN(nt_status);
+
+       nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
+       NT_STATUS_NOT_OK_RETURN(nt_status);
+
+       *_session_info = session_info;
+
+       return NT_STATUS_OK;
+}
+
 /* As a server, this could in theory accept any GSSAPI mech */
 static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
        .name           = "gssapi_krb5",
@@ -334,6 +659,13 @@ static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
        .client_start   = gensec_gssapi_client_start,
        .server_start   = gensec_gssapi_server_start,
        .update         = gensec_gssapi_update,
+       .session_key    = gensec_gssapi_session_key,
+       .session_info   = gensec_gssapi_session_info,
+       .sig_size       = gensec_gssapi_sig_size,
+       .sign_packet    = gensec_gssapi_sign_packet,
+       .check_packet   = gensec_gssapi_check_packet,
+       .seal_packet    = gensec_gssapi_seal_packet,
+       .unseal_packet  = gensec_gssapi_unseal_packet,
        .wrap           = gensec_gssapi_wrap,
        .unwrap         = gensec_gssapi_unwrap,
        .have_feature   = gensec_gssapi_have_feature,
@@ -348,6 +680,12 @@ static const struct gensec_security_ops gensec_gssapi_spnego_security_ops = {
        .client_start   = gensec_gssapi_client_start,
        .server_start   = gensec_gssapi_server_start,
        .update         = gensec_gssapi_update,
+       .session_key    = gensec_gssapi_session_key,
+       .sig_size       = gensec_gssapi_sig_size,
+       .sign_packet    = gensec_gssapi_sign_packet,
+       .check_packet   = gensec_gssapi_check_packet,
+       .seal_packet    = gensec_gssapi_seal_packet,
+       .unseal_packet  = gensec_gssapi_unseal_packet,
        .wrap           = gensec_gssapi_wrap,
        .unwrap         = gensec_gssapi_unwrap,
        .have_feature   = gensec_gssapi_have_feature,
index f18386a91acb580ceb5cff7c540009c27f55e210..f9a2d66c0ab2d3cb44e64ae02bee00a9ea83865c 100644 (file)
@@ -452,6 +452,9 @@ if test x"$with_krb5_support" != x"no"; then
                samba_cv_GSS_C_DCE_STYLE=yes,
                samba_cv_GSS_C_DCE_STYLE=no)])
 
+       AC_CHECK_FUNC_EXT(gsskrb5_get_initiator_subkey, $KRB5_LIBS)
+       AC_CHECK_FUNC_EXT(gsskrb5_extract_authz_data_from_sec_context, $KRB5_LIBS)
+
        if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then
                AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support])
                AC_MSG_CHECKING(whether KRB5 support is used)