RPKI Engine 1.0
|
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 $Id: rootd.py 3793 2011-04-27 04:34:52Z sra $ 00009 00010 Copyright (C) 2009--2010 Internet Systems Consortium ("ISC") 00011 00012 Permission to use, copy, modify, and distribute this software for any 00013 purpose with or without fee is hereby granted, provided that the above 00014 copyright notice and this permission notice appear in all copies. 00015 00016 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 00017 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00018 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 00019 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00020 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00021 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00022 PERFORMANCE OF THIS SOFTWARE. 00023 00024 Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN") 00025 00026 Permission to use, copy, modify, and distribute this software for any 00027 purpose with or without fee is hereby granted, provided that the above 00028 copyright notice and this permission notice appear in all copies. 00029 00030 THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH 00031 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00032 AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, 00033 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00034 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00035 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00036 PERFORMANCE OF THIS SOFTWARE. 00037 """ 00038 00039 import os, time, getopt, sys 00040 import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509 00041 import rpki.http, rpki.config, rpki.exceptions, rpki.relaxng 00042 import rpki.sundial, rpki.log 00043 00044 rootd = None 00045 00046 class list_pdu(rpki.up_down.list_pdu): 00047 def serve_pdu(self, q_msg, r_msg, ignored, callback, errback): 00048 r_msg.payload = rpki.up_down.list_response_pdu() 00049 rootd.compose_response(r_msg) 00050 callback() 00051 00052 class issue_pdu(rpki.up_down.issue_pdu): 00053 def serve_pdu(self, q_msg, r_msg, ignored, callback, errback): 00054 self.pkcs10.check_valid_rpki() 00055 r_msg.payload = rpki.up_down.issue_response_pdu() 00056 rootd.compose_response(r_msg, self.pkcs10) 00057 callback() 00058 00059 class revoke_pdu(rpki.up_down.revoke_pdu): 00060 def serve_pdu(self, q_msg, r_msg, ignored, callback, errback): 00061 rootd.subject_cert = get_subject_cert() 00062 if subject_cert is None or subject_cert.gSKI() != self.ski: 00063 raise rpki.exceptions.NotInDatabase 00064 rootd.del_subject_cert() 00065 rootd.del_subject_pkcs10() 00066 r_msg.payload = rpki.up_down.revoke_response_pdu() 00067 r_msg.payload.class_name = self.class_name 00068 r_msg.payload.ski = self.ski 00069 callback() 00070 00071 class message_pdu(rpki.up_down.message_pdu): 00072 00073 name2type = { 00074 "list" : list_pdu, 00075 "list_response" : rpki.up_down.list_response_pdu, 00076 "issue" : issue_pdu, 00077 "issue_response" : rpki.up_down.issue_response_pdu, 00078 "revoke" : revoke_pdu, 00079 "revoke_response" : rpki.up_down.revoke_response_pdu, 00080 "error_response" : rpki.up_down.error_response_pdu } 00081 00082 type2name = dict((v, k) for k, v in name2type.items()) 00083 00084 def log_query(self, child): 00085 """ 00086 Log query we're handling. 00087 """ 00088 rpki.log.info("Serving %s query" % self.type) 00089 00090 class sax_handler(rpki.up_down.sax_handler): 00091 pdu = message_pdu 00092 00093 class cms_msg(rpki.up_down.cms_msg): 00094 saxify = sax_handler.saxify 00095 00096 class main(object): 00097 00098 rpki_root_cert = None 00099 00100 def get_root_cert(self): 00101 rpki.log.debug("Read root cert %s" % self.rpki_root_cert_file) 00102 self.rpki_root_cert = rpki.x509.X509(Auto_file = self.rpki_root_cert_file) 00103 00104 def root_newer_than_subject(self): 00105 return os.stat(self.rpki_root_cert_file).st_mtime > os.stat(self.rpki_root_dir + self.rpki_subject_cert).st_mtime 00106 00107 def get_subject_cert(self): 00108 filename = self.rpki_root_dir + self.rpki_subject_cert 00109 try: 00110 x = rpki.x509.X509(Auto_file = filename) 00111 rpki.log.debug("Read subject cert %s" % filename) 00112 return x 00113 except IOError: 00114 return None 00115 00116 def set_subject_cert(self, cert): 00117 filename = self.rpki_root_dir + self.rpki_subject_cert 00118 rpki.log.debug("Writing subject cert %s, SKI %s" % (filename, cert.hSKI())) 00119 f = open(filename, "wb") 00120 f.write(cert.get_DER()) 00121 f.close() 00122 00123 def del_subject_cert(self): 00124 filename = self.rpki_root_dir + self.rpki_subject_cert 00125 rpki.log.debug("Deleting subject cert %s" % filename) 00126 os.remove(filename) 00127 00128 def get_subject_pkcs10(self): 00129 try: 00130 x = rpki.x509.PKCS10(Auto_file = self.rpki_subject_pkcs10) 00131 rpki.log.debug("Read subject PKCS #10 %s" % self.rpki_subject_pkcs10) 00132 return x 00133 except IOError: 00134 return None 00135 00136 def set_subject_pkcs10(self, pkcs10): 00137 rpki.log.debug("Writing subject PKCS #10 %s" % self.rpki_subject_pkcs10) 00138 f = open(self.rpki_subject_pkcs10, "wb") 00139 f.write(pkcs10.get_DER()) 00140 f.close() 00141 00142 def del_subject_pkcs10(self): 00143 rpki.log.debug("Deleting subject PKCS #10 %s" % self.rpki_subject_pkcs10) 00144 try: 00145 os.remove(self.rpki_subject_pkcs10) 00146 except OSError: 00147 pass 00148 00149 def issue_subject_cert_maybe(self, new_pkcs10): 00150 now = rpki.sundial.now() 00151 subject_cert = self.get_subject_cert() 00152 old_pkcs10 = self.get_subject_pkcs10() 00153 if new_pkcs10 is not None and new_pkcs10 != old_pkcs10: 00154 self.set_subject_pkcs10(new_pkcs10) 00155 if subject_cert is not None: 00156 rpki.log.debug("PKCS #10 changed, regenerating subject certificate") 00157 subject_cert = None 00158 if subject_cert is not None and subject_cert.getNotAfter() <= now + self.rpki_subject_regen: 00159 rpki.log.debug("Subject certificate has reached expiration threshold, regenerating") 00160 subject_cert = None 00161 if subject_cert is not None and self.root_newer_than_subject(): 00162 rpki.log.debug("Root certificate has changed, regenerating subject") 00163 subject_cert = None 00164 self.get_root_cert() 00165 if subject_cert is not None: 00166 return subject_cert 00167 pkcs10 = old_pkcs10 if new_pkcs10 is None else new_pkcs10 00168 if pkcs10 is None: 00169 rpki.log.debug("No PKCS #10 request, can't generate subject certificate yet") 00170 return None 00171 resources = self.rpki_root_cert.get_3779resources() 00172 rpki.log.info("Generating subject cert with resources " + str(resources)) 00173 req_key = pkcs10.getPublicKey() 00174 req_sia = pkcs10.get_SIA() 00175 crldp = self.rpki_base_uri + self.rpki_root_crl 00176 serial = now.totimestamp() 00177 subject_cert = self.rpki_root_cert.issue( 00178 keypair = self.rpki_root_key, 00179 subject_key = req_key, 00180 serial = serial, 00181 sia = req_sia, 00182 aia = self.rpki_root_cert_uri, 00183 crldp = crldp, 00184 resources = resources, 00185 notAfter = now + self.rpki_subject_lifetime) 00186 crl = rpki.x509.CRL.generate( 00187 keypair = self.rpki_root_key, 00188 issuer = self.rpki_root_cert, 00189 serial = serial, 00190 thisUpdate = now, 00191 nextUpdate = now + self.rpki_subject_lifetime, 00192 revokedCertificates = ()) 00193 rpki.log.debug("Writing CRL %s" % (self.rpki_root_dir + self.rpki_root_crl)) 00194 f = open(self.rpki_root_dir + self.rpki_root_crl, "wb") 00195 f.write(crl.get_DER()) 00196 f.close() 00197 manifest_resources = rpki.resource_set.resource_bag.from_inheritance() 00198 manifest_keypair = rpki.x509.RSA.generate() 00199 manifest_cert = self.rpki_root_cert.issue( 00200 keypair = self.rpki_root_key, 00201 subject_key = manifest_keypair.get_RSApublic(), 00202 serial = serial + 1, 00203 sia = None, 00204 aia = self.rpki_root_cert_uri, 00205 crldp = crldp, 00206 resources = manifest_resources, 00207 notAfter = now + self.rpki_subject_lifetime, 00208 is_ca = False) 00209 manifest = rpki.x509.SignedManifest.build( 00210 serial = serial, 00211 thisUpdate = now, 00212 nextUpdate = now + self.rpki_subject_lifetime, 00213 names_and_objs = [(self.rpki_subject_cert, subject_cert), (self.rpki_root_crl, crl)], 00214 keypair = manifest_keypair, 00215 certs = manifest_cert) 00216 rpki.log.debug("Writing manifest %s" % (self.rpki_root_dir + self.rpki_root_manifest)) 00217 f = open(self.rpki_root_dir + self.rpki_root_manifest, "wb") 00218 f.write(manifest.get_DER()) 00219 f.close() 00220 self.set_subject_cert(subject_cert) 00221 return subject_cert 00222 00223 def compose_response(self, r_msg, pkcs10 = None): 00224 subject_cert = self.issue_subject_cert_maybe(pkcs10) 00225 rc = rpki.up_down.class_elt() 00226 rc.class_name = self.rpki_class_name 00227 rc.cert_url = rpki.up_down.multi_uri(self.rpki_root_cert_uri) 00228 rc.from_resource_bag(self.rpki_root_cert.get_3779resources()) 00229 rc.issuer = self.rpki_root_cert 00230 r_msg.payload.classes.append(rc) 00231 if subject_cert is not None: 00232 rc.certs.append(rpki.up_down.certificate_elt()) 00233 rc.certs[0].cert_url = rpki.up_down.multi_uri(self.rpki_base_uri + self.rpki_subject_cert) 00234 rc.certs[0].cert = subject_cert 00235 00236 def up_down_handler(self, query, path, cb): 00237 try: 00238 q_msg = cms_msg(DER = query).unwrap((self.bpki_ta, self.child_bpki_cert)) 00239 except (rpki.async.ExitNow, SystemExit): 00240 raise 00241 except Exception, e: 00242 rpki.log.traceback() 00243 return cb(400, reason = "Could not process PDU: %s" % e) 00244 00245 def done(r_msg): 00246 cb(200, body = cms_msg().wrap(r_msg, self.rootd_bpki_key, self.rootd_bpki_cert, self.rootd_bpki_crl)) 00247 00248 try: 00249 q_msg.serve_top_level(None, done) 00250 except (rpki.async.ExitNow, SystemExit): 00251 raise 00252 except Exception, e: 00253 rpki.log.traceback() 00254 try: 00255 done(q_msg.serve_error(e)) 00256 except (rpki.async.ExitNow, SystemExit): 00257 raise 00258 except Exception, e: 00259 rpki.log.traceback() 00260 cb(500, reason = "Could not process PDU: %s" % e) 00261 00262 def __init__(self): 00263 00264 global rootd 00265 rootd = self # Gross, but simpler than what we'd have to do otherwise 00266 00267 os.environ["TZ"] = "UTC" 00268 time.tzset() 00269 00270 self.cfg_file = None 00271 00272 opts, argv = getopt.getopt(sys.argv[1:], "c:dh?", ["config=", "debug", "help"]) 00273 for o, a in opts: 00274 if o in ("-h", "--help", "-?"): 00275 print __doc__ 00276 sys.exit(0) 00277 elif o in ("-c", "--config"): 00278 self.cfg_file = a 00279 elif o in ("-d", "--debug"): 00280 rpki.log.use_syslog = False 00281 if argv: 00282 raise rpki.exceptions.CommandParseFailure, "Unexpected arguments %s" % argv 00283 00284 rpki.log.init("rootd") 00285 00286 self.cfg = rpki.config.parser(self.cfg_file, "rootd") 00287 00288 self.cfg.set_global_flags() 00289 00290 self.bpki_ta = rpki.x509.X509(Auto_file = self.cfg.get("bpki-ta")) 00291 self.rootd_bpki_key = rpki.x509.RSA( Auto_file = self.cfg.get("rootd-bpki-key")) 00292 self.rootd_bpki_cert = rpki.x509.X509(Auto_file = self.cfg.get("rootd-bpki-cert")) 00293 self.rootd_bpki_crl = rpki.x509.CRL( Auto_file = self.cfg.get("rootd-bpki-crl")) 00294 self.child_bpki_cert = rpki.x509.X509(Auto_file = self.cfg.get("child-bpki-cert")) 00295 00296 self.http_server_host = self.cfg.get("server-host", "") 00297 self.http_server_port = int(self.cfg.get("server-port")) 00298 00299 self.rpki_class_name = self.cfg.get("rpki-class-name", "wombat") 00300 00301 self.rpki_root_dir = self.cfg.get("rpki-root-dir") 00302 self.rpki_base_uri = self.cfg.get("rpki-base-uri", "rsync://" + self.rpki_class_name + ".invalid/") 00303 00304 self.rpki_root_key = rpki.x509.RSA( Auto_file = self.cfg.get("rpki-root-key")) 00305 self.rpki_root_cert_file = self.cfg.get("rpki-root-cert") 00306 self.rpki_root_cert_uri = self.cfg.get("rpki-root-cert-uri", self.rpki_base_uri + "Root.cer") 00307 00308 self.rpki_root_manifest = self.cfg.get("rpki-root-manifest", "Root.mnf") 00309 self.rpki_root_crl = self.cfg.get("rpki-root-crl", "Root.crl") 00310 self.rpki_subject_cert = self.cfg.get("rpki-subject-cert", "Child.cer") 00311 self.rpki_subject_pkcs10 = self.cfg.get("rpki-subject-pkcs10", "Child.pkcs10") 00312 00313 self.rpki_subject_lifetime = rpki.sundial.timedelta.parse(self.cfg.get("rpki-subject-lifetime", "30d")) 00314 self.rpki_subject_regen = rpki.sundial.timedelta.parse(self.cfg.get("rpki-subject-regen", self.rpki_subject_lifetime.convert_to_seconds() / 2)) 00315 00316 rpki.http.server(host = self.http_server_host, 00317 port = self.http_server_port, 00318 handlers = self.up_down_handler)