[PYTHON-CRYPTO] m2crypto blocking all other threads

Rune Froysa rune.froysa at USIT.UIO.NO
Fri Sep 23 11:22:48 CEST 2005

Heikki Toivonen <heikki at OSAFOUNDATION.ORG> writes:

> Rune Froysa wrote:
> > I'm using m2crypto for a SSL-based xmlrpc service.  This service is
> > frequently DOSed by what appears to be a bug in m2crypto: it blocks
> It seems like this is user error. In a multithreaded application you
> need to initialize M2Crypto for threading. With those changes your
> sample works for me. See below:

Sorry about that.  Is this documented some place?  For some reason,
demo/ssl/https_srv.py works without it (two "openssl s_client -connect
localhost:19443" can connect simultaneously without problems).
However, a "telnet localhost 19443"+<do nothing> first will prevent
any later s_clients from reaching past the "CONNECTED(00000003)"

The threading.init() trick seems to work for my previous example, but
if I add a threading.init() to line 135 (first in __main__) of
demo/ssl/https_srv.py, i get a segfault when a client connects (python
2.3.4) (It works if I actually have created a separate thread):

Starting program: /usr/bin/python https_srv.py
[Thread debugging using libthread_db enabled]
[New Thread -1208785216 (LWP 12122)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1208785216 (LWP 12122)]
0x00dbdb4a in sem_post at GLIBC_2.0 () from /lib/tls/libpthread.so.0
(gdb) where
#0  0x00dbdb4a in sem_post at GLIBC_2.0 () from /lib/tls/libpthread.so.0
#1  0x080cf636 in PyThread_release_lock (lock=0x0) at Python/thread_pthread.h:431
#2  0x080ca519 in PyGILState_Release (oldstate=3080343864) at Python/pystate.c:473
#3  0xb7ad39e8 in ssl_info_callback (s=0x81ef058, where=16, ret=1) at SWIG/_m2crypto.c:1058
#4  0x00b251e2 in ssl23_accept () from /lib/libssl.so.4
#5  0x00b2a093 in SSL_accept () from /lib/libssl.so.4
#6  0xb7ad73b3 in ssl_accept (ssl=0x81ef058) at SWIG/_m2crypto.c:3492
#7  0xb7ae2d23 in _wrap_ssl_accept (self=0x0, args=0xb79b310c) at SWIG/_m2crypto.c:11191
#8  0x080ed9b0 in PyCFunction_Call (func=0xb7b2218c, arg=0xb79b310c, kw=0x1) at Objects/methodobject.c:108
#9  0x080a4f67 in call_function (pp_stack=0xbfffee9c, oparg=135426480) at Python/ceval.c:3439

> > BTW: under 0.15, the https_srv.py complains from line 126 -> SSL/Context.py: 118:
> > TypeError: ssl_ctx_load_verify_locations() argument 3 must be string, not None
> This does not happen for me.

Strange.  Latest version from svn:

/tmp/m2/demo/ssl at dresden >PYTHONPATH=/tmp/m2/build/lib.linux-i686-2.3 python https_srv.py 
Traceback (most recent call last):
  File "https_srv.py", line 141, in ?
  File "https_srv.py", line 126, in init_context
  File "/tmp/m2/build/lib.linux-i686-2.3/M2Crypto/SSL/Context.py", line 121, in load_verify_locations
    return m2.ssl_ctx_load_verify_locations(self.ctx, cafile, capath)
TypeError: ssl_ctx_load_verify_locations() argument 3 must be string, not None

It would be great if there was some way to set a timeout value on
connected clients.  With the below patch, https_srv.py could
instantiate the HTTPS_Server with a default_timeout=SSL.timeout(sec=4)
keyword argument to kill misbehaving clients (like the telnet above).
Could something like this be considered for future inclusion? (I don't
believe there is a standard pythonic way of setting timeout on client

Index: M2Crypto/SSL/SSLServer.py
--- M2Crypto/SSL/SSLServer.py   (revision 319)
+++ M2Crypto/SSL/SSLServer.py   (working copy)
@@ -12,7 +12,7 @@
 class SSLServer(SocketServer.TCPServer):
-    def __init__(self, server_address, RequestHandlerClass, ssl_context):
+    def __init__(self, server_address, RequestHandlerClass, ssl_context, default_timeout=None):
         Superclass says: Constructor. May be extended, do not override.
         This class says: Ho-hum.
@@ -21,6 +21,8 @@
+        if default_timeout is not None:
+            self.socket.set_default_client_timeout(default_timeout)
Index: M2Crypto/SSL/Connection.py
--- M2Crypto/SSL/Connection.py  (revision 319)
+++ M2Crypto/SSL/Connection.py  (working copy)
@@ -38,6 +38,7 @@
             self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         self._fileno = self.socket.fileno()
+        self._default_client_timeout = None
     def __del__(self):
         if getattr(self, 'sslbio', None):
@@ -102,16 +103,27 @@
     def accept_ssl(self):
         return m2.ssl_accept(self.ssl)
+    def set_default_client_timeout(self, timeout):
+        self._default_client_timeout = timeout
     def accept(self):
         """Accept an SSL connection. The return value is a pair (ssl, addr) where
         ssl is a new SSL connection object and addr is the address bound to the
         the other end of the SSL connection."""
         sock, addr = self.socket.accept()
+        if self._default_client_timeout is not None:
+            sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
+                            self._default_client_timeout.pack())
+            sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, 
+                            self._default_client_timeout.pack())
         ssl = Connection(self.ctx, sock)
         ssl.addr = addr
-        ssl.accept_ssl()
+        if ssl.accept_ssl() != 1:
+            raise SSLError(m2.err_reason_error_string(m2.err_get_error()))
         check = getattr(self, 'postConnectionCheck', self.serverPostConnectionCheck)
         if check is not None:
             if not check(self.get_peer_cert(), ssl.addr[0]):

Rune Frøysa

More information about the python-crypto mailing list