diff options
-rw-r--r-- | ca/tests/yamlconf.py | 2 | ||||
-rw-r--r-- | ca/tests/yamltest.py | 3 | ||||
-rw-r--r-- | ext/POW.c | 64 | ||||
-rw-r--r-- | rp/rcynic/rcynic.c | 45 | ||||
-rwxr-xr-x | rp/utils/uri | 13 | ||||
-rw-r--r-- | rpki/oids.py | 1 | ||||
-rw-r--r-- | rpki/pubdb/models.py | 2 | ||||
-rw-r--r-- | rpki/publication.py | 5 | ||||
-rw-r--r-- | rpki/rootd.py | 2 | ||||
-rw-r--r-- | rpki/rpkid.py | 6 | ||||
-rw-r--r-- | rpki/x509.py | 18 |
11 files changed, 110 insertions, 51 deletions
diff --git a/ca/tests/yamlconf.py b/ca/tests/yamlconf.py index bb82ef74..4c003835 100644 --- a/ca/tests/yamlconf.py +++ b/ca/tests/yamlconf.py @@ -522,7 +522,7 @@ class allocation(object): root_uri = "rsync://%s/rpki/" % self.rsync_server - root_sia = (root_uri, root_uri + "root.mft", None) + root_sia = (root_uri, root_uri + "root.mft", None, rpki.publication.rrdp_sia_uri_kludge) root_cert = rpki.x509.X509.self_certify( keypair = root_key, diff --git a/ca/tests/yamltest.py b/ca/tests/yamltest.py index e727789d..f49968b3 100644 --- a/ca/tests/yamltest.py +++ b/ca/tests/yamltest.py @@ -681,7 +681,8 @@ def create_root_certificate(db_root): root_uri = "rsync://localhost:%d/rpki/%s-root/root" % (db_root.pubd.rsync_port, db_root.name) - root_sia = (root_uri + "/", root_uri + "/root.mft", None) + from rpki.publication import rrdp_sia_uri_kludge + root_sia = (root_uri + "/", root_uri + "/root.mft", None, rrdp_sia_uri_kludge) root_cert = rpki.x509.X509.self_certify( keypair = root_key, @@ -170,6 +170,10 @@ static int NID_rpkiManifest; static int NID_signedObject; #endif +#ifndef NID_rpkiNotify +static int NID_rpkiNotify; +#endif + static const struct { int *nid; const char *oid; @@ -182,7 +186,11 @@ static const struct { #endif #ifndef NID_signedObject - {&NID_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"} + {&NID_signedObject, "1.3.6.1.5.5.7.48.11", "id-ad-signedObject", "Signed Object"}, +#endif + +#ifndef NID_rpkiNotify + {&NID_rpkiNotify, "1.3.6.1.5.5.7.48.13", "id-ad-rpkiNotify", "RPKI RRDP Notification"}, #endif }; @@ -1295,8 +1303,8 @@ extension_set_basic_constraints(X509_EXTENSIONS **exts, PyObject *args) #define EXTENSION_GET_SIA__DOC__ \ "If there is no SIA extension, this method returns None.\n" \ "\n" \ - "Otherwise, it returns a tuple containing three values:\n" \ - "caRepository URIs, rpkiManifest URIs, and signedObject URIs.\n" \ + "Otherwise, it returns a tuple containing four values:\n" \ + "caRepository URIs, rpkiManifest URIs, signedObject, and rpkiNotify URIs.\n" \ "Each of these values is a tuple of strings, representing an ordered\n" \ "sequence of URIs. Any or all of these sequences may be empty.\n" \ "\n" \ @@ -1310,9 +1318,11 @@ extension_get_sia(X509_EXTENSIONS **exts) PyObject *result_caRepository = NULL; PyObject *result_rpkiManifest = NULL; PyObject *result_signedObject = NULL; + PyObject *result_rpkiNotify = NULL; int n_caRepository = 0; int n_rpkiManifest = 0; int n_signedObject = 0; + int n_rpkiNotify = 0; const char *uri; PyObject *obj; int i, nid; @@ -1334,26 +1344,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_rpkiManifest) n_rpkiManifest++; - continue; - } - if (nid == NID_signedObject) { + else if (nid == NID_signedObject) n_signedObject++; - continue; - } + else if (nid == NID_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); @@ -1379,24 +1386,32 @@ extension_get_sia(X509_EXTENSIONS **exts) PyTuple_SET_ITEM(result_signedObject, n_signedObject++, obj); continue; } + if (nid == NID_rpkiNotify) { + if ((obj = PyString_FromString(uri)) == NULL) + goto error; + PyTuple_SET_ITEM(result_rpkiNotify, n_rpkiNotify++, obj); + continue; + } } - result = Py_BuildValue("(OOO)", + result = Py_BuildValue("(OOOO)", result_caRepository, result_rpkiManifest, - result_signedObject); + result_signedObject, + result_rpkiNotify); error: AUTHORITY_INFO_ACCESS_free(ext); Py_XDECREF(result_caRepository); Py_XDECREF(result_rpkiManifest); Py_XDECREF(result_signedObject); + Py_XDECREF(result_rpkiNotify); return result; } #define EXTENSION_SET_SIA__DOC__ \ - "This method Takes three arguments:\n" \ - "\"caRepository\", \"rpkiManifest\", and \"signedObject\".\n" \ + "This method takes four arguments: \"caRepository\"\n," \ + "\"rpkiManifest\", \"signedObject\", and \"rpkiNotify\".\n" \ "Each of these should be an iterable which returns URIs.\n" \ "\n" \ "None is acceptable as an alternate way of specifying an empty\n" \ @@ -1405,11 +1420,12 @@ extension_get_sia(X509_EXTENSIONS **exts) static PyObject * extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"caRepository", "rpkiManifest", "signedObject", NULL}; + static char *kwlist[] = {"caRepository", "rpkiManifest", "signedObject", "rpkiNotify", NULL}; AUTHORITY_INFO_ACCESS *ext = NULL; PyObject *caRepository = Py_None; PyObject *rpkiManifest = Py_None; PyObject *signedObject = Py_None; + PyObject *rpkiNotify = Py_None; PyObject *iterator = NULL; ASN1_OBJECT *oid = NULL; PyObject **pobj = NULL; @@ -1424,8 +1440,8 @@ extension_set_sia(X509_EXTENSIONS **exts, PyObject *args, PyObject *kwds) if (!exts) goto error; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, - &caRepository, &rpkiManifest, &signedObject)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, + &caRepository, &rpkiManifest, &signedObject, &rpkiNotify)) goto error; if ((ext = AUTHORITY_INFO_ACCESS_new()) == NULL) @@ -1437,11 +1453,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 3: pobj = &rpkiNotify; nid = NID_rpkiNotify; break; } if (*pobj == Py_None) @@ -1461,7 +1478,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; diff --git a/rp/rcynic/rcynic.c b/rp/rcynic/rcynic.c index 8db15e55..c5b82266 100644 --- a/rp/rcynic/rcynic.c +++ b/rp/rcynic/rcynic.c @@ -83,6 +83,9 @@ #define SCHEME_RSYNC ("rsync://") #define SIZEOF_RSYNC (sizeof(SCHEME_RSYNC) - 1) +#define SCHEME_HTTP ("http://") +#define SIZEOF_HTTP (sizeof(SCHEME_HTTP) - 1) + /** * Maximum length of a hostname. */ @@ -410,7 +413,7 @@ DECLARE_STACK_OF(validation_status_t) typedef struct certinfo { int ca, ta; object_generation_t generation; - uri_t uri, sia, aia, crldp, manifest, signedobject; + uri_t uri, sia, aia, crldp, manifest, signedobject, rrdpnotify; } certinfo_t; typedef struct rcynic_ctx rcynic_ctx_t; @@ -592,6 +595,10 @@ static int NID_ad_rpkiManifest; 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 @@ -630,6 +637,10 @@ static const struct { {&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 @@ -1043,6 +1054,14 @@ static int is_rsync(const char *uri) } /** + * Is string an http URI? + */ +static int is_http(const char *uri) +{ + return uri && !strncmp(uri, SCHEME_HTTP, SIZEOF_HTTP); +} + +/** * Convert an rsync URI to a filename, checking for evil character * sequences. NB: This routine can't call mib_increment(), because * mib_increment() calls it, so errors detected here only go into @@ -3155,7 +3174,8 @@ static int extract_access_uri(rcynic_ctx_t *rc, const AUTHORITY_INFO_ACCESS *xia, const int nid, uri_t *result, - int *count) + int *count, + int (*relevant)(const char *)) { int i; @@ -3168,9 +3188,9 @@ static int extract_access_uri(rcynic_ctx_t *rc, if (OBJ_obj2nid(a->method) != nid) continue; ++*count; - if (!is_rsync((char *) a->location->d.uniformResourceIdentifier->data)) - log_validation_status(rc, uri, non_rsync_uri_in_extension, generation); - else if (sizeof(result->s) <= a->location->d.uniformResourceIdentifier->length) + if (!relevant((char *) a->location->d.uniformResourceIdentifier->data)) + continue; + if (sizeof(result->s) <= a->location->d.uniformResourceIdentifier->length) log_validation_status(rc, uri, uri_too_long, generation); else if (result->s[0]) log_validation_status(rc, uri, multiple_rsync_uris_in_extension, generation); @@ -3685,7 +3705,7 @@ static int check_x509(rcynic_ctx_t *rc, int n_caIssuers = 0; ex_count--; if (!extract_access_uri(rc, uri, generation, aia, NID_ad_ca_issuers, - &certinfo->aia, &n_caIssuers) || + &certinfo->aia, &n_caIssuers, is_rsync) || !certinfo->aia.s[0] || sk_ACCESS_DESCRIPTION_num(aia) != n_caIssuers) { log_validation_status(rc, uri, malformed_aia_extension, generation); @@ -3715,18 +3735,21 @@ static int check_x509(rcynic_ctx_t *rc, if ((sia = X509_get_ext_d2i(x, NID_sinfo_access, NULL, NULL)) != NULL) { int got_caDirectory, got_rpkiManifest, got_signedObject; - int n_caDirectory = 0, n_rpkiManifest = 0, n_signedObject = 0; + int n_caDirectory = 0, n_rpkiManifest = 0, n_signedObject = 0, n_rpkiNotify = 0; ex_count--; ok = (extract_access_uri(rc, uri, generation, sia, NID_caRepository, - &certinfo->sia, &n_caDirectory) && + &certinfo->sia, &n_caDirectory, is_rsync) && extract_access_uri(rc, uri, generation, sia, NID_ad_rpkiManifest, - &certinfo->manifest, &n_rpkiManifest) && + &certinfo->manifest, &n_rpkiManifest, is_rsync) && extract_access_uri(rc, uri, generation, sia, NID_ad_signedObject, - &certinfo->signedobject, &n_signedObject)); + &certinfo->signedobject, &n_signedObject, is_rsync) && + extract_access_uri(rc, uri, generation, sia, NID_ad_rpkiNotify, + &certinfo->rrdpnotify, &n_rpkiNotify, is_http)); got_caDirectory = certinfo->sia.s[0] != '\0'; got_rpkiManifest = certinfo->manifest.s[0] != '\0'; got_signedObject = certinfo->signedobject.s[0] != '\0'; - ok &= sk_ACCESS_DESCRIPTION_num(sia) == n_caDirectory + n_rpkiManifest + n_signedObject; + ok &= (sk_ACCESS_DESCRIPTION_num(sia) == + n_caDirectory + n_rpkiManifest + n_signedObject + n_rpkiNotify); if (certinfo->ca) ok &= got_caDirectory && got_rpkiManifest && !got_signedObject; else if (rc->allow_ee_without_signedObject) diff --git a/rp/utils/uri b/rp/utils/uri index e72d5e0d..df6e710b 100755 --- a/rp/utils/uri +++ b/rp/utils/uri @@ -30,13 +30,19 @@ import rpki.POW class Certificate(object): @staticmethod - def first_rsync(uris): + def first_whatever(uris, prefix): if uris is not None: for uri in uris: - if uri.startswith("rsync://"): + if uri.startswith(prefix): return uri return None + def first_rsync(self, uris): + return self.first_whatever(uris, "rsync://") + + def first_http(self, uris): + return self.first_whatever(uris, "http://") + def __init__(self, fn): try: x = rpki.POW.X509.derReadFile(fn) @@ -47,13 +53,14 @@ class Certificate(object): x = cms.certs()[0] except: raise ValueError - sia = x.getSIA() or (None, None, None) + sia = x.getSIA() or (None, None, None, None) self.fn = fn self.uris = ( ("AIA:caIssuers", self.first_rsync(x.getAIA())), ("SIA:caRepository", self.first_rsync(sia[0])), ("SIA:rpkiManifest", self.first_rsync(sia[1])), ("SIA:signedObject", self.first_rsync(sia[2])), + ("SIA:rpkiNotify", self.first_http(sia[3])), ("CRLDP", self.first_rsync(x.getCRLDP()))) def __str__(self): diff --git a/rpki/oids.py b/rpki/oids.py index 9fa30a04..afb95020 100644 --- a/rpki/oids.py +++ b/rpki/oids.py @@ -57,6 +57,7 @@ id_ad_caRepository = "1.3.6.1.5.5.7.48.5" id_ad_signedObjectRepository = "1.3.6.1.5.5.7.48.9" id_ad_rpkiManifest = "1.3.6.1.5.5.7.48.10" id_ad_signedObject = "1.3.6.1.5.5.7.48.11" +id_ad_rpkiNotify = "1.3.6.1.5.5.7.48.13" commonName = "2.5.4.3" serialNumber = "2.5.4.5" countryName = "2.5.4.6" diff --git a/rpki/pubdb/models.py b/rpki/pubdb/models.py index 5241ea45..ce42c688 100644 --- a/rpki/pubdb/models.py +++ b/rpki/pubdb/models.py @@ -139,7 +139,7 @@ class Session(models.Model): @property def notification_fn(self): - return "updates.xml" + return "notify.xml" @staticmethod diff --git a/rpki/publication.py b/rpki/publication.py index ec2dabd4..524b5a53 100644 --- a/rpki/publication.py +++ b/rpki/publication.py @@ -50,6 +50,11 @@ tag_withdraw = rpki.relaxng.publication.xmlns + "withdraw" tag_report_error = rpki.relaxng.publication.xmlns + "report_error" +logger.warning("Horrible kludge: static RRDP URI for testing, this needs to be fixed") +from socket import getfqdn +rrdp_sia_uri_kludge = "http://%s/rrdp/notify.xml" % getfqdn() + + def raise_if_error(pdu): """ Raise an appropriate error if this is a <report_error/> PDU. diff --git a/rpki/rootd.py b/rpki/rootd.py index 987d8356..8f08e0dd 100644 --- a/rpki/rootd.py +++ b/rpki/rootd.py @@ -189,7 +189,7 @@ class main(object): keypair = self.rpki_root_key, subject_key = manifest_keypair.get_public(), serial = self.serial_number, - sia = (None, None, self.rpki_root_manifest_uri), + sia = (None, None, self.rpki_root_manifest_uri, rpki.publication.rrdp_sia_uri_kludge), aia = self.rpki_root_cert_uri, crldp = self.rpki_root_crl_uri, resources = manifest_resources, diff --git a/rpki/rpkid.py b/rpki/rpkid.py index 64157663..cc7fbc5b 100644 --- a/rpki/rpkid.py +++ b/rpki/rpkid.py @@ -1184,7 +1184,7 @@ class ca_detail_obj(rpki.sql.sql_persistent): ca = self.ca, resources = resources, subject_key = self.manifest_public_key, - sia = (None, None, self.manifest_uri)) + sia = (None, None, self.manifest_uri, rpki.publication.rrdp_sia_uri_kludge)) def issue(self, ca, child, subject_key, sia, resources, publisher, child_cert = None): """ @@ -1948,7 +1948,7 @@ class roa_obj(rpki.sql.sql_persistent): ca = ca, resources = resources, subject_key = keypair.get_public(), - sia = (None, None, self.uri_from_key(keypair))) + sia = (None, None, self.uri_from_key(keypair), rpki.publication.rrdp_sia_uri_kludge)) self.roa = rpki.x509.ROA.build(self.asn, self.ipv4, self.ipv6, keypair, (self.cert,)) self.published = rpki.sundial.now() self.sql_store() @@ -2158,7 +2158,7 @@ class ghostbuster_obj(rpki.sql.sql_persistent): ca = ca, resources = resources, subject_key = keypair.get_public(), - sia = (None, None, self.uri_from_key(keypair))) + sia = (None, None, self.uri_from_key(keypair), rpki.publication.rrdp_sia_uri_kludge)) self.ghostbuster = rpki.x509.Ghostbuster.build(self.vcard, keypair, (self.cert,)) self.published = rpki.sundial.now() self.sql_store() diff --git a/rpki/x509.py b/rpki/x509.py index 89b598d4..9bc34e19 100644 --- a/rpki/x509.py +++ b/rpki/x509.py @@ -784,11 +784,12 @@ class X509(DER_object): assert sia is not None or not is_ca if sia is not None: - caRepository, rpkiManifest, signedObject = sia + caRepository, rpkiManifest, signedObject, rpkiNotify = sia cert.setSIA( (caRepository,) if isinstance(caRepository, str) else caRepository, (rpkiManifest,) if isinstance(rpkiManifest, str) else rpkiManifest, - (signedObject,) if isinstance(signedObject, str) else signedObject) + (signedObject,) if isinstance(signedObject, str) else signedObject, + (rpkiNotify,) if isinstance(rpkiNotify, str) else rpkiNotify) if resources is not None: cert.setRFC3779( @@ -1045,7 +1046,7 @@ class PKCS10(DER_object): if sias is None: raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA missing") - caRepository, rpkiManifest, signedObject = sias + caRepository, rpkiManifest, signedObject, rpkiNotify = sias if signedObject: raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA must not have id-ad-signedObject") @@ -1095,7 +1096,7 @@ class PKCS10(DER_object): bc = self.get_POW().getBasicConstraints() sia = self.get_POW().getSIA() - caRepository, rpkiManifest, signedObject = sia or (None, None, None) + caRepository, rpkiManifest, signedObject, rpkiNotify = sia or (None, None, None, None) if alg not in (rpki.oids.sha256WithRSAEncryption, rpki.oids.ecdsa_with_SHA256): raise rpki.exceptions.BadPKCS10("PKCS #10 has bad signature algorithm for EE: %s" % alg) @@ -1149,7 +1150,7 @@ class PKCS10(DER_object): @classmethod def create(cls, keypair, exts = None, is_ca = False, caRepository = None, rpkiManifest = None, signedObject = None, - cn = None, sn = None, eku = None): + cn = None, sn = None, eku = None, rpkiNotify = None): """ Create a new request for a given keypair. """ @@ -1168,6 +1169,9 @@ class PKCS10(DER_object): if isinstance(signedObject, str): signedObject = (signedObject,) + if isinstance(rpkiNotify, str): + rpkiNotify = (rpkiNotify,) + req = rpki.POW.PKCS10() req.setVersion(0) req.setSubject(X501DN.from_cn(cn, sn).get_POW()) @@ -1177,8 +1181,8 @@ class PKCS10(DER_object): req.setBasicConstraints(True, None) req.setKeyUsage(cls.expected_ca_keyUsage) - if caRepository or rpkiManifest or signedObject: - req.setSIA(caRepository, rpkiManifest, signedObject) + if caRepository or rpkiManifest or signedObject or rpkiNotify: + req.setSIA(caRepository, rpkiManifest, signedObject, rpkiNotify) if eku: req.setEKU(eku) |