00001 """ 00002 Trivial RPKI up-down protocol root server, for testing. Not suitable 00003 for production use. Overrides a bunch of method definitions from the 00004 rpki.* classes in order to reuse as much code as possible. 00005 00006 Usage: python rootd.py [ { -c | --config } configfile ] [ { -h | --help } ] 00007 00008 Default configuration file is rootd.conf, override with --config option. 00009 00010 $Id: rootd.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, lxml 00028 import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509 00029 import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng 00030 import rpki.sundial, rpki.log 00031 00032 rpki_subject_lifetime = rpki.sundial.timedelta(days = 30) 00033 00034 def get_subject_cert(): 00035 try: 00036 x = rpki.x509.X509(Auto_file = rpki_subject_filename) 00037 return x 00038 except IOError: 00039 return None 00040 00041 def set_subject_cert(cert): 00042 f = open(rpki_subject_filename, "wb") 00043 f.write(cert.get_DER()) 00044 f.close() 00045 00046 def del_subject_cert(): 00047 os.remove(rpki_subject_filename) 00048 00049 def stash_subject_pkcs10(pkcs10): 00050 if rpki_pkcs10_filename: 00051 f = open(rpki_pkcs10_filename, "wb") 00052 f.write(pkcs10.get_DER()) 00053 f.close() 00054 00055 def compose_response(r_msg): 00056 rc = rpki.up_down.class_elt() 00057 rc.class_name = rootd_name 00058 rc.cert_url = rpki.up_down.multi_uri(rootd_cert) 00059 rc.from_resource_bag(rpki_issuer.get_3779resources()) 00060 rc.issuer = rpki_issuer 00061 r_msg.payload.classes.append(rc) 00062 rpki_subject = get_subject_cert() 00063 if rpki_subject is not None: 00064 rc.certs.append(rpki.up_down.certificate_elt()) 00065 rc.certs[0].cert_url = rpki.up_down.multi_uri(rootd_cert) 00066 rc.certs[0].cert = rpki_subject 00067 00068 class list_pdu(rpki.up_down.list_pdu): 00069 def serve_pdu(self, q_msg, r_msg, ignored): 00070 r_msg.payload = rpki.up_down.list_response_pdu() 00071 compose_response(r_msg) 00072 00073 class issue_pdu(rpki.up_down.issue_pdu): 00074 def serve_pdu(self, q_msg, r_msg, ignored): 00075 stash_subject_pkcs10(self.pkcs10) 00076 self.pkcs10.check_valid_rpki() 00077 r_msg.payload = rpki.up_down.issue_response_pdu() 00078 rpki_subject = get_subject_cert() 00079 if rpki_subject is None: 00080 resources = rpki_issuer.get_3779resources() 00081 rpki.log.info("Generating subject cert with resources " + str(resources)) 00082 req_key = self.pkcs10.getPublicKey() 00083 req_sia = self.pkcs10.get_SIA() 00084 crldp = rootd_base + rpki_issuer.gSKI() + ".crl" 00085 set_subject_cert(rpki_issuer.issue(keypair = rpki_key, 00086 subject_key = req_key, 00087 serial = int(time.time()), 00088 sia = req_sia, 00089 aia = rootd_cert, 00090 crldp = crldp, 00091 resources = resources, 00092 notAfter = rpki.sundial.now() + rpki_subject_lifetime)) 00093 now = rpki.sundial.now() 00094 crl = rpki.x509.CRL.generate( 00095 keypair = rpki_key, 00096 issuer = rpki_issuer, 00097 serial = 1, 00098 thisUpdate = now, 00099 nextUpdate = now + rpki_subject_lifetime, 00100 revokedCertificates = ()) 00101 f = open(os.path.dirname(rpki_subject_filename) + "/" + rpki_issuer.gSKI() + ".crl", "wb") 00102 f.write(crl.get_DER()) 00103 f.close() 00104 compose_response(r_msg) 00105 00106 class revoke_pdu(rpki.up_down.revoke_pdu): 00107 def serve_pdu(self, q_msg, r_msg, ignored): 00108 rpki_subject = get_subject_cert() 00109 if rpki_subject is None or rpki_subject.gSKI() != self.ski: 00110 raise rpki.exceptions.NotInDatabase 00111 del_subject_cert() 00112 r_msg.payload = rpki.up_down.revoke_response_pdu() 00113 r_msg.payload.class_name = self.class_name 00114 r_msg.payload.ski = self.ski 00115 00116 class message_pdu(rpki.up_down.message_pdu): 00117 name2type = { 00118 "list" : list_pdu, 00119 "list_response" : rpki.up_down.list_response_pdu, 00120 "issue" : issue_pdu, 00121 "issue_response" : rpki.up_down.issue_response_pdu, 00122 "revoke" : revoke_pdu, 00123 "revoke_response" : rpki.up_down.revoke_response_pdu, 00124 "error_response" : rpki.up_down.error_response_pdu } 00125 type2name = dict((v,k) for k,v in name2type.items()) 00126 00127 class sax_handler(rpki.up_down.sax_handler): 00128 pdu = message_pdu 00129 00130 class cms_msg(rpki.up_down.cms_msg): 00131 saxify = sax_handler.saxify 00132 00133 def up_down_handler(query, path): 00134 try: 00135 q_msg = cms_msg.unwrap(query, (bpki_ta, child_bpki_cert)) 00136 except Exception, data: 00137 rpki.log.error(traceback.format_exc()) 00138 return 400, "Could not process PDU: %s" % data 00139 try: 00140 r_msg = q_msg.serve_top_level(None) 00141 r_cms = cms_msg.wrap(r_msg, rootd_bpki_key, rootd_bpki_cert, rootd_bpki_crl) 00142 return 200, r_cms 00143 except Exception, data: 00144 rpki.log.error(traceback.format_exc()) 00145 try: 00146 r_msg = q_msg.serve_error(data) 00147 r_cms = cms_msg.wrap(r_msg, rootd_bpki_key, rootd_bpki_cert, rootd_bpki_crl) 00148 return 200, r_cms 00149 except Exception, data: 00150 rpki.log.error(traceback.format_exc()) 00151 return 500, "Could not process PDU: %s" % data 00152 00153 os.environ["TZ"] = "UTC" 00154 time.tzset() 00155 00156 rpki.log.init("rootd") 00157 00158 cfg_file = "rootd.conf" 00159 00160 opts,argv = getopt.getopt(sys.argv[1:], "c:h?", ["config=", "help"]) 00161 for o,a in opts: 00162 if o in ("-h", "--help", "-?"): 00163 print __doc__ 00164 sys.exit(0) 00165 if o in ("-c", "--config"): 00166 cfg_file = a 00167 if argv: 00168 raise RuntimeError, "Unexpected arguments %s" % argv 00169 00170 cfg = rpki.config.parser(cfg_file, "rootd") 00171 00172 bpki_ta = rpki.x509.X509(Auto_file = cfg.get("bpki-ta")) 00173 rootd_bpki_key = rpki.x509.RSA( Auto_file = cfg.get("rootd-bpki-key")) 00174 rootd_bpki_cert = rpki.x509.X509(Auto_file = cfg.get("rootd-bpki-cert")) 00175 rootd_bpki_crl = rpki.x509.CRL( Auto_file = cfg.get("rootd-bpki-crl")) 00176 child_bpki_cert = rpki.x509.X509(Auto_file = cfg.get("child-bpki-cert")) 00177 00178 https_server_host = cfg.get("server-host", "") 00179 https_server_port = int(cfg.get("server-port")) 00180 00181 rpki_key = rpki.x509.RSA( Auto_file = cfg.get("rpki-key")) 00182 rpki_issuer = rpki.x509.X509(Auto_file = cfg.get("rpki-issuer")) 00183 00184 rpki_subject_filename = cfg.get("rpki-subject-filename") 00185 rpki_pkcs10_filename = cfg.get("rpki-pkcs10-filename", "") 00186 00187 rootd_name = cfg.get("rootd_name", "wombat") 00188 rootd_base = cfg.get("rootd_base", "rsync://" + rootd_name + ".invalid/") 00189 rootd_cert = cfg.get("rootd_cert", rootd_base + "rootd.cer") 00190 00191 rpki.https.server(server_key = rootd_bpki_key, 00192 server_cert = rootd_bpki_cert, 00193 client_ta = (bpki_ta, child_bpki_cert), 00194 host = https_server_host, 00195 port = https_server_port, 00196 handlers = up_down_handler)