hx509: Add support for CSRs w/ BasicConstraints

This commit is contained in:
Nicolas Williams
2023-11-01 16:12:24 -05:00
committed by Nico Williams
parent 0f998cdbc2
commit be0d1e68e5
6 changed files with 260 additions and 8 deletions

View File

@@ -475,6 +475,22 @@ command = {
}
command = {
name = "request-create"
option = {
long = "ca"
type = "flag"
help = "Request CA certificate"
}
option = {
long = "ca-path-length"
type = "integer"
help = "Path length constraint for CA certificate"
default = "-1"
}
option = {
long = "ee"
type = "flag"
help = "Include BasicConstraints w/ cA set to false"
}
option = {
long = "subject"
type = "string"

View File

@@ -1498,6 +1498,25 @@ request_create(struct request_create_options *opt, int argc, char **argv)
if (ret)
hx509_err(context, 1, ret, "Could not initialize CSR context");
if (opt->ca_flag && opt->ee_flag)
errx(1, "request-create --ca and --ee are mutually exclusive");
if (opt->ca_flag) {
unsigned pathLenConstraint = 0;
unsigned *pathLenConstraintPtr = NULL;
if (opt->ca_path_length_integer > 0 &&
opt->ca_path_length_integer < INT_MAX) {
pathLenConstraint = opt->ca_path_length_integer;
pathLenConstraintPtr = &pathLenConstraint;
}
ret = hx509_request_set_cA(context, req, pathLenConstraintPtr);
if (ret)
errx(1, "hx509_request_set_cA: %d\n", ret);
} else if (opt->ee_flag) {
hx509_request_set_eE(context, req);
}
if (opt->subject_string) {
hx509_name name = NULL;

View File

@@ -36,6 +36,7 @@ EXPORTS
hx509_request_add_pkinit
hx509_request_add_registered
hx509_request_add_xmpp_name
hx509_request_authorize_cA
hx509_request_authorize_ku
hx509_request_authorize_eku
hx509_request_authorize_san
@@ -48,6 +49,8 @@ EXPORTS
_hx509_private_key_ref
hx509_request_eku_authorized_p
hx509_request_free
hx509_request_get_cA
hx509_request_get_cA_pathLenConstraint
hx509_request_get_eku
hx509_request_get_exts
hx509_request_get_ku
@@ -63,6 +66,8 @@ EXPORTS
hx509_request_add_email
hx509_request_reject_eku
hx509_request_reject_san
hx509_request_set_cA
hx509_request_set_eE
hx509_request_set_name
hx509_request_set_ku
hx509_request_san_authorized_p

View File

@@ -46,11 +46,15 @@ struct hx509_request_data {
KeyUsage ku;
ExtKeyUsage eku;
GeneralNames san;
BasicConstraints bc;
struct abitstring_s authorized_EKUs;
struct abitstring_s authorized_SANs;
uint32_t nunsupported; /* Count of unsupported features requested */
uint32_t nauthorized; /* Count of supported features authorized */
uint32_t nunsupported_crit; /* Count of unsupported critical features requested */
uint32_t nunsupported_opt; /* Count of unsupported optional features requested */
uint32_t nauthorized; /* Count of supported features authorized */
uint32_t ca_is_authorized:1;
uint32_t ku_are_authorized:1;
uint32_t include_BasicConstraints:1;
};
/**
@@ -97,10 +101,55 @@ hx509_request_free(hx509_request *reqp)
free_SubjectPublicKeyInfo(&req->key);
free_ExtKeyUsage(&req->eku);
free_GeneralNames(&req->san);
free_BasicConstraints(&req->bc);
memset(req, 0, sizeof(*req));
free(req);
}
/**
* Make the CSR request a CA certificate
*
* @param context An hx509 context.
* @param req The hx509_request to alter.
* @param pathLenConstraint the pathLenConstraint for the BasicConstraints (optional)
*
* @return An hx509 error code, see hx509_get_error_string().
*
* @ingroup hx509_request
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_request_set_cA(hx509_context context,
hx509_request req,
unsigned *pathLenConstraint)
{
req->bc.cA = 1;
if (pathLenConstraint) {
if (req->bc.pathLenConstraint == NULL)
req->bc.pathLenConstraint = malloc(sizeof(*pathLenConstraint));
if (req->bc.pathLenConstraint == NULL)
return ENOMEM;
*req->bc.pathLenConstraint = *pathLenConstraint;
}
return 0;
}
/**
* Make the CSR request an EE (end-entity, i.e., not a CA) certificate
*
* @param context An hx509 context.
* @param req The hx509_request to alter.
*
* @ingroup hx509_request
*/
HX509_LIB_FUNCTION void HX509_LIB_CALL
hx509_request_set_eE(hx509_context context, hx509_request req)
{
req->bc.cA = 0;
free(req->bc.pathLenConstraint);
req->include_BasicConstraints = 1;
req->ca_is_authorized = 0;
}
/**
* Set the subjectName of the CSR.
*
@@ -524,6 +573,29 @@ get_exts(hx509_context context,
exts->val = NULL;
exts->len = 0;
if (req->bc.cA || req->include_BasicConstraints) {
Extension e;
/*
* If `!req->bc.cA' then we don't include BasicConstraints unless
* hx509_request_set_eE() was called on this request, and in that case
* we make this extension non-critical. We do this to emulate Dell
* iDRAC CSR-making software.
*
* If `req->bc.cA' then we make the BasicConstraints critical,
* obviously.
*/
memset(&e, 0, sizeof(e));
e.critical = req->bc.cA ? 1 : 0;
if (ret == 0)
ASN1_MALLOC_ENCODE(BasicConstraints, e.extnValue.data, e.extnValue.length,
&req->bc, &size, ret);
if (ret == 0)
ret = der_copy_oid(&asn1_oid_id_x509_ce_basicConstraints, &e.extnID);
if (ret == 0)
ret = add_Extensions(exts, &e);
free_Extension(&e);
}
if (KeyUsage2int(req->ku)) {
Extension e;
@@ -851,8 +923,12 @@ hx509_request_parse_der(hx509_context context,
* Count all KUs as one requested extension to be authorized,
* though the caller will have to check the KU values individually.
*/
if (KeyUsage2int((*req)->ku) & ~KeyUsage2int(int2KeyUsage(~0)))
(*req)->nunsupported++;
if (KeyUsage2int((*req)->ku) & ~KeyUsage2int(int2KeyUsage(~0ULL))) {
if (e->critical)
(*req)->nunsupported_crit++;
else
(*req)->nunsupported_opt++;
}
} else if (der_heim_oid_cmp(&e->extnID,
&asn1_oid_id_x509_ce_extKeyUsage) == 0) {
ret = decode_ExtKeyUsage(e->extnValue.data, e->extnValue.length,
@@ -873,10 +949,18 @@ hx509_request_parse_der(hx509_context context,
* Count each SAN as a separate requested extension to be
* authorized.
*/
} else if (der_heim_oid_cmp(&e->extnID,
&asn1_oid_id_x509_ce_basicConstraints) == 0) {
(*req)->include_BasicConstraints = 1;
ret = decode_BasicConstraints(e->extnValue.data, e->extnValue.length,
&(*req)->bc, NULL);
} else {
char *oidstr = NULL;
(*req)->nunsupported++;
if (e->critical)
(*req)->nunsupported_crit++;
else
(*req)->nunsupported_opt++;
/*
* We need an HX509_TRACE facility for this sort of warning.
@@ -1073,6 +1157,26 @@ reject_feat(hx509_request req, abitstring a, size_t n, int idx)
}
}
/**
* Authorize issuance of a CA certificate as requested.
*
* @param req The hx509_request object.
* @param pathLenConstraint the pathLenConstraint for the BasicConstraints (optional)
*
* @return an hx509 or system error.
*
* @ingroup hx509_request
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_request_authorize_cA(hx509_request req, unsigned *pathLenConstraint)
{
int ret;
ret = hx509_request_set_cA(NULL, req, pathLenConstraint);
req->ca_is_authorized++;
return ret;
}
/**
* Filter the requested KeyUsage and mark it authorized.
*
@@ -1201,7 +1305,7 @@ hx509_request_san_authorized_p(hx509_request req, size_t idx)
HX509_LIB_FUNCTION size_t HX509_LIB_CALL
hx509_request_count_unsupported(hx509_request req)
{
return req->nunsupported;
return req->nunsupported_crit;
}
/**
@@ -1216,9 +1320,10 @@ HX509_LIB_FUNCTION size_t HX509_LIB_CALL
hx509_request_count_unauthorized(hx509_request req)
{
size_t nrequested = req->eku.len + req->san.len +
(KeyUsage2int(req->ku) ? 1 : 0) + req->nunsupported;
(KeyUsage2int(req->ku) ? 1 : 0) + !!req->bc.cA +
req->nunsupported_crit;
return nrequested - (req->nauthorized + req->ku_are_authorized);
return nrequested - (req->nauthorized + req->ku_are_authorized + req->ca_is_authorized);
}
static hx509_san_type
@@ -1390,6 +1495,45 @@ hx509_request_get_san(hx509_request req,
return 0;
}
/**
* Indicate if a CSR requested a CA certificate.
*
* @param context An hx509 context.
* @param req The hx509_request object.
*
* @return 1 if the CSR requested CA certificate, 0 otherwise.
*
* @ingroup hx509_request
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_request_get_cA(hx509_context context,
hx509_request req)
{
return req->bc.cA;
}
/**
* Return the CSR's requested BasicConstraints pathLenConstraint.
*
* @param context An hx509 context.
* @param req The hx509_request object.
*
* @return -1 if no pathLenConstraint was requested (or the BasicConstraints
* does not request a CA certificate), or the actual requested
* pathLenConstraint.
*
* @ingroup hx509_request
*/
HX509_LIB_FUNCTION int HX509_LIB_CALL
hx509_request_get_cA_pathLenConstraint(hx509_context context,
hx509_request req)
{
if (req->bc.cA && req->bc.pathLenConstraint &&
*req->bc.pathLenConstraint < INT_MAX)
return *req->bc.pathLenConstraint;
return -1;
}
/**
* Display a CSR.
*
@@ -1437,6 +1581,13 @@ hx509_request_print(hx509_context context, hx509_request req, FILE *f)
*/
fprintf(f, "PKCS#10 CertificationRequest:\n");
if (req->include_BasicConstraints) {
fprintf(f, " cA: %s\n", req->bc.cA ? "yes" : "no");
if (req->bc.pathLenConstraint)
fprintf(f, " pathLenConstraint: %u\n", *req->bc.pathLenConstraint);
else
fprintf(f, " pathLenConstraint: unspecified\n");
}
if (req->name) {
char *subject;
ret = hx509_name_to_string(req->name, &subject);
@@ -1515,6 +1666,14 @@ hx509_request_print(hx509_context context, hx509_request req, FILE *f)
break;
}
}
if (req->nunsupported_crit) {
fprintf(f, " unsupported_critical_extensions_count: %u\n",
(unsigned)req->nunsupported_crit);
}
if (req->nunsupported_crit) {
fprintf(f, " unsupported_optional_extensions_count: %u\n",
(unsigned)req->nunsupported_opt);
}
free(s); s = NULL;
if (ret == HX509_NO_ITEM)
ret = 0;

View File

@@ -161,3 +161,51 @@ Certificate Request:
EOF
fi
fi
${hxtool} request-create \
--ca \
--ca-path-length=3 \
--subject="cn=ca-cert" \
--key=FILE:$srcdir/data/key.der \
pkcs10-request.der || exit 1
${hxtool} request-print PKCS10:pkcs10-request.der > "${objdir}/actual"|| exit 1
cat > "$objdir/expected" <<EOF
request print
PKCS#10 CertificationRequest:
cA: yes
pathLenConstraint: 3
name: CN=ca-cert
EOF
diff "$objdir/expected" "${objdir}/actual" || exit 1
${hxtool} request-create \
--ca \
--subject="cn=ca-cert" \
--key=FILE:$srcdir/data/key.der \
pkcs10-request.der || exit 1
${hxtool} request-print PKCS10:pkcs10-request.der > "${objdir}/actual"|| exit 1
cat > "$objdir/expected" <<EOF
request print
PKCS#10 CertificationRequest:
cA: yes
pathLenConstraint: unspecified
name: CN=ca-cert
EOF
diff "$objdir/expected" "${objdir}/actual" || exit 1
${hxtool} request-create \
--ee \
--subject="cn=ca-cert" \
--key=FILE:$srcdir/data/key.der \
pkcs10-request.der || exit 1
${hxtool} request-print PKCS10:pkcs10-request.der > "${objdir}/actual" || exit 1
cat > "$objdir/expected" <<EOF
request print
PKCS#10 CertificationRequest:
cA: no
pathLenConstraint: unspecified
name: CN=ca-cert
EOF
diff "$objdir/expected" "${objdir}/actual" || exit 1
exit 0

View File

@@ -36,6 +36,7 @@ HEIMDAL_X509_1.2 {
hx509_request_add_pkinit;
hx509_request_add_registered;
hx509_request_add_xmpp_name;
hx509_request_authorize_cA;
hx509_request_authorize_ku;
hx509_request_authorize_eku;
hx509_request_authorize_san;
@@ -244,6 +245,8 @@ HEIMDAL_X509_1.2 {
hx509_query_match_option;
hx509_query_statistic_file;
hx509_query_unparse_stats;
hx509_request_get_cA;
hx509_request_get_cA_pathLenConstraint;
hx509_request_get_eku;
hx509_request_get_exts;
hx509_request_get_ku;
@@ -254,6 +257,8 @@ HEIMDAL_X509_1.2 {
hx509_request_init;
hx509_request_parse;
hx509_request_parse_der;
hx509_request_set_cA;
hx509_request_set_eE;
hx509_request_set_ku;
hx509_request_set_name;
hx509_request_set_SubjectPublicKeyInfo;