From 674278d5b0d68e96d68f7beab2289a502efa6bc4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 17 Feb 2012 13:36:35 +1100 Subject: [PATCH] auth/kerberos: Move gse_get_session_key() to common code and use in gensec_gssapi Thie ensures that both code bases use the same logic to determine the use of NEW_SPNEGO. Andrew Bartlett --- auth/kerberos/gssapi_pac.c | 113 +++++++++++++++++++++++++++ libcli/auth/krb5_wrap.h | 17 ++++ source3/include/smb_krb5.h | 12 --- source3/librpc/crypto/gse.c | 116 +--------------------------- source4/auth/gensec/gensec_gssapi.c | 55 ++++++------- 5 files changed, 155 insertions(+), 158 deletions(-) diff --git a/auth/kerberos/gssapi_pac.c b/auth/kerberos/gssapi_pac.c index 70bc9e576a0..d0de11efdf2 100644 --- a/auth/kerberos/gssapi_pac.c +++ b/auth/kerberos/gssapi_pac.c @@ -22,6 +22,7 @@ #ifdef HAVE_KRB5 #include "libcli/auth/krb5_wrap.h" +#include "lib/util/asn1.h" #if 0 /* FIXME - need proper configure/waf test @@ -47,6 +48,26 @@ const gss_OID_desc * const gss_mech_krb5_old = krb5_gss_oid_array+1; const gss_OID_desc * const gss_mech_krb5_wrong = krb5_gss_oid_array+2; #endif +#ifndef GSS_KRB5_INQ_SSPI_SESSION_KEY_OID +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" +#endif + +gss_OID_desc gse_sesskey_inq_oid = { + GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH, + (void *)GSS_KRB5_INQ_SSPI_SESSION_KEY_OID +}; + +#ifndef GSS_KRB5_SESSION_KEY_ENCTYPE_OID +#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10 +#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04" +#endif + +gss_OID_desc gse_sesskeytype_oid = { + GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH, + (void *)GSS_KRB5_SESSION_KEY_ENCTYPE_OID +}; + /* The Heimdal OID for getting the PAC */ #define EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH 8 /* EXTRACTION OID AUTHZ ID */ @@ -149,4 +170,96 @@ NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx, #endif return NT_STATUS_ACCESS_DENIED; } + +NTSTATUS gssapi_get_session_key(TALLOC_CTX *mem_ctx, + gss_ctx_id_t gssapi_context, + DATA_BLOB *session_key, + uint32_t *keytype) +{ + OM_uint32 gss_min, gss_maj; + gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; + + gss_maj = gss_inquire_sec_context_by_oid( + &gss_min, gssapi_context, + &gse_sesskey_inq_oid, &set); + if (gss_maj) { + DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n", + gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5))); + return NT_STATUS_NO_USER_SESSION_KEY; + } + + if ((set == GSS_C_NO_BUFFER_SET) || + (set->count == 0)) { +#ifdef HAVE_GSSKRB5_GET_SUBKEY + krb5_keyblock *subkey; + gss_maj = gsskrb5_get_subkey(&gss_min, + gssapi_context, + &subkey); + if (gss_maj != 0) { + DEBUG(1, ("NO session key for this mech\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + if (session_key) { + *session_key = data_blob_talloc(mem_ctx, + KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey)); + } + if (keytype) { + *keytype = KRB5_KEY_TYPE(subkey); + } + krb5_free_keyblock(NULL /* should be krb5_context */, subkey); + return NT_STATUS_OK; +#else + DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown " + "OID for data in results:\n")); + dump_data(1, (uint8_t *)set->elements[1].value, + set->elements[1].length); + return NT_STATUS_NO_USER_SESSION_KEY; +#endif + } + + if (session_key) { + *session_key = data_blob_talloc(mem_ctx, set->elements[0].value, + set->elements[0].length); + } + + if (keytype) { + char *oid; + char *p, *q = NULL; + + if (set->count < 2 + || memcmp(set->elements[1].value, + gse_sesskeytype_oid.elements, + gse_sesskeytype_oid.length) != 0) { + /* Perhaps a non-krb5 session key */ + *keytype = 0; + gss_maj = gss_release_buffer_set(&gss_min, &set); + return NT_STATUS_OK; + } + if (!ber_read_OID_String(mem_ctx, + data_blob_const(set->elements[1].value, + set->elements[1].length), &oid)) { + TALLOC_FREE(oid); + gss_maj = gss_release_buffer_set(&gss_min, &set); + return NT_STATUS_INVALID_PARAMETER; + } + p = strrchr(oid, '.'); + if (!p) { + TALLOC_FREE(oid); + gss_maj = gss_release_buffer_set(&gss_min, &set); + return NT_STATUS_INVALID_PARAMETER; + } else { + p++; + *keytype = strtoul(p, &q, 10); + if (q == NULL || *q != '\0') { + TALLOC_FREE(oid); + return NT_STATUS_INVALID_PARAMETER; + } + } + TALLOC_FREE(oid); + } + + gss_maj = gss_release_buffer_set(&gss_min, &set); + return NT_STATUS_OK; +} + #endif diff --git a/libcli/auth/krb5_wrap.h b/libcli/auth/krb5_wrap.h index 814c427a56d..01ea6acd070 100644 --- a/libcli/auth/krb5_wrap.h +++ b/libcli/auth/krb5_wrap.h @@ -24,6 +24,18 @@ struct PAC_SIGNATURE_DATA; struct PAC_DATA; +#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE /* Heimdal */ +#define KRB5_KEY_TYPE(k) ((k)->keytype) +#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length) +#define KRB5_KEY_DATA(k) ((k)->keyvalue.data) +#define KRB5_KEY_DATA_CAST void +#else /* MIT */ +#define KRB5_KEY_TYPE(k) ((k)->enctype) +#define KRB5_KEY_LENGTH(k) ((k)->length) +#define KRB5_KEY_DATA(k) ((k)->contents) +#define KRB5_KEY_DATA_CAST krb5_octet +#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */ + int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, @@ -76,6 +88,11 @@ NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx, gss_ctx_id_t gssapi_context, gss_name_t gss_client_name, DATA_BLOB *pac_data); +NTSTATUS gssapi_get_session_key(TALLOC_CTX *mem_ctx, + gss_ctx_id_t gssapi_context, + DATA_BLOB *session_key, + uint32_t *keytype); + DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2]); bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2]); diff --git a/source3/include/smb_krb5.h b/source3/include/smb_krb5.h index bc9996c541d..152652512d9 100644 --- a/source3/include/smb_krb5.h +++ b/source3/include/smb_krb5.h @@ -66,18 +66,6 @@ typedef struct { #endif /* defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) */ } smb_krb5_addresses; -#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE /* Heimdal */ -#define KRB5_KEY_TYPE(k) ((k)->keytype) -#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length) -#define KRB5_KEY_DATA(k) ((k)->keyvalue.data) -#define KRB5_KEY_DATA_CAST void -#else /* MIT */ -#define KRB5_KEY_TYPE(k) ((k)->enctype) -#define KRB5_KEY_LENGTH(k) ((k)->length) -#define KRB5_KEY_DATA(k) ((k)->contents) -#define KRB5_KEY_DATA_CAST krb5_octet -#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */ - #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY /* MIT */ #define KRB5_KT_KEY(k) (&(k)->key) #elif HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK /* Heimdal */ diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c index d8f3af08978..1ce3761ae79 100644 --- a/source3/librpc/crypto/gse.c +++ b/source3/librpc/crypto/gse.c @@ -35,26 +35,6 @@ #include "smb_krb5.h" #include "gse_krb5.h" -#ifndef GSS_KRB5_INQ_SSPI_SESSION_KEY_OID -#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 -#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" -#endif - -gss_OID_desc gse_sesskey_inq_oid = { - GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH, - (void *)GSS_KRB5_INQ_SSPI_SESSION_KEY_OID -}; - -#ifndef GSS_KRB5_SESSION_KEY_ENCTYPE_OID -#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10 -#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04" -#endif - -gss_OID_desc gse_sesskeytype_oid = { - GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH, - (void *)GSS_KRB5_SESSION_KEY_ENCTYPE_OID -}; - static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min); struct gse_context { @@ -563,96 +543,6 @@ done: return errstr; } -static NTSTATUS gse_get_session_key(TALLOC_CTX *mem_ctx, - struct gse_context *gse_ctx, - DATA_BLOB *session_key, - uint32_t *keytype) -{ - OM_uint32 gss_min, gss_maj; - gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; - - gss_maj = gss_inquire_sec_context_by_oid( - &gss_min, gse_ctx->gssapi_context, - &gse_sesskey_inq_oid, &set); - if (gss_maj) { - DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n", - gse_errstr(talloc_tos(), gss_maj, gss_min))); - return NT_STATUS_NO_USER_SESSION_KEY; - } - - if ((set == GSS_C_NO_BUFFER_SET) || - (set->count == 0)) { -#ifdef HAVE_GSSKRB5_GET_SUBKEY - krb5_keyblock *subkey; - gss_maj = gsskrb5_get_subkey(&gss_min, - gse_ctx->gssapi_context, - &subkey); - if (gss_maj != 0) { - DEBUG(1, ("NO session key for this mech\n")); - return NT_STATUS_NO_USER_SESSION_KEY; - } - if (session_key) { - *session_key = data_blob_talloc(mem_ctx, - KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey)); - } - if (keytype) { - *keytype = KRB5_KEY_TYPE(subkey); - } - krb5_free_keyblock(NULL /* should be krb5_context */, subkey); - return NT_STATUS_OK; -#else - DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown " - "OID for data in results:\n")); - dump_data(1, (uint8_t *)set->elements[1].value, - set->elements[1].length); - return NT_STATUS_NO_USER_SESSION_KEY; -#endif - } - - if (session_key) { - *session_key = data_blob_talloc(mem_ctx, set->elements[0].value, - set->elements[0].length); - } - - if (keytype) { - char *oid; - char *p, *q = NULL; - - if (set->count < 2 - || memcmp(set->elements[1].value, - gse_sesskeytype_oid.elements, - gse_sesskeytype_oid.length) != 0) { - /* Perhaps a non-krb5 session key */ - *keytype = 0; - gss_maj = gss_release_buffer_set(&gss_min, &set); - return NT_STATUS_OK; - } - if (!ber_read_OID_String(talloc_tos(), - data_blob_const(set->elements[1].value, - set->elements[1].length), &oid)) { - TALLOC_FREE(oid); - gss_maj = gss_release_buffer_set(&gss_min, &set); - return NT_STATUS_INVALID_PARAMETER; - } - p = strrchr(oid, '.'); - if (!p) { - TALLOC_FREE(oid); - gss_maj = gss_release_buffer_set(&gss_min, &set); - return NT_STATUS_INVALID_PARAMETER; - } else { - p++; - *keytype = strtoul(p, &q, 10); - if (q == NULL || *q != '\0') { - return NT_STATUS_INVALID_PARAMETER; - } - } - TALLOC_FREE(oid); - } - - gss_maj = gss_release_buffer_set(&gss_min, &set); - return NT_STATUS_OK; -} - static size_t gse_get_signature_length(struct gse_context *gse_ctx, bool seal, size_t payload_size) { @@ -1125,8 +1015,8 @@ static bool gensec_gse_have_feature(struct gensec_security *gensec_security, return false; } - status = gse_get_session_key(talloc_tos(), - gse_ctx, NULL, &keytype); + status = gssapi_get_session_key(talloc_tos(), + gse_ctx->gssapi_context, NULL, &keytype); /* * We should do a proper sig on the mechListMic unless * we know we have to be backwards compatible with @@ -1168,7 +1058,7 @@ static NTSTATUS gensec_gse_session_key(struct gensec_security *gensec_security, talloc_get_type_abort(gensec_security->private_data, struct gse_context); - return gse_get_session_key(mem_ctx, gse_ctx, session_key, NULL); + return gssapi_get_session_key(mem_ctx, gse_ctx->gssapi_context, session_key, NULL); } /* Get some basic (and authorization) information about the user on diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 7f504c5a0ff..b2729a9bae3 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -1229,6 +1229,7 @@ static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security, } if (feature & GENSEC_FEATURE_NEW_SPNEGO) { NTSTATUS status; + uint32_t keytype; if (!(gensec_gssapi_state->gss_got_flags & GSS_C_INTEG_FLAG)) { return false; @@ -1241,16 +1242,27 @@ static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security, return false; } - status = gensec_gssapi_init_lucid(gensec_gssapi_state); - if (!NT_STATUS_IS_OK(status)) { - return false; + status = gssapi_get_session_key(gensec_gssapi_state, + gensec_gssapi_state->gssapi_context, NULL, &keytype); + /* + * We should do a proper sig on the mechListMic unless + * we know we have to be backwards compatible with + * earlier windows versions. + * + * Negotiating a non-krb5 + * mech for example should be regarded as having + * NEW_SPNEGO + */ + if (NT_STATUS_IS_OK(status)) { + switch (keytype) { + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_DES3_CBC_SHA1: + return false; + } } - - if (gensec_gssapi_state->lucid->protocol == 1) { - return true; - } - - return false; + return true; } /* We can always do async (rather than strict request/reply) packets. */ if (feature & GENSEC_FEATURE_ASYNC_REPLIES) { @@ -1271,30 +1283,7 @@ static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_securit { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); - OM_uint32 maj_stat, min_stat; - krb5_keyblock *subkey; - - if (gensec_gssapi_state->sasl_state != STAGE_DONE) { - return NT_STATUS_NO_USER_SESSION_KEY; - } - - 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%s\n", - (int)KRB5_KEY_LENGTH(subkey), - (gensec_gssapi_state->sasl_state == STAGE_DONE)?" (done)":"")); - *session_key = data_blob_talloc(mem_ctx, - KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey)); - krb5_free_keyblock(gensec_gssapi_state->smb_krb5_context->krb5_context, subkey); - dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); - - return NT_STATUS_OK; + return gssapi_get_session_key(mem_ctx, gensec_gssapi_state->gssapi_context, session_key, NULL); } /* Get some basic (and authorization) information about the user on