From 8a0f6c9c7909162a9669197bc0238d0636de69da Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 12 Aug 2004 07:37:49 +0000 Subject: r1770: here's the krb5 server code, there're some cleanups needed and we need to verify the PAC correctly and create the auth_session_info correctly... metze (This used to be commit d8fe497097ee49611bb05c4a2fed36912d8e16b4) --- source4/libcli/auth/gensec_krb5.c | 264 +++++++++++++++++++++++++++++++++- source4/libcli/auth/kerberos.h | 7 +- source4/libcli/auth/kerberos_verify.c | 75 +++++++--- source4/librpc/idl/krb5pac.idl | 18 +-- source4/librpc/ndr/ndr_sec.c | 1 + 5 files changed, 328 insertions(+), 37 deletions(-) (limited to 'source4') diff --git a/source4/libcli/auth/gensec_krb5.c b/source4/libcli/auth/gensec_krb5.c index f5f02d1421..260b256934 100644 --- a/source4/libcli/auth/gensec_krb5.c +++ b/source4/libcli/auth/gensec_krb5.c @@ -38,14 +38,167 @@ enum GENSEC_KRB5_STATE { struct gensec_krb5_state { TALLOC_CTX *mem_ctx; DATA_BLOB session_key; - DATA_BLOB pac; + struct PAC_LOGON_INFO logon_info; enum GENSEC_KRB5_STATE state_position; krb5_context krb5_context; krb5_auth_context krb5_auth_context; krb5_ccache krb5_ccache; krb5_data ticket; + krb5_keyblock krb5_keyblock; }; +static NTSTATUS gensec_krb5_pac_checksum(DATA_BLOB pac_data, + struct PAC_SIGNATURE_DATA *sig, + struct gensec_krb5_state *gensec_krb5_state, + uint32 cksum_type) +{ + krb5_error_code ret; + krb5_crypto crypto; + Checksum cksum; + + cksum.cksumtype = (CKSUMTYPE)sig->type; + cksum.checksum.length = sizeof(sig->signature); + cksum.checksum.data = sig->signature; + + + ret = krb5_crypto_init(gensec_krb5_state->krb5_context, + &gensec_krb5_state->krb5_keyblock, + cksum_type, + &crypto); + if (ret) { + DEBUG(0,("krb5_crypto_init() failed\n")); + return NT_STATUS_FOOBAR; + } + + ret = krb5_verify_checksum(gensec_krb5_state->krb5_context, + crypto, + cksum_type, + pac_data.data, + pac_data.length, + &cksum); + + krb5_crypto_destroy(gensec_krb5_state->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; +} + +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; + uint8_t *srv_key = NULL; + struct PAC_SIGNATURE_DATA kdc_sig; + uint8_t *kdc_key = NULL; + struct PAC_LOGON_INFO *logon_info = NULL; + struct PAC_DATA pac_data; + DATA_BLOB tmp_blob; + 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; + } + + 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_key = (uint8_t *)&pac_data.buffers[i].info->srv_cksum.signature; + srv_sig = pac_data.buffers[i].info->srv_cksum; + break; + case PAC_TYPE_KDC_CHECKSUM: + if (!pac_data.buffers[i].info) { + break; + } + kdc_key = (uint8_t *)&pac_data.buffers[i].info->kdc_cksum.signature; + 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_key) { + DEBUG(0,("PAC no srv_key\n")); + return NT_STATUS_FOOBAR; + } + + if (!kdc_key) { + DEBUG(0,("PAC no kdc_key\n")); + return NT_STATUS_FOOBAR; + } + + /* clear the kdc_key */ + memset(kdc_key , '\0', 16); + + 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; + } + + /* 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(srv_key , '\0', 16); + + 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; + } + + /* 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; + } + + DEBUG(0,("account_name: %s [%s]\n",logon_info->account_name.string, logon_info->full_name.string)); + *logon_info_out = *logon_info; + + return status; +} + static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security) { struct gensec_krb5_state *gensec_krb5_state; @@ -334,7 +487,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL } return nt_status; } - + case GENSEC_KRB5_SERVER_START: { char *principal; @@ -348,19 +501,25 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL gensec_krb5_state->krb5_context, gensec_krb5_state->krb5_auth_context, lp_realm(), &in, - &principal, &pac, &unwrapped_out); + &principal, &pac, &unwrapped_out, + &gensec_krb5_state->krb5_keyblock); } else { /* TODO: check the tok_id */ nt_status = ads_verify_ticket(out_mem_ctx, gensec_krb5_state->krb5_context, gensec_krb5_state->krb5_auth_context, lp_realm(), &unwrapped_in, - &principal, &pac, &unwrapped_out); + &principal, &pac, &unwrapped_out, + &gensec_krb5_state->krb5_keyblock); } - gensec_krb5_state->pac = data_blob_talloc_steal(out_mem_ctx, gensec_krb5_state->mem_ctx, - &pac); - /* TODO: parse the pac */ + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + /* decode and verify the pac */ + nt_status = gensec_krb5_decode_pac(gensec_krb5_state->mem_ctx, &gensec_krb5_state->logon_info, pac, + gensec_krb5_state); if (NT_STATUS_IS_OK(nt_status)) { gensec_krb5_state->state_position = GENSEC_KRB5_DONE; @@ -413,6 +572,95 @@ static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, return NT_STATUS_NO_USER_SESSION_KEY; } } +/* +struct gensec_krb5_state { + TALLOC_CTX *mem_ctx; + DATA_BLOB session_key; + struct PAC_LOGON_INFO logon_info; + enum GENSEC_KRB5_STATE state_position; + krb5_context krb5_context; + krb5_auth_context krb5_auth_context; + krb5_ccache krb5_ccache; + krb5_data ticket; + krb5_keyblock krb5_keyblock; +}; +struct auth_session_info +{ + TALLOC_CTX *mem_ctx; + + int refcount; + + NT_USER_TOKEN *nt_user_token; + + struct auth_serversupplied_info *server_info; + + DATA_BLOB session_key; + + const char *workstation; +}; +*/ +static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info_out) +{ + struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; + TALLOC_CTX *mem_ctx; + struct auth_session_info *session_info = NULL; + struct PAC_LOGON_INFO *logon_info = &gensec_krb5_state->logon_info; + struct nt_user_token *ptoken; + struct dom_sid *sid; + + *session_info_out = NULL; + + mem_ctx = talloc_init("krb5: session_info"); + + session_info = talloc_p(mem_ctx, struct auth_session_info); + if (!session_info) { + return NT_STATUS_NO_MEMORY; + } + + session_info->mem_ctx = mem_ctx; + session_info->refcount = 1; + session_info->server_info = NULL; + + ptoken = talloc_p(session_info->mem_ctx, struct nt_user_token); + if (!ptoken) { + return NT_STATUS_NO_MEMORY; + } + + ptoken->num_sids = 0; + + ptoken->user_sids = talloc_array_p(mem_ctx, struct dom_sid*, logon_info->groups_count + 2); + if (!ptoken->user_sids) { + return NT_STATUS_NO_MEMORY; + } + + + sid = dom_sid_dup(session_info->mem_ctx, logon_info->dom_sid); + ptoken->user_sids[0] = dom_sid_add_rid(session_info->mem_ctx, sid, logon_info->user_rid); + ptoken->num_sids++; + sid = dom_sid_dup(session_info->mem_ctx, logon_info->dom_sid); + ptoken->user_sids[1] = dom_sid_add_rid(session_info->mem_ctx, sid, logon_info->group_rid); + ptoken->num_sids++; + + for (;ptoken->num_sids < logon_info->groups_count; ptoken->num_sids++) { + sid = dom_sid_dup(session_info->mem_ctx, logon_info->dom_sid); + ptoken->user_sids[ptoken->num_sids] = dom_sid_add_rid(session_info->mem_ctx, sid, logon_info->groups[ptoken->num_sids - 2].rid); + } + + debug_nt_user_token(DBGC_AUTH, 0, ptoken); + + session_info->nt_user_token = ptoken; + + session_info->session_key = data_blob_talloc(session_info->mem_ctx, + gensec_krb5_state->session_key.data, + gensec_krb5_state->session_key.length); + + session_info->workstation = NULL; + + *session_info_out = session_info; + + return NT_STATUS_OK; +} static const struct gensec_security_ops gensec_krb5_security_ops = { @@ -423,6 +671,7 @@ static const struct gensec_security_ops gensec_krb5_security_ops = { .server_start = gensec_krb5_server_start, .update = gensec_krb5_update, .session_key = gensec_krb5_session_key, + .session_info = gensec_krb5_session_info, .end = gensec_krb5_end }; @@ -434,6 +683,7 @@ static const struct gensec_security_ops gensec_ms_krb5_security_ops = { .server_start = gensec_krb5_server_start, .update = gensec_krb5_update, .session_key = gensec_krb5_session_key, + .session_info = gensec_krb5_session_info, .end = gensec_krb5_end }; diff --git a/source4/libcli/auth/kerberos.h b/source4/libcli/auth/kerberos.h index ca796d0c86..e35079a4ee 100644 --- a/source4/libcli/auth/kerberos.h +++ b/source4/libcli/auth/kerberos.h @@ -47,6 +47,10 @@ krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_con void krb5_free_unparsed_name(krb5_context ctx, char *val); #endif +#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT) +const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ); +#endif + /* Samba wrapper function for krb5 functionality. */ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr); int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); @@ -68,7 +72,8 @@ NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, krb5_auth_context auth_context, const char *realm, const DATA_BLOB *ticket, char **principal, DATA_BLOB *auth_data, - DATA_BLOB *ap_rep); + DATA_BLOB *ap_rep, + krb5_keyblock *keyblock); int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time); #endif /* HAVE_KRB5 */ diff --git a/source4/libcli/auth/kerberos_verify.c b/source4/libcli/auth/kerberos_verify.c index 5cd22b3749..d1f0433ccc 100644 --- a/source4/libcli/auth/kerberos_verify.c +++ b/source4/libcli/auth/kerberos_verify.c @@ -26,6 +26,33 @@ #ifdef HAVE_KRB5 +static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) +{ + DATA_BLOB out; + DATA_BLOB pac_contents = data_blob(NULL, 0); + ASN1_DATA data; + int data_type; + + asn1_load(&data, *auth_data); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_CONTEXT(0)); + asn1_read_Integer(&data, &data_type); + asn1_end_tag(&data); + asn1_start_tag(&data, ASN1_CONTEXT(1)); + asn1_read_OctetString(&data, &pac_contents); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_free(&data); + + out = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length); + + data_blob_free(&pac_contents); + + return out; +} + /********************************************************************************** Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so it's more like what microsoft does... see comment in utils/net_ads.c in the @@ -33,7 +60,8 @@ ***********************************************************************************/ static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context, - const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt) + const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt, + krb5_keyblock *keyblock) { krb5_error_code ret = 0; BOOL auth_ok = False; @@ -46,6 +74,8 @@ static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context aut ZERO_STRUCT(kt_entry); ZERO_STRUCT(cursor); + ZERO_STRUCTP(keyblock); + ret = krb5_kt_default(context, &keytab); if (ret) { DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", error_message(ret))); @@ -75,17 +105,22 @@ static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context aut p_packet->length = ticket->length; p_packet->data = (krb5_pointer)ticket->data; - if (!(ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt))) { + ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt); + if (!ret) { unsigned int keytype; krb5_free_unparsed_name(context, princ_name); princ_name = NULL; #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK keytype = (unsigned int) kt_entry.keyblock.keytype; + copy_EncryptionKey(&kt_entry.keyblock, keyblock); #else keytype = (unsigned int) kt_entry.key.enctype; + /* I'not sure if that works --metze*/ + copy_EncryptionKey(&kt_entry.key, keyblock); #endif DEBUG(10,("ads_keytab_verify_ticket: enc type [%u] decrypted message !\n", keytype)); + auth_ok = True; break; } @@ -124,7 +159,8 @@ static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context aut static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context, krb5_principal host_princ, - const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt) + const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt, + krb5_keyblock *keyblock) { krb5_error_code ret = 0; BOOL auth_ok = False; @@ -133,6 +169,8 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au krb5_enctype *enctypes = NULL; int i; + ZERO_STRUCTP(keyblock); + if (!secrets_init()) { DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n")); return False; @@ -160,30 +198,24 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au /* We need to setup a auth context with each possible encoding type in turn. */ for (i=0;enctypes[i];i++) { - krb5_keyblock *key = NULL; - - if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) { - goto out; - } - - if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) { - SAFE_FREE(key); + if (create_kerberos_key_from_string(context, host_princ, &password, keyblock, enctypes[i])) { continue; } - krb5_auth_con_setuseruserkey(context, auth_context, key); - - krb5_free_keyblock(context, key); + krb5_auth_con_setuseruserkey(context, auth_context, keyblock); - if (!(ret = krb5_rd_req(context, &auth_context, p_packet, + ret = krb5_rd_req(context, &auth_context, p_packet, NULL, - NULL, NULL, pp_tkt))) { + NULL, NULL, pp_tkt); + if (!ret) { DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n", (unsigned int)enctypes[i] )); auth_ok = True; break; } - + + free_EncryptionKey(keyblock); + DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10, ("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n", (unsigned int)enctypes[i], error_message(ret))); @@ -207,7 +239,8 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au krb5_auth_context auth_context, const char *realm, const DATA_BLOB *ticket, char **principal, DATA_BLOB *auth_data, - DATA_BLOB *ap_rep) + DATA_BLOB *ap_rep, + krb5_keyblock *keyblock) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; krb5_data packet; @@ -267,10 +300,10 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au goto out; } - auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt); + auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt, keyblock); if (!auth_ok) { auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ, - ticket, &packet, &tkt); + ticket, &packet, &tkt, keyblock); } release_server_mutex(); @@ -299,6 +332,8 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au get_auth_data_from_tkt(mem_ctx, auth_data, tkt); + *auth_data = unwrap_pac(mem_ctx, auth_data); + #if 0 if (tkt->enc_part2) { file_save("/tmp/authdata.dat", diff --git a/source4/librpc/idl/krb5pac.idl b/source4/librpc/idl/krb5pac.idl index ec1caf9171..6c2bad4590 100644 --- a/source4/librpc/idl/krb5pac.idl +++ b/source4/librpc/idl/krb5pac.idl @@ -14,7 +14,7 @@ interface krb5pac typedef struct { NTTIME logon_time; uint16 unknown; - nstring account_name; + astring account_name; } UNKNOWN_TYPE_10; typedef [flag(NDR_PAHEX)] struct { @@ -89,15 +89,15 @@ interface krb5pac } PAC_LOGON_INFO; const uint8 PAC_TYPE_LOGON_INFO = 1; - const uint8 PAC_TYPE_SERVER_CHECKSUM = 6; - const uint8 PAC_TYPE_PRIVSVR_CHECKSUM = 7; + const uint8 PAC_TYPE_SRV_CHECKSUM = 6; + const uint8 PAC_TYPE_KDC_CHECKSUM = 7; const uint8 PAC_TYPE_UNKNOWN_10 = 10; typedef [nodiscriminant] union { - [case(PAC_TYPE_LOGON_INFO)] PAC_LOGON_INFO logon_info; - [case(PAC_TYPE_SERVER_CHECKSUM)] PAC_SIGNATURE_DATA srv_cksum; - [case(PAC_TYPE_PRIVSVR_CHECKSUM)] PAC_SIGNATURE_DATA privsrv_cksum; - [case(PAC_TYPE_UNKNOWN_10)] UNKNOWN_TYPE_10 type_10; + [case(PAC_TYPE_LOGON_INFO)] PAC_LOGON_INFO logon_info; + [case(PAC_TYPE_SRV_CHECKSUM)] PAC_SIGNATURE_DATA srv_cksum; + [case(PAC_TYPE_KDC_CHECKSUM)] PAC_SIGNATURE_DATA kdc_cksum; + [case(PAC_TYPE_UNKNOWN_10)] UNKNOWN_TYPE_10 type_10; } PAC_INFO; typedef struct { @@ -105,12 +105,12 @@ interface krb5pac uint32 size; [relative,switch_is(type)] PAC_INFO *info; uint32 _pad; - } PAC_INFO_HDR; + } PAC_BUFFER; typedef [public] struct { uint32 num_buffers; uint32 version; - PAC_INFO_HDR pac_info_hdr_ptr[num_buffers]; + PAC_BUFFER buffers[num_buffers]; } PAC_DATA; void decode_pac( diff --git a/source4/librpc/ndr/ndr_sec.c b/source4/librpc/ndr/ndr_sec.c index 1fe8adbf4a..d1d84cd724 100644 --- a/source4/librpc/ndr/ndr_sec.c +++ b/source4/librpc/ndr/ndr_sec.c @@ -118,6 +118,7 @@ struct dom_sid *dom_sid_add_rid(TALLOC_CTX *mem_ctx, if (!sid) return NULL; *sid = *domain_sid; + /*TODO: use realloc! */ sid->sub_auths = talloc_array_p(mem_ctx, uint32_t, sid->num_auths+1); if (!sid->sub_auths) { return NULL; -- cgit