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 = {
|
command = {
|
||||||
name = "request-create"
|
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 = {
|
option = {
|
||||||
long = "subject"
|
long = "subject"
|
||||||
type = "string"
|
type = "string"
|
||||||
|
@@ -1498,6 +1498,25 @@ request_create(struct request_create_options *opt, int argc, char **argv)
|
|||||||
if (ret)
|
if (ret)
|
||||||
hx509_err(context, 1, ret, "Could not initialize CSR context");
|
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) {
|
if (opt->subject_string) {
|
||||||
hx509_name name = NULL;
|
hx509_name name = NULL;
|
||||||
|
|
||||||
|
@@ -36,6 +36,7 @@ EXPORTS
|
|||||||
hx509_request_add_pkinit
|
hx509_request_add_pkinit
|
||||||
hx509_request_add_registered
|
hx509_request_add_registered
|
||||||
hx509_request_add_xmpp_name
|
hx509_request_add_xmpp_name
|
||||||
|
hx509_request_authorize_cA
|
||||||
hx509_request_authorize_ku
|
hx509_request_authorize_ku
|
||||||
hx509_request_authorize_eku
|
hx509_request_authorize_eku
|
||||||
hx509_request_authorize_san
|
hx509_request_authorize_san
|
||||||
@@ -48,6 +49,8 @@ EXPORTS
|
|||||||
_hx509_private_key_ref
|
_hx509_private_key_ref
|
||||||
hx509_request_eku_authorized_p
|
hx509_request_eku_authorized_p
|
||||||
hx509_request_free
|
hx509_request_free
|
||||||
|
hx509_request_get_cA
|
||||||
|
hx509_request_get_cA_pathLenConstraint
|
||||||
hx509_request_get_eku
|
hx509_request_get_eku
|
||||||
hx509_request_get_exts
|
hx509_request_get_exts
|
||||||
hx509_request_get_ku
|
hx509_request_get_ku
|
||||||
@@ -63,6 +66,8 @@ EXPORTS
|
|||||||
hx509_request_add_email
|
hx509_request_add_email
|
||||||
hx509_request_reject_eku
|
hx509_request_reject_eku
|
||||||
hx509_request_reject_san
|
hx509_request_reject_san
|
||||||
|
hx509_request_set_cA
|
||||||
|
hx509_request_set_eE
|
||||||
hx509_request_set_name
|
hx509_request_set_name
|
||||||
hx509_request_set_ku
|
hx509_request_set_ku
|
||||||
hx509_request_san_authorized_p
|
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;
|
KeyUsage ku;
|
||||||
ExtKeyUsage eku;
|
ExtKeyUsage eku;
|
||||||
GeneralNames san;
|
GeneralNames san;
|
||||||
|
BasicConstraints bc;
|
||||||
struct abitstring_s authorized_EKUs;
|
struct abitstring_s authorized_EKUs;
|
||||||
struct abitstring_s authorized_SANs;
|
struct abitstring_s authorized_SANs;
|
||||||
uint32_t nunsupported; /* Count of unsupported features requested */
|
uint32_t nunsupported_crit; /* Count of unsupported critical features requested */
|
||||||
uint32_t nauthorized; /* Count of supported features authorized */
|
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 ku_are_authorized:1;
|
||||||
|
uint32_t include_BasicConstraints:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,10 +101,55 @@ hx509_request_free(hx509_request *reqp)
|
|||||||
free_SubjectPublicKeyInfo(&req->key);
|
free_SubjectPublicKeyInfo(&req->key);
|
||||||
free_ExtKeyUsage(&req->eku);
|
free_ExtKeyUsage(&req->eku);
|
||||||
free_GeneralNames(&req->san);
|
free_GeneralNames(&req->san);
|
||||||
|
free_BasicConstraints(&req->bc);
|
||||||
memset(req, 0, sizeof(*req));
|
memset(req, 0, sizeof(*req));
|
||||||
free(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.
|
* Set the subjectName of the CSR.
|
||||||
*
|
*
|
||||||
@@ -524,6 +573,29 @@ get_exts(hx509_context context,
|
|||||||
exts->val = NULL;
|
exts->val = NULL;
|
||||||
exts->len = 0;
|
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)) {
|
if (KeyUsage2int(req->ku)) {
|
||||||
Extension e;
|
Extension e;
|
||||||
|
|
||||||
@@ -851,8 +923,12 @@ hx509_request_parse_der(hx509_context context,
|
|||||||
* Count all KUs as one requested extension to be authorized,
|
* Count all KUs as one requested extension to be authorized,
|
||||||
* though the caller will have to check the KU values individually.
|
* though the caller will have to check the KU values individually.
|
||||||
*/
|
*/
|
||||||
if (KeyUsage2int((*req)->ku) & ~KeyUsage2int(int2KeyUsage(~0)))
|
if (KeyUsage2int((*req)->ku) & ~KeyUsage2int(int2KeyUsage(~0ULL))) {
|
||||||
(*req)->nunsupported++;
|
if (e->critical)
|
||||||
|
(*req)->nunsupported_crit++;
|
||||||
|
else
|
||||||
|
(*req)->nunsupported_opt++;
|
||||||
|
}
|
||||||
} else if (der_heim_oid_cmp(&e->extnID,
|
} else if (der_heim_oid_cmp(&e->extnID,
|
||||||
&asn1_oid_id_x509_ce_extKeyUsage) == 0) {
|
&asn1_oid_id_x509_ce_extKeyUsage) == 0) {
|
||||||
ret = decode_ExtKeyUsage(e->extnValue.data, e->extnValue.length,
|
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
|
* Count each SAN as a separate requested extension to be
|
||||||
* authorized.
|
* 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 {
|
} else {
|
||||||
char *oidstr = NULL;
|
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.
|
* 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.
|
* 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_LIB_FUNCTION size_t HX509_LIB_CALL
|
||||||
hx509_request_count_unsupported(hx509_request req)
|
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)
|
hx509_request_count_unauthorized(hx509_request req)
|
||||||
{
|
{
|
||||||
size_t nrequested = req->eku.len + req->san.len +
|
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
|
static hx509_san_type
|
||||||
@@ -1390,6 +1495,45 @@ hx509_request_get_san(hx509_request req,
|
|||||||
return 0;
|
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.
|
* Display a CSR.
|
||||||
*
|
*
|
||||||
@@ -1437,6 +1581,13 @@ hx509_request_print(hx509_context context, hx509_request req, FILE *f)
|
|||||||
*/
|
*/
|
||||||
fprintf(f, "PKCS#10 CertificationRequest:\n");
|
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) {
|
if (req->name) {
|
||||||
char *subject;
|
char *subject;
|
||||||
ret = hx509_name_to_string(req->name, &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;
|
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;
|
free(s); s = NULL;
|
||||||
if (ret == HX509_NO_ITEM)
|
if (ret == HX509_NO_ITEM)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@@ -161,3 +161,51 @@ Certificate Request:
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
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_pkinit;
|
||||||
hx509_request_add_registered;
|
hx509_request_add_registered;
|
||||||
hx509_request_add_xmpp_name;
|
hx509_request_add_xmpp_name;
|
||||||
|
hx509_request_authorize_cA;
|
||||||
hx509_request_authorize_ku;
|
hx509_request_authorize_ku;
|
||||||
hx509_request_authorize_eku;
|
hx509_request_authorize_eku;
|
||||||
hx509_request_authorize_san;
|
hx509_request_authorize_san;
|
||||||
@@ -244,6 +245,8 @@ HEIMDAL_X509_1.2 {
|
|||||||
hx509_query_match_option;
|
hx509_query_match_option;
|
||||||
hx509_query_statistic_file;
|
hx509_query_statistic_file;
|
||||||
hx509_query_unparse_stats;
|
hx509_query_unparse_stats;
|
||||||
|
hx509_request_get_cA;
|
||||||
|
hx509_request_get_cA_pathLenConstraint;
|
||||||
hx509_request_get_eku;
|
hx509_request_get_eku;
|
||||||
hx509_request_get_exts;
|
hx509_request_get_exts;
|
||||||
hx509_request_get_ku;
|
hx509_request_get_ku;
|
||||||
@@ -254,6 +257,8 @@ HEIMDAL_X509_1.2 {
|
|||||||
hx509_request_init;
|
hx509_request_init;
|
||||||
hx509_request_parse;
|
hx509_request_parse;
|
||||||
hx509_request_parse_der;
|
hx509_request_parse_der;
|
||||||
|
hx509_request_set_cA;
|
||||||
|
hx509_request_set_eE;
|
||||||
hx509_request_set_ku;
|
hx509_request_set_ku;
|
||||||
hx509_request_set_name;
|
hx509_request_set_name;
|
||||||
hx509_request_set_SubjectPublicKeyInfo;
|
hx509_request_set_SubjectPublicKeyInfo;
|
||||||
|
Reference in New Issue
Block a user