/* * Copyright (c) 2021 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "gsskrb5_locl.h" /* * (Not-yet-)Standard name attributes for Kerberos MNs, * GSS_KRB5_NAME_ATTRIBUTE_BASE_URN + "...". * * I.e., "urn:ietf:kerberos:nameattr-...". (XXX Register this URN namespace * with IANA.) * * Note that we do use URN fragments. * * Specific attributes below the base URN: * * - name access attributes: * - "realm" -> realm of name * - "name-ncomp" -> count of name components * - "name-ncomp#" -> name component N (0 <= N <= 9) * * Ticket and Authenticator access attributes: * * - "transit-path" -> encoding of the transited path * - "authenticator-authz-data" -> encoding of all of the authz-data from * the AP-REQ's Authenticator * - "ticket-authz-data" -> encoding of all of the authz-data from * the AP-REQ's Ticket * - "ticket-authz-data#pac" -> the PAC * - "authz-data#" -> encoding of all of a specific auth-data * element type N (e.g., 2, meaning * AD-INTENDED-FOR-SERVER) * * Misc. attributes: * * - "peer-realm" -> name of peer's realm (if this is an MN * resulting for establishing a security * context) * - "canonical-name" -> exported name token and RFC1964 display * syntax of the name's canonical name * * Compatibility with MIT: * * - "urn:mspac:" -> the PAC and its individual info buffers * * TODO: * * - Add some sort of display syntax for transit path * - Add support for URN q-components or attribute prefixes to specify * 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 PAC logon fields, including SIDs (one at a time) * - Add support for CAMMAC? */ static int attr_eq(gss_const_buffer_t attr, const char *aname, size_t aname_len, \ int prefix_check) { if (attr->length < aname_len) return 0; 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, 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 split_attr(gss_const_buffer_t orig, gss_buffer_t prefix, gss_buffer_t attr, gss_buffer_t frag, int *is_urn) { char *last = NULL; char *p = orig->value; *attr = *orig; prefix->value = orig->value; prefix->length = 0; frag->length = 0; frag->value = NULL; /* FIXME We don't have a memrchr() in lib/roken */ for (p = memchr(p, ' ', orig->length); p; p = memchr(p + 1, ' ', orig->length)) { last = p; prefix->length = last - (const char *)orig->value; attr->value = last + 1; attr->length = orig->length - (prefix->length + 1); } if (prefix->length == 0) prefix->value = NULL; if ((*is_urn = (strncmp(attr->value, "urn:", sizeof("urn:") - 1) == 0)) && (p = memchr((char *)attr->value + 1, '#', attr->length - 1))) { frag->value = ++p; frag->length = attr->length - (p - (const char *)attr->value); attr->length = --p - (const char *)attr->value; } } typedef OM_uint32 get_name_attr_f(OM_uint32 *, const CompositePrincipal *, gss_const_buffer_t, gss_const_buffer_t, gss_const_buffer_t, int *, int *, gss_buffer_t, gss_buffer_t, int *); typedef OM_uint32 set_name_attr_f(OM_uint32 *, CompositePrincipal *, gss_const_buffer_t, gss_const_buffer_t, gss_const_buffer_t, int, gss_buffer_t); typedef OM_uint32 del_name_attr_f(OM_uint32 *, CompositePrincipal *, gss_const_buffer_t, gss_const_buffer_t, gss_const_buffer_t); typedef get_name_attr_f *get_name_attr_fp; typedef set_name_attr_f *set_name_attr_fp; typedef del_name_attr_f *del_name_attr_fp; static get_name_attr_f get_realm; static get_name_attr_f get_ncomps; static get_name_attr_f get_peer_realm; static get_name_attr_f get_pac; static get_name_attr_f get_pac_buffer; static get_name_attr_f get_authz_data; static get_name_attr_f get_ticket_authz_data; static get_name_attr_f get_authenticator_authz_data; static set_name_attr_f set_authenticator_authz_data; static get_name_attr_f get_transited; static get_name_attr_f get_canonical_name; #define NB(n) \ GSS_KRB5_NAME_ATTRIBUTE_BASE_URN n, n, \ sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN n) - 1, \ sizeof(n) - 1 #define NM(n) \ "urn:mspac:" n, n, sizeof("urn:mspac:" n) - 1, sizeof(n) - 1 static struct krb5_name_attrs { const char *fullname; const char *name; size_t fullnamelen; size_t namelen; get_name_attr_fp getter; set_name_attr_fp setter; del_name_attr_fp deleter; unsigned int indicate:1; unsigned int is_krb5_name_attr_urn:1; } name_attrs[] = { /* XXX We should sort these so we can binary search them */ { NB("realm"), get_realm, NULL, NULL, 1, 1 }, { NB("name-ncomp"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#0"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#1"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#2"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#3"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#4"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#5"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#6"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#7"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#8"), get_ncomps, NULL, NULL, 1, 1 }, { NB("name-ncomp#9"), get_ncomps, NULL, NULL, 1, 1 }, { NB("peer-realm"), get_peer_realm, NULL, NULL, 1, 1 }, { NB("ticket-authz-data#pac"), get_pac, NULL, NULL, 1, 1 }, { NM(""), get_pac, NULL, NULL, 1, 0 }, { NM("logon-info"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("credentials-info"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("server-checksum"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("privsvr-checksum"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("client-info"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("delegation-info"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("upn-dns-info"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("ticket-checksum"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("attributes-info"), get_pac_buffer, NULL, NULL, 1, 0 }, { NM("requestor-sid"), get_pac_buffer, NULL, NULL, 1, 0 }, { NB("ticket-authz-data#kdc-issued"), get_ticket_authz_data, NULL, NULL, 1, 1 }, { NB("ticket-authz-data"), get_ticket_authz_data, NULL, NULL, 1, 1 }, { NB("authenticator-authz-data"), get_authenticator_authz_data, set_authenticator_authz_data, NULL, 1, 1 }, { NB("authz-data"), get_authz_data, NULL, NULL, 1, 1 }, { NB("transit-path"), get_transited, NULL, NULL, 1, 1 }, { NB("canonical-name"), get_canonical_name, NULL, NULL, 1, 1 }, }; OM_uint32 GSSAPI_CALLCONV _gsskrb5_get_name_attribute(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t original_attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { gss_buffer_desc prefix, attr, suffix, frag; size_t i; int is_krb5_name_attr_urn = 0; int is_urn = 0; *minor_status = 0; if (authenticated) *authenticated = 0; if (complete) *complete = 0; if (more) *more = 0; if (value) { value->length = 0; value->value = NULL; } if (display_value) { display_value->length = 0; display_value->value = NULL; } suffix.value = NULL; suffix.length = 0; split_attr(original_attr, &prefix, &attr, &frag, &is_urn); if (prefix.length || !is_urn) return GSS_S_UNAVAILABLE; is_krb5_name_attr_urn = ATTR_EQ_PREFIX(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN); if (is_krb5_name_attr_urn) { suffix.value = (char *)attr.value + sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN) - 1; suffix.length = attr.length - (sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN) - 1); } for (i = 0; i < sizeof(name_attrs)/sizeof(name_attrs[0]); i++) { if (!name_attrs[i].getter) continue; if (name_attrs[i].is_krb5_name_attr_urn && is_krb5_name_attr_urn) { if (!attr_eq(&suffix, name_attrs[i].name, name_attrs[i].namelen, 0)) continue; } else if (!name_attrs[i].is_krb5_name_attr_urn && !is_krb5_name_attr_urn) { if (!attr_eq(&attr, name_attrs[i].fullname, name_attrs[i].fullnamelen, 0)) continue; } else continue; return name_attrs[i].getter(minor_status, (const CompositePrincipal *)name, &prefix, &attr, &frag, authenticated, complete, value, display_value, more); } return GSS_S_UNAVAILABLE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_set_name_attribute(OM_uint32 *minor_status, gss_name_t name, int complete, gss_buffer_t original_attr, gss_buffer_t value) { gss_buffer_desc prefix, attr, suffix, frag; size_t i; int is_krb5_name_attr_urn = 0; int is_urn = 0; *minor_status = 0; suffix.value = NULL; suffix.length = 0; split_attr(original_attr, &prefix, &attr, &frag, &is_urn); if (prefix.length || !is_urn) return GSS_S_UNAVAILABLE; is_krb5_name_attr_urn = ATTR_EQ_PREFIX(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN); if (is_krb5_name_attr_urn) { suffix.value = (char *)attr.value + sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN) - 1; suffix.length = attr.length - (sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN) - 1); } for (i = 0; i < sizeof(name_attrs)/sizeof(name_attrs[0]); i++) { if (!name_attrs[i].setter) continue; if (name_attrs[i].is_krb5_name_attr_urn && is_krb5_name_attr_urn) { if (!attr_eq(&suffix, name_attrs[i].name, name_attrs[i].namelen, 0)) continue; } else if (!name_attrs[i].is_krb5_name_attr_urn && !is_krb5_name_attr_urn) { if (!attr_eq(&attr, name_attrs[i].name, name_attrs[i].namelen, 0)) continue; } else continue; return name_attrs[i].setter(minor_status, (CompositePrincipal *)name, &prefix, &attr, &frag, complete, value); } return GSS_S_UNAVAILABLE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_delete_name_attribute(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t original_attr) { gss_buffer_desc prefix, attr, suffix, frag; size_t i; int is_krb5_name_attr_urn = 0; int is_urn = 0; *minor_status = 0; suffix.value = NULL; suffix.length = 0; split_attr(original_attr, &prefix, &attr, &frag, &is_urn); if (prefix.length || !is_urn) return GSS_S_UNAVAILABLE; is_krb5_name_attr_urn = ATTR_EQ_PREFIX(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN); if (is_krb5_name_attr_urn) { suffix.value = (char *)attr.value + sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN) - 1; suffix.length = attr.length - (sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN) - 1); } for (i = 0; i < sizeof(name_attrs)/sizeof(name_attrs[0]); i++) { if (!name_attrs[i].deleter) continue; if (name_attrs[i].is_krb5_name_attr_urn && is_krb5_name_attr_urn) { if (!attr_eq(&suffix, name_attrs[i].name, name_attrs[i].namelen, 0)) continue; } else if (!name_attrs[i].is_krb5_name_attr_urn && !is_krb5_name_attr_urn) { if (!attr_eq(&attr, name_attrs[i].fullname, name_attrs[i].fullnamelen, 0)) continue; } else continue; return name_attrs[i].deleter(minor_status, (CompositePrincipal *)name, &prefix, &attr, &frag); } return GSS_S_UNAVAILABLE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_name(OM_uint32 *minor_status, gss_name_t name, int *name_is_MN, gss_OID *MN_mech, gss_buffer_set_t *attrs) { gss_buffer_desc prefix, attr, frag, a; OM_uint32 major = GSS_S_UNAVAILABLE; size_t i; int authenticated, is_urn; *minor_status = 0; if (name_is_MN) *name_is_MN = 1; if (MN_mech) *MN_mech = GSS_KRB5_MECHANISM; if (name == GSS_C_NO_NAME) return GSS_S_CALL_INACCESSIBLE_READ; if (attrs == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; for (i = 0; i < sizeof(name_attrs)/sizeof(name_attrs[0]); i++) { if (!name_attrs[i].indicate) continue; a.value = (void *)(uintptr_t)name_attrs[i].fullname; a.length = name_attrs[i].fullnamelen; split_attr(&a, &prefix, &attr, &frag, &is_urn); major = name_attrs[i].getter(minor_status, (const CompositePrincipal *)name, &prefix, &attr, &frag, &authenticated, NULL, NULL, NULL, NULL); if (major == GSS_S_UNAVAILABLE) continue; if (major != GSS_S_COMPLETE) break; major = gss_add_buffer_set_member(minor_status, &a, attrs); } if (major == GSS_S_UNAVAILABLE) major = GSS_S_COMPLETE; return major; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_display_name_ext(OM_uint32 *minor_status, gss_name_t name, gss_OID display_as_name_type, gss_buffer_t display_name) { krb5_const_principal p = (void *)name; char *s = NULL; *minor_status = 0; if (display_name == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; display_name->length = 0; display_name->value = NULL; if (gss_oid_equal(display_as_name_type, GSS_C_NT_USER_NAME)) { if (p->name.name_string.len != 1) return GSS_S_UNAVAILABLE; return _gsskrb5_localname(minor_status, name, GSS_KRB5_MECHANISM, display_name); } if (!gss_oid_equal(display_as_name_type, GSS_C_NT_HOSTBASED_SERVICE) || p->name.name_string.len != 2 || strchr(p->name.name_string.val[0], '@') || strchr(p->name.name_string.val[1], '.') == NULL) return GSS_S_UNAVAILABLE; if (asprintf(&s, "%s@%s", p->name.name_string.val[0], p->name.name_string.val[1]) == -1 || s == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } display_name->length = strlen(s); display_name->value = s; return GSS_S_COMPLETE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_export_name_composite(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t exported_name) { krb5_error_code kret; gss_buffer_desc inner = GSS_C_EMPTY_BUFFER; unsigned char *buf; size_t sz; if (name == NULL) return GSS_S_CALL_INACCESSIBLE_READ; if (exported_name == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; ASN1_MALLOC_ENCODE(CompositePrincipal, inner.value, inner.length, (void *)name, &sz, kret); if (kret != 0) { *minor_status = kret; return GSS_S_FAILURE; } exported_name->length = 10 + inner.length + GSS_KRB5_MECHANISM->length; exported_name->value = malloc(exported_name->length); if (exported_name->value == NULL) { free(inner.value); *minor_status = ENOMEM; return GSS_S_FAILURE; } /* TOK, MECH_OID_LEN, DER(MECH_OID), NAME_LEN, NAME */ buf = exported_name->value; buf[0] = 0x04; buf[1] = 0x02; buf[2] = ((GSS_KRB5_MECHANISM->length + 2) >> 8) & 0xff; buf[3] = (GSS_KRB5_MECHANISM->length + 2) & 0xff; buf[4] = 0x06; buf[5] = (GSS_KRB5_MECHANISM->length) & 0xFF; memcpy(buf + 6, GSS_KRB5_MECHANISM->elements, GSS_KRB5_MECHANISM->length); buf += 6 + GSS_KRB5_MECHANISM->length; buf[0] = (inner.length >> 24) & 0xff; buf[1] = (inner.length >> 16) & 0xff; buf[2] = (inner.length >> 8) & 0xff; buf[3] = (inner.length) & 0xff; buf += 4; memcpy(buf, inner.value, inner.length); free(inner.value); *minor_status = 0; return GSS_S_COMPLETE; } #define CHECK_ENOMEM(v, dv) \ do { \ if (((v) && !(v)->value) || ((dv) && !(dv)->value)) { \ if ((v) && (v)->value) { \ free((v)->value); \ (v)->length = 0; \ (v)->value = NULL; \ } \ *minor_status = ENOMEM; \ return GSS_S_FAILURE; \ } \ } while (0) static OM_uint32 get_realm(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { PrincipalNameAttrs *nameattrs = name->nameattrs; if (prefix->length || frag->length || !name->realm) return GSS_S_UNAVAILABLE; if (authenticated && nameattrs && nameattrs->authenticated) *authenticated = 1; if (complete) *complete = 1; if (value && (value->value = strdup(name->realm))) value->length = strlen(name->realm); if (display_value && (display_value->value = strdup(name->realm))) display_value->length = strlen(name->realm); CHECK_ENOMEM(value, display_value); return GSS_S_COMPLETE; } static OM_uint32 get_ncomps(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { PrincipalNameAttrs *nameattrs = name->nameattrs; int n = -1; if (authenticated && nameattrs && nameattrs->authenticated) *authenticated = 1; if (complete) *complete = 1; if (frag->length == 1 && ((const char *)frag->value)[0] >= '0' && ((const char *)frag->value)[0] <= '9') { n = ((const char *)frag->value)[0] - '0'; } else if (frag->length == sizeof("all") - 1 && strncmp(frag->value, "all", sizeof("all") - 1) == 0) { if (!more || *more < -1 || *more == 0 || *more > CHAR_MAX || *more > (int)name->name.name_string.len) { *minor_status = EINVAL; return GSS_S_UNAVAILABLE; } if (*more == -1) { *more = name->name.name_string.len - 1; n = 0; } else { n = name->name.name_string.len - *more; (*more)--; } } if (frag->length == 0) { char *s = NULL; /* Outut count of components */ if (value && (value->value = malloc(sizeof(size_t)))) { *((size_t *)value->value) = name->name.name_string.len; value->length = sizeof(size_t); } if (display_value && asprintf(&s, "%u", (unsigned int)name->name.name_string.len) > 0) { display_value->value = s; display_value->length = strlen(display_value->value); } } else { /* * Output a component. The value and the display value are the same in * this case. */ if (n < 0 || n >= name->name.name_string.len) { *minor_status = EINVAL; return GSS_S_UNAVAILABLE; } if (value && (value->value = strdup(name->name.name_string.val[n]))) value->length = strlen(name->name.name_string.val[n]); if (display_value && (display_value->value = strdup(name->name.name_string.val[n]))) display_value->length = strlen(name->name.name_string.val[n]); } CHECK_ENOMEM(value, display_value); return GSS_S_COMPLETE; } static OM_uint32 get_peer_realm(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { PrincipalNameAttrs *nameattrs = name->nameattrs; if (prefix->length || frag->length || !nameattrs || !nameattrs->peer_realm) return GSS_S_UNAVAILABLE; if (authenticated) *authenticated = 1; if (complete) *complete = 1; if (value && (value->value = strdup(nameattrs->peer_realm[0]))) value->length = strlen(value->value); if (display_value && (display_value->value = strdup(nameattrs->peer_realm[0]))) display_value->length = strlen(display_value->value); CHECK_ENOMEM(value, display_value); return GSS_S_COMPLETE; } static OM_uint32 get_pac(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_error_code kret; krb5_context context; krb5_data data; PrincipalNameAttrs *nameattrs = name->nameattrs; PrincipalNameAttrSrc *src = nameattrs ? nameattrs->source : NULL; EncTicketPart *ticket = NULL; krb5_data_zero(&data); if (src == NULL || src->element != choice_PrincipalNameAttrSrc_enc_ticket_part) return GSS_S_UNAVAILABLE; ticket = &src->u.enc_ticket_part; if (prefix->length || !authenticated || !ticket) return GSS_S_UNAVAILABLE; GSSAPI_KRB5_INIT(&context); *authenticated = nameattrs->pac_verified; if (complete) *complete = 1; kret = _krb5_get_ad(context, ticket->authorization_data, NULL, KRB5_AUTHDATA_WIN2K_PAC, value ? &data : NULL); 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; } static OM_uint32 get_pac_buffer(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_error_code kret; krb5_context context; krb5_data data; PrincipalNameAttrs *nameattrs = name->nameattrs; krb5_data suffix; krb5_data_zero(&data); if (prefix->length || !authenticated || !nameattrs || !nameattrs->pac) return GSS_S_UNAVAILABLE; GSSAPI_KRB5_INIT(&context); 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 return GSS_S_UNAVAILABLE; /* should not be reached */ *authenticated = nameattrs->pac_verified; if (complete) *complete = 1; kret = _krb5_pac_get_buffer_by_name(context, nameattrs->pac, &suffix, value ? &data : NULL); 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; } static OM_uint32 get_authz_data(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_error_code kret = 0; PrincipalNameAttrs *nameattrs = name->nameattrs; PrincipalNameAttrSrc *src = nameattrs ? nameattrs->source : NULL; EncTicketPart *ticket = NULL; krb5_context context; krb5_data data; char s[22]; char *end; int64_t n; if (src) switch (src->element) { case choice_PrincipalNameAttrSrc_enc_ticket_part: ticket = &src->u.enc_ticket_part; break; case choice_PrincipalNameAttrSrc_enc_kdc_rep_part: default: return GSS_S_UNAVAILABLE; } if (!nameattrs || !frag->length || frag->length > sizeof(s) - 1) return GSS_S_UNAVAILABLE; /* Output a specific AD element from the ticket or authenticator */ krb5_data_zero(&data); memcpy(s, frag->value, frag->length); s[frag->length] = '\0'; errno = 0; n = strtoll(s, &end, 10); if (end[0] == '\0' && (errno || n > INT_MAX || n < INT_MIN)) { *minor_status = ERANGE; return GSS_S_UNAVAILABLE; } if (end[0] != '\0') { *minor_status = EINVAL; return GSS_S_UNAVAILABLE; } if (authenticated) *authenticated = 0; if (complete) *complete = 1; GSSAPI_KRB5_INIT(&context); kret = ENOENT; if (ticket && ticket->authorization_data) { kret = _krb5_get_ad(context, ticket->authorization_data, NULL, n, value ? &data : NULL); /* If it's from the ticket, it _may_ be authenticated: */ if (kret == 0 && authenticated) { if (n == KRB5_AUTHDATA_KDC_ISSUED) *authenticated = nameattrs->kdc_issued_verified; else if (n == KRB5_AUTHDATA_WIN2K_PAC) *authenticated = nameattrs->pac_verified; } } if (kret == ENOENT && nameattrs->authenticator_ad && n != KRB5_AUTHDATA_KDC_ISSUED && n != KRB5_AUTHDATA_WIN2K_PAC) { kret = _krb5_get_ad(context, nameattrs->authenticator_ad, NULL, n, value ? &data : NULL); } 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; } static OM_uint32 get_ticket_authz_data(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_error_code kret = 0; PrincipalNameAttrs *nameattrs = name->nameattrs; PrincipalNameAttrSrc *src = nameattrs ? nameattrs->source : NULL; EncTicketPart *ticket = NULL; size_t sz; if (src) switch (src->element) { case choice_PrincipalNameAttrSrc_enc_ticket_part: ticket = &src->u.enc_ticket_part; break; case choice_PrincipalNameAttrSrc_enc_kdc_rep_part: default: return GSS_S_UNAVAILABLE; } if (!ticket) return GSS_S_UNAVAILABLE; if (complete) *complete = 1; if (frag->length == sizeof("kdc-issued") - 1 && strncmp(frag->value, "kdc-issued", sizeof("kdc-issued") - 1) == 0) { krb5_context context; krb5_data data; GSSAPI_KRB5_INIT(&context); if (authenticated) *authenticated = nameattrs->kdc_issued_verified; kret = _krb5_get_ad(context, ticket->authorization_data, NULL, KRB5_AUTHDATA_KDC_ISSUED, value ? &data : NULL); if (value) { value->length = data.length; value->value = data.data; } if (kret == ENOENT) return GSS_S_UNAVAILABLE; *minor_status = kret; return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; } else if (frag->length) { return GSS_S_UNAVAILABLE; } /* Just because it's in the Ticket doesn't make it authenticated */ if (authenticated) *authenticated = 0; if (value) { ASN1_MALLOC_ENCODE(AuthorizationData, value->value, value->length, ticket->authorization_data, &sz, kret); *minor_status = kret; } return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; } static OM_uint32 get_authenticator_authz_data(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_error_code kret = 0; PrincipalNameAttrs *nameattrs = name->nameattrs; size_t sz; if (!nameattrs || !nameattrs->authenticator_ad) return GSS_S_UNAVAILABLE; if (authenticated) *authenticated = 0; if (complete) *complete = 1; if (value) { ASN1_MALLOC_ENCODE(AuthorizationData, value->value, value->length, nameattrs->authenticator_ad, &sz, kret); *minor_status = kret; } return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; } static OM_uint32 set_authenticator_authz_data(OM_uint32 *minor_status, CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int complete, gss_buffer_t value) { AuthorizationDataElement e; krb5_error_code kret; size_t sz; if (!value) return GSS_S_CALL_INACCESSIBLE_READ; if (frag->length && !ATTR_EQ(frag, "if-relevant")) return GSS_S_UNAVAILABLE; if ((name->nameattrs == NULL && (name->nameattrs = calloc(1, sizeof(*name->nameattrs))) == NULL) || (name->nameattrs->want_ad == NULL && (name->nameattrs->want_ad = calloc(1, sizeof(*name->nameattrs->want_ad))) == NULL)) { *minor_status = ENOMEM; return GSS_S_FAILURE; } memset(&e, 0, sizeof(e)); kret = decode_AuthorizationDataElement(value->value, value->length, &e, &sz); if (kret == 0) { if (frag->length) { AuthorizationData ir; ir.len = 0; ir.val = NULL; kret = add_AuthorizationData(&ir, &e); free_AuthorizationDataElement(&e); if (kret == 0) { e.ad_type = KRB5_AUTHDATA_IF_RELEVANT; ASN1_MALLOC_ENCODE(AuthorizationData, e.ad_data.data, e.ad_data.length, &ir, &sz, kret); kret = add_AuthorizationData(name->nameattrs->want_ad, &e); } free_AuthorizationData(&ir); } else { kret = add_AuthorizationData(name->nameattrs->want_ad, &e); free_AuthorizationDataElement(&e); } } *minor_status = kret; return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; } static OM_uint32 get_transited(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_error_code kret = 0; PrincipalNameAttrs *nameattrs = name->nameattrs; PrincipalNameAttrSrc *src = nameattrs ? nameattrs->source : NULL; EncTicketPart *ticket = NULL; size_t sz; if (src) switch (src->element) { case choice_PrincipalNameAttrSrc_enc_kdc_rep_part: break; case choice_PrincipalNameAttrSrc_enc_ticket_part: ticket = &src->u.enc_ticket_part; break; default: return GSS_S_UNAVAILABLE; } if (!nameattrs && !ticket) return GSS_S_UNAVAILABLE; if (nameattrs && !nameattrs->transited && !ticket) return GSS_S_UNAVAILABLE; if (authenticated) *authenticated = 1; if (complete) *complete = 1; if (value && ticket) ASN1_MALLOC_ENCODE(TransitedEncoding, value->value, value->length, &ticket->transited, &sz, kret); else if (value && nameattrs->transited) ASN1_MALLOC_ENCODE(TransitedEncoding, value->value, value->length, nameattrs->transited, &sz, kret); *minor_status = kret; return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; } static OM_uint32 get_canonical_name(OM_uint32 *minor_status, const CompositePrincipal *name, gss_const_buffer_t prefix, gss_const_buffer_t attr, gss_const_buffer_t frag, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_error_code kret = 0; PrincipalNameAttrs *nameattrs = name->nameattrs; PrincipalNameAttrSrc *src = nameattrs ? nameattrs->source : NULL; krb5_principal p = NULL; krb5_context context; EncTicketPart *ticket = NULL; EncKDCRepPart *kdcrep = NULL; if (src) switch (src->element) { case choice_PrincipalNameAttrSrc_enc_kdc_rep_part: kdcrep = &src->u.enc_kdc_rep_part; break; case choice_PrincipalNameAttrSrc_enc_ticket_part: ticket = &src->u.enc_ticket_part; break; default: return GSS_S_UNAVAILABLE; } GSSAPI_KRB5_INIT(&context); if (authenticated) *authenticated = 1; if (complete) *complete = 1; if (kdcrep) { kret = _krb5_principalname2krb5_principal(context, &p, kdcrep->sname, kdcrep->srealm); } else if (nameattrs && nameattrs->pac && (_krb5_pac_get_canon_principal(context, nameattrs->pac, &p)) == 0) { if (authenticated) *authenticated = nameattrs->pac_verified; } else if (ticket) { krb5_data data; krb5_pac pac = NULL; krb5_data_zero(&data); /* Use canonical name from PAC if available */ kret = _krb5_get_ad(context, ticket->authorization_data, NULL, KRB5_AUTHDATA_WIN2K_PAC, &data); if (kret == 0) kret = krb5_pac_parse(context, data.data, data.length, &pac); if (kret == 0) kret = _krb5_pac_get_canon_principal(context, pac, &p); if (kret == 0 && authenticated) *authenticated = nameattrs->pac_verified; else if (kret == ENOENT) kret = _krb5_principalname2krb5_principal(context, &p, ticket->cname, ticket->crealm); krb5_data_free(&data); krb5_pac_free(context, pac); } else return GSS_S_UNAVAILABLE; if (kret == 0 && value) { OM_uint32 major; /* * Value is exported name token (exported composite name token * should also work). */ major = _gsskrb5_export_name(minor_status, (gss_name_t)p, value); if (major != GSS_S_COMPLETE) { krb5_free_principal(context, p); return major; } } if (kret == 0 && display_value) { /* Display value is principal name display form */ kret = krb5_unparse_name(context, p, (char **)&display_value->value); if (kret == 0) display_value->length = strlen(display_value->value); } krb5_free_principal(context, p); if (kret) { if (value) { free(value->value); value->length = 0; value->value = NULL; } *minor_status = kret; return GSS_S_UNAVAILABLE; } return GSS_S_COMPLETE; }