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 1974 2008-07-04 23:45:58Z 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 filename = rpki_root_dir + rpki_subject_cert 00036 try: 00037 x = rpki.x509.X509(Auto_file = filename) 00038 rpki.log.debug("Read subject cert %s" % filename) 00039 return x 00040 except IOError: 00041 return None 00042 00043 def set_subject_cert(cert): 00044 filename = rpki_root_dir + rpki_subject_cert 00045 rpki.log.debug("Writing subject cert %s" % filename) 00046 f = open(filename, "wb") 00047 f.write(cert.get_DER()) 00048 f.close() 00049 00050 def del_subject_cert(): 00051 filename = rpki_root_dir + rpki_subject_cert 00052 rpki.log.debug("Deleting subject cert %s" % filename) 00053 os.remove(filename) 00054 00055 def stash_subject_pkcs10(pkcs10): 00056 if rpki_subject_pkcs10: 00057 rpki.log.debug("Writing subject PKCS #10 %s" % rpki_subject_pkcs10) 00058 f = open(rpki_subject_pkcs10, "wb") 00059 f.write(pkcs10.get_DER()) 00060 f.close() 00061 00062 def compose_response(r_msg): 00063 rc = rpki.up_down.class_elt() 00064 rc.class_name = rpki_class_name 00065 rc.cert_url = rpki.up_down.multi_uri(rpki_root_cert_uri) 00066 rc.from_resource_bag(rpki_root_cert.get_3779resources()) 00067 rc.issuer = rpki_root_cert 00068 r_msg.payload.classes.append(rc) 00069 subject_cert = get_subject_cert() 00070 if subject_cert is not None: 00071 rc.certs.append(rpki.up_down.certificate_elt()) 00072 rc.certs[0].cert_url = rpki.up_down.multi_uri(rpki_base_uri + rpki_subject_cert) 00073 rc.certs[0].cert = subject_cert 00074 00075 class list_pdu(rpki.up_down.list_pdu): 00076 def serve_pdu(self, q_msg, r_msg, ignored): 00077 r_msg.payload = rpki.up_down.list_response_pdu() 00078 compose_response(r_msg) 00079 00080 class issue_pdu(rpki.up_down.issue_pdu): 00081 def serve_pdu(self, q_msg, r_msg, ignored): 00082 stash_subject_pkcs10(self.pkcs10) 00083 self.pkcs10.check_valid_rpki() 00084 r_msg.payload = rpki.up_down.issue_response_pdu() 00085 subject_cert = get_subject_cert() 00086 if subject_cert is None: 00087 resources = rpki_root_cert.get_3779resources() 00088 rpki.log.info("Generating subject cert with resources " + str(resources)) 00089 req_key = self.pkcs10.getPublicKey() 00090 req_sia = self.pkcs10.get_SIA() 00091 crldp = rpki_base_uri + rpki_root_crl 00092 now = rpki.sundial.now() 00093 subject_cert = rpki_root_cert.issue( 00094 keypair = rpki_root_key, 00095 subject_key = req_key, 00096 serial = int(time.time()), 00097 sia = req_sia, 00098 aia = rpki_root_cert_uri, 00099 crldp = crldp, 00100 resources = resources, 00101 notAfter = now + rpki_subject_lifetime) 00102 set_subject_cert(subject_cert) 00103 crl = rpki.x509.CRL.generate( 00104 keypair = rpki_root_key, 00105 issuer = rpki_root_cert, 00106 serial = 1, 00107 thisUpdate = now, 00108 nextUpdate = now + rpki_subject_lifetime, 00109 revokedCertificates = ()) 00110 rpki.log.debug("Writing CRL %s" % rpki_root_dir + rpki_root_crl) 00111 f = open(rpki_root_dir + rpki_root_crl, "wb") 00112 f.write(crl.get_DER()) 00113 f.close() 00114 manifest_resources = rpki.resource_set.resource_bag( 00115 asn = rpki.resource_set.resource_set_as("<inherit>"), 00116 v4 = rpki.resource_set.resource_set_ipv4("<inherit>"), 00117 v6 = rpki.resource_set.resource_set_ipv6("<inherit>")) 00118 manifest_keypair = rpki.x509.RSA.generate() 00119 manifest_cert = rpki_root_cert.issue( 00120 keypair = rpki_root_key, 00121 subject_key = manifest_keypair.get_RSApublic(), 00122 serial = int(time.time()) + 1, 00123 sia = None, 00124 aia = rpki_root_cert_uri, 00125 crldp = crldp, 00126 resources = manifest_resources, 00127 notAfter = now + rpki_subject_lifetime, 00128 is_ca = False) 00129 manifest = rpki.x509.SignedManifest.build( 00130 serial = int(time.time()), 00131 thisUpdate = now, 00132 nextUpdate = now + rpki_subject_lifetime, 00133 names_and_objs = [(rpki_subject_cert, subject_cert), (rpki_root_crl, crl)], 00134 keypair = manifest_keypair, 00135 certs = manifest_cert) 00136 rpki.log.debug("Writing manifest %s" % rpki_root_dir + rpki_root_manifest) 00137 f = open(rpki_root_dir + rpki_root_manifest, "wb") 00138 f.write(manifest.get_DER()) 00139 f.close() 00140 compose_response(r_msg) 00141 00142 class revoke_pdu(rpki.up_down.revoke_pdu): 00143 def serve_pdu(self, q_msg, r_msg, ignored): 00144 subject_cert = get_subject_cert() 00145 if subject_cert is None or subject_cert.gSKI() != self.ski: 00146 raise rpki.exceptions.NotInDatabase 00147 del_subject_cert() 00148 r_msg.payload = rpki.up_down.revoke_response_pdu() 00149 r_msg.payload.class_name = self.class_name 00150 r_msg.payload.ski = self.ski 00151 00152 class message_pdu(rpki.up_down.message_pdu): 00153 name2type = { 00154 "list" : list_pdu, 00155 "list_response" : rpki.up_down.list_response_pdu, 00156 "issue" : issue_pdu, 00157 "issue_response" : rpki.up_down.issue_response_pdu, 00158 "revoke" : revoke_pdu, 00159 "revoke_response" : rpki.up_down.revoke_response_pdu, 00160 "error_response" : rpki.up_down.error_response_pdu } 00161 type2name = dict((v,k) for k,v in name2type.items()) 00162 00163 class sax_handler(rpki.up_down.sax_handler): 00164 pdu = message_pdu 00165 00166 class cms_msg(rpki.up_down.cms_msg): 00167 saxify = sax_handler.saxify 00168 00169 def up_down_handler(query, path): 00170 try: 00171 q_msg = cms_msg.unwrap(query, (bpki_ta, child_bpki_cert)) 00172 except Exception, data: 00173 rpki.log.error(traceback.format_exc()) 00174 return 400, "Could not process PDU: %s" % data 00175 try: 00176 r_msg = q_msg.serve_top_level(None) 00177 r_cms = cms_msg.wrap(r_msg, rootd_bpki_key, rootd_bpki_cert, rootd_bpki_crl) 00178 return 200, r_cms 00179 except Exception, data: 00180 rpki.log.error(traceback.format_exc()) 00181 try: 00182 r_msg = q_msg.serve_error(data) 00183 r_cms = cms_msg.wrap(r_msg, rootd_bpki_key, rootd_bpki_cert, rootd_bpki_crl) 00184 return 200, r_cms 00185 except Exception, data: 00186 rpki.log.error(traceback.format_exc()) 00187 return 500, "Could not process PDU: %s" % data 00188 00189 os.environ["TZ"] = "UTC" 00190 time.tzset() 00191 00192 rpki.log.init("rootd") 00193 00194 cfg_file = "rootd.conf" 00195 00196 opts,argv = getopt.getopt(sys.argv[1:], "c:h?", ["config=", "help"]) 00197 for o,a in opts: 00198 if o in ("-h", "--help", "-?"): 00199 print __doc__ 00200 sys.exit(0) 00201 if o in ("-c", "--config"): 00202 cfg_file = a 00203 if argv: 00204 raise RuntimeError, "Unexpected arguments %s" % argv 00205 00206 cfg = rpki.config.parser(cfg_file, "rootd") 00207 00208 bpki_ta = rpki.x509.X509(Auto_file = cfg.get("bpki-ta")) 00209 rootd_bpki_key = rpki.x509.RSA( Auto_file = cfg.get("rootd-bpki-key")) 00210 rootd_bpki_cert = rpki.x509.X509(Auto_file = cfg.get("rootd-bpki-cert")) 00211 rootd_bpki_crl = rpki.x509.CRL( Auto_file = cfg.get("rootd-bpki-crl")) 00212 child_bpki_cert = rpki.x509.X509(Auto_file = cfg.get("child-bpki-cert")) 00213 00214 https_server_host = cfg.get("server-host", "") 00215 https_server_port = int(cfg.get("server-port")) 00216 00217 rpki_class_name = cfg.get("rpki-class-name", "wombat") 00218 00219 rpki_root_dir = cfg.get("rpki-root-dir") 00220 rpki_base_uri = cfg.get("rpki-base-uri", "rsync://" + rpki_class_name + ".invalid/") 00221 00222 rpki_root_key = rpki.x509.RSA( Auto_file = cfg.get("rpki-root-key")) 00223 rpki_root_cert = rpki.x509.X509(Auto_file = cfg.get("rpki-root-cert")) 00224 rpki_root_cert_uri = cfg.get("rpki-root-cert-uri", rpki_base_uri + "Root.cer") 00225 00226 rpki_root_manifest = cfg.get("rpki-root-manifest", "Root.mnf") 00227 rpki_root_crl = cfg.get("rpki-root-crl", "Root.crl") 00228 rpki_subject_cert = cfg.get("rpki-subject-cert", "Subroot.cer") 00229 rpki_subject_pkcs10 = cfg.get("rpki-subject-pkcs10", "") 00230 00231 rpki.https.server(server_key = rootd_bpki_key, 00232 server_cert = rootd_bpki_cert, 00233 client_ta = (bpki_ta, child_bpki_cert), 00234 host = https_server_host, 00235 port = https_server_port, 00236 handlers = up_down_handler)