diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/biz-certs/Bob-CA.srl | 2 | ||||
-rw-r--r-- | scripts/pkcs10.py | 8 | ||||
-rw-r--r-- | scripts/rpki/left_right.py | 23 | ||||
-rw-r--r-- | scripts/rpki/resource_set.py | 79 | ||||
-rw-r--r-- | scripts/rpki/sql.py | 61 | ||||
-rw-r--r-- | scripts/rpki/up_down.py | 36 | ||||
-rw-r--r-- | scripts/rpki/x509.py | 22 | ||||
-rw-r--r-- | scripts/test-pow.py | 14 | ||||
-rwxr-xr-x | scripts/testroot.py | 8 |
9 files changed, 141 insertions, 112 deletions
diff --git a/scripts/biz-certs/Bob-CA.srl b/scripts/biz-certs/Bob-CA.srl index a7659166..4c1a4d01 100644 --- a/scripts/biz-certs/Bob-CA.srl +++ b/scripts/biz-certs/Bob-CA.srl @@ -1 +1 @@ -90801F1ED194549C +90801F1ED19454A1 diff --git a/scripts/pkcs10.py b/scripts/pkcs10.py index 77272ff0..77cb8666 100644 --- a/scripts/pkcs10.py +++ b/scripts/pkcs10.py @@ -58,10 +58,10 @@ if parse_test: exts = pkcs10.getExtensions() - as, v4, v6 = rpki.resource_set.parse_extensions(exts) - if as: print "ASN =", as - if v4: print "IPv4 =", v4 - if v6: print "IPv6 =", v6 + bag = rpki.resource_set.parse_extensions(exts) + if bag.as: print "ASN =", bag.as + if bag.v4: print "IPv4 =", bag.v4 + if bag.v6: print "IPv6 =", bag.v6 for oid, crit, val in exts: if oid in ((1, 3, 6, 1, 5, 5, 7, 1, 7), (1, 3, 6, 1, 5, 5, 7, 1, 8)): diff --git a/scripts/rpki/left_right.py b/scripts/rpki/left_right.py index 3979931b..3528152d 100644 --- a/scripts/rpki/left_right.py +++ b/scripts/rpki/left_right.py @@ -729,17 +729,14 @@ class sax_handler(rpki.sax_utils.handler): return self.pdu() def irdb_query(gctx, self_id, child_id = None): - """Perform an IRDB callback query. - - In the long run this should not be a blocking routine, it should - instead issue a query and set up a handler to receive the response. - For the moment, though, we're doing simple lock step and damn the - torpedos. - - Not yet doing anything useful with validity interval or subject - name. Most likely this function should really be wrapped up in a - class that carries both the query result and also the intermediate state - needed for the event-driven code that this function will need to become. + """Perform an IRDB callback query. In the long run this should not + be a blocking routine, it should instead issue a query and set up a + handler to receive the response. For the moment, though, we are + doing simple lock step and damn the torpedos. Not yet doing + anything useful with validity interval or subject name. Most likely + this function should really be wrapped up in a class that carries + both the query result and also the intermediate state needed for the + event-driven code that this function will need to become. """ q_msg = msg() @@ -760,4 +757,6 @@ def irdb_query(gctx, self_id, child_id = None): r_msg = rpki.left_right.sax_handler.saxify(r_elt) if len(r_msg) == 0 or not isinstance(r_msg[0], list_resources_elt) or r_msg[0].type != "reply": raise rpki.exceptions.BadIRDBReply, "Unexpected response to IRDB query: %s" % r_msg.toXML() - return r_msg[0].as, r_msg[0].ipv4, r_msg[0].ipv6 + return rpki.resource_set.resource_bag(r_msg[0].as, + r_msg[0].ipv4, + r_msg[0].ipv6) diff --git a/scripts/rpki/resource_set.py b/scripts/rpki/resource_set.py index 0c09f19d..48c6cfb1 100644 --- a/scripts/rpki/resource_set.py +++ b/scripts/rpki/resource_set.py @@ -378,27 +378,64 @@ def _long2bs(number, addrlen, prefixlen = None, strip = None): bs.pop() return tuple(bs) -def parse_extensions(exts): - """Parse RFC 3779 extensions from intermediate form returned by ASN.1 decoder.""" - as = None - v4 = None - v6 = None - for x in exts: - if x[0] == (1, 3, 6, 1, 5, 5, 7, 1, 8): # sbgp-autonomousSysNum - assert x[2][1] is None, "RDI not implemented: %s" % (str(x)) - assert as is None - as = resource_set_as(x[2][0]) - if x[0] == (1, 3, 6, 1, 5, 5, 7, 1, 7): # sbgp-ipAddrBlock - for fam in x[2]: - if fam[0] == resource_set_ipv4.afi: - assert v4 is None - v4 = resource_set_ipv4(fam[1]) - if fam[0] == resource_set_ipv6.afi: - assert v6 is None - v6 = resource_set_ipv6(fam[1]) - return as or resource_set_as(), \ - v4 or resource_set_ipv4(), \ - v6 or resource_set_ipv6() +class resource_bag(object): + """Container to simplify passing around the usual triple of AS, + IPv4, and IPv6 resource sets. + """ + + def __init__(self, as = None, v4 = None, v6 = None): + self.as = as or resource_set_as() + self.v4 = v4 or resource_set_ipv4() + self.v6 = v6 or resource_set_ipv6() + + def oversized(self, other): + """True iff self is oversized with respect to other.""" + return not self.as.issubset(other.as) or \ + not self.v4.issubset(other.v4) or \ + not self.v6.issubset(other.v6) + + def undersized(self, other): + """True iff self is undersized with respect to other.""" + return not other.as.issubset(self.as) or \ + not other.v4.issubset(self.v4) or \ + not other.v6.issubset(self.v6) + + @classmethod + def from_asn1_tuples(cls, exts): + """Build a resource_bag from intermediate form returned by ASN.1 decoder.""" + as = None + v4 = None + v6 = None + for x in exts: + if x[0] == (1, 3, 6, 1, 5, 5, 7, 1, 8): # sbgp-autonomousSysNum + assert x[2][1] is None, "RDI not implemented: %s" % (str(x)) + assert as is None + as = resource_set_as(x[2][0]) + if x[0] == (1, 3, 6, 1, 5, 5, 7, 1, 7): # sbgp-ipAddrBlock + for fam in x[2]: + if fam[0] == resource_set_ipv4.afi: + assert v4 is None + v4 = resource_set_ipv4(fam[1]) + if fam[0] == resource_set_ipv6.afi: + assert v6 is None + v6 = resource_set_ipv6(fam[1]) + return cls(as, v4, v6) + + def empty(self): + """Return True iff all resource sets in this bag are empty.""" + return not self.as and not self.v4 and not self.v6 + + def __eq__(self, other): + return self.as == other.as and self.v4 == other.v4 and self.v6 == other.v6 + + def __ne__(self, other): + return not (self == other) + + def intersection(self, other): + """Compute intersection with another resource_bag.""" + return self.__class__(self.as.intersection(other.as), + self.v4.intersection(other.v4), + self.v6.intersection(other.v6)) # Test suite for set operations. This will probably go away eventually diff --git a/scripts/rpki/sql.py b/scripts/rpki/sql.py index b3348f87..2ac0c210 100644 --- a/scripts/rpki/sql.py +++ b/scripts/rpki/sql.py @@ -223,24 +223,23 @@ class ca_obj(sql_persistant): off to the affected ca_detail for processing. """ - cert_map = dict((c.cert.get_SKI(), c) for c in rc.certs) - ca_details = ca_detail_obj.sql_fetch_where(gctx, "ca_id = %s AND latest_ca_cert IS NOT NULL" % self.ca_id) - as, v4, v6 = ca_detail_obj.sql_fetch_active(gctx, self.ca_id).latest_ca_cert.get_3779resources() - undersized = not rc.resource_set_as.issubset(as) or \ - not rc.resource_set_ipv4.issubset(v4) or \ - not rc.resource_set_ipv6.issubset(v6) - oversized = not as.issubset(rc.resource_set_as) or \ - not v4.issubset(rc.resource_set_ipv4) or \ - not v6.issubset(rc.resource_set_ipv6) sia_uri = self.construct_sia_uri(gctx, parent, rc) sia_uri_changed = self.sia_uri != sia_uri if sia_uri_changed: self.sia_uri = sia_uri self.sql_mark_dirty() - for ca_detail in ca_details: + + rc_resources = rc.to_resource_bag() + cert_map = dict((c.cert.get_SKI(), c) for c in rc.certs) + + for ca_detail in ca_detail_obj.sql_fetch_where(gctx, "ca_id = %s AND latest_ca_cert IS NOT NULL AND state != 'revoked'" % self.ca_id): ski = ca_detail.latest_ca_cert.get_SKI() - if ca_detail.state not in ("deprecated", "revoked") and (undersized or oversized or sia_uri_changed or ca_detail.latest_ca_cert != cert_map[ski].cert): - ca_detail.update(gctx, parent, self, rc, cert_map[ski].cert, undersized, oversized, sia_uri_changed, as, v4, v6) + if ca_detail.state != "deprecated": + current_resources = ca_detail_obj.sql_fetch_active(gctx, self.ca_id).latest_ca_cert.get_3779resources() + undersized = current_resources.undersized(rc_resources) + oversized = current_resources.oversized(rc_resources) + if undersized or oversized or sia_uri_changed or ca_detail.latest_ca_cert != cert_map[ski].cert: + ca_detail.update(gctx, parent, self, rc, cert_map[ski].cert, undersized, oversized, sia_uri_changed, current_resources, rc_resources) del cert_map[ski] assert not cert_map, "Certificates in list_response missing from our database, SKIs %s" % ", ".join(c.cert.hSKI() for c in cert_map.values()) @@ -341,7 +340,7 @@ class ca_detail_obj(sql_persistant): """Fetch the current active ca_detail_obj associated with a given ca_id.""" return cls.sql_fetch_where1(gctx, "ca_id = %s AND state = 'active'" % ca_id) - def update(self, gctx, parent, ca, rc, newcert, undersized, oversized, sia_uri_changed, as, v4, v6): + def update(self, gctx, parent, ca, rc, newcert, undersized, oversized, sia_uri_changed, current_resources, rc_resources): """CA has received a cert for this ca_detail that doesn't match the current one, figure out what to do about it. Cases: @@ -358,17 +357,14 @@ class ca_detail_obj(sql_persistant): """ if undersized: issue_response = rpki.up_down.issue_pdu.query(gctx, parent, ca, self) - issue_response.check() - self.latest_ca_cert = issue_response.classes[0].certs[0] - as, v4, v6 = self.latest_ca_cert.get_3779resources() + issue_response.check_syntax() + self.latest_ca_cert = issue_response.classes[0].certs[0].cert + current_resources = self.latest_ca_cert.get_3779resources() if oversized or sia_uri_changed: for child_cert in child_cert_obj.sql_fetch_where(gctx, "ca_detail_id = %s" % self.ca_detail_id): - child_as, child_v4, child_v6 = child_cert.cert.get_3779resources() - if sia_uri_changed or \ - not child_as.issubset(as) or \ - not child_v4.issubset(v4) or \ - not child_v6.issubset(v6): - child_cert.reissue(gctx, self, as, v4, v6, ca.sia_uri) + child_resources = child_cert.cert.get_3779resources() + if sia_uri_changed or child_resources.oversized(current_resources): + child_cert.reissue(gctx, self, child_resources.intersection(current_resources), ca.sia_uri) @classmethod def create(cls, gctx, ca): @@ -391,18 +387,20 @@ class ca_detail_obj(sql_persistant): def generate_manifest_cert(self, ca): """Generate a new manifest certificate for this ca_detail.""" + resources = rpki.resource_set.resource_bag(as = rpki.resource_set.resource_set_as("<inherit>"), + v4 = rpki.resource_set.resource_set_ipv4("<inherit>"), + v6 = rpki.resource_set.resource_set_ipv6("<inherit>")) + self.latest_manifest_cert = self.latest_ca_cert.issue(keypair = self.private_key_id, subject_key = self.manifest_public_key, serial = ca.next_serial_number(), sia = None, aia = self.ca_cert_uri, crldp = ca.sia_uri + self.latest_ca_cert.gSKI() + ".crl", - as = rpki.resource_set.resource_set_as("<inherit>"), - v4 = rpki.resource_set.resource_set_ipv4("<inherit>"), - v6 = rpki.resource_set.resource_set_ipv6("<inherit>"), + resources = resources, is_ca = False) - def issue(self, gctx, ca, child, subject_key, sia, as, v4, v6, child_cert = None): + def issue(self, gctx, ca, child, subject_key, sia, resources, child_cert = None): """Issue a new certificate to a child. Need to figure out how to share code between issuance of a new @@ -425,10 +423,7 @@ class ca_detail_obj(sql_persistant): aia = self.ca_cert_uri, crldp = ca.sia_uri + self.latest_ca_cert.gSKI() + ".crl", sia = sia, - as = as, - v4 = v4, - v6 = v6) - + resources = resources) if child_cert is None: child_cert = rpki.sql.child_cert_obj(child_id = child.child_id, @@ -525,7 +520,7 @@ class child_cert_obj(sql_persistant): d["cert"] = self.cert.get_DER() return d - def reissue(self, gctx, ca_detail, as, v4, v6, sia): + def reissue(self, gctx, ca_detail, resources, sia): """Reissue an existing child_cert_obj, reusing the public key.""" if sia is None: sia = self.cert.get_SIA() @@ -534,9 +529,7 @@ class child_cert_obj(sql_persistant): child = rpki.left_right.child_elt.sql_fetch(gctx, self.child_id), subject_key = self.cert.getPublicKey(), sia = sia, - as = as, - v4 = v4, - v6 = v6, + resources = resources, child_cert = self) def revoke(self): diff --git a/scripts/rpki/up_down.py b/scripts/rpki/up_down.py index 93fa2ec7..d4c0f287 100644 --- a/scripts/rpki/up_down.py +++ b/scripts/rpki/up_down.py @@ -142,6 +142,18 @@ class class_elt(base_elt): self.make_b64elt(elt, "issuer", self.issuer.get_DER()) return elt + def to_resource_bag(self): + """Build a resource_bag from from this <class/> element.""" + return rpki.resource_set.resource_bag(self.resource_set_as, + self.resource_set_ipv4, + self.resource_set_ipv6) + + def from_resource_bag(self, bag): + """Set resources of this class element from a resource_bag.""" + self.resource_set_as = bag.as + self.resource_set_ipv4 = bag.v4 + self.resource_set_ipv6 = bag.v6 + class list_pdu(base_elt): """Up-Down protocol "list" PDU.""" @@ -152,19 +164,19 @@ class list_pdu(base_elt): def serve_pdu(self, gctx, q_msg, r_msg, child): """Serve one "list" PDU.""" r_msg.payload = list_response_pdu() - irdb_as, irdb_v4, irdb_v6 = rpki.left_right.irdb_query(gctx, child.self_id, child.child_id) + irdb_resources = rpki.left_right.irdb_query(gctx, child.self_id, child.child_id) for parent in rpki.left_right.parent_elt.sql_fetch_where(gctx, "parent.self_id = %s" % child.self_id): for ca in rpki.sql.ca_obj.sql_fetch_where(gctx, "ca.parent_id = %s" % parent.parent_id): ca_detail = rpki.sql.ca_detail_obj.sql_fetch_active(gctx, ca.ca_id) if not ca_detail: continue - rc_as, rc_v4, rc_v6 = ca_detail.latest_ca_cert.get_3779resources(irdb_as, irdb_v4, irdb_v6) - if not rc_as and not rc_v4 and not rc_v6: + resources = ca_detail.latest_ca_cert.get_3779resources().intersection(irdb_resources) + if resources.empty(): continue rc = class_elt() rc.class_name = str(ca.ca_id) rc.cert_url = multi_uri(ca_detail.ca_cert_uri) - rc.resource_set_as, rc.resource_set_ipv4, rc.resource_set_ipv6 = rc_as, rc_v4, rc_v6 + rc.from_resource_bag(resources) for child_cert in rpki.sql.child_cert_obj.sql_fetch_where(gctx, """ child_id = %s AND ca_detail_id = %s """ % (child.child_id, ca_detail.ca_detail_id)): @@ -243,7 +255,7 @@ class issue_pdu(base_elt): # Check current cert, if any irdb_resources = rpki.left_right.irdb_query(gctx, child.self_id, child.child_id) - rc_as, rc_v4, rc_v6 = ca_detail.latest_ca_cert.get_3779resources(*irdb_resources) + resources = ca_detail.latest_ca_cert.get_3779resources().intersection(irdb_resources) req_key = self.pkcs10.getPublicKey() req_sia = self.pkcs10.get_SIA() child_cert = rpki.sql.child_cert_obj.sql_fetch_where1(gctx, """ @@ -258,16 +270,12 @@ class issue_pdu(base_elt): child = child, subject_key = req_key, sia = req_sia, - as = rc_as, - v4 = rc_v4, - v6 = rc_v6) - elif ((rc_as, rc_v4, rc_v6) != child_cert.cert.get_3779resources()) or child_cert.cert.get_SIA() != req_sia: + resources = resources) + elif resources != child_cert.cert.get_3779resources() or child_cert.cert.get_SIA() != req_sia: child_cert.reissue(gctx = gctx, ca_detail = ca_detail, - as = rc_as, - v4 = rc_v4, - v6 = rc_v6, - sia = req_sia) + sia = req_sia, + resources = resources) # Save anything we modified and generate response rpki.sql.sql_sweep(gctx) @@ -278,7 +286,7 @@ class issue_pdu(base_elt): rc = class_elt() rc.class_name = str(ca_id) rc.cert_url = multi_uri(ca_detail.ca_cert_uri) - rc.resource_set_as, rc.resource_set_ipv4, rc.resource_set_ipv6 = rc_as, rc_v4, rc_v6 + rc.from_resource_bag(resources) rc.certs.append(c) rc.issuer = ca_detail.latest_ca_cert r_msg.payload = issue_response_pdu() diff --git a/scripts/rpki/x509.py b/scripts/rpki/x509.py index 069c84d6..a9ebf625 100644 --- a/scripts/rpki/x509.py +++ b/scripts/rpki/x509.py @@ -194,18 +194,11 @@ class DER_object(object): """Get the SIA extension from this object. Only works for subclasses that support getExtension().""" return (self.get_POWpkix().getExtension(name2oid["subjectInfoAccess"]) or ((), 0, None))[2] - def get_3779resources(self, as_intersector = None, v4_intersector = None, v6_intersector = None): + def get_3779resources(self): """Get RFC 3779 resources as rpki.resource_set objects. Only works for subclasses that support getExtensions(). """ - as, v4, v6 = rpki.resource_set.parse_extensions(self.get_POWpkix().getExtensions()) - if as_intersector is not None: - as = as.intersection(as_intersector) - if v4_intersector is not None: - v4 = v4.intersection(v4_intersector) - if v6_intersector is not None: - v6 = v6.intersection(v6_intersector) - return as, v4, v6 + return rpki.resource_set.resource_bag.from_asn1_tuples(self.get_POWpkix().getExtensions()) class X509(DER_object): """X.509 certificates. @@ -283,7 +276,7 @@ class X509(DER_object): return RSApublic(DER = self.get_POWpkix().tbs.subjectPublicKeyInfo.toString()) def issue(self, keypair, subject_key, serial, sia, aia, crldp, - cn = None, notAfter = None, as = None, v4 = None, v6 = None, is_ca = True): + cn = None, notAfter = None, resources = None, is_ca = True): """Issue a certificate.""" now = time.time() @@ -322,10 +315,11 @@ class X509(DER_object): else: assert not is_ca - if as: - exts.append(["sbgp-autonomousSysNum", True, (as.to_tuple(), None)]) - if v4 or v6: - exts.append(["sbgp-ipAddrBlock", True, [x for x in (v4.to_tuple(), v6.to_tuple()) if x is not None]]) + if resources is not None and resources.as: + exts.append(["sbgp-autonomousSysNum", True, (resources.as.to_tuple(), None)]) + + if resources is not None and (resources.v4 or resources.v6): + exts.append(["sbgp-ipAddrBlock", True, [x for x in (resources.v4.to_tuple(), resources.v6.to_tuple()) if x is not None]]) for x in exts: x[0] = name2oid[x[0]] diff --git a/scripts/test-pow.py b/scripts/test-pow.py index f371cc46..06827995 100644 --- a/scripts/test-pow.py +++ b/scripts/test-pow.py @@ -80,10 +80,10 @@ for der in (alice, apnic): print " Crit: ", crit print " Value:", val print - as, v4, v6 = rpki.resource_set.parse_extensions(cert.getExtensions()) - if as: print ",".join(map(lambda x: "AS:" + str(x), as)) - if v4: print ",".join(map(lambda x: "IPv4:" + str(x), v4)) - if v6: print ",".join(map(lambda x: "IPv6:" + str(x), v6)) - if as is not None: print as.to_tuple() - if v4 is not None: print v4.to_tuple() - if v6 is not None: print v6.to_tuple() + bag = rpki.resource_set.parse_extensions(cert.getExtensions()) + if bag.as: print ",".join(map(lambda x: "AS:" + str(x), bag.as)) + if bag.v4: print ",".join(map(lambda x: "IPv4:" + str(x), bag.v4)) + if bag.v6: print ",".join(map(lambda x: "IPv6:" + str(x), bag.v6)) + if bag.as is not None: print bag.as.to_tuple() + if bag.v4 is not None: print bag.v4.to_tuple() + if bag.v6 is not None: print bag.v6.to_tuple() diff --git a/scripts/testroot.py b/scripts/testroot.py index 6f959c3f..762a1757 100755 --- a/scripts/testroot.py +++ b/scripts/testroot.py @@ -40,7 +40,7 @@ def compose_response(r_msg): rc = rpki.up_down.class_elt() rc.class_name = root_name rc.cert_url = rpki.up_down.multi_uri(root_cert) - rc.resource_set_as, rc.resource_set_ipv4, rc.resource_set_ipv6 = rpki_issuer.get_3779resources() + rc.from_resource_bag(rpki_issuer.get_3779resources()) rc.issuer = rpki_issuer r_msg.payload.classes.append(rc) rpki_subject = get_subject_cert() @@ -61,7 +61,7 @@ class issue_pdu(rpki.up_down.issue_pdu): r_msg.payload = rpki.up_down.issue_response_pdu() rpki_subject = get_subject_cert() if rpki_subject is None: - as, v4, v6 = rpki_issuer.get_3779resources() + resources = rpki_issuer.get_3779resources() req_key = self.pkcs10.getPublicKey() req_sia = self.pkcs10.get_SIA() crldp = root_base + rpki_issuer.gSKI() + ".crl" @@ -71,9 +71,7 @@ class issue_pdu(rpki.up_down.issue_pdu): sia = req_sia, aia = root_cert, crldp = crldp, - as = as, - v4 = v4, - v6 = v6)) + resources = resources)) compose_response(r_msg) class revoke_pdu(rpki.up_down.revoke_pdu): |