aboutsummaryrefslogtreecommitdiff
path: root/ext/POW.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/POW.c')
-rw-r--r--ext/POW.c1760
1 files changed, 1273 insertions, 487 deletions
diff --git a/ext/POW.c b/ext/POW.c
index 990d344d..c7325666 100644
--- a/ext/POW.c
+++ b/ext/POW.c
@@ -80,7 +80,6 @@
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/err.h>
-#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/cms.h>
@@ -112,9 +111,12 @@ define GCC_UNUSED
*/
#define MAX_ASN1_INTEGER_LEN 20
+/*
+ * How many bytes is a SHA256 digest?
+ */
+#define HASH_SHA256_LEN 32
+
/* Digests */
-#define MD5_DIGEST 2
-#define SHA_DIGEST 3
#define SHA1_DIGEST 4
#define SHA256_DIGEST 6
#define SHA384_DIGEST 7
@@ -130,7 +132,6 @@ define GCC_UNUSED
/* Object check functions */
#define POW_X509_Check(op) PyObject_TypeCheck(op, &POW_X509_Type)
-#define POW_X509Store_Check(op) PyObject_TypeCheck(op, &POW_X509Store_Type)
#define POW_X509StoreCTX_Check(op) PyObject_TypeCheck(op, &POW_X509StoreCTX_Type)
#define POW_CRL_Check(op) PyObject_TypeCheck(op, &POW_CRL_Type)
#define POW_Asymmetric_Check(op) PyObject_TypeCheck(op, &POW_Asymmetric_Type)
@@ -162,12 +163,40 @@ 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_ad_signedObject
+static int NID_ad_signedObject;
+#endif
+
+#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_signedObject
-static int NID_signedObject;
+#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
+
+#ifndef NID_binary_signing_time
+static int NID_binary_signing_time;
#endif
static const struct {
@@ -177,12 +206,40 @@ 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_ad_signedObject
+ {&NID_ad_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"},
+#endif
+
+#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_signedObject
- {&NID_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"}
+#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
+
+#ifndef NID_binary_signing_time
+ {&NID_binary_signing_time, "1.2.840.113549.1.9.16.2.46", "id-aa-binarySigningTime", "CMS Binary Signing Time"},
#endif
};
@@ -235,7 +292,8 @@ static PyObject
*ErrorObject,
*OpenSSLErrorObject,
*POWErrorObject,
- *NotVerifiedErrorObject;
+ *NotVerifiedErrorObject,
+ *ValidationErrorObject;
/*
* Constructor for customized datetime class.
@@ -251,12 +309,17 @@ static PyObject *custom_datetime;
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;
+
+/*
* Declarations of type objects (definitions come later).
*/
static PyTypeObject
POW_X509_Type,
- POW_X509Store_Type,
POW_X509StoreCTX_Type,
POW_CRL_Type,
POW_Asymmetric_Type,
@@ -286,14 +349,8 @@ typedef struct {
typedef struct {
PyObject_HEAD
- X509_STORE *store;
- PyObject *ctxclass;
-} x509_store_object;
-
-typedef struct {
- PyObject_HEAD
X509_STORE_CTX *ctx;
- x509_store_object *store;
+ X509_STORE *store;
} x509_store_ctx_object;
typedef struct {
@@ -401,6 +458,12 @@ typedef struct {
goto error; \
} while (0)
+#define lose_validation_error(_msg_) \
+ do { \
+ PyErr_SetString(ValidationErrorObject, (_msg_)); \
+ goto error; \
+ } while (0)
+
#define assert_no_unhandled_openssl_errors() \
do { \
if (ERR_peek_error()) { \
@@ -427,8 +490,6 @@ static const EVP_MD *
evp_digest_factory(int digest_type)
{
switch (digest_type) {
- case MD5_DIGEST: return EVP_md5();
- case SHA_DIGEST: return EVP_sha();
case SHA1_DIGEST: return EVP_sha1();
case SHA256_DIGEST: return EVP_sha256();
case SHA384_DIGEST: return EVP_sha384();
@@ -524,7 +585,7 @@ x509_object_helper_set_name(PyObject *dn_obj)
goto error;
if (!PySequence_Check(rdn_obj) || PySequence_Size(rdn_obj) == 0)
- lose_type_error("each RDN must be a sequence with at least one element");
+ lose_type_error("Each RDN must be a sequence with at least one element");
for (j = 0; j < PySequence_Size(rdn_obj); j++) {
@@ -532,7 +593,7 @@ x509_object_helper_set_name(PyObject *dn_obj)
goto error;
if (!PySequence_Check(pair_obj) || PySequence_Size(pair_obj) != 2)
- lose_type_error("each name entry must be a two-element sequence");
+ lose_type_error("Each name entry must be a two-element sequence");
if ((type_obj = PySequence_GetItem(pair_obj, 0)) == NULL ||
(type_str = PyString_AsString(type_obj)) == NULL ||
@@ -677,7 +738,7 @@ x509_helper_iterable_to_stack(PyObject *iterable)
while ((item = PyIter_Next(iterator)) != NULL) {
if (!POW_X509_Check(item))
- lose_type_error("Inapropriate type");
+ lose_type_error("Expected an X509 object");
if (!sk_X509_push(stack, ((x509_object *) item)->x509))
lose("Couldn't add X509 object to stack");
@@ -1084,6 +1145,501 @@ 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 support from the
+ * Python side (see rpki/POW/__init__.py).
+ */
+
+/*
+ * Add code to status object, throwing an error if something goes
+ * horribly wrong.
+ */
+
+#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)
+{
+ if (status == Py_None)
+ return 1;
+ PyObject *value = PyString_FromString(code);
+ if (value == NULL)
+ return 0;
+ int result = PySet_Add(status, value);
+ Py_XDECREF(value);
+ return result == 0;
+}
+
+
+
+/*
+ * 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.
+ */
+
+/*
+ * 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;
+}
+
+/*
+ * Compare filename fields of two FileAndHash structures.
+ */
+
+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 a lot of pesky low-level things about RPKI CRLs.
+ */
+
+static int check_crl(X509_CRL *crl,
+ X509 *issuer,
+ PyObject *status)
+{
+ STACK_OF(X509_REVOKED) *revoked;
+ EVP_PKEY *pkey;
+ int i, ret = 0;
+
+ if (!crl->crl || !crl->crl->sig_alg || !crl->crl->sig_alg->algorithm ||
+ OBJ_obj2nid(crl->crl->sig_alg->algorithm) != NID_sha256WithRSAEncryption)
+ record_validation_status(status, NONCONFORMANT_SIGNATURE_ALGORITHM);
+
+ if (!check_allowed_time_encoding(X509_CRL_get_lastUpdate(crl)) ||
+ !check_allowed_time_encoding(X509_CRL_get_nextUpdate(crl)))
+ record_validation_status(status, NONCONFORMANT_ASN1_TIME_VALUE);
+
+ 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);
+
+ if (X509_CRL_get_ext_count(crl) > 2)
+ record_validation_status(status, DISALLOWED_X509V3_EXTENSION);
+
+ if (!check_allowed_dn(X509_CRL_get_issuer(crl)))
+ record_validation_status(status, NONCONFORMANT_ISSUER_NAME);
+
+ 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)
+ record_validation_status(status, DISALLOWED_X509V3_EXTENSION);
+
+ if ((pkey = X509_get_pubkey(issuer)) != NULL) {
+ ret = X509_CRL_verify(crl, pkey) > 0;
+ EVP_PKEY_free(pkey);
+ }
+
+ error:
+ return ret;
+}
+
+/*
+ * Extract one datum from a CMS_SignerInfo.
+ */
+
+static void *extract_si_datum(CMS_SignerInfo *si,
+ int *n,
+ const int optional,
+ const int nid,
+ const int asn1_type)
+{
+ int i = CMS_signed_get_attr_by_NID(si, nid, -1);
+ void *result = NULL;
+ X509_ATTRIBUTE *a;
+
+ if (i < 0 && optional)
+ return NULL;
+
+ if (i >= 0 &&
+ CMS_signed_get_attr_by_NID(si, nid, i) < 0 &&
+ (a = CMS_signed_get_attr(si, i)) != NULL &&
+ X509_ATTRIBUTE_count(a) == 1 &&
+ (result = X509_ATTRIBUTE_get0_data(a, 0, asn1_type, NULL)) != NULL)
+ --*n;
+ else
+ *n = -1;
+
+ return result;
+}
+
+/*
+ * Check a lot of pesky low-level things about RPKI CMS objects.
+ *
+ * We already have code elsewhere for checking X.509 certificates, so
+ * we assume that the caller has already used use that code to check
+ * the embedded EE certificate.
+ */
+
+static int check_cms(CMS_ContentInfo *cms,
+ PyObject *status)
+{
+ STACK_OF(CMS_SignerInfo) *signer_infos = NULL;
+ CMS_SignerInfo *si = NULL;
+ ASN1_OCTET_STRING *sid = NULL;
+ X509_NAME *si_issuer = NULL;
+ ASN1_INTEGER *si_serial = NULL;
+ STACK_OF(X509_CRL) *crls = NULL;
+ STACK_OF(X509) *certs = NULL;
+ X509_ALGOR *signature_alg = NULL, *digest_alg = NULL;
+ ASN1_OBJECT *oid = NULL;
+ X509 *x = NULL;
+ int i, ret = 0;
+
+ if ((crls = CMS_get1_crls(cms)) != NULL)
+ record_validation_status(status, CMS_INCLUDES_CRLS);
+
+ 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)
+ record_validation_status(status, BAD_CMS_SIGNER_INFOS);
+
+ if (si != NULL)
+ CMS_SignerInfo_get0_algs(si, NULL, &x, &digest_alg, &signature_alg);
+
+ if (x == 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)))
+ 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)
+ record_validation_status(status, WRONG_CMS_SI_SIGNATURE_ALGORITHM);
+
+ X509_ALGOR_get0(&oid, NULL, NULL, digest_alg);
+ if (OBJ_obj2nid(oid) != NID_sha256)
+ record_validation_status(status, WRONG_CMS_SI_DIGEST_ALGORITHM);
+
+ i = CMS_signed_get_attr_count(si);
+
+ (void) extract_si_datum(si, &i, 1, NID_pkcs9_signingTime, V_ASN1_UTCTIME);
+ (void) extract_si_datum(si, &i, 1, NID_binary_signing_time, V_ASN1_INTEGER);
+ oid = extract_si_datum(si, &i, 0, NID_pkcs9_contentType, V_ASN1_OBJECT);
+ (void) extract_si_datum(si, &i, 0, NID_pkcs9_messageDigest, V_ASN1_OCTET_STRING);
+
+ if (i != 0)
+ record_validation_status(status, BAD_CMS_SI_SIGNED_ATTRIBUTES);
+
+ if (OBJ_cmp(oid, CMS_get0_eContentType(cms)) != 0)
+ record_validation_status(status, BAD_CMS_SI_CONTENTTYPE);
+
+ if (CMS_SignerInfo_cert_cmp(si, x))
+ record_validation_status(status, CMS_SKI_MISMATCH);
+
+ ret = 1;
+
+ error:
+ sk_X509_CRL_pop_free(crls, X509_CRL_free);
+ sk_X509_pop_free(certs, X509_free);
+
+ return ret;
+}
+
+/*
+ * 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)
+{
+ STACK_OF(FileAndHash) *sorted_fileList = NULL;
+ FileAndHash *fah1 = NULL, *fah2 = NULL;
+ STACK_OF(X509) *certs = NULL;
+ int i, ret = 0;
+
+#warning Could be done in Python
+ if (OBJ_obj2nid(CMS_get0_eContentType(cms)) != NID_ct_rpkiManifest)
+ record_validation_status(status, BAD_CMS_ECONTENTTYPE);
+
+#warning Can check value in Python, but not whether encoding was defaulted
+ if (manifest->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)
+ 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)
+ record_validation_status(status, BAD_MANIFEST_NUMBER);
+
+#warning Could be done in Python
+ if (OBJ_obj2nid(manifest->fileHashAlg) != NID_sha256)
+ record_validation_status(status, NONCONFORMANT_DIGEST_ALGORITHM);
+
+ if ((sorted_fileList = sk_FileAndHash_dup(manifest->fileList)) == NULL)
+ lose_no_memory();
+
+#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))
+ 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)
+ record_validation_status(status, BAD_MANIFEST_DIGEST_LENGTH);
+
+ ret = 1;
+
+ error:
+ sk_FileAndHash_free(sorted_fileList);
+ sk_X509_pop_free(certs, X509_free);
+
+ return ret;
+}
+
+/*
+ * Extract a ROA prefix from the ASN.1 bitstring encoding.
+ */
+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;
+
+ assert(ra && addr && prefixlen && max_prefixlen);
+
+ maxlen = ASN1_INTEGER_get(ra->maxLength);
+
+ switch (afi) {
+ case IANA_AFI_IPV4: length = 4; break;
+ case IANA_AFI_IPV6: length = 16; break;
+ default: return 0;
+ }
+
+ if (ra->IPAddress->length < 0 || ra->IPAddress->length > length ||
+ maxlen < 0 || maxlen > (long) length * 8)
+ return 0;
+
+ if (ra->IPAddress->length > 0) {
+ memcpy(addr, ra->IPAddress->data, ra->IPAddress->length);
+ if ((ra->IPAddress->flags & 7) != 0) {
+ unsigned char mask = 0xFF >> (8 - (ra->IPAddress->flags & 7));
+ addr[ra->IPAddress->length - 1] &= ~mask;
+ }
+ }
+
+ memset(addr + ra->IPAddress->length, 0, length - ra->IPAddress->length);
+ *prefixlen = (ra->IPAddress->length * 8) - (ra->IPAddress->flags & 7);
+ *max_prefixlen = ra->maxLength ? (unsigned) maxlen : *prefixlen;
+
+ return 1;
+}
+
+/*
+ * Check a lot of pesky low-level things about RPKI ROAs.
+ */
+
+static int check_roa(CMS_ContentInfo *cms,
+ ROA *roa,
+ PyObject *status)
+{
+ STACK_OF(IPAddressFamily) *roa_resources = NULL, *ee_resources = NULL;
+ unsigned afi, *safi = NULL, safi_, prefixlen, max_prefixlen;
+ unsigned char addrbuf[RAW_IPADDR_BUFLEN];
+ STACK_OF(X509) *certs = NULL;
+ ROAIPAddressFamily *rf;
+ ROAIPAddress *ra;
+ int i, j, result = 0;
+
+#warning Could be done in Python
+ if (OBJ_obj2nid(CMS_get0_eContentType(cms)) != NID_ct_ROA)
+ record_validation_status(status, BAD_CMS_ECONTENTTYPE);
+
+ if (roa->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)
+ 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)
+ 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)
+ record_validation_status(status, BAD_IPADDRBLOCKS);
+
+ /*
+ * Convert ROA prefixes to resource set. This goes on a bit.
+ */
+
+ if ((roa_resources = sk_IPAddressFamily_new_null()) == NULL)
+ lose_no_memory();
+
+ for (i = 0; i < sk_ROAIPAddressFamily_num(roa->ipAddrBlocks); i++) {
+ rf = sk_ROAIPAddressFamily_value(roa->ipAddrBlocks, i);
+
+ if (rf == NULL || rf->addressFamily == NULL)
+ lose_no_memory();
+
+ if (rf->addressFamily->length < 2 || rf->addressFamily->length > 3)
+ record_validation_status(status, MALFORMED_ROA_ADDRESSFAMILY);
+
+ afi = (rf->addressFamily->data[0] << 8) | (rf->addressFamily->data[1]);
+ if (rf->addressFamily->length == 3)
+ *(safi = &safi_) = rf->addressFamily->data[2];
+
+ for (j = 0; j < sk_ROAIPAddress_num(rf->addresses); j++) {
+ ra = sk_ROAIPAddress_value(rf->addresses, j);
+
+ if (ra == NULL ||
+ !check_roa_extract_roa_prefix(ra, afi, addrbuf, &prefixlen, &max_prefixlen) ||
+ !v3_addr_add_prefix(roa_resources, afi, safi, addrbuf, prefixlen))
+ record_validation_status(status, ROA_RESOURCES_MALFORMED);
+
+ else if (max_prefixlen < prefixlen)
+ record_validation_status(status, ROA_MAX_PREFIXLEN_TOO_SHORT);
+ }
+ }
+
+ /*
+ * ROAs can include nested prefixes, so direct translation to
+ * resource sets could include overlapping ranges, which is illegal.
+ * So we have to remove nested stuff before whacking into canonical
+ * form. Fortunately, this is relatively easy, since we know these
+ * are just prefixes, not ranges: in a list of prefixes sorted by
+ * the RFC 3779 rules, the first element of a set of nested prefixes
+ * will always be the least specific.
+ */
+
+ for (i = 0; i < sk_IPAddressFamily_num(roa_resources); i++) {
+ IPAddressFamily *f = sk_IPAddressFamily_value(roa_resources, i);
+
+ if ((afi = v3_addr_get_afi(f)) == 0)
+ record_validation_status(status, ROA_CONTAINS_BAD_AFI_VALUE);
+
+ if (f->ipAddressChoice->type == IPAddressChoice_addressesOrRanges) {
+ IPAddressOrRanges *aors = f->ipAddressChoice->u.addressesOrRanges;
+
+ sk_IPAddressOrRange_sort(aors);
+
+ for (j = 0; j < sk_IPAddressOrRange_num(aors) - 1; j++) {
+ IPAddressOrRange *a = sk_IPAddressOrRange_value(aors, j);
+ IPAddressOrRange *b = sk_IPAddressOrRange_value(aors, j + 1);
+ unsigned char a_min[RAW_IPADDR_BUFLEN], a_max[RAW_IPADDR_BUFLEN];
+ unsigned char b_min[RAW_IPADDR_BUFLEN], b_max[RAW_IPADDR_BUFLEN];
+ int length;
+
+#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)
+ record_validation_status(status, ROA_RESOURCES_MALFORMED);
+
+ if (memcmp(a_max, b_max, length) >= 0) {
+ (void) sk_IPAddressOrRange_delete(aors, j + 1);
+ IPAddressOrRange_free(b);
+ --j;
+ }
+ }
+ }
+ }
+
+ if (!v3_addr_canonize(roa_resources))
+ record_validation_status(status, ROA_RESOURCES_MALFORMED);
+
+ if (ee_resources == NULL || !v3_addr_subset(roa_resources, ee_resources))
+ record_validation_status(status, ROA_RESOURCE_NOT_IN_EE);
+
+ result = 1;
+
+ error:
+ sk_IPAddressFamily_pop_free(roa_resources, IPAddressFamily_free);
+ sk_IPAddressFamily_pop_free(ee_resources, IPAddressFamily_free);
+ sk_X509_pop_free(certs, X509_free);
+
+ return result;
+}
+
+
+
+/*
* 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
@@ -1265,7 +1821,7 @@ extension_set_basic_constraints(X509_EXTENSIONS **exts, PyObject *args)
goto error;
if (pathlen_obj != Py_None && (pathlen = PyInt_AsLong(pathlen_obj)) < 0)
- lose_type_error("Bad pathLenConstraint value");
+ lose_value_error("Bad pathLenConstraint value");
if ((ext = BASIC_CONSTRAINTS_new()) == NULL)
lose_no_memory();
@@ -1295,8 +1851,8 @@ extension_set_basic_constraints(X509_EXTENSIONS **exts, PyObject *args)
#define EXTENSION_GET_SIA__DOC__ \
"If there is no SIA extension, this method returns None.\n" \
"\n" \
- "Otherwise, it returns a tuple containing three values:\n" \
- "caRepository URIs, rpkiManifest URIs, and signedObject URIs.\n" \
+ "Otherwise, it returns a tuple containing four values:\n" \
+ "caRepository URIs, rpkiManifest URIs, signedObject, and rpkiNotify URIs.\n" \
"Each of these values is a tuple of strings, representing an ordered\n" \
"sequence of URIs. Any or all of these sequences may be empty.\n" \
"\n" \
@@ -1310,9 +1866,11 @@ extension_get_sia(X509_EXTENSIONS **exts)
PyObject *result_caRepository = NULL;
PyObject *result_rpkiManifest = NULL;
PyObject *result_signedObject = NULL;
+ PyObject *result_rpkiNotify = NULL;
int n_caRepository = 0;
int n_rpkiManifest = 0;
int n_signedObject = 0;
+ int n_rpkiNotify = 0;
const char *uri;
PyObject *obj;
int i, nid;
@@ -1334,26 +1892,23 @@ extension_get_sia(X509_EXTENSIONS **exts)
if (a->location->type != GEN_URI)
continue;
nid = OBJ_obj2nid(a->method);
- if (nid == NID_caRepository) {
+ if (nid == NID_caRepository)
n_caRepository++;
- continue;
- }
- if (nid == NID_rpkiManifest) {
+ else if (nid == NID_ad_rpkiManifest)
n_rpkiManifest++;
- continue;
- }
- if (nid == NID_signedObject) {
+ else if (nid == NID_ad_signedObject)
n_signedObject++;
- continue;
- }
+ else if (nid == NID_ad_rpkiNotify)
+ n_rpkiNotify++;
}
if (((result_caRepository = PyTuple_New(n_caRepository)) == NULL) ||
((result_rpkiManifest = PyTuple_New(n_rpkiManifest)) == NULL) ||
- ((result_signedObject = PyTuple_New(n_signedObject)) == NULL))
+ ((result_signedObject = PyTuple_New(n_signedObject)) == NULL) ||
+ ((result_rpkiNotify = PyTuple_New(n_rpkiNotify)) == NULL))
goto error;
- n_caRepository = n_rpkiManifest = n_signedObject = 0;
+ n_caRepository = n_rpkiManifest = n_signedObject = n_rpkiNotify = 0;
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ext); i++) {
ACCESS_DESCRIPTION *a = sk_ACCESS_DESCRIPTION_value(ext, i);
@@ -1367,36 +1922,44 @@ 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_ad_rpkiNotify) {
+ if ((obj = PyString_FromString(uri)) == NULL)
+ goto error;
+ PyTuple_SET_ITEM(result_rpkiNotify, n_rpkiNotify++, obj);
+ continue;
+ }
}
- result = Py_BuildValue("(OOO)",
+ result = Py_BuildValue("(OOOO)",
result_caRepository,
result_rpkiManifest,
- result_signedObject);
+ result_signedObject,
+ result_rpkiNotify);
error:
AUTHORITY_INFO_ACCESS_free(ext);
Py_XDECREF(result_caRepository);
Py_XDECREF(result_rpkiManifest);
Py_XDECREF(result_signedObject);
+ Py_XDECREF(result_rpkiNotify);
return result;
}
#define EXTENSION_SET_SIA__DOC__ \
- "This method Takes three arguments:\n" \
- "\"caRepository\", \"rpkiManifest\", and \"signedObject\".\n" \
+ "This method takes four arguments: \"caRepository\"\n," \
+ "\"rpkiManifest\", \"signedObject\", and \"rpkiNotify\".\n" \
"Each of these should be an iterable which returns URIs.\n" \
"\n" \
"None is acceptable as an alternate way of specifying an empty\n" \
@@ -1405,11 +1968,12 @@ extension_get_sia(X509_EXTENSIONS **exts)
static PyObject *
extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"caRepository", "rpkiManifest", "signedObject", NULL};
+ static char *kwlist[] = {"caRepository", "rpkiManifest", "signedObject", "rpkiNotify", NULL};
AUTHORITY_INFO_ACCESS *ext = NULL;
PyObject *caRepository = Py_None;
PyObject *rpkiManifest = Py_None;
PyObject *signedObject = Py_None;
+ PyObject *rpkiNotify = Py_None;
PyObject *iterator = NULL;
ASN1_OBJECT *oid = NULL;
PyObject **pobj = NULL;
@@ -1424,8 +1988,8 @@ extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
if (!exts)
goto error;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist,
- &caRepository, &rpkiManifest, &signedObject))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist,
+ &caRepository, &rpkiManifest, &signedObject, &rpkiNotify))
goto error;
if ((ext = AUTHORITY_INFO_ACCESS_new()) == NULL)
@@ -1437,11 +2001,12 @@ extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
* single URI as an abbreviation for a collection containing one URI.
*/
- for (i = 0; i < 3; i++) {
+ 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 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)
@@ -1461,7 +2026,8 @@ extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
if ((a = ACCESS_DESCRIPTION_new()) == NULL ||
(a->method = OBJ_dup(oid)) == NULL ||
(a->location->d.uniformResourceIdentifier = ASN1_IA5STRING_new()) == NULL ||
- !ASN1_OCTET_STRING_set(a->location->d.uniformResourceIdentifier, (unsigned char *) uri, urilen))
+ !ASN1_OCTET_STRING_set(a->location->d.uniformResourceIdentifier,
+ (unsigned char *) uri, urilen))
lose_no_memory();
a->location->type = GEN_URI;
@@ -2489,8 +3055,6 @@ static char x509_object_sign__doc__[] =
"The optional \"digest\" parameter indicates which digest to compute and\n"
"sign, and should be one of the following:\n"
"\n"
- "* MD5_DIGEST\n"
- "* SHA_DIGEST\n"
"* SHA1_DIGEST\n"
"* SHA256_DIGEST\n"
"* SHA384_DIGEST\n"
@@ -2523,6 +3087,411 @@ x509_object_sign(x509_object *self, PyObject *args)
return NULL;
}
+static int x509_store_ctx_object_verify_cb(int ok, X509_STORE_CTX *ctx);
+
+static char x509_object_verify__doc__[] =
+ "Verify a certificate.\n"
+ ;
+
+#warning Write real x509_object_verify__doc__[] once API is stable.
+
+static PyObject *
+x509_object_verify(x509_object *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"trusted", "untrusted", "crl", "policy", "context_class", NULL};
+ PyObject *ctxclass = (PyObject *) &POW_X509StoreCTX_Type;
+ STACK_OF(X509) *trusted_stack = NULL;
+ STACK_OF(X509) *untrusted_stack = NULL;
+ STACK_OF(X509_CRL) *crl_stack = NULL;
+ x509_store_ctx_object *ctx = NULL;
+ PyObject *trusted = Py_None;
+ PyObject *untrusted = Py_None;
+ PyObject *crl = Py_None;
+ PyObject *policy = Py_None;
+ int ok = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOO", kwlist, &trusted, &untrusted, &crl, &policy, &ctxclass))
+ goto error;
+
+ if ((trusted_stack = x509_helper_iterable_to_stack(trusted)) == NULL)
+ goto error;
+
+ if ((untrusted_stack = x509_helper_iterable_to_stack(untrusted)) == NULL)
+ goto error;
+
+ if (crl != Py_None && !POW_CRL_Check(crl))
+ lose_type_error("Not a CRL");
+
+ if (crl != Py_None && ((crl_stack = sk_X509_CRL_new_null()) == NULL ||
+ !sk_X509_CRL_push(crl_stack, ((crl_object *) crl)->crl)))
+ lose_no_memory();
+
+ if (!PyCallable_Check(ctxclass))
+ lose_type_error("Context class must be callable");
+
+ if ((ctx = (x509_store_ctx_object *) PyObject_CallFunctionObjArgs(ctxclass, NULL)) == NULL)
+ goto error;
+
+ if (!POW_X509StoreCTX_Check(ctx))
+ lose_type_error("Returned context is not a X509StoreCTX");
+
+ if (ctx->ctx == NULL)
+ lose("Uninitialized X509StoreCTX");
+
+ if (crl != Py_None)
+ X509_VERIFY_PARAM_set_flags(ctx->ctx->param, X509_V_FLAG_CRL_CHECK);
+
+ if (policy != Py_None) {
+ const char *oid_txt = NULL;
+ ASN1_OBJECT *oid_obj = NULL;
+
+ if ((oid_txt = PyString_AsString(policy)) == NULL)
+ goto error;
+
+ if ((oid_obj = OBJ_txt2obj(oid_txt, 1)) == NULL)
+ lose("Couldn't parse policy OID");
+
+ X509_VERIFY_PARAM_set_flags(ctx->ctx->param, X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY);
+ X509_VERIFY_PARAM_add0_policy(ctx->ctx->param, oid_obj);
+ }
+
+ Py_XINCREF(trusted);
+ Py_XINCREF(untrusted);
+ Py_XINCREF(crl);
+ X509_STORE_CTX_set_cert(ctx->ctx, self->x509);
+ X509_STORE_CTX_trusted_stack(ctx->ctx, trusted_stack);
+ X509_STORE_CTX_set_chain(ctx->ctx, untrusted_stack);
+ X509_STORE_CTX_set0_crls(ctx->ctx, crl_stack);
+
+ X509_STORE_CTX_set_verify_cb(ctx->ctx, x509_store_ctx_object_verify_cb);
+ X509_VERIFY_PARAM_set_flags(ctx->ctx->param, X509_V_FLAG_X509_STRICT);
+
+ ok = X509_verify_cert(ctx->ctx) >= 0;
+
+ X509_STORE_CTX_set0_crls(ctx->ctx, NULL);
+ X509_STORE_CTX_set_chain(ctx->ctx, NULL);
+ X509_STORE_CTX_trusted_stack(ctx->ctx, NULL);
+ X509_STORE_CTX_set_cert(ctx->ctx, NULL);
+ Py_XDECREF(crl);
+ Py_XDECREF(untrusted);
+ Py_XDECREF(trusted);
+
+ if (PyErr_Occurred())
+ goto error;
+
+ if (!ok)
+ lose_validation_error("X509_verify_cert() raised an exception");
+
+ error:
+ sk_X509_free(trusted_stack);
+ sk_X509_free(untrusted_stack);
+ sk_X509_CRL_free(crl_stack);
+
+ if (ok)
+ return (PyObject *) ctx;
+
+ Py_XDECREF(ctx);
+ return NULL;
+}
+
+
+static char x509_object_check_rpki_conformance__doc__[] =
+ "Check a certificate for conformance to the RPKI profile.\n"
+ ;
+
+#warning Write real x509_object_check_rpki_conformance__doc__[] once API is stable.
+
+static PyObject *
+x509_object_check_rpki_conformance(x509_object *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"status", "eku", NULL};
+ PyObject *status = Py_None;
+ PyObject *ekuarg = Py_None;
+ EVP_PKEY *issuer_pkey = NULL, *subject_pkey = NULL;
+ 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 = 0, ekunid = NID_undef, ret = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O", kwlist, &PySet_Type, &status, &ekuarg))
+ goto error;
+
+ if (ekuarg != Py_None) {
+ const char *ekutxt = PyString_AsString(ekuarg);
+ if (ekutxt == NULL)
+ goto error;
+ ekunid = OBJ_txt2nid(ekutxt);
+ }
+
+ /*
+ * 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(self->x509);
+
+ if (!check_allowed_time_encoding(X509_get_notBefore(self->x509)) ||
+ !check_allowed_time_encoding(X509_get_notAfter(self->x509)))
+ record_validation_status(status, NONCONFORMANT_ASN1_TIME_VALUE);
+
+ if (self->x509->cert_info == NULL ||
+ self->x509->cert_info->signature == NULL ||
+ self->x509->cert_info->signature->algorithm == NULL ||
+ OBJ_obj2nid(self->x509->cert_info->signature->algorithm) != NID_sha256WithRSAEncryption)
+ record_validation_status(status, NONCONFORMANT_SIGNATURE_ALGORITHM);
+
+ if (!check_allowed_dn(X509_get_subject_name(self->x509)))
+ record_validation_status(status, NONCONFORMANT_SUBJECT_NAME);
+
+ if (!check_allowed_dn(X509_get_issuer_name(self->x509)))
+ 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.
+ */
+
+ if (!self->x509->cert_info || self->x509->cert_info->issuerUID || self->x509->cert_info->subjectUID)
+ record_validation_status(status, NONCONFORMANT_CERTIFICATE_UID);
+
+ /*
+ * Public key checks postponed until we've checked extensions (in
+ * particular, until we've checked Basic Constraints and know
+ * whether to apply the CA or EE rules).
+ */
+
+ /*
+ * 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(self->x509);
+
+ /* Critical */
+ if ((bc = X509_get_ext_d2i(self->x509, NID_basic_constraints, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (!crit || bc->ca <= 0 || bc->pathlen != NULL)
+ record_validation_status(status, MALFORMED_BASIC_CONSTRAINTS);
+ }
+
+ is_ca = bc != NULL;
+
+ /*
+ * Check for presence of AIA, SIA, and CRLDP, and make sure that
+ * they're in the correct format, but leave checking of the URIs
+ * themselves for Python code to handle.
+ */
+
+ /* Non-criticial */
+ if ((aia = X509_get_ext_d2i(self->x509, NID_info_access, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (crit)
+ record_validation_status(status, GRATUITOUSLY_CRITICAL_EXTENSION);
+ ok = sk_ACCESS_DESCRIPTION_num(aia) > 0;
+ for (i = 0; ok && i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
+ ACCESS_DESCRIPTION *a = sk_ACCESS_DESCRIPTION_value(aia, i);
+ ok = (a != NULL && a->location->type == GEN_URI &&
+ OBJ_obj2nid(a->method) == NID_ad_ca_issuers);
+ }
+ if (!ok)
+ record_validation_status(status, MALFORMED_AIA_EXTENSION);
+ }
+
+ /* Non-criticial */
+ if ((sia = X509_get_ext_d2i(self->x509, NID_sinfo_access, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (crit)
+ record_validation_status(status, GRATUITOUSLY_CRITICAL_EXTENSION);
+ ok = sk_ACCESS_DESCRIPTION_num(sia) > 0;
+ for (i = 0; ok && i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
+ ACCESS_DESCRIPTION *a = sk_ACCESS_DESCRIPTION_value(sia, i);
+ int nid = a == NULL ? NID_undef : OBJ_obj2nid(a->method);
+ ok = (a != NULL && a->location->type == GEN_URI &&
+ (nid == NID_caRepository || nid == NID_ad_rpkiManifest ||
+ nid == NID_ad_signedObject || nid == NID_ad_rpkiNotify));
+ }
+ if (!ok)
+ record_validation_status(status, MALFORMED_SIA_EXTENSION);
+ }
+
+ /* Non-critical */
+ if ((crldp = X509_get_ext_d2i(self->x509, NID_crl_distribution_points, &crit, NULL)) != NULL) {
+ DIST_POINT *dp = sk_DIST_POINT_value(crldp, 0);
+ ex_count--;
+ if (crit)
+ record_validation_status(status, GRATUITOUSLY_CRITICAL_EXTENSION);
+ ok = (sk_DIST_POINT_num(crldp) == 1 &&
+ dp->reasons == NULL && dp->CRLissuer == NULL &&
+ dp->distpoint != NULL && dp->distpoint->type == 0);
+ for (i = 0; ok && i < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); i++) {
+ GENERAL_NAME *gn = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, i);
+ ok = gn != NULL && gn->type == GEN_URI;
+ }
+ if (!ok)
+ record_validation_status(status, MALFORMED_CRLDP_EXTENSION);
+ }
+
+ /* Non-critical */
+ if ((eku = X509_get_ext_d2i(self->x509, NID_ext_key_usage, &crit, NULL)) != NULL) {
+ ex_count--;
+ ok = 0;
+ if (!crit && !is_ca && sk_ASN1_OBJECT_num(eku) > 0 && ekunid != NID_undef)
+ for (i = 0; !ok && i < sk_ASN1_OBJECT_num(eku); i++)
+ ok = OBJ_obj2nid(sk_ASN1_OBJECT_value(eku, i)) == ekunid;
+ if (!ok)
+ record_validation_status(status, INAPPROPRIATE_EKU_EXTENSION);
+ }
+
+ /* Critical */
+ if ((policies = X509_get_ext_d2i(self->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)))
+ record_validation_status(status, BAD_CERTIFICATE_POLICY);
+ else if (qualifier != NULL)
+ record_validation_status(status, POLICY_QUALIFIER_CPS);
+ }
+
+ /* Critical */
+ if ((self->x509->ex_flags & EXFLAG_KUSAGE) == 0)
+ record_validation_status(status, KEY_USAGE_MISSING);
+ else {
+ ex_count--;
+ if (!X509_EXTENSION_get_critical(X509_get_ext(self->x509, X509_get_ext_by_NID(self->x509, NID_key_usage, -1))) ||
+ self->x509->ex_kusage != (is_ca ? KU_KEY_CERT_SIGN | KU_CRL_SIGN : KU_DIGITAL_SIGNATURE))
+ record_validation_status(status, BAD_KEY_USAGE);
+ }
+
+ /* Critical */
+ if (self->x509->rfc3779_addr) {
+ ex_count--;
+ if (ekunid == NID_id_kp_bgpsec_router ||
+ (loc = X509_get_ext_by_NID(self->x509, NID_sbgp_ipAddrBlock, -1)) < 0 ||
+ !X509_EXTENSION_get_critical(X509_get_ext(self->x509, loc)) ||
+ !v3_addr_is_canonical(self->x509->rfc3779_addr) ||
+ sk_IPAddressFamily_num(self->x509->rfc3779_addr) == 0)
+ record_validation_status(status, BAD_IPADDRBLOCKS);
+ else
+ for (i = 0; i < sk_IPAddressFamily_num(self->x509->rfc3779_addr); i++) {
+ IPAddressFamily *f = sk_IPAddressFamily_value(self->x509->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);
+ }
+ }
+
+ /* Critical */
+ if (self->x509->rfc3779_asid) {
+ ex_count--;
+ if ((loc = X509_get_ext_by_NID(self->x509, NID_sbgp_autonomousSysNum, -1)) < 0 ||
+ !X509_EXTENSION_get_critical(X509_get_ext(self->x509, loc)) ||
+ !v3_asid_is_canonical(self->x509->rfc3779_asid) ||
+ self->x509->rfc3779_asid->asnum == NULL ||
+ self->x509->rfc3779_asid->rdi != NULL ||
+ (ekunid == NID_id_kp_bgpsec_router && self->x509->rfc3779_asid->asnum->type == ASIdentifierChoice_inherit))
+ record_validation_status(status, BAD_ASIDENTIFIERS);
+ }
+
+ if (!self->x509->rfc3779_addr && !self->x509->rfc3779_asid)
+ record_validation_status(status, MISSING_RESOURCES);
+
+ /* Non-critical */
+ if (!self->x509->skid)
+ record_validation_status(status, SKI_EXTENSION_MISSING);
+ else {
+ ex_count--;
+ if (X509_EXTENSION_get_critical(X509_get_ext(self->x509, X509_get_ext_by_NID(self->x509, NID_subject_key_identifier, -1))))
+ record_validation_status(status, GRATUITOUSLY_CRITICAL_EXTENSION);
+ if ((ski_pubkey = X509_get0_pubkey_bitstr(self->x509)) == NULL ||
+ !EVP_Digest(ski_pubkey->data, ski_pubkey->length,
+ ski_hashbuf, &ski_hashlen, EVP_sha1(), NULL) ||
+ ski_hashlen != 20 ||
+ ski_hashlen != self->x509->skid->length ||
+ memcmp(ski_hashbuf, self->x509->skid->data, ski_hashlen))
+ record_validation_status(status, SKI_PUBLIC_KEY_MISMATCH);
+ }
+
+ /* Non-critical */
+ if (self->x509->akid) {
+ ex_count--;
+ if (X509_EXTENSION_get_critical(X509_get_ext(self->x509, X509_get_ext_by_NID(self->x509, NID_authority_key_identifier, -1))))
+ record_validation_status(status, GRATUITOUSLY_CRITICAL_EXTENSION);
+ if (!self->x509->akid->keyid || self->x509->akid->serial || self->x509->akid->issuer)
+ record_validation_status(status, AKI_EXTENSION_WRONG_FORMAT);
+ }
+
+ if (ex_count > 0)
+ record_validation_status(status, DISALLOWED_X509V3_EXTENSION);
+
+ /*
+ * Public key checks.
+ */
+
+ subject_pkey = X509_get_pubkey(self->x509);
+ ok = subject_pkey != NULL;
+ if (ok) {
+ ASN1_OBJECT *algorithm;
+
+ (void) X509_PUBKEY_get0_param(&algorithm, NULL, NULL, NULL, X509_get_X509_PUBKEY(self->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 &&
+ BN_num_bits(subject_pkey->pkey.rsa->n) == 2048);
+ break;
+
+ case NID_X9_62_id_ecPublicKey:
+ if (ekunid == NID_id_kp_bgpsec_router)
+ ok = (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_prime256v1);
+ else
+ ok = 0;
+ break;
+
+ default:
+ ok = 0;
+ }
+ }
+ if (!ok)
+ record_validation_status(status, BAD_PUBLIC_KEY);
+
+ 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);
+
+ if (ret)
+ Py_RETURN_NONE;
+ else
+ return NULL;
+}
+
static char x509_object_get_version__doc__[] =
"Return version number of this certificate.\n"
;
@@ -2694,7 +3663,7 @@ x509_object_set_subject(x509_object *self, PyObject *args)
goto error;
if (!PySequence_Check(name_sequence))
- lose_type_error("Inapropriate type");
+ lose_type_error("Expected a sequence object");
if ((name = x509_object_helper_set_name(name_sequence)) == NULL)
goto error;
@@ -2730,7 +3699,7 @@ x509_object_set_issuer(x509_object *self, PyObject *args)
goto error;
if (!PySequence_Check(name_sequence))
- lose_type_error("Inapropriate type");
+ lose_type_error("Expected a sequence object");
if ((name = x509_object_helper_set_name(name_sequence)) == NULL)
goto error;
@@ -3021,12 +3990,12 @@ x509_object_get_rfc3779(x509_object *self)
break;
default:
- lose_type_error("Unexpected asIdsOrRanges type");
+ lose_value_error("Unexpected asIdsOrRanges type");
}
if (ASN1_STRING_type(b) == V_ASN1_NEG_INTEGER ||
ASN1_STRING_type(e) == V_ASN1_NEG_INTEGER)
- lose_type_error("I don't believe in negative ASNs");
+ lose_value_error("I don't believe in negative ASNs");
if ((range_b = ASN1_INTEGER_to_PyLong(b)) == NULL ||
(range_e = ASN1_INTEGER_to_PyLong(e)) == NULL ||
@@ -3040,7 +4009,7 @@ x509_object_get_rfc3779(x509_object *self)
break;
default:
- lose_type_error("Unexpected ASIdentifierChoice type");
+ lose_value_error("Unexpected ASIdentifierChoice type");
}
}
@@ -3055,14 +4024,14 @@ x509_object_get_rfc3779(x509_object *self)
switch (afi) {
case IANA_AFI_IPV4: result_obj = &ipv4_result; ip_type = &ipaddress_version_4; break;
case IANA_AFI_IPV6: result_obj = &ipv6_result; ip_type = &ipaddress_version_6; break;
- default: lose_type_error("Unknown AFI");
+ default: lose_value_error("Unknown AFI");
}
if (*result_obj != NULL)
- lose_type_error("Duplicate IPAddressFamily");
+ lose_value_error("Duplicate IPAddressFamily");
if (f->addressFamily->length > 2)
- lose_type_error("Unsupported SAFI");
+ lose_value_error("Unsupported SAFI");
switch (f->ipAddressChoice->type) {
@@ -3075,7 +4044,7 @@ x509_object_get_rfc3779(x509_object *self)
break;
default:
- lose_type_error("Unexpected IPAddressChoice type");
+ lose_value_error("Unexpected IPAddressChoice type");
}
if ((*result_obj = PyTuple_New(sk_IPAddressOrRange_num(f->ipAddressChoice->u.addressesOrRanges))) == NULL)
@@ -3095,7 +4064,7 @@ x509_object_get_rfc3779(x509_object *self)
if ((addr_len = v3_addr_get_range(aor, afi, addr_b->address, addr_e->address,
sizeof(addr_b->address))) == 0)
- lose_type_error("Couldn't unpack IP addresses from BIT STRINGs");
+ lose_value_error("Couldn't unpack IP addresses from BIT STRINGs");
addr_b->type = addr_e->type = ip_type;
@@ -3783,6 +4752,8 @@ static struct PyMethodDef x509_object_methods[] = {
Define_Method(pemWrite, x509_object_pem_write, METH_NOARGS),
Define_Method(derWrite, x509_object_der_write, METH_NOARGS),
Define_Method(sign, x509_object_sign, METH_VARARGS),
+ Define_Method(verify, x509_object_verify, METH_KEYWORDS),
+ Define_Method(checkRPKIConformance, x509_object_check_rpki_conformance, METH_KEYWORDS),
Define_Method(getPublicKey, x509_object_get_public_key, METH_NOARGS),
Define_Method(setPublicKey, x509_object_set_public_key, METH_VARARGS),
Define_Method(getVersion, x509_object_get_version, METH_NOARGS),
@@ -3877,286 +4848,6 @@ static PyTypeObject POW_X509_Type = {
/*
- * X509Store object.
- */
-
-static PyObject *
-x509_store_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds)
-{
- x509_store_object *self = NULL;
-
- ENTERING(x509_store_object_new);
-
- if ((self = (x509_store_object *) type->tp_alloc(type, 0)) == NULL)
- goto error;
-
- if ((self->store = X509_STORE_new()) == NULL)
- lose_no_memory();
-
- self->ctxclass = (PyObject *) &POW_X509StoreCTX_Type;
- Py_XINCREF(self->ctxclass);
- return (PyObject *) self;
-
- error:
- Py_XDECREF(self);
- return NULL;
-}
-
-static void
-x509_store_object_dealloc(x509_store_object *self)
-{
- ENTERING(x509_store_object_dealloc);
- X509_STORE_free(self->store);
- Py_XDECREF(self->ctxclass);
- self->ob_type->tp_free((PyObject*) self);
-}
-
-static char x509_store_object_add_trust__doc__[] =
- "Add a trusted certificate to this certificate store object.\n"
- "\n"
- "The \"certificate\" parameter should be an instance of the X509 class.\n"
- ;
-
-static PyObject *
-x509_store_object_add_trust(x509_store_object *self, PyObject *args)
-{
- x509_object *x509 = NULL;
-
- ENTERING(x509_store_object_add_trust);
-
- if (!PyArg_ParseTuple(args, "O!", &POW_X509_Type, &x509))
- goto error;
-
- X509_STORE_add_cert(self->store, x509->x509);
-
- Py_RETURN_NONE;
-
- error:
-
- return NULL;
-}
-
-static char x509_store_object_add_crl__doc__[] =
- "Add a CRL to this certificate store object.\n"
- "\n"
- "The \"crl\" parameter should be an instance of the CRL class.\n"
- ;
-
-static PyObject *
-x509_store_object_add_crl(x509_store_object *self, PyObject *args)
-{
- crl_object *crl = NULL;
-
- ENTERING(x509_store_object_add_crl);
-
- if (!PyArg_ParseTuple(args, "O!", &POW_CRL_Type, &crl))
- goto error;
-
- X509_STORE_add_crl(self->store, crl->crl);
-
- Py_RETURN_NONE;
-
- error:
-
- return NULL;
-}
-
-static char x509_store_object_set_flags__doc__[] =
- "Set validation flags for this X509Store.\n"
- "\n"
- "Argument is an integer containing bit flags to set.\n"
- ;
-
-static PyObject *
-x509_store_object_set_flags (x509_store_object *self, PyObject *args)
-{
- unsigned long flags;
-
- if (!PyArg_ParseTuple(args, "k", &flags))
- goto error;
-
- if (!X509_VERIFY_PARAM_set_flags(self->store->param, flags))
- lose_openssl_error("X509_VERIFY_PARAM_set_flags() failed");
-
- Py_RETURN_NONE;
-
- error:
- return NULL;
-}
-
-static char x509_store_object_clear_flags__doc__[] =
- "Clear validation flags for this X509Store.\n"
- "\n"
- "Argument is an integer containing bit flags to clear.\n"
- ;
-
-static PyObject *
-x509_store_object_clear_flags (x509_store_object *self, PyObject *args)
-{
- unsigned long flags;
-
- if (!PyArg_ParseTuple(args, "k", &flags))
- goto error;
-
- if (!X509_VERIFY_PARAM_clear_flags(self->store->param, flags))
- lose_openssl_error("X509_VERIFY_PARAM_clear_flags() failed");
-
- Py_RETURN_NONE;
-
- error:
- return NULL;
-}
-
-static char x509_store_object_set_context_class__doc__[] =
- "Set validation context class factory for this X509Store.\n"
- "\n"
- "This must be a callable object which takes one argument, an X509Store,\n"
- "and returns a subclass of X509StoreCTX. The callable can be a class\n"
- "object but need not be, so long as calling it returns an instance of an\n"
- "appropriate class. The default is X509StoreCTX.\n"
- ;
-
-static PyObject *
-x509_store_object_set_context_class (x509_store_object *self, PyObject *args)
-{
- PyObject *ctxclass = (PyObject *) &POW_X509StoreCTX_Type;
-
- if (!PyArg_ParseTuple(args, "|O", &ctxclass))
- goto error;
-
- if (!PyCallable_Check(ctxclass))
- lose("Context class must be callable");
-
- Py_XDECREF(self->ctxclass);
- self->ctxclass = ctxclass;
- Py_XINCREF(self->ctxclass);
-
- Py_RETURN_NONE;
-
- error:
- return NULL;
-}
-
-static char x509_store_object_verify__doc__[] =
- "Verify an X509 certificate object using this certificate store.\n"
- "\n"
- "Optional second argument is an iterable that supplies untrusted certificates\n"
- "to be considered when building a chain to the trust anchor.\n"
- "\n"
- "This method returns an instance of the store's verification context class.\n"
- ;
-
-static PyObject *
-x509_store_object_verify(x509_store_object *self, PyObject *args)
-{
- x509_store_ctx_object *ctx = NULL;
- STACK_OF(X509) *stack = NULL;
- x509_object *x509 = NULL;
- PyObject *chain = Py_None;
- int ok;
-
- if (!PyArg_ParseTuple(args, "O!|O", &POW_X509_Type, &x509, &chain))
- goto error;
-
- if ((ctx = (x509_store_ctx_object *) PyObject_CallFunctionObjArgs(self->ctxclass, self, NULL)) == NULL)
- goto error;
-
- if (!POW_X509StoreCTX_Check(ctx))
- lose_type_error("Returned context is not a X509StoreCTX");
-
- if (ctx->ctx == NULL)
- lose("Uninitialized X509StoreCTX");
-
- if (chain != Py_None && (stack = x509_helper_iterable_to_stack(chain)) == NULL)
- goto error;
-
- Py_XINCREF(x509);
- Py_XINCREF(chain);
- X509_STORE_CTX_set_cert(ctx->ctx, x509->x509);
- X509_STORE_CTX_set_chain(ctx->ctx, stack);
-
- ok = X509_verify_cert(ctx->ctx);
-
- X509_STORE_CTX_set_chain(ctx->ctx, NULL);
- X509_STORE_CTX_set_cert(ctx->ctx, NULL);
- Py_XDECREF(chain);
- Py_XDECREF(x509);
-
- sk_X509_free(stack);
-
- if (PyErr_Occurred())
- goto error;
-
- if (ok < 0)
- lose_openssl_error("X509_verify_cert() raised an exception");
-
- return (PyObject *) ctx;
-
- error: /* fall through */
- Py_XDECREF(ctx);
- return NULL;
-}
-
-static struct PyMethodDef x509_store_object_methods[] = {
- Define_Method(addTrust, x509_store_object_add_trust, METH_VARARGS),
- Define_Method(addCrl, x509_store_object_add_crl, METH_VARARGS),
- 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),
- {NULL}
-};
-
-static char POW_X509Store_Type__doc__[] =
- "This class holds the OpenSSL certificate store objects used in CMS\n"
- "and certificate verification.\n"
- ;
-
-static PyTypeObject POW_X509Store_Type = {
- PyObject_HEAD_INIT(0)
- 0, /* ob_size */
- "rpki.POW.X509Store", /* tp_name */
- sizeof(x509_store_object), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor)x509_store_object_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
- POW_X509Store_Type__doc__, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- x509_store_object_methods, /* tp_methods */
- 0, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- x509_store_object_new, /* tp_new */
-};
-
-
-
-/*
* X509StoreCTX object.
*/
@@ -4164,13 +4855,16 @@ 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;
- if (self == NULL || !PyObject_HasAttrString(self, method_name))
+ if (self == NULL)
return ok;
- if ((result = PyObject_CallMethod(self, method_name, "i", ok)) == NULL)
+ if (!PyObject_HasAttrString((PyObject *) self, method_name))
+ return ok;
+
+ if ((result = PyObject_CallMethod((PyObject *) self, method_name, "i", ok)) == NULL)
return -1;
ok = PyObject_IsTrue(result);
@@ -4200,26 +4894,19 @@ x509_store_ctx_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNU
static int
x509_store_ctx_object_init(x509_store_ctx_object *self, PyObject *args, GCC_UNUSED PyObject *kwds)
{
- x509_store_object *store = NULL;
-
- if (!PyArg_ParseTuple(args, "|O!", &POW_X509Store_Type, &store))
- goto error;
+ ENTERING(x509_store_ctx_object_init);
- if ((self->ctx = X509_STORE_CTX_new()) == NULL)
+ if ((self->store = X509_STORE_new()) == NULL ||
+ (self->ctx = X509_STORE_CTX_new()) == NULL)
lose_no_memory();
- if (!X509_STORE_CTX_init(self->ctx, store ? store->store : NULL, NULL, NULL))
+ if (!X509_STORE_CTX_init(self->ctx, self->store, NULL, NULL))
lose_openssl_error("Couldn't initialize X509_STORE_CTX");
if (!X509_STORE_CTX_set_ex_data(self->ctx, x509_store_ctx_ex_data_idx, self))
lose_openssl_error("Couldn't set X509_STORE_CTX ex_data");
- Py_XDECREF(self->store);
- self->store = store;
- Py_XINCREF(self->store);
-
X509_VERIFY_PARAM_set_flags(self->ctx->param, X509_V_FLAG_X509_STRICT);
- X509_STORE_CTX_set_verify_cb(self->ctx, x509_store_ctx_object_verify_cb);
return 0;
error:
@@ -4231,16 +4918,10 @@ x509_store_ctx_object_dealloc(x509_store_ctx_object *self)
{
ENTERING(x509_store_ctx_object_dealloc);
X509_STORE_CTX_free(self->ctx);
- Py_XDECREF(self->store);
+ X509_STORE_free(self->store);
self->ob_type->tp_free((PyObject*) self);
}
-static PyObject *
-x509_store_ctx_object_get_store (x509_store_ctx_object *self, GCC_UNUSED void *closure)
-{
- return Py_BuildValue("O", self->store == NULL ? Py_None : (PyObject *) self->store);
-}
-
static char x509_store_ctx_object_get_error__doc__[] =
"Extract verification error code from this X509StoreCTX.\n"
;
@@ -4323,52 +5004,6 @@ x509_store_ctx_object_get_chain (x509_store_ctx_object *self)
}
/*
- * For some reason, there are no methods for the policy mechanism for
- * X509_STORE, only for X509_STORE_CTX. Presumably we can whack these
- * anyway using the X509_VERIFY_PARAM_*() calls, the question is
- * whether there's a good reason for this omission.
- *
- * For the moment, I'm just going to leave the policy stuff
- * unimplemented, until we figure out whether it belongs in X509Store
- * or X509StoreCTX.
- */
-
-#define IMPLEMENT_X509StoreCTX_POLICY 0
-
-#if IMPLEMENT_X509StoreCTX_POLICY
-
-static char x509_store_ctx_object_set_policy__doc__[] =
- "Set this X509StoreCTX to require a specified certificate policy.\n"
- ;
-
-static PyObject*
-x509_store_ctx_object_set_policy (x509_store_ctx_object *self, PyObject *args)
-{
- ASN1_OBJECT *policy = NULL;
- char *oid = NULL;
-
- if (!PyArg_ParseTuple(args, "s", &oid))
- goto error;
-
- if ((policy = OBJ_txt2obj(oid, 1)) == NULL)
- lose_openssl_error("Couldn't parse OID");
-
- if (!X509_VERIFY_PARAM_set_flags(self->ctx->param, X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY))
- lose_openssl_error("Couldn't set policy flags");
-
- if (!X509_VERIFY_PARAM_add0_policy(self->ctx->param, policy))
- lose_openssl_error("Couldn't set policy");
-
- Py_RETURN_NONE;
-
- error:
- ASN1_OBJECT_free(policy);
- return NULL;
-}
-
-#endif /* IMPLEMENT_X509StoreCTX_POLICY */
-
-/*
* See (omnibus) man page for X509_STORE_CTX_get_error() for other
* query methods we might want to expose. Someday we might want to
* support X509_V_FLAG_USE_CHECK_TIME too.
@@ -4380,29 +5015,20 @@ static struct PyMethodDef x509_store_ctx_object_methods[] = {
Define_Method(getErrorDepth, x509_store_ctx_object_get_error_depth, METH_NOARGS),
Define_Method(getCurrentCertificate, x509_store_ctx_object_get_current_certificate, METH_NOARGS),
Define_Method(getChain, x509_store_ctx_object_get_chain, METH_NOARGS),
-
-#if IMPLEMENT_X509StoreCTX_POLICY
- Define_Method(setPolicy, x509_store_ctx_object_set_policy, METH_VARARGS),
-#endif
{NULL}
};
-static PyGetSetDef x509_store_ctx_object_getsetters[] = {
- {"store", (getter) x509_store_ctx_object_get_store},
- {NULL}
-};
-
static char POW_X509StoreCTX_Type__doc__[] =
"This class holds the state of an OpenSSL certificate verification\n"
"operation. Ordinarily, the user will never have cause to instantiate\n"
"this class directly, instead, an object of this class will be returned\n"
- "by X509Store.verify().\n"
+ "by X509.verify().\n"
"\n"
"If you need to see OpenSSL's verification callbacks, you can do so\n"
- "by subclassing X509StoreCTX and attaching your subclass to an X509Store\n"
- "object using X509Store.setContextClass(). Your subclass should provide\n"
- "a .verify_callback() method, which should expect to receive one argument:\n"
- "the integer \"ok\" value passed by OpenSSL's verification callbacks.\n"
+ "by subclassing X509StoreCTX and passing your subclass as an argument\n"
+ "to X509.verify. Your subclass should provide a .verify_callback()\n"
+ "method, which should expect to receive one argument: the integer \"ok\"\n"
+ "value passed by OpenSSL's verification callbacks.\n"
"\n"
"The return value from your .verify_callback() method will be is interpreted\n"
"as a boolean value: anything which evaluates to True will be result in a\n"
@@ -4441,7 +5067,7 @@ static PyTypeObject POW_X509StoreCTX_Type = {
0, /* tp_iternext */
x509_store_ctx_object_methods, /* tp_methods */
0, /* tp_members */
- x509_store_ctx_object_getsetters, /* tp_getset */
+ 0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
@@ -4672,7 +5298,7 @@ crl_object_set_issuer(crl_object *self, PyObject *args)
goto error;
if (!PySequence_Check(name_sequence))
- lose_type_error("Inapropriate type");
+ lose_type_error("Expected a sequence object");
if ((name = x509_object_helper_set_name(name_sequence)) == NULL)
goto error;
@@ -4917,6 +5543,21 @@ crl_object_get_revoked(crl_object *self)
return NULL;
}
+static char crl_object_is_revoked__doc__[] =
+ "Check whether a particular certificate has been revoked.\n"
+ ;
+
+static PyObject *
+crl_object_is_revoked(crl_object *self, PyObject *args)
+{
+ x509_object *x = NULL;
+
+ if (!PyArg_ParseTuple(args, "O!", &POW_X509_Type, &x))
+ return NULL;
+
+ return PyBool_FromLong(X509_CRL_get0_by_cert(self->crl, NULL, x->x509));
+}
+
static char crl_object_clear_extensions__doc__[] =
"Clear all extensions attached to this CRL.\n"
;
@@ -4943,8 +5584,6 @@ static char crl_object_sign__doc__[] =
"The optional \"digest\" parameter indicates which digest to compute and\n"
"sign, and should be one of the following:\n"
"\n"
- "* MD5_DIGEST\n"
- "* SHA_DIGEST\n"
"* SHA1_DIGEST\n"
"* SHA256_DIGEST\n"
"* SHA384_DIGEST\n"
@@ -4978,25 +5617,27 @@ crl_object_sign(crl_object *self, PyObject *args)
}
static char crl_object_verify__doc__[] =
- "Verify this CRL's signature.\n"
- "\n"
- "The check is performed using OpenSSL's X509_CRL_verify() function.\n"
- "\n"
- "The \"key\" parameter should be an instance of the Asymmetric class\n"
- "containing the public key of the purported signer.\n"
+ "Verify this CRL's signature against its issuer.\n"
;
static PyObject *
crl_object_verify(crl_object *self, PyObject *args)
{
- asymmetric_object *asym;
+ x509_object *issuer = NULL;
+ PyObject *status = Py_None;
ENTERING(crl_object_verify);
- if (!PyArg_ParseTuple(args, "O!", &POW_Asymmetric_Type, &asym))
+ if (!PyArg_ParseTuple(args, "O!|O!", &POW_X509_Type, &issuer, &PySet_Type, &status))
goto error;
- return PyBool_FromLong(X509_CRL_verify(self->crl, asym->pkey));
+ if (status != Py_None && !check_crl(self->crl, issuer->x509, status))
+ goto error;
+
+ if (!X509_CRL_verify(self->crl, X509_get_pubkey(issuer->x509)))
+ lose_validation_error("X509_CRL_verify() raised an exception");
+
+ Py_RETURN_NONE;
error:
return NULL;
@@ -5158,6 +5799,7 @@ static struct PyMethodDef crl_object_methods[] = {
Define_Method(getNextUpdate, crl_object_get_next_update, METH_NOARGS),
Define_Method(setNextUpdate, crl_object_set_next_update, METH_VARARGS),
Define_Method(getRevoked, crl_object_get_revoked, METH_NOARGS),
+ Define_Method(isRevoked, crl_object_is_revoked, METH_VARARGS),
Define_Method(addRevocations, crl_object_add_revocations, METH_VARARGS),
Define_Method(clearExtensions, crl_object_clear_extensions, METH_NOARGS),
Define_Method(pemWrite, crl_object_pem_write, METH_NOARGS),
@@ -6273,8 +6915,6 @@ static char POW_Digest_Type__doc__[] =
"The constructor takes one parameter, the kind of Digest object to create.\n"
"This should be one of the following:\n"
"\n"
- " * MD5_DIGEST\n"
- " * SHA_DIGEST\n"
" * SHA1_DIGEST\n"
" * SHA256_DIGEST\n"
" * SHA384_DIGEST\n"
@@ -6542,7 +7182,7 @@ cms_object_sign_helper(cms_object *self,
while ((item = PyIter_Next(iterator)) != NULL) {
if (!POW_CRL_Check(item))
- lose_type_error("Inappropriate type");
+ lose_type_error("Expected a CRL object");
if (!CMS_add1_crl(cms, ((crl_object *) item)->crl))
lose_openssl_error("Couldn't add CRL to CMS");
@@ -6651,15 +7291,12 @@ cms_object_sign(cms_object *self, PyObject *args)
return NULL;
}
-#define DONT_VERIFY_ANYTHING \
- (CMS_NOCRL | \
- CMS_NO_SIGNER_CERT_VERIFY | \
- CMS_NO_ATTR_VERIFY | \
- CMS_NO_CONTENT_VERIFY)
-
static BIO *
cms_object_extract_without_verifying_helper(cms_object *self)
{
+ const unsigned flags =
+ CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY | CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY;
+
BIO *bio = NULL;
ENTERING(cms_object_extract_without_verifying_helper);
@@ -6667,7 +7304,7 @@ cms_object_extract_without_verifying_helper(cms_object *self)
if ((bio = BIO_new(BIO_s_mem())) == NULL)
lose_no_memory();
- if (CMS_verify(self->cms, NULL, NULL, NULL, bio, DONT_VERIFY_ANYTHING) <= 0)
+ if (CMS_verify(self->cms, NULL, NULL, NULL, bio, flags) <= 0)
lose_openssl_error("Couldn't parse CMS message");
return bio;
@@ -6677,13 +7314,9 @@ cms_object_extract_without_verifying_helper(cms_object *self)
return NULL;
}
-#undef DONT_VERIFY_ANYTHING
#define CMS_OBJECT_VERIFY_HELPER__DOC__ \
"\n" \
- "The \"store\" parameter is an X509Store object, the trusted certificate\n" \
- "store to use in verification.\n" \
- "\n" \
"The optional \"certs\" parameter is a set of certificates to search\n" \
"for the signer's certificate.\n" \
"\n" \
@@ -6694,40 +7327,61 @@ cms_object_extract_without_verifying_helper(cms_object *self)
" * CMS_NOCRL\n" \
" * CMS_NO_SIGNER_CERT_VERIFY\n" \
" * CMS_NO_ATTR_VERIFY\n" \
- " * CMS_NO_CONTENT_VERIFY\n"
+ " * CMS_NO_CONTENT_VERIFY\n" \
+ "\n" \
+ "Note that this method does NOT verify X.509 certificates, it just\n" \
+ "verifies the CMS signature. Use certificate verification functions\n" \
+ "to verify certificates."
+
+#warning Should we really allow the full range of flags here, or constrain to just the useful cases?
static BIO *
-cms_object_verify_helper(cms_object *self, PyObject *args, PyObject *kwds)
+cms_object_verify_helper(cms_object *self, PyObject *args, PyObject *kwds, PyObject **status)
{
- static char *kwlist[] = {"store", "certs", "flags", NULL};
- x509_store_object *store = NULL;
+ static char *kwlist[] = {"certs", "flags", "status", NULL};
PyObject *certs_iterable = Py_None;
STACK_OF(X509) *certs_stack = NULL;
unsigned flags = 0, ok = 0;
BIO *bio = NULL;
+ const unsigned flag_mask =
+ CMS_NOINTERN | CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY |
+ CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY;
+
ENTERING(cms_object_verify_helper);
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OI", kwlist,
- &POW_X509Store_Type, &store, &certs_iterable, &flags))
+ if (status == NULL || *status != Py_None)
+ lose("cms_object_verify_helper() called with bad status argument (internal error)");
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OIO!", kwlist,
+ &certs_iterable,
+ &flags,
+ &PySet_Type, status))
goto error;
+ if ((flags & ~flag_mask) != 0)
+ lose_value_error("Bad CMS_verify() flags");
+
+ flags |= CMS_NO_SIGNER_CERT_VERIFY;
+
if ((bio = BIO_new(BIO_s_mem())) == NULL)
lose_no_memory();
assert_no_unhandled_openssl_errors();
- flags &= (CMS_NOINTERN | CMS_NOCRL | CMS_NO_SIGNER_CERT_VERIFY |
- CMS_NO_ATTR_VERIFY | CMS_NO_CONTENT_VERIFY);
-
if (certs_iterable != Py_None &&
(certs_stack = x509_helper_iterable_to_stack(certs_iterable)) == NULL)
goto error;
assert_no_unhandled_openssl_errors();
- if (CMS_verify(self->cms, certs_stack, store->store, NULL, bio, flags) <= 0)
+ if (CMS_verify(self->cms, certs_stack, NULL, NULL, bio, flags) <= 0) {
+ record_validation_status(*status, CMS_VALIDATION_FAILURE);
lose_openssl_error("Couldn't verify CMS message");
+ }
+
+ if (*status != Py_None && !check_cms(self->cms, *status))
+ goto error;
assert_no_unhandled_openssl_errors();
@@ -6754,12 +7408,13 @@ static char cms_object_verify__doc__[] =
static PyObject *
cms_object_verify(cms_object *self, PyObject *args, PyObject *kwds)
{
+ PyObject *status = Py_None;
PyObject *result = NULL;
BIO *bio = NULL;
ENTERING(cms_object_verify);
- if ((bio = cms_object_verify_helper(self, args, kwds)) != NULL)
+ if ((bio = cms_object_verify_helper(self, args, kwds, &status)) != NULL)
result = BIO_to_PyString_helper(bio);
BIO_free(bio);
@@ -6895,6 +7550,10 @@ static char cms_object_certs__doc__[] =
"contains no certificates.\n"
;
+/*
+ * Might want to accept an optional subclass argument.
+ */
+
static PyObject *
cms_object_certs(cms_object *self)
{
@@ -6921,6 +7580,10 @@ static char cms_object_crls__doc__[] =
"This sequence will be empty if the message contains no CRLs.\n"
;
+/*
+ * Might want to accept an optional subclass argument.
+ */
+
static PyObject *
cms_object_crls(cms_object *self)
{
@@ -7045,16 +7708,25 @@ static char manifest_object_verify__doc__[] =
static PyObject *
manifest_object_verify(manifest_object *self, PyObject *args, PyObject *kwds)
{
+ PyObject *status = Py_None;
BIO *bio = NULL;
int ok = 0;
ENTERING(manifest_object_verify);
- if ((bio = cms_object_verify_helper(&self->cms, args, kwds)) == NULL)
+ if ((bio = cms_object_verify_helper(&self->cms, args, kwds, &status)) == NULL)
goto error;
- if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(Manifest), bio, &self->manifest))
+ if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(Manifest), bio, &self->manifest)) {
+ record_validation_status(status, CMS_ECONTENT_DECODE_ERROR);
lose_openssl_error("Couldn't decode manifest");
+ }
+
+ if (status != Py_None && !check_cms(self->cms.cms, status))
+ goto error;
+
+ if (status != Py_None && !check_manifest(self->cms.cms, self->manifest, status))
+ goto error;
ok = 1;
@@ -7720,16 +8392,25 @@ static char roa_object_verify__doc__[] =
static PyObject *
roa_object_verify(roa_object *self, PyObject *args, PyObject *kwds)
{
+ PyObject *status = Py_None;
BIO *bio = NULL;
int ok = 0;
ENTERING(roa_object_verify);
- if ((bio = cms_object_verify_helper(&self->cms, args, kwds)) == NULL)
+ if ((bio = cms_object_verify_helper(&self->cms, args, kwds, &status)) == NULL)
goto error;
- if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, &self->roa))
+ if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, &self->roa)) {
+ record_validation_status(status, CMS_ECONTENT_DECODE_ERROR);
lose_openssl_error("Couldn't decode ROA");
+ }
+
+ if (status != Py_None && !check_cms(self->cms.cms, status))
+ goto error;
+
+ if (status != Py_None && !check_roa(self->cms.cms, self->roa, status))
+ goto error;
ok = 1;
@@ -8005,14 +8686,14 @@ roa_object_get_prefixes(roa_object *self)
switch (afi) {
case IANA_AFI_IPV4: resultp = &ipv4_result; ip_type = &ipaddress_version_4; break;
case IANA_AFI_IPV6: resultp = &ipv6_result; ip_type = &ipaddress_version_6; break;
- default: lose_type_error("Unknown AFI");
+ default: lose_value_error("Unknown AFI");
}
if (fam->addressFamily->length > 2)
- lose_type_error("Unsupported SAFI");
+ lose_value_error("Unsupported SAFI");
if (*resultp != NULL)
- lose_type_error("Duplicate ROAIPAddressFamily");
+ lose_value_error("Duplicate ROAIPAddressFamily");
if ((*resultp = PyTuple_New(sk_ROAIPAddress_num(fam->addresses))) == NULL)
goto error;
@@ -8158,7 +8839,7 @@ roa_object_set_prefixes(roa_object *self, PyObject *args, PyObject *kwds)
}
if (addr->type != ip_type)
- lose_type_error("Bad ROA prefix");
+ lose_value_error("Bad ROA prefix");
if (prefixlen > addr->type->length * 8)
lose("Bad prefix length");
@@ -8597,8 +9278,6 @@ static char pkcs10_object_sign__doc__[] =
"The optional \"digest\" parameter indicates which digest to compute and\n"
"sign, and should be one of the following:\n"
"\n"
- "* MD5_DIGEST\n"
- "* SHA_DIGEST\n"
"* SHA1_DIGEST\n"
"* SHA256_DIGEST\n"
"* SHA384_DIGEST\n"
@@ -8752,7 +9431,7 @@ pkcs10_object_set_subject(pkcs10_object *self, PyObject *args)
goto error;
if (!PySequence_Check(name_sequence))
- lose_type_error("Inapropriate type");
+ lose_type_error("Expected a sequence object");
if ((name = x509_object_helper_set_name(name_sequence)) == NULL)
goto error;
@@ -9082,6 +9761,110 @@ pow_module_clear_error(GCC_UNUSED PyObject *self)
Py_RETURN_NONE;
}
+static char pow_module_get_verification_errors__doc__[] =
+ "Return strings for known OpenSSL certificate verification errors.\n"
+ "Returns a list of (number, symbol, text) tuples.\n"
+ ;
+
+static PyObject *
+pow_module_get_verification_errors(GCC_UNUSED PyObject *self)
+{
+ PyObject *result = NULL, *item = NULL;
+
+ ENTERING(pow_module_get_verification_errors);
+
+ /*
+ * This function is only called once, and doesn't need to be
+ * particularly efficient, so we use a list to keep the code simple.
+ */
+
+ if ((result = PyList_New(0)) == NULL)
+ goto error;
+
+#define Verification_Error(_v_) \
+ do { \
+ const char *msg = X509_verify_cert_error_string(_v_); \
+ if ((item = Py_BuildValue("(iss)", _v_, #_v_, msg)) == NULL || \
+ PyList_Append(result, item) < 0) \
+ goto error; \
+ Py_XDECREF(item); \
+ item = NULL; \
+ } while (0)
+
+ Verification_Error( X509_V_OK );
+ Verification_Error( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT );
+ Verification_Error( X509_V_ERR_UNABLE_TO_GET_CRL );
+ Verification_Error( X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE );
+ Verification_Error( X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE );
+ Verification_Error( X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY );
+ Verification_Error( X509_V_ERR_CERT_SIGNATURE_FAILURE );
+ Verification_Error( X509_V_ERR_CRL_SIGNATURE_FAILURE );
+ Verification_Error( X509_V_ERR_CERT_NOT_YET_VALID );
+ Verification_Error( X509_V_ERR_CERT_HAS_EXPIRED );
+ Verification_Error( X509_V_ERR_CRL_NOT_YET_VALID );
+ Verification_Error( X509_V_ERR_CRL_HAS_EXPIRED );
+ Verification_Error( X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD );
+ Verification_Error( X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD );
+ Verification_Error( X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD );
+ Verification_Error( X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD );
+ Verification_Error( X509_V_ERR_OUT_OF_MEM );
+ Verification_Error( X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT );
+ Verification_Error( X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN );
+ Verification_Error( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY );
+ Verification_Error( X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE );
+ Verification_Error( X509_V_ERR_CERT_CHAIN_TOO_LONG );
+ Verification_Error( X509_V_ERR_CERT_REVOKED );
+ Verification_Error( X509_V_ERR_INVALID_CA );
+ Verification_Error( X509_V_ERR_PATH_LENGTH_EXCEEDED );
+ Verification_Error( X509_V_ERR_INVALID_PURPOSE );
+ Verification_Error( X509_V_ERR_CERT_UNTRUSTED );
+ Verification_Error( X509_V_ERR_CERT_REJECTED );
+ Verification_Error( X509_V_ERR_SUBJECT_ISSUER_MISMATCH );
+ Verification_Error( X509_V_ERR_AKID_SKID_MISMATCH );
+ Verification_Error( X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH );
+ Verification_Error( X509_V_ERR_KEYUSAGE_NO_CERTSIGN );
+ Verification_Error( X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER );
+ Verification_Error( X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION );
+ Verification_Error( X509_V_ERR_KEYUSAGE_NO_CRL_SIGN );
+ Verification_Error( X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION );
+ Verification_Error( X509_V_ERR_INVALID_NON_CA );
+ Verification_Error( X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED );
+ Verification_Error( X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE );
+ Verification_Error( X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED );
+ Verification_Error( X509_V_ERR_INVALID_EXTENSION );
+ Verification_Error( X509_V_ERR_INVALID_POLICY_EXTENSION );
+ Verification_Error( X509_V_ERR_NO_EXPLICIT_POLICY );
+ Verification_Error( X509_V_ERR_DIFFERENT_CRL_SCOPE );
+ Verification_Error( X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE );
+ Verification_Error( X509_V_ERR_UNNESTED_RESOURCE );
+ Verification_Error( X509_V_ERR_PERMITTED_VIOLATION );
+ Verification_Error( X509_V_ERR_EXCLUDED_VIOLATION );
+ Verification_Error( X509_V_ERR_SUBTREE_MINMAX );
+ Verification_Error( X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE );
+ Verification_Error( X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX );
+ Verification_Error( X509_V_ERR_UNSUPPORTED_NAME_SYNTAX );
+ Verification_Error( X509_V_ERR_CRL_PATH_VALIDATION_ERROR );
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_VERSION );
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_ALGORITHM );
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_CURVE );
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM );
+ Verification_Error( X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED );
+ Verification_Error( X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 );
+ Verification_Error( X509_V_ERR_HOSTNAME_MISMATCH );
+ Verification_Error( X509_V_ERR_EMAIL_MISMATCH );
+ Verification_Error( X509_V_ERR_IP_ADDRESS_MISMATCH );
+ Verification_Error( X509_V_ERR_APPLICATION_VERIFICATION );
+
+#undef Verification_Error
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ Py_XDECREF(item);
+ return NULL;
+}
+
static char pow_module_seed__doc__[] =
"Add data to OpenSSL's pseudo-random number generator state.\n"
"\n"
@@ -9217,14 +10000,15 @@ pow_module_custom_datetime(GCC_UNUSED PyObject *self, PyObject *args)
static struct PyMethodDef pow_module_methods[] = {
- Define_Method(getError, pow_module_get_error, METH_NOARGS),
- Define_Method(clearError, pow_module_clear_error, METH_NOARGS),
- Define_Method(seed, pow_module_seed, METH_VARARGS),
- Define_Method(add, pow_module_add, METH_VARARGS),
- Define_Method(readRandomFile, pow_module_read_random_file, METH_VARARGS),
- Define_Method(writeRandomFile, pow_module_write_random_file, METH_VARARGS),
- Define_Method(addObject, pow_module_add_object, METH_VARARGS),
- Define_Method(customDatetime, pow_module_custom_datetime, METH_VARARGS),
+ Define_Method(getError, pow_module_get_error, METH_NOARGS),
+ Define_Method(clearError, pow_module_clear_error, METH_NOARGS),
+ Define_Method(getVerificationErrors, pow_module_get_verification_errors, METH_NOARGS),
+ Define_Method(seed, pow_module_seed, METH_VARARGS),
+ Define_Method(add, pow_module_add, METH_VARARGS),
+ Define_Method(readRandomFile, pow_module_read_random_file, METH_VARARGS),
+ Define_Method(writeRandomFile, pow_module_write_random_file, METH_VARARGS),
+ Define_Method(addObject, pow_module_add_object, METH_VARARGS),
+ Define_Method(customDatetime, pow_module_custom_datetime, METH_VARARGS),
{NULL}
};
@@ -9274,7 +10058,6 @@ init_POW(void)
} while (0)
Define_Class(POW_X509_Type);
- Define_Class(POW_X509Store_Type);
Define_Class(POW_X509StoreCTX_Type);
Define_Class(POW_CRL_Type);
Define_Class(POW_Asymmetric_Type);
@@ -9296,6 +10079,7 @@ init_POW(void)
Define_Exception(OpenSSLError, ErrorObject);
Define_Exception(POWError, ErrorObject);
Define_Exception(NotVerifiedError, ErrorObject);
+ Define_Exception(ValidationError, ErrorObject);
#undef Define_Exception
@@ -9308,8 +10092,6 @@ init_POW(void)
Define_Integer_Constant(OIDNAME_FORMAT);
/* Message digests */
- Define_Integer_Constant(MD5_DIGEST);
- Define_Integer_Constant(SHA_DIGEST);
Define_Integer_Constant(SHA1_DIGEST);
Define_Integer_Constant(SHA256_DIGEST);
Define_Integer_Constant(SHA384_DIGEST);
@@ -9411,6 +10193,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");
}