diff options
Diffstat (limited to 'rpkid/rpki')
-rw-r--r-- | rpkid/rpki/gctx.py | 33 | ||||
-rw-r--r-- | rpkid/rpki/https.py | 48 | ||||
-rw-r--r-- | rpkid/rpki/left_right.py | 24 | ||||
-rw-r--r-- | rpkid/rpki/relaxng.py | 12 | ||||
-rw-r--r-- | rpkid/rpki/sql.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 104 |
6 files changed, 105 insertions, 118 deletions
diff --git a/rpkid/rpki/gctx.py b/rpkid/rpki/gctx.py index 2bdc5daf..127205f7 100644 --- a/rpkid/rpki/gctx.py +++ b/rpkid/rpki/gctx.py @@ -36,10 +36,11 @@ class global_context(object): passwd = cfg.get("sql-password")) self.cur = self.db.cursor() - self.ta_irdb = rpki.x509.X509(Auto_file = cfg.get("ta-irdb")) - self.ta_irbe = rpki.x509.X509(Auto_file = cfg.get("ta-irbe")) - self.ee_key = rpki.x509.RSA(Auto_file = cfg.get("ee-key")) - self.cert_chain = rpki.x509.X509_chain(Auto_files = cfg.multiget("cert-chain")) + self.bpki_ta = rpki.x509.X509(Auto_file = cfg.get("bpki-ta")) + self.irdb_cert = rpki.x509.X509(Auto_file = cfg.get("irdb-cert")) + self.irbe_cert = rpki.x509.X509(Auto_file = cfg.get("irbe-cert")) + self.rpkid_cert = rpki.x509.X509(Auto_file = cfg.get("rpkid-cert")) + self.rpkid_key = rpki.x509.RSA( Auto_file = cfg.get("rpkid-key")) self.irdb_url = cfg.get("irdb-url") @@ -69,14 +70,14 @@ class global_context(object): q_msg[0].type = "query" q_msg[0].self_id = self_id q_msg[0].child_id = child_id - q_cms = rpki.left_right.cms_msg.wrap(q_msg, self.ee_key, self.cert_chain) + q_cms = rpki.left_right.cms_msg.wrap(q_msg, self.rpkid_key, self.rpkid_cert) der = rpki.https.client( - client_key = self.ee_key, - client_certs = self.cert_chain, - server_ta = self.ta_irdb, + client_key = self.rpkid_key, + client_cert = self.rpkid_cert, + server_ta = self.irdb_cert, url = self.irdb_url, msg = q_cms) - r_msg = rpki.left_right.cms_msg.unwrap(der, self.ta_irdb) + r_msg = rpki.left_right.cms_msg.unwrap(der, self.irdb_cert) if len(r_msg) == 0 or not isinstance(r_msg[0], rpki.left_right.list_resources_elt) or r_msg[0].type != "reply": raise rpki.exceptions.BadIRDBReply, "Unexpected response to IRDB query: %s" % lxml.etree.tostring(r_msg.toXML(), pretty_print = True, encoding = "us-ascii") return rpki.resource_set.resource_bag( @@ -107,9 +108,9 @@ class global_context(object): """Process one left-right PDU.""" rpki.log.trace() try: - q_msg = rpki.left_right.cms_msg.unwrap(query, self.ta_irbe) + q_msg = rpki.left_right.cms_msg.unwrap(query, self.bpki_ta) r_msg = q_msg.serve_top_level(self) - reply = rpki.left_right.cms_msg.wrap(r_msg, self.ee_key, self.cert_chain) + reply = rpki.left_right.cms_msg.wrap(r_msg, self.rpkid_key, self.rpkid_cert) self.sql_sweep() return 200, reply except Exception, data: @@ -155,7 +156,7 @@ class global_context(object): """Clear cached HTTPS trust anchor X509Store.""" if self.https_ta_cache is not None: - rpki.log.debug("Clearing HTTPS trust anchor cache") + rpki.log.debug("Clearing HTTPS trusted cert cache") self.https_ta_cache = None def build_x509store(self): @@ -168,15 +169,17 @@ class global_context(object): """ if self.https_ta_cache is None: - store = POW.X509Store() + selves = rpki.left_right.self_elt.sql_fetch_all(self) children = rpki.left_right.child_elt.sql_fetch_all(self) certs = [c.peer_biz_cert for c in children if c.peer_biz_cert is not None] + \ [c.peer_biz_glue for c in children if c.peer_biz_glue is not None] + \ - [ self.ta_irbe ] + [s.biz_cert for s in selves if s.biz_cert is not None] + \ + [s.biz_glue for s in selves if s.biz_glue is not None] + \ + [self.irbe_cert, self.irdb_cert, self.bpki_ta] for x in certs: if rpki.https.debug_tls_certs: - rpki.log.debug("HTTPS dynamic trust anchor %s" % x.getSubject()) + rpki.log.debug("HTTPS dynamic trusted cert %s" % x.getSubject()) store.addTrust(x.get_POW()) self.https_ta_cache = store diff --git a/rpkid/rpki/https.py b/rpkid/rpki/https.py index db94e721..b5338f5d 100644 --- a/rpkid/rpki/https.py +++ b/rpkid/rpki/https.py @@ -31,10 +31,17 @@ import POW disable_tls_certificate_validation_exceptions = False # Chatter suppression -debug_tls_certs = False +debug_tls_certs = True rpki_content_type = "application/x-rpki" +def tlslite_certChain(x509): + """Utility function to construct tlslite certChains.""" + if isinstance(x509, rpki.x509.X509): + return tlslite.api.X509CertChain([x509.get_tlslite()]) + else: + return tlslite.api.X509CertChain([x.get_tlslite() for x in x509]) + class Checker(tlslite.api.Checker): """Derived class to handle X.509 client certificate checking.""" @@ -43,13 +50,18 @@ class Checker(tlslite.api.Checker): self.dynamic_x509store = dynamic_x509store - if dynamic_x509store is None: - self.x509store = POW.X509Store() + if dynamic_x509store is not None: + return + + self.x509store = POW.X509Store() + + if isinstance(trust_anchor, rpki.x509.X509): + trust_anchor = (trust_anchor,) + + for x in trust_anchor: if debug_tls_certs: - rpki.log.debug("HTTPS trust anchor %s" % trust_anchor.getSubject()) - self.x509store.addTrust(trust_anchor.get_POW()) - elif debug_tls_certs: - rpki.log.debug("HTTPS dynamic trust anchors") + rpki.log.debug("HTTPS trusted cert %s" % x.getSubject()) + self.x509store.addTrust(x.get_POW()) def x509store_thunk(self): if self.dynamic_x509store is not None: @@ -83,17 +95,17 @@ class httpsClient(tlslite.api.HTTPTLSConnection): """Derived class to let us replace the default Checker.""" def __init__(self, host, port = None, - client_certs = None, client_key = None, + client_cert = None, client_key = None, server_ta = None, settings = None): """Create a new httpsClient.""" tlslite.api.HTTPTLSConnection.__init__( self, host = host, port = port, settings = settings, - certChain = client_certs, privateKey = client_key) + certChain = client_cert, privateKey = client_key) self.checker = Checker(trust_anchor = server_ta) -def client(msg, client_key, client_certs, server_ta, url, timeout = 300): +def client(msg, client_key, client_cert, server_ta, url, timeout = 300): """Open client HTTPS connection, send a message, wait for response. This function wraps most of what one needs to do to send a message @@ -112,8 +124,8 @@ def client(msg, client_key, client_certs, server_ta, url, timeout = 300): u.fragment == "" if debug_tls_certs: - for client_cert in client_certs: - rpki.log.debug("Sending client TLS cert %s" % client_cert.getSubject()) + for cert in (client_cert,) if isinstance(client_cert, rpki.x509.X509) else client_cert: + rpki.log.debug("Sending client TLS cert %s" % cert.getSubject()) # We could add a "settings = foo" argument to the following call to # pass in a tlslite.HandshakeSettings object that would let us @@ -122,7 +134,7 @@ def client(msg, client_key, client_certs, server_ta, url, timeout = 300): httpc = httpsClient(host = u.hostname or "localhost", port = u.port or 443, client_key = client_key.get_tlslite(), - client_certs = client_certs.tlslite_certChain(), + client_cert = tlslite_certChain(client_cert), server_ta = server_ta) httpc.connect() httpc.sock.settimeout(timeout) @@ -179,12 +191,12 @@ class httpsServer(tlslite.api.TLSSocketServerMixIn, BaseHTTPServer.HTTPServer): rpki_sessionCache = None rpki_server_key = None - rpki_server_certs = None + rpki_server_cert = None rpki_checker = None def handshake(self, tlsConnection): """TLS handshake handler.""" - assert self.rpki_server_certs is not None + assert self.rpki_server_cert is not None assert self.rpki_server_key is not None assert self.rpki_sessionCache is not None @@ -194,7 +206,7 @@ class httpsServer(tlslite.api.TLSSocketServerMixIn, BaseHTTPServer.HTTPServer): # to pass in a tlslite.HandshakeSettings object that would let # us insist on, eg, particular SSL/TLS versions. # - tlsConnection.handshakeServer(certChain = self.rpki_server_certs, + tlsConnection.handshakeServer(certChain = self.rpki_server_cert, privateKey = self.rpki_server_key, sessionCache = self.rpki_sessionCache, checker = self.rpki_checker, @@ -205,7 +217,7 @@ class httpsServer(tlslite.api.TLSSocketServerMixIn, BaseHTTPServer.HTTPServer): rpki.log.warn("TLS handshake failure: " + str(error)) return False -def server(handlers, server_key, server_certs, port = 4433, host = "", client_ta = None, dynamic_x509store = None): +def server(handlers, server_key, server_cert, port = 4433, host = "", client_ta = None, dynamic_x509store = None): """Run an HTTPS server and wait (forever) for connections.""" if not isinstance(handlers, (tuple, list)): @@ -217,7 +229,7 @@ def server(handlers, server_key, server_certs, port = 4433, host = "", client_ta httpd = httpsServer((host, port), boundRequestHandler) httpd.rpki_server_key = server_key.get_tlslite() - httpd.rpki_server_certs = server_certs.tlslite_certChain() + httpd.rpki_server_cert = tlslite_certChain(server_cert) httpd.rpki_sessionCache = tlslite.api.SessionCache() httpd.rpki_checker = Checker(trust_anchor = client_ta, dynamic_x509store = dynamic_x509store) diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py index 3361ac5e..db7f9191 100644 --- a/rpkid/rpki/left_right.py +++ b/rpkid/rpki/left_right.py @@ -215,15 +215,18 @@ class self_elt(data_elt): element_name = "self" attributes = ("action", "type", "tag", "self_id", "crl_interval", "regen_margin") - elements = ("extension_preference",) + elements = ("extension_preference", "biz_cert", "biz_glue") booleans = ("rekey", "reissue", "revoke", "run_now", "publish_world_now", "clear_extension_preferences") - sql_template = rpki.sql.template("self", "self_id", "use_hsm", "crl_interval", "regen_margin") + sql_template = rpki.sql.template("self", "self_id", "use_hsm", "crl_interval", "regen_margin", + ("biz_cert", rpki.x509.X509), ("biz_glue", rpki.x509.X509)) self_id = None use_hsm = False crl_interval = None regen_margin = None + biz_cert = None + biz_glue = None def __init__(self): """Initialize a self_elt.""" @@ -322,14 +325,19 @@ class self_elt(data_elt): self.prefs.append(pref) stack.append(pref) pref.startElement(stack, name, attrs) - else: + elif name not in ("biz_cert", "biz_glue"): assert name == "self", "Unexpected name %s, stack %s" % (name, stack) self.read_attrs(attrs) def endElement(self, stack, name, text): """Handle <self/> element.""" - assert name == "self", "Unexpected name %s, stack %s" % (name, stack) - stack.pop() + if name == "biz_cert": + self.biz_cert = rpki.x509.X509(Base64 = text) + elif name == "biz_glue": + self.biz_glue = rpki.x509.X509(Base64 = text) + else: + assert name == "self", "Unexpected name %s, stack %s" % (name, stack) + stack.pop() def toXML(self): """Generate <self/> element.""" @@ -443,12 +451,12 @@ class bsc_elt(data_elt): def __init__(self): """Initialize bsc_elt.""" - self.signing_cert = rpki.x509.X509_chain() + self.signing_cert = [] def sql_fetch_hook(self): """Extra SQL fetch actions for bsc_elt -- handle signing certs.""" self.gctx.cur.execute("SELECT cert FROM bsc_cert WHERE bsc_id = %s", (self.bsc_id,)) - self.signing_cert[:] = [rpki.x509.X509(DER = x) for (x,) in self.gctx.cur.fetchall()] + self.signing_cert = [rpki.x509.X509(DER = x) for (x,) in self.gctx.cur.fetchall()] def sql_insert_hook(self): """Extra SQL insert actions for bsc_elt -- handle signing certs.""" @@ -613,7 +621,7 @@ class parent_elt(data_elt): der = rpki.https.client(server_ta = self.peer_biz_cert, client_key = bsc.private_key_id, - client_certs = bsc.signing_cert, + client_cert = bsc.signing_cert, msg = q_cms, url = self.peer_contact_uri) diff --git a/rpkid/rpki/relaxng.py b/rpkid/rpki/relaxng.py index c04a3158..8b3ab862 100644 --- a/rpkid/rpki/relaxng.py +++ b/rpkid/rpki/relaxng.py @@ -6,7 +6,7 @@ import lxml.etree ## Parsed RelaxNG left_right schema left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" encoding="UTF-8"?> <!-- - $Id: left-right-schema.rng 1668 2008-04-16 04:58:58Z sra $ + $Id: left-right-schema.rnc 1668 2008-04-16 04:58:58Z sra $ RelaxNG (Compact Syntax) Schema for RPKI left-right protocol. @@ -227,6 +227,16 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" enc </data> </element> </zeroOrMore> + <optional> + <element name="biz_cert"> + <ref name="base64"/> + </element> + </optional> + <optional> + <element name="biz_glue"> + <ref name="base64"/> + </element> + </optional> </define> <define name="self_id"> <attribute name="self_id"> diff --git a/rpkid/rpki/sql.py b/rpkid/rpki/sql.py index 892e99b7..85c85a6a 100644 --- a/rpkid/rpki/sql.py +++ b/rpkid/rpki/sql.py @@ -675,7 +675,7 @@ class ca_detail_obj(sql_persistant): nextUpdate = nextUpdate, names_and_objs = certs, keypair = self.manifest_private_key_id, - certs = rpki.x509.X509_chain(self.latest_manifest_cert)) + certs = self.latest_manifest_cert) repository.publish(self.latest_manifest, self.manifest_uri(ca)) diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index a74fc429..71ff4d53 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -356,74 +356,6 @@ class X509(DER_object): return X509(POWpkix = cert) -class X509_chain(list): - """Collections of certs. - - This class provides sorting and conversion functions for various - packages. - """ - - def __init__(self, *args, **kw): - """Initialize an X509_chain.""" - if args: - self[:] = args - elif "PEM_files" in kw: - self.load_from_PEM(kw["PEM_files"]) - elif "DER_files" in kw: - self.load_from_DER(kw["DER_files"]) - elif "Auto_files" in kw: - self.load_from_Auto(kw["Auto_files"]) - elif kw: - raise TypeError - - def chainsort(self): - """Sort a bag of certs into a chain, leaf first. - - Various other routines want their certs presented in this order. - """ - if len(self) > 1: - bag = self[:] - issuer_names = [x.getIssuer() for x in bag] - subject_map = dict([(x.getSubject(), x) for x in bag]) - chain = [] - for subject in subject_map: - if subject not in issuer_names: - cert = subject_map[subject] - chain.append(cert) - bag.remove(cert) - if len(chain) != 1: - raise rpki.exceptions.NotACertificateChain, "Certificates in bag don't form a proper chain" - while bag: - cert = subject_map[chain[-1].getIssuer()] - chain.append(cert) - bag.remove(cert) - self[:] = chain - - def tlslite_certChain(self): - """Return a certChain in the format tlslite likes.""" - self.chainsort() - return tlslite.api.X509CertChain([x.get_tlslite() for x in self]) - - def tlslite_trustList(self): - """Return a trustList in the format tlslite likes.""" - return [x.get_tlslite() for x in self] - - def clear(self): - """Drop all certs from this bag onto the floor.""" - self[:] = [] - - def load_from_PEM(self, files): - """Load a set of certs from a list of PEM files.""" - self.extend([X509(PEM_file=f) for f in files]) - - def load_from_DER(self, files): - """Load a set of certs from a list of DER files.""" - self.extend([X509(DER_file=f) for f in files]) - - def load_from_Auto(self, files): - """Load a set of certs from a list of DER or PEM files (guessing).""" - self.extend([X509(Auto_file=f) for f in files]) - class PKCS10(DER_object): """Class to hold a PKCS #10 request.""" @@ -622,6 +554,7 @@ class CMS_object(DER_object): econtent_oid = POWify("id-data") dump_on_verify_failure = False + debug_cms_certs = True def get_DER(self): """Get the DER value of this CMS_object.""" @@ -644,14 +577,27 @@ class CMS_object(DER_object): """Verify CMS wrapper and store inner content.""" cms = POW.derRead(POW.CMS_MESSAGE, self.get_DER()) + if cms.eContentType() != self.econtent_oid: raise rpki.exceptions.WrongEContentType, "Got CMS eContentType %s, expected %s" % (cms.eContentType(), self.econtent_oid) + store = POW.X509Store() - if isinstance(ta, (tuple, list)): - for x in ta: - store.addTrust(x.get_POW()) - else: - store.addTrust(ta.get_POW()) + + if isinstance(ta, X509): + ta = (ta,) + + for x in ta: + if self.debug_cms_certs: + rpki.log.debug("CMS trusted cert %s" % x.getSubject()) + store.addTrust(x.get_POW()) + + if self.debug_cms_certs: + try: + for x in cms.certs(): + rpki.log.debug("Received CMS cert %s" % x.getSubject()) + except: + pass + try: content = cms.verify(store) except: @@ -659,17 +605,25 @@ class CMS_object(DER_object): print "CMS verification failed, dumping ASN.1:" self.dumpasn1() raise rpki.exceptions.CMSVerificationFailed, "CMS verification failed" + self.decode(content) return self.get_content() def sign(self, keypair, certs, crls = None, no_certs = False): """Sign and wrap inner content.""" + if isinstance(certs, X509): + cert = certs + certs = () + else: + cert = certs[0] + certs = certs[1:] + cms = POW.CMS() - cms.sign(certs[0].get_POW(), + cms.sign(cert.get_POW(), keypair.get_POW(), self.encode(), - [x.get_POW() for x in certs[1:]], + [x.get_POW() for x in certs], crls, self.econtent_oid, POW.CMS_NOCERTS if no_certs else 0) |