hx509: Revamp name handling ahead of new SAN types

This commit is contained in:
Nicolas Williams
2021-01-15 11:57:10 -06:00
parent 989fd1199b
commit b193d75a15
4 changed files with 223 additions and 172 deletions

View File

@@ -1020,22 +1020,31 @@ hx509_name_is_null_p(const hx509_name name)
* Note that we cannot handle embedded NULs because of Heimdal's representation * Note that we cannot handle embedded NULs because of Heimdal's representation
* of ASN.1 strings as C strings. * of ASN.1 strings as C strings.
*/ */
struct rk_strpool * int
_hx509_unparse_KRB5PrincipalName(struct rk_strpool *strpool, heim_any *value) _hx509_unparse_KRB5PrincipalName(hx509_context context,
struct rk_strpool **strpool,
heim_any *value)
{ {
KRB5PrincipalName kn; KRB5PrincipalName kn;
size_t len; size_t len;
int ret; int ret;
ret = decode_KRB5PrincipalName(value->data, value->length, &kn, &len); ret = decode_KRB5PrincipalName(value->data, value->length, &kn, &len);
if (ret == 0) if (ret == 0 &&
strpool = _hx509_unparse_kerberos_name(strpool, &kn); (*strpool = _hx509_unparse_kerberos_name(*strpool, &kn)) == NULL)
ret = hx509_enomem(context);
free_KRB5PrincipalName(&kn); free_KRB5PrincipalName(&kn);
if (ret == 0 && strpool && (value->length != len)) if (ret == 0 && (value->length != len) &&
strpool = rk_strpoolprintf(strpool, " <garbage>"); (*strpool = rk_strpoolprintf(*strpool, " <garbage>")) == NULL)
if (ret) ret = hx509_enomem(context);
return rk_strpoolprintf(strpool, "<error-decoding-PrincipalName"); if (ret) {
return strpool; rk_strpoolfree(*strpool);
*strpool = rk_strpoolprintf(NULL,
"<error-decoding-PrincipalName");
hx509_set_error_string(context, 0, ret,
"Failed to decode PermanentIdentifier");
}
return ret;
} }
struct rk_strpool * struct rk_strpool *
@@ -1097,19 +1106,74 @@ _hx509_unparse_kerberos_name(struct rk_strpool *strpool, KRB5PrincipalName *kn)
return strpool; return strpool;
} }
struct rk_strpool * int
hx509_unparse_utf8_string_name(struct rk_strpool *strpool, heim_any *value) _hx509_unparse_utf8_string_name(hx509_context context,
struct rk_strpool **strpool,
heim_any *value)
{ {
PKIXXmppAddr us; PKIXXmppAddr us;
size_t size; size_t size;
int ret;
if (decode_PKIXXmppAddr(value->data, value->length, &us, &size)) ret = decode_PKIXXmppAddr(value->data, value->length, &us, &size);
return rk_strpoolprintf(strpool, "<decode-error>"); if (ret == 0 &&
strpool = rk_strpoolprintf(strpool, "%s", us); (*strpool = rk_strpoolprintf(*strpool, "%s", us)) == NULL)
ret = hx509_enomem(context);
if (ret) {
rk_strpoolfree(*strpool);
*strpool = rk_strpoolprintf(NULL,
"<error-decoding-UTF8String-SAN>");
hx509_set_error_string(context, 0, ret,
"Failed to decode UTF8String SAN");
}
free_PKIXXmppAddr(&us); free_PKIXXmppAddr(&us);
return strpool; return ret;
} }
int
_hx509_unparse_ia5_string_name(hx509_context context,
struct rk_strpool **strpool,
heim_any *value)
{
SRVName us;
size_t size;
int ret;
ret = decode_SRVName(value->data, value->length, &us, &size);
if (ret == 0) {
rk_strpoolfree(*strpool);
*strpool = rk_strpoolprintf(NULL,
"<error-decoding-IA5String-SAN>");
hx509_set_error_string(context, 0, ret,
"Failed to decode UTF8String SAN");
return ret;
}
*strpool = rk_strpoolprintf(*strpool, "%.*s",
(int)us.length, (char *)us.data);
free_SRVName(&us);
return ret;
}
typedef int (*other_unparser_f)(hx509_context,
struct rk_strpool **,
heim_any *);
struct {
const heim_oid *oid;
const char *friendly_name;
other_unparser_f f;
} o_unparsers[] = {
{ &asn1_oid_id_pkinit_san,
"KerberosPrincipalName",
_hx509_unparse_KRB5PrincipalName },
{ &asn1_oid_id_pkix_on_xmppAddr,
"XMPPName",
_hx509_unparse_utf8_string_name },
{ &asn1_oid_id_pkinit_ms_san,
"MSFTKerberosPrincipalName",
_hx509_unparse_utf8_string_name },
};
/** /**
* Unparse the hx509 name in name into a string. * Unparse the hx509 name in name into a string.
* *
@@ -1123,35 +1187,62 @@ hx509_unparse_utf8_string_name(struct rk_strpool *strpool, heim_any *value)
HX509_LIB_FUNCTION int HX509_LIB_CALL HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_general_name_unparse(GeneralName *name, char **str) hx509_general_name_unparse(GeneralName *name, char **str)
{
hx509_context context;
int ret;
if ((ret = hx509_context_init(&context)))
return ret;
return hx509_general_name_unparse2(context, name, str);
}
/**
* Unparse the hx509 name in name into a string.
*
* @param context hx509 library context
* @param name the name to print
* @param str an allocated string returns the name in string form
*
* @return An hx509 error code, see hx509_get_error_string().
*
* @ingroup hx509_name
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_general_name_unparse2(hx509_context context,
GeneralName *name,
char **str)
{ {
struct rk_strpool *strpool = NULL; struct rk_strpool *strpool = NULL;
int ret = 0;
*str = NULL; *str = NULL;
switch (name->element) { switch (name->element) {
case choice_GeneralName_otherName: { case choice_GeneralName_otherName: {
size_t i;
char *oid; char *oid;
hx509_oid_sprint(&name->u.otherName.type_id, &oid);
if (oid == NULL) ret = hx509_oid_sprint(&name->u.otherName.type_id, &oid);
return ENOMEM; if (ret == 0)
strpool = rk_strpoolprintf(strpool, "otherName: %s ", oid); strpool = rk_strpoolprintf(strpool, "otherName: %s ", oid);
if (der_heim_oid_cmp(&name->u.otherName.type_id, if (strpool == NULL)
&asn1_oid_id_pkinit_san) == 0) { ret = ENOMEM;
strpool =
_hx509_unparse_KRB5PrincipalName(strpool, for (i = 0; ret == 0 && i < sizeof(o_unparsers)/sizeof(o_unparsers[0]); i++) {
&name->u.otherName.value); if (der_heim_oid_cmp(&name->u.otherName.type_id,
} else if (der_heim_oid_cmp(&name->u.otherName.type_id, o_unparsers[i].oid))
&asn1_oid_id_pkix_on_xmppAddr) == 0) { continue;
strpool = rk_strpoolprintf(strpool, "xmppAddr "); strpool = rk_strpoolprintf(strpool, "%s ",o_unparsers[i].friendly_name);
strpool = hx509_unparse_utf8_string_name(strpool, if (strpool == NULL)
&name->u.otherName.value); ret = ENOMEM;
} else if (der_heim_oid_cmp(&name->u.otherName.type_id, if (ret == 0)
&asn1_oid_id_pkinit_ms_san) == 0) { ret = o_unparsers[i].f(context, &strpool, &name->u.otherName.value);
strpool = rk_strpoolprintf(strpool, "pkinitMsSan "); break;
strpool = hx509_unparse_utf8_string_name(strpool, }
&name->u.otherName.value); if (ret == 0 && i == sizeof(o_unparsers)/sizeof(o_unparsers[0])) {
} else { strpool = rk_strpoolprintf(strpool, "<unknown-other-name-type>");
strpool = rk_strpoolprintf(strpool, "<unknown-other-name-type"); ret = ENOTSUP;
} }
free(oid); free(oid);
break; break;
@@ -1169,7 +1260,6 @@ hx509_general_name_unparse(GeneralName *name, char **str)
case choice_GeneralName_directoryName: { case choice_GeneralName_directoryName: {
Name dir; Name dir;
char *s; char *s;
int ret;
memset(&dir, 0, sizeof(dir)); memset(&dir, 0, sizeof(dir));
dir.element = (enum Name_enum)name->u.directoryName.element; dir.element = (enum Name_enum)name->u.directoryName.element;
dir.u.rdnSequence = name->u.directoryName.u.rdnSequence; dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;

View File

@@ -32,6 +32,7 @@
*/ */
#include "hx_locl.h" #include "hx_locl.h"
#include <vis.h>
/** /**
* @page page_print Hx509 printing functions * @page page_print Hx509 printing functions
@@ -40,6 +41,7 @@
*/ */
struct hx509_validate_ctx_data { struct hx509_validate_ctx_data {
hx509_context context;
int flags; int flags;
hx509_vprint_func vprint_func; hx509_vprint_func vprint_func;
void *ctx; void *ctx;
@@ -412,67 +414,6 @@ check_extKeyUsage(hx509_validate_ctx ctx,
return 0; return 0;
} }
static int
check_pkinit_san(hx509_validate_ctx ctx, heim_any *a)
{
KRB5PrincipalName kn;
unsigned i;
size_t size;
int ret;
ret = decode_KRB5PrincipalName(a->data, a->length, &kn, &size);
if (ret) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"Decoding kerberos name in SAN failed: %d", ret);
return 1;
}
if (size != a->length) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"Decoding kerberos name have extra bits on the end");
return 1;
}
/* print kerberos principal, add code to quote / within components */
for (i = 0; i < kn.principalName.name_string.len; i++) {
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s",
kn.principalName.name_string.val[i]);
if (i + 1 < kn.principalName.name_string.len)
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "/");
}
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "@");
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", kn.realm);
free_KRB5PrincipalName(&kn);
return 0;
}
static int
check_utf8_string_san(hx509_validate_ctx ctx, heim_any *a)
{
PKIXXmppAddr jid;
size_t size;
int ret;
ret = decode_PKIXXmppAddr(a->data, a->length, &jid, &size);
if (ret) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"Decoding JID in SAN failed: %d", ret);
return 1;
}
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", jid);
free_PKIXXmppAddr(&jid);
return 0;
}
static int
check_altnull(hx509_validate_ctx ctx, heim_any *a)
{
return 0;
}
static int static int
check_CRLDistributionPoints(hx509_validate_ctx ctx, check_CRLDistributionPoints(hx509_validate_ctx ctx,
struct cert_status *status, struct cert_status *status,
@@ -518,8 +459,13 @@ check_CRLDistributionPoints(hx509_validate_ctx ctx,
char *s; char *s;
GeneralName *name = &dpname.u.fullName.val[j]; GeneralName *name = &dpname.u.fullName.val[j];
ret = hx509_general_name_unparse(name, &s); ret = hx509_general_name_unparse2(ctx->context, name, &s);
if (ret == 0 && s != NULL) { if (ret) {
s = hx509_get_error_string(ctx->context, ret);
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"Unknown DistributionPointName: %s", s);
hx509_free_error_string(s);
} else {
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " %s\n", s); validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " %s\n", s);
free(s); free(s);
} }
@@ -544,19 +490,6 @@ check_CRLDistributionPoints(hx509_validate_ctx ctx,
return 0; return 0;
} }
struct {
const char *name;
const heim_oid *oid;
int (*func)(hx509_validate_ctx, heim_any *);
} altname_types[] = {
{ "pk-init", &asn1_oid_id_pkinit_san, check_pkinit_san },
{ "jabber", &asn1_oid_id_pkix_on_xmppAddr, check_utf8_string_san },
{ "dns-srv", &asn1_oid_id_pkix_on_dnsSRV, check_altnull },
{ "card-id", &asn1_oid_id_uspkicommon_card_id, check_altnull },
{ "Microsoft NT-PRINCIPAL-NAME", &asn1_oid_id_pkinit_ms_san, check_utf8_string_san }
};
static int static int
check_altName(hx509_validate_ctx ctx, check_altName(hx509_validate_ctx ctx,
struct cert_status *status, struct cert_status *status,
@@ -591,48 +524,21 @@ check_altName(hx509_validate_ctx ctx,
} }
for (i = 0; i < gn.len; i++) { for (i = 0; i < gn.len; i++) {
switch (gn.val[i].element) { char *s;
case choice_GeneralName_otherName: {
unsigned j;
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, ret = hx509_general_name_unparse2(ctx->context, &gn.val[i], &s);
"%sAltName otherName ", name); if (ret) {
s = hx509_get_error_string(ctx->context, ret);
for (j = 0; j < sizeof(altname_types)/sizeof(altname_types[0]); j++) { validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
if (der_heim_oid_cmp(altname_types[j].oid, "Error unparsing GeneralName: %s\n", s);
&gn.val[i].u.otherName.type_id) != 0) hx509_free_error_string(s);
continue; return 1;
}
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s: ", validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\t%s\n", s);
altname_types[j].name); free(s);
(*altname_types[j].func)(ctx, &gn.val[i].u.otherName.value);
break;
}
if (j == sizeof(altname_types)/sizeof(altname_types[0])) {
hx509_oid_print(&gn.val[i].u.otherName.type_id,
validate_vprint, ctx);
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " unknown");
}
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n");
break;
}
default: {
char *s;
ret = hx509_general_name_unparse(&gn.val[i], &s);
if (ret) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"ret = %d unparsing GeneralName\n", ret);
return 1;
}
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s\n", s);
free(s);
break;
}
}
} }
free_GeneralNames(&gn); free_GeneralNames(&gn);
return 0; return 0;
} }
@@ -737,14 +643,59 @@ check_authorityInfoAccess(hx509_validate_ctx ctx,
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
"\ttype: "); "\ttype: ");
hx509_oid_print(&aia.val[i].accessMethod, validate_vprint, ctx); hx509_oid_print(&aia.val[i].accessMethod, validate_vprint, ctx);
hx509_general_name_unparse(&aia.val[i].accessLocation, &str); ret = hx509_general_name_unparse2(ctx->context,
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, &aia.val[i].accessLocation, &str);
"\n\tdirname: %s\n", str); if (ret) {
free(str); str = hx509_get_error_string(ctx->context, ret);
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"Error unparsing AuthorityInfoAccessSyntax "
"accessLocation: %s", str);
hx509_free_error_string(str);
} else {
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
"\n\tdirname: %s\n", str);
free(str);
}
} }
free_AuthorityInfoAccessSyntax(&aia); free_AuthorityInfoAccessSyntax(&aia);
return 0; return ret;
}
static int
get_display_text(DisplayText *dt, char **out)
{
int r = -1;
*out = NULL;
/*
* XXX We're cheating with various string types here.
*
* Proper support for IA5String is a real pain, and we don't have it.
*
* We also don't have support for BMPString.
*/
switch (dt->element) {
case choice_DisplayText_ia5String:
r = rk_strasvisx(out, dt->u.ia5String.data, dt->u.ia5String.length,
VIS_CSTYLE | VIS_TAB | VIS_NL, "");
break;
case choice_DisplayText_visibleString:
r = rk_strasvis(out, dt->u.visibleString,
VIS_CSTYLE | VIS_TAB | VIS_NL, "");
break;
case choice_DisplayText_bmpString:
errno = ENOTSUP; /* XXX Need a UTF-16 -> UTF-8 conversion */
break;
case choice_DisplayText_utf8String:
r = rk_strasvis(out, dt->u.visibleString,
VIS_CSTYLE | VIS_TAB | VIS_NL, "");
break;
default:
errno = EINVAL;
}
return r < 0 ? errno : 0;
} }
/* /*
@@ -810,10 +761,10 @@ struct {
HX509_LIB_FUNCTION int HX509_LIB_CALL HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_validate_ctx_init(hx509_context context, hx509_validate_ctx *ctx) hx509_validate_ctx_init(hx509_context context, hx509_validate_ctx *ctx)
{ {
*ctx = malloc(sizeof(**ctx)); *ctx = calloc(1, sizeof(**ctx));
if (*ctx == NULL) if (*ctx == NULL)
return ENOMEM; return hx509_enomem(context);
memset(*ctx, 0, sizeof(**ctx)); (*ctx)->context = context;
return 0; return 0;
} }

View File

@@ -40,6 +40,7 @@ typedef struct abitstring_s {
} *abitstring; } *abitstring;
struct hx509_request_data { struct hx509_request_data {
hx509_context context;
hx509_name name; hx509_name name;
SubjectPublicKeyInfo key; SubjectPublicKeyInfo key;
KeyUsage ku; KeyUsage ku;
@@ -70,6 +71,7 @@ hx509_request_init(hx509_context context, hx509_request *req)
if (*req == NULL) if (*req == NULL)
return ENOMEM; return ENOMEM;
(*req)->context = context;
return 0; return 0;
} }
@@ -1228,7 +1230,7 @@ hx509_request_get_san(hx509_request req,
hx509_san_type *type, hx509_san_type *type,
char **out) char **out)
{ {
struct rk_strpool *pool; struct rk_strpool *pool = NULL;
GeneralName *san; GeneralName *san;
*out = NULL; *out = NULL;
@@ -1262,18 +1264,26 @@ hx509_request_get_san(hx509_request req,
return der_print_heim_oid(&san->u.registeredID, '.', out); return der_print_heim_oid(&san->u.registeredID, '.', out);
case HX509_SAN_TYPE_XMPP: case HX509_SAN_TYPE_XMPP:
/*fallthrough*/ /*fallthrough*/
case HX509_SAN_TYPE_MS_UPN: case HX509_SAN_TYPE_MS_UPN: {
pool = hx509_unparse_utf8_string_name(NULL, &san->u.otherName.value); int ret;
if (pool == NULL ||
ret = _hx509_unparse_utf8_string_name(req->context, &pool,
&san->u.otherName.value);
if (ret == 0 &&
(*out = rk_strpoolcollect(pool)) == NULL) (*out = rk_strpoolcollect(pool)) == NULL)
return ENOMEM; return hx509_enomem(req->context);
return 0; return ret;
case HX509_SAN_TYPE_PKINIT: }
pool = _hx509_unparse_KRB5PrincipalName(NULL, &san->u.otherName.value); case HX509_SAN_TYPE_PKINIT: {
if (pool == NULL || int ret;
ret = _hx509_unparse_KRB5PrincipalName(req->context, &pool,
&san->u.otherName.value);
if (ret == 0 &&
(*out = rk_strpoolcollect(pool)) == NULL) (*out = rk_strpoolcollect(pool)) == NULL)
return ENOMEM; return hx509_enomem(req->context);
return 0; return 0;
}
default: default:
*type = HX509_SAN_TYPE_UNSUPPORTED; *type = HX509_SAN_TYPE_UNSUPPORTED;
return 0; return 0;

View File

@@ -390,10 +390,10 @@ test_pkinit_san(hx509_context context, const char *p, const char *realm, ...)
ret = hx509_general_name_unparse(&gn, &round_trip); ret = hx509_general_name_unparse(&gn, &round_trip);
if (ret) if (ret)
return 1; return 1;
if (strncmp(round_trip, "otherName: 1.3.6.1.5.2.2 ", if (strncmp(round_trip, "otherName: 1.3.6.1.5.2.2 KerberosPrincipalName ",
sizeof("otherName: 1.3.6.1.5.2.2 ") - 1)) sizeof("otherName: 1.3.6.1.5.2.2 KerberosPrincipalName ") - 1))
return 1; return 1;
if (ret || strcmp(round_trip + sizeof("otherName: 1.3.6.1.5.2.2 ") - 1, p)) if (ret || strcmp(round_trip + sizeof("otherName: 1.3.6.1.5.2.2 KerberosPrincipalName ") - 1, p))
return 1; return 1;
free_KRB5PrincipalName(&kn); free_KRB5PrincipalName(&kn);
free_GeneralName(&gn); free_GeneralName(&gn);