From ec3c103520a5061e657581b388e2b8ba6f74602a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:04:51 +0200 Subject: 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 0000000..447186c --- /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 5c8df8b..d90e46d 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 12f02b3..d239d69 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 e9de8ca..e273c5c 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 */ -- cgit v0.12 From f0746ca46376647993a47e24051a80fdf679014a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:08:04 +0200 Subject: 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 7423a4c..2f68df7 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 5466d06..f398a64 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 420302c..226e79e 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 d239d69..67afa14 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 -- cgit v0.12 From c9cb18d3f7e5bf03220c213183ff0caa75905bdd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:12:24 +0200 Subject: =?UTF-8?q?Issue=20#16038:=20CVE-2013-1752:=20ftplib:=20Limit=20am?= =?UTF-8?q?ount=20of=20data=20read=20by=20limiting=20the=20call=20to=20rea?= =?UTF-8?q?dline().=20=20Original=20patch=20by=20Micha=C5=82=20Jastrz?= =?UTF-8?q?=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 8b733cf..74ae1ab 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 71bc23e..79a4ec7 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 67afa14..9f1107c 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. -- cgit v0.12 From 70088f14ad33e9f6e1734513594f83d42880e885 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 29 Oct 2013 21:08:56 +0100 Subject: 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 715f654..ebba21a 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 0f5cea2..56fcc52 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. -- cgit v0.12 From 210ee47e3340d8e689d8cce584e7c918d368f16b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:18:02 +0200 Subject: 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 679e478..b03533e 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 8036932..7e748b5 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 ec971ea..7799580 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 9f1107c..1de9c10 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. -- cgit v0.12 From eaca8616ab0e219ebb5cf37d495f4bf336ec0f5e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:45:39 +0200 Subject: 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 84ea88d..13e0f71 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 e3901b8..17b2261 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 1de9c10..44b0e96 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. -- cgit v0.12 From 21bf3f942be920f3b051f6af43f7c37b9aa5cff3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:54:39 +0200 Subject: 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 8ef314e..72a844f 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 44b0e96..c6df72b 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 a8278d4..f788e5c 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); -- cgit v0.12 From 860c367c29eb557930099a7cc7fe297a259275f6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 14:56:46 +0200 Subject: 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 08f8d9a..c0bfaa8 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 af82f95..9c4a83d 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 c1df480..c183dc7 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 c6df72b..d8e61c30 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. -- cgit v0.12 From dad182c16e6c9d10267a659bd376ba3d10affd4f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 17 Sep 2014 00:23:55 +0200 Subject: 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 ddbcbf8..28c1161 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 1f1ca58..30d4898 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 c183dc7..428fd01 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 d8e61c30..398ed29 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. -- cgit v0.12 From 0840b415829f7fab8db48e1b38bbbfc7da2df8c0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 17 Sep 2014 13:17:58 +0800 Subject: 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 fa02eda..2a0cbf3 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 42a4089..b632434 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 398ed29..60946e8 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. -- cgit v0.12 From fd9262cf2a044f6909530cdb565f38e13ff98263 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 16:00:09 +0200 Subject: 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 e2a0581..9950761 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 6b29943..62feea7 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 60946e8..b72b7a3 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. -- cgit v0.12 From e558181660d096e3e53c795bc5a528cdbd04eca6 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Wed, 26 Mar 2014 23:31:39 -0700 Subject: 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 feeaf24..2c832e1 --- 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 573c0c1..1b6dee2 --- 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 b72b7a3..babfb14 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? =========================== -- cgit v0.12 From 786c8e7dd565aecafb020683705564c42e62fedc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 16:31:21 +0200 Subject: 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 b03533e..cb36d28 --- 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 -- cgit v0.12 From 4615076471404c5e586d90a68ef2f26fc2201c84 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 16:41:11 +0200 Subject: 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 e4d5090..5260e6b 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 -- cgit v0.12 From 3bc35672a23a4505ffe5b6eeb822125dce9db770 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 17:30:18 +0200 Subject: 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 1b6dee2..3e47d26 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 " -- cgit v0.12 From 51c116223e7698ce3cbbac46e8a6779aea8ec9c6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 30 Sep 2014 19:34:19 +0200 Subject: 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 43fa656..7ba0967 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 5684ad7..53d5da5 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 428fd01..8887dd2 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 babfb14..5b84414 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. -- cgit v0.12 From ff3e5e3779ae6fab9f5d33149c95441eb847c196 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Oct 2014 19:15:11 +0200 Subject: 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 5b84414..14d9630 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 cd4e9e9..b798850 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; -- cgit v0.12 From 4480d30b8b69ce33f7fa98d9f38cdee343d1d2f3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Oct 2014 22:31:04 +0200 Subject: 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 7ba0967..9f7d606 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 = '''\ -- cgit v0.12 From e800a0e1c2b93f185580ae3926b239e2944b56d9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:15:42 +0200 Subject: 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 4ba3b06..b3e5422 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 f9016d6..382e4b1 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 4a7c336..b69949d 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 14d9630..1a704df 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 c93f6bf..664589e 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 6d8e18c..50f11e0 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 -- cgit v0.12 From edc3cbaaba1493a00572ad46daaab14e341d738e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:16:59 +0200 Subject: 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 c63fc60..a16c14a 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 a3257f8..32a36b9 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 b9e3927..3fdc973 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 f31c836..3f55723 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 218d55f..d9da116 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\ -- cgit v0.12 From f84422da1dd651fc9f0b474f3cd955b6baf2bdcc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:17:10 +0200 Subject: Added tag v3.2.6rc1 for changeset 51382a5598ec --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 83c67ec..5d8dcbd 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 -- cgit v0.12 From b3ac84322fe6dd542aa755779cdbc155edca8064 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:50:11 +0200 Subject: #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 32bffd8..0d4bbbb 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 7335b23..0875f71 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 1a704df..fc53c0f 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. -- cgit v0.12 From 439d88542ef77d5c867b8298e4c2c6be4a412a5e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:50:38 +0200 Subject: 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 b3e5422..b6b207f 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 382e4b1..6eb79cb 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 b69949d..643ea9f 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 664589e..e0f94c5 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 50f11e0..7ecd6d1 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. -- cgit v0.12 From 6f1abda02b53b29f60338462fb770e73c565bc46 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:51:30 +0200 Subject: Added tag v3.2.6 for changeset 0bd5f4f14de9 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 5d8dcbd..8183126 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 -- cgit v0.12