summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/gssapi/krb5/name_attrs.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/gssapi/krb5/name_attrs.c')
-rw-r--r--third_party/heimdal/lib/gssapi/krb5/name_attrs.c1171
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;
+}