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 1880 2008-06-12 21:54:53Z sra $
00011
00012 Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
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 ARIN DISCLAIMS ALL WARRANTIES WITH
00019 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00020 AND FITNESS. IN NO EVENT SHALL ARIN 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
00027 import traceback, os, time, getopt, sys, MySQLdb, lxml.etree
00028 import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509, rpki.sql
00029 import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log
00030 import rpki.publication
00031
00032 class pubd_context(object):
00033 """A container for various pubd parameters."""
00034
00035 def __init__(self, cfg):
00036
00037 self.sql = rpki.sql.session(cfg)
00038
00039 self.bpki_ta = rpki.x509.X509(Auto_file = cfg.get("bpki-ta"))
00040 self.irbe_cert = rpki.x509.X509(Auto_file = cfg.get("irbe-cert"))
00041 self.pubd_cert = rpki.x509.X509(Auto_file = cfg.get("pubd-cert"))
00042 self.pubd_key = rpki.x509.RSA( Auto_file = cfg.get("pubd-key"))
00043
00044 self.https_server_host = cfg.get("server-host", "")
00045 self.https_server_port = int(cfg.get("server-port", "4434"))
00046
00047 self.publication_base = cfg.get("publication-base", "publication/")
00048
00049 def handler_common(self, query, client, certs, crl = None):
00050 """Common PDU handler code."""
00051 q_msg = rpki.publication.cms_msg.unwrap(query, certs)
00052 r_msg = q_msg.serve_top_level(self, client)
00053 reply = rpki.publication.cms_msg.wrap(r_msg, self.pubd_key, self.pubd_cert, crl)
00054 self.sql.sweep()
00055 return reply
00056
00057 def control_handler(self, query, path):
00058 """Process one PDU from the IRBE."""
00059 rpki.log.trace()
00060 try:
00061 self.sql.ping()
00062 return 200, self.handler_common(query, None, (self.bpki_ta, self.irbe_cert))
00063 except Exception, data:
00064 rpki.log.error(traceback.format_exc())
00065 return 500, "Unhandled exception %s" % data
00066
00067 def client_handler(self, query, path):
00068 """Process one PDU from a client."""
00069 rpki.log.trace()
00070 try:
00071 self.sql.ping()
00072 client_id = path.partition("/client/")[2]
00073 if not client_id.isdigit():
00074 raise rpki.exceptions.BadContactURL, "Bad path: %s" % path
00075 client = rpki.publication.client_elt.sql_fetch(self, long(client_id))
00076 if client is None:
00077 raise rpki.exceptions.ClientNotFound, "Could not find client %s" % client_id
00078 config = rpki.publication.config_elt.fetch(self)
00079 if config is None or config.bpki_crl is None:
00080 raise rpki.exceptions.CMSCRLNotSet
00081 return 200, self.handler_common(query, client, (self.bpki_ta, client.bpki_cert, client.bpki_glue), config.bpki_crl)
00082 except Exception, data:
00083 rpki.log.error(traceback.format_exc())
00084 return 500, "Could not process PDU: %s" % data
00085
00086
00087
00088 https_ta_cache = None
00089
00090 def clear_https_ta_cache(self):
00091 """Clear dynamic TLS trust anchors."""
00092
00093 if self.https_ta_cache is not None:
00094 rpki.log.debug("Clearing HTTPS trusted cert cache")
00095 self.https_ta_cache = None
00096
00097 def build_https_ta_cache(self):
00098 """Build dynamic TLS trust anchors."""
00099 if self.https_ta_cache is None:
00100 clients = rpki.publication.client_elt.sql_fetch_all(self)
00101 self.https_ta_cache = rpki.https.build_https_ta_cache(
00102 [c.bpki_cert for c in clients if c.bpki_cert is not None] +
00103 [c.bpki_glue for c in clients if c.bpki_glue is not None] +
00104 [self.irbe_cert, self.bpki_ta])
00105 return self.https_ta_cache
00106
00107 os.environ["TZ"] = "UTC"
00108 time.tzset()
00109
00110 rpki.log.init("pubd")
00111
00112 cfg_file = "pubd.conf"
00113 profile = False
00114
00115 opts,argv = getopt.getopt(sys.argv[1:], "c:hp:?", ["config=", "help"])
00116 for o,a in opts:
00117 if o in ("-h", "--help", "-?"):
00118 print __doc__
00119 sys.exit(0)
00120 elif o in ("-c", "--config"):
00121 cfg_file = a
00122 elif o in ("-p", "--profile"):
00123 profile = a
00124 if argv:
00125 raise RuntimeError, "Unexpected arguments %s" % argv
00126
00127 def main():
00128
00129 cfg = rpki.config.parser(cfg_file, "pubd")
00130
00131 if profile:
00132 rpki.log.info("Running in profile mode with output to %s" % profile)
00133
00134 pctx = pubd_context(cfg)
00135
00136 rpki.https.server(
00137 dynamic_https_trust_anchor = pctx.build_https_ta_cache,
00138 host = pctx.https_server_host,
00139 port = pctx.https_server_port,
00140 server_key = pctx.pubd_key,
00141 server_cert = pctx.pubd_cert,
00142 handlers = (("/control", pctx.control_handler),
00143 ("/client/", pctx.client_handler)))
00144
00145 if profile:
00146 import cProfile
00147 cProfile.run("main()", profile)
00148 else:
00149 main()