aboutsummaryrefslogtreecommitdiff
path: root/rpkid.stable/rpki/https.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid.stable/rpki/https.py')
-rw-r--r--rpkid.stable/rpki/https.py291
1 files changed, 0 insertions, 291 deletions
diff --git a/rpkid.stable/rpki/https.py b/rpkid.stable/rpki/https.py
deleted file mode 100644
index a0443c01..00000000
--- a/rpkid.stable/rpki/https.py
+++ /dev/null
@@ -1,291 +0,0 @@
-"""HTTPS utilities, both client and server.
-
-At the moment this only knows how to use the PEM certs in my
-subversion repository; generalizing it would not be hard, but the more
-general version should use SQL anyway.
-
-$Id$
-
-Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL ARIN 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.
-"""
-
-import httplib, BaseHTTPServer, tlslite.api, glob, traceback, urlparse, socket, signal
-import rpki.x509, rpki.exceptions, rpki.log
-
-# This should be wrapped somewhere in rpki.x509 eventually
-import POW
-
-# Do not set this to True for production use!
-disable_tls_certificate_validation_exceptions = False
-
-# Chatter about TLS certificates
-debug_tls_certs = False
-
-rpki_content_type = "application/x-rpki"
-
-def tlslite_certChain(x509):
- """Utility function to construct tlslite certChains."""
- if isinstance(x509, rpki.x509.X509):
- return tlslite.api.X509CertChain([x509.get_tlslite()])
- else:
- return tlslite.api.X509CertChain([x.get_tlslite() for x in x509])
-
-def build_https_ta_cache(certs):
- """Build a dynamic TLS trust anchor cache."""
-
- store = POW.X509Store()
- for x in certs:
- if rpki.https.debug_tls_certs:
- rpki.log.debug("HTTPS dynamic trusted cert issuer %s [%s] subject %s [%s]" % (x.getIssuer(), x.hAKI(), x.getSubject(), x.hSKI()))
- store.addTrust(x.get_POW())
- return store
-
-class Checker(tlslite.api.Checker):
- """Derived class to handle X.509 client certificate checking."""
-
- ## @var refuse_tls_ca_certs
- # Raise an exception upon receiving CA certificates via TLS rather
- # than just quietly ignoring them.
-
- refuse_tls_ca_certs = False
-
- ## @var pem_dump_tls_certs
- # Vile debugging hack
-
- pem_dump_tls_certs = False
-
- def __init__(self, trust_anchor = None, dynamic_https_trust_anchor = None):
- """Initialize our modified certificate checker."""
-
- self.dynamic_https_trust_anchor = dynamic_https_trust_anchor
-
- if dynamic_https_trust_anchor is not None:
- return
-
- self.x509store = POW.X509Store()
-
- trust_anchor = rpki.x509.X509.normalize_chain(trust_anchor)
- assert trust_anchor
-
- for x in trust_anchor:
- if debug_tls_certs:
- rpki.log.debug("HTTPS trusted cert issuer %s [%s] subject %s [%s]" % (x.getIssuer(), x.hAKI(), x.getSubject(), x.hSKI()))
- self.x509store.addTrust(x.get_POW())
- if self.pem_dump_tls_certs:
- print x.get_PEM()
-
- def x509store_thunk(self):
- if self.dynamic_https_trust_anchor is not None:
- return self.dynamic_https_trust_anchor()
- else:
- return self.x509store
-
- def __call__(self, tlsConnection):
- """POW/OpenSSL-based certificate checker.
-
- Given our BPKI model, we're only interested in the TLS EE
- certificates.
- """
-
- if tlsConnection._client:
- chain = tlsConnection.session.serverCertChain
- peer = "server"
- else:
- chain = tlsConnection.session.clientCertChain
- peer = "client"
-
- chain = [rpki.x509.X509(tlslite = chain.x509List[i]) for i in range(chain.getNumCerts())]
-
- ee = None
-
- for x in chain:
-
- if debug_tls_certs:
- rpki.log.debug("Received %s TLS %s cert issuer %s [%s] subject %s [%s]"
- % (peer, "CA" if x.is_CA() else "EE", x.getIssuer(), x.hAKI(), x.getSubject(), x.hSKI()))
- if self.pem_dump_tls_certs:
- print x.get_PEM()
-
- if x.is_CA():
- if self.refuse_tls_ca_certs:
- raise rpki.exceptions.ReceivedTLSCACert
- continue
-
- if ee is not None:
- raise rpki.exceptions.MultipleTLSEECert, chain
- ee = x
-
- result = self.x509store_thunk().verifyDetailed(ee.get_POW())
- if not result[0]:
- rpki.log.debug("TLS certificate validation result %s" % repr(result))
- if disable_tls_certificate_validation_exceptions:
- rpki.log.warn("DANGER WILL ROBINSON! IGNORING TLS VALIDATION FAILURE!")
- else:
- raise rpki.exceptions.TLSValidationError
-
-class httpsClient(tlslite.api.HTTPTLSConnection):
- """Derived class to let us replace the default Checker."""
-
- def __init__(self, host, port = None,
- client_cert = None, client_key = None,
- server_ta = None, settings = None):
- """Create a new httpsClient."""
-
- tlslite.api.HTTPTLSConnection.__init__(
- self, host = host, port = port, settings = settings,
- certChain = client_cert, privateKey = client_key)
-
- self.checker = Checker(trust_anchor = server_ta)
-
-def client(msg, client_key, client_cert, server_ta, url, timeout = 300):
- """Open client HTTPS connection, send a message, wait for response.
-
- This function wraps most of what one needs to do to send a message
- over HTTPS and get a response. The certificate checking isn't quite
- up to snuff; it's better than with the other packages I've found,
- but doesn't appear to handle subjectAltName extensions (sigh).
- """
-
- u = urlparse.urlparse(url)
-
- assert u.scheme in ("", "https") and \
- u.username is None and \
- u.password is None and \
- u.params == "" and \
- u.query == "" and \
- u.fragment == ""
-
- rpki.log.debug("Contacting %s" % url)
-
- if debug_tls_certs:
- for cert in (client_cert,) if isinstance(client_cert, rpki.x509.X509) else client_cert:
- rpki.log.debug("Sending client TLS cert issuer %s subject %s" % (cert.getIssuer(), cert.getSubject()))
-
- # We could add a "settings = foo" argument to the following call to
- # pass in a tlslite.HandshakeSettings object that would let us
- # insist on, eg, particular SSL/TLS versions.
-
- httpc = httpsClient(host = u.hostname or "localhost",
- port = u.port or 443,
- client_key = client_key.get_tlslite(),
- client_cert = tlslite_certChain(client_cert),
- server_ta = server_ta)
- httpc.connect()
- httpc.sock.settimeout(timeout)
- httpc.request("POST", u.path, msg, {"Content-Type" : rpki_content_type})
- response = httpc.getresponse()
- if response.status == httplib.OK:
- return response.read()
- else:
- r = response.read()
- raise rpki.exceptions.HTTPRequestFailed, \
- "HTTP request failed with status %s, response %s" % (response.status, r)
-
-class requestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- """Derived type to supply POST handler and override logging."""
-
- rpki_handlers = None # Subclass must bind
-
- def rpki_find_handler(self):
- """Helper method to search self.rpki_handlers."""
- for s,h in self.rpki_handlers:
- if self.path.startswith(s):
- return h
- return None
-
- def do_POST(self):
- """POST handler."""
- try:
- handler = self.rpki_find_handler()
- if self.headers["Content-Type"] != rpki_content_type:
- rcode, rtext = 415, "Received Content-Type %s, expected %s" \
- % (self.headers["Content-Type"], rpki_content_type)
- elif handler is None:
- rcode, rtext = 404, "No handler found for URL " + self.path
- else:
- rcode, rtext = handler(query = self.rfile.read(int(self.headers["Content-Length"])),
- path = self.path)
- except Exception, edata:
- rpki.log.error(traceback.format_exc())
- rcode, rtext = 500, "Unhandled exception %s" % edata
- self.send_response(rcode)
- self.send_header("Content-Type", rpki_content_type)
- self.end_headers()
- self.wfile.write(rtext)
-
- def log_message(self, format, *args):
- """Redirect HTTP server logging into our own logging system."""
- if args:
- rpki.log.info(format % args)
- else:
- rpki.log.info(format)
-
-class httpsServer(tlslite.api.TLSSocketServerMixIn, BaseHTTPServer.HTTPServer):
- """Derived type to handle TLS aspects of HTTPS."""
-
- rpki_sessionCache = None
- rpki_server_key = None
- rpki_server_cert = None
- rpki_checker = None
-
- def handshake(self, tlsConnection):
- """TLS handshake handler."""
- assert self.rpki_server_cert is not None
- assert self.rpki_server_key is not None
- assert self.rpki_sessionCache is not None
-
- try:
- #
- # We could add a "settings = foo" argument to the following call
- # to pass in a tlslite.HandshakeSettings object that would let
- # us insist on, eg, particular SSL/TLS versions.
- #
- tlsConnection.handshakeServer(certChain = self.rpki_server_cert,
- privateKey = self.rpki_server_key,
- sessionCache = self.rpki_sessionCache,
- checker = self.rpki_checker,
- reqCert = True)
- tlsConnection.ignoreAbruptClose = True
- return True
- except (tlslite.api.TLSError, rpki.exceptions.TLSValidationError), error:
- rpki.log.warn("TLS handshake failure: " + str(error))
- return False
-
-def server(handlers, server_key, server_cert, port = 4433, host ="", client_ta = None, dynamic_https_trust_anchor = None, catch_signals = (signal.SIGINT, signal.SIGTERM)):
- """Run an HTTPS server and wait (forever) for connections."""
-
- if not isinstance(handlers, (tuple, list)):
- handlers = (("/", handlers),)
-
- class boundRequestHandler(requestHandler):
- rpki_handlers = handlers
-
- httpd = httpsServer((host, port), boundRequestHandler)
-
- httpd.rpki_server_key = server_key.get_tlslite()
- httpd.rpki_server_cert = tlslite_certChain(server_cert)
- httpd.rpki_sessionCache = tlslite.api.SessionCache()
- httpd.rpki_checker = Checker(trust_anchor = client_ta, dynamic_https_trust_anchor = dynamic_https_trust_anchor)
-
- try:
- def raiseServerShuttingDown(signum, frame):
- raise rpki.exceptions.ServerShuttingDown
- old_signal_handlers = tuple((sig, signal.signal(sig, raiseServerShuttingDown)) for sig in catch_signals)
- httpd.serve_forever()
- except rpki.exceptions.ServerShuttingDown:
- pass
- finally:
- for sig,handler in old_signal_handlers:
- signal.signal(sig, handler)