diff --git a/lib/gssapi/Makefile.am b/lib/gssapi/Makefile.am index a4b203b0f..451903bf6 100644 --- a/lib/gssapi/Makefile.am +++ b/lib/gssapi/Makefile.am @@ -383,6 +383,7 @@ LDADD = libgssapi.la \ $(LIB_roken) test_names_LDADD = $(LDADD) $(top_builddir)/lib/asn1/libasn1.la +test_context_LDADD = $(LDADD) $(top_builddir)/lib/wind/libwind.la # gss diff --git a/lib/gssapi/krb5/name_attrs.c b/lib/gssapi/krb5/name_attrs.c index 4c06c65ec..77327e9bb 100644 --- a/lib/gssapi/krb5/name_attrs.c +++ b/lib/gssapi/krb5/name_attrs.c @@ -71,7 +71,7 @@ * * Compatibility with MIT: * - * - "urn:mspac:" -> the PAC + * - "urn:mspac:" -> the PAC and its individual info buffers * * TODO: * @@ -80,25 +80,25 @@ * alternative raw and/or display value encodings (JSON?) * - Add support for attributes for accessing other parts of the Ticket / KDC * reply enc-parts, like auth times - * - Add support for getting specific PAC buffers - * - Add support for getting PAC login fields, including SIDs (one at a time) + * - Add support for getting PAC logon fields, including SIDs (one at a time) * - Add support for CAMMAC? */ static int -attr_eq(gss_buffer_t attr, const char *aname, size_t aname_len) +attr_eq(gss_buffer_t attr, const char *aname, size_t aname_len, + int prefix_check) { - const char *s; - if (attr->length < aname_len) return 0; - /* Note: `s' is not NUL-terminated */ - s = ((const char *)attr->value) + attr->length - aname_len; - return strncmp(s, aname, aname_len) == 0 && - (attr->length == aname_len || s[aname_len] == '#'); + + if (strncmp((char *)attr->value, aname, aname_len) != 0) + return 0; + + return prefix_check || attr->length == aname_len; } -#define ATTR_EQ(a, an) (attr_eq(a, an, sizeof(an) - 1)) +#define ATTR_EQ(a, an) (attr_eq(a, an, sizeof(an) - 1, FALSE)) +#define ATTR_EQ_PREFIX(a, an) (attr_eq(a, an, sizeof(an) - 1, TRUE)) /* Split attribute into prefix, suffix, and fragment. See RFC6680. */ static void @@ -394,17 +394,25 @@ _gsskrb5_get_name_attribute(OM_uint32 *minor_status, if (kret == ENOENT) return GSS_S_UNAVAILABLE; return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; - } else if ((ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN - "ticket-authz-data#pac") || - ATTR_EQ(&attr, "urn:mspac:")) && - ticket && ticket->authorization_data) { + } else if (ticket && ticket->authorization_data && + (ATTR_EQ_PREFIX(&attr, "urn:mspac:") || + (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "ticket-authz-data") && + (ATTR_EQ(&frag, "pac") || ATTR_EQ_PREFIX(&frag, "pac-"))))) { krb5_context context; - krb5_data data; + krb5_data data, pac_data, *datap; + krb5_data suffix; + + if (ATTR_EQ_PREFIX(&attr, "urn:mspac:")) { + suffix.length = attr.length - (sizeof("urn:mspac:") - 1); + suffix.data = (char *)attr.value + sizeof("urn:mspac:") - 1; + } else if (ATTR_EQ_PREFIX(&frag, "pac-")) { + suffix.length = frag.length - sizeof("pac-") - 1; + suffix.data = (char *)frag.value + sizeof("pac-") - 1; + } else + krb5_data_zero(&suffix); /* ticket-authz-data#pac */ /* * In MIT the attribute for the whole PAC is "urn:mspac:". - * - * TBD: Add support for attributes for specific PAC buffers, like MIT. */ GSSAPI_KRB5_INIT(&context); @@ -414,22 +422,39 @@ _gsskrb5_get_name_attribute(OM_uint32 *minor_status, if (complete) *complete = 1; + if (suffix.length) + datap = &pac_data; + else if (value) + datap = &data; + else + datap = NULL; + kret = _krb5_get_ad(context, ticket->authorization_data, - NULL, KRB5_AUTHDATA_WIN2K_PAC, - value ? &data : NULL); + NULL, KRB5_AUTHDATA_WIN2K_PAC, datap); + if (kret == 0 && suffix.length) { + krb5_pac pac; + + kret = krb5_pac_parse(context, pac_data.data, pac_data.length, &pac); + if (kret == 0) { + kret = _krb5_pac_get_buffer_by_name(context, pac, &suffix, + value ? &data : NULL); + krb5_pac_free(context, pac); + } + krb5_data_free(&pac_data); + } if (value) { value->length = data.length; value->value = data.data; } + *minor_status = kret; if (kret == ENOENT) return GSS_S_UNAVAILABLE; return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; - } else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN - "ticket-authz-data#kdc-issued") && - ticket && - ticket->authorization_data) { + } else if (ticket && ticket->authorization_data && + ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "ticket-authz-data") && + ATTR_EQ(&frag, "kdc-issued")) { krb5_context context; krb5_data data; @@ -583,7 +608,25 @@ _gsskrb5_inquire_name(OM_uint32 *minor_status, ADD_URN("name-ncomp#9"); ADD_URN("peer-realm"); ADD_URN("ticket-authz-data#pac"); + ADD_URN("ticket-authz-data#pac-logon-info"); + ADD_URN("ticket-authz-data#pac-credentials-info"); + ADD_URN("ticket-authz-data#pac-server-checksum"); + ADD_URN("ticket-authz-data#pac-privsvr-checksum"); + ADD_URN("ticket-authz-data#pac-client-info"); + ADD_URN("ticket-authz-data#pac-delegation-info"); + ADD_URN("ticket-authz-data#pac-upn-dns-info"); + ADD_URN("ticket-authz-data#pac-attributes-info"); + ADD_URN("ticket-authz-data#pac-requestor-sid"); ADD_URN("urn:mspac:"); + ADD_URN("urn:mspac:logon-info"); + ADD_URN("urn:mspac:credentials-info"); + ADD_URN("urn:mspac:server-checksum"); + ADD_URN("urn:mspac:privsvr-checksum"); + ADD_URN("urn:mspac:client-info"); + ADD_URN("urn:mspac:delegation-info"); + ADD_URN("urn:mspac:upn-dns-info"); + ADD_URN("urn:mspac:attributes-info"); + ADD_URN("urn:mspac:requestor-sid"); ADD_URN("authenticator-authz-data"); /* XXX Add fragments? */ ADD_URN("ticket-authz-data"); /* XXX Add fragments? */ ADD_URN("authz-data"); diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c index 30fb5cb23..35fd13265 100644 --- a/lib/gssapi/test_context.c +++ b/lib/gssapi/test_context.c @@ -134,6 +134,87 @@ string_to_oids(gss_OID_set *oidsetp, char *names) } } +static void +show_pac_client_info(gss_name_t n) +{ + gss_buffer_desc dv = GSS_C_EMPTY_BUFFER; + gss_buffer_desc v = GSS_C_EMPTY_BUFFER; + gss_buffer_desc a; + OM_uint32 maj, min; + int authenticated, complete, more; + + krb5_error_code ret; + krb5_storage *sp = NULL; + uint16_t len = 0, *s; + uint64_t tmp; + char *logon_string = NULL; + + a.value = "urn:mspac:client-info"; + a.length = strlen((char *)a.value); + more = 0; + maj = gss_get_name_attribute(&min, n, &a, &authenticated, &complete, &v, + &dv, &more); + if (maj != GSS_S_COMPLETE) + errx(1, "gss_get_name_attribute: %s", + gssapi_err(maj, min, GSS_KRB5_MECHANISM)); + + + sp = krb5_storage_from_readonly_mem(v.value, v.length); + if (sp == NULL) + errx(1, "show_pac_client_info: out of memory"); + + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + + ret = krb5_ret_uint64(sp, &tmp); /* skip over time */ + if (ret == 0) + ret = krb5_ret_uint16(sp, &len); + if (ret || len == 0) + errx(1, "show_pac_client_info: invalid PAC logon info length"); + + s = malloc(len); + ret = krb5_storage_read(sp, s, len); + if (ret != len) + errx(1, "show_pac_client_info:, failed to read PAC logon name"); + + krb5_storage_free(sp); + + { + size_t ucs2len = len / 2; + uint16_t *ucs2; + size_t u8len; + unsigned int flags = WIND_RW_LE; + + ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); + if (ucs2 == NULL) + errx(1, "show_pac_client_info: out of memory"); + + ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); + free(s); + if (ret) + errx(1, "failed to convert string to UCS-2"); + + ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); + if (ret) + errx(1, "failed to count length of UCS-2 string"); + + u8len += 1; /* Add space for NUL */ + logon_string = malloc(u8len); + if (logon_string == NULL) + errx(1, "show_pac_client_info: out of memory"); + + ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len); + free(ucs2); + if (ret) + errx(1, "failed to convert to UTF-8"); + } + + printf("logon name: %s\n", logon_string); + free(logon_string); + + gss_release_buffer(&min, &dv); + gss_release_buffer(&min, &v); +} + static void loop(gss_OID mechoid, gss_OID nameoid, const char *target, @@ -393,6 +474,8 @@ loop(gss_OID mechoid, } else warnx("display_name: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); + if (gss_oid_equal(actual_mech_server, GSS_KRB5_MECHANISM)) + show_pac_client_info(src_name); } gss_release_name(&min_stat, &src_name); diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index 5174d3c94..cf46c74d4 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -500,6 +500,7 @@ EXPORTS krb5_pac_add_buffer krb5_pac_free krb5_pac_get_buffer + _krb5_pac_get_buffer_by_name krb5_pac_get_kdc_checksum_info krb5_pac_get_types krb5_pac_init diff --git a/lib/krb5/pac.c b/lib/krb5/pac.c index 9fce4fd5d..2e1401a2d 100644 --- a/lib/krb5/pac.c +++ b/lib/krb5/pac.c @@ -73,6 +73,8 @@ struct krb5_pac_data { #define PACTYPE_SIZE 8 #define PAC_INFO_BUFFER_SIZE 16 +#define PAC_LOGON_INFO 1 +#define PAC_CREDENTIALS_INFO 2 #define PAC_SERVER_CHECKSUM 6 #define PAC_PRIVSVR_CHECKSUM 7 #define PAC_LOGON_NAME 10 @@ -432,11 +434,14 @@ krb5_pac_get_buffer(krb5_context context, krb5_pac p, if (p->pac->buffers[i].type != type) continue; - ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len); - if (ret) { - krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); - return ret; + if (data) { + ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len); + if (ret) { + krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); + return ret; + } } + return 0; } krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found", @@ -444,6 +449,45 @@ krb5_pac_get_buffer(krb5_context context, krb5_pac p, return ENOENT; } +static struct { + uint32_t type; + krb5_data name; +} pac_buffer_name_map[] = { +#define PAC_MAP_ENTRY(type, name) { PAC_##type, { sizeof(name) - 1, name } } + PAC_MAP_ENTRY(LOGON_INFO, "logon-info" ), + PAC_MAP_ENTRY(CREDENTIALS_INFO, "credentials-info" ), + PAC_MAP_ENTRY(SERVER_CHECKSUM, "server-checksum" ), + PAC_MAP_ENTRY(PRIVSVR_CHECKSUM, "privsvr-checksum" ), + PAC_MAP_ENTRY(LOGON_NAME, "client-info" ), + PAC_MAP_ENTRY(CONSTRAINED_DELEGATION, "delegation-info" ), + PAC_MAP_ENTRY(UPN_DNS_INFO, "upn-dns-info" ), + PAC_MAP_ENTRY(TICKET_CHECKSUM, "ticket-checksum" ), + PAC_MAP_ENTRY(ATTRIBUTES_INFO, "attributes-info" ), + PAC_MAP_ENTRY(REQUESTOR_SID, "requestor-sid" ) +}; + +/* + * + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_pac_get_buffer_by_name(krb5_context context, krb5_pac p, + const krb5_data *name, krb5_data *data) +{ + size_t i; + + for (i = 0; + i < sizeof(pac_buffer_name_map) / sizeof(pac_buffer_name_map[0]); + i++) { + if (krb5_data_cmp(name, &pac_buffer_name_map[i].name) == 0) + return krb5_pac_get_buffer(context, p, pac_buffer_name_map[i].type, data); + } + + krb5_set_error_message(context, ENOENT, "No PAC buffer with name %.*s was found", + (int)name->length, (char *)name->data); + return ENOENT; +} + /* * */ diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index 991c4d5ff..a389d1b64 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -493,6 +493,7 @@ HEIMDAL_KRB5_2.0 { krb5_pac_add_buffer; krb5_pac_free; krb5_pac_get_buffer; + _krb5_pac_get_buffer_by_name; krb5_pac_get_kdc_checksum_info; krb5_pac_get_types; krb5_pac_init;