hx509: Add Heimdal cert ext for ticket max_life
This adds support for using a Heimdal-specific PKIX extension to derive a maximum Kerberos ticket lifetime from a client's PKINIT certificate: - a `--pkinit-max-life` to the `hxtool ca` command - `hx509_ca_tbs_set_pkinit_max_life()` - `hx509_cert_get_pkinit_max_life()` - `HX509_CA_TEMPLATE_PKINIT_MAX_LIFE` There are two extensions. One is an EKU, which if present means that the maximum ticket lifetime should be derived from the notAfter minus notBefore. The other is a certificate extension whose value is a maximum ticket lifetime in seconds. The latter is preferred.
This commit is contained in:
@@ -58,6 +58,7 @@ struct hx509_ca_tbs {
|
||||
} flags;
|
||||
time_t notBefore;
|
||||
time_t notAfter;
|
||||
HeimPkinitPrincMaxLifeSecs pkinitTicketMaxLife;
|
||||
int pathLenConstraint; /* both for CA and Proxy */
|
||||
CRLDistributionPoints crldp;
|
||||
heim_bit_string subjectUniqueID;
|
||||
@@ -186,6 +187,15 @@ hx509_ca_tbs_set_notAfter_lifetime(hx509_context context,
|
||||
return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta);
|
||||
}
|
||||
|
||||
HX509_LIB_FUNCTION int HX509_LIB_CALL
|
||||
hx509_ca_tbs_set_pkinit_max_life(hx509_context context,
|
||||
hx509_ca_tbs tbs,
|
||||
time_t max_life)
|
||||
{
|
||||
tbs->pkinitTicketMaxLife = max_life;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct units templatebits[] = {
|
||||
{ "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU },
|
||||
{ "KeyUsage", HX509_CA_TEMPLATE_KU },
|
||||
@@ -194,6 +204,7 @@ static const struct units templatebits[] = {
|
||||
{ "notBefore", HX509_CA_TEMPLATE_NOTBEFORE },
|
||||
{ "serial", HX509_CA_TEMPLATE_SERIAL },
|
||||
{ "subject", HX509_CA_TEMPLATE_SUBJECT },
|
||||
{ "pkinitMaxLife", HX509_CA_TEMPLATE_PKINIT_MAX_LIFE },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
@@ -285,6 +296,12 @@ hx509_ca_tbs_set_template(hx509_context context,
|
||||
}
|
||||
free_ExtKeyUsage(&eku);
|
||||
}
|
||||
if (flags & HX509_CA_TEMPLATE_PKINIT_MAX_LIFE) {
|
||||
time_t max_life;
|
||||
|
||||
if ((max_life = hx509_cert_get_pkinit_max_life(context, cert, 0)) > 0)
|
||||
hx509_ca_tbs_set_pkinit_max_life(context, tbs, max_life);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1812,7 +1829,7 @@ ca_sign(hx509_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* add KeyUsage */
|
||||
/* Add KeyUsage */
|
||||
if (KeyUsage2int(tbs->ku) > 0) {
|
||||
ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length,
|
||||
&tbs->ku, &size, ret);
|
||||
@@ -1829,7 +1846,7 @@ ca_sign(hx509_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* add ExtendedKeyUsage */
|
||||
/* Add ExtendedKeyUsage */
|
||||
if (tbs->eku.len > 0) {
|
||||
ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length,
|
||||
&tbs->eku, &size, ret);
|
||||
@@ -1846,7 +1863,7 @@ ca_sign(hx509_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* add Subject Alternative Name */
|
||||
/* Add Subject Alternative Name */
|
||||
if (tbs->san.len > 0) {
|
||||
ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length,
|
||||
&tbs->san, &size, ret);
|
||||
@@ -1950,7 +1967,7 @@ ca_sign(hx509_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* add Proxy */
|
||||
/* Add Proxy */
|
||||
if (tbs->flags.proxy) {
|
||||
ProxyCertInfo info;
|
||||
|
||||
@@ -2044,6 +2061,23 @@ ca_sign(hx509_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add Heimdal PKINIT ticket max life extension */
|
||||
if (tbs->pkinitTicketMaxLife > 0) {
|
||||
ASN1_MALLOC_ENCODE(HeimPkinitPrincMaxLifeSecs, data.data, data.length,
|
||||
&tbs->pkinitTicketMaxLife, &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_heim_ce_pkinit_princ_max_life, &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");
|
||||
|
@@ -1633,6 +1633,63 @@ hx509_cert_get_notAfter(hx509_cert p)
|
||||
return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notAfter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a maximum Kerberos credential lifetime from a Heimdal certificate
|
||||
* extension.
|
||||
*
|
||||
* @param context hx509 context.
|
||||
* @param cert Certificate.
|
||||
* @param bound If larger than zero, return no more than this.
|
||||
*
|
||||
* @return maximum ticket lifetime.
|
||||
*/
|
||||
HX509_LIB_FUNCTION time_t HX509_LIB_CALL
|
||||
hx509_cert_get_pkinit_max_life(hx509_context context,
|
||||
hx509_cert cert,
|
||||
time_t bound)
|
||||
{
|
||||
HeimPkinitPrincMaxLifeSecs r = 0;
|
||||
size_t sz, i;
|
||||
time_t b, e;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < cert->data->tbsCertificate.extensions->len; i++) {
|
||||
Extension *ext = &cert->data->tbsCertificate.extensions->val[i];
|
||||
|
||||
if (ext->_ioschoice_extnValue.element !=
|
||||
choice_Extension_iosnumunknown &&
|
||||
ext->_ioschoice_extnValue.element !=
|
||||
choice_Extension_iosnum_id_heim_ce_pkinit_princ_max_life)
|
||||
continue;
|
||||
if (ext->_ioschoice_extnValue.element == choice_Extension_iosnumunknown &&
|
||||
der_heim_oid_cmp(&asn1_oid_id_heim_ce_pkinit_princ_max_life, &ext->extnID))
|
||||
continue;
|
||||
if (ext->_ioschoice_extnValue.u.ext_HeimPkinitPrincMaxLife) {
|
||||
r = *ext->_ioschoice_extnValue.u.ext_HeimPkinitPrincMaxLife;
|
||||
} else {
|
||||
ret = decode_HeimPkinitPrincMaxLifeSecs(ext->extnValue.data,
|
||||
ext->extnValue.length,
|
||||
&r, &sz);
|
||||
/* No need to free_HeimPkinitPrincMaxLifeSecs(); it's an int */
|
||||
if (ret || r < 1)
|
||||
return 0;
|
||||
}
|
||||
if (bound > 0 && r > bound)
|
||||
return bound;
|
||||
return r;
|
||||
}
|
||||
if (hx509_cert_check_eku(context, cert,
|
||||
&asn1_oid_id_heim_eku_pkinit_certlife_is_max_life, 0))
|
||||
return 0;
|
||||
b = hx509_cert_get_notBefore(cert);
|
||||
e = hx509_cert_get_notAfter(cert);
|
||||
if (e > b)
|
||||
r = e - b;
|
||||
if (bound > 0 && r > bound)
|
||||
return bound;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SubjectPublicKeyInfo structure from the hx509 certificate.
|
||||
*
|
||||
|
@@ -197,6 +197,7 @@ typedef enum {
|
||||
#define HX509_CA_TEMPLATE_SPKI 16
|
||||
#define HX509_CA_TEMPLATE_KU 32
|
||||
#define HX509_CA_TEMPLATE_EKU 64
|
||||
#define HX509_CA_TEMPLATE_PKINIT_MAX_LIFE 128
|
||||
|
||||
/* flags hx509_cms_create_signed* */
|
||||
#define HX509_CMS_SIGNATURE_DETACHED 0x01
|
||||
|
@@ -791,6 +791,11 @@ command = {
|
||||
type = "strings"
|
||||
help = "Certificate Policy mapping (OID:OID)"
|
||||
}
|
||||
option = {
|
||||
long = "pkinit-max-life"
|
||||
type = "string"
|
||||
help = "maximum Kerberos ticket lifetime extension for PKINIT"
|
||||
}
|
||||
option = {
|
||||
long = "req"
|
||||
type = "string"
|
||||
|
@@ -2297,6 +2297,13 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_ca_tbs_set_notAfter_lifetime");
|
||||
}
|
||||
if (opt->pkinit_max_life_string) {
|
||||
time_t t = parse_time(opt->pkinit_max_life_string, "s");
|
||||
|
||||
ret = hx509_ca_tbs_set_pkinit_max_life(context, tbs, t);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_ca_tbs_set_pkinit_max_life");
|
||||
}
|
||||
|
||||
if (opt->self_signed_flag) {
|
||||
ret = hx509_ca_sign_self(context, tbs, private_key, &cert);
|
||||
|
@@ -90,6 +90,7 @@ EXPORTS
|
||||
hx509_ca_tbs_set_notAfter
|
||||
hx509_ca_tbs_set_notAfter_lifetime
|
||||
hx509_ca_tbs_set_notBefore
|
||||
hx509_ca_tbs_set_pkinit_max_life
|
||||
hx509_ca_tbs_set_proxy
|
||||
hx509_ca_tbs_set_serialnumber
|
||||
hx509_ca_tbs_set_signature_algorithm
|
||||
@@ -113,6 +114,7 @@ EXPORTS
|
||||
hx509_cert_get_issuer
|
||||
hx509_cert_get_notAfter
|
||||
hx509_cert_get_notBefore
|
||||
hx509_cert_get_pkinit_max_life
|
||||
hx509_cert_get_serialnumber
|
||||
hx509_cert_get_subject
|
||||
hx509_cert_have_private_key
|
||||
|
@@ -74,6 +74,7 @@ HEIMDAL_X509_1.2 {
|
||||
hx509_ca_tbs_set_notAfter;
|
||||
hx509_ca_tbs_set_notAfter_lifetime;
|
||||
hx509_ca_tbs_set_notBefore;
|
||||
hx509_ca_tbs_set_pkinit_max_life;
|
||||
hx509_ca_tbs_set_proxy;
|
||||
hx509_ca_tbs_set_serialnumber;
|
||||
hx509_ca_tbs_set_spki;
|
||||
@@ -97,6 +98,7 @@ HEIMDAL_X509_1.2 {
|
||||
hx509_cert_get_issuer;
|
||||
hx509_cert_get_notAfter;
|
||||
hx509_cert_get_notBefore;
|
||||
hx509_cert_get_pkinit_max_life;
|
||||
hx509_cert_get_serialnumber;
|
||||
hx509_cert_get_subject;
|
||||
hx509_cert_get_issuer_unique_id;
|
||||
|
Reference in New Issue
Block a user