Skip to content
Snippets Groups Projects
Commit 1e006745 authored by steve-e's avatar steve-e
Browse files

Coding standards and documentation improvements in tls_nb.py

parent 5c02a907
No related branches found
No related tags found
No related merge requests found
......@@ -17,11 +17,9 @@
import socket
from client import PlugIn
from protocol import *
import sys
import os
import errno
import time
import traceback
......@@ -48,25 +46,27 @@
print >> sys.stderr, "PyOpenSSL not found, falling back to Python builtin SSL objects (insecure)."
print >> sys.stderr, "=" * 79
def torf(cond, tv, fv):
if cond: return tv
return fv
def gattr(obj, attr, default=None):
try:
return getattr(obj, attr)
except:
except AttributeError:
return default
class SSLWrapper:
'''
Abstract SSLWrapper base class
'''
class Error(IOError):
def __init__(self, sock=None, exc=None, errno=None, strerror=None, peer=None):
''' Generic SSL Error Wrapper '''
def __init__(self, sock=None, exc=None, errno=None, strerror=None,
peer=None):
self.parent = IOError
errno = errno or gattr(exc, 'errno')
strerror = strerror or gattr(exc, 'strerror') or gattr(exc, 'args')
if not isinstance(strerror, basestring): strerror = repr(strerror)
if not isinstance(strerror, basestring):
strerror = repr(strerror)
self.sock = sock
self.exc = exc
......@@ -97,17 +97,22 @@ def __init__(self, sock=None, exc=None, errno=None, strerror=None, peer=None):
if len(ppeer) == 2 and isinstance(ppeer[0], basestring) \
and isinstance(ppeer[1], int):
self.peer = ppeer
except: pass
except:
pass
def __str__(self):
s = str(self.__class__)
if self.peer: s += " for %s:%d" % self.peer
if self.errno is not None: s += ": [Errno: %d]" % self.errno
if self.strerror: s += " (%s)" % self.strerror
if self.peer:
s += " for %s:%d" % self.peer
if self.errno is not None:
s += ": [Errno: %d]" % self.errno
if self.strerror:
s += " (%s)" % self.strerror
if self.exc_name:
s += ", Caused by %s" % self.exc_name
if self.exc_str:
if self.strerror: s += "(%s)" % self.exc_str
if self.strerror:
s += "(%s)" % self.exc_str
else: s += "(%s)" % str(self.exc_args)
return s
......@@ -117,18 +122,21 @@ def __init__(self, sslobj, sock=None):
log.debug("%s.__init__ called with %s", self.__class__, sslobj)
def recv(self, data, flags=None):
''' Receive wrapper for SSL object
'''
Receive wrapper for SSL object
We can return None out of this function to signal that no data is
available right now. Better than an exception, which differs
depending on which SSL lib we're using. Unfortunately returning ''
can indicate that the socket has been closed, so to be sure, we avoid
this by returning None. '''
this by returning None.
'''
raise NotImplementedError
raise NotImplementedException()
def send(self, data, flags=None, now=False):
''' Send wrapper for SSL object '''
raise NotImplementedError
def send(self, data, flags=None, now = False):
raise NotImplementedException()
class PyOpenSSLWrapper(SSLWrapper):
'''Wrapper class for PyOpenSSL's recv() and send() methods'''
......@@ -138,21 +146,24 @@ def __init__(self, *args):
self.parent.__init__(self, *args)
def is_numtoolarge(self, e):
''' Magic methods don't need documentation '''
t = ('asn1 encoding routines', 'a2d_ASN1_OBJECT', 'first num too large')
return isinstance(e.args, (list, tuple)) and len(e.args) == 1 and \
isinstance(e.args[0], (list, tuple)) and len(e.args[0]) == 2 and \
e.args[0][0] == e.args[0][1] == t
return (isinstance(e.args, (list, tuple)) and len(e.args) == 1 and
isinstance(e.args[0], (list, tuple)) and len(e.args[0]) == 2 and
e.args[0][0] == e.args[0][1] == t)
def recv(self, bufsize, flags=None):
retval = None
try:
if flags is None: retval = self.sslobj.recv(bufsize)
else: retval = self.sslobj.recv(bufsize, flags)
if flags is None:
retval = self.sslobj.recv(bufsize)
else:
retval = self.sslobj.recv(bufsize, flags)
except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e:
log.debug("Recv: Want-error: " + repr(e))
except OpenSSL.SSL.SysCallError, e:
log.debug("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True)
#traceback.print_exc()
log.debug("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e),
exc_info=True)
raise SSLWrapper.Error(self.sock or self.sslobj, e)
except OpenSSL.SSL.Error, e:
if self.is_numtoolarge(e):
......@@ -160,22 +171,21 @@ def recv(self, bufsize, flags=None):
log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)")
else:
log.debug("Recv: Caught OpenSSL.SSL.Error:", exc_info=True)
#traceback.print_exc()
#print "Current Stack:"
#traceback.print_stack()
raise SSLWrapper.Error(self.sock or self.sslobj, e)
return retval
def send(self, data, flags=None, now = False):
def send(self, data, flags=None, now=False):
try:
if flags is None: return self.sslobj.send(data)
else: return self.sslobj.send(data, flags)
if flags is None:
return self.sslobj.send(data)
else:
return self.sslobj.send(data, flags)
except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e:
#log.debug("Send: " + repr(e))
time.sleep(0.1) # prevent 100% CPU usage
except OpenSSL.SSL.SysCallError, e:
log.error("Send: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True)
#traceback.print_exc()
log.error("Send: Got OpenSSL.SSL.SysCallError: " + repr(e),
exc_info=True)
raise SSLWrapper.Error(self.sock or self.sslobj, e)
except OpenSSL.SSL.Error, e:
if self.is_numtoolarge(e):
......@@ -183,12 +193,10 @@ def send(self, data, flags=None, now = False):
log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)")
else:
log.error("Send: Caught OpenSSL.SSL.Error:", exc_info=True)
#traceback.print_exc()
#print "Current Stack:"
#traceback.print_stack()
raise SSLWrapper.Error(self.sock or self.sslobj, e)
return 0
class StdlibSSLWrapper(SSLWrapper):
'''Wrapper class for Python socket.ssl read() and write() methods'''
......@@ -218,7 +226,12 @@ def send(self, data, flags=None, now = False):
class NonBlockingTLS(PlugIn):
''' TLS connection used to encrypts already estabilished tcp connection.'''
'''
TLS connection used to encrypts already estabilished tcp connection.
Can be plugged into NonBlockingTCP and will make use of StdlibSSLWrapper or
PyOpenSSLWrapper.
'''
def __init__(self, cacerts, mycerts):
'''
......@@ -238,7 +251,8 @@ def __init__(self, cacerts, mycerts):
def PlugIn(self, owner):
'''
start using encryption immediately
Use to PlugIn TLS into transport and start establishing immediately
Returns True if TLS/SSL was established correctly, otherwise False.
'''
log.info('Starting TLS estabilishing')
PlugIn.PlugIn(self, owner)
......@@ -249,14 +263,12 @@ def PlugIn(self, owner):
return False
return res
def _dumpX509(self, cert, stream=sys.stderr):
print >> stream, "Digest (SHA-1):", cert.digest("sha1")
print >> stream, "Digest (MD5):", cert.digest("md5")
print >> stream, "Serial #:", cert.get_serial_number()
print >> stream, "Version:", cert.get_version()
print >> stream, "Expired:", torf(cert.has_expired(), "Yes", "No")
print >> stream, "Expired:", ("Yes" if cert.has_expired() else "No")
print >> stream, "Subject:"
self._dumpX509Name(cert.get_subject(), stream)
print >> stream, "Issuer:"
......@@ -267,23 +279,51 @@ def _dumpX509Name(self, name, stream=sys.stderr):
print >> stream, "X509Name:", str(name)
def _dumpPKey(self, pkey, stream=sys.stderr):
typedict = {OpenSSL.crypto.TYPE_RSA: "RSA", OpenSSL.crypto.TYPE_DSA: "DSA"}
typedict = {OpenSSL.crypto.TYPE_RSA: "RSA",
OpenSSL.crypto.TYPE_DSA: "DSA"}
print >> stream, "PKey bits:", pkey.bits()
print >> stream, "PKey type: %s (%d)" % (typedict.get(pkey.type(), "Unknown"), pkey.type())
print >> stream, "PKey type: %s (%d)" % (typedict.get(pkey.type(),
"Unknown"), pkey.type())
def _startSSL(self):
''' Immediatedly switch socket to TLS mode. Used internally.'''
log.debug("_startSSL called")
if USE_PYOPENSSL: result = self._startSSL_pyOpenSSL()
else: result = self._startSSL_stdlib()
if USE_PYOPENSSL:
result = self._startSSL_pyOpenSSL()
else:
result = self._startSSL_stdlib()
if result:
log.debug("Synchronous handshake completed")
log.debug('Synchronous handshake completed')
return True
else:
return False
def _load_user_certs(self, cert_path, cert_store):
if not os.path.isfile(cert_path):
return
f = open(cert_path)
lines = f.readlines()
i = 0
begin = -1
for line in lines:
if 'BEGIN CERTIFICATE' in line:
begin = i
elif 'END CERTIFICATE' in line and begin > -1:
cert = ''.join(lines[begin:i+2])
try:
x509cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
cert_store.add_cert(x509cert)
except OpenSSL.crypto.Error, exception_obj:
log.warning('Unable to load a certificate from file %s: %s' %\
(self.mycerts, exception_obj.args[0][0][2]))
except:
log.warning('Unknown error while loading certificate from file%s'
% self.mycerts)
begin = -1
i += 1
def _startSSL_pyOpenSSL(self):
log.debug("_startSSL_pyOpenSSL called")
......@@ -292,39 +332,18 @@ def _startSSL_pyOpenSSL(self):
#tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
tcpsock.ssl_errnum = 0
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback)
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER,
self._ssl_verify_callback)
try:
tcpsock._sslContext.load_verify_locations(self.cacerts)
except:
log.warning('Unable to load SSL certificates from file %s' % \
os.path.abspath(self.cacerts))
# load users certs
if os.path.isfile(self.mycerts):
store = tcpsock._sslContext.get_cert_store()
f = open(self.mycerts)
lines = f.readlines()
i = 0
begin = -1
for line in lines:
if 'BEGIN CERTIFICATE' in line:
begin = i
elif 'END CERTIFICATE' in line and begin > -1:
cert = ''.join(lines[begin:i+2])
try:
X509cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
store.add_cert(X509cert)
except OpenSSL.crypto.Error, exception_obj:
log.warning('Unable to load a certificate from file %s: %s' %\
(self.mycerts, exception_obj.args[0][0][2]))
except:
log.warning('Unknown error while loading certificate from file %s' %\
self.mycerts)
begin = -1
i += 1
tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock)
tcpsock._sslObj.set_connect_state() # set to client mode
self._load_user_certs(self.mycerts, tcpsock._sslContext.get_cert_store())
tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext,
tcpsock._sock)
tcpsock._sslObj.set_connect_state() # set to client mode
wrapper = PyOpenSSLWrapper(tcpsock._sslObj)
tcpsock._recv = wrapper.recv
tcpsock._send = wrapper.send
......@@ -340,7 +359,6 @@ def _startSSL_pyOpenSSL(self):
self._owner.ssl_lib = PYOPENSSL
return True
def _startSSL_stdlib(self):
log.debug("_startSSL_stdlib called")
tcpsock=self._owner
......@@ -371,5 +389,7 @@ def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok):
return True
except:
log.error("Exception caught in _ssl_info_callback:", exc_info=True)
traceback.print_exc() # Make sure something is printed, even if log is disabled.
# Make sure something is printed, even if log is disabled.
traceback.print_exc()
# vim: se ts=3:
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment