aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2009-06-09 04:29:20 +0000
committerRob Austein <sra@hactrn.net>2009-06-09 04:29:20 +0000
commitd689e9ce44ab7d7a07c4195fe6fc28289f7c5654 (patch)
tree9e0886ef12c8c9711efc440428d42fd133d5f3a5
parent51d3efcc2d122ac8d2049dbe3421d8ba80e633f6 (diff)
Checkpoint
svn path=/rpkid/rpki/https.py; revision=2508
-rw-r--r--rpkid/rpki/https.py6
-rw-r--r--rpkid/rpki/rpki_engine.py260
-rw-r--r--rpkid/rpkid.sql34
3 files changed, 286 insertions, 14 deletions
diff --git a/rpkid/rpki/https.py b/rpkid/rpki/https.py
index 2dabfe50..95fe2ad9 100644
--- a/rpkid/rpki/https.py
+++ b/rpkid/rpki/https.py
@@ -378,8 +378,7 @@ class http_server(http_stream):
self.log_cert("server", cert)
self.tls.useCertificate(cert.get_POW())
self.tls.useKey(key.get_POW())
- ta = set(dynamic_ta() if dynamic_ta else ta)
- ta.discard(None)
+ ta = rpki.x509.X509.normalize_chain(dynamic_ta() if dynamic_ta else ta)
if not ta:
raise RuntimeError, "No trust anchor(s) specified, this is unlikely to work"
for x in ta:
@@ -508,8 +507,7 @@ class http_client(http_stream):
self.expect_close = not want_persistent_client
self.cert = cert
self.key = key
- self.ta = set(ta)
- self.ta.discard(None)
+ self.ta = rpki.x509.X509.normalize_chain(ta)
def start(self):
try:
diff --git a/rpkid/rpki/rpki_engine.py b/rpkid/rpki/rpki_engine.py
index 7dbd78f5..45c1eff6 100644
--- a/rpkid/rpki/rpki_engine.py
+++ b/rpkid/rpki/rpki_engine.py
@@ -1045,3 +1045,263 @@ class revoked_cert_obj(rpki.sql.sql_persistent):
revoked = rpki.sundial.now(),
gctx = ca_detail.gctx,
ca_detail_id = ca_detail.ca_detail_id)
+
+class roa_obj(rpki.sql.sql_persistent):
+ """
+ Route Origin Authorization.
+ """
+
+ sql_template = rpki.sql.template(
+ "roa",
+ "roa_id",
+ "ca_detail_id",
+ "self_id",
+ "as_number",
+ ("roa", rpki.x509.ROA),
+ ("cert", rpki.x509.X509))
+
+ ca_detail_id = None
+ cert = None
+ roa = None
+
+ def sql_fetch_hook(self):
+ """
+ Extra SQL fetch actions for roa_obj -- handle prefix list.
+ """
+ for version, datatype, attribute in ((4, rpki.resource_set.roa_prefix_set_ipv4, "ipv4"),
+ (6, rpki.resource_set.roa_prefix_set_ipv6, "ipv6")):
+ setattr(self, attribute, datatype.from_sql(
+ self.gctx.sql,
+ """
+ SELECT address, prefixlen, max_prefixlen FROM roa_prefix
+ WHERE roa_id = %s AND version = %s
+ """,
+ (self.roa_id, version)))
+
+ def sql_insert_hook(self):
+ """
+ Extra SQL insert actions for roa_obj -- handle address
+ ranges.
+ """
+ for version, prefix_set in ((4, self.ipv4), (6, self.ipv6)):
+ if prefix_set:
+ self.gctx.sql.executemany(
+ """
+ INSERT roa_prefix (roa_id, address, prefixlen, max_prefixlen, version)
+ VALUES (%s, %s, %s, %s, %s)
+ """,
+ ((self.roa_id, x.address, x.prefixlen, x.max_prefixlen, version)
+ for x in prefix_set))
+
+ def sql_delete_hook(self):
+ """
+ Extra SQL delete actions for roa_obj -- handle address
+ ranges.
+ """
+ self.gctx.sql.execute("DELETE FROM roa_prefix WHERE roa_id = %s", (self.roa_id,))
+
+ def ca_detail(self):
+ """
+ Fetch all ca_detail objects that link to this roa_obj.
+ """
+ return rpki.rpki_engine.ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id)
+
+ def update_roa(self, callback):
+ """
+ Bring this roa_obj's ROA up to date if necesssary.
+ """
+
+ def lose(e):
+ rpki.log.error(traceback.format_exc())
+ rpki.log.warn("Could not update ROA %r, skipping: %s" % (self, e))
+ callback()
+ return
+
+ if self.roa is None:
+ self.generate_roa(callback, lose)
+ return
+
+ ca_detail = self.ca_detail()
+
+ if ca_detail is None or ca_detail.state != "active":
+ self.regenerate_roa(callback, lose)
+ return
+
+ regen_margin = rpki.sundial.timedelta(seconds = self.self().regen_margin)
+
+ if rpki.sundial.now() + regen_margin > self.cert.getNotAfter():
+ self.regenerate_roa(callback, lose)
+ return
+
+ ca_resources = ca_detail.latest_ca_cert.get_3779resources()
+ ee_resources = self.cert.get_3779resources()
+
+ if ee_resources.oversized(ca_resources):
+ self.regenerate_roa(callback, lose)
+ return
+
+ v4 = self.ipv4.to_resource_set() if self.ipv4 is not None else rpki.resource_set.resource_set_ipv4()
+ v6 = self.ipv6.to_resource_set() if self.ipv6 is not None else rpki.resource_set.resource_set_ipv6()
+
+ if ee_resources.v4 != v4 or ee_resources.v6 != v6:
+ self.regenerate_roa(callback, lose)
+ return
+
+ callback()
+
+ def generate_roa(self, callback, errback):
+ """
+ Generate a ROA.
+
+ At present this does not support ROAs with multiple signatures
+ (neither does the current CMS code).
+
+ At present we have no way of performing a direct lookup from a
+ desired set of resources to a covering certificate, so we have to
+ search. This could be quite slow if we have a lot of active
+ ca_detail objects. Punt on the issue for now, revisit if
+ profiling shows this as a hotspot.
+
+ Once we have the right covering certificate, we generate the ROA
+ payload, generate a new EE certificate, use the EE certificate to
+ sign the ROA payload, publish the result, then throw away the
+ private key for the EE cert, all per the ROA specification. This
+ implies that generating a lot of ROAs will tend to thrash
+ /dev/random, but there is not much we can do about that.
+ """
+
+ if self.ipv4 is None and self.ipv6 is None:
+ rpki.log.warn("Can't generate ROA for empty prefix list")
+ return
+
+ # Ugly and expensive search for covering ca_detail, there has to
+ # be a better way, but it would require the ability to test for
+ # resource subsets in SQL.
+
+ v4 = self.ipv4.to_resource_set() if self.ipv4 is not None else rpki.resource_set.resource_set_ipv4()
+ v6 = self.ipv6.to_resource_set() if self.ipv6 is not None else rpki.resource_set.resource_set_ipv6()
+
+ ca_detail = self.ca_detail()
+ if ca_detail is None or ca_detail.state != "active":
+ ca_detail = None
+ for parent in self.self().parents():
+ for ca in parent.cas():
+ ca_detail = ca.fetch_active()
+ if ca_detail is not None:
+ resources = ca_detail.latest_ca_cert.get_3779resources()
+ if v4.issubset(resources.v4) and v6.issubset(resources.v6):
+ break
+ ca_detail = None
+ if ca_detail is not None:
+ break
+
+ if ca_detail is None:
+ rpki.log.warn("generate_roa() could not find a certificate covering %s %s" % (v4, v6))
+ return
+
+ ca = ca_detail.ca()
+
+ resources = rpki.resource_set.resource_bag(v4 = v4, v6 = v6)
+
+ keypair = rpki.x509.RSA.generate()
+
+ self.ca_detail_id = ca_detail.ca_detail_id
+
+ self.cert = ca_detail.issue_ee(ca, resources, keypair.get_RSApublic(),
+ sia = ((rpki.oids.name2oid["id-ad-signedObject"],
+ ("uri", self.roa_uri(keypair))),))
+
+ self.roa = rpki.x509.ROA.build(self.as_number, self.ipv4, self.ipv6, keypair, (self.cert,))
+
+ self.sql_store()
+
+ repository = ca.parent().repository()
+
+ def one():
+ repository.publish(self.cert, self.ee_uri(), two, errback)
+
+ def two():
+ ca_detail.generate_manifest(callback, errback)
+
+ repository.publish(self.roa, self.roa_uri(),
+ one if self.publish_ee_separately else two,
+ errback)
+
+ def withdraw_roa(self, callback, errback, regenerate = False):
+ """
+ Withdraw ROA associated with this roa_obj.
+
+ In order to preserve make-before-break properties without
+ duplicating code, this method also handles generating a
+ replacement ROA when requested.
+ """
+
+ ca_detail = self.ca_detail()
+ ca = ca_detail.ca()
+ repository = ca.parent().repository()
+ cert = self.cert
+ roa = self.roa
+ roa_uri = self.roa_uri()
+ ee_uri = self.ee_uri()
+
+ if ca_detail.state != 'active':
+ self.ca_detail_id = None
+
+ def one():
+ rpki.log.debug("Withdrawing ROA and revoking its EE cert")
+ rpki.rpki_engine.revoked_cert_obj.revoke(cert = cert, ca_detail = ca_detail)
+ repository.withdraw(roa, roa_uri,
+ two if self.publish_ee_separately else three,
+ errback)
+
+ def two():
+ repository.withdraw(cert, ee_uri, three, errback)
+
+ def three():
+ self.gctx.sql.sweep()
+ ca_detail.generate_crl(four, errback)
+
+ def four():
+ ca_detail.generate_manifest(callback, errback)
+
+ if regenerate:
+ self.generate_roa(one, errback)
+ else:
+ one()
+
+ def regenerate_roa(self, callback, errback):
+ """
+ Reissue ROA associated with this roa_obj.
+ """
+ if self.ca_detail() is None:
+ self.generate_roa(callback, errback)
+ else:
+ self.withdraw_roa(callback, errback, regenerate = True)
+
+ def roa_uri(self, key = None):
+ """
+ Return the publication URI for this roa_obj's ROA.
+ """
+ return self.ca_detail().ca().sia_uri + self.roa_uri_tail(key)
+
+ def roa_uri_tail(self, key = None):
+ """
+ Return the tail (filename portion) of the publication URI for this
+ roa_obj's ROA.
+ """
+ return (key or self.cert).gSKI() + ".roa"
+
+ def ee_uri_tail(self):
+ """
+ Return the tail (filename) portion of the URI for this roa_obj's
+ ROA's EE certificate.
+ """
+ return self.cert.gSKI() + ".cer"
+
+ def ee_uri(self):
+ """
+ Return the publication URI for this roa_obj's ROA's EE
+ certificate.
+ """
+ return self.ca_detail().ca().sia_uri + self.ee_uri_tail()
+
diff --git a/rpkid/rpkid.sql b/rpkid/rpkid.sql
index 8cb2484c..96725b85 100644
--- a/rpkid/rpkid.sql
+++ b/rpkid/rpkid.sql
@@ -19,6 +19,8 @@
-- DROP TABLE commands must be in correct (reverse dependency) order
-- to satisfy FOREIGN KEY constraints.
+DROP TABLE IF EXISTS roa_prefix;
+DROP TABLE IF EXISTS roa;
DROP TABLE IF EXISTS route_origin_prefix;
DROP TABLE IF EXISTS route_origin;
DROP TABLE IF EXISTS revoked_cert;
@@ -43,7 +45,6 @@ CREATE TABLE self (
UNIQUE (self_handle)
) ENGINE=InnoDB;
-
CREATE TABLE bsc (
bsc_id SERIAL NOT NULL,
bsc_handle VARCHAR(255) NOT NULL,
@@ -58,7 +59,6 @@ CREATE TABLE bsc (
UNIQUE (self_id, bsc_handle)
) ENGINE=InnoDB;
-
CREATE TABLE repository (
repository_id SERIAL NOT NULL,
repository_handle VARCHAR(255) NOT NULL,
@@ -75,7 +75,6 @@ CREATE TABLE repository (
UNIQUE (self_id, repository_handle)
) ENGINE=InnoDB;
-
CREATE TABLE parent (
parent_id SERIAL NOT NULL,
parent_handle VARCHAR(255) NOT NULL,
@@ -97,7 +96,6 @@ CREATE TABLE parent (
UNIQUE (self_id, parent_handle)
) ENGINE=InnoDB;
-
CREATE TABLE ca (
ca_id SERIAL NOT NULL,
last_crl_sn BIGINT UNSIGNED NOT NULL,
@@ -112,7 +110,6 @@ CREATE TABLE ca (
FOREIGN KEY (parent_id) REFERENCES parent (parent_id)
) ENGINE=InnoDB;
-
CREATE TABLE ca_detail (
ca_detail_id SERIAL NOT NULL,
public_key LONGBLOB,
@@ -130,7 +127,6 @@ CREATE TABLE ca_detail (
FOREIGN KEY (ca_id) REFERENCES ca (ca_id)
) ENGINE=InnoDB;
-
CREATE TABLE child (
child_id SERIAL NOT NULL,
child_handle VARCHAR(255) NOT NULL,
@@ -144,7 +140,6 @@ CREATE TABLE child (
UNIQUE (self_id, child_handle)
) ENGINE=InnoDB;
-
CREATE TABLE child_cert (
child_cert_id SERIAL NOT NULL,
cert LONGBLOB NOT NULL,
@@ -156,7 +151,6 @@ CREATE TABLE child_cert (
FOREIGN KEY (child_id) REFERENCES child (child_id)
) ENGINE=InnoDB;
-
CREATE TABLE revoked_cert (
revoked_cert_id SERIAL NOT NULL,
serial BIGINT UNSIGNED NOT NULL,
@@ -167,7 +161,6 @@ CREATE TABLE revoked_cert (
FOREIGN KEY (ca_detail_id) REFERENCES ca_detail (ca_detail_id)
) ENGINE=InnoDB;
-
CREATE TABLE route_origin (
route_origin_id SERIAL NOT NULL,
route_origin_handle VARCHAR(255) NOT NULL,
@@ -182,7 +175,6 @@ CREATE TABLE route_origin (
UNIQUE (self_id, route_origin_handle)
) ENGINE=InnoDB;
-
CREATE TABLE route_origin_prefix (
address VARCHAR(40) NOT NULL,
prefixlen TINYINT NOT NULL,
@@ -193,6 +185,28 @@ CREATE TABLE route_origin_prefix (
FOREIGN KEY (route_origin_id) REFERENCES route_origin (route_origin_id)
) ENGINE=InnoDB;
+CREATE TABLE roa (
+ roa_id SERIAL NOT NULL,
+ as_number DECIMAL(24,0),
+ cert LONGBLOB,
+ roa LONGBLOB,
+ self_id BIGINT UNSIGNED NOT NULL,
+ ca_detail_id BIGINT UNSIGNED,
+ PRIMARY KEY (roa_id),
+ FOREIGN KEY (self_id) REFERENCES self (self_id),
+ FOREIGN KEY (ca_detail_id) REFERENCES ca_detail (ca_detail_id)
+) ENGINE=InnoDB;
+
+CREATE TABLE roa_prefix (
+ address VARCHAR(40) NOT NULL,
+ prefixlen TINYINT NOT NULL,
+ max_prefixlen TINYINT NOT NULL,
+ version TINYINT NOT NULL,
+ roa_id BIGINT UNSIGNED NOT NULL,
+ PRIMARY KEY (roa_id, address, prefixlen, max_prefixlen),
+ FOREIGN KEY (roa_id) REFERENCES roa (roa_id)
+) ENGINE=InnoDB;
+
-- Local Variables:
-- indent-tabs-mode: nil
-- End: