aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/POW.c508
-rw-r--r--rpki/rpkid_tasks.py29
2 files changed, 502 insertions, 35 deletions
diff --git a/ext/POW.c b/ext/POW.c
index 645a4a96..40bd5d02 100644
--- a/ext/POW.c
+++ b/ext/POW.c
@@ -162,16 +162,36 @@ static char pow_module__doc__ [] =
* but we try to put all the magic associated with this in one place.
*/
-#ifndef NID_rpkiManifest
-static int NID_rpkiManifest;
+#ifndef NID_ad_rpkiManifest
+static int NID_ad_rpkiManifest;
#endif
-#ifndef NID_signedObject
-static int NID_signedObject;
+#ifndef NID_ad_signedObject
+static int NID_ad_signedObject;
#endif
-#ifndef NID_rpkiNotify
-static int NID_rpkiNotify;
+#ifndef NID_ad_rpkiNotify
+static int NID_ad_rpkiNotify;
+#endif
+
+#ifndef NID_ct_ROA
+static int NID_ct_ROA;
+#endif
+
+#ifndef NID_ct_rpkiManifest
+static int NID_ct_rpkiManifest;
+#endif
+
+#ifndef NID_ct_rpkiGhostbusters
+static int NID_ct_rpkiGhostbusters;
+#endif
+
+#ifndef NID_cp_ipAddr_asNumber
+static int NID_cp_ipAddr_asNumber;
+#endif
+
+#ifndef NID_id_kp_bgpsec_router
+static int NID_id_kp_bgpsec_router;
#endif
static const struct {
@@ -181,16 +201,36 @@ static const struct {
const char *ln;
} missing_nids[] = {
-#ifndef NID_rpkiManifest
- {&NID_rpkiManifest, "1.3.6.1.5.5.7.48.10", "id-ad-rpkiManifest", "RPKI Manifest"},
+#ifndef NID_ad_rpkiManifest
+ {&NID_ad_rpkiManifest, "1.3.6.1.5.5.7.48.10", "id-ad-rpkiManifest", "RPKI Manifest"},
#endif
-#ifndef NID_signedObject
- {&NID_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"},
+#ifndef NID_ad_signedObject
+ {&NID_ad_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"},
#endif
-#ifndef NID_rpkiNotify
- {&NID_rpkiNotify, "1.3.6.1.5.5.7.48.13", "id-ad-rpkiNotify", "RPKI RRDP Notification"},
+#ifndef NID_ad_rpkiNotify
+ {&NID_ad_rpkiNotify, "1.3.6.1.5.5.7.48.13", "id-ad-rpkiNotify", "RPKI RRDP Notification"},
+#endif
+
+#ifndef NID_ct_ROA
+ {&NID_ct_ROA, "1.2.840.113549.1.9.16.1.24", "id-ct-routeOriginAttestation", "ROA eContent"},
+#endif
+
+#ifndef NID_ct_rpkiManifest
+ {&NID_ct_rpkiManifest, "1.2.840.113549.1.9.16.1.26", "id-ct-rpkiManifest", "RPKI Manifest eContent"},
+#endif
+
+#ifndef NID_ct_rpkiGhostbusters
+ {&NID_ct_rpkiGhostbusters, "1.2.840.113549.1.9.16.1.35", "id-ct-rpkiGhostbusters", "RPKI Ghostbusters eContent"},
+#endif
+
+#ifndef NID_cp_ipAddr_asNumber
+ {&NID_cp_ipAddr_asNumber, "1.3.6.1.5.5.7.14.2", "id-cp-ipAddr-asNumber", "RPKI Certificate Policy"},
+#endif
+
+#ifndef NID_id_kp_bgpsec_router
+ {&NID_id_kp_bgpsec_router, "1.3.6.1.5.5.7.3.30", "id-kp-bgpsec-router", "BGPSEC Router Certificate"},
#endif
};
@@ -243,7 +283,8 @@ static PyObject
*ErrorObject,
*OpenSSLErrorObject,
*POWErrorObject,
- *NotVerifiedErrorObject;
+ *NotVerifiedErrorObject,
+ *ValidationErrorObject;
/*
* Constructor for customized datetime class.
@@ -265,6 +306,27 @@ static PyObject *status_codes;
static int x509_store_ctx_ex_data_idx = -1;
/*
+ * ASN.1 "constants" constructed at runtime.
+ */
+
+static const ASN1_INTEGER *asn1_zero, *asn1_four_octets, *asn1_twenty_octets;
+
+#warning TEMPORARY REPLACEMENTS FOR RUNTIME CONTROL FLAGS
+/*
+ * These should become bit flags to the _verify() functions, or something.
+ */
+
+static const int
+ allow_stale_crl = 1,
+ allow_stale_manifest = 1,
+ allow_digest_mismatch = 1,
+ allow_crl_digest_mismatch = 1,
+ allow_nonconformant_name = 1,
+ allow_ee_without_signedObject = 1,
+ allow_1024_bit_ee_key = 1,
+ allow_wrong_cms_si_attributes = 1;
+
+/*
* Declarations of type objects (definitions come later).
*/
@@ -425,6 +487,28 @@ typedef struct {
goto error; \
} while (0)
+#define lose_validation_error(_msg_) \
+ do { \
+ PyErr_SetString(ValidationErrorObject, (_msg_)); \
+ goto error; \
+ } while (0)
+
+#define lose_validation_error_from_code(_status_, _code_) \
+ do { \
+ if (record_validation_status(_status_, _code_)) \
+ PyErr_SetObject(ValidationErrorObject, \
+ PyTuple_GetItem(status_codes, _code_)); \
+ goto error; \
+ } while (0)
+
+#define lose_validation_error_from_code_maybe(_test_, _status_, _code_) \
+ do { \
+ if (!(_test_)) \
+ lose_validation_error_from_code(_status_, _code_); \
+ else if (!record_validation_status(_status_, _code_)) \
+ goto error; \
+ } while (0)
+
#define assert_no_unhandled_openssl_errors() \
do { \
if (ERR_peek_error()) { \
@@ -1624,6 +1708,349 @@ validation_status_x509_verify_cert_cb(int ok, X509_STORE_CTX *ctx, PyObject *sta
/*
+ * Detail checking functions. These are only used by the relying
+ * party code, and only when the caller of one of the verification
+ * functions has requested detailed checking by passing in a result
+ * status set object.
+ */
+
+typedef enum {
+ check_object_type_cer,
+ check_object_type_crl,
+ check_object_type_mft,
+ check_object_type_roa,
+ check_object_type_gbr
+} check_object_type_t;
+
+/*
+ * Check whether a Distinguished Name conforms to the rescert profile.
+ * The profile is very restrictive: it only allows one mandatory
+ * CommonName field and one optional SerialNumber field, both of which
+ * must be of type PrintableString.
+ */
+
+static int check_allowed_dn(X509_NAME *dn)
+{
+ X509_NAME_ENTRY *ne;
+ ASN1_STRING *s;
+ int loc;
+
+ if (dn == NULL)
+ return 0;
+
+ switch (X509_NAME_entry_count(dn)) {
+
+ case 2:
+ if ((loc = X509_NAME_get_index_by_NID(dn, NID_serialNumber, -1)) < 0 ||
+ (ne = X509_NAME_get_entry(dn, loc)) == NULL ||
+ (s = X509_NAME_ENTRY_get_data(ne)) == NULL ||
+ ASN1_STRING_type(s) != V_ASN1_PRINTABLESTRING)
+ return 0;
+
+ /* Fall through */
+
+ case 1:
+ if ((loc = X509_NAME_get_index_by_NID(dn, NID_commonName, -1)) < 0 ||
+ (ne = X509_NAME_get_entry(dn, loc)) == NULL ||
+ (s = X509_NAME_ENTRY_get_data(ne)) == NULL ||
+ ASN1_STRING_type(s) != V_ASN1_PRINTABLESTRING)
+ return 0;
+
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Check whether an ASN.1 TIME value conforms to RFC 5280 4.1.2.5.
+ */
+
+static int check_allowed_time_encoding(ASN1_TIME *t)
+{
+ switch (t->type) {
+
+ case V_ASN1_UTCTIME:
+ return t->length == sizeof("yymmddHHMMSSZ") - 1;
+
+ case V_ASN1_GENERALIZEDTIME:
+ return (t->length == sizeof("yyyymmddHHMMSSZ") - 1 &&
+ strcmp("205", (char *) t->data) <= 0);
+
+ }
+ return 0;
+}
+
+/*
+ * Check to see whether an AKI extension is present, is of the right
+ * form, and matches the issuer.
+ */
+static int check_aki(PyObject *status, const X509 *issuer, const AUTHORITY_KEYID *aki)
+{
+ if (aki == NULL)
+ lose_validation_error_from_code(status, AKI_EXTENSION_MISSING);
+
+ if (!aki->keyid || aki->serial || aki->issuer)
+ lose_validation_error_from_code(status, AKI_EXTENSION_WRONG_FORMAT);
+
+ if (ASN1_OCTET_STRING_cmp(aki->keyid, issuer->skid))
+ lose_validation_error_from_code(status, AKI_EXTENSION_ISSUER_MISMATCH);
+
+ return 1;
+
+ error:
+ return 0;
+}
+
+/*
+ * Check a lot of pesky low-level things about RPKI certificates.
+ */
+
+static int check_x509(x509_object *x509,
+ STACK_OF(X509) *stack,
+ PyObject *status,
+ const int is_ta,
+ const check_object_type_t object_type)
+{
+ EVP_PKEY *issuer_pkey = NULL, *subject_pkey = NULL;
+ unsigned long flags = (X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY | X509_V_FLAG_X509_STRICT);
+ AUTHORITY_INFO_ACCESS *sia = NULL, *aia = NULL;
+ STACK_OF(POLICYINFO) *policies = NULL;
+ ASN1_BIT_STRING *ski_pubkey = NULL;
+ STACK_OF(DIST_POINT) *crldp = NULL;
+ EXTENDED_KEY_USAGE *eku = NULL;
+ BASIC_CONSTRAINTS *bc = NULL;
+ unsigned char ski_hashbuf[EVP_MAX_MD_SIZE];
+ unsigned ski_hashlen, afi;
+ int i, ok, crit, loc, ex_count, is_ca, routercert = 0, ret = 0;
+
+ if (ASN1_INTEGER_cmp(X509_get_serialNumber(x509->x509), asn1_zero) <= 0 ||
+ ASN1_INTEGER_cmp(X509_get_serialNumber(x509->x509), asn1_twenty_octets) > 0)
+ lose_validation_error_from_code(status, BAD_CERTIFICATE_SERIAL_NUMBER);
+
+ if (!check_allowed_time_encoding(X509_get_notBefore(x509->x509)) ||
+ !check_allowed_time_encoding(X509_get_notAfter(x509->x509)))
+ lose_validation_error_from_code(status, NONCONFORMANT_ASN1_TIME_VALUE);
+
+ /*
+ * Apparently nothing ever looks at these fields, so there are no
+ * API functions for them. We wouldn't bother either if they
+ * weren't forbidden by the RPKI certificate profile.
+ */
+ if (!x509->x509->cert_info || x509->x509->cert_info->issuerUID || x509->x509->cert_info->subjectUID)
+ lose_validation_error_from_code(status, NONCONFORMANT_CERTIFICATE_UID);
+
+ /*
+ * Keep track of allowed extensions we've seen. Once we've
+ * processed all the ones we expect, anything left is an error.
+ */
+ ex_count = X509_get_ext_count(x509->x509);
+
+ /*
+ * We don't use X509_check_ca() to check whether the certificate is
+ * a CA, because it's not paranoid enough to enforce the RPKI
+ * certificate profile, but we still call it because we need it (or
+ * something) to invoke x509v3_cache_extensions() for us.
+ */
+ (void) X509_check_ca(x509->x509);
+
+ if ((bc = X509_get_ext_d2i(x509->x509, NID_basic_constraints, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (!crit || bc->ca <= 0 || bc->pathlen != NULL)
+ lose_validation_error_from_code(status, MALFORMED_BASIC_CONSTRAINTS);
+ }
+
+ is_ca = bc != NULL;
+
+ if (is_ta && !is_ca)
+ lose_validation_error_from_code(status, MALFORMED_TRUST_ANCHOR);
+
+ /*
+ * Check for presence of AIA, SIA, and CRLDP, but leave URI checking for Python code.
+ */
+
+#warning Why are we not checking the critical flag on these extensions?
+
+ if ((aia = X509_get_ext_d2i(x509->x509, NID_info_access, NULL, NULL)) != NULL)
+ ex_count--;
+
+ if ((sia = X509_get_ext_d2i(x509->x509, NID_sinfo_access, NULL, NULL)) != NULL)
+ ex_count--;
+
+ if ((crldp = X509_get_ext_d2i(x509->x509, NID_crl_distribution_points, NULL, NULL)) != NULL)
+ ex_count--;
+
+ if ((eku = X509_get_ext_d2i(x509->x509, NID_ext_key_usage, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (crit || is_ca || object_type != check_object_type_cer || sk_ASN1_OBJECT_num(eku) == 0)
+ lose_validation_error_from_code(status, INAPPROPRIATE_EKU_EXTENSION);
+ for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++)
+ routercert |= OBJ_obj2nid(sk_ASN1_OBJECT_value(eku, i)) == NID_id_kp_bgpsec_router;
+ }
+
+ if (X509_get_version(x509->x509) != 2)
+ lose_validation_error_from_code(status, WRONG_OBJECT_VERSION);
+
+ if (x509->x509->cert_info == NULL ||
+ x509->x509->cert_info->signature == NULL ||
+ x509->x509->cert_info->signature->algorithm == NULL ||
+ OBJ_obj2nid(x509->x509->cert_info->signature->algorithm) != NID_sha256WithRSAEncryption)
+ lose_validation_error_from_code(status, NONCONFORMANT_SIGNATURE_ALGORITHM);
+
+ if (x509->x509->skid)
+ ex_count--;
+ else
+ lose_validation_error_from_code(status, SKI_EXTENSION_MISSING);
+
+ if (!check_allowed_dn(X509_get_subject_name(x509->x509)))
+ lose_validation_error_from_code_maybe(allow_nonconformant_name, status, NONCONFORMANT_SUBJECT_NAME);
+
+ if (!check_allowed_dn(X509_get_issuer_name(x509->x509)))
+ lose_validation_error_from_code_maybe(allow_nonconformant_name, status, NONCONFORMANT_ISSUER_NAME);
+
+ if ((policies = X509_get_ext_d2i(x509->x509, NID_certificate_policies, &crit, NULL)) != NULL) {
+ POLICYQUALINFO *qualifier = NULL;
+ POLICYINFO *policy = NULL;
+ ex_count--;
+ if (!crit || sk_POLICYINFO_num(policies) != 1 ||
+ (policy = sk_POLICYINFO_value(policies, 0)) == NULL ||
+ OBJ_obj2nid(policy->policyid) != NID_cp_ipAddr_asNumber ||
+ sk_POLICYQUALINFO_num(policy->qualifiers) > 1 ||
+ (sk_POLICYQUALINFO_num(policy->qualifiers) == 1 &&
+ ((qualifier = sk_POLICYQUALINFO_value(policy->qualifiers, 0)) == NULL ||
+ OBJ_obj2nid(qualifier->pqualid) != NID_id_qt_cps)))
+ lose_validation_error_from_code(status, BAD_CERTIFICATE_POLICY);
+ if (qualifier && !record_validation_status(status, POLICY_QUALIFIER_CPS))
+ goto error;
+ }
+
+ if (!X509_EXTENSION_get_critical(X509_get_ext(x509->x509, X509_get_ext_by_NID(x509->x509, NID_key_usage, -1))) ||
+ (x509->x509->ex_flags & EXFLAG_KUSAGE) == 0 ||
+ x509->x509->ex_kusage != (is_ca ? KU_KEY_CERT_SIGN | KU_CRL_SIGN : KU_DIGITAL_SIGNATURE))
+ lose_validation_error_from_code(status, BAD_KEY_USAGE);
+ ex_count--;
+
+ if (x509->x509->rfc3779_addr) {
+ ex_count--;
+ if (routercert ||
+ (loc = X509_get_ext_by_NID(x509->x509, NID_sbgp_ipAddrBlock, -1)) < 0 ||
+ !X509_EXTENSION_get_critical(X509_get_ext(x509->x509, loc)) ||
+ !v3_addr_is_canonical(x509->x509->rfc3779_addr) ||
+ sk_IPAddressFamily_num(x509->x509->rfc3779_addr) == 0)
+ lose_validation_error_from_code(status, BAD_IPADDRBLOCKS);
+ for (i = 0; i < sk_IPAddressFamily_num(x509->x509->rfc3779_addr); i++) {
+ IPAddressFamily *f = sk_IPAddressFamily_value(x509->x509->rfc3779_addr, i);
+ afi = v3_addr_get_afi(f);
+ if (afi != IANA_AFI_IPV4 && afi != IANA_AFI_IPV6)
+ lose_validation_error_from_code(status, UNKNOWN_AFI);
+ if (f->addressFamily->length != 2)
+ lose_validation_error_from_code(status, SAFI_NOT_ALLOWED);
+ }
+ }
+
+ if (x509->x509->rfc3779_asid) {
+ ex_count--;
+ if ((loc = X509_get_ext_by_NID(x509->x509, NID_sbgp_autonomousSysNum, -1)) < 0 ||
+ !X509_EXTENSION_get_critical(X509_get_ext(x509->x509, loc)) ||
+ !v3_asid_is_canonical(x509->x509->rfc3779_asid) ||
+ x509->x509->rfc3779_asid->asnum == NULL ||
+ x509->x509->rfc3779_asid->rdi != NULL ||
+ (routercert && x509->x509->rfc3779_asid->asnum->type == ASIdentifierChoice_inherit))
+ lose_validation_error_from_code(status, BAD_ASIDENTIFIERS);
+ }
+
+ if (!x509->x509->rfc3779_addr && !x509->x509->rfc3779_asid)
+ lose_validation_error_from_code(status, MISSING_RESOURCES);
+
+ subject_pkey = X509_get_pubkey(x509->x509);
+ ok = subject_pkey != NULL;
+ if (ok) {
+ ASN1_OBJECT *algorithm;
+
+ (void) X509_PUBKEY_get0_param(&algorithm, NULL, NULL, NULL, X509_get_X509_PUBKEY(x509->x509));
+
+ switch (OBJ_obj2nid(algorithm)) {
+
+ case NID_rsaEncryption:
+ ok = EVP_PKEY_type(subject_pkey->type) == EVP_PKEY_RSA && BN_get_word(subject_pkey->pkey.rsa->e) == 65537;
+ if (ok && BN_num_bits(subject_pkey->pkey.rsa->n) == 2048)
+ break;
+ ok = ok && !is_ca && allow_1024_bit_ee_key && BN_num_bits(subject_pkey->pkey.rsa->n) == 1024;
+ if (ok && !record_validation_status(status, EE_CERTIFICATE_WITH_1024_BIT_KEY))
+ goto error;
+ break;
+
+ case NID_X9_62_id_ecPublicKey:
+ ok = !is_ca && routercert;
+ break;
+
+ default:
+ ok = 0;
+ }
+ }
+ if (!ok)
+ lose_validation_error_from_code(status, BAD_PUBLIC_KEY);
+
+ if (x509->x509->skid == NULL ||
+ (ski_pubkey = X509_get0_pubkey_bitstr(x509->x509)) == NULL ||
+ !EVP_Digest(ski_pubkey->data, ski_pubkey->length,
+ ski_hashbuf, &ski_hashlen, EVP_sha1(), NULL) ||
+ ski_hashlen != 20 ||
+ ski_hashlen != x509->x509->skid->length ||
+ memcmp(ski_hashbuf, x509->x509->skid->data, ski_hashlen))
+ lose_validation_error_from_code(status, SKI_PUBLIC_KEY_MISMATCH);
+
+ if (x509->x509->akid) {
+ ex_count--;
+ if (!check_aki(status, sk_X509_value(stack, 0), x509->x509->akid))
+ goto error;
+ }
+
+ if (!x509->x509->akid && !is_ta)
+ lose_validation_error_from_code(status, AKI_EXTENSION_MISSING);
+
+ if ((issuer_pkey = X509_get_pubkey(sk_X509_value(stack, 0))) == NULL || X509_verify(x509->x509, issuer_pkey) <= 0)
+ lose_validation_error_from_code(status, CERTIFICATE_BAD_SIGNATURE);
+
+ /*
+ * rcynic.c's check_x509() has a whole lot of complex code here to
+ * deal with finding and binding CRLs into the X509_STORE_CTX for
+ * non the TA case. Finding we want to handle in Python; binding is
+ * something the caller of this function should handle, based on
+ * whether or not it has been passed a CRL object as an (as yet
+ * unspecified) argument.
+ */
+
+ if (ex_count > 0)
+ lose_validation_error_from_code(status, DISALLOWED_X509V3_EXTENSION);
+
+ /*
+ * rcynic.c's check_x509() also handles setting up the policy
+ * binding and trusted certificate stack here, then finally calls
+ * X509_verify_cert().
+ */
+
+ ret = 1;
+
+ error:
+ EVP_PKEY_free(issuer_pkey);
+ EVP_PKEY_free(subject_pkey);
+ BASIC_CONSTRAINTS_free(bc);
+ sk_ACCESS_DESCRIPTION_pop_free(sia, ACCESS_DESCRIPTION_free);
+ sk_ACCESS_DESCRIPTION_pop_free(aia, ACCESS_DESCRIPTION_free);
+ sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
+ sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
+ sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free);
+
+ return ret;
+}
+
+
+
+
+
+/*
* Extension functions. Calling sequence here is a little weird,
* because it turns out that the simplest way to avoid massive
* duplication of code between classes is to work directly with
@@ -1878,11 +2305,11 @@ extension_get_sia(X509_EXTENSIONS **exts)
nid = OBJ_obj2nid(a->method);
if (nid == NID_caRepository)
n_caRepository++;
- else if (nid == NID_rpkiManifest)
+ else if (nid == NID_ad_rpkiManifest)
n_rpkiManifest++;
- else if (nid == NID_signedObject)
+ else if (nid == NID_ad_signedObject)
n_signedObject++;
- else if (nid == NID_rpkiNotify)
+ else if (nid == NID_ad_rpkiNotify)
n_rpkiNotify++;
}
@@ -1906,19 +2333,19 @@ extension_get_sia(X509_EXTENSIONS **exts)
PyTuple_SET_ITEM(result_caRepository, n_caRepository++, obj);
continue;
}
- if (nid == NID_rpkiManifest) {
+ if (nid == NID_ad_rpkiManifest) {
if ((obj = PyString_FromString(uri)) == NULL)
goto error;
PyTuple_SET_ITEM(result_rpkiManifest, n_rpkiManifest++, obj);
continue;
}
- if (nid == NID_signedObject) {
+ if (nid == NID_ad_signedObject) {
if ((obj = PyString_FromString(uri)) == NULL)
goto error;
PyTuple_SET_ITEM(result_signedObject, n_signedObject++, obj);
continue;
}
- if (nid == NID_rpkiNotify) {
+ if (nid == NID_ad_rpkiNotify) {
if ((obj = PyString_FromString(uri)) == NULL)
goto error;
PyTuple_SET_ITEM(result_rpkiNotify, n_rpkiNotify++, obj);
@@ -1987,10 +2414,10 @@ extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
for (i = 0; i < 4; i++) {
switch (i) {
- case 0: pobj = &caRepository; nid = NID_caRepository; break;
- case 1: pobj = &rpkiManifest; nid = NID_rpkiManifest; break;
- case 2: pobj = &signedObject; nid = NID_signedObject; break;
- case 3: pobj = &rpkiNotify; nid = NID_rpkiNotify; break;
+ case 0: pobj = &caRepository; nid = NID_caRepository; break;
+ case 1: pobj = &rpkiManifest; nid = NID_ad_rpkiManifest; break;
+ case 2: pobj = &signedObject; nid = NID_ad_signedObject; break;
+ case 3: pobj = &rpkiNotify; nid = NID_ad_rpkiNotify; break;
}
if (*pobj == Py_None)
@@ -4598,17 +5025,21 @@ static char x509_store_object_verify__doc__[] =
"This method returns an instance of the store's verification context class.\n"
;
+#warning Update function doc once we know what extended API looks like
+
static PyObject *
-x509_store_object_verify(x509_store_object *self, PyObject *args)
+x509_store_object_verify(x509_store_object *self, PyObject *args, PyObject *kwds)
{
+ static char *kwlist[] = {"cert", "chain", "status", "ta", NULL};
x509_store_ctx_object *ctx = NULL;
STACK_OF(X509) *stack = NULL;
x509_object *x509 = NULL;
PyObject *chain = Py_None;
PyObject *status = Py_None;
+ PyObject *ta = Py_None;
int ok;
- if (!PyArg_ParseTuple(args, "O!|OO!", &POW_X509_Type, &x509, &chain, &PySet_Type, &status))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OO!O", kwlist, &POW_X509_Type, &x509, &chain, &PySet_Type, &status, &ta))
goto error;
if ((ctx = (x509_store_ctx_object *) PyObject_CallFunctionObjArgs(self->ctxclass, self, NULL)) == NULL)
@@ -4623,6 +5054,9 @@ x509_store_object_verify(x509_store_object *self, PyObject *args)
if (chain != Py_None && (stack = x509_helper_iterable_to_stack(chain)) == NULL)
goto error;
+ if (status != Py_None && !check_x509(x509, stack, status, PyObject_IsTrue(ta), check_object_type_cer))
+ goto error;
+
Py_XINCREF(x509);
Py_XINCREF(chain);
Py_XINCREF(status);
@@ -4647,7 +5081,7 @@ x509_store_object_verify(x509_store_object *self, PyObject *args)
goto error;
if (ok < 0)
- lose_openssl_error("X509_verify_cert() raised an exception");
+ lose_validation_error("X509_verify_cert() raised an exception");
return (PyObject *) ctx;
@@ -4662,7 +5096,7 @@ static struct PyMethodDef x509_store_object_methods[] = {
Define_Method(setContextClass,x509_store_object_set_context_class, METH_VARARGS),
Define_Method(setFlags, x509_store_object_set_flags, METH_VARARGS),
Define_Method(clearFlags, x509_store_object_clear_flags, METH_VARARGS),
- Define_Method(verify, x509_store_object_verify, METH_VARARGS),
+ Define_Method(verify, x509_store_object_verify, METH_KEYWORDS),
{NULL}
};
@@ -4723,22 +5157,23 @@ static int
x509_store_ctx_object_verify_cb(int ok, X509_STORE_CTX *ctx)
{
static char method_name[] = "verify_callback";
- PyObject *self = X509_STORE_CTX_get_ex_data(ctx, x509_store_ctx_ex_data_idx);
+ x509_store_ctx_object *self = (x509_store_ctx_object *) X509_STORE_CTX_get_ex_data(ctx, x509_store_ctx_ex_data_idx);
PyObject *result = NULL;
- PyObject *status = NULL;
if (self == NULL)
return ok;
- status = ((x509_store_ctx_object *) self)->status;
+ /*
+ * This is where we'd extract flags (allow_*, etc) to pass to the callback handler.
+ */
- if (status != NULL && status != Py_None)
- ok = validation_status_x509_verify_cert_cb(ok, ctx, status);
+ if (self->status != NULL && self->status != Py_None)
+ ok = validation_status_x509_verify_cert_cb(ok, ctx, self->status);
- if (!PyObject_HasAttrString(self, method_name))
+ if (!PyObject_HasAttrString((PyObject *) self, method_name))
return ok;
- if ((result = PyObject_CallMethod(self, method_name, "i", ok)) == NULL)
+ if ((result = PyObject_CallMethod((PyObject *) self, method_name, "i", ok)) == NULL)
return -1;
ok = PyObject_IsTrue(result);
@@ -9881,6 +10316,7 @@ init_POW(void)
Define_Exception(OpenSSLError, ErrorObject);
Define_Exception(POWError, ErrorObject);
Define_Exception(NotVerifiedError, ErrorObject);
+ Define_Exception(ValidationError, ErrorObject);
#undef Define_Exception
@@ -10002,6 +10438,10 @@ init_POW(void)
x509_store_ctx_ex_data_idx = X509_STORE_CTX_get_ex_new_index(0, "x590_store_ctx_object for verify callback",
NULL, NULL, NULL);
+ asn1_zero = s2i_ASN1_INTEGER(NULL, "0x0");
+ asn1_four_octets = s2i_ASN1_INTEGER(NULL, "0xFFFFFFFF");
+ asn1_twenty_octets = s2i_ASN1_INTEGER(NULL, "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
if (PyErr_Occurred() || !OpenSSL_ok)
Py_FatalError("Can't initialize module POW");
}
diff --git a/rpki/rpkid_tasks.py b/rpki/rpkid_tasks.py
index 5e2a28f6..9759deff 100644
--- a/rpki/rpkid_tasks.py
+++ b/rpki/rpkid_tasks.py
@@ -295,6 +295,25 @@ class UpdateChildrenTask(AbstractTask):
rsn = now + rpki.sundial.timedelta(seconds = self.tenant.regen_margin)
publisher = rpki.rpkid.publication_queue(self.rpkid)
+ # XXX This loop could be better written.
+ #
+ # As written, this is just maintenance on existing ChildCert
+ # objects (no attempt to generate new ones for which we did
+ # not previously have the resources, unclear whether that's a
+ # bug). Assuming for purposes of discussion that this is what
+ # this task should be doing, this loop could be written better:
+ #
+ # We're looking for ChildCert objects issued by active
+ # CADetails, so we should querying for that directly before
+ # starting the loop. From that result, we can trivially pull
+ # the set of distinct child_handle values, at which point we
+ # can do a single yield on a dict to get all the IRDB results
+ # back, keyed by child_handle, still before starting the loop.
+ #
+ # Once we have all that, we can run the loop without any
+ # interruptions, which should make it easier to avoid
+ # potential races while building up the publication queue.
+
for child in self.tenant.children.all():
try:
if self.overdue:
@@ -355,6 +374,14 @@ class UpdateROAsTask(AbstractTask):
self.publisher = None
self.ca_details = None
+ # XXX This might need rewriting to avoid race conditions.
+ #
+ # There's a theoretical race condition here if we're chugging away
+ # and something else needs to update the manifest or CRL, or if
+ # some back-end operation generates or destroys ROAs. The risk is
+ # fairly low given that we defer CRL and manifest generation until
+ # we're ready to publish, but it's theoretically present.
+
@tornado.gen.coroutine
def main(self):
logger.debug("%r: Updating ROAs", self)
@@ -503,7 +530,7 @@ class UpdateGhostbustersTask(AbstractTask):
@queue_task
class UpdateEECertificatesTask(AbstractTask):
"""
- Generate or update EE certificates for this self.
+ Generate or update EE certificates for this tenant.
Not yet sure what kind of scaling constraints this task might have,
so keeping it simple for initial version, we can optimize later.