hx509: Add support for CSRs w/ BasicConstraints
This commit is contained in:

committed by
Nico Williams

parent
0f998cdbc2
commit
be0d1e68e5
@@ -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"
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
175
lib/hx509/req.c
175
lib/hx509/req.c
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user