00001 """
00002 RPKI publication engine.
00003
00004 Usage: python pubd.py [ { -c | --config } configfile ]
00005 [ { -h | --help } ]
00006 [ { -p | --profile } outputfile ]
00007
00008 Default configuration file is pubd.conf, override with --config option.
00009
00010 $Id: pubd.py 3107 2010-03-16 23:13:08Z sra $
00011
00012 Copyright (C) 2009-2010 Internet Systems Consortium ("ISC")
00013
00014 Permission to use, copy, modify, and distribute this software for any
00015 purpose with or without fee is hereby granted, provided that the above
00016 copyright notice and this permission notice appear in all copies.
00017
00018 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00019 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00020 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00021 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00022 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00023 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00024 PERFORMANCE OF THIS SOFTWARE.
00025
00026 Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
00027
00028 Permission to use, copy, modify, and distribute this software for any
00029 purpose with or without fee is hereby granted, provided that the above
00030 copyright notice and this permission notice appear in all copies.
00031
00032 THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
00033 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00034 AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT,
00035 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00036 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00037 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00038 PERFORMANCE OF THIS SOFTWARE.
00039 """
00040
00041 import os, time, getopt, sys, re
00042 import rpki.resource_set, rpki.up_down, rpki.x509, rpki.sql
00043 import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng
00044 import rpki.log, rpki.publication
00045
00046 class pubd_context(object):
00047 """
00048 A container for various pubd parameters.
00049 """
00050
00051 def __init__(self, cfg):
00052
00053 self.sql = rpki.sql.session(cfg)
00054
00055 self.bpki_ta = rpki.x509.X509(Auto_file = cfg.get("bpki-ta"))
00056 self.irbe_cert = rpki.x509.X509(Auto_file = cfg.get("irbe-cert"))
00057 self.pubd_cert = rpki.x509.X509(Auto_file = cfg.get("pubd-cert"))
00058 self.pubd_key = rpki.x509.RSA( Auto_file = cfg.get("pubd-key"))
00059
00060 self.https_server_host = cfg.get("server-host", "")
00061 self.https_server_port = int(cfg.get("server-port", "4434"))
00062
00063 self.publication_base = cfg.get("publication-base", "publication/")
00064
00065 self.publication_multimodule = cfg.getboolean("publication-multimodule", False)
00066
00067 def handler_common(self, query, client, cb, certs, crl = None):
00068 """
00069 Common PDU handler code.
00070 """
00071
00072 def done(r_msg):
00073 reply = rpki.publication.cms_msg.wrap(r_msg, self.pubd_key, self.pubd_cert, crl)
00074 self.sql.sweep()
00075 cb(reply)
00076
00077 q_msg = rpki.publication.cms_msg.unwrap(query, certs)
00078 q_msg.serve_top_level(self, client, done)
00079
00080 def control_handler(self, query, path, cb):
00081 """
00082 Process one PDU from the IRBE.
00083 """
00084
00085 def done(x):
00086 cb(200, x)
00087
00088 rpki.log.trace()
00089 try:
00090 self.sql.ping()
00091 self.handler_common(query, None, done, (self.bpki_ta, self.irbe_cert))
00092 except (rpki.async.ExitNow, SystemExit):
00093 raise
00094 except Exception, data:
00095 rpki.log.traceback()
00096 cb(500, "Unhandled exception %s" % data)
00097
00098 client_url_regexp = re.compile("/client/([-A-Z0-9_/]+)$", re.I)
00099
00100 def client_handler(self, query, path, cb):
00101 """
00102 Process one PDU from a client.
00103 """
00104
00105 def done(x):
00106 cb(200, x)
00107
00108 rpki.log.trace()
00109 try:
00110 self.sql.ping()
00111 match = self.client_url_regexp.search(path)
00112 if match is None:
00113 raise rpki.exceptions.BadContactURL, "Bad path: %s" % path
00114 client_handle = match.group(1)
00115 client = rpki.publication.client_elt.sql_fetch_where1(self, "client_handle = %s", (client_handle,))
00116 if client is None:
00117 raise rpki.exceptions.ClientNotFound, "Could not find client %s" % client_handle
00118 config = rpki.publication.config_elt.fetch(self)
00119 if config is None or config.bpki_crl is None:
00120 raise rpki.exceptions.CMSCRLNotSet
00121 self.handler_common(query, client, done, (self.bpki_ta, client.bpki_cert, client.bpki_glue), config.bpki_crl)
00122 except (rpki.async.ExitNow, SystemExit):
00123 raise
00124 except Exception, data:
00125 rpki.log.traceback()
00126 cb(500, "Could not process PDU: %s" % data)
00127
00128
00129
00130 https_ta_cache = None
00131
00132 def clear_https_ta_cache(self):
00133 """
00134 Clear dynamic TLS trust anchors.
00135 """
00136 if self.https_ta_cache is not None:
00137 rpki.log.debug("Clearing HTTPS trusted cert cache")
00138 self.https_ta_cache = None
00139
00140 def build_https_ta_cache(self):
00141 """
00142 Build dynamic TLS trust anchors.
00143 """
00144 if self.https_ta_cache is None:
00145 clients = rpki.publication.client_elt.sql_fetch_all(self)
00146 self.https_ta_cache = rpki.https.build_https_ta_cache(
00147 [c.bpki_cert for c in clients if c.bpki_cert is not None] +
00148 [c.bpki_glue for c in clients if c.bpki_glue is not None] +
00149 [self.irbe_cert, self.bpki_ta])
00150 return self.https_ta_cache
00151
00152 os.environ["TZ"] = "UTC"
00153 time.tzset()
00154
00155 cfg_file = "pubd.conf"
00156 profile = False
00157
00158 opts, argv = getopt.getopt(sys.argv[1:], "c:dhp:?", ["config=", "debug", "help"])
00159 for o, a in opts:
00160 if o in ("-h", "--help", "-?"):
00161 print __doc__
00162 sys.exit(0)
00163 elif o in ("-c", "--config"):
00164 cfg_file = a
00165 elif o in ("-d", "--debug"):
00166 rpki.log.use_syslog = False
00167 elif o in ("-p", "--profile"):
00168 profile = a
00169 if argv:
00170 raise RuntimeError, "Unexpected arguments %s" % argv
00171
00172 rpki.log.init("pubd")
00173
00174 def main():
00175
00176 cfg = rpki.config.parser(cfg_file, "pubd")
00177
00178 if profile:
00179 rpki.log.info("Running in profile mode with output to %s" % profile)
00180
00181 cfg.set_global_flags()
00182
00183 pctx = pubd_context(cfg)
00184
00185 rpki.https.server(
00186 dynamic_https_trust_anchor = pctx.build_https_ta_cache,
00187 host = pctx.https_server_host,
00188 port = pctx.https_server_port,
00189 server_key = pctx.pubd_key,
00190 server_cert = pctx.pubd_cert,
00191 handlers = (("/control", pctx.control_handler),
00192 ("/client/", pctx.client_handler)))
00193
00194 if profile:
00195 import cProfile
00196 cProfile.run("main()", profile)
00197 else:
00198 main()