RPKI Engine
1.0
|
00001 """ 00002 RPKI "left-right" protocol. 00003 00004 $Id: left_right.py 4014 2011-10-05 16:30:24Z sra $ 00005 00006 Copyright (C) 2009--2011 Internet Systems Consortium ("ISC") 00007 00008 Permission to use, copy, modify, and distribute this software for any 00009 purpose with or without fee is hereby granted, provided that the above 00010 copyright notice and this permission notice appear in all copies. 00011 00012 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 00013 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00014 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 00015 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00016 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00017 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00018 PERFORMANCE OF THIS SOFTWARE. 00019 00020 Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN") 00021 00022 Permission to use, copy, modify, and distribute this software for any 00023 purpose with or without fee is hereby granted, provided that the above 00024 copyright notice and this permission notice appear in all copies. 00025 00026 THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH 00027 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00028 AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, 00029 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00030 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00031 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00032 PERFORMANCE OF THIS SOFTWARE. 00033 """ 00034 00035 import rpki.resource_set, rpki.x509, rpki.sql, rpki.exceptions, rpki.xml_utils 00036 import rpki.http, rpki.up_down, rpki.relaxng, rpki.sundial, rpki.log, rpki.roa 00037 import rpki.publication, rpki.async 00038 00039 # Enforce strict checking of XML "sender" field in up-down protocol 00040 enforce_strict_up_down_xml_sender = False 00041 00042 class left_right_namespace(object): 00043 """ 00044 XML namespace parameters for left-right protocol. 00045 """ 00046 00047 xmlns = "http://www.hactrn.net/uris/rpki/left-right-spec/" 00048 nsmap = { None : xmlns } 00049 00050 class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistent, left_right_namespace): 00051 """ 00052 Virtual class for top-level left-right protocol data elements. 00053 """ 00054 00055 handles = () 00056 00057 self_id = None 00058 self_handle = None 00059 00060 @property 00061 def self(self): 00062 """ 00063 Fetch self object to which this object links. 00064 """ 00065 return self_elt.sql_fetch(self.gctx, self.self_id) 00066 00067 @property 00068 def bsc(self): 00069 """ 00070 Return BSC object to which this object links. 00071 """ 00072 return bsc_elt.sql_fetch(self.gctx, self.bsc_id) 00073 00074 def make_reply_clone_hook(self, r_pdu): 00075 """ 00076 Set handles when cloning, including _id -> _handle translation. 00077 """ 00078 if r_pdu.self_handle is None: 00079 r_pdu.self_handle = self.self_handle 00080 for tag, elt in self.handles: 00081 id_name = tag + "_id" 00082 handle_name = tag + "_handle" 00083 if getattr(r_pdu, handle_name, None) is None: 00084 try: 00085 setattr(r_pdu, handle_name, getattr(elt.sql_fetch(self.gctx, getattr(r_pdu, id_name)), handle_name)) 00086 except AttributeError: 00087 continue 00088 00089 @classmethod 00090 def serve_fetch_handle(cls, gctx, self_id, handle): 00091 """ 00092 Find an object based on its handle. 00093 """ 00094 return cls.sql_fetch_where1(gctx, cls.element_name + "_handle = %s AND self_id = %s", (handle, self_id)) 00095 00096 def serve_fetch_one_maybe(self): 00097 """ 00098 Find the object on which a get, set, or destroy method should 00099 operate, or which would conflict with a create method. 00100 """ 00101 where = "%s.%s_handle = %%s AND %s.self_id = self.self_id AND self.self_handle = %%s" % ((self.element_name,) * 3) 00102 args = (getattr(self, self.element_name + "_handle"), self.self_handle) 00103 return self.sql_fetch_where1(self.gctx, where, args, "self") 00104 00105 def serve_fetch_all(self): 00106 """ 00107 Find the objects on which a list method should operate. 00108 """ 00109 where = "%s.self_id = self.self_id and self.self_handle = %%s" % self.element_name 00110 return self.sql_fetch_where(self.gctx, where, (self.self_handle,), "self") 00111 00112 def serve_pre_save_hook(self, q_pdu, r_pdu, cb, eb): 00113 """ 00114 Hook to do _handle => _id translation before saving. 00115 00116 self is always the object to be saved to SQL. For create 00117 operations, self and q_pdu are be the same object; for set 00118 operations, self is the pre-existing object from SQL and q_pdu is 00119 the set request received from the the IRBE. 00120 """ 00121 for tag, elt in self.handles: 00122 id_name = tag + "_id" 00123 if getattr(self, id_name, None) is None: 00124 x = elt.serve_fetch_handle(self.gctx, self.self_id, getattr(q_pdu, tag + "_handle")) 00125 if x is None: 00126 raise rpki.exceptions.HandleTranslationError, "Could not translate %r %s_handle" % (self, tag) 00127 setattr(self, id_name, getattr(x, id_name)) 00128 cb() 00129 00130 class self_elt(data_elt): 00131 """ 00132 <self/> element. 00133 """ 00134 00135 element_name = "self" 00136 attributes = ("action", "tag", "self_handle", "crl_interval", "regen_margin") 00137 elements = ("bpki_cert", "bpki_glue") 00138 booleans = ("rekey", "reissue", "revoke", "run_now", "publish_world_now", "revoke_forgotten") 00139 00140 sql_template = rpki.sql.template("self", "self_id", "self_handle", 00141 "use_hsm", "crl_interval", "regen_margin", 00142 ("bpki_cert", rpki.x509.X509), ("bpki_glue", rpki.x509.X509)) 00143 handles = () 00144 00145 use_hsm = False 00146 crl_interval = None 00147 regen_margin = None 00148 bpki_cert = None 00149 bpki_glue = None 00150 00151 @property 00152 def bscs(self): 00153 """ 00154 Fetch all BSC objects that link to this self object. 00155 """ 00156 return bsc_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) 00157 00158 @property 00159 def repositories(self): 00160 """ 00161 Fetch all repository objects that link to this self object. 00162 """ 00163 return repository_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) 00164 00165 @property 00166 def parents(self): 00167 """ 00168 Fetch all parent objects that link to this self object. 00169 """ 00170 return parent_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) 00171 00172 @property 00173 def children(self): 00174 """ 00175 Fetch all child objects that link to this self object. 00176 """ 00177 return child_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) 00178 00179 @property 00180 def roas(self): 00181 """ 00182 Fetch all ROA objects that link to this self object. 00183 """ 00184 return rpki.rpkid.roa_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) 00185 00186 @property 00187 def ghostbusters(self): 00188 """ 00189 Fetch all Ghostbuster record objects that link to this self object. 00190 """ 00191 return rpki.rpkid.ghostbuster_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) 00192 00193 def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): 00194 """ 00195 Extra server actions for self_elt. 00196 """ 00197 rpki.log.trace() 00198 actions = [] 00199 if q_pdu.rekey: 00200 actions.append(self.serve_rekey) 00201 if q_pdu.revoke: 00202 actions.append(self.serve_revoke) 00203 if q_pdu.reissue: 00204 actions.append(self.serve_reissue) 00205 if q_pdu.revoke_forgotten: 00206 actions.append(self.serve_revoke_forgotten) 00207 if q_pdu.publish_world_now: 00208 actions.append(self.serve_publish_world_now) 00209 if q_pdu.run_now: 00210 actions.append(self.serve_run_now) 00211 def loop(iterator, action): 00212 action(iterator, eb) 00213 rpki.async.iterator(actions, loop, cb) 00214 00215 def serve_rekey(self, cb, eb): 00216 """ 00217 Handle a left-right rekey action for this self. 00218 """ 00219 rpki.log.trace() 00220 def loop(iterator, parent): 00221 parent.serve_rekey(iterator, eb) 00222 rpki.async.iterator(self.parents, loop, cb) 00223 00224 def serve_revoke(self, cb, eb): 00225 """ 00226 Handle a left-right revoke action for this self. 00227 """ 00228 rpki.log.trace() 00229 def loop(iterator, parent): 00230 parent.serve_revoke(iterator, eb) 00231 rpki.async.iterator(self.parents, loop, cb) 00232 00233 def serve_reissue(self, cb, eb): 00234 """ 00235 Handle a left-right reissue action for this self. 00236 """ 00237 rpki.log.trace() 00238 def loop(iterator, parent): 00239 parent.serve_reissue(iterator, eb) 00240 rpki.async.iterator(self.parents, loop, cb) 00241 00242 def serve_revoke_forgotten(self, cb, eb): 00243 """ 00244 Handle a left-right revoke_forgotten action for this self. 00245 """ 00246 rpki.log.trace() 00247 def loop(iterator, parent): 00248 parent.serve_revoke_forgotten(iterator, eb) 00249 rpki.async.iterator(self.parents, loop, cb) 00250 00251 def serve_publish_world_now(self, cb, eb): 00252 """ 00253 Handle a left-right publish_world_now action for this self. 00254 00255 The publication stuff needs refactoring, right now publication is 00256 interleaved with local operations in a way that forces far too 00257 many bounces through the task system for any complex update. The 00258 whole thing ought to be rewritten to queue up outgoing publication 00259 PDUs and only send them when we're all done or when we need to 00260 force publication at a particular point in a multi-phase operation. 00261 00262 Once that reorganization has been done, this method should be 00263 rewritten to reuse the low-level publish() methods that each 00264 object will have...but we're not there yet. So, for now, we just 00265 do this via brute force. Think of it as a trial version to see 00266 whether we've identified everything that needs to be republished 00267 for this operation. 00268 """ 00269 00270 def loop(iterator, parent): 00271 q_msg = rpki.publication.msg.query() 00272 for ca in parent.cas: 00273 ca_detail = ca.active_ca_detail 00274 if ca_detail is not None: 00275 q_msg.append(rpki.publication.crl_elt.make_publish(ca_detail.crl_uri, ca_detail.latest_crl)) 00276 q_msg.append(rpki.publication.manifest_elt.make_publish(ca_detail.manifest_uri, ca_detail.latest_manifest)) 00277 q_msg.extend(rpki.publication.certificate_elt.make_publish(c.uri, c.cert) for c in ca_detail.child_certs) 00278 q_msg.extend(rpki.publication.roa_elt.make_publish(r.uri, r.roa) for r in ca_detail.roas if r.roa is not None) 00279 q_msg.extend(rpki.publication.ghostbuster_elt.make_publish(g.uri, g.ghostbuster) for g in ca_detail.ghostbusters) 00280 parent.repository.call_pubd(iterator, eb, q_msg) 00281 00282 rpki.async.iterator(self.parents, loop, cb) 00283 00284 def serve_run_now(self, cb, eb): 00285 """ 00286 Handle a left-right run_now action for this self. 00287 """ 00288 rpki.log.debug("Forced immediate run of periodic actions for self %s[%d]" % (self.self_handle, self.self_id)) 00289 self.cron(cb) 00290 00291 def serve_fetch_one_maybe(self): 00292 """ 00293 Find the self object upon which a get, set, or destroy action 00294 should operate, or which would conflict with a create method. 00295 """ 00296 return self.serve_fetch_handle(self.gctx, None, self.self_handle) 00297 00298 @classmethod 00299 def serve_fetch_handle(cls, gctx, self_id, self_handle): 00300 """ 00301 Find a self object based on its self_handle. 00302 """ 00303 return cls.sql_fetch_where1(gctx, "self_handle = %s", self_handle) 00304 00305 def serve_fetch_all(self): 00306 """ 00307 Find the self objects upon which a list action should operate. 00308 This is different from the list action for all other objects, 00309 where list only works within a given self_id context. 00310 """ 00311 return self.sql_fetch_all(self.gctx) 00312 00313 def cron(self, cb): 00314 """ 00315 Periodic tasks. 00316 """ 00317 00318 def one(): 00319 self.gctx.checkpoint() 00320 rpki.log.debug("Self %s[%d] polling parents" % (self.self_handle, self.self_id)) 00321 self.client_poll(two) 00322 00323 def two(): 00324 self.gctx.checkpoint() 00325 rpki.log.debug("Self %s[%d] updating children" % (self.self_handle, self.self_id)) 00326 self.update_children(three) 00327 00328 def three(): 00329 self.gctx.checkpoint() 00330 rpki.log.debug("Self %s[%d] updating ROAs" % (self.self_handle, self.self_id)) 00331 self.update_roas(four) 00332 00333 def four(): 00334 self.gctx.checkpoint() 00335 rpki.log.debug("Self %s[%d] updating Ghostbuster records" % (self.self_handle, self.self_id)) 00336 self.update_ghostbusters(five) 00337 00338 def five(): 00339 self.gctx.checkpoint() 00340 rpki.log.debug("Self %s[%d] regenerating CRLs and manifests" % (self.self_handle, self.self_id)) 00341 self.regenerate_crls_and_manifests(cb) 00342 00343 one() 00344 00345 00346 def client_poll(self, callback): 00347 """ 00348 Run the regular client poll cycle with each of this self's parents 00349 in turn. 00350 """ 00351 00352 rpki.log.trace() 00353 00354 def parent_loop(parent_iterator, parent): 00355 00356 def got_list(r_msg): 00357 ca_map = dict((ca.parent_resource_class, ca) for ca in parent.cas) 00358 self.gctx.checkpoint() 00359 00360 def class_loop(class_iterator, rc): 00361 00362 def class_update_failed(e): 00363 rpki.log.traceback() 00364 rpki.log.warn("Couldn't update class, skipping: %s" % e) 00365 class_iterator() 00366 00367 def class_create_failed(e): 00368 rpki.log.traceback() 00369 rpki.log.warn("Couldn't create class, skipping: %s" % e) 00370 class_iterator() 00371 00372 self.gctx.checkpoint() 00373 if rc.class_name in ca_map: 00374 ca = ca_map[rc.class_name] 00375 del ca_map[rc.class_name] 00376 ca.check_for_updates(parent, rc, class_iterator, class_update_failed) 00377 else: 00378 rpki.rpkid.ca_obj.create(parent, rc, class_iterator, class_create_failed) 00379 00380 def class_done(): 00381 00382 def ca_loop(iterator, ca): 00383 self.gctx.checkpoint() 00384 ca.delete(parent, iterator) 00385 00386 def ca_done(): 00387 self.gctx.checkpoint() 00388 self.gctx.sql.sweep() 00389 parent_iterator() 00390 00391 rpki.async.iterator(ca_map.values(), ca_loop, ca_done) 00392 00393 rpki.async.iterator(r_msg.payload.classes, class_loop, class_done) 00394 00395 def list_failed(e): 00396 rpki.log.traceback() 00397 rpki.log.warn("Couldn't get resource class list from parent %r, skipping: %s" % (parent, e)) 00398 parent_iterator() 00399 00400 rpki.up_down.list_pdu.query(parent, got_list, list_failed) 00401 00402 rpki.async.iterator(self.parents, parent_loop, callback) 00403 00404 00405 def update_children(self, cb): 00406 """ 00407 Check for updated IRDB data for all of this self's children and 00408 issue new certs as necessary. Must handle changes both in 00409 resources and in expiration date. 00410 """ 00411 00412 rpki.log.trace() 00413 now = rpki.sundial.now() 00414 rsn = now + rpki.sundial.timedelta(seconds = self.regen_margin) 00415 publisher = rpki.rpkid.publication_queue() 00416 00417 def loop(iterator, child): 00418 00419 def lose(e): 00420 rpki.log.traceback() 00421 rpki.log.warn("Couldn't update child %r, skipping: %s" % (child, e)) 00422 iterator() 00423 00424 def got_resources(irdb_resources): 00425 try: 00426 for child_cert in child_certs: 00427 ca_detail = child_cert.ca_detail 00428 ca = ca_detail.ca 00429 if ca_detail.state == "active": 00430 old_resources = child_cert.cert.get_3779resources() 00431 new_resources = irdb_resources.intersection(old_resources).intersection(ca_detail.latest_ca_cert.get_3779resources()) 00432 00433 if new_resources.empty(): 00434 rpki.log.debug("Resources shrank to the null set, revoking and withdrawing child %s certificate SKI %s" % (child.child_handle, child_cert.cert.gSKI())) 00435 child_cert.revoke(publisher = publisher) 00436 ca_detail.generate_crl(publisher = publisher) 00437 ca_detail.generate_manifest(publisher = publisher) 00438 00439 elif old_resources != new_resources or (old_resources.valid_until < rsn and irdb_resources.valid_until > now): 00440 rpki.log.debug("Need to reissue child %s certificate SKI %s" % (child.child_handle, child_cert.cert.gSKI())) 00441 child_cert.reissue( 00442 ca_detail = ca_detail, 00443 resources = new_resources, 00444 publisher = publisher) 00445 00446 elif old_resources.valid_until < now: 00447 rpki.log.debug("Child %s certificate SKI %s has expired: cert.valid_until %s, irdb.valid_until %s" 00448 % (child.child_handle, child_cert.cert.gSKI(), old_resources.valid_until, irdb_resources.valid_until)) 00449 child_cert.sql_delete() 00450 publisher.withdraw(cls = rpki.publication.certificate_elt, uri = child_cert.uri, obj = child_cert.cert, repository = ca.parent.repository) 00451 ca_detail.generate_manifest(publisher = publisher) 00452 00453 except (SystemExit, rpki.async.ExitNow): 00454 raise 00455 except Exception, e: 00456 self.gctx.checkpoint() 00457 lose(e) 00458 else: 00459 self.gctx.checkpoint() 00460 self.gctx.sql.sweep() 00461 iterator() 00462 00463 self.gctx.checkpoint() 00464 self.gctx.sql.sweep() 00465 child_certs = child.child_certs 00466 if child_certs: 00467 self.gctx.irdb_query_child_resources(child.self.self_handle, child.child_handle, got_resources, lose) 00468 else: 00469 iterator() 00470 00471 def done(): 00472 def lose(e): 00473 rpki.log.traceback() 00474 rpki.log.warn("Couldn't publish for %s, skipping: %s" % (self.self_handle, e)) 00475 self.gctx.checkpoint() 00476 cb() 00477 self.gctx.checkpoint() 00478 self.gctx.sql.sweep() 00479 publisher.call_pubd(cb, lose) 00480 00481 rpki.async.iterator(self.children, loop, done) 00482 00483 00484 def regenerate_crls_and_manifests(self, cb): 00485 """ 00486 Generate new CRLs and manifests as necessary for all of this 00487 self's CAs. Extracting nextUpdate from a manifest is hard at the 00488 moment due to implementation silliness, so for now we generate a 00489 new manifest whenever we generate a new CRL 00490 00491 This method also cleans up tombstones left behind by revoked 00492 ca_detail objects, since we're walking through the relevant 00493 portions of the database anyway. 00494 """ 00495 00496 rpki.log.trace() 00497 now = rpki.sundial.now() 00498 regen_margin = rpki.sundial.timedelta(seconds = self.regen_margin) 00499 publisher = rpki.rpkid.publication_queue() 00500 00501 for parent in self.parents: 00502 for ca in parent.cas: 00503 try: 00504 for ca_detail in ca.revoked_ca_details: 00505 if now > ca_detail.latest_crl.getNextUpdate(): 00506 ca_detail.delete(ca = ca, publisher = publisher) 00507 ca_detail = ca.active_ca_detail 00508 if ca_detail is not None and now + regen_margin > ca_detail.latest_crl.getNextUpdate(): 00509 ca_detail.generate_crl(publisher = publisher) 00510 ca_detail.generate_manifest(publisher = publisher) 00511 except (SystemExit, rpki.async.ExitNow): 00512 raise 00513 except Exception, e: 00514 rpki.log.traceback() 00515 rpki.log.warn("Couldn't regenerate CRLs and manifests for CA %r, skipping: %s" % (ca, e)) 00516 00517 def lose(e): 00518 rpki.log.traceback() 00519 rpki.log.warn("Couldn't publish updated CRLs and manifests for self %r, skipping: %s" % (self.self_handle, e)) 00520 self.gctx.checkpoint() 00521 cb() 00522 00523 self.gctx.checkpoint() 00524 self.gctx.sql.sweep() 00525 publisher.call_pubd(cb, lose) 00526 00527 00528 def update_ghostbusters(self, cb): 00529 """ 00530 Generate or update Ghostbuster records for this self. 00531 00532 This is heavily based on .update_roas(), and probably both of them 00533 need refactoring. 00534 """ 00535 00536 parents = dict((p.parent_handle, p) for p in self.parents) 00537 00538 def got_ghostbuster_requests(ghostbuster_requests): 00539 00540 try: 00541 self.gctx.checkpoint() 00542 if self.gctx.sql.dirty: 00543 rpki.log.warn("Unexpected dirty SQL cache, flushing") 00544 self.gctx.sql.sweep() 00545 00546 ghostbusters = {} 00547 orphans = [] 00548 for ghostbuster in self.ghostbusters: 00549 k = (ghostbuster.ca_detail_id, ghostbuster.vcard) 00550 if ghostbuster.ca_detail.state != "active" or k in ghostbusters: 00551 orphans.append(ghostbuster) 00552 else: 00553 ghostbusters[k] = ghostbuster 00554 00555 publisher = rpki.rpkid.publication_queue() 00556 ca_details = set() 00557 00558 seen = set() 00559 for ghostbuster_request in ghostbuster_requests: 00560 if ghostbuster_request.parent_handle not in parents: 00561 rpki.log.warn("Unknown parent_handle %r in Ghostbuster request, skipping" % ghostbuster_request.parent_handle) 00562 continue 00563 k = (ghostbuster_request.parent_handle, ghostbuster_request.vcard) 00564 if k in seen: 00565 rpki.log.warn("Skipping duplicate Ghostbuster request %r" % ghostbuster_request) 00566 continue 00567 seen.add(k) 00568 for ca in parents[ghostbuster_request.parent_handle].cas: 00569 ca_detail = ca.active_ca_detail 00570 if ca_detail is not None: 00571 ghostbuster = ghostbusters.pop((ca_detail.ca_detail_id, ghostbuster_request.vcard), None) 00572 if ghostbuster is None: 00573 ghostbuster = rpki.rpkid.ghostbuster_obj(self.gctx, self.self_id, ca_detail.ca_detail_id, ghostbuster_request.vcard) 00574 rpki.log.debug("Created new Ghostbuster request for %r" % ghostbuster_request.parent_handle) 00575 else: 00576 rpki.log.debug("Found existing Ghostbuster request for %r" % ghostbuster_request.parent_handle) 00577 ghostbuster.update(publisher = publisher, fast = True) 00578 ca_details.add(ca_detail) 00579 00580 orphans.extend(ghostbusters.itervalues()) 00581 for ghostbuster in orphans: 00582 ca_details.add(ghostbuster.ca_detail) 00583 ghostbuster.revoke(publisher = publisher, fast = True) 00584 00585 for ca_detail in ca_details: 00586 ca_detail.generate_crl(publisher = publisher) 00587 ca_detail.generate_manifest(publisher = publisher) 00588 00589 self.gctx.sql.sweep() 00590 00591 def publication_failed(e): 00592 rpki.log.traceback() 00593 rpki.log.warn("Couldn't publish Ghostbuster updates for %s, skipping: %s" % (self.self_handle, e)) 00594 self.gctx.checkpoint() 00595 cb() 00596 00597 self.gctx.checkpoint() 00598 publisher.call_pubd(cb, publication_failed) 00599 00600 except (SystemExit, rpki.async.ExitNow): 00601 raise 00602 except Exception, e: 00603 rpki.log.traceback() 00604 rpki.log.warn("Could not update Ghostbuster records for %s, skipping: %s" % (self.self_handle, e)) 00605 cb() 00606 00607 def ghostbuster_requests_failed(e): 00608 rpki.log.traceback() 00609 rpki.log.warn("Could not fetch Ghostbuster record requests for %s, skipping: %s" % (self.self_handle, e)) 00610 cb() 00611 00612 self.gctx.checkpoint() 00613 self.gctx.sql.sweep() 00614 self.gctx.irdb_query_ghostbuster_requests(self.self_handle, parents.iterkeys(), 00615 got_ghostbuster_requests, ghostbuster_requests_failed) 00616 00617 00618 def update_roas(self, cb): 00619 """ 00620 Generate or update ROAs for this self. 00621 """ 00622 00623 def got_roa_requests(roa_requests): 00624 00625 self.gctx.checkpoint() 00626 00627 if self.gctx.sql.dirty: 00628 rpki.log.warn("Unexpected dirty SQL cache, flushing") 00629 self.gctx.sql.sweep() 00630 00631 roas = {} 00632 orphans = [] 00633 for roa in self.roas: 00634 k = (roa.asn, str(roa.ipv4), str(roa.ipv6)) 00635 if k not in roas: 00636 roas[k] = roa 00637 elif (roa.roa is not None and roa.cert is not None and roa.ca_detail is not None and roa.ca_detail.state == "active" and 00638 (roas[k].roa is None or roas[k].cert is None or roas[k].ca_detail is None or roas[k].ca_detail.state != "active")): 00639 orphans.append(roas[k]) 00640 roas[k] = roa 00641 else: 00642 orphans.append(roa) 00643 00644 publisher = rpki.rpkid.publication_queue() 00645 ca_details = set() 00646 00647 seen = set() 00648 for roa_request in roa_requests: 00649 try: 00650 k = (roa_request.asn, str(roa_request.ipv4), str(roa_request.ipv6)) 00651 if k in seen: 00652 rpki.log.warn("Skipping duplicate ROA request %r" % roa_request) 00653 continue 00654 seen.add(k) 00655 roa = roas.pop(k, None) 00656 if roa is None: 00657 roa = rpki.rpkid.roa_obj(self.gctx, self.self_id, roa_request.asn, roa_request.ipv4, roa_request.ipv6) 00658 rpki.log.debug("Couldn't find existing ROA, created %r" % roa) 00659 else: 00660 rpki.log.debug("Found existing %r" % roa) 00661 roa.update(publisher = publisher, fast = True) 00662 ca_details.add(roa.ca_detail) 00663 except (SystemExit, rpki.async.ExitNow): 00664 raise 00665 except Exception, e: 00666 if not isinstance(e, rpki.exceptions.NoCoveringCertForROA): 00667 rpki.log.traceback() 00668 rpki.log.warn("Could not update %r, skipping: %s" % (roa, e)) 00669 00670 orphans.extend(roas.itervalues()) 00671 for roa in orphans: 00672 try: 00673 ca_details.add(roa.ca_detail) 00674 roa.revoke(publisher = publisher, fast = True) 00675 except (SystemExit, rpki.async.ExitNow): 00676 raise 00677 except Exception, e: 00678 rpki.log.traceback() 00679 rpki.log.warn("Could not revoke %r: %s" % (roa, e)) 00680 00681 self.gctx.sql.sweep() 00682 00683 for ca_detail in ca_details: 00684 ca_detail.generate_crl(publisher = publisher) 00685 ca_detail.generate_manifest(publisher = publisher) 00686 00687 self.gctx.sql.sweep() 00688 00689 def publication_failed(e): 00690 rpki.log.traceback() 00691 rpki.log.warn("Couldn't publish for %s, skipping: %s" % (self.self_handle, e)) 00692 self.gctx.checkpoint() 00693 cb() 00694 00695 self.gctx.checkpoint() 00696 publisher.call_pubd(cb, publication_failed) 00697 00698 def roa_requests_failed(e): 00699 rpki.log.traceback() 00700 rpki.log.warn("Could not fetch ROA requests for %s, skipping: %s" % (self.self_handle, e)) 00701 cb() 00702 00703 self.gctx.checkpoint() 00704 self.gctx.sql.sweep() 00705 self.gctx.irdb_query_roa_requests(self.self_handle, got_roa_requests, roa_requests_failed) 00706 00707 class bsc_elt(data_elt): 00708 """ 00709 <bsc/> (Business Signing Context) element. 00710 """ 00711 00712 element_name = "bsc" 00713 attributes = ("action", "tag", "self_handle", "bsc_handle", "key_type", "hash_alg", "key_length") 00714 elements = ("signing_cert", "signing_cert_crl", "pkcs10_request") 00715 booleans = ("generate_keypair",) 00716 00717 sql_template = rpki.sql.template("bsc", "bsc_id", "bsc_handle", 00718 "self_id", "hash_alg", 00719 ("private_key_id", rpki.x509.RSA), 00720 ("pkcs10_request", rpki.x509.PKCS10), 00721 ("signing_cert", rpki.x509.X509), 00722 ("signing_cert_crl", rpki.x509.CRL)) 00723 handles = (("self", self_elt),) 00724 00725 private_key_id = None 00726 pkcs10_request = None 00727 signing_cert = None 00728 signing_cert_crl = None 00729 00730 @property 00731 def repositories(self): 00732 """ 00733 Fetch all repository objects that link to this BSC object. 00734 """ 00735 return repository_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,)) 00736 00737 @property 00738 def parents(self): 00739 """ 00740 Fetch all parent objects that link to this BSC object. 00741 """ 00742 return parent_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,)) 00743 00744 @property 00745 def children(self): 00746 """ 00747 Fetch all child objects that link to this BSC object. 00748 """ 00749 return child_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,)) 00750 00751 def serve_pre_save_hook(self, q_pdu, r_pdu, cb, eb): 00752 """ 00753 Extra server actions for bsc_elt -- handle key generation. For 00754 now this only allows RSA with SHA-256. 00755 """ 00756 if q_pdu.generate_keypair: 00757 assert q_pdu.key_type in (None, "rsa") and q_pdu.hash_alg in (None, "sha256") 00758 self.private_key_id = rpki.x509.RSA.generate(keylength = q_pdu.key_length or 2048) 00759 self.pkcs10_request = rpki.x509.PKCS10.create(self.private_key_id) 00760 r_pdu.pkcs10_request = self.pkcs10_request 00761 data_elt.serve_pre_save_hook(self, q_pdu, r_pdu, cb, eb) 00762 00763 class repository_elt(data_elt): 00764 """ 00765 <repository/> element. 00766 """ 00767 00768 element_name = "repository" 00769 attributes = ("action", "tag", "self_handle", "repository_handle", "bsc_handle", "peer_contact_uri") 00770 elements = ("bpki_cert", "bpki_glue") 00771 00772 sql_template = rpki.sql.template("repository", "repository_id", "repository_handle", 00773 "self_id", "bsc_id", "peer_contact_uri", 00774 ("bpki_cert", rpki.x509.X509), 00775 ("bpki_glue", rpki.x509.X509), 00776 ("last_cms_timestamp", rpki.sundial.datetime)) 00777 00778 handles = (("self", self_elt), ("bsc", bsc_elt)) 00779 00780 bpki_cert = None 00781 bpki_glue = None 00782 00783 @property 00784 def parents(self): 00785 """ 00786 Fetch all parent objects that link to this repository object. 00787 """ 00788 return parent_elt.sql_fetch_where(self.gctx, "repository_id = %s", (self.repository_id,)) 00789 00790 @staticmethod 00791 def default_pubd_handler(pdu): 00792 """ 00793 Default handler for publication response PDUs. 00794 """ 00795 pdu.raise_if_error() 00796 00797 def call_pubd(self, callback, errback, q_msg, handlers = None): 00798 """ 00799 Send a message to publication daemon and return the response. 00800 00801 As a convenience, attempting to send an empty message returns 00802 immediate success without sending anything. 00803 00804 Handlers is a dict of handler functions to process the response 00805 PDUs. If the tag value in the response PDU appears in the dict, 00806 the associated handler is called to process the PDU. If no tag 00807 matches, default_pubd_handler() is called. A handler value of 00808 False suppresses calling of the default handler. 00809 """ 00810 00811 try: 00812 rpki.log.trace() 00813 00814 self.gctx.sql.sweep() 00815 00816 if not q_msg: 00817 return callback() 00818 00819 if handlers is None: 00820 handlers = {} 00821 00822 for q_pdu in q_msg: 00823 rpki.log.info("Sending %s %s to pubd" % (q_pdu.action, q_pdu.uri)) 00824 00825 bsc = self.bsc 00826 q_der = rpki.publication.cms_msg().wrap(q_msg, bsc.private_key_id, bsc.signing_cert, bsc.signing_cert_crl) 00827 bpki_ta_path = (self.gctx.bpki_ta, self.self.bpki_cert, self.self.bpki_glue, self.bpki_cert, self.bpki_glue) 00828 00829 def done(r_der): 00830 try: 00831 r_msg = rpki.publication.cms_msg(DER = r_der).unwrap(bpki_ta_path) 00832 for r_pdu in r_msg: 00833 handler = handlers.get(r_pdu.tag, self.default_pubd_handler) 00834 if handler: 00835 handler(r_pdu) 00836 if len(q_msg) != len(r_msg): 00837 raise rpki.exceptions.BadPublicationReply, "Wrong number of response PDUs from pubd: sent %r, got %r" % (q_msg, r_msg) 00838 callback() 00839 except (rpki.async.ExitNow, SystemExit): 00840 raise 00841 except Exception, e: 00842 errback(e) 00843 00844 rpki.http.client( 00845 url = self.peer_contact_uri, 00846 msg = q_der, 00847 callback = done, 00848 errback = errback) 00849 00850 except (rpki.async.ExitNow, SystemExit): 00851 raise 00852 except Exception, e: 00853 errback(e) 00854 00855 class parent_elt(data_elt): 00856 """ 00857 <parent/> element. 00858 """ 00859 00860 element_name = "parent" 00861 attributes = ("action", "tag", "self_handle", "parent_handle", "bsc_handle", "repository_handle", 00862 "peer_contact_uri", "sia_base", "sender_name", "recipient_name") 00863 elements = ("bpki_cms_cert", "bpki_cms_glue") 00864 booleans = ("rekey", "reissue", "revoke", "revoke_forgotten") 00865 00866 sql_template = rpki.sql.template("parent", "parent_id", "parent_handle", 00867 "self_id", "bsc_id", "repository_id", 00868 "peer_contact_uri", "sia_base", 00869 "sender_name", "recipient_name", 00870 ("bpki_cms_cert", rpki.x509.X509), 00871 ("bpki_cms_glue", rpki.x509.X509), 00872 ("last_cms_timestamp", rpki.sundial.datetime)) 00873 00874 handles = (("self", self_elt), ("bsc", bsc_elt), ("repository", repository_elt)) 00875 00876 bpki_cms_cert = None 00877 bpki_cms_glue = None 00878 00879 @property 00880 def repository(self): 00881 """ 00882 Fetch repository object to which this parent object links. 00883 """ 00884 return repository_elt.sql_fetch(self.gctx, self.repository_id) 00885 00886 @property 00887 def cas(self): 00888 """ 00889 Fetch all CA objects that link to this parent object. 00890 """ 00891 return rpki.rpkid.ca_obj.sql_fetch_where(self.gctx, "parent_id = %s", (self.parent_id,)) 00892 00893 def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): 00894 """ 00895 Extra server actions for parent_elt. 00896 """ 00897 actions = [] 00898 if q_pdu.rekey: 00899 actions.append(self.serve_rekey) 00900 if q_pdu.revoke: 00901 actions.append(self.serve_revoke) 00902 if q_pdu.reissue: 00903 actions.append(self.serve_reissue) 00904 if q_pdu.revoke_forgotten: 00905 actions.append(self.serve_revoke_forgotten) 00906 def loop(iterator, action): 00907 action(iterator, eb) 00908 rpki.async.iterator(actions, loop, cb) 00909 00910 def serve_rekey(self, cb, eb): 00911 """ 00912 Handle a left-right rekey action for this parent. 00913 """ 00914 def loop(iterator, ca): 00915 ca.rekey(iterator, eb) 00916 rpki.async.iterator(self.cas, loop, cb) 00917 00918 def serve_revoke(self, cb, eb): 00919 """ 00920 Handle a left-right revoke action for this parent. 00921 """ 00922 def loop(iterator, ca): 00923 ca.revoke(cb = iterator, eb = eb) 00924 rpki.async.iterator(self.cas, loop, cb) 00925 00926 def serve_reissue(self, cb, eb): 00927 """ 00928 Handle a left-right reissue action for this parent. 00929 """ 00930 def loop(iterator, ca): 00931 ca.reissue(cb = iterator, eb = eb) 00932 rpki.async.iterator(self.cas, loop, cb) 00933 00934 def serve_revoke_forgotten(self, cb, eb): 00935 """ 00936 Handle a left-right revoke_forgotten action for this parent. 00937 00938 This is a bit fiddly: we have to compare the result of an up-down 00939 list query with what we have locally and identify the SKIs of any 00940 certificates that have gone missing. This should never happen in 00941 ordinary operation, but can arise if we have somehow lost a 00942 private key, in which case there is nothing more we can do with 00943 the issued cert, so we have to clear it. As this really is not 00944 supposed to happen, we don't clear it automatically, instead we 00945 require an explicit trigger. 00946 """ 00947 00948 def got_list(r_msg): 00949 00950 ca_map = dict((ca.parent_resource_class, ca) for ca in self.cas) 00951 00952 def rc_loop(rc_iterator, rc): 00953 00954 if rc.class_name in ca_map: 00955 00956 def ski_loop(ski_iterator, ski): 00957 rpki.log.warn("Revoking certificates missing from our database, class %r, SKI %s" % (rc.class_name, ski)) 00958 rpki.up_down.revoke_pdu.query(ca, ski, lambda x: ski_iterator(), eb) 00959 00960 ca = ca_map[rc.class_name] 00961 skis_parent_knows_about = set(c.cert.gSKI() for c in rc.certs) 00962 skis_ca_knows_about = set(ca_detail.latest_ca_cert.gSKI() for ca_detail in ca.issue_response_candidate_ca_details) 00963 skis_only_parent_knows_about = skis_parent_knows_about - skis_ca_knows_about 00964 rpki.async.iterator(skis_only_parent_knows_about, ski_loop, rc_iterator) 00965 00966 else: 00967 rc_iterator() 00968 00969 rpki.async.iterator(r_msg.payload.classes, rc_loop, cb) 00970 00971 rpki.up_down.list_pdu.query(self, got_list, eb) 00972 00973 00974 def query_up_down(self, q_pdu, cb, eb): 00975 """ 00976 Client code for sending one up-down query PDU to this parent. 00977 """ 00978 00979 rpki.log.trace() 00980 00981 bsc = self.bsc 00982 if bsc is None: 00983 raise rpki.exceptions.BSCNotFound, "Could not find BSC %s" % self.bsc_id 00984 00985 if bsc.signing_cert is None: 00986 raise rpki.exceptions.BSCNotReady, "BSC %r[%s] is not yet usable" % (bsc.bsc_handle, bsc.bsc_id) 00987 00988 q_msg = rpki.up_down.message_pdu.make_query( 00989 payload = q_pdu, 00990 sender = self.sender_name, 00991 recipient = self.recipient_name) 00992 00993 q_der = rpki.up_down.cms_msg().wrap(q_msg, bsc.private_key_id, 00994 bsc.signing_cert, 00995 bsc.signing_cert_crl) 00996 00997 def unwrap(r_der): 00998 try: 00999 r_msg = rpki.up_down.cms_msg(DER = r_der).unwrap((self.gctx.bpki_ta, 01000 self.self.bpki_cert, 01001 self.self.bpki_glue, 01002 self.bpki_cms_cert, 01003 self.bpki_cms_glue)) 01004 r_msg.payload.check_response() 01005 except (SystemExit, rpki.async.ExitNow): 01006 raise 01007 except Exception, e: 01008 eb(e) 01009 else: 01010 cb(r_msg) 01011 01012 rpki.http.client( 01013 msg = q_der, 01014 url = self.peer_contact_uri, 01015 callback = unwrap, 01016 errback = eb) 01017 01018 class child_elt(data_elt): 01019 """ 01020 <child/> element. 01021 """ 01022 01023 element_name = "child" 01024 attributes = ("action", "tag", "self_handle", "child_handle", "bsc_handle") 01025 elements = ("bpki_cert", "bpki_glue") 01026 booleans = ("reissue", ) 01027 01028 sql_template = rpki.sql.template("child", "child_id", "child_handle", 01029 "self_id", "bsc_id", 01030 ("bpki_cert", rpki.x509.X509), 01031 ("bpki_glue", rpki.x509.X509), 01032 ("last_cms_timestamp", rpki.sundial.datetime)) 01033 01034 handles = (("self", self_elt), ("bsc", bsc_elt)) 01035 01036 bpki_cert = None 01037 bpki_glue = None 01038 01039 def fetch_child_certs(self, ca_detail = None, ski = None, unique = False): 01040 """ 01041 Fetch all child_cert objects that link to this child object. 01042 """ 01043 return rpki.rpkid.child_cert_obj.fetch(self.gctx, self, ca_detail, ski, unique) 01044 01045 @property 01046 def child_certs(self): 01047 """ 01048 Fetch all child_cert objects that link to this child object. 01049 """ 01050 return self.fetch_child_certs() 01051 01052 @property 01053 def parents(self): 01054 """ 01055 Fetch all parent objects that link to self object to which this child object links. 01056 """ 01057 return parent_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) 01058 01059 def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): 01060 """ 01061 Extra server actions for child_elt. 01062 """ 01063 if q_pdu.reissue: 01064 self.serve_reissue(cb, eb) 01065 else: 01066 cb() 01067 01068 def serve_reissue(self, cb, eb): 01069 """ 01070 Handle a left-right reissue action for this child. 01071 """ 01072 publisher = rpki.rpkid.publication_queue() 01073 for child_cert in self.child_certs: 01074 child_cert.reissue(child_cert.ca_detail, publisher, force = True) 01075 publisher.call_pubd(cb, eb) 01076 01077 def ca_from_class_name(self, class_name): 01078 """ 01079 Fetch the CA corresponding to an up-down class_name. 01080 """ 01081 if not class_name.isdigit(): 01082 raise rpki.exceptions.BadClassNameSyntax, "Bad class name %s" % class_name 01083 ca = rpki.rpkid.ca_obj.sql_fetch(self.gctx, long(class_name)) 01084 if ca is None: 01085 raise rpki.exceptions.ClassNameUnknown, "Unknown class name %s" % class_name 01086 parent = ca.parent 01087 if self.self_id != parent.self_id: 01088 raise rpki.exceptions.ClassNameMismatch, "Class name mismatch: child.self_id = %d, parent.self_id = %d" % (self.self_id, parent.self_id) 01089 return ca 01090 01091 def serve_destroy_hook(self, cb, eb): 01092 """ 01093 Extra server actions when destroying a child_elt. 01094 """ 01095 publisher = rpki.rpkid.publication_queue() 01096 for child_cert in self.child_certs: 01097 child_cert.revoke(publisher = publisher, 01098 generate_crl_and_manifest = True) 01099 publisher.call_pubd(cb, eb) 01100 01101 def serve_up_down(self, query, callback): 01102 """ 01103 Outer layer of server handling for one up-down PDU from this child. 01104 """ 01105 01106 rpki.log.trace() 01107 01108 bsc = self.bsc 01109 if bsc is None: 01110 raise rpki.exceptions.BSCNotFound, "Could not find BSC %s" % self.bsc_id 01111 q_msg = rpki.up_down.cms_msg(DER = query).unwrap((self.gctx.bpki_ta, 01112 self.self.bpki_cert, 01113 self.self.bpki_glue, 01114 self.bpki_cert, 01115 self.bpki_glue)) 01116 q_msg.payload.gctx = self.gctx 01117 if enforce_strict_up_down_xml_sender and q_msg.sender != str(self.child_id): 01118 raise rpki.exceptions.BadSender, "Unexpected XML sender %s" % q_msg.sender 01119 01120 def done(r_msg): 01121 # 01122 # Exceptions from this point on are problematic, as we have no 01123 # sane way of reporting errors in the error reporting mechanism. 01124 # May require refactoring, ignore the issue for now. 01125 # 01126 reply = rpki.up_down.cms_msg().wrap(r_msg, bsc.private_key_id, 01127 bsc.signing_cert, bsc.signing_cert_crl) 01128 callback(reply) 01129 01130 try: 01131 q_msg.serve_top_level(self, done) 01132 except (rpki.async.ExitNow, SystemExit): 01133 raise 01134 except rpki.exceptions.NoActiveCA, data: 01135 done(q_msg.serve_error(data)) 01136 except Exception, e: 01137 rpki.log.traceback() 01138 done(q_msg.serve_error(e)) 01139 01140 class list_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): 01141 """ 01142 <list_resources/> element. 01143 """ 01144 01145 element_name = "list_resources" 01146 attributes = ("self_handle", "tag", "child_handle", "valid_until", "asn", "ipv4", "ipv6") 01147 valid_until = None 01148 01149 def startElement(self, stack, name, attrs): 01150 """ 01151 Handle <list_resources/> element. This requires special handling 01152 due to the data types of some of the attributes. 01153 """ 01154 assert name == "list_resources", "Unexpected name %s, stack %s" % (name, stack) 01155 self.read_attrs(attrs) 01156 if isinstance(self.valid_until, str): 01157 self.valid_until = rpki.sundial.datetime.fromXMLtime(self.valid_until) 01158 if self.asn is not None: 01159 self.asn = rpki.resource_set.resource_set_as(self.asn) 01160 if self.ipv4 is not None: 01161 self.ipv4 = rpki.resource_set.resource_set_ipv4(self.ipv4) 01162 if self.ipv6 is not None: 01163 self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6) 01164 01165 def toXML(self): 01166 """ 01167 Generate <list_resources/> element. This requires special 01168 handling due to the data types of some of the attributes. 01169 """ 01170 elt = self.make_elt() 01171 if isinstance(self.valid_until, int): 01172 elt.set("valid_until", self.valid_until.toXMLtime()) 01173 return elt 01174 01175 class list_roa_requests_elt(rpki.xml_utils.base_elt, left_right_namespace): 01176 """ 01177 <list_roa_requests/> element. 01178 """ 01179 01180 element_name = "list_roa_requests" 01181 attributes = ("self_handle", "tag", "asn", "ipv4", "ipv6") 01182 01183 def startElement(self, stack, name, attrs): 01184 """ 01185 Handle <list_roa_requests/> element. This requires special handling 01186 due to the data types of some of the attributes. 01187 """ 01188 assert name == "list_roa_requests", "Unexpected name %s, stack %s" % (name, stack) 01189 self.read_attrs(attrs) 01190 if self.ipv4 is not None: 01191 self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4(self.ipv4) 01192 if self.ipv6 is not None: 01193 self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(self.ipv6) 01194 01195 class list_ghostbuster_requests_elt(rpki.xml_utils.text_elt, left_right_namespace): 01196 """ 01197 <list_ghostbuster_requests/> element. 01198 """ 01199 01200 element_name = "list_ghostbuster_requests" 01201 attributes = ("self_handle", "tag", "parent_handle") 01202 text_attribute = "vcard" 01203 01204 vcard = None 01205 01206 01207 class list_published_objects_elt(rpki.xml_utils.text_elt, left_right_namespace): 01208 """ 01209 <list_published_objects/> element. 01210 """ 01211 01212 element_name = "list_published_objects" 01213 attributes = ("self_handle", "tag", "uri") 01214 text_attribute = "obj" 01215 01216 obj = None 01217 01218 def serve_dispatch(self, r_msg, cb, eb): 01219 """ 01220 Handle a <list_published_objects/> query. The method name is a 01221 misnomer here, there's no action attribute and no dispatch, we 01222 just dump every published object for the specified <self/> and return. 01223 """ 01224 for parent in self_elt.serve_fetch_handle(self.gctx, None, self.self_handle).parents: 01225 for ca in parent.cas: 01226 ca_detail = ca.active_ca_detail 01227 if ca_detail is not None: 01228 r_msg.append(self.make_reply(ca_detail.crl_uri, ca_detail.latest_crl)) 01229 r_msg.append(self.make_reply(ca_detail.manifest_uri, ca_detail.latest_manifest)) 01230 r_msg.extend(self.make_reply(c.uri, c.cert) for c in ca_detail.child_certs) 01231 r_msg.extend(self.make_reply(r.uri, r.roa) for r in ca_detail.roas if r.roa is not None) 01232 r_msg.extend(self.make_reply(g.uri, g.ghostbuster) for g in ca_detail.ghostbusters) 01233 cb() 01234 01235 def make_reply(self, uri, obj): 01236 """ 01237 Generate one reply PDU. 01238 """ 01239 r_pdu = self.make_pdu(tag = self.tag, self_handle = self.self_handle, uri = uri) 01240 r_pdu.obj = obj.get_Base64() 01241 return r_pdu 01242 01243 class list_received_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): 01244 """ 01245 <list_received_resources/> element. 01246 """ 01247 01248 element_name = "list_received_resources" 01249 attributes = ("self_handle", "tag", "parent_handle", 01250 "notBefore", "notAfter", "uri", "sia_uri", "aia_uri", "asn", "ipv4", "ipv6") 01251 01252 def serve_dispatch(self, r_msg, cb, eb): 01253 """ 01254 Handle a <list_received_resources/> query. The method name is a 01255 misnomer here, there's no action attribute and no dispatch, we 01256 just dump a bunch of data about every certificate issued to us by 01257 one of our parents, then return. 01258 """ 01259 for parent in self_elt.serve_fetch_handle(self.gctx, None, self.self_handle).parents: 01260 for ca in parent.cas: 01261 ca_detail = ca.active_ca_detail 01262 if ca_detail is not None and ca_detail.latest_ca_cert is not None: 01263 r_msg.append(self.make_reply(parent.parent_handle, ca_detail.ca_cert_uri, ca_detail.latest_ca_cert)) 01264 cb() 01265 01266 def make_reply(self, parent_handle, uri, cert): 01267 """ 01268 Generate one reply PDU. 01269 """ 01270 resources = cert.get_3779resources() 01271 return self.make_pdu( 01272 tag = self.tag, 01273 self_handle = self.self_handle, 01274 parent_handle = parent_handle, 01275 notBefore = str(cert.getNotBefore()), 01276 notAfter = str(cert.getNotAfter()), 01277 uri = uri, 01278 sia_uri = cert.get_sia_directory_uri(), 01279 aia_uri = cert.get_aia_uri(), 01280 asn = resources.asn, 01281 ipv4 = resources.v4, 01282 ipv6 = resources.v6) 01283 01284 class report_error_elt(rpki.xml_utils.text_elt, left_right_namespace): 01285 """ 01286 <report_error/> element. 01287 """ 01288 01289 element_name = "report_error" 01290 attributes = ("tag", "self_handle", "error_code") 01291 text_attribute = "error_text" 01292 01293 error_text = None 01294 01295 @classmethod 01296 def from_exception(cls, e, self_handle = None, tag = None): 01297 """ 01298 Generate a <report_error/> element from an exception. 01299 """ 01300 self = cls() 01301 self.self_handle = self_handle 01302 self.tag = tag 01303 self.error_code = e.__class__.__name__ 01304 self.error_text = str(e) 01305 return self 01306 01307 class msg(rpki.xml_utils.msg, left_right_namespace): 01308 """ 01309 Left-right PDU. 01310 """ 01311 01312 ## @var version 01313 # Protocol version 01314 version = 1 01315 01316 ## @var pdus 01317 # Dispatch table of PDUs for this protocol. 01318 pdus = dict((x.element_name, x) 01319 for x in (self_elt, child_elt, parent_elt, bsc_elt, 01320 repository_elt, list_resources_elt, 01321 list_roa_requests_elt, list_ghostbuster_requests_elt, 01322 list_published_objects_elt, 01323 list_received_resources_elt, report_error_elt)) 01324 01325 def serve_top_level(self, gctx, cb): 01326 """ 01327 Serve one msg PDU. 01328 """ 01329 01330 r_msg = self.__class__.reply() 01331 01332 def loop(iterator, q_pdu): 01333 01334 def fail(e): 01335 if not isinstance(e, rpki.exceptions.NotFound): 01336 rpki.log.traceback() 01337 r_msg.append(report_error_elt.from_exception(e, self_handle = q_pdu.self_handle, tag = q_pdu.tag)) 01338 cb(r_msg) 01339 01340 try: 01341 q_pdu.gctx = gctx 01342 q_pdu.serve_dispatch(r_msg, iterator, fail) 01343 except (rpki.async.ExitNow, SystemExit): 01344 raise 01345 except Exception, e: 01346 fail(e) 01347 01348 def done(): 01349 cb(r_msg) 01350 01351 rpki.async.iterator(self, loop, done) 01352 01353 class sax_handler(rpki.xml_utils.sax_handler): 01354 """ 01355 SAX handler for Left-Right protocol. 01356 """ 01357 01358 pdu = msg 01359 name = "msg" 01360 version = "1" 01361 01362 class cms_msg(rpki.x509.XML_CMS_object): 01363 """ 01364 Class to hold a CMS-signed left-right PDU. 01365 """ 01366 01367 encoding = "us-ascii" 01368 schema = rpki.relaxng.left_right 01369 saxify = sax_handler.saxify