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 2913 2009-12-28 20:55:38Z sra $
00011
00012 Copyright (C) 2009 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 def handler_common(self, query, client, cb, certs, crl = None):
00066 """
00067 Common PDU handler code.
00068 """
00069
00070 def done(r_msg):
00071 reply = rpki.publication.cms_msg.wrap(r_msg, self.pubd_key, self.pubd_cert, crl)
00072 self.sql.sweep()
00073 cb(reply)
00074
00075 q_msg = rpki.publication.cms_msg.unwrap(query, certs)
00076 q_msg.serve_top_level(self, client, done)
00077
00078 def control_handler(self, query, path, cb):
00079 """
00080 Process one PDU from the IRBE.
00081 """
00082
00083 def done(x):
00084 cb(200, x)
00085
00086 rpki.log.trace()
00087 try:
00088 self.sql.ping()
00089 self.handler_common(query, None, done, (self.bpki_ta, self.irbe_cert))
00090 except (rpki.async.ExitNow, SystemExit):
00091 raise
00092 except Exception, data:
00093 rpki.log.traceback()
00094 cb(500, "Unhandled exception %s" % data)
00095
00096 client_url_regexp = re.compile("/client/([-A-Z0-9_/]+)$", re.I)
00097
00098 def client_handler(self, query, path, cb):
00099 """
00100 Process one PDU from a client.
00101 """
00102
00103 def done(x):
00104 cb(200, x)
00105
00106 rpki.log.trace()
00107 try:
00108 self.sql.ping()
00109 match = self.client_url_regexp.search(path)
00110 if match is None:
00111 raise rpki.exceptions.BadContactURL, "Bad path: %s" % path
00112 client_handle = match.group(1)
00113 client = rpki.publication.client_elt.sql_fetch_where1(self, "client_handle = %s", (client_handle,))
00114 if client is None:
00115 raise rpki.exceptions.ClientNotFound, "Could not find client %s" % client_handle
00116 config = rpki.publication.config_elt.fetch(self)
00117 if config is None or config.bpki_crl is None:
00118 raise rpki.exceptions.CMSCRLNotSet
00119 self.handler_common(query, client, done, (self.bpki_ta, client.bpki_cert, client.bpki_glue), config.bpki_crl)
00120 except (rpki.async.ExitNow, SystemExit):
00121 raise
00122 except Exception, data:
00123 rpki.log.traceback()
00124 cb(500, "Could not process PDU: %s" % data)
00125
00126
00127
00128 https_ta_cache = None
00129
00130 def clear_https_ta_cache(self):
00131 """
00132 Clear dynamic TLS trust anchors.
00133 """
00134 if self.https_ta_cache is not None:
00135 rpki.log.debug("Clearing HTTPS trusted cert cache")
00136 self.https_ta_cache = None
00137
00138 def build_https_ta_cache(self):
00139 """
00140 Build dynamic TLS trust anchors.
00141 """
00142 if self.https_ta_cache is None:
00143 clients = rpki.publication.client_elt.sql_fetch_all(self)
00144 self.https_ta_cache = rpki.https.build_https_ta_cache(
00145 [c.bpki_cert for c in clients if c.bpki_cert is not None] +
00146 [c.bpki_glue for c in clients if c.bpki_glue is not None] +
00147 [self.irbe_cert, self.bpki_ta])
00148 return self.https_ta_cache
00149
00150 os.environ["TZ"] = "UTC"
00151 time.tzset()
00152
00153 cfg_file = "pubd.conf"
00154 profile = False
00155
00156 opts, argv = getopt.getopt(sys.argv[1:], "c:dhp:?", ["config=", "debug", "help"])
00157 for o, a in opts:
00158 if o in ("-h", "--help", "-?"):
00159 print __doc__
00160 sys.exit(0)
00161 elif o in ("-c", "--config"):
00162 cfg_file = a
00163 elif o in ("-d", "--debug"):
00164 rpki.log.use_syslog = False
00165 elif o in ("-p", "--profile"):
00166 profile = a
00167 if argv:
00168 raise RuntimeError, "Unexpected arguments %s" % argv
00169
00170 rpki.log.init("pubd")
00171
00172 def main():
00173
00174 cfg = rpki.config.parser(cfg_file, "pubd")
00175
00176 if profile:
00177 rpki.log.info("Running in profile mode with output to %s" % profile)
00178
00179 cfg.set_global_flags()
00180
00181 pctx = pubd_context(cfg)
00182
00183 rpki.https.server(
00184 dynamic_https_trust_anchor = pctx.build_https_ta_cache,
00185 host = pctx.https_server_host,
00186 port = pctx.https_server_port,
00187 server_key = pctx.pubd_key,
00188 server_cert = pctx.pubd_cert,
00189 handlers = (("/control", pctx.control_handler),
00190 ("/client/", pctx.client_handler)))
00191
00192 if profile:
00193 import cProfile
00194 cProfile.run("main()", profile)
00195 else:
00196 main()