From 8b2eb02d159774168eeb9c2499447ecf13e1b915 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 May 2005 12:03:48 +0000 Subject: r6727: One more step down the long march to the 'Kerberos domain join'. 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 (This used to be commit 937ee361615a487af9e0279145e75b6c27720a6b) --- source4/auth/gensec/gensec.mk | 5 +- source4/auth/gensec/gensec_gssapi.c | 350 +++++++++++++++++++++++++++++++++++- source4/auth/kerberos/kerberos.m4 | 3 + 3 files changed, 350 insertions(+), 8 deletions(-) (limited to 'source4') diff --git a/source4/auth/gensec/gensec.mk b/source4/auth/gensec/gensec.mk index 02823fc11f..2057674c5e 100644 --- a/source4/auth/gensec/gensec.mk +++ b/source4/auth/gensec/gensec.mk @@ -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 ################################################ diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index c974b93952..b051e9cb44 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -4,6 +4,7 @@ Kerberos backend for GENSEC Copyright (C) Andrew Bartlett 2004 + Copyright (C) Stefan Metzmacher 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 @@ -23,11 +24,19 @@ #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, diff --git a/source4/auth/kerberos/kerberos.m4 b/source4/auth/kerberos/kerberos.m4 index f18386a91a..f9a2d66c0a 100644 --- a/source4/auth/kerberos/kerberos.m4 +++ b/source4/auth/kerberos/kerberos.m4 @@ -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) -- cgit