From 944ab082a2924dca6d158d15b2a5b75e426494bb Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sat, 29 Jan 2011 16:10:53 +0100 Subject: Cherrypick two pending upstream bug fixes. * Non-maintainer upload. * Avoid deprecated RandomPool. Patch by Gary van der Merwe. Closes: #576697 * Try connecting to each available address family until one succeeds. Patch by Andrew Bennetts. Closes: #602251 * Bump standards version to 3.9.1 (no changes). --- debian/changelog | 11 + debian/control | 8 +- debian/patches/01_no-randompool.patch | 780 ++++++++++++++++++++++++++++++++ debian/patches/02_addressfamilies.patch | 30 ++ debian/patches/series | 2 + debian/rules | 5 +- 6 files changed, 831 insertions(+), 5 deletions(-) create mode 100644 debian/patches/01_no-randompool.patch create mode 100644 debian/patches/02_addressfamilies.patch create mode 100644 debian/patches/series diff --git a/debian/changelog b/debian/changelog index 057c74a..7291343 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +paramiko (1.7.6-5.1) unstable; urgency=low + + * Non-maintainer upload. + * Avoid deprecated RandomPool. Patch by Gary van der Merwe. Closes: + #576697 + * Try connecting to each available address family until one succeeds. + Patch by Andrew Bennetts. Closes: #602251 + * Bump standards version to 3.9.1 (no changes). + + -- Jelmer Vernooij Fri, 28 Jan 2011 12:35:12 +0100 + paramiko (1.7.6-5) unstable; urgency=low * debian/control: Fix python-crypto version dependency diff --git a/debian/control b/debian/control index a8cd249..2e8b315 100644 --- a/debian/control +++ b/debian/control @@ -3,11 +3,11 @@ Section: python Priority: optional Maintainer: Jeremy T. Bouse Uploaders: Guido Guenther -Build-Depends: quilt (>= 0.46-7~), - debhelper (>> 7.0.50), +Build-Depends: debhelper (>> 7.0.50), python-all (>= 2.3.5-11), - python-support (>= 0.5.3) -Standards-Version: 3.9.0 + python-support (>= 0.5.3), + python-crypto (>= 2.1.0-2) +Standards-Version: 3.9.1 Homepage: http://www.lag.net/paramiko/ Vcs-Git: git://git.debian.org/collab-maint/paramiko.git Vcs-Browser: http://git.debian.org/?p=collab-maint/paramiko.git diff --git a/debian/patches/01_no-randompool.patch b/debian/patches/01_no-randompool.patch new file mode 100644 index 0000000..9d87d62 --- /dev/null +++ b/debian/patches/01_no-randompool.patch @@ -0,0 +1,780 @@ +=== modified file 'a/paramiko/__init__.py' +--- a/paramiko/__init__.py 2010-04-26 00:05:06 +0000 ++++ b/paramiko/__init__.py 2010-08-02 22:13:08 +0000 +@@ -66,7 +66,7 @@ + __license__ = "GNU Lesser General Public License (LGPL)" + + +-from transport import randpool, SecurityOptions, Transport ++from transport import SecurityOptions, Transport + from client import SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy, WarningPolicy + from auth_handler import AuthHandler + from channel import Channel, ChannelFile + +=== modified file 'a/paramiko/agent.py' +--- a/paramiko/agent.py 2007-02-13 19:17:06 +0000 ++++ b/paramiko/agent.py 2010-08-02 22:13:08 +0000 +@@ -139,7 +139,7 @@ + def get_name(self): + return self.name + +- def sign_ssh_data(self, randpool, data): ++ def sign_ssh_data(self, rng, data): + msg = Message() + msg.add_byte(chr(SSH2_AGENTC_SIGN_REQUEST)) + msg.add_string(self.blob) + +=== modified file 'a/paramiko/auth_handler.py' +--- a/paramiko/auth_handler.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/auth_handler.py 2010-08-02 22:13:08 +0000 +@@ -206,7 +206,7 @@ + m.add_string(self.private_key.get_name()) + m.add_string(str(self.private_key)) + blob = self._get_session_blob(self.private_key, 'ssh-connection', self.username) +- sig = self.private_key.sign_ssh_data(self.transport.randpool, blob) ++ sig = self.private_key.sign_ssh_data(self.transport.rng, blob) + m.add_string(str(sig)) + elif self.auth_method == 'keyboard-interactive': + m.add_string('') + +=== modified file 'a/paramiko/channel.py' +--- a/paramiko/channel.py 2009-11-01 00:55:52 +0000 ++++ b/paramiko/channel.py 2010-08-02 22:13:08 +0000 +@@ -364,7 +364,7 @@ + if auth_protocol is None: + auth_protocol = 'MIT-MAGIC-COOKIE-1' + if auth_cookie is None: +- auth_cookie = binascii.hexlify(self.transport.randpool.get_bytes(16)) ++ auth_cookie = binascii.hexlify(self.transport.rng.read(16)) + + m = Message() + m.add_byte(chr(MSG_CHANNEL_REQUEST)) + +=== modified file 'a/paramiko/common.py' +--- a/paramiko/common.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/common.py 2010-08-02 22:13:08 +0000 +@@ -95,10 +95,10 @@ + DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \ + DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14 + +-from rng import StrongLockingRandomPool ++from Crypto import Random + + # keep a crypto-strong PRNG nearby +-randpool = StrongLockingRandomPool() ++rng = Random.new() + + import sys + if sys.version_info < (2, 3): + +=== modified file 'a/paramiko/dsskey.py' +--- a/paramiko/dsskey.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/dsskey.py 2010-08-02 22:13:08 +0000 +@@ -91,13 +91,13 @@ + def can_sign(self): + return self.x is not None + +- def sign_ssh_data(self, rpool, data): ++ def sign_ssh_data(self, rng, data): + digest = SHA.new(data).digest() + dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) + # generate a suitable k + qsize = len(util.deflate_long(self.q, 0)) + while True: +- k = util.inflate_long(rpool.get_bytes(qsize), 1) ++ k = util.inflate_long(rng.read(qsize), 1) + if (k > 2) and (k < self.q): + break + r, s = dss.sign(util.inflate_long(digest, 1), k) +@@ -161,8 +161,7 @@ + @return: new private key + @rtype: L{DSSKey} + """ +- randpool.stir() +- dsa = DSA.generate(bits, randpool.get_bytes, progress_func) ++ dsa = DSA.generate(bits, rng.read, progress_func) + key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y)) + key.x = dsa.x + return key + +=== modified file 'a/paramiko/hostkeys.py' +--- a/paramiko/hostkeys.py 2009-11-02 05:33:13 +0000 ++++ b/paramiko/hostkeys.py 2010-08-02 22:13:08 +0000 +@@ -303,7 +303,7 @@ + @rtype: str + """ + if salt is None: +- salt = randpool.get_bytes(SHA.digest_size) ++ salt = rng.read(SHA.digest_size) + else: + if salt.startswith('|1|'): + salt = salt.split('|')[2] + +=== modified file 'a/paramiko/kex_gex.py' +--- a/paramiko/kex_gex.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/kex_gex.py 2010-08-02 22:13:08 +0000 +@@ -101,8 +101,7 @@ + qhbyte <<= 1 + qmask >>= 1 + while True: +- self.transport.randpool.stir() +- x_bytes = self.transport.randpool.get_bytes(bytes) ++ x_bytes = self.transport.rng.read(bytes) + x_bytes = chr(ord(x_bytes[0]) & qmask) + x_bytes[1:] + x = util.inflate_long(x_bytes, 1) + if (x > 1) and (x < q): +@@ -207,7 +206,7 @@ + H = SHA.new(str(hm)).digest() + self.transport._set_K_H(K, H) + # sign it +- sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H) ++ sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H) + # send reply + m = Message() + m.add_byte(chr(_MSG_KEXDH_GEX_REPLY)) + +=== modified file 'a/paramiko/kex_group1.py' +--- a/paramiko/kex_group1.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/kex_group1.py 2010-08-02 22:13:08 +0000 +@@ -79,8 +79,7 @@ + # potential x where the first 63 bits are 1, because some of those will be + # larger than q (but this is a tiny tiny subset of potential x). + while 1: +- self.transport.randpool.stir() +- x_bytes = self.transport.randpool.get_bytes(128) ++ x_bytes = self.transport.rng.read(128) + x_bytes = chr(ord(x_bytes[0]) & 0x7f) + x_bytes[1:] + if (x_bytes[:8] != '\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF') and \ + (x_bytes[:8] != '\x00\x00\x00\x00\x00\x00\x00\x00'): +@@ -125,7 +124,7 @@ + H = SHA.new(str(hm)).digest() + self.transport._set_K_H(K, H) + # sign it +- sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H) ++ sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H) + # send reply + m = Message() + m.add_byte(chr(_MSG_KEXDH_REPLY)) + +=== modified file 'a/paramiko/packet.py' +--- a/paramiko/packet.py 2010-04-14 01:51:45 +0000 ++++ b/paramiko/packet.py 2010-08-02 22:13:08 +0000 +@@ -311,9 +311,6 @@ + + self.__sent_bytes += len(out) + self.__sent_packets += 1 +- if (self.__sent_packets % 100) == 0: +- # stirring the randpool takes 30ms on my ibook!! +- randpool.stir() + if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \ + and not self.__need_rekey: + # only ask once for rekeying +@@ -359,7 +356,7 @@ + raise SSHException('Mismatched MAC') + padding = ord(packet[0]) + payload = packet[1:packet_size - padding] +- randpool.add_event() ++ + if self.__dump_packets: + self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding)) + +@@ -476,7 +473,7 @@ + packet = struct.pack('>IB', len(payload) + padding + 1, padding) + packet += payload + if self.__block_engine_out is not None: +- packet += randpool.get_bytes(padding) ++ packet += rng.read(padding) + else: + # cute trick i caught openssh doing: if we're not encrypting, + # don't waste random bytes for the padding + +=== modified file 'a/paramiko/pkey.py' +--- a/paramiko/pkey.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/pkey.py 2010-08-02 22:13:08 +0000 +@@ -143,13 +143,13 @@ + """ + return base64.encodestring(str(self)).replace('\n', '') + +- def sign_ssh_data(self, randpool, data): ++ def sign_ssh_data(self, rng, data): + """ + Sign a blob of data with this private key, and return a L{Message} + representing an SSH signature message. + +- @param randpool: a secure random number generator. +- @type randpool: L{Crypto.Util.randpool.RandomPool} ++ @param rng: a secure random number generator. ++ @type rng: L{Crypto.Util.rng.RandomPool} + @param data: the data to sign. + @type data: str + @return: an SSH signature message. +@@ -360,11 +360,11 @@ + keysize = self._CIPHER_TABLE[cipher_name]['keysize'] + blocksize = self._CIPHER_TABLE[cipher_name]['blocksize'] + mode = self._CIPHER_TABLE[cipher_name]['mode'] +- salt = randpool.get_bytes(8) ++ salt = rng.read(8) + key = util.generate_key_bytes(MD5, salt, password, keysize) + if len(data) % blocksize != 0: + n = blocksize - len(data) % blocksize +- #data += randpool.get_bytes(n) ++ #data += rng.read(n) + # that would make more sense ^, but it confuses openssh. + data += '\0' * n + data = cipher.new(key, mode, salt).encrypt(data) + +=== modified file 'a/paramiko/primes.py' +--- a/paramiko/primes.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/primes.py 2010-08-02 22:13:08 +0000 +@@ -26,12 +26,12 @@ + from paramiko.ssh_exception import SSHException + + +-def _generate_prime(bits, randpool): ++def _generate_prime(bits, rng): + "primtive attempt at prime generation" + hbyte_mask = pow(2, bits % 8) - 1 + while True: + # loop catches the case where we increment n into a higher bit-range +- x = randpool.get_bytes((bits+7) // 8) ++ x = rng.read((bits+7) // 8) + if hbyte_mask > 0: + x = chr(ord(x[0]) & hbyte_mask) + x[1:] + n = util.inflate_long(x, 1) +@@ -43,7 +43,7 @@ + break + return n + +-def _roll_random(rpool, n): ++def _roll_random(rng, n): + "returns a random # from 0 to N-1" + bits = util.bit_length(n-1) + bytes = (bits + 7) // 8 +@@ -56,7 +56,7 @@ + # fits, so i can't guarantee that this loop will ever finish, but the odds + # of it looping forever should be infinitesimal. + while True: +- x = rpool.get_bytes(bytes) ++ x = rng.read(bytes) + if hbyte_mask > 0: + x = chr(ord(x[0]) & hbyte_mask) + x[1:] + num = util.inflate_long(x, 1) +@@ -75,7 +75,7 @@ + # pack is a hash of: bits -> [ (generator, modulus) ... ] + self.pack = {} + self.discarded = [] +- self.randpool = rpool ++ self.rng = rpool + + def _parse_modulus(self, line): + timestamp, mod_type, tests, tries, size, generator, modulus = line.split() +@@ -147,5 +147,5 @@ + if min > good: + good = bitsizes[-1] + # now pick a random modulus of this bitsize +- n = _roll_random(self.randpool, len(self.pack[good])) ++ n = _roll_random(self.rng, len(self.pack[good])) + return self.pack[good][n] + +=== removed file 'a/paramiko/rng.py' +--- a/paramiko/rng.py 2008-05-18 22:45:25 +0000 ++++ b/paramiko/rng.py 1970-01-01 00:00:00 +0000 +@@ -1,112 +0,0 @@ +-#!/usr/bin/python +-# -*- coding: ascii -*- +-# Copyright (C) 2008 Dwayne C. Litzenberger +-# +-# This file is part of paramiko. +-# +-# Paramiko is free software; you can redistribute it and/or modify it under the +-# terms of the GNU Lesser General Public License as published by the Free +-# Software Foundation; either version 2.1 of the License, or (at your option) +-# any later version. +-# +-# Paramiko is distrubuted 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 Lesser General Public License for more +-# details. +-# +-# You should have received a copy of the GNU Lesser General Public License +-# along with Paramiko; if not, write to the Free Software Foundation, Inc., +-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- +-import sys +-import threading +-from Crypto.Util.randpool import RandomPool as _RandomPool +- +-try: +- import platform +-except ImportError: +- platform = None # Not available using Python 2.2 +- +-def _strxor(a, b): +- assert len(a) == len(b) +- return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), a, b)) +- +-## +-## Find a strong random entropy source, depending on the detected platform. +-## WARNING TO DEVELOPERS: This will fail on some systems, but do NOT use +-## Crypto.Util.randpool.RandomPool as a fall-back. RandomPool will happily run +-## with very little entropy, thus _silently_ defeating any security that +-## Paramiko attempts to provide. (This is current as of PyCrypto 2.0.1). +-## See http://www.lag.net/pipermail/paramiko/2008-January/000599.html +-## and http://www.lag.net/pipermail/paramiko/2008-April/000678.html +-## +- +-if ((platform is not None and platform.system().lower() == 'windows') or +- sys.platform == 'win32'): +- # MS Windows +- from paramiko import rng_win32 +- rng_device = rng_win32.open_rng_device() +-else: +- # Assume POSIX (any system where /dev/urandom exists) +- from paramiko import rng_posix +- rng_device = rng_posix.open_rng_device() +- +- +-class StrongLockingRandomPool(object): +- """Wrapper around RandomPool guaranteeing strong random numbers. +- +- Crypto.Util.randpool.RandomPool will silently operate even if it is seeded +- with little or no entropy, and it provides no prediction resistance if its +- state is ever compromised throughout its runtime. It is also not thread-safe. +- +- This wrapper augments RandomPool by XORing its output with random bits from +- the operating system, and by controlling access to the underlying +- RandomPool using an exclusive lock. +- """ +- +- def __init__(self, instance=None): +- if instance is None: +- instance = _RandomPool() +- self.randpool = instance +- self.randpool_lock = threading.Lock() +- self.entropy = rng_device +- +- # Stir 256 bits of entropy from the RNG device into the RandomPool. +- self.randpool.stir(self.entropy.read(32)) +- self.entropy.randomize() +- +- def stir(self, s=''): +- self.randpool_lock.acquire() +- try: +- self.randpool.stir(s) +- finally: +- self.randpool_lock.release() +- self.entropy.randomize() +- +- def randomize(self, N=0): +- self.randpool_lock.acquire() +- try: +- self.randpool.randomize(N) +- finally: +- self.randpool_lock.release() +- self.entropy.randomize() +- +- def add_event(self, s=''): +- self.randpool_lock.acquire() +- try: +- self.randpool.add_event(s) +- finally: +- self.randpool_lock.release() +- +- def get_bytes(self, N): +- self.randpool_lock.acquire() +- try: +- randpool_data = self.randpool.get_bytes(N) +- finally: +- self.randpool_lock.release() +- entropy_data = self.entropy.read(N) +- result = _strxor(randpool_data, entropy_data) +- assert len(randpool_data) == N and len(entropy_data) == N and len(result) == N +- return result +- +-# vim:set ts=4 sw=4 sts=4 expandtab: + +=== removed file 'a/paramiko/rng_posix.py' +--- a/paramiko/rng_posix.py 2009-11-02 05:33:13 +0000 ++++ b/paramiko/rng_posix.py 1970-01-01 00:00:00 +0000 +@@ -1,97 +0,0 @@ +-#!/usr/bin/python +-# -*- coding: ascii -*- +-# Copyright (C) 2008 Dwayne C. Litzenberger +-# Copyright (C) 2008 Open Systems Canada Limited +-# +-# This file is part of paramiko. +-# +-# Paramiko is free software; you can redistribute it and/or modify it under the +-# terms of the GNU Lesser General Public License as published by the Free +-# Software Foundation; either version 2.1 of the License, or (at your option) +-# any later version. +-# +-# Paramiko is distrubuted 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 Lesser General Public License for more +-# details. +-# +-# You should have received a copy of the GNU Lesser General Public License +-# along with Paramiko; if not, write to the Free Software Foundation, Inc., +-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- +-import os +-import stat +- +-class error(Exception): +- pass +- +-class _RNG(object): +- def __init__(self, file): +- self.file = file +- +- def read(self, bytes): +- return self.file.read(bytes) +- +- def close(self): +- return self.file.close() +- +- def randomize(self): +- return +- +-def open_rng_device(device_path=None): +- """Open /dev/urandom and perform some sanity checks.""" +- +- f = None +- g = None +- +- if device_path is None: +- device_path = "/dev/urandom" +- +- try: +- # Try to open /dev/urandom now so that paramiko will be able to access +- # it even if os.chroot() is invoked later. +- try: +- f = open(device_path, "rb", 0) +- except EnvironmentError: +- raise error("Unable to open /dev/urandom") +- +- # Open a second file descriptor for sanity checking later. +- try: +- g = open(device_path, "rb", 0) +- except EnvironmentError: +- raise error("Unable to open /dev/urandom") +- +- # Check that /dev/urandom is a character special device, not a regular file. +- st = os.fstat(f.fileno()) # f +- if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode): +- raise error("/dev/urandom is not a character special device") +- +- st = os.fstat(g.fileno()) # g +- if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode): +- raise error("/dev/urandom is not a character special device") +- +- # Check that /dev/urandom always returns the number of bytes requested +- x = f.read(20) +- y = g.read(20) +- if len(x) != 20 or len(y) != 20: +- raise error("Error reading from /dev/urandom: input truncated") +- +- # Check that different reads return different data +- if x == y: +- raise error("/dev/urandom is broken; returning identical data: %r == %r" % (x, y)) +- +- # Close the duplicate file object +- g.close() +- +- # Return the first file object +- return _RNG(f) +- +- except error: +- if f is not None: +- f.close() +- if g is not None: +- g.close() +- raise +- +-# vim:set ts=4 sw=4 sts=4 expandtab: +- + +=== removed file 'a/paramiko/rng_win32.py' +--- a/paramiko/rng_win32.py 2008-05-18 22:45:25 +0000 ++++ b/paramiko/rng_win32.py 1970-01-01 00:00:00 +0000 +@@ -1,121 +0,0 @@ +-#!/usr/bin/python +-# -*- coding: ascii -*- +-# Copyright (C) 2008 Dwayne C. Litzenberger +-# Copyright (C) 2008 Open Systems Canada Limited +-# +-# This file is part of paramiko. +-# +-# Paramiko is free software; you can redistribute it and/or modify it under the +-# terms of the GNU Lesser General Public License as published by the Free +-# Software Foundation; either version 2.1 of the License, or (at your option) +-# any later version. +-# +-# Paramiko is distrubuted 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 Lesser General Public License for more +-# details. +-# +-# You should have received a copy of the GNU Lesser General Public License +-# along with Paramiko; if not, write to the Free Software Foundation, Inc., +-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- +-class error(Exception): +- pass +- +-# Try to import the "winrandom" module +-try: +- from Crypto.Util import winrandom as _winrandom +-except ImportError: +- _winrandom = None +- +-# Try to import the "urandom" module +-try: +- from os import urandom as _urandom +-except ImportError: +- _urandom = None +- +- +-class _RNG(object): +- def __init__(self, readfunc): +- self.read = readfunc +- +- def randomize(self): +- # According to "Cryptanalysis of the Random Number Generator of the +- # Windows Operating System", by Leo Dorrendorf and Zvi Gutterman +- # and Benny Pinkas , +- # CryptGenRandom only updates its internal state using kernel-provided +- # random data every 128KiB of output. +- self.read(128*1024) # discard 128 KiB of output +- +-def _open_winrandom(): +- if _winrandom is None: +- raise error("Crypto.Util.winrandom module not found") +- +- # Check that we can open the winrandom module +- try: +- r0 = _winrandom.new() +- r1 = _winrandom.new() +- except Exception, exc: +- raise error("winrandom.new() failed: %s" % str(exc), exc) +- +- # Check that we can read from the winrandom module +- try: +- x = r0.get_bytes(20) +- y = r1.get_bytes(20) +- except Exception, exc: +- raise error("winrandom get_bytes failed: %s" % str(exc), exc) +- +- # Check that the requested number of bytes are returned +- if len(x) != 20 or len(y) != 20: +- raise error("Error reading from winrandom: input truncated") +- +- # Check that different reads return different data +- if x == y: +- raise error("winrandom broken: returning identical data") +- +- return _RNG(r0.get_bytes) +- +-def _open_urandom(): +- if _urandom is None: +- raise error("os.urandom function not found") +- +- # Check that we can read from os.urandom() +- try: +- x = _urandom(20) +- y = _urandom(20) +- except Exception, exc: +- raise error("os.urandom failed: %s" % str(exc), exc) +- +- # Check that the requested number of bytes are returned +- if len(x) != 20 or len(y) != 20: +- raise error("os.urandom failed: input truncated") +- +- # Check that different reads return different data +- if x == y: +- raise error("os.urandom failed: returning identical data") +- +- return _RNG(_urandom) +- +-def open_rng_device(): +- # Try using the Crypto.Util.winrandom module +- try: +- return _open_winrandom() +- except error: +- pass +- +- # Several versions of PyCrypto do not contain the winrandom module, but +- # Python >= 2.4 has os.urandom, so try to use that. +- try: +- return _open_urandom() +- except error: +- pass +- +- # SECURITY NOTE: DO NOT USE Crypto.Util.randpool.RandomPool HERE! +- # If we got to this point, RandomPool will silently run with very little +- # entropy. (This is current as of PyCrypto 2.0.1). +- # See http://www.lag.net/pipermail/paramiko/2008-January/000599.html +- # and http://www.lag.net/pipermail/paramiko/2008-April/000678.html +- +- raise error("Unable to find a strong random entropy source. You cannot run this software securely under the current configuration.") +- +-# vim:set ts=4 sw=4 sts=4 expandtab: + +=== modified file 'a/paramiko/rsakey.py' +--- a/paramiko/rsakey.py 2009-07-20 02:45:02 +0000 ++++ b/paramiko/rsakey.py 2010-08-02 22:13:08 +0000 +@@ -137,8 +137,7 @@ + @return: new private key + @rtype: L{RSAKey} + """ +- randpool.stir() +- rsa = RSA.generate(bits, randpool.get_bytes, progress_func) ++ rsa = RSA.generate(bits, rng.read, progress_func) + key = RSAKey(vals=(rsa.e, rsa.n)) + key.d = rsa.d + key.p = rsa.p + +=== modified file 'a/paramiko/transport.py' +--- a/paramiko/transport.py 2010-04-25 23:42:45 +0000 ++++ b/paramiko/transport.py 2010-08-02 22:13:08 +0000 +@@ -297,7 +297,7 @@ + # okay, normal socket-ish flow here... + threading.Thread.__init__(self) + self.setDaemon(True) +- self.randpool = randpool ++ self.rng = rng + self.sock = sock + # Python < 2.3 doesn't have the settimeout method - RogerB + try: +@@ -585,7 +585,7 @@ + + @note: This has no effect when used in client mode. + """ +- Transport._modulus_pack = ModulusPack(randpool) ++ Transport._modulus_pack = ModulusPack(rng) + # places to look for the openssh "moduli" file + file_list = [ '/etc/ssh/moduli', '/usr/local/etc/moduli' ] + if filename is not None: +@@ -837,10 +837,9 @@ + """ + m = Message() + m.add_byte(chr(MSG_IGNORE)) +- randpool.stir() + if bytes is None: +- bytes = (ord(randpool.get_bytes(1)) % 32) + 10 +- m.add_bytes(randpool.get_bytes(bytes)) ++ bytes = (ord(rng.read(1)) % 32) + 10 ++ m.add_bytes(rng.read(bytes)) + self._send_user_message(m) + + def renegotiate_keys(self): +@@ -1674,10 +1673,9 @@ + else: + available_server_keys = self._preferred_keys + +- randpool.stir() + m = Message() + m.add_byte(chr(MSG_KEXINIT)) +- m.add_bytes(randpool.get_bytes(16)) ++ m.add_bytes(rng.read(16)) + m.add_list(self._preferred_kex) + m.add_list(available_server_keys) + m.add_list(self._preferred_ciphers) + +=== modified file 'a/tests/test_kex.py' +--- a/tests/test_kex.py 2009-07-20 02:45:02 +0000 ++++ b/tests/test_kex.py 2010-08-02 22:13:08 +0000 +@@ -28,17 +28,15 @@ + from paramiko import Message + + +-class FakeRandpool (object): +- def stir(self): +- pass +- def get_bytes(self, n): ++class FakeRng (object): ++ def read(self, n): + return chr(0xcc) * n + + + class FakeKey (object): + def __str__(self): + return 'fake-key' +- def sign_ssh_data(self, randpool, H): ++ def sign_ssh_data(self, rng, H): + return 'fake-sig' + + +@@ -50,7 +48,7 @@ + + + class FakeTransport (object): +- randpool = FakeRandpool() ++ rng = FakeRng() + local_version = 'SSH-2.0-paramiko_1.0' + remote_version = 'SSH-2.0-lame' + local_kex_init = 'local-kex-init' + +=== modified file 'a/tests/test_pkey.py' +--- a/tests/test_pkey.py 2009-07-20 02:45:02 +0000 ++++ b/tests/test_pkey.py 2010-08-02 22:13:08 +0000 +@@ -23,7 +23,8 @@ + from binascii import hexlify, unhexlify + import StringIO + import unittest +-from paramiko import RSAKey, DSSKey, Message, util, randpool ++from paramiko import RSAKey, DSSKey, Message, util ++from paramiko.common import rng + + # from openssh's ssh-keygen + PUB_RSA = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4c=' +@@ -151,7 +152,7 @@ + def test_8_sign_rsa(self): + # verify that the rsa private key can sign and verify + key = RSAKey.from_private_key_file('tests/test_rsa.key') +- msg = key.sign_ssh_data(randpool, 'ice weasels') ++ msg = key.sign_ssh_data(rng, 'ice weasels') + self.assert_(type(msg) is Message) + msg.rewind() + self.assertEquals('ssh-rsa', msg.get_string()) +@@ -164,7 +165,7 @@ + def test_9_sign_dss(self): + # verify that the dss private key can sign and verify + key = DSSKey.from_private_key_file('tests/test_dss.key') +- msg = key.sign_ssh_data(randpool, 'ice weasels') ++ msg = key.sign_ssh_data(rng, 'ice weasels') + self.assert_(type(msg) is Message) + msg.rewind() + self.assertEquals('ssh-dss', msg.get_string()) +@@ -178,12 +179,12 @@ + + def test_A_generate_rsa(self): + key = RSAKey.generate(1024) +- msg = key.sign_ssh_data(randpool, 'jerri blank') ++ msg = key.sign_ssh_data(rng, 'jerri blank') + msg.rewind() + self.assert_(key.verify_ssh_sig('jerri blank', msg)) + + def test_B_generate_dss(self): + key = DSSKey.generate(1024) +- msg = key.sign_ssh_data(randpool, 'jerri blank') ++ msg = key.sign_ssh_data(rng, 'jerri blank') + msg.rewind() + self.assert_(key.verify_ssh_sig('jerri blank', msg)) + +=== modified file 'a/tests/test_util.py' +--- a/tests/test_util.py 2009-07-20 02:45:02 +0000 ++++ b/tests/test_util.py 2010-08-02 22:13:08 +0000 +@@ -147,8 +147,8 @@ + os.unlink('hostfile.temp') + + def test_6_random(self): +- from paramiko.common import randpool ++ from paramiko.common import rng + # just verify that we can pull out 32 bytes and not get an exception. +- x = randpool.get_bytes(32) ++ x = rng.read(32) + self.assertEquals(len(x), 32) + + diff --git a/debian/patches/02_addressfamilies.patch b/debian/patches/02_addressfamilies.patch new file mode 100644 index 0000000..3a5f719 --- /dev/null +++ b/debian/patches/02_addressfamilies.patch @@ -0,0 +1,30 @@ +=== modified file 'paramiko/transport.py' +--- a/paramiko/transport.py 2009-12-16 08:15:36 +0000 ++++ ib/paramiko/transport.py 2011-01-14 06:01:07 +0000 +@@ -285,15 +285,21 @@ + if type(sock) is tuple: + # connect to the given (host, port) + hostname, port = sock ++ reason = 'No suitable address family' + for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): + if socktype == socket.SOCK_STREAM: + af = family + addr = sockaddr +- break ++ sock = socket.socket(af, socket.SOCK_STREAM) ++ try: ++ sock.connect((hostname, port)) ++ except socket.error, e: ++ reason = str(e) ++ else: ++ break + else: +- raise SSHException('No suitable address family for %s' % hostname) +- sock = socket.socket(af, socket.SOCK_STREAM) +- sock.connect((hostname, port)) ++ raise SSHException( ++ 'Unable to connect to %s: %s' % (hostname, reason)) + # okay, normal socket-ish flow here... + threading.Thread.__init__(self) + self.setDaemon(True) + diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..e301af3 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,2 @@ +01_no-randompool.patch +02_addressfamilies.patch diff --git a/debian/rules b/debian/rules index 495ebec..b3b9ccb 100755 --- a/debian/rules +++ b/debian/rules @@ -1,12 +1,15 @@ #!/usr/bin/make -f %: - dh --with python-support,quilt $@ + dh --with python-support $@ override_dh_clean: rm -rf build dh_clean +override_dh_auto_test: + ./test.py + # Commands not to run override_dh_installcatalogs override_dh_installcron: override_dh_installdebconf override_dh_installemacsen override_dh_installifupdown: -- cgit v1.2.3