From f9861c9c5aee332545a9ea51683da28a87bdb10c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 28 Jun 2005 00:55:44 +0000 Subject: r7968: Pull the PAC from within GSSAPI, rather than only when using our own 'mock GSSAPI'. Many thanks to Luke Howard for the work he has done on Heimdal for XAD, to provide the right API hooks in GSSAPI. Next step is to verify the signatures, and to build the PAC for the KDC end. Andrew Bartlett (This used to be commit 2e82743c98e563e97c5a215d09efa0121854d0f7) --- source4/auth/gensec/gensec_gssapi.c | 80 +++++++++--- source4/auth/gensec/gensec_krb5.c | 184 +-------------------------- source4/auth/kerberos/kerberos_pac.c | 213 ++++++++++++++++++++++++++++++++ source4/auth/kerberos/kerberos_verify.c | 2 +- 4 files changed, 277 insertions(+), 202 deletions(-) create mode 100644 source4/auth/kerberos/kerberos_pac.c (limited to 'source4/auth') diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 533448e06f..a95805f9fa 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -3,8 +3,8 @@ Kerberos backend for GENSEC - Copyright (C) Andrew Bartlett 2004 - Copyright (C) Stefan Metzmacher 2005 + Copyright (C) Andrew Bartlett 2004-2005 + Copyright (C) Stefan Metzmacher 2004-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 @@ -224,6 +224,7 @@ static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_securi static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security) { struct gensec_gssapi_state *gensec_gssapi_state; + struct cli_credentials *creds = gensec_get_credentials(gensec_security); NTSTATUS nt_status; gss_buffer_desc name_token; OM_uint32 maj_stat, min_stat; @@ -251,8 +252,8 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi return NT_STATUS_UNSUCCESSFUL; } - name_token.value = cli_credentials_get_principal(gensec_get_credentials(gensec_security), - gensec_gssapi_state), + name_token.value = cli_credentials_get_principal(creds, + gensec_gssapi_state); name_token.length = strlen(name_token.value); maj_stat = gss_import_name (&min_stat, @@ -267,7 +268,7 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi } nt_status = kinit_to_ccache(gensec_gssapi_state, - gensec_get_credentials(gensec_security), + creds, gensec_gssapi_state->smb_krb5_context, &gensec_gssapi_state->ccache, &gensec_gssapi_state->ccache_name); if (!NT_STATUS_IS_OK(nt_status)) { @@ -724,16 +725,22 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi struct auth_session_info **_session_info) { NTSTATUS nt_status; + TALLOC_CTX *mem_ctx; 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; + struct PAC_LOGON_INFO *logon_info; char *p; char *principal; const char *account_name; const char *realm; OM_uint32 maj_stat, min_stat; gss_buffer_desc name_token; + gss_buffer_desc pac; + mem_ctx = talloc_named(gensec_gssapi_state, 0, "gensec_gssapi_session_info context"); + NT_STATUS_HAVE_NO_MEMORY(mem_ctx); + maj_stat = gss_display_name (&min_stat, gensec_gssapi_state->client_name, &name_token, @@ -742,11 +749,14 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi return NT_STATUS_FOOBAR; } - principal = talloc_strndup(gensec_gssapi_state, name_token.value, name_token.length); + principal = talloc_strndup(mem_ctx, name_token.value, name_token.length); gss_release_buffer(&min_stat, &name_token); - NT_STATUS_HAVE_NO_MEMORY(principal); + if (!principal) { + talloc_free(mem_ctx); + return NT_STATUS_NO_MEMORY; + } p = strchr(principal, '@'); if (p) { @@ -757,24 +767,56 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi realm = lp_realm(); } account_name = principal; + + maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, + gensec_gssapi_state->gssapi_context, + 1, + &pac); + + if (maj_stat == 0) { + DATA_BLOB pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length); + pac_blob = unwrap_pac(mem_ctx, &pac_blob); + gss_release_buffer(&min_stat, &pac); + + /* decode and verify the pac */ + nt_status = kerberos_decode_pac(mem_ctx, &logon_info, pac_blob, + gensec_gssapi_state->smb_krb5_context); + + if (NT_STATUS_IS_OK(nt_status)) { + union netr_Validation validation; + validation.sam3 = &logon_info->info3; + nt_status = make_server_info_netlogon_validation(gensec_gssapi_state, + account_name, + 3, &validation, + &server_info); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(mem_ctx); + return nt_status; + } + } else { + maj_stat = 1; + } + } + + if (maj_stat) { + /* 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 + */ - /* 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, + nt_status = sam_get_server_info(mem_ctx, account_name, realm, user_sess_key, lm_sess_key, &server_info); - talloc_free(principal); - NT_STATUS_NOT_OK_RETURN(nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(mem_ctx); + return nt_status; + } } /* references the server_info into the session_info */ diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index 348a75b535..6d3c105405 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -51,185 +51,6 @@ struct gensec_krb5_state { char *peer_principal; }; -#ifdef KRB5_DO_VERIFY_PAC -static NTSTATUS gensec_krb5_pac_checksum(DATA_BLOB pac_data, - struct PAC_SIGNATURE_DATA *sig, - struct gensec_krb5_state *gensec_krb5_state, - uint32 keyusage) -{ - krb5_error_code ret; - krb5_crypto crypto; - Checksum cksum; - int i; - - cksum.cksumtype = (CKSUMTYPE)sig->type; - cksum.checksum.length = sizeof(sig->signature); - cksum.checksum.data = sig->signature; - - - ret = krb5_crypto_init(gensec_krb5_state->smb_krb5_context->krb5_context, - &gensec_krb5_state->keyblock, - 0, - &crypto); - if (ret) { - DEBUG(0,("krb5_crypto_init() failed\n")); - return NT_STATUS_FOOBAR; - } - for (i=0; i < 40; i++) { - keyusage = i; - ret = krb5_verify_checksum(gensec_krb5_state->smb_krb5_context->krb5_context, - crypto, - keyusage, - pac_data.data, - pac_data.length, - &cksum); - if (!ret) { - DEBUG(0,("PAC Verified: keyusage: %d\n", keyusage)); - break; - } - } - krb5_crypto_destroy(gensec_krb5_state->smb_krb5_context->krb5_context, crypto); - - if (ret) { - DEBUG(0,("NOT verifying PAC checksums yet!\n")); - //return NT_STATUS_LOGON_FAILURE; - } else { - DEBUG(0,("PAC checksums verified!\n")); - } - - return NT_STATUS_OK; -} -#endif - -static NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx, - struct PAC_LOGON_INFO **logon_info_out, - DATA_BLOB blob, - struct gensec_krb5_state *gensec_krb5_state) -{ - NTSTATUS status; - struct PAC_SIGNATURE_DATA srv_sig; - struct PAC_SIGNATURE_DATA *srv_sig_ptr; - struct PAC_SIGNATURE_DATA kdc_sig; - struct PAC_SIGNATURE_DATA *kdc_sig_ptr; - struct PAC_LOGON_INFO *logon_info = NULL; - struct PAC_DATA pac_data; -#ifdef KRB5_DO_VERIFY_PAC - DATA_BLOB tmp_blob = data_blob(NULL, 0); -#endif - int i; - - status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("can't parse the PAC\n")); - return status; - } - NDR_PRINT_DEBUG(PAC_DATA, &pac_data); - - if (pac_data.num_buffers < 3) { - /* we need logon_ingo, service_key and kdc_key */ - DEBUG(0,("less than 3 PAC buffers\n")); - return NT_STATUS_FOOBAR; - } - - for (i=0; i < pac_data.num_buffers; i++) { - switch (pac_data.buffers[i].type) { - case PAC_TYPE_LOGON_INFO: - if (!pac_data.buffers[i].info) { - break; - } - logon_info = &pac_data.buffers[i].info->logon_info; - break; - case PAC_TYPE_SRV_CHECKSUM: - if (!pac_data.buffers[i].info) { - break; - } - srv_sig_ptr = &pac_data.buffers[i].info->srv_cksum; - srv_sig = pac_data.buffers[i].info->srv_cksum; - break; - case PAC_TYPE_KDC_CHECKSUM: - if (!pac_data.buffers[i].info) { - break; - } - kdc_sig_ptr = &pac_data.buffers[i].info->kdc_cksum; - kdc_sig = pac_data.buffers[i].info->kdc_cksum; - break; - case PAC_TYPE_UNKNOWN_10: - break; - default: - break; - } - } - - if (!logon_info) { - DEBUG(0,("PAC no logon_info\n")); - return NT_STATUS_FOOBAR; - } - - if (!srv_sig_ptr) { - DEBUG(0,("PAC no srv_key\n")); - return NT_STATUS_FOOBAR; - } - - if (!kdc_sig_ptr) { - DEBUG(0,("PAC no kdc_key\n")); - return NT_STATUS_FOOBAR; - } -#ifdef KRB5_DO_VERIFY_PAC - /* clear the kdc_key */ -/* memset((void *)kdc_sig_ptr , '\0', sizeof(*kdc_sig_ptr));*/ - - status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_push_flags_fn_t)ndr_push_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("can't parse the PAC\n")); - return status; - } - /*NDR_PRINT_DEBUG(PAC_DATA, &pac_data);*/ - - /* verify by kdc_key */ - status = gensec_krb5_pac_checksum(tmp_blob, &kdc_sig, gensec_krb5_state, 0); - - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* clear the service_key */ -/* memset((void *)srv_sig_ptr , '\0', sizeof(*srv_sig_ptr));*/ - - status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_push_flags_fn_t)ndr_push_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("can't parse the PAC\n")); - return status; - } - NDR_PRINT_DEBUG(PAC_DATA, &pac_data); - - /* verify by servie_key */ - status = gensec_krb5_pac_checksum(tmp_blob, &srv_sig, gensec_krb5_state, 0); - - if (!NT_STATUS_IS_OK(status)) { - return status; - } -#endif - DEBUG(0,("account_name: %s [%s]\n", - logon_info->info3.base.account_name.string, - logon_info->info3.base.full_name.string)); - *logon_info_out = logon_info; - - return status; -} - static int gensec_krb5_destory(void *ptr) { struct gensec_krb5_state *gensec_krb5_state = ptr; @@ -263,7 +84,6 @@ static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security) gensec_security->private_data = gensec_krb5_state; - initialize_krb5_error_table(); gensec_krb5_state->auth_context = NULL; gensec_krb5_state->ccache = NULL; ZERO_STRUCT(gensec_krb5_state->ticket); @@ -623,8 +443,8 @@ static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security account_name = principal; /* decode and verify the pac */ - nt_status = gensec_krb5_decode_pac(gensec_krb5_state, &logon_info, gensec_krb5_state->pac, - gensec_krb5_state); + nt_status = kerberos_decode_pac(gensec_krb5_state, &logon_info, gensec_krb5_state->pac, + gensec_krb5_state); /* IF we have the PAC - otherwise we need to get this * data from elsewere - local ldb, or (TODO) lookup of some diff --git a/source4/auth/kerberos/kerberos_pac.c b/source4/auth/kerberos/kerberos_pac.c new file mode 100644 index 0000000000..8f3d2cd72c --- /dev/null +++ b/source4/auth/kerberos/kerberos_pac.c @@ -0,0 +1,213 @@ +/* + Unix SMB/CIFS implementation. + + Kerberos backend for GENSEC + + Copyright (C) Andrew Bartlett 2004 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Luke Howard 2002-2003 + Copyright (C) Stefan Metzmacher 2004-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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "system/time.h" +#include "system/network.h" +#include "auth/kerberos/kerberos.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "auth/auth.h" + +#ifdef KRB5_DO_VERIFY_PAC +static NTSTATUS kerberos_pac_checksum(DATA_BLOB pac_data, + struct PAC_SIGNATURE_DATA *sig, + struct smb_krb5_context *smb_krb5_context, + uint32 keyusage) +{ + krb5_error_code ret; + krb5_crypto crypto; + Checksum cksum; + int i; + + cksum.cksumtype = (CKSUMTYPE)sig->type; + cksum.checksum.length = sizeof(sig->signature); + cksum.checksum.data = sig->signature; + + + ret = krb5_crypto_init(smb_krb5_context->krb5_context, + &gensec_krb5_state->keyblock, + 0, + &crypto); + if (ret) { + DEBUG(0,("krb5_crypto_init() failed\n")); + return NT_STATUS_FOOBAR; + } + for (i=0; i < 40; i++) { + keyusage = i; + ret = krb5_verify_checksum(smb_krb5_context->krb5_context, + crypto, + keyusage, + pac_data.data, + pac_data.length, + &cksum); + if (!ret) { + DEBUG(0,("PAC Verified: keyusage: %d\n", keyusage)); + break; + } + } + krb5_crypto_destroy(smb_krb5_context->krb5_context, crypto); + + if (ret) { + DEBUG(0,("NOT verifying PAC checksums yet!\n")); + //return NT_STATUS_LOGON_FAILURE; + } else { + DEBUG(0,("PAC checksums verified!\n")); + } + + return NT_STATUS_OK; +} +#endif + +NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + struct PAC_LOGON_INFO **logon_info_out, + DATA_BLOB blob, + struct smb_krb5_context *smb_krb5_context) +{ + NTSTATUS status; + struct PAC_SIGNATURE_DATA srv_sig; + struct PAC_SIGNATURE_DATA *srv_sig_ptr; + struct PAC_SIGNATURE_DATA kdc_sig; + struct PAC_SIGNATURE_DATA *kdc_sig_ptr; + struct PAC_LOGON_INFO *logon_info = NULL; + struct PAC_DATA pac_data; +#ifdef KRB5_DO_VERIFY_PAC + DATA_BLOB tmp_blob = data_blob(NULL, 0); +#endif + int i; + + status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("can't parse the PAC\n")); + return status; + } + NDR_PRINT_DEBUG(PAC_DATA, &pac_data); + + if (pac_data.num_buffers < 3) { + /* we need logon_ingo, service_key and kdc_key */ + DEBUG(0,("less than 3 PAC buffers\n")); + return NT_STATUS_FOOBAR; + } + + for (i=0; i < pac_data.num_buffers; i++) { + switch (pac_data.buffers[i].type) { + case PAC_TYPE_LOGON_INFO: + if (!pac_data.buffers[i].info) { + break; + } + logon_info = &pac_data.buffers[i].info->logon_info; + break; + case PAC_TYPE_SRV_CHECKSUM: + if (!pac_data.buffers[i].info) { + break; + } + srv_sig_ptr = &pac_data.buffers[i].info->srv_cksum; + srv_sig = pac_data.buffers[i].info->srv_cksum; + break; + case PAC_TYPE_KDC_CHECKSUM: + if (!pac_data.buffers[i].info) { + break; + } + kdc_sig_ptr = &pac_data.buffers[i].info->kdc_cksum; + kdc_sig = pac_data.buffers[i].info->kdc_cksum; + break; + case PAC_TYPE_UNKNOWN_10: + break; + default: + break; + } + } + + if (!logon_info) { + DEBUG(0,("PAC no logon_info\n")); + return NT_STATUS_FOOBAR; + } + + if (!srv_sig_ptr) { + DEBUG(0,("PAC no srv_key\n")); + return NT_STATUS_FOOBAR; + } + + if (!kdc_sig_ptr) { + DEBUG(0,("PAC no kdc_key\n")); + return NT_STATUS_FOOBAR; + } +#ifdef KRB5_DO_VERIFY_PAC + /* clear the kdc_key */ +/* memset((void *)kdc_sig_ptr , '\0', sizeof(*kdc_sig_ptr));*/ + + status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_push_flags_fn_t)ndr_push_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("can't parse the PAC\n")); + return status; + } + /*NDR_PRINT_DEBUG(PAC_DATA, &pac_data);*/ + + /* verify by kdc_key */ + status = kerberos_pac_checksum(tmp_blob, &kdc_sig, smb_krb5_context, 0); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* clear the service_key */ +/* memset((void *)srv_sig_ptr , '\0', sizeof(*srv_sig_ptr));*/ + + status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_push_flags_fn_t)ndr_push_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("can't parse the PAC\n")); + return status; + } + NDR_PRINT_DEBUG(PAC_DATA, &pac_data); + + /* verify by servie_key */ + status = kerberos_pac_checksum(tmp_blob, &srv_sig, smb_krb5_context, 0); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } +#endif + DEBUG(0,("account_name: %s [%s]\n", + logon_info->info3.base.account_name.string, + logon_info->info3.base.full_name.string)); + *logon_info_out = logon_info; + + return status; +} + diff --git a/source4/auth/kerberos/kerberos_verify.c b/source4/auth/kerberos/kerberos_verify.c index f269012ae3..01b8a75c95 100644 --- a/source4/auth/kerberos/kerberos_verify.c +++ b/source4/auth/kerberos/kerberos_verify.c @@ -34,7 +34,7 @@ #ifdef HAVE_KRB5 -static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) +DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) { DATA_BLOB out; DATA_BLOB pac_contents = data_blob(NULL, 0); -- cgit