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 2481 2009-06-01 05:07:46Z 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 traceback, os, time, getopt, sys
00042 import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509, rpki.sql
00043 import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log
00044 import 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.error(traceback.format_exc())
00094 cb(500, "Unhandled exception %s" % data)
00095
00096 def client_handler(self, query, path, cb):
00097 """
00098 Process one PDU from a client.
00099 """
00100
00101 def done(x):
00102 cb(200, x)
00103
00104
00105 rpki.log.trace()
00106 try:
00107 self.sql.ping()
00108 client_id = path.partition("/client/")[2]
00109 if not client_id.isdigit():
00110 raise rpki.exceptions.BadContactURL, "Bad path: %s" % path
00111 client = rpki.publication.client_elt.sql_fetch(self, long(client_id))
00112 if client is None:
00113 raise rpki.exceptions.ClientNotFound, "Could not find client %s" % client_id
00114 config = rpki.publication.config_elt.fetch(self)
00115 if config is None or config.bpki_crl is None:
00116 raise rpki.exceptions.CMSCRLNotSet
00117 self.handler_common(query, client, done, (self.bpki_ta, client.bpki_cert, client.bpki_glue), config.bpki_crl)
00118 except (rpki.async.ExitNow, SystemExit):
00119 raise
00120 except Exception, data:
00121 rpki.log.error(traceback.format_exc())
00122 cb(500, "Could not process PDU: %s" % data)
00123
00124
00125
00126 https_ta_cache = None
00127
00128 def clear_https_ta_cache(self):
00129 """
00130 Clear dynamic TLS trust anchors.
00131 """
00132 if self.https_ta_cache is not None:
00133 rpki.log.debug("Clearing HTTPS trusted cert cache")
00134 self.https_ta_cache = None
00135
00136 def build_https_ta_cache(self):
00137 """
00138 Build dynamic TLS trust anchors.
00139 """
00140 if self.https_ta_cache is None:
00141 clients = rpki.publication.client_elt.sql_fetch_all(self)
00142 self.https_ta_cache = rpki.https.build_https_ta_cache(
00143 [c.bpki_cert for c in clients if c.bpki_cert is not None] +
00144 [c.bpki_glue for c in clients if c.bpki_glue is not None] +
00145 [self.irbe_cert, self.bpki_ta])
00146 return self.https_ta_cache
00147
00148 os.environ["TZ"] = "UTC"
00149 time.tzset()
00150
00151 rpki.log.init("pubd")
00152
00153 cfg_file = "pubd.conf"
00154 profile = False
00155
00156 opts, argv = getopt.getopt(sys.argv[1:], "c:hp:?", ["config=", "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 ("-p", "--profile"):
00164 profile = a
00165 if argv:
00166 raise RuntimeError, "Unexpected arguments %s" % argv
00167
00168 def main():
00169
00170 cfg = rpki.config.parser(cfg_file, "pubd")
00171
00172 if profile:
00173 rpki.log.info("Running in profile mode with output to %s" % profile)
00174
00175 pctx = pubd_context(cfg)
00176
00177 rpki.https.server(
00178 dynamic_https_trust_anchor = pctx.build_https_ta_cache,
00179 host = pctx.https_server_host,
00180 port = pctx.https_server_port,
00181 server_key = pctx.pubd_key,
00182 server_cert = pctx.pubd_cert,
00183 handlers = (("/control", pctx.control_handler),
00184 ("/client/", pctx.client_handler)))
00185
00186 if profile:
00187 import cProfile
00188 cProfile.run("main()", profile)
00189 else:
00190 main()