From f7964251ff6cfd2e77a65147f4fa827de45ccb6a Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Mon, 20 Dec 2021 15:31:33 +1100 Subject: [PATCH] kdc: support for PAC_ATTRIBUTES_INFO Add PAC_ATTRIBUTES_INFO to the PAC. This info buffer indicates whether the user explicitly requested a PAC be present or absent. Note: this changes the windc plugin ABI. --- kdc/fast.c | 2 +- kdc/kdc_locl.h | 2 + kdc/kerberos5.c | 28 ++-- kdc/krb5tgs.c | 19 ++- kdc/windc.c | 8 +- kdc/windc_plugin.h | 2 +- lib/krb5/krb5_locl.h | 4 + lib/krb5/libkrb5-exports.def.in | 2 +- lib/krb5/pac.c | 219 +++++++++++++++++++++++++++----- lib/krb5/test_pac.c | 70 +++++----- lib/krb5/version-script.map | 2 +- tests/plugin/windc.c | 5 +- 12 files changed, 268 insertions(+), 95 deletions(-) diff --git a/kdc/fast.c b/kdc/fast.c index 751b0376f..ac52f8c5b 100644 --- a/kdc/fast.c +++ b/kdc/fast.c @@ -848,7 +848,7 @@ _kdc_fast_check_armor_pac(astgs_request_t r) armor_client, r->armor_server, r->armor_server, r->armor_server, &r->armor_key->key, &r->armor_key->key, - &r->armor_ticket->ticket, &ad_kdc_issued, &mspac, NULL); + &r->armor_ticket->ticket, &ad_kdc_issued, &mspac, NULL, NULL); if (ret) { const char *msg = krb5_get_error_message(r->context, ret); diff --git a/kdc/kdc_locl.h b/kdc/kdc_locl.h index c1c58faba..646448531 100644 --- a/kdc/kdc_locl.h +++ b/kdc/kdc_locl.h @@ -109,6 +109,8 @@ struct astgs_request_desc { Key *armor_key; KDCFastState fast; + + uint64_t pac_attributes; }; typedef struct kx509_req_context_desc { diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c index fc324f11f..c19b5fb6c 100644 --- a/kdc/kerberos5.c +++ b/kdc/kerberos5.c @@ -1783,34 +1783,33 @@ _kdc_check_anon_policy(astgs_request_t r) } /* - * + * Determine whether the client requested a PAC be included + * or excluded explictly, or whether it doesn't care. */ -static krb5_error_code -check_pa_pac_request(krb5_context context, - KDC_REQ *req, - krb5_boolean *include_pac) +static uint64_t +get_pac_attributes(krb5_context context, KDC_REQ *req) { krb5_error_code ret; PA_PAC_REQUEST pacreq; const PA_DATA *pa; int i = 0; - - *include_pac = TRUE; + uint32_t pac_attributes; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST); if (pa == NULL) - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + return KRB5_PAC_WAS_GIVEN_IMPLICITLY; ret = decode_PA_PAC_REQUEST(pa->padata_value.data, pa->padata_value.length, &pacreq, NULL); if (ret) - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; - *include_pac = pacreq.include_pac; + return KRB5_PAC_WAS_GIVEN_IMPLICITLY; + + pac_attributes = pacreq.include_pac ? KRB5_PAC_WAS_REQUESTED : 0; free_PA_PAC_REQUEST(&pacreq); - return 0; + return pac_attributes; } /* @@ -1825,11 +1824,9 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey) krb5_data data; uint16_t rodc_id; krb5_principal client; - krb5_boolean client_sent_pac_req, pac_request; krb5_const_principal canon_princ = NULL; - client_sent_pac_req = - (check_pa_pac_request(r->context, &r->req, &pac_request) == 0); + r->pac_attributes = get_pac_attributes(r->context, &r->req); /* * When a PA mech replaces the reply key, the PAC may include the @@ -1841,7 +1838,7 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey) r->client, r->server, r->replaced_reply_key ? &r->reply_key : NULL, - client_sent_pac_req ? &pac_request : NULL, + r->pac_attributes, &p); if (ret) { _kdc_r_log(r, 4, "PAC generation failed for -- %s", @@ -1885,6 +1882,7 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey) rodc_id, NULL, /* UPN */ canon_princ, + &r->pac_attributes, &data); krb5_free_principal(r->context, client); krb5_pac_free(r->context, p); diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index 1a8644583..7775f727e 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -89,7 +89,8 @@ _kdc_check_pac(krb5_context context, EncTicketPart *tkt, krb5_boolean *kdc_issued, krb5_pac *ppac, - krb5_principal *pac_canon_name) + krb5_principal *pac_canon_name, + uint64_t *pac_attributes) { krb5_pac pac = NULL; krb5_error_code ret; @@ -129,6 +130,8 @@ _kdc_check_pac(krb5_context context, return ret; } } + if (pac_attributes) + _krb5_pac_get_attributes_info(context, pac, pac_attributes); } else if (ret == KRB5_PLUGIN_NO_HANDLE) { /* * We can't verify the KDC signatures if the ticket was issued by @@ -150,6 +153,8 @@ _kdc_check_pac(krb5_context context, krb5_pac_free(context, pac); return ret; } + if (pac_attributes) + _krb5_pac_get_attributes_info(context, pac, pac_attributes); } /* Discard the PAC if the plugin didn't handle it */ @@ -818,8 +823,8 @@ tgs_make_reply(astgs_request_t r, /* The PAC should be the last change to the ticket. */ ret = _krb5_kdc_pac_sign_ticket(r->context, mspac, tgt_name, serverkey, - krbtgtkey, rodc_id, add_ticket_sig, &et, - NULL, r->client_princ); + krbtgtkey, rodc_id, NULL, r->client_princ, + add_ticket_sig, &et, &r->pac_attributes); if (ret) goto out; } @@ -1842,7 +1847,7 @@ server_lookup: ret = _kdc_check_pac(context, config, user2user_princ, NULL, user2user_client, user2user_krbtgt, user2user_krbtgt, user2user_krbtgt, &uukey->key, &priv->ticket_key->key, &adtkt, - &user2user_kdc_issued, &user2user_pac, NULL); + &user2user_kdc_issued, &user2user_pac, NULL, NULL); _kdc_free_ent(context, user2user_client); if (ret) { const char *msg = krb5_get_error_message(context, ret); @@ -1970,7 +1975,7 @@ server_lookup: ret = _kdc_check_pac(context, config, cp, NULL, client, server, krbtgt, krbtgt, &priv->ticket_key->key, &priv->ticket_key->key, tgt, - &kdc_issued, &mspac, &priv->client_princ); + &kdc_issued, &mspac, &priv->client_princ, &priv->pac_attributes); if (ret) { const char *msg = krb5_get_error_message(context, ret); _kdc_audit_addreason((kdc_request_t)priv, "PAC check failed"); @@ -2131,7 +2136,7 @@ server_lookup: s4u2self_impersonated_client, server, NULL, - NULL, + KRB5_PAC_WAS_GIVEN_IMPLICITLY, &mspac); if (ret) { kdc_log(context, config, 4, "PAC generation failed for -- %s", tpn); @@ -2302,7 +2307,7 @@ server_lookup: */ ret = _kdc_check_pac(context, config, tp, dp, adclient, server, krbtgt, client, &clientkey->key, &priv->ticket_key->key, &adtkt, - &ad_kdc_issued, &mspac, &priv->client_princ); + &ad_kdc_issued, &mspac, &priv->client_princ, &priv->pac_attributes); if (adclient) _kdc_free_ent(context, adclient); if (ret) { diff --git a/kdc/windc.c b/kdc/windc.c index c472a56e9..c9dd699bd 100644 --- a/kdc/windc.c +++ b/kdc/windc.c @@ -73,8 +73,8 @@ struct generate_uc { hdb_entry_ex *client; hdb_entry_ex *server; const krb5_keyblock *reply_key; + uint64_t pac_attributes; krb5_pac *pac; - const krb5_boolean *pac_request; }; static krb5_error_code KRB5_LIB_CALL @@ -90,7 +90,7 @@ generate(krb5_context context, const void *plug, void *plugctx, void *userctx) uc->client, uc->server, uc->reply_key, - uc->pac_request, + uc->pac_attributes, uc->pac); } @@ -100,7 +100,7 @@ _kdc_pac_generate(krb5_context context, hdb_entry_ex *client, hdb_entry_ex *server, const krb5_keyblock *reply_key, - const krb5_boolean *pac_request, + uint64_t pac_attributes, krb5_pac *pac) { krb5_error_code ret = 0; @@ -118,7 +118,7 @@ _kdc_pac_generate(krb5_context context, uc.server = server; uc.reply_key = reply_key; uc.pac = pac; - uc.pac_request = pac_request; + uc.pac_attributes = pac_attributes; ret = _krb5_plugin_run_f(context, &windc_plugin_data, 0, &uc, generate); diff --git a/kdc/windc_plugin.h b/kdc/windc_plugin.h index 2dfa8ba4d..0c2448492 100644 --- a/kdc/windc_plugin.h +++ b/kdc/windc_plugin.h @@ -57,7 +57,7 @@ typedef krb5_error_code struct hdb_entry_ex *, /* client */ struct hdb_entry_ex *, /* server */ const krb5_keyblock *, /* pk_replykey */ - const krb5_boolean *, /* pac_request */ + uint64_t, /* pac_attributes */ krb5_pac *); typedef krb5_error_code diff --git a/lib/krb5/krb5_locl.h b/lib/krb5/krb5_locl.h index eca434fd5..6b74653f5 100644 --- a/lib/krb5/krb5_locl.h +++ b/lib/krb5/krb5_locl.h @@ -480,4 +480,8 @@ struct krb5_decrypt_tkt_with_subkey_state { /* Flag in KRB5_AUTHDATA_AP_OPTIONS */ #define KERB_AP_OPTIONS_CBT 0x00004000 +/* Flag in PAC_ATTRIBUTES_INFO */ +#define KRB5_PAC_WAS_REQUESTED 0x1 +#define KRB5_PAC_WAS_GIVEN_IMPLICITLY 0x2 + #endif /* __KRB5_LOCL_H__ */ diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index 7957a1191..245f4b1bf 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -822,6 +822,7 @@ EXPORTS _krb5_get_int _krb5_get_int64 _krb5_pac_sign + _krb5_pac_get_attributes_info _krb5_pac_get_canon_principal _krb5_kdc_pac_sign_ticket _krb5_kdc_pac_ticket_parse @@ -843,7 +844,6 @@ EXPORTS _krb5_expand_path_tokens ;! _krb5_make_pa_enc_challenge _krb5_validate_pa_enc_challenge - _krb5_store_utf8_as_ucs2le_at_offset ; kinit helper krb5_get_init_creds_opt_set_pkinit_user_certs diff --git a/lib/krb5/pac.c b/lib/krb5/pac.c index 2c230617b..c5e096629 100644 --- a/lib/krb5/pac.c +++ b/lib/krb5/pac.c @@ -55,13 +55,17 @@ struct krb5_pac_data { struct PAC_INFO_BUFFER *logon_name; struct PAC_INFO_BUFFER *upn_dns_info; struct PAC_INFO_BUFFER *ticket_checksum; + struct PAC_INFO_BUFFER *attributes_info; krb5_data ticket_sign_data; - /* parsed upn_dns_info, krb5_pac_verify only */ + /* PAC_UPN_DNS_INFO */ krb5_principal upn_princ; uint32_t upn_flags; krb5_principal canon_princ; krb5_data sid; + + /* PAC_ATTRIBUTES_INFO */ + uint64_t pac_attributes; }; #define PAC_ALIGNMENT 8 @@ -75,8 +79,10 @@ struct krb5_pac_data { #define PAC_CONSTRAINED_DELEGATION 11 #define PAC_UPN_DNS_INFO 12 #define PAC_TICKET_CHECKSUM 16 +#define PAC_ATTRIBUTES_INFO 17 +#define PAC_REQUESTOR_SID 18 -/* Flag in PAC_UPN_DNS_INFO, _krb5_pac_get_upn_dns_info() */ +/* Flag in PAC_UPN_DNS_INFO */ #define PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED 0x1 #define PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID 0x2 @@ -270,6 +276,14 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, goto out; } p->ticket_checksum = &p->pac->buffers[i]; + } else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) { + if (p->attributes_info) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("PAC has multiple attributes info buffers", "")); + goto out; + } + p->attributes_info = &p->pac->buffers[i]; } } @@ -635,14 +649,14 @@ parse_upn_dns_info(krb5_context context, const krb5_data *data, krb5_principal *upn_princ, uint32_t *flags, - krb5_principal *sam_name_princ, + krb5_principal *canon_princ, krb5_data *sid) { krb5_error_code ret; krb5_storage *sp = NULL; uint16_t upn_length, upn_offset; uint16_t dns_domain_name_length, dns_domain_name_offset; - uint16_t sam_name_length, sam_name_offset; + uint16_t canon_princ_length, canon_princ_offset; uint16_t sid_length, sid_offset; char *upn = NULL; char *dns_domain_name = NULL; @@ -650,7 +664,7 @@ parse_upn_dns_info(krb5_context context, *upn_princ = NULL; *flags = 0; - *sam_name_princ = NULL; + *canon_princ = NULL; krb5_data_zero(sid); sp = krb5_storage_from_readonly_mem((const char *)data->data + upndnsinfo->offset_lo, @@ -667,13 +681,13 @@ parse_upn_dns_info(krb5_context context, CHECK(ret, krb5_ret_uint32(sp, flags), out); if (*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) { - CHECK(ret, krb5_ret_uint16(sp, &sam_name_length), out); - CHECK(ret, krb5_ret_uint16(sp, &sam_name_offset), out); + CHECK(ret, krb5_ret_uint16(sp, &canon_princ_length), out); + CHECK(ret, krb5_ret_uint16(sp, &canon_princ_offset), out); CHECK(ret, krb5_ret_uint16(sp, &sid_length), out); CHECK(ret, krb5_ret_uint16(sp, &sid_offset), out); } else { - sam_name_offset = 0; - sid_offset = 0; + canon_princ_length = canon_princ_offset = 0; + sid_length = sid_offset = 0; } if (upn_offset) { @@ -682,9 +696,9 @@ parse_upn_dns_info(krb5_context context, } CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, dns_domain_name_offset, dns_domain_name_length, &dns_domain_name), out); - if ((*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) && sam_name_offset) { - CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, sam_name_offset, - sam_name_length, &sam_name), out); + if ((*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) && canon_princ_offset) { + CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, canon_princ_offset, + canon_princ_length, &sam_name), out); } if (upn_offset) { @@ -701,16 +715,16 @@ parse_upn_dns_info(krb5_context context, goto out; } - if (sam_name_offset) { + if (canon_princ_offset) { ret = krb5_parse_name_flags(context, sam_name, KRB5_PRINCIPAL_PARSE_NO_REALM | KRB5_PRINCIPAL_PARSE_NO_DEF_REALM, - sam_name_princ); + canon_princ); if (ret) goto out; - ret = krb5_principal_set_realm(context, *sam_name_princ, dns_domain_name); + ret = krb5_principal_set_realm(context, *canon_princ, dns_domain_name); if (ret) goto out; } @@ -734,6 +748,7 @@ static krb5_error_code build_upn_dns_info(krb5_context context, krb5_const_principal upn_princ, krb5_const_principal canon_princ, + const krb5_data *sid, krb5_data *upn_dns_info) { krb5_error_code ret; @@ -779,8 +794,10 @@ build_upn_dns_info(krb5_context context, realm = canon_princ->realm; else if (upn_princ) realm = upn_princ->realm; - else - realm = NULL; + else { + ret = EINVAL; + goto out; + } ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, realm); if (ret) @@ -789,17 +806,23 @@ build_upn_dns_info(krb5_context context, flags = 0; if (upn_princ) flags |= PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED; - if (canon_princ) + if (canon_princ || sid) flags |= PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID; ret = krb5_store_uint32(sp, flags); if (ret) goto out; - ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, - canon_princ_name); - if (ret) - goto out; + if (flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) { + ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, + canon_princ_name); + if (ret) + goto out; + + ret = _krb5_store_data_at_offset(sp, (off_t)-1, sid); + if (ret) + goto out; + } ret = krb5_storage_to_data(sp, upn_dns_info); if (ret) @@ -1054,6 +1077,80 @@ out: return ret; } +static krb5_error_code +parse_attributes_info(krb5_context context, + const struct PAC_INFO_BUFFER *attributes_info, + const krb5_data *data, + uint64_t *pac_attributes) +{ + krb5_error_code ret; + krb5_storage *sp = NULL; + uint32_t flags_length; + + *pac_attributes = 0; + + sp = krb5_storage_from_readonly_mem((const char *)data->data + attributes_info->offset_lo, + attributes_info->buffersize); + if (sp == NULL) + return krb5_enomem(context); + + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + + ret = krb5_ret_uint32(sp, &flags_length); + if (ret == 0) { + if (flags_length > 32) + ret = krb5_ret_uint64(sp, pac_attributes); + else { + uint32_t pac_attributes32 = 0; + ret = krb5_ret_uint32(sp, &pac_attributes32); + *pac_attributes = pac_attributes32; + } + } + + krb5_storage_free(sp); + + return ret; +} + +static krb5_error_code +build_attributes_info(krb5_context context, + uint64_t pac_attributes, + krb5_data *attributes_info) +{ + krb5_error_code ret; + krb5_storage *sp = NULL; + uint32_t flags_length; + + krb5_data_zero(attributes_info); + + sp = krb5_storage_emem(); + if (sp == NULL) + return krb5_enomem(context); + + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + + if (pac_attributes == 0) + flags_length = 0; + else + flags_length = 64 - rk_clzll(pac_attributes); + if (flags_length < KRB5_PAC_WAS_GIVEN_IMPLICITLY) + flags_length = KRB5_PAC_WAS_GIVEN_IMPLICITLY; + + ret = krb5_store_uint32(sp, flags_length); + if (ret == 0) { + if (flags_length > 32) + ret = krb5_store_uint64(sp, pac_attributes); + else + ret = krb5_store_uint32(sp, (uint32_t)pac_attributes); + } + if (ret == 0) + ret = krb5_storage_to_data(sp, attributes_info); + + krb5_storage_free(sp); + + return ret; +} + /** * Verify the PAC. * @@ -1169,12 +1266,19 @@ krb5_pac_verify(krb5_context context, if (ret) return ret; - if (pac->canon_princ && + if (principal && pac->canon_princ && !krb5_realm_compare(context, principal, pac->canon_princ)) { return KRB5KRB_AP_ERR_MODIFIED; } } + if (pac->attributes_info) { + ret = parse_attributes_info(context, pac->attributes_info, &pac->data, + &pac->pac_attributes); + if (ret) + return ret; + } + return 0; } @@ -1244,6 +1348,7 @@ _krb5_pac_sign(krb5_context context, uint16_t rodc_id, krb5_const_principal upn_princ, krb5_const_principal canon_princ, + uint64_t *pac_attributes, /* optional */ krb5_data *data) { krb5_error_code ret; @@ -1256,10 +1361,12 @@ _krb5_pac_sign(krb5_context context, size_t i, sz; krb5_data logon, d; krb5_data upn_dns_info; + krb5_data attributes_info; krb5_data_zero(&d); krb5_data_zero(&logon); krb5_data_zero(&upn_dns_info); + krb5_data_zero(&attributes_info); for (i = 0; i < p->pac->numbuffers; i++) { if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { @@ -1312,6 +1419,16 @@ _krb5_pac_sign(krb5_context context, N_("PAC has multiple ticket checksums", "")); goto out; } + } else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) { + if (p->attributes_info == NULL) { + p->attributes_info = &p->pac->buffers[i]; + } + if (p->attributes_info != &p->pac->buffers[i]) { + ret = KRB5KDC_ERR_BADOPTION; + krb5_set_error_message(context, ret, + N_("PAC has multiple attributes info buffers", "")); + goto out; + } } } @@ -1325,6 +1442,8 @@ _krb5_pac_sign(krb5_context context, num++; if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) num++; + if (pac_attributes && p->attributes_info == NULL) + num++; if (num) { void *ptr; @@ -1362,6 +1481,11 @@ _krb5_pac_sign(krb5_context context, memset(p->ticket_checksum, 0, sizeof(*p->ticket_checksum)); p->ticket_checksum->type = PAC_TICKET_CHECKSUM; } + if (pac_attributes && p->attributes_info == NULL) { + p->attributes_info = &p->pac->buffers[p->pac->numbuffers++]; + memset(p->attributes_info, 0, sizeof(*p->attributes_info)); + p->attributes_info->type = PAC_ATTRIBUTES_INFO; + } } /* Calculate LOGON NAME */ @@ -1374,8 +1498,11 @@ _krb5_pac_sign(krb5_context context, if (ret == 0) ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); - if (upn_princ || canon_princ) - ret = build_upn_dns_info(context, upn_princ, canon_princ, &upn_dns_info); + if (ret == 0 && (upn_princ || canon_princ)) + ret = build_upn_dns_info(context, upn_princ, canon_princ, NULL, &upn_dns_info); + + if (ret == 0 && pac_attributes) + ret = build_attributes_info(context, *pac_attributes, &attributes_info); /* Encode PAC */ if (ret == 0) { @@ -1440,13 +1567,20 @@ _krb5_pac_sign(krb5_context context, ret = KRB5KDC_ERR_BADOPTION; goto out; } - } else if ((upn_princ || canon_princ) && + } else if (upn_dns_info.length != 0 && p->pac->buffers[i].type == PAC_UPN_DNS_INFO) { len = krb5_storage_write(spdata, upn_dns_info.data, upn_dns_info.length); if (upn_dns_info.length != len) { ret = KRB5KDC_ERR_BADOPTION; goto out; } + } else if (attributes_info.length != 0 && + p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) { + len = krb5_storage_write(spdata, attributes_info.data, attributes_info.length); + if (attributes_info.length != len) { + ret = KRB5KDC_ERR_BADOPTION; + goto out; + } } else { len = p->pac->buffers[i].buffersize; ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; @@ -1537,6 +1671,7 @@ _krb5_pac_sign(krb5_context context, krb5_data_free(&logon); krb5_data_free(&upn_dns_info); + krb5_data_free(&attributes_info); krb5_storage_free(sp); krb5_storage_free(spdata); @@ -1545,6 +1680,7 @@ out: krb5_data_free(&d); krb5_data_free(&logon); krb5_data_free(&upn_dns_info); + krb5_data_free(&attributes_info); if (sp) krb5_storage_free(sp); if (spdata) @@ -1614,12 +1750,33 @@ _krb5_pac_get_canon_principal(krb5_context context, { *canon_princ = NULL; - if (pac->canon_princ == NULL) + if (pac->canon_princ == NULL) { + krb5_set_error_message(context, ENOENT, + "PAC missing UPN DNS info buffer"); return ENOENT; + } return krb5_copy_principal(context, pac->canon_princ, canon_princ); } +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_pac_get_attributes_info(krb5_context context, + krb5_pac pac, + uint64_t *pac_attributes) +{ + *pac_attributes = 0; + + if (pac->attributes_info == NULL) { + krb5_set_error_message(context, ENOENT, + "PAC missing attributes info buffer"); + return ENOENT; + } + + *pac_attributes = pac->pac_attributes; + + return 0; +} + static unsigned char single_zero = '\0'; static krb5_data single_zero_pac = { 1, &single_zero }; @@ -1747,10 +1904,11 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, const krb5_keyblock *server_key, const krb5_keyblock *kdc_key, uint16_t rodc_id, + krb5_const_principal upn, + krb5_const_principal canon_name, krb5_boolean add_ticket_sig, EncTicketPart *tkt, - krb5_const_principal upn, - krb5_const_principal canon_name) + uint64_t *pac_attributes) /* optional */ { krb5_error_code ret; krb5_data tkt_data; @@ -1785,7 +1943,8 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, } ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key, - kdc_key, rodc_id, upn, canon_name, &rspac); + kdc_key, rodc_id, upn, canon_name, + pac_attributes, &rspac); if (ret == 0) ret = _kdc_tkt_insert_pac(context, tkt, &rspac); krb5_data_free(&rspac); diff --git a/lib/krb5/test_pac.c b/lib/krb5/test_pac.c index bd1d51c8e..51b4fff16 100644 --- a/lib/krb5/test_pac.c +++ b/lib/krb5/test_pac.c @@ -897,7 +897,8 @@ check_ticket_signature(krb5_context context, t_err(context, tkt->name, "remove_AuthorizationData", ret); ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key, - tkt->kdc_key, tkt->rodc_id, signedticket, &et); + tkt->kdc_key, tkt->rodc_id, + NULL, NULL, signedticket, &et, NULL); if (ret) t_err(context, tkt->name, "_krb5_kdc_pac_sign_ticket", ret); @@ -915,7 +916,8 @@ check_ticket_signature(krb5_context context, t_err(context, tkt->name, "remove_AuthorizationData 2", ret); ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key, - tkt->kdc_key, tkt->rodc_id, signedticket, &et); + tkt->kdc_key, tkt->rodc_id, + NULL, NULL, signedticket, &et, NULL); if (ret) t_err(context, tkt->name, "_krb5_kdcsignedticketsign_ticket 2", ret); @@ -978,7 +980,8 @@ main(int argc, char **argv) krb5_err(context, 1, ret, "krb5_pac_verify"); ret = _krb5_pac_sign(context, pac, authtime, p, - &member_keyblock, &kdc_keyblock, 0, &data); + &member_keyblock, &kdc_keyblock, 0, NULL, NULL, + NULL, &data); if (ret) krb5_err(context, 1, ret, "_krb5_pac_sign"); @@ -1034,7 +1037,8 @@ main(int argc, char **argv) free(list); ret = _krb5_pac_sign(context, pac2, authtime, p, - &member_keyblock, &kdc_keyblock, 0, &data); + &member_keyblock, &kdc_keyblock, 0, + NULL, NULL, NULL, &data); if (ret) krb5_err(context, 1, ret, "_krb5_pac_sign 4"); @@ -1076,34 +1080,6 @@ main(int argc, char **argv) krb5_pac_free(context, pac); krb5_free_principal(context, p2); - /* - * check extra logon info PAC - */ - ret = krb5_pac_parse(context, extra_logon_info, - sizeof(extra_logon_info) - 1, &pac); - if (ret) - krb5_err(context, 1, ret, "krb5_pac_parse"); - - { - krb5_principal upn = NULL; - uint32_t flags = 0; - krb5_principal sam_name = NULL; - krb5_data sid; - - krb5_data_zero(&sid); - - ret = _krb5_pac_get_upn_dns_info(context, pac, &upn, - &flags, &sam_name, &sid); - if (ret) - krb5_err(context, 1, ret, "_krb5_pac_get_upn_dns_info"); - - krb5_free_principal(context, upn); - krb5_free_principal(context, sam_name); - krb5_data_free(&sid); - } - - krb5_pac_free(context, pac); - /* * Test empty free */ @@ -1161,7 +1137,8 @@ main(int argc, char **argv) } ret = _krb5_pac_sign(context, pac, authtime, p, - &member_keyblock, &kdc_keyblock, 0, &data); + &member_keyblock, &kdc_keyblock, 0, + NULL, NULL, NULL, &data); if (ret) krb5_err(context, 1, ret, "_krb5_pac_sign"); @@ -1193,6 +1170,33 @@ main(int argc, char **argv) krb5_pac_free(context, pac); krb5_free_principal(context, p); + /* + * check extra logon info PAC + */ + ret = krb5_pac_parse(context, extra_logon_info, + sizeof(extra_logon_info) - 1, &pac); + if (ret) + krb5_err(context, 1, ret, "krb5_pac_parse"); + + ret = krb5_pac_verify(context, pac, 0, NULL, NULL, NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_pac_verify"); + + ret = krb5_parse_name(context, "c9d801a8_0@EXAMPLE.COM", &p); + if (ret) + krb5_err(context, 1, ret, "_krb5_pac_get_canon_principal"); + + ret = _krb5_pac_get_canon_principal(context, pac, &p2); + if (ret) + krb5_err(context, 1, ret, "_krb5_pac_get_canon_principal"); + + if (!krb5_principal_compare(context, p, p2)) + krb5_errx(context, 1, "canon principal doesn't match"); + + krb5_pac_free(context, pac); + krb5_free_principal(context, p); + krb5_free_principal(context, p2); + /* Test PAC ticket-signature */ { const struct test_pac_ticket *tkt; diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index bc97098d0..ce2783c35 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -814,6 +814,7 @@ HEIMDAL_KRB5_2.0 { _krb5_get_int; _krb5_get_int64; _krb5_pac_sign; + _krb5_pac_get_attributes_info; _krb5_pac_get_canon_principal; _krb5_kdc_pac_sign_ticket; _krb5_kdc_pac_ticket_parse; @@ -833,7 +834,6 @@ HEIMDAL_KRB5_2.0 { _krb5_crypto_set_flags; _krb5_make_pa_enc_challenge; _krb5_validate_pa_enc_challenge; - _krb5_store_utf8_as_ucs2le_at_offset; # kinit helper krb5_get_init_creds_opt_set_pkinit_user_certs; diff --git a/tests/plugin/windc.c b/tests/plugin/windc.c index 2664f658e..8acbf2908 100644 --- a/tests/plugin/windc.c +++ b/tests/plugin/windc.c @@ -23,13 +23,14 @@ pac_generate(void *ctx, krb5_context context, struct hdb_entry_ex *client, struct hdb_entry_ex *server, const krb5_keyblock *pk_replykey, - const krb5_boolean *pac_request, + uint64_t pac_attributes, krb5_pac *pac) { krb5_error_code ret; krb5_data data; - if (pac_request != NULL && *pac_request == FALSE) { + if ((pac_attributes & (KRB5_PAC_WAS_REQUESTED | + KRB5_PAC_WAS_GIVEN_IMPLICITLY)) == 0) { *pac = NULL; return 0; }