aboutsummaryrefslogtreecommitdiff
path: root/scripts/convert-from-entitydb-to-sql.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/convert-from-entitydb-to-sql.py')
-rw-r--r--scripts/convert-from-entitydb-to-sql.py440
1 files changed, 440 insertions, 0 deletions
diff --git a/scripts/convert-from-entitydb-to-sql.py b/scripts/convert-from-entitydb-to-sql.py
new file mode 100644
index 00000000..95e8e985
--- /dev/null
+++ b/scripts/convert-from-entitydb-to-sql.py
@@ -0,0 +1,440 @@
+"""
+Merge XML entitydb and OpenSSL command-line BPKI into SQL IRDB.
+
+This is a work in progress, don't use it unless you really know what
+you're doing.
+
+$Id$
+
+Copyright (C) 2011 Internet Systems Consortium ("ISC")
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+"""
+
+import sys, os, time, getopt, glob, subprocess, base64
+import rpki.config, rpki.x509, rpki.relaxng, rpki.sundial
+from rpki.mysql_import import MySQLdb
+from lxml.etree import ElementTree
+
+if os.getlogin() != "sra":
+ sys.exit("I //said// this was a work in progress")
+
+cfg_file = "rpki.conf"
+entitydb = "entitydb"
+bpki = "bpki"
+copy_csv_data = True
+
+opts, argv = getopt.getopt(sys.argv[1:], "c:h?", ["config=", "help"])
+for o, a in opts:
+ if o in ("-h", "--help", "-?"):
+ print __doc__
+ sys.exit(0)
+ if o in ("-c", "--config"):
+ cfg_file = a
+if argv:
+ sys.exit("Unexpected arguments %s" % argv)
+
+cfg = rpki.config.parser(cfg_file)
+
+sql_database = cfg.get("sql-database", section = "irdbd")
+sql_username = cfg.get("sql-username", section = "irdbd")
+sql_password = cfg.get("sql-password", section = "irdbd")
+
+db = MySQLdb.connect(user = sql_username, db = sql_database, passwd = sql_password)
+cur = db.cursor()
+
+# Configure the Django model system
+
+from django.conf import settings
+
+settings.configure(
+ DATABASES = { "default" : {
+ "ENGINE" : "django.db.backends.mysql",
+ "NAME" : sql_database,
+ "USER" : sql_username,
+ "PASSWORD" : sql_password,
+ "HOST" : "",
+ "PORT" : "",
+ "OPTIONS" : { "init_command": "SET storage_engine=INNODB" }}},
+ INSTALLED_APPS = ("rpki.irdb",),
+)
+
+import rpki.irdb
+
+# Create the model-based tables if they don't already exist
+
+import django.core.management
+
+django.core.management.call_command("syncdb", verbosity = 4, load_initial_data = False)
+
+# From here down will be an awful lot of messing about with XML and
+# X.509 data, extracting stuff from the old SQL database and whacking
+# it into the new. Still working out these bits.
+
+xmlns = "{http://www.hactrn.net/uris/rpki/myrpki/}"
+
+tag_authorization = xmlns + "authorization"
+tag_bpki_child_ta = xmlns + "bpki_child_ta"
+tag_bpki_client_ta = xmlns + "bpki_client_ta"
+tag_bpki_resource_ta = xmlns + "bpki_resource_ta"
+tag_bpki_server_ta = xmlns + "bpki_server_ta"
+tag_bpki_ta = xmlns + "bpki_ta"
+tag_contact_info = xmlns + "contact_info"
+tag_identity = xmlns + "identity"
+tag_parent = xmlns + "parent"
+tag_repository = xmlns + "repository"
+
+e = ElementTree(file = os.path.join(entitydb, "identity.xml")).getroot()
+rpki.relaxng.myrpki.assertValid(e)
+assert e.tag == tag_identity
+
+self_handle = e.get("handle")
+assert self_handle == cfg.get("handle", section = "myrpki")
+
+# Some BPKI utillity routines
+
+def read_openssl_serial(filename):
+ f = open(filename, "r")
+ text = f.read()
+ f.close()
+ return int(text.strip(), 16)
+
+def get_or_create_ServerEE(issuer, purpose):
+ cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "servers", purpose + ".cer"))
+ key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", purpose + ".key"))
+ rpki.irdb.ServerEE.objects.get_or_create(
+ issuer = issuer,
+ purpose = purpose,
+ certificate = cer,
+ private_key = key)
+
+# Load BPKI CAs and directly certified EEs
+
+cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "resources", "ca.cer"))
+key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "resources", "ca.key"))
+crl = rpki.x509.CRL(Auto_file = os.path.join(bpki, "resources", "ca.crl"))
+serial = read_openssl_serial(os.path.join(bpki, "resources", "serial"))
+crl_number = read_openssl_serial(os.path.join(bpki, "resources", "crl_number"))
+
+resource_ca = rpki.irdb.ResourceHolderCA.objects.get_or_create(
+ handle = self_handle,
+ certificate = cer,
+ private_key = key,
+ latest_crl = crl,
+ next_serial = serial,
+ next_crl_number = crl_number,
+ last_crl_update = crl.getThisUpdate().to_sql(),
+ next_crl_update = crl.getNextUpdate().to_sql())[0]
+
+if os.path.exists(os.path.join(bpki, "resources", "referral.cer")):
+ cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "resources", "referral.cer"))
+ key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "resources", "referral.key"))
+ rpki.irdb.Referral.objects.get_or_create(
+ issuer = resource_ca,
+ certificate = cer,
+ private_key = key)
+
+run_rpkid = cfg.getboolean("run_rpkid", section = "myrpki")
+run_pubd = cfg.getboolean("run_pubd", section = "myrpki")
+run_rootd = cfg.getboolean("run_rootd", section = "myrpki")
+
+if run_rpkid or run_pubd:
+ cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "servers", "ca.cer"))
+ key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", "ca.key"))
+ crl = rpki.x509.CRL(Auto_file = os.path.join(bpki, "servers", "ca.crl"))
+ serial = read_openssl_serial(os.path.join(bpki, "servers", "serial"))
+ crl_number = read_openssl_serial(os.path.join(bpki, "servers", "crl_number"))
+ server_ca = rpki.irdb.ServerCA.objects.get_or_create(
+ certificate = cer,
+ private_key = key,
+ latest_crl = crl,
+ next_serial = serial,
+ next_crl_number = crl_number,
+ last_crl_update = crl.getThisUpdate().to_sql(),
+ next_crl_update = crl.getNextUpdate().to_sql())[0]
+ get_or_create_ServerEE(server_ca, "irbe")
+
+else:
+ server_ca = None
+
+if run_rpkid:
+ get_or_create_ServerEE(server_ca, "rpkid")
+ get_or_create_ServerEE(server_ca, "irdbd")
+
+if run_pubd:
+ get_or_create_ServerEE(server_ca, "pubd")
+
+# Certification model for rootd has changed. We can reuse the old
+# key, but we have to recertify under a different CA than previously.
+# Yes, we're pulling a key from the servers BPKI tree and certifying
+# it under the resource holder CA, that's part of the change.
+
+if run_rootd:
+ rpki.irdb.Rootd.objects.get_or_certify(
+ issuer = resource_ca,
+ service_uri = "http://localhost:%s/" % cfg.get("rootd_server_port", section = "myrpki"),
+ private_key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", "rootd.key")))
+
+# Load BSC certificates and requests. Yes, this currently wires in
+# exactly one BSC handle, "bsc". So does the old myrpki code. Ick.
+
+for fn in glob.iglob(os.path.join(bpki, "resources", "bsc.*.cer")):
+ rpki.irdb.BSC.objects.get_or_create(
+ issuer = resource_ca,
+ handle = "bsc",
+ certificate = rpki.x509.X509(Auto_file = fn),
+ pkcs10 = rpki.x509.PKCS10(Auto_file = fn[:-4] + ".req"))
+
+def xcert_hash(cert):
+ """
+ Generate the filename hash that myrpki would have generated for a
+ cross-certification. This is nasty, don't look.
+ """
+
+ cmd1 = ("openssl", "x509", "-noout", "-pubkey", "-subject")
+ cmd2 = ("openssl", "dgst", "-md5")
+
+ env = { "PATH" : os.environ["PATH"], "OPENSSL_CONF" : "/dev/null" }
+ p1 = subprocess.Popen(cmd1, env = env, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
+ p2 = subprocess.Popen(cmd2, env = env, stdin = p1.stdout, stdout = subprocess.PIPE)
+ p1.stdin.write(cert.get_PEM())
+ p1.stdin.close()
+ hash = p2.stdout.read()
+ if p1.wait() != 0:
+ raise subprocess.CalledProcessError(returncode = p1.returncode, cmd = cmd1)
+ if p2.wait() != 0:
+ raise subprocess.CalledProcessError(returncode = p2.returncode, cmd = cmd2)
+
+ hash = "".join(hash.split())
+ if hash.startswith("(stdin)="):
+ hash = hash[len("(stdin)="):]
+ return hash
+
+# Let's try keeping track of all the xcert filenames we use, so we can
+# list the ones we didn't.
+
+xcert_filenames = set(glob.iglob(os.path.join(bpki, "*", "xcert.*.cer")))
+
+# Scrape child data out of the entitydb.
+
+for filename in glob.iglob(os.path.join(entitydb, "children", "*.xml")):
+ child_handle = os.path.splitext(os.path.split(filename)[1])[0]
+
+ e = ElementTree(file = filename).getroot()
+ rpki.relaxng.myrpki.assertValid(e)
+ assert e.tag == tag_parent
+
+ ta = rpki.x509.X509(Base64 = e.findtext(tag_bpki_child_ta))
+ xcfn = os.path.join(bpki, "resources", "xcert.%s.cer" % xcert_hash(ta))
+ xcert_filenames.discard(xcfn)
+ xcert = rpki.x509.X509(Auto_file = xcfn)
+
+ cur.execute("""
+ SELECT registrant_id, valid_until FROM registrant
+ WHERE registry_handle = %s AND registrant_handle = %s
+ """, (self_handle, child_handle))
+ assert cur.rowcount == 1
+ registrant_id, valid_until = cur.fetchone()
+
+ valid_until = rpki.sundial.datetime.fromdatetime(valid_until)
+ if valid_until != rpki.sundial.datetime.fromXMLtime(e.get("valid_until")):
+ print "WARNING: valid_until dates in XML and SQL do not match for child", child_handle
+ print " SQL:", str(valid_until)
+ print " XML:", str(rpki.sundial.datetime.fromXMLtime(e.get("valid_until")))
+ print "Blundering onwards"
+
+ child = rpki.irdb.Child.objects.get_or_create(
+ handle = child_handle,
+ valid_until = valid_until.to_sql(),
+ ta = ta,
+ certificate = xcert,
+ issuer = resource_ca)[0]
+
+ if copy_csv_data:
+
+ cur.execute("""
+ SELECT start_as, end_as FROM registrant_asn WHERE registrant_id = %s
+ """, (registrant_id,))
+ for start_as, end_as in cur.fetchall():
+ rpki.irdb.ChildASN.objects.get_or_create(
+ start_as = start_as,
+ end_as = end_as,
+ child = child)
+
+ cur.execute("""
+ SELECT start_ip, end_ip, version FROM registrant_net WHERE registrant_id = %s
+ """, (registrant_id,))
+ for start_ip, end_ip, version in cur.fetchall():
+ rpki.irdb.ChildNet.objects.get_or_create(
+ start_ip = start_ip,
+ end_ip = end_ip,
+ version = version,
+ child = child)
+
+# Scrape parent data out of the entitydb.
+
+for filename in glob.iglob(os.path.join(entitydb, "parents", "*.xml")):
+ parent_handle = os.path.splitext(os.path.split(filename)[1])[0]
+
+ e = ElementTree(file = filename).getroot()
+ rpki.relaxng.myrpki.assertValid(e)
+ assert e.tag == tag_parent
+
+ if parent_handle == self_handle:
+ assert run_rootd
+ assert e.get("service_uri") == "http://localhost:%s/" % cfg.get("rootd_server_port", section = "myrpki")
+ continue
+
+ ta = rpki.x509.X509(Base64 = e.findtext(tag_bpki_resource_ta))
+ xcfn = os.path.join(bpki, "resources", "xcert.%s.cer" % xcert_hash(ta))
+ xcert_filenames.discard(xcfn)
+ xcert = rpki.x509.X509(Auto_file = xcfn)
+
+ r = e.find(tag_repository)
+ repository_type = r.get("type")
+ if repository_type == "referral":
+ a = r.find(tag_authorization)
+ referrer = a.get("referrer")
+ referral_authorization = base64.b64decode(a.text)
+ else:
+ referrer = None
+ referral_authorization = None
+
+ parent = rpki.irdb.Parent.objects.get_or_create(
+ handle = parent_handle,
+ parent_handle = e.get("parent_handle"),
+ child_handle = e.get("child_handle"),
+ ta = ta,
+ certificate = xcert,
+ service_uri = e.get("service_uri"),
+ repository_type = repository_type,
+ referrer = referrer,
+ referral_authorization = referral_authorization,
+ issuer = resource_ca)[0]
+
+ # While we have the parent object in hand, load any Ghostbuster
+ # entries specific to this parent.
+
+ if copy_csv_data:
+ cur.execute("""
+ SELECT vcard FROM ghostbuster_request
+ WHERE self_handle = %s AND parent_handle = %s
+ """, (self_handle, parent_handle))
+ for row in cur.fetchall():
+ rpki.irdb.GhostbusterRequest.objects.get_or_create(
+ issuer = resource_ca,
+ parent = parent,
+ vcard = row[0])
+
+# Scrape repository data out of the entitydb.
+
+for filename in glob.iglob(os.path.join(entitydb, "repositories", "*.xml")):
+ repository_handle = os.path.splitext(os.path.split(filename)[1])[0]
+
+ e = ElementTree(file = filename).getroot()
+ rpki.relaxng.myrpki.assertValid(e)
+ assert e.tag == tag_repository
+
+ if e.get("type") != "confirmed":
+ continue
+
+ ta = rpki.x509.X509(Base64 = e.findtext(tag_bpki_server_ta))
+ xcfn = os.path.join(bpki, "resources", "xcert.%s.cer" % xcert_hash(ta))
+ xcert_filenames.discard(xcfn)
+ xcert = rpki.x509.X509(Auto_file = xcfn)
+
+ parent_handle = e.get("parent_handle")
+ if parent_handle == self_handle:
+ turtle = resource_ca.rootd
+ else:
+ turtle = rpki.irdb.Parent.objects.get(handle = parent_handle, issuer = resource_ca)
+
+ rpki.irdb.Repository.objects.get_or_create(
+ handle = repository_handle,
+ client_handle = e.get("client_handle"),
+ ta = ta,
+ certificate = xcert,
+ service_uri = e.get("service_uri"),
+ sia_base = e.get("sia_base"),
+ turtle = turtle,
+ issuer = resource_ca)
+
+# Scrape client data out of the entitydb.
+
+for filename in glob.iglob(os.path.join(entitydb, "pubclients", "*.xml")):
+ client_handle = os.path.splitext(os.path.split(filename)[1])[0].replace(".", "/")
+
+ e = ElementTree(file = filename).getroot()
+ rpki.relaxng.myrpki.assertValid(e)
+ assert e.tag == tag_repository
+
+ assert e.get("type") == "confirmed"
+
+ ta = rpki.x509.X509(Base64 = e.findtext(tag_bpki_client_ta))
+ xcfn = os.path.join(bpki, "servers", "xcert.%s.cer" % xcert_hash(ta))
+ xcert_filenames.discard(xcfn)
+ xcert = rpki.x509.X509(Auto_file = xcfn)
+
+ rpki.irdb.Client.objects.get_or_create(
+ handle = client_handle,
+ ta = ta,
+ certificate = xcert,
+ issuer = server_ca,
+ sia_base = e.get("sia_base"))
+
+if copy_csv_data:
+
+ # Copy over any ROA requests
+
+ cur.execute("""
+ SELECT roa_request_id, asn FROM roa_request
+ WHERE roa_request_handle = %s
+ """, (self_handle,))
+ for roa_request_id, asn in cur.fetchall():
+ roa_request = rpki.irdb.ROARequest.objects.get_or_create(issuer = resource_ca, asn = asn)[0]
+ cur.execute("""
+ SELECT prefix, prefixlen, max_prefixlen, version FROM roa_request_prefix
+ WHERE roa_request_id = %s
+ """, (roa_request_id,))
+ for prefix, prefixlen, max_prefixlen, version in cur.fetchall():
+ rpki.irdb.ROARequestPrefix.objects.get_or_create(
+ roa_request = roa_request,
+ version = version,
+ prefix = prefix,
+ prefixlen = prefixlen,
+ max_prefixlen = max_prefixlen)
+
+ # Copy over any non-parent-specific Ghostbuster requests.
+
+ cur.execute("""
+ SELECT vcard FROM ghostbuster_request
+ WHERE self_handle = %s AND parent_handle IS NULL
+ """, (self_handle,))
+ for row in cur.fetchall():
+ rpki.irdb.GhostbusterRequest.objects.get_or_create(
+ issuer = resource_ca,
+ parent = None,
+ vcard = row[0])
+
+# List cross certifications we didn't use.
+
+if False:
+ for filename in sorted(xcert_filenames):
+ cer = rpki.x509.X509(Auto_file = filename)
+ #print "Unused cross-certificate:", filename, cer.getSubject()
+ print "Unused cross-certificate:", filename, cer.get_POW().pprint()
+
+# Done!
+
+cur.close()
+db.close()