aboutsummaryrefslogtreecommitdiff
path: root/ext/POW.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/POW.c')
-rw-r--r--ext/POW.c2347
1 files changed, 1645 insertions, 702 deletions
diff --git a/ext/POW.c b/ext/POW.c
index 990d344d..4519da5f 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_ct_rpkiGhostbusters
+static int NID_ct_rpkiGhostbusters;
+#endif
+
+#ifndef NID_cp_ipAddr_asNumber
+static int NID_cp_ipAddr_asNumber;
#endif
-#ifndef NID_signedObject
-static int NID_signedObject;
+#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_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_signedObject
- {&NID_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"}
+#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 {
@@ -338,6 +395,17 @@ typedef struct {
X509_EXTENSIONS *exts;
} pkcs10_object;
+/*
+ * Container for a generic extension, including a destructor.
+ */
+
+typedef struct {
+ void (*destructor)(void *);
+ void *value;
+ int nid;
+ int critical;
+} extension_wrapper;
+
/*
@@ -401,6 +469,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 +501,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 +596,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 +604,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 ||
@@ -573,7 +645,7 @@ x509_object_helper_set_name(PyObject *dn_obj)
static PyObject *
x509_object_helper_get_name(X509_NAME *name, int format)
{
- X509_NAME_ENTRY *entry = NULL;
+ X509_NAME_ENTRY *ne = NULL;
PyObject *result = NULL;
PyObject *rdn = NULL;
PyObject *item = NULL;
@@ -593,18 +665,18 @@ x509_object_helper_get_name(X509_NAME *name, int format)
for (i = 0; i < X509_NAME_entry_count(name); i++) {
- if ((entry = X509_NAME_get_entry(name, i)) == NULL)
+ if ((ne = X509_NAME_get_entry(name, i)) == NULL)
lose("Couldn't get certificate name");
- if (entry->set < 0 || entry->set < set || entry->set > set + 1)
+ if (ne->set < 0 || ne->set < set || ne->set > set + 1)
lose("X509_NAME->set value out of expected range");
switch (format) {
case SHORTNAME_FORMAT:
- oid = OBJ_nid2sn(OBJ_obj2nid(entry->object));
+ oid = OBJ_nid2sn(OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne)));
break;
case LONGNAME_FORMAT:
- oid = OBJ_nid2ln(OBJ_obj2nid(entry->object));
+ oid = OBJ_nid2ln(OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne)));
break;
case OIDNAME_FORMAT:
oid = NULL;
@@ -614,16 +686,16 @@ x509_object_helper_get_name(X509_NAME *name, int format)
}
if (oid == NULL) {
- if (OBJ_obj2txt(oidbuf, sizeof(oidbuf), entry->object, 1) <= 0)
+ if (OBJ_obj2txt(oidbuf, sizeof(oidbuf), X509_NAME_ENTRY_get_object(ne), 1) <= 0)
lose_openssl_error("Couldn't translate OID");
oid = oidbuf;
}
- if (entry->set > set) {
+ if (ne->set > set) {
set++;
- if ((item = Py_BuildValue("((ss#))", oid, ASN1_STRING_data(entry->value),
- (Py_ssize_t) ASN1_STRING_length(entry->value))) == NULL)
+ if ((item = Py_BuildValue("((ss#))", oid, ASN1_STRING_data(X509_NAME_ENTRY_get_data(ne)),
+ (Py_ssize_t) ASN1_STRING_length(X509_NAME_ENTRY_get_data(ne)))) == NULL)
goto error;
PyTuple_SET_ITEM(result, set, item);
item = NULL;
@@ -636,8 +708,8 @@ x509_object_helper_get_name(X509_NAME *name, int format)
PyTuple_SET_ITEM(result, set, rdn);
if (rdn == NULL)
goto error;
- if ((item = Py_BuildValue("(ss#)", oid, ASN1_STRING_data(entry->value),
- (Py_ssize_t) ASN1_STRING_length(entry->value))) == NULL)
+ if ((item = Py_BuildValue("(ss#)", oid, ASN1_STRING_data(X509_NAME_ENTRY_get_data(ne)),
+ (Py_ssize_t) ASN1_STRING_length(X509_NAME_ENTRY_get_data(ne)))) == NULL)
goto error;
PyTuple_SetItem(rdn, PyTuple_Size(rdn) - 1, item);
rdn = item = NULL;
@@ -677,7 +749,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,10 +1156,504 @@ whack_ec_key_to_namedCurve(EVP_PKEY *pkey)
/*
- * 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
- * X509_EXTENSIONS objects.
+ * 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;
+ AUTHORITY_KEYID *aki = NULL;
+ EVP_PKEY *pkey;
+ int i, ret = 0;
+
+ if (crl->crl == NULL ||
+ crl->crl->sig_alg == NULL || crl->crl->sig_alg->algorithm == NULL ||
+ 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 ((aki = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL)) == NULL)
+ record_validation_status(status, AKI_EXTENSION_MISSING);
+ else if (aki->keyid == NULL || aki->serial != NULL || aki->issuer != NULL)
+ 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:
+ AUTHORITY_KEYID_free(aki);
+ 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 (si != NULL && x != NULL && 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;
+
+ if (manifest == NULL)
+ lose_not_verified("Can't check an unverified manifest");
+
+ 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);
+
+ if ((certs = CMS_get1_certs(cms)) == NULL || sk_X509_num(certs) != 1)
+ record_validation_status(status, BAD_CMS_SIGNER);
+
+ 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);
+
+ 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();
+
+ (void) sk_FileAndHash_set_cmp_func(sorted_fileList, check_manifest_FileAndHash_name_cmp);
+ sk_FileAndHash_sort(sorted_fileList);
+
+ 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);
+
+ 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;
+
+ if (roa == NULL)
+ lose_not_verified("Can't check an unverified ROA");
+
+#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 a_len, b_len;
+
+ if ((a_len = v3_addr_get_range(a, afi, a_min, a_max, RAW_IPADDR_BUFLEN)) == 0 ||
+ (b_len = v3_addr_get_range(b, afi, b_min, b_max, RAW_IPADDR_BUFLEN)) == 0 ||
+ a_len != b_len)
+ record_validation_status(status, ROA_RESOURCES_MALFORMED);
+
+ if (memcmp(a_max, b_max, a_len) >= 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.
*/
#define EXTENSION_GET_KEY_USAGE__DOC__ \
@@ -1096,7 +1662,7 @@ whack_ec_key_to_namedCurve(EVP_PKEY *pkey)
"extension. The bits have the same names as in RFC 5280.\n"
static PyObject *
-extension_get_key_usage(X509_EXTENSIONS **exts)
+extension_get_key_usage(X509_EXTENSION *ext_)
{
ASN1_BIT_STRING *ext = NULL;
PyObject *result = NULL;
@@ -1105,12 +1671,12 @@ extension_get_key_usage(X509_EXTENSIONS **exts)
ENTERING(extension_get_key_usage);
- if (!exts)
- goto error;
-
- if ((ext = X509V3_get_d2i(*exts, NID_key_usage, NULL, NULL)) == NULL)
+ if (!ext_)
Py_RETURN_NONE;
+ if ((ext = X509V3_EXT_d2i(ext_)) == NULL)
+ lose_openssl_error("Couldn't parse KeyUsage extension");
+
if ((result = PyFrozenSet_New(NULL)) == NULL)
goto error;
@@ -1141,8 +1707,14 @@ extension_get_key_usage(X509_EXTENSIONS **exts)
"should be marked as critical or not. RFC 5280 4.2.1.3 says this extension SHOULD\n" \
"be marked as critical when used, so the default is True.\n"
-static PyObject *
-extension_set_key_usage(X509_EXTENSIONS **exts, PyObject *args)
+static void
+extension_set_key_usage_destructor(void *value)
+{
+ ASN1_BIT_STRING_free(value);
+}
+
+static extension_wrapper
+extension_set_key_usage(PyObject *args)
{
ASN1_BIT_STRING *ext = NULL;
PyObject *iterable = NULL;
@@ -1151,12 +1723,10 @@ extension_set_key_usage(X509_EXTENSIONS **exts, PyObject *args)
PyObject *item = NULL;
const char *token;
int bit = -1;
- int ok = 0;
- ENTERING(extension_set_key_usage);
+ extension_wrapper result = {extension_set_key_usage_destructor};
- if (!exts)
- goto error;
+ ENTERING(extension_set_key_usage);
if ((ext = ASN1_BIT_STRING_new()) == NULL)
lose_no_memory();
@@ -1184,22 +1754,16 @@ extension_set_key_usage(X509_EXTENSIONS **exts, PyObject *args)
item = NULL;
}
- if (!X509V3_add1_i2d(exts, NID_key_usage, ext,
- PyObject_IsTrue(critical),
- X509V3_ADD_REPLACE))
- lose_openssl_error("Couldn't add KeyUsage extension to OpenSSL object");
-
- ok = 1;
+ result.value = ext;
+ result.nid = NID_key_usage;
+ result.critical = PyObject_IsTrue(critical);
+ ext = NULL;
error: /* Fall through */
ASN1_BIT_STRING_free(ext);
Py_XDECREF(iterator);
Py_XDECREF(item);
-
- if (ok)
- Py_RETURN_NONE;
- else
- return NULL;
+ return result;
}
#define EXTENSION_GET_BASIC_CONSTRAINTS__DOC__ \
@@ -1211,19 +1775,19 @@ extension_set_key_usage(X509_EXTENSIONS **exts, PyObject *args)
"pathLenConstraint value or None if there is no pathLenConstraint.\n"
static PyObject *
-extension_get_basic_constraints(X509_EXTENSIONS **exts)
+extension_get_basic_constraints(X509_EXTENSION *ext_)
{
BASIC_CONSTRAINTS *ext = NULL;
PyObject *result = NULL;
ENTERING(extension_get_basic_constraints);
- if (!exts)
- goto error;
-
- if ((ext = X509V3_get_d2i(*exts, NID_basic_constraints, NULL, NULL)) == NULL)
+ if (!ext_)
Py_RETURN_NONE;
+ if ((ext = X509V3_EXT_d2i(ext_)) == NULL)
+ lose_openssl_error("Couldn't parse BasicConstraints extension");
+
if (ext->pathlen == NULL)
result = Py_BuildValue("(NO)", PyBool_FromLong(ext->ca), Py_None);
else
@@ -1246,26 +1810,30 @@ extension_get_basic_constraints(X509_EXTENSIONS **exts)
"should be marked as critical. RFC 5280 4.2.1.9 requires that CA\n" \
"certificates mark this extension as critical, so the default is True.\n"
-static PyObject *
-extension_set_basic_constraints(X509_EXTENSIONS **exts, PyObject *args)
+static void
+extension_set_basic_constraints_destructor(void *value)
+{
+ BASIC_CONSTRAINTS_free(value);
+}
+
+static extension_wrapper
+extension_set_basic_constraints(PyObject *args)
{
BASIC_CONSTRAINTS *ext = NULL;
PyObject *is_ca = NULL;
PyObject *pathlen_obj = Py_None;
PyObject *critical = Py_True;
long pathlen = -1;
- int ok = 0;
- ENTERING(extension_set_basic_constraints);
+ extension_wrapper result = {extension_set_basic_constraints_destructor};
- if (!exts)
- goto error;
+ ENTERING(extension_set_basic_constraints);
if (!PyArg_ParseTuple(args, "O|OO", &is_ca, &pathlen_obj, &critical))
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();
@@ -1277,54 +1845,51 @@ extension_set_basic_constraints(X509_EXTENSIONS **exts, PyObject *args)
!ASN1_INTEGER_set(ext->pathlen, pathlen)))
lose_no_memory();
- if (!X509V3_add1_i2d(exts, NID_basic_constraints, ext,
- PyObject_IsTrue(critical), X509V3_ADD_REPLACE))
- lose_openssl_error("Couldn't add BasicConstraints extension to OpenSSL object");
+ result.value = ext;
+ result.nid = NID_basic_constraints;
+ result.critical = PyObject_IsTrue(critical);
+ ext = NULL;
- ok = 1;
-
- error:
+ error: /* Fall through */
BASIC_CONSTRAINTS_free(ext);
-
- if (ok)
- Py_RETURN_NONE;
- else
- return NULL;
+ return result;
}
#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" \
"Any other accessMethods are ignored, as are any non-URI accessLocations.\n"
static PyObject *
-extension_get_sia(X509_EXTENSIONS **exts)
+extension_get_sia(X509_EXTENSION *ext_)
{
AUTHORITY_INFO_ACCESS *ext = NULL;
PyObject *result = NULL;
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;
- ENTERING(pkcs10_object_get_sia);
-
- if (!exts)
- goto error;
+ ENTERING(extension_get_sia);
- if ((ext = X509V3_get_d2i(*exts, NID_sinfo_access, NULL, NULL)) == NULL)
+ if (!ext_)
Py_RETURN_NONE;
+ if ((ext = X509V3_EXT_d2i(ext_)) == NULL)
+ lose_openssl_error("Couldn't parse SubjectInformationAccess extension");
+
/*
* Easiest to do this in two passes, first pass just counts URIs.
*/
@@ -1334,26 +1899,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,65 +1929,79 @@ 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" \
"collection of URIs for a particular argument.\n"
-static PyObject *
-extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
+static void
+extension_set_sia_destructor(void *value)
+{
+ AUTHORITY_INFO_ACCESS_free(value);
+}
+
+static extension_wrapper
+extension_set_sia(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;
PyObject *item = NULL;
ACCESS_DESCRIPTION *a = NULL;
- int i, nid = NID_undef, ok = 0;
+ int i, nid = NID_undef;
Py_ssize_t urilen;
char *uri;
- ENTERING(extension_set_sia);
+ extension_wrapper result = {extension_set_sia_destructor};
- if (!exts)
- goto error;
+ ENTERING(extension_set_sia);
- 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 +2013,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 +2038,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;
@@ -1478,21 +2056,17 @@ extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
iterator = NULL;
}
- if (!X509V3_add1_i2d(exts, NID_sinfo_access, ext, 0, X509V3_ADD_REPLACE))
- lose_openssl_error("Couldn't add SIA extension to OpenSSL object");
-
- ok = 1;
+ result.value = ext;
+ result.nid = NID_sinfo_access;
+ result.critical = 0;
+ ext = NULL;
- error:
+ error: /* Fall through */
AUTHORITY_INFO_ACCESS_free(ext);
ACCESS_DESCRIPTION_free(a);
Py_XDECREF(item);
Py_XDECREF(iterator);
-
- if (ok)
- Py_RETURN_NONE;
- else
- return NULL;
+ return result;
}
#define EXTENSION_GET_EKU__DOC__ \
@@ -1501,7 +2075,7 @@ extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds)
"has no ExtendedKeyUsage extension.\n"
static PyObject *
-extension_get_eku(X509_EXTENSIONS **exts)
+extension_get_eku(X509_EXTENSION *ext_)
{
EXTENDED_KEY_USAGE *ext = NULL;
PyObject *result = NULL;
@@ -1510,12 +2084,12 @@ extension_get_eku(X509_EXTENSIONS **exts)
ENTERING(extension_get_eku);
- if (!exts)
- goto error;
-
- if ((ext = X509V3_get_d2i(*exts, NID_ext_key_usage, NULL, NULL)) == NULL)
+ if (!ext_)
Py_RETURN_NONE;
+ if ((ext = X509V3_EXT_d2i(ext_)) == NULL)
+ lose_openssl_error("Couldn't parse ExtendedKeyUsage extension");
+
if ((result = PyFrozenSet_New(NULL)) == NULL)
goto error;
@@ -1545,8 +2119,14 @@ extension_get_eku(X509_EXTENSIONS **exts)
"should be marked as critical or not. RFC 6487 4.8.5 says this extension\n" \
"MUST NOT be marked as non-critical when used, so the default is False.\n"
-static PyObject *
-extension_set_eku(X509_EXTENSIONS **exts, PyObject *args)
+static void
+extension_set_eku_destructor(void *value)
+{
+ sk_ASN1_OBJECT_pop_free(value, ASN1_OBJECT_free);
+}
+
+static extension_wrapper
+extension_set_eku(PyObject *args)
{
EXTENDED_KEY_USAGE *ext = NULL;
PyObject *iterable = NULL;
@@ -1555,12 +2135,10 @@ extension_set_eku(X509_EXTENSIONS **exts, PyObject *args)
PyObject *item = NULL;
ASN1_OBJECT *obj = NULL;
const char *txt;
- int ok = 0;
- ENTERING(extension_set_eku);
+ extension_wrapper result = {extension_set_eku_destructor};
- if (!exts)
- goto error;
+ ENTERING(extension_set_eku);
if ((ext = sk_ASN1_OBJECT_new_null()) == NULL)
lose_no_memory();
@@ -1588,22 +2166,16 @@ extension_set_eku(X509_EXTENSIONS **exts, PyObject *args)
if (sk_ASN1_OBJECT_num(ext) < 1)
lose("Empty ExtendedKeyUsage extension");
- if (!X509V3_add1_i2d(exts, NID_ext_key_usage, ext,
- PyObject_IsTrue(critical),
- X509V3_ADD_REPLACE))
- lose_openssl_error("Couldn't add ExtendedKeyUsage extension to OpenSSL object");
-
- ok = 1;
+ result.value = ext;
+ result.nid = NID_ext_key_usage;
+ result.critical = PyObject_IsTrue(critical);
+ ext = NULL;
error: /* Fall through */
sk_ASN1_OBJECT_pop_free(ext, ASN1_OBJECT_free);
Py_XDECREF(item);
Py_XDECREF(iterator);
-
- if (ok)
- Py_RETURN_NONE;
- else
- return NULL;
+ return result;
}
#define EXTENSION_GET_SKI__DOC__ \
@@ -1611,19 +2183,19 @@ extension_set_eku(X509_EXTENSIONS **exts, PyObject *args)
"or None if the object has no SKI extension.\n"
static PyObject *
-extension_get_ski(X509_EXTENSIONS **exts)
+extension_get_ski(X509_EXTENSION *ext_)
{
ASN1_OCTET_STRING *ext = NULL;
PyObject *result = NULL;
ENTERING(extension_get_ski);
- if (!exts)
- goto error;
-
- if ((ext = X509V3_get_d2i(*exts, NID_subject_key_identifier, NULL, NULL)) == NULL)
+ if (!ext_)
Py_RETURN_NONE;
+ if ((ext = X509V3_EXT_d2i(ext_)) == NULL)
+ lose_openssl_error("Couldn't parse SubjectKeyIdentifier extension");
+
result = Py_BuildValue("s#", ASN1_STRING_data(ext),
(Py_ssize_t) ASN1_STRING_length(ext));
@@ -1635,18 +2207,22 @@ extension_get_ski(X509_EXTENSIONS **exts)
#define EXTENSION_SET_SKI__DOC__ \
"Set the Subject Key Identifier (SKI) value for this object.\n"
-static PyObject *
-extension_set_ski(X509_EXTENSIONS **exts, PyObject *args)
+static void
+extension_set_ski_destructor(void *value)
+{
+ ASN1_OCTET_STRING_free(value);
+}
+
+static extension_wrapper
+extension_set_ski(PyObject *args)
{
ASN1_OCTET_STRING *ext = NULL;
const unsigned char *buf = NULL;
Py_ssize_t len;
- int ok = 0;
- ENTERING(extension_set_ski);
+ extension_wrapper result = {extension_set_ski_destructor};
- if (!exts)
- goto error;
+ ENTERING(extension_set_ski);
if (!PyArg_ParseTuple(args, "s#", &buf, &len))
goto error;
@@ -1659,19 +2235,14 @@ extension_set_ski(X509_EXTENSIONS **exts, PyObject *args)
* RFC 5280 says this MUST be non-critical.
*/
- if (!X509V3_add1_i2d(exts, NID_subject_key_identifier,
- ext, 0, X509V3_ADD_REPLACE))
- lose_openssl_error("Couldn't add SKI extension to OpenSSL object");
-
- ok = 1;
+ result.value = ext;
+ result.nid = NID_subject_key_identifier;
+ result.critical = 0;
+ ext = NULL;
error:
ASN1_OCTET_STRING_free(ext);
-
- if (ok)
- Py_RETURN_NONE;
- else
- return NULL;
+ return result;
}
#define EXTENSION_GET_AKI__DOC__ \
@@ -1680,19 +2251,19 @@ extension_set_ski(X509_EXTENSIONS **exts, PyObject *args)
"no keyIdentifier value.\n"
static PyObject *
-extension_get_aki(X509_EXTENSIONS **exts)
+extension_get_aki(X509_EXTENSION *ext_)
{
AUTHORITY_KEYID *ext = NULL;
PyObject *result = NULL;
ENTERING(extension_get_aki);
- if (!exts)
- goto error;
-
- if ((ext = X509V3_get_d2i(*exts, NID_authority_key_identifier, NULL, NULL)) == NULL)
+ if (!ext_)
Py_RETURN_NONE;
+ if ((ext = X509V3_EXT_d2i(ext_)) == NULL)
+ lose_openssl_error("Couldn't parse AuthorityKeyIdentifier extension");
+
result = Py_BuildValue("s#", ASN1_STRING_data(ext->keyid),
(Py_ssize_t) ASN1_STRING_length(ext->keyid));
@@ -1707,13 +2278,20 @@ extension_get_aki(X509_EXTENSIONS **exts)
"We only support the keyIdentifier method, as that's the only form\n" \
"which is legal for RPKI certificates.\n"
-static PyObject *
-extension_set_aki(X509_EXTENSIONS **exts, PyObject *args)
+static void
+extension_set_aki_destructor(void *value)
+{
+ AUTHORITY_KEYID_free(value);
+}
+
+static extension_wrapper
+extension_set_aki(PyObject *args)
{
AUTHORITY_KEYID *ext = NULL;
const unsigned char *buf = NULL;
Py_ssize_t len;
- int ok = 0;
+
+ extension_wrapper result = {extension_set_aki_destructor};
ENTERING(extension_set_aki);
@@ -1731,19 +2309,14 @@ extension_set_aki(X509_EXTENSIONS **exts, PyObject *args)
* RFC 5280 says this MUST be non-critical.
*/
- if (!X509V3_add1_i2d(exts, NID_authority_key_identifier,
- ext, 0, X509V3_ADD_REPLACE))
- lose_openssl_error("Couldn't add AKI extension to OpenSSL object");
-
- ok = 1;
+ result.value = ext;
+ result.nid = NID_authority_key_identifier;
+ result.critical = 0;
+ ext = NULL;
error:
AUTHORITY_KEYID_free(ext);
-
- if (ok)
- Py_RETURN_NONE;
- else
- return NULL;
+ return result;
}
@@ -2417,13 +2990,34 @@ x509_object_der_write(x509_object *self)
return result;
}
-static X509_EXTENSIONS **
-x509_object_extension_helper(x509_object *self)
+static X509_EXTENSION *
+x509_object_extension_get_helper(x509_object *self, int nid)
{
- if (self && self->x509 && self->x509->cert_info)
- return &self->x509->cert_info->extensions;
- PyErr_SetString(PyExc_ValueError, "Can't find X509_EXTENSIONS in X509 object");
- return NULL;
+ if (self != NULL && self->x509 != NULL)
+ return X509_get_ext(self->x509, X509_get_ext_by_NID(self->x509, nid, -1));
+ else
+ return NULL;
+}
+
+static PyObject *
+x509_object_extension_set_helper(x509_object *self, extension_wrapper ext)
+{
+ int ok = 0;
+
+ if (ext.value == NULL)
+ goto error;
+
+ if (!X509_add1_ext_i2d(self->x509, ext.nid, ext.value, ext.critical, X509V3_ADD_REPLACE))
+ lose_openssl_error("Couldn't add extension to certificate");
+
+ ok = 1;
+
+ error:
+ ext.destructor(ext.value);
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
}
static char x509_object_get_public_key__doc__[] =
@@ -2489,8 +3083,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 +3115,407 @@ 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;
+ ASN1_OCTET_STRING *ski = NULL;
+ AUTHORITY_KEYID *aki = NULL;
+ ASIdentifiers *asid = NULL;
+ IPAddrBlocks *addr = NULL;
+ unsigned char ski_hashbuf[EVP_MAX_MD_SIZE];
+ unsigned ski_hashlen, afi;
+ int i, ok, crit, 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 (X509_get_signature_nid(self->x509) != 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. 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 ((addr = X509_get_ext_d2i(self->x509, NID_sbgp_ipAddrBlock, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (!crit || ekunid == NID_id_kp_bgpsec_router ||
+ !v3_addr_is_canonical(addr) || sk_IPAddressFamily_num(addr) == 0)
+ record_validation_status(status, BAD_IPADDRBLOCKS);
+ else
+ for (i = 0; i < sk_IPAddressFamily_num(addr); i++) {
+ IPAddressFamily *f = sk_IPAddressFamily_value(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 ((asid = X509_get_ext_d2i(self->x509, NID_sbgp_autonomousSysNum, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (!crit || asid->asnum == NULL || asid->rdi != NULL || !v3_asid_is_canonical(asid) ||
+ (ekunid == NID_id_kp_bgpsec_router && asid->asnum->type == ASIdentifierChoice_inherit))
+ record_validation_status(status, BAD_ASIDENTIFIERS);
+ }
+
+ if (addr == NULL && asid == NULL)
+ record_validation_status(status, MISSING_RESOURCES);
+
+ /* Non-critical */
+ if ((ski = X509_get_ext_d2i(self->x509, NID_subject_key_identifier, &crit, NULL)) == NULL)
+ record_validation_status(status, SKI_EXTENSION_MISSING);
+ else {
+ ex_count--;
+ if (crit)
+ 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 != ASN1_STRING_length(ski) ||
+ memcmp(ski_hashbuf, ASN1_STRING_data(ski), ski_hashlen))
+ record_validation_status(status, SKI_PUBLIC_KEY_MISMATCH);
+ }
+
+ /* Non-critical */
+ if ((aki = X509_get_ext_d2i(self->x509, NID_authority_key_identifier, &crit, NULL)) != NULL) {
+ ex_count--;
+ if (crit)
+ record_validation_status(status, GRATUITOUSLY_CRITICAL_EXTENSION);
+ if (aki->keyid == NULL || aki->serial != NULL || aki->issuer != NULL)
+ 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_base_id(subject_pkey) == EVP_PKEY_RSA &&
+ EVP_PKEY_bits(subject_pkey) == 2048 &&
+ BN_get_word(subject_pkey->pkey.rsa->e) == 65537);
+ break;
+
+ case NID_X9_62_id_ecPublicKey:
+ ok = (EVP_PKEY_base_id(subject_pkey) == EVP_PKEY_EC &&
+ ekunid == NID_id_kp_bgpsec_router &&
+ EC_GROUP_get_curve_name(EC_KEY_get0_group(subject_pkey->pkey.ec)) == NID_X9_62_prime256v1);
+ 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);
+ ASN1_OCTET_STRING_free(ski);
+ AUTHORITY_KEYID_free(aki);
+ ASIdentifiers_free(asid);
+ sk_IPAddressFamily_pop_free(addr, IPAddressFamily_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 +3687,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 +3723,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;
@@ -2877,7 +3870,7 @@ static char x509_object_get_ski__doc__[] =
static PyObject *
x509_object_get_ski(x509_object *self)
{
- return extension_get_ski(x509_object_extension_helper(self));
+ return extension_get_ski(x509_object_extension_get_helper(self, NID_subject_key_identifier));
}
static char x509_object_set_ski__doc__[] =
@@ -2887,7 +3880,7 @@ static char x509_object_set_ski__doc__[] =
static PyObject *
x509_object_set_ski(x509_object *self, PyObject *args)
{
- return extension_set_ski(x509_object_extension_helper(self), args);
+ return x509_object_extension_set_helper(self, extension_set_ski(args));
}
static char x509_object_get_aki__doc__[] =
@@ -2897,7 +3890,7 @@ static char x509_object_get_aki__doc__[] =
static PyObject *
x509_object_get_aki(x509_object *self)
{
- return extension_get_aki(x509_object_extension_helper(self));
+ return extension_get_aki(x509_object_extension_get_helper(self, NID_authority_key_identifier));
}
static char x509_object_set_aki__doc__[] =
@@ -2907,7 +3900,7 @@ static char x509_object_set_aki__doc__[] =
static PyObject *
x509_object_set_aki(x509_object *self, PyObject *args)
{
- return extension_set_aki(x509_object_extension_helper(self), args);
+ return x509_object_extension_set_helper(self, extension_set_aki(args));
}
static char x509_object_get_key_usage__doc__[] =
@@ -2917,7 +3910,7 @@ static char x509_object_get_key_usage__doc__[] =
static PyObject *
x509_object_get_key_usage(x509_object *self)
{
- return extension_get_key_usage(x509_object_extension_helper(self));
+ return extension_get_key_usage(x509_object_extension_get_helper(self, NID_key_usage));
}
static char x509_object_set_key_usage__doc__[] =
@@ -2929,7 +3922,7 @@ static char x509_object_set_key_usage__doc__[] =
static PyObject *
x509_object_set_key_usage(x509_object *self, PyObject *args)
{
- return extension_set_key_usage(x509_object_extension_helper(self), args);
+ return x509_object_extension_set_helper(self, extension_set_key_usage(args));
}
static char x509_object_get_eku__doc__[] =
@@ -2939,7 +3932,7 @@ static char x509_object_get_eku__doc__[] =
static PyObject *
x509_object_get_eku(x509_object *self)
{
- return extension_get_eku(x509_object_extension_helper(self));
+ return extension_get_eku(x509_object_extension_get_helper(self, NID_ext_key_usage));
}
static char x509_object_set_eku__doc__[] =
@@ -2951,7 +3944,7 @@ static char x509_object_set_eku__doc__[] =
static PyObject *
x509_object_set_eku(x509_object *self, PyObject *args)
{
- return extension_set_eku(x509_object_extension_helper(self), args);
+ return x509_object_extension_set_helper(self, extension_set_eku(args));
}
static char x509_object_get_rfc3779__doc__[] =
@@ -3021,12 +4014,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 +4033,7 @@ x509_object_get_rfc3779(x509_object *self)
break;
default:
- lose_type_error("Unexpected ASIdentifierChoice type");
+ lose_value_error("Unexpected ASIdentifierChoice type");
}
}
@@ -3055,14 +4048,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 +4068,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 +4088,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;
@@ -3329,7 +4322,7 @@ static char x509_object_get_basic_constraints__doc__[] =
static PyObject *
x509_object_get_basic_constraints(x509_object *self)
{
- return extension_get_basic_constraints(x509_object_extension_helper(self));
+ return extension_get_basic_constraints(x509_object_extension_get_helper(self, NID_basic_constraints));
}
static char x509_object_set_basic_constraints__doc__[] =
@@ -3341,7 +4334,7 @@ static char x509_object_set_basic_constraints__doc__[] =
static PyObject *
x509_object_set_basic_constraints(x509_object *self, PyObject *args)
{
- return extension_set_basic_constraints(x509_object_extension_helper(self), args);
+ return x509_object_extension_set_helper(self, extension_set_basic_constraints(args));
}
static char x509_object_get_sia__doc__[] =
@@ -3353,7 +4346,7 @@ static char x509_object_get_sia__doc__[] =
static PyObject *
x509_object_get_sia(x509_object *self)
{
- return extension_get_sia(x509_object_extension_helper(self));
+ return extension_get_sia(x509_object_extension_get_helper(self, NID_sinfo_access));
}
static char x509_object_set_sia__doc__[] =
@@ -3365,7 +4358,7 @@ static char x509_object_set_sia__doc__[] =
static PyObject *
x509_object_set_sia(x509_object *self, PyObject *args, PyObject *kwds)
{
- return extension_set_sia(x509_object_extension_helper(self), args, kwds);
+ return x509_object_extension_set_helper(self, extension_set_sia(args, kwds));
}
static char x509_object_get_aia__doc__[] =
@@ -3783,6 +4776,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 +4872,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 +4879,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 (!PyObject_HasAttrString((PyObject *) self, method_name))
return ok;
- if ((result = PyObject_CallMethod(self, method_name, "i", ok)) == NULL)
+ if ((result = PyObject_CallMethod((PyObject *) self, method_name, "i", ok)) == NULL)
return -1;
ok = PyObject_IsTrue(result);
@@ -4200,26 +4918,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 +4942,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 +5028,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 +5039,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 +5091,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 */
@@ -4586,15 +5236,38 @@ crl_object_der_read_file(PyTypeObject *type, PyObject *args)
return read_from_file_helper(crl_object_der_read_helper, type, args);
}
-static X509_EXTENSIONS **
-crl_object_extension_helper(crl_object *self)
+static X509_EXTENSION *
+crl_object_extension_get_helper(crl_object *self, int nid)
{
- if (self && self->crl && self->crl->crl)
- return &self->crl->crl->extensions;
- PyErr_SetString(PyExc_ValueError, "Can't find X509_EXTENSIONS in CRL object");
- return NULL;
+ if (self != NULL && self->crl != NULL)
+ return X509_CRL_get_ext(self->crl, X509_CRL_get_ext_by_NID(self->crl, nid, -1));
+ else
+ return NULL;
}
+static PyObject *
+crl_object_extension_set_helper(crl_object *self, extension_wrapper ext)
+{
+ int ok = 0;
+
+ if (ext.value == NULL)
+ goto error;
+
+ if (!X509_CRL_add1_ext_i2d(self->crl, ext.nid, ext.value, ext.critical, X509V3_ADD_REPLACE))
+ lose_openssl_error("Couldn't add extension to CRL");
+
+ ok = 1;
+
+ error:
+ ext.destructor(ext.value);
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
+}
+
+
+
static char crl_object_get_version__doc__[] =
"Return the version number of this CRL.\n"
;
@@ -4672,7 +5345,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 +5590,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 +5631,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 +5664,48 @@ 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;
ENTERING(crl_object_verify);
- if (!PyArg_ParseTuple(args, "O!", &POW_Asymmetric_Type, &asym))
+ if (!PyArg_ParseTuple(args, "O!", &POW_X509_Type, &issuer))
goto error;
- return PyBool_FromLong(X509_CRL_verify(self->crl, asym->pkey));
+ 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;
+}
+
+static char crl_object_check_rpki_conformance__doc__[] =
+ "Check this CRL for conformance to the RPKI profile.\n"
+ ;
+
+static PyObject *
+crl_object_check_rpki_conformance(crl_object *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"issuer", "status", NULL};
+ x509_object *issuer = NULL;
+ PyObject *status = Py_None;
+
+ ENTERING(crl_object_check_rpki_conformance);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", kwlist, &POW_X509_Type, &issuer, &PySet_Type, &status))
+ goto error;
+
+ if (!check_crl(self->crl, issuer->x509, status))
+ goto error;
+
+ Py_RETURN_NONE;
error:
return NULL;
@@ -5059,7 +5768,7 @@ static char crl_object_get_aki__doc__[] =
static PyObject *
crl_object_get_aki(crl_object *self)
{
- return extension_get_aki(crl_object_extension_helper(self));
+ return extension_get_aki(crl_object_extension_get_helper(self, NID_authority_key_identifier));
}
static char crl_object_set_aki__doc__[] =
@@ -5069,7 +5778,7 @@ static char crl_object_set_aki__doc__[] =
static PyObject *
crl_object_set_aki(crl_object *self, PyObject *args)
{
- return extension_set_aki(crl_object_extension_helper(self), args);
+ return crl_object_extension_set_helper(self, extension_set_aki(args));
}
static char crl_object_get_crl_number__doc__[] =
@@ -5147,31 +5856,33 @@ crl_object_pprint(crl_object *self)
}
static struct PyMethodDef crl_object_methods[] = {
- Define_Method(sign, crl_object_sign, METH_VARARGS),
- Define_Method(verify, crl_object_verify, METH_VARARGS),
- Define_Method(getVersion, crl_object_get_version, METH_NOARGS),
- Define_Method(setVersion, crl_object_set_version, METH_VARARGS),
- Define_Method(getIssuer, crl_object_get_issuer, METH_VARARGS),
- Define_Method(setIssuer, crl_object_set_issuer, METH_VARARGS),
- Define_Method(getThisUpdate, crl_object_get_this_update, METH_NOARGS),
- Define_Method(setThisUpdate, crl_object_set_this_update, METH_VARARGS),
- 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(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),
- Define_Method(derWrite, crl_object_der_write, METH_NOARGS),
- Define_Method(pprint, crl_object_pprint, METH_NOARGS),
- Define_Method(getAKI, crl_object_get_aki, METH_NOARGS),
- Define_Method(setAKI, crl_object_set_aki, METH_VARARGS),
- Define_Method(getCRLNumber, crl_object_get_crl_number, METH_NOARGS),
- Define_Method(setCRLNumber, crl_object_set_crl_number, METH_VARARGS),
- Define_Method(getIssuerHash, crl_object_get_issuer_hash, METH_NOARGS),
- Define_Class_Method(pemRead, crl_object_pem_read, METH_VARARGS),
- Define_Class_Method(pemReadFile, crl_object_pem_read_file, METH_VARARGS),
- Define_Class_Method(derRead, crl_object_der_read, METH_VARARGS),
- Define_Class_Method(derReadFile, crl_object_der_read_file, METH_VARARGS),
+ Define_Method(sign, crl_object_sign, METH_VARARGS),
+ Define_Method(verify, crl_object_verify, METH_VARARGS),
+ Define_Method(checkRPKIConformance, crl_object_check_rpki_conformance, METH_KEYWORDS),
+ Define_Method(getVersion, crl_object_get_version, METH_NOARGS),
+ Define_Method(setVersion, crl_object_set_version, METH_VARARGS),
+ Define_Method(getIssuer, crl_object_get_issuer, METH_VARARGS),
+ Define_Method(setIssuer, crl_object_set_issuer, METH_VARARGS),
+ Define_Method(getThisUpdate, crl_object_get_this_update, METH_NOARGS),
+ Define_Method(setThisUpdate, crl_object_set_this_update, METH_VARARGS),
+ 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),
+ Define_Method(derWrite, crl_object_der_write, METH_NOARGS),
+ Define_Method(pprint, crl_object_pprint, METH_NOARGS),
+ Define_Method(getAKI, crl_object_get_aki, METH_NOARGS),
+ Define_Method(setAKI, crl_object_set_aki, METH_VARARGS),
+ Define_Method(getCRLNumber, crl_object_get_crl_number, METH_NOARGS),
+ Define_Method(setCRLNumber, crl_object_set_crl_number, METH_VARARGS),
+ Define_Method(getIssuerHash, crl_object_get_issuer_hash, METH_NOARGS),
+ Define_Class_Method(pemRead, crl_object_pem_read, METH_VARARGS),
+ Define_Class_Method(pemReadFile, crl_object_pem_read_file, METH_VARARGS),
+ Define_Class_Method(derRead, crl_object_der_read, METH_VARARGS),
+ Define_Class_Method(derReadFile, crl_object_der_read_file, METH_VARARGS),
{NULL}
};
@@ -5730,14 +6441,16 @@ asymmetric_object_calculate_ski(asymmetric_object *self)
X509_PUBKEY *pubkey = NULL;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned digest_length;
+ const unsigned char *key_data = NULL;
+ int key_length;
ENTERING(asymmetric_object_calculate_ski);
- if (!X509_PUBKEY_set(&pubkey, self->pkey))
+ if (!X509_PUBKEY_set(&pubkey, self->pkey) ||
+ !X509_PUBKEY_get0_param(NULL, &key_data, &key_length, NULL, pubkey))
lose_openssl_error("Couldn't extract public key");
- if (!EVP_Digest(pubkey->public_key->data, pubkey->public_key->length,
- digest, &digest_length, EVP_sha1(), NULL))
+ if (!EVP_Digest(key_data, key_length, digest, &digest_length, EVP_sha1(), NULL))
lose_openssl_error("Couldn't calculate SHA-1 digest of public key");
result = PyString_FromStringAndSize((char *) digest, digest_length);
@@ -6240,8 +6953,9 @@ static PyObject *
digest_object_digest(digest_object *self)
{
unsigned char digest_text[EVP_MAX_MD_SIZE];
- EVP_MD_CTX ctx;
unsigned digest_len = 0;
+ PyObject *result = NULL;
+ EVP_MD_CTX ctx;
ENTERING(digest_object_digest);
@@ -6250,12 +6964,11 @@ digest_object_digest(digest_object *self)
EVP_DigestFinal(&ctx, digest_text, &digest_len);
- EVP_MD_CTX_cleanup(&ctx);
-
- return Py_BuildValue("s#", digest_text, (Py_ssize_t) digest_len);
+ result = Py_BuildValue("s#", digest_text, (Py_ssize_t) digest_len);
error:
- return NULL;
+ EVP_MD_CTX_cleanup(&ctx);
+ return result;
}
static struct PyMethodDef digest_object_methods[] = {
@@ -6273,8 +6986,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 +7253,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 +7362,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 +7375,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 +7385,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,39 +7398,49 @@ 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)
{
- static char *kwlist[] = {"store", "certs", "flags", NULL};
- x509_store_object *store = NULL;
+ static char *kwlist[] = {"certs", "flags", 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 (!PyArg_ParseTupleAndKeywords(args, kwds, "|OI", kwlist, &certs_iterable, &flags))
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)
lose_openssl_error("Couldn't verify CMS message");
assert_no_unhandled_openssl_errors();
@@ -6787,6 +7501,30 @@ cms_object_extract_without_verifying(cms_object *self)
return result;
}
+static char cms_object_check_rpki_conformance__doc__[] =
+ "Check this CMS message for conformance to the RPKI profile.\n"
+ ;
+
+static PyObject *
+cms_object_check_rpki_conformance(cms_object *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"status", NULL};
+ PyObject *status = Py_None;
+
+ ENTERING(cms_object_check_rpki_conformance);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &PySet_Type, &status))
+ goto error;
+
+ if (!check_cms(self->cms, status))
+ goto error;
+
+ Py_RETURN_NONE;
+
+ error:
+ return NULL;
+}
+
static char cms_object_eContentType__doc__[] =
"Return the eContentType OID of this CMS message.\n"
;
@@ -6843,10 +7581,10 @@ cms_object_signingTime(cms_object *self)
if (xa->single)
lose("Couldn't extract signerInfos from CMS message[5]");
- if (sk_ASN1_TYPE_num(xa->value.set) != 1)
+ if (X509_ATTRIBUTE_count(xa) != 1)
lose("Couldn't extract signerInfos from CMS message[6]");
- if ((so = sk_ASN1_TYPE_value(xa->value.set, 0)) == NULL)
+ if ((so = X509_ATTRIBUTE_get0_type(xa, 0)) == NULL)
lose("Couldn't extract signerInfos from CMS message[7]");
switch (so->type) {
@@ -6895,6 +7633,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 +7663,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)
{
@@ -6948,6 +7694,7 @@ static struct PyMethodDef cms_object_methods[] = {
Define_Method(sign, cms_object_sign, METH_VARARGS),
Define_Method(verify, cms_object_verify, METH_KEYWORDS),
Define_Method(extractWithoutVerifying, cms_object_extract_without_verifying, METH_NOARGS),
+ Define_Method(checkRPKIConformance, cms_object_check_rpki_conformance, METH_KEYWORDS),
Define_Method(eContentType, cms_object_eContentType, METH_NOARGS),
Define_Method(signingTime, cms_object_signingTime, METH_NOARGS),
Define_Method(pprint, cms_object_pprint, METH_NOARGS),
@@ -7076,14 +7823,13 @@ static char manifest_object_extract_without_verifying__doc__[] =
static PyObject *
manifest_object_extract_without_verifying(manifest_object *self)
{
- PyObject *result = NULL;
BIO *bio = NULL;
int ok = 0;
ENTERING(manifest_object_extract_without_verifying);
- if ((bio = cms_object_extract_without_verifying_helper(&self->cms)) != NULL)
- result = BIO_to_PyString_helper(bio);
+ if ((bio = cms_object_extract_without_verifying_helper(&self->cms)) == NULL)
+ goto error;
if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(Manifest), bio, &self->manifest))
lose_openssl_error("Couldn't decode manifest");
@@ -7099,6 +7845,31 @@ manifest_object_extract_without_verifying(manifest_object *self)
return NULL;
}
+static char manifest_object_check_rpki_conformance__doc__[] =
+ "Check this manifest for conformance to the RPKI profile.\n"
+ ;
+
+static PyObject *
+manifest_object_check_rpki_conformance(manifest_object *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"status", NULL};
+ PyObject *status = Py_None;
+
+ ENTERING(manifest_object_check_rpki_conformance);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &PySet_Type, &status))
+ goto error;
+
+ if (!check_cms(self->cms.cms, status) || !check_manifest(self->cms.cms, self->manifest, status))
+ goto error;
+
+ Py_RETURN_NONE;
+
+ error:
+ return NULL;
+}
+
+
static PyObject *
manifest_object_der_read_helper(PyTypeObject *type, BIO *bio)
{
@@ -7627,6 +8398,7 @@ static struct PyMethodDef manifest_object_methods[] = {
Define_Method(addFiles, manifest_object_add_files, METH_VARARGS),
Define_Method(sign, manifest_object_sign, METH_VARARGS),
Define_Method(verify, manifest_object_verify, METH_KEYWORDS),
+ Define_Method(checkRPKIConformance, manifest_object_check_rpki_conformance, METH_KEYWORDS),
Define_Method(extractWithoutVerifying, manifest_object_extract_without_verifying, METH_NOARGS),
Define_Class_Method(pemRead, manifest_object_pem_read, METH_VARARGS),
Define_Class_Method(pemReadFile, manifest_object_pem_read_file, METH_VARARGS),
@@ -7752,14 +8524,13 @@ static char roa_object_extract_without_verifying__doc__[] =
static PyObject *
roa_object_extract_without_verifying(roa_object *self)
{
- PyObject *result = NULL;
BIO *bio = NULL;
int ok = 0;
ENTERING(roa_object_extract_without_verifying);
- if ((bio = cms_object_extract_without_verifying_helper(&self->cms)) != NULL)
- result = BIO_to_PyString_helper(bio);
+ if ((bio = cms_object_extract_without_verifying_helper(&self->cms)) == NULL)
+ goto error;
if (!ASN1_item_d2i_bio(ASN1_ITEM_rptr(ROA), bio, &self->roa))
lose_openssl_error("Couldn't decode ROA");
@@ -7775,6 +8546,30 @@ roa_object_extract_without_verifying(roa_object *self)
return NULL;
}
+static char roa_object_check_rpki_conformance__doc__[] =
+ "Check this ROA for conformance to the RPKI profile.\n"
+ ;
+
+static PyObject *
+roa_object_check_rpki_conformance(roa_object *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"status", NULL};
+ PyObject *status = Py_None;
+
+ ENTERING(roa_object_check_rpki_conformance);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &PySet_Type, &status))
+ goto error;
+
+ if (!check_cms(self->cms.cms, status) || !check_roa(self->cms.cms, self->roa, status))
+ goto error;
+
+ Py_RETURN_NONE;
+
+ error:
+ return NULL;
+}
+
static PyObject *
roa_object_pem_read_helper(PyTypeObject *type, BIO *bio)
{
@@ -8005,14 +8800,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 +8953,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");
@@ -8290,6 +9085,7 @@ static struct PyMethodDef roa_object_methods[] = {
Define_Method(sign, roa_object_sign, METH_VARARGS),
Define_Method(verify, roa_object_verify, METH_KEYWORDS),
Define_Method(extractWithoutVerifying, roa_object_extract_without_verifying, METH_NOARGS),
+ Define_Method(checkRPKIConformance, roa_object_check_rpki_conformance, METH_KEYWORDS),
Define_Class_Method(pemRead, roa_object_pem_read, METH_VARARGS),
Define_Class_Method(pemReadFile, roa_object_pem_read_file, METH_VARARGS),
Define_Class_Method(derRead, roa_object_der_read, METH_VARARGS),
@@ -8528,10 +9324,34 @@ pkcs10_object_der_write(pkcs10_object *self)
return result;
}
-static X509_EXTENSIONS **
-pkcs10_object_extension_helper(pkcs10_object *self)
+static X509_EXTENSION *
+pkcs10_object_extension_get_helper(pkcs10_object *self, int nid)
{
- return &self->exts;
+ if (self != NULL && self->exts != NULL)
+ return X509v3_get_ext(self->exts, X509v3_get_ext_by_NID(self->exts, nid, -1));
+ else
+ return NULL;
+}
+
+static PyObject *
+pkcs10_object_extension_set_helper(pkcs10_object *self, extension_wrapper ext)
+{
+ int ok = 0;
+
+ if (ext.value == NULL)
+ goto error;
+
+ if (!X509V3_add1_i2d(&self->exts, ext.nid, ext.value, ext.critical, X509V3_ADD_REPLACE))
+ lose_openssl_error("Couldn't add extension to PKCS #10 object");
+
+ ok = 1;
+
+ error:
+ ext.destructor(ext.value);
+ if (ok)
+ Py_RETURN_NONE;
+ else
+ return NULL;
}
static char pkcs10_object_get_public_key__doc__[] =
@@ -8597,8 +9417,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 +9570,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;
@@ -8776,7 +9594,7 @@ static char pkcs10_object_get_key_usage__doc__[] =
static PyObject *
pkcs10_object_get_key_usage(pkcs10_object *self)
{
- return extension_get_key_usage(pkcs10_object_extension_helper(self));
+ return extension_get_key_usage(pkcs10_object_extension_get_helper(self, NID_key_usage));
}
static char pkcs10_object_set_key_usage__doc__[] =
@@ -8788,7 +9606,7 @@ static char pkcs10_object_set_key_usage__doc__[] =
static PyObject *
pkcs10_object_set_key_usage(pkcs10_object *self, PyObject *args)
{
- return extension_set_key_usage(pkcs10_object_extension_helper(self), args);
+ return pkcs10_object_extension_set_helper(self, extension_set_key_usage(args));
}
static char pkcs10_object_get_eku__doc__[] =
@@ -8798,7 +9616,7 @@ static char pkcs10_object_get_eku__doc__[] =
static PyObject *
pkcs10_object_get_eku(pkcs10_object *self)
{
- return extension_get_eku(pkcs10_object_extension_helper(self));
+ return extension_get_eku(pkcs10_object_extension_get_helper(self, NID_ext_key_usage));
}
static char pkcs10_object_set_eku__doc__[] =
@@ -8810,7 +9628,7 @@ static char pkcs10_object_set_eku__doc__[] =
static PyObject *
pkcs10_object_set_eku(pkcs10_object *self, PyObject *args)
{
- return extension_set_eku(pkcs10_object_extension_helper(self), args);
+ return pkcs10_object_extension_set_helper(self, extension_set_eku(args));
}
static char pkcs10_object_get_basic_constraints__doc__[] =
@@ -8822,7 +9640,7 @@ static char pkcs10_object_get_basic_constraints__doc__[] =
static PyObject *
pkcs10_object_get_basic_constraints(pkcs10_object *self)
{
- return extension_get_basic_constraints(pkcs10_object_extension_helper(self));
+ return extension_get_basic_constraints(pkcs10_object_extension_get_helper(self, NID_basic_constraints));
}
static char pkcs10_object_set_basic_constraints__doc__[] =
@@ -8834,7 +9652,7 @@ static char pkcs10_object_set_basic_constraints__doc__[] =
static PyObject *
pkcs10_object_set_basic_constraints(pkcs10_object *self, PyObject *args)
{
- return extension_set_basic_constraints(pkcs10_object_extension_helper(self), args);
+ return pkcs10_object_extension_set_helper(self, extension_set_basic_constraints(args));
}
static char pkcs10_object_get_sia__doc__[] =
@@ -8846,7 +9664,7 @@ static char pkcs10_object_get_sia__doc__[] =
static PyObject *
pkcs10_object_get_sia(pkcs10_object *self)
{
- return extension_get_sia(pkcs10_object_extension_helper(self));
+ return extension_get_sia(pkcs10_object_extension_get_helper(self, NID_sinfo_access));
}
static char pkcs10_object_set_sia__doc__[] =
@@ -8858,7 +9676,7 @@ static char pkcs10_object_set_sia__doc__[] =
static PyObject *
pkcs10_object_set_sia(pkcs10_object *self, PyObject *args, PyObject *kwds)
{
- return extension_set_sia(pkcs10_object_extension_helper(self), args, kwds);
+ return pkcs10_object_extension_set_helper(self, extension_set_sia(args, kwds));
}
static char pkcs10_object_get_signature_algorithm__doc__[] =
@@ -8897,7 +9715,7 @@ pkcs10_object_get_extension_oids(pkcs10_object *self)
for (i = 0; i < sk_X509_EXTENSION_num(self->exts); i++) {
X509_EXTENSION *ext = sk_X509_EXTENSION_value(self->exts, i);
- if ((oid = ASN1_OBJECT_to_PyString(ext->object)) == NULL ||
+ if ((oid = ASN1_OBJECT_to_PyString(X509_EXTENSION_get_object(ext))) == NULL ||
PySet_Add(result, oid) < 0)
goto error;
Py_XDECREF(oid);
@@ -9082,6 +9900,128 @@ 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 );
+#ifdef X509_V_ERR_SUITE_B_INVALID_VERSION
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_VERSION );
+#endif
+#ifdef X509_V_ERR_SUITE_B_INVALID_ALGORITHM
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_ALGORITHM );
+#endif
+#ifdef X509_V_ERR_SUITE_B_INVALID_CURVE
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_CURVE );
+#endif
+#ifdef X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM
+ Verification_Error( X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM );
+#endif
+#ifdef X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED
+ Verification_Error( X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED );
+#endif
+#ifdef X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256
+ Verification_Error( X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 );
+#endif
+#ifdef X509_V_ERR_HOSTNAME_MISMATCH
+ Verification_Error( X509_V_ERR_HOSTNAME_MISMATCH );
+#endif
+#ifdef X509_V_ERR_EMAIL_MISMATCH
+ Verification_Error( X509_V_ERR_EMAIL_MISMATCH );
+#endif
+#ifdef X509_V_ERR_IP_ADDRESS_MISMATCH
+ Verification_Error( X509_V_ERR_IP_ADDRESS_MISMATCH );
+#endif
+ 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 +10157,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 +10215,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 +10236,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 +10249,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 +10350,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");
}