From 8db8279730c6d4ef6b03b9f96381dee890a8da57 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 5 Aug 2005 00:41:53 +0000 Subject: [PATCH] r9084: 'resign' the sample PAC for the validation of the signature algorithms. If we ever get problems with the kerberos code, it should show up as a different signature in this PAC. This involved returning more data from the pac functions, so changed some callers and split up some functions. Andrew Bartlett (This used to be commit d514a7491208afa0533bf9e99601147eb69e08c9) --- source4/auth/gensec/gensec_gssapi.c | 6 +- source4/auth/gensec/gensec_krb5.c | 6 +- source4/auth/kerberos/kerberos.h | 22 ++- source4/auth/kerberos/kerberos_pac.c | 259 ++++++++++++++++++--------- source4/kdc/pac-glue.c | 2 +- source4/torture/auth/pac.c | 56 +++--- 6 files changed, 219 insertions(+), 132 deletions(-) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 0a98b69f829..b6fda0402f8 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -795,9 +795,9 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi 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, - NULL, keyblock); + nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, pac_blob, + gensec_gssapi_state->smb_krb5_context, + NULL, keyblock); if (NT_STATUS_IS_OK(nt_status)) { union netr_Validation validation; diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index 76f91717135..2568f11006f 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -449,9 +449,9 @@ static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security account_name = principal; /* decode and verify the pac */ - nt_status = kerberos_decode_pac(gensec_krb5_state, &logon_info, gensec_krb5_state->pac, - gensec_krb5_state->smb_krb5_context, - NULL, gensec_krb5_state->keyblock); + nt_status = kerberos_pac_logon_info(gensec_krb5_state, &logon_info, gensec_krb5_state->pac, + gensec_krb5_state->smb_krb5_context, + NULL, gensec_krb5_state->keyblock); /* 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.h b/source4/auth/kerberos/kerberos.h index dcafea3c0cb..0f1b0779b2e 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -127,17 +127,29 @@ NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx, struct smb_krb5_context *smb_krb5_context, krb5_keytab *keytab); NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, - struct PAC_LOGON_INFO **logon_info_out, + struct PAC_DATA **pac_data_out, DATA_BLOB blob, struct smb_krb5_context *smb_krb5_context, - krb5_keyblock *service_keyblock, - krb5_keyblock *krbtgt_keyblock); - -krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx, + krb5_keyblock *krbtgt_keyblock, + krb5_keyblock *service_keyblock); +NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx, + struct PAC_LOGON_INFO **logon_info, + DATA_BLOB blob, + struct smb_krb5_context *smb_krb5_context, + krb5_keyblock *krbtgt_keyblock, + krb5_keyblock *service_keyblock); +krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info *server_info, krb5_context context, krb5_keyblock *krbtgt_keyblock, krb5_keyblock *server_keyblock, DATA_BLOB *pac); + +krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx, + struct PAC_DATA *pac_data, + krb5_context context, + krb5_keyblock *krbtgt_keyblock, + krb5_keyblock *service_keyblock, + DATA_BLOB *pac); #endif /* HAVE_KRB5 */ diff --git a/source4/auth/kerberos/kerberos_pac.c b/source4/auth/kerberos/kerberos_pac.c index f561bdfe76b..83679972d9e 100644 --- a/source4/auth/kerberos/kerberos_pac.c +++ b/source4/auth/kerberos/kerberos_pac.c @@ -78,7 +78,7 @@ static NTSTATUS check_pac_checksum(TALLOC_CTX *mem_ctx, } NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, - struct PAC_LOGON_INFO **logon_info_out, + struct PAC_DATA **pac_data_out, DATA_BLOB blob, struct smb_krb5_context *smb_krb5_context, krb5_keyblock *krbtgt_keyblock, @@ -90,46 +90,50 @@ static NTSTATUS check_pac_checksum(TALLOC_CTX *mem_ctx, struct PAC_SIGNATURE_DATA kdc_sig; struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL; struct PAC_LOGON_INFO *logon_info = NULL; - struct PAC_DATA pac_data; + struct PAC_DATA *pac_data; + DATA_BLOB modified_pac_blob = data_blob_talloc(mem_ctx, blob.data, blob.length); int i; - /* file_save("tmp_pac_data.dat",blob.data,blob.length); */ + pac_data = talloc(mem_ctx, struct PAC_DATA); + if (!pac_data) { + return NT_STATUS_NO_MEMORY; + } - status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + 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; } - if (pac_data.num_buffers < 3) { + 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) { + 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) { + if (!pac_data->buffers[i].info) { break; } - logon_info = pac_data.buffers[i].info->logon_info.info; + logon_info = pac_data->buffers[i].info->logon_info.info; break; case PAC_TYPE_SRV_CHECKSUM: - if (!pac_data.buffers[i].info) { + 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; + 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) { + 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; + kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum; + kdc_sig = pac_data->buffers[i].info->kdc_cksum; break; case PAC_TYPE_LOGON_NAME: break; @@ -181,16 +185,56 @@ static NTSTATUS check_pac_checksum(TALLOC_CTX *mem_ctx, } } +#if 0 + if (strcasecmp(logon_info->info3.base.account_name.string, + "Administrator")== 0) { + file_save("tmp_pac_data-admin.dat",blob.data,blob.length); + } +#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; + *pac_data_out = pac_data; return status; } + NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx, + struct PAC_LOGON_INFO **logon_info, + DATA_BLOB blob, + struct smb_krb5_context *smb_krb5_context, + krb5_keyblock *krbtgt_keyblock, + krb5_keyblock *service_keyblock) +{ + NTSTATUS nt_status; + struct PAC_DATA *pac_data; + int i; + + nt_status = kerberos_decode_pac(mem_ctx, &pac_data, + blob, + smb_krb5_context, + krbtgt_keyblock, + service_keyblock); + if (NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + *logon_info = NULL; + for (i=0; i < pac_data->num_buffers; i++) { + if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) { + continue; + } + *logon_info = pac_data->buffers[i].info->logon_info.info; + } + if (!*logon_info) { + return NT_STATUS_INVALID_PARAMETER; + } + return NT_STATUS_OK; +} + static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, - DATA_BLOB pac_data, + DATA_BLOB *pac_data, struct PAC_SIGNATURE_DATA *sig, krb5_context context, krb5_keyblock *keyblock) @@ -205,15 +249,16 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, 0, &crypto); if (ret) { - DEBUG(0,("krb5_crypto_init() failed\n")); + DEBUG(0,("krb5_crypto_init() failed: %s\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); return ret; } ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, - pac_data.data, - pac_data.length, + pac_data->data, + pac_data->length, &cksum); if (ret) { DEBUG(2, ("PAC Verification failed: %s\n", @@ -235,6 +280,107 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, } krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx, + struct PAC_DATA *pac_data, + krb5_context context, + krb5_keyblock *krbtgt_keyblock, + krb5_keyblock *service_keyblock, + DATA_BLOB *pac) +{ + NTSTATUS nt_status; + krb5_error_code ret; + DATA_BLOB zero_blob = data_blob(NULL, 0); + DATA_BLOB tmp_blob = data_blob(NULL, 0); + DATA_BLOB service_checksum_blob; + struct PAC_SIGNATURE_DATA *kdc_checksum = NULL; + struct PAC_SIGNATURE_DATA *srv_checksum = NULL; + int i; + + /* First, just get the keytypes filled in (and lengths right, eventually) */ + for (i=0; i < pac_data->num_buffers; i++) { + if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) { + continue; + } + kdc_checksum = &pac_data->buffers[i].info->kdc_cksum, + ret = make_pac_checksum(mem_ctx, &zero_blob, + kdc_checksum, + context, krbtgt_keyblock); + if (ret) { + DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); + talloc_free(pac_data); + return ret; + } + } + + for (i=0; i < pac_data->num_buffers; i++) { + if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) { + continue; + } + srv_checksum = &pac_data->buffers[i].info->srv_cksum; + ret = make_pac_checksum(mem_ctx, &zero_blob, + srv_checksum, + context, service_keyblock); + if (ret) { + DEBUG(2, ("making service PAC checksum failed: %s\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); + talloc_free(pac_data); + return ret; + } + } + + if (!kdc_checksum) { + DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!")); + return EINVAL; + } + if (!srv_checksum) { + DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!")); + return EINVAL; + } + + /* But wipe out the actual signatures */ + ZERO_STRUCT(kdc_checksum->signature); + ZERO_STRUCT(srv_checksum->signature); + + nt_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(nt_status)) { + DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status))); + talloc_free(pac_data); + return EINVAL; + } + + /* Then sign the result of the previous push, where the sig was zero'ed out */ + ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum, + context, service_keyblock); + + service_checksum_blob + = data_blob_const(srv_checksum->signature, sizeof(srv_checksum->signature)); + + /* Then sign Server checksum */ + ret = make_pac_checksum(mem_ctx, &service_checksum_blob, kdc_checksum, context, krbtgt_keyblock); + if (ret) { + DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); + talloc_free(pac_data); + return ret; + } + + /* And push it out again, this time to the world. This relies on determanistic pointer values */ + nt_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(nt_status)) { + DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status))); + talloc_free(pac_data); + return EINVAL; + } + + *pac = tmp_blob; + + return ret; +} + + + krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info *server_info, krb5_context context, krb5_keyblock *krbtgt_keyblock, @@ -242,9 +388,6 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, DATA_BLOB *pac) { NTSTATUS nt_status; - DATA_BLOB zero_blob = data_blob(NULL, 0); - DATA_BLOB tmp_blob = data_blob(NULL, 0); - DATA_BLOB service_checksum_blob; krb5_error_code ret; struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA); struct netr_SamInfo3 *sam3; @@ -254,9 +397,7 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, union PAC_INFO *u_LOGON_NAME; struct PAC_LOGON_NAME *LOGON_NAME; union PAC_INFO *u_KDC_CHECKSUM; - struct PAC_SIGNATURE_DATA *KDC_CHECKSUM; union PAC_INFO *u_SRV_CHECKSUM; - struct PAC_SIGNATURE_DATA *SRV_CHECKSUM; enum { PAC_BUF_LOGON_INFO = 0, @@ -308,7 +449,6 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, } pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM; pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM; - SRV_CHECKSUM = &u_SRV_CHECKSUM->srv_cksum; /* KDC_CHECKSUM */ u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO); @@ -318,7 +458,6 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, } pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM; pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM; - KDC_CHECKSUM = &u_KDC_CHECKSUM->kdc_cksum; /* now the real work begins... */ @@ -341,64 +480,12 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, LOGON_NAME->account_name = server_info->account_name; LOGON_NAME->logon_time = timeval_to_nttime(&tv); - - - /* First, just get the keytypes filled in (and lengths right, eventually) */ - ret = make_pac_checksum(mem_ctx, zero_blob, KDC_CHECKSUM, context, krbtgt_keyblock); - if (ret) { - DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); - talloc_free(pac_data); - return ret; - } - - ret = make_pac_checksum(mem_ctx, zero_blob, SRV_CHECKSUM, context, service_keyblock); - if (ret) { - DEBUG(2, ("making service PAC checksum failed: %s\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); - talloc_free(pac_data); - return ret; - } - - /* But wipe out the actual signatures */ - ZERO_STRUCT(KDC_CHECKSUM->signature); - ZERO_STRUCT(SRV_CHECKSUM->signature); - - nt_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(nt_status)) { - DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status))); - talloc_free(pac_data); - return EINVAL; - } - - /* Then sign the result of the previous push, where the sig was zero'ed out */ - ret = make_pac_checksum(mem_ctx, tmp_blob, SRV_CHECKSUM, - context, service_keyblock); - - service_checksum_blob - = data_blob_const(SRV_CHECKSUM->signature, sizeof(SRV_CHECKSUM->signature)); - - /* Then sign Server checksum */ - ret = make_pac_checksum(mem_ctx, service_checksum_blob, KDC_CHECKSUM, context, krbtgt_keyblock); - if (ret) { - DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); - talloc_free(pac_data); - return ret; - } - - /* And push it out again, this time to the world. This relies on determanistic pointer values */ - nt_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(nt_status)) { - DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status))); - talloc_free(pac_data); - return EINVAL; - } - - *pac = tmp_blob; - + ret = kerberos_encode_pac(mem_ctx, + pac_data, + context, + krbtgt_keyblock, + service_keyblock, + pac); talloc_free(pac_data); return ret; } diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index c986d60fdf3..44326cabef0 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -70,7 +70,7 @@ return EINVAL; } - ret = kerberos_encode_pac(mem_ctx, server_info, + ret = kerberos_create_pac(mem_ctx, server_info, context, krbtgt_keyblock, server_keyblock, diff --git a/source4/torture/auth/pac.c b/source4/torture/auth/pac.c index 59bf0089706..74a31af890a 100644 --- a/source4/torture/auth/pac.c +++ b/source4/torture/auth/pac.c @@ -28,14 +28,12 @@ #include "librpc/gen_ndr/ndr_krb5pac.h" #include "librpc/gen_ndr/ndr_samr.h" -#ifdef HAVE_KRB5 - static BOOL torture_pac_self_check(void) { NTSTATUS nt_status; TALLOC_CTX *mem_ctx = talloc_named(NULL, 0, "PAC self check"); DATA_BLOB tmp_blob; - struct PAC_LOGON_INFO *pac_info; + struct PAC_DATA *pac_data; /* Generate a nice, arbitary keyblock */ uint8_t server_bytes[16]; @@ -100,7 +98,7 @@ static BOOL torture_pac_self_check(void) } /* OK, go ahead and make a PAC */ - ret = kerberos_encode_pac(mem_ctx, server_info, + ret = kerberos_create_pac(mem_ctx, server_info, smb_krb5_context->krb5_context, &krbtgt_keyblock, &server_keyblock, @@ -122,7 +120,7 @@ static BOOL torture_pac_self_check(void) dump_data(10,tmp_blob.data,tmp_blob.length); /* Now check that we can read it back */ - nt_status = kerberos_decode_pac(mem_ctx, &pac_info, + nt_status = kerberos_decode_pac(mem_ctx, &pac_data, tmp_blob, smb_krb5_context, &krbtgt_keyblock, @@ -197,8 +195,7 @@ static BOOL torture_pac_saved_check(void) NTSTATUS nt_status; TALLOC_CTX *mem_ctx = talloc_named(NULL, 0, "PAC saved check"); DATA_BLOB tmp_blob, validate_blob; - struct PAC_LOGON_INFO *pac_info; - struct PAC_DATA pac_data; + struct PAC_DATA *pac_data; krb5_keyblock server_keyblock; krb5_keyblock krbtgt_keyblock; uint8_t server_bytes[16]; @@ -264,35 +261,37 @@ static BOOL torture_pac_saved_check(void) dump_data(10,tmp_blob.data,tmp_blob.length); /* Decode and verify the signaure on the PAC */ - nt_status = kerberos_decode_pac(mem_ctx, &pac_info, + nt_status = kerberos_decode_pac(mem_ctx, &pac_data, tmp_blob, smb_krb5_context, &krbtgt_keyblock, &server_keyblock); - krb5_free_keyblock_contents(smb_krb5_context->krb5_context, - &krbtgt_keyblock); - krb5_free_keyblock_contents(smb_krb5_context->krb5_context, - &server_keyblock); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("PAC decoding failed: %s\n", nt_errstr(nt_status))); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); talloc_free(mem_ctx); return False; } - nt_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(nt_status)) { - DEBUG(0,("can't parse the PAC\n")); - talloc_free(mem_ctx); - return False; - } + ret = kerberos_encode_pac(mem_ctx, + pac_data, + smb_krb5_context->krb5_context, + &krbtgt_keyblock, + &server_keyblock, + &validate_blob); - nt_status = ndr_push_struct_blob(&validate_blob, mem_ctx, &pac_data, - (ndr_push_flags_fn_t)ndr_push_PAC_DATA); - if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(0, ("PAC push failed: %s\n", nt_errstr(nt_status))); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &krbtgt_keyblock); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, + &server_keyblock); + + if (ret != 0) { + DEBUG(0, ("PAC push failed\n")); talloc_free(mem_ctx); return False; } @@ -328,14 +327,3 @@ BOOL torture_pac(void) ret &= torture_pac_saved_check(); return ret; } - -#else - -BOOL torture_pac(void) -{ - printf("Cannot do PAC test without Krb5\n"); - return False; -} - -#endif - -- 2.34.1