Merge lp:~jelmer/ubuntu/natty/paramiko/randompool+addressfamilies into lp:ubuntu/natty/paramiko
- Natty (11.04)
- randompool+addressfamilies
- Merge into natty
Proposed by
Jelmer Vernooij
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Steve Kowalik | Approve | ||
Review via email: mp+46669@code.launchpad.net |
Commit message
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.
Revision history for this message
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) |