krb5: support for canonical name in PAC

If the UPN_DNS_INFO buffer in the Windows PAC contains a canonical principal
name, use it in lieu of the ticket client name to determine the GSS-API
initiator name.
This commit is contained in:
Luke Howard
2021-09-23 13:39:36 +10:00
parent b3bb3ac49d
commit 0ab3b7b2dd
11 changed files with 493 additions and 7 deletions

View File

@@ -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_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_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_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) if (context->default_cc_name)
free(context->default_cc_name); free(context->default_cc_name);

View File

@@ -518,6 +518,9 @@ If this flag is true, then all application protocol authentication
requests will be flagged to indicate that the application supports requests will be flagged to indicate that the application supports
channel bindings when operating over a secure channel. channel bindings when operating over a secure channel.
The default value is false. 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 .El
.It Li [domain_realm] .It Li [domain_realm]
This is a list of mappings from DNS domain to Kerberos realm. This is a list of mappings from DNS domain to Kerberos realm.

View File

@@ -328,6 +328,7 @@ typedef struct krb5_context_data {
#define KRB5_CTX_F_RD_REQ_IGNORE 16 #define KRB5_CTX_F_RD_REQ_IGNORE 16
#define KRB5_CTX_F_FCACHE_STRICT_CHECKING 32 #define KRB5_CTX_F_FCACHE_STRICT_CHECKING 32
#define KRB5_CTX_F_ENFORCE_OK_AS_DELEGATE 64 #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; struct send_to_kdc *send_to_kdc;
#ifdef PKINIT #ifdef PKINIT
hx509_context hx509ctx; hx509_context hx509ctx;

View File

@@ -822,6 +822,7 @@ EXPORTS
_krb5_get_int _krb5_get_int
_krb5_get_int64 _krb5_get_int64
_krb5_pac_sign _krb5_pac_sign
_krb5_pac_get_canon_principal
_krb5_kdc_pac_sign_ticket _krb5_kdc_pac_sign_ticket
_krb5_kdc_pac_ticket_parse _krb5_kdc_pac_ticket_parse
_kdc_tkt_insert_pac _kdc_tkt_insert_pac

View File

@@ -53,8 +53,15 @@ struct krb5_pac_data {
struct PAC_INFO_BUFFER *server_checksum; struct PAC_INFO_BUFFER *server_checksum;
struct PAC_INFO_BUFFER *privsvr_checksum; struct PAC_INFO_BUFFER *privsvr_checksum;
struct PAC_INFO_BUFFER *logon_name; struct PAC_INFO_BUFFER *logon_name;
struct PAC_INFO_BUFFER *upn_dns_info;
struct PAC_INFO_BUFFER *ticket_checksum; struct PAC_INFO_BUFFER *ticket_checksum;
krb5_data ticket_sign_data; 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 #define PAC_ALIGNMENT 8
@@ -66,8 +73,13 @@ struct krb5_pac_data {
#define PAC_PRIVSVR_CHECKSUM 7 #define PAC_PRIVSVR_CHECKSUM 7
#define PAC_LOGON_NAME 10 #define PAC_LOGON_NAME 10
#define PAC_CONSTRAINED_DELEGATION 11 #define PAC_CONSTRAINED_DELEGATION 11
#define PAC_UPN_DNS_INFO 12
#define PAC_TICKET_CHECKSUM 16 #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) \ #define CHECK(r,f,l) \
do { \ do { \
if (((r) = f ) != 0) { \ if (((r) = f ) != 0) { \
@@ -242,6 +254,14 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
goto out; goto out;
} }
p->logon_name = &p->pac->buffers[i]; 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) { } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
if (p->ticket_checksum) { if (p->ticket_checksum) {
ret = EINVAL; ret = EINVAL;
@@ -445,6 +465,11 @@ krb5_pac_free(krb5_context context, krb5_pac pac)
return; return;
krb5_data_free(&pac->data); krb5_data_free(&pac->data);
krb5_data_free(&pac->ticket_sign_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->pac);
free(pac); free(pac);
} }
@@ -604,6 +629,185 @@ create_checksum(krb5_context context,
return 0; 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; krb5_error_code ret;
uint32_t time1, time2; uint32_t time1, time2;
krb5_storage *sp; krb5_storage *sp = NULL;
uint16_t len; uint16_t len;
char *s = NULL; char *s = NULL;
char *principal_string = NULL; char *principal_string = NULL;
@@ -734,6 +938,7 @@ verify_logonname(krb5_context context,
free(principal_string); free(principal_string);
return ret; return ret;
out: out:
krb5_storage_free(sp);
return ret; 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; return 0;
} }
@@ -1012,10 +1231,12 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pac_sign(krb5_context context, _krb5_pac_sign(krb5_context context,
krb5_pac p, krb5_pac p,
time_t authtime, time_t authtime,
krb5_principal principal, krb5_const_principal principal,
const krb5_keyblock *server_key, const krb5_keyblock *server_key,
const krb5_keyblock *priv_key, const krb5_keyblock *priv_key,
uint16_t rodc_id, uint16_t rodc_id,
krb5_const_principal upn_princ,
krb5_const_principal canon_princ,
krb5_data *data) krb5_data *data)
{ {
krb5_error_code ret; krb5_error_code ret;
@@ -1027,9 +1248,11 @@ _krb5_pac_sign(krb5_context context,
int num = 0; int num = 0;
size_t i, sz; size_t i, sz;
krb5_data logon, d; krb5_data logon, d;
krb5_data upn_dns_info;
krb5_data_zero(&d); krb5_data_zero(&d);
krb5_data_zero(&logon); krb5_data_zero(&logon);
krb5_data_zero(&upn_dns_info);
for (i = 0; i < p->pac->numbuffers; i++) { for (i = 0; i < p->pac->numbuffers; i++) {
if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { 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", "")); N_("PAC has multiple logon names", ""));
goto out; 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) { } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
if (p->ticket_checksum == NULL) { if (p->ticket_checksum == NULL) {
p->ticket_checksum = &p->pac->buffers[i]; p->ticket_checksum = &p->pac->buffers[i];
@@ -1081,6 +1314,8 @@ _krb5_pac_sign(krb5_context context,
num++; num++;
if (p->privsvr_checksum == NULL) if (p->privsvr_checksum == NULL)
num++; num++;
if ((upn_princ || canon_princ) && p->upn_dns_info == NULL)
num++;
if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL)
num++; num++;
@@ -1110,9 +1345,14 @@ _krb5_pac_sign(krb5_context context,
memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
p->privsvr_checksum->type = PAC_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) { if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) {
p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++]; 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; p->ticket_checksum->type = PAC_TICKET_CHECKSUM;
} }
} }
@@ -1127,6 +1367,9 @@ _krb5_pac_sign(krb5_context context,
if (ret == 0) if (ret == 0)
ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); 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 */ /* Encode PAC */
if (ret == 0) { if (ret == 0) {
sp = krb5_storage_emem(); sp = krb5_storage_emem();
@@ -1190,6 +1433,13 @@ _krb5_pac_sign(krb5_context context,
ret = KRB5KDC_ERR_BADOPTION; ret = KRB5KDC_ERR_BADOPTION;
goto out; 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 { } else {
len = p->pac->buffers[i].buffersize; len = p->pac->buffers[i].buffersize;
ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;
@@ -1279,6 +1529,7 @@ _krb5_pac_sign(krb5_context context,
*data = d; *data = d;
krb5_data_free(&logon); krb5_data_free(&logon);
krb5_data_free(&upn_dns_info);
krb5_storage_free(sp); krb5_storage_free(sp);
krb5_storage_free(spdata); krb5_storage_free(spdata);
@@ -1286,6 +1537,7 @@ _krb5_pac_sign(krb5_context context,
out: out:
krb5_data_free(&d); krb5_data_free(&d);
krb5_data_free(&logon); krb5_data_free(&logon);
krb5_data_free(&upn_dns_info);
if (sp) if (sp)
krb5_storage_free(sp); krb5_storage_free(sp);
if (spdata) if (spdata)
@@ -1347,6 +1599,20 @@ out:
return ret; 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 unsigned char single_zero = '\0';
static krb5_data single_zero_pac = { 1, &single_zero }; static krb5_data single_zero_pac = { 1, &single_zero };
@@ -1470,12 +1736,14 @@ out:
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kdc_pac_sign_ticket(krb5_context context, _krb5_kdc_pac_sign_ticket(krb5_context context,
const krb5_pac pac, const krb5_pac pac,
krb5_principal client, krb5_const_principal client,
const krb5_keyblock *server_key, const krb5_keyblock *server_key,
const krb5_keyblock *kdc_key, const krb5_keyblock *kdc_key,
uint16_t rodc_id, uint16_t rodc_id,
krb5_boolean add_ticket_sig, krb5_boolean add_ticket_sig,
EncTicketPart *tkt) EncTicketPart *tkt,
krb5_const_principal upn,
krb5_const_principal canon_name)
{ {
krb5_error_code ret; krb5_error_code ret;
krb5_data tkt_data; 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, 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) if (ret == 0)
ret = _kdc_tkt_insert_pac(context, tkt, &rspac); ret = _kdc_tkt_insert_pac(context, tkt, &rspac);
krb5_data_free(&rspac); krb5_data_free(&rspac);

View File

@@ -1042,6 +1042,17 @@ krb5_rd_req_ctx(krb5_context context,
o->ticket->client, o->ticket->client,
o->keyblock, o->keyblock,
NULL); 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); krb5_pac_free(context, pac);
if (ret) if (ret)
goto out; goto out;

View File

@@ -1823,3 +1823,99 @@ cleanup:
} }
return ret; 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;
}

View File

@@ -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" "\x69\x9d\x4f\x44\xce\xc3\x6d\xae\x51\x20\x24\x61\xb6\x6f\xff\x27"
"\xc4\x36\xb1"; "\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 { struct test_pac_ticket {
const char *name; const char *name;
const unsigned char *ticket; const unsigned char *ticket;
@@ -901,7 +959,7 @@ main(int argc, char **argv)
ret = krb5_init_context(&context); ret = krb5_init_context(&context);
if (ret) if (ret)
errx(1, "krb5_init_contex"); errx(1, "krb5_init_context");
krb5_enctype_enable(context, ETYPE_DES_CBC_MD5); krb5_enctype_enable(context, ETYPE_DES_CBC_MD5);
@@ -1018,6 +1076,34 @@ main(int argc, char **argv)
krb5_pac_free(context, pac); krb5_pac_free(context, pac);
krb5_free_principal(context, p2); 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 * Test empty free
*/ */

View File

@@ -814,6 +814,7 @@ HEIMDAL_KRB5_2.0 {
_krb5_get_int; _krb5_get_int;
_krb5_get_int64; _krb5_get_int64;
_krb5_pac_sign; _krb5_pac_sign;
_krb5_pac_get_canon_principal;
_krb5_kdc_pac_sign_ticket; _krb5_kdc_pac_sign_ticket;
_krb5_kdc_pac_ticket_parse; _krb5_kdc_pac_ticket_parse;
_kdc_tkt_insert_pac; _kdc_tkt_insert_pac;

View File

@@ -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 kaka --use-defaults digest/${R}@${R} || exit 1
${kadmin} add -p u1 --use-defaults user1@${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 # Create a server principal with no AES
${kadmin} add -p p1 --use-defaults host/no-aes.test.h5l.se@${R} || exit 1 ${kadmin} add -p p1 --use-defaults host/no-aes.test.h5l.se@${R} || exit 1
@@ -312,6 +313,22 @@ done
rm ${keytabfile}.new 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." echo "====== test channel-bindings."
for mech in krb5 spnego; do for mech in krb5 spnego; do

View File

@@ -2,3 +2,4 @@ include @objdirabs@/krb5.conf
[libdefaults] [libdefaults]
client_aware_channel_bindings = true client_aware_channel_bindings = true
report_canonical_client_name = true