gsskrb5: Add simple name attributes support

This adds Kerberos mechanism support for:

 - composite principal name export/import
 - getting rudimentary name attributes from GSS names using
   gss_get_name_attribute():
    - all (raw) authorization data from the Ticket
    - all (raw) authorization data from the Authenticator
    - transit path
    - realm
    - component count
    - each component
 - gss_inquire_name()
 - gss_display_name_ext() (just for the hostbased service name type
                           though)

The test exercises almost all of the functionality, except for:

 - getting the PAC
 - getting authz-data from the Authenticator
 - getting the transit path

TBD (much) later:

 - amend test_context to do minimal name attribute checks as well
 - gss_set_name_attribute() (to request authz-data)
 - gss_delete_name_attribute()
 - getting specific authorization data elements via URN fragments (as
   opposed to all of them)
 - parsing the PAC, extracting SIDs (each one as a separate value)
 - some configurable local policy (?)
 - plugin interface for additional local policy
This commit is contained in:
Nicolas Williams
2021-12-22 17:01:12 -06:00
committed by Nico Williams
parent 1cede09a0b
commit be708ca3cf
10 changed files with 1220 additions and 56 deletions

View File

@@ -59,6 +59,7 @@ krb5src = \
krb5/inquire_mechs_for_name.c \
krb5/inquire_names_for_mech.c \
krb5/inquire_sec_context_by_oid.c \
krb5/name_attrs.c \
krb5/pname_to_uid.c \
krb5/process_context_token.c \
krb5/prf.c \
@@ -381,6 +382,8 @@ LDADD = libgssapi.la \
$(top_builddir)/lib/krb5/libkrb5.la \
$(LIB_roken)
test_names_LDADD = $(LDADD) $(top_builddir)/lib/asn1/libasn1.la
# gss
dist_gsstool_SOURCES = gsstool.c

View File

@@ -233,6 +233,7 @@ typedef OM_uint32 gss_qop_t;
#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
#define GSS_C_EMPTY_BUFFER {0, NULL}
#define GSS_C_EMPTY_BUFFER_SET {0, NULL}
#define GSS_C_NO_IOV_BUFFER ((gss_iov_buffer_t)0)
#define GSS_C_NO_CRED_STORE ((gss_key_value_set_t)0)
@@ -393,6 +394,18 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_nt_anonymous_oid_desc;
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_nt_export_name_oid_desc;
#define GSS_C_NT_EXPORT_NAME (&__gss_c_nt_export_name_oid_desc)
/*
* The implementation must reserve static storage for a
* gss_OID_desc object containing the value
* {6, (void *)"\x2b\x06\x01\x05\x06\x06"}, corresponding to an
* object-identifier value of {iso(1) identified-organization(3) dod(6)
* internet(1) security(5) nametypes(6) gss-composite-export(6)}.
* The constant GSS_C_NT_COMPOSITE_EXPORT [RFC6680] should be initialized to
* point to that gss_OID_desc.
*/
extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_nt_composite_export_oid_desc;
#define GSS_C_NT_COMPOSITE_EXPORT (&__gss_c_nt_composite_export_oid_desc)
/* Major status codes */
#define GSS_S_COMPLETE 0

View File

@@ -218,6 +218,8 @@ gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
OM_uint32 num_enctypes,
int32_t *enctypes);
#define GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "urn:ietf:kerberos:nameattr-"
GSSAPI_CPP_END
#endif /* GSSAPI_SPNEGO_H_ */

View File

@@ -152,6 +152,13 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_nt_export_name_oid_desc =
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_nt_principal_name_oid_desc =
{10, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01") };
/*
* GSS_C_NT_COMPOSITE_EXPORT [RFC6680], OID {iso(1) identified-organization(3)
* dod(6) internet(1) security(5) nametypes(6) gss-composite-export(6)}.
*/
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_nt_composite_export_oid_desc =
{6, rk_UNCONST("\x2b\x06\x01\x05\x06\x06")};
/*
* draft-ietf-cat-iakerb-09, IAKERB:
* The mechanism ID for IAKERB proxy GSS-API Kerberos, in accordance
@@ -383,12 +390,12 @@ static gssapi_mech_interface_desc krb5_mech = {
sizeof(krb5_mo) / sizeof(krb5_mo[0]),
_gsskrb5_localname,
_gsskrb5_authorize_localname,
NULL, /* gm_display_name_ext */
NULL, /* gm_inquire_name */
NULL, /* gm_get_name_attribute */
_gsskrb5_display_name_ext,
_gsskrb5_inquire_name,
_gsskrb5_get_name_attribute,
NULL, /* gm_set_name_attribute */
NULL, /* gm_delete_name_attribute */
NULL, /* gm_export_name_composite */
_gsskrb5_export_name_composite,
_gsskrb5_duplicate_cred,
_gsskrb5_add_cred_from,
_gsskrb5_store_cred_into,

View File

@@ -169,9 +169,11 @@ import_export_name (OM_uint32 *minor_status,
const gss_buffer_t input_name_buffer,
gss_name_t *output_name)
{
CompositePrincipal *composite;
unsigned char *p;
uint32_t length;
size_t length, sz;
OM_uint32 ret;
int is_composite;
char *name;
if (input_name_buffer->length < 10 + GSS_KRB5_MECHANISM->length)
@@ -181,7 +183,9 @@ import_export_name (OM_uint32 *minor_status,
p = input_name_buffer->value;
if (memcmp(&p[0], "\x04\x01\x00", 3) != 0 ||
if (p[0] != 0x04 ||
(p[1] != 0x01 && p[1] != 0x02) ||
p[2] != 0x00 ||
p[3] != GSS_KRB5_MECHANISM->length + 2 ||
p[4] != 0x06 ||
p[5] != GSS_KRB5_MECHANISM->length ||
@@ -189,6 +193,8 @@ import_export_name (OM_uint32 *minor_status,
GSS_KRB5_MECHANISM->length) != 0)
return GSS_S_BAD_NAME;
is_composite = p[1] == 0x02;
p += 6 + GSS_KRB5_MECHANISM->length;
length = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
@@ -197,6 +203,28 @@ import_export_name (OM_uint32 *minor_status,
if (length > input_name_buffer->length - 10 - GSS_KRB5_MECHANISM->length)
return GSS_S_BAD_NAME;
if (is_composite) {
if ((composite = calloc(1, sizeof(*composite))) == NULL) {
*minor_status = ENOMEM;
return GSS_S_FAILURE;
}
ret = decode_CompositePrincipal(p, length, composite, &sz);
if (ret) {
*minor_status = ret;
return GSS_S_FAILURE;
}
if (sz != length) {
free_CompositePrincipal(composite);
free(composite);
*minor_status = EINVAL;
return GSS_S_FAILURE;
}
*output_name = (void *)composite;
return GSS_S_COMPLETE;
}
name = malloc(length + 1);
if (name == NULL) {
*minor_status = ENOMEM;
@@ -207,7 +235,6 @@ import_export_name (OM_uint32 *minor_status,
ret = parse_krb5_name(minor_status, context, name, output_name);
free(name);
return ret;
}
@@ -239,7 +266,8 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_import_name
context,
input_name_buffer,
output_name);
else if (gss_oid_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) {
else if (gss_oid_equal(input_name_type, GSS_C_NT_EXPORT_NAME) ||
gss_oid_equal(input_name_type, GSS_C_NT_COMPOSITE_EXPORT)) {
return import_export_name(minor_status,
context,
input_name_buffer,

View File

@@ -0,0 +1,692 @@
/*
* 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
*
* 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 specific PAC buffers
* - Add support for getting PAC login fields, including SIDs (one at a time)
* - Add support for CAMMAC?
*/
static int
attr_eq(gss_buffer_t attr, const char *aname, size_t aname_len)
{
const char *s;
if (attr->length < aname_len)
return 0;
/* Note: `s' is not NUL-terminated */
s = ((const char *)attr->value) + attr->length - aname_len;
return strncmp(s, aname, aname_len) == 0 &&
(attr->length == aname_len || s[aname_len] == '#');
}
#define ATTR_EQ(a, an) (attr_eq(a, an, sizeof(an) - 1))
/* Split attribute into prefix, suffix, and fragment. See RFC6680. */
static void
split_attr(restrict gss_const_buffer_t orig,
restrict gss_buffer_t prefix,
restrict gss_buffer_t attr,
restrict 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 = p ? memchr(p + 1, ' ', orig->length) : NULL) {
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;
}
}
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_get_name_attribute(OM_uint32 *minor_status,
gss_name_t gname,
gss_buffer_t original_attr,
int *authenticated,
int *complete,
gss_buffer_t value,
gss_buffer_t display_value,
int *more)
{
krb5_const_principal name = (krb5_const_principal)gname;
krb5_error_code kret = 0;
gss_buffer_desc prefix, attr, frag;
PrincipalNameAttrs *nameattrs = name->nameattrs;
PrincipalNameAttrSrc *src = nameattrs ? nameattrs->source : NULL;
EncTicketPart *ticket = NULL;
EncKDCRepPart *kdcrep = NULL;
int is_urn;
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:
break;
}
*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;
}
split_attr(original_attr, &prefix, &attr, &frag, &is_urn);
if (prefix.length || !is_urn)
return GSS_S_UNAVAILABLE;
if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "realm")) {
/*
* Output the principal's realm. The value and display value are the
* same in this case.
*/
if (authenticated && nameattrs && nameattrs->authenticated)
*authenticated = 1;
if (complete)
*complete = 1;
if (value) {
if ((value->value = strdup(name->realm)) == NULL)
goto enomem;
value->length = strlen(value->value);
}
if (display_value) {
if ((display_value->value = strdup(name->realm)) == NULL)
goto enomem;
display_value->length = strlen(display_value->value);
}
return GSS_S_COMPLETE;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "peer-realm") &&
nameattrs && nameattrs->peer_realm) {
/*
* Output the peer's realm. The value and display value are the
* same in this case.
*/
if (authenticated)
*authenticated = 1;
if (complete)
*complete = 1;
if (value) {
if ((value->value = strdup(nameattrs->peer_realm[0])) == NULL)
goto enomem;
value->length = strlen(value->value);
}
if (display_value) {
if ((display_value->value =
strdup(nameattrs->peer_realm[0])) == NULL)
goto enomem;
display_value->length = strlen(display_value->value);
}
return GSS_S_COMPLETE;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "name-ncomp")) {
unsigned char n;
if (authenticated && nameattrs && nameattrs->authenticated)
*authenticated = 1;
if (complete)
*complete = 1;
if (frag.length == 0) {
if (value) {
if ((value->value = malloc(sizeof(size_t))) == NULL)
goto enomem;
*((size_t *)value->value) = name->name.name_string.len;
value->length = sizeof(size_t);
}
if (display_value) {
char *s = NULL;
if (asprintf(&s, "%u",
(unsigned int)name->name.name_string.len) == -1 ||
s == NULL)
goto enomem;
display_value->value = s;
display_value->length = strlen(display_value->value);
}
return GSS_S_COMPLETE;
} /* else caller wants a component */
if (frag.length != 1 ||
((const char *)frag.value)[0] < '0' ||
((const char *)frag.value)[0] > '9') {
*minor_status = EINVAL;
return GSS_S_UNAVAILABLE;
}
n = ((const char *)frag.value)[0] - '0';
if (n >= name->name.name_string.len) {
*minor_status = EINVAL;
return GSS_S_UNAVAILABLE;
}
/* The value and the display value are the same in this case */
if (value) {
if ((value->value = strdup(name->name.name_string.val[n])) == NULL)
goto enomem;
value->length = strlen(name->name.name_string.val[n]);
}
if (display_value) {
if ((display_value->value =
strdup(name->name.name_string.val[n])) == NULL)
goto enomem;
if (display_value)
display_value->length = strlen(name->name.name_string.val[n]);
}
return GSS_S_COMPLETE;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"canonical-name") && src) {
krb5_principal p = NULL;
krb5_context context;
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 (ticket) {
kret = _krb5_principalname2krb5_principal(context, &p,
ticket->cname,
ticket->crealm);
} 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;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "authz-data") &&
frag.length &&
((nameattrs && nameattrs->authenticator_ad) ||
(ticket && ticket->authorization_data))) {
krb5_context context;
krb5_data data;
char *s, *end;
int64_t n;
/* Output a specific AD element from the ticket or authenticator */
if ((s = strndup(frag.value, frag.length)) == NULL) {
*minor_status = ENOMEM;
return GSS_S_FAILURE;
}
errno = 0;
n = strtoll(s, &end, 10);
free(s);
if (end[0] == '\0' && (errno || n > INT_MAX || n < INT_MIN)) {
*minor_status = ERANGE;
return GSS_S_FAILURE;
}
if (end[0] != '\0') {
*minor_status = EINVAL;
return GSS_S_FAILURE;
}
if (authenticated)
*authenticated = 0;
if (complete)
*complete = 1;
GSSAPI_KRB5_INIT(&context);
kret = ENOENT;
if (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, ticket->authorization_data,
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;
} else if ((ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"ticket-authz-data#pac") ||
ATTR_EQ(&attr, "urn:mspac:")) &&
ticket && ticket->authorization_data) {
krb5_context context;
krb5_data data;
/*
* In MIT the attribute for the whole PAC is "urn:mspac:".
*
* TBD: Add support for attributes for specific PAC buffers, like MIT.
*/
GSSAPI_KRB5_INIT(&context);
if (authenticated)
*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;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"ticket-authz-data#kdc-issued") &&
ticket &&
ticket->authorization_data) {
krb5_context context;
krb5_data data;
GSSAPI_KRB5_INIT(&context);
if (authenticated)
*authenticated = nameattrs->kdc_issued_verified;
if (complete)
*complete = 1;
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;
}
*minor_status = kret;
if (kret == ENOENT)
return GSS_S_UNAVAILABLE;
return kret == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"ticket-authz-data") &&
frag.length == 0 && ticket && ticket->authorization_data) {
size_t sz;
/* Just because it's in the Ticket doesn't make it authenticated */
if (authenticated)
*authenticated = 0;
if (complete)
*complete = 1;
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;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"authenticator-authz-data") &&
nameattrs && nameattrs->authenticator_ad) {
size_t sz;
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;
} else if (ATTR_EQ(&attr, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"transit-path") &&
(ticket || (nameattrs && nameattrs->transited))) {
size_t sz;
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;
}
return GSS_S_UNAVAILABLE;
enomem:
if (value)
gss_release_buffer(minor_status, value);
*minor_status = ENOMEM;
return GSS_S_FAILURE;
}
static OM_uint32
add_urn(OM_uint32 *minor_status,
gss_name_t name,
gss_buffer_t urn,
gss_buffer_set_t *attrs)
{
OM_uint32 major;
major = _gsskrb5_get_name_attribute(minor_status, name, urn,
0, 0, 0, 0, 0);
if (major == GSS_S_COMPLETE) {
major = gss_add_buffer_set_member(minor_status, urn, attrs);
if (major)
return major;
}
if (major == GSS_S_UNAVAILABLE)
return GSS_S_COMPLETE;
return major;
}
#define ADD_URN(l) \
do if (major == GSS_S_COMPLETE) { \
if (strncmp(l, "urn:", sizeof("urn:") - 1) == 0) { \
urn.value = l; \
urn.length = sizeof(l) - 1; \
} else { \
urn.value = GSS_KRB5_NAME_ATTRIBUTE_BASE_URN l; \
urn.length = sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN l) - 1;\
} \
major = add_urn(minor_status, name, &urn, attrs); \
} while (0)
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)
{
OM_uint32 major = GSS_S_COMPLETE;
gss_buffer_desc urn;
krb5_error_code ret;
krb5_context context;
char lname[32];
GSSAPI_KRB5_INIT(&context);
*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;
ADD_URN("realm");
ADD_URN("name-ncomp");
ADD_URN("name-ncomp#0");
ADD_URN("name-ncomp#1");
ADD_URN("name-ncomp#2");
ADD_URN("name-ncomp#3");
ADD_URN("name-ncomp#4");
ADD_URN("name-ncomp#5");
ADD_URN("name-ncomp#6");
ADD_URN("name-ncomp#7");
ADD_URN("name-ncomp#8");
ADD_URN("name-ncomp#9");
ADD_URN("peer-realm");
ADD_URN("ticket-authz-data#pac");
ADD_URN("urn:mspac:");
ADD_URN("authenticator-authz-data"); /* XXX Add fragments? */
ADD_URN("ticket-authz-data"); /* XXX Add fragments? */
ADD_URN("authz-data");
ADD_URN("transit-path");
ADD_URN("canonical-name");
major = GSS_S_COMPLETE;
lname[0] = '\0';
ret = krb5_aname_to_localname(context, (void *)name,
sizeof(lname) - 1, lname);
if (ret == 0 && lname[0] != '\0')
major = gss_add_buffer_set_member(minor_status,
GSS_C_ATTR_LOCAL_LOGIN_USER, attrs);
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;
}

View File

@@ -1,5 +1,6 @@
EXPORTS
__gss_c_nt_anonymous_oid_desc DATA
__gss_c_nt_composite_export_oid_desc DATA
__gss_c_nt_export_name_oid_desc DATA
__gss_c_nt_hostbased_service_oid_desc DATA
__gss_c_nt_hostbased_service_x_oid_desc DATA

View File

@@ -31,6 +31,7 @@
static OM_uint32
_gss_import_export_name(OM_uint32 *minor_status,
const gss_buffer_t input_name_buffer,
const gss_OID name_type,
gss_name_t *output_name)
{
OM_uint32 major_status;
@@ -65,6 +66,24 @@ _gss_import_export_name(OM_uint32 *minor_status,
p += 2;
len -= 2;
/*
* If the name token is a composite token (TOK_ID 0x04 0x02) then per
* RFC6680 everything after that is implementation-specific. This
* mech-glue is pluggable however, so we need the format of the rest of
* the header to be stable, otherwise we couldn't reliably determine
* what mechanism the token is for and we'd have to try all of them.
*
* So... we keep the same format for the exported composite name token
* as for normal exported name tokens (see RFC2743, section 3.2), with
* the TOK_ID 0x04 0x02, but only up to the mechanism OID. We don't
* enforce that there be a NAME_LEN in the exported composite name
* token, or that it match the length of the remainder of the token.
*
* FYI, at least one out-of-tree mechanism implements exported
* composite name tokens as the same as exported name tokens with
* attributes appended and the NAME_LEN not modified to match.
*/
/*
* Get the mech length and the name length and sanity
* check the size of of the buffer.
@@ -107,17 +126,19 @@ _gss_import_export_name(OM_uint32 *minor_status,
mech_oid.elements = p;
if (len < t + 4)
return (GSS_S_BAD_NAME);
p += t;
len -= t;
if (!composite) {
if (len < t + 4)
return (GSS_S_BAD_NAME);
p += t;
len -= t;
t = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
p += 4;
len -= 4;
t = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
p += 4;
len -= 4;
if (!composite && len != t)
return (GSS_S_BAD_NAME);
if (len != t)
return (GSS_S_BAD_NAME);
}
m = __gss_get_mechanism(&mech_oid);
if (!m || !m->gm_import_name)
@@ -127,7 +148,7 @@ _gss_import_export_name(OM_uint32 *minor_status,
* Ask the mechanism to import the name.
*/
major_status = m->gm_import_name(minor_status,
input_name_buffer, GSS_C_NT_EXPORT_NAME, &new_canonical_name);
input_name_buffer, name_type, &new_canonical_name);
if (major_status != GSS_S_COMPLETE) {
_gss_mg_error(m, *minor_status);
return major_status;
@@ -156,6 +177,7 @@ _gss_import_export_name(OM_uint32 *minor_status,
* - GSS_C_NT_USER_NAME
* - GSS_C_NT_HOSTBASED_SERVICE
* - GSS_C_NT_EXPORT_NAME
* - GSS_C_NT_COMPOSITE_EXPORT
* - GSS_C_NT_ANONYMOUS
* - GSS_KRB5_NT_PRINCIPAL_NAME
*
@@ -202,9 +224,10 @@ gss_import_name(OM_uint32 *minor_status,
* the mechanism and then import it as an MN. See RFC 2743
* section 3.2 for a description of the format.
*/
if (gss_oid_equal(name_type, GSS_C_NT_EXPORT_NAME)) {
return _gss_import_export_name(minor_status,
input_name_buffer, output_name);
if (gss_oid_equal(name_type, GSS_C_NT_EXPORT_NAME) ||
gss_oid_equal(name_type, GSS_C_NT_COMPOSITE_EXPORT)) {
return _gss_import_export_name(minor_status, input_name_buffer,
name_type, output_name);
}

View File

@@ -43,42 +43,286 @@
#include <gssapi.h>
#include <gssapi_krb5.h>
#include <gssapi_spnego.h>
#include <krb5_asn1.h>
#include <err.h>
#include <getarg.h>
static void make_composite_name(CompositePrincipal *, gss_name_t *);
static void assert_attr(gss_name_t, const char *, OM_uint32, gss_buffer_t,
const char *, int, int, int);
static void assert_attr_unavail(gss_name_t, const char *);
static void assert_attr_set(gss_name_t, gss_buffer_set_t);
static void
gss_print_errors (int min_stat)
gss_print_errors(OM_uint32 stat, gss_OID mech)
{
OM_uint32 new_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status_string;
OM_uint32 junk;
OM_uint32 more = 0;
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
OM_uint32 ret;
if (mech) {
junk = gss_oid_to_str(&junk, mech, &buf);
if (junk == GSS_S_COMPLETE)
fprintf(stderr, "mech = %.*s\n", (int)buf.length, buf.value);
gss_release_buffer(&junk, &buf);
}
do {
ret = gss_display_status (&new_stat,
min_stat,
GSS_C_MECH_CODE,
GSS_C_NO_OID,
&msg_ctx,
&status_string);
if (!GSS_ERROR(ret)) {
fprintf (stderr, "%.*s\n", (int)status_string.length,
(char *)status_string.value);
gss_release_buffer (&new_stat, &status_string);
}
} while (!GSS_ERROR(ret) && msg_ctx != 0);
ret = gss_display_status(&junk,
stat,
mech ? GSS_C_MECH_CODE : GSS_C_GSS_CODE,
mech,
&more,
&buf);
if (ret != GSS_S_COMPLETE)
errx(1, "gss_display_status() failed");
fprintf(stderr, "%.*s\n", (int)buf.length, (char *)buf.value);
gss_release_buffer(&junk, &buf);
} while (more);
}
static void
gss_err(int exitval, int status, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 5, 6)))
gss_err(int exitval,
OM_uint32 maj,
OM_uint32 min,
gss_OID mech,
const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vwarnx (fmt, args);
gss_print_errors (status);
vwarnx(fmt, args);
va_end(args);
exit (exitval);
gss_print_errors(maj, GSS_C_NO_OID);
if (mech)
gss_print_errors(min, mech);
exit(exitval);
}
#define MAKE_URN(tail) \
{ sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN tail) - 1, \
GSS_KRB5_NAME_ATTRIBUTE_BASE_URN tail }
/*
* Test RFC6680 name attributes for Kerberos.
*/
static void
check_name_attrs(void)
{
CompositePrincipal p;
EncTicketPart *t;
gss_buffer_desc v = GSS_C_EMPTY_BUFFER;
gss_name_t n;
OM_uint32 maj, min;
int32_t ret;
gss_buffer_desc attrs[] = {
MAKE_URN("realm"),
MAKE_URN("name-ncomp"),
MAKE_URN("name-ncomp#0"),
MAKE_URN("peer-realm"),
MAKE_URN("ticket-authz-data"),
MAKE_URN("transit-path"),
MAKE_URN("canonical-name"),
}; /* Set of attributes we expect to see indicated */
gss_buffer_set_desc attr_set;
size_t i, sz;
memset(&p, 0, sizeof(p));
attr_set.elements = attrs;
/*
* attr_set.count is set in each of the following sections to ever more
* items.
*/
/*
* Testing name attributes is pretty tricky.
*
* Our approach is to construct a composite name, construct an exported
* composite name token for it, import it, then test the gss_inquire_name()
* and gss_get_name_attribute() accessors, and then gss_display_name_ext().
*
* Ideally we'd test the accessors on names imported from query forms with
* gss_import_name(), and on names from established contexts. However,
* that belongs in the test_context program.
*
* TODO: Implement and test gss_set_name_attribute() and
* gss_delete_name_attribute().
*/
/* First construct and test an unauthenticated name */
p.realm = estrdup("TEST.H5L.SE");
p.name.name_type = KRB5_NT_PRINCIPAL;
p.name.name_string.val = ecalloc(1, sizeof(p.name.name_string.val[0]));
p.name.name_string.len = 1;
p.name.name_string.val[0] = estrdup("someuser");
p.nameattrs = NULL;
make_composite_name(&p, &n);
/* Test the attributes we expect it to have */
v.length = sizeof("TEST.H5L.SE") - 1;
v.value = "TEST.H5L.SE";
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "realm", GSS_S_COMPLETE,
&v, "TEST.H5L.SE", 0, 1, 0);
i = 1;
v.length = sizeof(size_t);
v.value = &i;
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "name-ncomp",
GSS_S_COMPLETE, &v, "1", 0, 1, 0);
v.length = sizeof("someuser") - 1;
v.value = "someuser";
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "name-ncomp#0",
GSS_S_COMPLETE, &v, "someuser", 0, 1, 0);
attr_set.count = 3;
assert_attr_set(n, &attr_set);
/* Check that it does not have prefixed attributes */
assert_attr_unavail(n, "whatever " GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"realm");
assert_attr_unavail(n, "whatever " GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"name-ncomp");
assert_attr_unavail(n, "whatever " GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"name-ncomp#0");
assert_attr_unavail(n, "what ever " GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"name-ncomp#0");
/* Check that it does not have various other supported attributes */
assert_attr_unavail(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "peer-realm");
assert_attr_unavail(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "name-ncomp#1");
assert_attr_unavail(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "canonical-name");
assert_attr_unavail(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"ticket-authz-data#pac");
assert_attr_unavail(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN
"ticket-authz-data");
assert_attr_unavail(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "transit-path");
/* Exercise URN parser */
assert_attr_unavail(n, "urn:whatever");
assert_attr_unavail(n, "urn:whatever#");
assert_attr_unavail(n, "urn:what#ever");
assert_attr_unavail(n, "#");
assert_attr_unavail(n, "#whatever");
assert_attr_unavail(n, "whatever");
assert_attr_unavail(n, "what ever");
assert_attr_unavail(n, "what ever#");
/* Now test an authenticated name */
gss_release_name(&min, &n);
p.nameattrs = ecalloc(1, sizeof(p.nameattrs[0]));
p.nameattrs->authenticated = 1;
make_composite_name(&p, &n);
v.length = sizeof("TEST.H5L.SE") - 1;
v.value = "TEST.H5L.SE";
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "realm", GSS_S_COMPLETE,
&v, "TEST.H5L.SE", 1, 1, 0);
i = 1;
v.length = sizeof(size_t);
v.value = &i;
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "name-ncomp",
GSS_S_COMPLETE, &v, "1", 1, 1, 0);
v.length = sizeof("someuser") - 1;
v.value = "someuser";
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "name-ncomp#0",
GSS_S_COMPLETE, &v, "someuser", 1, 1, 0);
assert_attr_set(n, &attr_set);
/* Now add a peer realm */
gss_release_name(&min, &n);
p.nameattrs->peer_realm = ecalloc(1, sizeof(p.nameattrs->peer_realm[0]));
p.nameattrs->peer_realm[0] = estrdup("FOO.TEST.H5L.SE");
make_composite_name(&p, &n);
v.length = sizeof("FOO.TEST.H5L.SE") - 1;
v.value = "FOO.TEST.H5L.SE";
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "peer-realm",
GSS_S_COMPLETE, &v, "FOO.TEST.H5L.SE", 1, 1, 0);
attr_set.count = 4;
assert_attr_set(n, &attr_set);
/* Now add canonical name and an authz-data element */
gss_release_name(&min, &n);
p.nameattrs->source = ecalloc(1, sizeof(p.nameattrs->source[0]));
p.nameattrs->source->element = choice_PrincipalNameAttrSrc_enc_ticket_part;
t = &p.nameattrs->source->u.enc_ticket_part;
t->cname.name_type = KRB5_NT_PRINCIPAL;
t->cname.name_string.val = ecalloc(1, sizeof(t->cname.name_string.val[0]));
t->crealm = estrdup("TEST.H5L.SE");
t->cname.name_string.len = 1;
t->cname.name_string.val[0] = estrdup("realusername");
t->authorization_data = ecalloc(1, sizeof(t->authorization_data[0]));
t->authorization_data->val =
ecalloc(1, sizeof(t->authorization_data->val[0]));
t->authorization_data->len = 1;
t->authorization_data->val[0].ad_type =
KRB5_AUTHDATA_ON_BEHALF_OF; /* whatever */
t->authorization_data->val[0].ad_data.data =
estrdup("foobar@TEST.H5L.SE");
t->authorization_data->val[0].ad_data.length =
sizeof("foobar@TEST.H5L.SE") - 1;
make_composite_name(&p, &n);
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "canonical-name",
GSS_S_COMPLETE, GSS_C_NO_BUFFER, "realusername@TEST.H5L.SE", 1,
1, 0);
ASN1_MALLOC_ENCODE(AuthorizationData, v.value, v.length,
t->authorization_data, &sz, ret);
if (ret)
errx(1, "Failed to encode AuthorizationData");
assert_attr(n, GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "ticket-authz-data",
GSS_S_COMPLETE, &v, NULL, 0, 1, 0);
free(v.value);
attr_set.count = 7;
assert_attr_set(n, &attr_set);
gss_release_name(&min, &n);
free_CompositePrincipal(&p);
/*
* Test gss_display_name_ext() with a host-based service principal
* "host/somehost.test.h5l.se@TEST.H5L.SE".
*
* Where gss_display_name() would display this as a Kerberos principal
* name, gss_display_name_ext() with GSS_C_NT_HOSTBASED_SERVICE should
* display it as "host@somehost.test.h5l.se".
*/
p.realm = estrdup("TEST.H5L.SE");
p.name.name_type = KRB5_NT_SRV_HST;
p.name.name_string.val = ecalloc(2, sizeof(p.name.name_string.val[0]));
p.name.name_string.len = 2;
p.name.name_string.val[0] = estrdup("host");
p.name.name_string.val[1] = estrdup("somehost.test.h5l.se");
p.nameattrs = NULL;
make_composite_name(&p, &n);
maj = gss_display_name_ext(&min, n, GSS_C_NT_HOSTBASED_SERVICE, &v);
if (maj)
gss_err(1, maj, min, GSS_KRB5_MECHANISM, "display name ext");
if (v.length != sizeof("host@somehost.test.h5l.se") - 1 ||
strncmp(v.value, "host@somehost.test.h5l.se", v.length) != 0)
errx(1, "display name ext");
gss_release_buffer(&min, &v);
gss_release_name(&min, &n);
free_CompositePrincipal(&p);
/*
* TODO:
*
* - test URN fragments for access to specific authorization data element
* types
* - test GSS_C_ATTR_LOCAL_LOGIN_USER support (requires configuration or
* that we register a plugin here)
*/
}
static int version_flag = 0;
@@ -145,7 +389,7 @@ main(int argc, char **argv)
GSS_C_NT_HOSTBASED_SERVICE,
&name);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "import name error");
gss_err(1, maj_stat, min_stat, GSS_C_NO_OID, "import name error");
free(str);
if (anon_flag)
@@ -158,13 +402,13 @@ main(int argc, char **argv)
mech_oid,
&MNname);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "canonicalize name error");
gss_err(1, maj_stat, min_stat, mech_oid, "canonicalize name error");
maj_stat = gss_export_name(&min_stat,
MNname,
&name_buffer);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "export name error (KRB5)");
gss_err(1, maj_stat, min_stat, mech_oid, "export name error");
/*
* Import the exported name and compare
@@ -174,13 +418,13 @@ main(int argc, char **argv)
GSS_C_NT_EXPORT_NAME,
&MNname2);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "import name error (exported KRB5 name)");
gss_err(1, maj_stat, min_stat, mech_oid, "export name error");
maj_stat = gss_compare_name(&min_stat, MNname, MNname2, &equal);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_compare_name");
if (equal == anon_flag)
gss_err(1, maj_stat, min_stat, mech_oid, "compare name error");
if (equal && anon_flag)
errx(1, "names %s equal", anon_flag ? "incorrectly" : "not");
gss_release_name(&min_stat, &MNname2);
@@ -205,13 +449,13 @@ main(int argc, char **argv)
GSS_C_NO_OID,
&name);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "import (no oid) name error");
gss_err(1, maj_stat, min_stat, NULL, "import (no oid) name error");
maj_stat = gss_import_name(&min_stat, &name_buffer,
GSS_KRB5_NT_USER_NAME,
&MNname);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "import (krb5 mn) name error");
gss_err(1, maj_stat, min_stat, NULL, "import (krb5 mn) name error");
free(str);
@@ -230,14 +474,16 @@ main(int argc, char **argv)
GSS_SPNEGO_MECHANISM,
&MNname);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "canonicalize name error");
gss_err(1, maj_stat, min_stat, GSS_SPNEGO_MECHANISM,
"canonicalize name error");
maj_stat = gss_export_name(&maj_stat,
MNname,
&name_buffer);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "export name error (SPNEGO)");
gss_err(1, maj_stat, min_stat, GSS_SPNEGO_MECHANISM,
"export name error (SPNEGO)");
gss_release_name(&min_stat, &MNname);
gss_release_buffer(&min_stat, &name_buffer);
@@ -253,29 +499,177 @@ main(int argc, char **argv)
maj_stat = gss_import_name(&min_stat, &name_buffer,
GSS_C_NT_ANONYMOUS, &name);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "import (anon) name error");
gss_err(1, maj_stat, min_stat, GSS_C_NO_OID,
"import (anon) name error");
maj_stat = gss_canonicalize_name(&min_stat, name,
GSS_SANON_X25519_MECHANISM,
&MNname);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "canonicalize (anon) name error");
gss_err(1, maj_stat, min_stat, GSS_SANON_X25519_MECHANISM,
"canonicalize (anon) name error");
maj_stat = gss_display_name(&min_stat, MNname,
&name_buffer, &name_type);
if (maj_stat != GSS_S_COMPLETE)
gss_err(1, min_stat, "display_name (anon) name error");
gss_err(1, maj_stat, min_stat, GSS_SANON_X25519_MECHANISM,
"display_name (anon) name error");
if (!gss_oid_equal(name_type, GSS_C_NT_ANONYMOUS))
gss_err(1, 0, "display name type not anonymous");
errx(1, "display name type not anonymous");
if (memcmp(name_buffer.value, "WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS",
sizeof("WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS") - 1) != 0)
gss_err(1, 0, "display name string not well known anonymous name");
errx(1, "display name string not well known anonymous name");
gss_release_name(&min_stat, &MNname);
gss_release_name(&min_stat, &name);
gss_release_buffer(&min_stat, &name_buffer);
}
check_name_attrs();
return 0;
}
/* Copied from _gsskrb5_export_name_composite() */
static void
export_name_composite(CompositePrincipal *name, gss_buffer_t exported_name)
{
gss_buffer_desc inner = GSS_C_EMPTY_BUFFER;
unsigned char *buf;
int32_t ret;
size_t sz;
ASN1_MALLOC_ENCODE(CompositePrincipal, inner.value, inner.length,
(void *)name, &sz, ret);
if (ret)
errx(1, "Failed to encode exported composite name token");
exported_name->length = 10 + inner.length + GSS_KRB5_MECHANISM->length;
exported_name->value = malloc(exported_name->length);
if (exported_name->value == NULL)
errx(1, "Failed to allocate exported composite name token");
/* 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);
}
static void
make_composite_name(CompositePrincipal *princ, gss_name_t *n)
{
gss_buffer_desc token, exported;
OM_uint32 maj, min;
export_name_composite(princ, &token);
maj = gss_import_name(&min, &token, GSS_C_NT_COMPOSITE_EXPORT, n);
if (maj)
gss_err(1, maj, min, GSS_KRB5_MECHANISM, "import composite name");
maj = gss_export_name_composite(&min, *n, &exported);
if (maj)
gss_err(1, maj, min, GSS_KRB5_MECHANISM, "export composite name");
if (token.length != exported.length ||
memcmp(token.value, exported.value, token.length) != 0)
errx(1, "import/export composite token disagreement");
gss_release_buffer(&min, &exported);
free(token.value); /* Use free because we allocated this one */
}
static void
assert_attr(gss_name_t n,
const char *aname,
OM_uint32 exp_maj,
gss_buffer_t exp_v,
const char *exp_dv,
int exp_authenticated,
int exp_complete,
int exp_multivalued)
{
gss_buffer_desc dv = GSS_C_EMPTY_BUFFER;
gss_buffer_desc v = GSS_C_EMPTY_BUFFER;
gss_buffer_desc a;
OM_uint32 maj, min;
int authenticated, complete, more;
a.value = (void*)(uintptr_t)aname;
a.length = strlen(aname);
more = 0;
maj = gss_get_name_attribute(&min, n, &a, &authenticated, &complete, &v,
&dv, &more);
if (maj != GSS_S_COMPLETE && maj != exp_maj)
gss_err(1, maj, min, GSS_KRB5_MECHANISM,
"import composite name error");
if (maj == GSS_S_COMPLETE && maj != exp_maj)
errx(1, "unexpected name attribute %s", aname);
if (maj == GSS_S_COMPLETE) {
if (exp_v &&
(v.length != exp_v->length ||
memcmp(v.value, exp_v->value, exp_v->length) != 0))
errx(1, "import composite name: wrong %s value", aname);
if (exp_dv &&
(dv.length != strlen(exp_dv) ||
strncmp(dv.value, exp_dv, dv.length) != 0))
errx(1, "import composite name: wrong %s display value "
"(wanted %s, got %.*s)", aname, exp_dv,
(int)dv.length, (char *)dv.value);
if (authenticated != exp_authenticated)
errx(1, "import composite name: %s incorrectly marked "
"%sauthenticated", aname, authenticated ? "" : "un");
if (complete != exp_complete)
errx(1, "import composite name: %s incorrectly marked "
"%scomplete", aname, complete ? "" : "in");
if (more != exp_multivalued)
errx(1, "import composite name: %s incorrectly marked "
"%s-valued", aname, more ? "multi" : "single");
}
gss_release_buffer(&min, &dv);
gss_release_buffer(&min, &v);
}
static void
assert_attr_unavail(gss_name_t n, const char *aname)
{
assert_attr(n, aname, GSS_S_UNAVAILABLE, GSS_C_NO_BUFFER, NULL, 0, 0, 0);
}
static void
assert_attr_set(gss_name_t n, gss_buffer_set_t exp_as)
{
OM_uint32 maj, min;
gss_buffer_set_t as = NULL;
gss_OID MN_mech = GSS_C_NO_OID;
size_t i;
int name_is_MN = 0;
maj = gss_inquire_name(&min, n, &name_is_MN, &MN_mech, &as);
if (maj)
gss_err(1, maj, min, MN_mech, "inquire name");
for (i = 0; i < as->count && i < exp_as->count; i++) {
if (as->elements[i].length != exp_as->elements[i].length ||
memcmp(as->elements[i].value, exp_as->elements[i].value,
as->elements[i].length) != 0)
errx(1, "attribute sets differ");
}
if (i < as->count)
errx(1, "more attributes indicated than expected");
if (i < exp_as->count)
errx(1, "fewer attributes indicated than expected");
gss_release_buffer_set(&min, &as);
}

View File

@@ -4,6 +4,7 @@ HEIMDAL_GSS_2.0 {
global:
# __gss_c_nt_anonymous;
__gss_c_nt_anonymous_oid_desc;
__gss_c_nt_composite_export_oid_desc;
__gss_c_nt_export_name_oid_desc;
__gss_c_nt_hostbased_service_oid_desc;
__gss_c_nt_hostbased_service_x_oid_desc;