hx509: Add support for cert policies and mappings

This commit is contained in:
Nicolas Williams
2021-01-15 12:01:12 -06:00
parent b193d75a15
commit dbb0463ca8
6 changed files with 388 additions and 15 deletions

View File

@@ -32,7 +32,6 @@
*/
#include "hx_locl.h"
#include <pkinit_asn1.h>
/**
* @page page_ca Hx509 CA functions
@@ -46,6 +45,8 @@ struct hx509_ca_tbs {
KeyUsage ku;
ExtKeyUsage eku;
GeneralNames san;
CertificatePolicies cps;
PolicyMappings pms;
heim_integer serial;
struct {
unsigned int proxy:1;
@@ -102,6 +103,8 @@ hx509_ca_tbs_free(hx509_ca_tbs *tbs)
return;
free_SubjectPublicKeyInfo(&(*tbs)->spki);
free_CertificatePolicies(&(*tbs)->cps);
free_PolicyMappings(&(*tbs)->pms);
free_GeneralNames(&(*tbs)->san);
free_ExtKeyUsage(&(*tbs)->eku);
der_free_heim_integer(&(*tbs)->serial);
@@ -528,6 +531,127 @@ hx509_ca_tbs_add_eku(hx509_context context,
return 0;
}
/**
* Add a certificate policy to the to-be-signed certificate object. Duplicates
* will detected and not added.
*
* @param context A hx509 context.
* @param tbs object to be signed.
* @param oid policy OID.
* @param cps_uri CPS URI to qualify policy with.
* @param user_notice user notice display text to qualify policy with.
*
* @return An hx509 error code, see hx509_get_error_string().
*
* @ingroup hx509_ca
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_ca_tbs_add_pol(hx509_context context,
hx509_ca_tbs tbs,
const heim_oid *oid,
const char *cps_uri,
const char *user_notice)
{
PolicyQualifierInfos pqis;
PolicyQualifierInfo pqi;
PolicyInformation pi;
size_t i, size;
int ret = 0;
/* search for duplicates */
for (i = 0; i < tbs->cps.len; i++) {
if (der_heim_oid_cmp(oid, &tbs->cps.val[i].policyIdentifier) == 0)
return 0;
}
memset(&pi, 0, sizeof(pi));
memset(&pqi, 0, sizeof(pqi));
memset(&pqis, 0, sizeof(pqis));
pi.policyIdentifier = *oid;
if (cps_uri) {
CPSuri uri;
uri.length = strlen(cps_uri);
uri.data = (void *)(uintptr_t)cps_uri;
pqi.policyQualifierId = asn1_oid_id_pkix_qt_cps;
ASN1_MALLOC_ENCODE(CPSuri,
pqi.qualifier.data,
pqi.qualifier.length,
&uri, &size, ret);
if (ret == 0) {
ret = add_PolicyQualifierInfos(&pqis, &pqi);
free_heim_any(&pqi.qualifier);
}
}
if (ret == 0 && user_notice) {
DisplayText dt;
UserNotice un;
dt.element = choice_DisplayText_utf8String;
dt.u.utf8String = (void *)(uintptr_t)user_notice;
un.explicitText = &dt;
un.noticeRef = 0;
pqi.policyQualifierId = asn1_oid_id_pkix_qt_unotice;
ASN1_MALLOC_ENCODE(UserNotice,
pqi.qualifier.data,
pqi.qualifier.length,
&un, &size, ret);
if (ret == 0) {
ret = add_PolicyQualifierInfos(&pqis, &pqi);
free_heim_any(&pqi.qualifier);
}
}
pi.policyQualifiers = pqis.len ? &pqis : 0;
if (ret == 0)
ret = add_CertificatePolicies(&tbs->cps, &pi);
free_PolicyQualifierInfos(&pqis);
return ret;
}
/**
* Add a certificate policy mapping to the to-be-signed certificate object.
* Duplicates will detected and not added.
*
* @param context A hx509 context.
* @param tbs object to be signed.
* @param issuer issuerDomainPolicy policy OID.
* @param subject subjectDomainPolicy policy OID.
*
* @return An hx509 error code, see hx509_get_error_string().
*
* @ingroup hx509_ca
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_ca_tbs_add_pol_mapping(hx509_context context,
hx509_ca_tbs tbs,
const heim_oid *issuer,
const heim_oid *subject)
{
PolicyMapping pm;
size_t i;
/* search for duplicates */
for (i = 0; i < tbs->pms.len; i++) {
PolicyMapping *pmp = &tbs->pms.val[i];
if (der_heim_oid_cmp(issuer, &pmp->issuerDomainPolicy) == 0 &&
der_heim_oid_cmp(subject, &pmp->subjectDomainPolicy) == 0)
return 0;
}
memset(&pm, 0, sizeof(pm));
pm.issuerDomainPolicy = *issuer;
pm.subjectDomainPolicy = *subject;
return add_PolicyMappings(&tbs->pms, &pm);
}
/**
* Add CRL distribution point URI to the to-be-signed certificate
* object.
@@ -1613,8 +1737,8 @@ ca_sign(hx509_context context,
goto out;
}
/* Add CRL distribution point */
if (tbs->crldp.len) {
ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length,
&tbs->crldp, &size, ret);
if (ret) {
@@ -1631,6 +1755,40 @@ ca_sign(hx509_context context,
goto out;
}
/* Add CertificatePolicies */
if (tbs->cps.len) {
ASN1_MALLOC_ENCODE(CertificatePolicies, data.data, data.length,
&tbs->cps, &size, ret);
if (ret) {
hx509_set_error_string(context, 0, ret, "Out of memory");
goto out;
}
if (size != data.length)
_hx509_abort("internal ASN.1 encoder error");
ret = add_extension(context, tbsc, FALSE,
&asn1_oid_id_x509_ce_certificatePolicies, &data);
free(data.data);
if (ret)
goto out;
}
/* Add PolicyMappings */
if (tbs->cps.len) {
ASN1_MALLOC_ENCODE(PolicyMappings, data.data, data.length,
&tbs->pms, &size, ret);
if (ret) {
hx509_set_error_string(context, 0, ret, "Out of memory");
goto out;
}
if (size != data.length)
_hx509_abort("internal ASN.1 encoder error");
ret = add_extension(context, tbsc, FALSE,
&asn1_oid_id_x509_ce_policyMappings, &data);
free(data.data);
if (ret)
goto out;
}
ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret);
if (ret) {
hx509_set_error_string(context, 0, ret, "malloc out of memory");

View File

@@ -761,6 +761,16 @@ command = {
type = "string"
help = "XMPP jabber id (for SAN)"
}
option = {
long = "policy"
type = "strings"
help = "Certificate Policy OID and optional URI and/or notice (OID:URI<space>notice_text)"
}
option = {
long = "policy-mapping"
type = "strings"
help = "Certificate Policy mapping (OID:OID)"
}
option = {
long = "req"
type = "string"

View File

@@ -109,7 +109,7 @@ parse_oid(const char *str, const heim_oid *def, heim_oid *oid)
if (ret == 0)
ret = der_copy_oid(found, oid);
else
ret = der_parse_heim_oid (str, " .", oid);
ret = der_parse_heim_oid(str, " .", oid);
} else {
ret = der_copy_oid(def, oid);
}
@@ -1411,10 +1411,7 @@ request_create(struct request_create_options *opt, int argc, char **argv)
for (i = 0; i < opt->registered_strings.num_strings; i++) {
heim_oid oid;
ret = der_parse_heim_oid(opt->registered_strings.strings[i], NULL,
&oid);
if (ret)
hx509_err(context, 1, ret, "OID parse error");
parse_oid(opt->registered_strings.strings[i], NULL, &oid);
ret = hx509_request_add_registered(context, req, &oid);
der_free_oid(&oid);
if (ret)
@@ -1879,9 +1876,11 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
hx509_private_key cert_key = NULL;
hx509_name subject = NULL;
SubjectPublicKeyInfo spki;
heim_oid oid;
size_t i;
int delta = 0;
memset(&oid, 0, sizeof(oid));
memset(&spki, 0, sizeof(spki));
if (opt->ca_certificate_string == NULL && !opt->self_signed_flag)
@@ -2021,12 +2020,11 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
hx509_err(context, 1, ret, "hx509_ca_tbs_init");
for (i = 0; i < opt->eku_strings.num_strings; i++) {
heim_oid oid;
parse_oid(opt->eku_strings.strings[i], NULL, &oid);
ret = hx509_ca_tbs_add_eku(context, tbs, &oid);
if (ret)
hx509_err(context, 1, ret, "hx509_request_add_eku");
der_free_oid(&oid);
}
if (opt->ku_strings.num_strings) {
const struct units *kus = asn1_KeyUsage_units();
@@ -2117,6 +2115,48 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
eval_types(context, tbs, opt);
for (i = 0; ret == 0 && i < opt->policy_strings.num_strings; i++) {
char *oidstr, *uri, *dt;
if ((oidstr = strdup(opt->policy_strings.strings[i])) == NULL)
hx509_err(context, 1, ENOMEM, "out of memory");
uri = strchr(oidstr, ':');
if (uri)
*(uri++) = '\0';
dt = strchr(uri ? uri : "", ' ');
if (dt)
*(dt++) = '\0';
parse_oid(oidstr, NULL, &oid);
ret = hx509_ca_tbs_add_pol(context, tbs, &oid, uri, dt);
der_free_oid(&oid);
free(oidstr);
}
for (i = 0; ret == 0 && i < opt->policy_mapping_strings.num_strings; i++) {
char *issuer_oidstr, *subject_oidstr;
heim_oid issuer_oid, subject_oid;
if ((issuer_oidstr =
strdup(opt->policy_mapping_strings.strings[i])) == NULL)
hx509_err(context, 1, ENOMEM, "out of memory");
subject_oidstr = strchr(issuer_oidstr, ':');
if (subject_oidstr == NULL)
subject_oidstr = issuer_oidstr;
else
*(subject_oidstr++) = '\0';
parse_oid(issuer_oidstr, NULL, &issuer_oid);
parse_oid(subject_oidstr, NULL, &subject_oid);
ret = hx509_ca_tbs_add_pol_mapping(context, tbs, &issuer_oid,
&subject_oid);
if (ret)
hx509_err(context, 1, ret, "failed to add policy mapping");
der_free_oid(&issuer_oid);
der_free_oid(&subject_oid);
free(issuer_oidstr);
}
if (opt->issue_ca_flag) {
ret = hx509_ca_tbs_set_ca(context, tbs, opt->path_length_integer);
if (ret)
@@ -2521,8 +2561,7 @@ acert1_sans(struct acert_options *opt,
s = opt->has_registeredID_san_strings.strings[k];
memset(&oid, 0, sizeof(oid));
if ((ret = der_parse_heim_oid(s, NULL, &oid)))
break;
parse_oid(s, NULL, &oid);
if (der_heim_oid_cmp(&gn->u.registeredID, &oid) == 0) {
der_free_oid(&oid);
if (opt->verbose_flag)
@@ -2571,8 +2610,7 @@ acert1_ekus(struct acert_options *opt,
heim_oid oid;
memset(&oid, 0, sizeof(oid));
if ((ret = der_parse_heim_oid(s, NULL, &oid)))
break;
parse_oid(s, NULL, &oid);
if (der_heim_oid_cmp(&eku.val[i], &oid) == 0) {
der_free_oid(&oid);
if (opt->verbose_flag)

View File

@@ -65,6 +65,8 @@ EXPORTS
hx509_ca_tbs_add_crl_dp_uri
hx509_ca_tbs_add_eku
hx509_ca_tbs_add_ku
hx509_ca_tbs_add_pol
hx509_ca_tbs_add_pol_mapping
hx509_ca_tbs_add_san
hx509_ca_tbs_add_san_hostname
hx509_ca_tbs_add_san_jid

View File

@@ -698,6 +698,169 @@ get_display_text(DisplayText *dt, char **out)
return r < 0 ? errno : 0;
}
static int
check_certificatePolicies(hx509_validate_ctx ctx,
struct cert_status *status,
enum critical_flag cf,
const Extension *e)
{
CertificatePolicies cp;
size_t i, size;
int ret = 0;
check_Null(ctx, status, cf, e);
if (e->extnValue.length == 0) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"CertificatePolicies empty, not allowed");
return 1;
}
ret = decode_CertificatePolicies(e->extnValue.data, e->extnValue.length,
&cp, &size);
if (ret) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"\tret = %d while decoding CertificatePolicies\n", ret);
return 1;
}
if (cp.len == 0) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"CertificatePolicies empty, not allowed\n");
return 1;
}
for (i = 0; ret == 0 && i < cp.len; i++) {
size_t k;
char *poid = NULL;
char *qoid = NULL;
char *dt = NULL;
ret = der_print_heim_oid(&cp.val[i].policyIdentifier, '.', &poid);
if (ret == 0)
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tPolicy: %s", poid);
for (k = 0;
ret == 0 && cp.val[i].policyQualifiers &&
k < cp.val[i].policyQualifiers->len;
k++) {
PolicyQualifierInfo *pi = &cp.val[i].policyQualifiers->val[k];
if (der_heim_oid_cmp(&pi->policyQualifierId,
&asn1_oid_id_pkix_qt_cps) == 0) {
CPSuri cps;
ret = decode_CPSuri(pi->qualifier.data, pi->qualifier.length,
&cps, &size);
if (ret == 0) {
if (cps.length > 4096)
cps.length = 4096;
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
":CPSuri:%.*s",
(int)cps.length, (char *)cps.data);
free_CPSuri(&cps);
}
} else if (der_heim_oid_cmp(&pi->policyQualifierId,
&asn1_oid_id_pkix_qt_unotice) == 0) {
UserNotice un;
ret = decode_UserNotice(pi->qualifier.data,
pi->qualifier.length, &un, &size);
if (ret == 0) {
if (un.explicitText) {
/*
* get_display_text() will strvis to make it safer to
* print.
*/
ret = get_display_text(un.explicitText, &dt);
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
" UserNotice:DistplayText:%s", dt);
} else if (un.noticeRef) {
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
" UserNotice:NoticeRef:<noticeRef-not-supported>",
qoid);
} else {
ret = der_print_heim_oid(&pi->policyQualifierId, '.',
&qoid);
if (ret)
break;
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
" Unknown:%s", qoid);
}
}
} else {
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
", qualifier %s:<unknown>", qoid);
}
free(qoid);
free(dt);
qoid = dt = 0;
}
if (ret == 0) {
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n");
} else {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"\nOut of memory formatting certificate policy");
ret = ENOMEM;
}
free(poid);
free(qoid);
free(dt);
poid = qoid = dt = 0;
}
free_CertificatePolicies(&cp);
return ret ? 1 : 0;
}
static int
check_policyMappings(hx509_validate_ctx ctx,
struct cert_status *status,
enum critical_flag cf,
const Extension *e)
{
PolicyMappings pm;
size_t i, size;
int ret = 0;
check_Null(ctx, status, cf, e);
if (e->extnValue.length == 0) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"PolicyMappings empty, not allowed");
return 1;
}
ret = decode_PolicyMappings(e->extnValue.data, e->extnValue.length,
&pm, &size);
if (ret) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"\tret = %d while decoding PolicyMappings\n", ret);
return 1;
}
if (pm.len == 0) {
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"PolicyMappings empty, not allowed\n");
return 1;
}
for (i = 0; ret == 0 && i < pm.len; i++) {
char *idpoid = NULL;
char *sdpoid = NULL;
ret = der_print_heim_oid(&pm.val[i].issuerDomainPolicy, '.', &idpoid);
if (ret == 0)
ret = der_print_heim_oid(&pm.val[i].subjectDomainPolicy, '.',
&sdpoid);
if (ret == 0)
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
"\tPolicy mapping %s -> %s\n", idpoid, sdpoid);
else
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
"ret=%d while decoding PolicyMappings\n", ret);
}
return 0;
}
/*
*
*/
@@ -727,8 +890,8 @@ struct {
{ ext(certificateIssuer, Null), M_C },
{ ext(nameConstraints, Null), M_C },
{ ext(cRLDistributionPoints, CRLDistributionPoints), S_N_C },
{ ext(certificatePolicies, Null), 0 },
{ ext(policyMappings, Null), M_N_C },
{ ext(certificatePolicies, certificatePolicies), 0 },
{ ext(policyMappings, policyMappings), M_N_C },
{ ext(authorityKeyIdentifier, authorityKeyIdentifier), M_N_C },
{ ext(policyConstraints, Null), D_C },
{ ext(extKeyUsage, extKeyUsage), D_C },

View File

@@ -49,6 +49,8 @@ HEIMDAL_X509_1.2 {
hx509_ca_tbs_add_crl_dp_uri;
hx509_ca_tbs_add_eku;
hx509_ca_tbs_add_ku;
hx509_ca_tbs_add_pol;
hx509_ca_tbs_add_pol_mapping;
hx509_ca_tbs_add_san;
hx509_ca_tbs_add_san_hostname;
hx509_ca_tbs_add_san_jid;