diff options
-rw-r--r-- | ext/POW.c | 386 | ||||
-rwxr-xr-x | rp/rcynic/rcynicng | 57 | ||||
-rw-r--r-- | rpki/POW/__init__.py | 3 |
3 files changed, 194 insertions, 252 deletions
@@ -314,17 +314,6 @@ static int x509_store_ctx_ex_data_idx = -1; 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_nonconformant_name = 1, - allow_1024_bit_ee_key = 1, - allow_wrong_cms_si_attributes = 1, - allow_non_self_signed_trust_anchor = 0; - /* * Declarations of type objects (definitions come later). */ @@ -476,23 +465,6 @@ typedef struct { goto error; \ } while (0) -#define lose_validation_error_from_code(_status_, _code_) \ - do { \ - if (_record_validation_status(_status_, #_code_)) \ - PyErr_SetString(ValidationErrorObject, #_code_); \ - goto error; \ - } while (0) - -#define lose_validation_error_from_code_maybe(_test_, _status_, _code_) \ - do { \ - if (!_record_validation_status(_status_, #_code_)) \ - goto error; \ - if (_test_) \ - break; \ - PyErr_SetString(ValidationErrorObject, #_code_); \ - goto error; \ - } while (0) - #define assert_no_unhandled_openssl_errors() \ do { \ if (ERR_peek_error()) { \ @@ -1177,17 +1149,20 @@ whack_ec_key_to_namedCurve(EVP_PKEY *pkey) * Validation status codes. Still under construction. Conceptually * modeled after rcynic's validation status database, implementation * somewhat different due to language issues and desire to keep the C - * side of this as simple as possible. Depends on suppot from the + * side of this as simple as possible. Depends on support from the * Python side (see rpki/POW/__init__.py). */ /* - * Add code to status object, return C boolean indicating success. - * Do nothing and return success if the status object is None. + * Add code to status object, throwing an error if something goes + * horribly wrong. */ -#define record_validation_status(_status_, _code_) \ - _record_validation_status(_status_, #_code_) +#define record_validation_status(_status_, _code_) \ + do { \ + if (!_record_validation_status(_status_, #_code_)) \ + goto error; \ + } while (0) static int _record_validation_status(PyObject *status, const char *code) @@ -1205,12 +1180,14 @@ _record_validation_status(PyObject *status, const char *code) /* * Validation callback code for use with x509_verify_cert(). * - * In theory record_validation_status() can throw an exception, but - * there's no direct way to handle that here, so we're depending on - * x509_store_object_verify() calling PyErr_Occurred() to check (which - * it has to do anyway to handle exceptions generated by Python + * There's no direct way to handle exceptions here, so we're depending + * on x509_store_object_verify() calling PyErr_Occurred() to check + * (which it has to do anyway to handle exceptions generated by Python * callback handlers. */ + +#warning Probably most of this callback handler should become Python code + static int validation_status_x509_verify_cert_cb(int ok, X509_STORE_CTX *ctx, PyObject *status) { @@ -1233,7 +1210,7 @@ validation_status_x509_verify_cert_cb(int ok, X509_STORE_CTX *ctx, PyObject *sta * "expire". */ -#warning Should be kept in C +#warning Could be done in Python return 1; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: @@ -1251,8 +1228,10 @@ validation_status_x509_verify_cert_cb(int ok, X509_STORE_CTX *ctx, PyObject *sta * output not to work with other OpenSSL-based applications. */ #warning Could be done in Python +#if 0 if (allow_non_self_signed_trust_anchor) ok = 1; +#endif record_validation_status(status, TRUST_ANCHOR_NOT_SELF_SIGNED); return ok; @@ -1269,6 +1248,9 @@ validation_status_x509_verify_cert_cb(int ok, X509_STORE_CTX *ctx, PyObject *sta } return ok; } + + error: + return ok; } @@ -1287,7 +1269,6 @@ validation_status_x509_verify_cert_cb(int ok, X509_STORE_CTX *ctx, PyObject *sta * must be of type PrintableString. */ -#warning Should be kept in C static int check_allowed_dn(X509_NAME *dn) { X509_NAME_ENTRY *ne; @@ -1326,7 +1307,6 @@ static int check_allowed_dn(X509_NAME *dn) * Check whether an ASN.1 TIME value conforms to RFC 5280 4.1.2.5. */ -#warning Should be kept in C static int check_allowed_time_encoding(ASN1_TIME *t) { switch (t->type) { @@ -1346,42 +1326,12 @@ static int check_allowed_time_encoding(ASN1_TIME *t) * Compare filename fields of two FileAndHash structures. */ -static int FileAndHash_name_cmp(const FileAndHash * const *a, const FileAndHash * const *b) +static int check_manifest_FileAndHash_name_cmp(const FileAndHash * const *a, const FileAndHash * const *b) { return strcmp((char *) (*a)->file->data, (char *) (*b)->file->data); } /* - * Check to see whether an AKI extension is present, is of the right - * form, and matches the issuer. - */ - -#warning Probably could be done in Python -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 (issuer != NULL && issuer->skid == NULL) - /* Called for side effect of running x509v3_cache_extensions() */ - (void) X509_check_ca(issuer); - - if (issuer == NULL || issuer->skid == NULL) - lose("Could not find issuer SKI"); - - 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. */ @@ -1402,19 +1352,29 @@ static int check_x509(X509 *x, unsigned ski_hashlen, afi; int i, ok, crit, loc, ex_count, is_ca, routercert = 0, ret = 0; -#warning Should remain in C if (!check_allowed_time_encoding(X509_get_notBefore(x)) || !check_allowed_time_encoding(X509_get_notAfter(x))) - lose_validation_error_from_code(status, NONCONFORMANT_ASN1_TIME_VALUE); + record_validation_status(status, NONCONFORMANT_ASN1_TIME_VALUE); + + if (x->cert_info == NULL || + x->cert_info->signature == NULL || + x->cert_info->signature->algorithm == NULL || + OBJ_obj2nid(x->cert_info->signature->algorithm) != NID_sha256WithRSAEncryption) + record_validation_status(status, NONCONFORMANT_SIGNATURE_ALGORITHM); + + if (!check_allowed_dn(X509_get_subject_name(x))) + record_validation_status(status, NONCONFORMANT_SUBJECT_NAME); + + if (!check_allowed_dn(X509_get_issuer_name(x))) + record_validation_status(status, NONCONFORMANT_ISSUER_NAME); /* * 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. */ -#warning Should remain in C if (!x->cert_info || x->cert_info->issuerUID || x->cert_info->subjectUID) - lose_validation_error_from_code(status, NONCONFORMANT_CERTIFICATE_UID); + record_validation_status(status, NONCONFORMANT_CERTIFICATE_UID); /* * Keep track of allowed extensions we've seen. Once we've @@ -1433,20 +1393,22 @@ static int check_x509(X509 *x, if ((bc = X509_get_ext_d2i(x, 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); + record_validation_status(status, MALFORMED_BASIC_CONSTRAINTS); } is_ca = bc != NULL; if (is_ta && !is_ca) - lose_validation_error_from_code(status, MALFORMED_TRUST_ANCHOR); + record_validation_status(status, MALFORMED_TRUST_ANCHOR); /* - * Check for presence of AIA, SIA, and CRLDP, but leave URI checking for Python code. + * Check for presence of AIA, SIA, and CRLDP, and make sure that + * they're in the correct format, but leave checking of the URIs for + * Python code. */ #warning Why are we not checking the critical flag on these extensions? -#warning We may need to check that these extensions only contain URIs +#warning We may need to check that these extensions only contain URIs (see extract_crldp_uri() and extract_access_uri()) if ((aia = X509_get_ext_d2i(x, NID_info_access, NULL, NULL)) != NULL) ex_count--; @@ -1472,33 +1434,12 @@ static int check_x509(X509 *x, if ((eku = X509_get_ext_d2i(x, NID_ext_key_usage, &crit, NULL)) != NULL) { ex_count--; if (crit || is_ca || 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; + record_validation_status(status, INAPPROPRIATE_EKU_EXTENSION); + else + for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) + routercert |= OBJ_obj2nid(sk_ASN1_OBJECT_value(eku, i)) == NID_id_kp_bgpsec_router; } -#warning Should remain in C - if (x->cert_info == NULL || - x->cert_info->signature == NULL || - x->cert_info->signature->algorithm == NULL || - OBJ_obj2nid(x->cert_info->signature->algorithm) != NID_sha256WithRSAEncryption) - lose_validation_error_from_code(status, NONCONFORMANT_SIGNATURE_ALGORITHM); - -#warning Part of this needs to remain in C - if (x->skid) - ex_count--; - else - lose_validation_error_from_code(status, SKI_EXTENSION_MISSING); - -#warning Should remain in C - if (!check_allowed_dn(X509_get_subject_name(x))) - lose_validation_error_from_code_maybe(allow_nonconformant_name, status, NONCONFORMANT_SUBJECT_NAME); - -#warning Should remain in C - if (!check_allowed_dn(X509_get_issuer_name(x))) - lose_validation_error_from_code_maybe(allow_nonconformant_name, status, NONCONFORMANT_ISSUER_NAME); - -#warning Should remain in C if ((policies = X509_get_ext_d2i(x, NID_certificate_policies, &crit, NULL)) != NULL) { POLICYQUALINFO *qualifier = NULL; POLICYINFO *policy = NULL; @@ -1510,19 +1451,20 @@ static int check_x509(X509 *x, (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; + record_validation_status(status, BAD_CERTIFICATE_POLICY); + else if (qualifier != NULL) + record_validation_status(status, POLICY_QUALIFIER_CPS); } -#warning Should remain in C - if (!X509_EXTENSION_get_critical(X509_get_ext(x, X509_get_ext_by_NID(x, NID_key_usage, -1))) || - (x->ex_flags & EXFLAG_KUSAGE) == 0 || - x->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 ((x->ex_flags & EXFLAG_KUSAGE) == 0) + record_validation_status(status, KEY_USAGE_MISSING); + else { + ex_count--; + if (!X509_EXTENSION_get_critical(X509_get_ext(x, X509_get_ext_by_NID(x, NID_key_usage, -1))) || + x->ex_kusage != (is_ca ? KU_KEY_CERT_SIGN | KU_CRL_SIGN : KU_DIGITAL_SIGNATURE)) + record_validation_status(status, BAD_KEY_USAGE); + } -#warning Should remain in C if (x->rfc3779_addr) { ex_count--; if (routercert || @@ -1530,18 +1472,18 @@ static int check_x509(X509 *x, !X509_EXTENSION_get_critical(X509_get_ext(x, loc)) || !v3_addr_is_canonical(x->rfc3779_addr) || sk_IPAddressFamily_num(x->rfc3779_addr) == 0) - lose_validation_error_from_code(status, BAD_IPADDRBLOCKS); - for (i = 0; i < sk_IPAddressFamily_num(x->rfc3779_addr); i++) { - IPAddressFamily *f = sk_IPAddressFamily_value(x->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); - } + record_validation_status(status, BAD_IPADDRBLOCKS); + else + for (i = 0; i < sk_IPAddressFamily_num(x->rfc3779_addr); i++) { + IPAddressFamily *f = sk_IPAddressFamily_value(x->rfc3779_addr, i); + afi = v3_addr_get_afi(f); + if (afi != IANA_AFI_IPV4 && afi != IANA_AFI_IPV6) + record_validation_status(status, UNKNOWN_AFI); + else if (f->addressFamily->length != 2) + record_validation_status(status, SAFI_NOT_ALLOWED); + } } -#warning Should remain in C if (x->rfc3779_asid) { ex_count--; if ((loc = X509_get_ext_by_NID(x, NID_sbgp_autonomousSysNum, -1)) < 0 || @@ -1550,14 +1492,12 @@ static int check_x509(X509 *x, x->rfc3779_asid->asnum == NULL || x->rfc3779_asid->rdi != NULL || (routercert && x->rfc3779_asid->asnum->type == ASIdentifierChoice_inherit)) - lose_validation_error_from_code(status, BAD_ASIDENTIFIERS); + record_validation_status(status, BAD_ASIDENTIFIERS); } -#warning Should remain in C if (!x->rfc3779_addr && !x->rfc3779_asid) - lose_validation_error_from_code(status, MISSING_RESOURCES); + record_validation_status(status, MISSING_RESOURCES); -#warning Should remain in C subject_pkey = X509_get_pubkey(x); ok = subject_pkey != NULL; if (ok) { @@ -1568,16 +1508,19 @@ static int check_x509(X509 *x, 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; + ok = (EVP_PKEY_type(subject_pkey->type) == EVP_PKEY_RSA && + BN_get_word(subject_pkey->pkey.rsa->e) == 65537 && + BN_num_bits(subject_pkey->pkey.rsa->n) == 2048); break; case NID_X9_62_id_ecPublicKey: ok = !is_ca && routercert; + /* + * Perhaps this should also be testing: + * + * && EVP_PKEY_type(subject_pkey->type) == EVP_PKEY_EC) + * && EC_GROUP_get_curve_name(EC_KEY_get0_group(subject_pkey->pkey.ec)) == NID_X9_62_prime_field + */ break; default: @@ -1585,34 +1528,32 @@ static int check_x509(X509 *x, } } if (!ok) - lose_validation_error_from_code(status, BAD_PUBLIC_KEY); - -#warning Should remain in C - if (x->skid == NULL || - (ski_pubkey = X509_get0_pubkey_bitstr(x)) == NULL || - !EVP_Digest(ski_pubkey->data, ski_pubkey->length, - ski_hashbuf, &ski_hashlen, EVP_sha1(), NULL) || - ski_hashlen != 20 || - ski_hashlen != x->skid->length || - memcmp(ski_hashbuf, x->skid->data, ski_hashlen)) - lose_validation_error_from_code(status, SKI_PUBLIC_KEY_MISMATCH); - -#warning Should remain in C (at least partially) - if (x->akid) { + record_validation_status(status, BAD_PUBLIC_KEY); + + if (!x->skid) + record_validation_status(status, SKI_EXTENSION_MISSING); + else { ex_count--; - if (!check_aki(status, issuer, x->akid)) - goto error; + if ((ski_pubkey = X509_get0_pubkey_bitstr(x)) == NULL || + !EVP_Digest(ski_pubkey->data, ski_pubkey->length, + ski_hashbuf, &ski_hashlen, EVP_sha1(), NULL) || + ski_hashlen != 20 || + ski_hashlen != x->skid->length || + memcmp(ski_hashbuf, x->skid->data, ski_hashlen)) + record_validation_status(status, SKI_PUBLIC_KEY_MISMATCH); } -#warning Could be done in Python - if (!x->akid && !is_ta) - lose_validation_error_from_code(status, AKI_EXTENSION_MISSING); + if (x->akid) { + ex_count--; + if (!x->akid->keyid || x->akid->serial || x->akid->issuer) + record_validation_status(status, AKI_EXTENSION_WRONG_FORMAT); + } if ((issuer_pkey = X509_get_pubkey(issuer)) == NULL || X509_verify(x, issuer_pkey) <= 0) - lose_validation_error_from_code(status, CERTIFICATE_BAD_SIGNATURE); + record_validation_status(status, CERTIFICATE_BAD_SIGNATURE); if (ex_count > 0) - lose_validation_error_from_code(status, DISALLOWED_X509V3_EXTENSION); + record_validation_status(status, DISALLOWED_X509V3_EXTENSION); #warning Move policy stuff to optional OID in base X509.verify() /* @@ -1654,35 +1595,30 @@ static int check_crl(X509_CRL *crl, EVP_PKEY *pkey; int i, ret = 0; -#warning Should be kept in C if (!crl->crl || !crl->crl->sig_alg || !crl->crl->sig_alg->algorithm || OBJ_obj2nid(crl->crl->sig_alg->algorithm) != NID_sha256WithRSAEncryption) - lose_validation_error_from_code(status, NONCONFORMANT_SIGNATURE_ALGORITHM); + record_validation_status(status, NONCONFORMANT_SIGNATURE_ALGORITHM); -#warning Should be kept in C if (!check_allowed_time_encoding(X509_CRL_get_lastUpdate(crl)) || !check_allowed_time_encoding(X509_CRL_get_nextUpdate(crl))) - lose_validation_error_from_code(status, NONCONFORMANT_ASN1_TIME_VALUE); + record_validation_status(status, NONCONFORMANT_ASN1_TIME_VALUE); -#warning Could be done in Python - if (!check_aki(status, issuer, crl->akid)) - goto error; + if (crl->akid == NULL) + record_validation_status(status, AKI_EXTENSION_MISSING); + else if (!crl->akid->keyid || crl->akid->serial || crl->akid->issuer) + record_validation_status(status, AKI_EXTENSION_WRONG_FORMAT); -#warning Should be kept in C - if (X509_CRL_get_ext_count(crl) != 2) - lose_validation_error_from_code(status, DISALLOWED_X509V3_EXTENSION); + if (X509_CRL_get_ext_count(crl) > 2) + record_validation_status(status, DISALLOWED_X509V3_EXTENSION); -#warning Should be kept in C if (!check_allowed_dn(X509_CRL_get_issuer(crl))) - lose_validation_error_from_code_maybe(allow_nonconformant_name, status, NONCONFORMANT_ISSUER_NAME); + record_validation_status(status, NONCONFORMANT_ISSUER_NAME); -#warning Should be kept in C if ((revoked = X509_CRL_get_REVOKED(crl)) != NULL) for (i = sk_X509_REVOKED_num(revoked) - 1; i >= 0; --i) if (X509_REVOKED_get_ext_count(sk_X509_REVOKED_value(revoked, i)) > 0) - lose_validation_error_from_code(status, DISALLOWED_X509V3_EXTENSION); + record_validation_status(status, DISALLOWED_X509V3_EXTENSION); -#warning Should be kept in C if ((pkey = X509_get_pubkey(issuer)) != NULL) { ret = X509_CRL_verify(crl, pkey) > 0; EVP_PKEY_free(pkey); @@ -1695,7 +1631,7 @@ static int check_crl(X509_CRL *crl, /* * Extract one datum from a CMS_SignerInfo. */ -#warning Should be kept in C + static void *extract_si_datum(CMS_SignerInfo *si, int *n, const int optional, @@ -1744,37 +1680,35 @@ static int check_cms(CMS_ContentInfo *cms, X509 *x = NULL; int i, ret = 0; -#warning Could be done in Python if ((crls = CMS_get1_crls(cms)) != NULL) - lose_validation_error_from_code(status, CMS_INCLUDES_CRLS); + record_validation_status(status, CMS_INCLUDES_CRLS); -#warning Should be kept in C if ((signer_infos = CMS_get0_SignerInfos(cms)) == NULL || sk_CMS_SignerInfo_num(signer_infos) != 1 || (si = sk_CMS_SignerInfo_value(signer_infos, 0)) == NULL || !CMS_SignerInfo_get0_signer_id(si, &sid, &si_issuer, &si_serial) || sid == NULL || si_issuer != NULL || si_serial != NULL || CMS_unsigned_get_attr_count(si) != -1) - lose_validation_error_from_code(status, BAD_CMS_SIGNER_INFOS); + record_validation_status(status, BAD_CMS_SIGNER_INFOS); - CMS_SignerInfo_get0_algs(si, NULL, &x, &digest_alg, &signature_alg); + if (si != NULL) + CMS_SignerInfo_get0_algs(si, NULL, &x, &digest_alg, &signature_alg); if (x == NULL) - lose_validation_error_from_code(status, CMS_SIGNER_MISSING); - - if ((certs = CMS_get1_certs(cms)) == NULL || + record_validation_status(status, CMS_SIGNER_MISSING); + else if ((certs = CMS_get1_certs(cms)) == NULL || sk_X509_num(certs) != 1 || X509_cmp(x, sk_X509_value(certs, 0))) - lose_validation_error_from_code(status, BAD_CMS_SIGNER); + record_validation_status(status, BAD_CMS_SIGNER); X509_ALGOR_get0(&oid, NULL, NULL, signature_alg); i = OBJ_obj2nid(oid); if (i != NID_sha256WithRSAEncryption && i != NID_rsaEncryption) - lose_validation_error_from_code(status, WRONG_CMS_SI_SIGNATURE_ALGORITHM); + record_validation_status(status, WRONG_CMS_SI_SIGNATURE_ALGORITHM); X509_ALGOR_get0(&oid, NULL, NULL, digest_alg); if (OBJ_obj2nid(oid) != NID_sha256) - lose_validation_error_from_code(status, WRONG_CMS_SI_DIGEST_ALGORITHM); + record_validation_status(status, WRONG_CMS_SI_DIGEST_ALGORITHM); i = CMS_signed_get_attr_count(si); @@ -1784,13 +1718,13 @@ static int check_cms(CMS_ContentInfo *cms, (void) extract_si_datum(si, &i, 0, NID_pkcs9_messageDigest, V_ASN1_OCTET_STRING); if (i != 0) - lose_validation_error_from_code_maybe(allow_wrong_cms_si_attributes, status, BAD_CMS_SI_SIGNED_ATTRIBUTES); + record_validation_status(status, BAD_CMS_SI_SIGNED_ATTRIBUTES); if (OBJ_cmp(oid, CMS_get0_eContentType(cms)) != 0) - lose_validation_error_from_code(status, BAD_CMS_SI_CONTENTTYPE); + record_validation_status(status, BAD_CMS_SI_CONTENTTYPE); if (CMS_SignerInfo_cert_cmp(si, x)) - lose_validation_error_from_code(status, CMS_SKI_MISMATCH); + record_validation_status(status, CMS_SKI_MISMATCH); ret = 1; @@ -1805,6 +1739,8 @@ static int check_cms(CMS_ContentInfo *cms, * Check a lot of pesky low-level things about RPKI manifests. */ +#warning Almost everything in this function could be done in Python + static int check_manifest(CMS_ContentInfo *cms, Manifest *manifest, PyObject *status) @@ -1816,40 +1752,43 @@ static int check_manifest(CMS_ContentInfo *cms, #warning Could be done in Python if (OBJ_obj2nid(CMS_get0_eContentType(cms)) != NID_ct_rpkiManifest) - lose_validation_error_from_code(status, BAD_CMS_ECONTENTTYPE); + record_validation_status(status, BAD_CMS_ECONTENTTYPE); -#warning Should be kept in C +#warning Can check value in Python, but not whether encoding was defaulted if (manifest->version) - lose_validation_error_from_code(status, WRONG_OBJECT_VERSION); + record_validation_status(status, WRONG_OBJECT_VERSION); #warning Could be done in Python if ((certs = CMS_get1_certs(cms)) == NULL || sk_X509_num(certs) != 1) - lose_validation_error_from_code(status, BAD_CMS_SIGNER); + record_validation_status(status, BAD_CMS_SIGNER); #warning Could be done in Python if (ASN1_INTEGER_cmp(manifest->manifestNumber, asn1_zero) < 0 || ASN1_INTEGER_cmp(manifest->manifestNumber, asn1_twenty_octets) > 0) - lose_validation_error_from_code(status, BAD_MANIFEST_NUMBER); + record_validation_status(status, BAD_MANIFEST_NUMBER); #warning Could be done in Python if (OBJ_obj2nid(manifest->fileHashAlg) != NID_sha256) - lose_validation_error_from_code(status, NONCONFORMANT_DIGEST_ALGORITHM); + record_validation_status(status, NONCONFORMANT_DIGEST_ALGORITHM); if ((sorted_fileList = sk_FileAndHash_dup(manifest->fileList)) == NULL) lose_no_memory(); - (void) sk_FileAndHash_set_cmp_func(sorted_fileList, FileAndHash_name_cmp); +#warning Could be done in Python + (void) sk_FileAndHash_set_cmp_func(sorted_fileList, check_manifest_FileAndHash_name_cmp); sk_FileAndHash_sort(sorted_fileList); +#warning Could be done in Python for (i = 0; ((fah1 = sk_FileAndHash_value(sorted_fileList, i + 0)) != NULL && (fah2 = sk_FileAndHash_value(sorted_fileList, i + 1)) != NULL); i++) if (!strcmp((char *) fah1->file->data, (char *) fah2->file->data)) - lose_validation_error_from_code(status, DUPLICATE_NAME_IN_MANIFEST); + record_validation_status(status, DUPLICATE_NAME_IN_MANIFEST); +#warning Could be done in Python for (i = 0; (fah1 = sk_FileAndHash_value(manifest->fileList, i)) != NULL; i++) if (fah1->hash->length != HASH_SHA256_LEN || (fah1->hash->flags & (ASN1_STRING_FLAG_BITS_LEFT | 7)) > ASN1_STRING_FLAG_BITS_LEFT) - lose_validation_error_from_code(status, BAD_MANIFEST_DIGEST_LENGTH); + record_validation_status(status, BAD_MANIFEST_DIGEST_LENGTH); ret = 1; @@ -1863,11 +1802,11 @@ static int check_manifest(CMS_ContentInfo *cms, /* * Extract a ROA prefix from the ASN.1 bitstring encoding. */ -static int extract_roa_prefix(const ROAIPAddress *ra, - const unsigned afi, - unsigned char *addr, - unsigned *prefixlen, - unsigned *max_prefixlen) +static int check_roa_extract_roa_prefix(const ROAIPAddress *ra, + const unsigned afi, + unsigned char *addr, + unsigned *prefixlen, + unsigned *max_prefixlen) { unsigned length; long maxlen; @@ -1919,23 +1858,22 @@ static int check_roa(CMS_ContentInfo *cms, #warning Could be done in Python if (OBJ_obj2nid(CMS_get0_eContentType(cms)) != NID_ct_ROA) - lose_validation_error_from_code(status, BAD_CMS_ECONTENTTYPE); + record_validation_status(status, BAD_CMS_ECONTENTTYPE); -#warning Should be kept in C if (roa->version) - lose_validation_error_from_code(status, WRONG_OBJECT_VERSION); + record_validation_status(status, WRONG_OBJECT_VERSION); #warning Could be done in Python if (ASN1_INTEGER_cmp(roa->asID, asn1_zero) < 0 || ASN1_INTEGER_cmp(roa->asID, asn1_four_octets) > 0) - lose_validation_error_from_code(status, BAD_ROA_ASID); + record_validation_status(status, BAD_ROA_ASID); #warning Could be done in Python if ((certs = CMS_get1_certs(cms)) == NULL || sk_X509_num(certs) != 1) - lose_validation_error_from_code(status, BAD_CMS_SIGNER); + record_validation_status(status, BAD_CMS_SIGNER); if ((ee_resources = X509_get_ext_d2i(sk_X509_value(certs, 0), NID_sbgp_ipAddrBlock, NULL, NULL)) == NULL) - lose_validation_error_from_code(status, BAD_IPADDRBLOCKS); + record_validation_status(status, BAD_IPADDRBLOCKS); /* * Convert ROA prefixes to resource set. This goes on a bit. @@ -1951,7 +1889,7 @@ static int check_roa(CMS_ContentInfo *cms, lose_no_memory(); if (rf->addressFamily->length < 2 || rf->addressFamily->length > 3) - lose_validation_error_from_code(status, MALFORMED_ROA_ADDRESSFAMILY); + record_validation_status(status, MALFORMED_ROA_ADDRESSFAMILY); afi = (rf->addressFamily->data[0] << 8) | (rf->addressFamily->data[1]); if (rf->addressFamily->length == 3) @@ -1961,12 +1899,12 @@ static int check_roa(CMS_ContentInfo *cms, ra = sk_ROAIPAddress_value(rf->addresses, j); if (ra == NULL || - !extract_roa_prefix(ra, afi, addrbuf, &prefixlen, &max_prefixlen) || + !check_roa_extract_roa_prefix(ra, afi, addrbuf, &prefixlen, &max_prefixlen) || !v3_addr_add_prefix(roa_resources, afi, safi, addrbuf, prefixlen)) - lose_validation_error_from_code(status, ROA_RESOURCES_MALFORMED); + record_validation_status(status, ROA_RESOURCES_MALFORMED); - if (max_prefixlen < prefixlen) - lose_validation_error_from_code(status, ROA_MAX_PREFIXLEN_TOO_SHORT); + else if (max_prefixlen < prefixlen) + record_validation_status(status, ROA_MAX_PREFIXLEN_TOO_SHORT); } } @@ -1984,7 +1922,7 @@ static int check_roa(CMS_ContentInfo *cms, IPAddressFamily *f = sk_IPAddressFamily_value(roa_resources, i); if ((afi = v3_addr_get_afi(f)) == 0) - lose_validation_error_from_code(status, ROA_CONTAINS_BAD_AFI_VALUE); + record_validation_status(status, ROA_CONTAINS_BAD_AFI_VALUE); if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges) { IPAddressOrRanges *aors = f->ipAddressChoice->u.addressesOrRanges; @@ -2001,7 +1939,7 @@ static int check_roa(CMS_ContentInfo *cms, #warning Handling of length here looks weird, double check if ((length = v3_addr_get_range(a, afi, a_min, a_max, RAW_IPADDR_BUFLEN)) == 0 || (length = v3_addr_get_range(b, afi, b_min, b_max, RAW_IPADDR_BUFLEN)) == 0) - lose_validation_error_from_code(status, ROA_RESOURCES_MALFORMED); + record_validation_status(status, ROA_RESOURCES_MALFORMED); if (memcmp(a_max, b_max, length) >= 0) { (void) sk_IPAddressOrRange_delete(aors, j + 1); @@ -2013,10 +1951,10 @@ static int check_roa(CMS_ContentInfo *cms, } if (!v3_addr_canonize(roa_resources)) - lose_validation_error_from_code(status, ROA_RESOURCES_MALFORMED); + record_validation_status(status, ROA_RESOURCES_MALFORMED); - if (!v3_addr_subset(roa_resources, ee_resources)) - lose_validation_error_from_code(status, ROA_RESOURCE_NOT_IN_EE); + if (ee_resources == NULL || !v3_addr_subset(roa_resources, ee_resources)) + record_validation_status(status, ROA_RESOURCE_NOT_IN_EE); result = 1; @@ -7490,10 +7428,8 @@ cms_object_verify_helper(cms_object *self, PyObject *args, PyObject *kwds, PyObj assert_no_unhandled_openssl_errors(); if (CMS_verify(self->cms, certs_stack, NULL, NULL, bio, flags) <= 0) { - if (*status == Py_None) - lose_openssl_error("Couldn't verify CMS message"); - else - lose_validation_error_from_code(*status, CMS_VALIDATION_FAILURE); + record_validation_status(*status, CMS_VALIDATION_FAILURE); + lose_openssl_error("Couldn't verify CMS message"); } if (*status != Py_None && !check_cms(self->cms, *status)) @@ -7834,10 +7770,8 @@ manifest_object_verify(manifest_object *self, PyObject *args, PyObject *kwds) goto error; if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(Manifest), bio, &self->manifest)) { - if (status == Py_None) - lose_openssl_error("Couldn't decode manifest"); - else - lose_validation_error_from_code(status, CMS_ECONTENT_DECODE_ERROR); + record_validation_status(status, CMS_ECONTENT_DECODE_ERROR); + lose_openssl_error("Couldn't decode manifest"); } if (status != Py_None && !check_manifest(self->cms.cms, self->manifest, status)) @@ -8517,10 +8451,8 @@ roa_object_verify(roa_object *self, PyObject *args, PyObject *kwds) goto error; if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, &self->roa)) { - if (status == Py_None) - lose_openssl_error("Couldn't decode ROA"); - else - lose_validation_error_from_code(status, CMS_ECONTENT_DECODE_ERROR); + record_validation_status(status, CMS_ECONTENT_DECODE_ERROR); + lose_openssl_error("Couldn't decode ROA"); } if (status != Py_None && !check_roa(self->cms.cms, self->roa, status)) diff --git a/rp/rcynic/rcynicng b/rp/rcynic/rcynicng index 6c0a9b46..b06154cb 100755 --- a/rp/rcynic/rcynicng +++ b/rp/rcynic/rcynicng @@ -131,29 +131,30 @@ def install_object(obj): def final_install(): - real_old = os.path.realpath(old_authenticated).rstrip("/") - real_new = os.path.realpath(new_authenticated).rstrip("/") - - fn = args.authenticated.rstrip("/") + ".new" - logger.debug("Symlinking %s to %s", os.path.basename(real_new), args.authenticated) - if os.path.exists(fn): - os.unlink(fn) - os.symlink(os.path.basename(real_new), fn) - os.rename(fn, args.authenticated) - - if os.path.isdir(real_old): - fn = args.authenticated.rstrip("/") + ".old" - logger.debug("Symlinking %s to %s", os.path.basename(real_old), fn) - if os.path.exists(fn): - os.unlink(fn) - os.symlink(os.path.basename(real_old), fn) - - dn = os.path.dirname(args.authenticated.rstrip("/")) - for fn in os.listdir(dn): - fn = os.path.join(dn, fn) - if fn.startswith(args.authenticated.rstrip("/") + ".") and os.path.realpath(fn) not in (real_new, real_old): - logger.debug("Removing %s", fn) - shutil.rmtree(fn) + cur_link = old_authenticated + new_link = cur_link + ".new" + old_link = cur_link + ".old" + dir_base = os.path.realpath(cur_link + ".") + new_real = os.path.realpath(new_authenticated) + old_real = os.path.realpath(old_authenticated) + + if os.path.islink(old_link): + logger.debug("Unlinking %s", old_link) + os.unlink(old_link) + + if os.path.isdir(old_real): + logger.debug("Symlinking %s to %s", os.path.basename(old_real), old_link) + os.symlink(os.path.basename(old_real), old_link) + + logger.debug("Symlinking %s to %s", os.path.basename(new_real), cur_link) + os.symlink(os.path.basename(new_real), new_link) + os.rename(new_link, cur_link) + + for path in os.listdir(os.path.dirname(dir_base)): + path = os.path.realpath(os.path.join(os.path.dirname(dir_base), path)) + if path.startswith(dir_base) and path not in (new_real, old_real) and os.path.isdir(path): + logger.debug("Removing %s", path) + shutil.rmtree(path) class X509(rpki.POW.X509): @@ -225,6 +226,10 @@ class X509(rpki.POW.X509): status.add(codes.CRLDP_EXTENSION_FORBIDDEN) if not is_ta and self.crldp is None: status.add(codes.CRLDP_EXTENSION_MISSING) + if not is_ta and self.aki is None: + status.add(codes.AKI_EXTENSION_MISSING) + elif not is_ta and self.aki != trusted[0].ski: + status.add(codes.AKI_EXTENSION_ISSUER_MISMATCH) serial = self.getSerial() if serial <= 0 or serial > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF: status.add(codes.BAD_CERTIFICATE_SERIAL_NUMBER) @@ -308,6 +313,10 @@ class CRL(rpki.POW.CRL): status.add(codes.CRL_NUMBER_OUT_OF_RANGE) if self.getIssuer() != issuer.getSubject(): status.add(codes.CRL_ISSUER_NAME_MISMATCH) + if self.aki is None: + status.add(codes.AKI_EXTENSION_MISSING) + elif self.aki != issuer.ski: + status.add(codes.AKI_EXTENSION_ISSUER_MISMATCH) return not any(s.kind == "bad" for s in status) @@ -947,7 +956,7 @@ def main(): global new_authenticated, old_authenticated new_authenticated = args.authenticated.rstrip("/") + time.strftime(".%Y-%m-%dT%H:%M:%SZ") - old_authenticated = args.authenticated + old_authenticated = args.authenticated.rstrip("/") Generation("current", args.unauthenticated) Generation("backup", old_authenticated) diff --git a/rpki/POW/__init__.py b/rpki/POW/__init__.py index fdb2143b..a00c8fd8 100644 --- a/rpki/POW/__init__.py +++ b/rpki/POW/__init__.py @@ -164,7 +164,8 @@ validation_status = StatusCodeDB( UNREADABLE_TRUST_ANCHOR = "Unreadable trust anchor", UNREADABLE_TRUST_ANCHOR_LOCATOR = "Unreadable trust anchor locator", WRONG_OBJECT_VERSION = "Wrong object version", - OBJECT_NOT_FOUND = "Object not found"), + OBJECT_NOT_FOUND = "Object not found", + KEY_USAGE_MISSING = "Key usage missing"), warn = dict( AIA_DOESNT_MATCH_ISSUER = "AIA doesn't match issuer", |