diff options
author | Rob Austein <sra@hactrn.net> | 2012-01-08 04:12:32 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2012-01-08 04:12:32 +0000 |
commit | 252393a3b6760cf01f9f60e796ad380c41529067 (patch) | |
tree | 560abde5563aed803d083cb464dabb85eb4fc211 | |
parent | 39676701fc6c5dedde2248178b16b394e90d48b9 (diff) |
Checkpoint. New irdbd now work, after some transaction voodoo.
svn path=/branches/tk100/; revision=4147
-rw-r--r-- | rpkid/rpki/ipaddrs.py | 4 | ||||
-rw-r--r-- | rpkid/rpki/irdbd.py | 179 | ||||
-rw-r--r-- | rpkid/rpki/left_right.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/resource_set.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/rootd.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/rpkic.py | 52 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 66 | ||||
-rw-r--r-- | rpkid/tests/yamltest.py | 36 |
8 files changed, 214 insertions, 129 deletions
diff --git a/rpkid/rpki/ipaddrs.py b/rpkid/rpki/ipaddrs.py index 531bcbb9..a192f92b 100644 --- a/rpkid/rpki/ipaddrs.py +++ b/rpkid/rpki/ipaddrs.py @@ -57,6 +57,8 @@ class v4addr(long): """ Construct a v4addr object. """ + if isinstance(x, unicode): + x = x.encode("ascii") if isinstance(x, str): return cls.from_bytes(socket.inet_pton(socket.AF_INET, ".".join(str(int(i)) for i in x.split(".")))) else: @@ -94,6 +96,8 @@ class v6addr(long): """ Construct a v6addr object. """ + if isinstance(x, unicode): + x = x.encode("ascii") if isinstance(x, str): return cls.from_bytes(socket.inet_pton(socket.AF_INET6, x)) else: diff --git a/rpkid/rpki/irdbd.py b/rpkid/rpki/irdbd.py index adda4be2..706ad0ca 100644 --- a/rpkid/rpki/irdbd.py +++ b/rpkid/rpki/irdbd.py @@ -34,55 +34,13 @@ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ +from __future__ import with_statement + import sys, os, time, getopt, urlparse, warnings import rpki.http, rpki.config, rpki.resource_set, rpki.relaxng import rpki.exceptions, rpki.left_right, rpki.log, rpki.x509 -from django.conf import settings - -os.environ["TZ"] = "UTC" -time.tzset() - -cfg_file = None - -opts, argv = getopt.getopt(sys.argv[1:], "c:dh?", ["config=", "debug", "help"]) -for o, a in opts: - if o in ("-h", "--help", "-?"): - print __doc__ - sys.exit(0) - if o in ("-c", "--config"): - cfg_file = a - elif o in ("-d", "--debug"): - rpki.log.use_syslog = False -if argv: - raise rpki.exceptions.CommandParseFailure, "Unexpected arguments %s" % argv - -rpki.log.init("irdbd") - -cfg = rpki.config.parser(cfg_file, "irdbd") - -startup_msg = cfg.get("startup-message", "") -if startup_msg: - rpki.log.info(startup_msg) - -cfg.set_global_flags() - -settings.configure( - DEBUG = True, - DATABASES = { "default" : { - "ENGINE" : "django.db.backends.mysql", - "NAME" : cfg.get("sql-database"), - "USER" : cfg.get("sql-username"), - "PASSWORD" : cfg.get("sql-password"), - "HOST" : "", - "PORT" : ""}}, - INSTALLED_APPS = ("rpki.irdb",),) - -import rpki.irdb - - -class Main(object): - +class main(object): def handle_list_resources(self, q_pdu, r_msg): child = rpki.irdb.Child.objects.get(issuer__handle__exact = q_pdu.self_handle, handle = q_pdu.child_handle) @@ -92,14 +50,13 @@ class Main(object): r_pdu.child_handle = q_pdu.child_handle r_pdu.valid_until = child.valid_until.strftime("%Y-%m-%dT%H:%M:%SZ") r_pdu.asn = rpki.resource_set.resource_set_as.from_django( - (a.start_as, a.end_s) for a in child.asns.all()) + (a.start_as, a.end_as) for a in child.asns.all()) r_pdu.ipv4 = rpki.resource_set.resource_set_ipv4.from_django( (a.start_ip, a.end_ip) for a in child.address_ranges.filter(version = 4)) r_pdu.ipv6 = rpki.resource_set.resource_set_ipv6.from_django( (a.start_ip, a.end_ip) for a in child.address_ranges.filter(version = 6)) r_msg.append(r_pdu) - def handle_list_roa_requests(self, q_pdu, r_msg): for request in rpki.irdb.ROARequest.objects.filter(issuer__handle__exact = q_pdu.self_handle): r_pdu = rpki.left_right.list_roa_requests_elt() @@ -112,13 +69,14 @@ class Main(object): (p.prefix, p.prefixlen, p.max_prefixlen) for p in request.prefixes.filter(version = 6)) r_msg.append(r_pdu) - def handle_list_ghostbuster_requests(self, q_pdu, r_msg): - ghostbusters = rpki.irdb.GhostbusterRequest.objects.filter(issuer__handle__exact = q_pdu.self_handle, - parent__handle__exact = q_pdu.parent_handle) + ghostbusters = rpki.irdb.GhostbusterRequest.objects.filter( + issuer__handle__exact = q_pdu.self_handle, + parent__handle__exact = q_pdu.parent_handle) if ghostbusters.count() == 0: - ghostbusters = rpki.irdb.GhostbusterRequest.objects.filter(issuer__handle__exact = q_pdu.self_handle, - parent = None) + ghostbusters = rpki.irdb.GhostbusterRequest.objects.filter( + issuer__handle__exact = q_pdu.self_handle, + parent = None) for ghostbuster in ghostbusters: r_pdu = rpki.left_right.list_ghostbuster_requests_elt() r_pdu.tag = q_pdu.tag @@ -127,84 +85,109 @@ class Main(object): r_pdu.vcard = ghostbuster.vcard r_msg.append(r_pdu) + def voodoo(self): + # http://stackoverflow.com/questions/3346124/how-do-i-force-django-to-ignore-any-caches-and-reload-data + import django.db.transaction + with django.db.transaction.commit_manually(): + django.db.transaction.commit() def handler(self, query, path, cb): - try: + q_pdu = None r_msg = rpki.left_right.msg.reply() - + self.voodoo() serverCA = rpki.irdb.ServerCA.objects.get() rpkid = serverCA.ee_certificates.get(purpose = "rpkid") - try: q_msg = rpki.left_right.cms_msg(DER = query).unwrap((serverCA.certificate, rpkid.certificate)) - if not isinstance(q_msg, rpki.left_right.msg) or not q_msg.is_query(): - raise rpki.exceptions.BadQuery, "Unexpected %r PDU" % q_msg - + raise rpki.exceptions.BadQuery("Unexpected %r PDU" % q_msg) for q_pdu in q_msg: - - try: - - try: - h = self.handle_dispatch[type(q_pdu)] - except KeyError: - raise rpki.exceptions.BadQuery, "Unexpected %r PDU" % q_pdu - else: - h(self, q_pdu, r_msg) - - except (rpki.async.ExitNow, SystemExit): - raise - - except Exception, e: - rpki.log.traceback() - r_msg.append(rpki.left_right.report_error_elt.from_exception(e, q_pdu.self_handle, q_pdu.tag)) - + self.dispatch(q_pdu, r_msg) except (rpki.async.ExitNow, SystemExit): raise - except Exception, e: rpki.log.traceback() - r_msg.append(rpki.left_right.report_error_elt.from_exception(e)) - + if q_pdu is None: + r_msg.append(rpki.left_right.report_error_elt.from_exception(e)) + else: + r_msg.append(rpki.left_right.report_error_elt.from_exception(e, q_pdu.self_handle, q_pdu.tag)) irdbd = serverCA.ee_certificates.get(purpose = "irdbd") - cb(200, body = rpki.left_right.cms_msg().wrap(r_msg, irdbd.private_key, irdbd.certificate)) - except (rpki.async.ExitNow, SystemExit): raise - except Exception, e: rpki.log.traceback() + cb(500, reason = "Unhandled exception %s: %s" % (e.__class__.__name__, e)) - # We only get here in cases where we couldn't or wouldn't generate - # <report_error/>, so just return HTTP failure. + def dispatch(self, q_pdu, r_msg): + try: + handler = self.dispatch_vector[type(q_pdu)] + except KeyError: + raise rpki.exceptions.BadQuery("Unexpected %r PDU" % q_pdu) + else: + handler(q_pdu, r_msg) - cb(500, reason = "Unhandled exception %s: %s" % (e.__class__.__name__, e)) + def __init__(self, **kwargs): + global rpki + from django.conf import settings - handle_dispatch = { - rpki.left_right.list_resources_elt : handle_list_resources, - rpki.left_right.list_roa_requests_elt : handle_list_roa_requests, - rpki.left_right.list_ghostbuster_requests_elt : handle_list_ghostbuster_requests} + os.environ["TZ"] = "UTC" + time.tzset() + cfg_file = None - def __init__(self, **kwargs): - for k in kwargs: - setattr(self, k, kwargs[k]) + opts, argv = getopt.getopt(sys.argv[1:], "c:dh?", ["config=", "debug", "help"]) + for o, a in opts: + if o in ("-h", "--help", "-?"): + print __doc__ + sys.exit(0) + if o in ("-c", "--config"): + cfg_file = a + elif o in ("-d", "--debug"): + rpki.log.use_syslog = False + if argv: + raise rpki.exceptions.CommandParseFailure("Unexpected arguments %s" % argv) + + rpki.log.init("irdbd") + + cfg = rpki.config.parser(cfg_file, "irdbd") + + startup_msg = cfg.get("startup-message", "") + if startup_msg: + rpki.log.info(startup_msg) + cfg.set_global_flags() + + settings.configure( + DEBUG = True, + DATABASES = { "default" : { + "ENGINE" : "django.db.backends.mysql", + "NAME" : cfg.get("sql-database"), + "USER" : cfg.get("sql-username"), + "PASSWORD" : cfg.get("sql-password"), + "HOST" : "", + "PORT" : ""}}, + INSTALLED_APPS = ("rpki.irdb",),) + + import rpki.irdb + + self.dispatch_vector = { + rpki.left_right.list_resources_elt : self.handle_list_resources, + rpki.left_right.list_roa_requests_elt : self.handle_list_roa_requests, + rpki.left_right.list_ghostbuster_requests_elt : self.handle_list_ghostbuster_requests } + + u = urlparse.urlparse(cfg.get("http-url")) - def __call__(self): - u = urlparse.urlparse(self.http_url) assert u.scheme in ("", "http") and \ u.username is None and \ u.password is None and \ u.params == "" and \ u.query == "" and \ u.fragment == "" - rpki.http.server(host = u.hostname or "localhost", - port = u.port or 443, - handlers = ((u.path, self.handler),)) - -main = Main(http_url = cfg.get("http-url")) + rpki.http.server( + host = u.hostname or "localhost", + port = u.port or 443, + handlers = ((u.path, self.handler),)) diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py index 72412cbe..ac480ff0 100644 --- a/rpkid/rpki/left_right.py +++ b/rpkid/rpki/left_right.py @@ -394,7 +394,7 @@ class self_elt(data_elt): def list_failed(e): rpki.log.traceback() - rpki.log.warn("Couldn't get resource class list from parent %r, skipping: %s" % (parent, e)) + rpki.log.warn("Couldn't get resource class list from parent %r, skipping: %s (%r)" % (parent, e, e)) parent_iterator() rpki.up_down.list_pdu.query(parent, got_list, list_failed) diff --git a/rpkid/rpki/resource_set.py b/rpkid/rpki/resource_set.py index 2be8a614..06b2ebc7 100644 --- a/rpkid/rpki/resource_set.py +++ b/rpkid/rpki/resource_set.py @@ -996,7 +996,7 @@ class roa_prefix_set(list): for (x, y, z) in sql.fetchall()]) @classmethod - def from_sql(cls, iterable): + def from_django(cls, iterable): """ Create ROA prefix set from a Django query. diff --git a/rpkid/rpki/rootd.py b/rpkid/rpki/rootd.py index b289c3e8..7c569f7b 100644 --- a/rpkid/rpki/rootd.py +++ b/rpkid/rpki/rootd.py @@ -301,7 +301,7 @@ class main(object): self.rpki_root_dir = self.cfg.get("rpki-root-dir") self.rpki_base_uri = self.cfg.get("rpki-base-uri", "rsync://" + self.rpki_class_name + ".invalid/") - self.rpki_root_key = rpki.x509.RSA( Auto_file = self.cfg.get("rpki-root-key")) + self.rpki_root_key = rpki.x509.RSA(Auto_update = self.cfg.get("rpki-root-key")) self.rpki_root_cert_file = self.cfg.get("rpki-root-cert") self.rpki_root_cert_uri = self.cfg.get("rpki-root-cert-uri", self.rpki_base_uri + "Root.cer") diff --git a/rpkid/rpki/rpkic.py b/rpkid/rpki/rpkic.py index c098eea5..0b249f0a 100644 --- a/rpkid/rpki/rpkic.py +++ b/rpkid/rpki/rpkic.py @@ -567,7 +567,7 @@ class main(rpki.cli.Cmd): ta = rpki.x509.X509(Base64 = p.findtext("bpki_resource_ta")), repository_type = repository_type, referrer = referrer, - referral_authorization = referral_authorization)[0] + referral_authorization = referral_authorization) if repository_type == "none": r = Element("repository", type = "none") @@ -840,11 +840,11 @@ class main(rpki.cli.Cmd): for handle, prefixes in grouped.iteritems(): child = self.resource_ca.children.get(handle = handle) for prefix in rset(",".join(prefixes)): - obj = rpki.irdb.ChildNet.objects.get_or_create( + obj, created = rpki.irdb.ChildNet.objects.get_or_create( child = child, start_ip = str(prefix.min), end_ip = str(prefix.max), - version = version)[0] + version = version) primary_keys.append(obj.pk) q = rpki.irdb.ChildNet.objects @@ -852,6 +852,33 @@ class main(rpki.cli.Cmd): q = q.exclude(pk__in = primary_keys) q.delete() + + def do_show_child_resources(self, arg): + """ + Show resources assigned to children. + """ + + if arg.strip(): + raise BadCommandSyntax("This command takes no arguments") + + for child in self.resource_ca.children.all(): + + asn = rpki.resource_set.resource_set_as.from_django( + (a.start_as, a.end_as) for a in child.asns.all()) + ipv4 = rpki.resource_set.resource_set_ipv4.from_django( + (a.start_ip, a.end_ip) for a in child.address_ranges.filter(version = 4)) + ipv6 = rpki.resource_set.resource_set_ipv6.from_django( + (a.start_ip, a.end_ip) for a in child.address_ranges.filter(version = 6)) + + print "Child:", child.handle + if asn: + print " ASN:", asn + if ipv4: + print " IPv4:", ipv4 + if ipv6: + print " IPv6:", ipv6 + + def do_synchronize_asns(self, arg): """ Synchronize IRDB against asns.csv. @@ -878,10 +905,10 @@ class main(rpki.cli.Cmd): for handle, asns in grouped.iteritems(): child = self.resource_ca.children.get(handle = handle) for asn in rpki.resource_set.resource_set_as(",".join(asns)): - obj = rpki.irdb.ChildASN.objects.get_or_create( + obj, created = rpki.irdb.ChildASN.objects.get_or_create( child = child, start_as = str(asn.min), - end_as = str(asn.max))[0] + end_as = str(asn.max)) primary_keys.append(obj.pk) q = rpki.irdb.ChildASN.objects @@ -923,9 +950,7 @@ class main(rpki.cli.Cmd): for key, pnms in grouped.iteritems(): asn, group = key - roa_request = rpki.irdb.ROARequest.objects.create( - issuer = self.resource_ca, - asn = asn) + roa_request = self.resource_ca.roa_requests.create(asn = asn) for pnm in pnms: if ":" in pnm: @@ -934,8 +959,7 @@ class main(rpki.cli.Cmd): else: p = rpki.resource_set.roa_prefix_ipv4.parse_str(pnm) v = 4 - rpki.irdb.ROARequestPrefix.objects.create( - roa_request = roa_request, + roa_request.prefixes.create( version = v, prefix = str(p.prefix), prefixlen = int(p.prefixlen), @@ -1028,9 +1052,9 @@ class main(rpki.cli.Cmd): pubd_query = [] rpkid_query = [] - self_cert = rpki.irdb.HostedCA.objects.get_or_certify( + self_cert, created = rpki.irdb.HostedCA.objects.get_or_certify( issuer = self.server_ca, - hosted = ca)[0] + hosted = ca) # There should be exactly one <self/> object per hosted entity, by definition @@ -1094,10 +1118,10 @@ class main(rpki.cli.Cmd): assert bsc_pdu.pkcs10_request is not None - bsc = rpki.irdb.BSC.objects.get_or_certify( + bsc, created = rpki.irdb.BSC.objects.get_or_certify( issuer = ca, handle = bsc_handle, - pkcs10 = bsc_pdu.pkcs10_request)[0] + pkcs10 = bsc_pdu.pkcs10_request) if bsc_pdu.signing_cert != bsc.certificate or bsc_pdu.signing_cert_crl != ca.latest_crl: rpkid_query.append(rpki.left_right.bsc_elt.make_pdu( diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index bccdce03..7bbb47bc 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -568,11 +568,60 @@ class X509(DER_object): def issue(self, keypair, subject_key, serial, sia, aia, crldp, notAfter, cn = None, resources = None, is_ca = True): """ - Issue a certificate. + Issue an RPKI certificate. + """ + + assert aia is not None and crldp is not None + + return self._issue( + keypair = keypair, + subject_key = subject_key, + serial = serial, + sia = sia, + aia = aia, + crldp = crldp, + notAfter = notAfter, + cn = cn, + resources = resources, + is_ca = is_ca, + aki = self.get_SKI(), + issuer_name = self.get_POWpkix().getSubject()) + + + @classmethod + def self_certify(cls, keypair, subject_key, serial, sia, notAfter, + cn = None, resources = None): + """ + Generate a self-certified RPKI certificate. + """ + + ski = subject_key.get_SKI() + if cn is None: + cn = "".join(("%02X" % ord(i) for i in ski)) + + return cls._issue( + keypair = keypair, + subject_key = subject_key, + serial = serial, + sia = sia, + aia = None, + crldp = None, + notAfter = notAfter, + cn = cn, + resources = resources, + is_ca = True, + aki = ski, + issuer_name = (((rpki.oids.name2oid["commonName"], ("printableString", cn)),),)) + + + @staticmethod + def _issue(keypair, subject_key, serial, sia, aia, crldp, notAfter, + cn, resources, is_ca, aki, issuer_name): + """ + Common code to issue an RPKI certificate. """ now = rpki.sundial.now() - aki = self.get_SKI() ski = subject_key.get_SKI() if cn is None: @@ -583,7 +632,7 @@ class X509(DER_object): cert = rpki.POW.pkix.Certificate() cert.setVersion(2) cert.setSerial(serial) - cert.setIssuer(self.get_POWpkix().getSubject()) + cert.setIssuer(issuer_name) cert.setSubject((((rpki.oids.name2oid["commonName"], ("printableString", cn)),),)) cert.setNotBefore(now.toASN1tuple()) cert.setNotAfter(notAfter.toASN1tuple()) @@ -591,10 +640,15 @@ class X509(DER_object): exts = [ ["subjectKeyIdentifier", False, ski], ["authorityKeyIdentifier", False, (aki, (), None)], - ["cRLDistributionPoints", False, ((("fullName", (("uri", crldp),)), None, ()),)], - ["authorityInfoAccess", False, ((rpki.oids.name2oid["id-ad-caIssuers"], ("uri", aia)),)], ["certificatePolicies", True, ((rpki.oids.name2oid["id-cp-ipAddr-asNumber"], ()),)] ] + + if crldp is not None: + exts.append(["cRLDistributionPoints", False, ((("fullName", (("uri", crldp),)), None, ()),)]) + + if aia is not None: + exts.append(["authorityInfoAccess", False, ((rpki.oids.name2oid["id-ad-caIssuers"], ("uri", aia)),)]) + if is_ca: exts.append(["basicConstraints", True, (1, None)]) exts.append(["keyUsage", True, (0, 0, 0, 0, 0, 1, 1)]) @@ -1095,7 +1149,7 @@ class CMS_object(DER_object): if certs and (len(certs) > 1 or certs[0].getSubject() != trusted_ee.getSubject() or certs[0].getPublicKey() != trusted_ee.getPublicKey()): raise rpki.exceptions.UnexpectedCMSCerts # , certs if crls: - raise rpki.exceptions.UnexpectedCMSCRLs # , crls + rpki.log.warn("Ignoring unexpected CMS CRL%s from trusted peer" % ("" if len(crls) == 1 else "s")) else: if not certs: raise rpki.exceptions.MissingCMSEEcert # , certs diff --git a/rpkid/tests/yamltest.py b/rpkid/tests/yamltest.py index 600a0e62..40dd0e5f 100644 --- a/rpkid/tests/yamltest.py +++ b/rpkid/tests/yamltest.py @@ -46,7 +46,8 @@ PERFORMANCE OF THIS SOFTWARE. """ import subprocess, re, os, getopt, sys, yaml, signal, time -import rpki.resource_set, rpki.sundial, rpki.config, rpki.log, rpki.csv_utils +import rpki.resource_set, rpki.sundial, rpki.config, rpki.log +import rpki.csv_utils, rpki.x509 # Nasty regular expressions for parsing config files. Sadly, while # the Python ConfigParser supports writing config files, it does so in @@ -588,14 +589,33 @@ try: print "Creating rootd RPKI root certificate" - # Should use req -subj here to set subject name. Later. - db.root.run_openssl("req", "-x509", "-sha256", "-outform", "DER", - "-newkey", "rsa:2048", - "-keyout", "bpki/servers/root.key", - "-out", "publication/root.cer", - "-config", "rpki.conf", - "-extensions", "rootd_x509_extensions") + root_resources = rpki.resource_set.resource_bag( + asn = rpki.resource_set.resource_set_as("0-4294967295"), + v4 = rpki.resource_set.resource_set_ipv4("0.0.0.0/0"), + v6 = rpki.resource_set.resource_set_ipv6("::/0")) + root_key = rpki.x509.RSA.generate() + + root_uri = "rsync://localhost:%d/rpki/" % db.root.pubd.rsync_port + + root_sia = ((rpki.oids.name2oid["id-ad-caRepository"], ("uri", root_uri)), + (rpki.oids.name2oid["id-ad-rpkiManifest"], ("uri", root_uri + "root.mnf"))) + + root_cert = rpki.x509.X509.self_certify( + keypair = root_key, + subject_key = root_key.get_RSApublic(), + serial = 1, + sia = root_sia, + notAfter = rpki.sundial.now() + rpki.sundial.timedelta(days = 365), + resources = root_resources) + + f = open(db.root.path("publication/root.cer"), "wb") + f.write(root_cert.get_DER()) + f.close() + + f = open(db.root.path("bpki/servers/root.key"), "wb") + f.write(root_key.get_DER()) + f.close() # From here on we need to pay attention to initialization order. We # used to do all the pre-configure_daemons stuff before running any |