diff options
Diffstat (limited to 'third_party/heimdal/lib/gssapi/krb5/name_attrs.c')
-rw-r--r-- | third_party/heimdal/lib/gssapi/krb5/name_attrs.c | 1171 |
1 files changed, 1171 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/gssapi/krb5/name_attrs.c b/third_party/heimdal/lib/gssapi/krb5/name_attrs.c new file mode 100644 index 0000000..11fc2ef --- /dev/null +++ b/third_party/heimdal/lib/gssapi/krb5/name_attrs.c @@ -0,0 +1,1171 @@ +/* + * 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#<digit>" -> 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#<N>" -> 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; +} |