RPKI Engine 1.0

left_right.py (3868)

Go to the documentation of this file.
00001 """
00002 RPKI "left-right" protocol.
00003 
00004 $Id: left_right.py 3868 2011-06-13 23:17:25Z 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, data:
01137       rpki.log.traceback()
01138       done(q_msg.serve_error(data))
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
 All Classes Namespaces Files Functions Variables