aboutsummaryrefslogtreecommitdiff
path: root/openssl/trunk/crypto/pqueue
ModeNameSize
-rw-r--r--Makefile2045logplainblame
-rw-r--r--pq_compat.h6142logplainblame
-rw-r--r--pq_test.c3421logplainblame
-rw-r--r--pqueue.c5412logplainblame
-rw-r--r--pqueue.h3519logplainblame
10 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
#!/usr/bin/env python
# $Id$
#
# Copyright (C) 2015--2016  Parsons Government Services ("PARSONS")
# Portions copyright (C) 2014  Dragon Research Labs ("DRL")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notices and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND PARSONS AND DRL DISCLAIM ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
# PARSONS OR DRL BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

"""
Fetch RPKI data using RRDP starting from a TAL.

Work in progress, don't be too surprised by anything this does or
doesn't do.
"""

import rpki.relaxng
import rpki.x509
import lxml.etree
import argparse
import urlparse
import urllib2            
import sys
import os


class Tags(object):
  def __init__(self, *tags):
    for tag in tags:
      setattr(self, tag, rpki.relaxng.rrdp.xmlns + tag)

tags = Tags("notification", "delta", "snapshot", "publish", "withdraw")


class RSyncHandler(urllib2.BaseHandler):
  """
  Jam support for rsync:// URIs into urllib2 framework.
  Very basic, probably not paranoid enough.
  """

  _n = 0

  def rsync_open(self, req):
    import subprocess, mimetools
    u = req.get_full_url()
    if u.endswith("/"):
      raise urllib2.URLError("rsync directory URI not allowed")
    t = "/tmp/rrdp-fetch-from-tal.%d.%d" % (os.getpid(), self._n)
    self._n += 1
    subprocess.check_call(("rsync", u, t))
    h = mimetools.Message(open("/dev/null"))
    h["Content-type"] = "text/plain"
    h["Content-length"] = str(os.stat(t).st_size)
    f = open(t, "rb")
    os.unlink(t)
    return urllib2.addinfourl(f, h, u)

urllib2.install_opener(urllib2.build_opener(RSyncHandler))


class main(object):

  def __init__(self):
    parser = argparse.ArgumentParser(description = __doc__)
    parser.add_argument("--rcynic-tree", default = "rcynic-data/unauthenticated",
                        help = "directory tree in which to write extracted RPKI objects")
    parser.add_argument("--serial-filename", # default handled later
                        help = "file name in which to store RRDP serial number")
    parser.add_argument("tal", help = "trust anchor locator")
    self.args = parser.parse_args()
    if not os.path.isdir(self.args.rcynic_tree):
      os.makedirs(self.args.rcynic_tree)
    self.urls = set()
    self.ta = self.ta_fetch()
    url = self.ta.get_sia_rrdp_notify()
    if url is None:
      sys.exit("Couldn't get RRDP URI from trust anchor")
    self.rrdp_fetch(url)
    self.write_ta()

  def rrdp_fetch(self, url):
    if url in self.urls:
      print "Already fetched %s, skipping" % url
      return
    self.urls.add(url)
    xml = lxml.etree.ElementTree(file = urllib2.urlopen(url)).getroot()
    rpki.relaxng.rrdp.assertValid(xml)
    if xml.tag[len(rpki.relaxng.rrdp.xmlns):] != "notification":
      sys.exit("Expected notification at %s, found %s" % (url, xml.tag))
    self.prettyprint_notification(xml)

    # We should be checking session_id here, but we're not storing it yet

    old_serial = self.get_serial()
    new_serial = int(xml.get("serial"))
    deltas = dict((int(elt.get("serial")), elt)
                  for elt in xml.iterchildren(tags.delta))
    if old_serial == 0 or not all(serial + 1 in deltas
                                  for serial in xrange(old_serial, new_serial)):
      return self.snapshot_fetch(xml.iterchildren(tags.snapshot).next())
    for serial in sorted(deltas):
      if serial > old_serial:
        self.delta_fetch(deltas[serial])

  def prettyprint_notification(self, xml):
    print "Notification version %s session %s serial %s" % (
      xml.get("version"), xml.get("session_id"), xml.get("serial"))
    elt = xml.iterchildren(tags.snapshot).next()
    print " Snapshot URI %s hash %s" % (
      elt.get("uri"), elt.get("hash"))
    for elt in xml.iterchildren(tags.delta):
      print " Delta %6s URI %s hash %s" % (
        elt.get("serial"), elt.get("uri"), elt.get("hash"))

  def ta_fetch(self):
    with open(self.args.tal, "r") as f:
      tal = f.read()
    uris, key = tal.split("\n\n", 2)
    key = rpki.x509.PublicKey(Base64 = key)
    for uri in uris.split():
      ta = rpki.x509.X509(DER = urllib2.urlopen(uri).read())
      if ta.getPublicKey() == key:
        return ta
      print "TAL key mismatch for certificate", url
    sys.exit("Could not fetch trust anchor")

  @property
  def serial_filename(self):
    return self.args.serial_filename or os.path.join(self.args.rcynic_tree, "serial")

  def get_serial(self):
    try:
      with open(self.serial_filename, "r") as f:
        return int(f.read().strip())
    except:
      return 0

  def set_serial(self, value):
    with open(self.serial_filename, "w") as f:
      f.write("%s\n" % value)

  def uri_to_filename(self, uri):
    assert uri.startswith("rsync://")
    return os.path.join(self.args.rcynic_tree, uri[len("rsync://"):])

  def add_obj(self, uri, obj):
    fn = self.uri_to_filename(uri)
    dn = os.path.dirname(fn)
    if not os.path.isdir(dn):
      os.makedirs(dn)
    with open(fn, "wb") as f:
      f.write(obj)

  def del_obj(self, uri, hash):
    fn = self.uri_to_filename(uri)
    with open(fn, "rb") as f:
      if hash.lower() != rpki.x509.sha256(f.read()).encode("hex"):
        raise RuntimeError("Hash mismatch for URI %s" % uri)
    os.unlink(fn)
    dn = os.path.dirname(fn)
    while True:
      try:
        os.rmdir(dn)
      except OSError:
        break
      else:
        dn = os.path.dirname(dn)

  def xml_fetch(self, elt):
    url = elt.get("uri")
    hash = elt.get("hash").lower()
    print "Fetching", url
    text = urllib2.urlopen(url).read()
    h = rpki.x509.sha256(text).encode("hex")
    if h != hash:
      sys.exit("Bad hash for %s: expected %s got %s" % (url, hash, h))
    xml = lxml.etree.XML(text)
    rpki.relaxng.rrdp.schema.assertValid(xml)
    return xml

  def snapshot_fetch(self, xml):
    xml = self.xml_fetch(xml)
    print "Unpacking snapshot version %s session %s serial %6s" % (
      xml.get("version"), xml.get("session_id"), xml.get("serial"))
    for elt in xml.iterchildren(tags.publish):
      print " ", elt.get("uri")
      self.add_obj(elt.get("uri"), elt.text.decode("base64"))
    self.set_serial(xml.get("serial"))

  def delta_fetch(self, xml):
    xml = self.xml_fetch(xml)
    old_serial = int(self.get_serial())
    new_serial = int(xml.get("serial"))
    print "Unpacking deltas version %s session %s serial %s" % (
      xml.get("version"), xml.get("session_id"), new_serial)
    if old_serial != new_serial - 1:
      raise RuntimeError("Can't apply deltas: old serial %s new serial %s" % (old_serial, new_serial))
    for i, elt in enumerate(xml.iterchildren(tags.withdraw)):
      uri = elt.get("uri")
      hash = elt.get("hash")
      print "  %3d withdraw URI %s hash %s" % (i, uri, hash)
      self.del_obj(uri, hash)
    for i, elt in enumerate(xml.iterchildren(tags.publish)):
      uri = elt.get("uri")
      hash = elt.get("hash", None)
      print "  %3d publish  URI %s hash %s" % (i, uri, hash)
      if hash is not None:
        self.del_obj(uri, hash)
      self.add_obj(elt.get("uri"), elt.text.decode("base64"))
    self.set_serial(new_serial)

  def write_ta(self):
    der = self.ta.get_DER()
    fn = rpki.x509.sha256(der).encode("hex") + ".cer"
    if not os.path.exists(fn):
      print "Writing", fn
      with open(fn, "wb") as f:
        f.write(der)

if __name__ == "__main__":
  main()