diff --git a/lib/krb5/context.c b/lib/krb5/context.c index 071b2a8c1..c4fe5a63c 100644 --- a/lib/krb5/context.c +++ b/lib/krb5/context.c @@ -240,6 +240,7 @@ init_context_from_config_file(krb5_context context) INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname"); INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac"); INIT_FLAG(context, flags, KRB5_CTX_F_ENFORCE_OK_AS_DELEGATE, FALSE, "enforce_ok_as_delegate"); + INIT_FLAG(context, flags, KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME, FALSE, "report_canonical_client_name"); if (context->default_cc_name) free(context->default_cc_name); diff --git a/lib/krb5/krb5.conf.5 b/lib/krb5/krb5.conf.5 index bb2e61d88..0a0fab071 100644 --- a/lib/krb5/krb5.conf.5 +++ b/lib/krb5/krb5.conf.5 @@ -518,6 +518,9 @@ If this flag is true, then all application protocol authentication requests will be flagged to indicate that the application supports channel bindings when operating over a secure channel. The default value is false. +.It Li report_canonical_client_name = Va boolean +If this flag is true, then the canonical client name from the PAC will +be used instead of the client name in the ticket. The default value is false. .El .It Li [domain_realm] This is a list of mappings from DNS domain to Kerberos realm. diff --git a/lib/krb5/krb5_locl.h b/lib/krb5/krb5_locl.h index b2a53a55f..eca434fd5 100644 --- a/lib/krb5/krb5_locl.h +++ b/lib/krb5/krb5_locl.h @@ -328,6 +328,7 @@ typedef struct krb5_context_data { #define KRB5_CTX_F_RD_REQ_IGNORE 16 #define KRB5_CTX_F_FCACHE_STRICT_CHECKING 32 #define KRB5_CTX_F_ENFORCE_OK_AS_DELEGATE 64 +#define KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME 128 struct send_to_kdc *send_to_kdc; #ifdef PKINIT hx509_context hx509ctx; diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index 90212173e..5cc5fe97c 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_canon_principal _krb5_kdc_pac_sign_ticket _krb5_kdc_pac_ticket_parse _kdc_tkt_insert_pac diff --git a/lib/krb5/pac.c b/lib/krb5/pac.c index 30dbd400e..130e9a098 100644 --- a/lib/krb5/pac.c +++ b/lib/krb5/pac.c @@ -53,8 +53,15 @@ struct krb5_pac_data { struct PAC_INFO_BUFFER *server_checksum; struct PAC_INFO_BUFFER *privsvr_checksum; struct PAC_INFO_BUFFER *logon_name; + struct PAC_INFO_BUFFER *upn_dns_info; struct PAC_INFO_BUFFER *ticket_checksum; krb5_data ticket_sign_data; + + /* parsed upn_dns_info, krb5_pac_verify only */ + krb5_principal upn_princ; + uint32_t upn_flags; + krb5_principal canon_princ; + krb5_data sid; }; #define PAC_ALIGNMENT 8 @@ -66,8 +73,13 @@ struct krb5_pac_data { #define PAC_PRIVSVR_CHECKSUM 7 #define PAC_LOGON_NAME 10 #define PAC_CONSTRAINED_DELEGATION 11 +#define PAC_UPN_DNS_INFO 12 #define PAC_TICKET_CHECKSUM 16 +/* Flag in PAC_UPN_DNS_INFO, _krb5_pac_get_upn_dns_info() */ +#define PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED 0x1 +#define PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID 0x2 + #define CHECK(r,f,l) \ do { \ if (((r) = f ) != 0) { \ @@ -242,6 +254,14 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, goto out; } p->logon_name = &p->pac->buffers[i]; + } else if (p->pac->buffers[i].type == PAC_UPN_DNS_INFO) { + if (p->upn_dns_info) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("PAC has multiple UPN DNS info buffers", "")); + goto out; + } + p->upn_dns_info = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) { if (p->ticket_checksum) { ret = EINVAL; @@ -445,6 +465,11 @@ krb5_pac_free(krb5_context context, krb5_pac pac) return; krb5_data_free(&pac->data); krb5_data_free(&pac->ticket_sign_data); + + krb5_free_principal(context, pac->upn_princ); + krb5_free_principal(context, pac->canon_princ); + krb5_data_free(&pac->sid); + free(pac->pac); free(pac); } @@ -604,6 +629,185 @@ create_checksum(krb5_context context, return 0; } +static krb5_error_code +parse_upn_dns_info(krb5_context context, + const struct PAC_INFO_BUFFER *upndnsinfo, + const krb5_data *data, + krb5_principal *upn_princ, + uint32_t *flags, + krb5_principal *sam_name_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 sid_length, sid_offset; + char *upn = NULL; + char *dns_domain_name = NULL; + char *sam_name = NULL; + + *upn_princ = NULL; + *flags = 0; + *sam_name_princ = NULL; + krb5_data_zero(sid); + + sp = krb5_storage_from_readonly_mem((const char *)data->data + upndnsinfo->offset_lo, + upndnsinfo->buffersize); + if (sp == NULL) + return krb5_enomem(context); + + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + + CHECK(ret, krb5_ret_uint16(sp, &upn_length), out); + CHECK(ret, krb5_ret_uint16(sp, &upn_offset), out); + CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_length), out); + CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_offset), out); + 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, &sid_length), out); + CHECK(ret, krb5_ret_uint16(sp, &sid_offset), out); + } + + CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, upn_offset, + upn_length, &upn), out); + 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) { + CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, sam_name_offset, + sam_name_length, &sam_name), out); + } + + if (upn_length) { + ret = krb5_parse_name_flags(context, + upn, + KRB5_PRINCIPAL_PARSE_ENTERPRISE | + KRB5_PRINCIPAL_PARSE_NO_DEF_REALM, + upn_princ); + if (ret) + goto out; + + ret = krb5_principal_set_realm(context, *upn_princ, dns_domain_name); + if (ret) + goto out; + } + + if (*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) { + ret = krb5_parse_name_flags(context, + sam_name, + KRB5_PRINCIPAL_PARSE_NO_REALM | + KRB5_PRINCIPAL_PARSE_NO_DEF_REALM, + sam_name_princ); + if (ret) + goto out; + + ret = krb5_principal_set_realm(context, *sam_name_princ, dns_domain_name); + if (ret) + goto out; + + CHECK(ret, _krb5_ret_data_at_offset(sp, sid_offset, sid_length, sid), out); + } + +out: + free(upn); + free(dns_domain_name); + free(sam_name); + + krb5_storage_free(sp); + return ret; +} + +#define UPN_DNS_INFO_EX_LENGTH 20 + +static krb5_error_code +build_upn_dns_info(krb5_context context, + krb5_const_principal upn_princ, + krb5_const_principal canon_princ, + krb5_data *upn_dns_info) +{ + krb5_error_code ret; + krb5_storage *sp = NULL; + char *upn_princ_name = NULL; + char *canon_princ_name = NULL; + uint32_t flags; + krb5_const_realm realm; + + sp = krb5_storage_emem(); + if (sp == NULL) { + ret = krb5_enomem(context); + goto out; + } + + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + + if (upn_princ) { + ret = krb5_unparse_name_flags(context, upn_princ, + KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &upn_princ_name); + if (ret) + goto out; + } + + ret = krb5_storage_truncate(sp, UPN_DNS_INFO_EX_LENGTH); + if (ret) + goto out; + + ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, upn_princ_name); + if (ret) + goto out; + + if (canon_princ) { + ret = krb5_unparse_name_flags(context, canon_princ, + KRB5_PRINCIPAL_UNPARSE_NO_REALM | + KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &canon_princ_name); + if (ret) + goto out; + } + + if (canon_princ) + realm = canon_princ->realm; + else if (upn_princ) + realm = upn_princ->realm; + else + realm = NULL; + + ret = _krb5_store_utf8_as_ucs2le_at_offset(sp, (off_t)-1, realm); + if (ret) + goto out; + + flags = 0; + if (upn_princ) + flags |= PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED; + if (canon_princ) + 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; + + ret = krb5_storage_to_data(sp, upn_dns_info); + if (ret) + goto out; + +out: + if (ret) + krb5_data_free(upn_dns_info); + + krb5_xfree(canon_princ_name); + krb5_xfree(upn_princ_name); + + return ret; +} /* * @@ -628,7 +832,7 @@ verify_logonname(krb5_context context, { krb5_error_code ret; uint32_t time1, time2; - krb5_storage *sp; + krb5_storage *sp = NULL; uint16_t len; char *s = NULL; char *principal_string = NULL; @@ -734,6 +938,7 @@ verify_logonname(krb5_context context, free(principal_string); return ret; out: + krb5_storage_free(sp); return ret; } @@ -949,6 +1154,20 @@ krb5_pac_verify(krb5_context context, } } + if (pac->upn_dns_info && + pac->upn_princ == NULL && pac->canon_princ == NULL && pac->sid.data == NULL) { + ret = parse_upn_dns_info(context, pac->upn_dns_info, &pac->data, + &pac->upn_princ, &pac->upn_flags, + &pac->canon_princ, &pac->sid); + if (ret) + return ret; + + if (pac->canon_princ && + !krb5_realm_compare(context, principal, pac->canon_princ)) { + return KRB5KRB_AP_ERR_MODIFIED; + } + } + return 0; } @@ -1012,10 +1231,12 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_pac_sign(krb5_context context, krb5_pac p, time_t authtime, - krb5_principal principal, + krb5_const_principal principal, const krb5_keyblock *server_key, const krb5_keyblock *priv_key, uint16_t rodc_id, + krb5_const_principal upn_princ, + krb5_const_principal canon_princ, krb5_data *data) { krb5_error_code ret; @@ -1027,9 +1248,11 @@ _krb5_pac_sign(krb5_context context, int num = 0; size_t i, sz; krb5_data logon, d; + krb5_data upn_dns_info; krb5_data_zero(&d); krb5_data_zero(&logon); + krb5_data_zero(&upn_dns_info); for (i = 0; i < p->pac->numbuffers; i++) { if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { @@ -1062,6 +1285,16 @@ _krb5_pac_sign(krb5_context context, N_("PAC has multiple logon names", "")); goto out; } + } else if (p->pac->buffers[i].type == PAC_UPN_DNS_INFO) { + if (p->upn_dns_info == NULL) { + p->upn_dns_info = &p->pac->buffers[i]; + } + if (p->upn_dns_info != &p->pac->buffers[i]) { + ret = KRB5KDC_ERR_BADOPTION; + krb5_set_error_message(context, ret, + N_("PAC has multiple UPN DNS info buffers", "")); + goto out; + } } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) { if (p->ticket_checksum == NULL) { p->ticket_checksum = &p->pac->buffers[i]; @@ -1081,6 +1314,8 @@ _krb5_pac_sign(krb5_context context, num++; if (p->privsvr_checksum == NULL) num++; + if ((upn_princ || canon_princ) && p->upn_dns_info == NULL) + num++; if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) num++; @@ -1110,9 +1345,14 @@ _krb5_pac_sign(krb5_context context, memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM; } + if ((upn_princ || canon_princ) && p->upn_dns_info == NULL) { + p->upn_dns_info = &p->pac->buffers[p->pac->numbuffers++]; + memset(p->upn_dns_info, 0, sizeof(*p->upn_dns_info)); + p->upn_dns_info->type = PAC_UPN_DNS_INFO; + } if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) { p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++]; - memset(p->ticket_checksum, 0, sizeof(*p->privsvr_checksum)); + memset(p->ticket_checksum, 0, sizeof(*p->ticket_checksum)); p->ticket_checksum->type = PAC_TICKET_CHECKSUM; } } @@ -1127,6 +1367,9 @@ _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); + /* Encode PAC */ if (ret == 0) { sp = krb5_storage_emem(); @@ -1190,6 +1433,13 @@ _krb5_pac_sign(krb5_context context, ret = KRB5KDC_ERR_BADOPTION; goto out; } + } else if ((upn_princ || canon_princ) && + 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 { len = p->pac->buffers[i].buffersize; ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; @@ -1279,6 +1529,7 @@ _krb5_pac_sign(krb5_context context, *data = d; krb5_data_free(&logon); + krb5_data_free(&upn_dns_info); krb5_storage_free(sp); krb5_storage_free(spdata); @@ -1286,6 +1537,7 @@ _krb5_pac_sign(krb5_context context, out: krb5_data_free(&d); krb5_data_free(&logon); + krb5_data_free(&upn_dns_info); if (sp) krb5_storage_free(sp); if (spdata) @@ -1347,6 +1599,20 @@ out: return ret; } + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_pac_get_canon_principal(krb5_context context, + krb5_pac pac, + krb5_principal *canon_princ) +{ + *canon_princ = NULL; + + if (pac->canon_princ == NULL) + return ENOENT; + + return krb5_copy_principal(context, pac->canon_princ, canon_princ); +} + static unsigned char single_zero = '\0'; static krb5_data single_zero_pac = { 1, &single_zero }; @@ -1470,12 +1736,14 @@ out: KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_kdc_pac_sign_ticket(krb5_context context, const krb5_pac pac, - krb5_principal client, + krb5_const_principal client, const krb5_keyblock *server_key, const krb5_keyblock *kdc_key, uint16_t rodc_id, krb5_boolean add_ticket_sig, - EncTicketPart *tkt) + EncTicketPart *tkt, + krb5_const_principal upn, + krb5_const_principal canon_name) { krb5_error_code ret; krb5_data tkt_data; @@ -1510,7 +1778,7 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, } ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key, - kdc_key, rodc_id, &rspac); + kdc_key, rodc_id, upn, canon_name, &rspac); if (ret == 0) ret = _kdc_tkt_insert_pac(context, tkt, &rspac); krb5_data_free(&rspac); diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c index 58ff04568..bd0b68b9c 100644 --- a/lib/krb5/rd_req.c +++ b/lib/krb5/rd_req.c @@ -1042,6 +1042,17 @@ krb5_rd_req_ctx(krb5_context context, o->ticket->client, o->keyblock, NULL); + if (ret == 0 && (context->flags & KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME)) { + krb5_error_code ret2; + krb5_principal canon_name; + + ret2 = _krb5_pac_get_canon_principal(context, pac, &canon_name); + if (ret2 == 0) { + krb5_free_principal(context, o->ticket->client); + o->ticket->client = canon_name; + } else if (ret2 != ENOENT) + ret = ret2; + } krb5_pac_free(context, pac); if (ret) goto out; diff --git a/lib/krb5/store.c b/lib/krb5/store.c index 2cb42c9a0..e3e4aa8b8 100644 --- a/lib/krb5/store.c +++ b/lib/krb5/store.c @@ -1823,3 +1823,99 @@ cleanup: } return ret; } + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_ret_data_at_offset(krb5_storage *sp, + size_t offset, + size_t length, + krb5_data *data) +{ + krb5_error_code ret; + off_t cur, size; + + krb5_data_zero(data); + + cur = sp->seek(sp, 0, SEEK_CUR); + if (cur < 0) + return HEIM_ERR_NOT_SEEKABLE; + + size = sp->seek(sp, 0, SEEK_END); + if (offset + length > size) { + ret = ERANGE; + goto cleanup; + } + + ret = krb5_data_alloc(data, length); + if (ret) + goto cleanup; + + if (length) { + sp->seek(sp, offset, SEEK_SET); + + size = sp->fetch(sp, data->data, length); + heim_assert(size == length, "incomplete buffer fetched"); + } + +cleanup: + sp->seek(sp, cur, SEEK_SET); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_ret_utf8_from_ucs2le_at_offset(krb5_storage *sp, + size_t offset, + size_t length, + char **utf8) +{ + krb5_error_code ret; + krb5_data data; + size_t ucs2len = length / 2; + uint16_t *ucs2 = NULL; + size_t u8len; + unsigned int flags = WIND_RW_LE; + + *utf8 = NULL; + + krb5_data_zero(&data); + + ret = _krb5_ret_data_at_offset(sp, offset, length, &data); + if (ret) + goto out; + + ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); + if (ucs2 == NULL) { + ret = ENOMEM; + goto out; + } + + ret = wind_ucs2read(data.data, data.length, &flags, ucs2, &ucs2len); + if (ret) + goto out; + + ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); + if (ret) + goto out; + + u8len += 1; /* Add space for NUL */ + + *utf8 = malloc(u8len); + if (*utf8 == NULL) { + ret = ENOMEM; + goto out; + } + + ret = wind_ucs2utf8(ucs2, ucs2len, *utf8, &u8len); + if (ret) + goto out; + +out: + if (ret && *utf8) { + free(*utf8); + *utf8 = NULL; + } + free(ucs2); + krb5_data_free(&data); + + return ret; +} diff --git a/lib/krb5/test_pac.c b/lib/krb5/test_pac.c index 133859407..bd1d51c8e 100644 --- a/lib/krb5/test_pac.c +++ b/lib/krb5/test_pac.c @@ -735,6 +735,64 @@ static const unsigned char s4u2proxy_rodc[] = "\x69\x9d\x4f\x44\xce\xc3\x6d\xae\x51\x20\x24\x61\xb6\x6f\xff\x27" "\xc4\x36\xb1"; +static const unsigned char extra_logon_info[] = + "\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xf0\x01\x00\x00" + "\x88\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x1e\x00\x00\x00" + "\x78\x02\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x98\x00\x00\x00" + "\x98\x02\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00" + "\x30\x03\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x08\x00\x00\x00" + "\x30\x03\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x1c\x00\x00\x00" + "\x38\x03\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x10\x00\x00\x00" + "\x58\x03\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00" + "\x68\x03\x00\x00\x00\x00\x00\x00\x01\x10\x08\x00\xcc\xcc\xcc\xcc" + "\xe0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x7f\xff\xff\xff\xff" + "\xff\xff\xff\x7f\x59\xa1\x0f\x59\x77\xf5\xd7\x01\x59\xa1\x0f\x59" + "\x77\xf5\xd7\x01\x59\x21\x69\x4e\x78\x16\xd8\x01\x14\x00\x14\x00" + "\x04\x00\x02\x00\x00\x00\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00" + "\x0c\x00\x02\x00\x00\x00\x00\x00\x10\x00\x02\x00\x00\x00\x00\x00" + "\x14\x00\x02\x00\x00\x00\x00\x00\x18\x00\x02\x00\x00\x00\x00\x00" + "\x4d\x04\x00\x00\x01\x02\x00\x00\x01\x00\x00\x00\x1c\x00\x02\x00" + "\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x0e\x00\x10\x00\x20\x00\x02\x00\x0e\x00\x10\x00" + "\x24\x00\x02\x00\x28\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x02\x00\x00\x00\x2c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00" + "\x63\x00\x39\x00\x64\x00\x38\x00\x30\x00\x31\x00\x61\x00\x38\x00" + "\x5f\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x01\x00\x00\x00\x01\x02\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00" + "\x00\x00\x00\x00\x07\x00\x00\x00\x54\x00\x45\x00\x53\x00\x54\x00" + "\x2d\x00\x44\x00\x43\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00" + "\x07\x00\x00\x00\x45\x00\x58\x00\x41\x00\x4d\x00\x50\x00\x4c\x00" + "\x45\x00\x00\x00\x04\x00\x00\x00\x01\x04\x00\x00\x00\x00\x00\x05" + "\x15\x00\x00\x00\xa1\xa5\x92\x7f\x29\x19\xc5\x3b\xbb\x56\xb0\x05" + "\x02\x00\x00\x00\x30\x00\x02\x00\x07\x00\x00\x00\x34\x00\x02\x00" + "\x07\x00\x00\x00\x05\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05" + "\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xf1\x01\x00\x00\x01\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x12" + "\x01\x00\x00\x00\x00\x00\x00\x00\x00\xb6\x8f\x5a\x77\xf5\xd7\x01" + "\x14\x00\x63\x00\x39\x00\x64\x00\x38\x00\x30\x00\x31\x00\x61\x00" + "\x38\x00\x5f\x00\x30\x00\x00\x00\x2c\x00\x18\x00\x16\x00\x48\x00" + "\x03\x00\x00\x00\x14\x00\x60\x00\x1c\x00\x78\x00\x00\x00\x00\x00" + "\x63\x00\x39\x00\x64\x00\x38\x00\x30\x00\x31\x00\x61\x00\x38\x00" + "\x5f\x00\x30\x00\x40\x00\x45\x00\x58\x00\x41\x00\x4d\x00\x50\x00" + "\x4c\x00\x45\x00\x2e\x00\x43\x00\x4f\x00\x4d\x00\x00\x00\x00\x00" + "\x45\x00\x58\x00\x41\x00\x4d\x00\x50\x00\x4c\x00\x45\x00\x2e\x00" + "\x43\x00\x4f\x00\x4d\x00\x00\x00\x63\x00\x39\x00\x64\x00\x38\x00" + "\x30\x00\x31\x00\x61\x00\x38\x00\x5f\x00\x30\x00\x00\x00\x00\x00" + "\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xa1\xa5\x92\x7f" + "\x29\x19\xc5\x3b\xbb\x56\xb0\x05\x4d\x04\x00\x00\x00\x00\x00\x00" + "\x02\x00\x00\x00\x01\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05" + "\x15\x00\x00\x00\xa1\xa5\x92\x7f\x29\x19\xc5\x3b\xbb\x56\xb0\x05" + "\x4d\x04\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\xfd\x39\xd8\x32" + "\x3b\x8c\xa5\xfe\x6b\x3d\x27\xff\x10\x00\x00\x00\x2f\x99\x7e\x57" + "\xca\xe4\xe1\xc7\x1b\xc0\xbd\xee"; + struct test_pac_ticket { const char *name; const unsigned char *ticket; @@ -901,7 +959,7 @@ main(int argc, char **argv) ret = krb5_init_context(&context); if (ret) - errx(1, "krb5_init_contex"); + errx(1, "krb5_init_context"); krb5_enctype_enable(context, ETYPE_DES_CBC_MD5); @@ -1018,6 +1076,34 @@ 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 */ diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index 2cc49fe0f..0245dffba 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_canon_principal; _krb5_kdc_pac_sign_ticket; _krb5_kdc_pac_ticket_parse; _kdc_tkt_insert_pac; diff --git a/tests/gss/check-context.in b/tests/gss/check-context.in index 699e373e6..42ea15eec 100644 --- a/tests/gss/check-context.in +++ b/tests/gss/check-context.in @@ -100,6 +100,7 @@ ${ktutil} -k ${keytab} rename --no-delete host/short@${R} host/long.test.h5l.se@ ${kadmin} add -p kaka --use-defaults digest/${R}@${R} || exit 1 ${kadmin} add -p u1 --use-defaults user1@${R} || exit 1 +${kadmin} mod --alias=user1.alias user1@${R} || exit 1 # Create a server principal with no AES ${kadmin} add -p p1 --use-defaults host/no-aes.test.h5l.se@${R} || exit 1 @@ -312,6 +313,22 @@ done rm ${keytabfile}.new +echo "====== test PAC-based name canonicalization" + +${kdestroy} +${kinit} --password-file=${objdir}/foopassword user1.alias@${R} || \ + { eval "$testfailed"; } + +for mech in krb5 spnego; do + KRB5_CONFIG="${objdir}/new_clients_k5.conf" ${context} -v \ + --mech-type=$mech host@lucid.test.h5l.se > name-canon.log || \ + { eval "$testfailed"; } + grep "client name:" name-canon.log | grep "user1.alias@TEST.H5L.SE" > /dev/null && \ + { echo "client name not canonicalized"; eval "$testfailed"; } + grep "client name:" name-canon.log | grep "user1@TEST.H5L.SE" > /dev/null || \ + { echo "wrong client name"; eval "$testfailed"; } +done + echo "====== test channel-bindings." for mech in krb5 spnego; do diff --git a/tests/gss/new_clients_k5.conf.in b/tests/gss/new_clients_k5.conf.in index 145f8dc46..41c9e21d5 100644 --- a/tests/gss/new_clients_k5.conf.in +++ b/tests/gss/new_clients_k5.conf.in @@ -2,3 +2,4 @@ include @objdirabs@/krb5.conf [libdefaults] client_aware_channel_bindings = true + report_canonical_client_name = true