Commit 381e67a3 authored by David Johnson's avatar David Johnson

Ensure the SSL part of accept() does not block the main thread.

parent e7f73e81
......@@ -30,7 +30,7 @@ import traceback
import syslog
import string
import socket
import SocketServer
import BaseHTTPServer
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
......@@ -47,6 +47,7 @@ from libdb import *
try:
from M2Crypto import SSL
from M2Crypto.SSL import SSLError
from M2Crypto.SSL import Checker
except ImportError, e:
sys.stderr.write("error: The py-m2crypto port is not installed\n")
sys.exit(1)
......@@ -65,6 +66,13 @@ ADDR = "0.0.0.0"
server_cert = "@prefix@/etc/server.pem"
ca_cert = "@prefix@/etc/emulab.pem"
# Set a timeout for the SSL_accept phase of the client session setup.
# This allows us to not block the main thread indefinitely if a non-SSL
# or malicious client connects to us and says nothing.
SSL_CLIENT_ACCEPT_TIMEOUT = 2
# Set a timeout that is used for the client socket *after* SSL accept.
SSL_CLIENT_REQUEST_TIMEOUT = 30
#
# By default, run a wrapper class that includes all off the modules.
# The client can invoke methods of the form experiment.swapexp when
......@@ -157,6 +165,51 @@ class MyXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
if self.server.logRequests:
BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
class MyConnection(SSL.Connection):
"""
A simple subclassing of the native SSL.Connection, because that
class does not give you the ability to set a timeout for the ssl
acceptance of a new client socket, once it has been accept()ed.
"""
def accept(self):
"""
(NB: this comes nearly directly from M2Crypto.SSL.Connection; it
is only slightly modified to allow a timeout during the SSL part
of the accept, once the socket accept has finished. Once that
succeeds, we restore a sane timeout for the client socket,
because we use a forking mixin, so all further work will happen
in the child.
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 other end of the SSL connection.
"""
sock, addr = self.socket.accept()
# Use our subclass, not the default SSL.connection
ssl = MyConnection(self.ctx, sock)
# Set non-blocking so that the M2Crypto openssl wrapper glue
# will honor the _timeout we're about to set; but only if the
# timeout is not None.
if SSL_CLIENT_ACCEPT_TIMEOUT is not None:
sock.setblocking(False)
# Set a timeout that gets used in the below call of ssl.accept_ssl()
ssl._timeout = SSL_CLIENT_ACCEPT_TIMEOUT
ssl.addr = addr
ssl.setup_ssl()
ssl.set_accept_state()
ssl.accept_ssl()
check = getattr(self, 'postConnectionCheck',
self.serverPostConnectionCheck)
if check is not None:
if not check(ssl.get_peer_cert(), ssl.addr[0]):
raise Checker.SSLVerificationError(
'post connection check failed')
# Make the socket blocking again.
sock.setblocking(True)
# ... and undo our timeout.
ssl._timeout = SSL_CLIENT_REQUEST_TIMEOUT
return ssl, addr
#
# A simple server based on the forking version SSLServer. We fork cause
......@@ -183,8 +236,19 @@ class MyServer(SSL.ForkingSSLServer, SimpleXMLRPCDispatcher):
dargs = (self,False,None)
pass
SimpleXMLRPCDispatcher.__init__(*dargs)
SSL.SSLServer.__init__(self, (ADDR, PORT),
MyXMLRPCRequestHandler, ctx)
#
# Ugh -- duplicate the SSL.SSLServer constructor body, other
# than to use our little MyConnection wrapper instead of
# SSLConnection.
#
#SSL.SSLServer.__init__(self, (ADDR, PORT),
# MyXMLRPCRequestHandler, ctx)
SocketServer.BaseServer.__init__(self, (ADDR,PORT),
MyXMLRPCRequestHandler)
self.ssl_ctx = ctx
self.socket = MyConnection(self.ssl_ctx)
self.server_bind()
self.server_activate()
pass
##
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment