aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ca/tests/smoketest.py6
-rw-r--r--ca/tests/yamlconf.py12
-rwxr-xr-xca/tests/yamltest.py12
-rwxr-xr-xrp/config/rpki-generate-root-certificate6
-rw-r--r--rpki/irdb/migrations/0002_root.py31
-rw-r--r--rpki/irdb/models.py19
-rw-r--r--rpki/irdb/zookeeper.py80
-rw-r--r--rpki/resource_set.py8
-rw-r--r--rpki/rootd.py11
-rw-r--r--rpki/rpkid.py8
-rw-r--r--rpki/rpkid_tasks.py16
-rw-r--r--rpki/rpkidb/models.py276
-rw-r--r--rpki/sundial.py2
13 files changed, 361 insertions, 126 deletions
diff --git a/ca/tests/smoketest.py b/ca/tests/smoketest.py
index 9d82c640..6479883e 100644
--- a/ca/tests/smoketest.py
+++ b/ca/tests/smoketest.py
@@ -537,9 +537,9 @@ class allocation(object):
if valid_until is None and "valid_for" in yaml:
valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"])
self.base = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(yaml.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(yaml.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(yaml.get("ipv6")),
+ asn = str(yaml.get("asn", "")),
+ v4 = yaml.get("ipv4"),
+ v6 = yaml.get("ipv6"),
valid_until = valid_until)
self.sia_base = yaml.get("sia_base")
if "crl_interval" in yaml:
diff --git a/ca/tests/yamlconf.py b/ca/tests/yamlconf.py
index 52c4da26..08827acd 100644
--- a/ca/tests/yamlconf.py
+++ b/ca/tests/yamlconf.py
@@ -218,9 +218,9 @@ class allocation(object):
if valid_until is None and "valid_for" in y:
valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(y["valid_for"])
self.base = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(y.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(y.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(y.get("ipv6")),
+ asn = str(y.get("asn", "")),
+ v4 = y.get("ipv4"),
+ v6 = y.get("ipv6"),
valid_until = valid_until)
if "crl_interval" in y:
self.crl_interval = rpki.sundial.timedelta.parse(y["crl_interval"]).convert_to_seconds()
@@ -514,9 +514,9 @@ class allocation(object):
assert self.is_root and not self.is_hosted
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"))
+ asn = "0-4294967295",
+ v4 = "0.0.0.0/0",
+ v6 = "::/0")
root_key = rpki.x509.RSA.generate(quiet = True)
diff --git a/ca/tests/yamltest.py b/ca/tests/yamltest.py
index 38b5bdac..d413df5c 100755
--- a/ca/tests/yamltest.py
+++ b/ca/tests/yamltest.py
@@ -244,9 +244,9 @@ class allocation(object):
if valid_until is None and "valid_for" in yaml:
valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"])
self.base = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(yaml.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(yaml.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(yaml.get("ipv6")),
+ asn = str(yaml.get("asn", "")),
+ v4 = yaml.get("ipv4"),
+ v6 = yaml.get("ipv6"),
valid_until = valid_until)
if "crl_interval" in yaml:
self.crl_interval = rpki.sundial.timedelta.parse(yaml["crl_interval"]).convert_to_seconds()
@@ -788,9 +788,9 @@ def create_root_certificate(db_root):
print "Creating rootd RPKI root certificate"
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"))
+ asn = "0-4294967295",
+ v4 = "0.0.0.0/0",
+ v6 = "::/0")
root_key = rpki.x509.RSA.generate(quiet = True)
diff --git a/rp/config/rpki-generate-root-certificate b/rp/config/rpki-generate-root-certificate
index d4ee08fd..10b8b194 100755
--- a/rp/config/rpki-generate-root-certificate
+++ b/rp/config/rpki-generate-root-certificate
@@ -37,9 +37,9 @@ cfg.argparser.add_argument("--tal", help = "TAL file", default =
args = cfg.argparser.parse_args()
resources = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(args.asns),
- v4 = rpki.resource_set.resource_set_ipv4(args.ipv4),
- v6 = rpki.resource_set.resource_set_ipv6(args.ipv6))
+ asn = args.asns,
+ v4 = args.ipv4,
+ v6 = args.ipv6)
keypair = rpki.x509.RSA.generate(quiet = True)
diff --git a/rpki/irdb/migrations/0002_root.py b/rpki/irdb/migrations/0002_root.py
index 73c08dde..6bdc060e 100644
--- a/rpki/irdb/migrations/0002_root.py
+++ b/rpki/irdb/migrations/0002_root.py
@@ -2,8 +2,6 @@
from __future__ import unicode_literals
from django.db import migrations, models
-import rpki.irdb.models
-import rpki.fields
class Migration(migrations.Migration):
@@ -13,22 +11,19 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.CreateModel(
- name='Root',
- fields=[
- ('turtle_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='irdb.Turtle')),
- ('certificate', rpki.fields.CertificateField()),
- ('handle', rpki.irdb.models.HandleField(max_length=120)),
- ('ta', rpki.fields.CertificateField()),
- ('asn_resources', models.TextField()),
- ('ipv4_resources', models.TextField()),
- ('ipv6_resources', models.TextField()),
- ('issuer', models.OneToOneField(related_name='root', to='irdb.ResourceHolderCA')),
- ],
- bases=('irdb.turtle', models.Model),
+ migrations.AddField(
+ model_name='parent',
+ name='asn_resources',
+ field=models.TextField(blank=True),
),
- migrations.AlterUniqueTogether(
- name='root',
- unique_together=set([('issuer', 'handle')]),
+ migrations.AddField(
+ model_name='parent',
+ name='ipv4_resources',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AddField(
+ model_name='parent',
+ name='ipv6_resources',
+ field=models.TextField(blank=True),
),
]
diff --git a/rpki/irdb/models.py b/rpki/irdb/models.py
index dc3723d8..ab81aa84 100644
--- a/rpki/irdb/models.py
+++ b/rpki/irdb/models.py
@@ -452,27 +452,14 @@ class Parent(CrossCertification, Turtle):
repository_type = EnumField(choices = ("none", "offer", "referral"))
referrer = HandleField(null = True, blank = True)
referral_authorization = SignedReferralField(null = True, blank = True)
+ asn_resources = django.db.models.TextField(blank = True) # root only
+ ipv4_resources = django.db.models.TextField(blank = True) # root only
+ ipv6_resources = django.db.models.TextField(blank = True) # root only
# This shouldn't be necessary
class Meta:
unique_together = ("issuer", "handle")
-class Root(CrossCertification, Turtle):
- #
- # This is sort of a cross between a Rootd and a Parent with extra
- # fields for the root resources. As with Parent, the private key
- # comes from a BSC rather than from a server EE cert as with
- # Rootd, so this looks looks to us like a cross certification (of
- # ourself). We may want to revisit this.
- #
- issuer = django.db.models.OneToOneField(ResourceHolderCA, related_name = "root")
- asn_resources = django.db.models.TextField()
- ipv4_resources = django.db.models.TextField()
- ipv6_resources = django.db.models.TextField()
-
- class Meta:
- unique_together = ("issuer", "handle")
-
class ROARequest(django.db.models.Model):
issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "roa_requests")
asn = django.db.models.BigIntegerField()
diff --git a/rpki/irdb/zookeeper.py b/rpki/irdb/zookeeper.py
index 7446e7c7..1f6fb6c2 100644
--- a/rpki/irdb/zookeeper.py
+++ b/rpki/irdb/zookeeper.py
@@ -368,34 +368,23 @@ class Zookeeper(object):
def configure_root(self, handle, resources):
# XXX This should be some other exception, not an assertion
- assert self.run_rpkid and self.run_pubd and self.run_rootd
+ assert self.run_rpkid and self.run_pubd
- rpki.irdb.models.Rootd.objects.get_or_certify(
- issuer = self.resource_ca,
- service_uri = "http://localhost:%s/" % self.cfg.get("rootd_server_port",
- section = myrpki_section))
+ if not handle:
+ handle = self.handle
- rpki.irdb.models.Root.objects.get_or_certify(
- handle = handle or self.handle,
- issuer = self.resource_ca,
- ta = self.resource_ca.certificate,
- asn_resources = str(resources.asn),
- ipv4_resources = str(resources.v4),
- ipv6_resources = str(resources.v6))
+ parent = rpki.irdb.models.Parent.objects.get_or_certify(
+ issuer = self.resource_ca,
+ handle = handle,
+ parent_handle = handle,
+ child_handle = handle,
+ ta = self.resource_ca.certificate,
+ repository_type = "none",
+ asn_resources = str(resources.asn),
+ ipv4_resources = str(resources.v4),
+ ipv6_resources = str(resources.v6))[0]
- return self.generate_root_repository_offer()
-
-
- def generate_root_repository_offer(self):
- """
- Generate repository offer for rootd. Split out of
- configure_rootd() because that's easier for the GUI.
- """
-
- e = Element(tag_oob_publisher_request, nsmap = oob_nsmap, version = oob_version,
- publisher_handle = self.handle)
- B64Element(e, tag_oob_publisher_bpki_ta, self.resource_ca.certificate)
- return etree_wrapper(e, msg = 'This is the "repository offer" file for you to use if you want to publish in your own repository')
+ return self.generate_repository_request(parent)
def write_bpki_files(self):
@@ -1341,7 +1330,9 @@ class Zookeeper(object):
# might make a case for a day instead, but we've been running with
# six hours for a while now and haven't seen a lot of whining.
- tenant_crl_interval = self.cfg.getint("tenant_crl_interval", 6 * 60 * 60, section = myrpki_section)
+ tenant_crl_interval = self.cfg.getint("tenant_crl_interval",
+ 6 * 60 * 60,
+ section = myrpki_section)
# regen_margin now just controls how long before RPKI certificate
# expiration we should regenerate; it used to control the interval
@@ -1353,7 +1344,9 @@ class Zookeeper(object):
# that this will regenerate certificates just *before* the
# companion cron job warns of impending doom.
- tenant_regen_margin = self.cfg.getint("tenant_regen_margin", 14 * 24 * 60 * 60 + 2 * 60, section = myrpki_section)
+ tenant_regen_margin = self.cfg.getint("tenant_regen_margin",
+ 14 * 24 * 60 * 60 + 2 * 60,
+ section = myrpki_section)
# See what rpkid already has on file for this entity.
@@ -1390,7 +1383,8 @@ class Zookeeper(object):
if (tenant_pdu is None or
tenant_pdu.get("crl_interval") != str(tenant_crl_interval) or
tenant_pdu.get("regen_margin") != str(tenant_regen_margin) or
- tenant_pdu.findtext(rpki.left_right.tag_bpki_cert, "").decode("base64") != tenant_cert.certificate.get_DER()):
+ tenant_pdu.findtext(rpki.left_right.tag_bpki_cert,
+ "").decode("base64") != tenant_cert.certificate.get_DER()):
q_pdu = SubElement(q_msg, rpki.left_right.tag_tenant,
action = "create" if tenant_pdu is None else "set",
tag = "tenant",
@@ -1422,7 +1416,8 @@ class Zookeeper(object):
# can finish setting up the BSC before anything tries to use it.
if len(q_msg) > 0:
- SubElement(q_msg, rpki.left_right.tag_bsc, action = "list", tag = "bsc", tenant_handle = ca.handle)
+ SubElement(q_msg, rpki.left_right.tag_bsc,
+ action = "list", tag = "bsc", tenant_handle = ca.handle)
r_msg = self.call_rpkid(q_msg)
bsc_pdus = dict((r_pdu.get("bsc_handle"), r_pdu)
for r_pdu in r_msg.getiterator(rpki.left_right.tag_bsc)
@@ -1439,8 +1434,10 @@ class Zookeeper(object):
handle = bsc_handle,
pkcs10 = rpki.x509.PKCS10(Base64 = bsc_pkcs10.text))[0]
- if (bsc_pdu.findtext(rpki.left_right.tag_signing_cert, "").decode("base64") != bsc.certificate.get_DER() or
- bsc_pdu.findtext(rpki.left_right.tag_signing_cert_crl, "").decode("base64") != ca.latest_crl.get_DER()):
+ if (bsc_pdu.findtext(rpki.left_right.tag_signing_cert,
+ "").decode("base64") != bsc.certificate.get_DER() or
+ bsc_pdu.findtext(rpki.left_right.tag_signing_cert_crl,
+ "").decode("base64") != ca.latest_crl.get_DER()):
q_pdu = SubElement(q_msg, rpki.left_right.tag_bsc,
action = "set",
tag = "bsc",
@@ -1463,7 +1460,8 @@ class Zookeeper(object):
repository_pdu.get("bsc_handle") != bsc_handle or
repository_pdu.get("peer_contact_uri") != repository.service_uri or
repository_pdu.get("rrdp_notification_uri") != repository.rrdp_notification_uri or
- repository_pdu.findtext(rpki.left_right.tag_bpki_cert, "").decode("base64") != repository.certificate.get_DER()):
+ repository_pdu.findtext(rpki.left_right.tag_bpki_cert,
+ "").decode("base64") != repository.certificate.get_DER()):
q_pdu = SubElement(q_msg, rpki.left_right.tag_repository,
action = "create" if repository_pdu is None else "set",
tag = repository.handle,
@@ -1473,7 +1471,8 @@ class Zookeeper(object):
peer_contact_uri = repository.service_uri)
if repository.rrdp_notification_uri:
q_pdu.set("rrdp_notification_uri", repository.rrdp_notification_uri)
- SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = repository.certificate.get_Base64()
+ SubElement(q_pdu,
+ rpki.left_right.tag_bpki_cert).text = repository.certificate.get_Base64()
for repository_handle in repository_pdus:
SubElement(q_msg, rpki.left_right.tag_repository, action = "destroy",
@@ -1499,7 +1498,11 @@ class Zookeeper(object):
parent_pdu.get("sia_base") != parent.repository.sia_base or
parent_pdu.get("sender_name") != parent.child_handle or
parent_pdu.get("recipient_name") != parent.parent_handle or
- parent_pdu.findtext(rpki.left_right.tag_bpki_cert, "").decode("base64") != parent.certificate.get_DER()):
+ parent_pdu.get("root_asn_resources", "") != parent.asn_resources or
+ parent_pdu.get("root_ipv4_resources", "") != parent.ipv4_resources or
+ parent_pdu.get("root_ipv6_resources", "") != parent.ipv6_resources or
+ parent_pdu.findtext(rpki.left_right.tag_bpki_cert,
+ "").decode("base64") != parent.certificate.get_DER()):
q_pdu = SubElement(q_msg, rpki.left_right.tag_parent,
action = "create" if parent_pdu is None else "set",
tag = parent.handle,
@@ -1510,14 +1513,17 @@ class Zookeeper(object):
peer_contact_uri = parent.service_uri,
sia_base = parent.repository.sia_base,
sender_name = parent.child_handle,
- recipient_name = parent.parent_handle)
- SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = parent.certificate.get_Base64()
+ recipient_name = parent.parent_handle,
+ root_asn_resources = parent.asn_resources,
+ root_ipv4_resources = parent.ipv4_resources,
+ root_ipv6_resources = parent.ipv6_resources)
+ SubElement(q_pdu,
+ rpki.left_right.tag_bpki_cert).text = parent.certificate.get_Base64()
except rpki.irdb.models.Repository.DoesNotExist:
pass
try:
-
parent_pdu = parent_pdus.pop(ca.handle, None)
if (parent_pdu is None or
diff --git a/rpki/resource_set.py b/rpki/resource_set.py
index 319e2677..055076dd 100644
--- a/rpki/resource_set.py
+++ b/rpki/resource_set.py
@@ -644,6 +644,14 @@ class resource_bag(object):
# Expiration date of resources, for setting certificate notAfter field.
def __init__(self, asn = None, v4 = None, v6 = None, valid_until = None):
+ if isinstance(asn, (str, unicode)):
+ asn = resource_set_as(asn)
+ if isinstance(v4, (str, unicode)):
+ v4 = resource_set_ipv4(v4)
+ if isinstance(v6, (str, unicode)):
+ v6 = resource_set_ipv6(v6)
+ if isinstance(valid_until, (str, unicode)):
+ valid_until = rpki.sundial.datetime.fromXMLtime(valid_until)
self.asn = asn or resource_set_as()
self.v4 = v4 or resource_set_ipv4()
self.v6 = v6 or resource_set_ipv6()
diff --git a/rpki/rootd.py b/rpki/rootd.py
index 08259a9a..70669345 100644
--- a/rpki/rootd.py
+++ b/rpki/rootd.py
@@ -349,16 +349,19 @@ class main(object):
q_msg = q_cms.unwrap((self.bpki_ta, self.child_bpki_cert))
q_type = q_msg.get("type")
logger.info("Serving %s query", q_type)
- r_msg = Element(rpki.up_down.tag_message, nsmap = rpki.up_down.nsmap, version = rpki.up_down.version,
- sender = q_msg.get("recipient"), recipient = q_msg.get("sender"), type = q_type + "_response")
+ r_msg = Element(rpki.up_down.tag_message, nsmap = rpki.up_down.nsmap,
+ version = rpki.up_down.version,
+ sender = q_msg.get("recipient"), recipient = q_msg.get("sender"),
+ type = q_type + "_response")
try:
self.rpkid_cms_timestamp = q_cms.check_replay(self.rpkid_cms_timestamp, request.path)
getattr(self, "handle_" + q_type)(q_msg, r_msg)
except Exception, e:
logger.exception("Exception processing up-down %s message", q_type)
rpki.up_down.generate_error_response_from_exception(r_msg, e, q_type)
- request.send_cms_response(rpki.up_down.cms_msg().wrap(r_msg, self.rootd_bpki_key, self.rootd_bpki_cert,
- self.rootd_bpki_crl if self.include_bpki_crl else None))
+ request.send_cms_response(rpki.up_down.cms_msg().wrap(
+ r_msg, self.rootd_bpki_key, self.rootd_bpki_cert,
+ self.rootd_bpki_crl if self.include_bpki_crl else None))
except Exception, e:
logger.exception("Unhandled exception processing up-down message")
request.send_error(500, "Unhandled exception %s: %s" % (e.__class__.__name__, e))
diff --git a/rpki/rpkid.py b/rpki/rpkid.py
index bafad8a9..48645396 100644
--- a/rpki/rpkid.py
+++ b/rpki/rpkid.py
@@ -371,10 +371,10 @@ class main(object):
if len(r_msg) != len(q_msg):
raise rpki.exceptions.BadIRDBReply("Expected IRDB response to be same length as query: %s" % r_msg.pretty_print_content())
- bags = [rpki.resource_set.resource_bag(asn = rpki.resource_set.resource_set_as(r_pdu.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(r_pdu.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(r_pdu.get("ipv6")),
- valid_until = rpki.sundial.datetime.fromXMLtime(r_pdu.get("valid_until")))
+ bags = [rpki.resource_set.resource_bag(asn = r_pdu.get("asn"),
+ v4 = r_pdu.get("ipv4"),
+ v6 = r_pdu.get("ipv6"),
+ valid_until = r_pdu.get("valid_until"))
for r_pdu in r_msg]
raise tornado.gen.Return(bags)
diff --git a/rpki/rpkid_tasks.py b/rpki/rpkid_tasks.py
index e101f1d1..a2545f90 100644
--- a/rpki/rpkid_tasks.py
+++ b/rpki/rpkid_tasks.py
@@ -209,10 +209,10 @@ class PollParentTask(AbstractTask):
ca.sia_uri = sia_uri
rc_resources = rpki.resource_set.resource_bag(
- rc.get("resource_set_as"),
- rc.get("resource_set_ipv4"),
- rc.get("resource_set_ipv6"),
- rc.get("resource_set_notafter"))
+ asn = rc.get("resource_set_as"),
+ v4 = rc.get("resource_set_ipv4"),
+ v6 = rc.get("resource_set_ipv6"),
+ valid_until = rc.get("resource_set_notafter"))
cert_map = {}
@@ -554,10 +554,10 @@ class UpdateEECertificatesTask(AbstractTask):
ees = existing.pop(gski, ())
resources = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(r_pdu.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(r_pdu.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(r_pdu.get("ipv6")),
- valid_until = rpki.sundial.datetime.fromXMLtime(r_pdu.get("valid_until")))
+ asn = r_pdu.get("asn"),
+ v4 = r_pdu.get("ipv4"),
+ v6 = r_pdu.get("ipv6"),
+ valid_until = r_pdu.get("valid_until"))
covering = self.tenant.find_covering_ca_details(resources)
ca_details.update(covering)
diff --git a/rpki/rpkidb/models.py b/rpki/rpkidb/models.py
index d21d7e2e..7cfed2e5 100644
--- a/rpki/rpkidb/models.py
+++ b/rpki/rpkidb/models.py
@@ -16,6 +16,7 @@ import tornado.httpserver
from django.db import models
import rpki.left_right
+import rpki.sundial
from rpki.fields import (EnumField, SundialField,
CertificateField, RSAPrivateKeyField,
@@ -515,7 +516,7 @@ class Repository(models.Model):
body = rpki.publication.cms_msg().wrap(q_msg, self.bsc.private_key_id,
self.bsc.signing_cert, self.bsc.signing_cert_crl),
headers = { "Content-Type" : rpki.publication.content_type },
- connect_timeout = rpkid.http_client_timeout,
+ connect_timeout = rpkid.http_client_timeout,
request_timeout = rpkid.http_client_timeout)
http_response = yield rpkid.http_fetch(http_request)
if http_response.headers.get("Content-Type") not in rpki.publication.allowed_content_types:
@@ -593,8 +594,8 @@ class Parent(Turtle):
@property
def rpki_root_cert(self):
try:
- ca_detail = self.root.cas.ca_details.get(state = "active")
- except (Root.DoesNotExist, CA.DoesNotExist, CADetail.DoesNotExist):
+ ca_detail = CADetail.objects.get(ca__turtle__root__parent = self, state = "active")
+ except (Root.DoesNotExist, CADetail.DoesNotExist):
return None
else:
return ca_detail.latest_ca_cert
@@ -610,10 +611,9 @@ class Parent(Turtle):
def root_asn_resources(self, value):
try:
self.root.asn_resources = value
- self.root.save()
except Root.DoesNotExist:
if value:
- Root.objects.create(parent = self, asn_resources = value)
+ self.root = Root(asn_resources = value)
@property
def root_ipv4_resources(self):
@@ -626,10 +626,9 @@ class Parent(Turtle):
def root_ipv4_resources(self, value):
try:
self.root.ipv4_resources = value
- self.root.save()
except Root.DoesNotExist:
if value:
- Root.objects.create(parent = self, ipv4_resources = value)
+ self.root = Root(ipv4_resources = value)
@property
def root_ipv6_resources(self):
@@ -642,10 +641,9 @@ class Parent(Turtle):
def root_ipv6_resources(self, value):
try:
self.root.ipv6_resources = value
- self.root.save()
except Root.DoesNotExist:
if value:
- Root.objects.create(parent = self, ipv6_resources = value)
+ self.root = Root(ipv6_resources = value)
@tornado.gen.coroutine
def xml_pre_delete_hook(self, rpkid):
@@ -655,6 +653,13 @@ class Parent(Turtle):
@tornado.gen.coroutine
def xml_post_save_hook(self, rpkid, q_pdu):
trace_call_chain()
+ try:
+ self.root.tenant = self.tenant
+ self.root.parent = self
+ self.root.repository = self.repository
+ self.root.save()
+ except Root.DoesNotExist:
+ pass
if q_pdu.get("clear_replay_protection"):
self.clear_replay_protection()
futures = []
@@ -765,8 +770,9 @@ class Parent(Turtle):
def _compose_up_down_query(self, query_type):
- return Element(rpki.up_down.tag_message, nsmap = rpki.up_down.nsmap, version = rpki.up_down.version,
- sender = self.sender_name, recipient = self.recipient_name, type = query_type)
+ return Element(rpki.up_down.tag_message, nsmap = rpki.up_down.nsmap,
+ version = rpki.up_down.version, type = query_type,
+ sender = self.sender_name, recipient = self.recipient_name)
@tornado.gen.coroutine
@@ -806,6 +812,9 @@ class Parent(Turtle):
@tornado.gen.coroutine
def query_up_down(self, rpkid, q_msg):
trace_call_chain()
+ if Root.objects.filter(parent = self).exists():
+ r_msg = yield self.query_up_down_root(rpkid, q_msg)
+ raise tornado.gen.Return(r_msg)
if self.bsc is None:
raise rpki.exceptions.BSCNotFound("Could not find BSC")
if self.bsc.signing_cert is None:
@@ -814,20 +823,231 @@ class Parent(Turtle):
url = self.peer_contact_uri,
method = "POST",
body = rpki.up_down.cms_msg().wrap(q_msg, self.bsc.private_key_id,
- self.bsc.signing_cert, self.bsc.signing_cert_crl),
+ self.bsc.signing_cert,
+ self.bsc.signing_cert_crl),
headers = { "Content-Type" : rpki.up_down.content_type },
- connect_timeout = rpkid.http_client_timeout,
+ connect_timeout = rpkid.http_client_timeout,
request_timeout = rpkid.http_client_timeout)
http_response = yield rpkid.http_fetch(http_request)
if http_response.headers.get("Content-Type") not in rpki.up_down.allowed_content_types:
raise rpki.exceptions.BadContentType("HTTP Content-Type %r, expected %r" % (
rpki.up_down.content_type, http_response.headers.get("Content-Type")))
r_cms = rpki.up_down.cms_msg(DER = http_response.body)
- r_msg = r_cms.unwrap((rpkid.bpki_ta, self.tenant.bpki_cert, self.tenant.bpki_glue, self.bpki_cert, self.bpki_glue))
+ r_msg = r_cms.unwrap((rpkid.bpki_ta,
+ self.tenant.bpki_cert, self.tenant.bpki_glue,
+ self.bpki_cert, self.bpki_glue))
r_cms.check_replay_sql(self, self.peer_contact_uri)
rpki.up_down.check_response(r_msg, q_msg.get("type"))
raise tornado.gen.Return(r_msg)
+ # Extracting TALs from a root is a little tricky. If we're
+ # willing to have rpkic know how rpkid constructs things,
+ # rpkic can piece it together from the sia_base value it got
+ # back in the OOB protocol (which, hmm, is probably
+ # rsync-only, may need to revisit that) and knowing that the
+ # root certificate will be in the top-directory of that tree
+ # with a g(SKI).cer filename which it can calculate from the
+ # certificate. rpkic probably needs to know some of this
+ # anyway if it's going to supply a https:// URI in the TAL.
+
+ @tornado.gen.coroutine
+ def query_up_down_root(self, rpkid, q_msg):
+ """
+ Internal RPKI root, divered from the normal up_down client.
+
+ While it looks a bit silly, the simplest way to drop this in
+ without rewriting all of the up-down client code is to
+ implement a minimal version of the server side of the up-down
+ protocol here, XML and all. This has the additional advantage
+ of using a well-defined protocol, one with a formal schema,
+ even. Yes, there's a bit of XML overhead, but we'd be paying
+ that in any case for an external root, so it's just a minor
+ optimization we've chosen not to take.
+
+ We do skip the CMS wrapper, though, since this is all internal
+ not just to a single Tenant but to a single Parent.
+ """
+
+ trace_call_chain()
+ publisher = rpki.rpkid.publication_queue(rpkid = rpkid)
+
+ r_msg = Element(rpki.up_down.tag_message,
+ nsmap = rpki.up_down.nsmap,
+ version = rpki.up_down.version,
+ sender = self.recipient_name,
+ recipient = self.sender_name)
+
+ # CA.parent_resource_class, CA.sia_uri
+ # CADetail.private_key_id, CADetail.public_key,
+ # CADetail.latest_ca_cert, CADetail.state, CADetail.ca_cert_uri
+ #
+ # CA.sia_uri + gski + ".cer" == CADetail.ca_cert_uri
+
+ if q_msg.get("type") == "revoke":
+ r_msg.set("type", "revoke_response")
+ try:
+ parent_ca_detail = CADetail.objects.get(
+ ca__turtle = self,
+ state__in = ("active", "deprecated"),
+ ca__parent_resource_class = q_msg[0].get("class_name"),
+ ca_cert_uri__endswith = q_msg[0].get("ski") + ".cer")
+ old_cert = parent_ca_detail.latest_ca_cert.certificate
+ old_uri = parent_ca_detail.ca_cert_uri
+ root_ca_detail = CADetail.objects.get(
+ ca__turtle = self.root,
+ state = "active")
+ RevokedCert.revoke(cert = old_cert, ca_detail = root_ca_detail)
+ publisher.queue(uri = old_uri, old_obj = old_cert, repository = self.repository)
+ root_ca_detail.generate_crl_and_manifest(publisher = publisher)
+ yield publisher.call_pubd()
+ SubElement(r_msg, rpki.up_down.tag_key,
+ class_name = q_msg[0].get("class_name"),
+ ski = q_msg[0].get("ski"))
+ except CA.DoesNotExist:
+ r_msg.set("type", "error_response")
+ SubElement(r_msg, rpki.up_down.tag_status).text = "1301"
+ except CADetail.DoesNotExist:
+ r_msg.set("type", "error_response")
+ SubElement(r_msg, rpki.up_down.tag_status).text = "1302"
+ except:
+ r_msg.set("type", "error_response")
+ SubElement(r_msg, rpki.up_down.tag_status).text = "2001"
+ raise tornado.gen.Return(r_msg)
+
+ # I can think of no sane reason why we would ever have more
+ # than one root CA here, so code for one.
+
+ try:
+ root_ca = self.root.cas.get()
+
+ except CA.DoesNotExist:
+ root_ca = CA.objects.create(
+ turtle = self.root,
+ parent_resource_class = "root",
+ sia_uri = self.sia_base + "root/")
+ logger.debug("%r query_up_down_root() created new internal root CA %r", self, root_ca)
+
+ try:
+ root_ca_detail = root_ca.ca_details.get(state = "active")
+
+ except CADetail.DoesNotExist:
+ root_ca_detail = root_ca.create_detail()
+ logger.debug("%r query_up_down_root() created internal root CADetail %r",
+ self, root_ca_detail)
+ sia = (root_ca.sia_uri, root_ca_detail.manifest_uri,
+ None, self.repository.rrdp_notification_uri)
+ notAfter = rpki.sundial.now() + rpki.sundial.timedelta.parse(
+ rpkid.cfg.get("rpki-root-certificate-lifetime", "1y"))
+ bag = rpki.resource_set.resource_bag(
+ asn = self.root.asn_resources,
+ v4 = self.root.ipv4_resources,
+ v6 = self.root.ipv6_resources,
+ valid_until = notAfter)
+ root_cert = rpki.x509.X509.self_certify(
+ keypair = root_ca_detail.private_key_id,
+ subject_key = root_ca_detail.public_key,
+ serial = root_ca.next_serial_number(),
+ sia = sia,
+ notAfter = notAfter,
+ resources = bag)
+ root_uri = self.sia_base + root_cert.gSKI() + ".cer"
+ publisher.queue(
+ uri = root_uri,
+ new_obj = root_cert,
+ repository = self.repository)
+ yield publisher.call_pubd()
+ yield root_ca_detail.activate(
+ rpkid = rpkid,
+ ca = root_ca,
+ cert = root_cert,
+ uri = root_uri)
+ logger.debug("%r query_up_down_root() activated internal root CADetail %r",
+ self, root_ca_detail)
+
+ try:
+ bag = root_ca_detail.latest_ca_cert.get_3779resources()
+ rc = SubElement(
+ r_msg, rpki.up_down.tag_class,
+ class_name = root_ca.parent_resource_class,
+ cert_url = root_ca.ca_cert_uri,
+ resource_set_as = str(bag.asn),
+ resource_set_ipv4 = str(bag.v4),
+ resource_set_ipv6 = str(bag.v6),
+ resource_set_notafter = str(bag.valid_until))
+
+ if q_msg.get("type") == rpki.up_down.tag_list:
+ r_msg.set("type", "list_response")
+ for parent_ca_detail in CADetail.objects.filter(
+ ca__turtle = self,
+ state__in = ("active", "deprecated"),
+ ca__parent_resource_class = root_ca.parent_resource_class):
+ c = SubElement(rc, rpki.up_down.tag_certificate,
+ cert_url = parent_ca_detail.ca.ca_cert_uri)
+ c.text = parent_ca_detail.latest_ca_cert.get_Base64()
+
+ else:
+ assert q_msg.get("type") == "issue"
+ assert q_msg[0].get("class_name") == root_ca.parent_resource_class
+ r_msg.set("type", "issue_response")
+ pkcs10 = rpki.x509.PKCS10(Base64 = q_msg[0].text)
+ pkcs10_key = pkcs10.getPublicKey()
+ pkcs10_sia = pkcs10.get_SIA()
+
+ # There are other reasons why we might want to regenerate, but
+ # for the moment just focus on changed PKCS #10 (key, SIA), assume
+ # something in rpkid_tasks will handle the rest.
+ try:
+ parent_ca_detail = CADetail.objects.get(
+ ca__turtle = self,
+ ca__parent_resource_class = root_ca.parent_resource_class,
+ state = "active")
+ parent_cert = parent_ca_detail.latest_ca_cert
+ need_to_issue = (parent_ca_detail.public_key != pkcs10_key or
+ parent_cert.get_SIA() != pkcs10_sia)
+ except CADetail.DoesNotExist:
+ parent_ca_detail = None
+ need_to_issue = True
+
+ parent_uri = self.sia_base + "root"/ + pkcs10_key.gSKI() + ".cer"
+
+ if need_to_issue:
+ logger.debug("%r query_up_down_root() issuing new worker CADetail, old %r",
+ self, parent_ca_detail)
+ parent_cert = root_ca_detail.latest_ca_cert.certificate.issue(
+ keypair = root_ca_detail.private_key_id,
+ subject_key = pkcs10.key,
+ serial = root_ca.next_serial_number(),
+ sia = pkcs10_sia,
+ notAfter = bag.valid_until,
+ resources = bag)
+ publisher.queue(
+ uri = parent_uri,
+ new_obj = parent_cert,
+ repository = self.repository)
+ nextUpdate = rpki.sundial.now() + rpki.sundial.timedelta(
+ seconds = self.tenant.crl_interval)
+ root_ca_detail.generate_crl_and_manifest(
+ publisher = publisher,
+ nextUpdate = nextUpdate)
+ yield publisher.call_pubd()
+ logger.debug("%r query_up_down_root() issued new worker cert %r",
+ self, parent_cert)
+
+ SubElement(rc, rpki.up_down.tag_certificate,
+ cert_url = parent_uri).text = parent_cert.get_Base64()
+
+ SubElement(rc, rpki.up_down.tag_issuer).text = root_ca_detail.latest_ca_cert.get_Base64()
+ raise tornado.gen.Return(r_msg)
+
+ except tornado.gen.Return:
+ raise
+
+ except:
+ del r_msg[:]
+ r_msg.set("type", "error_response")
+ SubElement(r_msg, rpki.up_down.tag_status).text = "2001"
+
+ raise tornado.gen.Return(r_msg)
def construct_sia_uri(self, rc):
"""
@@ -851,12 +1071,18 @@ class Root(Turtle):
ipv6_resources = models.TextField()
parent = models.OneToOneField(Parent)
+ def __repr__(self):
+ try:
+ return "<Root: {!r}>".format(self.parent)
+ except:
+ return "<Root: Root object>"
+
class CA(models.Model):
last_crl_manifest_number = models.BigIntegerField(default = 1)
last_issued_sn = models.BigIntegerField(default = 1)
sia_uri = models.TextField(null = True)
- parent_resource_class = models.TextField(null = True) # Not sure this should allow NULL
+ parent_resource_class = models.TextField(null = True) # Not sure this should allow NULL
turtle = models.ForeignKey(Turtle, related_name = "cas")
# So it turns out that there's always a 1:1 mapping between the
@@ -1349,12 +1575,18 @@ class CADetail(models.Model):
nextUpdate = nextUpdate,
revokedCertificates = certlist)
+ # XXX
+ logger.debug("%r Generating manifest, child_certs_all(): %r", self, self.child_certs.all())
+
objs = [(self.crl_uri_tail, self.latest_crl)]
objs.extend((c.uri_tail, c.cert) for c in self.child_certs.all())
objs.extend((r.uri_tail, r.roa) for r in self.roas.filter(roa__isnull = False))
objs.extend((g.uri_tail, g.ghostbuster) for g in self.ghostbusters.all())
objs.extend((e.uri_tail, e.cert) for e in self.ee_certificates.all())
+ # XXX
+ logger.debug("%r Generating manifest, objs: %r", self, objs)
+
self.latest_manifest = rpki.x509.SignedManifest.build(
serial = crl_manifest_number,
thisUpdate = now,
@@ -1597,18 +1829,22 @@ class Child(models.Model):
req = q_msg[0]
assert req.tag == rpki.up_down.tag_request
- # Subsetting not yet implemented, this is the one place where we have to handle it, by reporting that we're lame.
+ # Subsetting not yet implemented, this is the one place where
+ # we have to handle it, by reporting that we're lame.
- if any(req.get(a) for a in ("req_resource_set_as", "req_resource_set_ipv4", "req_resource_set_ipv6")):
+ if any(req.get(a) for a in ("req_resource_set_as",
+ "req_resource_set_ipv4", "req_resource_set_ipv6")):
raise rpki.exceptions.NotImplementedYet("req_* attributes not implemented yet, sorry")
class_name = req.get("class_name")
pkcs10 = rpki.x509.PKCS10(Base64 = req.text)
pkcs10.check_valid_request_ca()
- ca_detail = CADetail.objects.get(ca__turtle__tenant = self.tenant, state = "active",
- ca__parent_resource_class = class_name)
+ ca_detail = CADetail.objects.get(ca__turtle__tenant = self.tenant,
+ ca__parent_resource_class = class_name,
+ state = "active")
- irdb_resources = yield rpkid.irdb_query_child_resources(self.tenant.tenant_handle, self.child_handle)
+ irdb_resources = yield rpkid.irdb_query_child_resources(self.tenant.tenant_handle,
+ self.child_handle)
if irdb_resources.valid_until < rpki.sundial.now():
raise rpki.exceptions.IRDBExpired("IRDB entry for child %s expired %s" % (
diff --git a/rpki/sundial.py b/rpki/sundial.py
index 1556d0bd..b788940d 100644
--- a/rpki/sundial.py
+++ b/rpki/sundial.py
@@ -235,7 +235,7 @@ class timedelta(pydatetime.timedelta):
Parse text into a timedelta object.
"""
- if not isinstance(arg, str):
+ if not isinstance(arg, (str, unicode)):
return cls(seconds = arg)
elif arg.isdigit():
return cls(seconds = int(arg))