[or-cvs] [torflow/master 09/92] SSL socket timeouts using SIGALARM

mikeperry at torproject.org mikeperry at torproject.org
Sat Aug 21 05:13:57 UTC 2010


Author: John M. Schanck <john at anomos.info>
Date: Mon, 21 Jun 2010 01:31:20 -0400
Subject: SSL socket timeouts using SIGALARM
Commit: b8d530322ac64ee3e9794fee64f548d44d1f94ec

---
 NetworkScanners/ExitAuthority/soat.py |   52 ++++++++++++++++++++++-----------
 1 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/NetworkScanners/ExitAuthority/soat.py b/NetworkScanners/ExitAuthority/soat.py
index 33869db..1ddc459 100755
--- a/NetworkScanners/ExitAuthority/soat.py
+++ b/NetworkScanners/ExitAuthority/soat.py
@@ -33,6 +33,7 @@ import os
 import random
 import re
 import sha
+import signal
 import smtplib
 import socket
 import sys
@@ -1717,7 +1718,18 @@ class SSLTest(SearchBasedTest):
   def get_targets(self):
     return self.get_search_urls('https', self.test_hosts, True, search_mode=google_search_mode)
 
-  def ssl_request(self, address, method='TLSv1_METHOD'):
+  def _raise_timeout(self, signum, frame):
+    raise socket.timeout("SSL connection timed out")
+
+  def ssl_request(self, address):
+    # The SIGALARM can be triggered outside of the try/except in
+    # _ssl_request, so we need to catch socket.timeout here
+    try:
+      return self._ssl_request(address)
+    except socket.timeout, e:
+      return (-6.0, None, "Socket timeout")
+
+  def _ssl_request(self, address, method='TLSv1_METHOD'):
     ''' initiate an ssl connection and return the server certificate '''
     address=str(address) # Unicode hostnames not supported..
 
@@ -1725,16 +1737,15 @@ class SSLTest(SearchBasedTest):
     ctx = SSL.Context(getattr(SSL,method))
 
     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    # XXX: This creats a blocking socket with no timeout. Setting a timeout
-    # won't help because we can't differentiate a timeout from an
-    # SSL.WantReadError. An attacker can hang SoaT here by doing:
-    # nc -l -p 443, and waiting for us to connect.
     s.settimeout(None)
 
+    signal.signal(signal.SIGALRM, self._raise_timeout)
     # open an ssl connection
+    rval = (None, None, None)
     try:
       c = SSL.Connection(ctx, s)
       c.set_connect_state()
+      signal.alarm(int(read_timeout)) # raise a timeout after read_timeout
       c.connect((address, 443)) # DNS OK.
       # XXX: A PEM encoded certificate request was a bizarre and fingerprintable
       # thing to send here. All we actually need to do is perform a handshake,
@@ -1742,42 +1753,49 @@ class SSLTest(SearchBasedTest):
       # fingerprintability.
       # c.send(crypto.dump_certificate_request(crypto.FILETYPE_PEM,request))
       c.do_handshake()
-      # return the cert
-      return (0, c.get_peer_certificate(), None)
+      rval = (0, c.get_peer_certificate(), None)
+    except socket.timeout, e:
+      rval = (-6.0, None, "Socket timeout")
     except socks.Socks5Error, e:
       plog('WARN', 'A SOCKS5 error '+str(e.value[0])+' occured for '+address+": "+str(e))
-      return (-float(e.value[0]), None,  e.__class__.__name__+str(e))
+      rval = (-float(e.value[0]), None,  e.__class__.__name__+str(e))
     except crypto.Error, e:
       traceback.print_exc()
-      return (-23.0, None, e.__class__.__name__+str(e))
+      rval = (-23.0, None, e.__class__.__name__+str(e))
     except (SSL.ZeroReturnError, SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError), e:
       # XXX: None of these are really "errors" per se
       traceback.print_exc()
-      return (-666.0, None, e.__class__.__name__+str(e))
+      rval = (-666.0, None, e.__class__.__name__+str(e))
     except SSL.SysCallError, e:
       # Errors on the underlying socket will be caught here.
       if e[0] == -1: # unexpected eof
         # Might be an SSLv2 server, but it's unlikely, let's just call it a CONNERROR
-        return (float(e[0]), None, e[1])
+        rval = (float(e[0]), None, e[1])
       else:
         traceback.print_exc()
-        return (-666.0, None, e.__class__.__name__+str(e))
+        rval = (-666.0, None, e.__class__.__name__+str(e))
     except SSL.Error, e:
+      signal.alarm(0) # Since we might recurse
       for (lib, func, reason) in e.message: # e.message is always list of 3-tuples
         if reason in ('wrong version number','sslv3 alert illegal parameter'):
           # Check if the server supports a different SSL version
           if method == 'TLSv1_METHOD':
             plog('DEBUG','Could not negotiate SSL handshake with %s, retrying with SSLv3_METHOD' % address)
-            return self.ssl_request(address, 'SSLv3_METHOD')
-      plog('WARN', 'An unknown SSL error occured for '+address+': '+str(e))
-      traceback.print_exc()
-      return (-666.0, None,  e.__class__.__name__+str(e))
+            rval = self._ssl_request(address, 'SSLv3_METHOD')
+            break
+      else:
+        plog('WARN', 'An unknown SSL error occured for '+address+': '+str(e))
+        traceback.print_exc()
+        rval = (-666.0, None,  e.__class__.__name__+str(e))
     except KeyboardInterrupt:
+      signal.alarm(0)
       raise
     except Exception, e:
       plog('WARN', 'An unknown SSL error occured for '+address+': '+str(e))
       traceback.print_exc()
-      return (-666.0, None,  e.__class__.__name__+str(e))
+      rval = (-666.0, None,  e.__class__.__name__+str(e))
+    signal.alarm(0)
+    return rval
 
   def get_resolved_ip(self, hostname):
     # XXX: This is some extreme GIL abuse.. It may have race conditions
-- 
1.7.1




More information about the tor-commits mailing list