From ec3c103520a5061e657581b388e2b8ba6f74602a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:04:51 +0200 Subject: [PATCH 01/24] Issue #18709: Fix CVE-2013-4238. The SSL module now handles NULL bytes inside subjectAltName correctly. Formerly the module has used OpenSSL's GENERAL_NAME_print() function to get the string represention of ASN.1 strings for ``rfc822Name`` (email), ``dNSName`` (DNS) and ``uniformResourceIdentifier`` (URI). --- Lib/test/nullbytecert.pem | 90 +++++++++++++++++++++++++++++++++++++++ Lib/test/test_ssl.py | 29 +++++++++++++ Misc/NEWS | 6 +++ Modules/_ssl.c | 66 +++++++++++++++++++++++++--- 4 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 Lib/test/nullbytecert.pem diff --git a/Lib/test/nullbytecert.pem b/Lib/test/nullbytecert.pem new file mode 100644 index 00000000000..447186c9508 --- /dev/null +++ b/Lib/test/nullbytecert.pem @@ -0,0 +1,90 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, ST=Oregon, L=Beaverton, O=Python Software Foundation, OU=Python Core Development, CN=null.python.org\x00example.org/emailAddress=python-dev@python.org + Validity + Not Before: Aug 7 13:11:52 2013 GMT + Not After : Aug 7 13:12:52 2013 GMT + Subject: C=US, ST=Oregon, L=Beaverton, O=Python Software Foundation, OU=Python Core Development, CN=null.python.org\x00example.org/emailAddress=python-dev@python.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b5:ea:ed:c9:fb:46:7d:6f:3b:76:80:dd:3a:f3: + 03:94:0b:a7:a6:db:ec:1d:df:ff:23:74:08:9d:97: + 16:3f:a3:a4:7b:3e:1b:0e:96:59:25:03:a7:26:e2: + 88:a9:cf:79:cd:f7:04:56:b0:ab:79:32:6e:59:c1: + 32:30:54:eb:58:a8:cb:91:f0:42:a5:64:27:cb:d4: + 56:31:88:52:ad:cf:bd:7f:f0:06:64:1f:cc:27:b8: + a3:8b:8c:f3:d8:29:1f:25:0b:f5:46:06:1b:ca:02: + 45:ad:7b:76:0a:9c:bf:bb:b9:ae:0d:16:ab:60:75: + ae:06:3e:9c:7c:31:dc:92:2f:29:1a:e0:4b:0c:91: + 90:6c:e9:37:c5:90:d7:2a:d7:97:15:a3:80:8f:5d: + 7b:49:8f:54:30:d4:97:2c:1c:5b:37:b5:ab:69:30: + 68:43:d3:33:78:4b:02:60:f5:3c:44:80:a1:8f:e7: + f0:0f:d1:5e:87:9e:46:cf:62:fc:f9:bf:0c:65:12: + f1:93:c8:35:79:3f:c8:ec:ec:47:f5:ef:be:44:d5: + ae:82:1e:2d:9a:9f:98:5a:67:65:e1:74:70:7c:cb: + d3:c2:ce:0e:45:49:27:dc:e3:2d:d4:fb:48:0e:2f: + 9e:77:b8:14:46:c0:c4:36:ca:02:ae:6a:91:8c:da: + 2f:85 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Key Identifier: + 88:5A:55:C0:52:FF:61:CD:52:A3:35:0F:EA:5A:9C:24:38:22:F7:5C + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + X509v3 Subject Alternative Name: + ************************************************************* + WARNING: The values for DNS, email and URI are WRONG. OpenSSL + doesn't print the text after a NULL byte. + ************************************************************* + DNS:altnull.python.org, email:null@python.org, URI:http://null.python.org, IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1 + Signature Algorithm: sha1WithRSAEncryption + ac:4f:45:ef:7d:49:a8:21:70:8e:88:59:3e:d4:36:42:70:f5: + a3:bd:8b:d7:a8:d0:58:f6:31:4a:b1:a4:a6:dd:6f:d9:e8:44: + 3c:b6:0a:71:d6:7f:b1:08:61:9d:60:ce:75:cf:77:0c:d2:37: + 86:02:8d:5e:5d:f9:0f:71:b4:16:a8:c1:3d:23:1c:f1:11:b3: + 56:6e:ca:d0:8d:34:94:e6:87:2a:99:f2:ae:ae:cc:c2:e8:86: + de:08:a8:7f:c5:05:fa:6f:81:a7:82:e6:d0:53:9d:34:f4:ac: + 3e:40:fe:89:57:7a:29:a4:91:7e:0b:c6:51:31:e5:10:2f:a4: + 60:76:cd:95:51:1a:be:8b:a1:b0:fd:ad:52:bd:d7:1b:87:60: + d2:31:c7:17:c4:18:4f:2d:08:25:a3:a7:4f:b7:92:ca:e2:f5: + 25:f1:54:75:81:9d:b3:3d:61:a2:f7:da:ed:e1:c6:6f:2c:60: + 1f:d8:6f:c5:92:05:ab:c9:09:62:49:a9:14:ad:55:11:cc:d6: + 4a:19:94:99:97:37:1d:81:5f:8b:cf:a3:a8:96:44:51:08:3d: + 0b:05:65:12:eb:b6:70:80:88:48:72:4f:c6:c2:da:cf:cd:8e: + 5b:ba:97:2f:60:b4:96:56:49:5e:3a:43:76:63:04:be:2a:f6: + c1:ca:a9:94 +-----BEGIN CERTIFICATE----- +MIIE2DCCA8CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBxTELMAkGA1UEBhMCVVMx +DzANBgNVBAgMBk9yZWdvbjESMBAGA1UEBwwJQmVhdmVydG9uMSMwIQYDVQQKDBpQ +eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEgMB4GA1UECwwXUHl0aG9uIENvcmUg +RGV2ZWxvcG1lbnQxJDAiBgNVBAMMG251bGwucHl0aG9uLm9yZwBleGFtcGxlLm9y +ZzEkMCIGCSqGSIb3DQEJARYVcHl0aG9uLWRldkBweXRob24ub3JnMB4XDTEzMDgw +NzEzMTE1MloXDTEzMDgwNzEzMTI1MlowgcUxCzAJBgNVBAYTAlVTMQ8wDQYDVQQI +DAZPcmVnb24xEjAQBgNVBAcMCUJlYXZlcnRvbjEjMCEGA1UECgwaUHl0aG9uIFNv +ZnR3YXJlIEZvdW5kYXRpb24xIDAeBgNVBAsMF1B5dGhvbiBDb3JlIERldmVsb3Bt +ZW50MSQwIgYDVQQDDBtudWxsLnB5dGhvbi5vcmcAZXhhbXBsZS5vcmcxJDAiBgkq +hkiG9w0BCQEWFXB5dGhvbi1kZXZAcHl0aG9uLm9yZzCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBALXq7cn7Rn1vO3aA3TrzA5QLp6bb7B3f/yN0CJ2XFj+j +pHs+Gw6WWSUDpybiiKnPec33BFawq3kyblnBMjBU61ioy5HwQqVkJ8vUVjGIUq3P +vX/wBmQfzCe4o4uM89gpHyUL9UYGG8oCRa17dgqcv7u5rg0Wq2B1rgY+nHwx3JIv +KRrgSwyRkGzpN8WQ1yrXlxWjgI9de0mPVDDUlywcWze1q2kwaEPTM3hLAmD1PESA +oY/n8A/RXoeeRs9i/Pm/DGUS8ZPINXk/yOzsR/XvvkTVroIeLZqfmFpnZeF0cHzL +08LODkVJJ9zjLdT7SA4vnne4FEbAxDbKAq5qkYzaL4UCAwEAAaOB0DCBzTAMBgNV +HRMBAf8EAjAAMB0GA1UdDgQWBBSIWlXAUv9hzVKjNQ/qWpwkOCL3XDALBgNVHQ8E +BAMCBeAwgZAGA1UdEQSBiDCBhYIeYWx0bnVsbC5weXRob24ub3JnAGV4YW1wbGUu +Y29tgSBudWxsQHB5dGhvbi5vcmcAdXNlckBleGFtcGxlLm9yZ4YpaHR0cDovL251 +bGwucHl0aG9uLm9yZwBodHRwOi8vZXhhbXBsZS5vcmeHBMAAAgGHECABDbgAAAAA +AAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAKxPRe99SaghcI6IWT7UNkJw9aO9 +i9eo0Fj2MUqxpKbdb9noRDy2CnHWf7EIYZ1gznXPdwzSN4YCjV5d+Q9xtBaowT0j +HPERs1ZuytCNNJTmhyqZ8q6uzMLoht4IqH/FBfpvgaeC5tBTnTT0rD5A/olXeimk +kX4LxlEx5RAvpGB2zZVRGr6LobD9rVK91xuHYNIxxxfEGE8tCCWjp0+3ksri9SXx +VHWBnbM9YaL32u3hxm8sYB/Yb8WSBavJCWJJqRStVRHM1koZlJmXNx2BX4vPo6iW +RFEIPQsFZRLrtnCAiEhyT8bC2s/Njlu6ly9gtJZWSV46Q3ZjBL4q9sHKqZQ= +-----END CERTIFICATE----- diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 5c8df8bad86..d90e46dc49f 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -52,6 +52,7 @@ BADCERT = data_file("badcert.pem") WRONGCERT = data_file("XXXnonexisting.pem") BADKEY = data_file("badkey.pem") NOKIACERT = data_file("nokia.pem") +NULLBYTECERT = data_file("nullbytecert.pem") def handle_error(prefix): @@ -140,6 +141,27 @@ class BasicSocketTests(unittest.TestCase): ('DNS', 'projects.forum.nokia.com')) ) + def test_parse_cert_CVE_2013_4073(self): + p = ssl._ssl._test_decode_cert(NULLBYTECERT) + if support.verbose: + sys.stdout.write("\n" + pprint.pformat(p) + "\n") + subject = ((('countryName', 'US'),), + (('stateOrProvinceName', 'Oregon'),), + (('localityName', 'Beaverton'),), + (('organizationName', 'Python Software Foundation'),), + (('organizationalUnitName', 'Python Core Development'),), + (('commonName', 'null.python.org\x00example.org'),), + (('emailAddress', 'python-dev@python.org'),)) + self.assertEqual(p['subject'], subject) + self.assertEqual(p['issuer'], subject) + self.assertEqual(p['subjectAltName'], + (('DNS', 'altnull.python.org\x00example.com'), + ('email', 'null@python.org\x00user@example.org'), + ('URI', 'http://null.python.org\x00http://example.org'), + ('IP Address', '192.0.2.1'), + ('IP Address', '2001:DB8:0:0:0:0:0:1\n')) + ) + def test_DER_to_PEM(self): with open(SVN_PYTHON_ORG_ROOT_CERT, 'r') as f: pem = f.read() @@ -271,6 +293,13 @@ class BasicSocketTests(unittest.TestCase): fail(cert, 'foo.a.com') fail(cert, 'bar.foo.com') + # NULL bytes are bad, CVE-2013-4073 + cert = {'subject': ((('commonName', + 'null.python.org\x00example.org'),),)} + ok(cert, 'null.python.org\x00example.org') # or raise an error? + fail(cert, 'example.org') + fail(cert, 'null.python.org') + # Slightly fake real-world example cert = {'notAfter': 'Jun 26 21:41:46 2011 GMT', 'subject': ((('commonName', 'linuxfrz.org'),),), diff --git a/Misc/NEWS b/Misc/NEWS index 12f02b3df7d..d239d696527 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,12 @@ What's New in Python 3.2.6? Library ------- +- Issue #18709: Fix CVE-2013-4238. The SSL module now handles NULL bytes + inside subjectAltName correctly. Formerly the module has used OpenSSL's + GENERAL_NAME_print() function to get the string represention of ASN.1 + strings for ``rfc822Name`` (email), ``dNSName`` (DNS) and + ``uniformResourceIdentifier`` (URI). + - Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths before checking for a CGI script at that path. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index e9de8cad273..e273c5c42be 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -561,7 +561,7 @@ _get_peer_alt_names (X509 *certificate) { int i, j; PyObject *peer_alt_names = Py_None; - PyObject *v, *t; + PyObject *v = NULL, *t; X509_EXTENSION *ext = NULL; GENERAL_NAMES *names = NULL; GENERAL_NAME *name; @@ -616,12 +616,14 @@ _get_peer_alt_names (X509 *certificate) { ext->value->length)); for(j = 0; j < sk_GENERAL_NAME_num(names); j++) { - /* get a rendering of each name in the set of names */ + int gntype; + ASN1_STRING *as = NULL; name = sk_GENERAL_NAME_value(names, j); - if (name->type == GEN_DIRNAME) { - + gntype = name-> type; + switch (gntype) { + case GEN_DIRNAME: /* we special-case DirName as a tuple of tuples of attributes */ @@ -643,11 +645,62 @@ _get_peer_alt_names (X509 *certificate) { goto fail; } PyTuple_SET_ITEM(t, 1, v); + break; - } else { + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + /* GENERAL_NAME_print() doesn't handle NUL bytes in ASN1_string + correctly. */ + t = PyTuple_New(2); + if (t == NULL) + goto fail; + switch (gntype) { + case GEN_EMAIL: + v = PyUnicode_FromString("email"); + as = name->d.rfc822Name; + break; + case GEN_DNS: + v = PyUnicode_FromString("DNS"); + as = name->d.dNSName; + break; + case GEN_URI: + v = PyUnicode_FromString("URI"); + as = name->d.uniformResourceIdentifier; + break; + } + if (v == NULL) { + Py_DECREF(t); + goto fail; + } + PyTuple_SET_ITEM(t, 0, v); + v = PyUnicode_FromStringAndSize((char *)ASN1_STRING_data(as), + ASN1_STRING_length(as)); + if (v == NULL) { + Py_DECREF(t); + goto fail; + } + PyTuple_SET_ITEM(t, 1, v); + break; + default: /* for everything else, we use the OpenSSL print form */ - + switch (gntype) { + /* check for new general name type */ + case GEN_OTHERNAME: + case GEN_X400: + case GEN_EDIPARTY: + case GEN_IPADD: + case GEN_RID: + break; + default: + if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, + "Unknown general name type %d", + gntype) == -1) { + goto fail; + } + break; + } (void) BIO_reset(biobuf); GENERAL_NAME_print(biobuf, name); len = BIO_gets(biobuf, buf, sizeof(buf)-1); @@ -674,6 +727,7 @@ _get_peer_alt_names (X509 *certificate) { goto fail; } PyTuple_SET_ITEM(t, 1, v); + break; } /* and add that rendering to the list */ From f0746ca46376647993a47e24051a80fdf679014a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:08:04 +0200 Subject: [PATCH 02/24] Issue #16037: HTTPMessage.readheaders() raises an HTTPException when more than 100 headers are read. Adapted from patch by Jyrki Pulliainen. --- Doc/library/http.client.rst | 2 +- Lib/http/client.py | 4 ++++ Lib/test/test_httplib.py | 9 +++++++++ Misc/NEWS | 5 ++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 7423a4cd361..2f68df751c0 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -169,8 +169,8 @@ The following exceptions are raised as appropriate: A subclass of :exc:`HTTPException`. Raised if a server responds with a HTTP status code that we don't understand. -The constants defined in this module are: +The constants defined in this module are: .. data:: HTTP_PORT diff --git a/Lib/http/client.py b/Lib/http/client.py index 5466d0618d3..f398a64a9f1 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -206,6 +206,8 @@ MAXAMOUNT = 1048576 # maximal line length when calling readline(). _MAXLINE = 65536 +_MAXHEADERS = 100 + class HTTPMessage(email.message.Message): # XXX The only usage of this method is in @@ -253,6 +255,8 @@ def parse_headers(fp, _class=HTTPMessage): if len(line) > _MAXLINE: raise LineTooLong("header line") headers.append(line) + if len(headers) > _MAXHEADERS: + raise HTTPException("got more than %d headers" % _MAXHEADERS) if line in (b'\r\n', b'\n', b''): break hstring = b''.join(headers).decode('iso-8859-1') diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 420302c332c..226e79eccac 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -272,6 +272,15 @@ class BasicTest(TestCase): if resp.read(): self.fail("Did not expect response from HEAD request") + def test_too_many_headers(self): + headers = '\r\n'.join('Header%d: foo' % i + for i in range(client._MAXHEADERS + 1)) + '\r\n' + text = ('HTTP/1.1 200 OK\r\n' + headers) + s = FakeSocket(text) + r = client.HTTPResponse(s) + self.assertRaisesRegex(client.HTTPException, + r"got more than \d+ headers", r.begin) + def test_send_file(self): expected = (b'GET /foo HTTP/1.1\r\nHost: example.com\r\n' b'Accept-Encoding: identity\r\nContent-Length:') diff --git a/Misc/NEWS b/Misc/NEWS index d239d696527..67afa146517 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1,4 +1,4 @@ -++++++++++ ++++++++++++ Python News +++++++++++ @@ -10,6 +10,9 @@ What's New in Python 3.2.6? Library ------- +- Issue #16037: HTTPMessage.readheaders() raises an HTTPException when more than + 100 headers are read. Adapted from patch by Jyrki Pulliainen. + - Issue #18709: Fix CVE-2013-4238. The SSL module now handles NULL bytes inside subjectAltName correctly. Formerly the module has used OpenSSL's GENERAL_NAME_print() function to get the string represention of ASN.1 From c9cb18d3f7e5bf03220c213183ff0caa75905bdd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:12:24 +0200 Subject: [PATCH 03/24] =?UTF-8?q?Issue=20#16038:=20CVE-2013-1752:=20ftplib?= =?UTF-8?q?:=20Limit=20amount=20of=20data=20read=20by=20limiting=20the=20c?= =?UTF-8?q?all=20to=20readline().=20=20Original=20patch=20by=20Micha=C5=82?= =?UTF-8?q?=20Jastrz=C4=99bski=20and=20Giampaolo=20Rodola.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/ftplib.py | 23 ++++++++++++++++++----- Lib/test/test_ftplib.py | 22 +++++++++++++++++++++- Misc/NEWS | 4 ++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 8b733cf9c11..74ae1ab92c4 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -49,6 +49,8 @@ MSG_OOB = 0x1 # Process data out of band # The standard FTP server control port FTP_PORT = 21 +# The sizehint parameter passed to readline() calls +MAXLINE = 8192 # Exception raised when an error or invalid response is received @@ -96,6 +98,7 @@ class FTP: debugging = 0 host = '' port = FTP_PORT + maxline = MAXLINE sock = None file = None welcome = None @@ -190,7 +193,9 @@ class FTP: # Internal: return one line from the server, stripping CRLF. # Raise EOFError if the connection is closed def getline(self): - line = self.file.readline() + line = self.file.readline(self.maxline + 1) + if len(line) > self.maxline: + raise Error("got more than %d bytes" % self.maxline) if self.debugging > 1: print('*get*', self.sanitize(line)) if not line: raise EOFError @@ -444,7 +449,9 @@ class FTP: with self.transfercmd(cmd) as conn, \ conn.makefile('r', encoding=self.encoding) as fp: while 1: - line = fp.readline() + line = fp.readline(self.maxline + 1) + if len(line) > self.maxline: + raise Error("got more than %d bytes" % self.maxline) if self.debugging > 2: print('*retr*', repr(line)) if not line: break @@ -494,7 +501,9 @@ class FTP: self.voidcmd('TYPE A') with self.transfercmd(cmd) as conn: while 1: - buf = fp.readline() + buf = fp.readline(self.maxline + 1) + if len(buf) > self.maxline: + raise Error("got more than %d bytes" % self.maxline) if not buf: break if buf[-2:] != B_CRLF: if buf[-1] in B_CRLF: buf = buf[:-1] @@ -741,7 +750,9 @@ else: fp = conn.makefile('r', encoding=self.encoding) try: while 1: - line = fp.readline() + line = fp.readline(self.maxline + 1) + if len(line) > self.maxline: + raise Error("got more than %d bytes" % self.maxline) if self.debugging > 2: print('*retr*', repr(line)) if not line: break @@ -779,7 +790,9 @@ else: conn = self.transfercmd(cmd) try: while 1: - buf = fp.readline() + buf = fp.readline(self.maxline + 1) + if len(buf) > self.maxline: + raise Error("got more than %d bytes" % self.maxline) if not buf: break if buf[-2:] != B_CRLF: if buf[-1] in B_CRLF: buf = buf[:-1] diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index 71bc23e615a..79a4ec7f961 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -70,6 +70,7 @@ class DummyFTPHandler(asynchat.async_chat): self.last_received_data = '' self.next_response = '' self.rest = None + self.next_retr_data = RETR_DATA self.push('220 welcome') def collect_incoming_data(self, data): @@ -199,7 +200,7 @@ class DummyFTPHandler(asynchat.async_chat): offset = int(self.rest) else: offset = 0 - self.dtp.push(RETR_DATA[offset:]) + self.dtp.push(self.next_retr_data[offset:]) self.dtp.close_when_done() self.rest = None @@ -213,6 +214,11 @@ class DummyFTPHandler(asynchat.async_chat): self.dtp.push(NLST_DATA) self.dtp.close_when_done() + def cmd_setlongretr(self, arg): + # For testing. Next RETR will return long line. + self.next_retr_data = 'x' * int(arg) + self.push('125 setlongretr ok') + class DummyFTPServer(asyncore.dispatcher, threading.Thread): @@ -628,6 +634,20 @@ class TestFTPClass(TestCase): self.assertEqual(ftplib.parse257('257 "/foo/b""ar"'), '/foo/b"ar') self.assertEqual(ftplib.parse257('257 "/foo/b""ar" created'), '/foo/b"ar') + def test_line_too_long(self): + self.assertRaises(ftplib.Error, self.client.sendcmd, + 'x' * self.client.maxline * 2) + + def test_retrlines_too_long(self): + self.client.sendcmd('SETLONGRETR %d' % (self.client.maxline * 2)) + received = [] + self.assertRaises(ftplib.Error, + self.client.retrlines, 'retr', received.append) + + def test_storlines_too_long(self): + f = io.BytesIO(b'x' * self.client.maxline * 2) + self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f) + class TestIPv6Environment(TestCase): diff --git a/Misc/NEWS b/Misc/NEWS index 67afa146517..9f1107cc784 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ What's New in Python 3.2.6? Library ------- +- Issue #16038: CVE-2013-1752: ftplib: Limit amount of data read by + limiting the call to readline(). Original patch by MichaÅ‚ + JastrzÄ™bski and Giampaolo Rodola. + - Issue #16037: HTTPMessage.readheaders() raises an HTTPException when more than 100 headers are read. Adapted from patch by Jyrki Pulliainen. From 70088f14ad33e9f6e1734513594f83d42880e885 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 29 Oct 2013 21:08:56 +0100 Subject: [PATCH 04/24] Issue #18747: document issue with OpenSSL's CPRNG state and fork --- Doc/library/os.rst | 4 ++++ Doc/library/ssl.rst | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 715f6543b74..ebba21ac460 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1809,6 +1809,10 @@ written in Python, such as a mail server's external command delivery program. Note that some platforms including FreeBSD <= 6.3, Cygwin and OS/2 EMX have known issues when using fork() from a thread. + .. warning:: + + See :mod:`ssl` for applications that use the SSL module with fork(). + Availability: Unix. diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 0f5cea2f0b2..56fcc524c89 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -28,6 +28,14 @@ probably additional platforms, as long as OpenSSL is installed on that platform. operating system socket APIs. The installed version of OpenSSL may also cause variations in behavior. +.. warning:: + + OpenSSL's internal random number generator does not properly handle fork. + Applications must change the PRNG state of the parent process if they use + any SSL feature with with :func:`os.fork`. Any successful call of + :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or + :func:`~ssl.RAND_pseudo_bytes` is sufficient. + This section documents the objects and functions in the ``ssl`` module; for more general information about TLS, SSL, and certificates, the reader is referred to the documents in the "See Also" section at the bottom. From 210ee47e3340d8e689d8cce584e7c918d368f16b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:18:02 +0200 Subject: [PATCH 05/24] Issue #16042: CVE-2013-1752: smtplib: Limit amount of data read by limiting the call to readline(). Original patch by Christian Heimes. --- Lib/smtplib.py | 5 ++++- Lib/test/mock_socket.py | 9 +++++++-- Lib/test/test_smtplib.py | 30 +++++++++++++++++++++++++++++- Misc/NEWS | 3 +++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 679e4782950..b03533e1f70 100644 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -62,6 +62,7 @@ SMTP_PORT = 25 SMTP_SSL_PORT = 465 CRLF = "\r\n" bCRLF = b"\r\n" +_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3 OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) @@ -363,7 +364,7 @@ class SMTP: self.file = self.sock.makefile('rb') while 1: try: - line = self.file.readline() + line = self.file.readline(_MAXLINE + 1) except socket.error as e: self.close() raise SMTPServerDisconnected("Connection unexpectedly closed: " @@ -373,6 +374,8 @@ class SMTP: raise SMTPServerDisconnected("Connection unexpectedly closed") if self.debuglevel > 0: print('reply:', repr(line), file=stderr) + if len(line) > _MAXLINE: + raise SMTPResponseException(500, "Line too long.") resp.append(line[4:].strip(b' \t\r\n')) code = line[:3] # Check that the error code is syntactically correct. diff --git a/Lib/test/mock_socket.py b/Lib/test/mock_socket.py index 803693283b3..7e748b50653 100644 --- a/Lib/test/mock_socket.py +++ b/Lib/test/mock_socket.py @@ -21,8 +21,13 @@ class MockFile: """ def __init__(self, lines): self.lines = lines - def readline(self): - return self.lines.pop(0) + b'\r\n' + def readline(self, limit=-1): + result = self.lines.pop(0) + b'\r\n' + if limit >= 0: + # Re-insert the line, removing the \r\n we added. + self.lines.insert(0, result[limit:-2]) + result = result[:limit] + return result def close(self): pass diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index ec971ea52cc..77995802c24 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -537,6 +537,33 @@ class BadHELOServerTests(unittest.TestCase): HOST, self.port, 'localhost', 3) +@unittest.skipUnless(threading, 'Threading required for this test.') +class TooLongLineTests(unittest.TestCase): + respdata = b'250 OK' + (b'.' * smtplib._MAXLINE * 2) + b'\n' + + def setUp(self): + self.old_stdout = sys.stdout + self.output = io.StringIO() + sys.stdout = self.output + + self.evt = threading.Event() + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.settimeout(15) + self.port = support.bind_port(self.sock) + servargs = (self.evt, self.respdata, self.sock) + threading.Thread(target=server, args=servargs).start() + self.evt.wait() + self.evt.clear() + + def tearDown(self): + self.evt.wait() + sys.stdout = self.old_stdout + + def testLineTooLong(self): + self.assertRaises(smtplib.SMTPResponseException, smtplib.SMTP, + HOST, self.port, 'localhost', 3) + + sim_users = {'Mr.A@somewhere.com':'John A', 'Ms.B@xn--fo-fka.com':'Sally B', 'Mrs.C@somewhereesle.com':'Ruth C', @@ -826,7 +853,8 @@ class SMTPSimTests(unittest.TestCase): def test_main(verbose=None): support.run_unittest(GeneralTests, DebuggingServerTests, NonConnectingTests, - BadHELOServerTests, SMTPSimTests) + BadHELOServerTests, SMTPSimTests, + TooLongLineTests) if __name__ == '__main__': test_main() diff --git a/Misc/NEWS b/Misc/NEWS index 9f1107cc784..1de9c10cbc3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.2.6? Library ------- +- Issue #16042: CVE-2013-1752: smtplib: Limit amount of data read by + limiting the call to readline(). Original patch by Christian Heimes. + - Issue #16038: CVE-2013-1752: ftplib: Limit amount of data read by limiting the call to readline(). Original patch by MichaÅ‚ JastrzÄ™bski and Giampaolo Rodola. From eaca8616ab0e219ebb5cf37d495f4bf336ec0f5e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:45:39 +0200 Subject: [PATCH 06/24] Issue #16041: CVE-2013-1752: poplib: Limit maximum line lengths to 2048 to prevent readline() calls from consuming too much memory. Patch by Jyrki Pulliainen. --- Lib/poplib.py | 11 ++++++++++- Lib/test/test_poplib.py | 6 +++++- Misc/NEWS | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/poplib.py b/Lib/poplib.py index 84ea88de46b..13e0f716e49 100644 --- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -32,6 +32,12 @@ CR = b'\r' LF = b'\n' CRLF = CR+LF +# maximal line length when calling readline(). This is to prevent +# reading arbitrary lenght lines. RFC 1939 limits POP3 line length to +# 512 characters, including CRLF. We have selected 2048 just to be on +# the safe side. +_MAXLINE = 2048 + class POP3: @@ -107,7 +113,10 @@ class POP3: # Raise error_proto('-ERR EOF') if the connection is closed. def _getline(self): - line = self.file.readline() + line = self.file.readline(_MAXLINE + 1) + if len(line) > _MAXLINE: + raise error_proto('line too long') + if self._debugging > 1: print('*get*', repr(line)) if not line: raise error_proto('-ERR EOF') octets = len(line) diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index e3901b886ca..17b22611fa6 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -83,7 +83,7 @@ class DummyPOP3Handler(asynchat.async_chat): def cmd_list(self, arg): if arg: - self.push('+OK %s %s' %(arg, arg)) + self.push('+OK %s %s' % (arg, arg)) else: self.push('+OK') asynchat.async_chat.push(self, LIST_RESP) @@ -204,6 +204,10 @@ class TestPOP3Class(TestCase): foo = self.client.retr('foo') self.assertEqual(foo, expected) + def test_too_long_lines(self): + self.assertRaises(poplib.error_proto, self.client._shortcmd, + 'echo +%s' % ((poplib._MAXLINE + 10) * 'a')) + def test_dele(self): self.assertOK(self.client.dele('foo')) diff --git a/Misc/NEWS b/Misc/NEWS index 1de9c10cbc3..44b0e96410f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ What's New in Python 3.2.6? Library ------- +- Issue #16041: CVE-2013-1752: poplib: Limit maximum line lengths to 2048 to + prevent readline() calls from consuming too much memory. Patch by Jyrki + Pulliainen. + - Issue #16042: CVE-2013-1752: smtplib: Limit amount of data read by limiting the call to readline(). Original patch by Christian Heimes. From 21bf3f942be920f3b051f6af43f7c37b9aa5cff3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:54:39 +0200 Subject: [PATCH 07/24] Issue #22517: When a io.BufferedRWPair object is deallocated, clear its weakrefs. --- Lib/test/test_io.py | 6 ++++++ Misc/NEWS | 3 +++ Modules/_io/bufferedio.c | 2 ++ 3 files changed, 11 insertions(+) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 8ef314e55d2..72a844feac1 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1454,6 +1454,12 @@ class BufferedRWPairTest(unittest.TestCase): pair = self.tp(SelectableIsAtty(True), SelectableIsAtty(True)) self.assertTrue(pair.isatty()) + def test_weakref_clearing(self): + brw = self.tp(self.MockRawIO(), self.MockRawIO()) + ref = weakref.ref(brw) + brw = None + ref = None # Shouldn't segfault. + class CBufferedRWPairTest(BufferedRWPairTest): tp = io.BufferedRWPair diff --git a/Misc/NEWS b/Misc/NEWS index 44b0e96410f..c6df72b0bb0 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.2.6? Library ------- +- Issue #22517: When a io.BufferedRWPair object is deallocated, clear its + weakrefs. + - Issue #16041: CVE-2013-1752: poplib: Limit maximum line lengths to 2048 to prevent readline() calls from consuming too much memory. Patch by Jyrki Pulliainen. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index a8278d466ce..f788e5cedec 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2141,6 +2141,8 @@ static void bufferedrwpair_dealloc(rwpair *self) { _PyObject_GC_UNTRACK(self); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *)self); Py_CLEAR(self->reader); Py_CLEAR(self->writer); Py_CLEAR(self->dict); From 860c367c29eb557930099a7cc7fe297a259275f6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:56:46 +0200 Subject: [PATCH 08/24] Issue #22419: Limit the length of incoming HTTP request in wsgiref server to 65536 bytes and send a 414 error code for higher lengths. Patch contributed by Devin Cook. --- Lib/test/test_wsgiref.py | 5 +++++ Lib/wsgiref/simple_server.py | 9 ++++++++- Misc/ACKS | 1 + Misc/NEWS | 4 ++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index 08f8d9a6043..c0bfaa838a7 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -114,6 +114,11 @@ class IntegrationTests(TestCase): out, err = run_amock() self.check_hello(out) + def test_request_length(self): + out, err = run_amock(data=b"GET " + (b"x" * 65537) + b" HTTP/1.0\n\n") + self.assertEqual(out.splitlines()[0], + b"HTTP/1.0 414 Request-URI Too Long") + def test_validated_hello(self): out, err = run_amock(validator(hello_app)) # the middleware doesn't support len(), so content-length isn't there diff --git a/Lib/wsgiref/simple_server.py b/Lib/wsgiref/simple_server.py index af82f953c53..9c4a83d8982 100644 --- a/Lib/wsgiref/simple_server.py +++ b/Lib/wsgiref/simple_server.py @@ -114,7 +114,14 @@ class WSGIRequestHandler(BaseHTTPRequestHandler): def handle(self): """Handle a single HTTP request""" - self.raw_requestline = self.rfile.readline() + self.raw_requestline = self.rfile.readline(65537) + if len(self.raw_requestline) > 65536: + self.requestline = '' + self.request_version = '' + self.command = '' + self.send_error(414) + return + if not self.parse_request(): # An error code has been sent, just exit return diff --git a/Misc/ACKS b/Misc/ACKS index c1df48054f5..c183dc78f3d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -219,6 +219,7 @@ Denver Coneybeare Geremy Condra Juan José Conti Matt Conway +Devin Cook David M. Cooke Jason R. Coombs Garrett Cooper diff --git a/Misc/NEWS b/Misc/NEWS index c6df72b0bb0..d8e61c30388 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ What's New in Python 3.2.6? Library ------- +- Issue #22419: Limit the length of incoming HTTP request in wsgiref server to + 65536 bytes and send a 414 error code for higher lengths. Patch contributed + by Devin Cook. + - Issue #22517: When a io.BufferedRWPair object is deallocated, clear its weakrefs. From dad182c16e6c9d10267a659bd376ba3d10affd4f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 17 Sep 2014 00:23:55 +0200 Subject: [PATCH 09/24] Lax cookie parsing in http.cookies could be a security issue when combined with non-standard cookie handling in some Web browsers. Reported by Sergey Bobrov. --- Lib/http/cookies.py | 3 ++- Lib/test/test_http_cookies.py | 9 +++++++++ Misc/ACKS | 1 + Misc/NEWS | 4 ++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py index ddbcbf877bf..28c11615829 100644 --- a/Lib/http/cookies.py +++ b/Lib/http/cookies.py @@ -432,6 +432,7 @@ class Morsel(dict): _LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" _CookiePattern = re.compile(r""" (?x) # This is a verbose pattern + \s* # Optional whitespace at start of cookie (?P # Start of group 'key' """ + _LegalCharsPatt + r"""+? # Any word of at least one letter ) # End of group 'key' @@ -532,7 +533,7 @@ class BaseCookie(dict): while 0 <= i < n: # Start looking for a cookie - match = patt.search(str, i) + match = patt.match(str, i) if not match: # No more cookies break diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py index 1f1ca5852ef..30d48983c69 100644 --- a/Lib/test/test_http_cookies.py +++ b/Lib/test/test_http_cookies.py @@ -132,6 +132,15 @@ class CookieTests(unittest.TestCase): """) + def test_invalid_cookies(self): + # Accepting these could be a security issue + C = cookies.SimpleCookie() + for s in (']foo=x', '[foo=x', 'blah]foo=x', 'blah[foo=x'): + C.load(s) + self.assertEqual(dict(C), {}) + self.assertEqual(C.output(), '') + + class MorselTests(unittest.TestCase): """Tests for the Morsel object.""" diff --git a/Misc/ACKS b/Misc/ACKS index c183dc78f3d..428fd01e8a5 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -117,6 +117,7 @@ Martin Bless Pablo Bleyer Erik van Blokland Eric Blossom +Sergey Bobrov Finn Bock Paul Boddie Matthew Boedicker diff --git a/Misc/NEWS b/Misc/NEWS index d8e61c30388..398ed294cfe 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -37,6 +37,10 @@ Library strings for ``rfc822Name`` (email), ``dNSName`` (DNS) and ``uniformResourceIdentifier`` (URI). +- Lax cookie parsing in http.cookies could be a security issue when combined + with non-standard cookie handling in some Web browsers. Reported by + Sergey Bobrov. + - Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths before checking for a CGI script at that path. From 0840b415829f7fab8db48e1b38bbbfc7da2df8c0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 17 Sep 2014 13:17:58 +0800 Subject: [PATCH 10/24] Issue #22421 - Secure pydoc server run. Bind it to localhost instead of all interfaces. --- Lib/pydoc.py | 4 ++-- Lib/test/test_pydoc.py | 2 ++ Misc/NEWS | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index fa02edaffca..2a0cbf341ca 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -2431,8 +2431,8 @@ def _start_server(urlhandler, port): class DocServer(http.server.HTTPServer): def __init__(self, port, callback): - self.host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost' - self.address = ('', port) + self.host = 'localhost' + self.address = (self.host, port) self.callback = callback self.base.__init__(self, self.address, self.handler) self.quit = False diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 42a4089940c..b632434e9ab 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -510,6 +510,8 @@ class PydocServerTest(unittest.TestCase): return text serverthread = pydoc._start_server(my_url_handler, port=0) + self.assertIn('localhost', serverthread.docserver.address) + starttime = time.time() timeout = 1 #seconds diff --git a/Misc/NEWS b/Misc/NEWS index 398ed294cfe..60946e816be 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.2.6? Library ------- +- Issue #22421: Fix a regression that caused the pydoc server to be bound to + all interfaces instead of only localhost. + - Issue #22419: Limit the length of incoming HTTP request in wsgiref server to 65536 bytes and send a 414 error code for higher lengths. Patch contributed by Devin Cook. From fd9262cf2a044f6909530cdb565f38e13ff98263 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 16:00:09 +0200 Subject: [PATCH 11/24] Issue #16039: CVE-2013-1752: Change use of readline in imaplib module to limit line length. Patch by Emil Lind. --- Lib/imaplib.py | 14 +++++++++++++- Lib/test/test_imaplib.py | 11 +++++++++++ Misc/NEWS | 3 +++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Lib/imaplib.py b/Lib/imaplib.py index e2a05818fa6..9950761c091 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -42,6 +42,15 @@ IMAP4_PORT = 143 IMAP4_SSL_PORT = 993 AllowedVersions = ('IMAP4REV1', 'IMAP4') # Most recent first +# Maximal line length when calling readline(). This is to prevent +# reading arbitrary length lines. RFC 3501 and 2060 (IMAP 4rev1) +# don't specify a line length. RFC 2683 however suggests limiting client +# command lines to 1000 octets and server command lines to 8000 octets. +# We have selected 10000 for some extra margin and since that is supposedly +# also what UW and Panda IMAP does. +_MAXLINE = 10000 + + # Commands Commands = { @@ -263,7 +272,10 @@ class IMAP4: def readline(self): """Read line from remote.""" - return self.file.readline() + line = self.file.readline(_MAXLINE + 1) + if len(line) > _MAXLINE: + raise self.error("got more than %d bytes" % _MAXLINE) + return line def send(self, data): diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 6b29943ebda..62feea70afc 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -309,6 +309,17 @@ class BaseThreadedNetworkedTests(unittest.TestCase): self.assertEqual(ret, "OK") + def test_linetoolong(self): + class TooLongHandler(SimpleIMAPHandler): + def handle(self): + # Send a very long response line + self.wfile.write(b'* OK ' + imaplib._MAXLINE*b'x' + b'\r\n') + + with self.reaped_server(TooLongHandler) as server: + self.assertRaises(imaplib.IMAP4.error, + self.imap_class, *server.server_address) + + class ThreadedNetworkedTests(BaseThreadedNetworkedTests): server_class = socketserver.TCPServer diff --git a/Misc/NEWS b/Misc/NEWS index 60946e816be..b72b7a3e10f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.2.6? Library ------- +- Issue #16039: CVE-2013-1752: Change use of readline in imaplib module to limit + line length. Patch by Emil Lind. + - Issue #22421: Fix a regression that caused the pydoc server to be bound to all interfaces instead of only localhost. From e558181660d096e3e53c795bc5a528cdbd04eca6 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Wed, 26 Mar 2014 23:31:39 -0700 Subject: [PATCH 12/24] Issue #20939: Use www.example.com instead of www.python.org to avoid test failures when ssl is not present. --- Lib/test/test_urllib2net.py | 10 +++++----- Lib/test/test_urllibnet.py | 26 +++++++++++++------------- Misc/NEWS | 7 +++++++ 3 files changed, 25 insertions(+), 18 deletions(-) mode change 100644 => 100755 Lib/test/test_urllib2net.py mode change 100644 => 100755 Lib/test/test_urllibnet.py diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py old mode 100644 new mode 100755 index feeaf24e8a3..2c832e1f489 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -84,7 +84,7 @@ class CloseSocketTest(unittest.TestCase): # calling .close() on urllib2's response objects should close the # underlying socket - response = _urlopen_with_retry("http://www.python.org/") + response = _urlopen_with_retry("http://www.example.com/") sock = response.fp self.assertTrue(not sock.closed) response.close() @@ -254,7 +254,7 @@ class OtherNetworkTests(unittest.TestCase): class TimeoutTest(unittest.TestCase): def test_http_basic(self): self.assertTrue(socket.getdefaulttimeout() is None) - url = "http://www.python.org" + url = "http://www.example.com" with support.transient_internet(url, timeout=None): u = _urlopen_with_retry(url) self.addCleanup(u.close) @@ -262,7 +262,7 @@ class TimeoutTest(unittest.TestCase): def test_http_default_timeout(self): self.assertTrue(socket.getdefaulttimeout() is None) - url = "http://www.python.org" + url = "http://www.example.com" with support.transient_internet(url): socket.setdefaulttimeout(60) try: @@ -274,7 +274,7 @@ class TimeoutTest(unittest.TestCase): def test_http_no_timeout(self): self.assertTrue(socket.getdefaulttimeout() is None) - url = "http://www.python.org" + url = "http://www.example.com" with support.transient_internet(url): socket.setdefaulttimeout(60) try: @@ -285,7 +285,7 @@ class TimeoutTest(unittest.TestCase): self.assertTrue(u.fp.raw._sock.gettimeout() is None) def test_http_timeout(self): - url = "http://www.python.org" + url = "http://www.example.com" with support.transient_internet(url): u = _urlopen_with_retry(url, timeout=120) self.addCleanup(u.close) diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py old mode 100644 new mode 100755 index 573c0c15d54..1b6dee261fc --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -24,8 +24,8 @@ class URLTimeoutTest(unittest.TestCase): socket.setdefaulttimeout(None) def testURLread(self): - with support.transient_internet("www.python.org"): - f = urllib.request.urlopen("http://www.python.org/") + with support.transient_internet("www.example.com"): + f = urllib.request.urlopen("http://www.example.com/") x = f.read() @@ -38,7 +38,7 @@ class urlopenNetworkTests(unittest.TestCase): for transparent redirection have been written. setUp is not used for always constructing a connection to - http://www.python.org/ since there a few tests that don't use that address + http://www.example.com/ since there a few tests that don't use that address and making a connection is expensive enough to warrant minimizing unneeded connections. @@ -56,7 +56,7 @@ class urlopenNetworkTests(unittest.TestCase): def test_basic(self): # Simple test expected to pass. - with self.urlopen("http://www.python.org/") as open_url: + with self.urlopen("http://www.example.com/") as open_url: for attr in ("read", "readline", "readlines", "fileno", "close", "info", "geturl"): self.assertTrue(hasattr(open_url, attr), "object returned from " @@ -65,7 +65,7 @@ class urlopenNetworkTests(unittest.TestCase): def test_readlines(self): # Test both readline and readlines. - with self.urlopen("http://www.python.org/") as open_url: + with self.urlopen("http://www.example.com/") as open_url: self.assertIsInstance(open_url.readline(), bytes, "readline did not return a string") self.assertIsInstance(open_url.readlines(), list, @@ -73,7 +73,7 @@ class urlopenNetworkTests(unittest.TestCase): def test_info(self): # Test 'info'. - with self.urlopen("http://www.python.org/") as open_url: + with self.urlopen("http://www.example.com/") as open_url: info_obj = open_url.info() self.assertIsInstance(info_obj, email.message.Message, "object returned by 'info' is not an " @@ -82,14 +82,14 @@ class urlopenNetworkTests(unittest.TestCase): def test_geturl(self): # Make sure same URL as opened is returned by geturl. - URL = "http://www.python.org/" + URL = "http://www.example.com/" with self.urlopen(URL) as open_url: gotten_url = open_url.geturl() self.assertEqual(gotten_url, URL) def test_getcode(self): # test getcode() with the fancy opener to get 404 error codes - URL = "http://www.python.org/XXXinvalidXXX" + URL = "http://www.example.com/XXXinvalidXXX" with support.transient_internet(URL): open_url = urllib.request.FancyURLopener().open(URL) try: @@ -104,7 +104,7 @@ class urlopenNetworkTests(unittest.TestCase): # test can't pass on Windows. return # Make sure fd returned by fileno is valid. - with self.urlopen("http://www.python.org/", timeout=None) as open_url: + with self.urlopen("http://www.example.com/", timeout=None) as open_url: fd = open_url.fileno() with os.fdopen(fd, 'rb') as f: self.assertTrue(f.read(), "reading from file created using fd " @@ -148,7 +148,7 @@ class urlretrieveNetworkTests(unittest.TestCase): def test_basic(self): # Test basic functionality. - with self.urlretrieve("http://www.python.org/") as (file_location, info): + with self.urlretrieve("http://www.example.com/") as (file_location, info): self.assertTrue(os.path.exists(file_location), "file location returned by" " urlretrieve is not a valid path") with open(file_location, 'rb') as f: @@ -157,7 +157,7 @@ class urlretrieveNetworkTests(unittest.TestCase): def test_specified_path(self): # Make sure that specifying the location of the file to write to works. - with self.urlretrieve("http://www.python.org/", + with self.urlretrieve("http://www.example.com/", support.TESTFN) as (file_location, info): self.assertEqual(file_location, support.TESTFN) self.assertTrue(os.path.exists(file_location)) @@ -166,12 +166,12 @@ class urlretrieveNetworkTests(unittest.TestCase): def test_header(self): # Make sure header returned as 2nd value from urlretrieve is good. - with self.urlretrieve("http://www.python.org/") as (file_location, info): + with self.urlretrieve("http://www.example.com/") as (file_location, info): self.assertIsInstance(info, email.message.Message, "info is not an instance of email.message.Message") def test_data_header(self): - logo = "http://www.python.org/static/community_logos/python-logo-master-v3-TM.png" + logo = "http://www.example.com/" with self.urlretrieve(logo) as (file_location, fileheaders): datevalue = fileheaders.get('Date') dateformat = '%a, %d %b %Y %H:%M:%S GMT' diff --git a/Misc/NEWS b/Misc/NEWS index b72b7a3e10f..babfb14e245 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -76,6 +76,13 @@ Library - Issue #21323: Fix http.server to again handle scripts in CGI subdirectories, broken by the fix for security issue #19435. Patch by Zach Byrne. +Tests +----- + +- Issue #20939: Avoid various network test failures due to new + redirect of http://www.python.org/ to https://www.python.org: + use http://www.example.com instead. + What's New in Python 3.2.5? =========================== From 786c8e7dd565aecafb020683705564c42e62fedc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 16:31:21 +0200 Subject: [PATCH 13/24] Fix-up for 0f362676460d: add missing size argument to SSLFakeFile.readline(), as in 2.6 backport 8a6def3add5b --- Lib/smtplib.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) mode change 100644 => 100755 Lib/smtplib.py diff --git a/Lib/smtplib.py b/Lib/smtplib.py old mode 100644 new mode 100755 index b03533e1f70..cb36d2869e3 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -189,10 +189,14 @@ else: def __init__(self, sslobj): self.sslobj = sslobj - def readline(self): + def readline(self, size=-1): + if size < 0: + size = None str = b"" chr = None while chr != b"\n": + if size is not None and len(str) > size: + break chr = self.sslobj.read(1) if not chr: break From 4615076471404c5e586d90a68ef2f26fc2201c84 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 16:41:11 +0200 Subject: [PATCH 14/24] Add a dummy "touch" target to the Makefile so that the custom buildbots can test this branch. --- Makefile.pre.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index e4d50901720..5260e6b27c5 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1371,6 +1371,9 @@ patchcheck: Python/thread.o: @THREADHEADERS@ +# A dummy target so that we are "buildbot step compatible" with newer versions +touch: + # Declare targets that aren't real files .PHONY: all build_all sharedmods oldsharedmods test quicktest memtest .PHONY: install altinstall oldsharedinstall bininstall altbininstall @@ -1378,7 +1381,7 @@ Python/thread.o: @THREADHEADERS@ .PHONY: frameworkinstall frameworkinstallframework frameworkinstallstructure .PHONY: frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools .PHONY: frameworkaltinstallunixtools recheck autoconf clean clobber distclean -.PHONY: smelly funny patchcheck altmaninstall +.PHONY: smelly funny patchcheck altmaninstall touch .PHONY: gdbhooks # IF YOU PUT ANYTHING HERE IT WILL GO AWAY From 3bc35672a23a4505ffe5b6eeb822125dce9db770 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 17:30:18 +0200 Subject: [PATCH 15/24] Backport b533cc11d114 to fix intermittent test_urllibnet failures. --- Lib/test/test_urllibnet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index 1b6dee261fc..3e47d267167 100755 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -104,7 +104,7 @@ class urlopenNetworkTests(unittest.TestCase): # test can't pass on Windows. return # Make sure fd returned by fileno is valid. - with self.urlopen("http://www.example.com/", timeout=None) as open_url: + with self.urlopen("http://www.google.com/", timeout=None) as open_url: fd = open_url.fileno() with os.fdopen(fd, 'rb') as f: self.assertTrue(f.read(), "reading from file created using fd " From 51c116223e7698ce3cbbac46e8a6779aea8ec9c6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 19:34:19 +0200 Subject: [PATCH 16/24] Issue #19855: uuid.getnode() on Unix now looks on the PATH for the executables used to find the mac address, with /sbin and /usr/sbin as fallbacks. Issue #11508: Fixed uuid.getnode() and uuid.uuid1() on environment with virtual interface. Original patch by Kent Frazier. Issue #18784: The uuid module no more attempts to load libc via ctypes.CDLL, if all necessary functions are already found in libuuid. Patch by Evgeny Sologubov. Issue #16102: Make uuid._netbios_getnode() work again on Python 3. --- Lib/test/test_uuid.py | 21 +++++++++++++++++++ Lib/uuid.py | 47 ++++++++++++++++++++++++++++--------------- Misc/ACKS | 2 ++ Misc/NEWS | 13 ++++++++++++ 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 43fa6561023..7ba09677a70 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -1,6 +1,8 @@ from unittest import TestCase from test import support import builtins +import io +import os import uuid def importable(name): @@ -360,6 +362,25 @@ class TestUUID(TestCase): self.assertEqual(node1, node2) + def test_find_mac(self): + data = '''\ + +fake hwaddr +cscotun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 +eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab +''' + def mock_popen(cmd): + return io.StringIO(data) + + with support.swap_attr(os, 'popen', mock_popen): + mac = uuid._find_mac( + command='ifconfig', + args='', + hw_identifiers=['hwaddr'], + get_index=lambda x: x + 1, + ) + self.assertEqual(mac, 0x1234567890ab) + def test_uuid1(self): # uuid1 requires ctypes. try: diff --git a/Lib/uuid.py b/Lib/uuid.py index 5684ad7acee..53d5da55e8f 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -313,25 +313,38 @@ class UUID(object): def _find_mac(command, args, hw_identifiers, get_index): import os - for dir in ['', '/sbin/', '/usr/sbin']: + path = os.environ.get("PATH", os.defpath).split(os.pathsep) + path.extend(('/sbin', '/usr/sbin')) + for dir in path: executable = os.path.join(dir, command) - if not os.path.exists(executable): - continue + if (os.path.exists(executable) and + os.access(executable, os.F_OK | os.X_OK) and + not os.path.isdir(executable)): + break + else: + return None - try: - # LC_ALL to get English output, 2>/dev/null to - # prevent output on stderr - cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args) - with os.popen(cmd) as pipe: - for line in pipe: - words = line.lower().split() - for i in range(len(words)): - if words[i] in hw_identifiers: + try: + # LC_ALL to ensure English output, 2>/dev/null to + # prevent output on stderr + cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args) + with os.popen(cmd) as pipe: + for line in pipe: + words = line.lower().split() + for i in range(len(words)): + if words[i] in hw_identifiers: + try: return int( words[get_index(i)].replace(':', ''), 16) - except IOError: - continue - return None + except (ValueError, IndexError): + # Virtual interfaces, such as those provided by + # VPNs, do not have a colon-delimited MAC address + # as expected, but a 16-byte HWAddr separated by + # dashes. These should be ignored in favor of a + # real MAC address + pass + except IOError: + pass def _ifconfig_getnode(): """Get the hardware address on Unix by running ifconfig.""" @@ -406,7 +419,7 @@ def _netbios_getnode(): if win32wnet.Netbios(ncb) != 0: continue status._unpack() - bytes = map(ord, status.adapter_address) + bytes = status.adapter_address return ((bytes[0]<<40) + (bytes[1]<<32) + (bytes[2]<<24) + (bytes[3]<<16) + (bytes[4]<<8) + bytes[5]) @@ -429,6 +442,8 @@ try: _uuid_generate_random = lib.uuid_generate_random if hasattr(lib, 'uuid_generate_time'): _uuid_generate_time = lib.uuid_generate_time + if _uuid_generate_random is not None: + break # found everything we were looking for # The uuid_generate_* functions are broken on MacOS X 10.5, as noted # in issue #8621 the function generates the same sequence of values diff --git a/Misc/ACKS b/Misc/ACKS index 428fd01e8a5..8887dd28bec 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -353,6 +353,7 @@ Doug Fort John Fouhy Stefan Franke Martin Franklin +Kent Frazier Robin Friedrich Bradley Froehle Ivan Frohne @@ -1028,6 +1029,7 @@ Ryan Smith-Roberts Rafal Smotrzyk Dirk Soede Paul Sokolovsky +Evgeny Sologubov Cody Somerville Clay Spence Stefan Sperling diff --git a/Misc/NEWS b/Misc/NEWS index babfb14e245..5b84414a3f9 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -47,6 +47,19 @@ Library with non-standard cookie handling in some Web browsers. Reported by Sergey Bobrov. +- Issue #19855: uuid.getnode() on Unix now looks on the PATH for the + executables used to find the mac address, with /sbin and /usr/sbin as + fallbacks. + +- Issue #11508: Fixed uuid.getnode() and uuid.uuid1() on environment with + virtual interface. Original patch by Kent Frazier. + +- Issue #18784: The uuid module no more attempts to load libc via ctypes.CDLL, + if all necessary functions are already found in libuuid. + Patch by Evgeny Sologubov. + +- Issue #16102: Make uuid._netbios_getnode() work again on Python 3. + - Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths before checking for a CGI script at that path. From ff3e5e3779ae6fab9f5d33149c95441eb847c196 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Oct 2014 19:15:11 +0200 Subject: [PATCH 17/24] Fix unicode_aswidechar() for 4b unicode and 2b wchar_t (AIX). --- Misc/NEWS | 6 ++++++ Objects/unicodeobject.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS b/Misc/NEWS index 5b84414a3f9..14d9630a8fa 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -7,6 +7,12 @@ What's New in Python 3.2.6? *Release date: TBD* +Core and Builtins +----------------- + +- Issue #19529: Fix a potential crash in converting Unicode objects to wchar_t + when Py_UNICODE is 4 bytes but wchar_t is 2 bytes, for example on AIX. + Library ------- diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index cd4e9e9295c..b7988509fbe 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -1267,7 +1267,7 @@ unicode_aswidechar(PyUnicodeObject *unicode, Py_ssize_t nchar; u = PyUnicode_AS_UNICODE(unicode); - uend = u + PyUnicode_GET_SIZE(u); + uend = u + PyUnicode_GET_SIZE(unicode); if (w != NULL) { worig = w; wend = w + size; From 4480d30b8b69ce33f7fa98d9f38cdee343d1d2f3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Oct 2014 22:31:04 +0200 Subject: [PATCH 18/24] ref #19855: skip uuid test_find_mac on non-Posix as in later branches --- Lib/test/test_uuid.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 7ba09677a70..9f7d6062383 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -1,4 +1,4 @@ -from unittest import TestCase +import unittest from test import support import builtins import io @@ -12,7 +12,7 @@ def importable(name): except: return False -class TestUUID(TestCase): +class TestUUID(unittest.TestCase): last_node = None source2node = {} @@ -362,6 +362,7 @@ class TestUUID(TestCase): self.assertEqual(node1, node2) + @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_find_mac(self): data = '''\ From e800a0e1c2b93f185580ae3926b239e2944b56d9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:15:42 +0200 Subject: [PATCH 19/24] Bump to 3.2.6rc1 --- Include/patchlevel.h | 8 ++++---- Lib/distutils/__init__.py | 2 +- Lib/idlelib/idlever.py | 2 +- Misc/NEWS | 2 +- Misc/RPM/python-3.2.spec | 2 +- README | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 4ba3b067470..b3e5422f4dc 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -18,12 +18,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 2 -#define PY_MICRO_VERSION 5 -#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL -#define PY_RELEASE_SERIAL 0 +#define PY_MICRO_VERSION 6 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA +#define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "3.2.5" +#define PY_VERSION "3.2.6rc1" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py index f9016d6d174..382e4b1c9d0 100644 --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.5" +__version__ = "3.2.6rc1" #--end constants-- diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index 4a7c3362af9..b69949dfd91 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "3.2.5" +IDLE_VERSION = "3.2.6rc1" diff --git a/Misc/NEWS b/Misc/NEWS index 14d9630a8fa..1a704df9965 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -5,7 +5,7 @@ Python News What's New in Python 3.2.6? =========================== -*Release date: TBD* +*Release date: 11-Oct-2014* Core and Builtins ----------------- diff --git a/Misc/RPM/python-3.2.spec b/Misc/RPM/python-3.2.spec index c93f6bf5139..664589ee056 100644 --- a/Misc/RPM/python-3.2.spec +++ b/Misc/RPM/python-3.2.spec @@ -39,7 +39,7 @@ %define name python #--start constants-- -%define version 3.2.5 +%define version 3.2.6rc1 %define libvers 3.2 #--end constants-- %define release 1pydotorg diff --git a/README b/README index 6d8e18c9184..50f11e057af 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ -This is Python version 3.2.5 -============================ +This is Python version 3.2.6 release candidate 1 +================================================ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, -2012, 2013 Python Software Foundation. All rights reserved. +2012, 2013, 2014 Python Software Foundation. All rights reserved. Python 3.x is a new version of the language, which is incompatible with the 2.x line of releases. The language is mostly the same, but many details, especially From edc3cbaaba1493a00572ad46daaab14e341d738e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:16:59 +0200 Subject: [PATCH 20/24] Copyright year update, add version to licenses. --- Doc/README.txt | 2 +- Doc/license.rst | 6 +++++- LICENSE | 4 +++- PC/python_nt.rc | 2 +- Python/getcopyright.c | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Doc/README.txt b/Doc/README.txt index c63fc608a8c..a16c14ac61d 100644 --- a/Doc/README.txt +++ b/Doc/README.txt @@ -135,7 +135,7 @@ The Python source is copyrighted, but you can freely use and copy it as long as you don't change or remove the copyright notice: ---------------------------------------------------------------------- -Copyright (c) 2000-2013 Python Software Foundation. +Copyright (c) 2000-2014 Python Software Foundation. All rights reserved. Copyright (c) 2000 BeOpen.com. diff --git a/Doc/license.rst b/Doc/license.rst index a3257f87d4b..32a36b9e6e2 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -122,6 +122,10 @@ been GPL-compatible; the table below summarizes the various releases. +----------------+--------------+------------+------------+-----------------+ | 3.2.4 | 3.2.3 | 2013 | PSF | yes | +----------------+--------------+------------+------------+-----------------+ +| 3.2.5 | 3.2.4 | 2013 | PSF | yes | ++----------------+--------------+------------+------------+-----------------+ +| 3.2.6 | 3.2.5 | 2014 | PSF | yes | ++----------------+--------------+------------+------------+-----------------+ .. note:: @@ -150,7 +154,7 @@ Terms and conditions for accessing or otherwise using Python analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright © 2001-2013 Python Software Foundation; All Rights + copyright, i.e., "Copyright © 2001-2014 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index b9e39274812..3fdc973b896 100644 --- a/LICENSE +++ b/LICENSE @@ -75,6 +75,8 @@ the various releases. 3.2.2 3.2.1 2011 PSF yes 3.2.3 3.2.2 2012 PSF yes 3.2.4 3.2.3 2013 PSF yes + 3.2.5 3.2.4 2013 PSF yes + 3.2.6 3.2.5 2014 PSF yes Footnotes: @@ -110,7 +112,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013 Python Software Foundation; All Rights Reserved" are retained +2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on diff --git a/PC/python_nt.rc b/PC/python_nt.rc index f31c8368782..3f557231191 100644 --- a/PC/python_nt.rc +++ b/PC/python_nt.rc @@ -61,7 +61,7 @@ BEGIN VALUE "FileDescription", "Python Core\0" VALUE "FileVersion", PYTHON_VERSION VALUE "InternalName", "Python DLL\0" - VALUE "LegalCopyright", "Copyright © 2001-2013 Python Software Foundation. Copyright © 2000 BeOpen.com. Copyright © 1995-2001 CNRI. Copyright © 1991-1995 SMC.\0" + VALUE "LegalCopyright", "Copyright © 2001-2014 Python Software Foundation. Copyright © 2000 BeOpen.com. Copyright © 1995-2001 CNRI. Copyright © 1991-1995 SMC.\0" VALUE "OriginalFilename", PYTHON_DLL_NAME "\0" VALUE "ProductName", "Python\0" VALUE "ProductVersion", PYTHON_VERSION diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 218d55f6edd..d9da116e462 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static char cprt[] = "\ -Copyright (c) 2001-2013 Python Software Foundation.\n\ +Copyright (c) 2001-2014 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ From f84422da1dd651fc9f0b474f3cd955b6baf2bdcc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:17:10 +0200 Subject: [PATCH 21/24] Added tag v3.2.6rc1 for changeset 51382a5598ec --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 83c67ec624e..5d8dcbdfc1c 100644 --- a/.hgtags +++ b/.hgtags @@ -102,3 +102,4 @@ c860feaa348d663e598986894ee4680480577e15 v3.2.2rc1 b2cb7bc1edb8493c0a78f9331eae3e8fba6a881d v3.2.4rc1 1e10bdeabe3de02f038a63c001911561ac1d13a7 v3.2.4 cef745775b6583446572cffad704100983db2bea v3.2.5 +51382a5598ec96119cb84594572901c9c964dc3c v3.2.6rc1 From b3ac84322fe6dd542aa755779cdbc155edca8064 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:50:11 +0200 Subject: [PATCH 22/24] #16040: fix unlimited read from connection in nntplib. --- Lib/nntplib.py | 11 ++++++++++- Lib/test/test_nntplib.py | 10 ++++++++++ Misc/NEWS | 4 ++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Lib/nntplib.py b/Lib/nntplib.py index 32bffd8e27c..0d4bbbbaab9 100644 --- a/Lib/nntplib.py +++ b/Lib/nntplib.py @@ -85,6 +85,13 @@ __all__ = ["NNTP", "decode_header", ] +# maximal line length when calling readline(). This is to prevent +# reading arbitrary lenght lines. RFC 3977 limits NNTP line length to +# 512 characters, including CRLF. We have selected 2048 just to be on +# the safe side. +_MAXLINE = 2048 + + # Exceptions raised when an error or invalid response is received class NNTPError(Exception): """Base class for all nntplib exceptions""" @@ -410,7 +417,9 @@ class _NNTPBase: """Internal: return one line from the server, stripping _CRLF. Raise EOFError if the connection is closed. Returns a bytes object.""" - line = self.file.readline() + line = self.file.readline(_MAXLINE +1) + if len(line) > _MAXLINE: + raise NNTPDataError('line too long') if self.debugging > 1: print('*get*', repr(line)) if not line: raise EOFError diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py index 7335b23b2d4..0875f71371a 100644 --- a/Lib/test/test_nntplib.py +++ b/Lib/test/test_nntplib.py @@ -563,6 +563,11 @@ class NNTPv1Handler: .""") + elif (group == 'comp.lang.python' and + date_str in ('20100101', '100101') and + time_str == '090000'): + self.push_lit('too long line' * 3000 + + '\n.') else: self.push_lit("""\ 230 An empty list of newsarticles follows @@ -1158,6 +1163,11 @@ class NNTPv1v2TestsMixin: self.assertEqual(cm.exception.response, "435 Article not wanted") + def test_too_long_lines(self): + dt = datetime.datetime(2010, 1, 1, 9, 0, 0) + self.assertRaises(nntplib.NNTPDataError, + self.server.newnews, "comp.lang.python", dt) + class NNTPv1Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase): """Tests an NNTP v1 server (no capabilities).""" diff --git a/Misc/NEWS b/Misc/NEWS index 1a704df9965..fc53c0f4d99 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,10 @@ Core and Builtins Library ------- +- Issue #16040: CVE-2013-1752: nntplib: Limit maximum line lengths to 2048 to + prevent readline() calls from consuming too much memory. Patch by Jyrki + Pulliainen. + - Issue #16039: CVE-2013-1752: Change use of readline in imaplib module to limit line length. Patch by Emil Lind. From 439d88542ef77d5c867b8298e4c2c6be4a412a5e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:50:38 +0200 Subject: [PATCH 23/24] Bump to 3.2.6 --- Include/patchlevel.h | 6 +++--- Lib/distutils/__init__.py | 2 +- Lib/idlelib/idlever.py | 2 +- Misc/RPM/python-3.2.spec | 2 +- README | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index b3e5422f4dc..b6b207f2b8c 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -19,11 +19,11 @@ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 2 #define PY_MICRO_VERSION 6 -#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL +#define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.2.6rc1" +#define PY_VERSION "3.2.6" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py index 382e4b1c9d0..6eb79cb6391 100644 --- a/Lib/distutils/__init__.py +++ b/Lib/distutils/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.6rc1" +__version__ = "3.2.6" #--end constants-- diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index b69949dfd91..643ea9f2354 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "3.2.6rc1" +IDLE_VERSION = "3.2.6" diff --git a/Misc/RPM/python-3.2.spec b/Misc/RPM/python-3.2.spec index 664589ee056..e0f94c58550 100644 --- a/Misc/RPM/python-3.2.spec +++ b/Misc/RPM/python-3.2.spec @@ -39,7 +39,7 @@ %define name python #--start constants-- -%define version 3.2.6rc1 +%define version 3.2.6 %define libvers 3.2 #--end constants-- %define release 1pydotorg diff --git a/README b/README index 50f11e057af..7ecd6d123cd 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -This is Python version 3.2.6 release candidate 1 -================================================ +This is Python version 3.2.6 +============================ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Python Software Foundation. All rights reserved. From 6f1abda02b53b29f60338462fb770e73c565bc46 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:51:30 +0200 Subject: [PATCH 24/24] Added tag v3.2.6 for changeset 0bd5f4f14de9 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 5d8dcbdfc1c..81831262058 100644 --- a/.hgtags +++ b/.hgtags @@ -103,3 +103,4 @@ b2cb7bc1edb8493c0a78f9331eae3e8fba6a881d v3.2.4rc1 1e10bdeabe3de02f038a63c001911561ac1d13a7 v3.2.4 cef745775b6583446572cffad704100983db2bea v3.2.5 51382a5598ec96119cb84594572901c9c964dc3c v3.2.6rc1 +0bd5f4f14de965ca8e44c6e3965fee106176cfc4 v3.2.6