RPKI Engine
1.0
|
00001 """ 00002 Prototype of an iterator class to parse the output of an rcynic run. 00003 This script will almost certainly move to the library package once 00004 it's stable. 00005 00006 $Id: rcynic.py 4054 2011-10-18 22:56:05Z melkins $ 00007 00008 Copyright (C) 2010-2011 Internet Systems Consortium ("ISC") 00009 00010 Permission to use, copy, modify, and distribute this software for any 00011 purpose with or without fee is hereby granted, provided that the above 00012 copyright notice and this permission notice appear in all copies. 00013 00014 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 00015 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00016 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 00017 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00018 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00019 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00020 PERFORMANCE OF THIS SOFTWARE. 00021 """ 00022 00023 import sys, os, rpki.x509, rpki.exceptions 00024 from xml.etree.ElementTree import ElementTree 00025 00026 class UnknownObject(rpki.exceptions.RPKI_Exception): 00027 """ 00028 Unrecognized object in rcynic result cache. 00029 """ 00030 00031 class NotRsyncURI(rpki.exceptions.RPKI_Exception): 00032 """ 00033 URI is not an rsync URI. 00034 """ 00035 00036 class rcynic_object(object): 00037 """ 00038 An object read from rcynic cache. 00039 """ 00040 00041 def __init__(self, filename, **kwargs): 00042 self.filename = filename 00043 for k, v in kwargs.iteritems(): 00044 setattr(self, k, v) 00045 self.obj = self.obj_class(DER_file = filename) 00046 00047 def __repr__(self): 00048 return "<%s %s %s at 0x%x>" % (self.__class__.__name__, self.uri, self.resources, id(self)) 00049 00050 def show_attrs(self, *attrs): 00051 """ 00052 Print a bunch of object attributes, quietly ignoring any that 00053 might be missing. 00054 """ 00055 for a in attrs: 00056 try: 00057 print "%s: %s" % (a.capitalize(), getattr(self, a)) 00058 except AttributeError: 00059 pass 00060 00061 def show(self): 00062 """ 00063 Print common object attributes. 00064 """ 00065 self.show_attrs("filename", "uri", "status", "timestamp") 00066 00067 class rcynic_certificate(rcynic_object): 00068 """ 00069 A certificate from rcynic cache. 00070 """ 00071 00072 obj_class = rpki.x509.X509 00073 00074 def __init__(self, filename, **kwargs): 00075 rcynic_object.__init__(self, filename, **kwargs) 00076 self.notBefore = self.obj.getNotBefore() 00077 self.notAfter = self.obj.getNotAfter() 00078 self.aia_uri = self.obj.get_aia_uri() 00079 self.sia_directory_uri = self.obj.get_sia_directory_uri() 00080 self.manifest_uri = self.obj.get_sia_manifest_uri() 00081 self.resources = self.obj.get_3779resources() 00082 self.is_ca = self.obj.is_CA() 00083 self.serial = self.obj.getSerial() 00084 self.issuer = self.obj.getIssuer() 00085 self.subject = self.obj.getSubject() 00086 self.ski = self.obj.hSKI() 00087 self.aki = self.obj.hAKI() 00088 00089 def show(self): 00090 """ 00091 Print certificate attributes. 00092 """ 00093 rcynic_object.show(self) 00094 self.show_attrs("notBefore", "notAfter", "aia_uri", "sia_directory_uri", "resources") 00095 00096 class rcynic_roa(rcynic_object): 00097 """ 00098 A ROA from rcynic cache. 00099 """ 00100 00101 obj_class = rpki.x509.ROA 00102 00103 _afi_map = dict((cls.resource_set_type.afi, cls) 00104 for cls in (rpki.resource_set.roa_prefix_set_ipv4, 00105 rpki.resource_set.roa_prefix_set_ipv6)) 00106 00107 def __init__(self, filename, **kwargs): 00108 rcynic_object.__init__(self, filename, **kwargs) 00109 self.obj.extract() 00110 self.asID = self.obj.get_content().asID.get() 00111 self.prefix_sets = [] 00112 for fam in self.obj.get_content().ipAddrBlocks: 00113 prefix_set = self._afi_map[fam.addressFamily.get()]() 00114 addr_type = prefix_set.resource_set_type.range_type.datum_type 00115 self.prefix_sets.append(prefix_set) 00116 for addr in fam.addresses: 00117 prefix = addr.address.get() 00118 prefixlen = len(prefix) 00119 prefix = addr_type(rpki.resource_set._bs2long(prefix, addr_type.bits, 0)) 00120 maxprefixlen = addr.maxLength.get() 00121 prefix_set.append(prefix_set.prefix_type(prefix, prefixlen, maxprefixlen)) 00122 self.ee = rpki.x509.X509(POW = self.obj.get_POW().certs()[0]) 00123 self.notBefore = self.ee.getNotBefore() 00124 self.notAfter = self.ee.getNotAfter() 00125 self.aia_uri = self.ee.get_aia_uri() 00126 self.resources = self.ee.get_3779resources() 00127 self.issuer = self.ee.getIssuer() 00128 self.serial = self.ee.getSerial() 00129 self.subject = self.ee.getSubject() 00130 self.aki = self.ee.hAKI() 00131 self.ski = self.ee.hSKI() 00132 00133 def show(self): 00134 """ 00135 Print ROA attributes. 00136 """ 00137 rcynic_object.show(self) 00138 self.show_attrs("notBefore", "notAfter", "aia_uri", "resources", "asID") 00139 if self.prefix_sets: 00140 print "Prefixes:", ",".join(str(i) for i in self.prefix_sets) 00141 00142 class rcynic_ghostbuster(rcynic_object): 00143 """ 00144 Ghostbuster record from the rcynic cache. 00145 """ 00146 00147 obj_class = rpki.x509.Ghostbuster 00148 00149 def __init__(self, *args, **kwargs): 00150 rcynic_object.__init__(self, *args, **kwargs) 00151 self.obj.extract() 00152 self.vcard = self.obj.get_content() 00153 self.ee = rpki.x509.X509(POW = self.obj.get_POW().certs()[0]) 00154 self.notBefore = self.ee.getNotBefore() 00155 self.notAfter = self.ee.getNotAfter() 00156 self.aia_uri = self.ee.get_aia_uri() 00157 self.issuer = self.ee.getIssuer() 00158 self.serial = self.ee.getSerial() 00159 self.subject = self.ee.getSubject() 00160 self.aki = self.ee.hAKI() 00161 self.ski = self.ee.hSKI() 00162 00163 def show(self): 00164 rcynic_object.show(self) 00165 self.show_attrs("notBefore", "notAfter", "vcard") 00166 00167 file_name_classes = { 00168 ".cer" : rcynic_certificate, 00169 ".gbr" : rcynic_ghostbuster, 00170 ".roa" : rcynic_roa } 00171 00172 class rcynic_file_iterator(object): 00173 """ 00174 Iterate over files in an rcynic output tree, yielding a Python 00175 representation of each object found. 00176 """ 00177 00178 def __init__(self, rcynic_root, 00179 authenticated_subdir = "authenticated"): 00180 self.rcynic_dir = os.path.join(rcynic_root, authenticated_subdir) 00181 00182 def __iter__(self): 00183 for root, dirs, files in os.walk(self.rcynic_dir): 00184 for filename in files: 00185 filename = os.path.join(root, filename) 00186 ext = os.path.splitext(filename)[1] 00187 if ext in file_name_classes: 00188 yield file_name_classes[ext](filename) 00189 00190 class validation_status_element(object): 00191 def __init__(self, *args, **kwargs): 00192 for k,v in kwargs.iteritems(): 00193 setattr(self, k, v) 00194 self._obj = None 00195 00196 def get_obj(self): 00197 if not self._obj: 00198 self._obj = self.file_class(filename=self.filename, uri=self.uri) 00199 return self._obj 00200 00201 obj = property(get_obj) 00202 00203 class rcynic_xml_iterator(object): 00204 """ 00205 Iterate over validation_status entries in the XML output from an 00206 rcynic run. Yields a tuple for each entry: 00207 00208 timestamp, generation, status, object 00209 00210 where URI, status, and timestamp are the corresponding values from 00211 the XML element, OK is a boolean indicating whether validation was 00212 considered succesful, and object is a Python representation of the 00213 object in question. If OK is True, object will be from rcynic's 00214 authenticated output tree; otherwise, object will be from rcynic's 00215 unauthenticated output tree. 00216 00217 Note that it is possible for the same URI to appear in more than one 00218 validation_status element; in such cases, the succesful case (OK 00219 True) should be the last entry (as rcynic will stop trying once it 00220 gets a good copy), but there may be multiple failures, which might 00221 or might not have different status codes. 00222 """ 00223 00224 def __init__(self, rcynic_root, xml_file, 00225 authenticated_old_subdir = "authenticated.old", 00226 unauthenticated_subdir = "unauthenticated"): 00227 self.rcynic_root = rcynic_root 00228 self.xml_file = xml_file 00229 self.authenticated_old_subdir = os.path.join(rcynic_root, authenticated_old_subdir) 00230 self.unauthenticated_subdir = os.path.join(rcynic_root, unauthenticated_subdir) 00231 00232 base_uri = "rsync://" 00233 00234 def uri_to_filename(self, uri): 00235 if uri.startswith(self.base_uri): 00236 return uri[len(self.base_uri):] 00237 else: 00238 raise NotRsyncURI, "Not an rsync URI %r" % uri 00239 00240 def __iter__(self): 00241 for validation_status in ElementTree(file=self.xml_file).getroot().getiterator("validation_status"): 00242 timestamp = validation_status.get("timestamp") 00243 status = validation_status.get("status") 00244 uri = validation_status.text.strip() 00245 generation = validation_status.get("generation") 00246 00247 # determine the path to this object 00248 filename = os.path.join(self.authenticated_old_subdir if generation == 'backup' else self.unauthenticated_subdir, 00249 self.uri_to_filename(uri)) 00250 00251 ext = os.path.splitext(filename)[1] 00252 if ext in file_name_classes: 00253 yield validation_status_element(timestamp=timestamp, generation=generation, uri=uri, 00254 status=status, filename=filename, file_class=file_name_classes[ext]) 00255 00256 def label_iterator(xml_file): 00257 """ 00258 Returns an iterator which contains all defined labels from an rcynic XML 00259 output file. Each item is a tuple of the form 00260 (label, kind, description). 00261 """ 00262 00263 for label in ElementTree(file=xml_file).find("labels"): 00264 yield label.tag, label.get("kind"), label.text.strip()