Merge lp:~jelmer/ubuntu/natty/paramiko/randompool+addressfamilies into lp:ubuntu/natty/paramiko

Proposed by Jelmer Vernooij on 2011-01-18
Status: Merged
Merge reported by: Jelmer Vernooij
Merged at revision: not available
Proposed branch: lp:~jelmer/ubuntu/natty/paramiko/randompool+addressfamilies
Merge into: lp:ubuntu/natty/paramiko
Diff against target: 857 lines (+828/-0)
5 files modified
debian/changelog (+15/-0)
debian/patches/01_no-randompool.patch (+780/-0)
debian/patches/02_addressfamilies.patch (+30/-0)
debian/patches/series (+2/-0)
debian/source/format (+1/-0)
To merge this branch: bzr merge lp:~jelmer/ubuntu/natty/paramiko/randompool+addressfamilies
Reviewer Review Type Date Requested Status
Steve Kowalik 2011-01-18 Approve on 2011-01-18
Review via email: mp+46669@code.launchpad.net

Description of the change

Cherry-pick two patches that have been forwarded upstream:

* paramiko does not try all available address families (bug 638675)
* Fix RandomPool Depreciation Warning from pycrypto (bug 271791)

To post a comment you must log in.
Steve Kowalik (stevenk) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2010-07-17 00:00:40 +0000
3+++ debian/changelog 2011-01-18 19:52:31 +0000
4@@ -1,3 +1,18 @@
5+paramiko (1.7.6-5ubuntu1) natty; urgency=low
6+
7+ [ Jelmer Vernooij ]
8+
9+ * Avoid deprecated RandomPool. Patch by Gary van der Merwe. Closes:
10+ #576697, LP: #271791, LP: #682600.
11+ * Switch to source format 3.0 (quilt).
12+
13+ [ Andrew Bennetts ]
14+
15+ * Try connecting to each available address family until one succeeds.
16+ LP: #579530
17+
18+ -- Jelmer Vernooij <jelmer@ubuntu.com> Fri, 14 Jan 2011 06:54:41 +0100
19+
20 paramiko (1.7.6-5) unstable; urgency=low
21
22 * debian/control: Fix python-crypto version dependency
23
24=== added directory 'debian/patches'
25=== added file 'debian/patches/01_no-randompool.patch'
26--- debian/patches/01_no-randompool.patch 1970-01-01 00:00:00 +0000
27+++ debian/patches/01_no-randompool.patch 2011-01-18 19:52:31 +0000
28@@ -0,0 +1,780 @@
29+=== modified file 'a/paramiko/__init__.py'
30+--- a/paramiko/__init__.py 2010-04-26 00:05:06 +0000
31++++ b/paramiko/__init__.py 2010-08-02 22:13:08 +0000
32+@@ -66,7 +66,7 @@
33+ __license__ = "GNU Lesser General Public License (LGPL)"
34+
35+
36+-from transport import randpool, SecurityOptions, Transport
37++from transport import SecurityOptions, Transport
38+ from client import SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy, WarningPolicy
39+ from auth_handler import AuthHandler
40+ from channel import Channel, ChannelFile
41+
42+=== modified file 'a/paramiko/agent.py'
43+--- a/paramiko/agent.py 2007-02-13 19:17:06 +0000
44++++ b/paramiko/agent.py 2010-08-02 22:13:08 +0000
45+@@ -139,7 +139,7 @@
46+ def get_name(self):
47+ return self.name
48+
49+- def sign_ssh_data(self, randpool, data):
50++ def sign_ssh_data(self, rng, data):
51+ msg = Message()
52+ msg.add_byte(chr(SSH2_AGENTC_SIGN_REQUEST))
53+ msg.add_string(self.blob)
54+
55+=== modified file 'a/paramiko/auth_handler.py'
56+--- a/paramiko/auth_handler.py 2009-07-20 02:45:02 +0000
57++++ b/paramiko/auth_handler.py 2010-08-02 22:13:08 +0000
58+@@ -206,7 +206,7 @@
59+ m.add_string(self.private_key.get_name())
60+ m.add_string(str(self.private_key))
61+ blob = self._get_session_blob(self.private_key, 'ssh-connection', self.username)
62+- sig = self.private_key.sign_ssh_data(self.transport.randpool, blob)
63++ sig = self.private_key.sign_ssh_data(self.transport.rng, blob)
64+ m.add_string(str(sig))
65+ elif self.auth_method == 'keyboard-interactive':
66+ m.add_string('')
67+
68+=== modified file 'a/paramiko/channel.py'
69+--- a/paramiko/channel.py 2009-11-01 00:55:52 +0000
70++++ b/paramiko/channel.py 2010-08-02 22:13:08 +0000
71+@@ -364,7 +364,7 @@
72+ if auth_protocol is None:
73+ auth_protocol = 'MIT-MAGIC-COOKIE-1'
74+ if auth_cookie is None:
75+- auth_cookie = binascii.hexlify(self.transport.randpool.get_bytes(16))
76++ auth_cookie = binascii.hexlify(self.transport.rng.read(16))
77+
78+ m = Message()
79+ m.add_byte(chr(MSG_CHANNEL_REQUEST))
80+
81+=== modified file 'a/paramiko/common.py'
82+--- a/paramiko/common.py 2009-07-20 02:45:02 +0000
83++++ b/paramiko/common.py 2010-08-02 22:13:08 +0000
84+@@ -95,10 +95,10 @@
85+ DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
86+ DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
87+
88+-from rng import StrongLockingRandomPool
89++from Crypto import Random
90+
91+ # keep a crypto-strong PRNG nearby
92+-randpool = StrongLockingRandomPool()
93++rng = Random.new()
94+
95+ import sys
96+ if sys.version_info < (2, 3):
97+
98+=== modified file 'a/paramiko/dsskey.py'
99+--- a/paramiko/dsskey.py 2009-07-20 02:45:02 +0000
100++++ b/paramiko/dsskey.py 2010-08-02 22:13:08 +0000
101+@@ -91,13 +91,13 @@
102+ def can_sign(self):
103+ return self.x is not None
104+
105+- def sign_ssh_data(self, rpool, data):
106++ def sign_ssh_data(self, rng, data):
107+ digest = SHA.new(data).digest()
108+ dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x)))
109+ # generate a suitable k
110+ qsize = len(util.deflate_long(self.q, 0))
111+ while True:
112+- k = util.inflate_long(rpool.get_bytes(qsize), 1)
113++ k = util.inflate_long(rng.read(qsize), 1)
114+ if (k > 2) and (k < self.q):
115+ break
116+ r, s = dss.sign(util.inflate_long(digest, 1), k)
117+@@ -161,8 +161,7 @@
118+ @return: new private key
119+ @rtype: L{DSSKey}
120+ """
121+- randpool.stir()
122+- dsa = DSA.generate(bits, randpool.get_bytes, progress_func)
123++ dsa = DSA.generate(bits, rng.read, progress_func)
124+ key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
125+ key.x = dsa.x
126+ return key
127+
128+=== modified file 'a/paramiko/hostkeys.py'
129+--- a/paramiko/hostkeys.py 2009-11-02 05:33:13 +0000
130++++ b/paramiko/hostkeys.py 2010-08-02 22:13:08 +0000
131+@@ -303,7 +303,7 @@
132+ @rtype: str
133+ """
134+ if salt is None:
135+- salt = randpool.get_bytes(SHA.digest_size)
136++ salt = rng.read(SHA.digest_size)
137+ else:
138+ if salt.startswith('|1|'):
139+ salt = salt.split('|')[2]
140+
141+=== modified file 'a/paramiko/kex_gex.py'
142+--- a/paramiko/kex_gex.py 2009-07-20 02:45:02 +0000
143++++ b/paramiko/kex_gex.py 2010-08-02 22:13:08 +0000
144+@@ -101,8 +101,7 @@
145+ qhbyte <<= 1
146+ qmask >>= 1
147+ while True:
148+- self.transport.randpool.stir()
149+- x_bytes = self.transport.randpool.get_bytes(bytes)
150++ x_bytes = self.transport.rng.read(bytes)
151+ x_bytes = chr(ord(x_bytes[0]) & qmask) + x_bytes[1:]
152+ x = util.inflate_long(x_bytes, 1)
153+ if (x > 1) and (x < q):
154+@@ -207,7 +206,7 @@
155+ H = SHA.new(str(hm)).digest()
156+ self.transport._set_K_H(K, H)
157+ # sign it
158+- sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H)
159++ sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H)
160+ # send reply
161+ m = Message()
162+ m.add_byte(chr(_MSG_KEXDH_GEX_REPLY))
163+
164+=== modified file 'a/paramiko/kex_group1.py'
165+--- a/paramiko/kex_group1.py 2009-07-20 02:45:02 +0000
166++++ b/paramiko/kex_group1.py 2010-08-02 22:13:08 +0000
167+@@ -79,8 +79,7 @@
168+ # potential x where the first 63 bits are 1, because some of those will be
169+ # larger than q (but this is a tiny tiny subset of potential x).
170+ while 1:
171+- self.transport.randpool.stir()
172+- x_bytes = self.transport.randpool.get_bytes(128)
173++ x_bytes = self.transport.rng.read(128)
174+ x_bytes = chr(ord(x_bytes[0]) & 0x7f) + x_bytes[1:]
175+ if (x_bytes[:8] != '\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF') and \
176+ (x_bytes[:8] != '\x00\x00\x00\x00\x00\x00\x00\x00'):
177+@@ -125,7 +124,7 @@
178+ H = SHA.new(str(hm)).digest()
179+ self.transport._set_K_H(K, H)
180+ # sign it
181+- sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H)
182++ sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H)
183+ # send reply
184+ m = Message()
185+ m.add_byte(chr(_MSG_KEXDH_REPLY))
186+
187+=== modified file 'a/paramiko/packet.py'
188+--- a/paramiko/packet.py 2010-04-14 01:51:45 +0000
189++++ b/paramiko/packet.py 2010-08-02 22:13:08 +0000
190+@@ -311,9 +311,6 @@
191+
192+ self.__sent_bytes += len(out)
193+ self.__sent_packets += 1
194+- if (self.__sent_packets % 100) == 0:
195+- # stirring the randpool takes 30ms on my ibook!!
196+- randpool.stir()
197+ if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \
198+ and not self.__need_rekey:
199+ # only ask once for rekeying
200+@@ -359,7 +356,7 @@
201+ raise SSHException('Mismatched MAC')
202+ padding = ord(packet[0])
203+ payload = packet[1:packet_size - padding]
204+- randpool.add_event()
205++
206+ if self.__dump_packets:
207+ self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
208+
209+@@ -476,7 +473,7 @@
210+ packet = struct.pack('>IB', len(payload) + padding + 1, padding)
211+ packet += payload
212+ if self.__block_engine_out is not None:
213+- packet += randpool.get_bytes(padding)
214++ packet += rng.read(padding)
215+ else:
216+ # cute trick i caught openssh doing: if we're not encrypting,
217+ # don't waste random bytes for the padding
218+
219+=== modified file 'a/paramiko/pkey.py'
220+--- a/paramiko/pkey.py 2009-07-20 02:45:02 +0000
221++++ b/paramiko/pkey.py 2010-08-02 22:13:08 +0000
222+@@ -143,13 +143,13 @@
223+ """
224+ return base64.encodestring(str(self)).replace('\n', '')
225+
226+- def sign_ssh_data(self, randpool, data):
227++ def sign_ssh_data(self, rng, data):
228+ """
229+ Sign a blob of data with this private key, and return a L{Message}
230+ representing an SSH signature message.
231+
232+- @param randpool: a secure random number generator.
233+- @type randpool: L{Crypto.Util.randpool.RandomPool}
234++ @param rng: a secure random number generator.
235++ @type rng: L{Crypto.Util.rng.RandomPool}
236+ @param data: the data to sign.
237+ @type data: str
238+ @return: an SSH signature message.
239+@@ -360,11 +360,11 @@
240+ keysize = self._CIPHER_TABLE[cipher_name]['keysize']
241+ blocksize = self._CIPHER_TABLE[cipher_name]['blocksize']
242+ mode = self._CIPHER_TABLE[cipher_name]['mode']
243+- salt = randpool.get_bytes(8)
244++ salt = rng.read(8)
245+ key = util.generate_key_bytes(MD5, salt, password, keysize)
246+ if len(data) % blocksize != 0:
247+ n = blocksize - len(data) % blocksize
248+- #data += randpool.get_bytes(n)
249++ #data += rng.read(n)
250+ # that would make more sense ^, but it confuses openssh.
251+ data += '\0' * n
252+ data = cipher.new(key, mode, salt).encrypt(data)
253+
254+=== modified file 'a/paramiko/primes.py'
255+--- a/paramiko/primes.py 2009-07-20 02:45:02 +0000
256++++ b/paramiko/primes.py 2010-08-02 22:13:08 +0000
257+@@ -26,12 +26,12 @@
258+ from paramiko.ssh_exception import SSHException
259+
260+
261+-def _generate_prime(bits, randpool):
262++def _generate_prime(bits, rng):
263+ "primtive attempt at prime generation"
264+ hbyte_mask = pow(2, bits % 8) - 1
265+ while True:
266+ # loop catches the case where we increment n into a higher bit-range
267+- x = randpool.get_bytes((bits+7) // 8)
268++ x = rng.read((bits+7) // 8)
269+ if hbyte_mask > 0:
270+ x = chr(ord(x[0]) & hbyte_mask) + x[1:]
271+ n = util.inflate_long(x, 1)
272+@@ -43,7 +43,7 @@
273+ break
274+ return n
275+
276+-def _roll_random(rpool, n):
277++def _roll_random(rng, n):
278+ "returns a random # from 0 to N-1"
279+ bits = util.bit_length(n-1)
280+ bytes = (bits + 7) // 8
281+@@ -56,7 +56,7 @@
282+ # fits, so i can't guarantee that this loop will ever finish, but the odds
283+ # of it looping forever should be infinitesimal.
284+ while True:
285+- x = rpool.get_bytes(bytes)
286++ x = rng.read(bytes)
287+ if hbyte_mask > 0:
288+ x = chr(ord(x[0]) & hbyte_mask) + x[1:]
289+ num = util.inflate_long(x, 1)
290+@@ -75,7 +75,7 @@
291+ # pack is a hash of: bits -> [ (generator, modulus) ... ]
292+ self.pack = {}
293+ self.discarded = []
294+- self.randpool = rpool
295++ self.rng = rpool
296+
297+ def _parse_modulus(self, line):
298+ timestamp, mod_type, tests, tries, size, generator, modulus = line.split()
299+@@ -147,5 +147,5 @@
300+ if min > good:
301+ good = bitsizes[-1]
302+ # now pick a random modulus of this bitsize
303+- n = _roll_random(self.randpool, len(self.pack[good]))
304++ n = _roll_random(self.rng, len(self.pack[good]))
305+ return self.pack[good][n]
306+
307+=== removed file 'a/paramiko/rng.py'
308+--- a/paramiko/rng.py 2008-05-18 22:45:25 +0000
309++++ b/paramiko/rng.py 1970-01-01 00:00:00 +0000
310+@@ -1,112 +0,0 @@
311+-#!/usr/bin/python
312+-# -*- coding: ascii -*-
313+-# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
314+-#
315+-# This file is part of paramiko.
316+-#
317+-# Paramiko is free software; you can redistribute it and/or modify it under the
318+-# terms of the GNU Lesser General Public License as published by the Free
319+-# Software Foundation; either version 2.1 of the License, or (at your option)
320+-# any later version.
321+-#
322+-# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
323+-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
324+-# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
325+-# details.
326+-#
327+-# You should have received a copy of the GNU Lesser General Public License
328+-# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
329+-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
330+-
331+-import sys
332+-import threading
333+-from Crypto.Util.randpool import RandomPool as _RandomPool
334+-
335+-try:
336+- import platform
337+-except ImportError:
338+- platform = None # Not available using Python 2.2
339+-
340+-def _strxor(a, b):
341+- assert len(a) == len(b)
342+- return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), a, b))
343+-
344+-##
345+-## Find a strong random entropy source, depending on the detected platform.
346+-## WARNING TO DEVELOPERS: This will fail on some systems, but do NOT use
347+-## Crypto.Util.randpool.RandomPool as a fall-back. RandomPool will happily run
348+-## with very little entropy, thus _silently_ defeating any security that
349+-## Paramiko attempts to provide. (This is current as of PyCrypto 2.0.1).
350+-## See http://www.lag.net/pipermail/paramiko/2008-January/000599.html
351+-## and http://www.lag.net/pipermail/paramiko/2008-April/000678.html
352+-##
353+-
354+-if ((platform is not None and platform.system().lower() == 'windows') or
355+- sys.platform == 'win32'):
356+- # MS Windows
357+- from paramiko import rng_win32
358+- rng_device = rng_win32.open_rng_device()
359+-else:
360+- # Assume POSIX (any system where /dev/urandom exists)
361+- from paramiko import rng_posix
362+- rng_device = rng_posix.open_rng_device()
363+-
364+-
365+-class StrongLockingRandomPool(object):
366+- """Wrapper around RandomPool guaranteeing strong random numbers.
367+-
368+- Crypto.Util.randpool.RandomPool will silently operate even if it is seeded
369+- with little or no entropy, and it provides no prediction resistance if its
370+- state is ever compromised throughout its runtime. It is also not thread-safe.
371+-
372+- This wrapper augments RandomPool by XORing its output with random bits from
373+- the operating system, and by controlling access to the underlying
374+- RandomPool using an exclusive lock.
375+- """
376+-
377+- def __init__(self, instance=None):
378+- if instance is None:
379+- instance = _RandomPool()
380+- self.randpool = instance
381+- self.randpool_lock = threading.Lock()
382+- self.entropy = rng_device
383+-
384+- # Stir 256 bits of entropy from the RNG device into the RandomPool.
385+- self.randpool.stir(self.entropy.read(32))
386+- self.entropy.randomize()
387+-
388+- def stir(self, s=''):
389+- self.randpool_lock.acquire()
390+- try:
391+- self.randpool.stir(s)
392+- finally:
393+- self.randpool_lock.release()
394+- self.entropy.randomize()
395+-
396+- def randomize(self, N=0):
397+- self.randpool_lock.acquire()
398+- try:
399+- self.randpool.randomize(N)
400+- finally:
401+- self.randpool_lock.release()
402+- self.entropy.randomize()
403+-
404+- def add_event(self, s=''):
405+- self.randpool_lock.acquire()
406+- try:
407+- self.randpool.add_event(s)
408+- finally:
409+- self.randpool_lock.release()
410+-
411+- def get_bytes(self, N):
412+- self.randpool_lock.acquire()
413+- try:
414+- randpool_data = self.randpool.get_bytes(N)
415+- finally:
416+- self.randpool_lock.release()
417+- entropy_data = self.entropy.read(N)
418+- result = _strxor(randpool_data, entropy_data)
419+- assert len(randpool_data) == N and len(entropy_data) == N and len(result) == N
420+- return result
421+-
422+-# vim:set ts=4 sw=4 sts=4 expandtab:
423+
424+=== removed file 'a/paramiko/rng_posix.py'
425+--- a/paramiko/rng_posix.py 2009-11-02 05:33:13 +0000
426++++ b/paramiko/rng_posix.py 1970-01-01 00:00:00 +0000
427+@@ -1,97 +0,0 @@
428+-#!/usr/bin/python
429+-# -*- coding: ascii -*-
430+-# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
431+-# Copyright (C) 2008 Open Systems Canada Limited
432+-#
433+-# This file is part of paramiko.
434+-#
435+-# Paramiko is free software; you can redistribute it and/or modify it under the
436+-# terms of the GNU Lesser General Public License as published by the Free
437+-# Software Foundation; either version 2.1 of the License, or (at your option)
438+-# any later version.
439+-#
440+-# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
441+-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
442+-# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
443+-# details.
444+-#
445+-# You should have received a copy of the GNU Lesser General Public License
446+-# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
447+-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
448+-
449+-import os
450+-import stat
451+-
452+-class error(Exception):
453+- pass
454+-
455+-class _RNG(object):
456+- def __init__(self, file):
457+- self.file = file
458+-
459+- def read(self, bytes):
460+- return self.file.read(bytes)
461+-
462+- def close(self):
463+- return self.file.close()
464+-
465+- def randomize(self):
466+- return
467+-
468+-def open_rng_device(device_path=None):
469+- """Open /dev/urandom and perform some sanity checks."""
470+-
471+- f = None
472+- g = None
473+-
474+- if device_path is None:
475+- device_path = "/dev/urandom"
476+-
477+- try:
478+- # Try to open /dev/urandom now so that paramiko will be able to access
479+- # it even if os.chroot() is invoked later.
480+- try:
481+- f = open(device_path, "rb", 0)
482+- except EnvironmentError:
483+- raise error("Unable to open /dev/urandom")
484+-
485+- # Open a second file descriptor for sanity checking later.
486+- try:
487+- g = open(device_path, "rb", 0)
488+- except EnvironmentError:
489+- raise error("Unable to open /dev/urandom")
490+-
491+- # Check that /dev/urandom is a character special device, not a regular file.
492+- st = os.fstat(f.fileno()) # f
493+- if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
494+- raise error("/dev/urandom is not a character special device")
495+-
496+- st = os.fstat(g.fileno()) # g
497+- if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
498+- raise error("/dev/urandom is not a character special device")
499+-
500+- # Check that /dev/urandom always returns the number of bytes requested
501+- x = f.read(20)
502+- y = g.read(20)
503+- if len(x) != 20 or len(y) != 20:
504+- raise error("Error reading from /dev/urandom: input truncated")
505+-
506+- # Check that different reads return different data
507+- if x == y:
508+- raise error("/dev/urandom is broken; returning identical data: %r == %r" % (x, y))
509+-
510+- # Close the duplicate file object
511+- g.close()
512+-
513+- # Return the first file object
514+- return _RNG(f)
515+-
516+- except error:
517+- if f is not None:
518+- f.close()
519+- if g is not None:
520+- g.close()
521+- raise
522+-
523+-# vim:set ts=4 sw=4 sts=4 expandtab:
524+-
525+
526+=== removed file 'a/paramiko/rng_win32.py'
527+--- a/paramiko/rng_win32.py 2008-05-18 22:45:25 +0000
528++++ b/paramiko/rng_win32.py 1970-01-01 00:00:00 +0000
529+@@ -1,121 +0,0 @@
530+-#!/usr/bin/python
531+-# -*- coding: ascii -*-
532+-# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
533+-# Copyright (C) 2008 Open Systems Canada Limited
534+-#
535+-# This file is part of paramiko.
536+-#
537+-# Paramiko is free software; you can redistribute it and/or modify it under the
538+-# terms of the GNU Lesser General Public License as published by the Free
539+-# Software Foundation; either version 2.1 of the License, or (at your option)
540+-# any later version.
541+-#
542+-# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
543+-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
544+-# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
545+-# details.
546+-#
547+-# You should have received a copy of the GNU Lesser General Public License
548+-# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
549+-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
550+-
551+-class error(Exception):
552+- pass
553+-
554+-# Try to import the "winrandom" module
555+-try:
556+- from Crypto.Util import winrandom as _winrandom
557+-except ImportError:
558+- _winrandom = None
559+-
560+-# Try to import the "urandom" module
561+-try:
562+- from os import urandom as _urandom
563+-except ImportError:
564+- _urandom = None
565+-
566+-
567+-class _RNG(object):
568+- def __init__(self, readfunc):
569+- self.read = readfunc
570+-
571+- def randomize(self):
572+- # According to "Cryptanalysis of the Random Number Generator of the
573+- # Windows Operating System", by Leo Dorrendorf and Zvi Gutterman
574+- # and Benny Pinkas <http://eprint.iacr.org/2007/419>,
575+- # CryptGenRandom only updates its internal state using kernel-provided
576+- # random data every 128KiB of output.
577+- self.read(128*1024) # discard 128 KiB of output
578+-
579+-def _open_winrandom():
580+- if _winrandom is None:
581+- raise error("Crypto.Util.winrandom module not found")
582+-
583+- # Check that we can open the winrandom module
584+- try:
585+- r0 = _winrandom.new()
586+- r1 = _winrandom.new()
587+- except Exception, exc:
588+- raise error("winrandom.new() failed: %s" % str(exc), exc)
589+-
590+- # Check that we can read from the winrandom module
591+- try:
592+- x = r0.get_bytes(20)
593+- y = r1.get_bytes(20)
594+- except Exception, exc:
595+- raise error("winrandom get_bytes failed: %s" % str(exc), exc)
596+-
597+- # Check that the requested number of bytes are returned
598+- if len(x) != 20 or len(y) != 20:
599+- raise error("Error reading from winrandom: input truncated")
600+-
601+- # Check that different reads return different data
602+- if x == y:
603+- raise error("winrandom broken: returning identical data")
604+-
605+- return _RNG(r0.get_bytes)
606+-
607+-def _open_urandom():
608+- if _urandom is None:
609+- raise error("os.urandom function not found")
610+-
611+- # Check that we can read from os.urandom()
612+- try:
613+- x = _urandom(20)
614+- y = _urandom(20)
615+- except Exception, exc:
616+- raise error("os.urandom failed: %s" % str(exc), exc)
617+-
618+- # Check that the requested number of bytes are returned
619+- if len(x) != 20 or len(y) != 20:
620+- raise error("os.urandom failed: input truncated")
621+-
622+- # Check that different reads return different data
623+- if x == y:
624+- raise error("os.urandom failed: returning identical data")
625+-
626+- return _RNG(_urandom)
627+-
628+-def open_rng_device():
629+- # Try using the Crypto.Util.winrandom module
630+- try:
631+- return _open_winrandom()
632+- except error:
633+- pass
634+-
635+- # Several versions of PyCrypto do not contain the winrandom module, but
636+- # Python >= 2.4 has os.urandom, so try to use that.
637+- try:
638+- return _open_urandom()
639+- except error:
640+- pass
641+-
642+- # SECURITY NOTE: DO NOT USE Crypto.Util.randpool.RandomPool HERE!
643+- # If we got to this point, RandomPool will silently run with very little
644+- # entropy. (This is current as of PyCrypto 2.0.1).
645+- # See http://www.lag.net/pipermail/paramiko/2008-January/000599.html
646+- # and http://www.lag.net/pipermail/paramiko/2008-April/000678.html
647+-
648+- raise error("Unable to find a strong random entropy source. You cannot run this software securely under the current configuration.")
649+-
650+-# vim:set ts=4 sw=4 sts=4 expandtab:
651+
652+=== modified file 'a/paramiko/rsakey.py'
653+--- a/paramiko/rsakey.py 2009-07-20 02:45:02 +0000
654++++ b/paramiko/rsakey.py 2010-08-02 22:13:08 +0000
655+@@ -137,8 +137,7 @@
656+ @return: new private key
657+ @rtype: L{RSAKey}
658+ """
659+- randpool.stir()
660+- rsa = RSA.generate(bits, randpool.get_bytes, progress_func)
661++ rsa = RSA.generate(bits, rng.read, progress_func)
662+ key = RSAKey(vals=(rsa.e, rsa.n))
663+ key.d = rsa.d
664+ key.p = rsa.p
665+
666+=== modified file 'a/paramiko/transport.py'
667+--- a/paramiko/transport.py 2010-04-25 23:42:45 +0000
668++++ b/paramiko/transport.py 2010-08-02 22:13:08 +0000
669+@@ -297,7 +297,7 @@
670+ # okay, normal socket-ish flow here...
671+ threading.Thread.__init__(self)
672+ self.setDaemon(True)
673+- self.randpool = randpool
674++ self.rng = rng
675+ self.sock = sock
676+ # Python < 2.3 doesn't have the settimeout method - RogerB
677+ try:
678+@@ -585,7 +585,7 @@
679+
680+ @note: This has no effect when used in client mode.
681+ """
682+- Transport._modulus_pack = ModulusPack(randpool)
683++ Transport._modulus_pack = ModulusPack(rng)
684+ # places to look for the openssh "moduli" file
685+ file_list = [ '/etc/ssh/moduli', '/usr/local/etc/moduli' ]
686+ if filename is not None:
687+@@ -837,10 +837,9 @@
688+ """
689+ m = Message()
690+ m.add_byte(chr(MSG_IGNORE))
691+- randpool.stir()
692+ if bytes is None:
693+- bytes = (ord(randpool.get_bytes(1)) % 32) + 10
694+- m.add_bytes(randpool.get_bytes(bytes))
695++ bytes = (ord(rng.read(1)) % 32) + 10
696++ m.add_bytes(rng.read(bytes))
697+ self._send_user_message(m)
698+
699+ def renegotiate_keys(self):
700+@@ -1674,10 +1673,9 @@
701+ else:
702+ available_server_keys = self._preferred_keys
703+
704+- randpool.stir()
705+ m = Message()
706+ m.add_byte(chr(MSG_KEXINIT))
707+- m.add_bytes(randpool.get_bytes(16))
708++ m.add_bytes(rng.read(16))
709+ m.add_list(self._preferred_kex)
710+ m.add_list(available_server_keys)
711+ m.add_list(self._preferred_ciphers)
712+
713+=== modified file 'a/tests/test_kex.py'
714+--- a/tests/test_kex.py 2009-07-20 02:45:02 +0000
715++++ b/tests/test_kex.py 2010-08-02 22:13:08 +0000
716+@@ -28,17 +28,15 @@
717+ from paramiko import Message
718+
719+
720+-class FakeRandpool (object):
721+- def stir(self):
722+- pass
723+- def get_bytes(self, n):
724++class FakeRng (object):
725++ def read(self, n):
726+ return chr(0xcc) * n
727+
728+
729+ class FakeKey (object):
730+ def __str__(self):
731+ return 'fake-key'
732+- def sign_ssh_data(self, randpool, H):
733++ def sign_ssh_data(self, rng, H):
734+ return 'fake-sig'
735+
736+
737+@@ -50,7 +48,7 @@
738+
739+
740+ class FakeTransport (object):
741+- randpool = FakeRandpool()
742++ rng = FakeRng()
743+ local_version = 'SSH-2.0-paramiko_1.0'
744+ remote_version = 'SSH-2.0-lame'
745+ local_kex_init = 'local-kex-init'
746+
747+=== modified file 'a/tests/test_pkey.py'
748+--- a/tests/test_pkey.py 2009-07-20 02:45:02 +0000
749++++ b/tests/test_pkey.py 2010-08-02 22:13:08 +0000
750+@@ -23,7 +23,8 @@
751+ from binascii import hexlify, unhexlify
752+ import StringIO
753+ import unittest
754+-from paramiko import RSAKey, DSSKey, Message, util, randpool
755++from paramiko import RSAKey, DSSKey, Message, util
756++from paramiko.common import rng
757+
758+ # from openssh's ssh-keygen
759+ PUB_RSA = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4c='
760+@@ -151,7 +152,7 @@
761+ def test_8_sign_rsa(self):
762+ # verify that the rsa private key can sign and verify
763+ key = RSAKey.from_private_key_file('tests/test_rsa.key')
764+- msg = key.sign_ssh_data(randpool, 'ice weasels')
765++ msg = key.sign_ssh_data(rng, 'ice weasels')
766+ self.assert_(type(msg) is Message)
767+ msg.rewind()
768+ self.assertEquals('ssh-rsa', msg.get_string())
769+@@ -164,7 +165,7 @@
770+ def test_9_sign_dss(self):
771+ # verify that the dss private key can sign and verify
772+ key = DSSKey.from_private_key_file('tests/test_dss.key')
773+- msg = key.sign_ssh_data(randpool, 'ice weasels')
774++ msg = key.sign_ssh_data(rng, 'ice weasels')
775+ self.assert_(type(msg) is Message)
776+ msg.rewind()
777+ self.assertEquals('ssh-dss', msg.get_string())
778+@@ -178,12 +179,12 @@
779+
780+ def test_A_generate_rsa(self):
781+ key = RSAKey.generate(1024)
782+- msg = key.sign_ssh_data(randpool, 'jerri blank')
783++ msg = key.sign_ssh_data(rng, 'jerri blank')
784+ msg.rewind()
785+ self.assert_(key.verify_ssh_sig('jerri blank', msg))
786+
787+ def test_B_generate_dss(self):
788+ key = DSSKey.generate(1024)
789+- msg = key.sign_ssh_data(randpool, 'jerri blank')
790++ msg = key.sign_ssh_data(rng, 'jerri blank')
791+ msg.rewind()
792+ self.assert_(key.verify_ssh_sig('jerri blank', msg))
793+
794+=== modified file 'a/tests/test_util.py'
795+--- a/tests/test_util.py 2009-07-20 02:45:02 +0000
796++++ b/tests/test_util.py 2010-08-02 22:13:08 +0000
797+@@ -147,8 +147,8 @@
798+ os.unlink('hostfile.temp')
799+
800+ def test_6_random(self):
801+- from paramiko.common import randpool
802++ from paramiko.common import rng
803+ # just verify that we can pull out 32 bytes and not get an exception.
804+- x = randpool.get_bytes(32)
805++ x = rng.read(32)
806+ self.assertEquals(len(x), 32)
807+
808+
809
810=== added file 'debian/patches/02_addressfamilies.patch'
811--- debian/patches/02_addressfamilies.patch 1970-01-01 00:00:00 +0000
812+++ debian/patches/02_addressfamilies.patch 2011-01-18 19:52:31 +0000
813@@ -0,0 +1,30 @@
814+=== modified file 'paramiko/transport.py'
815+--- a/paramiko/transport.py 2009-12-16 08:15:36 +0000
816++++ ib/paramiko/transport.py 2011-01-14 06:01:07 +0000
817+@@ -285,15 +285,21 @@
818+ if type(sock) is tuple:
819+ # connect to the given (host, port)
820+ hostname, port = sock
821++ reason = 'No suitable address family'
822+ for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
823+ if socktype == socket.SOCK_STREAM:
824+ af = family
825+ addr = sockaddr
826+- break
827++ sock = socket.socket(af, socket.SOCK_STREAM)
828++ try:
829++ sock.connect((hostname, port))
830++ except socket.error, e:
831++ reason = str(e)
832++ else:
833++ break
834+ else:
835+- raise SSHException('No suitable address family for %s' % hostname)
836+- sock = socket.socket(af, socket.SOCK_STREAM)
837+- sock.connect((hostname, port))
838++ raise SSHException(
839++ 'Unable to connect to %s: %s' % (hostname, reason))
840+ # okay, normal socket-ish flow here...
841+ threading.Thread.__init__(self)
842+ self.setDaemon(True)
843+
844
845=== added file 'debian/patches/series'
846--- debian/patches/series 1970-01-01 00:00:00 +0000
847+++ debian/patches/series 2011-01-18 19:52:31 +0000
848@@ -0,0 +1,2 @@
849+01_no-randompool.patch
850+02_addressfamilies.patch
851
852=== added directory 'debian/source'
853=== added file 'debian/source/format'
854--- debian/source/format 1970-01-01 00:00:00 +0000
855+++ debian/source/format 2011-01-18 19:52:31 +0000
856@@ -0,0 +1,1 @@
857+3.0 (quilt)

Subscribers

People subscribed via source and target branches