gerv%gerv.net 760d40497b Bug 716563 - update license to MPL 2. r=rrelyea.
git-svn-id: svn://10.0.0.236/trunk@263784 18797224-902f-48f8-a5cc-f745e15eee43
2012-05-03 09:43:22 +00:00

324 lines
11 KiB
Python
Executable File

#!/usr/bin/python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import sys
import errno
import getopt
import urlparse
import httplib
import getpass
import logging
from nss.error import NSPRError
import nss.io as io
import nss.nss as nss
import nss.ssl as ssl
#------------------------------------------------------------------------------
httplib_debug_level = 0
logging_debug_level = logging.INFO
certdir = 'pki'
password = ''
nickname = ''
url = 'https://sourceforge.net/projects/python'
use_ssl = True
use_connection_class = True
timeout_secs = 3
#------------------------------------------------------------------------------
def auth_certificate_callback(sock, check_sig, is_server, certdb):
cert_is_valid = False
cert = sock.get_peer_certificate()
logging.debug("auth_certificate_callback: check_sig=%s is_server=%s\n%s",
check_sig, is_server, str(cert))
pin_args = sock.get_pkcs11_pin_arg()
if pin_args is None:
pin_args = ()
# Define how the cert is being used based upon the is_server flag. This may
# seem backwards, but isn't. If we're a server we're trying to validate a
# client cert. If we're a client we're trying to validate a server cert.
if is_server:
intended_usage = nss.certificateUsageSSLClient
else:
intended_usage = nss.certificateUsageSSLServer
try:
# If the cert fails validation it will raise an exception, the errno attribute
# will be set to the error code matching the reason why the validation failed
# and the strerror attribute will contain a string describing the reason.
approved_usage = cert.verify_now(certdb, check_sig, intended_usage, *pin_args)
except Exception, e:
logging.error('cert validation failed for "%s" (%s)', cert.subject, e.strerror)
cert_is_valid = False
return cert_is_valid
logging.debug("approved_usage = %s intended_usage = %s",
', '.join(nss.cert_usage_flags(approved_usage)),
', '.join(nss.cert_usage_flags(intended_usage)))
# Is the intended usage a proper subset of the approved usage
if approved_usage & intended_usage:
cert_is_valid = True
else:
cert_is_valid = False
# If this is a server, we're finished
if is_server or not cert_is_valid:
logging.debug('cert valid %s for "%s"', cert_is_valid, cert.subject)
return cert_is_valid
# Certificate is OK. Since this is the client side of an SSL
# connection, we need to verify that the name field in the cert
# matches the desired hostname. This is our defense against
# man-in-the-middle attacks.
hostname = sock.get_hostname()
try:
# If the cert fails validation it will raise an exception
cert_is_valid = cert.verify_hostname(hostname)
except Exception, e:
logging.error('failed verifying socket hostname "%s" matches cert subject "%s" (%s)',
hostname, cert.subject, e.strerror)
cert_is_valid = False
return cert_is_valid
logging.debug('cert valid %s for "%s"', cert_is_valid, cert.subject)
return cert_is_valid
def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
cert = None
if chosen_nickname:
try:
cert = nss.find_cert_from_nickname(chosen_nickname, password)
priv_key = nss.find_key_by_any_cert(cert, password)
return cert, priv_key
except NSPRError:
return False
else:
nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER)
for nickname in nicknames:
try:
cert = nss.find_cert_from_nickname(nickname, password)
if cert.check_valid_times():
if cert.has_signer_in_ca_names(ca_names):
priv_key = nss.find_key_by_any_cert(cert, password)
return cert, priv_key
except NSPRError:
return False
return False
def password_callback(slot, retry, password):
if not retry and password: return password
return getpass.getpass("Enter password for %s: " % slot.token_name);
def handshake_callback(sock):
"""
Verify callback. If we get here then the certificate is ok.
"""
logging.debug("handshake complete, peer = %s", sock.get_peer_name())
pass
class NSSConnection(httplib.HTTPConnection):
default_port = httplib.HTTPSConnection.default_port
def __init__(self, host, port=None, strict=None, dbdir=None):
httplib.HTTPConnection.__init__(self, host, port, strict)
if not dbdir:
raise RuntimeError("dbdir is required")
logging.debug('%s init %s', self.__class__.__name__, host)
if not nss.nss_is_initialized(): nss.nss_init(dbdir)
self.sock = None
ssl.set_domestic_policy()
nss.set_password_callback(password_callback)
def _create_socket(self, family):
self.sock = ssl.SSLSocket(family)
self.sock.set_ssl_option(ssl.SSL_SECURITY, True)
self.sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True)
self.sock.set_hostname(self.host)
# Provide a callback which notifies us when the SSL handshake is complete
self.sock.set_handshake_callback(handshake_callback)
# Provide a callback to verify the servers certificate
self.sock.set_auth_certificate_callback(auth_certificate_callback,
nss.get_default_certdb())
def connect(self):
logging.debug("connect: host=%s port=%s", self.host, self.port)
try:
addr_info = io.AddrInfo(self.host)
except Exception, e:
logging.error("could not resolve host address \"%s\"", self.host)
raise
for net_addr in addr_info:
net_addr.port = self.port
self._create_socket(net_addr.family)
try:
logging.debug("try connect: %s", net_addr)
self.sock.connect(net_addr, timeout=io.seconds_to_interval(timeout_secs))
logging.debug("connected to: %s", net_addr)
return
except Exception, e:
logging.debug("connect failed: %s (%s)", net_addr, e)
raise IOError(errno.ENOTCONN, "could not connect to %s at port %d" % (self.host, self.port))
class NSPRConnection(httplib.HTTPConnection):
default_port = httplib.HTTPConnection.default_port
def __init__(self, host, port=None, strict=None):
httplib.HTTPConnection.__init__(self, host, port, strict)
logging.debug('%s init %s', self.__class__.__name__, host)
if not nss.nss_is_initialized(): nss.nss_init_nodb()
self.sock = None
def connect(self):
logging.debug("connect: host=%s port=%s", self.host, self.port)
try:
addr_info = io.AddrInfo(self.host)
except Exception, e:
logging.error("could not resolve host address \"%s\"", self.host)
raise
for net_addr in addr_info:
net_addr.port = self.port
self.sock = io.Socket(net_addr.family)
try:
logging.debug("try connect: %s", net_addr)
self.sock.connect(net_addr, timeout=io.seconds_to_interval(timeout_secs))
logging.debug("connected to: %s", net_addr)
return
except Exception, e:
logging.debug("connect failed: %s (%s)", net_addr, e)
raise IOError(errno.ENOTCONN, "could not connect to %s at port %d" % (self.host, self.port))
class NSSHTTPS(httplib.HTTP):
_http_vsn = 11
_http_vsn_str = 'HTTP/1.1'
_connection_class = NSSConnection
def __init__(self, host='', port=None, strict=None, dbdir=None):
# provide a default host, pass the X509 cert info
# urf. compensate for bad input.
if port == 0:
port = None
self._setup(self._connection_class(host, port, strict, dbdir=dbdir))
class NSPRHTTP(httplib.HTTP):
_http_vsn = 11
_http_vsn_str = 'HTTP/1.1'
_connection_class = NSPRConnection
#------------------------------------------------------------------------------
opts, args = getopt.getopt(sys.argv[1:],
'Dd:n:w:sScC',
['debuglevel','certdir=','nickname=','password=',
'use-ssl', 'no-ssl', 'use-connection-class', 'no-connection-class'])
for o, a in opts:
if o in('-D', '--httplib_debug_level'):
httplib_debug_level = httplib_debug_level + 1
elif o in ("-d", "--certdir"):
certdir = a
elif o in ("-n", "--nickname"):
nickname = a
elif o in ("-w", "--password"):
password = a
elif o in ("-s", "--use-ssl"):
use_ssl = True
elif o in ("-S", "--no-ssl"):
use_ssl = False
elif o in ("-c", "--use-connection-class"):
use_connection_class = True
elif o in ("-C", "--no-connection-class"):
use_connection_class = False
if len(args) > 0:
url = args[0]
if httplib_debug_level > 0:
logging_debug_level = logging.DEBUG
else:
logging_debug_level = logging.INFO
logging.basicConfig(level=logging_debug_level,
format='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M')
# Perform basic configuration and setup
url_components = urlparse.urlsplit(url)
if use_ssl:
url_components.schema = 'https'
else:
url_components.schema = 'http'
url = urlparse.urlunsplit(url_components)
url_components = urlparse.urlsplit(url)
if not url_components.scheme or not url_components.netloc:
print "ERROR: bad url \"%s\"" % (url)
sys.exit(1)
if use_connection_class:
if use_ssl:
logging.info("Start (using NSSConnection class) %s", url)
conn = NSSConnection(url_components.netloc, 443, dbdir="/etc/pki/nssdb")
else:
logging.info("Start (using NSPRConnection class) %s", url)
conn = NSPRConnection(url_components.netloc, 80)
conn.set_debuglevel(httplib_debug_level)
conn.connect()
conn.request("GET", "/")
response = conn.getresponse()
print "status = %s %s" % (response.status, response.reason)
headers = response.getheaders()
print "headers:"
for header in headers:
print "%s: %s" % (header[0], header[1])
content_length = int(response.getheader('content-length'))
data = response.read()
assert(content_length == len(data))
print data
conn.close()
else:
if use_ssl:
logging.info("Start (using NSSHTTPS class) %s", url)
h = NSSHTTPS(url_components.netloc, 443, dbdir="/etc/pki/nssdb")
else:
logging.info("Start (using NSPRHTTP class) %s", url)
h = NSPRHTTP(url_components.netloc, 80)
h.set_debuglevel(httplib_debug_level)
h.connect()
h.putrequest('GET', '/')
h.endheaders()
http_status, http_reason, headers = h.getreply()
print "status = %s %s" % (http_status, http_reason)
print "headers:\n%s" % headers
content_length = int(headers['content-length'])
f = h.getfile()
data = f.read() # Get the raw HTML
assert(content_length == len(data))
f.close()
print data