diff options
-rw-r--r-- | ipalib/rpc.py | 11 | ||||
-rw-r--r-- | ipapython/ipasslfile.py | 179 | ||||
-rw-r--r-- | ipapython/nsslib.py | 11 |
3 files changed, 197 insertions, 4 deletions
diff --git a/ipalib/rpc.py b/ipalib/rpc.py index 48ac16b5a..6ac0cfa83 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -45,6 +45,13 @@ from ipapython import ipautil from OpenSSL import SSL import httplib +try: + from httplib import SSLFile + from httplib import FakeSocket +except ImportError: + from ipapython.ipasslfile import SSLFile + from ipapython.ipasslfile import FakeSocket + # Some Kerberos error definitions from krb5.h KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = (-1765328377L) KRB5KRB_AP_ERR_TKT_EXPIRED = (-1765328352L) @@ -195,7 +202,7 @@ class SSLTransport(Transport): return SSLSocket(host, None, **(x509 or {})) -class SSLFile(httplib.SSLFile): +class SSLFile(SSLFile): """ Override the _read method so we can handle PyOpenSSL errors gracefully. @@ -227,7 +234,7 @@ class SSLFile(httplib.SSLFile): return buf -class FakeSocket(httplib.FakeSocket): +class FakeSocket(FakeSocket): """ Override this class so we can end up using our own SSLFile implementation. diff --git a/ipapython/ipasslfile.py b/ipapython/ipasslfile.py new file mode 100644 index 000000000..5525ab9b5 --- /dev/null +++ b/ipapython/ipasslfile.py @@ -0,0 +1,179 @@ +# This is a forward backport of the Python2.5 uuid module. It isn't available +# in Python 2.6 + +# The next several classes are used to define FakeSocket, a socket-like +# interface to an SSL connection. + +# The primary complexity comes from faking a makefile() method. The +# standard socket makefile() implementation calls dup() on the socket +# file descriptor. As a consequence, clients can call close() on the +# parent socket and its makefile children in any order. The underlying +# socket isn't closed until they are all closed. + +# The implementation uses reference counting to keep the socket open +# until the last client calls close(). SharedSocket keeps track of +# the reference counting and SharedSocketClient provides an constructor +# and close() method that call incref() and decref() correctly. + +class SharedSocket: + def __init__(self, sock): + self.sock = sock + self._refcnt = 0 + + def incref(self): + self._refcnt += 1 + + def decref(self): + self._refcnt -= 1 + assert self._refcnt >= 0 + if self._refcnt == 0: + self.sock.close() + + def __del__(self): + self.sock.close() + +class SharedSocketClient: + + def __init__(self, shared): + self._closed = 0 + self._shared = shared + self._shared.incref() + self._sock = shared.sock + + def close(self): + if not self._closed: + self._shared.decref() + self._closed = 1 + self._shared = None + +class SSLFile(SharedSocketClient): + """File-like object wrapping an SSL socket.""" + + BUFSIZE = 8192 + + def __init__(self, sock, ssl, bufsize=None): + SharedSocketClient.__init__(self, sock) + self._ssl = ssl + self._buf = '' + self._bufsize = bufsize or self.__class__.BUFSIZE + + def _read(self): + buf = '' + # put in a loop so that we retry on transient errors + while True: + try: + buf = self._ssl.read(self._bufsize) + except socket.sslerror, err: + if (err[0] == socket.SSL_ERROR_WANT_READ + or err[0] == socket.SSL_ERROR_WANT_WRITE): + continue + if (err[0] == socket.SSL_ERROR_ZERO_RETURN + or err[0] == socket.SSL_ERROR_EOF): + break + raise + except socket.error, err: + if err[0] == errno.EINTR: + continue + if err[0] == errno.EBADF: + # XXX socket was closed? + break + raise + else: + break + return buf + + def read(self, size=None): + L = [self._buf] + avail = len(self._buf) + while size is None or avail < size: + s = self._read() + if s == '': + break + L.append(s) + avail += len(s) + all = "".join(L) + if size is None: + self._buf = '' + return all + else: + self._buf = all[size:] + return all[:size] + + def readline(self): + L = [self._buf] + self._buf = '' + while 1: + i = L[-1].find("\n") + if i >= 0: + break + s = self._read() + if s == '': + break + L.append(s) + if i == -1: + # loop exited because there is no more data + return "".join(L) + else: + all = "".join(L) + # XXX could do enough bookkeeping not to do a 2nd search + i = all.find("\n") + 1 + line = all[:i] + self._buf = all[i:] + return line + + def readlines(self, sizehint=0): + total = 0 + list = [] + while True: + line = self.readline() + if not line: + break + list.append(line) + total += len(line) + if sizehint and total >= sizehint: + break + return list + + def fileno(self): + return self._sock.fileno() + + def __iter__(self): + return self + + def next(self): + line = self.readline() + if not line: + raise StopIteration + return line + +class FakeSocket(SharedSocketClient): + + class _closedsocket: + def __getattr__(self, name): + raise error(9, 'Bad file descriptor') + + def __init__(self, sock, ssl): + sock = SharedSocket(sock) + SharedSocketClient.__init__(self, sock) + self._ssl = ssl + + def close(self): + SharedSocketClient.close(self) + self._sock = self.__class__._closedsocket() + + def makefile(self, mode, bufsize=None): + if mode != 'r' and mode != 'rb': + raise UnimplementedFileMode() + return SSLFile(self._shared, self._ssl, bufsize) + + def send(self, stuff, flags = 0): + return self._ssl.write(stuff) + + sendall = send + + def recv(self, len = 1024, flags = 0): + return self._ssl.read(len) + + def __getattr__(self, attr): + return getattr(self._sock, attr) + diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py index fd27028e7..1b678305b 100644 --- a/ipapython/nsslib.py +++ b/ipapython/nsslib.py @@ -27,6 +27,13 @@ import nss.io as io import nss.nss as nss import nss.ssl as ssl +try: + from httplib import SSLFile + from httplib import FakeSocket +except ImportError: + from ipapython.ipasslfile import SSLFile + from ipapython.ipasslfile import FakeSocket + def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): cert = None if chosen_nickname: @@ -49,7 +56,7 @@ def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): return False return False -class SSLFile(httplib.SSLFile): +class SSLFile(SSLFile): """ Override the _read method so we can use the NSS recv method. """ @@ -64,7 +71,7 @@ class SSLFile(httplib.SSLFile): break return buf -class NSSFakeSocket(httplib.FakeSocket): +class NSSFakeSocket(FakeSocket): def makefile(self, mode, bufsize=None): if mode != 'r' and mode != 'rb': raise httplib.UnimplementedFileMode() |