aboutsummaryrefslogtreecommitdiff
path: root/rpkid.stable/rpki
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2009-08-28 19:58:08 +0000
committerRob Austein <sra@hactrn.net>2009-08-28 19:58:08 +0000
commit9f4bc84a8be52b5fc838f2bf52d37bd8d6ecd112 (patch)
tree9d5aca21ff0dc1a92210e785a783225f55dd1ee4 /rpkid.stable/rpki
parent65dd0c1eebf86559508ac24a8200fa4194dec8c0 (diff)
Clean up old "stable" branch that is now badly out of date
svn path=/rpkid.stable; revision=2714
Diffstat (limited to 'rpkid.stable/rpki')
-rw-r--r--rpkid.stable/rpki/__init__.py1992
-rw-r--r--rpkid.stable/rpki/config.py56
-rw-r--r--rpkid.stable/rpki/exceptions.py135
-rw-r--r--rpkid.stable/rpki/https.py291
-rw-r--r--rpkid.stable/rpki/ipaddrs.py103
-rw-r--r--rpkid.stable/rpki/left_right.py833
-rw-r--r--rpkid.stable/rpki/log.py58
-rw-r--r--rpkid.stable/rpki/manifest.py53
-rw-r--r--rpkid.stable/rpki/oids.py57
-rw-r--r--rpkid.stable/rpki/publication.py282
-rw-r--r--rpkid.stable/rpki/relaxng.py1699
-rw-r--r--rpkid.stable/rpki/resource_set.py795
-rw-r--r--rpkid.stable/rpki/roa.py75
-rw-r--r--rpkid.stable/rpki/rpki_engine.py819
-rw-r--r--rpkid.stable/rpki/sql.py295
-rw-r--r--rpkid.stable/rpki/sundial.py198
-rw-r--r--rpkid.stable/rpki/up_down.py535
-rw-r--r--rpkid.stable/rpki/x509.py995
-rw-r--r--rpkid.stable/rpki/xml_utils.py317
19 files changed, 0 insertions, 9588 deletions
diff --git a/rpkid.stable/rpki/__init__.py b/rpkid.stable/rpki/__init__.py
deleted file mode 100644
index 1ac55ca3..00000000
--- a/rpkid.stable/rpki/__init__.py
+++ /dev/null
@@ -1,1992 +0,0 @@
-# $Id$
-
-# Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-#
-# 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 ARIN DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ARIN 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.
-
-# This file exists to tell Python that this the content of this
-# directory constitute a Python package. Since we're not doing
-# anything exotic, this file doesn't need to contain any code, but
-# since its existance defines the package, it's as sensible a place as
-# any to put the Doxygen mainpage.
-
-# The "usage" text for irbe_cli in the OPERATIONS section is generated
-# automatically by running the program with its --help command.
-# Should do the same with the other programs. Don't yet have a sane
-# way to automate options in config files, though. Would be nice.
-
-## @mainpage RPKI Engine Reference Manual
-#
-# This collection of Python modules implements a prototype of the
-# RPKI Engine. This is a work in progress.
-#
-# See http://viewvc.hactrn.net/subvert-rpki.hactrn.net/ for code,
-# design documents, a text mirror of portions of APNIC's Wiki, etc.
-#
-# The documentation you're reading is generated automatically by
-# Doxygen from comments and documentation in
-# <a href="http://viewvc.hactrn.net/subvert-rpki.hactrn.net/rpkid/rpki/">the code</a>.
-#
-# Besides the automatically-generated code documentation, this manual
-# also includes documentation of the overall package:
-#
-# @li The @subpage Installation "installation instructions"
-# @li The @subpage Operation "operation instructions"
-# @li A description of the @subpage Left-right "left-right protocol"
-# @li A description of the @subpage Publication "publication protocol"
-# @li A description of the @subpage bpki-model "BPKI model"
-# used to secure the up-down, left-right, and %publication protocols
-# @li A description of the several @subpage sql-schemas "SQL database schemas"
-# @li Some suggestions for @subpage further-reading "further reading"
-#
-# This work has been funded by <a
-# href="http://www.arin.net/">ARIN</a>, in collaboration with the
-# other Regional Internet Registries.
-
-## @page further-reading Further Reading
-#
-# If you're interested in this package you might also be interested
-# in:
-#
-# @li <a href="http://viewvc.hactrn.net/subvert-rpki.hactrn.net/rcynic/">The rcynic validation tool</a>
-# @li <a href="http://www.hactrn.net/opaque/rcynic.html">A live sample of rcynic's summary output</a>
-# @li <a href="http://mirin.apnic.net/resourcecerts/wiki/">APNIC's Wiki</a>
-# @li <a href="http://mirin.apnic.net/trac/">APNIC's project Trac instance</a>
-
-## @page Installation Installation Guide
-#
-# Preliminary installation instructions for rpkid et al. These are the
-# production-side RPKI tools, for Internet Registries (RIRs, LIRs, etc).
-# See the "rcynic" program for relying party tools.
-#
-# rpkid is a set of Python modules supporting generation and maintenance
-# of resource certificates. Most of the code is in the rpkid/rpki/
-# directory. rpkid itself is a relatively small program that calls the
-# library modules. There are several other programs that make use of
-# the same libraries, as well as a collection of test programs.
-#
-# At present the package is intended to be run out of its build
-# directory. Setting up proper installation in a system area using the
-# Python distutils package would likely not be very hard but has not yet
-# been done.
-#
-# Note that initial development of this code has been on FreeBSD, so
-# installation will probably be easiest on FreeBSD.
-#
-# Before attempting to build the package, you need to install any
-# missing prerequisites. Note that the Python code requires Python
-# version 2.5. rpkid et al are mostly self-contained, but do require
-# a small number of external packages to run.
-#
-# <ul>
-# <li>
-# <a href="http://codespeak.net/lxml/">http://codespeak.net/lxml/</a>.
-# lxml in turn requires the Gnome LibXML2 C libraries.
-# <ul>
-# <li>FreeBSD: /usr/ports/devel/py-lxml</li>
-# <li>Fedora: python-lxml.i386</li>
-# </ul>
-# </li>
-# <li>
-# <a href="http://sourceforge.net/projects/mysql-python/">http://sourceforge.net/projects/mysql-python/</a>.
-# MySQLdb in turn requires MySQL client and server. rpkid et al have
-# been tested with MySQL 5.0 and 5.1.
-# <ul>
-# <li>FreeBSD: /usr/ports/databases/py-MySQLdb</li>
-# <li>Fedora: MySQL-python.i386</li>
-# </ul>
-# </li>
-# <li>
-# <a href="http://trevp.net/tlslite/">http://trevp.net/tlslite/</a>.
-# TLSLite pulls in other crypto packages.
-# <ul>
-# <li>FreeBSD: /usr/ports/security/py-tlslite</li>
-# </ul>
-# </li>
-# </ul>
-#
-# rpkid et al also make heavy use of a modified copy of the Python
-# OpenSSL Wrappers (POW) package, but this copy has enough modifications
-# and additions that it's included in the subversion tree.
-#
-# The next step is to build the OpenSSL and POW binaries. At present
-# the OpenSSL code is just a copy of the stock OpenSSL 0.9.8g release,
-# compiled with special options to enable RFC 3779 support that ISC
-# wrote under previous contract to ARIN. The POW (Python OpenSSL
-# Wrapper) library is an extended copy of the stock POW release.
-#
-# To build these, cd to the top-level directory in the distribution and
-# type "make".
-#
-# @verbatim
-# $ cd $top
-# $ make
-# @endverbatim
-#
-# This should automatically build everything, in the right order,
-# including staticly linking the POW extension module with the OpenSSL
-# library to provide RFC 3779 support.
-#
-# You will also need a MySQL installation. This code was developed
-# using MySQL 5.1 and has been tested with MySQL 5.0 and 5.1.
-#
-# The architecture is intended to support hardware signing modules
-# (HSMs), but the code to support them has not been written.
-#
-# At this point, you should have all the necessary software installed.
-# You will probably want to test it. All tests should be run from the
-# rpkid/ directory. The test suite requires a few more external
-# packages, only one of which is Python code.
-#
-# <ul>
-# <li>
-# <a href="http://pyyaml.org/">http://pyyaml.org/</a>.
-# testpoke.py (an up-down protocol command line test client) and
-# testbed.py (a test harness) use PyYAML.
-# <ul>
-# <li>FreeBSD: /usr/ports/devel/py-yaml</li>
-# </ul>
-# </li>
-# <li>
-# <a href="http://xmlsoft.org/XSLT/">http://xmlsoft.org/XSLT/</a>.
-# Some of the test code uses xsltproc, from the Gnome LibXSLT
-# package.
-# <ul>
-# <li>FreeBSD: /usr/ports/textproc/libxslt</li>
-# </ul>
-# </li>
-# <li>
-# <a href="http://w3m.sourceforge.net/">http://w3m.sourceforge.net/</a>.
-# testbed.py uses w3m to display the summary output from rcynic.
-# Nothing terrible will happen if w3m isn't available, testbed.py
-# will just complain about it being missing and won't display
-# rcynic's output.
-# <ul>
-# <li>FreeBSD: /usr/ports/www/w3m</li>
-# </ul>
-# </li>
-# </ul>
-#
-# Some of the tests require MySQL databases to store their data. To set
-# up all the databases that the tests will need, run the SQL commands in
-# rpkid/testbed.sql. The MySQL command line client is usually the
-# easiest way to do this, eg:
-#
-# @verbatim
-# $ cd $top/rpkid
-# $ mysql -u root -p <testbed.sql
-# @endverbatim
-#
-# To run the tests, run "make all-tests":
-#
-# @verbatim
-# $ cd $top/rpkid
-# $ make all-tests
-# @endverbatim
-#
-# If nothing explodes, your installation is probably ok. Any Python
-# backtraces in the output indicate a problem.
-#
-# There's a last set of tools that only developers should need, as
-# they're only used when modifying schemas or regenerating the
-# documentation. These tools are listed here for completeness.
-#
-# <ul>
-# <li>
-# <a href="http://www.doxygen.org/">http://www.doxygen.org/</a>.
-# Doxygen in turn pulls in several other tools, notably Graphviz,
-# pdfLaTeX, and Ghostscript.
-# <ul>
-# <li>FreeBSD: /usr/ports/devel/doxygen</li>
-# </ul>
-# </li>
-# <li>
-# <a href="http://lynx.isc.org/current/">http://lynx.isc.org/current/</a>.
-# The documentation build process uses xsltproc and Lynx to dump
-# flat text versions of a few critical documentation pages.
-# <ul>
-# <li>FreeBSD: /usr/ports/www/lynx</li>
-# </ul>
-# </li>
-# <li>
-# <a href="http://www.thaiopensource.com/relaxng/trang.html">http://www.thaiopensource.com/relaxng/trang.html</a>.
-# Trang is used to convert RelaxNG schemas from the human-readable
-# "compact" form to the XML form that LibXML2 understands. Trang in
-# turn requires Java.
-# <ul>
-# <li>FreeBSD: /usr/ports/textproc/trang</li>
-# </ul>
-# </li>
-# <li>
-# <a href="http://search.cpan.org/dist/SQL-Translator/">http://search.cpan.org/dist/SQL-Translator/</a>.
-# SQL-Translator, also known as "SQL Fairy", includes code to parse
-# an SQL schema and dump a description of it as Graphviz input.
-# SQL Fairy in turn requires Perl.
-# </li>
-# </ul>
-
-## @page Operation Operation Guide
-#
-# Preliminary operation instructions for rpkid et al. These are the
-# production-side RPKI tools, for Internet Registries (RIRs, LIRs, etc).
-# See rcynic/README for relying party tools.
-#
-# @warning
-# rpkid is still in development, and the code changes more often than
-# the hand-maintained portions of this documentation. The following
-# text was reasonably accurate at the time it was written but may be
-# obsolete by the time you read it.
-#
-# At present the package is intended to be run out of the @c rpkid/
-# directory.
-#
-# In addition to the library routines in the @c rpkid/rpki/ directory,
-# the package includes the following programs:
-#
-# @li @c rpkid.py:
-# The main RPKI engine daemon.
-#
-# @li @c pubd.py:
-# The publication engine daemon.
-#
-# @li @c rootd.py:
-# A separate daemon for handling the root of an RPKI
-# certificate tree. This is essentially a stripped down
-# version of rpkid with no SQL database, no left-right
-# protocol implementation, and only the parent side of
-# the up-down protocol. It's separate because the root
-# is a special case in several ways and it was simpler
-# to keep the special cases out of the main daemon.
-#
-# @li @c irdbd.py:
-# A sample implementation of an IR database daemon.
-# rpkid calls into this to perform lookups via the
-# left-right protocol.
-#
-# @li @c irbe_cli.py:
-# A command-line client for the left-right control
-# protocol.
-#
-# @li @c cross_certify.py:
-# A BPKI cross-certification tool.
-#
-# @li @c irbe-setup.py:
-# An example of a script to set up the mappings between
-# the IRDB and rpkid's own database, using the
-# left-right control protocol.
-#
-# @li @c cronjob.py:
-# A trivial HTTP client used to drive rpkid cron events.
-#
-# @li @c testbed.py:
-# A test tool for running a collection of rpkid and irdb
-# instances under common control, driven by a unified
-# test script.
-#
-# @li @c testpoke.py:
-# A simple client for the up-down protocol, mostly
-# compatable with APNIC's rpki_poke.pl tool.
-#
-# Most of these programs take configuration files in a common format
-# similar to that used by the OpenSSL command line tool. The test
-# programs also take input in YAML format to drive the tests. Runs of
-# the testbed.py test tool will generate a fairly complete set
-# configuration files which may be useful as examples.
-#
-# Basic operation consists of creating the appropriate MySQL databases,
-# starting rpkid, pubd, rootd, and irdbd, using the left-right control
-# protocol to set up rpkid's internal state, and setting up a cron job
-# to invoke rpkid's cron action at regular intervals. All other
-# operations should occur either as a result of cron events or as a
-# result of incoming left-right and up-down protocol requests.
-#
-# Note that the full event-driven model for rpkid hasn't yet been
-# implemented. The design is intended to allow an arbitrary number of
-# hosted RPKI engines to run in a single rpkid instance, but without the
-# event-driven tasking model one must set up a separate rpkid instance
-# for each hosted RPKI engine.
-#
-# At present the daemon programs all run in foreground, that is, if one
-# wants them to run in background one must do so manually, eg, using
-# Bourne shell syntax:
-#
-# @verbatim
-# $ python whatever.py &
-# $ echo >whatever.pid "$!"
-# @endverbatim
-#
-# All of the daemons use syslog. At present they all set LOG_PERROR, so
-# all logging also goes to stderr.
-#
-#
-# @section rpkid rpkid.py
-#
-# rpkid is the main RPKI engine daemon. Configuration of rpkid is a
-# two step process: a %config file to bootstrap rpkid to the point
-# where it can speak using the @link Left-right left-right protocol,
-# @endlink followed by dynamic configuration via the left-right
-# protocol. In production use the latter stage would be handled by
-# the IRBE stub; for test and develoment purposes it's handled by the
-# irbe_cli.py command line interface or by the testbed.py test
-# framework.
-#
-# rpkid stores dynamic data in an SQL database, which must have been
-# created for it, as explained in the @link Installation installation
-# guide. @endlink
-#
-# The default %config file is rpkid.conf, start rpkid with "-c filename"
-# to choose a different %config file. All options are in the section
-# "[rpkid]". Certificates, keys, and trust anchors may be in either DER
-# or PEM format.
-#
-# %Config file options:
-#
-# @li @c startup-message:
-# String to %log on startup, useful when
-# debugging a collection of rpkid instances at
-# once.
-#
-# @li @c sql-username:
-# Username to hand to MySQL when connecting to
-# rpkid's database.
-#
-# @li @c sql-database:
-# MySQL's database name for rpkid's database.
-#
-# @li @c sql-password:
-# Password to hand to MySQL when connecting to
-# rpkid's database.
-#
-# @li @c bpki-ta:
-# Name of file containing BPKI trust anchor.
-# All BPKI certificate verification within rpkid
-# traces back to this trust anchor.
-#
-# @li @c rpkid-cert:
-# Name of file containing rpkid's own BPKI EE
-# certificate.
-#
-# @li @c rpkid-key:
-# Name of file containing RSA key corresponding
-# to rpkid-cert.
-#
-# @li @c irbe-cert:
-# Name of file containing BPKI certificate used
-# by IRBE when talking to rpkid.
-#
-# @li @c irdb-cert:
-# Name of file containing BPKI certificate used
-# by irdbd.
-#
-# @li @c irdb-url:
-# Service URL for irdbd. Must be a %https:// URL.
-#
-# @li @c server-host:
-# Hostname or IP address on which to listen for
-# HTTPS connections. Current default is
-# INADDR_ANY (IPv4 0.0.0.0); this will need to
-# be hacked to support IPv6 for production.
-#
-# @li @c server-port:
-# TCP port on which to listen for HTTPS
-# connections.
-#
-#
-# @section pubd pubd.py
-#
-# pubd is the publication daemon. It implements the server side of
-# the publication protocol, and is used by rpkid to publish the
-# certificates and other objects that rpkid generates.
-#
-# pubd is separate from rpkid for two reasons:
-#
-# @li The hosting model allows entities which choose to run their own
-# copies of rpkid to publish their output under a common
-# publication point. In general, encouraging shared publication
-# services where practical is a good thing for relying parties,
-# as it will speed up rcynic synchronization time.
-#
-# @li The publication server has to run on (or at least close to) the
-# publication point itself, which in turn must be on a publically
-# reachable server to be useful. rpkid, on the other hand, need
-# only be reachable by the IRBE and its children in the RPKI tree.
-# rpkid is a much more complex piece of software than pubd, so in
-# some situations it might make sense to wrap tighter firewall
-# constraints around rpkid than would be practical if rpkid and
-# pubd were a single program.
-#
-# pubd stores dynamic data in an SQL database, which must have been
-# created for it, as explained in the installation guide. pubd also
-# stores the published objects themselves as disk files in a
-# configurable location which should correspond to an appropriate
-# module definition in rsync.conf.
-#
-# The default %config file is pubd.conf, start pubd with "-c
-# filename" to choose a different %config file. ALl options are in
-# the section "[pubd]". Certifiates, keys, and trust anchors may be
-# either DER or PEM format.
-#
-# %Config file options:
-#
-# @li @c sql-username:
-# Username to hand to MySQL when connecting to
-# pubd's database.
-#
-# @li @c sql-database:
-# MySQL's database name for pubd's database.
-#
-# @li @c sql-password:
-# Password to hand to MySQL when connecting to
-# pubd's database.
-#
-# @li @c bpki-ta:
-# Name of file containing master BPKI trust
-# anchor for pubd. All BPKI validation in pubd
-# traces back to this trust anchor.
-#
-# @li @c irbe-cert:
-# Name of file containing BPKI certificate used
-# by IRBE when talking to pubd.
-#
-# @li @c pubd-cert:
-# Name of file containing BPKI certificate used
-# by pubd.
-#
-# @li @c pubd-key:
-# Name of file containing RSA key corresponding
-# to @c pubd-cert.
-#
-# @li @c server-host:
-# Hostname or IP address on which to listen for
-# HTTPS connections. Current default is
-# INADDR_ANY (IPv4 0.0.0.0); this will need to
-# be hacked to support IPv6 for production.
-#
-# @li @c server-port:
-# TCP port on which to listen for HTTPS
-# connections.
-#
-# @li @c publication-base:
-# Path to base of filesystem tree where pubd
-# should store publishable objects. Default is
-# "publication/".
-#
-#
-# @section rootd rootd.py
-#
-# rootd is a stripped down implmenetation of (only) the server side of
-# the up-down protocol. It's a separate program because the root
-# certificate of an RPKI certificate tree requires special handling and
-# may also require a special handling policy. rootd is a simple
-# implementation intended for test use, it's not suitable for use in a
-# production system. All configuration comes via the %config file.
-#
-# The default %config file is rootd.conf, start rootd with "-c filename"
-# to choose a different %config file. All options are in the section
-# "[rootd]". Certificates, keys, and trust anchors may be in either DER
-# or PEM format.
-#
-# %Config file options:
-#
-# @li @c bpki-ta:
-# Name of file containing BPKI trust anchor. All
-# BPKI certificate validation in rootd traces
-# back to this trust anchor.
-#
-# @li @c rootd-bpki-cert:
-# Name of file containing rootd's own BPKI
-# certificate.
-#
-# @li @c rootd-bpki-key:
-# Name of file containing RSA key corresponding to
-# rootd-bpki-cert.
-#
-# @li @c rootd-bpki-crl:
-# Name of file containing BPKI CRL that would
-# cover rootd-bpki-cert had it been revoked.
-#
-# @li @c child-bpki-cert:
-# Name of file containing BPKI certificate for
-# rootd's one and only child (RPKI engine to
-# which rootd issues an RPKI certificate).
-#
-# @li @c server-host:
-# Hostname or IP address on which to listen for
-# HTTPS connections. Default is localhost.
-#
-# @li @c server-port:
-# TCP port on which to listen for HTTPS
-# connections.
-#
-# @li @c rpki-root-key:
-# Name of file containing RSA key to use in
-# signing resource certificates.
-#
-# @li @c rpki-root-cert:
-# Name of file containing self-signed root
-# resource certificate corresponding to
-# rpki-root-key.
-#
-# @li @c rpki-root-dir:
-# Name of directory where rootd should write
-# RPKI subject certificate, manifest, and CRL.
-#
-# @li @c rpki-subject-cert:
-# Name of file that rootd should use to save the
-# one and only certificate it issues.
-# Default is "Subroot.cer".
-#
-# @li @c rpki-root-crl:
-# Name of file to which rootd should save its
-# RPKI CRL. Default is "Root.crl".
-#
-# @li @c rpki-root-manifest:
-# Name of file to which rootd should save its
-# RPKI manifest. Default is "Root.mnf".
-#
-# @li @c rpki-subject-pkcs10:
-# Name of file that rootd should use when saving
-# a copy of the received PKCS #10 request for a
-# resource certificate. This is only used for
-# debugging. Default is not to save the PKCS
-# #10 request.
-#
-#
-# @section irdbd irdbd.py
-#
-# irdbd is a sample implemntation of the server side of the IRDB
-# callback subset of the left-right protocol. In production use this
-# service is a function of the IRBE stub; irdbd may be suitable for
-# production use in simple cases, but an IR with a complex IRDB may need
-# to extend or rewrite irdbd.
-#
-# irdbd requires a pre-populated database to represent the IR's
-# customers. irdbd expects this database to use the SQL schema defined
-# in rpkid/irdbd.sql. Once this database has been populated, the
-# IRBE stub needs to create the appropriate objects in rpkid's database
-# via the control subset of the left-right protocol, and store the
-# linkage IDs (foreign keys into rpkid's database, basicly) in the
-# IRDB. The irbe-setup.py program shows an example of how to do this.
-#
-# irdbd's default %config file is irdbd.conf, start irdbd with "-c
-# filename" to choose a different %config file. All options are in the
-# section "[irdbd]". Certificates, keys, and trust anchors may be in
-# either DER or PEM format.
-#
-# %Config file options:
-#
-# @li @c startup-message:
-# String to %log on startup, useful when
-# debugging a collection of irdbd instances at
-# once.
-#
-# @li @c sql-username:
-# Username to hand to MySQL when connecting to
-# irdbd's database.
-#
-# @li @c sql-database:
-# MySQL's database name for irdbd's database.
-#
-# @li @c sql-password:
-# Password to hand to MySQL when connecting to
-# irdbd's database.
-#
-# @li @c bpki-ta:
-# Name of file containing BPKI trust anchor. All
-# BPKI certificate validation in irdbd traces
-# back to this trust anchor.
-#
-# @li @c irdbd-cert:
-# Name of file containing irdbd's own BPKI
-# certificate.
-#
-# @li @c irdbd-key:
-# Name of file containing RSA key corresponding
-# to irdbd-cert.
-#
-# @li @c rpkid-cert:
-# Name of file containing certificate used the
-# one and only by rpkid instance authorized to
-# contact this irdbd instance.
-#
-# @li @c https-url:
-# Service URL for irdbd. Must be a %https:// URL.
-#
-#
-# @section irdbd_cli irbe_cli.py
-#
-# irbe_cli is a simple command line client for the control subsets of
-# the @link Left-right left-right @endlink and @link Publication
-# publication @endlink protocols. In production use this
-# functionality would be part of the IRBE stub.
-#
-# Basic configuration of irbe_cli is handled via a %config file. The
-# specific action or actions to be performed are specified on the
-# command line, and map closely to the protocols themselves.
-#
-# At present the user is assumed to be able to read the (XML)
-# left-right and publication protocol messages, and with one
-# exception, irdbd-cli makes no attempt to interpret the responses
-# other than to check for signature and syntax errors. The one
-# exception is that, if the @c --pem_out option is specified on the
-# command line, any PKCS \#10 requests received from rpkid will be
-# written in PEM format to that file; this makes it easier to hand
-# these requests off to the business PKI (BPKI in order to issue signing
-# certs corresponding to newly generated business keys.
-#
-# @verbinclude irbe_cli.usage
-#
-# Global options (@c --config, @c --help, @c --pem_out) come first,
-# then zero or more commands (@c parent, @c repository, @c self, @c
-# child, @c route_origin, @c bsc, @c config, @c client), each followed
-# by its own set of options. The commands map to elements in the
-# protocols, and the command-specific options map to attributes or
-# subelements for those commands.
-#
-# @c --tag is an optional arbitrary tag (think IMAP) to simplify
-# matching up replies with batched queries.
-#
-# @c --*_id options refer to the primary keys of previously created
-# objects.
-#
-# The remaining options are specific to the particular commands, and
-# follow directly from the protocol specifications.
-#
-# A trailing "=" in the above option summary indicates that an option
-# takes a value, eg, "--action create" or "--action=create". Options
-# without a trailing "=" correspond to boolean control attributes.
-#
-# The default %config file for irbe_cli is irbe_cli.conf, start
-# irbe_cli with "-c filename" (or "--config filename") to choose a
-# different %config file. All options are in the section
-# "[irbe_cli]". Certificates, keys, and trust anchors may be in
-# either DER or PEM format.
-#
-# %Config file options:
-#
-# @li @c rpkid-bpki-ta:
-# Name of file containing BPKI trust anchor to
-# use when authenticating messages from rpkid.
-#
-# @li @c rpkid-irbe-cert:
-# Name of file containing BPKI certificate
-# irbe_cli should use when talking to rpkid.
-#
-# @li @c rpkid-irbe-key:
-# Name of file containing RSA key corresponding to
-# rpkid-irbe-cert.
-#
-# @li @c rpkid-cert:
-# Name of file containing rpkid's BPKI certificate.
-#
-# @li @c rpkid-url:
-# Service URL for rpkid. Must be a %https:// URL.
-#
-# @li @c pubd-bpki-ta:
-# Name of file containing BPKI trust anchor to
-# use when authenticating messages from pubd.
-#
-# @li @c pubd-irbe-cert:
-# Name of file containing BPKI certificate
-# irbe_cli should use when talking to pubd.
-#
-# @li @c pubd-irbe-key:
-# Name of file containing RSA key corresponding to
-# pubd-irbe-cert.
-#
-# @li @c pubd-cert:
-# Name of file containing pubd's BPKI certificate.
-#
-# @li @c pubd-url:
-# Service URL for pubd. Must be a %https:// URL.
-#
-#
-#
-# @section cross_certify cross_certify.py
-#
-# cross_certify.py is a small tool to extract certain fields from an
-# existing X.509 certificate and generate issue a new certificate that
-# can be used as part of a cross-certification chain. cross_certify
-# doesn't take a config file, all of its arguments are specified on
-# the command line.
-#
-# @verbatim
-# python cross_certify.py { -i | --in } input_cert
-# { -c | --ca } issuing_cert
-# { -k | --key } issuing_cert_key
-# { -s | --serial } serial_filename
-# [ { -h | --help } ]
-# [ { -o | --out } filename ]
-# [ { -l | --lifetime } timedelta ]
-# @endverbatim
-#
-#
-# @section irbe_setup irbe-setup.py config file
-#
-# @warning
-# irbe-setup is old code, not currently used, kept in case it is
-# useful at some later date. It may not work properly or at all. If
-# you don't understand what it does, you don't need it. You have been
-# warned.
-#
-# The default %config file is irbe.conf, start rpkid with "-c filename"
-# to choose a different %config file. Most options are in the section
-# "[irbe_cli]", but a few are in the section "[irdbd]". Certificates,
-# keys, and trust anchors may be in either DER or PEM format.
-#
-# Options in the "[irbe_cli]" section:
-#
-# @li @c bpki-ta:
-# Name of file containing BPKI trust anchor.
-#
-# @li @c irbe-cert:
-# Name of file containing BPKI certificate
-# irbe-setup should use.
-#
-# @li @c irbe-key:
-# Name of file containing RSA key corresponding
-# to irbe-cert.
-#
-# @li @c rpkid-cert:
-# Name of file containing rpkid's BPKI
-# certificate.
-#
-# @li @c https-url:
-# Service URL for rpkid. Must be a %https:// URL.
-#
-# Options in the "[irdbd]" section:
-#
-# @li @c sql-username:
-# Username to hand to MySQL when connecting to
-# irdbd's database.
-#
-# @li @c sql-database:
-# MySQL's database name for irdbd's database.
-#
-# @li @c sql-password:
-# Password to hand to MySQL when connecting to
-# irdbd's database.
-#
-#
-# @section cronjob cronjob.py
-#
-# This is a trivial program to trigger a cron run within rpkid. Once
-# rpkid has been converted to the planned event-driven model, this
-# function will be handled internally, but for now it has to be
-# triggered by an external program. For pseudo-production use one would
-# run this program under the system cron daemon. For scripted testing
-# it happens to be useful to be able to control when cron cycles occur,
-# so at the current stage of code development use of an external trigger
-# is a useful feature.
-#
-# The default %config file is cronjob.conf, start cronjob with "-c
-# filename" to choose a different %config file. All options are in the
-# section "[cronjob]". Certificates, keys, and trust anchors may be in
-# either DER or PEM format.
-#
-# %Config file options:
-#
-# @li @c bpki-ta:
-# Name of file containing BPKI trust anchor.
-#
-# @li @c irbe-cert:
-# Name of file containing cronjob.py's BPKI
-# certificate.
-#
-# @li @c https-key:
-# Name of file containing RSA key corresponding
-# to irbe-cert.
-#
-# @li @c rpkid-cert:
-# Name of file containing rpkid's BPKI certificate.
-#
-# @li @c https-url:
-# Service URL for rpkid. Must be a %https:// URL.
-#
-#
-# @section testbed testbed.py:
-#
-# testbed is a test harness to set up and run a collection of rpkid and
-# irdbd instances under scripted control. testbed is a very recent
-# addition to the toolset and is still evolving rapidly.
-#
-# Unlike the programs described above, testbed takes two configuration
-# files in different languages. The first configuration file uses the
-# same syntax as the above configuration files but is completely
-# optional. The second configuration file is the test script, which is
-# encoded using the YAML serialization language (see
-# http://www.yaml.org/ for more information on YAML). The YAML script
-# is not optional, as it describes the test layout. testbed is designed
-# to support running a fairly wide set of test configurations as canned
-# scripts without writing any new control code. The intent is to make
-# it possible to write meaningful regression tests.
-#
-# All of the options in in the first (optional) configuration file are
-# just overrides for wired-in default values. In most cases the
-# defaults will suffice, and the set of options is still in flux, so
-# only a few of the options are described here. The default name for
-# this configuration file is testbed.conf, run testbed with "-c
-# filename" to change it.
-#
-# testbed.conf options:
-#
-# @li @c testbed_dir:
-# Working directory into which testbed should write the
-# (many) files it generates. Default is "testbed.dir".
-#
-# @li @c irdb_db_pass:
-# MySQL password for the "irdb" user. Default is
-# "fnord". You may want to override this.
-#
-# @li @c rpki_db_pass:
-# MySQL password for the "rpki" user. Default is
-# "fnord". You may want to override this.
-#
-# @li @c rootd_sia:
-# rsync URI naming a (perhaps fictious) directory to use
-# as the id-ad-caRepository SIA value in the generated
-# root resource certificate. Default is
-# "rsync://wombat.invalid/". You may want to override
-# this if you intend to run an rsync server and test
-# against the generated results using rcynic. This
-# default will likely change if and when testbed learns
-# how to run rcynic itself as part of the test suite.
-#
-# The second configuration file is named testbed.yaml by default, run
-# testbed with "-y filename" to change it. The YAML file contains
-# multiple YAML "documents". The first document describes the initial
-# test layout and resource allocations, subsequent documents describe
-# modifications to the initial allocations and other parameters.
-# Resources listed in the initial layout are aggregated automatically,
-# so that a node in the resource hierarchy automatically receives the
-# resources it needs to issue whatever its children are listed as
-# holding. Actions in the subsequent documents are modifications to the
-# current resource set, modifications to validity dates or other
-# non-resource parameters, or special commands like "sleep". The
-# details are still evolving, but here's an example of current usage:
-#
-# @verbatim
-# name: RIR
-# valid_for: 2d
-# sia_base: "rsync://wombat.invalid/"
-# kids:
-# - name: LIR0
-# kids:
-# - name: Alice
-# ipv4: 192.0.2.1-192.0.2.33
-# asn: 64533
-# ---
-# - name: Alice
-# valid_add: 10
-# ---
-# - name: Alice
-# add_as: 33
-# valid_add: 2d
-# ---
-# - name: Alice
-# valid_sub: 2d
-# ---
-# - name: Alice
-# valid_for: 10d
-# @endverbatim
-#
-# This specifies an initial layout consisting of an RPKI engine named
-# "RIR", with one child "LIR0", which in turn has one child "Alice".
-# Alice has a set of assigned resources, and all resources in the system
-# are initially set to be valid for two days from the time at which the
-# test is started. The first subsequent document adds ten seconds to
-# the validity interval for Alice's resources and makes no other
-# modifications. The second subsequent document grants Alice additional
-# resources and adds another two days to the validity interval for
-# Alice's resources. The next document subtracts two days from the
-# validity interval for Alice's resources. The final document sets the
-# validity interval for Alice's resources to ten days.
-#
-# Operators in subsequent (update) documents:
-#
-# @li @c add_as, @c add_v4, @c add_v6:
-# These add ASN, IPv4, or IPv6 resources, respectively.
-#
-# @li @c sub_as, @c sub_v4, @c sub_v6:
-# These subtract resources.
-#
-# @li @c valid_until:
-# Set an absolute expiration date.
-#
-# @li @c valid_for:
-# Set a relative expiration date.
-#
-# @li @c valid_add, @c valid_sub:
-# Add to or subtract from validity interval.
-#
-# @li @c sleep [interval]:
-# Sleep for specified interval, or until testbed receives a SIGALRM signal.
-#
-# Absolute timestamps should be in the form shown (UTC timestamp format
-# as used in XML).
-#
-# Intervals (@c valid_add, @c valid_sub, @c valid_for, @c sleep) are either
-# integers, in which case they're interpreted as seconds, or are a
-# string of the form "wD xH yM zS" where w, x, y, and z are integers and
-# D, H, M, and S indicate days, hours, minutes, and seconds. In the
-# latter case all of the fields are optional, but at least one must be
-# specified. For example, "3D4H" means "three days plus four hours".
-#
-#
-# @section testpoke testpoke.py
-#
-# This is a command-line client for the up-down protocol. Unlike all of
-# the above programs, testpoke does not accept a %config file in
-# OpenSSL-compatable format at all. Instead, it is configured
-# exclusively by a YAML script. testpoke's design was constrained by a
-# desire to have it be compatable with APNIC's rpki_poke.pl tool, so
-# that the two tools could use a common configuration language to
-# simplify scripted testing. There are minor variations due to slightly
-# different feature sets, but YAML files intended for one program will
-# usually work with the other.
-#
-# README for APNIC's tool describing the input language can be found at
-# <a href="http://mirin.apnic.net/svn/rpki_engine/branches/gary-poker/client/poke/README">
-# http://mirin.apnic.net/svn/rpki_engine/branches/gary-poker/client/poke/README</a>.
-#
-# testpoke.py takes a simplified command line and uses only one YAML
-# input file.
-#
-# @verbatim
-# Usage: python testpoke.py [ { -y | --yaml } configfile ]
-# [ { -r | --request } requestname ]
-# [ { -h | --help } ]
-# @endverbatim
-#
-# Default configuration file is testpoke.yaml, override with --yaml
-# option.
-#
-# The --request option specifies the specific command within the YAML
-# file to execute.
-#
-# Sample configuration file:
-#
-# @verbatim
-# ---
-# # Sample YAML configuration file for testpoke.py
-#
-# version: 1
-# posturl: https://localhost:4433/up-down/1
-# recipient-id: wombat
-# sender-id: "1"
-#
-# cms-cert-file: biz-certs/Frank-EE.cer
-# cms-key-file: biz-certs/Frank-EE.key
-# cms-ca-cert-file: biz-certs/Bob-Root.cer
-# cms-cert-chain-file: [ biz-certs/Frank-CA.cer ]
-#
-# ssl-cert-file: biz-certs/Frank-EE.cer
-# ssl-key-file: biz-certs/Frank-EE.key
-# ssl-ca-cert-file: biz-certs/Bob-Root.cer
-#
-# requests:
-# list:
-# type: list
-# issue:
-# type: issue
-# class: 1
-# sia: [ "rsync://bandicoot.invalid/some/where/" ]
-# revoke:
-# type: revoke
-# class: 1
-# ski: "CB5K6APY-4KcGAW9jaK_cVPXKX0"
-# @endverbatim
-#
-# testpoke adds one extension to the language described in APNIC's
-# README: the cms-cert-chain-* and ssl-cert-chain-* options, which allow
-# one to specify a chain of intermediate certificates to be presented in
-# the CMS or TLS protocol. APNIC's initial implementation required
-# direct knowledge of the issuing certificate (ie, it supported a
-# maximum chain length of one); subsequent APNIC code changes have
-# probably relaxed this restriction, and with luck APNIC has copied
-# testpoke's syntax to express chains of intermediate certificates.
-
-## @page Left-right Left-right protocol
-#
-# The left-right protocol is really two separate client/server
-# protocols over separate channels between the RPKI engine and the IR
-# back end (IRBE). The IRBE is the client for one of the
-# subprotocols, the RPKI engine is the client for the other.
-#
-# @section Terminology
-#
-# @li @em IRBE: Internet Registry Back End
-#
-# @li @em IRDB: Internet Registry Data Base
-#
-# @li @em BPKI: Business PKI
-#
-# @li @em RPKI: Resource PKI
-#
-# @section Operations initiated by the IRBE
-#
-# This part of the protcol uses a kind of message-passing. Each %object
-# that the RPKI engine knows about takes five messages: "create", "set",
-# "get", "list", and "destroy". Actions which are not just data
-# operations on %objects are handled via an SNMP-like mechanism, as if
-# they were fields to be set. For example, to generate a keypair one
-# "sets" the "generate-keypair" field of a BSC %object, even though there
-# is no such field in the %object itself as stored in SQL. This is a bit
-# of a kludge, but the reason for doing it as if these were variables
-# being set is to allow composite operations such as creating a BSC,
-# populating all of its data fields, and generating a keypair, all as a
-# single operation. With this model, that's trivial, otherwise it's at
-# least two round trips.
-#
-# Fields can be set in either "create" or "set" operations, the
-# difference just being whether the %object already exists. A "get"
-# operation returns all visible fields of the %object. A "list"
-# operation returns a %list containing what "get" would have returned on
-# each of those %objects.
-#
-# Left-right protocol %objects are encoded as signed CMS messages
-# containing XML as eContent and using an eContentType OID of @c id-ct-xml
-# (1.2.840.113549.1.9.16.1.28). These CMS messages are in turn passed
-# as the data for HTTPS POST operations, with an HTTP content type of
-# "application/x-rpki" for both the POST data and the response data.
-#
-# All operations allow an optional "tag" attribute which can be any
-# alphanumeric token. The main purpose of the tag attribute is to allow
-# batching of multiple requests into a single PDU.
-#
-# @subsection self_obj <self/> object
-#
-# A @c &lt;self/&gt; %object represents one virtual RPKI engine. In simple cases
-# where the RPKI engine operator operates the engine only on their own
-# behalf, there will only be one @c &lt;self/&gt; %object, representing the engine
-# operator's organization, but in environments where the engine operator
-# hosts other entities, there will be one @c @c &lt;self/&gt; %object per hosted
-# entity (probably including the engine operator's own organization,
-# considered as a hosted customer of itself).
-#
-# Some of the RPKI engine's configured parameters and data are shared by
-# all hosted entities, but most are tied to a specific @c &lt;self/&gt; %object.
-# Data which are shared by all hosted entities are referred to as
-# "per-engine" data, data which are specific to a particular @c &lt;self/&gt;
-# %object are "per-self" data.
-#
-# Since all other RPKI engine %objects refer to a @c &lt;self/&gt; %object via a
-# "self_id" value, one must create a @c &lt;self/&gt; %object before one can
-# usefully configure any other left-right protocol %objects.
-#
-# Every @c &lt;self/&gt; %object has a self_id attribute, which must be specified
-# for the "set", "get", and "destroy" actions.
-#
-# Payload data which can be configured in a @c &lt;self/&gt; %object:
-#
-# @li @c use_hsm (attribute):
-# Whether to use a Hardware Signing Module. At present this option
-# has no effect, as the implementation does not yet support HSMs.
-#
-# @li @c crl_interval (attribute):
-# Positive integer representing the planned lifetime of an RPKI CRL
-# for this @c &lt;self/&gt;, measured in seconds.
-#
-# @li @c regen_margin (attribute):
-# Positive integer representing how long before expiration of an
-# RPKI certificiate a new one should be generated, measured in
-# seconds. At present this only affects the one-off EE certificates
-# associated with ROAs.
-#
-# @li @c bpki_cert (element):
-# BPKI CA certificate for this @c &lt;self/&gt;. This is used as part of the
-# certificate chain when validating incoming TLS and CMS messages,
-# and should be the issuer of cross-certification BPKI certificates
-# used in @c &lt;repository/&gt;, @c &lt;parent/&gt;, and @c &lt;child/&gt; %objects. If the
-# bpki_glue certificate is in use (below), the bpki_cert certificate
-# should be issued by the bpki_glue certificate; otherwise, the
-# bpki_cert certificate should be issued by the per-engine bpki_ta
-# certificate.
-#
-# @li @c bpki_glue (element):
-# Another BPKI CA certificate for this @c &lt;self/&gt;, usually not needed.
-# Certain pathological cross-certification cases require a
-# two-certificate chain due to issuer name conflicts. If used, the
-# bpki_glue certificate should be the issuer of the bpki_cert
-# certificate and should be issued by the per-engine bpki_ta
-# certificate; if not needed, the bpki_glue certificate should be
-# left unset.
-#
-# Control attributes that can be set to "yes" to force actions:
-#
-# @li @c rekey:
-# Start a key rollover for every RPKI CA associated with every
-# @c &lt;parent/&gt; %object associated with this @c &lt;self/&gt; %object. This is the
-# first phase of a key rollover operation.
-#
-# @li @c revoke:
-# Revoke any remaining certificates for any expired key associated
-# with any RPKI CA for any @c &lt;parent/&gt; %object associated with this
-# @c &lt;self/&gt; %object. This is the second (cleanup) phase for a key
-# rollover operation; it's separate from the first phase to leave
-# time for new RPKI certificates to propegate and be installed.
-#
-# @li @c reissue:
-# Not implemented, may be removed from protocol. Original theory
-# was that this operation would force reissuance of any %object with
-# a changed key, but as that happens automatically as part of the
-# key rollover mechanism this operation seems unnecessary.
-#
-# @li @c run_now:
-# Force immediate processing for all tasks associated with this
-# @c &lt;self/&gt; %object that would ordinarily be performed under cron. Not
-# currently implemented.
-#
-# @li @c publish_world_now:
-# Force (re)publication of every publishable %object for this @c &lt;self/&gt;
-# %object. Not currently implemented. Intended to aid in recovery
-# if RPKI engine and publication engine somehow get out of sync.
-#
-#
-# @subsection bsc_obj <bsc/> object
-#
-# The @c &lt;bsc/&gt; ("business signing context") %object represents all the BPKI
-# data needed to sign outgoing CMS or HTTPS messages. Various other
-# %objects include pointers to a @c &lt;bsc/&gt; %object. Whether a particular
-# @c &lt;self/&gt; uses only one @c &lt;bsc/&gt; or multiple is a configuration decision
-# based on external requirements: the RPKI engine code doesn't care, it
-# just cares that, for any %object representing a relationship for which
-# it must sign messages, there be a @c &lt;bsc/&gt; %object that it can use to
-# produce that signature.
-#
-# Every @c &lt;bsc/&gt; %object has a bsc_id, which must be specified for the
-# "get", "set", and "destroy" actions. Every @c &lt;bsc/&gt; also has a self_id
-# attribute which indicates the @c &lt;self/&gt; %object with which this @c &lt;bsc/&gt;
-# %object is associated.
-#
-# Payload data which can be configured in a @c &lt;isc/&gt; %object:
-#
-# @li @c signing_cert (element):
-# BPKI certificate to use when generating a signature.
-#
-# @li @c signing_cert_crl (element):
-# CRL which would %list signing_cert if it had been revoked.
-#
-# Control attributes that can be set to "yes" to force actions:
-#
-# @li @c generate_keypair:
-# Generate a new BPKI keypair and return a PKCS #10 certificate
-# request. The resulting certificate, once issued, should be
-# configured as this @c &lt;bsc/&gt; %object's signing_cert.
-#
-# Additional attributes which may be specified when specifying
-# "generate_keypair":
-#
-# @li @c key_type:
-# Type of BPKI keypair to generate. "rsa" is both the default and,
-# at the moment, the only allowed value.
-#
-# @li @c hash_alg:
-# Cryptographic hash algorithm to use with this keypair. "sha256"
-# is both the default and, at the moment, the only allowed value.
-#
-# @li @c key_length:
-# Length in bits of the keypair to be generated. "2048" is both the
-# default and, at the moment, the only allowed value.
-#
-# Replies to "create" and "set" actions that specify "generate-keypair"
-# include a &lt;bsc_pkcs10/> element, as do replies to "get" and "list"
-# actions for a @c &lt;bsc/&gt; %object for which a "generate-keypair" command has
-# been issued. The RPKI engine stores the PKCS #10 request, which
-# allows the IRBE to reuse the request if and when it needs to reissue
-# the corresponding BPKI signing certificate.
-#
-# @subsection parent_obj <parent/> object
-#
-# The @c &lt;parent/&gt; %object represents the RPKI engine's view of a particular
-# parent of the current @c &lt;self/&gt; %object in the up-down protocol. Due to
-# the way that the resource hierarchy works, a given @c &lt;self/&gt; may obtain
-# resources from multiple parents, but it will always have at least one;
-# in the case of IANA or an RIR, the parent RPKI engine may be a trivial
-# stub.
-#
-# Every @c &lt;parent/&gt; %object has a parent_id, which must be specified for
-# the "get", "set", and "destroy" actions. Every @c &lt;parent/&gt; also has a
-# self_id attribute which indicates the @c &lt;self/&gt; %object with which this
-# @c &lt;parent/&gt; %object is associated, a bsc_id attribute indicating the @c &lt;bsc/&gt;
-# %object to be used when signing messages sent to this parent, and a
-# repository_id indicating the @c &lt;repository/&gt; %object to be used when
-# publishing issued by the certificate issued by this parent.
-#
-# Payload data which can be configured in a @c &lt;parent/&gt; %object:
-#
-# @li @c peer_contact_uri (attribute):
-# HTTPS URI used to contact this parent.
-#
-# @li @c sia_base (attribute):
-# The leading portion of an rsync URI that the RPKI engine should
-# use when composing the publication URI for %objects issued by the
-# RPKI certificate issued by this parent.
-#
-# @li @c sender_name (attribute):
-# Sender name to use in the up-down protocol when talking to this
-# parent. The RPKI engine doesn't really care what this value is,
-# but other implementations of the up-down protocol do care.
-#
-# @li @c recipient_name (attribute):
-# Recipient name to use in the up-down protocol when talking to this
-# parent. The RPKI engine doesn't really care what this value is,
-# but other implementations of the up-down protocol do care.
-#
-# @li @c bpki_cms_cert (element):
-# BPKI CMS CA certificate for this @c &lt;parent/&gt;. This is used as part
-# of the certificate chain when validating incoming CMS messages If
-# the bpki_cms_glue certificate is in use (below), the bpki_cms_cert
-# certificate should be issued by the bpki_cms_glue certificate;
-# otherwise, the bpki_cms_cert certificate should be issued by the
-# bpki_cert certificate in the @c &lt;self/&gt; %object.
-#
-# @li @c bpki_cms_glue (element):
-# Another BPKI CMS CA certificate for this @c &lt;parent/&gt;, usually not
-# needed. Certain pathological cross-certification cases require a
-# two-certificate chain due to issuer name conflicts. If used, the
-# bpki_cms_glue certificate should be the issuer of the
-# bpki_cms_cert certificate and should be issued by the bpki_cert
-# certificate in the @c &lt;self/&gt; %object; if not needed, the
-# bpki_cms_glue certificate should be left unset.
-#
-# @li @c bpki_https_cert (element):
-# BPKI HTTPS CA certificate for this @c &lt;parent/&gt;. This is like the
-# bpki_cms_cert %object, only used for validating incoming TLS
-# messages rather than CMS.
-#
-# @li @c bpki_cms_glue (element):
-# Another BPKI HTTPS CA certificate for this @c &lt;parent/&gt;, usually not
-# needed. This is like the bpki_cms_glue certificate, only used for
-# validating incoming TLS messages rather than CMS.
-#
-# Control attributes that can be set to "yes" to force actions:
-#
-# @li @c rekey:
-# This is like the rekey command in the @c &lt;self/&gt; %object, but limited
-# to RPKI CAs under this parent.
-#
-# @li @c reissue:
-# This is like the reissue command in the @c &lt;self/&gt; %object, but limited
-# to RPKI CAs under this parent.
-#
-# @li @c revoke:
-# This is like the revoke command in the @c &lt;self/&gt; %object, but limited
-# to RPKI CAs under this parent.
-#
-# @subsection child_obj <child/> object
-#
-# The @c &lt;child/&gt; %object represents the RPKI engine's view of particular
-# child of the current @c &lt;self/&gt; in the up-down protocol.
-#
-# Every @c &lt;child/&gt; %object has a parent_id, which must be specified for the
-# "get", "set", and "destroy" actions. Every @c &lt;child/&gt; also has a
-# self_id attribute which indicates the @c &lt;self/&gt; %object with which this
-# @c &lt;child/&gt; %object is associated.
-#
-# Payload data which can be configured in a @c &lt;child/&gt; %object:
-#
-# @li @c bpki_cert (element):
-# BPKI CA certificate for this @c &lt;child/&gt;. This is used as part of
-# the certificate chain when validating incoming TLS and CMS
-# messages. If the bpki_glue certificate is in use (below), the
-# bpki_cert certificate should be issued by the bpki_glue
-# certificate; otherwise, the bpki_cert certificate should be issued
-# by the bpki_cert certificate in the @c &lt;self/&gt; %object.
-#
-# @li @c bpki_glue (element):
-# Another BPKI CA certificate for this @c &lt;child/&gt;, usually not needed.
-# Certain pathological cross-certification cases require a
-# two-certificate chain due to issuer name conflicts. If used, the
-# bpki_glue certificate should be the issuer of the bpki_cert
-# certificate and should be issued by the bpki_cert certificate in
-# the @c &lt;self/&gt; %object; if not needed, the bpki_glue certificate
-# should be left unset.
-#
-# Control attributes that can be set to "yes" to force actions:
-#
-# @li @c reissue:
-# Not implemented, may be removed from protocol.
-#
-# @subsection repository_obj <repository/> object
-#
-# The @c &lt;repository/&gt; %object represents the RPKI engine's view of a
-# particular publication repository used by the current @c &lt;self/&gt; %object.
-#
-# Every @c &lt;repository/&gt; %object has a repository_id, which must be
-# specified for the "get", "set", and "destroy" actions. Every
-# @c &lt;repository/&gt; also has a self_id attribute which indicates the @c &lt;self/&gt;
-# %object with which this @c &lt;repository/&gt; %object is associated.
-#
-# Payload data which can be configured in a @c &lt;repository/&gt; %object:
-#
-# @li @c peer_contact_uri (attribute):
-# HTTPS URI used to contact this repository.
-#
-# @li @c bpki_cms_cert (element):
-# BPKI CMS CA certificate for this @c &lt;repository/&gt;. This is used as part
-# of the certificate chain when validating incoming CMS messages If
-# the bpki_cms_glue certificate is in use (below), the bpki_cms_cert
-# certificate should be issued by the bpki_cms_glue certificate;
-# otherwise, the bpki_cms_cert certificate should be issued by the
-# bpki_cert certificate in the @c &lt;self/&gt; %object.
-#
-# @li @c bpki_cms_glue (element):
-# Another BPKI CMS CA certificate for this @c &lt;repository/&gt;, usually not
-# needed. Certain pathological cross-certification cases require a
-# two-certificate chain due to issuer name conflicts. If used, the
-# bpki_cms_glue certificate should be the issuer of the
-# bpki_cms_cert certificate and should be issued by the bpki_cert
-# certificate in the @c &lt;self/&gt; %object; if not needed, the
-# bpki_cms_glue certificate should be left unset.
-#
-# @li @c bpki_https_cert (element):
-# BPKI HTTPS CA certificate for this @c &lt;repository/&gt;. This is like the
-# bpki_cms_cert %object, only used for validating incoming TLS
-# messages rather than CMS.
-#
-# @li @c bpki_cms_glue (element):
-# Another BPKI HTTPS CA certificate for this @c &lt;repository/&gt;, usually not
-# needed. This is like the bpki_cms_glue certificate, only used for
-# validating incoming TLS messages rather than CMS.
-#
-# At present there are no control attributes for @c &lt;repository/&gt; %objects.
-#
-# @subsection route_origin_obj <route_origin/> object
-#
-# The @c &lt;route_origin/&gt; %object is a kind of prototype for a ROA. It
-# contains all the information needed to generate a ROA once the RPKI
-# engine obtains the appropriate RPKI certificates from its parent(s).
-#
-# Note that a @c &lt;route_origin/&gt; %object represents a ROA to be generated on
-# behalf of @c &lt;self/&gt;, not on behalf of a @c &lt;child/&gt;. Thus, a hosted entity
-# that has no children but which does need to generate ROAs would be
-# represented by a hosted @c &lt;self/&gt; with no @c &lt;child/&gt; %objects but one or
-# more @c &lt;route_origin/&gt; %objects. While lumping ROA generation in with
-# the other RPKI engine activities may seem a little odd at first, it's
-# a natural consequence of the design requirement that the RPKI daemon
-# never transmit private keys across the network in any form; given this
-# requirement, the RPKI engine that holds the private keys for an RPKI
-# certificate must also be the engine which generates any ROAs that
-# derive from that RPKI certificate.
-#
-# The precise content of the @c &lt;route_origin/&gt; has changed over time as
-# the underlying ROA specification has changed. The current
-# implementation as of this writing matches what we expect to see in
-# draft-ietf-sidr-roa-format-03, once it is issued. In particular, note
-# that the exactMatch boolean from the -02 draft has been replaced by
-# the prefix and maxLength encoding used in the -03 draft.
-#
-# Payload data which can be configured in a @c &lt;route_origin/&gt; %object:
-#
-# @li @c as_number (attribute):
-# Autonomous System Number (ASN) to place in the generated ROA. A
-# single ROA can only grant authorization to a single ASN; multiple
-# ASNs require multiple ROAs, thus multiple @c &lt;route_origin/&gt; %objects.
-#
-# @li @c ipv4 (attribute):
-# %List of IPv4 prefix and maxLength values, see below for format.
-#
-# @li @c ipv6 (attribute):
-# %List of IPv6 prefix and maxLength values, see below for format.
-#
-# Control attributes that can be set to "yes" to force actions:
-#
-# @li @c suppress_publication:
-# Not implemented, may be removed from protocol.
-#
-# The lists of IPv4 and IPv6 prefix and maxLength values are represented
-# as comma-separated text strings, with no whitespace permitted. Each
-# entry in such a string represents a single prefix/maxLength pair.
-#
-# ABNF for these address lists:
-#
-# @verbatim
-#
-# <ROAIPAddress> ::= <address> "/" <prefixlen> [ "-" <max_prefixlen> ]
-# ; Where <max_prefixlen> defaults to the same
-# ; value as <prefixlen>.
-#
-# <ROAIPAddressList> ::= <ROAIPAddress> *( "," <ROAIPAddress> )
-#
-# @endverbatim
-#
-# For example, @c "10.0.1.0/24-32,10.0.2.0/24", which is a shorthand
-# form of @c "10.0.1.0/24-32,10.0.2.0/24-24".
-#
-# @section irdb_queries Operations initiated by the RPKI engine
-#
-# The left-right protocol also includes queries from the RPKI engine
-# back to the IRDB. These queries do not follow the message-passing
-# pattern used in the IRBE-initiated part of the protocol. Instead,
-# there's a single query back to the IRDB, with a corresponding
-# response. The CMS and HTTPS encoding are the same as in the rest of
-# the protocol, but the BPKI certificates will be different as the
-# back-queries and responses form a separate communication channel.
-#
-# @subsection list_resources_msg <list_resources/> messages
-#
-# The @c &lt;list_resources/&gt; query and response allow the RPKI engine to ask
-# the IRDB for information about resources assigned to a particular
-# child. The query must include both a @c "self_id" attribute naming
-# the @c &lt;self/&gt; that is making the request and also a @c "child_id"
-# attribute naming the child that is the subject of the query. The
-# query and response also allow an optional @c "tag" attribute of the
-# same form used elsewhere in this protocol, to allow batching.
-#
-# A @c &lt;list_resources/&gt; response includes the following attributes, along
-# with the @c tag (if specified), @c self_id, and @c child_id copied
-# from the request:
-#
-# @li @c valid_until:
-# A timestamp indicating the date and time at which certificates
-# generated by the RPKI engine for these data should expire. The
-# timestamp is expressed as an XML @c xsd:dateTime, must be
-# expressed in UTC, and must carry the "Z" suffix indicating UTC.
-#
-# @li @c subject_name:
-# An optional text string naming the child. Not currently used.
-#
-# @li @c asn:
-# A %list of autonomous sequence numbers, expressed as a
-# comma-separated sequence of decimal integers with no whitespace.
-#
-# @li @c ipv4:
-# A %list of IPv4 address prefixes and ranges, expressed as a
-# comma-separated %list of prefixes and ranges with no whitespace.
-# See below for format details.
-#
-# @li @c ipv6:
-# A %list of IPv6 address prefixes and ranges, expressed as a
-# comma-separated %list of prefixes and ranges with no whitespace.
-# See below for format details.
-#
-# Entries in a %list of address prefixes and ranges can be either
-# prefixes, which are written in the usual address/prefixlen notation,
-# or ranges, which are expressed as a pair of addresses denoting the
-# beginning and end of the range, written in ascending order separated
-# by a single "-" character. This format is superficially similar to
-# the format used for prefix and maxLength values in the @c &lt;route_origin/&gt;
-# %object, but the semantics differ: note in particular that
-# @c &lt;route_origin/&gt; %objects don't allow ranges, while @c &lt;list_resources/&gt;
-# messages don't allow a maxLength specification.
-#
-# @section left_right_error_handling Error handling
-#
-# Error in this protocol are handled at two levels.
-#
-# Since all messages in this protocol are conveyed over HTTPS
-# connections, basic errors are indicated via the HTTP response code.
-# 4xx and 5xx responses indicate that something bad happened. Errors
-# that make it impossible to decode a query or encode a response are
-# handled in this way.
-#
-# Where possible, errors will result in a @c &lt;report_error/&gt; message which
-# takes the place of the expected protocol response message.
-# @c &lt;report_error/&gt; messages are CMS-signed XML messages like the rest of
-# this protocol, and thus can be archived to provide an audit trail.
-#
-# @c &lt;report_error/&gt; messages only appear in replies, never in queries.
-# The @c &lt;report_error/&gt; message can appear on either the "forward" (IRBE
-# as client of RPKI engine) or "back" (RPKI engine as client of IRDB)
-# communication channel.
-#
-# The @c &lt;report_error/&gt; message includes an optional @c "tag" attribute to
-# assist in matching the error with a particular query when using
-# batching, and also includes a @c "self_id" attribute indicating the
-# @c &lt;self/&gt; that issued the error.
-#
-# The error itself is conveyed in the @c error_code (attribute). The
-# value of this attribute is a token indicating the specific error that
-# occurred. At present this will be the name of a Python exception; the
-# production version of this protocol will nail down the allowed error
-# tokens here, probably in the RelaxNG schema.
-#
-# The body of the @c &lt;report_error/&gt; element itself is an optional text
-# string; if present, this is debugging information. At present this
-# capabilty is not used, debugging information goes to syslog.
-
-## @page Publication Publication protocol
-#
-# The %publication protocol is really two separate client/server
-# protocols, between different parties. The first is a configuration
-# protocol for an IRBE to use to configure a %publication engine,
-# the second is the interface by which authorized clients request
-# %publication of specific objects.
-#
-# Much of the architecture of the %publication protocol is borrowed
-# from the @link Left-right left-right protocol: @endlink like the
-# left-right protocol, the %publication protocol uses CMS-wrapped XML
-# over HTTPS with the same eContentType OID and the same HTTPS
-# content-type, and the overall style of the XML messages is very
-# similar to the left-right protocol. All operations allow an
-# optional "tag" attribute to allow batching.
-#
-# The %publication engine operates a single HTTPS server which serves
-# both of these subprotocols. The two subprotocols share a single
-# server port, but use distinct URLs to allow demultiplexing.
-#
-# @section Terminology
-#
-# @li @em IRBE: Internet Registry Back End
-#
-# @li @em IRDB: Internet Registry Data Base
-#
-# @li @em BPKI: Business PKI
-#
-# @li @em RPKI: Resource PKI
-#
-# @section Publication-control Publication control subprotocol
-#
-# The control subprotocol reuses the message-passing design of the
-# left-right protocol. Configured objects support the "create", "set",
-# "get", "list", and "destroy" actions, or a subset thereof when the
-# full set of actions doesn't make sense.
-#
-# @subsection config_obj <config/> object
-#
-# The &lt;config/&gt; %object allows configuration of data that apply to the
-# entire %publication server rather than a particular client.
-#
-# There is exactly one &lt;config/&gt; %object in the %publication server, and
-# it only supports the "set" and "get" actions -- it cannot be created
-# or destroyed.
-#
-# Payload data which can be configured in a &lt;config/&gt; %object:
-#
-# @li @c bpki_crl (element):
-# This is the BPKI CRL used by the %publication server when
-# signing the CMS wrapper on responses in the %publication
-# subprotocol. As the CRL must be updated at regular intervals,
-# it's not practical to restart the %publication server when the
-# BPKI CRL needs to be updated. The BPKI model doesn't require
-# use of a BPKI CRL between the IRBE and the %publication server,
-# so we can use the %publication control subprotocol to update the
-# BPKI CRL.
-#
-# @subsection client_obj <client/> object
-#
-# The &lt;client/&gt; %object represents one client authorized to use the
-# %publication server.
-#
-# The &lt;client/&gt; %object supports the full set of "create", "set", "get",
-# "list", and "destroy" actions. Each client has a "client_id"
-# attribute, which is used in responses and must be specified in "set",
-# "get", or "destroy" actions.
-#
-# Payload data which can be configured in a &lt;client/&gt; %object:
-#
-# @li @c base_uri (attribute):
-# This is the base URI below which this client is allowed to publish
-# data. The %publication server may impose additional constraints in
-# the case of a child publishing beneath its parent.
-#
-# @li @c bpki_cert (element):
-# BPKI CA certificate for this &lt;client/&gt;. This is used as part of
-# the certificate chain when validating incoming TLS and CMS
-# messages. If the bpki_glue certificate is in use (below), the
-# bpki_cert certificate should be issued by the bpki_glue
-# certificate; otherwise, the bpki_cert certificate should be issued
-# by the %publication engine's bpki_ta certificate.
-#
-# @li @c bpki_glue (element):
-# Another BPKI CA certificate for this &lt;client/&gt;, usually not
-# needed. Certain pathological cross-certification cases require a
-# two-certificate chain due to issuer name conflicts. If used, the
-# bpki_glue certificate should be the issuer of the bpki_cert
-# certificate and should be issued by the %publication engine's
-# bpki_ta certificate; if not needed, the bpki_glue certificate
-# should be left unset.
-#
-# @section Publication-publication Publication subprotocol
-#
-# The %publication subprotocol is structured somewhat differently from
-# the %publication control protocol. Objects in the %publication
-# subprotocol represent objects to be published or objects to be
-# withdrawn from %publication. Each kind of %object supports two actions:
-# "publish" and "withdraw". In each case the XML element representing
-# hte %object to be published or withdrawn has a "uri" attribute which
-# contains the %publication URI. For "publish" actions, the XML element
-# body contains the DER %object to be published, encoded in Base64; for
-# "withdraw" actions, the XML element body is empty.
-#
-# In theory, the detailed access control for each kind of %object might
-# be different. In practice, as of this writing, access control for all
-# objects is a simple check that the client's @c "base_uri" is a leading
-# substring of the %publication URI. Details of why access control might
-# need to become more complicated are discussed in a later section.
-#
-# @subsection certificate_obj <certificate/> object
-#
-# The &lt;certificate/&gt; %object represents an RPKI certificate to be
-# published or withdrawn.
-#
-# @subsection crl_obj <crl/> object
-#
-# The &lt;crl/&gt; %object represents an RPKI CRL to be published or withdrawn.
-#
-# @subsection manifest_obj <manifest/> object
-#
-# The &lt;manifest/&gt; %object represents an RPKI %publication %manifest to be
-# published or withdrawn.
-#
-# Note that part of the reason for the batching support in the
-# %publication protocol is because @em every %publication or withdrawal
-# action requires a new %manifest, thus every %publication or withdrawal
-# action will involve at least two objects.
-#
-# @subsection roa_obj <roa/> object
-#
-# The &lt;roa/&gt; %object represents a ROA to be published or withdrawn.
-#
-# @section publication_error_handling Error handling
-#
-# Error in this protocol are handled at two levels.
-#
-# Since all messages in this protocol are conveyed over HTTPS
-# connections, basic errors are indicated via the HTTP response code.
-# 4xx and 5xx responses indicate that something bad happened. Errors
-# that make it impossible to decode a query or encode a response are
-# handled in this way.
-#
-# Where possible, errors will result in a &lt;report_error/&gt; message which
-# takes the place of the expected protocol response message.
-# &lt;report_error/&gt; messages are CMS-signed XML messages like the rest of
-# this protocol, and thus can be archived to provide an audit trail.
-#
-# &lt;report_error/&gt; messages only appear in replies, never in
-# queries. The &lt;report_error/&gt; message can appear in both the
-# control and publication subprotocols.
-#
-# The &lt;report_error/&gt; message includes an optional @c "tag" attribute to
-# assist in matching the error with a particular query when using
-# batching.
-#
-# The error itself is conveyed in the @c error_code (attribute). The
-# value of this attribute is a token indicating the specific error that
-# occurred. At present this will be the name of a Python exception; the
-# production version of this protocol will nail down the allowed error
-# tokens here, probably in the RelaxNG schema.
-#
-# The body of the &lt;report_error/&gt; element itself is an optional text
-# string; if present, this is debugging information. At present this
-# capabilty is not used, debugging information goes to syslog.
-#
-# @section publication_access_control Additional access control considerations.
-#
-# As detailed above, the %publication protocol is trivially simple. This
-# glosses over two bits of potential complexity:
-#
-# @li In the case where parent and child are sharing a repository, we'd
-# like to nest child under parent, because testing has demonstrated
-# that even on relatively slow hardware the delays involved in
-# setting up separate rsync connections tend to dominate
-# synchronization time for relying parties.
-#
-# @li The repository operator might also want to do some checks to
-# assure itself that what it's about to allow the RPKI engine to
-# publish is not dangerous toxic waste.
-#
-# The up-down protocol includes a mechanism by which a parent can
-# suggest a %publication URI to each of its children. The children are
-# not required to accept this hint, and the children must make separate
-# arrangements with the repository operator (who might or might not be
-# the same as the entity that hosts the children's RPKI engine
-# operations) to use the suggested %publication point, but if everything
-# works out, this allows children to nest cleanly under their parents
-# %publication points, which helps reduce synchronization time for
-# relying parties.
-#
-# In this case, one could argue that the %publication server is
-# responsible for preventing one of its clients (the child in the above
-# description) from stomping on data published by another of its clients
-# (the parent in the above description). This goes beyond the basic
-# access check and requires the %publication server to determine whether
-# the parent has given its consent for the child to publish under the
-# parent. Since the RPKI certificate profile requires the child's
-# %publication point to be indicated in an SIA extension in a certificate
-# issued by the parent to the child, the %publication engine can infer
-# this permission from the parent's issuance of a certificate to the
-# child. Since, by definition, the parent also uses this %publication
-# server, this is an easy check, as the %publication server should
-# already have the parent's certificate available by the time it needs
-# to check the child's certificate.
-#
-# The previous paragraph only covers a "publish" action for a
-# &lt;certificate/&gt; %object. For "publish" actions on other
-# objects, the %publication server would need to trace permission back
-# to the certificate issued by the parent; for "withdraw" actions,
-# the %publication server would have to perform the same checks it
-# would perform for a "publish" action, using the current published
-# data before withdrawing it. The latter in turn implies an ordering
-# constraint on "withdraw" actions in order to preserve the data
-# necessary for these access control decisions; as this may prove
-# impractical, the %publication server may probably need to make
-# periodic sweeps over its published data looking for orphaned
-# objects, but that's probably a good idea anyway.
-#
-# Note that, in this %publication model, any agreement that the
-# repository makes to publish the RPKI engine's output is conditional
-# upon the %object to be published passing whatever access control checks
-# the %publication server imposes.
-
-## @page sql-schemas SQL database schemas
-#
-# @li @subpage rpkid-sql "rpkid database schema"
-# @li @subpage pubd-sql "pubd database schema"
-# @li @subpage irdbd-sql "irdbd database schema"
-
-## @page rpkid-sql rpkid SQL schema
-#
-# @dotfile rpkid.dot "Diagram of rpkid.sql"
-#
-# @verbinclude rpkid.sql
-
-## @page pubd-sql pubd SQL Schema
-#
-# @dotfile pubd.dot "Diagram of pubd.sql"
-#
-# @verbinclude pubd.sql
-
-## @page irdbd-sql irdbd SQL Schema
-#
-# @dotfile irdbd.dot "Diagram of irdbd.sql"
-#
-# @verbinclude irdbd.sql
-
-## @page bpki-model BPKI model
-#
-# The "business PKI" (BPKI) is the PKI used to authenticate
-# communication on the up-down, left-right, and %publication protocols.
-# BPKI certificates are @em not resource PKI (RPKI) certificates. The
-# BPKI is a separate PKI that represents relationships between the
-# various entities involved in the production side of the RPKI system.
-# In most cases the BPKI tree will follow existing business
-# relationships, hence the name "BPKI".
-#
-# Setup of the BPKI is handled by the back end; for the most part,
-# rpkid and pubd just use the result. The one place where the engines
-# are directly involved in creation of new BPKI certificates is in the
-# production of end-entity certificates for use by the engines.
-#
-# There are a few design principals that underly the chosen BPKI model:
-# @li Each engine should rely on a single BPKI trust anchor which is
-# controlled by the back end entity that runs the engine; all
-# other trust material should be cross-certified into the engine's
-# BPKI tree.
-# @li Private keys must never transit the network.
-# @li Except for end entity certificates, the engine should only have
-# access to the BPKI certificates; in particular, the private key
-# for the BPKI trust anchor should not be accessible to the engine.
-# @li The number of BPKI keys and certificates that the engine has to
-# manage should be no larger than is necessary.
-#
-# rpkid's hosting model adds an additional constraint: rpkid's BPKI
-# trust anchor belongs to the entity operating rpkid, but the entities
-# hosted by rpkid should have control of their own BPKI private keys.
-# This implies the need for an additional layer of BPKI certificate
-# hierarchy within rpkid.
-#
-# Here is a simplified picture of what the BPKI might look like for an
-# rpkid operator that hosts two entities, "Alice" and "Ellen":
-#
-# @dot
-# // Color code:
-# // Black: Hosting entity
-# // Blue: Hosted entity
-# // Red: Cross-certified peer
-# //
-# // Shape code:
-# // Octagon: TA
-# // Diamond: CA
-# // Record: EE
-#
-# digraph bpki_rpkid {
-# splines = true;
-# size = "14,14";
-# node [ fontname = Times, fontsize = 9 ];
-#
-# // Hosting entity
-# node [ color = black, shape = record ];
-# TA [ shape = octagon, label = "BPKI TA" ];
-# rpkid [ label = "rpkid|{HTTPS server|HTTPS left-right client|CMS left-right}" ];
-# irdbd [ label = "irdbd|{HTTPS left-right server|CMS left-right}" ];
-# irbe [ label = "IRBE|{HTTPS left-right client|CMS left-right}" ];
-#
-# // Hosted entities
-# node [ color = blue, fontcolor = blue ];
-# Alice_CA [ shape = diamond ];
-# Alice_EE [ label = "Alice\nBSC EE|{HTTPS up-down client|CMS up-down}" ];
-# Ellen_CA [ shape = diamond ];
-# Ellen_EE [ label = "Ellen\nBSC EE|{HTTPS up-down client|CMS up-down}" ];
-#
-# // Peers
-# node [ color = red, fontcolor = red, shape = diamond ];
-# Bob_CA;
-# Carol_CA;
-# Dave_CA;
-# Frank_CA;
-# Ginny_CA;
-# Harry_CA;
-# node [ shape = record ];
-# Bob_EE [ label = "Bob\nEE|{HTTPS up-down|CMS up-down}" ];
-# Carol_EE [ label = "Carol\nEE|{HTTPS up-down|CMS up-down}" ];
-# Dave_EE [ label = "Dave\nEE|{HTTPS up-down|CMS up-down}" ];
-# Frank_EE [ label = "Frank\nEE|{HTTPS up-down|CMS up-down}" ];
-# Ginny_EE [ label = "Ginny\nEE|{HTTPS up-down|CMS up-down}" ];
-# Harry_EE [ label = "Bob\nEE|{HTTPS up-down|CMS up-down}" ];
-#
-# edge [ color = black, style = solid ];
-# TA -> Alice_CA;
-# TA -> Ellen_CA;
-#
-# edge [ color = black, style = dotted ];
-# TA -> rpkid;
-# TA -> irdbd;
-# TA -> irbe;
-#
-# edge [ color = blue, style = solid ];
-# Alice_CA -> Bob_CA;
-# Alice_CA -> Carol_CA;
-# Alice_CA -> Dave_CA;
-# Ellen_CA -> Frank_CA;
-# Ellen_CA -> Ginny_CA;
-# Ellen_CA -> Harry_CA;
-#
-# edge [ color = blue, style = dotted ];
-# Alice_CA -> Alice_EE;
-# Ellen_CA -> Ellen_EE;
-#
-# edge [ color = red, style = solid ];
-# Bob_CA -> Bob_EE;
-# Carol_CA -> Carol_EE;
-# Dave_CA -> Dave_EE;
-# Frank_CA -> Frank_EE;
-# Ginny_CA -> Ginny_EE;
-# Harry_CA -> Harry_EE;
-# }
-# @enddot
-#
-# Black objects belong to the hosting entity, blue objects belong to
-# the hosted entities, red objects are cross-certified objects from
-# the hosted entities' peers. The arrows indicate certificate
-# issuance: solid arrows are the ones that rpkid will care about
-# during certificate validation, dotted arrows show the origin of the
-# EE certificates that rpkid uses to sign CMS and TLS messages.
-#
-# There's one nasty bit where the model had to bend to fit the current
-# state of the underlying protocols: it's not possible to use exactly
-# the same BPKI keys and certificates for HTTPS and CMS. The reason
-# for this is simple: each hosted entity has its own BPKI, as does the
-# hosting entity, but the HTTPS listener is shared. The only ways to
-# avoid sharing the HTTPS server certificate would be to use separate
-# listeners for each hosted entity, which scales poorly, or to rely on
-# the TLS "Server Name Indication" extension (RFC 4366 3.1) which is
-# not yet widely implemented.
-#
-# The certificate tree looks complicated, but the set of certificates
-# needed to build any particular validation chain is obvious, again
-# excepting the HTTPS server case, where the client certificate is the
-# first hint that the engine has of the client's identity, so the
-# server must be prepared to accept any current client certificate.
-#
-# Detailed instructions on how to build a BPKI are beyond the scope of
-# this document, but one can handle simple cases using the OpenSSL
-# command line tool and cross_certify.py; the latter is a tool
-# designed specifically for the purpose of generating the
-# cross-certification certificates needed to splice foreign trust
-# material into a BPKI tree.
-#
-# The BPKI tree for a pubd instance is similar to to the BPKI tree for
-# an rpkid instance, but is a bit simpler, as pubd does not provide
-# hosting in the same sense that rpkid does: pubd is a relatively
-# simple server that publishes objects as instructed by its clients.
-#
-# Here's a simplified picture of what the BPKI might look like for a
-# pubd operator that serves two clients, "Alice" and "Bob":
-#
-# @dot
-# // Color code:
-# // Black: Operating entity
-# // Red: Cross-certified client
-# //
-# // Shape code:
-# // Octagon: TA
-# // Diamond: CA
-# // Record: EE
-#
-# digraph bpki_pubd {
-# splines = true;
-# size = "14,14";
-# node [ fontname = Times, fontsize = 9 ];
-#
-# // Operating entity
-# node [ color = black, fontcolor = black, shape = record ];
-# TA [ shape = octagon, label = "BPKI TA" ];
-# pubd [ label = "pubd|{HTTPS server|CMS}" ];
-# ctl [ label = "Control|{HTTPS client|CMS}" ];
-#
-# // Clients
-# node [ color = red, fontcolor = red, shape = diamond ];
-# Alice_CA;
-# Bob_CA;
-# node [ color = red, fontcolor = red, shape = record ];
-# Alice_EE [ label = "Alice\nEE|{HTTPS client|CMS}" ];
-# Bob_EE [ label = "Bob\nEE|{HTTPS client|CMS}" ];
-#
-# edge [ color = black, style = dotted ];
-# TA -> pubd;
-# TA -> ctl;
-#
-# edge [ color = black, style = solid ];
-# TA -> Alice_CA;
-# TA -> Bob_CA;
-#
-# edge [ color = red, style = solid ];
-# Alice_CA -> Alice_EE;
-# Bob_CA -> Bob_EE;
-# }
-# @enddot
-#
-# While it is likely that RIRs (at least) will operate both rpkid and
-# pubd instances, the two functions are conceptually separate. As far
-# as pubd is concerned, it doesn't matter who operates the rpkid
-# instance: pubd just has clients, each of which has trust material
-# that has been cross-certified into pubd's BPKI. Similarly, rpkid
-# doesn't really care who operates a pubd instance that it's been
-# configured to use, it just treats that pubd as a foreign BPKI whose
-# trust material has to be cross-certified into its own BPKI. Cross
-# certification itself is done by the back end operator, using
-# cross_certify or some equivalent tool; the resulting BPKI
-# certificates are configured into rpkid and pubd via the left-right
-# protocol and the control subprotocol of the publication protocol,
-# respectively.
-#
-# Because the BPKI tree is almost entirely controlled by the operating
-# entity, CRLs are not necessary for most of the BPKI. The one
-# exception to this is the EE certificates issued under the
-# cross-certification points. These EE certificates are generated by
-# the peer, not the local operator, and thus require CRLs. Because of
-# this, both rpkid and pubd require regular updates of certain BPKI
-# CRLs, again via the left-right and publication control protocols.
-#
-# Because the left-right protocol and the publication control
-# subprotocol are used to configure BPKI certificates and CRLs, they
-# cannot themselves use certificates and CRLs configured in this way.
-# This is why the configuration files for rpkid and pubd require
-# static configuration of the left-right and publication control
-# certificates.
-
-# Local Variables:
-# compile-command: "cd .. && make doc"
-# End:
diff --git a/rpkid.stable/rpki/config.py b/rpkid.stable/rpki/config.py
deleted file mode 100644
index df856311..00000000
--- a/rpkid.stable/rpki/config.py
+++ /dev/null
@@ -1,56 +0,0 @@
-"""Configuration file parsing utilities, layered on top of stock
-Python ConfigParser module.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 ConfigParser
-
-class parser(ConfigParser.RawConfigParser):
-
- def __init__(self, file = None, section = None):
- """Initialize this parser."""
- ConfigParser.RawConfigParser.__init__(self)
- if file:
- self.read(file)
- self.default_section = section
-
- def multiget(self, option, section = None):
- """Parse OpenSSL-style foo.0, foo.1, ... subscripted options.
-
- Returns a list of values matching the specified option name.
- """
- matches = []
- if section is None:
- section = self.default_section
- if self.has_option(section, option):
- matches.append((-1, self.get(option, section = section)))
- for key, value in self.items(section):
- s = key.rsplit(".", 1)
- if len(s) == 2 and s[0] == option and s[1].isdigit():
- matches.append((int(s[1]), value))
- matches.sort()
- return [match[1] for match in matches]
-
- def get(self, option, default = None, section = None):
- """Get an option, perhaps with a default value."""
- if section is None:
- section = self.default_section
- if default is None or self.has_option(section, option):
- return ConfigParser.RawConfigParser.get(self, section, option)
- else:
- return default
diff --git a/rpkid.stable/rpki/exceptions.py b/rpkid.stable/rpki/exceptions.py
deleted file mode 100644
index 393700e6..00000000
--- a/rpkid.stable/rpki/exceptions.py
+++ /dev/null
@@ -1,135 +0,0 @@
-"""Exception definitions for RPKI modules.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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.
-"""
-
-class RPKI_Exception(Exception):
- """Base class for RPKI exceptions."""
-
-class NotInDatabase(RPKI_Exception):
- """Lookup failed for an object expected to be in the database."""
-
-class BadURISyntax(RPKI_Exception):
- """Illegal syntax for a URI."""
-
-class BadStatusCode(RPKI_Exception):
- """Unrecognized protocol status code."""
-
-class BadQuery(RPKI_Exception):
- """Unexpected protocol query."""
-
-class DBConsistancyError(RPKI_Exception):
- """Found multiple matches for a database query that shouldn't ever return that."""
-
-class CMSVerificationFailed(RPKI_Exception):
- """Verification of a CMS message failed."""
-
-class HTTPRequestFailed(RPKI_Exception):
- """HTTP request failed."""
-
-class DERObjectConversionError(RPKI_Exception):
- """Error trying to convert a DER-based object from one representation to another."""
-
-class NotACertificateChain(RPKI_Exception):
- """Certificates don't form a proper chain."""
-
-class BadContactURL(RPKI_Exception):
- """Error trying to parse up-down protocol contact URL."""
-
-class BadClassNameSyntax(RPKI_Exception):
- """Illegal syntax for a class_name."""
-
-class BadIssueResponse(RPKI_Exception):
- """issue_response PDU with wrong number of classes or certificates."""
-
-class NotImplementedYet(RPKI_Exception):
- """Internal error -- not implemented yet."""
-
-class BadPKCS10(RPKI_Exception):
- """Bad PKCS #10 object."""
-
-class UpstreamError(RPKI_Exception):
- """Received an error from upstream."""
-
-class ChildNotFound(RPKI_Exception):
- """Could not find specified child in database."""
-
-class BSCNotFound(RPKI_Exception):
- """Could not find specified BSC in database."""
-
-class BadSender(RPKI_Exception):
- """Unexpected XML sender value."""
-
-class ClassNameMismatch(RPKI_Exception):
- """class_name does not match child context."""
-
-class ClassNameUnknown(RPKI_Exception):
- """Unknown class_name."""
-
-class SKIMismatch(RPKI_Exception):
- """SKI value in response does not match request."""
-
-class SubprocessError(RPKI_Exception):
- """Subprocess returned unexpected error."""
-
-class BadIRDBReply(RPKI_Exception):
- """Unexpected reply to IRDB query."""
-
-class NotFound(RPKI_Exception):
- """Object not found in database."""
-
-class MustBePrefix(RPKI_Exception):
- """Resource range cannot be expressed as a prefix."""
-
-class TLSValidationError(RPKI_Exception):
- """TLS certificate validation error."""
-
-class MultipleTLSEECert(TLSValidationError):
- """Received more than one TLS EE certificate."""
-
-class ReceivedTLSCACert(TLSValidationError):
- """Received CA certificate via TLS."""
-
-class WrongEContentType(RPKI_Exception):
- """Received wrong CMS eContentType."""
-
-class EmptyPEM(RPKI_Exception):
- """Couldn't find PEM block to convert."""
-
-class UnexpectedCMSCerts(RPKI_Exception):
- """Received CMS certs when not expecting any."""
-
-class UnexpectedCMSCRLs(RPKI_Exception):
- """Received CMS CRLs when not expecting any."""
-
-class MissingCMSEEcert(RPKI_Exception):
- """Didn't receive CMS EE cert when expecting one."""
-
-class MissingCMSCRL(RPKI_Exception):
- """Didn't receive CMS CRL when expecting one."""
-
-class UnparsableCMSDER(RPKI_Exception):
- """Alleged CMS DER wasn't parsable."""
-
-class CMSCRLNotSet(RPKI_Exception):
- """CMS CRL has not been configured."""
-
-class ServerShuttingDown(RPKI_Exception):
- """Server is shutting down."""
-
-class NoActiveCA(RPKI_Exception):
- """No active ca_detail for specified class."""
diff --git a/rpkid.stable/rpki/https.py b/rpkid.stable/rpki/https.py
deleted file mode 100644
index a0443c01..00000000
--- a/rpkid.stable/rpki/https.py
+++ /dev/null
@@ -1,291 +0,0 @@
-"""HTTPS utilities, both client and server.
-
-At the moment this only knows how to use the PEM certs in my
-subversion repository; generalizing it would not be hard, but the more
-general version should use SQL anyway.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 httplib, BaseHTTPServer, tlslite.api, glob, traceback, urlparse, socket, signal
-import rpki.x509, rpki.exceptions, rpki.log
-
-# This should be wrapped somewhere in rpki.x509 eventually
-import POW
-
-# Do not set this to True for production use!
-disable_tls_certificate_validation_exceptions = False
-
-# Chatter about TLS certificates
-debug_tls_certs = False
-
-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])
-
-def build_https_ta_cache(certs):
- """Build a dynamic TLS trust anchor cache."""
-
- store = POW.X509Store()
- for x in certs:
- if rpki.https.debug_tls_certs:
- rpki.log.debug("HTTPS dynamic trusted cert issuer %s [%s] subject %s [%s]" % (x.getIssuer(), x.hAKI(), x.getSubject(), x.hSKI()))
- store.addTrust(x.get_POW())
- return store
-
-class Checker(tlslite.api.Checker):
- """Derived class to handle X.509 client certificate checking."""
-
- ## @var refuse_tls_ca_certs
- # Raise an exception upon receiving CA certificates via TLS rather
- # than just quietly ignoring them.
-
- refuse_tls_ca_certs = False
-
- ## @var pem_dump_tls_certs
- # Vile debugging hack
-
- pem_dump_tls_certs = False
-
- def __init__(self, trust_anchor = None, dynamic_https_trust_anchor = None):
- """Initialize our modified certificate checker."""
-
- self.dynamic_https_trust_anchor = dynamic_https_trust_anchor
-
- if dynamic_https_trust_anchor is not None:
- return
-
- self.x509store = POW.X509Store()
-
- trust_anchor = rpki.x509.X509.normalize_chain(trust_anchor)
- assert trust_anchor
-
- for x in trust_anchor:
- if debug_tls_certs:
- rpki.log.debug("HTTPS trusted cert issuer %s [%s] subject %s [%s]" % (x.getIssuer(), x.hAKI(), x.getSubject(), x.hSKI()))
- self.x509store.addTrust(x.get_POW())
- if self.pem_dump_tls_certs:
- print x.get_PEM()
-
- def x509store_thunk(self):
- if self.dynamic_https_trust_anchor is not None:
- return self.dynamic_https_trust_anchor()
- else:
- return self.x509store
-
- def __call__(self, tlsConnection):
- """POW/OpenSSL-based certificate checker.
-
- Given our BPKI model, we're only interested in the TLS EE
- certificates.
- """
-
- if tlsConnection._client:
- chain = tlsConnection.session.serverCertChain
- peer = "server"
- else:
- chain = tlsConnection.session.clientCertChain
- peer = "client"
-
- chain = [rpki.x509.X509(tlslite = chain.x509List[i]) for i in range(chain.getNumCerts())]
-
- ee = None
-
- for x in chain:
-
- if debug_tls_certs:
- rpki.log.debug("Received %s TLS %s cert issuer %s [%s] subject %s [%s]"
- % (peer, "CA" if x.is_CA() else "EE", x.getIssuer(), x.hAKI(), x.getSubject(), x.hSKI()))
- if self.pem_dump_tls_certs:
- print x.get_PEM()
-
- if x.is_CA():
- if self.refuse_tls_ca_certs:
- raise rpki.exceptions.ReceivedTLSCACert
- continue
-
- if ee is not None:
- raise rpki.exceptions.MultipleTLSEECert, chain
- ee = x
-
- result = self.x509store_thunk().verifyDetailed(ee.get_POW())
- if not result[0]:
- rpki.log.debug("TLS certificate validation result %s" % repr(result))
- if disable_tls_certificate_validation_exceptions:
- rpki.log.warn("DANGER WILL ROBINSON! IGNORING TLS VALIDATION FAILURE!")
- else:
- raise rpki.exceptions.TLSValidationError
-
-class httpsClient(tlslite.api.HTTPTLSConnection):
- """Derived class to let us replace the default Checker."""
-
- def __init__(self, host, port = 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_cert, privateKey = client_key)
-
- self.checker = Checker(trust_anchor = server_ta)
-
-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
- over HTTPS and get a response. The certificate checking isn't quite
- up to snuff; it's better than with the other packages I've found,
- but doesn't appear to handle subjectAltName extensions (sigh).
- """
-
- u = urlparse.urlparse(url)
-
- assert u.scheme in ("", "https") and \
- u.username is None and \
- u.password is None and \
- u.params == "" and \
- u.query == "" and \
- u.fragment == ""
-
- rpki.log.debug("Contacting %s" % url)
-
- if debug_tls_certs:
- for cert in (client_cert,) if isinstance(client_cert, rpki.x509.X509) else client_cert:
- rpki.log.debug("Sending client TLS cert issuer %s subject %s" % (cert.getIssuer(), cert.getSubject()))
-
- # We could add a "settings = foo" argument to the following call to
- # pass in a tlslite.HandshakeSettings object that would let us
- # insist on, eg, particular SSL/TLS versions.
-
- httpc = httpsClient(host = u.hostname or "localhost",
- port = u.port or 443,
- client_key = client_key.get_tlslite(),
- client_cert = tlslite_certChain(client_cert),
- server_ta = server_ta)
- httpc.connect()
- httpc.sock.settimeout(timeout)
- httpc.request("POST", u.path, msg, {"Content-Type" : rpki_content_type})
- response = httpc.getresponse()
- if response.status == httplib.OK:
- return response.read()
- else:
- r = response.read()
- raise rpki.exceptions.HTTPRequestFailed, \
- "HTTP request failed with status %s, response %s" % (response.status, r)
-
-class requestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- """Derived type to supply POST handler and override logging."""
-
- rpki_handlers = None # Subclass must bind
-
- def rpki_find_handler(self):
- """Helper method to search self.rpki_handlers."""
- for s,h in self.rpki_handlers:
- if self.path.startswith(s):
- return h
- return None
-
- def do_POST(self):
- """POST handler."""
- try:
- handler = self.rpki_find_handler()
- if self.headers["Content-Type"] != rpki_content_type:
- rcode, rtext = 415, "Received Content-Type %s, expected %s" \
- % (self.headers["Content-Type"], rpki_content_type)
- elif handler is None:
- rcode, rtext = 404, "No handler found for URL " + self.path
- else:
- rcode, rtext = handler(query = self.rfile.read(int(self.headers["Content-Length"])),
- path = self.path)
- except Exception, edata:
- rpki.log.error(traceback.format_exc())
- rcode, rtext = 500, "Unhandled exception %s" % edata
- self.send_response(rcode)
- self.send_header("Content-Type", rpki_content_type)
- self.end_headers()
- self.wfile.write(rtext)
-
- def log_message(self, format, *args):
- """Redirect HTTP server logging into our own logging system."""
- if args:
- rpki.log.info(format % args)
- else:
- rpki.log.info(format)
-
-class httpsServer(tlslite.api.TLSSocketServerMixIn, BaseHTTPServer.HTTPServer):
- """Derived type to handle TLS aspects of HTTPS."""
-
- rpki_sessionCache = None
- rpki_server_key = None
- rpki_server_cert = None
- rpki_checker = None
-
- def handshake(self, tlsConnection):
- """TLS handshake handler."""
- assert self.rpki_server_cert is not None
- assert self.rpki_server_key is not None
- assert self.rpki_sessionCache is not None
-
- try:
- #
- # We could add a "settings = foo" argument to the following call
- # to pass in a tlslite.HandshakeSettings object that would let
- # us insist on, eg, particular SSL/TLS versions.
- #
- tlsConnection.handshakeServer(certChain = self.rpki_server_cert,
- privateKey = self.rpki_server_key,
- sessionCache = self.rpki_sessionCache,
- checker = self.rpki_checker,
- reqCert = True)
- tlsConnection.ignoreAbruptClose = True
- return True
- except (tlslite.api.TLSError, rpki.exceptions.TLSValidationError), error:
- rpki.log.warn("TLS handshake failure: " + str(error))
- return False
-
-def server(handlers, server_key, server_cert, port = 4433, host ="", client_ta = None, dynamic_https_trust_anchor = None, catch_signals = (signal.SIGINT, signal.SIGTERM)):
- """Run an HTTPS server and wait (forever) for connections."""
-
- if not isinstance(handlers, (tuple, list)):
- handlers = (("/", handlers),)
-
- class boundRequestHandler(requestHandler):
- rpki_handlers = handlers
-
- httpd = httpsServer((host, port), boundRequestHandler)
-
- httpd.rpki_server_key = server_key.get_tlslite()
- httpd.rpki_server_cert = tlslite_certChain(server_cert)
- httpd.rpki_sessionCache = tlslite.api.SessionCache()
- httpd.rpki_checker = Checker(trust_anchor = client_ta, dynamic_https_trust_anchor = dynamic_https_trust_anchor)
-
- try:
- def raiseServerShuttingDown(signum, frame):
- raise rpki.exceptions.ServerShuttingDown
- old_signal_handlers = tuple((sig, signal.signal(sig, raiseServerShuttingDown)) for sig in catch_signals)
- httpd.serve_forever()
- except rpki.exceptions.ServerShuttingDown:
- pass
- finally:
- for sig,handler in old_signal_handlers:
- signal.signal(sig, handler)
diff --git a/rpkid.stable/rpki/ipaddrs.py b/rpkid.stable/rpki/ipaddrs.py
deleted file mode 100644
index db6a5891..00000000
--- a/rpkid.stable/rpki/ipaddrs.py
+++ /dev/null
@@ -1,103 +0,0 @@
-"""Classes to represent IP addresses.
-
-Given some of the other operations we need to perform on them, it's
-most convenient to represent IP addresses as Python "long" values.
-The classes in this module just wrap suitable read/write syntax around
-the underlying "long" type.
-
-These classes also supply a "bits" attribute for use by other code
-built on these classes; for the most part, IPv6 addresses really are
-just IPv4 addresses with more bits, so we supply the number of bits
-once, here, thus avoiding a lot of duplicate code elsewhere.
-
-$Id$
-
-
-Copyright (C) 2009 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.
-
-
-Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 socket, struct
-
-class v4addr(long):
- """IPv4 address.
-
- Derived from long, but supports IPv4 print syntax.
- """
-
- bits = 32
-
- def __new__(cls, x):
- """Construct a v4addr object."""
- if isinstance(x, str):
- return cls.from_bytes(socket.inet_pton(socket.AF_INET, ".".join(str(int(i)) for i in x.split("."))))
- else:
- return long.__new__(cls, x)
-
- def to_bytes(self):
- """Convert a v4addr object to a raw byte string."""
- return struct.pack("!I", long(self))
-
- @classmethod
- def from_bytes(cls, x):
- """Convert from a raw byte string to a v4addr object."""
- return cls(struct.unpack("!I", x)[0])
-
- def __str__(self):
- """Convert a v4addr object to string format."""
- return socket.inet_ntop(socket.AF_INET, self.to_bytes())
-
-class v6addr(long):
- """IPv6 address.
-
- Derived from long, but supports IPv6 print syntax.
- """
-
- bits = 128
-
- def __new__(cls, x):
- """Construct a v6addr object."""
- if isinstance(x, str):
- return cls.from_bytes(socket.inet_pton(socket.AF_INET6, x))
- else:
- return long.__new__(cls, x)
-
- def to_bytes(self):
- """Convert a v6addr object to a raw byte string."""
- return struct.pack("!QQ", long(self) >> 64, long(self) & 0xFFFFFFFFFFFFFFFF)
-
- @classmethod
- def from_bytes(cls, x):
- """Convert from a raw byte string to a v6addr object."""
- x = struct.unpack("!QQ", x)
- return cls((x[0] << 64) | x[1])
-
- def __str__(self):
- """Convert a v6addr object to string format."""
- return socket.inet_ntop(socket.AF_INET6, self.to_bytes())
diff --git a/rpkid.stable/rpki/left_right.py b/rpkid.stable/rpki/left_right.py
deleted file mode 100644
index 82bf93f4..00000000
--- a/rpkid.stable/rpki/left_right.py
+++ /dev/null
@@ -1,833 +0,0 @@
-"""RPKI "left-right" protocol.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 base64, lxml.etree, time, traceback, os
-import rpki.resource_set, rpki.x509, rpki.sql, rpki.exceptions, rpki.xml_utils
-import rpki.https, rpki.up_down, rpki.relaxng, rpki.sundial, rpki.log, rpki.roa
-import rpki.publication
-
-# Enforce strict checking of XML "sender" field in up-down protocol
-enforce_strict_up_down_xml_sender = False
-
-class left_right_namespace(object):
- """XML namespace parameters for left-right protocol."""
-
- xmlns = "http://www.hactrn.net/uris/rpki/left-right-spec/"
- nsmap = { None : xmlns }
-
-class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistant, left_right_namespace):
- """Virtual class for top-level left-right protocol data elements."""
-
- def self(this):
- """Fetch self object to which this object links."""
- return self_elt.sql_fetch(this.gctx, this.self_id)
-
- def bsc(self):
- """Return BSC object to which this object links."""
- return bsc_elt.sql_fetch(self.gctx, self.bsc_id)
-
- def make_reply_clone_hook(self, r_pdu):
- """Set self_id when cloning."""
- r_pdu.self_id = self.self_id
-
- def serve_fetch_one(self):
- """Find the object on which a get, set, or destroy method should
- operate.
- """
- where = self.sql_template.index + " = %s AND self_id = %s"
- args = (getattr(self, self.sql_template.index), self.self_id)
- r = self.sql_fetch_where1(self.gctx, where, args)
- if r is None:
- raise rpki.exceptions.NotFound, "Lookup failed where " + (where % args)
- return r
-
- def serve_fetch_all(self):
- """Find the objects on which a list method should operate."""
- return self.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
- def unimplemented_control(self, *controls):
- """Uniform handling for unimplemented control operations."""
- unimplemented = [x for x in controls if getattr(self, x, False)]
- if unimplemented:
- raise rpki.exceptions.NotImplementedYet, "Unimplemented control %s" % ", ".join(unimplemented)
-
-class self_elt(data_elt):
- """<self/> element."""
-
- element_name = "self"
- attributes = ("action", "tag", "self_id", "crl_interval", "regen_margin")
- elements = ("bpki_cert", "bpki_glue")
- booleans = ("rekey", "reissue", "revoke", "run_now", "publish_world_now")
-
- sql_template = rpki.sql.template("self", "self_id", "use_hsm", "crl_interval", "regen_margin",
- ("bpki_cert", rpki.x509.X509), ("bpki_glue", rpki.x509.X509))
-
- self_id = None
- use_hsm = False
- crl_interval = None
- regen_margin = None
- bpki_cert = None
- bpki_glue = None
-
- def bscs(self):
- """Fetch all BSC objects that link to this self object."""
- return bsc_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
- def repositories(self):
- """Fetch all repository objects that link to this self object."""
- return repository_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
- def parents(self):
- """Fetch all parent objects that link to this self object."""
- return parent_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
- def children(self):
- """Fetch all child objects that link to this self object."""
- return child_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
- def route_origins(self):
- """Fetch all route_origin objects that link to this self object."""
- return route_origin_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
- def serve_post_save_hook(self, q_pdu, r_pdu):
- """Extra server actions for self_elt."""
- rpki.log.trace()
- if q_pdu.rekey:
- self.serve_rekey()
- if q_pdu.revoke:
- self.serve_revoke()
- self.unimplemented_control("reissue", "run_now", "publish_world_now")
-
- def serve_rekey(self):
- """Handle a left-right rekey action for this self."""
- rpki.log.trace()
- for parent in self.parents():
- parent.serve_rekey()
-
- def serve_revoke(self):
- """Handle a left-right revoke action for this self."""
- rpki.log.trace()
- for parent in self.parents():
- parent.serve_revoke()
-
- def serve_fetch_one(self):
- """Find the self object upon which a get, set, or destroy action
- should operate.
- """
- r = self.sql_fetch(self.gctx, self.self_id)
- if r is None:
- raise rpki.exceptions.NotFound
- return r
-
- def serve_fetch_all(self):
- """Find the self objects upon which a list action should operate.
- This is different from the list action for all other objects,
- where list only works within a given self_id context.
- """
- return self.sql_fetch_all(self.gctx)
-
- def client_poll(self):
- """Run the regular client poll cycle with each of this self's parents in turn."""
-
- rpki.log.trace()
-
- for parent in self.parents():
-
- # This will need a callback when we go event-driven
- r_msg = rpki.up_down.list_pdu.query(parent)
-
- ca_map = dict((ca.parent_resource_class, ca) for ca in parent.cas())
- for rc in r_msg.payload.classes:
- if rc.class_name in ca_map:
- ca = ca_map[rc.class_name]
- del ca_map[rc.class_name]
- ca.check_for_updates(parent, rc)
- else:
- rpki.rpki_engine.ca_obj.create(parent, rc)
- for ca in ca_map.values():
- ca.delete(parent) # CA not listed by parent
- self.gctx.sql.sweep()
-
- def update_children(self):
- """Check for updated IRDB data for all of this self's children and
- issue new certs as necessary. Must handle changes both in
- resources and in expiration date.
- """
-
- rpki.log.trace()
-
- now = rpki.sundial.now()
-
- rsn = now + rpki.sundial.timedelta(seconds = self.regen_margin)
-
- for child in self.children():
- child_certs = child.child_certs()
- if not child_certs:
- continue
-
- # This will require a callback when we go event-driven
- irdb_resources = self.gctx.irdb_query(child.self_id, child.child_id)
-
- for child_cert in child_certs:
- ca_detail = child_cert.ca_detail()
- if ca_detail.state != "active":
- continue
- old_resources = child_cert.cert.get_3779resources()
- new_resources = irdb_resources.intersection(old_resources)
- if old_resources != new_resources or (old_resources.valid_until < rsn and irdb_resources.valid_until > now):
- rpki.log.debug("Need to reissue child certificate SKI %s" % child_cert.cert.gSKI())
- child_cert.reissue(
- ca_detail = ca_detail,
- resources = new_resources)
- elif old_resources.valid_until < now:
- rpki.log.debug("Child certificate SKI %s has expired: cert.valid_until %s, irdb.valid_until %s"
- % (child_cert.cert.gSKI(), old_resources.valid_until, irdb_resources.valid_until))
- ca = ca_detail.ca()
- parent = ca.parent()
- repository = parent.repository()
- child_cert.sql_delete()
- ca_detail.generate_manifest()
- repository.withdraw(child_cert.cert, child_cert.uri(ca))
-
- def regenerate_crls_and_manifests(self):
- """Generate new CRLs and manifests as necessary for all of this
- self's CAs. Extracting nextUpdate from a manifest is hard at the
- moment due to implementation silliness, so for now we generate a
- new manifest whenever we generate a new CRL
-
- This method also cleans up tombstones left behind by revoked
- ca_detail objects, since we're walking through the relevant
- portions of the database anyway.
- """
-
- rpki.log.trace()
-
- now = rpki.sundial.now()
- for parent in self.parents():
- repository = parent.repository()
- for ca in parent.cas():
- for ca_detail in ca.fetch_revoked():
- if now > ca_detail.latest_crl.getNextUpdate():
- ca_detail.delete(ca, repository)
- ca_detail = ca.fetch_active()
- if ca_detail is not None and now > ca_detail.latest_crl.getNextUpdate():
- ca_detail.generate_crl()
- ca_detail.generate_manifest()
-
- def update_roas(self):
- """Generate or update ROAs for this self's route_origin objects."""
-
- for route_origin in self.route_origins():
- route_origin.update_roa()
-
-class bsc_elt(data_elt):
- """<bsc/> (Business Signing Context) element."""
-
- element_name = "bsc"
- attributes = ("action", "tag", "self_id", "bsc_id", "key_type", "hash_alg", "key_length")
- elements = ("signing_cert", "signing_cert_crl", "pkcs10_request")
- booleans = ("generate_keypair",)
-
- sql_template = rpki.sql.template("bsc", "bsc_id", "self_id", "hash_alg",
- ("private_key_id", rpki.x509.RSA),
- ("pkcs10_request", rpki.x509.PKCS10),
- ("signing_cert", rpki.x509.X509),
- ("signing_cert_crl", rpki.x509.CRL))
-
- private_key_id = None
- pkcs10_request = None
- signing_cert = None
- signing_cert_crl = None
-
- def repositories(self):
- """Fetch all repository objects that link to this BSC object."""
- return repository_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,))
-
- def parents(self):
- """Fetch all parent objects that link to this BSC object."""
- return parent_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,))
-
- def children(self):
- """Fetch all child objects that link to this BSC object."""
- return child_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,))
-
- def serve_pre_save_hook(self, q_pdu, r_pdu):
- """Extra server actions for bsc_elt -- handle key generation.
- For now this only allows RSA with SHA-256.
- """
- if q_pdu.generate_keypair:
- assert q_pdu.key_type in (None, "rsa") and q_pdu.hash_alg in (None, "sha256")
- self.private_key_id = rpki.x509.RSA.generate(keylength = q_pdu.key_length or 2048)
- self.pkcs10_request = rpki.x509.PKCS10.create(self.private_key_id)
- r_pdu.pkcs10_request = self.pkcs10_request
-
-class parent_elt(data_elt):
- """<parent/> element."""
-
- element_name = "parent"
- attributes = ("action", "tag", "self_id", "parent_id", "bsc_id", "repository_id",
- "peer_contact_uri", "sia_base", "sender_name", "recipient_name")
- elements = ("bpki_cms_cert", "bpki_cms_glue", "bpki_https_cert", "bpki_https_glue")
- booleans = ("rekey", "reissue", "revoke")
-
- sql_template = rpki.sql.template("parent", "parent_id", "self_id", "bsc_id", "repository_id",
- ("bpki_cms_cert", rpki.x509.X509), ("bpki_cms_glue", rpki.x509.X509),
- ("bpki_https_cert", rpki.x509.X509), ("bpki_https_glue", rpki.x509.X509),
- "peer_contact_uri", "sia_base", "sender_name", "recipient_name")
-
- bpki_cms_cert = None
- bpki_cms_glue = None
- bpki_https_cert = None
- bpki_https_glue = None
-
- def repository(self):
- """Fetch repository object to which this parent object links."""
- return repository_elt.sql_fetch(self.gctx, self.repository_id)
-
- def cas(self):
- """Fetch all CA objects that link to this parent object."""
- return rpki.rpki_engine.ca_obj.sql_fetch_where(self.gctx, "parent_id = %s", (self.parent_id,))
-
- def serve_post_save_hook(self, q_pdu, r_pdu):
- """Extra server actions for parent_elt."""
- if q_pdu.rekey:
- self.serve_rekey()
- if q_pdu.revoke:
- self.serve_revoke()
- self.unimplemented_control("reissue")
-
- def serve_rekey(self):
- """Handle a left-right rekey action for this parent."""
- for ca in self.cas():
- ca.rekey()
-
- def serve_revoke(self):
- """Handle a left-right revoke action for this parent."""
- for ca in self.cas():
- ca.revoke()
-
- def query_up_down(self, q_pdu):
- """Client code for sending one up-down query PDU to this parent.
-
- I haven't figured out yet whether this method should do something
- clever like dispatching via a method in the response PDU payload,
- or just hand back the whole response to the caller. In the long
- run this will have to become event driven with a context object
- that has methods of its own, but as this method is common code for
- several different queries and I don't yet know what the response
- processing looks like, it's too soon to tell what will make sense.
-
- For now, keep this dead simple lock step, rewrite it later.
- """
-
- rpki.log.trace()
-
- bsc = self.bsc()
- if bsc is None:
- raise rpki.exceptions.BSCNotFound, "Could not find BSC %s" % self.bsc_id
-
- q_msg = rpki.up_down.message_pdu.make_query(
- payload = q_pdu,
- sender = self.sender_name,
- recipient = self.recipient_name)
-
- q_cms = rpki.up_down.cms_msg.wrap(q_msg, bsc.private_key_id,
- bsc.signing_cert,
- bsc.signing_cert_crl)
-
- der = rpki.https.client(server_ta = (self.gctx.bpki_ta,
- self.self().bpki_cert, self.self().bpki_glue,
- self.bpki_https_cert, self.bpki_https_glue),
- client_key = bsc.private_key_id,
- client_cert = bsc.signing_cert,
- msg = q_cms,
- url = self.peer_contact_uri)
-
- r_msg = rpki.up_down.cms_msg.unwrap(der, (self.gctx.bpki_ta,
- self.self().bpki_cert, self.self().bpki_glue,
- self.bpki_cms_cert, self.bpki_cms_glue))
-
- r_msg.payload.check_response()
- return r_msg
-
-
-class child_elt(data_elt):
- """<child/> element."""
-
- element_name = "child"
- attributes = ("action", "tag", "self_id", "child_id", "bsc_id")
- elements = ("bpki_cert", "bpki_glue")
- booleans = ("reissue", )
-
- sql_template = rpki.sql.template("child", "child_id", "self_id", "bsc_id",
- ("bpki_cert", rpki.x509.X509),
- ("bpki_glue", rpki.x509.X509))
-
- bpki_cert = None
- bpki_glue = None
- clear_https_ta_cache = False
-
- def child_certs(self, ca_detail = None, ski = None, unique = False):
- """Fetch all child_cert objects that link to this child object."""
- return rpki.rpki_engine.child_cert_obj.fetch(self.gctx, self, ca_detail, ski, unique)
-
- def parents(self):
- """Fetch all parent objects that link to self object to which this child object links."""
- return parent_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
- def ca_from_class_name(self, class_name):
- """Fetch the CA corresponding to an up-down class_name."""
- if not class_name.isdigit():
- raise rpki.exceptions.BadClassNameSyntax, "Bad class name %s" % class_name
- ca = rpki.rpki_engine.ca_obj.sql_fetch(self.gctx, long(class_name))
- if ca is None:
- raise rpki.exceptions.ClassNameUnknown, "Unknown class name %s" % class_name
- parent = ca.parent()
- if self.self_id != parent.self_id:
- raise rpki.exceptions.ClassNameMismatch, "Class name mismatch: child.self_id = %d, parent.self_id = %d" % (self.self_id, parent.self_id)
- return ca
-
- def serve_post_save_hook(self, q_pdu, r_pdu):
- """Extra server actions for child_elt."""
- self.unimplemented_control("reissue")
- if self.clear_https_ta_cache:
- self.gctx.clear_https_ta_cache()
- self.clear_https_ta_cache = False
-
- def endElement(self, stack, name, text):
- """Handle subelements of <child/> element. These require special
- handling because modifying them invalidates the HTTPS trust anchor
- cache.
- """
- rpki.xml_utils.data_elt.endElement(self, stack, name, text)
- if name in self.elements:
- self.clear_https_ta_cache = True
-
- def serve_up_down(self, query):
- """Outer layer of server handling for one up-down PDU from this child."""
-
- rpki.log.trace()
-
- bsc = self.bsc()
- if bsc is None:
- raise rpki.exceptions.BSCNotFound, "Could not find BSC %s" % self.bsc_id
- q_msg = rpki.up_down.cms_msg.unwrap(query, (self.gctx.bpki_ta,
- self.self().bpki_cert, self.self().bpki_glue,
- self.bpki_cert, self.bpki_glue))
- q_msg.payload.gctx = self.gctx
- if enforce_strict_up_down_xml_sender and q_msg.sender != str(self.child_id):
- raise rpki.exceptions.BadSender, "Unexpected XML sender %s" % q_msg.sender
- try:
- r_msg = q_msg.serve_top_level(self)
- except Exception, data:
- rpki.log.error(traceback.format_exc())
- r_msg = q_msg.serve_error(data)
- #
- # Exceptions from this point on are problematic, as we have no
- # sane way of reporting errors in the error reporting mechanism.
- # May require refactoring, ignore the issue for now.
- #
- r_cms = rpki.up_down.cms_msg.wrap(r_msg, bsc.private_key_id,
- bsc.signing_cert, bsc.signing_cert_crl)
- return r_cms
-
-class repository_elt(data_elt):
- """<repository/> element."""
-
- element_name = "repository"
- attributes = ("action", "tag", "self_id", "repository_id", "bsc_id", "peer_contact_uri")
- elements = ("bpki_cms_cert", "bpki_cms_glue", "bpki_https_cert", "bpki_https_glue")
-
- sql_template = rpki.sql.template("repository", "repository_id", "self_id", "bsc_id", "peer_contact_uri",
- ("bpki_cms_cert", rpki.x509.X509), ("bpki_cms_glue", rpki.x509.X509),
- ("bpki_https_cert", rpki.x509.X509), ("bpki_https_glue", rpki.x509.X509))
-
- bpki_cms_cert = None
- bpki_cms_glue = None
- bpki_https_cert = None
- bpki_https_glue = None
-
- use_pubd = True
-
- def parents(self):
- """Fetch all parent objects that link to this repository object."""
- return parent_elt.sql_fetch_where(self.gctx, "repository_id = %s", (self.repository_id,))
-
- @staticmethod
- def uri_to_filename(base, uri):
- """Convert a URI to a filename. [TEMPORARY]"""
- if not uri.startswith("rsync://"):
- raise rpki.exceptions.BadURISyntax
- filename = base + uri[len("rsync://"):]
- if filename.find("//") >= 0 or filename.find("/../") >= 0 or filename.endswith("/.."):
- raise rpki.exceptions.BadURISyntax
- return filename
-
- @classmethod
- def object_write(cls, base, uri, obj):
- """Write an object to disk. [TEMPORARY]"""
- rpki.log.trace()
- filename = cls.uri_to_filename(base, uri)
- dirname = os.path.dirname(filename)
- if not os.path.isdir(dirname):
- os.makedirs(dirname)
- f = open(filename, "wb")
- f.write(obj.get_DER())
- f.close()
-
- @classmethod
- def object_delete(cls, base, uri):
- """Delete an object from disk. [TEMPORARY]"""
- rpki.log.trace()
- os.remove(cls.uri_to_filename(base, uri))
-
- def call_pubd(self, *pdus):
- """Send a message to publication daemon and return the response."""
- rpki.log.trace()
- bsc = self.bsc()
- q_msg = rpki.publication.msg(pdus)
- q_msg.type = "query"
- q_cms = rpki.publication.cms_msg.wrap(q_msg, bsc.private_key_id, bsc.signing_cert, bsc.signing_cert_crl)
- bpki_ta_path = (self.gctx.bpki_ta, self.self().bpki_cert, self.self().bpki_glue, self.bpki_https_cert, self.bpki_https_glue)
- r_cms = rpki.https.client(
- client_key = bsc.private_key_id,
- client_cert = bsc.signing_cert,
- server_ta = bpki_ta_path,
- url = self.peer_contact_uri,
- msg = q_cms)
- r_msg = rpki.publication.cms_msg.unwrap(r_cms, bpki_ta_path)
- assert len(r_msg) == 1
- return r_msg[0]
-
- def publish(self, obj, uri):
- """Placeholder for publication operation. [TEMPORARY]"""
- rpki.log.trace()
- rpki.log.info("Publishing %s as %s" % (repr(obj), repr(uri)))
- if self.use_pubd:
- self.call_pubd(rpki.publication.obj2elt[type(obj)].make_pdu(action = "publish", uri = uri, payload = obj))
- else:
- self.object_write(self.gctx.publication_kludge_base, uri, obj)
-
- def withdraw(self, obj, uri):
- """Placeholder for publication withdrawal operation. [TEMPORARY]"""
- rpki.log.trace()
- rpki.log.info("Withdrawing %s from at %s" % (repr(obj), repr(uri)))
- if self.use_pubd:
- self.call_pubd(rpki.publication.obj2elt[type(obj)].make_pdu(action = "withdraw", uri = uri))
- else:
- self.object_delete(self.gctx.publication_kludge_base, uri)
-
-class route_origin_elt(data_elt):
- """<route_origin/> element."""
-
- element_name = "route_origin"
- attributes = ("action", "tag", "self_id", "route_origin_id", "as_number", "ipv4", "ipv6")
- booleans = ("suppress_publication",)
-
- sql_template = rpki.sql.template("route_origin", "route_origin_id", "ca_detail_id",
- "self_id", "as_number",
- ("roa", rpki.x509.ROA),
- ("cert", rpki.x509.X509))
-
- ca_detail_id = None
- cert = None
- roa = None
-
- ## @var publish_ee_separately
- # Whether to publish the ROA EE certificate separately from the ROA.
- publish_ee_separately = False
-
- def sql_fetch_hook(self):
- """Extra SQL fetch actions for route_origin_elt -- handle prefix list."""
- self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4.from_sql(
- self.gctx.sql,
- """
- SELECT address, prefixlen, max_prefixlen FROM route_origin_prefix
- WHERE route_origin_id = %s AND address NOT LIKE '%:%'
- """, (self.route_origin_id,))
- self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6.from_sql(
- self.gctx.sql,
- """
- SELECT address, prefixlen, max_prefixlen FROM route_origin_prefix
- WHERE route_origin_id = %s AND address LIKE '%:%'
- """, (self.route_origin_id,))
-
- def sql_insert_hook(self):
- """Extra SQL insert actions for route_origin_elt -- handle address ranges."""
- if self.ipv4 or self.ipv6:
- self.gctx.sql.executemany("""
- INSERT route_origin_prefix (route_origin_id, address, prefixlen, max_prefixlen)
- VALUES (%s, %s, %s, %s)""",
- ((self.route_origin_id, x.address, x.prefixlen, x.max_prefixlen)
- for x in (self.ipv4 or []) + (self.ipv6 or [])))
-
- def sql_delete_hook(self):
- """Extra SQL delete actions for route_origin_elt -- handle address ranges."""
- self.gctx.sql.execute("DELETE FROM route_origin_prefix WHERE route_origin_id = %s", (self.route_origin_id,))
-
- def ca_detail(self):
- """Fetch all ca_detail objects that link to this route_origin object."""
- return rpki.rpki_engine.ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id)
-
- def serve_post_save_hook(self, q_pdu, r_pdu):
- """Extra server actions for route_origin_elt."""
- self.unimplemented_control("suppress_publication")
-
- def startElement(self, stack, name, attrs):
- """Handle <route_origin/> element. This requires special
- processing due to the data types of some of the attributes.
- """
- assert name == "route_origin", "Unexpected name %s, stack %s" % (name, stack)
- self.read_attrs(attrs)
- if self.as_number is not None:
- self.as_number = long(self.as_number)
- if self.ipv4 is not None:
- self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4(self.ipv4)
- if self.ipv6 is not None:
- self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(self.ipv6)
-
- def update_roa(self):
- """Bring this route_origin's ROA up to date if necesssary."""
-
- if self.roa is None:
- return self.generate_roa()
-
- ca_detail = self.ca_detail()
-
- if ca_detail is None or ca_detail.state != "active":
- return self.regenerate_roa()
-
- regen_margin = rpki.sundial.timedelta(seconds = self.self().regen_margin)
-
- if rpki.sundial.now() + regen_margin > self.cert.getNotAfter():
- return self.regenerate_roa()
-
- ca_resources = ca_detail.latest_ca_cert.get_3779resources()
- ee_resources = self.cert.get_3779resources()
-
- if ee_resources.oversized(ca_resources):
- return self.regenerate_roa()
-
- 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:
- return self.regenerate_roa()
-
- def generate_roa(self):
- """Generate a ROA based on this <route_origin/> object.
-
- 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()
-
- sia = ((rpki.oids.name2oid["id-ad-signedObject"], ("uri", self.roa_uri(ca, keypair))),)
-
- self.cert = ca_detail.issue_ee(ca, resources, keypair.get_RSApublic(), sia = sia)
- self.roa = rpki.x509.ROA.build(self.as_number, self.ipv4, self.ipv6, keypair, (self.cert,))
- self.ca_detail_id = ca_detail.ca_detail_id
- self.sql_store()
-
- repository = ca.parent().repository()
- repository.publish(self.roa, self.roa_uri(ca))
- if self.publish_ee_separately:
- repository.publish(self.cert, self.ee_uri(ca))
- ca_detail.generate_manifest()
-
- def withdraw_roa(self, regenerate = False):
- """Withdraw ROA associated with this route_origin.
-
- 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(ca)
- ee_uri = self.ee_uri(ca)
-
- if ca_detail.state != 'active':
- self.ca_detail_id = None
- if regenerate:
- self.generate_roa()
-
- 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)
- if self.publish_ee_separately:
- repository.withdraw(cert, ee_uri)
- self.gctx.sql.sweep()
- ca_detail.generate_crl()
- ca_detail.generate_manifest()
-
- def regenerate_roa(self):
- """Reissue ROA associated with this route_origin."""
- if self.ca_detail() is None:
- self.generate_roa()
- else:
- self.withdraw_roa(regenerate = True)
-
- def roa_uri(self, ca, key = None):
- """Return the publication URI for this route_origin's ROA."""
- return 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 route_origin's ROA."""
- return (key or self.cert).gSKI() + ".roa"
-
- def ee_uri_tail(self):
- """Return the tail (filename) portion of the URI for this route_origin's ROA's EE certificate."""
- return self.cert.gSKI() + ".cer"
-
- def ee_uri(self, ca):
- """Return the publication URI for this route_origin's ROA's EE certificate."""
- return ca.sia_uri + self.ee_uri_tail()
-
-class list_resources_elt(rpki.xml_utils.base_elt, left_right_namespace):
- """<list_resources/> element."""
-
- element_name = "list_resources"
- attributes = ("self_id", "tag", "child_id", "valid_until", "asn", "ipv4", "ipv6", "subject_name")
- valid_until = None
-
- def startElement(self, stack, name, attrs):
- """Handle <list_resources/> element. This requires special
- handling due to the data types of some of the attributes.
- """
- assert name == "list_resources", "Unexpected name %s, stack %s" % (name, stack)
- self.read_attrs(attrs)
- if isinstance(self.valid_until, str):
- self.valid_until = rpki.sundial.datetime.fromXMLtime(self.valid_until)
- if self.asn is not None:
- self.asn = rpki.resource_set.resource_set_as(self.asn)
- if self.ipv4 is not None:
- self.ipv4 = rpki.resource_set.resource_set_ipv4(self.ipv4)
- if self.ipv6 is not None:
- self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6)
-
- def toXML(self):
- """Generate <list_resources/> element. This requires special
- handling due to the data types of some of the attributes.
- """
- elt = self.make_elt()
- if isinstance(self.valid_until, int):
- elt.set("valid_until", self.valid_until.toXMLtime())
- return elt
-
-class report_error_elt(rpki.xml_utils.base_elt, left_right_namespace):
- """<report_error/> element."""
-
- element_name = "report_error"
- attributes = ("tag", "self_id", "error_code")
-
- @classmethod
- def from_exception(cls, exc, self_id = None):
- """Generate a <report_error/> element from an exception."""
- self = cls()
- self.self_id = self_id
- self.error_code = exc.__class__.__name__
- return self
-
-class msg(rpki.xml_utils.msg, left_right_namespace):
- """Left-right PDU."""
-
- ## @var version
- # Protocol version
- version = 1
-
- ## @var pdus
- # Dispatch table of PDUs for this protocol.
- pdus = dict((x.element_name, x)
- for x in (self_elt, child_elt, parent_elt, bsc_elt, repository_elt,
- route_origin_elt, list_resources_elt, report_error_elt))
-
- def serve_top_level(self, gctx):
- """Serve one msg PDU."""
- r_msg = self.__class__()
- r_msg.type = "reply"
- for q_pdu in self:
- q_pdu.gctx = gctx
- q_pdu.serve_dispatch(r_msg)
- return r_msg
-
-class sax_handler(rpki.xml_utils.sax_handler):
- """SAX handler for Left-Right protocol."""
-
- pdu = msg
- name = "msg"
- version = "1"
-
-class cms_msg(rpki.x509.XML_CMS_object):
- """Class to hold a CMS-signed left-right PDU."""
-
- encoding = "us-ascii"
- schema = rpki.relaxng.left_right
- saxify = sax_handler.saxify
diff --git a/rpkid.stable/rpki/log.py b/rpkid.stable/rpki/log.py
deleted file mode 100644
index efaba10c..00000000
--- a/rpkid.stable/rpki/log.py
+++ /dev/null
@@ -1,58 +0,0 @@
-"""Logging facilities for RPKI libraries.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 syslog, traceback
-
-## @var enable_trace
-# Whether call tracing is enabled.
-
-enable_trace = False
-
-def init(ident = "rpki", flags = syslog.LOG_PID | syslog.LOG_PERROR, facility = syslog.LOG_DAEMON):
- """Initialize logging system."""
-
- return syslog.openlog(ident, flags, facility)
-
-def set_trace(trace):
- """Enable or disable call tracing."""
-
- global enable_trace
- enable_trace = trace
-
-class logger(object):
- """Closure for logging."""
-
- def __init__(self, priority):
- self.priority = priority
-
- def __call__(self, message):
- return syslog.syslog(self.priority, message)
-
-error = logger(syslog.LOG_ERR)
-warn = logger(syslog.LOG_WARNING)
-note = logger(syslog.LOG_NOTICE)
-info = logger(syslog.LOG_INFO)
-debug = logger(syslog.LOG_DEBUG)
-
-def trace():
- """Execution trace -- where are we now, and whence came we here?"""
-
- if enable_trace:
- bt = traceback.extract_stack(limit = 3)
- return debug("[%s() at %s:%d from %s:%d]" % (bt[1][2], bt[1][0], bt[1][1], bt[0][0], bt[0][1]))
diff --git a/rpkid.stable/rpki/manifest.py b/rpkid.stable/rpki/manifest.py
deleted file mode 100644
index 97b1cc71..00000000
--- a/rpkid.stable/rpki/manifest.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""Signed manifests. This is just the ASN.1 encoder, the rest is in
-rpki.x509 with the rest of the DER_object code.
-
-Note that rpki.x509.SignedManifest implements the signed manifest;
-the structures here are just the payload of the CMS eContent field.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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.
-"""
-
-from POW._der import *
-
-class FileAndHash(Sequence):
- def __init__(self, optional=0, default=''):
- self.file = IA5String()
- self.hash = AltBitString()
- contents = [ self.file, self.hash ]
- Sequence.__init__(self, contents, optional, default)
-
-class FilesAndHashes(SequenceOf):
- def __init__(self, optional=0, default=''):
- SequenceOf.__init__(self, FileAndHash, optional, default)
-
-class Manifest(Sequence):
- def __init__(self, optional=0, default=''):
- self.version = Integer()
- self.explicitVersion = Explicit(CLASS_CONTEXT, FORM_CONSTRUCTED, 0, self.version, 0, 'oAMCAQA=')
- self.manifestNumber = Integer()
- self.thisUpdate = GeneralizedTime()
- self.nextUpdate = GeneralizedTime()
- self.fileHashAlg = Oid()
- self.fileList = FilesAndHashes()
-
- contents = [ self.explicitVersion,
- self.manifestNumber,
- self.thisUpdate,
- self.nextUpdate,
- self.fileHashAlg,
- self.fileList ]
- Sequence.__init__(self, contents, optional, default)
diff --git a/rpkid.stable/rpki/oids.py b/rpkid.stable/rpki/oids.py
deleted file mode 100644
index 5824ad17..00000000
--- a/rpkid.stable/rpki/oids.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""OID database.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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.
-"""
-
-## @var oid2name
-# Mapping table of OIDs to conventional string names.
-
-oid2name = {
- (1, 2, 840, 113549, 1, 1, 11) : "sha256WithRSAEncryption",
- (1, 2, 840, 113549, 1, 1, 12) : "sha384WithRSAEncryption",
- (1, 2, 840, 113549, 1, 1, 13) : "sha512WithRSAEncryption",
- (1, 2, 840, 113549, 1, 7, 1) : "id-data",
- (1, 2, 840, 113549, 1, 9, 16) : "id-smime",
- (1, 2, 840, 113549, 1, 9, 16, 1) : "id-ct",
- (1, 2, 840, 113549, 1, 9, 16, 1, 24) : "id-ct-routeOriginAttestation",
- (1, 2, 840, 113549, 1, 9, 16, 1, 26) : "id-ct-rpkiManifest",
- (1, 2, 840, 113549, 1, 9, 16, 1, 28) : "id-ct-xml",
- (1, 3, 6, 1, 5, 5, 7, 1, 1) : "authorityInfoAccess",
- (1, 3, 6, 1, 5, 5, 7, 1, 11) : "subjectInfoAccess",
- (1, 3, 6, 1, 5, 5, 7, 1, 7) : "sbgp-ipAddrBlock",
- (1, 3, 6, 1, 5, 5, 7, 1, 8) : "sbgp-autonomousSysNum",
- (1, 3, 6, 1, 5, 5, 7, 14, 2) : "id-cp-ipAddr-asNumber",
- (1, 3, 6, 1, 5, 5, 7, 48, 2) : "id-ad-caIssuers",
- (1, 3, 6, 1, 5, 5, 7, 48, 5) : "id-ad-caRepository",
- (1, 3, 6, 1, 5, 5, 7, 48, 9) : "id-ad-signedObjectRepository",
- (1, 3, 6, 1, 5, 5, 7, 48, 10) : "id-ad-rpkiManifest",
- (1, 3, 6, 1, 5, 5, 7, 48, 11) : "id-ad-signedObject",
- (2, 16, 840, 1, 101, 3, 4, 2, 1) : "id-sha256",
- (2, 5, 29, 14) : "subjectKeyIdentifier",
- (2, 5, 29, 15) : "keyUsage",
- (2, 5, 29, 19) : "basicConstraints",
- (2, 5, 29, 20) : "cRLNumber",
- (2, 5, 29, 31) : "cRLDistributionPoints",
- (2, 5, 29, 32) : "certificatePolicies",
- (2, 5, 29, 35) : "authorityKeyIdentifier",
- (2, 5, 4, 3) : "commonName",
-}
-
-## @var name2oid
-# Mapping table of string names to OIDs
-
-name2oid = dict((v,k) for k,v in oid2name.items())
diff --git a/rpkid.stable/rpki/publication.py b/rpkid.stable/rpki/publication.py
deleted file mode 100644
index fe52b631..00000000
--- a/rpkid.stable/rpki/publication.py
+++ /dev/null
@@ -1,282 +0,0 @@
-"""RPKI "publication" protocol.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 base64, lxml.etree, time, traceback, os
-import rpki.resource_set, rpki.x509, rpki.sql, rpki.exceptions, rpki.xml_utils
-import rpki.https, rpki.up_down, rpki.relaxng, rpki.sundial, rpki.log, rpki.roa
-
-class publication_namespace(object):
- """XML namespace parameters for publication protocol."""
-
- xmlns = "http://www.hactrn.net/uris/rpki/publication-spec/"
- nsmap = { None : xmlns }
-
-class control_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistant, publication_namespace):
- """Virtual class for control channel objects."""
-
- def serve_dispatch(self, r_msg, client):
- """Action dispatch handler. This needs special handling because
- we need to make sure that this PDU arrived via the control channel.
- """
- if client is not None:
- raise rpki.exceptions.BadQuery, "Control query received on client channel"
- rpki.xml_utils.data_elt.serve_dispatch(self, r_msg)
-
-class config_elt(control_elt):
- """<config/> element. This is a little weird because there should
- never be more than one row in the SQL config table, but we have to
- put the BPKI CRL somewhere and SQL is the least bad place available.
-
- So we reuse a lot of the SQL machinery, but we nail config_id at 1,
- we don't expose it in the XML protocol, and we only support the get
- and set actions.
- """
-
- attributes = ("action", "tag")
- element_name = "config"
- elements = ("bpki_crl",)
-
- sql_template = rpki.sql.template("config", "config_id", ("bpki_crl", rpki.x509.CRL))
-
- wired_in_config_id = 1
-
- def startElement(self, stack, name, attrs):
- """StartElement() handler for config object. This requires
- special handling because of the weird way we treat config_id.
- """
- control_elt.startElement(self, stack, name, attrs)
- self.config_id = self.wired_in_config_id
-
- @classmethod
- def fetch(cls, gctx):
- """Fetch the config object from SQL. This requires special
- handling because of the weird way we treat config_id.
- """
- return cls.sql_fetch(gctx, cls.wired_in_config_id)
-
- def serve_set(self, r_msg):
- """Handle a set action. This requires special handling because
- config we don't support the create method.
- """
- if self.sql_fetch(self.gctx, self.config_id) is None:
- control_elt.serve_create(self, r_msg)
- else:
- control_elt.serve_set(self, r_msg)
-
- def serve_fetch_one(self):
- """Find the config object on which a get or set method should
- operate.
- """
- r = self.sql_fetch(self.gctx, self.config_id)
- if r is None:
- raise rpki.exceptions.NotFound
- return r
-
-class client_elt(control_elt):
- """<client/> element."""
-
- element_name = "client"
- attributes = ("action", "tag", "client_id", "base_uri")
- elements = ("bpki_cert", "bpki_glue")
-
- sql_template = rpki.sql.template("client", "client_id", "base_uri", ("bpki_cert", rpki.x509.X509), ("bpki_glue", rpki.x509.X509))
-
- base_uri = None
- bpki_cert = None
- bpki_glue = None
-
- clear_https_ta_cache = False
-
- def endElement(self, stack, name, text):
- """Handle subelements of <client/> element. These require special
- handling because modifying them invalidates the HTTPS trust anchor
- cache.
- """
- control_elt.endElement(self, stack, name, text)
- if name in self.elements:
- self.clear_https_ta_cache = True
-
- def serve_post_save_hook(self, q_pdu, r_pdu):
- """Extra server actions for client_elt."""
- if self.clear_https_ta_cache:
- self.gctx.clear_https_ta_cache()
- self.clear_https_ta_cache = False
-
- def serve_fetch_one(self):
- """Find the client object on which a get, set, or destroy method
- should operate.
- """
- r = self.sql_fetch(self.gctx, self.client_id)
- if r is None:
- raise rpki.exceptions.NotFound
- return r
-
- def serve_fetch_all(self):
- """Find client objects on which a list method should operate."""
- return self.sql_fetch_all(self.gctx)
-
- def check_allowed_uri(self, uri):
- if not uri.startswith(self.base_uri):
- raise rpki.exceptions.ForbiddenURI
-
-class publication_object_elt(rpki.xml_utils.base_elt, publication_namespace):
- """Virtual class for publishable objects. These have very similar
- syntax, differences lie in underlying datatype and methods. XML
- methods are a little different from the pattern used for objects
- that support the create/set/get/list/destroy actions, but
- publishable objects don't go in SQL either so these classes would be
- different in any case.
- """
-
- attributes = ("action", "tag", "client_id", "uri")
- payload = None
-
- def endElement(self, stack, name, text):
- """Handle a publishable element element."""
- assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack)
- if text:
- self.payload = self.payload_type(Base64 = text)
- stack.pop()
-
- def toXML(self):
- """Generate XML element for publishable object."""
- elt = self.make_elt()
- if self.payload:
- elt.text = base64.b64encode(self.payload.get_DER())
- return elt
-
- def serve_dispatch(self, r_msg, client):
- """Action dispatch handler."""
- if client is None:
- raise rpki.exceptions.BadQuery, "Client query received on control channel"
- dispatch = { "publish" : self.serve_publish,
- "withdraw" : self.serve_withdraw }
- if self.action not in dispatch:
- raise rpki.exceptions.BadQuery, "Unexpected query: action %s" % self.action
- client.check_allowed_uri(self.uri)
- dispatch[self.action]()
- r_pdu = self.__class__()
- r_pdu.action = self.action
- r_pdu.tag = self.tag
- r_pdu.uri = self.uri
- r_msg.append(r_pdu)
-
- def serve_publish(self):
- """Publish an object."""
- rpki.log.info("Publishing %s as %s" % (repr(self.payload), repr(self.uri)))
- filename = self.uri_to_filename()
- dirname = os.path.dirname(filename)
- if not os.path.isdir(dirname):
- os.makedirs(dirname)
- f = open(filename, "wb")
- f.write(self.payload.get_DER())
- f.close()
-
- def serve_withdraw(self):
- """Withdraw an object."""
- rpki.log.info("Withdrawing %s" % repr(self.uri))
- os.remove(self.uri_to_filename())
-
- def uri_to_filename(self):
- """Convert a URI to a local filename."""
- if not self.uri.startswith("rsync://"):
- raise rpki.exceptions.BadURISyntax
- filename = self.gctx.publication_base + self.uri[len("rsync://"):]
- if filename.find("//") >= 0 or filename.find("/../") >= 0 or filename.endswith("/.."):
- raise rpki.exceptions.BadURISyntax
- return filename
-
-class certificate_elt(publication_object_elt):
- """<certificate/> element."""
-
- element_name = "certificate"
- payload_type = rpki.x509.X509
-
-class crl_elt(publication_object_elt):
- """<crl/> element."""
-
- element_name = "crl"
- payload_type = rpki.x509.CRL
-
-class manifest_elt(publication_object_elt):
- """<manifest/> element."""
-
- element_name = "manifest"
- payload_type = rpki.x509.SignedManifest
-
-class roa_elt(publication_object_elt):
- """<roa/> element."""
-
- element_name = "roa"
- payload_type = rpki.x509.ROA
-
-## @var obj2elt
-# Map of data types to publication element wrapper types
-
-obj2elt = dict((e.payload_type, e) for e in (certificate_elt, crl_elt, manifest_elt, roa_elt))
-
-class report_error_elt(rpki.xml_utils.base_elt, publication_namespace):
- """<report_error/> element."""
-
- element_name = "report_error"
- attributes = ("tag", "error_code")
-
- @classmethod
- def from_exception(cls, exc):
- """Generate a <report_error/> element from an exception."""
- self = cls()
- self.error_code = exc.__class__.__name__
- return self
-
-class msg(rpki.xml_utils.msg, publication_namespace):
- """Publication PDU."""
-
- ## @var version
- # Protocol version
- version = 1
-
- ## @var pdus
- # Dispatch table of PDUs for this protocol.
- pdus = dict((x.element_name, x)
- for x in (config_elt, client_elt, certificate_elt, crl_elt, manifest_elt, roa_elt, report_error_elt))
-
- def serve_top_level(self, gctx, client):
- """Serve one msg PDU."""
- if self.type != "query":
- raise rpki.exceptions.BadQuery, "Message type is not query"
- r_msg = self.__class__()
- r_msg.type = "reply"
- for q_pdu in self:
- q_pdu.gctx = gctx
- q_pdu.serve_dispatch(r_msg, client)
- return r_msg
-
-class sax_handler(rpki.xml_utils.sax_handler):
- """SAX handler for publication protocol."""
-
- pdu = msg
- name = "msg"
- version = "1"
-
-class cms_msg(rpki.x509.XML_CMS_object):
- """Class to hold a CMS-signed publication PDU."""
-
- encoding = "us-ascii"
- schema = rpki.relaxng.publication
- saxify = sax_handler.saxify
diff --git a/rpkid.stable/rpki/relaxng.py b/rpkid.stable/rpki/relaxng.py
deleted file mode 100644
index c66d965a..00000000
--- a/rpkid.stable/rpki/relaxng.py
+++ /dev/null
@@ -1,1699 +0,0 @@
-# Automatically generated, do not edit.
-
-import lxml.etree
-
-## @var left_right
-## Parsed RelaxNG left_right schema
-left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" encoding="UTF-8"?>
-<!--
- $Id: left-right-schema.rnc 1835 2008-06-02 23:43:01Z sra $
-
- RelaxNG Schema for RPKI left-right protocol.
-
- libxml2 (including xmllint) only groks the XML syntax of RelaxNG, so
- run the compact syntax through trang to get XML syntax.
--->
-<grammar ns="http://www.hactrn.net/uris/rpki/left-right-spec/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
- <!-- Top level PDU -->
- <start>
- <element name="msg">
- <attribute name="version">
- <data type="positiveInteger">
- <param name="maxInclusive">1</param>
- </data>
- </attribute>
- <choice>
- <group>
- <attribute name="type">
- <value>query</value>
- </attribute>
- <zeroOrMore>
- <ref name="query_elt"/>
- </zeroOrMore>
- </group>
- <group>
- <attribute name="type">
- <value>reply</value>
- </attribute>
- <zeroOrMore>
- <ref name="reply_elt"/>
- </zeroOrMore>
- </group>
- </choice>
- </element>
- </start>
- <!-- PDUs allowed in a query -->
- <define name="query_elt" combine="choice">
- <ref name="self_query"/>
- </define>
- <define name="query_elt" combine="choice">
- <ref name="bsc_query"/>
- </define>
- <define name="query_elt" combine="choice">
- <ref name="parent_query"/>
- </define>
- <define name="query_elt" combine="choice">
- <ref name="child_query"/>
- </define>
- <define name="query_elt" combine="choice">
- <ref name="repository_query"/>
- </define>
- <define name="query_elt" combine="choice">
- <ref name="route_origin_query"/>
- </define>
- <define name="query_elt" combine="choice">
- <ref name="list_resources_query"/>
- </define>
- <!-- PDUs allowed in a reply -->
- <define name="reply_elt" combine="choice">
- <ref name="self_reply"/>
- </define>
- <define name="reply_elt" combine="choice">
- <ref name="bsc_reply"/>
- </define>
- <define name="reply_elt" combine="choice">
- <ref name="parent_reply"/>
- </define>
- <define name="reply_elt" combine="choice">
- <ref name="child_reply"/>
- </define>
- <define name="reply_elt" combine="choice">
- <ref name="repository_reply"/>
- </define>
- <define name="reply_elt" combine="choice">
- <ref name="route_origin_reply"/>
- </define>
- <define name="reply_elt" combine="choice">
- <ref name="list_resources_reply"/>
- </define>
- <define name="reply_elt" combine="choice">
- <ref name="report_error_reply"/>
- </define>
- <!-- Tag attributes for bulk operations -->
- <define name="tag">
- <optional>
- <attribute name="tag">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- </optional>
- </define>
- <!--
- Combinations of action and type attributes used in later definitions.
- The same patterns repeat in most of the elements in this protocol.
- -->
- <define name="ctl_create">
- <attribute name="action">
- <value>create</value>
- </attribute>
- <ref name="tag"/>
- </define>
- <define name="ctl_set">
- <attribute name="action">
- <value>set</value>
- </attribute>
- <ref name="tag"/>
- </define>
- <define name="ctl_get">
- <attribute name="action">
- <value>get</value>
- </attribute>
- <ref name="tag"/>
- </define>
- <define name="ctl_list">
- <attribute name="action">
- <value>list</value>
- </attribute>
- <ref name="tag"/>
- </define>
- <define name="ctl_destroy">
- <attribute name="action">
- <value>destroy</value>
- </attribute>
- <ref name="tag"/>
- </define>
- <!-- Base64 encoded DER stuff -->
- <define name="base64">
- <data type="base64Binary">
- <param name="maxLength">512000</param>
- </data>
- </define>
- <!-- Base definition for all fields that are really just SQL primary indices -->
- <define name="sql_id">
- <data type="nonNegativeInteger"/>
- </define>
- <!-- URIs -->
- <define name="uri">
- <data type="anyURI">
- <param name="maxLength">4096</param>
- </data>
- </define>
- <!-- Name fields imported from up-down protocol -->
- <define name="up_down_name">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </define>
- <!-- Resource lists -->
- <define name="asn_list">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9]*</param>
- </data>
- </define>
- <define name="ipv4_list">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9/.]*</param>
- </data>
- </define>
- <define name="ipv6_list">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9/:a-fA-F]*</param>
- </data>
- </define>
- <!-- <self/> element -->
- <define name="self_bool">
- <optional>
- <attribute name="rekey">
- <value>yes</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="reissue">
- <value>yes</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="revoke">
- <value>yes</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="run_now">
- <value>yes</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="publish_world_now">
- <value>yes</value>
- </attribute>
- </optional>
- </define>
- <define name="self_payload">
- <optional>
- <attribute name="use_hsm">
- <choice>
- <value>yes</value>
- <value>no</value>
- </choice>
- </attribute>
- </optional>
- <optional>
- <attribute name="crl_interval">
- <data type="positiveInteger"/>
- </attribute>
- </optional>
- <optional>
- <attribute name="regen_margin">
- <data type="positiveInteger"/>
- </attribute>
- </optional>
- <optional>
- <element name="bpki_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_glue">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="self_id">
- <attribute name="self_id">
- <ref name="sql_id"/>
- </attribute>
- </define>
- <define name="self_query" combine="choice">
- <element name="self">
- <ref name="ctl_create"/>
- <ref name="self_bool"/>
- <ref name="self_payload"/>
- </element>
- </define>
- <define name="self_reply" combine="choice">
- <element name="self">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="self_query" combine="choice">
- <element name="self">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="self_bool"/>
- <ref name="self_payload"/>
- </element>
- </define>
- <define name="self_reply" combine="choice">
- <element name="self">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="self_query" combine="choice">
- <element name="self">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="self_reply" combine="choice">
- <element name="self">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="self_payload"/>
- </element>
- </define>
- <define name="self_query" combine="choice">
- <element name="self">
- <ref name="ctl_list"/>
- </element>
- </define>
- <define name="self_reply" combine="choice">
- <element name="self">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- <ref name="self_payload"/>
- </element>
- </define>
- <define name="self_query" combine="choice">
- <element name="self">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="self_reply" combine="choice">
- <element name="self">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- </element>
- </define>
- <!-- <bsc/> element. Key parameters hardwired for now. -->
- <define name="bsc_bool">
- <optional>
- <attribute name="generate_keypair">
- <value>yes</value>
- </attribute>
- <optional>
- <attribute name="key_type">
- <value>rsa</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="hash_alg">
- <value>sha256</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="key_length">
- <value>2048</value>
- </attribute>
- </optional>
- </optional>
- </define>
- <define name="bsc_id">
- <attribute name="bsc_id">
- <ref name="sql_id"/>
- </attribute>
- </define>
- <define name="bsc_payload">
- <optional>
- <element name="signing_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="signing_cert_crl">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="bsc_pkcs10">
- <optional>
- <element name="pkcs10_request">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="bsc_query" combine="choice">
- <element name="bsc">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="bsc_bool"/>
- <ref name="bsc_payload"/>
- </element>
- </define>
- <define name="bsc_reply" combine="choice">
- <element name="bsc">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- <ref name="bsc_pkcs10"/>
- </element>
- </define>
- <define name="bsc_query" combine="choice">
- <element name="bsc">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- <ref name="bsc_bool"/>
- <ref name="bsc_payload"/>
- </element>
- </define>
- <define name="bsc_reply" combine="choice">
- <element name="bsc">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- <ref name="bsc_pkcs10"/>
- </element>
- </define>
- <define name="bsc_query" combine="choice">
- <element name="bsc">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- </element>
- </define>
- <define name="bsc_reply" combine="choice">
- <element name="bsc">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- <ref name="bsc_payload"/>
- <ref name="bsc_pkcs10"/>
- </element>
- </define>
- <define name="bsc_query" combine="choice">
- <element name="bsc">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="bsc_reply" combine="choice">
- <element name="bsc">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- <ref name="bsc_payload"/>
- <ref name="bsc_pkcs10"/>
- </element>
- </define>
- <define name="bsc_query" combine="choice">
- <element name="bsc">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- </element>
- </define>
- <define name="bsc_reply" combine="choice">
- <element name="bsc">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="bsc_id"/>
- </element>
- </define>
- <!-- <parent/> element -->
- <define name="parent_id">
- <attribute name="parent_id">
- <ref name="sql_id"/>
- </attribute>
- </define>
- <define name="parent_bool">
- <optional>
- <attribute name="rekey">
- <value>yes</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="reissue">
- <value>yes</value>
- </attribute>
- </optional>
- <optional>
- <attribute name="revoke">
- <value>yes</value>
- </attribute>
- </optional>
- </define>
- <define name="parent_payload">
- <optional>
- <attribute name="peer_contact_uri">
- <ref name="uri"/>
- </attribute>
- </optional>
- <optional>
- <attribute name="sia_base">
- <ref name="uri"/>
- </attribute>
- </optional>
- <optional>
- <ref name="bsc_id"/>
- </optional>
- <optional>
- <ref name="repository_id"/>
- </optional>
- <optional>
- <attribute name="sender_name">
- <ref name="up_down_name"/>
- </attribute>
- </optional>
- <optional>
- <attribute name="recipient_name">
- <ref name="up_down_name"/>
- </attribute>
- </optional>
- <optional>
- <element name="bpki_cms_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_cms_glue">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_https_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_https_glue">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="parent_query" combine="choice">
- <element name="parent">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="parent_bool"/>
- <ref name="parent_payload"/>
- </element>
- </define>
- <define name="parent_reply" combine="choice">
- <element name="parent">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- </element>
- </define>
- <define name="parent_query" combine="choice">
- <element name="parent">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- <ref name="parent_bool"/>
- <ref name="parent_payload"/>
- </element>
- </define>
- <define name="parent_reply" combine="choice">
- <element name="parent">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- </element>
- </define>
- <define name="parent_query" combine="choice">
- <element name="parent">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- </element>
- </define>
- <define name="parent_reply" combine="choice">
- <element name="parent">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- <ref name="parent_payload"/>
- </element>
- </define>
- <define name="parent_query" combine="choice">
- <element name="parent">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="parent_reply" combine="choice">
- <element name="parent">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- <ref name="parent_payload"/>
- </element>
- </define>
- <define name="parent_query" combine="choice">
- <element name="parent">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- </element>
- </define>
- <define name="parent_reply" combine="choice">
- <element name="parent">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="parent_id"/>
- </element>
- </define>
- <!-- <child/> element -->
- <define name="child_id">
- <attribute name="child_id">
- <ref name="sql_id"/>
- </attribute>
- </define>
- <define name="child_bool">
- <optional>
- <attribute name="reissue">
- <value>yes</value>
- </attribute>
- </optional>
- </define>
- <define name="child_payload">
- <optional>
- <ref name="bsc_id"/>
- </optional>
- <optional>
- <element name="bpki_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_glue">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="child_query" combine="choice">
- <element name="child">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="child_bool"/>
- <ref name="child_payload"/>
- </element>
- </define>
- <define name="child_reply" combine="choice">
- <element name="child">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- </element>
- </define>
- <define name="child_query" combine="choice">
- <element name="child">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- <ref name="child_bool"/>
- <ref name="child_payload"/>
- </element>
- </define>
- <define name="child_reply" combine="choice">
- <element name="child">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- </element>
- </define>
- <define name="child_query" combine="choice">
- <element name="child">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- </element>
- </define>
- <define name="child_reply" combine="choice">
- <element name="child">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- <ref name="child_payload"/>
- </element>
- </define>
- <define name="child_query" combine="choice">
- <element name="child">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="child_reply" combine="choice">
- <element name="child">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- <ref name="child_payload"/>
- </element>
- </define>
- <define name="child_query" combine="choice">
- <element name="child">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- </element>
- </define>
- <define name="child_reply" combine="choice">
- <element name="child">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- </element>
- </define>
- <!-- <repository/> element -->
- <define name="repository_id">
- <attribute name="repository_id">
- <ref name="sql_id"/>
- </attribute>
- </define>
- <define name="repository_payload">
- <optional>
- <attribute name="peer_contact_uri">
- <ref name="uri"/>
- </attribute>
- </optional>
- <optional>
- <ref name="bsc_id"/>
- </optional>
- <optional>
- <element name="bpki_cms_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_cms_glue">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_https_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_https_glue">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="repository_query" combine="choice">
- <element name="repository">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="repository_payload"/>
- </element>
- </define>
- <define name="repository_reply" combine="choice">
- <element name="repository">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- </element>
- </define>
- <define name="repository_query" combine="choice">
- <element name="repository">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- <ref name="repository_payload"/>
- </element>
- </define>
- <define name="repository_reply" combine="choice">
- <element name="repository">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- </element>
- </define>
- <define name="repository_query" combine="choice">
- <element name="repository">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- </element>
- </define>
- <define name="repository_reply" combine="choice">
- <element name="repository">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- <ref name="repository_payload"/>
- </element>
- </define>
- <define name="repository_query" combine="choice">
- <element name="repository">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="repository_reply" combine="choice">
- <element name="repository">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- <ref name="repository_payload"/>
- </element>
- </define>
- <define name="repository_query" combine="choice">
- <element name="repository">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- </element>
- </define>
- <define name="repository_reply" combine="choice">
- <element name="repository">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="repository_id"/>
- </element>
- </define>
- <!-- <route_origin/> element -->
- <define name="route_origin_id">
- <attribute name="route_origin_id">
- <ref name="sql_id"/>
- </attribute>
- </define>
- <define name="route_origin_bool">
- <optional>
- <attribute name="suppress_publication">
- <value>yes</value>
- </attribute>
- </optional>
- </define>
- <define name="route_origin_payload">
- <optional>
- <attribute name="as_number">
- <data type="positiveInteger"/>
- </attribute>
- </optional>
- <optional>
- <attribute name="ipv4">
- <ref name="ipv4_list"/>
- </attribute>
- </optional>
- <optional>
- <attribute name="ipv6">
- <ref name="ipv6_list"/>
- </attribute>
- </optional>
- </define>
- <define name="route_origin_query" combine="choice">
- <element name="route_origin">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="route_origin_bool"/>
- <ref name="route_origin_payload"/>
- </element>
- </define>
- <define name="route_origin_reply" combine="choice">
- <element name="route_origin">
- <ref name="ctl_create"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- </element>
- </define>
- <define name="route_origin_query" combine="choice">
- <element name="route_origin">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- <ref name="route_origin_bool"/>
- <ref name="route_origin_payload"/>
- </element>
- </define>
- <define name="route_origin_reply" combine="choice">
- <element name="route_origin">
- <ref name="ctl_set"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- </element>
- </define>
- <define name="route_origin_query" combine="choice">
- <element name="route_origin">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- </element>
- </define>
- <define name="route_origin_reply" combine="choice">
- <element name="route_origin">
- <ref name="ctl_get"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- <ref name="route_origin_payload"/>
- </element>
- </define>
- <define name="route_origin_query" combine="choice">
- <element name="route_origin">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- </element>
- </define>
- <define name="route_origin_reply" combine="choice">
- <element name="route_origin">
- <ref name="ctl_list"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- <ref name="route_origin_payload"/>
- </element>
- </define>
- <define name="route_origin_query" combine="choice">
- <element name="route_origin">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- </element>
- </define>
- <define name="route_origin_reply" combine="choice">
- <element name="route_origin">
- <ref name="ctl_destroy"/>
- <ref name="self_id"/>
- <ref name="route_origin_id"/>
- </element>
- </define>
- <!-- <list_resources/> element -->
- <define name="list_resources_query">
- <element name="list_resources">
- <ref name="tag"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- </element>
- </define>
- <define name="list_resources_reply">
- <element name="list_resources">
- <ref name="tag"/>
- <ref name="self_id"/>
- <ref name="child_id"/>
- <attribute name="valid_until">
- <data type="dateTime">
- <param name="pattern">.*Z</param>
- </data>
- </attribute>
- <optional>
- <attribute name="subject_name">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- </optional>
- <optional>
- <attribute name="asn">
- <ref name="asn_list"/>
- </attribute>
- </optional>
- <optional>
- <attribute name="ipv4">
- <ref name="ipv4_list"/>
- </attribute>
- </optional>
- <optional>
- <attribute name="ipv6">
- <ref name="ipv6_list"/>
- </attribute>
- </optional>
- </element>
- </define>
- <!-- <report_error/> element -->
- <define name="error">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </define>
- <define name="report_error_reply">
- <element name="report_error">
- <ref name="tag"/>
- <ref name="self_id"/>
- <attribute name="error_code">
- <ref name="error"/>
- </attribute>
- <optional>
- <data type="string">
- <param name="maxLength">512000</param>
- </data>
- </optional>
- </element>
- </define>
-</grammar>
-<!--
- Local Variables:
- indent-tabs-mode: nil
- End:
--->
-'''))
-
-## @var up_down
-## Parsed RelaxNG up_down schema
-up_down = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" encoding="UTF-8"?>
-<!--
- $Id: up-down-schema.rnc 1798 2008-05-17 08:21:50Z sra $
-
- RelaxNG Scheme for up-down protocol, extracted from APNIC Wiki.
-
- libxml2 (including xmllint) only groks the XML syntax of RelaxNG, so
- run the compact syntax through trang to get XML syntax.
--->
-<grammar ns="http://www.apnic.net/specs/rescerts/up-down/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
- <start>
- <element name="message">
- <attribute name="version">
- <data type="positiveInteger">
- <param name="maxInclusive">1</param>
- </data>
- </attribute>
- <attribute name="sender">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- <attribute name="recipient">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- <ref name="payload"/>
- </element>
- </start>
- <define name="payload" combine="choice">
- <attribute name="type">
- <value>list</value>
- </attribute>
- <ref name="list_request"/>
- </define>
- <define name="payload" combine="choice">
- <attribute name="type">
- <value>list_response</value>
- </attribute>
- <ref name="list_response"/>
- </define>
- <define name="payload" combine="choice">
- <attribute name="type">
- <value>issue</value>
- </attribute>
- <ref name="issue_request"/>
- </define>
- <define name="payload" combine="choice">
- <attribute name="type">
- <value>issue_response</value>
- </attribute>
- <ref name="issue_response"/>
- </define>
- <define name="payload" combine="choice">
- <attribute name="type">
- <value>revoke</value>
- </attribute>
- <ref name="revoke_request"/>
- </define>
- <define name="payload" combine="choice">
- <attribute name="type">
- <value>revoke_response</value>
- </attribute>
- <ref name="revoke_response"/>
- </define>
- <define name="payload" combine="choice">
- <attribute name="type">
- <value>error_response</value>
- </attribute>
- <ref name="error_response"/>
- </define>
- <define name="list_request">
- <empty/>
- </define>
- <define name="list_response">
- <zeroOrMore>
- <ref name="class"/>
- </zeroOrMore>
- </define>
- <define name="class">
- <element name="class">
- <attribute name="class_name">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- <attribute name="cert_url">
- <data type="string">
- <param name="maxLength">4096</param>
- </data>
- </attribute>
- <attribute name="resource_set_as">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9]*</param>
- </data>
- </attribute>
- <attribute name="resource_set_ipv4">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,/.0-9]*</param>
- </data>
- </attribute>
- <attribute name="resource_set_ipv6">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,/:0-9a-fA-F]*</param>
- </data>
- </attribute>
- <optional>
- <attribute name="resource_set_notafter">
- <data type="dateTime">
- <param name="pattern">.*Z</param>
- </data>
- </attribute>
- </optional>
- <optional>
- <attribute name="suggested_sia_head">
- <data type="anyURI">
- <param name="maxLength">1024</param>
- <param name="pattern">rsync://.+</param>
- </data>
- </attribute>
- </optional>
- <zeroOrMore>
- <element name="certificate">
- <attribute name="cert_url">
- <data type="string">
- <param name="maxLength">4096</param>
- </data>
- </attribute>
- <optional>
- <attribute name="req_resource_set_as">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9]*</param>
- </data>
- </attribute>
- </optional>
- <optional>
- <attribute name="req_resource_set_ipv4">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,/.0-9]*</param>
- </data>
- </attribute>
- </optional>
- <optional>
- <attribute name="req_resource_set_ipv6">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,/:0-9a-fA-F]*</param>
- </data>
- </attribute>
- </optional>
- <data type="base64Binary">
- <param name="maxLength">512000</param>
- </data>
- </element>
- </zeroOrMore>
- <element name="issuer">
- <data type="base64Binary">
- <param name="maxLength">512000</param>
- </data>
- </element>
- </element>
- </define>
- <define name="issue_request">
- <element name="request">
- <attribute name="class_name">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- <optional>
- <attribute name="req_resource_set_as">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9]*</param>
- </data>
- </attribute>
- </optional>
- <optional>
- <attribute name="req_resource_set_ipv4">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,/.0-9]*</param>
- </data>
- </attribute>
- </optional>
- <optional>
- <attribute name="req_resource_set_ipv6">
- <data type="string">
- <param name="maxLength">512000</param>
- <param name="pattern">[\-,/:0-9a-fA-F]*</param>
- </data>
- </attribute>
- </optional>
- <data type="base64Binary">
- <param name="maxLength">512000</param>
- </data>
- </element>
- </define>
- <define name="issue_response">
- <ref name="class"/>
- </define>
- <define name="revoke_request">
- <ref name="revocation"/>
- </define>
- <define name="revoke_response">
- <ref name="revocation"/>
- </define>
- <define name="revocation">
- <element name="key">
- <attribute name="class_name">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- <attribute name="ski">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- </element>
- </define>
- <define name="error_response">
- <element name="status">
- <data type="positiveInteger">
- <param name="maxInclusive">999999999999999</param>
- </data>
- </element>
- <optional>
- <element name="description">
- <attribute name="xml:lang">
- <data type="language"/>
- </attribute>
- <data type="string">
- <param name="maxLength">1024</param>
- </data>
- </element>
- </optional>
- </define>
-</grammar>
-<!--
- Local Variables:
- indent-tabs-mode: nil
- End:
--->
-'''))
-
-## @var publication
-## Parsed RelaxNG publication schema
-publication = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" encoding="UTF-8"?>
-<!--
- $Id: publication-schema.rnc 1837 2008-06-02 23:47:19Z sra $
-
- RelaxNG Schema for RPKI publication protocol.
-
- libxml2 (including xmllint) only groks the XML syntax of RelaxNG, so
- run the compact syntax through trang to get XML syntax.
--->
-<grammar ns="http://www.hactrn.net/uris/rpki/publication-spec/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
- <!-- Top level PDU -->
- <start>
- <element name="msg">
- <attribute name="version">
- <data type="positiveInteger">
- <param name="maxInclusive">1</param>
- </data>
- </attribute>
- <choice>
- <group>
- <attribute name="type">
- <value>query</value>
- </attribute>
- <zeroOrMore>
- <ref name="query_elt"/>
- </zeroOrMore>
- </group>
- <group>
- <attribute name="type">
- <value>reply</value>
- </attribute>
- <zeroOrMore>
- <ref name="reply_elt"/>
- </zeroOrMore>
- </group>
- </choice>
- </element>
- </start>
- <!-- PDUs allowed in a query -->
- <define name="query_elt">
- <choice>
- <ref name="config_query"/>
- <ref name="client_query"/>
- <ref name="certificate_query"/>
- <ref name="crl_query"/>
- <ref name="manifest_query"/>
- <ref name="roa_query"/>
- </choice>
- </define>
- <!-- PDUs allowed in a reply -->
- <define name="reply_elt">
- <choice>
- <ref name="config_reply"/>
- <ref name="client_reply"/>
- <ref name="certificate_reply"/>
- <ref name="crl_reply"/>
- <ref name="manifest_reply"/>
- <ref name="roa_reply"/>
- <ref name="report_error_reply"/>
- </choice>
- </define>
- <!-- Tag attributes for bulk operations -->
- <define name="tag">
- <attribute name="tag">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </attribute>
- </define>
- <!-- Base64 encoded DER stuff -->
- <define name="base64">
- <data type="base64Binary">
- <param name="maxLength">512000</param>
- </data>
- </define>
- <!-- Publication URLs -->
- <define name="uri_t">
- <data type="anyURI">
- <param name="maxLength">4096</param>
- </data>
- </define>
- <define name="uri">
- <attribute name="uri">
- <ref name="uri_t"/>
- </attribute>
- </define>
- <!--
- <config/> element (use restricted to repository operator)
- config_id attribute, create, list, and destroy commands omitted deliberately, see code for details
- -->
- <define name="config_payload">
- <optional>
- <element name="bpki_crl">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="config_query" combine="choice">
- <element name="config">
- <attribute name="action">
- <value>set</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="config_payload"/>
- </element>
- </define>
- <define name="config_reply" combine="choice">
- <element name="config">
- <attribute name="action">
- <value>set</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- </element>
- </define>
- <define name="config_query" combine="choice">
- <element name="config">
- <attribute name="action">
- <value>get</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- </element>
- </define>
- <define name="config_reply" combine="choice">
- <element name="config">
- <attribute name="action">
- <value>get</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="config_payload"/>
- </element>
- </define>
- <!-- <client/> element (use restricted to repository operator) -->
- <define name="client_id">
- <attribute name="client_id">
- <data type="nonNegativeInteger"/>
- </attribute>
- </define>
- <define name="client_payload">
- <optional>
- <attribute name="base_uri">
- <ref name="uri_t"/>
- </attribute>
- </optional>
- <optional>
- <element name="bpki_cert">
- <ref name="base64"/>
- </element>
- </optional>
- <optional>
- <element name="bpki_glue">
- <ref name="base64"/>
- </element>
- </optional>
- </define>
- <define name="client_query" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>create</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_payload"/>
- </element>
- </define>
- <define name="client_reply" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>create</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- </element>
- </define>
- <define name="client_query" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>set</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- <ref name="client_payload"/>
- </element>
- </define>
- <define name="client_reply" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>set</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- </element>
- </define>
- <define name="client_query" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>get</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- </element>
- </define>
- <define name="client_reply" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>get</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- <ref name="client_payload"/>
- </element>
- </define>
- <define name="client_query" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>list</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- </element>
- </define>
- <define name="client_reply" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>list</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- <ref name="client_payload"/>
- </element>
- </define>
- <define name="client_query" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>destroy</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- </element>
- </define>
- <define name="client_reply" combine="choice">
- <element name="client">
- <attribute name="action">
- <value>destroy</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="client_id"/>
- </element>
- </define>
- <!-- <certificate/> element -->
- <define name="certificate_query" combine="choice">
- <element name="certificate">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- <ref name="base64"/>
- </element>
- </define>
- <define name="certificate_reply" combine="choice">
- <element name="certificate">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="certificate_query" combine="choice">
- <element name="certificate">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="certificate_reply" combine="choice">
- <element name="certificate">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <!-- <crl/> element -->
- <define name="crl_query" combine="choice">
- <element name="crl">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- <ref name="base64"/>
- </element>
- </define>
- <define name="crl_reply" combine="choice">
- <element name="crl">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="crl_query" combine="choice">
- <element name="crl">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="crl_reply" combine="choice">
- <element name="crl">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <!-- <manifest/> element -->
- <define name="manifest_query" combine="choice">
- <element name="manifest">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- <ref name="base64"/>
- </element>
- </define>
- <define name="manifest_reply" combine="choice">
- <element name="manifest">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="manifest_query" combine="choice">
- <element name="manifest">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="manifest_reply" combine="choice">
- <element name="manifest">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <!-- <roa/> element -->
- <define name="roa_query" combine="choice">
- <element name="roa">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- <ref name="base64"/>
- </element>
- </define>
- <define name="roa_reply" combine="choice">
- <element name="roa">
- <attribute name="action">
- <value>publish</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="roa_query" combine="choice">
- <element name="roa">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <define name="roa_reply" combine="choice">
- <element name="roa">
- <attribute name="action">
- <value>withdraw</value>
- </attribute>
- <optional>
- <ref name="tag"/>
- </optional>
- <ref name="uri"/>
- </element>
- </define>
- <!-- <report_error/> element -->
- <define name="error">
- <data type="token">
- <param name="maxLength">1024</param>
- </data>
- </define>
- <define name="report_error_reply">
- <element name="report_error">
- <optional>
- <ref name="tag"/>
- </optional>
- <attribute name="error_code">
- <ref name="error"/>
- </attribute>
- <optional>
- <data type="string">
- <param name="maxLength">512000</param>
- </data>
- </optional>
- </element>
- </define>
-</grammar>
-<!--
- Local Variables:
- indent-tabs-mode: nil
- End:
--->
-'''))
diff --git a/rpkid.stable/rpki/resource_set.py b/rpkid.stable/rpki/resource_set.py
deleted file mode 100644
index baaccfc4..00000000
--- a/rpkid.stable/rpki/resource_set.py
+++ /dev/null
@@ -1,795 +0,0 @@
-"""Classes dealing with sets of resources.
-
-The basic mechanics of a resource set are the same for any of the
-resources we handle (ASNs, IPv4 addresses, or IPv6 addresses), so we
-can provide the same operations on any of them, even though the
-underlying details vary.
-
-We also provide some basic set operations (union, intersection, etc).
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 re
-import rpki.ipaddrs, rpki.oids, rpki.exceptions
-
-## @var inherit_token
-# Token used to indicate inheritance in read and print syntax.
-
-inherit_token = "<inherit>"
-
-class resource_range(object):
- """Generic resource range type. Assumes underlying type is some
- kind of integer.
-
- This is a virtual class. You probably don't want to use this type
- directly.
- """
-
- def __init__(self, min, max):
- """Initialize and sanity check a resource_range."""
- assert min <= max, "Mis-ordered range: %s before %s" % (str(min), str(max))
- self.min = min
- self.max = max
-
- def __cmp__(self, other):
- """Compare two resource_range objects."""
- assert self.__class__ is other.__class__
- c = self.min - other.min
- if c == 0: c = self.max - other.max
- if c < 0: c = -1
- if c > 0: c = 1
- return c
-
-class resource_range_as(resource_range):
- """Range of Autonomous System Numbers.
-
- Denotes a single ASN by a range whose min and max values are identical.
- """
-
- ## @var datum_type
- # Type of underlying data (min and max).
-
- datum_type = long
-
- def __str__(self):
- """Convert a resource_range_as to string format."""
- if self.min == self.max:
- return str(self.min)
- else:
- return str(self.min) + "-" + str(self.max)
-
- def to_rfc3779_tuple(self):
- """Convert a resource_range_as to tuple format for RFC 3779 ASN.1 encoding."""
- if self.min == self.max:
- return ("id", self.min)
- else:
- return ("range", (self.min, self.max))
-
-class resource_range_ip(resource_range):
- """Range of (generic) IP addresses.
-
- Prefixes are converted to ranges on input, and ranges that can be
- represented as prefixes are written as prefixes on output.
-
- This is a virtual class. You probably don't want to use it directly.
- """
-
- def _prefixlen(self):
- """Determine whether a resource_range_ip can be expressed as a prefix."""
- mask = self.min ^ self.max
- if self.min & mask != 0:
- return -1
- prefixlen = self.datum_type.bits
- while mask & 1:
- prefixlen -= 1
- mask >>= 1
- if mask:
- return -1
- else:
- return prefixlen
-
- def __str__(self):
- """Convert a resource_range_ip to string format."""
- prefixlen = self._prefixlen()
- if prefixlen < 0:
- return str(self.min) + "-" + str(self.max)
- else:
- return str(self.min) + "/" + str(prefixlen)
-
- def to_rfc3779_tuple(self):
- """Convert a resource_range_ip to tuple format for RFC 3779 ASN.1 encoding."""
- prefixlen = self._prefixlen()
- if prefixlen < 0:
- return ("addressRange", (_long2bs(self.min, self.datum_type.bits, strip = 0),
- _long2bs(self.max, self.datum_type.bits, strip = 1)))
- else:
- return ("addressPrefix", _long2bs(self.min, self.datum_type.bits, prefixlen = prefixlen))
-
- @classmethod
- def make_prefix(cls, address, prefixlen):
- """Construct a resource range corresponding to a prefix."""
- assert isinstance(address, cls.datum_type) and isinstance(prefixlen, (int, long))
- assert prefixlen >= 0 and prefixlen <= cls.datum_type.bits, "Nonsensical prefix length: %s" % prefixlen
- mask = (1 << (cls.datum_type.bits - prefixlen)) - 1
- assert (address & mask) == 0, "Resource not in canonical form: %s/%s" % (address, prefixlen)
- return cls(cls.datum_type(address), cls.datum_type(address | mask))
-
-class resource_range_ipv4(resource_range_ip):
- """Range of IPv4 addresses."""
-
- ## @var datum_type
- # Type of underlying data (min and max).
-
- datum_type = rpki.ipaddrs.v4addr
-
-class resource_range_ipv6(resource_range_ip):
- """Range of IPv6 addresses."""
-
- ## @var datum_type
- # Type of underlying data (min and max).
-
- datum_type = rpki.ipaddrs.v6addr
-
-def _rsplit(rset, that):
- """Utility function to split a resource range into two resource ranges."""
- this = rset.pop(0)
- cell_type = type(this.min)
- assert type(this) is type(that) and type(this.max) is cell_type and \
- type(that.min) is cell_type and type(that.max) is cell_type
- if this.min < that.min:
- rset.insert(0, type(this)(this.min, cell_type(that.min - 1)))
- rset.insert(1, type(this)(that.min, this.max))
- else:
- assert this.max > that.max
- rset.insert(0, type(this)(this.min, that.max))
- rset.insert(1, type(this)(cell_type(that.max + 1), this.max))
-
-class resource_set(list):
- """Generic resource set.
- This is a list subclass containing resource ranges.
-
- This is a virtual class. You probably don't want to use it
- directly.
- """
-
- ## @var inherit
- # Boolean indicating whether this resource_set uses RFC 3779 inheritance.
-
- inherit = False
-
- def __init__(self, ini = None):
- """Initialize a resource_set."""
- if isinstance(ini, (int, long)):
- ini = str(ini)
- if ini == inherit_token:
- self.inherit = True
- elif isinstance(ini, str) and len(ini):
- self.extend(self.parse_str(s) for s in ini.split(","))
- elif isinstance(ini, tuple):
- self.parse_rfc3779_tuple(ini)
- elif isinstance(ini, list):
- self.extend(ini)
- else:
- assert ini is None or ini == "", "Unexpected initializer: %s" % str(ini)
- assert not self.inherit or not self
- self.sort()
- for i in xrange(len(self) - 2, -1, -1):
- if self[i].max + 1 == self[i+1].min:
- self[i] = type(self[i])(self[i].min, self[i+1].max)
- self.pop(i + 1)
- if __debug__:
- for i in xrange(0, len(self) - 1):
- assert self[i].max < self[i+1].min, "Resource overlap: %s %s" % (self[i], self[i+1])
-
- def __str__(self):
- """Convert a resource_set to string format."""
- if self.inherit:
- return inherit_token
- else:
- return ",".join(str(x) for x in self)
-
- def _comm(self, other):
- """Like comm(1), sort of.
-
- Returns a tuple of three resource sets: resources only in self,
- resources only in other, and resources in both. Used (not very
- efficiently) as the basis for most set operations on resource
- sets.
- """
- assert not self.inherit
- assert type(self) is type(other), "Type mismatch %s %s" % (repr(type(self)), repr(type(other)))
- set1 = self[:]
- set2 = other[:]
- only1, only2, both = [], [], []
- while set1 or set2:
- if set1 and (not set2 or set1[0].max < set2[0].min):
- only1.append(set1.pop(0))
- elif set2 and (not set1 or set2[0].max < set1[0].min):
- only2.append(set2.pop(0))
- elif set1[0].min < set2[0].min:
- _rsplit(set1, set2[0])
- elif set2[0].min < set1[0].min:
- _rsplit(set2, set1[0])
- elif set1[0].max < set2[0].max:
- _rsplit(set2, set1[0])
- elif set2[0].max < set1[0].max:
- _rsplit(set1, set2[0])
- else:
- assert set1[0].min == set2[0].min and set1[0].max == set2[0].max
- both.append(set1.pop(0))
- set2.pop(0)
- return type(self)(only1), type(self)(only2), type(self)(both)
-
- def union(self, other):
- """Set union for resource sets."""
- assert not self.inherit
- assert type(self) is type(other), "Type mismatch: %s %s" % (repr(type(self)), repr(type(other)))
- set1 = self[:]
- set2 = other[:]
- result = []
- while set1 or set2:
- if set1 and (not set2 or set1[0].max < set2[0].min):
- result.append(set1.pop(0))
- elif set2 and (not set1 or set2[0].max < set1[0].min):
- result.append(set2.pop(0))
- else:
- this = set1.pop(0)
- that = set2.pop(0)
- assert type(this) is type(that)
- if this.min < that.min: min = this.min
- else: min = that.min
- if this.max > that.max: max = this.max
- else: max = that.max
- result.append(type(this)(min, max))
- return type(self)(result)
-
- def intersection(self, other):
- """Set intersection for resource sets."""
- return self._comm(other)[2]
-
- def difference(self, other):
- """Set difference for resource sets."""
- return self._comm(other)[0]
-
- def symmetric_difference(self, other):
- """Set symmetric difference (XOR) for resource sets."""
- com = self._comm(other)
- return com[0].union(com[1])
-
- def contains(self, item):
- """Set membership test for resource sets."""
- assert not self.inherit
- for i in self:
- if isinstance(item, type(i)) and i.min <= item.min and i.max >= item.max:
- return True
- elif isinstance(item, type(i.min)) and i.min <= item and i.max >= item:
- return True
- else:
- assert isinstance(item, (type(i), type(i.min)))
- return False
-
- def issubset(self, other):
- """Test whether self is a subset (possibly improper) of other."""
- for i in self:
- if not other.contains(i):
- return False
- return True
-
- def issuperset(self, other):
- """Test whether self is a superset (possibly improper) of other."""
- return other.issubset(self)
-
- @classmethod
- def from_sql(cls, sql, query, args = None):
- """Create resource set from an SQL query.
-
- sql is an object that supports execute() and fetchall() methods
- like a DB API 2.0 cursor object.
-
- query is an SQL query that returns a sequence of (min, max) pairs.
- """
-
- sql.execute(query, args)
- return cls(ini = [cls.range_type(cls.range_type.datum_type(b),
- cls.range_type.datum_type(e))
- for (b,e) in sql.fetchall()])
-
-class resource_set_as(resource_set):
- """ASN resource set."""
-
- ## @var range_type
- # Type of range underlying this type of resource_set.
-
- range_type = resource_range_as
-
- def parse_str(self, x):
- """Parse ASN resource sets from text (eg, XML attributes)."""
- r = re.match("^([0-9]+)-([0-9]+)$", x)
- if r:
- return resource_range_as(long(r.group(1)), long(r.group(2)))
- else:
- return resource_range_as(long(x), long(x))
-
- def parse_rfc3779_tuple(self, x):
- """Parse ASN resource from tuple format generated by RFC 3779 ASN.1 decoder."""
- if x[0] == "asIdsOrRanges":
- for aor in x[1]:
- if aor[0] == "range":
- min = aor[1][0]
- max = aor[1][1]
- else:
- min = aor[1]
- max = min
- self.append(resource_range_as(min, max))
- else:
- assert x[0] == "inherit"
- self.inherit = True
-
- def to_rfc3779_tuple(self):
- """Convert ASN resource set into tuple format used for RFC 3779 ASN.1 encoding."""
- if self:
- return ("asIdsOrRanges", tuple(a.to_rfc3779_tuple() for a in self))
- elif self.inherit:
- return ("inherit", "")
- else:
- return None
-
-class resource_set_ip(resource_set):
- """(Generic) IP address resource set.
-
- This is a virtual class. You probably don't want to use it
- directly.
- """
-
- def parse_str(self, x):
- """Parse IP address resource sets from text (eg, XML attributes)."""
- r = re.match("^([0-9:.a-fA-F]+)-([0-9:.a-fA-F]+)$", x)
- if r:
- return self.range_type(self.range_type.datum_type(r.group(1)), self.range_type.datum_type(r.group(2)))
- r = re.match("^([0-9:.a-fA-F]+)/([0-9]+)$", x)
- if r:
- return self.range_type.make_prefix(self.range_type.datum_type(r.group(1)), int(r.group(2)))
- raise RuntimeError, 'Bad IP resource "%s"' % (x)
-
- def parse_rfc3779_tuple(self, x):
- """Parse IP address resource sets from tuple format generated by RFC 3779 ASN.1 decoder."""
- if x[0] == "addressesOrRanges":
- for aor in x[1]:
- if aor[0] == "addressRange":
- min = _bs2long(aor[1][0], self.range_type.datum_type.bits, 0)
- max = _bs2long(aor[1][1], self.range_type.datum_type.bits, 1)
- else:
- min = _bs2long(aor[1], self.range_type.datum_type.bits, 0)
- max = _bs2long(aor[1], self.range_type.datum_type.bits, 1)
- self.append(self.range_type(self.range_type.datum_type(min), self.range_type.datum_type(max)))
- else:
- assert x[0] == "inherit"
- self.inherit = True
-
- def to_rfc3779_tuple(self):
- """Convert IP resource set into tuple format used by RFC 3779 ASN.1 encoder."""
- if self:
- return (self.afi, ("addressesOrRanges", tuple(a.to_rfc3779_tuple() for a in self)))
- elif self.inherit:
- return (self.afi, ("inherit", ""))
- else:
- return None
-
-class resource_set_ipv4(resource_set_ip):
- """IPv4 address resource set."""
-
- ## @var range_type
- # Type of range underlying this type of resource_set.
-
- range_type = resource_range_ipv4
-
- ## @var afi
- # Address Family Identifier value for IPv4.
-
- afi = "\x00\x01"
-
-class resource_set_ipv6(resource_set_ip):
- """IPv6 address resource set."""
-
- ## @var range_type
- # Type of range underlying this type of resource_set.
-
- range_type = resource_range_ipv6
-
- ## @var afi
- # Address Family Identifier value for IPv6.
-
- afi = "\x00\x02"
-
-def _bs2long(bs, addrlen, fill):
- """Utility function to convert a bitstring (POW.pkix tuple
- representation) into a Python long.
- """
- x = 0L
- for y in bs:
- x = (x << 1) | y
- for y in xrange(addrlen - len(bs)):
- x = (x << 1) | fill
- return x
-
-def _long2bs(number, addrlen, prefixlen = None, strip = None):
- """Utility function to convert a Python long into a POW.pkix tuple
- bitstring. This is a bit complicated because it supports the
- fiendishly compact encoding used in RFC 3779.
- """
- assert prefixlen is None or strip is None
- bs = []
- while number:
- bs.append(int(number & 1))
- number >>= 1
- if addrlen > len(bs):
- bs.extend((0 for i in xrange(addrlen - len(bs))))
- bs.reverse()
- if prefixlen is not None:
- return tuple(bs[0:prefixlen])
- if strip is not None:
- while bs and bs[-1] == strip:
- bs.pop()
- return tuple(bs)
-
-class resource_bag(object):
- """Container to simplify passing around the usual triple of ASN,
- IPv4, and IPv6 resource sets.
- """
-
- ## @var asn
- # Set of Autonomous System Number resources.
-
- ## @var v4
- # Set of IPv4 resources.
-
- ## @var v6
- # Set of IPv6 resources.
-
- ## @var valid_until
- # Expiration date of resources, for setting certificate notAfter field.
-
- def __init__(self, asn = None, v4 = None, v6 = None, valid_until = None):
- self.asn = asn or resource_set_as()
- self.v4 = v4 or resource_set_ipv4()
- self.v6 = v6 or resource_set_ipv6()
- self.valid_until = valid_until
-
- def oversized(self, other):
- """True iff self is oversized with respect to other."""
- return not self.asn.issubset(other.asn) or \
- not self.v4.issubset(other.v4) or \
- not self.v6.issubset(other.v6)
-
- def undersized(self, other):
- """True iff self is undersized with respect to other."""
- return not other.asn.issubset(self.asn) or \
- not other.v4.issubset(self.v4) or \
- not other.v6.issubset(self.v6)
-
- @classmethod
- def from_rfc3779_tuples(cls, exts):
- """Build a resource_bag from intermediate form generated by RFC 3779 ASN.1 decoder."""
- asn = None
- v4 = None
- v6 = None
- for x in exts:
- if x[0] == rpki.oids.name2oid["sbgp-autonomousSysNum"]: #
- assert len(x[2]) == 1 or x[2][1] is None, "RDI not implemented: %s" % (str(x))
- assert asn is None
- asn = resource_set_as(x[2][0])
- if x[0] == rpki.oids.name2oid["sbgp-ipAddrBlock"]:
- for fam in x[2]:
- if fam[0] == resource_set_ipv4.afi:
- assert v4 is None
- v4 = resource_set_ipv4(fam[1])
- if fam[0] == resource_set_ipv6.afi:
- assert v6 is None
- v6 = resource_set_ipv6(fam[1])
- return cls(asn, v4, v6)
-
- def empty(self):
- """Return True iff all resource sets in this bag are empty."""
- return not self.asn and not self.v4 and not self.v6
-
- def __eq__(self, other):
- return self.asn == other.asn and \
- self.v4 == other.v4 and \
- self.v6 == other.v6 and \
- self.valid_until == other.valid_until
-
- def __ne__(self, other):
- return not (self == other)
-
- def intersection(self, other):
- """Compute intersection with another resource_bag.
- valid_until attribute (if any) inherits from self.
- """
- return self.__class__(self.asn.intersection(other.asn),
- self.v4.intersection(other.v4),
- self.v6.intersection(other.v6),
- self.valid_until)
-
- def union(self, other):
- """Compute union with another resource_bag.
- valid_until attribute (if any) inherits from self.
- """
- return self.__class__(self.asn.union(other.asn),
- self.v4.union(other.v4),
- self.v6.union(other.v6),
- self.valid_until)
-
- def __str__(self):
- s = ""
- if self.asn:
- s += "ASN: %s" % self.asn
- if self.v4:
- if s:
- s += ", "
- s += "V4: %s" % self.v4
- if self.v6:
- if s:
- s += ", "
- s += "V6: %s" % self.v6
- return s
-
-# Sadly, there are enough differences between RFC 3779 and the data
-# structures in the latest proposed ROA format that we can't just use
-# the RFC 3779 code for ROAs. So we need a separate set of classes
-# that are similar in concept but different in detail, with conversion
-# functions. Such is life. I suppose it might be possible to do this
-# with multiple inheritance, but that's probably more bother than it's
-# worth.
-
-class roa_prefix(object):
- """ROA prefix. This is similar to the resource_range_ip class, but
- differs in that it only represents prefixes, never ranges, and
- includes the maximum prefix length as an additional value.
-
- This is a virtual class, you probably don't want to use it directly.
- """
-
- ## @var address
- # Address portion of prefix.
-
- ## @var prefixlen
- # (Minimum) prefix length.
-
- ## @var max_prefixlen
- # Maxmimum prefix length.
-
- def __init__(self, address, prefixlen, max_prefixlen = None):
- """Initialize a ROA prefix. max_prefixlen is optional and
- defaults to prefixlen. max_prefixlen must not be smaller than
- prefixlen.
- """
- if max_prefixlen is None:
- max_prefixlen = prefixlen
- assert max_prefixlen >= prefixlen, "Bad max_prefixlen: %d must not be shorter than %d" % (max_prefixlen, prefixlen)
- self.address = address
- self.prefixlen = prefixlen
- self.max_prefixlen = max_prefixlen
-
- def __cmp__(self, other):
- """Compare two ROA prefix objects. Comparision is based on
- address, prefixlen, and max_prefixlen, in that order.
- """
- assert self.__class__ is other.__class__
- c = self.address - other.address
- if c == 0: c = self.prefixlen - other.prefixlen
- if c == 0: c = self.max_prefixlen - other.max_prefixlen
- if c < 0: c = -1
- if c > 0: c = 1
- return c
-
- def __str__(self):
- """Convert a ROA prefix to string format."""
- if self.prefixlen == self.max_prefixlen:
- return str(self.address) + "/" + str(self.prefixlen)
- else:
- return str(self.address) + "/" + str(self.prefixlen) + "-" + str(self.max_prefixlen)
-
- def to_resource_range(self):
- """Convert this ROA prefix to the equivilent resource_range_ip
- object. This is an irreversable transformation because it loses
- the max_prefixlen attribute, nothing we can do about that.
- """
- return self.range_type.make_prefix(self.address, self.prefixlen)
-
- def min(self):
- """Return lowest address covered by prefix."""
- return self.address
-
- def max(self):
- """Return highest address covered by prefix."""
- t = self.range_type.datum_type
- return t(self.address | ((1 << (t.bits - self.prefixlen)) - 1))
-
- def to_roa_tuple(self):
- """Convert a resource_range_ip to tuple format for ROA ASN.1 encoding."""
- return (_long2bs(self.address, self.range_type.datum_type.bits, prefixlen = self.prefixlen),
- None if self.prefixlen == self.max_prefixlen else self.max_prefixlen)
-
-class roa_prefix_ipv4(roa_prefix):
- """IPv4 ROA prefix."""
-
- ## @var range_type
- # Type of corresponding resource_range_ip.
-
- range_type = resource_range_ipv4
-
-class roa_prefix_ipv6(roa_prefix):
- """IPv6 ROA prefix."""
-
- ## @var range_type
- # Type of corresponding resource_range_ip.
-
- range_type = resource_range_ipv6
-
-class roa_prefix_set(list):
- """Set of ROA prefixes, analogous to the resource_set_ip class."""
-
- def __init__(self, ini = None):
- """Initialize a ROA prefix set."""
- if isinstance(ini, str) and len(ini):
- self.extend(self.parse_str(s) for s in ini.split(","))
- elif isinstance(ini, (list, tuple)):
- self.extend(ini)
- else:
- assert ini is None or ini == "", "Unexpected initializer: %s" % str(ini)
- self.sort()
- if __debug__:
- for i in xrange(0, len(self) - 1):
- assert self[i].max() < self[i+1].min(), "Prefix overlap: %s %s" % (self[i], self[i+1])
-
- def __str__(self):
- """Convert a ROA prefix set to string format."""
- return ",".join(str(x) for x in self)
-
- def parse_str(self, x):
- """Parse ROA prefix from text (eg, an XML attribute)."""
- r = re.match("^([0-9:.a-fA-F]+)/([0-9]+)-([0-9]+)$", x)
- if r:
- return self.prefix_type(self.prefix_type.range_type.datum_type(r.group(1)), int(r.group(2)), int(r.group(3)))
- r = re.match("^([0-9:.a-fA-F]+)/([0-9]+)$", x)
- if r:
- return self.prefix_type(self.prefix_type.range_type.datum_type(r.group(1)), int(r.group(2)))
- raise RuntimeError, 'Bad ROA prefix "%s"' % (x)
-
- def to_resource_set(self):
- """Convert a ROA prefix set to a resource set. This is an
- irreversable transformation.
- """
- return self.resource_set_type([p.to_resource_range() for p in self])
-
- @classmethod
- def from_sql(cls, sql, query, args = None):
- """Create ROA prefix set from an SQL query.
-
- sql is an object that supports execute() and fetchall() methods
- like a DB API 2.0 cursor object.
-
- query is an SQL query that returns a sequence of (address,
- prefixlen, max_prefixlen) triples.
- """
-
- sql.execute(query, args)
- return cls([cls.prefix_type(cls.prefix_type.range_type.datum_type(x), int(y), int(z))
- for (x,y,z) in sql.fetchall()])
-
- def to_roa_tuple(self):
- """Convert ROA prefix set into tuple format used by ROA ASN.1 encoder.
- This is a variation on the format used in RFC 3779."""
- if self:
- return (self.resource_set_type.afi, tuple(a.to_roa_tuple() for a in self))
- else:
- return None
-
-class roa_prefix_set_ipv4(roa_prefix_set):
- """Set of IPv4 ROA prefixes."""
-
- ## @var prefix_type
- # Type of underlying roa_prefix.
-
- prefix_type = roa_prefix_ipv4
-
- ## @var resource_set_type
- # Type of corresponding resource_set_ip class.
-
- resource_set_type = resource_set_ipv4
-
-class roa_prefix_set_ipv6(roa_prefix_set):
- """Set of IPv6 ROA prefixes."""
-
- ## @var prefix_type
- # Type of underlying roa_prefix.
-
- prefix_type = roa_prefix_ipv6
-
- ## @var resource_set_type
- # Type of corresponding resource_set_ip class.
-
- resource_set_type = resource_set_ipv6
-
-# Test suite for set operations.
-
-if __name__ == "__main__":
-
- def test1(t, s1, s2):
- if isinstance(s1, str) and isinstance(s2, str):
- print "x: ", s1
- print "y: ", s2
- r1 = t(s1)
- r2 = t(s2)
- print "x: ", r1
- print "y: ", r2
- v1 = r1._comm(r2)
- v2 = r2._comm(r1)
- assert v1[0] == v2[1] and v1[1] == v2[0] and v1[2] == v2[2]
- for i in r1: assert r1.contains(i) and r1.contains(i.min) and r1.contains(i.max)
- for i in r2: assert r2.contains(i) and r2.contains(i.min) and r2.contains(i.max)
- for i in v1[0]: assert r1.contains(i) and not r2.contains(i)
- for i in v1[1]: assert not r1.contains(i) and r2.contains(i)
- for i in v1[2]: assert r1.contains(i) and r2.contains(i)
- v1 = r1.union(r2)
- v2 = r2.union(r1)
- assert v1 == v2
- print "x|y:", v1
- v1 = r1.difference(r2)
- v2 = r2.difference(r1)
- print "x-y:", v1
- print "y-x:", v2
- v1 = r1.symmetric_difference(r2)
- v2 = r2.symmetric_difference(r1)
- assert v1 == v2
- print "x^y:", v1
- v1 = r1.intersection(r2)
- v2 = r2.intersection(r1)
- assert v1 == v2
- print "x&y:", v1
-
- def test2(t, s1, s2):
- print "x: ", s1
- print "y: ", s2
- r1 = t(s1)
- r2 = t(s2)
- print "x: ", r1
- print "y: ", r2
- print "x>y:", (r1 > r2)
- print "x<y:", (r1 < r2)
- test1(t.resource_set_type, r1.to_resource_set(), r2.to_resource_set())
-
- print
- print "Testing set operations on resource sets"
- print
- test1(resource_set_as, "1,2,3,4,5,6,11,12,13,14,15", "1,2,3,4,5,6,111,121,131,141,151")
- print
- test1(resource_set_ipv4, "10.0.0.44/32,10.6.0.2/32", "10.3.0.0/24,10.0.0.77/32")
- print
- test1(resource_set_ipv4, "10.0.0.44/32,10.6.0.2/32", "10.0.0.0/24")
- print
- test1(resource_set_ipv4, "10.0.0.0/24", "10.3.0.0/24,10.0.0.77/32")
- print
- print "Testing set operations on ROA prefixes"
- print
- test2(roa_prefix_set_ipv4, "10.0.0.44/32,10.6.0.2/32", "10.3.0.0/24,10.0.0.77/32")
- print
- test2(roa_prefix_set_ipv4, "10.0.0.0/24-32,10.6.0.0/24-32", "10.3.0.0/24,10.0.0.0/16-32")
- print
- test2(roa_prefix_set_ipv4, "10.3.0.0/24-24,10.0.0.0/16-32", "10.3.0.0/24,10.0.0.0/16-32")
- print
diff --git a/rpkid.stable/rpki/roa.py b/rpkid.stable/rpki/roa.py
deleted file mode 100644
index ab178db0..00000000
--- a/rpkid.stable/rpki/roa.py
+++ /dev/null
@@ -1,75 +0,0 @@
-"""ROA (Route Origin Authorization).
-
-At the moment this is just the ASN.1 encoder.
-
-This corresponds to draft-ietf-sidr-roa-format, which is a work in
-progress, so this may need updating later.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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.
-
-draft-ietf-sidr-roa-format-03 2.1.3.2 specifies:
-
- RouteOriginAttestation ::= SEQUENCE {
- version [0] INTEGER DEFAULT 0,
- asID ASID,
- ipAddrBlocks SEQUENCE OF ROAIPAddressFamily }
-
- ASID ::= INTEGER
-
- ROAIPAddressFamily ::= SEQUENCE {
- addressFamily OCTET STRING (SIZE (2..3)),
- addresses SEQUENCE OF ROAIPAddress }
-
- ROAIPAddress ::= SEQUENCE {
- address IPAddress,
- maxLength INTEGER OPTIONAL }
-
- IPAddress ::= BIT STRING
-"""
-
-from POW._der import *
-
-class ROAIPAddress(Sequence):
- def __init__(self, optional=0, default=''):
- self.address = BitString()
- self.maxLength = Integer(1)
- contents = [ self.address, self.maxLength ]
- Sequence.__init__(self, contents, optional, default)
-
-class ROAIPAddresses(SequenceOf):
- def __init__(self, optional=0, default=''):
- SequenceOf.__init__(self, ROAIPAddress, optional, default)
-
-class ROAIPAddressFamily(Sequence):
- def __init__(self, optional=0, default=''):
- self.addressFamily = OctetString()
- self.addresses = ROAIPAddresses()
- contents = [ self.addressFamily, self.addresses ]
- Sequence.__init__(self, contents, optional, default)
-
-class ROAIPAddressFamilies(SequenceOf):
- def __init__(self, optional=0, default=''):
- SequenceOf.__init__(self, ROAIPAddressFamily, optional, default)
-
-class RouteOriginAttestation(Sequence):
- def __init__(self, optional=0, default=''):
- self.version = Integer()
- self.explicitVersion = Explicit(CLASS_CONTEXT, FORM_CONSTRUCTED, 0, self.version, 0, 'oAMCAQA=')
- self.asID = Integer()
- self.ipAddrBlocks = ROAIPAddressFamilies()
- contents = [ self.explicitVersion, self.asID, self.ipAddrBlocks ]
- Sequence.__init__(self, contents, optional, default)
diff --git a/rpkid.stable/rpki/rpki_engine.py b/rpkid.stable/rpki/rpki_engine.py
deleted file mode 100644
index a49121c1..00000000
--- a/rpkid.stable/rpki/rpki_engine.py
+++ /dev/null
@@ -1,819 +0,0 @@
-"""Global context for rpkid.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 traceback, os, time, getopt, sys, MySQLdb, lxml.etree
-import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509, rpki.sql
-import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log
-
-class rpkid_context(object):
- """A container for various global rpkid parameters."""
-
- def __init__(self, cfg):
-
- self.sql = rpki.sql.session(cfg)
-
- 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")
-
- self.https_server_host = cfg.get("server-host", "")
- self.https_server_port = int(cfg.get("server-port", "4433"))
-
- self.publication_kludge_base = cfg.get("publication-kludge-base", "publication/")
-
- def irdb_query(self, self_id, child_id = None):
- """Perform an IRDB callback query. In the long run this should not
- be a blocking routine, it should instead issue a query and set up a
- handler to receive the response. For the moment, though, we are
- doing simple lock step and damn the torpedos. Not yet doing
- anything useful with subject name. Most likely this function should
- really be wrapped up in a class that carries both the query result
- and also the intermediate state needed for the event-driven code
- that this function will need to become.
- """
-
- rpki.log.trace()
-
- q_msg = rpki.left_right.msg()
- q_msg.type = "query"
- q_msg.append(rpki.left_right.list_resources_elt())
- 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.rpkid_key, self.rpkid_cert)
- der = rpki.https.client(
- server_ta = (self.bpki_ta, self.irdb_cert),
- client_key = self.rpkid_key,
- client_cert = self.rpkid_cert,
- url = self.irdb_url,
- msg = q_cms)
- r_msg = rpki.left_right.cms_msg.unwrap(der, (self.bpki_ta, self.irdb_cert))
- if len(r_msg) == 0 or not isinstance(r_msg[0], rpki.left_right.list_resources_elt) or r_msg.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(
- asn = r_msg[0].asn,
- v4 = r_msg[0].ipv4,
- v6 = r_msg[0].ipv6,
- valid_until = r_msg[0].valid_until)
-
- def left_right_handler(self, query, path):
- """Process one left-right PDU."""
- rpki.log.trace()
- try:
- self.sql.ping()
- q_msg = rpki.left_right.cms_msg.unwrap(query, (self.bpki_ta, self.irbe_cert))
- if q_msg.type != "query":
- raise rpki.exceptions.BadQuery, "Message type is not query"
- r_msg = q_msg.serve_top_level(self)
- reply = rpki.left_right.cms_msg.wrap(r_msg, self.rpkid_key, self.rpkid_cert)
- self.sql.sweep()
- return 200, reply
- except Exception, data:
- rpki.log.error(traceback.format_exc())
- return 500, "Unhandled exception %s" % data
-
- def up_down_handler(self, query, path):
- """Process one up-down PDU."""
- rpki.log.trace()
- try:
- self.sql.ping()
- child_id = path.partition("/up-down/")[2]
- if not child_id.isdigit():
- raise rpki.exceptions.BadContactURL, "Bad path: %s" % path
- child = rpki.left_right.child_elt.sql_fetch(self, long(child_id))
- if child is None:
- raise rpki.exceptions.ChildNotFound, "Could not find child %s" % child_id
- reply = child.serve_up_down(query)
- self.sql.sweep()
- return 200, reply
- except Exception, data:
- rpki.log.error(traceback.format_exc())
- return 400, "Could not process PDU: %s" % data
-
- def cronjob_handler(self, query, path):
- """Periodic tasks. As simple as possible for now, may need to break
- this up into separate handlers later.
- """
-
- rpki.log.trace()
- try:
- self.sql.ping()
- for s in rpki.left_right.self_elt.sql_fetch_all(self):
- rpki.log.debug("Self %s polling parents" % s.self_id)
- s.client_poll()
- rpki.log.debug("Self %s updating children" % s.self_id)
- s.update_children()
- rpki.log.debug("Self %s updating ROAs" % s.self_id)
- s.update_roas()
- rpki.log.debug("Self %s regenerating CRLs and manifests" % s.self_id)
- s.regenerate_crls_and_manifests()
- self.sql.sweep()
- return 200, "OK"
- except Exception, data:
- rpki.log.error(traceback.format_exc())
- return 500, "Unhandled exception %s" % data
-
- ## @var https_ta_cache
- # HTTPS trust anchor cache, to avoid regenerating it for every TLS connection.
- https_ta_cache = None
-
- def clear_https_ta_cache(self):
- """Clear dynamic TLS trust anchors."""
-
- if self.https_ta_cache is not None:
- rpki.log.debug("Clearing HTTPS trusted cert cache")
- self.https_ta_cache = None
-
- def build_https_ta_cache(self):
- """Build dynamic TLS trust anchors."""
-
- if self.https_ta_cache is None:
-
- selves = rpki.left_right.self_elt.sql_fetch_all(self)
- children = rpki.left_right.child_elt.sql_fetch_all(self)
-
- self.https_ta_cache = rpki.https.build_https_ta_cache(
- [c.bpki_cert for c in children if c.bpki_cert is not None] +
- [c.bpki_glue for c in children if c.bpki_glue is not None] +
- [s.bpki_cert for s in selves if s.bpki_cert is not None] +
- [s.bpki_glue for s in selves if s.bpki_glue is not None] +
- [self.irbe_cert, self.irdb_cert, self.bpki_ta])
-
- return self.https_ta_cache
-
-
-class ca_obj(rpki.sql.sql_persistant):
- """Internal CA object."""
-
- sql_template = rpki.sql.template(
- "ca",
- "ca_id",
- "last_crl_sn",
- ("next_crl_update", rpki.sundial.datetime),
- "last_issued_sn", "last_manifest_sn",
- ("next_manifest_update", rpki.sundial.datetime),
- "sia_uri", "parent_id", "parent_resource_class")
-
- last_crl_sn = 0
- last_issued_sn = 0
- last_manifest_sn = 0
-
- def parent(self):
- """Fetch parent object to which this CA object links."""
- return rpki.left_right.parent_elt.sql_fetch(self.gctx, self.parent_id)
-
- def ca_details(self):
- """Fetch all ca_detail objects that link to this CA object."""
- return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s", (self.ca_id,))
-
- def fetch_pending(self):
- """Fetch the pending ca_details for this CA, if any."""
- return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND state = 'pending'", (self.ca_id,))
-
- def fetch_active(self):
- """Fetch the active ca_detail for this CA, if any."""
- return ca_detail_obj.sql_fetch_where1(self.gctx, "ca_id = %s AND state = 'active'", (self.ca_id,))
-
- def fetch_deprecated(self):
- """Fetch deprecated ca_details for this CA, if any."""
- return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND state = 'deprecated'", (self.ca_id,))
-
- def fetch_revoked(self):
- """Fetch revoked ca_details for this CA, if any."""
- return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND state = 'revoked'", (self.ca_id,))
-
- def construct_sia_uri(self, parent, rc):
- """Construct the sia_uri value for this CA given configured
- information and the parent's up-down protocol list_response PDU.
- """
-
- repository = parent.repository()
- sia_uri = rc.suggested_sia_head and rc.suggested_sia_head.rsync()
- if not sia_uri or not sia_uri.startswith(parent.sia_base):
- sia_uri = parent.sia_base
- elif not sia_uri.endswith("/"):
- raise rpki.exceptions.BadURISyntax, "SIA URI must end with a slash: %s" % sia_uri
- return sia_uri + str(self.ca_id) + "/"
-
- def check_for_updates(self, parent, rc):
- """Parent has signaled continued existance of a resource class we
- already knew about, so we need to check for an updated
- certificate, changes in resource coverage, revocation and reissue
- with the same key, etc.
- """
-
- sia_uri = self.construct_sia_uri(parent, rc)
- sia_uri_changed = self.sia_uri != sia_uri
- if sia_uri_changed:
- self.sia_uri = sia_uri
- self.sql_mark_dirty()
-
- rc_resources = rc.to_resource_bag()
- cert_map = dict((c.cert.get_SKI(), c) for c in rc.certs)
-
- for ca_detail in ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND latest_ca_cert IS NOT NULL AND state != 'revoked'", (self.ca_id,)):
-
- ski = ca_detail.latest_ca_cert.get_SKI()
-
- if ski not in cert_map:
- rpki.log.warn("Certificate in database missing from list_response, class %s, SKI %s, maybe parent certificate went away?"
- % (repr(rc.class_name), ca_detail.latest_ca_cert.gSKI()))
- ca_detail.delete(self, parent.repository())
- continue
-
- if ca_detail.state in ("pending", "active"):
- current_resources = ca_detail.latest_ca_cert.get_3779resources()
- if sia_uri_changed or \
- ca_detail.latest_ca_cert != cert_map[ski].cert or \
- current_resources.undersized(rc_resources) or \
- current_resources.oversized(rc_resources):
- ca_detail.update(
- parent = parent,
- ca = self,
- rc = rc,
- sia_uri_changed = sia_uri_changed,
- old_resources = current_resources)
-
- del cert_map[ski]
-
- if cert_map:
- rpki.log.warn("Certificates in list_response missing from our database, class %s, SKIs %s"
- % (repr(rc.class_name), ", ".join(c.cert.gSKI() for c in cert_map.values())))
-
- @classmethod
- def create(cls, parent, rc):
- """Parent has signaled existance of a new resource class, so we
- need to create and set up a corresponding CA object.
- """
-
- self = cls()
- self.gctx = parent.gctx
- self.parent_id = parent.parent_id
- self.parent_resource_class = rc.class_name
- self.sql_store()
- self.sia_uri = self.construct_sia_uri(parent, rc)
- ca_detail = ca_detail_obj.create(self)
-
- # This will need a callback when we go event-driven
- issue_response = rpki.up_down.issue_pdu.query(parent, self, ca_detail)
-
- ca_detail.activate(
- ca = self,
- cert = issue_response.payload.classes[0].certs[0].cert,
- uri = issue_response.payload.classes[0].certs[0].cert_url)
-
- def delete(self, parent):
- """The list of current resource classes received from parent does
- not include the class corresponding to this CA, so we need to
- delete it (and its little dog too...).
-
- All certs published by this CA are now invalid, so need to
- withdraw them, the CRL, and the manifest from the repository,
- delete all child_cert and ca_detail records associated with this
- CA, then finally delete this CA itself.
- """
-
- repository = parent.repository()
- for ca_detail in self.ca_details():
- ca_detail.delete(self, repository)
- self.sql_delete()
-
- def next_serial_number(self):
- """Allocate a certificate serial number."""
- self.last_issued_sn += 1
- self.sql_mark_dirty()
- return self.last_issued_sn
-
- def next_manifest_number(self):
- """Allocate a manifest serial number."""
- self.last_manifest_sn += 1
- self.sql_mark_dirty()
- return self.last_manifest_sn
-
- def next_crl_number(self):
- """Allocate a CRL serial number."""
- self.last_crl_sn += 1
- self.sql_mark_dirty()
- return self.last_crl_sn
-
- def rekey(self):
- """Initiate a rekey operation for this ca. Generate a new
- keypair. Request cert from parent using new keypair. Mark result
- as our active ca_detail. Reissue all child certs issued by this
- ca using the new ca_detail.
- """
-
- rpki.log.trace()
-
- parent = self.parent()
- old_detail = self.fetch_active()
- new_detail = ca_detail_obj.create(self)
-
- # This will need a callback when we go event-driven
- issue_response = rpki.up_down.issue_pdu.query(parent, self, new_detail)
-
- new_detail.activate(
- ca = self,
- cert = issue_response.payload.classes[0].certs[0].cert,
- uri = issue_response.payload.classes[0].certs[0].cert_url,
- predecessor = old_detail)
-
- def revoke(self):
- """Revoke deprecated ca_detail objects associated with this ca."""
-
- rpki.log.trace()
-
- for ca_detail in self.fetch_deprecated():
- ca_detail.revoke()
-
-class ca_detail_obj(rpki.sql.sql_persistant):
- """Internal CA detail object."""
-
- sql_template = rpki.sql.template(
- "ca_detail",
- "ca_detail_id",
- ("private_key_id", rpki.x509.RSA),
- ("public_key", rpki.x509.RSApublic),
- ("latest_ca_cert", rpki.x509.X509),
- ("manifest_private_key_id", rpki.x509.RSA),
- ("manifest_public_key", rpki.x509.RSApublic),
- ("latest_manifest_cert", rpki.x509.X509),
- ("latest_manifest", rpki.x509.SignedManifest),
- ("latest_crl", rpki.x509.CRL),
- "state",
- "ca_cert_uri",
- "ca_id")
-
- def sql_decode(self, vals):
- """Extra assertions for SQL decode of a ca_detail_obj."""
- rpki.sql.sql_persistant.sql_decode(self, vals)
- assert (self.public_key is None and self.private_key_id is None) or \
- self.public_key.get_DER() == self.private_key_id.get_public_DER()
- assert (self.manifest_public_key is None and self.manifest_private_key_id is None) or \
- self.manifest_public_key.get_DER() == self.manifest_private_key_id.get_public_DER()
-
- def ca(self):
- """Fetch CA object to which this ca_detail links."""
- return ca_obj.sql_fetch(self.gctx, self.ca_id)
-
- def child_certs(self, child = None, ski = None, unique = False):
- """Fetch all child_cert objects that link to this ca_detail."""
- return rpki.rpki_engine.child_cert_obj.fetch(self.gctx, child, self, ski, unique)
-
- def revoked_certs(self):
- """Fetch all revoked_cert objects that link to this ca_detail."""
- return revoked_cert_obj.sql_fetch_where(self.gctx, "ca_detail_id = %s", (self.ca_detail_id,))
-
- def route_origins(self):
- """Fetch all route_origin objects that link to this ca_detail."""
- return rpki.left_right.route_origin_elt.sql_fetch_where(self.gctx, "ca_detail_id = %s", (self.ca_detail_id,))
-
- def crl_uri(self, ca):
- """Return publication URI for this ca_detail's CRL."""
- return ca.sia_uri + self.crl_uri_tail()
-
- def crl_uri_tail(self):
- """Return tail (filename portion) of publication URI for this ca_detail's CRL."""
- return self.public_key.gSKI() + ".crl"
-
- def manifest_uri(self, ca):
- """Return publication URI for this ca_detail's manifest."""
- return ca.sia_uri + self.public_key.gSKI() + ".mnf"
-
- def activate(self, ca, cert, uri, predecessor = None):
- """Activate this ca_detail."""
-
- self.latest_ca_cert = cert
- self.ca_cert_uri = uri.rsync()
- self.generate_manifest_cert(ca)
- self.generate_crl()
- self.generate_manifest()
- self.state = "active"
- self.sql_mark_dirty()
-
- if predecessor is not None:
- predecessor.state = "deprecated"
- predecessor.sql_mark_dirty()
- for child_cert in predecessor.child_certs():
- child_cert.reissue(self)
- for route_origin in predecessor.route_origins():
- route_origin.regenerate_roa()
-
- def delete(self, ca, repository):
- """Delete this ca_detail and all of the certs it issued."""
-
- for child_cert in self.child_certs():
- repository.withdraw(child_cert.cert, child_cert.uri(ca))
- child_cert.sql_delete()
- for revoked_cert in self.revoked_certs():
- revoked_cert.sql_delete()
- for route_origin in self.route_origins():
- route_origin.withdraw_roa()
- repository.withdraw(self.latest_manifest, self.manifest_uri(ca))
- repository.withdraw(self.latest_crl, self.crl_uri(ca))
- self.sql_delete()
-
- def revoke(self):
- """Request revocation of all certificates whose SKI matches the key for this ca_detail.
-
- Tasks:
-
- - Request revocation of old keypair by parent.
-
- - Revoke all child certs issued by the old keypair.
-
- - Generate a final CRL, signed with the old keypair, listing all
- the revoked certs, with a next CRL time after the last cert or
- CRL signed by the old keypair will have expired.
-
- - Destroy old keypair (and manifest keypair).
-
- - Leave final CRL in place until its next CRL time has passed.
- """
-
- # This will need a callback when we go event-driven
- r_msg = rpki.up_down.revoke_pdu.query(self)
-
- if r_msg.payload.ski != self.latest_ca_cert.gSKI():
- raise rpki.exceptions.SKIMismatch
-
- ca = self.ca()
- parent = ca.parent()
- crl_interval = rpki.sundial.timedelta(seconds = parent.self().crl_interval)
-
- nextUpdate = rpki.sundial.now()
-
- if self.latest_manifest is not None:
- nextUpdate = nextUpdate.later(self.latest_manifest.getNextUpdate())
-
- if self.latest_crl is not None:
- nextUpdate = nextUpdate.later(self.latest_crl.getNextUpdate())
-
- for child_cert in self.child_certs():
- nextUpdate = nextUpdate.later(child_cert.cert.getNotAfter())
- child_cert.revoke()
-
- nextUpdate += crl_interval
-
- self.generate_crl(nextUpdate)
- self.generate_manifest(nextUpdate)
-
- self.private_key_id = None
- self.manifest_private_key_id = None
- self.manifest_public_key = None
- self.latest_manifest_cert = None
- self.state = "revoked"
- self.sql_mark_dirty()
-
- def update(self, parent, ca, rc, sia_uri_changed, old_resources):
- """Need to get a new certificate for this ca_detail and perhaps
- frob children of this ca_detail.
- """
-
- # This will need a callback when we go event-driven
- issue_response = rpki.up_down.issue_pdu.query(parent, ca, self)
-
- self.latest_ca_cert = issue_response.payload.classes[0].certs[0].cert
- new_resources = self.latest_ca_cert.get_3779resources()
-
- if sia_uri_changed or old_resources.oversized(new_resources):
- for child_cert in self.child_certs():
- child_resources = child_cert.cert.get_3779resources()
- if sia_uri_changed or child_resources.oversized(new_resources):
- child_cert.reissue(
- ca_detail = self,
- resources = child_resources.intersection(new_resources))
-
- @classmethod
- def create(cls, ca):
- """Create a new ca_detail object for a specified CA."""
- self = cls()
- self.gctx = ca.gctx
- self.ca_id = ca.ca_id
- self.state = "pending"
-
- self.private_key_id = rpki.x509.RSA.generate()
- self.public_key = self.private_key_id.get_RSApublic()
-
- self.manifest_private_key_id = rpki.x509.RSA.generate()
- self.manifest_public_key = self.manifest_private_key_id.get_RSApublic()
-
- self.sql_store()
- return self
-
- def issue_ee(self, ca, resources, subject_key, sia = None):
- """Issue a new EE certificate."""
-
- return self.latest_ca_cert.issue(
- keypair = self.private_key_id,
- subject_key = subject_key,
- serial = ca.next_serial_number(),
- sia = sia,
- aia = self.ca_cert_uri,
- crldp = self.crl_uri(ca),
- resources = resources,
- notAfter = self.latest_ca_cert.getNotAfter(),
- is_ca = False)
-
-
- def generate_manifest_cert(self, ca):
- """Generate a new manifest certificate for this ca_detail."""
-
- resources = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as("<inherit>"),
- v4 = rpki.resource_set.resource_set_ipv4("<inherit>"),
- v6 = rpki.resource_set.resource_set_ipv6("<inherit>"))
-
- self.latest_manifest_cert = self.issue_ee(ca, resources, self.manifest_public_key)
-
- def issue(self, ca, child, subject_key, sia, resources, child_cert = None):
- """Issue a new certificate to a child. Optional child_cert
- argument specifies an existing child_cert object to update in
- place; if not specified, we create a new one. Returns the
- child_cert object containing the newly issued cert.
- """
-
- assert child_cert is None or (child_cert.child_id == child.child_id and
- child_cert.ca_detail_id == self.ca_detail_id)
-
- cert = self.latest_ca_cert.issue(
- keypair = self.private_key_id,
- subject_key = subject_key,
- serial = ca.next_serial_number(),
- aia = self.ca_cert_uri,
- crldp = self.crl_uri(ca),
- sia = sia,
- resources = resources,
- notAfter = resources.valid_until)
-
- if child_cert is None:
- child_cert = rpki.rpki_engine.child_cert_obj(
- gctx = child.gctx,
- child_id = child.child_id,
- ca_detail_id = self.ca_detail_id,
- cert = cert)
- rpki.log.debug("Created new child_cert %s" % repr(child_cert))
- else:
- child_cert.cert = cert
- rpki.log.debug("Reusing existing child_cert %s" % repr(child_cert))
-
- child_cert.ski = cert.get_SKI()
-
- child_cert.sql_store()
-
- ca.parent().repository().publish(child_cert.cert, child_cert.uri(ca))
-
- self.generate_manifest()
-
- return child_cert
-
- def generate_crl(self, nextUpdate = None):
- """Generate a new CRL for this ca_detail. At the moment this is
- unconditional, that is, it is up to the caller to decide whether a
- new CRL is needed.
- """
-
- ca = self.ca()
- parent = ca.parent()
- repository = parent.repository()
- crl_interval = rpki.sundial.timedelta(seconds = parent.self().crl_interval)
- now = rpki.sundial.now()
-
- if nextUpdate is None:
- nextUpdate = now + crl_interval
-
- certlist = []
- for revoked_cert in self.revoked_certs():
- if now > revoked_cert.expires + crl_interval:
- revoked_cert.sql_delete()
- else:
- certlist.append((revoked_cert.serial, revoked_cert.revoked.toASN1tuple(), ()))
- certlist.sort()
-
- self.latest_crl = rpki.x509.CRL.generate(
- keypair = self.private_key_id,
- issuer = self.latest_ca_cert,
- serial = ca.next_crl_number(),
- thisUpdate = now,
- nextUpdate = nextUpdate,
- revokedCertificates = certlist)
-
- repository.publish(self.latest_crl, self.crl_uri(ca))
-
- def generate_manifest(self, nextUpdate = None):
- """Generate a new manifest for this ca_detail."""
-
- ca = self.ca()
- parent = ca.parent()
- repository = parent.repository()
- crl_interval = rpki.sundial.timedelta(seconds = parent.self().crl_interval)
- now = rpki.sundial.now()
-
- if nextUpdate is None:
- nextUpdate = now + crl_interval
-
- route_origins = [r for r in self.route_origins() if r.cert is not None and r.roa is not None]
-
- if self.latest_manifest_cert is None or self.latest_manifest_cert.getNotAfter() < nextUpdate:
- self.generate_manifest_cert(ca)
-
- certs = [(c.uri_tail(), c.cert) for c in self.child_certs()] + \
- [(r.roa_uri_tail(), r.roa) for r in route_origins] + \
- [(r.ee_uri_tail(), r.cert) for r in route_origins] + \
- [(self.crl_uri_tail(), self.latest_crl)]
-
- self.latest_manifest = rpki.x509.SignedManifest.build(
- serial = ca.next_manifest_number(),
- thisUpdate = now,
- nextUpdate = nextUpdate,
- names_and_objs = certs,
- keypair = self.manifest_private_key_id,
- certs = self.latest_manifest_cert)
-
- repository.publish(self.latest_manifest, self.manifest_uri(ca))
-
-class child_cert_obj(rpki.sql.sql_persistant):
- """Certificate that has been issued to a child."""
-
- sql_template = rpki.sql.template(
- "child_cert",
- "child_cert_id",
- ("cert", rpki.x509.X509),
- "child_id",
- "ca_detail_id",
- "ski")
-
- def __init__(self, gctx = None, child_id = None, ca_detail_id = None, cert = None):
- """Initialize a child_cert_obj."""
- self.gctx = gctx
- self.child_id = child_id
- self.ca_detail_id = ca_detail_id
- self.cert = cert
- if child_id or ca_detail_id or cert:
- self.sql_mark_dirty()
-
- def child(self):
- """Fetch child object to which this child_cert object links."""
- return rpki.left_right.child_elt.sql_fetch(self.gctx, self.child_id)
-
- def ca_detail(self):
- """Fetch ca_detail object to which this child_cert object links."""
- return ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id)
-
- def uri_tail(self):
- """Return the tail (filename) portion of the URI for this child_cert."""
- return self.cert.gSKI() + ".cer"
-
- def uri(self, ca):
- """Return the publication URI for this child_cert."""
- return ca.sia_uri + self.uri_tail()
-
- def revoke(self):
- """Revoke a child cert."""
- rpki.log.debug("Revoking %s" % repr(self))
- ca_detail = self.ca_detail()
- ca = ca_detail.ca()
- revoked_cert_obj.revoke(cert = self.cert, ca_detail = ca_detail)
- repository = ca.parent().repository()
- repository.withdraw(self.cert, self.uri(ca))
- self.gctx.sql.sweep()
- self.sql_delete()
-
- def reissue(self, ca_detail, resources = None, sia = None):
- """Reissue an existing cert, reusing the public key. If the cert
- we would generate is identical to the one we already have, we just
- return the one we already have. If we have to revoke the old
- certificate when generating the new one, we have to generate a new
- child_cert_obj, so calling code that needs the updated
- child_cert_obj must use the return value from this method.
- """
-
- ca = ca_detail.ca()
- child = self.child()
-
- old_resources = self.cert.get_3779resources()
- old_sia = self.cert.get_SIA()
- old_ca_detail = self.ca_detail()
-
- if resources is None:
- resources = old_resources
-
- if sia is None:
- sia = old_sia
-
- assert resources.valid_until is not None and old_resources.valid_until is not None
-
- if resources == old_resources and sia == old_sia and ca_detail == old_ca_detail:
- return self
-
- must_revoke = old_resources.oversized(resources) or old_resources.valid_until > resources.valid_until
- new_issuer = ca_detail != old_ca_detail
-
- if resources.valid_until != old_resources.valid_until:
- rpki.log.debug("Validity changed: %s %s" % ( old_resources.valid_until, resources.valid_until))
-
- if must_revoke or new_issuer:
- child_cert = None
- else:
- child_cert = self
-
- child_cert = ca_detail.issue(
- ca = ca,
- child = child,
- subject_key = self.cert.getPublicKey(),
- sia = sia,
- resources = resources,
- child_cert = child_cert)
-
- if must_revoke:
- for cert in child.child_certs(ca_detail = ca_detail, ski = self.ski):
- if cert is not child_cert:
- cert.revoke()
-
- return child_cert
-
- @classmethod
- def fetch(cls, gctx = None, child = None, ca_detail = None, ski = None, unique = False):
- """Fetch all child_cert objects matching a particular set of
- parameters. This is a wrapper to consolidate various queries that
- would otherwise be inline SQL WHERE expressions. In most cases
- code calls this indirectly, through methods in other classes.
- """
-
- args = []
- where = []
-
- if child:
- where.append("child_id = %s")
- args.append(child.child_id)
-
- if ca_detail:
- where.append("ca_detail_id = %s")
- args.append(ca_detail.ca_detail_id)
-
- if ski:
- where.append("ski = %s")
- args.append(ski)
-
- where = " AND ".join(where)
-
- gctx = gctx or (child and child.gctx) or (ca_detail and ca_detail.gctx) or None
-
- if unique:
- return cls.sql_fetch_where1(gctx, where, args)
- else:
- return cls.sql_fetch_where(gctx, where, args)
-
-class revoked_cert_obj(rpki.sql.sql_persistant):
- """Tombstone for a revoked certificate."""
-
- sql_template = rpki.sql.template(
- "revoked_cert",
- "revoked_cert_id",
- "serial",
- "ca_detail_id",
- ("revoked", rpki.sundial.datetime),
- ("expires", rpki.sundial.datetime))
-
- def __init__(self, gctx = None, serial = None, revoked = None, expires = None, ca_detail_id = None):
- """Initialize a revoked_cert_obj."""
- self.gctx = gctx
- self.serial = serial
- self.revoked = revoked
- self.expires = expires
- self.ca_detail_id = ca_detail_id
- if serial or revoked or expires or ca_detail_id:
- self.sql_mark_dirty()
-
- def ca_detail(self):
- """Fetch ca_detail object to which this revoked_cert_obj links."""
- return ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id)
-
- @classmethod
- def revoke(cls, cert, ca_detail):
- """Revoke a certificate."""
- return cls(
- serial = cert.getSerial(),
- expires = cert.getNotAfter(),
- revoked = rpki.sundial.now(),
- gctx = ca_detail.gctx,
- ca_detail_id = ca_detail.ca_detail_id)
diff --git a/rpkid.stable/rpki/sql.py b/rpkid.stable/rpki/sql.py
deleted file mode 100644
index e9284539..00000000
--- a/rpkid.stable/rpki/sql.py
+++ /dev/null
@@ -1,295 +0,0 @@
-"""SQL interface code.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 MySQLdb, time, warnings, _mysql_exceptions
-import rpki.x509, rpki.resource_set, rpki.sundial, rpki.log
-
-class session(object):
- """SQL session layer."""
-
- _exceptions_enabled = False
-
- def __init__(self, cfg):
-
- if not self._exceptions_enabled:
- warnings.simplefilter("error", _mysql_exceptions.Warning)
- self.__class__._exceptions_enabled = True
-
- self.username = cfg.get("sql-username")
- self.database = cfg.get("sql-database")
- self.password = cfg.get("sql-password")
-
- self.cache = {}
- self.dirty = set()
-
- self.connect()
-
- def connect(self):
- self.db = MySQLdb.connect(user = self.username, db = self.database, passwd = self.password)
- self.cur = self.db.cursor()
-
- def close(self):
- if self.cur:
- self.cur.close()
- self.cur = None
- if self.db:
- self.db.close()
- self.db = None
-
- def ping(self):
- return self.db.ping(True)
-
- def _wrap_execute(self, func, query, args):
- try:
- return func(query, args)
- except _mysql_exceptions.MySQLError:
- if self.dirty:
- rpki.log.warn("MySQL exception with dirty objects in SQL cache!")
- raise
-
- def execute(self, query, args = None):
- return self._wrap_execute(self.cur.execute, query, args)
-
- def executemany(self, query, args):
- return self._wrap_execute(self.cur.executemany, query, args)
-
- def fetchall(self):
- return self.cur.fetchall()
-
- def lastrowid(self):
- return self.cur.lastrowid
-
- def cache_clear(self):
- """Clear the object cache."""
- self.cache.clear()
-
- def assert_pristine(self):
- """Assert that there are no dirty objects in the cache."""
- assert not self.dirty, "Dirty objects in SQL cache: %s" % self.dirty
-
- def sweep(self):
- """Write any dirty objects out to SQL."""
- for s in self.dirty.copy():
- rpki.log.debug("Sweeping %s" % repr(s))
- if s.sql_deleted:
- s.sql_delete()
- else:
- s.sql_store()
- self.assert_pristine()
-
-class template(object):
- """SQL template generator."""
- def __init__(self, table_name, index_column, *data_columns):
- """Build a SQL template."""
- type_map = dict((x[0],x[1]) for x in data_columns if isinstance(x, tuple))
- data_columns = tuple(isinstance(x, tuple) and x[0] or x for x in data_columns)
- columns = (index_column,) + data_columns
- self.table = table_name
- self.index = index_column
- self.columns = columns
- self.map = type_map
- self.select = "SELECT %s FROM %s" % (", ".join(columns), table_name)
- self.insert = "INSERT %s (%s) VALUES (%s)" % (table_name, ", ".join(data_columns),
- ", ".join("%(" + s + ")s" for s in data_columns))
- self.update = "UPDATE %s SET %s WHERE %s = %%(%s)s" % \
- (table_name, ", ".join(s + " = %(" + s + ")s" for s in data_columns),
- index_column, index_column)
- self.delete = "DELETE FROM %s WHERE %s = %%s" % (table_name, index_column)
-
-class sql_persistant(object):
- """Mixin for persistant class that needs to be stored in SQL.
- """
-
- ## @var sql_in_db
- # Whether this object is already in SQL or not.
-
- sql_in_db = False
-
- ## @var sql_deleted
- # Whether our cached copy of this object has been deleted.
-
- sql_deleted = False
-
- ## @var sql_debug
- # Enable logging of SQL actions
-
- sql_debug = False
-
- @classmethod
- def sql_fetch(cls, gctx, id):
- """Fetch one object from SQL, based on its primary key.
-
- Since in this one case we know that the primary index is also the
- cache key, we check for a cache hit directly in the hope of
- bypassing the SQL lookup entirely.
-
- This method is usually called via a one-line class-specific
- wrapper. As a convenience, we also accept an id of None, and just
- return None in this case.
- """
-
- if id is None:
- return None
- assert isinstance(id, (int, long)), "id should be an integer, was %s" % repr(type(id))
- key = (cls, id)
- if key in gctx.sql.cache:
- return gctx.sql.cache[key]
- else:
- return cls.sql_fetch_where1(gctx, "%s = %%s" % cls.sql_template.index, (id,))
-
- @classmethod
- def sql_fetch_where1(cls, gctx, where, args = None):
- """Fetch one object from SQL, based on an arbitrary SQL WHERE expression."""
- results = cls.sql_fetch_where(gctx, where, args)
- if len(results) == 0:
- return None
- elif len(results) == 1:
- return results[0]
- else:
- raise rpki.exceptions.DBConsistancyError, \
- "Database contained multiple matches for %s where %s" % \
- (cls.__name__, where % tuple(repr(a) for a in args))
-
- @classmethod
- def sql_fetch_all(cls, gctx):
- """Fetch all objects of this type from SQL."""
- return cls.sql_fetch_where(gctx, None)
-
- @classmethod
- def sql_fetch_where(cls, gctx, where, args = None):
- """Fetch objects of this type matching an arbitrary SQL WHERE expression."""
- if where is None:
- assert args is None
- if cls.sql_debug:
- rpki.log.debug("sql_fetch_where(%s)" % repr(cls.sql_template.select))
- gctx.sql.execute(cls.sql_template.select)
- else:
- query = cls.sql_template.select + " WHERE " + where
- if cls.sql_debug:
- rpki.log.debug("sql_fetch_where(%s, %s)" % (repr(query), repr(args)))
- gctx.sql.execute(query, args)
- results = []
- for row in gctx.sql.fetchall():
- key = (cls, row[0])
- if key in gctx.sql.cache:
- results.append(gctx.sql.cache[key])
- else:
- results.append(cls.sql_init(gctx, row, key))
- return results
-
- @classmethod
- def sql_init(cls, gctx, row, key):
- """Initialize one Python object from the result of a SQL query."""
- self = cls()
- self.gctx = gctx
- self.sql_decode(dict(zip(cls.sql_template.columns, row)))
- gctx.sql.cache[key] = self
- self.sql_in_db = True
- self.sql_fetch_hook()
- return self
-
- def sql_mark_dirty(self):
- """Mark this object as needing to be written back to SQL."""
- self.gctx.sql.dirty.add(self)
-
- def sql_mark_clean(self):
- """Mark this object as not needing to be written back to SQL."""
- self.gctx.sql.dirty.discard(self)
-
- def sql_is_dirty(self):
- """Query whether this object needs to be written back to SQL."""
- return self in self.gctx.sql.dirty
-
- def sql_mark_deleted(self):
- """Mark this object as needing to be deleted in SQL."""
- self.sql_deleted = True
-
- def sql_store(self):
- """Store this object to SQL."""
- args = self.sql_encode()
- if not self.sql_in_db:
- if self.sql_debug:
- rpki.log.debug("sql_fetch_store(%s, %s)" % (repr(self.sql_template.insert), repr(args)))
- self.gctx.sql.execute(self.sql_template.insert, args)
- setattr(self, self.sql_template.index, self.gctx.sql.lastrowid())
- self.gctx.sql.cache[(self.__class__, self.gctx.sql.lastrowid())] = self
- self.sql_insert_hook()
- else:
- if self.sql_debug:
- rpki.log.debug("sql_fetch_store(%s, %s)" % (repr(self.sql_template.update), repr(args)))
- self.gctx.sql.execute(self.sql_template.update, args)
- self.sql_update_hook()
- key = (self.__class__, getattr(self, self.sql_template.index))
- assert key in self.gctx.sql.cache and self.gctx.sql.cache[key] == self
- self.sql_mark_clean()
- self.sql_in_db = True
-
- def sql_delete(self):
- """Delete this object from SQL."""
- if self.sql_in_db:
- id = getattr(self, self.sql_template.index)
- self.gctx.sql.execute(self.sql_template.delete, id)
- self.sql_delete_hook()
- key = (self.__class__, id)
- if self.gctx.sql.cache.get(key) == self:
- del self.gctx.sql.cache[key]
- self.sql_in_db = False
- self.sql_mark_clean()
-
- def sql_encode(self):
- """Convert object attributes into a dict for use with canned SQL
- queries. This is a default version that assumes a one-to-one
- mapping between column names in SQL and attribute names in Python.
- If you need something fancier, override this.
- """
- d = dict((a, getattr(self, a, None)) for a in self.sql_template.columns)
- for i in self.sql_template.map:
- if d.get(i) is not None:
- d[i] = self.sql_template.map[i].to_sql(d[i])
- return d
-
- def sql_decode(self, vals):
- """Initialize an object with values returned by self.sql_fetch().
- This is a default version that assumes a one-to-one mapping
- between column names in SQL and attribute names in Python. If you
- need something fancier, override this.
- """
- for a in self.sql_template.columns:
- if vals.get(a) is not None and a in self.sql_template.map:
- setattr(self, a, self.sql_template.map[a].from_sql(vals[a]))
- else:
- setattr(self, a, vals[a])
-
- def sql_fetch_hook(self):
- """Customization hook."""
- pass
-
- def sql_insert_hook(self):
- """Customization hook."""
- pass
-
- def sql_update_hook(self):
- """Customization hook."""
- self.sql_delete_hook()
- self.sql_insert_hook()
-
- def sql_delete_hook(self):
- """Customization hook."""
- pass
-
diff --git a/rpkid.stable/rpki/sundial.py b/rpkid.stable/rpki/sundial.py
deleted file mode 100644
index 1d7ff8bf..00000000
--- a/rpkid.stable/rpki/sundial.py
+++ /dev/null
@@ -1,198 +0,0 @@
-"""Unified RPKI date/time handling, based on the standard Python datetime module.
-
-Module name chosen to sidestep a nightmare of import-related errors
-that occur with the more obvious module names.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 datetime as pydatetime
-import re
-
-def now():
- """Get current timestamp."""
- return datetime.utcnow()
-
-class datetime(pydatetime.datetime):
- """RPKI extensions to standard datetime.datetime class. All work
- here is in UTC, so we use naive datetime objects.
- """
-
- def totimestamp(self):
- """Convert to seconds from epoch (like time.time()). Conversion
- method is a bit silly, but avoids time module timezone whackiness.
- """
- return int(self.strftime("%s"))
-
- @classmethod
- def fromUTCTime(cls, x):
- """Convert from ASN.1 UTCTime."""
- return cls.strptime(x, "%y%m%d%H%M%SZ")
-
- def toUTCTime(self):
- """Convert to ASN.1 UTCTime."""
- return self.strftime("%y%m%d%H%M%SZ")
-
- @classmethod
- def fromGeneralizedTime(cls, x):
- """Convert from ASN.1 GeneralizedTime."""
- return cls.strptime(x, "%Y%m%d%H%M%SZ")
-
- def toGeneralizedTime(self):
- """Convert to ASN.1 GeneralizedTime."""
- return self.strftime("%Y%m%d%H%M%SZ")
-
- @classmethod
- def fromASN1tuple(cls, x):
- """Convert from ASN.1 tuple representation."""
- assert isinstance(x, tuple) and len(x) == 2 and x[0] in ("utcTime", "generalTime")
- if x[0] == "utcTime":
- return cls.fromUTCTime(x[1])
- else:
- return cls.fromGeneralizedTime(x[1])
-
- ## @var PKIX_threshhold
- # Threshold specified in RFC 3280 for switchover from UTCTime to GeneralizedTime.
-
- PKIX_threshhold = pydatetime.datetime(2050, 1, 1)
-
- def toASN1tuple(self):
- """Convert to ASN.1 tuple representation."""
- if self < self.PKIX_threshhold:
- return "utcTime", self.toUTCTime()
- else:
- return "generalTime", self.toGeneralizedTime()
-
- @classmethod
- def fromXMLtime(cls, x):
- """Convert from XML time representation."""
- if x is None:
- return None
- else:
- return cls.strptime(x, "%Y-%m-%dT%H:%M:%SZ")
-
- def toXMLtime(self):
- """Convert to XML time representation."""
- return self.strftime("%Y-%m-%dT%H:%M:%SZ")
-
- def __str__(self):
- return self.toXMLtime()
-
- @classmethod
- def fromdatetime(cls, x):
- """Convert a datetime.datetime object into this subclass.
- This is whacky due to the weird constructors for datetime.
- """
- return cls.combine(x.date(), x.time())
-
- def __add__(self, other):
- """Force correct class for timedelta results."""
- return self.fromdatetime(pydatetime.datetime.__add__(self, other))
-
- def __sub__(self, other):
- """Force correct class for timedelta results."""
- return self.fromdatetime(pydatetime.datetime.__sub__(self, other))
-
- @classmethod
- def from_sql(cls, x):
- """Convert from SQL storage format."""
- return cls.fromdatetime(x)
-
- def to_sql(self):
- """Convert to SQL storage format.
-
- There's something whacky going on in the MySQLdb module, it throws
- range errors when storing a derived type into a DATETIME column.
- Investigate some day, but for now brute force this by copying the
- relevant fields into a datetime.datetime for MySQLdb's
- consumption.
-
- """
- return pydatetime.datetime(year = self.year, month = self.month, day = self.day,
- hour = self.hour, minute = self.minute, second = self.second,
- microsecond = 0, tzinfo = None)
-
- def later(self, other):
- """Return the later of two timestamps."""
- return other if other > self else self
-
- def earlier(self, other):
- """Return the earlier of two timestamps."""
- return other if other < self else self
-
-class timedelta(pydatetime.timedelta):
- """Timedelta with text parsing. This accepts two input formats:
-
- - A simple integer, indicating a number of seconds.
-
- - A string of the form "wD xH yM zS" where w, x, y, and z are integers
- and D, H, M, and S indicate days, hours, minutes, and seconds.
- All of the fields are optional, but at least one must be specified.
- Eg, "3D4H" means "three days plus four hours".
- """
-
- ## @var regexp
- # Hideously ugly regular expression to parse the complex text form.
- # Tags are intended for use with re.MatchObject.groupdict() and map
- # directly to the keywords expected by the timedelta constructor.
-
- regexp = re.compile("\\s*".join(("^",
- "(?:(?P<days>\\d+)D)?",
- "(?:(?P<hours>\\d+)H)?",
- "(?:(?P<minutes>\\d+)M)?",
- "(?:(?P<seconds>\\d+)S)?",
- "$")),
- re.I)
-
- @classmethod
- def parse(cls, arg):
- """Parse text into a timedelta object."""
- if not isinstance(arg, str):
- return cls(seconds = arg)
- elif arg.isdigit():
- return cls(seconds = int(arg))
- else:
- match = cls.regexp.match(arg)
- if match:
- return cls(**dict((k, int(v)) for (k, v) in match.groupdict().items() if v is not None))
- else:
- raise RuntimeError, "Couldn't parse timedelta %s" % repr(arg)
-
-
- def convert_to_seconds(self):
- """Convert a timedelta interval to seconds."""
- return self.days * 24 * 60 * 60 + self.seconds
-
-if __name__ == "__main__":
-
- def test(t):
- print
- print "str: ", t
- print "repr: ", repr(t)
- print "seconds since epoch:", t.strftime("%s")
- print "UTCTime: ", t.toUTCTime()
- print "GeneralizedTime: ", t.toGeneralizedTime()
- print "ASN1tuple: ", t.toASN1tuple()
- print "XMLtime: ", t.toXMLtime()
- print
-
- print
- print "Testing time conversion routines"
- test(now())
- test(now() + timedelta(days = 30))
- test(now() + timedelta.parse("3d5s"))
- timedelta.parse(" 3d 5s ")
diff --git a/rpkid.stable/rpki/up_down.py b/rpkid.stable/rpki/up_down.py
deleted file mode 100644
index 30085390..00000000
--- a/rpkid.stable/rpki/up_down.py
+++ /dev/null
@@ -1,535 +0,0 @@
-"""RPKI "up-down" protocol.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 base64, lxml.etree, time
-import rpki.resource_set, rpki.x509, rpki.exceptions
-import rpki.xml_utils, rpki.relaxng
-
-xmlns="http://www.apnic.net/specs/rescerts/up-down/"
-
-nsmap = { None : xmlns }
-
-class base_elt(object):
- """Generic PDU object.
-
- Virtual class, just provides some default methods.
- """
-
- def startElement(self, stack, name, attrs):
- """Ignore startElement() if there's no specific handler.
-
- Some elements have no attributes and we only care about their
- text content.
- """
- pass
-
- def endElement(self, stack, name, text):
- """Ignore endElement() if there's no specific handler.
-
- If we don't need to do anything else, just pop the stack.
- """
- stack.pop()
-
- def make_elt(self, name, *attrs):
- """Construct a element, copying over a set of attributes."""
- elt = lxml.etree.Element("{%s}%s" % (xmlns, name), nsmap=nsmap)
- for key in attrs:
- val = getattr(self, key, None)
- if val is not None:
- elt.set(key, str(val))
- return elt
-
- def make_b64elt(self, elt, name, value=None):
- """Construct a sub-element with Base64 text content."""
- if value is None:
- value = getattr(self, name, None)
- if value is not None:
- lxml.etree.SubElement(elt, "{%s}%s" % (xmlns, name), nsmap=nsmap).text = base64.b64encode(value)
-
- def serve_pdu(self, q_msg, r_msg, child):
- """Default PDU handler to catch unexpected types."""
- raise rpki.exceptions.BadQuery, "Unexpected query type %s" % q_msg.type
-
- def check_response(self):
- """Placeholder for response checking."""
- pass
-
-class multi_uri(list):
- """Container for a set of URIs."""
-
- def __init__(self, ini):
- """Initialize a set of URIs, which includes basic some syntax checking."""
- if isinstance(ini, (list, tuple)):
- self[:] = ini
- elif isinstance(ini, str):
- self[:] = ini.split(",")
- for s in self:
- if s.strip() != s or s.find("://") < 0:
- raise rpki.exceptions.BadURISyntax, "Bad URI \"%s\"" % s
- else:
- raise TypeError
-
- def __str__(self):
- """Convert a multi_uri back to a string representation."""
- return ",".join(self)
-
- def rsync(self):
- """Find first rsync://... URI in self."""
- for s in self:
- if s.startswith("rsync://"):
- return s
- return None
-
-class certificate_elt(base_elt):
- """Up-Down protocol representation of an issued certificate."""
-
- def startElement(self, stack, name, attrs):
- """Handle attributes of <certificate/> element."""
- assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack)
- self.cert_url = multi_uri(attrs["cert_url"])
- self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as"))
- self.req_resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs.get("req_resource_set_ipv4"))
- self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6"))
-
- def endElement(self, stack, name, text):
- """Handle text content of a <certificate/> element."""
- assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack)
- self.cert = rpki.x509.X509(Base64=text)
- stack.pop()
-
- def toXML(self):
- """Generate a <certificate/> element."""
- elt = self.make_elt("certificate", "cert_url",
- "req_resource_set_as", "req_resource_set_ipv4", "req_resource_set_ipv6")
- elt.text = self.cert.get_Base64()
- return elt
-
-class class_elt(base_elt):
- """Up-Down protocol representation of a resource class."""
-
- issuer = None
-
- def __init__(self):
- """Initialize class_elt."""
- self.certs = []
-
- def startElement(self, stack, name, attrs):
- """Handle <class/> elements and their children."""
- if name == "certificate":
- cert = certificate_elt()
- self.certs.append(cert)
- stack.append(cert)
- cert.startElement(stack, name, attrs)
- elif name != "issuer":
- assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
- self.class_name = attrs["class_name"]
- self.cert_url = multi_uri(attrs["cert_url"])
- self.suggested_sia_head = attrs.get("suggested_sia_head")
- self.resource_set_as = rpki.resource_set.resource_set_as(attrs["resource_set_as"])
- self.resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs["resource_set_ipv4"])
- self.resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs["resource_set_ipv6"])
- self.resource_set_notafter = rpki.sundial.datetime.fromXMLtime(attrs.get("resource_set_notafter"))
-
- def endElement(self, stack, name, text):
- """Handle <class/> elements and their children."""
- if name == "issuer":
- self.issuer = rpki.x509.X509(Base64=text)
- else:
- assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
- stack.pop()
-
- def toXML(self):
- """Generate a <class/> element."""
- elt = self.make_elt("class", "class_name", "cert_url", "resource_set_as",
- "resource_set_ipv4", "resource_set_ipv6",
- "resource_set_notafter", "suggested_sia_head")
- elt.extend([i.toXML() for i in self.certs])
- if self.issuer is not None:
- self.make_b64elt(elt, "issuer", self.issuer.get_DER())
- return elt
-
- def to_resource_bag(self):
- """Build a resource_bag from from this <class/> element."""
- return rpki.resource_set.resource_bag(self.resource_set_as,
- self.resource_set_ipv4,
- self.resource_set_ipv6,
- self.resource_set_notafter)
-
- def from_resource_bag(self, bag):
- """Set resources of this class element from a resource_bag."""
- self.resource_set_as = bag.asn
- self.resource_set_ipv4 = bag.v4
- self.resource_set_ipv6 = bag.v6
- self.resource_set_notafter = bag.valid_until
-
-class list_pdu(base_elt):
- """Up-Down protocol "list" PDU."""
-
- def toXML(self):
- """Generate (empty) payload of "list" PDU."""
- return []
-
- def serve_pdu(self, q_msg, r_msg, child):
- """Serve one "list" PDU."""
- r_msg.payload = list_response_pdu()
-
- # This will require a callback when we go event-driven
- irdb_resources = self.gctx.irdb_query(child.self_id, child.child_id)
-
- for parent in child.parents():
- for ca in parent.cas():
- ca_detail = ca.fetch_active()
- if not ca_detail:
- continue
- resources = ca_detail.latest_ca_cert.get_3779resources().intersection(irdb_resources)
- if resources.empty():
- continue
- rc = class_elt()
- rc.class_name = str(ca.ca_id)
- rc.cert_url = multi_uri(ca_detail.ca_cert_uri)
- rc.from_resource_bag(resources)
- for child_cert in child.child_certs(ca_detail = ca_detail):
- c = certificate_elt()
- c.cert_url = multi_uri(child_cert.uri(ca))
- c.cert = child_cert.cert
- rc.certs.append(c)
- rc.issuer = ca_detail.latest_ca_cert
- r_msg.payload.classes.append(rc)
-
- @classmethod
- def query(cls, parent):
- """Send a "list" query to parent."""
- return parent.query_up_down(cls())
-
-class class_response_syntax(base_elt):
- """Syntax for Up-Down protocol "list_response" and "issue_response" PDUs."""
-
- def __init__(self):
- """Initialize class_response_syntax."""
- self.classes = []
-
- def startElement(self, stack, name, attrs):
- """Handle "list_response" and "issue_response" PDUs."""
- assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
- c = class_elt()
- self.classes.append(c)
- stack.append(c)
- c.startElement(stack, name, attrs)
-
- def toXML(self):
- """Generate payload of "list_response" and "issue_response" PDUs."""
- return [c.toXML() for c in self.classes]
-
-class list_response_pdu(class_response_syntax):
- """Up-Down protocol "list_response" PDU."""
-
- pass
-
-class issue_pdu(base_elt):
- """Up-Down protocol "issue" PDU."""
-
- def startElement(self, stack, name, attrs):
- """Handle "issue" PDU."""
- assert name == "request", "Unexpected name %s, stack %s" % (name, stack)
- self.class_name = attrs["class_name"]
- self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as"))
- self.req_resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs.get("req_resource_set_ipv4"))
- self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6"))
-
- def endElement(self, stack, name, text):
- """Handle "issue" PDU."""
- assert name == "request", "Unexpected name %s, stack %s" % (name, stack)
- self.pkcs10 = rpki.x509.PKCS10(Base64=text)
- stack.pop()
-
- def toXML(self):
- """Generate payload of "issue" PDU."""
- elt = self.make_elt("request", "class_name", "req_resource_set_as",
- "req_resource_set_ipv4", "req_resource_set_ipv6")
- elt.text = self.pkcs10.get_Base64()
- return [elt]
-
- def serve_pdu(self, q_msg, r_msg, child):
- """Serve one issue request PDU."""
-
- # Subsetting not yet implemented, this is the one place where we
- # have to handle it, by reporting that we're lame.
-
- if self.req_resource_set_as or \
- self.req_resource_set_ipv4 or \
- self.req_resource_set_ipv6:
- raise rpki.exceptions.NotImplementedYet, "req_* attributes not implemented yet, sorry"
-
- # Check the request
- self.pkcs10.check_valid_rpki()
- ca = child.ca_from_class_name(self.class_name)
- ca_detail = ca.fetch_active()
- if ca_detail is None:
- raise rpki.exceptions.NoActiveCA, "No active CA for class %s" % repr(self.class_name)
-
- # Check current cert, if any
-
- # This will require a callback when we go event-driven
- irdb_resources = self.gctx.irdb_query(child.self_id, child.child_id)
-
- resources = irdb_resources.intersection(ca_detail.latest_ca_cert.get_3779resources())
- req_key = self.pkcs10.getPublicKey()
- req_sia = self.pkcs10.get_SIA()
- child_cert = child.child_certs(ca_detail = ca_detail, ski = req_key.get_SKI(), unique = True)
-
- # Generate new cert or regenerate old one if necessary
-
- if child_cert is None:
- child_cert = ca_detail.issue(
- ca = ca,
- child = child,
- subject_key = req_key,
- sia = req_sia,
- resources = resources)
- else:
- child_cert = child_cert.reissue(
- ca_detail = ca_detail,
- sia = req_sia,
- resources = resources)
-
- # Save anything we modified and generate response
- self.gctx.sql.sweep()
- assert child_cert and child_cert.sql_in_db
- c = certificate_elt()
- c.cert_url = multi_uri(child_cert.uri(ca))
- c.cert = child_cert.cert
- rc = class_elt()
- rc.class_name = self.class_name
- rc.cert_url = multi_uri(ca_detail.ca_cert_uri)
- rc.from_resource_bag(resources)
- rc.certs.append(c)
- rc.issuer = ca_detail.latest_ca_cert
- r_msg.payload = issue_response_pdu()
- r_msg.payload.classes.append(rc)
-
- @classmethod
- def query(cls, parent, ca, ca_detail):
- """Send an "issue" request to parent associated with ca."""
- assert ca_detail is not None and ca_detail.state in ("pending", "active")
- sia = ((rpki.oids.name2oid["id-ad-caRepository"], ("uri", ca.sia_uri)),
- (rpki.oids.name2oid["id-ad-rpkiManifest"], ("uri", ca_detail.manifest_uri(ca))))
- self = cls()
- self.class_name = ca.parent_resource_class
- self.pkcs10 = rpki.x509.PKCS10.create_ca(ca_detail.private_key_id, sia)
- return parent.query_up_down(self)
-
-class issue_response_pdu(class_response_syntax):
- """Up-Down protocol "issue_response" PDU."""
-
- def check_response(self):
- """Check whether this looks like a reasonable issue_response PDU.
- XML schema should be tighter for this response.
- """
- if len(self.classes) != 1 or len(self.classes[0].certs) != 1:
- raise rpki.exceptions.BadIssueResponse
-
-class revoke_syntax(base_elt):
- """Syntax for Up-Down protocol "revoke" and "revoke_response" PDUs."""
-
- def startElement(self, stack, name, attrs):
- """Handle "revoke" PDU."""
- self.class_name = attrs["class_name"]
- self.ski = attrs["ski"]
-
- def toXML(self):
- """Generate payload of "revoke" PDU."""
- return [self.make_elt("key", "class_name", "ski")]
-
-class revoke_pdu(revoke_syntax):
- """Up-Down protocol "revoke" PDU."""
-
- def get_SKI(self):
- """Convert g(SKI) encoding from PDU back to raw SKI."""
- return base64.urlsafe_b64decode(self.ski + "=")
-
- def serve_pdu(self, q_msg, r_msg, child):
- """Serve one revoke request PDU."""
- for ca_detail in child.ca_from_class_name(self.class_name).ca_details():
- for child_cert in child.child_certs(ca_detail = ca_detail, ski = self.get_SKI()):
- child_cert.revoke()
- self.gctx.sql.sweep()
- r_msg.payload = revoke_response_pdu()
- r_msg.payload.class_name = self.class_name
- r_msg.payload.ski = self.ski
-
- @classmethod
- def query(cls, ca_detail):
- """Send a "revoke" request to parent associated with ca_detail."""
- ca = ca_detail.ca()
- parent = ca.parent()
- self = cls()
- self.class_name = ca.parent_resource_class
- self.ski = ca_detail.latest_ca_cert.gSKI()
- return parent.query_up_down(self)
-
-class revoke_response_pdu(revoke_syntax):
- """Up-Down protocol "revoke_response" PDU."""
-
- pass
-
-class error_response_pdu(base_elt):
- """Up-Down protocol "error_response" PDU."""
-
- codes = {
- 1101 : "Already processing request",
- 1102 : "Version number error",
- 1103 : "Unrecognised request type",
- 1201 : "Request - no such resource class",
- 1202 : "Request - no resources allocated in resource class",
- 1203 : "Request - badly formed certificate request",
- 1301 : "Revoke - no such resource class",
- 1302 : "Revoke - no such key",
- 2001 : "Internal Server Error - Request not performed" }
-
- exceptions = {}
-
- def __init__(self, exception = None):
- """Initialize an error_response PDU from an exception object."""
- if exception is not None:
- if exception in self.exceptions:
- self.status = exceptions[exception]
- else:
- self.status = 2001
- self.description = str(exception)
-
- def endElement(self, stack, name, text):
- """Handle "error_response" PDU."""
- if name == "status":
- code = int(text)
- if code not in self.codes:
- raise rpki.exceptions.BadStatusCode, "%s is not a known status code" % code
- self.status = code
- elif name == "description":
- self.description = text
- else:
- assert name == "message", "Unexpected name %s, stack %s" % (name, stack)
- stack.pop()
- stack[-1].endElement(stack, name, text)
-
- def toXML(self):
- """Generate payload of "error_response" PDU."""
- assert self.status in self.codes
- elt = self.make_elt("status")
- elt.text = str(self.status)
- payload = [elt]
- if self.description:
- elt = self.make_elt("description")
- elt.text = str(self.description)
- elt.set("{http://www.w3.org/XML/1998/namespace}lang", "en-US")
- payload.append(elt)
- return payload
-
- def check_response(self):
- """Handle an error response. For now, just raise an exception,
- perhaps figure out something more clever to do later.
- """
- raise rpki.exceptions.UpstreamError, self.codes[self.status]
-
-class message_pdu(base_elt):
- """Up-Down protocol message wrapper PDU."""
-
- version = 1
-
- name2type = {
- "list" : list_pdu,
- "list_response" : list_response_pdu,
- "issue" : issue_pdu,
- "issue_response" : issue_response_pdu,
- "revoke" : revoke_pdu,
- "revoke_response" : revoke_response_pdu,
- "error_response" : error_response_pdu }
-
- type2name = dict((v,k) for k,v in name2type.items())
-
- def toXML(self):
- """Generate payload of message PDU."""
- elt = self.make_elt("message", "version", "sender", "recipient", "type")
- elt.extend(self.payload.toXML())
- return elt
-
- def startElement(self, stack, name, attrs):
- """Handle message PDU.
-
- Payload of the <message/> element varies depending on the "type"
- attribute, so after some basic checks we have to instantiate the
- right class object to handle whatever kind of PDU this is.
- """
- assert name == "message", "Unexpected name %s, stack %s" % (name, stack)
- assert self.version == int(attrs["version"])
- self.sender = attrs["sender"]
- self.recipient = attrs["recipient"]
- self.type = attrs["type"]
- self.payload = self.name2type[attrs["type"]]()
- stack.append(self.payload)
-
- def __str__(self):
- """Convert a message PDU to a string."""
- lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "UTF-8")
-
- def serve_top_level(self, child):
- """Serve one message request PDU."""
- r_msg = message_pdu()
- r_msg.sender = self.recipient
- r_msg.recipient = self.sender
- self.payload.serve_pdu(self, r_msg, child)
- r_msg.type = self.type2name[type(r_msg.payload)]
- return r_msg
-
- def serve_error(self, exception):
- """Generate an error_response message PDU."""
- r_msg = message_pdu()
- r_msg.sender = self.recipient
- r_msg.recipient = self.sender
- r_msg.payload = error_response_pdu(exception)
- r_msg.type = self.type2name[type(r_msg.payload)]
- return r_msg
-
- @classmethod
- def make_query(cls, payload, sender, recipient):
- """Construct one message PDU."""
- assert not cls.type2name[type(payload)].endswith("_response")
- if sender is None:
- sender = "tweedledee"
- if recipient is None:
- recipient = "tweedledum"
- self = cls()
- self.sender = sender
- self.recipient = recipient
- self.payload = payload
- self.type = self.type2name[type(payload)]
- return self
-
-class sax_handler(rpki.xml_utils.sax_handler):
- """SAX handler for Up-Down protocol."""
-
- pdu = message_pdu
- name = "message"
- version = "1"
-
-class cms_msg(rpki.x509.XML_CMS_object):
- """Class to hold a CMS-signed up-down PDU."""
-
- encoding = "UTF-8"
- schema = rpki.relaxng.up_down
- saxify = sax_handler.saxify
diff --git a/rpkid.stable/rpki/x509.py b/rpkid.stable/rpki/x509.py
deleted file mode 100644
index b167560c..00000000
--- a/rpkid.stable/rpki/x509.py
+++ /dev/null
@@ -1,995 +0,0 @@
-"""One X.509 implementation to rule them all...
-
-...and in the darkness hide the twisty maze of partially overlapping
-X.509 support packages in Python.
-
-There are several existing packages, none of which do quite what I
-need, due to age, lack of documentation, specialization, or lack of
-foresight on somebody's part (perhaps mine). This module attempts to
-bring together the functionality I need in a way that hides at least
-some of the nasty details. This involves a lot of format conversion.
-
-$Id$
-
-
-Copyright (C) 2009 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.
-
-
-Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 POW, tlslite.api, POW.pkix, base64, lxml.etree, os
-import rpki.exceptions, rpki.resource_set, rpki.oids, rpki.sundial
-import rpki.manifest, rpki.roa, rpki.log
-
-def calculate_SKI(public_key_der):
- """Calculate the SKI value given the DER representation of a public
- key, which requires first peeling the ASN.1 wrapper off the key.
- """
- k = POW.pkix.SubjectPublicKeyInfo()
- k.fromString(public_key_der)
- d = POW.Digest(POW.SHA1_DIGEST)
- d.update(k.subjectPublicKey.get())
- return d.digest()
-
-class PEM_converter(object):
- """Convert between DER and PEM encodings for various kinds of ASN.1 data."""
-
- def __init__(self, kind): # "CERTIFICATE", "RSA PRIVATE KEY", ...
- """Initialize PEM_converter."""
- self.b = "-----BEGIN %s-----" % kind
- self.e = "-----END %s-----" % kind
-
- def looks_like_PEM(self, text):
- """Guess whether text looks like a PEM encoding."""
- b = text.find(self.b)
- return b >= 0 and text.find(self.e) > b + len(self.b)
-
- def to_DER(self, pem):
- """Convert from PEM to DER."""
- lines = [line.strip() for line in pem.splitlines(0)]
- while lines and lines.pop(0) != self.b:
- pass
- while lines and lines.pop(-1) != self.e:
- pass
- if not lines:
- raise rpki.exceptions.EmptyPEM, "Could not find PEM in:\n%s" % pem
- return base64.b64decode("".join(lines))
-
- def to_PEM(self, der):
- """Convert from DER to PEM."""
- b64 = base64.b64encode(der)
- pem = self.b + "\n"
- while len(b64) > 64:
- pem += b64[0:64] + "\n"
- b64 = b64[64:]
- return pem + b64 + "\n" + self.e + "\n"
-
-class DER_object(object):
- """Virtual class to hold a generic DER object."""
-
- ## Formats supported in this object
- formats = ("DER",)
-
- ## PEM converter for this object
- pem_converter = None
-
- ## Other attributes that self.clear() should whack
- other_clear = ()
-
- ## @var DER
- ## DER value of this object
-
- def empty(self):
- """Test whether this object is empty."""
- for a in self.formats:
- if getattr(self, a, None) is not None:
- return False
- return True
-
- def clear(self):
- """Make this object empty."""
- for a in self.formats + self.other_clear:
- setattr(self, a, None)
-
- def __init__(self, **kw):
- """Initialize a DER_object."""
- self.clear()
- if len(kw):
- self.set(**kw)
-
- def set(self, **kw):
- """Set this object by setting one of its known formats.
-
- This method only allows one to set one format at a time.
- Subsequent calls will clear the object first. The point of all
- this is to let the object's internal converters handle mustering
- the object into whatever format you need at the moment.
- """
- if len(kw) == 1:
- name = kw.keys()[0]
- if name in self.formats:
- self.clear()
- setattr(self, name, kw[name])
- return
- if name == "PEM":
- self.clear()
- self.DER = self.pem_converter.to_DER(kw[name])
- return
- if name == "Base64":
- self.clear()
- self.DER = base64.b64decode(kw[name])
- return
- if name in ("PEM_file", "DER_file", "Auto_file"):
- f = open(kw[name], "rb")
- value = f.read()
- f.close()
- if name == "PEM_file" or (name == "Auto_file" and self.pem_converter.looks_like_PEM(value)):
- value = self.pem_converter.to_DER(value)
- self.clear()
- self.DER = value
- return
- raise rpki.exceptions.DERObjectConversionError, "Can't honor conversion request %s" % repr(kw)
-
- def get_DER(self):
- """Get the DER value of this object.
-
- Subclasses will almost certainly override this method.
- """
- assert not self.empty()
- if self.DER:
- return self.DER
- raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
-
- def get_Base64(self):
- """Get the Base64 encoding of the DER value of this object."""
- return base64.b64encode(self.get_DER())
-
- def get_PEM(self):
- """Get the PEM representation of this object."""
- return self.pem_converter.to_PEM(self.get_DER())
-
- def __cmp__(self, other):
- """Compare two DER-encoded objects."""
- return cmp(self.get_DER(), other.get_DER())
-
- def hSKI(self):
- """Return hexadecimal string representation of SKI for this
- object. Only work for subclasses that implement get_SKI().
- """
- ski = self.get_SKI()
- return ":".join(("%02X" % ord(i) for i in ski)) if ski else ""
-
- def gSKI(self):
- """Calculate g(SKI) for this object. Only work for subclasses
- that implement get_SKI().
- """
- return base64.urlsafe_b64encode(self.get_SKI()).rstrip("=")
-
- def hAKI(self):
- """Return hexadecimal string representation of AKI for this
- object. Only work for subclasses that implement get_AKI().
- """
- aki = self.get_AKI()
- return ":".join(("%02X" % ord(i) for i in aki)) if aki else ""
-
- def gAKI(self):
- """Calculate g(AKI) for this object. Only work for subclasses
- that implement get_AKI().
- """
- return base64.urlsafe_b64encode(self.get_AKI()).rstrip("=")
-
- def get_AKI(self):
- """Get the AKI extension from this object. Only works for subclasses that support getExtension()."""
- aki = (self.get_POWpkix().getExtension(rpki.oids.name2oid["authorityKeyIdentifier"]) or ((), 0, None))[2]
- return aki[0] if isinstance(aki, tuple) else aki
-
- def get_SKI(self):
- """Get the SKI extension from this object. Only works for subclasses that support getExtension()."""
- return (self.get_POWpkix().getExtension(rpki.oids.name2oid["subjectKeyIdentifier"]) or ((), 0, None))[2]
-
- def get_SIA(self):
- """Get the SIA extension from this object. Only works for subclasses that support getExtension()."""
- return (self.get_POWpkix().getExtension(rpki.oids.name2oid["subjectInfoAccess"]) or ((), 0, None))[2]
-
- def get_AIA(self):
- """Get the SIA extension from this object. Only works for subclasses that support getExtension()."""
- return (self.get_POWpkix().getExtension(rpki.oids.name2oid["subjectInfoAccess"]) or ((), 0, None))[2]
-
- def get_basicConstraints(self):
- """Get the basicConstraints extension from this object. Only works for subclasses that support getExtension()."""
- return (self.get_POWpkix().getExtension(rpki.oids.name2oid["basicConstraints"]) or ((), 0, None))[2]
-
- def is_CA(self):
- """Return True if and only if object has the basicConstraints extension and its cA value is true."""
- basicConstraints = self.get_basicConstraints()
- return basicConstraints and basicConstraints[0] != 0
-
- def get_3779resources(self):
- """Get RFC 3779 resources as rpki.resource_set objects.
- Only works for subclasses that support getExtensions().
- """
- resources = rpki.resource_set.resource_bag.from_rfc3779_tuples(self.get_POWpkix().getExtensions())
- try:
- resources.valid_until = self.getNotAfter()
- except AttributeError:
- pass
- return resources
-
- @classmethod
- def from_sql(cls, x):
- """Convert from SQL storage format."""
- return cls(DER = x)
-
- def to_sql(self):
- """Convert to SQL storage format."""
- return self.get_DER()
-
- def dumpasn1(self):
- """Pretty print an ASN.1 DER object using cryptlib dumpasn1 tool.
- Use a temporary file rather than popen4() because dumpasn1 uses
- seek() when decoding ASN.1 content nested in OCTET STRING values.
- """
-
- ret = None
- fn = "dumpasn1.tmp"
- try:
- f = open(fn, "wb")
- f.write(self.get_DER())
- f.close()
- f = os.popen("dumpasn1 2>&1 -a " + fn)
- ret = "\n".join(x for x in f.read().splitlines() if x.startswith(" "))
- f.close()
- finally:
- os.unlink(fn)
- return ret
-
-class X509(DER_object):
- """X.509 certificates.
-
- This class is designed to hold all the different representations of
- X.509 certs we're using and convert between them. X.509 support in
- Python a nasty maze of half-cooked stuff (except perhaps for
- cryptlib, which is just different). Users of this module should not
- have to care about this implementation nightmare.
- """
-
- formats = ("DER", "POW", "POWpkix", "tlslite")
- pem_converter = PEM_converter("CERTIFICATE")
-
- def get_DER(self):
- """Get the DER value of this certificate."""
- assert not self.empty()
- if self.DER:
- return self.DER
- if self.POW:
- self.DER = self.POW.derWrite()
- return self.get_DER()
- if self.POWpkix:
- self.DER = self.POWpkix.toString()
- return self.get_DER()
- if self.tlslite:
- der = self.tlslite.writeBytes()
- if not isinstance(der, str): # Apparently sometimes tlslite strings aren't strings,
- der = der.tostring() # then again somtimes they are. Isn't that special?
- self.DER = der
- return self.get_DER()
- raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
-
- def get_POW(self):
- """Get the POW value of this certificate."""
- assert not self.empty()
- if not self.POW:
- self.POW = POW.derRead(POW.X509_CERTIFICATE, self.get_DER())
- return self.POW
-
- def get_POWpkix(self):
- """Get the POW.pkix value of this certificate."""
- assert not self.empty()
- if not self.POWpkix:
- cert = POW.pkix.Certificate()
- cert.fromString(self.get_DER())
- self.POWpkix = cert
- return self.POWpkix
-
- def get_tlslite(self):
- """Get the tlslite value of this certificate."""
- assert not self.empty()
- if not self.tlslite:
- cert = tlslite.api.X509()
- cert.parseBinary(self.get_DER())
- self.tlslite = cert
- return self.tlslite
-
- def getIssuer(self):
- """Get the issuer of this certificate."""
- return self.get_POW().getIssuer()
-
- def getSubject(self):
- """Get the subject of this certificate."""
- return self.get_POW().getSubject()
-
- def getNotBefore(self):
- """Get the inception time of this certificate."""
- return rpki.sundial.datetime.fromASN1tuple(self.get_POWpkix().tbs.validity.notBefore.get())
-
- def getNotAfter(self):
- """Get the expiration time of this certificate."""
- return rpki.sundial.datetime.fromASN1tuple(self.get_POWpkix().tbs.validity.notAfter.get())
-
- def getSerial(self):
- """Get the serial number of this certificate."""
- return self.get_POW().getSerial()
-
- def getPublicKey(self):
- """Extract the public key from this certificate."""
- return RSApublic(DER = self.get_POWpkix().tbs.subjectPublicKeyInfo.toString())
-
- def expired(self):
- """Test whether this certificate has expired."""
- return self.getNotAfter() <= rpki.sundial.now()
-
- def issue(self, keypair, subject_key, serial, sia, aia, crldp, notAfter,
- cn = None, resources = None, is_ca = True):
- """Issue a certificate."""
-
- now = rpki.sundial.now()
- aki = self.get_SKI()
- ski = subject_key.get_SKI()
-
- if cn is None:
- cn = "".join(("%02X" % ord(i) for i in ski))
-
- # if notAfter is None: notAfter = now + rpki.sundial.timedelta(days = 30)
-
- cert = POW.pkix.Certificate()
- cert.setVersion(2)
- cert.setSerial(serial)
- cert.setIssuer(self.get_POWpkix().getSubject())
- cert.setSubject((((rpki.oids.name2oid["commonName"], ("printableString", cn)),),))
- cert.setNotBefore(now.toASN1tuple())
- cert.setNotAfter(notAfter.toASN1tuple())
- cert.tbs.subjectPublicKeyInfo.fromString(subject_key.get_DER())
-
- 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 is_ca:
- exts.append(["basicConstraints", True, (1, None)])
- exts.append(["keyUsage", True, (0, 0, 0, 0, 0, 1, 1)])
- else:
- exts.append(["keyUsage", True, (1,)])
-
- if sia is not None:
- exts.append(["subjectInfoAccess", False, sia])
- else:
- assert not is_ca
-
- if resources is not None and resources.asn:
- exts.append(["sbgp-autonomousSysNum", True, (resources.asn.to_rfc3779_tuple(), None)])
-
- if resources is not None and (resources.v4 or resources.v6):
- exts.append(["sbgp-ipAddrBlock", True, [x for x in (resources.v4.to_rfc3779_tuple(), resources.v6.to_rfc3779_tuple()) if x is not None]])
-
- for x in exts:
- x[0] = rpki.oids.name2oid[x[0]]
- cert.setExtensions(exts)
-
- cert.sign(keypair.get_POW(), POW.SHA256_DIGEST)
-
- return X509(POWpkix = cert)
-
- @classmethod
- def normalize_chain(cls, chain):
- """Normalize a chain of certificates into a tuple of X509 objects.
- Given all the glue certificates needed for BPKI cross
- certification, it's easiest to allow sloppy arguments to the HTTPS
- and CMS validation methods and provide a single method that
- normalizes the allowed cases. So this method allows X509, None,
- lists, and tuples, and returns a tuple of X509 objects.
- """
- if isinstance(chain, cls):
- chain = (chain,)
- return tuple(x for x in chain if x is not None)
-
-class PKCS10(DER_object):
- """Class to hold a PKCS #10 request."""
-
- formats = ("DER", "POWpkix")
- pem_converter = PEM_converter("CERTIFICATE REQUEST")
-
- def get_DER(self):
- """Get the DER value of this certification request."""
- assert not self.empty()
- if self.DER:
- return self.DER
- if self.POWpkix:
- self.DER = self.POWpkix.toString()
- return self.get_DER()
- raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
-
- def get_POWpkix(self):
- """Get the POW.pkix value of this certification request."""
- assert not self.empty()
- if not self.POWpkix:
- req = POW.pkix.CertificationRequest()
- req.fromString(self.get_DER())
- self.POWpkix = req
- return self.POWpkix
-
- def getPublicKey(self):
- """Extract the public key from this certification request."""
- return RSApublic(DER = self.get_POWpkix().certificationRequestInfo.subjectPublicKeyInfo.toString())
-
- def check_valid_rpki(self):
- """Check this certification request to see whether it's a valid
- request for an RPKI certificate. This is broken out of the
- up-down protocol code because it's somewhat involved and the
- up-down code doesn't need to know the details.
-
- Throws an exception if the request isn't valid, so if this method
- returns at all, the request is ok.
- """
-
- if not self.get_POWpkix().verify():
- raise rpki.exceptions.BadPKCS10, "Signature check failed"
-
- if self.get_POWpkix().certificationRequestInfo.version.get() != 0:
- raise rpki.exceptions.BadPKCS10, \
- "Bad version number %s" % self.get_POWpkix().certificationRequestInfo.version
-
- if rpki.oids.oid2name.get(self.get_POWpkix().signatureAlgorithm.algorithm.get()) \
- not in ("sha256WithRSAEncryption", "sha384WithRSAEncryption", "sha512WithRSAEncryption"):
- raise rpki.exceptions.BadPKCS10, "Bad signature algorithm %s" % self.get_POWpkix().signatureAlgorithm
-
- exts = self.get_POWpkix().getExtensions()
- for oid, critical, value in exts:
- if rpki.oids.oid2name.get(oid) not in ("basicConstraints", "keyUsage", "subjectInfoAccess"):
- raise rpki.exceptions.BadExtension, "Forbidden extension %s" % oid
- req_exts = dict((rpki.oids.oid2name[oid], value) for (oid, critical, value) in exts)
-
- if "basicConstraints" not in req_exts or not req_exts["basicConstraints"][0]:
- raise rpki.exceptions.BadPKCS10, "request for EE cert not allowed here"
-
- if req_exts["basicConstraints"][1] is not None:
- raise rpki.exceptions.BadPKCS10, "basicConstraints must not specify Path Length"
-
- if "keyUsage" in req_exts and (not req_exts["keyUsage"][5] or not req_exts["keyUsage"][6]):
- raise rpki.exceptions.BadPKCS10, "keyUsage doesn't match basicConstraints"
-
- for method, location in req_exts.get("subjectInfoAccess", ()):
- if rpki.oids.oid2name.get(method) == "id-ad-caRepository" and \
- (location[0] != "uri" or (location[1].startswith("rsync://") and not location[1].endswith("/"))):
- raise rpki.exceptions.BadPKCS10, "Certificate request includes bad SIA component: %s" % repr(location)
-
- # This one is an implementation restriction. I don't yet
- # understand what the spec is telling me to do in this case.
- assert "subjectInfoAccess" in req_exts, "Can't (yet) handle PKCS #10 without an SIA extension"
-
- @classmethod
- def create_ca(cls, keypair, sia = None):
- """Create a new request for a given keypair, including given SIA value."""
- exts = [["basicConstraints", True, (1, None)],
- ["keyUsage", True, (0, 0, 0, 0, 0, 1, 1)]]
- if sia is not None:
- exts.append(["subjectInfoAccess", False, sia])
- for x in exts:
- x[0] = rpki.oids.name2oid[x[0]]
- return cls.create(keypair, exts)
-
- @classmethod
- def create(cls, keypair, exts = None):
- """Create a new request for a given keypair, including given extensions."""
- cn = "".join(("%02X" % ord(i) for i in keypair.get_SKI()))
- req = POW.pkix.CertificationRequest()
- req.certificationRequestInfo.version.set(0)
- req.certificationRequestInfo.subject.set((((rpki.oids.name2oid["commonName"],
- ("printableString", cn)),),))
- if exts is not None:
- req.setExtensions(exts)
- req.sign(keypair.get_POW(), POW.SHA256_DIGEST)
- return cls(POWpkix = req)
-
-class RSA(DER_object):
- """Class to hold an RSA key pair."""
-
- formats = ("DER", "POW", "tlslite")
- pem_converter = PEM_converter("RSA PRIVATE KEY")
-
- def get_DER(self):
- """Get the DER value of this keypair."""
- assert not self.empty()
- if self.DER:
- return self.DER
- if self.POW:
- self.DER = self.POW.derWrite(POW.RSA_PRIVATE_KEY)
- return self.get_DER()
- raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
-
- def get_POW(self):
- """Get the POW value of this keypair."""
- assert not self.empty()
- if not self.POW:
- self.POW = POW.derRead(POW.RSA_PRIVATE_KEY, self.get_DER())
- return self.POW
-
- def get_tlslite(self):
- """Get the tlslite value of this keypair."""
- assert not self.empty()
- if not self.tlslite:
- self.tlslite = tlslite.api.parsePEMKey(self.get_PEM(), private=True)
- return self.tlslite
-
- @classmethod
- def generate(cls, keylength = 2048):
- """Generate a new keypair."""
- return cls(POW = POW.Asymmetric(POW.RSA_CIPHER, keylength))
-
- def get_public_DER(self):
- """Get the DER encoding of the public key from this keypair."""
- return self.get_POW().derWrite(POW.RSA_PUBLIC_KEY)
-
- def get_SKI(self):
- """Calculate the SKI of this keypair."""
- return calculate_SKI(self.get_public_DER())
-
- def get_RSApublic(self):
- """Convert the public key of this keypair into a RSApublic object."""
- return RSApublic(DER = self.get_public_DER())
-
-class RSApublic(DER_object):
- """Class to hold an RSA public key."""
-
- formats = ("DER", "POW")
- pem_converter = PEM_converter("RSA PUBLIC KEY")
-
- def get_DER(self):
- """Get the DER value of this public key."""
- assert not self.empty()
- if self.DER:
- return self.DER
- if self.POW:
- self.DER = self.POW.derWrite(POW.RSA_PUBLIC_KEY)
- return self.get_DER()
- raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
-
- def get_POW(self):
- """Get the POW value of this public key."""
- assert not self.empty()
- if not self.POW:
- self.POW = POW.derRead(POW.RSA_PUBLIC_KEY, self.get_DER())
- return self.POW
-
- def get_SKI(self):
- """Calculate the SKI of this public key."""
- return calculate_SKI(self.get_DER())
-
-def POWify_OID(oid):
- """Utility function to convert tuple form of an OID to
- the dotted-decimal string form that POW uses.
- """
- if isinstance(oid, str):
- return POWify_OID(rpki.oids.name2oid[oid])
- else:
- return ".".join(str(i) for i in oid)
-
-class CMS_object(DER_object):
- """Class to hold a CMS-wrapped object.
-
- CMS-wrapped objects are a little different from the other DER_object
- types because the signed object is CMS wrapping inner content that's
- also ASN.1, and due to our current minimal support for CMS we can't
- just handle this as a pretty composite object. So, for now anyway,
- a CMS_object is the outer CMS wrapped object so that the usual DER
- and PEM operations do the obvious things, and the inner content is
- handle via separate methods.
- """
-
- formats = ("DER", "POW")
- other_clear = ("content",)
- econtent_oid = POWify_OID("id-data")
- pem_converter = PEM_converter("CMS")
-
- ## @var dump_on_verify_failure
- # Set this to True to get dumpasn1 dumps of ASN.1 on CMS verify failures.
-
- dump_on_verify_failure = True
-
- ## @var debug_cms_certs
- # Set this to True to log a lot of chatter about CMS certificates.
-
- debug_cms_certs = False
-
- ## @var require_crls
- # Set this to False to make CMS CRLs optional in the cases where we
- # would otherwise require them. Some day this option should go away
- # and CRLs should be uncondtionally mandatory in such cases.
-
- require_crls = False
-
- ## @var print_on_der_error
- # Set this to True to log alleged DER when we have trouble parsing
- # it, in case it's really a Perl backtrace or something.
-
- print_on_der_error = True
-
- def get_DER(self):
- """Get the DER value of this CMS_object."""
- assert not self.empty()
- if self.DER:
- return self.DER
- if self.POW:
- self.DER = self.POW.derWrite()
- return self.get_DER()
- raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
-
- def get_POW(self):
- """Get the POW value of this CMS_object."""
- assert not self.empty()
- if not self.POW:
- self.POW = POW.derRead(POW.CMS_MESSAGE, self.get_DER())
- return self.POW
-
- def get_content(self):
- """Get the inner content of this CMS_object."""
- assert self.content is not None
- return self.content
-
- def set_content(self, content):
- """Set the (inner) content of this CMS_object, clearing the wrapper."""
- self.clear()
- self.content = content
-
- def verify(self, ta):
- """Verify CMS wrapper and store inner content."""
-
- try:
- cms = self.get_POW()
- except:
- if self.print_on_der_error:
- rpki.log.debug("Problem parsing DER CMS message, might not really be DER: %s"
- % repr(self.get_DER()))
- raise rpki.exceptions.UnparsableCMSDER
-
- if cms.eContentType() != self.econtent_oid:
- raise rpki.exceptions.WrongEContentType, "Got CMS eContentType %s, expected %s" % (cms.eContentType(), self.econtent_oid)
-
- certs = [X509(POW = x) for x in cms.certs()]
- crls = [CRL(POW = c) for c in cms.crls()]
-
- if self.debug_cms_certs:
- for x in certs:
- rpki.log.debug("Received CMS cert issuer %s subject %s" % (x.getIssuer(), x.getSubject()))
- for c in crls:
- rpki.log.debug("Received CMS CRL issuer %s" % repr(c.getIssuer()))
-
- store = POW.X509Store()
-
- trusted_ee = None
-
- for x in X509.normalize_chain(ta):
- if self.debug_cms_certs:
- rpki.log.debug("CMS trusted cert issuer %s subject %s" % (x.getIssuer(), x.getSubject()))
- if not x.is_CA():
- assert trusted_ee is None, "Can't have two EE certs in the same validation chain"
- trusted_ee = x
- store.addTrust(x.get_POW())
-
- if trusted_ee:
- if self.debug_cms_certs:
- rpki.log.debug("Trusted CMS EE cert issuer %s subject %s" % (trusted_ee.getIssuer(), trusted_ee.getSubject()))
- if certs and (len(certs) > 1 or certs[0] != trusted_ee):
- raise rpki.exceptions.UnexpectedCMSCerts, certs
- if crls:
- raise rpki.exceptions.UnexpectedCMSCRLs, crls
- else:
- if not certs:
- raise rpki.exceptions.MissingCMSEEcert, certs
- if len(certs) > 1 or certs[0].is_CA():
- raise rpki.exceptions.UnexpectedCMSCerts, certs
- if not crls:
- if self.require_crls:
- raise rpki.exceptions.MissingCMSCRL, crls
- else:
- rpki.log.warn("MISSING CMS CRL! Ignoring per self.require_crls setting")
- if len(crls) > 1:
- raise rpki.exceptions.UnexpectedCMSCRLs, crls
-
- try:
- content = cms.verify(store)
- except:
- if self.dump_on_verify_failure:
- if True:
- dbg = self.dumpasn1()
- else:
- dbg = cms.pprint()
- print "CMS verification failed, dumping ASN.1 (%d octets):\n%s" % (len(self.get_DER()), dbg)
- raise rpki.exceptions.CMSVerificationFailed, "CMS verification failed"
-
- self.decode(content)
- return self.get_content()
-
- def extract(self):
- """Extract and store inner content from CMS wrapper without
- verifying the CMS.
-
- DANGER WILL ROBINSON!!!
-
- Do not use this method on unvalidated data. Use the verify()
- method instead.
-
- If you don't understand this warning, don't use this method.
- """
-
- try:
- cms = self.get_POW()
- except:
- raise rpki.exceptions.UnparsableCMSDER
-
- if cms.eContentType() != self.econtent_oid:
- raise rpki.exceptions.WrongEContentType, "Got CMS eContentType %s, expected %s" % (cms.eContentType(), self.econtent_oid)
-
- content = cms.verify(POW.X509Store(), None, POW.CMS_NOCRL | POW.CMS_NO_SIGNER_CERT_VERIFY | POW.CMS_NO_ATTR_VERIFY | POW.CMS_NO_CONTENT_VERIFY)
-
- self.decode(content)
- return self.get_content()
-
- def sign(self, keypair, certs, crls = None, no_certs = False):
- """Sign and wrap inner content."""
-
- rpki.log.trace()
-
- if isinstance(certs, X509):
- cert = certs
- certs = ()
- else:
- cert = certs[0]
- certs = certs[1:]
-
- if crls is None:
- crls = ()
- elif isinstance(crls, CRL):
- crls = (crls,)
-
- cms = POW.CMS()
-
- cms.sign(cert.get_POW(),
- keypair.get_POW(),
- self.encode(),
- [x.get_POW() for x in certs],
- [c.get_POW() for c in crls],
- self.econtent_oid,
- POW.CMS_NOCERTS if no_certs else 0)
-
- self.POW = cms
-
-class DER_CMS_object(CMS_object):
- """Class to hold CMS objects with DER-based content."""
-
- def encode(self):
- """Encode inner content for signing."""
- return self.get_content().toString()
-
- def decode(self, der):
- """Decode DER and set inner content."""
- obj = self.content_class()
- obj.fromString(der)
- self.content = obj
-
-class SignedManifest(DER_CMS_object):
- """Class to hold a signed manifest."""
-
- pem_converter = PEM_converter("RPKI MANIFEST")
- content_class = rpki.manifest.Manifest
- econtent_oid = POWify_OID("id-ct-rpkiManifest")
-
- def getThisUpdate(self):
- """Get thisUpdate value from this manifest."""
- return rpki.sundial.datetime.fromGeneralizedTime(self.get_content().thisUpdate.get())
-
- def getNextUpdate(self):
- """Get nextUpdate value from this manifest."""
- return rpki.sundial.datetime.fromGeneralizedTime(self.get_content().nextUpdate.get())
-
- @classmethod
- def build(cls, serial, thisUpdate, nextUpdate, names_and_objs, keypair, certs, version = 0):
- """Build a signed manifest."""
- self = cls()
- filelist = []
- for name, obj in names_and_objs:
- d = POW.Digest(POW.SHA256_DIGEST)
- d.update(obj.get_DER())
- filelist.append((name.rpartition("/")[2], d.digest()))
- filelist.sort(key = lambda x: x[0])
- m = rpki.manifest.Manifest()
- m.version.set(version)
- m.manifestNumber.set(serial)
- m.thisUpdate.set(thisUpdate.toGeneralizedTime())
- m.nextUpdate.set(nextUpdate.toGeneralizedTime())
- m.fileHashAlg.set(rpki.oids.name2oid["id-sha256"])
- m.fileList.set(filelist)
- self.set_content(m)
- self.sign(keypair, certs)
- return self
-
-class ROA(DER_CMS_object):
- """Class to hold a signed ROA."""
-
- pem_converter = PEM_converter("ROUTE ORIGIN ATTESTATION")
- content_class = rpki.roa.RouteOriginAttestation
- econtent_oid = POWify_OID("id-ct-routeOriginAttestation")
-
- @classmethod
- def build(cls, as_number, ipv4, ipv6, keypair, certs, version = 0):
- """Build a ROA."""
- self = cls()
- r = rpki.roa.RouteOriginAttestation()
- r.version.set(version)
- r.asID.set(as_number)
- r.ipAddrBlocks.set((a.to_roa_tuple() for a in (ipv4, ipv6) if a))
- self.set_content(r)
- self.sign(keypair, certs)
- return self
-
-class XML_CMS_object(CMS_object):
- """Class to hold CMS-wrapped XML protocol data."""
-
- econtent_oid = POWify_OID("id-ct-xml")
-
- ## @var dump_outbound_cms
- # If set, we write all outbound XML-CMS PDUs to disk, for debugging.
- # Value of this variable is prefix portion of filename, tail will
- # be a timestamp.
-
- dump_outbound_cms = None
-
- ## @var dump_outbound_cms
- # If set, we write all inbound XML-CMS PDUs to disk, for debugging.
- # Value of this variable is prefix portion of filename, tail will
- # be a timestamp.
-
- dump_inbound_cms = None
-
- def encode(self):
- """Encode inner content for signing."""
- return lxml.etree.tostring(self.get_content(), pretty_print = True, encoding = self.encoding, xml_declaration = True)
-
- def decode(self, xml):
- """Decode XML and set inner content."""
- self.content = lxml.etree.fromstring(xml)
-
- def pretty_print_content(self):
- """Pretty print XML content of this message."""
- return lxml.etree.tostring(self.get_content(), pretty_print = True, encoding = self.encoding, xml_declaration = True)
-
- def schema_check(self):
- """Handle XML RelaxNG schema check."""
- try:
- self.schema.assertValid(self.get_content())
- except lxml.etree.DocumentInvalid:
- rpki.log.error("PDU failed schema check: " + self.pretty_print_content())
- raise
-
- def dump_to_disk(self, prefix):
- """Write DER of current message to disk, for debugging."""
- f = open(prefix + rpki.sundial.now().isoformat() + "Z.cms", "wb")
- f.write(self.get_DER())
- f.close()
-
- @classmethod
- def wrap(cls, msg, keypair, certs, crls = None, pretty_print = False):
- """Build a CMS-wrapped XML PDU and return its DER encoding."""
- rpki.log.trace()
- self = cls()
- self.set_content(msg.toXML())
- self.schema_check()
- self.sign(keypair, certs, crls)
- if self.dump_outbound_cms:
- self.dump_to_disk(self.dump_outbound_cms)
- if pretty_print:
- return self.get_DER(), self.pretty_print_content()
- else:
- return self.get_DER()
-
- @classmethod
- def unwrap(cls, der, ta, pretty_print = False):
- """Unwrap a CMS-wrapped XML PDU and return Python objects."""
- self = cls(DER = der)
- if self.dump_inbound_cms:
- self.dump_to_disk(self.dump_inbound_cms)
- self.verify(ta)
- self.schema_check()
- msg = self.saxify(self.get_content())
- if pretty_print:
- return msg, self.pretty_print_content()
- else:
- return msg
-
-class CRL(DER_object):
- """Class to hold a Certificate Revocation List."""
-
- formats = ("DER", "POW", "POWpkix")
- pem_converter = PEM_converter("X509 CRL")
-
- def get_DER(self):
- """Get the DER value of this CRL."""
- assert not self.empty()
- if self.DER:
- return self.DER
- if self.POW:
- self.DER = self.POW.derWrite()
- return self.get_DER()
- if self.POWpkix:
- self.DER = self.POWpkix.toString()
- return self.get_DER()
- raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
-
- def get_POW(self):
- """Get the POW value of this CRL."""
- assert not self.empty()
- if not self.POW:
- self.POW = POW.derRead(POW.X509_CRL, self.get_DER())
- return self.POW
-
- def get_POWpkix(self):
- """Get the POW.pkix value of this CRL."""
- assert not self.empty()
- if not self.POWpkix:
- crl = POW.pkix.CertificateList()
- crl.fromString(self.get_DER())
- self.POWpkix = crl
- return self.POWpkix
-
- def getThisUpdate(self):
- """Get thisUpdate value from this CRL."""
- return rpki.sundial.datetime.fromASN1tuple(self.get_POWpkix().getThisUpdate())
-
- def getNextUpdate(self):
- """Get nextUpdate value from this CRL."""
- return rpki.sundial.datetime.fromASN1tuple(self.get_POWpkix().getNextUpdate())
-
- def getIssuer(self):
- """Get issuer value of this CRL."""
- return self.get_POW().getIssuer()
-
- @classmethod
- def generate(cls, keypair, issuer, serial, thisUpdate, nextUpdate, revokedCertificates, version = 1, digestType = "sha256WithRSAEncryption"):
- crl = POW.pkix.CertificateList()
- crl.setVersion(version)
- crl.setIssuer(issuer.get_POWpkix().getSubject())
- crl.setThisUpdate(thisUpdate.toASN1tuple())
- crl.setNextUpdate(nextUpdate.toASN1tuple())
- if revokedCertificates:
- crl.setRevokedCertificates(revokedCertificates)
- crl.setExtensions(
- ((rpki.oids.name2oid["authorityKeyIdentifier"], False, (issuer.get_SKI(), (), None)),
- (rpki.oids.name2oid["cRLNumber"], False, serial)))
- crl.sign(keypair.get_POW(), digestType)
- return cls(POWpkix = crl)
diff --git a/rpkid.stable/rpki/xml_utils.py b/rpkid.stable/rpki/xml_utils.py
deleted file mode 100644
index eda8aa85..00000000
--- a/rpkid.stable/rpki/xml_utils.py
+++ /dev/null
@@ -1,317 +0,0 @@
-"""XML utilities.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-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 ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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 xml.sax, lxml.sax, lxml.etree, base64
-
-class sax_handler(xml.sax.handler.ContentHandler):
- """SAX handler for RPKI protocols.
-
- This class provides some basic amenities for parsing protocol XML of
- the kind we use in the RPKI protocols, including whacking all the
- protocol element text into US-ASCII, simplifying accumulation of
- text fields, and hiding some of the fun relating to XML namespaces.
-
- General assumption: by the time this parsing code gets invoked, the
- XML has already passed RelaxNG validation, so we only have to check
- for errors that the schema can't catch, and we don't have to play as
- many XML namespace games.
- """
-
- def __init__(self):
- """Initialize SAX handler."""
- self.text = ""
- self.stack = []
-
- def startElementNS(self, name, qname, attrs):
- """Redirect startElementNS() events to startElement()."""
- return self.startElement(name[1], attrs)
-
- def endElementNS(self, name, qname):
- """Redirect endElementNS() events to endElement()."""
- return self.endElement(name[1])
-
- def characters(self, content):
- """Accumulate a chuck of element content (text)."""
- self.text += content
-
- def startElement(self, name, attrs):
- """Handle startElement() events.
-
- We maintain a stack of nested elements under construction so that
- we can feed events directly to the current element rather than
- having to pass them through all the nesting elements.
-
- If the stack is empty, this event is for the outermost element, so
- we call a virtual method to create the corresponding object and
- that's the object we'll be returning as our final result.
- """
- a = dict()
- for k,v in attrs.items():
- if isinstance(k, tuple):
- if k == ("http://www.w3.org/XML/1998/namespace", "lang"):
- k = "xml:lang"
- else:
- assert k[0] is None
- k = k[1]
- a[k.encode("ascii")] = v.encode("ascii")
- if len(self.stack) == 0:
- assert not hasattr(self, "result")
- self.result = self.create_top_level(name, a)
- self.stack.append(self.result)
- self.stack[-1].startElement(self.stack, name, a)
-
- def endElement(self, name):
- """Handle endElement() events.
-
- Mostly this means handling any accumulated element text.
- """
- text = self.text.encode("ascii").strip()
- self.text = ""
- self.stack[-1].endElement(self.stack, name, text)
-
- @classmethod
- def saxify(cls, elt):
- """Create a one-off SAX parser, parse an ETree, return the result.
- """
- self = cls()
- lxml.sax.saxify(elt, self)
- return self.result
-
- def create_top_level(self, name, attrs):
- """Handle top-level PDU for this protocol."""
- assert name == self.name and attrs["version"] == self.version
- return self.pdu()
-
-class base_elt(object):
- """Virtual base class for XML message elements. The left-right and
- publication protocols use this. At least for now, the up-down
- protocol does not, due to different design assumptions.
- """
-
- ## @var attributes
- # XML attributes for this element.
- attributes = ()
-
- ## @var elements
- # XML elements contained by this element.
- elements = ()
-
- ## @var booleans
- # Boolean attributes (value "yes" or "no") for this element.
- booleans = ()
-
- def startElement(self, stack, name, attrs):
- """Default startElement() handler: just process attributes."""
- if name not in self.elements:
- assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack)
- self.read_attrs(attrs)
-
- def endElement(self, stack, name, text):
- """Default endElement() handler: just pop the stack."""
- assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack)
- stack.pop()
-
- def toXML(self):
- """Default toXML() element generator."""
- return self.make_elt()
-
- def read_attrs(self, attrs):
- """Template-driven attribute reader."""
- for key in self.attributes:
- val = attrs.get(key, None)
- if isinstance(val, str) and val.isdigit():
- val = long(val)
- setattr(self, key, val)
- for key in self.booleans:
- setattr(self, key, attrs.get(key, False))
-
- def make_elt(self):
- """XML element constructor."""
- elt = lxml.etree.Element("{%s}%s" % (self.xmlns, self.element_name), nsmap = self.nsmap)
- for key in self.attributes:
- val = getattr(self, key, None)
- if val is not None:
- elt.set(key, str(val))
- for key in self.booleans:
- if getattr(self, key, False):
- elt.set(key, "yes")
- return elt
-
- def make_b64elt(self, elt, name, value = None):
- """Constructor for Base64-encoded subelement."""
- if value is None:
- value = getattr(self, name, None)
- if value is not None:
- lxml.etree.SubElement(elt, "{%s}%s" % (self.xmlns, name), nsmap = self.nsmap).text = base64.b64encode(value)
-
- def __str__(self):
- """Convert a base_elt object to string format."""
- lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "us-ascii")
-
- @classmethod
- def make_pdu(cls, **kargs):
- """Generic PDU constructor."""
- self = cls()
- for k,v in kargs.items():
- if isinstance(v, bool):
- v = 1 if v else 0
- setattr(self, k, v)
- return self
-
-class data_elt(base_elt):
- """Virtual base class for PDUs that map to SQL objects. These
- objects all implement the create/set/get/list/destroy action
- attribute.
- """
-
- def endElement(self, stack, name, text):
- """Default endElement handler for SQL-based objects. This assumes
- that sub-elements are Base64-encoded using the sql_template mechanism.
- """
- if name in self.elements:
- elt_type = self.sql_template.map.get(name)
- assert elt_type is not None, "Couldn't find element type for %s, stack %s" % (name, stack)
- setattr(self, name, elt_type(Base64 = text))
- else:
- assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack)
- stack.pop()
-
- def toXML(self):
- """Default element generator for SQL-based objects. This assumes
- that sub-elements are Base64-encoded DER objects.
- """
- elt = self.make_elt()
- for i in self.elements:
- x = getattr(self, i, None)
- if x and not x.empty():
- self.make_b64elt(elt, i, x.get_DER())
- return elt
-
- def make_reply(self, r_pdu = None):
- """Construct a reply PDU."""
- if r_pdu is None:
- r_pdu = self.__class__()
- self.make_reply_clone_hook(r_pdu)
- setattr(r_pdu, self.sql_template.index, getattr(self, self.sql_template.index))
- else:
- for b in r_pdu.booleans:
- setattr(r_pdu, b, False)
- r_pdu.action = self.action
- r_pdu.tag = self.tag
- return r_pdu
-
- def make_reply_clone_hook(self, r_pdu):
- """Overridable hook."""
- pass
-
- def serve_pre_save_hook(self, q_pdu, r_pdu):
- """Overridable hook."""
- pass
-
- def serve_post_save_hook(self, q_pdu, r_pdu):
- """Overridable hook."""
- pass
-
- def serve_create(self, r_msg):
- """Handle a create action."""
- r_pdu = self.make_reply()
- self.serve_pre_save_hook(self, r_pdu)
- self.sql_store()
- setattr(r_pdu, self.sql_template.index, getattr(self, self.sql_template.index))
- self.serve_post_save_hook(self, r_pdu)
- r_msg.append(r_pdu)
-
- def serve_set(self, r_msg):
- """Handle a set action."""
- db_pdu = self.serve_fetch_one()
- r_pdu = self.make_reply()
- for a in db_pdu.sql_template.columns[1:]:
- v = getattr(self, a)
- if v is not None:
- setattr(db_pdu, a, v)
- db_pdu.sql_mark_dirty()
- db_pdu.serve_pre_save_hook(self, r_pdu)
- db_pdu.sql_store()
- db_pdu.serve_post_save_hook(self, r_pdu)
- r_msg.append(r_pdu)
-
- def serve_get(self, r_msg):
- """Handle a get action."""
- r_pdu = self.serve_fetch_one()
- self.make_reply(r_pdu)
- r_msg.append(r_pdu)
-
- def serve_list(self, r_msg):
- """Handle a list action for non-self objects."""
- for r_pdu in self.serve_fetch_all():
- self.make_reply(r_pdu)
- r_msg.append(r_pdu)
-
- def serve_destroy(self, r_msg):
- """Handle a destroy action."""
- db_pdu = self.serve_fetch_one()
- db_pdu.sql_delete()
- r_msg.append(self.make_reply())
-
- def serve_dispatch(self, r_msg):
- """Action dispatch handler."""
- dispatch = { "create" : self.serve_create,
- "set" : self.serve_set,
- "get" : self.serve_get,
- "list" : self.serve_list,
- "destroy" : self.serve_destroy }
- if self.action not in dispatch:
- raise rpki.exceptions.BadQuery, "Unexpected query: action %s" % self.action
- dispatch[self.action](r_msg)
-
- def unimplemented_control(self, *controls):
- """Uniform handling for unimplemented control operations."""
- unimplemented = [x for x in controls if getattr(self, x, False)]
- if unimplemented:
- raise rpki.exceptions.NotImplementedYet, "Unimplemented control %s" % ", ".join(unimplemented)
-
-class msg(list):
- """Generic top-level PDU."""
-
- def startElement(self, stack, name, attrs):
- """Handle top-level PDU."""
- if name == "msg":
- assert self.version == int(attrs["version"])
- self.type = attrs["type"]
- else:
- elt = self.pdus[name]()
- self.append(elt)
- stack.append(elt)
- elt.startElement(stack, name, attrs)
-
- def endElement(self, stack, name, text):
- """Handle top-level PDU."""
- assert name == "msg", "Unexpected name %s, stack %s" % (name, stack)
- assert len(stack) == 1
- stack.pop()
-
- def __str__(self):
- """Convert msg object to string."""
- lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "us-ascii")
-
- def toXML(self):
- """Generate top-level PDU."""
- elt = lxml.etree.Element("{%s}msg" % (self.xmlns), nsmap = self.nsmap, version = str(self.version), type = self.type)
- elt.extend([i.toXML() for i in self])
- return elt