summaryrefslogtreecommitdiffstats
path: root/func
diff options
context:
space:
mode:
authorSeth Vidal <skvidal@fedoraproject.org>2007-09-25 10:27:06 -0400
committerSeth Vidal <skvidal@fedoraproject.org>2007-09-25 10:27:06 -0400
commitfc335d72bd444bc899b07042b7c11c814259f188 (patch)
tree3ba23a23ae5c1698659a3bcae5c511739035d3b2 /func
parent7d543b6cf1d721c2e9f7ed88a230172870a9f558 (diff)
downloadthird_party-func-fc335d72bd444bc899b07042b7c11c814259f188.tar.gz
third_party-func-fc335d72bd444bc899b07042b7c11c814259f188.tar.xz
third_party-func-fc335d72bd444bc899b07042b7c11c814259f188.zip
- all the class bits to make the authssl xmlrpc server work - most of it copied and modified from plague
- modifications to setup.py to behave with func dir for python sitelib
Diffstat (limited to 'func')
-rw-r--r--func/CommonErrors.py62
-rw-r--r--func/SSLCommon.py122
-rw-r--r--func/SSLConnection.py161
3 files changed, 345 insertions, 0 deletions
diff --git a/func/CommonErrors.py b/func/CommonErrors.py
new file mode 100644
index 0000000..13ef505
--- /dev/null
+++ b/func/CommonErrors.py
@@ -0,0 +1,62 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2005 Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+
+import socket
+
+
+def canIgnoreSSLError(e):
+ """
+ Identify common network errors that mean we cannot connect to the server
+ """
+
+ # This is a bit complicated by the fact that different versions of
+ # M2Crypto & OpenSSL seem to return different error codes for the
+ # same type of error
+ s = "%s" % e
+ if e[0] == 104: # Connection refused
+ return True
+ elif e[0] == 111: # Connection reset by peer
+ return True
+ elif e[0] == 61: # Connection refused
+ return True
+ elif e[0] == 54: # Connection reset by peer
+ return True
+ elif s == "no certificate returned":
+ return True
+ elif s == "wrong version number":
+ return True
+ elif s == "unexpected eof":
+ return True
+
+ return False
+
+
+def canIgnoreSocketError(e):
+ """
+ Identify common network errors that mean we cannot connect to the server
+ """
+
+ try:
+ if e[0] == 111: # Connection refused
+ return True
+ elif e[0] == 104: # Connection reset by peer
+ return True
+ elif e[0] == 61: # Connection refused
+ return True
+ except IndexError:
+ return True
+
+ return False
diff --git a/func/SSLCommon.py b/func/SSLCommon.py
new file mode 100644
index 0000000..bdfd719
--- /dev/null
+++ b/func/SSLCommon.py
@@ -0,0 +1,122 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2005 Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+
+import os, sys
+import CommonErrors
+from OpenSSL import SSL
+import SSLConnection
+import httplib
+import socket
+import SocketServer
+
+def our_verify(connection, x509, errNum, errDepth, preverifyOK):
+ # print "Verify: errNum = %s, errDepth = %s, preverifyOK = %s" % (errNum, errDepth, preverifyOK)
+
+ # preverifyOK should tell us whether or not the client's certificate
+ # correctly authenticates against the CA chain
+ return preverifyOK
+
+
+def CreateSSLContext(pkey, cert, ca_cert):
+ for f in pkey, cert, ca_cert:
+ if f and not os.access(f, os.R_OK):
+ print "%s does not exist or is not readable." % f
+ os._exit(1)
+
+ ctx = SSL.Context(SSL.SSLv3_METHOD) # SSLv3 only
+ ctx.use_certificate_file(cert)
+ ctx.use_privatekey_file(pkey)
+ ctx.load_client_ca(ca_cert)
+ ctx.load_verify_locations(ca_cert)
+ verify = SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT
+ ctx.set_verify(verify, our_verify)
+ ctx.set_verify_depth(10)
+ ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_TLSv1)
+ return ctx
+
+
+
+class BaseServer(SocketServer.TCPServer):
+ allow_reuse_address = 1
+
+ def __init__(self, server_addr, req_handler):
+ self._quit = False
+ self.allow_reuse_address = 1
+ SocketServer.TCPServer.__init__(self, server_addr, req_handler)
+
+ def stop(self):
+ self._quit = True
+
+ def serve_forever(self):
+ while not self._quit:
+ self.handle_request()
+ self.server_close()
+
+
+class BaseSSLServer(BaseServer):
+ """ SSL-enabled variant """
+
+ def __init__(self, server_address, req_handler, pkey, cert, ca_cert, timeout=None):
+ self._timeout = timeout
+ self.ssl_ctx = CreateSSLContext(pkey, cert, ca_cert)
+
+ BaseServer.__init__(self, server_address, req_handler)
+
+ sock = socket.socket(self.address_family, self.socket_type)
+ con = SSL.Connection(self.ssl_ctx, sock)
+ self.socket = SSLConnection.SSLConnection(con)
+ if sys.version_info[:3] >= (2, 3, 0):
+ self.socket.settimeout(self._timeout)
+ self.server_bind()
+ self.server_activate()
+
+ host, port = self.socket.getsockname()[:2]
+ self.server_name = socket.getfqdn(host)
+ self.server_port = port
+
+
+class HTTPSConnection(httplib.HTTPConnection):
+ "This class allows communication via SSL."
+
+ response_class = httplib.HTTPResponse
+
+ def __init__(self, host, port=None, ssl_context=None, strict=None, timeout=None):
+ httplib.HTTPConnection.__init__(self, host, port, strict)
+ self.ssl_ctx = ssl_context
+ self._timeout = timeout
+
+ def connect(self):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ con = SSL.Connection(self.ssl_ctx, sock)
+ self.sock = SSLConnection.SSLConnection(con)
+ if sys.version_info[:3] >= (2, 3, 0):
+ self.sock.settimeout(self._timeout)
+ self.sock.connect((self.host, self.port))
+
+
+class HTTPS(httplib.HTTP):
+ """Compatibility with 1.5 httplib interface
+
+ Python 1.5.2 did not have an HTTPS class, but it defined an
+ interface for sending http requests that is also useful for
+ https.
+ """
+
+ _connection_class = HTTPSConnection
+
+ def __init__(self, host='', port=None, ssl_context=None, strict=None, timeout=None):
+ self._setup(self._connection_class(host, port, ssl_context, strict, timeout))
+
diff --git a/func/SSLConnection.py b/func/SSLConnection.py
new file mode 100644
index 0000000..47529b4
--- /dev/null
+++ b/func/SSLConnection.py
@@ -0,0 +1,161 @@
+#!/usr/bin/python
+#
+# Higher-level SSL objects used by rpclib
+#
+# Copyright (c) 2002 Red Hat, Inc.
+#
+# Author: Mihai Ibanescu <misa@redhat.com>
+# Modifications by Dan Williams <dcbw@redhat.com>
+
+
+from OpenSSL import SSL, crypto
+import os, string, time, socket, select
+
+
+class SSLConnection:
+ """
+ This whole class exists just to filter out a parameter
+ passed in to the shutdown() method in SimpleXMLRPC.doPOST()
+ """
+
+ DEFAULT_TIMEOUT = 20
+
+ def __init__(self, conn):
+ """
+ Connection is not yet a new-style class,
+ so I'm making a proxy instead of subclassing.
+ """
+ self.__dict__["conn"] = conn
+ self.__dict__["close_refcount"] = 0
+ self.__dict__["closed"] = False
+ self.__dict__["timeout"] = self.DEFAULT_TIMEOUT
+
+ def __del__(self):
+ self.__dict__["conn"].close()
+
+ def __getattr__(self,name):
+ return getattr(self.__dict__["conn"], name)
+
+ def __setattr__(self,name, value):
+ setattr(self.__dict__["conn"], name, value)
+
+ def settimeout(self, timeout):
+ if timeout == None:
+ self.__dict__["timeout"] = self.DEFAULT_TIMEOUT
+ else:
+ self.__dict__["timeout"] = timeout
+ self.__dict__["conn"].settimeout(timeout)
+
+ def shutdown(self, how=1):
+ """
+ SimpleXMLRpcServer.doPOST calls shutdown(1),
+ and Connection.shutdown() doesn't take
+ an argument. So we just discard the argument.
+ """
+ self.__dict__["conn"].shutdown()
+
+ def accept(self):
+ """
+ This is the other part of the shutdown() workaround.
+ Since servers create new sockets, we have to infect
+ them with our magic. :)
+ """
+ c, a = self.__dict__["conn"].accept()
+ return (SSLConnection(c), a)
+
+ def makefile(self, mode, bufsize):
+ """
+ We need to use socket._fileobject Because SSL.Connection
+ doesn't have a 'dup'. Not exactly sure WHY this is, but
+ this is backed up by comments in socket.py and SSL/connection.c
+
+ Since httplib.HTTPSResponse/HTTPConnection depend on the
+ socket being duplicated when they close it, we refcount the
+ socket object and don't actually close until its count is 0.
+ """
+ self.__dict__["close_refcount"] = self.__dict__["close_refcount"] + 1
+ return PlgFileObject(self, mode, bufsize)
+
+ def close(self):
+ if self.__dict__["closed"]:
+ return
+ self.__dict__["close_refcount"] = self.__dict__["close_refcount"] - 1
+ if self.__dict__["close_refcount"] == 0:
+ self.shutdown()
+ self.__dict__["conn"].close()
+ self.__dict__["closed"] = True
+
+ def sendall(self, data, flags=0):
+ """
+ - Use select() to simulate a socket timeout without setting the socket
+ to non-blocking mode.
+ - Don't use pyOpenSSL's sendall() either, since it just loops on WantRead
+ or WantWrite, consuming 100% CPU, and never times out.
+ """
+ timeout = self.__dict__["timeout"]
+ con = self.__dict__["conn"]
+ (read, write, excpt) = select.select([], [con], [], timeout)
+ if not con in write:
+ raise socket.timeout((110, "Operation timed out."))
+
+ starttime = time.time()
+ origlen = len(data)
+ sent = -1
+ while len(data):
+ curtime = time.time()
+ if curtime - starttime > timeout:
+ raise socket.timeout((110, "Operation timed out."))
+
+ try:
+ sent = con.send(data, flags)
+ except SSL.SysCallError, e:
+ if e[0] == 32: # Broken Pipe
+ self.close()
+ sent = 0
+ else:
+ raise socket.error(e)
+ except (SSL.WantWriteError, SSL.WantReadError):
+ time.sleep(0.2)
+ continue
+
+ data = data[sent:]
+ return origlen - len(data)
+
+ def recv(self, bufsize, flags=0):
+ """
+ Use select() to simulate a socket timeout without setting the socket
+ to non-blocking mode
+ """
+ timeout = self.__dict__["timeout"]
+ con = self.__dict__["conn"]
+ (read, write, excpt) = select.select([con], [], [], timeout)
+ if not con in read:
+ raise socket.timeout((110, "Operation timed out."))
+
+ starttime = time.time()
+ while True:
+ curtime = time.time()
+ if curtime - starttime > timeout:
+ raise socket.timeout((110, "Operation timed out."))
+
+ try:
+ return con.recv(bufsize, flags)
+ except SSL.ZeroReturnError:
+ return None
+ except SSL.WantReadError:
+ time.sleep(0.2)
+ return None
+
+class PlgFileObject(socket._fileobject):
+ def close(self):
+ """
+ socket._fileobject doesn't actually _close_ the socket,
+ which we want it to do, so we have to override.
+ """
+ try:
+ if self._sock:
+ self.flush()
+ self._sock.close()
+ finally:
+ self._sock = None
+