From 14a3abd5591a7c310bdd2638e5c06833dc2c8f92 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 26 Oct 2005 23:41:01 +0000 Subject: r11314: Use a patch from lha to have the kerberos libs extract the PAC, rather than doing ASN.1 parsing in Samba. Also use the API function for getting a client from a ticket, rather than just digging in the structure. Andrew Bartlett (This used to be commit 25d5ea6d724bd2b64a6086ae6e2e1c5148b8ca4a) --- source4/auth/gensec/gensec_gssapi.c | 9 +- source4/auth/gensec/gensec_krb5.c | 54 ++++++++--- source4/auth/kerberos/clikrb5.c | 112 ---------------------- source4/auth/kerberos/kerberos-notes.txt | 14 ++- source4/heimdal/lib/krb5/ticket.c | 158 +++++++++++++++++++++++++++++-- source4/heimdal_build/config.mk | 1 + 6 files changed, 199 insertions(+), 149 deletions(-) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 86ecb604ae..37c8333da3 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -873,18 +873,13 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi if (maj_stat == 0) { maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, gensec_gssapi_state->gssapi_context, - KRB5_AUTHDATA_IF_RELEVANT, + KRB5_AUTHDATA_WIN2K_PAC, &pac); } if (maj_stat == 0) { pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length); gss_release_buffer(&min_stat, &pac); - - if (!unwrap_pac(mem_ctx, &pac_blob, &unwrapped_pac)) { - /* No pac actually present */ - maj_stat = 1; - } } /* IF we have the PAC - otherwise we need to get this @@ -902,7 +897,7 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi } /* decode and verify the pac */ - nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, unwrapped_pac, + nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, pac_blob, gensec_gssapi_state->smb_krb5_context->krb5_context, NULL, keyblock, principal, authtime); krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal); diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index 3ed38a435c..36cfc49196 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -473,30 +473,44 @@ static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security { NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; + krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; struct auth_serversupplied_info *server_info = NULL; struct auth_session_info *session_info = NULL; struct PAC_LOGON_INFO *logon_info; - krb5_const_principal client_principal; + krb5_principal client_principal; DATA_BLOB pac; + krb5_data pac_data; - BOOL got_auth_data; + krb5_error_code ret; TALLOC_CTX *mem_ctx = talloc_new(gensec_security); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } - got_auth_data = get_auth_data_from_tkt(mem_ctx, &pac, gensec_krb5_state->ticket); - - /* IF we have the PAC - otherwise we need to get this - * data from elsewere - local ldb, or (TODO) lookup of some - * kind... - */ - if (got_auth_data) { + ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, + KRB5_AUTHDATA_WIN2K_PAC, + &pac_data); + + if (ret) { + DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", + smb_get_krb5_error_message(context, + ret, mem_ctx))); + } else { + pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length); + if (!pac.data) { + return NT_STATUS_NO_MEMORY; + } - client_principal = get_principal_from_tkt(gensec_krb5_state->ticket); + ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal); + if (ret) { + DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", + smb_get_krb5_error_message(context, + ret, mem_ctx))); + return NT_STATUS_NO_MEMORY; + } /* decode and verify the pac */ nt_status = kerberos_pac_logon_info(gensec_krb5_state, &logon_info, pac, @@ -504,6 +518,8 @@ static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security NULL, gensec_krb5_state->keyblock, client_principal, gensec_krb5_state->ticket->ticket.authtime); + krb5_free_principal(context, client_principal); + if (NT_STATUS_IS_OK(nt_status)) { union netr_Validation validation; validation.sam3 = &logon_info->info3; @@ -515,12 +531,26 @@ static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security talloc_free(mem_ctx); } + + + /* IF we have the PAC - otherwise we need to get this + * data from elsewere - local ldb, or (TODO) lookup of some + * kind... + */ if (!NT_STATUS_IS_OK(nt_status)) { /* NO pac, or can't parse or verify it */ - krb5_error_code ret; char *principal_string; + ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal); + if (ret) { + DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", + smb_get_krb5_error_message(context, + ret, mem_ctx))); + return NT_STATUS_NO_MEMORY; + } + ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, - get_principal_from_tkt(gensec_krb5_state->ticket), &principal_string); + client_principal, &principal_string); + krb5_free_principal(context, client_principal); if (ret) { return NT_STATUS_NO_MEMORY; } diff --git a/source4/auth/kerberos/clikrb5.c b/source4/auth/kerberos/clikrb5.c index 17a1e5f3d4..3cac97cdc6 100644 --- a/source4/auth/kerberos/clikrb5.c +++ b/source4/auth/kerberos/clikrb5.c @@ -159,118 +159,6 @@ } #endif -BOOL unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data) -{ - DATA_BLOB pac_contents; - struct asn1_data data; - int data_type; - - if (!auth_data->length) { - return False; - } - - 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); - - if (data_type != KRB5_AUTHDATA_WIN2K_PAC ) { - DEBUG(10,("authorization data is not a Windows PAC (type: %d)\n", data_type)); - asn1_free(&data); - return False; - } - - 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); - - *unwrapped_pac_data = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length); - - data_blob_free(&pac_contents); - - return True; -} - - BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt) -{ - DATA_BLOB auth_data_wrapped; - BOOL got_auth_data_pac = False; - int i; - -#if defined(HAVE_KRB5_TKT_ENC_PART2) - if (tkt->enc_part2 && tkt->enc_part2->authorization_data && - tkt->enc_part2->authorization_data[0] && - tkt->enc_part2->authorization_data[0]->length) - { - for (i = 0; tkt->enc_part2->authorization_data[i] != NULL; i++) { - - if (tkt->enc_part2->authorization_data[i]->ad_type != - KRB5_AUTHDATA_IF_RELEVANT) { - DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", - tkt->enc_part2->authorization_data[i]->ad_type)); - continue; - } - - auth_data_wrapped = data_blob(tkt->enc_part2->authorization_data[i]->contents, - tkt->enc_part2->authorization_data[i]->length); - - /* check if it is a PAC */ - got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data); - data_blob_free(&auth_data_wrapped); - - if (!got_auth_data_pac) { - continue; - } - } - - return got_auth_data_pac; - } - -#else - if (tkt->ticket.authorization_data && - tkt->ticket.authorization_data->len) - { - for (i = 0; i < tkt->ticket.authorization_data->len; i++) { - - if (tkt->ticket.authorization_data->val[i].ad_type != - KRB5_AUTHDATA_IF_RELEVANT) { - DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", - tkt->ticket.authorization_data->val[i].ad_type)); - continue; - } - - auth_data_wrapped = data_blob(tkt->ticket.authorization_data->val[i].ad_data.data, - tkt->ticket.authorization_data->val[i].ad_data.length); - - /* check if it is a PAC */ - got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data); - data_blob_free(&auth_data_wrapped); - - if (!got_auth_data_pac) { - continue; - } - } - - return got_auth_data_pac; - } -#endif - return False; -} - - krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt) -{ -#if defined(HAVE_KRB5_TKT_ENC_PART2) - return tkt->enc_part2->client; -#else - return tkt->client; -#endif -} - #if !defined(HAVE_KRB5_FREE_UNPARSED_NAME) void krb5_free_unparsed_name(krb5_context context, char *val) { diff --git a/source4/auth/kerberos/kerberos-notes.txt b/source4/auth/kerberos/kerberos-notes.txt index a36bf556aa..83fb886c45 100644 --- a/source4/auth/kerberos/kerberos-notes.txt +++ b/source4/auth/kerberos/kerberos-notes.txt @@ -309,6 +309,12 @@ Samba makes extensive use of the principal manipulation functions in Heimdal, including the known structure behind krb_principal and krb5_realm (a char *). +Authz data extraction +--------------------- + +We use krb5_ticket_get_authorization_data_type(), and expect it to +return the correct authz data, even if wrapped in an AD-IFRELEVENT container. + KDC Extensions -------------- @@ -392,14 +398,6 @@ PAC Correctness We need to put the PAC into the TGT, not just the service ticket. -Authz data extraction ---------------------- - -We need to parse the authz data field correctly, and have a generic -rouitine to get at particular types of data, no matter their inclusion -in 'if relevent' or other stuctures. This should be a utlity function -we can use in both the client libs and KDC. - Forwarded tickets ----------------- diff --git a/source4/heimdal/lib/krb5/ticket.c b/source4/heimdal/lib/krb5/ticket.c index 734cd4d4ca..8f4f8fb152 100644 --- a/source4/heimdal/lib/krb5/ticket.c +++ b/source4/heimdal/lib/krb5/ticket.c @@ -97,6 +97,141 @@ krb5_ticket_get_server(krb5_context context, return krb5_copy_principal(context, ticket->server, server); } +static int +find_type_in_ad(krb5_context context, + int type, + krb5_data *data, + int *found, + int failp, + krb5_keyblock *sessionkey, + const AuthorizationData *ad, + int level) +{ + krb5_error_code ret = ENOENT; + int i; + + if (level > 9) { + krb5_set_error_string(context, "Authorization data nested deeper " + "then %d levels, stop searching", level); + ret = ENOENT; /* XXX */ + goto out; + } + + /* + * Only copy out the element the first time we get to it, we need + * to run over the whole authorization data fields to check if + * there are any container clases we need to care about. + */ + for (i = 0; i < ad->len; i++) { + if (!*found && ad->val[i].ad_type == type) { + ret = copy_octet_string(&ad->val[i].ad_data, data); + if (ret) { + krb5_set_error_string(context, "malloc - out of memory"); + goto out; + } + *found = 1; + continue; + } + switch (ad->val[i].ad_type) { + case KRB5_AUTHDATA_IF_RELEVANT: { + AuthorizationData child; + ret = decode_AuthorizationData(ad->val[i].ad_data.data, + ad->val[i].ad_data.length, + &child, + NULL); + if (ret) { + krb5_set_error_string(context, "Failed to decode " + "IF_RELEVANT with %d", ret); + goto out; + } + ret = find_type_in_ad(context, type, data, found, 0, sessionkey, + &child, level + 1); + free_AuthorizationData(&child); + if (ret) + goto out; + break; + } + case KRB5_AUTHDATA_KDC_ISSUED: { + AD_KDCIssued child; + + ret = decode_AD_KDCIssued(ad->val[i].ad_data.data, + ad->val[i].ad_data.length, + &child, + NULL); + if (ret) { + krb5_set_error_string(context, "Failed to decode " + "AD_KDCIssued with %d", ret); + goto out; + } + if (failp) { + krb5_boolean valid; + krb5_data buf; + size_t len; + + ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length, + &child.elements, &len, ret); + if (ret) { + free_AD_KDCIssued(&child); + krb5_clear_error_string(context); + goto out; + } + if(buf.length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf, + &child.ad_checksum, &valid); + krb5_data_free(&buf); + if (ret) { + free_AD_KDCIssued(&child); + goto out; + } + if (!valid) { + krb5_clear_error_string(context); + ret = ENOENT; + free_AD_KDCIssued(&child); + goto out; + } + } + ret = find_type_in_ad(context, type, data, found, failp, sessionkey, + &child.elements, level + 1); + free_AD_KDCIssued(&child); + if (ret) + goto out; + break; + } + case KRB5_AUTHDATA_AND_OR: + if (!failp) + break; + krb5_set_error_string(context, "Authorization data contains " + "AND-OR element that is unknown to the " + "application"); + ret = ENOENT; /* XXX */ + goto out; + default: + if (!failp) + break; + krb5_set_error_string(context, "Authorization data contains " + "unknown type (%d) ", ad->val[i].ad_type); + ret = ENOENT; /* XXX */ + goto out; + } + } +out: + if (ret) { + if (*found) { + krb5_data_free(data); + *found = 0; + } + } + return ret; +} + +/* + * Extract the authorization data type of `type' from the + * 'ticket'. Store the field in `data'. This function is to use for + * kerberos applications + */ + krb5_error_code KRB5_LIB_FUNCTION krb5_ticket_get_authorization_data_type(krb5_context context, krb5_ticket *ticket, @@ -104,22 +239,25 @@ krb5_ticket_get_authorization_data_type(krb5_context context, krb5_data *data) { AuthorizationData *ad; - int i; + krb5_error_code ret; + int found = 0; - data->length = 0; - data->data = NULL; + krb5_data_zero(data); ad = ticket->ticket.authorization_data; - if (ad == NULL) { + if (ticket->ticket.authorization_data == NULL) { krb5_set_error_string(context, "Ticket have not authorization data"); return ENOENT; /* XXX */ } - for (i = 0; i < ad->len; i++) { - if (ad->val[i].ad_type == type) - return copy_octet_string(&ad->val[i].ad_data, data); - } - krb5_set_error_string(context, "Ticket have not authorization " + ret = find_type_in_ad(context, type, data, &found, 1, &ticket->ticket.key, + ticket->ticket.authorization_data, 0); + if (ret) + return ret; + if (!found) { + krb5_set_error_string(context, "Ticket have not authorization " "data of type %d", type); - return ENOENT; /* XXX */ + return ENOENT; /* XXX */ + } + return 0; } diff --git a/source4/heimdal_build/config.mk b/source4/heimdal_build/config.mk index f90cb4ce52..2786a010bb 100644 --- a/source4/heimdal_build/config.mk +++ b/source4/heimdal_build/config.mk @@ -191,6 +191,7 @@ ADD_OBJ_FILES = \ ../heimdal/lib/asn1/der_copy.o \ ../heimdal/lib/asn1/der_cmp.o \ ../heimdal/lib/asn1/asn1_AD_IF_RELEVANT.o \ + ../heimdal/lib/asn1/asn1_AD_KDCIssued.o \ ../heimdal/lib/asn1/asn1_APOptions.o \ ../heimdal/lib/asn1/asn1_AP_REP.o \ ../heimdal/lib/asn1/asn1_AP_REQ.o \ -- cgit