Compare commits
No commits in common. "c8" and "c10s" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
||||
SOURCES/Python-2.7.17-noexe.tar.xz
|
@ -1 +0,0 @@
|
||||
e63124a9a86b4b52c09384915a0842adf00b9d45 SOURCES/Python-2.7.17-noexe.tar.xz
|
844
00146-hashlib-fips.patch
Normal file
844
00146-hashlib-fips.patch
Normal file
@ -0,0 +1,844 @@
|
||||
From ece76465680b0df5b3fce7bf8ff1ff0253933889 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <pviktori@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 17:33:29 +0200
|
||||
Subject: [PATCH 01/11] Remove HASH_OBJ_CONSTRUCTOR
|
||||
|
||||
See https://github.com/python/cpython/commit/c7e219132aff1e21cb9ccb0a9b570dc6c750039b
|
||||
---
|
||||
Modules/_hashopenssl.c | 59 ------------------------------------------
|
||||
1 file changed, 59 deletions(-)
|
||||
|
||||
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
||||
index 78445ebabdd3..cb81e9765251 100644
|
||||
--- a/Modules/_hashopenssl.c
|
||||
+++ b/Modules/_hashopenssl.c
|
||||
@@ -48,10 +48,6 @@
|
||||
* to allow the user to optimize based on the platform they're using. */
|
||||
#define HASHLIB_GIL_MINSIZE 2048
|
||||
|
||||
-#ifndef HASH_OBJ_CONSTRUCTOR
|
||||
-#define HASH_OBJ_CONSTRUCTOR 0
|
||||
-#endif
|
||||
-
|
||||
#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x00908000)
|
||||
#define _OPENSSL_SUPPORTS_SHA2
|
||||
#endif
|
||||
@@ -384,53 +380,6 @@ EVP_repr(PyObject *self)
|
||||
return PyString_FromString(buf);
|
||||
}
|
||||
|
||||
-#if HASH_OBJ_CONSTRUCTOR
|
||||
-static int
|
||||
-EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
|
||||
-{
|
||||
- static char *kwlist[] = {"name", "string", NULL};
|
||||
- PyObject *name_obj = NULL;
|
||||
- Py_buffer view = { 0 };
|
||||
- char *nameStr;
|
||||
- const EVP_MD *digest;
|
||||
-
|
||||
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s*:HASH", kwlist,
|
||||
- &name_obj, &view)) {
|
||||
- return -1;
|
||||
- }
|
||||
-
|
||||
- if (!PyArg_Parse(name_obj, "s", &nameStr)) {
|
||||
- PyErr_SetString(PyExc_TypeError, "name must be a string");
|
||||
- PyBuffer_Release(&view);
|
||||
- return -1;
|
||||
- }
|
||||
-
|
||||
- digest = EVP_get_digestbyname(nameStr);
|
||||
- if (!digest) {
|
||||
- PyErr_SetString(PyExc_ValueError, "unknown hash function");
|
||||
- PyBuffer_Release(&view);
|
||||
- return -1;
|
||||
- }
|
||||
- EVP_DigestInit(self->ctx, digest);
|
||||
-
|
||||
- self->name = name_obj;
|
||||
- Py_INCREF(self->name);
|
||||
-
|
||||
- if (view.obj) {
|
||||
- if (view.len >= HASHLIB_GIL_MINSIZE) {
|
||||
- Py_BEGIN_ALLOW_THREADS
|
||||
- EVP_hash(self, view.buf, view.len);
|
||||
- Py_END_ALLOW_THREADS
|
||||
- } else {
|
||||
- EVP_hash(self, view.buf, view.len);
|
||||
- }
|
||||
- PyBuffer_Release(&view);
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
|
||||
PyDoc_STRVAR(hashtype_doc,
|
||||
"A hash represents the object used to calculate a checksum of a\n\
|
||||
@@ -487,9 +436,6 @@ static PyTypeObject EVPtype = {
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
#endif
|
||||
-#if HASH_OBJ_CONSTRUCTOR
|
||||
- (initproc)EVP_tp_init, /* tp_init */
|
||||
-#endif
|
||||
};
|
||||
|
||||
static PyObject *
|
||||
@@ -928,11 +874,6 @@ init_hashlib(void)
|
||||
return;
|
||||
}
|
||||
|
||||
-#if HASH_OBJ_CONSTRUCTOR
|
||||
- Py_INCREF(&EVPtype);
|
||||
- PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype);
|
||||
-#endif
|
||||
-
|
||||
/* these constants are used by the convenience constructors */
|
||||
INIT_CONSTRUCTOR_CONSTANTS(md5);
|
||||
INIT_CONSTRUCTOR_CONSTANTS(sha1);
|
||||
|
||||
From d7339af75678c760f6d6c0eb455b0eb889c22574 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <pviktori@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 18:02:25 +0200
|
||||
Subject: [PATCH 02/11] Add the usedforsecurity argument to _hashopenssl
|
||||
|
||||
---
|
||||
Modules/_hashopenssl.c | 63 ++++++++++++++++++++++++++++++++----------
|
||||
1 file changed, 48 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
||||
index cb81e9765251..f2dbc095cc66 100644
|
||||
--- a/Modules/_hashopenssl.c
|
||||
+++ b/Modules/_hashopenssl.c
|
||||
@@ -441,7 +441,7 @@ static PyTypeObject EVPtype = {
|
||||
static PyObject *
|
||||
EVPnew(PyObject *name_obj,
|
||||
const EVP_MD *digest, const EVP_MD_CTX *initial_ctx,
|
||||
- const unsigned char *cp, Py_ssize_t len)
|
||||
+ const unsigned char *cp, Py_ssize_t len, int usedforsecurity)
|
||||
{
|
||||
EVPobject *self;
|
||||
|
||||
@@ -456,7 +456,23 @@ EVPnew(PyObject *name_obj,
|
||||
if (initial_ctx) {
|
||||
EVP_MD_CTX_copy(self->ctx, initial_ctx);
|
||||
} else {
|
||||
- EVP_DigestInit(self->ctx, digest);
|
||||
+ EVP_MD_CTX_init(self->ctx);
|
||||
+
|
||||
+ /*
|
||||
+ If the user has declared that this digest is being used in a
|
||||
+ non-security role (e.g. indexing into a data structure), set
|
||||
+ the exception flag for openssl to allow it
|
||||
+ */
|
||||
+ if (!usedforsecurity) {
|
||||
+#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
|
||||
+ EVP_MD_CTX_set_flags(self->ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
|
||||
+#endif
|
||||
+ }
|
||||
+ if (!EVP_DigestInit_ex(self->ctx, digest, NULL)) {
|
||||
+ _setException(PyExc_ValueError);
|
||||
+ Py_DECREF(self);
|
||||
+ return NULL;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (cp && len) {
|
||||
@@ -485,15 +501,16 @@ The MD5 and SHA1 algorithms are always supported.\n");
|
||||
static PyObject *
|
||||
EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
|
||||
{
|
||||
- static char *kwlist[] = {"name", "string", NULL};
|
||||
+ static char *kwlist[] = {"name", "string", "usedforsecurity", NULL};
|
||||
PyObject *name_obj = NULL;
|
||||
Py_buffer view = { 0 };
|
||||
PyObject *ret_obj;
|
||||
char *name;
|
||||
const EVP_MD *digest;
|
||||
+ int usedforsecurity = 1;
|
||||
|
||||
- if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*:new", kwlist,
|
||||
- &name_obj, &view)) {
|
||||
+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*i:new", kwlist,
|
||||
+ &name_obj, &view, &usedforsecurity)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -506,7 +523,7 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
|
||||
digest = EVP_get_digestbyname(name);
|
||||
|
||||
ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf,
|
||||
- view.len);
|
||||
+ view.len, usedforsecurity);
|
||||
PyBuffer_Release(&view);
|
||||
|
||||
return ret_obj;
|
||||
@@ -771,30 +788,46 @@ generate_hash_name_list(void)
|
||||
* the generic one passing it a python string and are noticeably
|
||||
* faster than calling a python new() wrapper. Thats important for
|
||||
* code that wants to make hashes of a bunch of small strings.
|
||||
+ *
|
||||
+ * For usedforsecurity=False, the optimization is not used.
|
||||
*/
|
||||
#define GEN_CONSTRUCTOR(NAME) \
|
||||
static PyObject * \
|
||||
- EVP_new_ ## NAME (PyObject *self, PyObject *args) \
|
||||
+ EVP_new_ ## NAME (PyObject *self, PyObject *args, PyObject *kwdict) \
|
||||
{ \
|
||||
+ static char *kwlist[] = {"string", "usedforsecurity", NULL}; \
|
||||
Py_buffer view = { 0 }; \
|
||||
PyObject *ret_obj; \
|
||||
+ int usedforsecurity=1; \
|
||||
\
|
||||
- if (!PyArg_ParseTuple(args, "|s*:" #NAME , &view)) { \
|
||||
+ if (!PyArg_ParseTupleAndKeywords( \
|
||||
+ args, kwdict, "|s*i:" #NAME, kwlist, \
|
||||
+ &view, &usedforsecurity \
|
||||
+ )) { \
|
||||
return NULL; \
|
||||
} \
|
||||
- \
|
||||
- ret_obj = EVPnew( \
|
||||
- CONST_ ## NAME ## _name_obj, \
|
||||
- NULL, \
|
||||
- CONST_new_ ## NAME ## _ctx_p, \
|
||||
- (unsigned char*)view.buf, view.len); \
|
||||
+ if (usedforsecurity == 0) { \
|
||||
+ ret_obj = EVPnew( \
|
||||
+ CONST_ ## NAME ## _name_obj, \
|
||||
+ EVP_get_digestbyname(#NAME), \
|
||||
+ NULL, \
|
||||
+ (unsigned char*)view.buf, view.len, \
|
||||
+ usedforsecurity); \
|
||||
+ } else { \
|
||||
+ ret_obj = EVPnew( \
|
||||
+ CONST_ ## NAME ## _name_obj, \
|
||||
+ NULL, \
|
||||
+ CONST_new_ ## NAME ## _ctx_p, \
|
||||
+ (unsigned char*)view.buf, view.len, \
|
||||
+ usedforsecurity); \
|
||||
+ } \
|
||||
PyBuffer_Release(&view); \
|
||||
return ret_obj; \
|
||||
}
|
||||
|
||||
/* a PyMethodDef structure for the constructor */
|
||||
#define CONSTRUCTOR_METH_DEF(NAME) \
|
||||
- {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \
|
||||
+ {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS|METH_KEYWORDS, \
|
||||
PyDoc_STR("Returns a " #NAME \
|
||||
" hash object; optionally initialized with a string") \
|
||||
}
|
||||
|
||||
From c8102e61fb3ade364d4bb7f2fe3f3452e2018ecd Mon Sep 17 00:00:00 2001
|
||||
From: David Malcolm <dmalcolm@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 17:59:53 +0200
|
||||
Subject: [PATCH 03/11] hashlib.py: Avoid the builtin constructor
|
||||
|
||||
---
|
||||
Lib/hashlib.py | 58 +++++++++++++-------------------------------------
|
||||
1 file changed, 15 insertions(+), 43 deletions(-)
|
||||
|
||||
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
||||
index bbd06b9996ee..404ed6891fb9 100644
|
||||
--- a/Lib/hashlib.py
|
||||
+++ b/Lib/hashlib.py
|
||||
@@ -69,65 +69,37 @@
|
||||
'pbkdf2_hmac')
|
||||
|
||||
|
||||
-def __get_builtin_constructor(name):
|
||||
- try:
|
||||
- if name in ('SHA1', 'sha1'):
|
||||
- import _sha
|
||||
- return _sha.new
|
||||
- elif name in ('MD5', 'md5'):
|
||||
- import _md5
|
||||
- return _md5.new
|
||||
- elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'):
|
||||
- import _sha256
|
||||
- bs = name[3:]
|
||||
- if bs == '256':
|
||||
- return _sha256.sha256
|
||||
- elif bs == '224':
|
||||
- return _sha256.sha224
|
||||
- elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'):
|
||||
- import _sha512
|
||||
- bs = name[3:]
|
||||
- if bs == '512':
|
||||
- return _sha512.sha512
|
||||
- elif bs == '384':
|
||||
- return _sha512.sha384
|
||||
- except ImportError:
|
||||
- pass # no extension module, this hash is unsupported.
|
||||
-
|
||||
- raise ValueError('unsupported hash type ' + name)
|
||||
-
|
||||
-
|
||||
def __get_openssl_constructor(name):
|
||||
try:
|
||||
f = getattr(_hashlib, 'openssl_' + name)
|
||||
# Allow the C module to raise ValueError. The function will be
|
||||
# defined but the hash not actually available thanks to OpenSSL.
|
||||
- f()
|
||||
+ #
|
||||
+ # We pass "usedforsecurity=False" to disable FIPS-based restrictions:
|
||||
+ # at this stage we're merely seeing if the function is callable,
|
||||
+ # rather than using it for actual work.
|
||||
+ f(usedforsecurity=False)
|
||||
# Use the C function directly (very fast)
|
||||
return f
|
||||
except (AttributeError, ValueError):
|
||||
- return __get_builtin_constructor(name)
|
||||
-
|
||||
-
|
||||
-def __py_new(name, string=''):
|
||||
- """new(name, string='') - Return a new hashing object using the named algorithm;
|
||||
- optionally initialized with a string.
|
||||
- """
|
||||
- return __get_builtin_constructor(name)(string)
|
||||
+ raise
|
||||
|
||||
|
||||
-def __hash_new(name, string=''):
|
||||
- """new(name, string='') - Return a new hashing object using the named algorithm;
|
||||
- optionally initialized with a string.
|
||||
+def __hash_new(name, string='', usedforsecurity=True):
|
||||
+ """new(name, string='', usedforsecurity=True) - Return a new hashing object
|
||||
+ using the named algorithm; optionally initialized with a string.
|
||||
+
|
||||
+ Override 'usedforsecurity' to False when using for non-security purposes in
|
||||
+ a FIPS environment
|
||||
"""
|
||||
try:
|
||||
- return _hashlib.new(name, string)
|
||||
+ return _hashlib.new(name, string, usedforsecurity)
|
||||
except ValueError:
|
||||
# If the _hashlib module (OpenSSL) doesn't support the named
|
||||
# hash, try using our builtin implementations.
|
||||
# This allows for SHA224/256 and SHA384/512 support even though
|
||||
# the OpenSSL library prior to 0.9.8 doesn't provide them.
|
||||
- return __get_builtin_constructor(name)(string)
|
||||
+ raise
|
||||
|
||||
|
||||
try:
|
||||
@@ -218,4 +190,4 @@ def prf(msg, inner=inner, outer=outer):
|
||||
|
||||
# Cleanup locals()
|
||||
del __always_supported, __func_name, __get_hash
|
||||
-del __py_new, __hash_new, __get_openssl_constructor
|
||||
+del __hash_new, __get_openssl_constructor
|
||||
|
||||
From 2ade3e5a6c5732c0692c4cc2235a2bbe0948f50b Mon Sep 17 00:00:00 2001
|
||||
From: David Malcolm <dmalcolm@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 17:56:46 +0200
|
||||
Subject: [PATCH 04/11] Adjust docstrings & comments
|
||||
|
||||
---
|
||||
Lib/hashlib.py | 29 ++++++++++++++++++++++-------
|
||||
Modules/_hashopenssl.c | 9 ++++++++-
|
||||
2 files changed, 30 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
||||
index 404ed6891fb9..46d0b470ab4a 100644
|
||||
--- a/Lib/hashlib.py
|
||||
+++ b/Lib/hashlib.py
|
||||
@@ -6,9 +6,12 @@
|
||||
|
||||
__doc__ = """hashlib module - A common interface to many hash functions.
|
||||
|
||||
-new(name, string='') - returns a new hash object implementing the
|
||||
- given hash function; initializing the hash
|
||||
- using the given string data.
|
||||
+new(name, string='', usedforsecurity=True)
|
||||
+ - returns a new hash object implementing the given hash function;
|
||||
+ initializing the hash using the given string data.
|
||||
+
|
||||
+ "usedforsecurity" is a non-standard extension for better supporting
|
||||
+ FIPS-compliant environments (see below)
|
||||
|
||||
Named constructor functions are also available, these are much faster
|
||||
than using new():
|
||||
@@ -25,6 +28,20 @@
|
||||
Choose your hash function wisely. Some have known collision weaknesses.
|
||||
sha384 and sha512 will be slow on 32 bit platforms.
|
||||
|
||||
+Our implementation of hashlib uses OpenSSL.
|
||||
+
|
||||
+OpenSSL has a "FIPS mode", which, if enabled, may restrict the available hashes
|
||||
+to only those that are compliant with FIPS regulations. For example, it may
|
||||
+deny the use of MD5, on the grounds that this is not secure for uses such as
|
||||
+authentication, system integrity checking, or digital signatures.
|
||||
+
|
||||
+If you need to use such a hash for non-security purposes (such as indexing into
|
||||
+a data structure for speed), you can override the keyword argument
|
||||
+"usedforsecurity" from True to False to signify that your code is not relying
|
||||
+on the hash for security purposes, and this will allow the hash to be usable
|
||||
+even in FIPS mode. This is not a standard feature of Python 2.7's hashlib, and
|
||||
+is included here to better support FIPS mode.
|
||||
+
|
||||
Hash objects have these methods:
|
||||
- update(arg): Update the hash object with the string arg. Repeated calls
|
||||
are equivalent to a single call with the concatenation of all
|
||||
@@ -82,6 +99,7 @@ def __get_openssl_constructor(name):
|
||||
# Use the C function directly (very fast)
|
||||
return f
|
||||
except (AttributeError, ValueError):
|
||||
+ # RHEL only: Fallbacks removed; we always use OpenSSL for hashes.
|
||||
raise
|
||||
|
||||
|
||||
@@ -95,10 +113,7 @@ def __hash_new(name, string='', usedforsecurity=True):
|
||||
try:
|
||||
return _hashlib.new(name, string, usedforsecurity)
|
||||
except ValueError:
|
||||
- # If the _hashlib module (OpenSSL) doesn't support the named
|
||||
- # hash, try using our builtin implementations.
|
||||
- # This allows for SHA224/256 and SHA384/512 support even though
|
||||
- # the OpenSSL library prior to 0.9.8 doesn't provide them.
|
||||
+ # RHEL only: Fallbacks removed; we always use OpenSSL for hashes.
|
||||
raise
|
||||
|
||||
|
||||
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
||||
index f2dbc095cc66..d24432e048bf 100644
|
||||
--- a/Modules/_hashopenssl.c
|
||||
+++ b/Modules/_hashopenssl.c
|
||||
@@ -496,7 +496,14 @@ PyDoc_STRVAR(EVP_new__doc__,
|
||||
An optional string argument may be provided and will be\n\
|
||||
automatically hashed.\n\
|
||||
\n\
|
||||
-The MD5 and SHA1 algorithms are always supported.\n");
|
||||
+The MD5 and SHA1 algorithms are always supported.\n \
|
||||
+\n\
|
||||
+An optional \"usedforsecurity=True\" keyword argument is provided for use in\n\
|
||||
+environments that enforce FIPS-based restrictions. Some implementations of\n\
|
||||
+OpenSSL can be configured to prevent the usage of non-secure algorithms (such\n\
|
||||
+as MD5). If you have a non-security use for these algorithms (e.g. a hash\n\
|
||||
+table), you can override this argument by marking the callsite as\n\
|
||||
+\"usedforsecurity=False\".");
|
||||
|
||||
static PyObject *
|
||||
EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
|
||||
|
||||
From 6698e1d84c3f19bbb4438b2b2c78a5ef8bd5ad42 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <pviktori@redhat.com>
|
||||
Date: Thu, 29 Aug 2019 10:25:28 +0200
|
||||
Subject: [PATCH 05/11] Expose OpenSSL FIPS_mode as _hashlib.get_fips_mode
|
||||
|
||||
---
|
||||
Modules/_hashopenssl.c | 22 ++++++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
|
||||
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
||||
index d24432e048bf..74f9ab9ec150 100644
|
||||
--- a/Modules/_hashopenssl.c
|
||||
+++ b/Modules/_hashopenssl.c
|
||||
@@ -860,10 +860,32 @@ GEN_CONSTRUCTOR(sha384)
|
||||
GEN_CONSTRUCTOR(sha512)
|
||||
#endif
|
||||
|
||||
+static PyObject *
|
||||
+_hashlib_get_fips_mode(PyObject *module, PyObject *unused)
|
||||
+{
|
||||
+ // XXX: This function skips error checking.
|
||||
+ // This is only appropriate for RHEL.
|
||||
+
|
||||
+ // From the OpenSSL docs:
|
||||
+ // "If the library was built without support of the FIPS Object Module,
|
||||
+ // then the function will return 0 with an error code of
|
||||
+ // CRYPTO_R_FIPS_MODE_NOT_SUPPORTED (0x0f06d065)."
|
||||
+ // In RHEL:
|
||||
+ // * we do build with FIPS, so the function always succeeds
|
||||
+ // * even if it didn't, people seem used to errors being left on the
|
||||
+ // OpenSSL error stack.
|
||||
+
|
||||
+ // For more info, see:
|
||||
+ // https://bugzilla.redhat.com/show_bug.cgi?id=1745499
|
||||
+
|
||||
+ return PyInt_FromLong(FIPS_mode());
|
||||
+}
|
||||
+
|
||||
/* List of functions exported by this module */
|
||||
|
||||
static struct PyMethodDef EVP_functions[] = {
|
||||
{"new", (PyCFunction)EVP_new, METH_VARARGS|METH_KEYWORDS, EVP_new__doc__},
|
||||
+ {"get_fips_mode", (PyCFunction)_hashlib_get_fips_mode, METH_NOARGS, NULL},
|
||||
CONSTRUCTOR_METH_DEF(md5),
|
||||
CONSTRUCTOR_METH_DEF(sha1),
|
||||
#ifdef _OPENSSL_SUPPORTS_SHA2
|
||||
|
||||
From 9a8833619658c6be5ca72c60189a64da05536d85 Mon Sep 17 00:00:00 2001
|
||||
From: David Malcolm <dmalcolm@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 18:00:26 +0200
|
||||
Subject: [PATCH 06/11] Adjust tests
|
||||
|
||||
---
|
||||
Lib/test/test_hashlib.py | 118 ++++++++++++++++++++++++---------------
|
||||
1 file changed, 74 insertions(+), 44 deletions(-)
|
||||
|
||||
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
|
||||
index b8d6388feaf9..b03fc84f82b4 100644
|
||||
--- a/Lib/test/test_hashlib.py
|
||||
+++ b/Lib/test/test_hashlib.py
|
||||
@@ -34,6 +34,8 @@ def hexstr(s):
|
||||
r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
|
||||
return r
|
||||
|
||||
+from _hashlib import get_fips_mode
|
||||
+
|
||||
|
||||
class HashLibTestCase(unittest.TestCase):
|
||||
supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1',
|
||||
@@ -63,10 +65,10 @@ def __init__(self, *args, **kwargs):
|
||||
# of hashlib.new given the algorithm name.
|
||||
for algorithm, constructors in self.constructors_to_test.items():
|
||||
constructors.add(getattr(hashlib, algorithm))
|
||||
- def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm):
|
||||
+ def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, usedforsecurity=True):
|
||||
if data is None:
|
||||
- return hashlib.new(_alg)
|
||||
- return hashlib.new(_alg, data)
|
||||
+ return hashlib.new(_alg, usedforsecurity=usedforsecurity)
|
||||
+ return hashlib.new(_alg, data, usedforsecurity=usedforsecurity)
|
||||
constructors.add(_test_algorithm_via_hashlib_new)
|
||||
|
||||
_hashlib = self._conditional_import_module('_hashlib')
|
||||
@@ -80,28 +82,13 @@ def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm):
|
||||
if constructor:
|
||||
constructors.add(constructor)
|
||||
|
||||
- _md5 = self._conditional_import_module('_md5')
|
||||
- if _md5:
|
||||
- self.constructors_to_test['md5'].add(_md5.new)
|
||||
- _sha = self._conditional_import_module('_sha')
|
||||
- if _sha:
|
||||
- self.constructors_to_test['sha1'].add(_sha.new)
|
||||
- _sha256 = self._conditional_import_module('_sha256')
|
||||
- if _sha256:
|
||||
- self.constructors_to_test['sha224'].add(_sha256.sha224)
|
||||
- self.constructors_to_test['sha256'].add(_sha256.sha256)
|
||||
- _sha512 = self._conditional_import_module('_sha512')
|
||||
- if _sha512:
|
||||
- self.constructors_to_test['sha384'].add(_sha512.sha384)
|
||||
- self.constructors_to_test['sha512'].add(_sha512.sha512)
|
||||
-
|
||||
super(HashLibTestCase, self).__init__(*args, **kwargs)
|
||||
|
||||
def test_hash_array(self):
|
||||
a = array.array("b", range(10))
|
||||
constructors = self.constructors_to_test.itervalues()
|
||||
for cons in itertools.chain.from_iterable(constructors):
|
||||
- c = cons(a)
|
||||
+ c = cons(a, usedforsecurity=False)
|
||||
c.hexdigest()
|
||||
|
||||
def test_algorithms_attribute(self):
|
||||
@@ -122,28 +109,9 @@ def test_unknown_hash(self):
|
||||
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
|
||||
self.assertRaises(TypeError, hashlib.new, 1)
|
||||
|
||||
- def test_get_builtin_constructor(self):
|
||||
- get_builtin_constructor = hashlib.__dict__[
|
||||
- '__get_builtin_constructor']
|
||||
- self.assertRaises(ValueError, get_builtin_constructor, 'test')
|
||||
- try:
|
||||
- import _md5
|
||||
- except ImportError:
|
||||
- pass
|
||||
- # This forces an ImportError for "import _md5" statements
|
||||
- sys.modules['_md5'] = None
|
||||
- try:
|
||||
- self.assertRaises(ValueError, get_builtin_constructor, 'md5')
|
||||
- finally:
|
||||
- if '_md5' in locals():
|
||||
- sys.modules['_md5'] = _md5
|
||||
- else:
|
||||
- del sys.modules['_md5']
|
||||
- self.assertRaises(TypeError, get_builtin_constructor, 3)
|
||||
-
|
||||
def test_hexdigest(self):
|
||||
for name in self.supported_hash_names:
|
||||
- h = hashlib.new(name)
|
||||
+ h = hashlib.new(name, usedforsecurity=False)
|
||||
self.assertTrue(hexstr(h.digest()) == h.hexdigest())
|
||||
|
||||
def test_large_update(self):
|
||||
@@ -153,16 +121,16 @@ def test_large_update(self):
|
||||
abcs = aas + bees + cees
|
||||
|
||||
for name in self.supported_hash_names:
|
||||
- m1 = hashlib.new(name)
|
||||
+ m1 = hashlib.new(name, usedforsecurity=False)
|
||||
m1.update(aas)
|
||||
m1.update(bees)
|
||||
m1.update(cees)
|
||||
|
||||
- m2 = hashlib.new(name)
|
||||
+ m2 = hashlib.new(name, usedforsecurity=False)
|
||||
m2.update(abcs)
|
||||
self.assertEqual(m1.digest(), m2.digest(), name+' update problem.')
|
||||
|
||||
- m3 = hashlib.new(name, abcs)
|
||||
+ m3 = hashlib.new(name, abcs, usedforsecurity=False)
|
||||
self.assertEqual(m1.digest(), m3.digest(), name+' new problem.')
|
||||
|
||||
def check(self, name, data, digest):
|
||||
@@ -170,7 +138,7 @@ def check(self, name, data, digest):
|
||||
# 2 is for hashlib.name(...) and hashlib.new(name, ...)
|
||||
self.assertGreaterEqual(len(constructors), 2)
|
||||
for hash_object_constructor in constructors:
|
||||
- computed = hash_object_constructor(data).hexdigest()
|
||||
+ computed = hash_object_constructor(data, usedforsecurity=False).hexdigest()
|
||||
self.assertEqual(
|
||||
computed, digest,
|
||||
"Hash algorithm %s constructed using %s returned hexdigest"
|
||||
@@ -195,7 +163,7 @@ def check_update(self, name, data, digest):
|
||||
|
||||
def check_unicode(self, algorithm_name):
|
||||
# Unicode objects are not allowed as input.
|
||||
- expected = hashlib.new(algorithm_name, str(u'spam')).hexdigest()
|
||||
+ expected = hashlib.new(algorithm_name, str(u'spam'), usedforsecurity=False).hexdigest()
|
||||
self.check(algorithm_name, u'spam', expected)
|
||||
|
||||
def test_unicode(self):
|
||||
@@ -393,6 +361,68 @@ def hash_in_chunks(chunk_size):
|
||||
|
||||
self.assertEqual(expected_hash, hasher.hexdigest())
|
||||
|
||||
+ def test_issue9146(self):
|
||||
+ # Ensure that various ways to use "MD5" from "hashlib" don't segfault:
|
||||
+ m = hashlib.md5(usedforsecurity=False)
|
||||
+ m.update(b'abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = hashlib.new('md5', usedforsecurity=False)
|
||||
+ m.update(b'abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = hashlib.md5(b'abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = hashlib.new('md5', b'abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ def assertRaisesDisabledForFIPS(self, callable_obj=None, *args, **kwargs):
|
||||
+ try:
|
||||
+ callable_obj(*args, **kwargs)
|
||||
+ except ValueError, e:
|
||||
+ if not e.args[0].endswith('disabled for FIPS'):
|
||||
+ self.fail('Incorrect exception raised')
|
||||
+ else:
|
||||
+ self.fail('Exception was not raised')
|
||||
+
|
||||
+ @unittest.skipUnless(get_fips_mode(),
|
||||
+ 'FIPS enforcement required for this test.')
|
||||
+ def test_hashlib_fips_mode(self):
|
||||
+ # Ensure that we raise a ValueError on vanilla attempts to use MD5
|
||||
+ # in hashlib in a FIPS-enforced setting:
|
||||
+ self.assertRaisesDisabledForFIPS(hashlib.md5)
|
||||
+ self.assertRaisesDisabledForFIPS(hashlib.new, 'md5')
|
||||
+
|
||||
+ @unittest.skipUnless(get_fips_mode(),
|
||||
+ 'FIPS enforcement required for this test.')
|
||||
+ def test_hashopenssl_fips_mode(self):
|
||||
+ # Verify the _hashlib module's handling of md5:
|
||||
+ import _hashlib
|
||||
+
|
||||
+ assert hasattr(_hashlib, 'openssl_md5')
|
||||
+
|
||||
+ # Ensure that _hashlib raises a ValueError on vanilla attempts to
|
||||
+ # use MD5 in a FIPS-enforced setting:
|
||||
+ self.assertRaisesDisabledForFIPS(_hashlib.openssl_md5)
|
||||
+ self.assertRaisesDisabledForFIPS(_hashlib.new, 'md5')
|
||||
+
|
||||
+ # Ensure that in such a setting we can whitelist a callsite with
|
||||
+ # usedforsecurity=False and have it succeed:
|
||||
+ m = _hashlib.openssl_md5(usedforsecurity=False)
|
||||
+ m.update('abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = _hashlib.new('md5', usedforsecurity=False)
|
||||
+ m.update('abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = _hashlib.openssl_md5('abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = _hashlib.new('md5', 'abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
|
||||
class KDFTests(unittest.TestCase):
|
||||
pbkdf2_test_vectors = [
|
||||
|
||||
From 31e527aa4f57845dfb0c3dd4f0e9192af5a5b4e2 Mon Sep 17 00:00:00 2001
|
||||
From: David Malcolm <dmalcolm@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 18:00:47 +0200
|
||||
Subject: [PATCH 07/11] Don't build non-OpenSSL hash implementations
|
||||
|
||||
---
|
||||
setup.py | 15 ---------------
|
||||
1 file changed, 15 deletions(-)
|
||||
|
||||
diff --git a/setup.py b/setup.py
|
||||
index 33cecc687573..272d2f1b5bb8 100644
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -874,21 +874,6 @@ def detect_modules(self):
|
||||
print ("warning: openssl 0x%08x is too old for _hashlib" %
|
||||
openssl_ver)
|
||||
missing.append('_hashlib')
|
||||
- if COMPILED_WITH_PYDEBUG or not have_usable_openssl:
|
||||
- # The _sha module implements the SHA1 hash algorithm.
|
||||
- exts.append( Extension('_sha', ['shamodule.c']) )
|
||||
- # The _md5 module implements the RSA Data Security, Inc. MD5
|
||||
- # Message-Digest Algorithm, described in RFC 1321. The
|
||||
- # necessary files md5.c and md5.h are included here.
|
||||
- exts.append( Extension('_md5',
|
||||
- sources = ['md5module.c', 'md5.c'],
|
||||
- depends = ['md5.h']) )
|
||||
-
|
||||
- min_sha2_openssl_ver = 0x00908000
|
||||
- if COMPILED_WITH_PYDEBUG or openssl_ver < min_sha2_openssl_ver:
|
||||
- # OpenSSL doesn't do these until 0.9.8 so we'll bring our own hash
|
||||
- exts.append( Extension('_sha256', ['sha256module.c']) )
|
||||
- exts.append( Extension('_sha512', ['sha512module.c']) )
|
||||
|
||||
# Modules that provide persistent dictionary-like semantics. You will
|
||||
# probably want to arrange for at least one of them to be available on
|
||||
|
||||
From e9cd6a63ce17a0120b1d017bf08f05f3ed223bb1 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <pviktori@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 18:33:22 +0200
|
||||
Subject: [PATCH 08/11] Allow for errros in pre-created context creation
|
||||
|
||||
---
|
||||
Modules/_hashopenssl.c | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
||||
index 74f9ab9ec150..7609e9e490f0 100644
|
||||
--- a/Modules/_hashopenssl.c
|
||||
+++ b/Modules/_hashopenssl.c
|
||||
@@ -813,7 +813,7 @@ generate_hash_name_list(void)
|
||||
)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
- if (usedforsecurity == 0) { \
|
||||
+ if (usedforsecurity == 0 || CONST_new_ ## NAME ## _ctx_p == NULL) { \
|
||||
ret_obj = EVPnew( \
|
||||
CONST_ ## NAME ## _name_obj, \
|
||||
EVP_get_digestbyname(#NAME), \
|
||||
@@ -846,7 +846,9 @@ generate_hash_name_list(void)
|
||||
CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \
|
||||
if (EVP_get_digestbyname(#NAME)) { \
|
||||
CONST_new_ ## NAME ## _ctx_p = EVP_MD_CTX_new(); \
|
||||
- EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \
|
||||
+ if (!EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME))) { \
|
||||
+ CONST_new_ ## NAME ## _ctx_p = NULL; \
|
||||
+ } \
|
||||
} \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
From d0465ea1c07f24067b4d6f60f73a29c82f2ad03f Mon Sep 17 00:00:00 2001
|
||||
From: David Malcolm <dmalcolm@redhat.com>
|
||||
Date: Mon, 2 Sep 2019 18:40:08 +0200
|
||||
Subject: [PATCH 09/11] use SHA-256 rather than MD5 in
|
||||
multiprocessing.connection (patch 169; rhbz#879695)
|
||||
|
||||
---
|
||||
Lib/multiprocessing/connection.py | 12 ++++++++++--
|
||||
1 file changed, 10 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
|
||||
index 645a26f069ea..d4dc6ac19d53 100644
|
||||
--- a/Lib/multiprocessing/connection.py
|
||||
+++ b/Lib/multiprocessing/connection.py
|
||||
@@ -56,6 +56,10 @@
|
||||
# A very generous timeout when it comes to local connections...
|
||||
CONNECTION_TIMEOUT = 20.
|
||||
|
||||
+# The hmac module implicitly defaults to using MD5.
|
||||
+# Support using a stronger algorithm for the challenge/response code:
|
||||
+HMAC_DIGEST_NAME='sha256'
|
||||
+
|
||||
_mmap_counter = itertools.count()
|
||||
|
||||
default_family = 'AF_INET'
|
||||
@@ -413,12 +417,16 @@ def PipeClient(address):
|
||||
WELCOME = b'#WELCOME#'
|
||||
FAILURE = b'#FAILURE#'
|
||||
|
||||
+def get_digestmod_for_hmac():
|
||||
+ import hashlib
|
||||
+ return getattr(hashlib, HMAC_DIGEST_NAME)
|
||||
+
|
||||
def deliver_challenge(connection, authkey):
|
||||
import hmac
|
||||
assert isinstance(authkey, bytes)
|
||||
message = os.urandom(MESSAGE_LENGTH)
|
||||
connection.send_bytes(CHALLENGE + message)
|
||||
- digest = hmac.new(authkey, message).digest()
|
||||
+ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest()
|
||||
response = connection.recv_bytes(256) # reject large message
|
||||
if response == digest:
|
||||
connection.send_bytes(WELCOME)
|
||||
@@ -432,7 +440,7 @@ def answer_challenge(connection, authkey):
|
||||
message = connection.recv_bytes(256) # reject large message
|
||||
assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
|
||||
message = message[len(CHALLENGE):]
|
||||
- digest = hmac.new(authkey, message).digest()
|
||||
+ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest()
|
||||
connection.send_bytes(digest)
|
||||
response = connection.recv_bytes(256) # reject large message
|
||||
if response != WELCOME:
|
||||
|
||||
From 82b181a2c55be0f0766fdf1f0a3e950d22fe0602 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <pviktori@redhat.com>
|
||||
Date: Mon, 19 Aug 2019 13:59:40 +0200
|
||||
Subject: [PATCH 10/11] Make uuid.uuid3 work (using libuuid via ctypes)
|
||||
|
||||
---
|
||||
Lib/uuid.py | 8 ++++++++
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
diff --git a/Lib/uuid.py b/Lib/uuid.py
|
||||
index 80d33c0bd83f..bfb7477b5f58 100644
|
||||
--- a/Lib/uuid.py
|
||||
+++ b/Lib/uuid.py
|
||||
@@ -455,6 +455,7 @@ def _netbios_getnode():
|
||||
|
||||
# If ctypes is available, use it to find system routines for UUID generation.
|
||||
_uuid_generate_time = _UuidCreate = None
|
||||
+_uuid_generate_md5 = None
|
||||
try:
|
||||
import ctypes, ctypes.util
|
||||
import sys
|
||||
@@ -471,6 +472,8 @@ def _netbios_getnode():
|
||||
continue
|
||||
if hasattr(lib, 'uuid_generate_time'):
|
||||
_uuid_generate_time = lib.uuid_generate_time
|
||||
+ # The library that has uuid_generate_time should have md5 too.
|
||||
+ _uuid_generate_md5 = getattr(lib, 'uuid_generate_md5')
|
||||
break
|
||||
del _libnames
|
||||
|
||||
@@ -595,6 +598,11 @@ def uuid1(node=None, clock_seq=None):
|
||||
|
||||
def uuid3(namespace, name):
|
||||
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
|
||||
+ if _uuid_generate_md5:
|
||||
+ _buffer = ctypes.create_string_buffer(16)
|
||||
+ _uuid_generate_md5(_buffer, namespace.bytes, name, len(name))
|
||||
+ return UUID(bytes=_buffer.raw)
|
||||
+
|
||||
from hashlib import md5
|
||||
hash = md5(namespace.bytes + name).digest()
|
||||
return UUID(bytes=hash[:16], version=3)
|
||||
|
70
00189-use-rpm-wheels.patch
Normal file
70
00189-use-rpm-wheels.patch
Normal file
@ -0,0 +1,70 @@
|
||||
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
|
||||
index 5021ebf..29a7d1b 100644
|
||||
--- a/Lib/ensurepip/__init__.py
|
||||
+++ b/Lib/ensurepip/__init__.py
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env python2
|
||||
from __future__ import print_function
|
||||
|
||||
+import distutils.version
|
||||
+import glob
|
||||
import os
|
||||
import os.path
|
||||
-import pkgutil
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -12,9 +13,19 @@ import tempfile
|
||||
__all__ = ["version", "bootstrap"]
|
||||
|
||||
|
||||
-_SETUPTOOLS_VERSION = "41.2.0"
|
||||
+_WHEEL_DIR = "/usr/share/python{}-wheels/".format(sys.version_info[0])
|
||||
|
||||
-_PIP_VERSION = "19.2.3"
|
||||
+def _get_most_recent_wheel_version(pkg):
|
||||
+ prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg))
|
||||
+ suffix = "-py2.py3-none-any.whl"
|
||||
+ pattern = "{}*{}".format(prefix, suffix)
|
||||
+ versions = (p[len(prefix):-len(suffix)] for p in glob.glob(pattern))
|
||||
+ return str(max(versions, key=distutils.version.LooseVersion))
|
||||
+
|
||||
+
|
||||
+_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools")
|
||||
+
|
||||
+_PIP_VERSION = _get_most_recent_wheel_version("pip")
|
||||
|
||||
_PROJECTS = [
|
||||
("setuptools", _SETUPTOOLS_VERSION),
|
||||
@@ -28,8 +39,13 @@ def _run_pip(args, additional_paths=None):
|
||||
sys.path = additional_paths + sys.path
|
||||
|
||||
# Install the bundled software
|
||||
- import pip._internal
|
||||
- return pip._internal.main(args)
|
||||
+ try:
|
||||
+ # pip 10
|
||||
+ from pip._internal import main
|
||||
+ except ImportError:
|
||||
+ # pip 9
|
||||
+ from pip import main
|
||||
+ return main(args)
|
||||
|
||||
|
||||
def version():
|
||||
@@ -100,12 +116,9 @@ def _bootstrap(root=None, upgrade=False, user=False,
|
||||
additional_paths = []
|
||||
for project, version in _PROJECTS:
|
||||
wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
|
||||
- whl = pkgutil.get_data(
|
||||
- "ensurepip",
|
||||
- "_bundled/{}".format(wheel_name),
|
||||
- )
|
||||
- with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
|
||||
- fp.write(whl)
|
||||
+ with open(os.path.join(_WHEEL_DIR, wheel_name), "rb") as sfp:
|
||||
+ with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
|
||||
+ fp.write(sfp.read())
|
||||
|
||||
additional_paths.append(os.path.join(tmpdir, wheel_name))
|
||||
|
53
00288-ambiguous-python-version-rpmbuild-warn.patch
Normal file
53
00288-ambiguous-python-version-rpmbuild-warn.patch
Normal file
@ -0,0 +1,53 @@
|
||||
diff -U3 -r Python-2.7.14.orig/Lib/site.py Python-2.7.14/Lib/site.py
|
||||
--- Python-2.7.14.orig/Lib/site.py 2018-01-29 15:05:04.517599815 +0100
|
||||
+++ Python-2.7.14/Lib/site.py 2018-01-30 09:13:17.305270500 +0100
|
||||
@@ -515,6 +515,41 @@
|
||||
"'import usercustomize' failed; use -v for traceback"
|
||||
|
||||
|
||||
+def handle_ambiguous_python_version():
|
||||
+ """Warn or fail if /usr/bin/python is used
|
||||
+
|
||||
+ Behavior depends on the value of PYTHON_DISALLOW_AMBIGUOUS_VERSION:
|
||||
+ - "warn" - print warning to stderr
|
||||
+ - "1" - print error and exit with positive exit code
|
||||
+ - otherwise: do nothing
|
||||
+
|
||||
+ This is a Fedora modification, see the Change page for details:
|
||||
+ See https://fedoraproject.org/wiki/Changes/Avoid_usr_bin_python_in_RPM_Build
|
||||
+ """
|
||||
+ if sys.executable == "/usr/bin/python":
|
||||
+ setting = os.environ.get("PYTHON_DISALLOW_AMBIGUOUS_VERSION")
|
||||
+ if setting == 'warn':
|
||||
+ print>>sys.stderr, (
|
||||
+ "DEPRECATION WARNING: python2 invoked with /usr/bin/python.\n"
|
||||
+ " Use /usr/bin/python3 or /usr/bin/python2\n"
|
||||
+ " /usr/bin/python will be removed or switched to Python 3"
|
||||
+ " in the future.\n"
|
||||
+ " If you cannot make the switch now, please follow"
|
||||
+ " instructions at"
|
||||
+ " https://fedoraproject.org/wiki/Changes/"
|
||||
+ "Avoid_usr_bin_python_in_RPM_Build#Quick_Opt-Out")
|
||||
+ elif setting == '1':
|
||||
+ print>>sys.stderr, (
|
||||
+ "ERROR: python2 invoked with /usr/bin/python.\n"
|
||||
+ " Use /usr/bin/python3 or /usr/bin/python2\n"
|
||||
+ " /usr/bin/python will be switched to Python 3"
|
||||
+ " in the future.\n"
|
||||
+ " More details are at"
|
||||
+ " https://fedoraproject.org/wiki/Changes/"
|
||||
+ "Avoid_usr_bin_python_in_RPM_Build#Quick_Opt-Out")
|
||||
+ exit(1)
|
||||
+
|
||||
+
|
||||
def main():
|
||||
global ENABLE_USER_SITE
|
||||
|
||||
@@ -543,6 +578,7 @@
|
||||
# this module is run as a script, because this code is executed twice.
|
||||
if hasattr(sys, "setdefaultencoding"):
|
||||
del sys.setdefaultencoding
|
||||
+ handle_ambiguous_python_version()
|
||||
|
||||
main()
|
||||
|
70
00351-cve-2019-20907-fix-infinite-loop-in-tarfile.patch
Normal file
70
00351-cve-2019-20907-fix-infinite-loop-in-tarfile.patch
Normal file
@ -0,0 +1,70 @@
|
||||
From b099ce737f6e6cc9f3a1bf756af78eaa1c1480cd Mon Sep 17 00:00:00 2001
|
||||
From: Rishi <rishi_devan@mail.com>
|
||||
Date: Wed, 15 Jul 2020 13:51:00 +0200
|
||||
Subject: [PATCH] 00351-cve-2019-20907-fix-infinite-loop-in-tarfile.patch
|
||||
|
||||
00351 #
|
||||
Avoid infinite loop when reading specially crafted TAR files using the tarfile module
|
||||
(CVE-2019-20907).
|
||||
See: https://bugs.python.org/issue39017
|
||||
---
|
||||
Lib/tarfile.py | 2 ++
|
||||
Lib/test/recursion.tar | Bin 0 -> 516 bytes
|
||||
Lib/test/test_tarfile.py | 7 +++++++
|
||||
.../2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst | 1 +
|
||||
4 files changed, 10 insertions(+)
|
||||
create mode 100644 Lib/test/recursion.tar
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst
|
||||
|
||||
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
|
||||
index adf91d5..574a6bb 100644
|
||||
--- a/Lib/tarfile.py
|
||||
+++ b/Lib/tarfile.py
|
||||
@@ -1400,6 +1400,8 @@ class TarInfo(object):
|
||||
|
||||
length, keyword = match.groups()
|
||||
length = int(length)
|
||||
+ if length == 0:
|
||||
+ raise InvalidHeaderError("invalid header")
|
||||
value = buf[match.end(2) + 1:match.start(1) + length - 1]
|
||||
|
||||
keyword = keyword.decode("utf8")
|
||||
diff --git a/Lib/test/recursion.tar b/Lib/test/recursion.tar
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b8237251964983f54ed1966297e887636cd0c5f4
|
||||
GIT binary patch
|
||||
literal 516
|
||||
zcmYdFPRz+kEn=W0Fn}74P8%Xw3X=l~85kIuo0>8xq$A1Gm}!7)KUsFc41m#O8A5+e
|
||||
I1_}|j06>QaCIA2c
|
||||
|
||||
literal 0
|
||||
HcmV?d00001
|
||||
|
||||
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
|
||||
index 89bd738..4592156 100644
|
||||
--- a/Lib/test/test_tarfile.py
|
||||
+++ b/Lib/test/test_tarfile.py
|
||||
@@ -325,6 +325,13 @@ class CommonReadTest(ReadTest):
|
||||
class MiscReadTest(CommonReadTest):
|
||||
taropen = tarfile.TarFile.taropen
|
||||
|
||||
+ def test_length_zero_header(self):
|
||||
+ # bpo-39017 (CVE-2019-20907): reading a zero-length header should fail
|
||||
+ # with an exception
|
||||
+ with self.assertRaisesRegexp(tarfile.ReadError, "file could not be opened successfully"):
|
||||
+ with tarfile.open(support.findfile('recursion.tar')) as tar:
|
||||
+ pass
|
||||
+
|
||||
def test_no_name_argument(self):
|
||||
with open(self.tarname, "rb") as fobj:
|
||||
tar = tarfile.open(fileobj=fobj, mode=self.mode)
|
||||
diff --git a/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst
|
||||
new file mode 100644
|
||||
index 0000000..ad26676
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst
|
||||
@@ -0,0 +1 @@
|
||||
+Avoid infinite loop when reading specially crafted TAR files using the tarfile module (CVE-2019-20907).
|
||||
--
|
||||
2.25.4
|
||||
|
@ -0,0 +1,88 @@
|
||||
diff --git a/Lib/httplib.py b/Lib/httplib.py
|
||||
index fcc4152..a636774 100644
|
||||
--- a/Lib/httplib.py
|
||||
+++ b/Lib/httplib.py
|
||||
@@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f-\xff]')
|
||||
# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
|
||||
# We are more lenient for assumed real world compatibility purposes.
|
||||
|
||||
+# These characters are not allowed within HTTP method names
|
||||
+# to prevent http header injection.
|
||||
+_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')
|
||||
+
|
||||
# We always set the Content-Length header for these methods because some
|
||||
# servers will otherwise respond with a 411
|
||||
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
|
||||
@@ -935,6 +939,8 @@ class HTTPConnection:
|
||||
else:
|
||||
raise CannotSendRequest()
|
||||
|
||||
+ self._validate_method(method)
|
||||
+
|
||||
# Save the method for use later in the response phase
|
||||
self._method = method
|
||||
|
||||
@@ -1020,6 +1026,16 @@ class HTTPConnection:
|
||||
# On Python 2, request is already encoded (default)
|
||||
return request
|
||||
|
||||
+ def _validate_method(self, method):
|
||||
+ """Validate a method name for putrequest."""
|
||||
+ # prevent http header injection
|
||||
+ match = _contains_disallowed_method_pchar_re.search(method)
|
||||
+ if match:
|
||||
+ raise ValueError(
|
||||
+ "method can't contain control characters. %r "
|
||||
+ "(found at least %r)"
|
||||
+ % (method, match.group()))
|
||||
+
|
||||
def _validate_path(self, url):
|
||||
"""Validate a url for putrequest."""
|
||||
# Prevent CVE-2019-9740.
|
||||
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
|
||||
index d8a57f7..96a61dd 100644
|
||||
--- a/Lib/test/test_httplib.py
|
||||
+++ b/Lib/test/test_httplib.py
|
||||
@@ -385,6 +385,29 @@ class HeaderTests(TestCase):
|
||||
conn.putheader(name, value)
|
||||
|
||||
|
||||
+class HttpMethodTests(TestCase):
|
||||
+ def test_invalid_method_names(self):
|
||||
+ methods = (
|
||||
+ 'GET\r',
|
||||
+ 'POST\n',
|
||||
+ 'PUT\n\r',
|
||||
+ 'POST\nValue',
|
||||
+ 'POST\nHOST:abc',
|
||||
+ 'GET\nrHost:abc\n',
|
||||
+ 'POST\rRemainder:\r',
|
||||
+ 'GET\rHOST:\n',
|
||||
+ '\nPUT'
|
||||
+ )
|
||||
+
|
||||
+ for method in methods:
|
||||
+ with self.assertRaisesRegexp(
|
||||
+ ValueError, "method can't contain control characters"):
|
||||
+ conn = httplib.HTTPConnection('example.com')
|
||||
+ conn.sock = FakeSocket(None)
|
||||
+ conn.request(method=method, url="/")
|
||||
+
|
||||
+
|
||||
+
|
||||
class BasicTest(TestCase):
|
||||
def test_status_lines(self):
|
||||
# Test HTTP status lines
|
||||
@@ -1009,9 +1032,9 @@ class TunnelTests(TestCase):
|
||||
|
||||
@test_support.reap_threads
|
||||
def test_main(verbose=None):
|
||||
- test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
|
||||
- HTTPTest, HTTPSTest, SourceAddressTest,
|
||||
- TunnelTests)
|
||||
+ test_support.run_unittest(HeaderTests, OfflineTest, HttpMethodTests,
|
||||
+ BasicTest, TimeoutTest, HTTPTest, HTTPSTest,
|
||||
+ SourceAddressTest, TunnelTests)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_main()
|
42
00355-CVE-2020-27619.patch
Normal file
42
00355-CVE-2020-27619.patch
Normal file
@ -0,0 +1,42 @@
|
||||
diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py
|
||||
index 5b2329b6d84..53b5d64d453 100644
|
||||
--- a/Lib/test/multibytecodec_support.py
|
||||
+++ b/Lib/test/multibytecodec_support.py
|
||||
@@ -279,30 +279,22 @@ class TestBase_Mapping(unittest.TestCase):
|
||||
self._test_mapping_file_plain()
|
||||
|
||||
def _test_mapping_file_plain(self):
|
||||
- _unichr = lambda c: eval("u'\\U%08x'" % int(c, 16))
|
||||
- unichrs = lambda s: u''.join(_unichr(c) for c in s.split('+'))
|
||||
+ def unichrs(s):
|
||||
+ return ''.join(unichr(int(x, 16)) for x in s.split('+'))
|
||||
urt_wa = {}
|
||||
|
||||
with self.open_mapping_file() as f:
|
||||
for line in f:
|
||||
if not line:
|
||||
break
|
||||
- data = line.split('#')[0].strip().split()
|
||||
+ data = line.split('#')[0].split()
|
||||
if len(data) != 2:
|
||||
continue
|
||||
|
||||
- csetval = eval(data[0])
|
||||
- if csetval <= 0x7F:
|
||||
- csetch = chr(csetval & 0xff)
|
||||
- elif csetval >= 0x1000000:
|
||||
- csetch = chr(csetval >> 24) + chr((csetval >> 16) & 0xff) + \
|
||||
- chr((csetval >> 8) & 0xff) + chr(csetval & 0xff)
|
||||
- elif csetval >= 0x10000:
|
||||
- csetch = chr(csetval >> 16) + \
|
||||
- chr((csetval >> 8) & 0xff) + chr(csetval & 0xff)
|
||||
- elif csetval >= 0x100:
|
||||
- csetch = chr(csetval >> 8) + chr(csetval & 0xff)
|
||||
- else:
|
||||
+ if data[0][:2] != '0x':
|
||||
+ self.fail("Invalid line: {!r}".format(line))
|
||||
+ csetch = bytes.fromhex(data[0][2:])
|
||||
+ if len(csetch) == 1 and 0x80 <= csetch[0]:
|
||||
continue
|
||||
|
||||
unich = unichrs(data[1])
|
181
00357-CVE-2021-3177.patch
Normal file
181
00357-CVE-2021-3177.patch
Normal file
@ -0,0 +1,181 @@
|
||||
commit 30e41798f40c684be57d7ccfebf5c6ad94c0ff97
|
||||
Author: Petr Viktorin <pviktori@redhat.com>
|
||||
Date: Wed Jan 20 15:21:43 2021 +0100
|
||||
|
||||
CVE-2021-3177: Replace snprintf with Python unicode formatting in ctypes param reprs
|
||||
|
||||
Backport of Python3 commit 916610ef90a0d0761f08747f7b0905541f0977c7:
|
||||
https://bugs.python.org/issue42938
|
||||
https://github.com/python/cpython/pull/24239
|
||||
|
||||
diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py
|
||||
index 23c1b6e2259..77300d71ae1 100644
|
||||
--- a/Lib/ctypes/test/test_parameters.py
|
||||
+++ b/Lib/ctypes/test/test_parameters.py
|
||||
@@ -206,6 +206,49 @@ class SimpleTypesTestCase(unittest.TestCase):
|
||||
with self.assertRaises(ZeroDivisionError):
|
||||
WorseStruct().__setstate__({}, b'foo')
|
||||
|
||||
+ def test_parameter_repr(self):
|
||||
+ from ctypes import (
|
||||
+ c_bool,
|
||||
+ c_char,
|
||||
+ c_wchar,
|
||||
+ c_byte,
|
||||
+ c_ubyte,
|
||||
+ c_short,
|
||||
+ c_ushort,
|
||||
+ c_int,
|
||||
+ c_uint,
|
||||
+ c_long,
|
||||
+ c_ulong,
|
||||
+ c_longlong,
|
||||
+ c_ulonglong,
|
||||
+ c_float,
|
||||
+ c_double,
|
||||
+ c_longdouble,
|
||||
+ c_char_p,
|
||||
+ c_wchar_p,
|
||||
+ c_void_p,
|
||||
+ )
|
||||
+ self.assertRegexpMatches(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
|
||||
+ self.assertEqual(repr(c_char.from_param('a')), "<cparam 'c' ('a')>")
|
||||
+ self.assertRegexpMatches(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
|
||||
+ self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>")
|
||||
+ self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>")
|
||||
+ self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>")
|
||||
+ self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>")
|
||||
+ self.assertRegexpMatches(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
|
||||
+ self.assertRegexpMatches(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
|
||||
+ self.assertRegexpMatches(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
|
||||
+ self.assertRegexpMatches(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
|
||||
+ self.assertRegexpMatches(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$")
|
||||
+ self.assertRegexpMatches(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
|
||||
+ self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
|
||||
+ self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
|
||||
+ self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
|
||||
+ self.assertRegexpMatches(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
|
||||
+ self.assertRegexpMatches(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
|
||||
+ self.assertRegexpMatches(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
|
||||
+ self.assertRegexpMatches(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
|
||||
+
|
||||
################################################################
|
||||
|
||||
if __name__ == '__main__':
|
||||
diff --git a/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
|
||||
new file mode 100644
|
||||
index 00000000000..7df65a156fe
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
|
||||
@@ -0,0 +1,2 @@
|
||||
+Avoid static buffers when computing the repr of :class:`ctypes.c_double` and
|
||||
+:class:`ctypes.c_longdouble` values.
|
||||
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
|
||||
index 066fefc0cca..5cc3c4cf685 100644
|
||||
--- a/Modules/_ctypes/callproc.c
|
||||
+++ b/Modules/_ctypes/callproc.c
|
||||
@@ -460,50 +460,62 @@ PyCArg_dealloc(PyCArgObject *self)
|
||||
static PyObject *
|
||||
PyCArg_repr(PyCArgObject *self)
|
||||
{
|
||||
- char buffer[256];
|
||||
switch(self->tag) {
|
||||
case 'b':
|
||||
case 'B':
|
||||
- sprintf(buffer, "<cparam '%c' (%d)>",
|
||||
+ return PyString_FromFormat("<cparam '%c' (%d)>",
|
||||
self->tag, self->value.b);
|
||||
- break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
- sprintf(buffer, "<cparam '%c' (%d)>",
|
||||
+ return PyString_FromFormat("<cparam '%c' (%d)>",
|
||||
self->tag, self->value.h);
|
||||
- break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
- sprintf(buffer, "<cparam '%c' (%d)>",
|
||||
+ return PyString_FromFormat("<cparam '%c' (%d)>",
|
||||
self->tag, self->value.i);
|
||||
- break;
|
||||
case 'l':
|
||||
case 'L':
|
||||
- sprintf(buffer, "<cparam '%c' (%ld)>",
|
||||
+ return PyString_FromFormat("<cparam '%c' (%ld)>",
|
||||
self->tag, self->value.l);
|
||||
- break;
|
||||
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case 'q':
|
||||
case 'Q':
|
||||
- sprintf(buffer,
|
||||
- "<cparam '%c' (%" PY_FORMAT_LONG_LONG "d)>",
|
||||
+ return PyString_FromFormat("<cparam '%c' (%lld)>",
|
||||
self->tag, self->value.q);
|
||||
- break;
|
||||
#endif
|
||||
case 'd':
|
||||
- sprintf(buffer, "<cparam '%c' (%f)>",
|
||||
- self->tag, self->value.d);
|
||||
- break;
|
||||
- case 'f':
|
||||
- sprintf(buffer, "<cparam '%c' (%f)>",
|
||||
- self->tag, self->value.f);
|
||||
- break;
|
||||
-
|
||||
+ case 'f': {
|
||||
+ PyObject *s = PyString_FromFormat("<cparam '%c' (", self->tag);
|
||||
+ if (s == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d);
|
||||
+ if (f == NULL) {
|
||||
+ Py_DECREF(s);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ PyObject *r = PyObject_Repr(f);
|
||||
+ Py_DECREF(f);
|
||||
+ if (r == NULL) {
|
||||
+ Py_DECREF(s);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ PyString_ConcatAndDel(&s, r);
|
||||
+ if (s == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ r = PyString_FromString(")>");
|
||||
+ if (r == NULL) {
|
||||
+ Py_DECREF(s);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ PyString_ConcatAndDel(&s, r);
|
||||
+ return s;
|
||||
+ }
|
||||
case 'c':
|
||||
- sprintf(buffer, "<cparam '%c' (%c)>",
|
||||
+ return PyString_FromFormat("<cparam '%c' ('%c')>",
|
||||
self->tag, self->value.c);
|
||||
- break;
|
||||
|
||||
/* Hm, are these 'z' and 'Z' codes useful at all?
|
||||
Shouldn't they be replaced by the functionality of c_string
|
||||
@@ -512,16 +524,13 @@ PyCArg_repr(PyCArgObject *self)
|
||||
case 'z':
|
||||
case 'Z':
|
||||
case 'P':
|
||||
- sprintf(buffer, "<cparam '%c' (%p)>",
|
||||
+ return PyUnicode_FromFormat("<cparam '%c' (%p)>",
|
||||
self->tag, self->value.p);
|
||||
- break;
|
||||
|
||||
default:
|
||||
- sprintf(buffer, "<cparam '%c' at %p>",
|
||||
- self->tag, self);
|
||||
- break;
|
||||
+ return PyString_FromFormat("<cparam '%c' at %p>",
|
||||
+ (unsigned char)self->tag, (void *)self);
|
||||
}
|
||||
- return PyString_FromString(buffer);
|
||||
}
|
||||
|
||||
static PyMemberDef PyCArgType_members[] = {
|
707
00359-CVE-2021-23336.patch
Normal file
707
00359-CVE-2021-23336.patch
Normal file
@ -0,0 +1,707 @@
|
||||
From 976a4010aa4e450855dce5fa4c865bcbdc86cccd Mon Sep 17 00:00:00 2001
|
||||
From: Charalampos Stratakis <cstratak@redhat.com>
|
||||
Date: Fri, 16 Apr 2021 18:02:00 +0200
|
||||
Subject: [PATCH] CVE-2021-23336: Add `separator` argument to parse_qs; warn
|
||||
with default
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Partially backports https://bugs.python.org/issue42967 : [security] Address a web cache-poisoning issue reported in urllib.parse.parse_qsl().
|
||||
|
||||
Backported from the python3 branch.
|
||||
However, this solution is different than the upstream solution in Python 3.
|
||||
|
||||
Based on the downstream solution for python 3.6.13 by Petr Viktorin.
|
||||
|
||||
An optional argument seperator is added to specify the separator.
|
||||
It is recommended to set it to '&' or ';' to match the application or proxy in use.
|
||||
The default can be set with an env variable of a config file.
|
||||
If neither the argument, env var or config file specifies a separator, "&" is used
|
||||
but a warning is raised if parse_qs is used on input that contains ';'.
|
||||
|
||||
Co-authors of the downstream change:
|
||||
Co-authored-by: Petr Viktorin <pviktori@redhat.com>
|
||||
Co-authors of the upstream change (who do not necessarily agree with this):
|
||||
Co-authored-by: Adam Goldschmidt <adamgold7@gmail.com>
|
||||
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
|
||||
Co-authored-by: Éric Araujo <merwok@netwok.org>
|
||||
---
|
||||
Doc/library/cgi.rst | 5 +-
|
||||
Doc/library/urlparse.rst | 15 ++-
|
||||
Lib/cgi.py | 34 +++---
|
||||
Lib/test/test_cgi.py | 59 ++++++++++-
|
||||
Lib/test/test_urlparse.py | 210 +++++++++++++++++++++++++++++++++++++-
|
||||
Lib/urlparse.py | 78 +++++++++++++-
|
||||
6 files changed, 369 insertions(+), 32 deletions(-)
|
||||
|
||||
diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst
|
||||
index ecd62c8c019..a96cd38717b 100644
|
||||
--- a/Doc/library/cgi.rst
|
||||
+++ b/Doc/library/cgi.rst
|
||||
@@ -285,10 +285,10 @@ These are useful if you want more control, or if you want to employ some of the
|
||||
algorithms implemented in this module in other circumstances.
|
||||
|
||||
|
||||
-.. function:: parse(fp[, environ[, keep_blank_values[, strict_parsing]]])
|
||||
+.. function:: parse(fp[, environ[, keep_blank_values[, strict_parsing[, separator]]]])
|
||||
|
||||
Parse a query in the environment or from a file (the file defaults to
|
||||
- ``sys.stdin`` and environment defaults to ``os.environ``). The *keep_blank_values* and *strict_parsing* parameters are
|
||||
+ ``sys.stdin`` and environment defaults to ``os.environ``). The *keep_blank_values*, *strict_parsing* and *separator* parameters are
|
||||
passed to :func:`urlparse.parse_qs` unchanged.
|
||||
|
||||
|
||||
@@ -316,7 +316,6 @@ algorithms implemented in this module in other circumstances.
|
||||
Note that this does not parse nested multipart parts --- use
|
||||
:class:`FieldStorage` for that.
|
||||
|
||||
-
|
||||
.. function:: parse_header(string)
|
||||
|
||||
Parse a MIME header (such as :mailheader:`Content-Type`) into a main value and a
|
||||
diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst
|
||||
index 0989c88c302..97d1119257c 100644
|
||||
--- a/Doc/library/urlparse.rst
|
||||
+++ b/Doc/library/urlparse.rst
|
||||
@@ -136,7 +136,7 @@ The :mod:`urlparse` module defines the following functions:
|
||||
now raise :exc:`ValueError`.
|
||||
|
||||
|
||||
-.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]])
|
||||
+.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields[, separator]]]])
|
||||
|
||||
Parse a query string given as a string argument (data of type
|
||||
:mimetype:`application/x-www-form-urlencoded`). Data are returned as a
|
||||
@@ -157,6 +157,15 @@ The :mod:`urlparse` module defines the following functions:
|
||||
read. If set, then throws a :exc:`ValueError` if there are more than
|
||||
*max_num_fields* fields read.
|
||||
|
||||
+ The optional argument *separator* is the symbol to use for separating the
|
||||
+ query arguments. It is recommended to set it to ``'&'`` or ``';'``.
|
||||
+ It defaults to ``'&'``; a warning is raised if this default is used.
|
||||
+ This default may be changed with the following environment variable settings:
|
||||
+
|
||||
+ - ``PYTHON_URLLIB_QS_SEPARATOR='&'``: use only ``&`` as separator, without warning (as in Python 3.6.13+ or 3.10)
|
||||
+ - ``PYTHON_URLLIB_QS_SEPARATOR=';'``: use only ``;`` as separator
|
||||
+ - ``PYTHON_URLLIB_QS_SEPARATOR=legacy``: use both ``&`` and ``;`` (as in previous versions of Python)
|
||||
+
|
||||
Use the :func:`urllib.urlencode` function to convert such dictionaries into
|
||||
query strings.
|
||||
|
||||
@@ -186,6 +195,9 @@ The :mod:`urlparse` module defines the following functions:
|
||||
read. If set, then throws a :exc:`ValueError` if there are more than
|
||||
*max_num_fields* fields read.
|
||||
|
||||
+ The optional argument *separator* is the symbol to use for separating the
|
||||
+ query arguments. It works as in :py:func:`parse_qs`.
|
||||
+
|
||||
Use the :func:`urllib.urlencode` function to convert such lists of pairs into
|
||||
query strings.
|
||||
|
||||
@@ -195,6 +207,7 @@ The :mod:`urlparse` module defines the following functions:
|
||||
.. versionchanged:: 2.7.16
|
||||
Added *max_num_fields* parameter.
|
||||
|
||||
+
|
||||
.. function:: urlunparse(parts)
|
||||
|
||||
Construct a URL from a tuple as returned by ``urlparse()``. The *parts* argument
|
||||
diff --git a/Lib/cgi.py b/Lib/cgi.py
|
||||
index 5b903e03477..1421f2d90e0 100755
|
||||
--- a/Lib/cgi.py
|
||||
+++ b/Lib/cgi.py
|
||||
@@ -121,7 +121,8 @@ log = initlog # The current logging function
|
||||
# 0 ==> unlimited input
|
||||
maxlen = 0
|
||||
|
||||
-def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
|
||||
+def parse(fp=None, environ=os.environ, keep_blank_values=0,
|
||||
+ strict_parsing=0, separator=None):
|
||||
"""Parse a query in the environment or from a file (default stdin)
|
||||
|
||||
Arguments, all optional:
|
||||
@@ -140,6 +141,8 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
|
||||
strict_parsing: flag indicating what to do with parsing errors.
|
||||
If false (the default), errors are silently ignored.
|
||||
If true, errors raise a ValueError exception.
|
||||
+
|
||||
+ separator: str. The symbol to use for separating the query arguments.
|
||||
"""
|
||||
if fp is None:
|
||||
fp = sys.stdin
|
||||
@@ -171,25 +174,26 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
|
||||
else:
|
||||
qs = ""
|
||||
environ['QUERY_STRING'] = qs # XXX Shouldn't, really
|
||||
- return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
|
||||
+ return urlparse.parse_qs(qs, keep_blank_values, strict_parsing, separator=separator)
|
||||
|
||||
|
||||
# parse query string function called from urlparse,
|
||||
# this is done in order to maintain backward compatibility.
|
||||
|
||||
-def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
|
||||
+def parse_qs(qs, keep_blank_values=0, strict_parsing=0, separator=None):
|
||||
"""Parse a query given as a string argument."""
|
||||
warn("cgi.parse_qs is deprecated, use urlparse.parse_qs instead",
|
||||
PendingDeprecationWarning, 2)
|
||||
- return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
|
||||
+ return urlparse.parse_qs(qs, keep_blank_values, strict_parsing,
|
||||
+ separator=separator)
|
||||
|
||||
|
||||
-def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
|
||||
+def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None, separator=None):
|
||||
"""Parse a query given as a string argument."""
|
||||
warn("cgi.parse_qsl is deprecated, use urlparse.parse_qsl instead",
|
||||
PendingDeprecationWarning, 2)
|
||||
return urlparse.parse_qsl(qs, keep_blank_values, strict_parsing,
|
||||
- max_num_fields)
|
||||
+ max_num_fields, separator=separator)
|
||||
|
||||
def parse_multipart(fp, pdict):
|
||||
"""Parse multipart input.
|
||||
@@ -288,7 +292,6 @@ def parse_multipart(fp, pdict):
|
||||
|
||||
return partdict
|
||||
|
||||
-
|
||||
def _parseparam(s):
|
||||
while s[:1] == ';':
|
||||
s = s[1:]
|
||||
@@ -395,7 +398,7 @@ class FieldStorage:
|
||||
|
||||
def __init__(self, fp=None, headers=None, outerboundary="",
|
||||
environ=os.environ, keep_blank_values=0, strict_parsing=0,
|
||||
- max_num_fields=None):
|
||||
+ max_num_fields=None, separator=None):
|
||||
"""Constructor. Read multipart/* until last part.
|
||||
|
||||
Arguments, all optional:
|
||||
@@ -430,6 +433,7 @@ class FieldStorage:
|
||||
self.keep_blank_values = keep_blank_values
|
||||
self.strict_parsing = strict_parsing
|
||||
self.max_num_fields = max_num_fields
|
||||
+ self.separator = separator
|
||||
if 'REQUEST_METHOD' in environ:
|
||||
method = environ['REQUEST_METHOD'].upper()
|
||||
self.qs_on_post = None
|
||||
@@ -613,7 +617,8 @@ class FieldStorage:
|
||||
if self.qs_on_post:
|
||||
qs += '&' + self.qs_on_post
|
||||
query = urlparse.parse_qsl(qs, self.keep_blank_values,
|
||||
- self.strict_parsing, self.max_num_fields)
|
||||
+ self.strict_parsing, self.max_num_fields,
|
||||
+ self.separator)
|
||||
self.list = [MiniFieldStorage(key, value) for key, value in query]
|
||||
self.skip_lines()
|
||||
|
||||
@@ -629,7 +634,8 @@ class FieldStorage:
|
||||
query = urlparse.parse_qsl(self.qs_on_post,
|
||||
self.keep_blank_values,
|
||||
self.strict_parsing,
|
||||
- self.max_num_fields)
|
||||
+ self.max_num_fields,
|
||||
+ self.separator)
|
||||
self.list.extend(MiniFieldStorage(key, value)
|
||||
for key, value in query)
|
||||
FieldStorageClass = None
|
||||
@@ -649,7 +655,8 @@ class FieldStorage:
|
||||
headers = rfc822.Message(self.fp)
|
||||
part = klass(self.fp, headers, ib,
|
||||
environ, keep_blank_values, strict_parsing,
|
||||
- max_num_fields)
|
||||
+ max_num_fields,
|
||||
+ separator=self.separator)
|
||||
|
||||
if max_num_fields is not None:
|
||||
max_num_fields -= 1
|
||||
@@ -817,10 +824,11 @@ class FormContentDict(UserDict.UserDict):
|
||||
form.dict == {key: [val, val, ...], ...}
|
||||
|
||||
"""
|
||||
- def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0):
|
||||
+ def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0, separator=None):
|
||||
self.dict = self.data = parse(environ=environ,
|
||||
keep_blank_values=keep_blank_values,
|
||||
- strict_parsing=strict_parsing)
|
||||
+ strict_parsing=strict_parsing,
|
||||
+ separator=separator)
|
||||
self.query_string = environ['QUERY_STRING']
|
||||
|
||||
|
||||
diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py
|
||||
index 743c2afbd4c..9956ea9d4e8 100644
|
||||
--- a/Lib/test/test_cgi.py
|
||||
+++ b/Lib/test/test_cgi.py
|
||||
@@ -61,12 +61,9 @@ parse_strict_test_cases = [
|
||||
("", ValueError("bad query field: ''")),
|
||||
("&", ValueError("bad query field: ''")),
|
||||
("&&", ValueError("bad query field: ''")),
|
||||
- (";", ValueError("bad query field: ''")),
|
||||
- (";&;", ValueError("bad query field: ''")),
|
||||
# Should the next few really be valid?
|
||||
("=", {}),
|
||||
("=&=", {}),
|
||||
- ("=;=", {}),
|
||||
# This rest seem to make sense
|
||||
("=a", {'': ['a']}),
|
||||
("&=a", ValueError("bad query field: ''")),
|
||||
@@ -81,8 +78,6 @@ parse_strict_test_cases = [
|
||||
("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}),
|
||||
("a=a+b&a=b+a", {'a': ['a b', 'b a']}),
|
||||
("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
|
||||
- ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
|
||||
- ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
|
||||
("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env",
|
||||
{'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'],
|
||||
'cuyer': ['r'],
|
||||
@@ -177,6 +172,60 @@ class CgiTests(unittest.TestCase):
|
||||
self.assertItemsEqual(sd.items(),
|
||||
first_second_elts(expect.items()))
|
||||
|
||||
+ def test_separator(self):
|
||||
+ parse_semicolon = [
|
||||
+ ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}),
|
||||
+ ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
|
||||
+ (";", ValueError("bad query field: ''")),
|
||||
+ (";;", ValueError("bad query field: ''")),
|
||||
+ ("=;a", ValueError("bad query field: 'a'")),
|
||||
+ (";b=a", ValueError("bad query field: ''")),
|
||||
+ ("b;=a", ValueError("bad query field: 'b'")),
|
||||
+ ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
|
||||
+ ("a=a+b;a=b+a", {'a': ['a b', 'b a']}),
|
||||
+ ]
|
||||
+ for orig, expect in parse_semicolon:
|
||||
+ env = {'QUERY_STRING': orig}
|
||||
+ fcd = cgi.FormContentDict(env, separator=';')
|
||||
+ sd = cgi.SvFormContentDict(env, separator=';')
|
||||
+ fs = cgi.FieldStorage(environ=env, separator=';')
|
||||
+ if isinstance(expect, dict):
|
||||
+ # test dict interface
|
||||
+ self.assertEqual(len(expect), len(fcd))
|
||||
+ self.assertItemsEqual(expect.keys(), fcd.keys())
|
||||
+ self.assertItemsEqual(expect.values(), fcd.values())
|
||||
+ self.assertItemsEqual(expect.items(), fcd.items())
|
||||
+ self.assertEqual(fcd.get("nonexistent field", "default"), "default")
|
||||
+ self.assertEqual(len(sd), len(fs))
|
||||
+ self.assertItemsEqual(sd.keys(), fs.keys())
|
||||
+ self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
|
||||
+ # test individual fields
|
||||
+ for key in expect.keys():
|
||||
+ expect_val = expect[key]
|
||||
+ self.assertTrue(fcd.has_key(key))
|
||||
+ self.assertItemsEqual(fcd[key], expect[key])
|
||||
+ self.assertEqual(fcd.get(key, "default"), fcd[key])
|
||||
+ self.assertTrue(fs.has_key(key))
|
||||
+ if len(expect_val) > 1:
|
||||
+ single_value = 0
|
||||
+ else:
|
||||
+ single_value = 1
|
||||
+ try:
|
||||
+ val = sd[key]
|
||||
+ except IndexError:
|
||||
+ self.assertFalse(single_value)
|
||||
+ self.assertEqual(fs.getvalue(key), expect_val)
|
||||
+ else:
|
||||
+ self.assertTrue(single_value)
|
||||
+ self.assertEqual(val, expect_val[0])
|
||||
+ self.assertEqual(fs.getvalue(key), expect_val[0])
|
||||
+ self.assertItemsEqual(sd.getlist(key), expect_val)
|
||||
+ if single_value:
|
||||
+ self.assertItemsEqual(sd.values(),
|
||||
+ first_elts(expect.values()))
|
||||
+ self.assertItemsEqual(sd.items(),
|
||||
+ first_second_elts(expect.items()))
|
||||
+
|
||||
def test_weird_formcontentdict(self):
|
||||
# Test the weird FormContentDict classes
|
||||
env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"}
|
||||
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
|
||||
index 86c4a0595c4..21875bb2991 100644
|
||||
--- a/Lib/test/test_urlparse.py
|
||||
+++ b/Lib/test/test_urlparse.py
|
||||
@@ -3,6 +3,12 @@ import sys
|
||||
import unicodedata
|
||||
import unittest
|
||||
import urlparse
|
||||
+from test.support import EnvironmentVarGuard
|
||||
+from warnings import catch_warnings, filterwarnings
|
||||
+import tempfile
|
||||
+import contextlib
|
||||
+import os.path
|
||||
+import shutil
|
||||
|
||||
RFC1808_BASE = "http://a/b/c/d;p?q#f"
|
||||
RFC2396_BASE = "http://a/b/c/d;p?q"
|
||||
@@ -24,16 +30,29 @@ parse_qsl_test_cases = [
|
||||
("&a=b", [('a', 'b')]),
|
||||
("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]),
|
||||
("a=1&a=2", [('a', '1'), ('a', '2')]),
|
||||
+]
|
||||
+
|
||||
+parse_qsl_test_cases_semicolon = [
|
||||
(";", []),
|
||||
(";;", []),
|
||||
(";a=b", [('a', 'b')]),
|
||||
("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]),
|
||||
("a=1;a=2", [('a', '1'), ('a', '2')]),
|
||||
- (b";", []),
|
||||
- (b";;", []),
|
||||
- (b";a=b", [(b'a', b'b')]),
|
||||
- (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]),
|
||||
- (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]),
|
||||
+]
|
||||
+
|
||||
+parse_qsl_test_cases_legacy = [
|
||||
+ ("a=1;a=2&a=3", [('a', '1'), ('a', '2'), ('a', '3')]),
|
||||
+ ("a=1;b=2&c=3", [('a', '1'), ('b', '2'), ('c', '3')]),
|
||||
+ ("a=1&b=2&c=3;", [('a', '1'), ('b', '2'), ('c', '3')]),
|
||||
+]
|
||||
+
|
||||
+parse_qsl_test_cases_warn = [
|
||||
+ (";a=b", [(';a', 'b')]),
|
||||
+ ("a=a+b;b=b+c", [('a', 'a b;b=b c')]),
|
||||
+ (b";a=b", [(b';a', b'b')]),
|
||||
+ (b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]),
|
||||
+ ("a=1;a=2&a=3", [('a', '1;a=2'), ('a', '3')]),
|
||||
+ (b"a=1;a=2&a=3", [(b'a', b'1;a=2'), (b'a', b'3')]),
|
||||
]
|
||||
|
||||
parse_qs_test_cases = [
|
||||
@@ -57,6 +76,9 @@ parse_qs_test_cases = [
|
||||
(b"&a=b", {b'a': [b'b']}),
|
||||
(b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
|
||||
(b"a=1&a=2", {b'a': [b'1', b'2']}),
|
||||
+]
|
||||
+
|
||||
+parse_qs_test_cases_semicolon = [
|
||||
(";", {}),
|
||||
(";;", {}),
|
||||
(";a=b", {'a': ['b']}),
|
||||
@@ -69,6 +91,24 @@ parse_qs_test_cases = [
|
||||
(b"a=1;a=2", {b'a': [b'1', b'2']}),
|
||||
]
|
||||
|
||||
+parse_qs_test_cases_legacy = [
|
||||
+ ("a=1;a=2&a=3", {'a': ['1', '2', '3']}),
|
||||
+ ("a=1;b=2&c=3", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
|
||||
+ ("a=1&b=2&c=3;", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
|
||||
+ (b"a=1;a=2&a=3", {b'a': [b'1', b'2', b'3']}),
|
||||
+ (b"a=1;b=2&c=3", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
|
||||
+ (b"a=1&b=2&c=3;", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
|
||||
+]
|
||||
+
|
||||
+parse_qs_test_cases_warn = [
|
||||
+ (";a=b", {';a': ['b']}),
|
||||
+ ("a=a+b;b=b+c", {'a': ['a b;b=b c']}),
|
||||
+ (b";a=b", {b';a': [b'b']}),
|
||||
+ (b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}),
|
||||
+ ("a=1;a=2&a=3", {'a': ['1;a=2', '3']}),
|
||||
+ (b"a=1;a=2&a=3", {b'a': [b'1;a=2', b'3']}),
|
||||
+]
|
||||
+
|
||||
class UrlParseTestCase(unittest.TestCase):
|
||||
|
||||
def checkRoundtrips(self, url, parsed, split):
|
||||
@@ -141,6 +181,40 @@ class UrlParseTestCase(unittest.TestCase):
|
||||
self.assertEqual(result, expect_without_blanks,
|
||||
"Error parsing %r" % orig)
|
||||
|
||||
+ def test_qs_default_warn(self):
|
||||
+ for orig, expect in parse_qs_test_cases_warn:
|
||||
+ with catch_warnings(record=True) as w:
|
||||
+ filterwarnings(action='always',
|
||||
+ category=urlparse._QueryStringSeparatorWarning)
|
||||
+ result = urlparse.parse_qs(orig, keep_blank_values=True)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 1)
|
||||
+ self.assertEqual(w[0].category, urlparse._QueryStringSeparatorWarning)
|
||||
+
|
||||
+ def test_qsl_default_warn(self):
|
||||
+ for orig, expect in parse_qsl_test_cases_warn:
|
||||
+ with catch_warnings(record=True) as w:
|
||||
+ filterwarnings(action='always',
|
||||
+ category=urlparse._QueryStringSeparatorWarning)
|
||||
+ result = urlparse.parse_qsl(orig, keep_blank_values=True)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 1)
|
||||
+ self.assertEqual(w[0].category, urlparse._QueryStringSeparatorWarning)
|
||||
+
|
||||
+ def test_default_qs_no_warnings(self):
|
||||
+ for orig, expect in parse_qs_test_cases:
|
||||
+ with catch_warnings(record=True) as w:
|
||||
+ result = urlparse.parse_qs(orig, keep_blank_values=True)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+
|
||||
+ def test_default_qsl_no_warnings(self):
|
||||
+ for orig, expect in parse_qsl_test_cases:
|
||||
+ with catch_warnings(record=True) as w:
|
||||
+ result = urlparse.parse_qsl(orig, keep_blank_values=True)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+
|
||||
def test_roundtrips(self):
|
||||
testcases = [
|
||||
('file:///tmp/junk.txt',
|
||||
@@ -626,6 +700,132 @@ class UrlParseTestCase(unittest.TestCase):
|
||||
self.assertEqual(urlparse.urlparse("http://www.python.org:80"),
|
||||
('http','www.python.org:80','','','',''))
|
||||
|
||||
+ def test_parse_qs_separator_bytes(self):
|
||||
+ expected = {b'a': [b'1'], b'b': [b'2']}
|
||||
+
|
||||
+ result = urlparse.parse_qs(b'a=1;b=2', separator=b';')
|
||||
+ self.assertEqual(result, expected)
|
||||
+ result = urlparse.parse_qs(b'a=1;b=2', separator=';')
|
||||
+ self.assertEqual(result, expected)
|
||||
+ result = urlparse.parse_qs('a=1;b=2', separator=';')
|
||||
+ self.assertEqual(result, {'a': ['1'], 'b': ['2']})
|
||||
+
|
||||
+ @contextlib.contextmanager
|
||||
+ def _qsl_sep_config(self, sep):
|
||||
+ """Context for the given parse_qsl default separator configured in config file"""
|
||||
+ old_filename = urlparse._QS_SEPARATOR_CONFIG_FILENAME
|
||||
+ urlparse._default_qs_separator = None
|
||||
+ try:
|
||||
+ tmpdirname = tempfile.mkdtemp()
|
||||
+ filename = os.path.join(tmpdirname, 'conf.cfg')
|
||||
+ with open(filename, 'w') as file:
|
||||
+ file.write('[parse_qs]\n')
|
||||
+ file.write('PYTHON_URLLIB_QS_SEPARATOR = {}'.format(sep))
|
||||
+ urlparse._QS_SEPARATOR_CONFIG_FILENAME = filename
|
||||
+ yield
|
||||
+ finally:
|
||||
+ urlparse._QS_SEPARATOR_CONFIG_FILENAME = old_filename
|
||||
+ urlparse._default_qs_separator = None
|
||||
+ shutil.rmtree(tmpdirname)
|
||||
+
|
||||
+ def test_parse_qs_separator_semicolon(self):
|
||||
+ for orig, expect in parse_qs_test_cases_semicolon:
|
||||
+ result = urlparse.parse_qs(orig, separator=';')
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
|
||||
+ result = urlparse.parse_qs(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+ with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
|
||||
+ result = urlparse.parse_qs(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+
|
||||
+ def test_parse_qsl_separator_semicolon(self):
|
||||
+ for orig, expect in parse_qsl_test_cases_semicolon:
|
||||
+ result = urlparse.parse_qsl(orig, separator=';')
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
|
||||
+ result = urlparse.parse_qsl(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+ with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
|
||||
+ result = urlparse.parse_qsl(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+
|
||||
+ def test_parse_qs_separator_legacy(self):
|
||||
+ for orig, expect in parse_qs_test_cases_legacy:
|
||||
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
|
||||
+ result = urlparse.parse_qs(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+ with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
|
||||
+ result = urlparse.parse_qs(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+
|
||||
+ def test_parse_qsl_separator_legacy(self):
|
||||
+ for orig, expect in parse_qsl_test_cases_legacy:
|
||||
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
|
||||
+ result = urlparse.parse_qsl(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+ with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
|
||||
+ result = urlparse.parse_qsl(orig)
|
||||
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
+ self.assertEqual(len(w), 0)
|
||||
+
|
||||
+ def test_parse_qs_separator_bad_value_env_or_config(self):
|
||||
+ for bad_sep in '', 'abc', 'safe', '&;', 'SEP':
|
||||
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = bad_sep
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ urlparse.parse_qsl('a=1;b=2')
|
||||
+ with self._qsl_sep_config('bad_sep'), catch_warnings(record=True) as w:
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ urlparse.parse_qsl('a=1;b=2')
|
||||
+
|
||||
+ def test_parse_qs_separator_bad_value_arg(self):
|
||||
+ for bad_sep in True, {}, '':
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ urlparse.parse_qsl('a=1;b=2', separator=bad_sep)
|
||||
+
|
||||
+ def test_parse_qs_separator_num_fields(self):
|
||||
+ for qs, sep in (
|
||||
+ ('a&b&c', '&'),
|
||||
+ ('a;b;c', ';'),
|
||||
+ ('a&b;c', 'legacy'),
|
||||
+ ):
|
||||
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
|
||||
+ if sep != 'legacy':
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ urlparse.parse_qsl(qs, separator=sep, max_num_fields=2)
|
||||
+ if sep:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = sep
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ urlparse.parse_qsl(qs, max_num_fields=2)
|
||||
+
|
||||
+ def test_parse_qs_separator_priority(self):
|
||||
+ # env variable trumps config file
|
||||
+ with self._qsl_sep_config('~'), EnvironmentVarGuard() as environ:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = '!'
|
||||
+ result = urlparse.parse_qs('a=1!b=2~c=3')
|
||||
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
|
||||
+ # argument trumps config file
|
||||
+ with self._qsl_sep_config('~'):
|
||||
+ result = urlparse.parse_qs('a=1$b=2~c=3', separator='$')
|
||||
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
|
||||
+ # argument trumps env variable
|
||||
+ with EnvironmentVarGuard() as environ:
|
||||
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = '~'
|
||||
+ result = urlparse.parse_qs('a=1$b=2~c=3', separator='$')
|
||||
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
|
||||
+
|
||||
def test_urlsplit_normalization(self):
|
||||
# Certain characters should never occur in the netloc,
|
||||
# including under normalization.
|
||||
diff --git a/Lib/urlparse.py b/Lib/urlparse.py
|
||||
index 798b467b605..69504d8fd93 100644
|
||||
--- a/Lib/urlparse.py
|
||||
+++ b/Lib/urlparse.py
|
||||
@@ -29,6 +29,7 @@ test_urlparse.py provides a good indicator of parsing behavior.
|
||||
"""
|
||||
|
||||
import re
|
||||
+import os
|
||||
|
||||
__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
|
||||
"urlsplit", "urlunsplit", "parse_qs", "parse_qsl"]
|
||||
@@ -382,7 +383,8 @@ def unquote(s):
|
||||
append(item)
|
||||
return ''.join(res)
|
||||
|
||||
-def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
|
||||
+def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None,
|
||||
+ separator=None):
|
||||
"""Parse a query given as a string argument.
|
||||
|
||||
Arguments:
|
||||
@@ -405,14 +407,23 @@ def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
|
||||
"""
|
||||
dict = {}
|
||||
for name, value in parse_qsl(qs, keep_blank_values, strict_parsing,
|
||||
- max_num_fields):
|
||||
+ max_num_fields, separator):
|
||||
if name in dict:
|
||||
dict[name].append(value)
|
||||
else:
|
||||
dict[name] = [value]
|
||||
return dict
|
||||
|
||||
-def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
|
||||
+class _QueryStringSeparatorWarning(RuntimeWarning):
|
||||
+ """Warning for using default `separator` in parse_qs or parse_qsl"""
|
||||
+
|
||||
+# The default "separator" for parse_qsl can be specified in a config file.
|
||||
+# It's cached after first read.
|
||||
+_QS_SEPARATOR_CONFIG_FILENAME = '/etc/python/urllib.cfg'
|
||||
+_default_qs_separator = None
|
||||
+
|
||||
+def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None,
|
||||
+ separator=None):
|
||||
"""Parse a query given as a string argument.
|
||||
|
||||
Arguments:
|
||||
@@ -434,15 +445,72 @@ def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
|
||||
|
||||
Returns a list, as G-d intended.
|
||||
"""
|
||||
+
|
||||
+ if (not separator or (not isinstance(separator, (str, bytes)))) and separator is not None:
|
||||
+ raise ValueError("Separator must be of type string or bytes.")
|
||||
+
|
||||
+ # Used when both "&" and ";" act as separators. (Need a non-string value.)
|
||||
+ _legacy = object()
|
||||
+
|
||||
+ if separator is None:
|
||||
+ global _default_qs_separator
|
||||
+ separator = _default_qs_separator
|
||||
+ envvar_name = 'PYTHON_URLLIB_QS_SEPARATOR'
|
||||
+ if separator is None:
|
||||
+ # Set default separator from environment variable
|
||||
+ separator = os.environ.get(envvar_name)
|
||||
+ config_source = 'environment variable'
|
||||
+ if separator is None:
|
||||
+ # Set default separator from the configuration file
|
||||
+ try:
|
||||
+ file = open(_QS_SEPARATOR_CONFIG_FILENAME)
|
||||
+ except EnvironmentError:
|
||||
+ pass
|
||||
+ else:
|
||||
+ with file:
|
||||
+ import ConfigParser
|
||||
+ config = ConfigParser.ConfigParser()
|
||||
+ config.readfp(file)
|
||||
+ separator = config.get('parse_qs', envvar_name)
|
||||
+ _default_qs_separator = separator
|
||||
+ config_source = _QS_SEPARATOR_CONFIG_FILENAME
|
||||
+ if separator is None:
|
||||
+ # The default is '&', but warn if not specified explicitly
|
||||
+ if ';' in qs:
|
||||
+ from warnings import warn
|
||||
+ warn("The default separator of urlparse.parse_qsl and "
|
||||
+ + "parse_qs was changed to '&' to avoid a web cache "
|
||||
+ + "poisoning issue (CVE-2021-23336). "
|
||||
+ + "By default, semicolons no longer act as query field "
|
||||
+ + "separators. "
|
||||
+ + "See https://access.redhat.com/articles/5860431 for "
|
||||
+ + "more details.",
|
||||
+ _QueryStringSeparatorWarning, stacklevel=2)
|
||||
+ separator = '&'
|
||||
+ elif separator == 'legacy':
|
||||
+ separator = _legacy
|
||||
+ elif len(separator) != 1:
|
||||
+ raise ValueError(
|
||||
+ '{} (from {}) must contain '.format(envvar_name, config_source)
|
||||
+ + '1 character, or "legacy". See '
|
||||
+ + 'https://access.redhat.com/articles/5860431 for more details.'
|
||||
+ )
|
||||
+
|
||||
# If max_num_fields is defined then check that the number of fields
|
||||
# is less than max_num_fields. This prevents a memory exhaustion DOS
|
||||
# attack via post bodies with many fields.
|
||||
if max_num_fields is not None:
|
||||
- num_fields = 1 + qs.count('&') + qs.count(';')
|
||||
+ if separator is _legacy:
|
||||
+ num_fields = 1 + qs.count('&') + qs.count(';')
|
||||
+ else:
|
||||
+ num_fields = 1 + qs.count(separator)
|
||||
if max_num_fields < num_fields:
|
||||
raise ValueError('Max number of fields exceeded')
|
||||
|
||||
- pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
|
||||
+ if separator is _legacy:
|
||||
+ pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
|
||||
+ else:
|
||||
+ pairs = [s1 for s1 in qs.split(separator)]
|
||||
r = []
|
||||
for name_value in pairs:
|
||||
if not name_value and not strict_parsing:
|
||||
--
|
||||
2.30.2
|
||||
|
35
00366-CVE-2021-3733.patch
Normal file
35
00366-CVE-2021-3733.patch
Normal file
@ -0,0 +1,35 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lumir Balhar <lbalhar@redhat.com>
|
||||
Date: Tue, 14 Sep 2021 11:34:43 +0200
|
||||
Subject: [PATCH] 00366-CVE-2021-3733.patch
|
||||
|
||||
00366 #
|
||||
CVE-2021-3733: Fix ReDoS in urllib AbstractBasicAuthHandler
|
||||
|
||||
Fix Regular Expression Denial of Service (ReDoS) vulnerability in
|
||||
urllib2.AbstractBasicAuthHandler. The ReDoS-vulnerable regex
|
||||
has quadratic worst-case complexity and it allows cause a denial of
|
||||
service when identifying crafted invalid RFCs. This ReDoS issue is on
|
||||
the client side and needs remote attackers to control the HTTP server.
|
||||
|
||||
Backported from Python 3 together with another backward-compatible
|
||||
improvement of the regex from fix for CVE-2020-8492.
|
||||
|
||||
Co-authored-by: Yeting Li <liyt@ios.ac.cn>
|
||||
---
|
||||
Lib/urllib2.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/Lib/urllib2.py b/Lib/urllib2.py
|
||||
index fd19e1ae943..e286583ecba 100644
|
||||
--- a/Lib/urllib2.py
|
||||
+++ b/Lib/urllib2.py
|
||||
@@ -858,7 +858,7 @@ class AbstractBasicAuthHandler:
|
||||
|
||||
# allow for double- and single-quoted realm values
|
||||
# (single quotes are a violation of the RFC, but appear in the wild)
|
||||
- rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
|
||||
+ rx = re.compile('(?:[^,]*,)*[ \t]*([^ \t,]+)[ \t]+'
|
||||
'realm=(["\']?)([^"\']*)\\2', re.I)
|
||||
|
||||
# XXX could pre-emptively send auth info already accepted (RFC 2617,
|
89
00368-CVE-2021-3737.patch
Normal file
89
00368-CVE-2021-3737.patch
Normal file
@ -0,0 +1,89 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lumir Balhar <lbalhar@redhat.com>
|
||||
Date: Fri, 17 Sep 2021 07:56:50 +0200
|
||||
Subject: [PATCH] 00368-CVE-2021-3737.patch
|
||||
|
||||
00368 #
|
||||
CVE-2021-3737: http client infinite line reading (DoS) after a HTTP 100 Continue
|
||||
|
||||
Fixes http.client potential denial of service where it could get stuck reading
|
||||
lines from a malicious server after a 100 Continue response.
|
||||
|
||||
Backported from Python 3.
|
||||
|
||||
Co-authored-by: Gregory P. Smith <greg@krypto.org>
|
||||
Co-authored-by: Gen Xu <xgbarry@gmail.com>
|
||||
---
|
||||
Lib/httplib.py | 32 +++++++++++++++++++++++---------
|
||||
Lib/test/test_httplib.py | 8 ++++++++
|
||||
2 files changed, 31 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/Lib/httplib.py b/Lib/httplib.py
|
||||
index a63677477d5..f9a27619e62 100644
|
||||
--- a/Lib/httplib.py
|
||||
+++ b/Lib/httplib.py
|
||||
@@ -365,6 +365,25 @@ class HTTPMessage(mimetools.Message):
|
||||
# It's not a header line; skip it and try the next line.
|
||||
self.status = 'Non-header line where header expected'
|
||||
|
||||
+
|
||||
+def _read_headers(fp):
|
||||
+ """Reads potential header lines into a list from a file pointer.
|
||||
+ Length of line is limited by _MAXLINE, and number of
|
||||
+ headers is limited by _MAXHEADERS.
|
||||
+ """
|
||||
+ headers = []
|
||||
+ while True:
|
||||
+ line = fp.readline(_MAXLINE + 1)
|
||||
+ 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
|
||||
+ return headers
|
||||
+
|
||||
+
|
||||
class HTTPResponse:
|
||||
|
||||
# strict: If true, raise BadStatusLine if the status line can't be
|
||||
@@ -453,15 +472,10 @@ class HTTPResponse:
|
||||
if status != CONTINUE:
|
||||
break
|
||||
# skip the header from the 100 response
|
||||
- while True:
|
||||
- skip = self.fp.readline(_MAXLINE + 1)
|
||||
- if len(skip) > _MAXLINE:
|
||||
- raise LineTooLong("header line")
|
||||
- skip = skip.strip()
|
||||
- if not skip:
|
||||
- break
|
||||
- if self.debuglevel > 0:
|
||||
- print "header:", skip
|
||||
+ skipped_headers = _read_headers(self.fp)
|
||||
+ if self.debuglevel > 0:
|
||||
+ print("headers:", skipped_headers)
|
||||
+ del skipped_headers
|
||||
|
||||
self.status = status
|
||||
self.reason = reason.strip()
|
||||
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
|
||||
index b5fec9aa1ec..d05c0fc28d2 100644
|
||||
--- a/Lib/test/test_httplib.py
|
||||
+++ b/Lib/test/test_httplib.py
|
||||
@@ -700,6 +700,14 @@ class BasicTest(TestCase):
|
||||
resp = httplib.HTTPResponse(FakeSocket(body))
|
||||
self.assertRaises(httplib.LineTooLong, resp.begin)
|
||||
|
||||
+ def test_overflowing_header_limit_after_100(self):
|
||||
+ body = (
|
||||
+ 'HTTP/1.1 100 OK\r\n'
|
||||
+ 'r\n' * 32768
|
||||
+ )
|
||||
+ resp = httplib.HTTPResponse(FakeSocket(body))
|
||||
+ self.assertRaises(httplib.HTTPException, resp.begin)
|
||||
+
|
||||
def test_overflowing_chunked_line(self):
|
||||
body = (
|
||||
'HTTP/1.1 200 OK\r\n'
|
80
00372-CVE-2021-4189.patch
Normal file
80
00372-CVE-2021-4189.patch
Normal file
@ -0,0 +1,80 @@
|
||||
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
|
||||
index 6644554..0550f0a 100644
|
||||
--- a/Lib/ftplib.py
|
||||
+++ b/Lib/ftplib.py
|
||||
@@ -108,6 +108,8 @@ class FTP:
|
||||
file = None
|
||||
welcome = None
|
||||
passiveserver = 1
|
||||
+ # Disables https://bugs.python.org/issue43285 security if set to True.
|
||||
+ trust_server_pasv_ipv4_address = False
|
||||
|
||||
# Initialization method (called by class instantiation).
|
||||
# Initialize host to localhost, port to standard ftp port
|
||||
@@ -310,8 +312,13 @@ class FTP:
|
||||
return sock
|
||||
|
||||
def makepasv(self):
|
||||
+ """Internal: Does the PASV or EPSV handshake -> (address, port)"""
|
||||
if self.af == socket.AF_INET:
|
||||
- host, port = parse227(self.sendcmd('PASV'))
|
||||
+ untrusted_host, port = parse227(self.sendcmd('PASV'))
|
||||
+ if self.trust_server_pasv_ipv4_address:
|
||||
+ host = untrusted_host
|
||||
+ else:
|
||||
+ host = self.sock.getpeername()[0]
|
||||
else:
|
||||
host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
|
||||
return host, port
|
||||
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
|
||||
index 8a3eb06..62a3f5e 100644
|
||||
--- a/Lib/test/test_ftplib.py
|
||||
+++ b/Lib/test/test_ftplib.py
|
||||
@@ -67,6 +67,10 @@ class DummyFTPHandler(asynchat.async_chat):
|
||||
self.rest = None
|
||||
self.next_retr_data = RETR_DATA
|
||||
self.push('220 welcome')
|
||||
+ # We use this as the string IPv4 address to direct the client
|
||||
+ # to in response to a PASV command. To test security behavior.
|
||||
+ # https://bugs.python.org/issue43285/.
|
||||
+ self.fake_pasv_server_ip = '252.253.254.255'
|
||||
|
||||
def collect_incoming_data(self, data):
|
||||
self.in_buffer.append(data)
|
||||
@@ -109,7 +113,8 @@ class DummyFTPHandler(asynchat.async_chat):
|
||||
sock.bind((self.socket.getsockname()[0], 0))
|
||||
sock.listen(5)
|
||||
sock.settimeout(10)
|
||||
- ip, port = sock.getsockname()[:2]
|
||||
+ port = sock.getsockname()[1]
|
||||
+ ip = self.fake_pasv_server_ip
|
||||
ip = ip.replace('.', ',')
|
||||
p1, p2 = divmod(port, 256)
|
||||
self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
|
||||
@@ -577,6 +582,26 @@ class TestFTPClass(TestCase):
|
||||
# IPv4 is in use, just make sure send_epsv has not been used
|
||||
self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
|
||||
|
||||
+ def test_makepasv_issue43285_security_disabled(self):
|
||||
+ """Test the opt-in to the old vulnerable behavior."""
|
||||
+ self.client.trust_server_pasv_ipv4_address = True
|
||||
+ bad_host, port = self.client.makepasv()
|
||||
+ self.assertEqual(
|
||||
+ bad_host, self.server.handler_instance.fake_pasv_server_ip)
|
||||
+ # Opening and closing a connection keeps the dummy server happy
|
||||
+ # instead of timing out on accept.
|
||||
+ socket.create_connection((self.client.sock.getpeername()[0], port),
|
||||
+ timeout=TIMEOUT).close()
|
||||
+
|
||||
+ def test_makepasv_issue43285_security_enabled_default(self):
|
||||
+ self.assertFalse(self.client.trust_server_pasv_ipv4_address)
|
||||
+ trusted_host, port = self.client.makepasv()
|
||||
+ self.assertNotEqual(
|
||||
+ trusted_host, self.server.handler_instance.fake_pasv_server_ip)
|
||||
+ # Opening and closing a connection keeps the dummy server happy
|
||||
+ # instead of timing out on accept.
|
||||
+ socket.create_connection((trusted_host, port), timeout=TIMEOUT).close()
|
||||
+
|
||||
def test_line_too_long(self):
|
||||
self.assertRaises(ftplib.Error, self.client.sendcmd,
|
||||
'x' * self.client.maxline * 2)
|
127
00377-CVE-2022-0391.patch
Normal file
127
00377-CVE-2022-0391.patch
Normal file
@ -0,0 +1,127 @@
|
||||
diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst
|
||||
index 97d1119257c..c08c3dc8e8f 100644
|
||||
--- a/Doc/library/urlparse.rst
|
||||
+++ b/Doc/library/urlparse.rst
|
||||
@@ -125,6 +125,9 @@ The :mod:`urlparse` module defines the following functions:
|
||||
decomposed before parsing, or is not a Unicode string, no error will be
|
||||
raised.
|
||||
|
||||
+ Following the `WHATWG spec`_ that updates RFC 3986, ASCII newline
|
||||
+ ``\n``, ``\r`` and tab ``\t`` characters are stripped from the URL.
|
||||
+
|
||||
.. versionchanged:: 2.5
|
||||
Added attributes to return value.
|
||||
|
||||
@@ -321,6 +324,10 @@ The :mod:`urlparse` module defines the following functions:
|
||||
|
||||
.. seealso::
|
||||
|
||||
+ `WHATWG`_ - URL Living standard
|
||||
+ Working Group for the URL Standard that defines URLs, domains, IP addresses, the
|
||||
+ application/x-www-form-urlencoded format, and their API.
|
||||
+
|
||||
:rfc:`3986` - Uniform Resource Identifiers
|
||||
This is the current standard (STD66). Any changes to urlparse module
|
||||
should conform to this. Certain deviations could be observed, which are
|
||||
@@ -345,6 +352,7 @@ The :mod:`urlparse` module defines the following functions:
|
||||
:rfc:`1738` - Uniform Resource Locators (URL)
|
||||
This specifies the formal syntax and semantics of absolute URLs.
|
||||
|
||||
+.. _WHATWG: https://url.spec.whatwg.org/
|
||||
|
||||
.. _urlparse-result-object:
|
||||
|
||||
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
|
||||
index 21875bb2991..16eefed56f6 100644
|
||||
--- a/Lib/test/test_urlparse.py
|
||||
+++ b/Lib/test/test_urlparse.py
|
||||
@@ -618,6 +618,55 @@ class UrlParseTestCase(unittest.TestCase):
|
||||
self.assertEqual(p1.path, '863-1234')
|
||||
self.assertEqual(p1.params, 'phone-context=+1-914-555')
|
||||
|
||||
+ def test_urlsplit_remove_unsafe_bytes(self):
|
||||
+ # Remove ASCII tabs and newlines from input, for http common case scenario.
|
||||
+ url = "h\nttp://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urlparse.urlsplit(url)
|
||||
+ self.assertEqual(p.scheme, "http")
|
||||
+ self.assertEqual(p.netloc, "www.python.org")
|
||||
+ self.assertEqual(p.path, "/javascript:alert('msg')/")
|
||||
+ self.assertEqual(p.query, "query=something")
|
||||
+ self.assertEqual(p.fragment, "fragment")
|
||||
+ self.assertEqual(p.username, None)
|
||||
+ self.assertEqual(p.password, None)
|
||||
+ self.assertEqual(p.hostname, "www.python.org")
|
||||
+ self.assertEqual(p.port, None)
|
||||
+ self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # Remove ASCII tabs and newlines from input as bytes, for http common case scenario.
|
||||
+ url = b"h\nttp://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urlparse.urlsplit(url)
|
||||
+ self.assertEqual(p.scheme, b"http")
|
||||
+ self.assertEqual(p.netloc, b"www.python.org")
|
||||
+ self.assertEqual(p.path, b"/javascript:alert('msg')/")
|
||||
+ self.assertEqual(p.query, b"query=something")
|
||||
+ self.assertEqual(p.fragment, b"fragment")
|
||||
+ self.assertEqual(p.username, None)
|
||||
+ self.assertEqual(p.password, None)
|
||||
+ self.assertEqual(p.hostname, b"www.python.org")
|
||||
+ self.assertEqual(p.port, None)
|
||||
+ self.assertEqual(p.geturl(), b"http://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # any scheme
|
||||
+ url = "x-new-scheme\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urlparse.urlsplit(url)
|
||||
+ self.assertEqual(p.geturl(), "x-new-scheme://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # Remove ASCII tabs and newlines from input as bytes, any scheme.
|
||||
+ url = b"x-new-scheme\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urlparse.urlsplit(url)
|
||||
+ self.assertEqual(p.geturl(), b"x-new-scheme://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # Unsafe bytes is not returned from urlparse cache.
|
||||
+ # scheme is stored after parsing, sending an scheme with unsafe bytes *will not* return an unsafe scheme
|
||||
+ url = "https://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ scheme = "htt\nps"
|
||||
+ for _ in range(2):
|
||||
+ p = urlparse.urlsplit(url, scheme=scheme)
|
||||
+ self.assertEqual(p.scheme, "https")
|
||||
+ self.assertEqual(p.geturl(), "https://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+
|
||||
|
||||
def test_attributes_bad_port(self):
|
||||
"""Check handling of non-integer ports."""
|
||||
diff --git a/Lib/urlparse.py b/Lib/urlparse.py
|
||||
index 69504d8fd93..6cc40a8d2fb 100644
|
||||
--- a/Lib/urlparse.py
|
||||
+++ b/Lib/urlparse.py
|
||||
@@ -63,6 +63,9 @@ scheme_chars = ('abcdefghijklmnopqrstuvwxyz'
|
||||
'0123456789'
|
||||
'+-.')
|
||||
|
||||
+# Unsafe bytes to be removed per WHATWG spec
|
||||
+_UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n']
|
||||
+
|
||||
MAX_CACHE_SIZE = 20
|
||||
_parse_cache = {}
|
||||
|
||||
@@ -185,12 +188,19 @@ def _checknetloc(netloc):
|
||||
"under NFKC normalization"
|
||||
% netloc)
|
||||
|
||||
+def _remove_unsafe_bytes_from_url(url):
|
||||
+ for b in _UNSAFE_URL_BYTES_TO_REMOVE:
|
||||
+ url = url.replace(b, "")
|
||||
+ return url
|
||||
+
|
||||
def urlsplit(url, scheme='', allow_fragments=True):
|
||||
"""Parse a URL into 5 components:
|
||||
<scheme>://<netloc>/<path>?<query>#<fragment>
|
||||
Return a 5-tuple: (scheme, netloc, path, query, fragment).
|
||||
Note that we don't break the components up in smaller bits
|
||||
(e.g. netloc is a single string) and we don't expand % escapes."""
|
||||
+ url = _remove_unsafe_bytes_from_url(url)
|
||||
+ scheme = _remove_unsafe_bytes_from_url(scheme)
|
||||
allow_fragments = bool(allow_fragments)
|
||||
key = url, scheme, allow_fragments, type(url), type(scheme)
|
||||
cached = _parse_cache.get(key, None)
|
94
00378-support-expat-2-4-5.patch
Normal file
94
00378-support-expat-2-4-5.patch
Normal file
@ -0,0 +1,94 @@
|
||||
From 35f5707b555d3bca92858de16760918e76463a1e Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 21 Feb 2022 15:48:32 +0100
|
||||
Subject: [PATCH] 00378-support-expat-2-4-5.patch
|
||||
|
||||
00378 #
|
||||
Support expat 2.4.5
|
||||
|
||||
Curly brackets were never allowed in namespace URIs
|
||||
according to RFC 3986, and so-called namespace-validating
|
||||
XML parsers have the right to reject them a invalid URIs.
|
||||
|
||||
libexpat >=2.4.5 has become strcter in that regard due to
|
||||
related security issues; with ET.XML instantiating a
|
||||
namespace-aware parser under the hood, this test has no
|
||||
future in CPython.
|
||||
|
||||
References:
|
||||
- https://datatracker.ietf.org/doc/html/rfc3968
|
||||
- https://www.w3.org/TR/xml-names/
|
||||
|
||||
Also, test_minidom.py: Support Expat >=2.4.5
|
||||
|
||||
Upstream: https://bugs.python.org/issue46811
|
||||
|
||||
Backported from Python 3.
|
||||
|
||||
Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
|
||||
---
|
||||
Lib/test/test_minidom.py | 8 ++++++--
|
||||
Lib/test/test_xml_etree.py | 6 ------
|
||||
.../next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst | 1 +
|
||||
3 files changed, 7 insertions(+), 8 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst
|
||||
|
||||
diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py
|
||||
index 2eb6423..2c9a7a3 100644
|
||||
--- a/Lib/test/test_minidom.py
|
||||
+++ b/Lib/test/test_minidom.py
|
||||
@@ -6,12 +6,14 @@ from StringIO import StringIO
|
||||
from test import support
|
||||
import unittest
|
||||
|
||||
+import pyexpat
|
||||
import xml.dom
|
||||
import xml.dom.minidom
|
||||
import xml.parsers.expat
|
||||
|
||||
from xml.dom.minidom import parse, Node, Document, parseString
|
||||
from xml.dom.minidom import getDOMImplementation
|
||||
+from xml.parsers.expat import ExpatError
|
||||
|
||||
|
||||
tstfile = support.findfile("test.xml", subdir="xmltestdata")
|
||||
@@ -1051,8 +1053,10 @@ class MinidomTest(unittest.TestCase):
|
||||
|
||||
# Verify that character decoding errors raise exceptions instead
|
||||
# of crashing
|
||||
- self.assertRaises(UnicodeDecodeError, parseString,
|
||||
- '<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
|
||||
+ self.assertRaises(ExpatError, parseString,
|
||||
+ '<fran\xe7ais></fran\xe7ais>')
|
||||
+ self.assertRaises(ExpatError, parseString,
|
||||
+ '<franais>Comment \xe7a va ? Tr\xe8s bien ?</franais>')
|
||||
|
||||
doc.unlink()
|
||||
|
||||
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
|
||||
index c75d55f..0855bc0 100644
|
||||
--- a/Lib/test/test_xml_etree.py
|
||||
+++ b/Lib/test/test_xml_etree.py
|
||||
@@ -1482,12 +1482,6 @@ class BugsTest(unittest.TestCase):
|
||||
b"<?xml version='1.0' encoding='ascii'?>\n"
|
||||
b'<body>tãg</body>')
|
||||
|
||||
- def test_issue3151(self):
|
||||
- e = ET.XML('<prefix:localname xmlns:prefix="${stuff}"/>')
|
||||
- self.assertEqual(e.tag, '{${stuff}}localname')
|
||||
- t = ET.ElementTree(e)
|
||||
- self.assertEqual(ET.tostring(e), b'<ns0:localname xmlns:ns0="${stuff}" />')
|
||||
-
|
||||
def test_issue6565(self):
|
||||
elem = ET.XML("<body><tag/></body>")
|
||||
self.assertEqual(summarize_list(elem), ['tag'])
|
||||
diff --git a/Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst b/Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst
|
||||
new file mode 100644
|
||||
index 0000000..6969bd1
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst
|
||||
@@ -0,0 +1 @@
|
||||
+Make test suite support Expat >=2.4.5
|
||||
--
|
||||
2.35.1
|
||||
|
440
00382-cve-2015-20107.patch
Normal file
440
00382-cve-2015-20107.patch
Normal file
@ -0,0 +1,440 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <encukou@gmail.com>
|
||||
Date: Fri, 3 Jun 2022 11:43:35 +0200
|
||||
Subject: [PATCH] 00382-cve-2015-20107.patch
|
||||
|
||||
00382 #
|
||||
Make mailcap refuse to match unsafe filenames/types/params (GH-91993)
|
||||
|
||||
Upstream: https://github.com/python/cpython/issues/68966
|
||||
|
||||
Tracker bug: https://bugzilla.redhat.com/show_bug.cgi?id=2075390
|
||||
|
||||
Backported from python3.
|
||||
---
|
||||
Doc/library/mailcap.rst | 12 +
|
||||
Lib/mailcap.py | 29 +-
|
||||
Lib/test/mailcap.txt | 39 +++
|
||||
Lib/test/test_mailcap.py | 259 ++++++++++++++++++
|
||||
...2-04-27-18-25-30.gh-issue-68966.gjS8zs.rst | 4 +
|
||||
5 files changed, 341 insertions(+), 2 deletions(-)
|
||||
create mode 100644 Lib/test/mailcap.txt
|
||||
create mode 100644 Lib/test/test_mailcap.py
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst
|
||||
|
||||
diff --git a/Doc/library/mailcap.rst b/Doc/library/mailcap.rst
|
||||
index 750d085796f..5f75ee6086e 100644
|
||||
--- a/Doc/library/mailcap.rst
|
||||
+++ b/Doc/library/mailcap.rst
|
||||
@@ -54,6 +54,18 @@ standard. However, mailcap files are supported on most Unix systems.
|
||||
use) to determine whether or not the mailcap line applies. :func:`findmatch`
|
||||
will automatically check such conditions and skip the entry if the check fails.
|
||||
|
||||
+ .. versionchanged:: 3.11
|
||||
+
|
||||
+ To prevent security issues with shell metacharacters (symbols that have
|
||||
+ special effects in a shell command line), ``findmatch`` will refuse
|
||||
+ to inject ASCII characters other than alphanumerics and ``@+=:,./-_``
|
||||
+ into the returned command line.
|
||||
+
|
||||
+ If a disallowed character appears in *filename*, ``findmatch`` will always
|
||||
+ return ``(None, None)`` as if no entry was found.
|
||||
+ If such a character appears elsewhere (a value in *plist* or in *MIMEtype*),
|
||||
+ ``findmatch`` will ignore all mailcap entries which use that value.
|
||||
+ A :mod:`warning <warnings>` will be raised in either case.
|
||||
|
||||
.. function:: getcaps()
|
||||
|
||||
diff --git a/Lib/mailcap.py b/Lib/mailcap.py
|
||||
index 04077ba0db2..1108b447b1d 100644
|
||||
--- a/Lib/mailcap.py
|
||||
+++ b/Lib/mailcap.py
|
||||
@@ -1,9 +1,18 @@
|
||||
"""Mailcap file handling. See RFC 1524."""
|
||||
|
||||
import os
|
||||
+import warnings
|
||||
+import re
|
||||
|
||||
__all__ = ["getcaps","findmatch"]
|
||||
|
||||
+
|
||||
+_find_unsafe = re.compile(r'[^\xa1-\xff\w@+=:,./-]').search
|
||||
+
|
||||
+class UnsafeMailcapInput(Warning):
|
||||
+ """Warning raised when refusing unsafe input"""
|
||||
+
|
||||
+
|
||||
# Part 1: top-level interface.
|
||||
|
||||
def getcaps():
|
||||
@@ -144,15 +153,22 @@ def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]):
|
||||
entry to use.
|
||||
|
||||
"""
|
||||
+ if _find_unsafe(filename):
|
||||
+ msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (filename,)
|
||||
+ warnings.warn(msg, UnsafeMailcapInput)
|
||||
+ return None, None
|
||||
entries = lookup(caps, MIMEtype, key)
|
||||
# XXX This code should somehow check for the needsterminal flag.
|
||||
for e in entries:
|
||||
if 'test' in e:
|
||||
test = subst(e['test'], filename, plist)
|
||||
+ if test is None:
|
||||
+ continue
|
||||
if test and os.system(test) != 0:
|
||||
continue
|
||||
command = subst(e[key], MIMEtype, filename, plist)
|
||||
- return command, e
|
||||
+ if command is not None:
|
||||
+ return command, e
|
||||
return None, None
|
||||
|
||||
def lookup(caps, MIMEtype, key=None):
|
||||
@@ -184,6 +200,10 @@ def subst(field, MIMEtype, filename, plist=[]):
|
||||
elif c == 's':
|
||||
res = res + filename
|
||||
elif c == 't':
|
||||
+ if _find_unsafe(MIMEtype):
|
||||
+ msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,)
|
||||
+ warnings.warn(msg, UnsafeMailcapInput)
|
||||
+ return None
|
||||
res = res + MIMEtype
|
||||
elif c == '{':
|
||||
start = i
|
||||
@@ -191,7 +211,12 @@ def subst(field, MIMEtype, filename, plist=[]):
|
||||
i = i+1
|
||||
name = field[start:i]
|
||||
i = i+1
|
||||
- res = res + findparam(name, plist)
|
||||
+ param = findparam(name, plist)
|
||||
+ if _find_unsafe(param):
|
||||
+ msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name)
|
||||
+ warnings.warn(msg, UnsafeMailcapInput)
|
||||
+ return None
|
||||
+ res = res + param
|
||||
# XXX To do:
|
||||
# %n == number of parts if type is multipart/*
|
||||
# %F == list of alternating type and filename for parts
|
||||
diff --git a/Lib/test/mailcap.txt b/Lib/test/mailcap.txt
|
||||
new file mode 100644
|
||||
index 00000000000..08a76e65941
|
||||
--- /dev/null
|
||||
+++ b/Lib/test/mailcap.txt
|
||||
@@ -0,0 +1,39 @@
|
||||
+# Mailcap file for test_mailcap; based on RFC 1524
|
||||
+# Referred to by test_mailcap.py
|
||||
+
|
||||
+#
|
||||
+# This is a comment.
|
||||
+#
|
||||
+
|
||||
+application/frame; showframe %s; print="cat %s | lp"
|
||||
+application/postscript; ps-to-terminal %s;\
|
||||
+ needsterminal
|
||||
+application/postscript; ps-to-terminal %s; \
|
||||
+ compose=idraw %s
|
||||
+application/x-dvi; xdvi %s
|
||||
+application/x-movie; movieplayer %s; compose=moviemaker %s; \
|
||||
+ description="Movie"; \
|
||||
+ x11-bitmap="/usr/lib/Zmail/bitmaps/movie.xbm"
|
||||
+application/*; echo "This is \"%t\" but \
|
||||
+ is 50 \% Greek to me" \; cat %s; copiousoutput
|
||||
+
|
||||
+audio/basic; showaudio %s; compose=audiocompose %s; edit=audiocompose %s;\
|
||||
+description="An audio fragment"
|
||||
+audio/* ; /usr/local/bin/showaudio %t
|
||||
+
|
||||
+image/rgb; display %s
|
||||
+#image/gif; display %s
|
||||
+image/x-xwindowdump; display %s
|
||||
+
|
||||
+# The continuation char shouldn't \
|
||||
+# make a difference in a comment.
|
||||
+
|
||||
+message/external-body; showexternal %s %{access-type} %{name} %{site} \
|
||||
+ %{directory} %{mode} %{server}; needsterminal; composetyped = extcompose %s; \
|
||||
+ description="A reference to data stored in an external location"
|
||||
+
|
||||
+text/richtext; shownonascii iso-8859-8 -e richtext -p %s; test=test "`echo \
|
||||
+ %{charset} | tr '[A-Z]' '[a-z]'`" = iso-8859-8; copiousoutput
|
||||
+
|
||||
+video/*; animate %s
|
||||
+video/mpeg; mpeg_play %s
|
||||
\ No newline at end of file
|
||||
diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py
|
||||
new file mode 100644
|
||||
index 00000000000..35da7fb0741
|
||||
--- /dev/null
|
||||
+++ b/Lib/test/test_mailcap.py
|
||||
@@ -0,0 +1,259 @@
|
||||
+import copy
|
||||
+import os
|
||||
+import sys
|
||||
+import test.support
|
||||
+import unittest
|
||||
+from test import support as os_helper
|
||||
+from test import support as warnings_helper
|
||||
+from collections import OrderedDict
|
||||
+
|
||||
+import mailcap
|
||||
+
|
||||
+
|
||||
+# Location of mailcap file
|
||||
+MAILCAPFILE = test.support.findfile("mailcap.txt")
|
||||
+
|
||||
+# Dict to act as mock mailcap entry for this test
|
||||
+# The keys and values should match the contents of MAILCAPFILE
|
||||
+
|
||||
+MAILCAPDICT = {
|
||||
+ 'application/x-movie':
|
||||
+ [{'compose': 'moviemaker %s',
|
||||
+ 'x11-bitmap': '"/usr/lib/Zmail/bitmaps/movie.xbm"',
|
||||
+ 'description': '"Movie"',
|
||||
+ 'view': 'movieplayer %s',
|
||||
+ 'lineno': 4}],
|
||||
+ 'application/*':
|
||||
+ [{'copiousoutput': '',
|
||||
+ 'view': 'echo "This is \\"%t\\" but is 50 \\% Greek to me" \\; cat %s',
|
||||
+ 'lineno': 5}],
|
||||
+ 'audio/basic':
|
||||
+ [{'edit': 'audiocompose %s',
|
||||
+ 'compose': 'audiocompose %s',
|
||||
+ 'description': '"An audio fragment"',
|
||||
+ 'view': 'showaudio %s',
|
||||
+ 'lineno': 6}],
|
||||
+ 'video/mpeg':
|
||||
+ [{'view': 'mpeg_play %s', 'lineno': 13}],
|
||||
+ 'application/postscript':
|
||||
+ [{'needsterminal': '', 'view': 'ps-to-terminal %s', 'lineno': 1},
|
||||
+ {'compose': 'idraw %s', 'view': 'ps-to-terminal %s', 'lineno': 2}],
|
||||
+ 'application/x-dvi':
|
||||
+ [{'view': 'xdvi %s', 'lineno': 3}],
|
||||
+ 'message/external-body':
|
||||
+ [{'composetyped': 'extcompose %s',
|
||||
+ 'description': '"A reference to data stored in an external location"',
|
||||
+ 'needsterminal': '',
|
||||
+ 'view': 'showexternal %s %{access-type} %{name} %{site} %{directory} %{mode} %{server}',
|
||||
+ 'lineno': 10}],
|
||||
+ 'text/richtext':
|
||||
+ [{'test': 'test "`echo %{charset} | tr \'[A-Z]\' \'[a-z]\'`" = iso-8859-8',
|
||||
+ 'copiousoutput': '',
|
||||
+ 'view': 'shownonascii iso-8859-8 -e richtext -p %s',
|
||||
+ 'lineno': 11}],
|
||||
+ 'image/x-xwindowdump':
|
||||
+ [{'view': 'display %s', 'lineno': 9}],
|
||||
+ 'audio/*':
|
||||
+ [{'view': '/usr/local/bin/showaudio %t', 'lineno': 7}],
|
||||
+ 'video/*':
|
||||
+ [{'view': 'animate %s', 'lineno': 12}],
|
||||
+ 'application/frame':
|
||||
+ [{'print': '"cat %s | lp"', 'view': 'showframe %s', 'lineno': 0}],
|
||||
+ 'image/rgb':
|
||||
+ [{'view': 'display %s', 'lineno': 8}]
|
||||
+}
|
||||
+
|
||||
+# In Python 2, mailcap doesn't return line numbers.
|
||||
+# This test suite is copied from Python 3.11; for easier backporting we keep
|
||||
+# data from there and remove the lineno.
|
||||
+# So, for Python 2, MAILCAPDICT_DEPRECATED is the same as MAILCAPDICT
|
||||
+MAILCAPDICT_DEPRECATED = MAILCAPDICT
|
||||
+for entry_list in MAILCAPDICT_DEPRECATED.values():
|
||||
+ for entry in entry_list:
|
||||
+ entry.pop('lineno')
|
||||
+
|
||||
+
|
||||
+class HelperFunctionTest(unittest.TestCase):
|
||||
+
|
||||
+ def test_listmailcapfiles(self):
|
||||
+ # The return value for listmailcapfiles() will vary by system.
|
||||
+ # So verify that listmailcapfiles() returns a list of strings that is of
|
||||
+ # non-zero length.
|
||||
+ mcfiles = mailcap.listmailcapfiles()
|
||||
+ self.assertIsInstance(mcfiles, list)
|
||||
+ for m in mcfiles:
|
||||
+ self.assertIsInstance(m, str)
|
||||
+ with os_helper.EnvironmentVarGuard() as env:
|
||||
+ # According to RFC 1524, if MAILCAPS env variable exists, use that
|
||||
+ # and only that.
|
||||
+ if "MAILCAPS" in env:
|
||||
+ env_mailcaps = env["MAILCAPS"].split(os.pathsep)
|
||||
+ else:
|
||||
+ env_mailcaps = ["/testdir1/.mailcap", "/testdir2/mailcap"]
|
||||
+ env["MAILCAPS"] = os.pathsep.join(env_mailcaps)
|
||||
+ mcfiles = mailcap.listmailcapfiles()
|
||||
+ self.assertEqual(env_mailcaps, mcfiles)
|
||||
+
|
||||
+ def test_readmailcapfile(self):
|
||||
+ # Test readmailcapfile() using test file. It should match MAILCAPDICT.
|
||||
+ with open(MAILCAPFILE, 'r') as mcf:
|
||||
+ d = mailcap.readmailcapfile(mcf)
|
||||
+ self.assertDictEqual(d, MAILCAPDICT_DEPRECATED)
|
||||
+
|
||||
+ def test_lookup(self):
|
||||
+ # Test without key
|
||||
+
|
||||
+ # In Python 2, 'video/mpeg' is tried before 'video/*'
|
||||
+ # (unfixed bug: https://github.com/python/cpython/issues/59182 )
|
||||
+ # So, these are in reverse order:
|
||||
+ expected = [{'view': 'mpeg_play %s', },
|
||||
+ {'view': 'animate %s', }]
|
||||
+ actual = mailcap.lookup(MAILCAPDICT, 'video/mpeg')
|
||||
+ self.assertListEqual(expected, actual)
|
||||
+
|
||||
+ # Test with key
|
||||
+ key = 'compose'
|
||||
+ expected = [{'edit': 'audiocompose %s',
|
||||
+ 'compose': 'audiocompose %s',
|
||||
+ 'description': '"An audio fragment"',
|
||||
+ 'view': 'showaudio %s',
|
||||
+ }]
|
||||
+ actual = mailcap.lookup(MAILCAPDICT, 'audio/basic', key)
|
||||
+ self.assertListEqual(expected, actual)
|
||||
+
|
||||
+ # Test on user-defined dicts without line numbers
|
||||
+ expected = [{'view': 'mpeg_play %s'}, {'view': 'animate %s'}]
|
||||
+ actual = mailcap.lookup(MAILCAPDICT_DEPRECATED, 'video/mpeg')
|
||||
+ self.assertListEqual(expected, actual)
|
||||
+
|
||||
+ def test_subst(self):
|
||||
+ plist = ['id=1', 'number=2', 'total=3']
|
||||
+ # test case: ([field, MIMEtype, filename, plist=[]], <expected string>)
|
||||
+ test_cases = [
|
||||
+ (["", "audio/*", "foo.txt"], ""),
|
||||
+ (["echo foo", "audio/*", "foo.txt"], "echo foo"),
|
||||
+ (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"),
|
||||
+ (["echo %t", "audio/*", "foo.txt"], None),
|
||||
+ (["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"),
|
||||
+ (["echo \\%t", "audio/*", "foo.txt"], "echo %t"),
|
||||
+ (["echo foo", "audio/*", "foo.txt", plist], "echo foo"),
|
||||
+ (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3")
|
||||
+ ]
|
||||
+ for tc in test_cases:
|
||||
+ self.assertEqual(mailcap.subst(*tc[0]), tc[1])
|
||||
+
|
||||
+class GetcapsTest(unittest.TestCase):
|
||||
+
|
||||
+ def test_mock_getcaps(self):
|
||||
+ # Test mailcap.getcaps() using mock mailcap file in this dir.
|
||||
+ # Temporarily override any existing system mailcap file by pointing the
|
||||
+ # MAILCAPS environment variable to our mock file.
|
||||
+ with os_helper.EnvironmentVarGuard() as env:
|
||||
+ env["MAILCAPS"] = MAILCAPFILE
|
||||
+ caps = mailcap.getcaps()
|
||||
+ self.assertDictEqual(caps, MAILCAPDICT)
|
||||
+
|
||||
+ def test_system_mailcap(self):
|
||||
+ # Test mailcap.getcaps() with mailcap file(s) on system, if any.
|
||||
+ caps = mailcap.getcaps()
|
||||
+ self.assertIsInstance(caps, dict)
|
||||
+ mailcapfiles = mailcap.listmailcapfiles()
|
||||
+ existingmcfiles = [mcf for mcf in mailcapfiles if os.path.exists(mcf)]
|
||||
+ if existingmcfiles:
|
||||
+ # At least 1 mailcap file exists, so test that.
|
||||
+ for (k, v) in caps.items():
|
||||
+ self.assertIsInstance(k, str)
|
||||
+ self.assertIsInstance(v, list)
|
||||
+ for e in v:
|
||||
+ self.assertIsInstance(e, dict)
|
||||
+ else:
|
||||
+ # No mailcap files on system. getcaps() should return empty dict.
|
||||
+ self.assertEqual({}, caps)
|
||||
+
|
||||
+
|
||||
+class FindmatchTest(unittest.TestCase):
|
||||
+
|
||||
+ def test_findmatch(self):
|
||||
+
|
||||
+ # default findmatch arguments
|
||||
+ c = MAILCAPDICT
|
||||
+ fname = "foo.txt"
|
||||
+ plist = ["access-type=default", "name=john", "site=python.org",
|
||||
+ "directory=/tmp", "mode=foo", "server=bar"]
|
||||
+ audio_basic_entry = {
|
||||
+ 'edit': 'audiocompose %s',
|
||||
+ 'compose': 'audiocompose %s',
|
||||
+ 'description': '"An audio fragment"',
|
||||
+ 'view': 'showaudio %s',
|
||||
+ }
|
||||
+ audio_entry = {"view": "/usr/local/bin/showaudio %t", }
|
||||
+ video_entry = {'view': 'animate %s', }
|
||||
+ mpeg_entry = {'view': 'mpeg_play %s', }
|
||||
+ message_entry = {
|
||||
+ 'composetyped': 'extcompose %s',
|
||||
+ 'description': '"A reference to data stored in an external location"', 'needsterminal': '',
|
||||
+ 'view': 'showexternal %s %{access-type} %{name} %{site} %{directory} %{mode} %{server}',
|
||||
+ }
|
||||
+
|
||||
+ # test case: (findmatch args, findmatch keyword args, expected output)
|
||||
+ # positional args: caps, MIMEtype
|
||||
+ # keyword args: key="view", filename="/dev/null", plist=[]
|
||||
+ # output: (command line, mailcap entry)
|
||||
+ cases = [
|
||||
+ ([{}, "video/mpeg"], {}, (None, None)),
|
||||
+ ([c, "foo/bar"], {}, (None, None)),
|
||||
+
|
||||
+ # In Python 2, 'video/mpeg' is tried before 'video/*'
|
||||
+ # (unfixed bug: https://github.com/python/cpython/issues/59182 )
|
||||
+ #([c, "video/mpeg"], {}, ('animate /dev/null', video_entry)),
|
||||
+ ([c, "video/mpeg"], {}, ('mpeg_play /dev/null', mpeg_entry)),
|
||||
+
|
||||
+ ([c, "audio/basic", "edit"], {}, ("audiocompose /dev/null", audio_basic_entry)),
|
||||
+ ([c, "audio/basic", "compose"], {}, ("audiocompose /dev/null", audio_basic_entry)),
|
||||
+ ([c, "audio/basic", "description"], {}, ('"An audio fragment"', audio_basic_entry)),
|
||||
+ ([c, "audio/basic", "foobar"], {}, (None, None)),
|
||||
+ ([c, "video/*"], {"filename": fname}, ("animate %s" % fname, video_entry)),
|
||||
+ ([c, "audio/basic", "compose"],
|
||||
+ {"filename": fname},
|
||||
+ ("audiocompose %s" % fname, audio_basic_entry)),
|
||||
+ ([c, "audio/basic"],
|
||||
+ {"key": "description", "filename": fname},
|
||||
+ ('"An audio fragment"', audio_basic_entry)),
|
||||
+ ([c, "audio/*"],
|
||||
+ {"filename": fname},
|
||||
+ (None, None)),
|
||||
+ ([c, "audio/wav"],
|
||||
+ {"filename": fname},
|
||||
+ ("/usr/local/bin/showaudio audio/wav", audio_entry)),
|
||||
+ ([c, "message/external-body"],
|
||||
+ {"plist": plist},
|
||||
+ ("showexternal /dev/null default john python.org /tmp foo bar", message_entry))
|
||||
+ ]
|
||||
+ self._run_cases(cases)
|
||||
+
|
||||
+ @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system")
|
||||
+ @unittest.skipIf(sys.platform == "vxworks", "'test' command is not supported on VxWorks")
|
||||
+ def test_test(self):
|
||||
+ # findmatch() will automatically check any "test" conditions and skip
|
||||
+ # the entry if the check fails.
|
||||
+ caps = {"test/pass": [{"test": "test 1 -eq 1"}],
|
||||
+ "test/fail": [{"test": "test 1 -eq 0"}]}
|
||||
+ # test case: (findmatch args, findmatch keyword args, expected output)
|
||||
+ # positional args: caps, MIMEtype, key ("test")
|
||||
+ # keyword args: N/A
|
||||
+ # output: (command line, mailcap entry)
|
||||
+ cases = [
|
||||
+ # findmatch will return the mailcap entry for test/pass because it evaluates to true
|
||||
+ ([caps, "test/pass", "test"], {}, ("test 1 -eq 1", {"test": "test 1 -eq 1"})),
|
||||
+ # findmatch will return None because test/fail evaluates to false
|
||||
+ ([caps, "test/fail", "test"], {}, (None, None))
|
||||
+ ]
|
||||
+ self._run_cases(cases)
|
||||
+
|
||||
+ def _run_cases(self, cases):
|
||||
+ for c in cases:
|
||||
+ self.assertEqual(mailcap.findmatch(*c[0], **c[1]), c[2])
|
||||
+
|
||||
+
|
||||
+def test_main():
|
||||
+ test.support.run_unittest(HelperFunctionTest, GetcapsTest, FindmatchTest)
|
||||
diff --git a/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst b/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst
|
||||
new file mode 100644
|
||||
index 00000000000..da81a1f6993
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst
|
||||
@@ -0,0 +1,4 @@
|
||||
+The deprecated mailcap module now refuses to inject unsafe text (filenames,
|
||||
+MIME types, parameters) into shell commands. Instead of using such text, it
|
||||
+will warn and act as if a match was not found (or for test commands, as if
|
||||
+the test failed).
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Package Not Available
|
||||
This package is not available on CentOS Stream 10.
|
||||
It may be available on another branch.
|
@ -1,28 +0,0 @@
|
||||
diff -up Python-2.7.3/Lib/pydoc.py.no_gui Python-2.7.3/Lib/pydoc.py
|
||||
--- Python-2.7.3/Lib/pydoc.py.no_gui 2012-04-09 19:07:31.000000000 -0400
|
||||
+++ Python-2.7.3/Lib/pydoc.py 2013-02-19 13:48:44.480054515 -0500
|
||||
@@ -19,9 +19,6 @@ of all available modules.
|
||||
local machine to generate documentation web pages. Port number 0 can be
|
||||
used to get an arbitrary unused port.
|
||||
|
||||
-For platforms without a command line, "pydoc -g" starts the HTTP server
|
||||
-and also pops up a little window for controlling it.
|
||||
-
|
||||
Run "pydoc -w <name>" to write out the HTML documentation for a module
|
||||
to a file named "<name>.html".
|
||||
|
||||
@@ -2346,13 +2340,10 @@ def cli():
|
||||
Start an HTTP server on the given port on the local machine. Port
|
||||
number 0 can be used to get an arbitrary unused port.
|
||||
|
||||
-%s -g
|
||||
- Pop up a graphical interface for finding and serving documentation.
|
||||
-
|
||||
%s -w <name> ...
|
||||
Write out the HTML documentation for a module to a file in the current
|
||||
directory. If <name> contains a '%s', it is treated as a filename; if
|
||||
it names a directory, documentation is written for all the contents.
|
||||
-""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
|
||||
+""" % (cmd, os.sep, cmd, cmd, cmd, os.sep)
|
||||
|
||||
if __name__ == '__main__': cli()
|
@ -1,21 +0,0 @@
|
||||
diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
|
||||
index ab10ec5..923d1b7 100644
|
||||
--- a/Lib/ctypes/util.py
|
||||
+++ b/Lib/ctypes/util.py
|
||||
@@ -140,11 +140,15 @@ elif os.name == "posix":
|
||||
# assuming GNU binutils / ELF
|
||||
if not f:
|
||||
return None
|
||||
- cmd = 'if ! type objdump >/dev/null 2>&1; then exit; fi;' \
|
||||
+ cmd = 'if ! type objdump >/dev/null 2>&1; then exit 10; fi;' \
|
||||
'objdump -p -j .dynamic 2>/dev/null "$1"'
|
||||
proc = subprocess.Popen((cmd, '_get_soname', f), shell=True,
|
||||
stdout=subprocess.PIPE)
|
||||
[dump, _] = proc.communicate()
|
||||
+ if proc.returncode == 10:
|
||||
+ return os.path.basename(f) # This is good for GLibc, I think,
|
||||
+ # and a dep on binutils is big (for
|
||||
+ # live CDs).
|
||||
res = re.search(br'\sSONAME\s+([^\s]+)', dump)
|
||||
if not res:
|
||||
return None
|
@ -1,198 +0,0 @@
|
||||
diff -up Python-2.7rc1/configure.ac.systemtap Python-2.7rc1/configure.ac
|
||||
--- Python-2.7rc1/configure.ac.systemtap 2010-06-06 10:53:15.514975012 -0400
|
||||
+++ Python-2.7rc1/configure.ac 2010-06-06 10:53:15.520974361 -0400
|
||||
@@ -2616,6 +2616,38 @@ if test "$with_valgrind" != no; then
|
||||
)
|
||||
fi
|
||||
|
||||
+# Check for dtrace support
|
||||
+AC_MSG_CHECKING(for --with-dtrace)
|
||||
+AC_ARG_WITH(dtrace,
|
||||
+ AC_HELP_STRING(--with(out)-dtrace, disable/enable dtrace support))
|
||||
+
|
||||
+if test ! -z "$with_dtrace"
|
||||
+then
|
||||
+ if dtrace -G -o /dev/null -s $srcdir/Include/pydtrace.d 2>/dev/null
|
||||
+ then
|
||||
+ AC_DEFINE(WITH_DTRACE, 1,
|
||||
+ [Define if you want to compile in Dtrace support])
|
||||
+ with_dtrace="Sun"
|
||||
+ DTRACEOBJS="Python/dtrace.o"
|
||||
+ DTRADEHDRS=""
|
||||
+ elif dtrace -h -o /dev/null -s $srcdir/Include/pydtrace.d
|
||||
+ then
|
||||
+ AC_DEFINE(WITH_DTRACE, 1,
|
||||
+ [Define if you want to compile in Dtrace support])
|
||||
+ with_dtrace="Apple"
|
||||
+ DTRACEOBJS=""
|
||||
+ DTRADEHDRS="pydtrace.h"
|
||||
+ else
|
||||
+ with_dtrace="no"
|
||||
+ fi
|
||||
+else
|
||||
+ with_dtrace="no"
|
||||
+fi
|
||||
+
|
||||
+AC_MSG_RESULT($with_dtrace)
|
||||
+AC_SUBST(DTRACEOBJS)
|
||||
+AC_SUBST(DTRACEHDRS)
|
||||
+
|
||||
# Check for --with-wctype-functions
|
||||
AC_MSG_CHECKING(for --with-wctype-functions)
|
||||
AC_ARG_WITH(wctype-functions,
|
||||
diff -up Python-2.7rc1/Include/pydtrace.d.systemtap Python-2.7rc1/Include/pydtrace.d
|
||||
--- Python-2.7rc1/Include/pydtrace.d.systemtap 2010-06-06 10:53:15.520974361 -0400
|
||||
+++ Python-2.7rc1/Include/pydtrace.d 2010-06-06 10:53:15.520974361 -0400
|
||||
@@ -0,0 +1,10 @@
|
||||
+provider python {
|
||||
+ probe function__entry(const char *, const char *, int);
|
||||
+ probe function__return(const char *, const char *, int);
|
||||
+};
|
||||
+
|
||||
+#pragma D attributes Evolving/Evolving/Common provider python provider
|
||||
+#pragma D attributes Private/Private/Common provider python module
|
||||
+#pragma D attributes Private/Private/Common provider python function
|
||||
+#pragma D attributes Evolving/Evolving/Common provider python name
|
||||
+#pragma D attributes Evolving/Evolving/Common provider python args
|
||||
diff -up Python-2.7rc1/Makefile.pre.in.systemtap Python-2.7rc1/Makefile.pre.in
|
||||
--- Python-2.7rc1/Makefile.pre.in.systemtap 2010-06-06 10:53:15.488978775 -0400
|
||||
+++ Python-2.7rc1/Makefile.pre.in 2010-06-06 11:05:30.411100568 -0400
|
||||
@@ -298,6 +298,7 @@ PYTHON_OBJS= \
|
||||
Python/formatter_unicode.o \
|
||||
Python/formatter_string.o \
|
||||
Python/$(DYNLOADFILE) \
|
||||
+ @DTRACEOBJS@ \
|
||||
$(LIBOBJS) \
|
||||
$(MACHDEP_OBJS) \
|
||||
$(THREADOBJ)
|
||||
@@ -599,6 +600,18 @@ Python/formatter_unicode.o: $(srcdir)/Py
|
||||
Python/formatter_string.o: $(srcdir)/Python/formatter_string.c \
|
||||
$(STRINGLIB_HEADERS)
|
||||
|
||||
+# Only needed with --with-dtrace
|
||||
+buildinclude:
|
||||
+ mkdir -p Include
|
||||
+
|
||||
+Include/pydtrace.h: buildinclude $(srcdir)/Include/pydtrace.d
|
||||
+ dtrace -o $@ $(DFLAGS) -C -h -s $(srcdir)/Include/pydtrace.d
|
||||
+
|
||||
+Python/ceval.o: Include/pydtrace.h
|
||||
+
|
||||
+Python/dtrace.o: buildinclude $(srcdir)/Include/pydtrace.d Python/ceval.o
|
||||
+ dtrace -o $@ $(DFLAGS) -C -G -s $(srcdir)/Include/pydtrace.d Python/ceval.o
|
||||
+
|
||||
############################################################################
|
||||
# Header files
|
||||
|
||||
@@ -1251,7 +1264,7 @@ Python/thread.o: @THREADHEADERS@
|
||||
.PHONY: frameworkinstall frameworkinstallframework frameworkinstallstructure
|
||||
.PHONY: frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools
|
||||
.PHONY: frameworkaltinstallunixtools recheck clean clobber distclean
|
||||
-.PHONY: smelly funny patchcheck altmaninstall commoninstall
|
||||
+.PHONY: smelly funny patchcheck altmaninstall commoninstall buildinclude
|
||||
.PHONY: gdbhooks
|
||||
|
||||
# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
|
||||
diff -up Python-2.7rc1/pyconfig.h.in.systemtap Python-2.7rc1/pyconfig.h.in
|
||||
--- Python-2.7rc1/pyconfig.h.in.systemtap 2010-05-08 07:04:18.000000000 -0400
|
||||
+++ Python-2.7rc1/pyconfig.h.in 2010-06-06 10:53:15.521974070 -0400
|
||||
@@ -1074,6 +1074,9 @@
|
||||
/* Define if you want documentation strings in extension modules */
|
||||
#undef WITH_DOC_STRINGS
|
||||
|
||||
+/* Define if you want to compile in Dtrace support */
|
||||
+#undef WITH_DTRACE
|
||||
+
|
||||
/* Define if you want to use the new-style (Openstep, Rhapsody, MacOS) dynamic
|
||||
linker (dyld) instead of the old-style (NextStep) dynamic linker (rld).
|
||||
Dyld is necessary to support frameworks. */
|
||||
diff -up Python-2.7rc1/Python/ceval.c.systemtap Python-2.7rc1/Python/ceval.c
|
||||
--- Python-2.7rc1/Python/ceval.c.systemtap 2010-05-09 10:46:46.000000000 -0400
|
||||
+++ Python-2.7rc1/Python/ceval.c 2010-06-06 11:08:40.683100500 -0400
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
+#ifdef WITH_DTRACE
|
||||
+#include "pydtrace.h"
|
||||
+#endif
|
||||
+
|
||||
#ifndef WITH_TSC
|
||||
|
||||
#define READ_TIMESTAMP(var)
|
||||
@@ -671,6 +675,55 @@ PyEval_EvalCode(PyCodeObject *co, PyObje
|
||||
NULL);
|
||||
}
|
||||
|
||||
+#ifdef WITH_DTRACE
|
||||
+static void
|
||||
+dtrace_entry(PyFrameObject *f)
|
||||
+{
|
||||
+ const char *filename;
|
||||
+ const char *fname;
|
||||
+ int lineno;
|
||||
+
|
||||
+ filename = PyString_AsString(f->f_code->co_filename);
|
||||
+ fname = PyString_AsString(f->f_code->co_name);
|
||||
+ lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
|
||||
+
|
||||
+ PYTHON_FUNCTION_ENTRY((char *)filename, (char *)fname, lineno);
|
||||
+
|
||||
+ /*
|
||||
+ * Currently a USDT tail-call will not receive the correct arguments.
|
||||
+ * Disable the tail call here.
|
||||
+ */
|
||||
+#if defined(__sparc)
|
||||
+ asm("nop");
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+dtrace_return(PyFrameObject *f)
|
||||
+{
|
||||
+ const char *filename;
|
||||
+ const char *fname;
|
||||
+ int lineno;
|
||||
+
|
||||
+ filename = PyString_AsString(f->f_code->co_filename);
|
||||
+ fname = PyString_AsString(f->f_code->co_name);
|
||||
+ lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
|
||||
+ PYTHON_FUNCTION_RETURN((char *)filename, (char *)fname, lineno);
|
||||
+
|
||||
+ /*
|
||||
+ * Currently a USDT tail-call will not receive the correct arguments.
|
||||
+ * Disable the tail call here.
|
||||
+ */
|
||||
+#if defined(__sparc)
|
||||
+ asm("nop");
|
||||
+#endif
|
||||
+}
|
||||
+#else
|
||||
+#define PYTHON_FUNCTION_ENTRY_ENABLED() 0
|
||||
+#define PYTHON_FUNCTION_RETURN_ENABLED() 0
|
||||
+#define dtrace_entry(f)
|
||||
+#define dtrace_return(f)
|
||||
+#endif
|
||||
|
||||
/* Interpreter main loop */
|
||||
|
||||
@@ -909,6 +962,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int
|
||||
}
|
||||
}
|
||||
|
||||
+ if (PYTHON_FUNCTION_ENTRY_ENABLED())
|
||||
+ dtrace_entry(f);
|
||||
+
|
||||
co = f->f_code;
|
||||
names = co->co_names;
|
||||
consts = co->co_consts;
|
||||
@@ -3000,6 +3056,9 @@ fast_yield:
|
||||
|
||||
/* pop frame */
|
||||
exit_eval_frame:
|
||||
+ if (PYTHON_FUNCTION_RETURN_ENABLED())
|
||||
+ dtrace_return(f);
|
||||
+
|
||||
Py_LeaveRecursiveCall();
|
||||
tstate->frame = f->f_back;
|
||||
|
@ -1,193 +0,0 @@
|
||||
diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py
|
||||
index b9f1c6c..7b23714 100644
|
||||
--- a/Lib/distutils/command/install.py
|
||||
+++ b/Lib/distutils/command/install.py
|
||||
@@ -42,14 +42,14 @@ else:
|
||||
INSTALL_SCHEMES = {
|
||||
'unix_prefix': {
|
||||
'purelib': '$base/lib/python$py_version_short/site-packages',
|
||||
- 'platlib': '$platbase/lib/python$py_version_short/site-packages',
|
||||
+ 'platlib': '$platbase/lib64/python$py_version_short/site-packages',
|
||||
'headers': '$base/include/python$py_version_short/$dist_name',
|
||||
'scripts': '$base/bin',
|
||||
'data' : '$base',
|
||||
},
|
||||
'unix_home': {
|
||||
'purelib': '$base/lib/python',
|
||||
- 'platlib': '$base/lib/python',
|
||||
+ 'platlib': '$base/lib64/python',
|
||||
'headers': '$base/include/python/$dist_name',
|
||||
'scripts': '$base/bin',
|
||||
'data' : '$base',
|
||||
diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py
|
||||
index 031f809..ec5d584 100644
|
||||
--- a/Lib/distutils/sysconfig.py
|
||||
+++ b/Lib/distutils/sysconfig.py
|
||||
@@ -120,8 +120,12 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
|
||||
prefix = plat_specific and EXEC_PREFIX or PREFIX
|
||||
|
||||
if os.name == "posix":
|
||||
+ if plat_specific or standard_lib:
|
||||
+ lib = "lib64"
|
||||
+ else:
|
||||
+ lib = "lib"
|
||||
libpython = os.path.join(prefix,
|
||||
- "lib", "python" + get_python_version())
|
||||
+ lib, "python" + get_python_version())
|
||||
if standard_lib:
|
||||
return libpython
|
||||
else:
|
||||
diff --git a/Lib/site.py b/Lib/site.py
|
||||
index c360802..868b7cb 100644
|
||||
--- a/Lib/site.py
|
||||
+++ b/Lib/site.py
|
||||
@@ -288,12 +288,16 @@ def getsitepackages():
|
||||
if sys.platform in ('os2emx', 'riscos'):
|
||||
sitepackages.append(os.path.join(prefix, "Lib", "site-packages"))
|
||||
elif os.sep == '/':
|
||||
+ sitepackages.append(os.path.join(prefix, "lib64",
|
||||
+ "python" + sys.version[:3],
|
||||
+ "site-packages"))
|
||||
sitepackages.append(os.path.join(prefix, "lib",
|
||||
"python" + sys.version[:3],
|
||||
"site-packages"))
|
||||
sitepackages.append(os.path.join(prefix, "lib", "site-python"))
|
||||
else:
|
||||
sitepackages.append(prefix)
|
||||
+ sitepackages.append(os.path.join(prefix, "lib64", "site-packages"))
|
||||
sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
|
||||
return sitepackages
|
||||
|
||||
diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py
|
||||
index b4384ee..349f688 100644
|
||||
--- a/Lib/test/test_site.py
|
||||
+++ b/Lib/test/test_site.py
|
||||
@@ -254,17 +254,20 @@ class HelperFunctionsTests(unittest.TestCase):
|
||||
self.assertEqual(dirs[0], wanted)
|
||||
elif os.sep == '/':
|
||||
# OS X, Linux, FreeBSD, etc
|
||||
- self.assertEqual(len(dirs), 2)
|
||||
- wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3],
|
||||
+ self.assertEqual(len(dirs), 3)
|
||||
+ wanted = os.path.join('xoxo', 'lib64', 'python' + sys.version[:3],
|
||||
'site-packages')
|
||||
self.assertEqual(dirs[0], wanted)
|
||||
- wanted = os.path.join('xoxo', 'lib', 'site-python')
|
||||
+ wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3],
|
||||
+ 'site-packages')
|
||||
self.assertEqual(dirs[1], wanted)
|
||||
+ wanted = os.path.join('xoxo', 'lib', 'site-python')
|
||||
+ self.assertEqual(dirs[2], wanted)
|
||||
else:
|
||||
# other platforms
|
||||
self.assertEqual(len(dirs), 2)
|
||||
self.assertEqual(dirs[0], 'xoxo')
|
||||
- wanted = os.path.join('xoxo', 'lib', 'site-packages')
|
||||
+ wanted = os.path.join('xoxo', 'lib64', 'site-packages')
|
||||
self.assertEqual(dirs[1], wanted)
|
||||
|
||||
def test_no_home_directory(self):
|
||||
diff --git a/Makefile.pre.in b/Makefile.pre.in
|
||||
index 4f59dd3..877698c 100644
|
||||
--- a/Makefile.pre.in
|
||||
+++ b/Makefile.pre.in
|
||||
@@ -110,7 +110,7 @@ LIBDIR= @libdir@
|
||||
MANDIR= @mandir@
|
||||
INCLUDEDIR= @includedir@
|
||||
CONFINCLUDEDIR= $(exec_prefix)/include
|
||||
-SCRIPTDIR= $(prefix)/lib
|
||||
+SCRIPTDIR= $(prefix)/lib64
|
||||
|
||||
# Detailed destination directories
|
||||
BINLIBDEST= $(LIBDIR)/python$(VERSION)
|
||||
diff --git a/Modules/Setup.dist b/Modules/Setup.dist
|
||||
index 2cf35a9..c4c88cb 100644
|
||||
--- a/Modules/Setup.dist
|
||||
+++ b/Modules/Setup.dist
|
||||
@@ -231,7 +231,7 @@ crypt cryptmodule.c # -lcrypt # crypt(3); needs -lcrypt on some systems
|
||||
# Some more UNIX dependent modules -- off by default, since these
|
||||
# are not supported by all UNIX systems:
|
||||
|
||||
-nis nismodule.c -lnsl -ltirpc -I/usr/include/tirpc -I/usr/include/nsl -L/usr/lib/nsl
|
||||
+nis nismodule.c -lnsl -ltirpc -I/usr/include/tirpc -I/usr/include/nsl -L/usr/lib64/nsl
|
||||
termios termios.c # Steen Lumholt's termios module
|
||||
resource resource.c # Jeremy Hylton's rlimit interface
|
||||
|
||||
@@ -416,7 +416,7 @@ gdbm gdbmmodule.c -lgdbm
|
||||
# Edit the variables DB and DBLIBVERto point to the db top directory
|
||||
# and the subdirectory of PORT where you built it.
|
||||
DBINC=/usr/include/libdb
|
||||
-DBLIB=/usr/lib
|
||||
+DBLIB=/usr/lib64
|
||||
_bsddb _bsddb.c -I$(DBINC) -L$(DBLIB) -ldb
|
||||
|
||||
# Historical Berkeley DB 1.85
|
||||
@@ -462,7 +462,7 @@ cPickle cPickle.c
|
||||
# Andrew Kuchling's zlib module.
|
||||
# This require zlib 1.1.3 (or later).
|
||||
# See http://www.gzip.org/zlib/
|
||||
-zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib -lz
|
||||
+zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib64 -lz
|
||||
|
||||
# Interface to the Expat XML parser
|
||||
# More information on Expat can be found at www.libexpat.org.
|
||||
diff --git a/Modules/getpath.c b/Modules/getpath.c
|
||||
index fd33a01..c5c86fd 100644
|
||||
--- a/Modules/getpath.c
|
||||
+++ b/Modules/getpath.c
|
||||
@@ -108,7 +108,7 @@ static char prefix[MAXPATHLEN+1];
|
||||
static char exec_prefix[MAXPATHLEN+1];
|
||||
static char progpath[MAXPATHLEN+1];
|
||||
static char *module_search_path = NULL;
|
||||
-static char lib_python[] = "lib/python" VERSION;
|
||||
+static char lib_python[] = "lib64/python" VERSION;
|
||||
|
||||
static void
|
||||
reduce(char *dir)
|
||||
@@ -548,7 +548,7 @@ calculate_path(void)
|
||||
fprintf(stderr,
|
||||
"Could not find platform dependent libraries <exec_prefix>\n");
|
||||
strncpy(exec_prefix, EXEC_PREFIX, MAXPATHLEN);
|
||||
- joinpath(exec_prefix, "lib/lib-dynload");
|
||||
+ joinpath(exec_prefix, "lib64/lib-dynload");
|
||||
}
|
||||
/* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */
|
||||
|
||||
diff --git a/setup.py b/setup.py
|
||||
index 0288a6b..7905f6f 100644
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -456,7 +456,7 @@ class PyBuildExt(build_ext):
|
||||
def detect_modules(self):
|
||||
# Ensure that /usr/local is always used
|
||||
if not cross_compiling:
|
||||
- add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib')
|
||||
+ add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib64')
|
||||
add_dir_to_list(self.compiler.include_dirs, '/usr/local/include')
|
||||
if cross_compiling:
|
||||
self.add_gcc_paths()
|
||||
@@ -782,11 +782,11 @@ class PyBuildExt(build_ext):
|
||||
elif curses_library:
|
||||
readline_libs.append(curses_library)
|
||||
elif self.compiler.find_library_file(lib_dirs +
|
||||
- ['/usr/lib/termcap'],
|
||||
+ ['/usr/lib64/termcap'],
|
||||
'termcap'):
|
||||
readline_libs.append('termcap')
|
||||
exts.append( Extension('readline', ['readline.c'],
|
||||
- library_dirs=['/usr/lib/termcap'],
|
||||
+ library_dirs=['/usr/lib64/termcap'],
|
||||
extra_link_args=readline_extra_link_args,
|
||||
libraries=readline_libs) )
|
||||
else:
|
||||
@@ -821,8 +821,8 @@ class PyBuildExt(build_ext):
|
||||
if krb5_h:
|
||||
ssl_incs += krb5_h
|
||||
ssl_libs = find_library_file(self.compiler, 'ssl',lib_dirs,
|
||||
- ['/usr/local/ssl/lib',
|
||||
- '/usr/contrib/ssl/lib/'
|
||||
+ ['/usr/local/ssl/lib64',
|
||||
+ '/usr/contrib/ssl/lib64/'
|
||||
] )
|
||||
|
||||
if (ssl_incs is not None and
|
@ -1,13 +0,0 @@
|
||||
--- Python-2.7.2/Lib/distutils/tests/test_install.py.lib64 2011-09-08 17:51:57.851405376 -0400
|
||||
+++ Python-2.7.2/Lib/distutils/tests/test_install.py 2011-09-08 18:40:46.754205096 -0400
|
||||
@@ -41,8 +41,9 @@ class InstallTestCase(support.TempdirMan
|
||||
self.assertEqual(got, expected)
|
||||
|
||||
libdir = os.path.join(destination, "lib", "python")
|
||||
+ platlibdir = os.path.join(destination, "lib64", "python")
|
||||
check_path(cmd.install_lib, libdir)
|
||||
- check_path(cmd.install_platlib, libdir)
|
||||
+ check_path(cmd.install_platlib, platlibdir)
|
||||
check_path(cmd.install_purelib, libdir)
|
||||
check_path(cmd.install_headers,
|
||||
os.path.join(destination, "include", "python", "foopkg"))
|
@ -1,50 +0,0 @@
|
||||
diff -up Python-2.7.6/Makefile.pre.in.no-static-lib Python-2.7.6/Makefile.pre.in
|
||||
--- Python-2.7.6/Makefile.pre.in.no-static-lib 2014-01-29 13:58:32.933226720 +0100
|
||||
+++ Python-2.7.6/Makefile.pre.in 2014-01-29 14:10:25.002247272 +0100
|
||||
@@ -437,7 +437,7 @@ coverage:
|
||||
|
||||
|
||||
# Build the interpreter
|
||||
-$(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY)
|
||||
+$(BUILDPYTHON): Modules/python.o $(LDLIBRARY)
|
||||
$(LINKCC) $(CFLAGS) $(LDFLAGS) $(LINKFORSHARED) -o $@ \
|
||||
Modules/python.o \
|
||||
$(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
|
||||
@@ -464,18 +464,6 @@ sharedmods: $(BUILDPYTHON) pybuilddir.tx
|
||||
_TCLTK_INCLUDES='$(TCLTK_INCLUDES)' _TCLTK_LIBS='$(TCLTK_LIBS)' \
|
||||
$(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build
|
||||
|
||||
-# Build static library
|
||||
-# avoid long command lines, same as LIBRARY_OBJS
|
||||
-$(LIBRARY): $(LIBRARY_OBJS)
|
||||
- -rm -f $@
|
||||
- $(AR) $(ARFLAGS) $@ Modules/getbuildinfo.o
|
||||
- $(AR) $(ARFLAGS) $@ $(PARSER_OBJS)
|
||||
- $(AR) $(ARFLAGS) $@ $(OBJECT_OBJS)
|
||||
- $(AR) $(ARFLAGS) $@ $(PYTHON_OBJS)
|
||||
- $(AR) $(ARFLAGS) $@ $(MODULE_OBJS) $(SIGNAL_OBJS)
|
||||
- $(AR) $(ARFLAGS) $@ $(MODOBJS)
|
||||
- $(RANLIB) $@
|
||||
-
|
||||
libpython$(VERSION).so: $(LIBRARY_OBJS)
|
||||
if test $(INSTSONAME) != $(LDLIBRARY); then \
|
||||
$(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \
|
||||
@@ -1097,18 +1085,6 @@ libainstall: all python-config
|
||||
else true; \
|
||||
fi; \
|
||||
done
|
||||
- @if test -d $(LIBRARY); then :; else \
|
||||
- if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \
|
||||
- if test "$(SO)" = .dll; then \
|
||||
- $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \
|
||||
- else \
|
||||
- $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \
|
||||
- $(RANLIB) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \
|
||||
- fi; \
|
||||
- else \
|
||||
- echo Skip install of $(LIBRARY) - use make frameworkinstall; \
|
||||
- fi; \
|
||||
- fi
|
||||
$(INSTALL_DATA) Modules/config.c $(DESTDIR)$(LIBPL)/config.c
|
||||
$(INSTALL_DATA) Modules/python.o $(DESTDIR)$(LIBPL)/python.o
|
||||
$(INSTALL_DATA) $(srcdir)/Modules/config.c.in $(DESTDIR)$(LIBPL)/config.c.in
|
@ -1,324 +0,0 @@
|
||||
From 898f93aa206e577dfe854c59bc62d0cea09cd5ed Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Orsava <torsava@redhat.com>
|
||||
Date: Tue, 10 Jan 2017 16:19:50 +0100
|
||||
Subject: [PATCH] Patch to support building both optimized vs debug stacks DSO
|
||||
ABIs,
|
||||
|
||||
sharing the same .py and .pyc files, using "_d.so" to signify a debug build of
|
||||
an extension module.
|
||||
---
|
||||
Lib/distutils/command/build_ext.py | 7 ++++-
|
||||
Lib/distutils/sysconfig.py | 5 ++--
|
||||
Lib/distutils/tests/test_install.py | 3 +-
|
||||
Makefile.pre.in | 56 ++++++++++++++++++++-----------------
|
||||
Misc/python-config.in | 2 +-
|
||||
Modules/makesetup | 2 +-
|
||||
Python/dynload_shlib.c | 11 ++++++--
|
||||
Python/sysmodule.c | 6 ++++
|
||||
configure.ac | 14 ++++++++--
|
||||
9 files changed, 69 insertions(+), 37 deletions(-)
|
||||
|
||||
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
|
||||
index 2c68be3..029d144 100644
|
||||
--- a/Lib/distutils/command/build_ext.py
|
||||
+++ b/Lib/distutils/command/build_ext.py
|
||||
@@ -677,7 +677,10 @@ class build_ext (Command):
|
||||
so_ext = get_config_var('SO')
|
||||
if os.name == 'nt' and self.debug:
|
||||
return os.path.join(*ext_path) + '_d' + so_ext
|
||||
- return os.path.join(*ext_path) + so_ext
|
||||
+
|
||||
+ # Similarly, extensions in debug mode are named 'module_d.so', to
|
||||
+ # avoid adding the _d to the SO config variable:
|
||||
+ return os.path.join(*ext_path) + (sys.pydebug and "_d" or "") + so_ext
|
||||
|
||||
def get_export_symbols (self, ext):
|
||||
"""Return the list of symbols that a shared extension has to
|
||||
@@ -762,6 +765,8 @@ class build_ext (Command):
|
||||
template = "python%d.%d"
|
||||
pythonlib = (template %
|
||||
(sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
|
||||
+ if sys.pydebug:
|
||||
+ pythonlib += '_d'
|
||||
return ext.libraries + [pythonlib]
|
||||
else:
|
||||
return ext.libraries
|
||||
diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py
|
||||
index 3e7f077..ec5d584 100644
|
||||
--- a/Lib/distutils/sysconfig.py
|
||||
+++ b/Lib/distutils/sysconfig.py
|
||||
@@ -90,7 +90,8 @@ def get_python_inc(plat_specific=0, prefix=None):
|
||||
# Include is located in the srcdir
|
||||
inc_dir = os.path.join(srcdir, "Include")
|
||||
return inc_dir
|
||||
- return os.path.join(prefix, "include", "python" + get_python_version())
|
||||
+ return os.path.join(prefix, "include",
|
||||
+ "python" + get_python_version() + (sys.pydebug and '-debug' or ''))
|
||||
elif os.name == "nt":
|
||||
return os.path.join(prefix, "include")
|
||||
elif os.name == "os2":
|
||||
@@ -248,7 +249,7 @@ def get_makefile_filename():
|
||||
if python_build:
|
||||
return os.path.join(project_base, "Makefile")
|
||||
lib_dir = get_python_lib(plat_specific=1, standard_lib=1)
|
||||
- return os.path.join(lib_dir, "config", "Makefile")
|
||||
+ return os.path.join(lib_dir, "config" + (sys.pydebug and "-debug" or ""), "Makefile")
|
||||
|
||||
|
||||
def parse_config_h(fp, g=None):
|
||||
diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py
|
||||
index 78fac46..d1d0931 100644
|
||||
--- a/Lib/distutils/tests/test_install.py
|
||||
+++ b/Lib/distutils/tests/test_install.py
|
||||
@@ -20,8 +20,9 @@ from distutils.tests import support
|
||||
|
||||
|
||||
def _make_ext_name(modname):
|
||||
- if os.name == 'nt' and sys.executable.endswith('_d.exe'):
|
||||
+ if sys.pydebug:
|
||||
modname += '_d'
|
||||
+
|
||||
return modname + sysconfig.get_config_var('SO')
|
||||
|
||||
|
||||
diff --git a/Makefile.pre.in b/Makefile.pre.in
|
||||
index 997a2fc..467e782 100644
|
||||
--- a/Makefile.pre.in
|
||||
+++ b/Makefile.pre.in
|
||||
@@ -116,8 +116,8 @@ SCRIPTDIR= $(prefix)/lib64
|
||||
# Detailed destination directories
|
||||
BINLIBDEST= $(LIBDIR)/python$(VERSION)
|
||||
LIBDEST= $(SCRIPTDIR)/python$(VERSION)
|
||||
-INCLUDEPY= $(INCLUDEDIR)/python$(VERSION)
|
||||
-CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(VERSION)
|
||||
+INCLUDEPY= $(INCLUDEDIR)/python$(VERSION)$(DEBUG_SUFFIX)
|
||||
+CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(VERSION)$(DEBUG_SUFFIX)
|
||||
LIBP= $(LIBDIR)/python$(VERSION)
|
||||
|
||||
# Symbols used for using shared libraries
|
||||
@@ -131,6 +131,12 @@ DESTSHARED= $(BINLIBDEST)/lib-dynload
|
||||
EXE= @EXEEXT@
|
||||
BUILDEXE= @BUILDEXEEXT@
|
||||
|
||||
+# DEBUG_EXT is used by ELF files (names and SONAMEs); it will be "_d" for a debug build
|
||||
+# DEBUG_SUFFIX is used by filesystem paths; it will be "-debug" for a debug build
|
||||
+# Both will be empty in an optimized build
|
||||
+DEBUG_EXT= @DEBUG_EXT@
|
||||
+DEBUG_SUFFIX= @DEBUG_SUFFIX@
|
||||
+
|
||||
# Short name and location for Mac OS X Python framework
|
||||
UNIVERSALSDK=@UNIVERSALSDK@
|
||||
PYTHONFRAMEWORK= @PYTHONFRAMEWORK@
|
||||
@@ -197,8 +203,8 @@ LIBOBJDIR= Python/
|
||||
LIBOBJS= @LIBOBJS@
|
||||
UNICODE_OBJS= @UNICODE_OBJS@
|
||||
|
||||
-PYTHON= python$(EXE)
|
||||
-BUILDPYTHON= python$(BUILDEXE)
|
||||
+PYTHON= python$(DEBUG_SUFFIX)$(EXE)
|
||||
+BUILDPYTHON= python$(DEBUG_SUFFIX)$(BUILDEXE)
|
||||
|
||||
PYTHON_FOR_REGEN=@PYTHON_FOR_REGEN@
|
||||
PYTHON_FOR_BUILD=@PYTHON_FOR_BUILD@
|
||||
@@ -547,7 +553,7 @@ sharedmods: $(BUILDPYTHON) pybuilddir.txt Modules/_math.o
|
||||
_TCLTK_INCLUDES='$(TCLTK_INCLUDES)' _TCLTK_LIBS='$(TCLTK_LIBS)' \
|
||||
$(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build
|
||||
|
||||
-libpython$(VERSION).so: $(LIBRARY_OBJS)
|
||||
+libpython$(VERSION)$(DEBUG_EXT).so: $(LIBRARY_OBJS)
|
||||
if test $(INSTSONAME) != $(LDLIBRARY); then \
|
||||
$(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \
|
||||
$(LN) -f $(INSTSONAME) $@; \
|
||||
@@ -954,18 +960,18 @@ bininstall: altbininstall
|
||||
then rm -f $(DESTDIR)$(BINDIR)/$(PYTHON); \
|
||||
else true; \
|
||||
fi
|
||||
- (cd $(DESTDIR)$(BINDIR); $(LN) -s python2$(EXE) $(PYTHON))
|
||||
- -rm -f $(DESTDIR)$(BINDIR)/python2$(EXE)
|
||||
- (cd $(DESTDIR)$(BINDIR); $(LN) -s python$(VERSION)$(EXE) python2$(EXE))
|
||||
- -rm -f $(DESTDIR)$(BINDIR)/python2-config
|
||||
- (cd $(DESTDIR)$(BINDIR); $(LN) -s python$(VERSION)-config python2-config)
|
||||
- -rm -f $(DESTDIR)$(BINDIR)/python-config
|
||||
- (cd $(DESTDIR)$(BINDIR); $(LN) -s python2-config python-config)
|
||||
+ (cd $(DESTDIR)$(BINDIR); $(LN) -s python2$(DEBUG_SUFFIX)$(EXE) $(PYTHON))
|
||||
+ -rm -f $(DESTDIR)$(BINDIR)/python2$(DEBUG_SUFFIX)$(EXE)
|
||||
+ (cd $(DESTDIR)$(BINDIR); $(LN) -s python$(VERSION)$(DEBUG_SUFFIX)$(EXE) python2$(DEBUG_SUFFIX)$(EXE))
|
||||
+ -rm -f $(DESTDIR)$(BINDIR)/python2$(DEBUG_SUFFIX)-config
|
||||
+ (cd $(DESTDIR)$(BINDIR); $(LN) -s python$(VERSION)$(DEBUG_SUFFIX)-config python2$(DEBUG_SUFFIX)-config)
|
||||
+ -rm -f $(DESTDIR)$(BINDIR)/python$(DEBUG_SUFFIX)-config
|
||||
+ (cd $(DESTDIR)$(BINDIR); $(LN) -s python2$(DEBUG_SUFFIX)-config python$(DEBUG_SUFFIX)-config)
|
||||
-test -d $(DESTDIR)$(LIBPC) || $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(LIBPC)
|
||||
- -rm -f $(DESTDIR)$(LIBPC)/python2.pc
|
||||
- (cd $(DESTDIR)$(LIBPC); $(LN) -s python-$(VERSION).pc python2.pc)
|
||||
- -rm -f $(DESTDIR)$(LIBPC)/python.pc
|
||||
- (cd $(DESTDIR)$(LIBPC); $(LN) -s python2.pc python.pc)
|
||||
+ -rm -f $(DESTDIR)$(LIBPC)/python2$(DEBUG_SUFFIX).pc
|
||||
+ (cd $(DESTDIR)$(LIBPC); $(LN) -s python-$(VERSION)$(DEBUG_SUFFIX).pc python2$(DEBUG_SUFFIX).pc)
|
||||
+ -rm -f $(DESTDIR)$(LIBPC)/python$(DEBUG_SUFFIX).pc
|
||||
+ (cd $(DESTDIR)$(LIBPC); $(LN) -s python2$(DEBUG_SUFFIX).pc python$(DEBUG_SUFFIX).pc)
|
||||
|
||||
# Install the interpreter with $(VERSION) affixed
|
||||
# This goes into $(exec_prefix)
|
||||
@@ -978,7 +984,7 @@ altbininstall: $(BUILDPYTHON)
|
||||
else true; \
|
||||
fi; \
|
||||
done
|
||||
- $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(BINDIR)/python$(VERSION)$(EXE)
|
||||
+ $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(BINDIR)/python$(VERSION)$(DEBUG_SUFFIX)$(EXE)
|
||||
if test -f $(LDLIBRARY); then \
|
||||
if test -n "$(DLLLIBRARY)" ; then \
|
||||
$(INSTALL_SHARED) $(DLLLIBRARY) $(DESTDIR)$(BINDIR); \
|
||||
@@ -1148,10 +1154,11 @@ $(srcdir)/Lib/$(PLATDIR):
|
||||
fi; \
|
||||
cd $(srcdir)/Lib/$(PLATDIR); $(RUNSHARED) ./regen
|
||||
|
||||
-python-config: $(srcdir)/Misc/python-config.in
|
||||
+python$(DEBUG_SUFFIX)-config: $(srcdir)/Misc/python-config.in
|
||||
# Substitution happens here, as the completely-expanded BINDIR
|
||||
# is not available in configure
|
||||
- sed -e "s,@EXENAME@,$(BINDIR)/python$(VERSION)$(EXE)," < $(srcdir)/Misc/python-config.in >python-config
|
||||
+ sed -e "s,@EXENAME@,$(BINDIR)/python$(VERSION)$(DEBUG_SUFFIX)$(EXE)," < $(srcdir)/Misc/python-config.in >python$(DEBUG_SUFFIX)-config
|
||||
+
|
||||
|
||||
# Install the include files
|
||||
INCLDIRSTOMAKE=$(INCLUDEDIR) $(CONFINCLUDEDIR) $(INCLUDEPY) $(CONFINCLUDEPY)
|
||||
@@ -1172,13 +1179,13 @@ inclinstall:
|
||||
$(INSTALL_DATA) pyconfig.h $(DESTDIR)$(CONFINCLUDEPY)/pyconfig.h
|
||||
|
||||
# Install the library and miscellaneous stuff needed for extending/embedding
|
||||
-# This goes into $(exec_prefix)
|
||||
-LIBPL= $(LIBP)/config
|
||||
+# This goes into $(exec_prefix)$(DEBUG_SUFFIX)
|
||||
+LIBPL= $(LIBP)/config$(DEBUG_SUFFIX)
|
||||
|
||||
# pkgconfig directory
|
||||
LIBPC= $(LIBDIR)/pkgconfig
|
||||
|
||||
-libainstall: @DEF_MAKE_RULE@ python-config
|
||||
+libainstall: @DEF_MAKE_RULE@ python$(DEBUG_SUFFIX)-config
|
||||
@for i in $(LIBDIR) $(LIBP) $(LIBPL) $(LIBPC); \
|
||||
do \
|
||||
if test ! -d $(DESTDIR)$$i; then \
|
||||
@@ -1194,11 +1201,10 @@ libainstall: all python-config
|
||||
$(INSTALL_DATA) Modules/Setup $(DESTDIR)$(LIBPL)/Setup
|
||||
$(INSTALL_DATA) Modules/Setup.local $(DESTDIR)$(LIBPL)/Setup.local
|
||||
$(INSTALL_DATA) Modules/Setup.config $(DESTDIR)$(LIBPL)/Setup.config
|
||||
- $(INSTALL_DATA) Misc/python.pc $(DESTDIR)$(LIBPC)/python-$(VERSION).pc
|
||||
+ $(INSTALL_DATA) Misc/python.pc $(DESTDIR)$(LIBPC)/python-$(VERSION)$(DEBUG_SUFFIX).pc
|
||||
$(INSTALL_SCRIPT) $(srcdir)/Modules/makesetup $(DESTDIR)$(LIBPL)/makesetup
|
||||
$(INSTALL_SCRIPT) $(srcdir)/install-sh $(DESTDIR)$(LIBPL)/install-sh
|
||||
- $(INSTALL_SCRIPT) python-config $(DESTDIR)$(BINDIR)/python$(VERSION)-config
|
||||
- rm python-config
|
||||
+ $(INSTALL_SCRIPT) python$(DEBUG_SUFFIX)-config $(DESTDIR)$(BINDIR)/python$(VERSION)$(DEBUG_SUFFIX)-config
|
||||
@if [ -s Modules/python.exp -a \
|
||||
"`echo $(MACHDEP) | sed 's/^\(...\).*/\1/'`" = "aix" ]; then \
|
||||
echo; echo "Installing support files for building shared extension modules on AIX:"; \
|
||||
diff --git a/Misc/python-config.in b/Misc/python-config.in
|
||||
index a09e07c..c1691ef 100644
|
||||
--- a/Misc/python-config.in
|
||||
+++ b/Misc/python-config.in
|
||||
@@ -44,7 +44,7 @@ for opt in opt_flags:
|
||||
print ' '.join(flags)
|
||||
|
||||
elif opt in ('--libs', '--ldflags'):
|
||||
- libs = ['-lpython' + pyver]
|
||||
+ libs = ['-lpython' + pyver + (sys.pydebug and "_d" or "")]
|
||||
libs += getvar('LIBS').split()
|
||||
libs += getvar('SYSLIBS').split()
|
||||
# add the prefix/lib/pythonX.Y/config dir, but only if there is no
|
||||
diff --git a/Modules/makesetup b/Modules/makesetup
|
||||
index 1bffcbf..f0bc743 100755
|
||||
--- a/Modules/makesetup
|
||||
+++ b/Modules/makesetup
|
||||
@@ -233,7 +233,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
|
||||
*$mod.o*) base=$mod;;
|
||||
*) base=${mod}module;;
|
||||
esac
|
||||
- file="$srcdir/$base\$(SO)"
|
||||
+ file="$srcdir/$base\$(DEBUG_EXT)\$(SO)"
|
||||
case $doconfig in
|
||||
no) SHAREDMODS="$SHAREDMODS $file";;
|
||||
esac
|
||||
diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c
|
||||
index 17ebab1..02a94aa 100644
|
||||
--- a/Python/dynload_shlib.c
|
||||
+++ b/Python/dynload_shlib.c
|
||||
@@ -46,11 +46,16 @@ const struct filedescr _PyImport_DynLoadFiletab[] = {
|
||||
{"module.exe", "rb", C_EXTENSION},
|
||||
{"MODULE.EXE", "rb", C_EXTENSION},
|
||||
#else
|
||||
+#ifdef Py_DEBUG
|
||||
+ {"_d.so", "rb", C_EXTENSION},
|
||||
+ {"module_d.so", "rb", C_EXTENSION},
|
||||
+#else
|
||||
{".so", "rb", C_EXTENSION},
|
||||
{"module.so", "rb", C_EXTENSION},
|
||||
-#endif
|
||||
-#endif
|
||||
-#endif
|
||||
+#endif /* Py_DEBUG */
|
||||
+#endif /* __VMS */
|
||||
+#endif /* defined(PYOS_OS2) && defined(PYCC_GCC) */
|
||||
+#endif /* __CYGWIN__ */
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
|
||||
index aeff38a..183e3cc 100644
|
||||
--- a/Python/sysmodule.c
|
||||
+++ b/Python/sysmodule.c
|
||||
@@ -1524,6 +1524,12 @@ _PySys_Init(void)
|
||||
PyString_FromString("legacy"));
|
||||
#endif
|
||||
|
||||
+#ifdef Py_DEBUG
|
||||
+ PyDict_SetItemString(sysdict, "pydebug", Py_True);
|
||||
+#else
|
||||
+ PyDict_SetItemString(sysdict, "pydebug", Py_False);
|
||||
+#endif
|
||||
+
|
||||
#undef SET_SYS_FROM_STRING
|
||||
if (PyErr_Occurred())
|
||||
return NULL;
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 0a902c7..5caedb7 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -764,7 +764,7 @@ AC_SUBST(LIBRARY)
|
||||
AC_MSG_CHECKING(LIBRARY)
|
||||
if test -z "$LIBRARY"
|
||||
then
|
||||
- LIBRARY='libpython$(VERSION).a'
|
||||
+ LIBRARY='libpython$(VERSION)$(DEBUG_EXT).a'
|
||||
fi
|
||||
AC_MSG_RESULT($LIBRARY)
|
||||
|
||||
@@ -910,8 +910,8 @@ if test $enable_shared = "yes"; then
|
||||
INSTSONAME="$LDLIBRARY".$SOVERSION
|
||||
;;
|
||||
Linux*|GNU*|NetBSD*|FreeBSD*|DragonFly*|OpenBSD*)
|
||||
- LDLIBRARY='libpython$(VERSION).so'
|
||||
- BLDLIBRARY='-L. -lpython$(VERSION)'
|
||||
+ LDLIBRARY='libpython$(VERSION)$(DEBUG_EXT).so'
|
||||
+ BLDLIBRARY='-L. -lpython$(VERSION)$(DEBUG_EXT)'
|
||||
RUNSHARED=LD_LIBRARY_PATH=`pwd`${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
|
||||
case $ac_sys_system in
|
||||
FreeBSD*)
|
||||
@@ -1040,6 +1040,14 @@ else AC_MSG_RESULT(no); Py_DEBUG='false'
|
||||
fi],
|
||||
[AC_MSG_RESULT(no)])
|
||||
|
||||
+if test "$Py_DEBUG" = 'true'
|
||||
+then
|
||||
+ DEBUG_EXT=_d
|
||||
+ DEBUG_SUFFIX=-debug
|
||||
+fi
|
||||
+AC_SUBST(DEBUG_EXT)
|
||||
+AC_SUBST(DEBUG_SUFFIX)
|
||||
+
|
||||
# XXX Shouldn't the code above that fiddles with BASECFLAGS and OPT be
|
||||
# merged with this chunk of code?
|
||||
|
||||
--
|
||||
2.11.0
|
||||
|
@ -1,50 +0,0 @@
|
||||
diff -up Python-2.6.5/configure.ac.more-configuration-flags Python-2.6.5/configure.ac
|
||||
--- Python-2.6.5/configure.ac.more-configuration-flags 2010-05-24 18:51:25.410111792 -0400
|
||||
+++ Python-2.6.5/configure.ac 2010-05-24 18:59:23.954986388 -0400
|
||||
@@ -2515,6 +2515,30 @@ else AC_MSG_RESULT(no)
|
||||
fi],
|
||||
[AC_MSG_RESULT(no)])
|
||||
|
||||
+AC_MSG_CHECKING(for --with-count-allocs)
|
||||
+AC_ARG_WITH(count-allocs,
|
||||
+[ --with(out)count-allocs enable/disable per-type instance accounting], [
|
||||
+if test "$withval" != no
|
||||
+then
|
||||
+ AC_DEFINE(COUNT_ALLOCS, 1,
|
||||
+ [Define to keep records of the number of instances of each type])
|
||||
+ AC_MSG_RESULT(yes)
|
||||
+else AC_MSG_RESULT(no)
|
||||
+fi],
|
||||
+[AC_MSG_RESULT(no)])
|
||||
+
|
||||
+AC_MSG_CHECKING(for --with-call-profile)
|
||||
+AC_ARG_WITH(call-profile,
|
||||
+[ --with(out)-call-profile enable/disable statistics on function call invocation], [
|
||||
+if test "$withval" != no
|
||||
+then
|
||||
+ AC_DEFINE(CALL_PROFILE, 1,
|
||||
+ [Define to keep records on function call invocation])
|
||||
+ AC_MSG_RESULT(yes)
|
||||
+else AC_MSG_RESULT(no)
|
||||
+fi],
|
||||
+[AC_MSG_RESULT(no)])
|
||||
+
|
||||
# Check for Python-specific malloc support
|
||||
AC_MSG_CHECKING(for --with-pymalloc)
|
||||
AC_ARG_WITH(pymalloc,
|
||||
diff -up Python-2.6.5/pyconfig.h.in.more-configuration-flags Python-2.6.5/pyconfig.h.in
|
||||
--- Python-2.6.5/pyconfig.h.in.more-configuration-flags 2010-05-24 18:51:45.677988086 -0400
|
||||
+++ Python-2.6.5/pyconfig.h.in 2010-05-24 19:00:44.163987730 -0400
|
||||
@@ -1019,6 +1019,12 @@
|
||||
/* Define to profile with the Pentium timestamp counter */
|
||||
#undef WITH_TSC
|
||||
|
||||
+/* Define to keep records of the number of instances of each type */
|
||||
+#undef COUNT_ALLOCS
|
||||
+
|
||||
+/* Define to keep records on function call invocation */
|
||||
+#undef CALL_PROFILE
|
||||
+
|
||||
/* Define if you want pymalloc to be disabled when running under valgrind */
|
||||
#undef WITH_VALGRIND
|
||||
|
@ -1,47 +0,0 @@
|
||||
diff -up Python-2.7rc1/Modules/posixmodule.c.statvfs-f-flag-constants Python-2.7rc1/Modules/posixmodule.c
|
||||
--- Python-2.7rc1/Modules/posixmodule.c.statvfs-f-flag-constants 2010-05-15 17:45:30.000000000 -0400
|
||||
+++ Python-2.7rc1/Modules/posixmodule.c 2010-06-07 22:54:16.162068624 -0400
|
||||
@@ -9174,6 +9174,43 @@ all_ins(PyObject *d)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+ /* These came from statvfs.h */
|
||||
+#ifdef ST_RDONLY
|
||||
+ if (ins(d, "ST_RDONLY", (long)ST_RDONLY)) return -1;
|
||||
+#endif /* ST_RDONLY */
|
||||
+#ifdef ST_NOSUID
|
||||
+ if (ins(d, "ST_NOSUID", (long)ST_NOSUID)) return -1;
|
||||
+#endif /* ST_NOSUID */
|
||||
+
|
||||
+ /* GNU extensions */
|
||||
+#ifdef ST_NODEV
|
||||
+ if (ins(d, "ST_NODEV", (long)ST_NODEV)) return -1;
|
||||
+#endif /* ST_NODEV */
|
||||
+#ifdef ST_NOEXEC
|
||||
+ if (ins(d, "ST_NOEXEC", (long)ST_NOEXEC)) return -1;
|
||||
+#endif /* ST_NOEXEC */
|
||||
+#ifdef ST_SYNCHRONOUS
|
||||
+ if (ins(d, "ST_SYNCHRONOUS", (long)ST_SYNCHRONOUS)) return -1;
|
||||
+#endif /* ST_SYNCHRONOUS */
|
||||
+#ifdef ST_MANDLOCK
|
||||
+ if (ins(d, "ST_MANDLOCK", (long)ST_MANDLOCK)) return -1;
|
||||
+#endif /* ST_MANDLOCK */
|
||||
+#ifdef ST_WRITE
|
||||
+ if (ins(d, "ST_WRITE", (long)ST_WRITE)) return -1;
|
||||
+#endif /* ST_WRITE */
|
||||
+#ifdef ST_APPEND
|
||||
+ if (ins(d, "ST_APPEND", (long)ST_APPEND)) return -1;
|
||||
+#endif /* ST_APPEND */
|
||||
+#ifdef ST_NOATIME
|
||||
+ if (ins(d, "ST_NOATIME", (long)ST_NOATIME)) return -1;
|
||||
+#endif /* ST_NOATIME */
|
||||
+#ifdef ST_NODIRATIME
|
||||
+ if (ins(d, "ST_NODIRATIME", (long)ST_NODIRATIME)) return -1;
|
||||
+#endif /* ST_NODIRATIME */
|
||||
+#ifdef ST_RELATIME
|
||||
+ if (ins(d, "ST_RELATIME", (long)ST_RELATIME)) return -1;
|
||||
+#endif /* ST_RELATIME */
|
||||
+
|
||||
#if defined(PYOS_OS2)
|
||||
if (insertvalues(d)) return -1;
|
||||
#endif
|
@ -1,13 +0,0 @@
|
||||
--- Python-2.7.5/Lib/site.py.orig 2013-05-16 12:47:55.000000000 +0200
|
||||
+++ Python-2.7.5/Lib/site.py 2013-05-16 12:56:20.089058109 +0200
|
||||
@@ -529,6 +529,10 @@ def main():
|
||||
|
||||
abs__file__()
|
||||
known_paths = removeduppaths()
|
||||
+ from sysconfig import is_python_build
|
||||
+ if is_python_build():
|
||||
+ from _sysconfigdata import build_time_vars
|
||||
+ sys.path.append(os.path.join(build_time_vars['abs_builddir'], 'Modules'))
|
||||
if ENABLE_USER_SITE is None:
|
||||
ENABLE_USER_SITE = check_enableusersite()
|
||||
known_paths = addusersitepackages(known_paths)
|
@ -1,11 +0,0 @@
|
||||
diff -up Python-2.7.2/Lib/test/test_io.py.disable-tests-in-test_io Python-2.7.2/Lib/test/test_io.py
|
||||
--- Python-2.7.2/Lib/test/test_io.py.disable-tests-in-test_io 2011-09-01 14:18:45.963304089 -0400
|
||||
+++ Python-2.7.2/Lib/test/test_io.py 2011-09-01 15:08:53.796098413 -0400
|
||||
@@ -2669,6 +2669,7 @@ class SignalsTest(unittest.TestCase):
|
||||
self.check_interrupted_read_retry(lambda x: x,
|
||||
mode="r")
|
||||
|
||||
+ @unittest.skip('rhbz#732998')
|
||||
@unittest.skipUnless(threading, 'Threading required for this test.')
|
||||
def check_interrupted_write_retry(self, item, **fdopen_kwargs):
|
||||
"""Check that a buffered write, when it gets interrupted (either
|
@ -1,68 +0,0 @@
|
||||
diff -up Python-2.7.2/Lib/unittest/case.py.add-rpmbuild-hooks-to-unittest Python-2.7.2/Lib/unittest/case.py
|
||||
--- Python-2.7.2/Lib/unittest/case.py.add-rpmbuild-hooks-to-unittest 2011-09-08 14:45:47.677169191 -0400
|
||||
+++ Python-2.7.2/Lib/unittest/case.py 2011-09-08 16:01:36.287858159 -0400
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Test case implementation"""
|
||||
|
||||
import collections
|
||||
+import os
|
||||
import sys
|
||||
import functools
|
||||
import difflib
|
||||
@@ -94,6 +95,43 @@ def expectedFailure(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
+# Non-standard/downstream-only hooks for handling issues with specific test
|
||||
+# cases:
|
||||
+
|
||||
+def _skipInRpmBuild(reason):
|
||||
+ """
|
||||
+ Non-standard/downstream-only decorator for marking a specific unit test
|
||||
+ to be skipped when run within the %check of an rpmbuild.
|
||||
+
|
||||
+ Specifically, this takes effect when WITHIN_PYTHON_RPM_BUILD is set within
|
||||
+ the environment, and has no effect otherwise.
|
||||
+ """
|
||||
+ if 'WITHIN_PYTHON_RPM_BUILD' in os.environ:
|
||||
+ return skip(reason)
|
||||
+ else:
|
||||
+ return _id
|
||||
+
|
||||
+def _expectedFailureInRpmBuild(func):
|
||||
+ """
|
||||
+ Non-standard/downstream-only decorator for marking a specific unit test
|
||||
+ as expected to fail within the %check of an rpmbuild.
|
||||
+
|
||||
+ Specifically, this takes effect when WITHIN_PYTHON_RPM_BUILD is set within
|
||||
+ the environment, and has no effect otherwise.
|
||||
+ """
|
||||
+ @functools.wraps(func)
|
||||
+ def wrapper(*args, **kwargs):
|
||||
+ if 'WITHIN_PYTHON_RPM_BUILD' in os.environ:
|
||||
+ try:
|
||||
+ func(*args, **kwargs)
|
||||
+ except Exception:
|
||||
+ raise _ExpectedFailure(sys.exc_info())
|
||||
+ raise _UnexpectedSuccess
|
||||
+ else:
|
||||
+ # Call directly:
|
||||
+ func(*args, **kwargs)
|
||||
+ return wrapper
|
||||
+
|
||||
class _AssertRaisesContext(object):
|
||||
"""A context manager used to implement TestCase.assertRaises* methods."""
|
||||
|
||||
diff -up Python-2.7.2/Lib/unittest/__init__.py.add-rpmbuild-hooks-to-unittest Python-2.7.2/Lib/unittest/__init__.py
|
||||
--- Python-2.7.2/Lib/unittest/__init__.py.add-rpmbuild-hooks-to-unittest 2011-09-08 14:59:39.534112310 -0400
|
||||
+++ Python-2.7.2/Lib/unittest/__init__.py 2011-09-08 15:07:09.191081562 -0400
|
||||
@@ -57,7 +57,8 @@ __unittest = True
|
||||
|
||||
from .result import TestResult
|
||||
from .case import (TestCase, FunctionTestCase, SkipTest, skip, skipIf,
|
||||
- skipUnless, expectedFailure)
|
||||
+ skipUnless, expectedFailure,
|
||||
+ _skipInRpmBuild, _expectedFailureInRpmBuild)
|
||||
from .suite import BaseTestSuite, TestSuite
|
||||
from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,
|
||||
findTestCases)
|
@ -1,13 +0,0 @@
|
||||
diff -up Python-2.7.2/Lib/test/test_dl.py.skip-test_dl Python-2.7.2/Lib/test/test_dl.py
|
||||
--- Python-2.7.2/Lib/test/test_dl.py.skip-test_dl 2011-09-08 15:18:40.529034289 -0400
|
||||
+++ Python-2.7.2/Lib/test/test_dl.py 2011-09-08 16:29:45.184742670 -0400
|
||||
@@ -13,6 +13,9 @@ sharedlibs = [
|
||||
('/usr/lib/libc.dylib', 'getpid'),
|
||||
]
|
||||
|
||||
+# (also, "dl" is deprecated in favor of ctypes)
|
||||
+@unittest._skipInRpmBuild('fails on 64-bit builds: '
|
||||
+ 'module dl requires sizeof(int) == sizeof(long) == sizeof(char*)')
|
||||
def test_main():
|
||||
for s, func in sharedlibs:
|
||||
try:
|
@ -1,11 +0,0 @@
|
||||
diff -up Python-2.7.6/Lib/test/test_file2k.py.stdin-test Python-2.7.6/Lib/test/test_file2k.py
|
||||
--- Python-2.7.6/Lib/test/test_file2k.py.stdin-test 2013-11-10 08:36:40.000000000 +0100
|
||||
+++ Python-2.7.6/Lib/test/test_file2k.py 2014-01-29 14:28:01.029488055 +0100
|
||||
@@ -223,6 +223,7 @@ class OtherFileTests(unittest.TestCase):
|
||||
else:
|
||||
f.close()
|
||||
|
||||
+ @unittest._skipInRpmBuild('seems not to raise the exception when run in Koji')
|
||||
def testStdinSeek(self):
|
||||
if sys.platform == 'osf1V5':
|
||||
# This causes the interpreter to exit on OSF1 v5.1.
|
@ -1,12 +0,0 @@
|
||||
diff -up Python-2.7.3/Lib/distutils/tests/test_bdist_rpm.py.mark-tests-that-fail-in-rpmbuild Python-2.7.3/Lib/distutils/tests/test_bdist_rpm.py
|
||||
--- Python-2.7.3/Lib/distutils/tests/test_bdist_rpm.py.mark-tests-that-fail-in-rpmbuild 2012-04-09 19:07:29.000000000 -0400
|
||||
+++ Python-2.7.3/Lib/distutils/tests/test_bdist_rpm.py 2012-04-13 00:20:08.223819263 -0400
|
||||
@@ -24,6 +24,7 @@ setup(name='foo', version='0.1', py_modu
|
||||
|
||||
"""
|
||||
|
||||
+@unittest._skipInRpmBuild("don't try to nest one rpm build inside another rpm build")
|
||||
class BuildRpmTestCase(support.TempdirManager,
|
||||
support.EnvironGuard,
|
||||
support.LoggingSilencer,
|
||||
diff -up Python-2.7.3/Lib/distutils/tests/test_build_ext.py.mark-tests-that-fail-in-rpmbuild Python-2.7.3/Lib/distutils/tests/test_build_ext.py
|
@ -1,68 +0,0 @@
|
||||
diff -up Python-2.7.2/Lib/distutils/tests/test_build_ext.py.mark-tests-that-fail-in-rpmbuild Python-2.7.2/Lib/distutils/tests/test_build_ext.py
|
||||
--- Python-2.7.2/Lib/distutils/tests/test_build_ext.py.mark-tests-that-fail-in-rpmbuild 2011-09-08 16:07:25.033834312 -0400
|
||||
+++ Python-2.7.2/Lib/distutils/tests/test_build_ext.py 2011-09-08 17:43:15.656441082 -0400
|
||||
@@ -330,6 +332,7 @@ class BuildExtTestCase(support.TempdirMa
|
||||
self.assertEqual(lastdir, 'bar')
|
||||
|
||||
def test_ext_fullpath(self):
|
||||
+ debug_ext = sysconfig.get_config_var("DEBUG_EXT")
|
||||
ext = sysconfig.get_config_vars()['SO']
|
||||
dist = Distribution()
|
||||
cmd = build_ext(dist)
|
||||
@@ -337,14 +340,14 @@ class BuildExtTestCase(support.TempdirMa
|
||||
cmd.distribution.package_dir = {'': 'src'}
|
||||
cmd.distribution.packages = ['lxml', 'lxml.html']
|
||||
curdir = os.getcwd()
|
||||
- wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
|
||||
+ wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + debug_ext + ext)
|
||||
path = cmd.get_ext_fullpath('lxml.etree')
|
||||
self.assertEqual(wanted, path)
|
||||
|
||||
# building lxml.etree not inplace
|
||||
cmd.inplace = 0
|
||||
cmd.build_lib = os.path.join(curdir, 'tmpdir')
|
||||
- wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
|
||||
+ wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + debug_ext + ext)
|
||||
path = cmd.get_ext_fullpath('lxml.etree')
|
||||
self.assertEqual(wanted, path)
|
||||
|
||||
@@ -354,13 +357,13 @@ class BuildExtTestCase(support.TempdirMa
|
||||
cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
|
||||
path = cmd.get_ext_fullpath('twisted.runner.portmap')
|
||||
wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
|
||||
- 'portmap' + ext)
|
||||
+ 'portmap' + debug_ext + ext)
|
||||
self.assertEqual(wanted, path)
|
||||
|
||||
# building twisted.runner.portmap inplace
|
||||
cmd.inplace = 1
|
||||
path = cmd.get_ext_fullpath('twisted.runner.portmap')
|
||||
- wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
|
||||
+ wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + debug_ext + ext)
|
||||
self.assertEqual(wanted, path)
|
||||
|
||||
def test_build_ext_inplace(self):
|
||||
@@ -373,8 +376,9 @@ class BuildExtTestCase(support.TempdirMa
|
||||
cmd.distribution.package_dir = {'': 'src'}
|
||||
cmd.distribution.packages = ['lxml', 'lxml.html']
|
||||
curdir = os.getcwd()
|
||||
+ debug_ext = sysconfig.get_config_var("DEBUG_EXT")
|
||||
ext = sysconfig.get_config_var("SO")
|
||||
- wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
|
||||
+ wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + debug_ext + ext)
|
||||
path = cmd.get_ext_fullpath('lxml.etree')
|
||||
self.assertEqual(wanted, path)
|
||||
|
||||
@@ -412,10 +416,11 @@ class BuildExtTestCase(support.TempdirMa
|
||||
dist = Distribution({'name': 'UpdateManager'})
|
||||
cmd = build_ext(dist)
|
||||
cmd.ensure_finalized()
|
||||
+ debug_ext = sysconfig.get_config_var("DEBUG_EXT")
|
||||
ext = sysconfig.get_config_var("SO")
|
||||
ext_name = os.path.join('UpdateManager', 'fdsend')
|
||||
ext_path = cmd.get_ext_fullpath(ext_name)
|
||||
- wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext)
|
||||
+ wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + debug_ext + ext)
|
||||
self.assertEqual(ext_path, wanted)
|
||||
|
||||
@unittest.skipUnless(sys.platform == 'win32', 'these tests require Windows')
|
@ -1,11 +0,0 @@
|
||||
diff -up Python-2.7.2/Lib/test/test_float.py.skip-test_float-known-failure-on-arm Python-2.7.2/Lib/test/test_float.py
|
||||
--- Python-2.7.2/Lib/test/test_float.py.skip-test_float-known-failure-on-arm 2011-09-08 19:34:09.000986128 -0400
|
||||
+++ Python-2.7.2/Lib/test/test_float.py 2011-09-08 19:34:57.969982779 -0400
|
||||
@@ -1072,6 +1072,7 @@ class HexFloatTestCase(unittest.TestCase
|
||||
self.identical(got, expected)
|
||||
|
||||
|
||||
+ @unittest.skip('Known failure on ARM: http://bugs.python.org/issue8265')
|
||||
def test_from_hex(self):
|
||||
MIN = self.MIN;
|
||||
MAX = self.MAX;
|
@ -1,11 +0,0 @@
|
||||
diff -up Python-2.7.2/Lib/ctypes/test/test_callbacks.py.skip-test_ctypes-known-failure-on-sparc Python-2.7.2/Lib/ctypes/test/test_callbacks.py
|
||||
--- Python-2.7.2/Lib/ctypes/test/test_callbacks.py.skip-test_ctypes-known-failure-on-sparc 2011-09-08 19:42:35.541951490 -0400
|
||||
+++ Python-2.7.2/Lib/ctypes/test/test_callbacks.py 2011-09-08 19:43:40.676947036 -0400
|
||||
@@ -67,6 +67,7 @@ class Callbacks(unittest.TestCase):
|
||||
self.check_type(c_longlong, 42)
|
||||
self.check_type(c_longlong, -42)
|
||||
|
||||
+ @unittest.skip('Known failure on Sparc: http://bugs.python.org/issue8314')
|
||||
def test_ulonglong(self):
|
||||
# test some 64-bit values, with and without msb set.
|
||||
self.check_type(c_ulonglong, 10955412242170339782)
|
@ -1,22 +0,0 @@
|
||||
diff -up Python-2.7.6/Lib/test/test_openpty.py.tty-fail Python-2.7.6/Lib/test/test_openpty.py
|
||||
--- Python-2.7.6/Lib/test/test_openpty.py.tty-fail 2014-01-29 14:31:43.761343267 +0100
|
||||
+++ Python-2.7.6/Lib/test/test_openpty.py 2014-01-29 14:32:19.284090165 +0100
|
||||
@@ -8,6 +8,7 @@ if not hasattr(os, "openpty"):
|
||||
|
||||
|
||||
class OpenptyTest(unittest.TestCase):
|
||||
+ @unittest._skipInRpmBuild('sometimes fails in Koji, possibly due to a mock issue (rhbz#714627)')
|
||||
def test(self):
|
||||
master, slave = os.openpty()
|
||||
self.addCleanup(os.close, master)
|
||||
diff -up Python-2.7.6/Lib/test/test_pty.py.tty-fail Python-2.7.6/Lib/test/test_pty.py
|
||||
--- Python-2.7.6/Lib/test/test_pty.py.tty-fail 2013-11-10 08:36:40.000000000 +0100
|
||||
+++ Python-2.7.6/Lib/test/test_pty.py 2014-01-29 14:31:43.761343267 +0100
|
||||
@@ -111,6 +111,7 @@ class PtyTest(unittest.TestCase):
|
||||
os.close(master_fd)
|
||||
|
||||
|
||||
+ @unittest._skipInRpmBuild('sometimes fails in Koji, possibly due to a mock issue (rhbz#714627)')
|
||||
def test_fork(self):
|
||||
debug("calling pty.fork()")
|
||||
pid, master_fd = pty.fork()
|
@ -1,732 +0,0 @@
|
||||
diff -up Python-2.7.2/Lib/hashlib.py.hashlib-fips Python-2.7.2/Lib/hashlib.py
|
||||
--- Python-2.7.2/Lib/hashlib.py.hashlib-fips 2011-06-11 11:46:24.000000000 -0400
|
||||
+++ Python-2.7.2/Lib/hashlib.py 2011-09-14 00:21:26.194252001 -0400
|
||||
@@ -6,9 +6,12 @@
|
||||
|
||||
__doc__ = """hashlib module - A common interface to many hash functions.
|
||||
|
||||
-new(name, string='') - returns a new hash object implementing the
|
||||
- given hash function; initializing the hash
|
||||
- using the given string data.
|
||||
+new(name, string='', usedforsecurity=True)
|
||||
+ - returns a new hash object implementing the given hash function;
|
||||
+ initializing the hash using the given string data.
|
||||
+
|
||||
+ "usedforsecurity" is a non-standard extension for better supporting
|
||||
+ FIPS-compliant environments (see below)
|
||||
|
||||
Named constructor functions are also available, these are much faster
|
||||
than using new():
|
||||
@@ -24,6 +27,20 @@ the zlib module.
|
||||
Choose your hash function wisely. Some have known collision weaknesses.
|
||||
sha384 and sha512 will be slow on 32 bit platforms.
|
||||
|
||||
+Our implementation of hashlib uses OpenSSL.
|
||||
+
|
||||
+OpenSSL has a "FIPS mode", which, if enabled, may restrict the available hashes
|
||||
+to only those that are compliant with FIPS regulations. For example, it may
|
||||
+deny the use of MD5, on the grounds that this is not secure for uses such as
|
||||
+authentication, system integrity checking, or digital signatures.
|
||||
+
|
||||
+If you need to use such a hash for non-security purposes (such as indexing into
|
||||
+a data structure for speed), you can override the keyword argument
|
||||
+"usedforsecurity" from True to False to signify that your code is not relying
|
||||
+on the hash for security purposes, and this will allow the hash to be usable
|
||||
+even in FIPS mode. This is not a standard feature of Python 2.7's hashlib, and
|
||||
+is included here to better support FIPS mode.
|
||||
+
|
||||
Hash objects have these methods:
|
||||
- update(arg): Update the hash object with the string arg. Repeated calls
|
||||
are equivalent to a single call with the concatenation of all
|
||||
@@ -63,76 +80,41 @@ algorithms = __always_supported
|
||||
'pbkdf2_hmac')
|
||||
|
||||
|
||||
-def __get_builtin_constructor(name):
|
||||
- try:
|
||||
- if name in ('SHA1', 'sha1'):
|
||||
- import _sha
|
||||
- return _sha.new
|
||||
- elif name in ('MD5', 'md5'):
|
||||
- import _md5
|
||||
- return _md5.new
|
||||
- elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'):
|
||||
- import _sha256
|
||||
- bs = name[3:]
|
||||
- if bs == '256':
|
||||
- return _sha256.sha256
|
||||
- elif bs == '224':
|
||||
- return _sha256.sha224
|
||||
- elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'):
|
||||
- import _sha512
|
||||
- bs = name[3:]
|
||||
- if bs == '512':
|
||||
- return _sha512.sha512
|
||||
- elif bs == '384':
|
||||
- return _sha512.sha384
|
||||
- except ImportError:
|
||||
- pass # no extension module, this hash is unsupported.
|
||||
-
|
||||
- raise ValueError('unsupported hash type ' + name)
|
||||
-
|
||||
-
|
||||
def __get_openssl_constructor(name):
|
||||
try:
|
||||
f = getattr(_hashlib, 'openssl_' + name)
|
||||
# Allow the C module to raise ValueError. The function will be
|
||||
# defined but the hash not actually available thanks to OpenSSL.
|
||||
- f()
|
||||
+ #
|
||||
+ # We pass "usedforsecurity=False" to disable FIPS-based restrictions:
|
||||
+ # at this stage we're merely seeing if the function is callable,
|
||||
+ # rather than using it for actual work.
|
||||
+ f(usedforsecurity=False)
|
||||
# Use the C function directly (very fast)
|
||||
return f
|
||||
except (AttributeError, ValueError):
|
||||
- return __get_builtin_constructor(name)
|
||||
+ raise
|
||||
|
||||
-
|
||||
-def __py_new(name, string=''):
|
||||
- """new(name, string='') - Return a new hashing object using the named algorithm;
|
||||
- optionally initialized with a string.
|
||||
- """
|
||||
- return __get_builtin_constructor(name)(string)
|
||||
-
|
||||
-
|
||||
-def __hash_new(name, string=''):
|
||||
+def __hash_new(name, string='', usedforsecurity=True):
|
||||
"""new(name, string='') - Return a new hashing object using the named algorithm;
|
||||
optionally initialized with a string.
|
||||
+ Override 'usedforsecurity' to False when using for non-security purposes in
|
||||
+ a FIPS environment
|
||||
"""
|
||||
try:
|
||||
- return _hashlib.new(name, string)
|
||||
+ return _hashlib.new(name, string, usedforsecurity)
|
||||
except ValueError:
|
||||
- # If the _hashlib module (OpenSSL) doesn't support the named
|
||||
- # hash, try using our builtin implementations.
|
||||
- # This allows for SHA224/256 and SHA384/512 support even though
|
||||
- # the OpenSSL library prior to 0.9.8 doesn't provide them.
|
||||
- return __get_builtin_constructor(name)(string)
|
||||
-
|
||||
+ raise
|
||||
|
||||
try:
|
||||
import _hashlib
|
||||
new = __hash_new
|
||||
__get_hash = __get_openssl_constructor
|
||||
algorithms_available = algorithms_available.union(
|
||||
_hashlib.openssl_md_meth_names)
|
||||
except ImportError:
|
||||
- new = __py_new
|
||||
- __get_hash = __get_builtin_constructor
|
||||
+ # We don't build the legacy modules
|
||||
+ raise
|
||||
|
||||
for __func_name in __always_supported:
|
||||
# try them all, some may not work due to the OpenSSL
|
||||
@@ -143,4 +125,4 @@ for __func_name in __always_supported:
|
||||
|
||||
# Cleanup locals()
|
||||
del __always_supported, __func_name, __get_hash
|
||||
-del __py_new, __hash_new, __get_openssl_constructor
|
||||
+del __hash_new, __get_openssl_constructor
|
||||
diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/test/test_hashlib.py
|
||||
--- Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips 2011-06-11 11:46:25.000000000 -0400
|
||||
+++ Python-2.7.2/Lib/test/test_hashlib.py 2011-09-14 01:08:55.525254195 -0400
|
||||
@@ -32,6 +32,19 @@ def hexstr(s):
|
||||
r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
|
||||
return r
|
||||
|
||||
+def openssl_enforces_fips():
|
||||
+ # Use the "openssl" command (if present) to try to determine if the local
|
||||
+ # OpenSSL is configured to enforce FIPS
|
||||
+ from subprocess import Popen, PIPE
|
||||
+ try:
|
||||
+ p = Popen(['openssl', 'md5'],
|
||||
+ stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
+ except OSError:
|
||||
+ # "openssl" command not found
|
||||
+ return False
|
||||
+ stdout, stderr = p.communicate(input=b'abc')
|
||||
+ return b'unknown cipher' in stderr
|
||||
+OPENSSL_ENFORCES_FIPS = openssl_enforces_fips()
|
||||
|
||||
class HashLibTestCase(unittest.TestCase):
|
||||
supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1',
|
||||
@@ -61,10 +74,10 @@ class HashLibTestCase(unittest.TestCase)
|
||||
# of hashlib.new given the algorithm name.
|
||||
for algorithm, constructors in self.constructors_to_test.items():
|
||||
constructors.add(getattr(hashlib, algorithm))
|
||||
- def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm):
|
||||
+ def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, usedforsecurity=True):
|
||||
if data is None:
|
||||
- return hashlib.new(_alg)
|
||||
- return hashlib.new(_alg, data)
|
||||
+ return hashlib.new(_alg, usedforsecurity=usedforsecurity)
|
||||
+ return hashlib.new(_alg, data, usedforsecurity=usedforsecurity)
|
||||
constructors.add(_test_algorithm_via_hashlib_new)
|
||||
|
||||
_hashlib = self._conditional_import_module('_hashlib')
|
||||
@@ -78,28 +91,13 @@ class HashLibTestCase(unittest.TestCase)
|
||||
if constructor:
|
||||
constructors.add(constructor)
|
||||
|
||||
- _md5 = self._conditional_import_module('_md5')
|
||||
- if _md5:
|
||||
- self.constructors_to_test['md5'].add(_md5.new)
|
||||
- _sha = self._conditional_import_module('_sha')
|
||||
- if _sha:
|
||||
- self.constructors_to_test['sha1'].add(_sha.new)
|
||||
- _sha256 = self._conditional_import_module('_sha256')
|
||||
- if _sha256:
|
||||
- self.constructors_to_test['sha224'].add(_sha256.sha224)
|
||||
- self.constructors_to_test['sha256'].add(_sha256.sha256)
|
||||
- _sha512 = self._conditional_import_module('_sha512')
|
||||
- if _sha512:
|
||||
- self.constructors_to_test['sha384'].add(_sha512.sha384)
|
||||
- self.constructors_to_test['sha512'].add(_sha512.sha512)
|
||||
-
|
||||
super(HashLibTestCase, self).__init__(*args, **kwargs)
|
||||
|
||||
def test_hash_array(self):
|
||||
a = array.array("b", range(10))
|
||||
constructors = self.constructors_to_test.itervalues()
|
||||
for cons in itertools.chain.from_iterable(constructors):
|
||||
- c = cons(a)
|
||||
+ c = cons(a, usedforsecurity=False)
|
||||
c.hexdigest()
|
||||
|
||||
def test_algorithms_attribute(self):
|
||||
@@ -115,28 +113,9 @@ class HashLibTestCase(unittest.TestCase)
|
||||
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
|
||||
self.assertRaises(TypeError, hashlib.new, 1)
|
||||
|
||||
- def test_get_builtin_constructor(self):
|
||||
- get_builtin_constructor = hashlib.__dict__[
|
||||
- '__get_builtin_constructor']
|
||||
- self.assertRaises(ValueError, get_builtin_constructor, 'test')
|
||||
- try:
|
||||
- import _md5
|
||||
- except ImportError:
|
||||
- pass
|
||||
- # This forces an ImportError for "import _md5" statements
|
||||
- sys.modules['_md5'] = None
|
||||
- try:
|
||||
- self.assertRaises(ValueError, get_builtin_constructor, 'md5')
|
||||
- finally:
|
||||
- if '_md5' in locals():
|
||||
- sys.modules['_md5'] = _md5
|
||||
- else:
|
||||
- del sys.modules['_md5']
|
||||
- self.assertRaises(TypeError, get_builtin_constructor, 3)
|
||||
-
|
||||
def test_hexdigest(self):
|
||||
for name in self.supported_hash_names:
|
||||
- h = hashlib.new(name)
|
||||
+ h = hashlib.new(name, usedforsecurity=False)
|
||||
self.assertTrue(hexstr(h.digest()) == h.hexdigest())
|
||||
|
||||
def test_large_update(self):
|
||||
@@ -145,16 +125,16 @@ class HashLibTestCase(unittest.TestCase)
|
||||
abcs = aas + bees + cees
|
||||
|
||||
for name in self.supported_hash_names:
|
||||
- m1 = hashlib.new(name)
|
||||
+ m1 = hashlib.new(name, usedforsecurity=False)
|
||||
m1.update(aas)
|
||||
m1.update(bees)
|
||||
m1.update(cees)
|
||||
|
||||
- m2 = hashlib.new(name)
|
||||
+ m2 = hashlib.new(name, usedforsecurity=False)
|
||||
m2.update(abcs)
|
||||
self.assertEqual(m1.digest(), m2.digest(), name+' update problem.')
|
||||
|
||||
- m3 = hashlib.new(name, abcs)
|
||||
+ m3 = hashlib.new(name, abcs, usedforsecurity=False)
|
||||
self.assertEqual(m1.digest(), m3.digest(), name+' new problem.')
|
||||
|
||||
def check(self, name, data, digest):
|
||||
@@ -162,7 +142,7 @@ class HashLibTestCase(unittest.TestCase)
|
||||
# 2 is for hashlib.name(...) and hashlib.new(name, ...)
|
||||
self.assertGreaterEqual(len(constructors), 2)
|
||||
for hash_object_constructor in constructors:
|
||||
- computed = hash_object_constructor(data).hexdigest()
|
||||
+ computed = hash_object_constructor(data, usedforsecurity=False).hexdigest()
|
||||
self.assertEqual(
|
||||
computed, digest,
|
||||
"Hash algorithm %s constructed using %s returned hexdigest"
|
||||
@@ -172,7 +152,8 @@ class HashLibTestCase(unittest.TestCase)
|
||||
|
||||
def check_unicode(self, algorithm_name):
|
||||
# Unicode objects are not allowed as input.
|
||||
- expected = hashlib.new(algorithm_name, str(u'spam')).hexdigest()
|
||||
+ expected = hashlib.new(algorithm_name, str(u'spam'),
|
||||
+ usedforsecurity=False).hexdigest()
|
||||
self.check(algorithm_name, u'spam', expected)
|
||||
|
||||
def test_unicode(self):
|
||||
@@ -354,6 +335,70 @@ class HashLibTestCase(unittest.TestCase)
|
||||
self.assertEqual(expected_hash, hasher.hexdigest())
|
||||
|
||||
|
||||
+ def test_issue9146(self):
|
||||
+ # Ensure that various ways to use "MD5" from "hashlib" don't segfault:
|
||||
+ m = hashlib.md5(usedforsecurity=False)
|
||||
+ m.update(b'abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = hashlib.new('md5', usedforsecurity=False)
|
||||
+ m.update(b'abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = hashlib.md5(b'abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = hashlib.new('md5', b'abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ def assertRaisesUnknownCipher(self, callable_obj=None, *args, **kwargs):
|
||||
+ try:
|
||||
+ callable_obj(*args, **kwargs)
|
||||
+ except ValueError, e:
|
||||
+ if not e.args[0].endswith('unknown cipher'):
|
||||
+ self.fail('Incorrect exception raised')
|
||||
+ else:
|
||||
+ self.fail('Exception was not raised')
|
||||
+
|
||||
+ @unittest.skipUnless(OPENSSL_ENFORCES_FIPS,
|
||||
+ 'FIPS enforcement required for this test.')
|
||||
+ def test_hashlib_fips_mode(self):
|
||||
+ # Ensure that we raise a ValueError on vanilla attempts to use MD5
|
||||
+ # in hashlib in a FIPS-enforced setting:
|
||||
+ self.assertRaisesUnknownCipher(hashlib.md5)
|
||||
+ self.assertRaisesUnknownCipher(hashlib.new, 'md5')
|
||||
+
|
||||
+ @unittest.skipUnless(OPENSSL_ENFORCES_FIPS,
|
||||
+ 'FIPS enforcement required for this test.')
|
||||
+ def test_hashopenssl_fips_mode(self):
|
||||
+ # Verify the _hashlib module's handling of md5:
|
||||
+ import _hashlib
|
||||
+
|
||||
+ assert hasattr(_hashlib, 'openssl_md5')
|
||||
+
|
||||
+ # Ensure that _hashlib raises a ValueError on vanilla attempts to
|
||||
+ # use MD5 in a FIPS-enforced setting:
|
||||
+ self.assertRaisesUnknownCipher(_hashlib.openssl_md5)
|
||||
+ self.assertRaisesUnknownCipher(_hashlib.new, 'md5')
|
||||
+
|
||||
+ # Ensure that in such a setting we can whitelist a callsite with
|
||||
+ # usedforsecurity=False and have it succeed:
|
||||
+ m = _hashlib.openssl_md5(usedforsecurity=False)
|
||||
+ m.update('abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = _hashlib.new('md5', usedforsecurity=False)
|
||||
+ m.update('abc\n')
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = _hashlib.openssl_md5('abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+ m = _hashlib.new('md5', 'abc\n', usedforsecurity=False)
|
||||
+ self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1")
|
||||
+
|
||||
+
|
||||
+
|
||||
class KDFTests(unittest.TestCase):
|
||||
pbkdf2_test_vectors = [
|
||||
(b'password', b'salt', 1, None),
|
||||
diff -up Python-2.7.2/Modules/Setup.dist.hashlib-fips Python-2.7.2/Modules/Setup.dist
|
||||
--- Python-2.7.2/Modules/Setup.dist.hashlib-fips 2011-09-14 00:21:26.163252001 -0400
|
||||
+++ Python-2.7.2/Modules/Setup.dist 2011-09-14 00:21:26.201252001 -0400
|
||||
@@ -248,14 +248,14 @@ imageop imageop.c # Operations on images
|
||||
# Message-Digest Algorithm, described in RFC 1321. The necessary files
|
||||
# md5.c and md5.h are included here.
|
||||
|
||||
-_md5 md5module.c md5.c
|
||||
+#_md5 md5module.c md5.c
|
||||
|
||||
|
||||
# The _sha module implements the SHA checksum algorithms.
|
||||
# (NIST's Secure Hash Algorithms.)
|
||||
-_sha shamodule.c
|
||||
-_sha256 sha256module.c
|
||||
-_sha512 sha512module.c
|
||||
+#_sha shamodule.c
|
||||
+#_sha256 sha256module.c
|
||||
+#_sha512 sha512module.c
|
||||
|
||||
|
||||
# SGI IRIX specific modules -- off by default.
|
||||
diff -up Python-2.7.2/setup.py.hashlib-fips Python-2.7.2/setup.py
|
||||
--- Python-2.7.2/setup.py.hashlib-fips 2011-09-14 00:21:25.722252001 -0400
|
||||
+++ Python-2.7.2/setup.py 2011-09-14 00:21:26.203252001 -0400
|
||||
@@ -768,21 +768,6 @@ class PyBuildExt(build_ext):
|
||||
print ("warning: openssl 0x%08x is too old for _hashlib" %
|
||||
openssl_ver)
|
||||
missing.append('_hashlib')
|
||||
- if COMPILED_WITH_PYDEBUG or not have_usable_openssl:
|
||||
- # The _sha module implements the SHA1 hash algorithm.
|
||||
- exts.append( Extension('_sha', ['shamodule.c']) )
|
||||
- # The _md5 module implements the RSA Data Security, Inc. MD5
|
||||
- # Message-Digest Algorithm, described in RFC 1321. The
|
||||
- # necessary files md5.c and md5.h are included here.
|
||||
- exts.append( Extension('_md5',
|
||||
- sources = ['md5module.c', 'md5.c'],
|
||||
- depends = ['md5.h']) )
|
||||
-
|
||||
- min_sha2_openssl_ver = 0x00908000
|
||||
- if COMPILED_WITH_PYDEBUG or openssl_ver < min_sha2_openssl_ver:
|
||||
- # OpenSSL doesn't do these until 0.9.8 so we'll bring our own hash
|
||||
- exts.append( Extension('_sha256', ['sha256module.c']) )
|
||||
- exts.append( Extension('_sha512', ['sha512module.c']) )
|
||||
|
||||
# Modules that provide persistent dictionary-like semantics. You will
|
||||
# probably want to arrange for at least one of them to be available on
|
||||
--- Python-2.7.8/Modules/_hashopenssl.c.orig 2014-06-30 04:05:41.000000000 +0200
|
||||
+++ Python-2.7.8/Modules/_hashopenssl.c 2014-07-14 14:21:59.546386572 +0200
|
||||
@@ -36,6 +36,8 @@
|
||||
#endif
|
||||
|
||||
/* EVP is the preferred interface to hashing in OpenSSL */
|
||||
+#include <openssl/ssl.h>
|
||||
+#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/err.h>
|
||||
@@ -67,11 +69,19 @@
|
||||
|
||||
static PyTypeObject EVPtype;
|
||||
|
||||
+/* Struct to hold all the cached information we need on a specific algorithm.
|
||||
+ We have one of these per algorithm */
|
||||
+typedef struct {
|
||||
+ PyObject *name_obj;
|
||||
+ EVP_MD_CTX ctxs[2];
|
||||
+ /* ctx_ptrs will point to ctxs unless an error occurred, when it will
|
||||
+ be NULL: */
|
||||
+ EVP_MD_CTX *ctx_ptrs[2];
|
||||
+ PyObject *error_msgs[2];
|
||||
+} EVPCachedInfo;
|
||||
|
||||
-#define DEFINE_CONSTS_FOR_NEW(Name) \
|
||||
- static PyObject *CONST_ ## Name ## _name_obj = NULL; \
|
||||
- static EVP_MD_CTX CONST_new_ ## Name ## _ctx; \
|
||||
- static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL;
|
||||
+#define DEFINE_CONSTS_FOR_NEW(Name) \
|
||||
+ static EVPCachedInfo cached_info_ ##Name;
|
||||
|
||||
DEFINE_CONSTS_FOR_NEW(md5)
|
||||
DEFINE_CONSTS_FOR_NEW(sha1)
|
||||
@@ -117,6 +127,48 @@
|
||||
}
|
||||
}
|
||||
|
||||
+static void
|
||||
+mc_ctx_init(EVP_MD_CTX *ctx, int usedforsecurity)
|
||||
+{
|
||||
+ EVP_MD_CTX_init(ctx);
|
||||
+
|
||||
+ /*
|
||||
+ If the user has declared that this digest is being used in a
|
||||
+ non-security role (e.g. indexing into a data structure), set
|
||||
+ the exception flag for openssl to allow it
|
||||
+ */
|
||||
+ if (!usedforsecurity) {
|
||||
+#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
|
||||
+ EVP_MD_CTX_set_flags(ctx,
|
||||
+ EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
|
||||
+#endif
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* Get an error msg for the last error as a PyObject */
|
||||
+static PyObject *
|
||||
+error_msg_for_last_error(void)
|
||||
+{
|
||||
+ char *errstr;
|
||||
+
|
||||
+ errstr = ERR_error_string(ERR_peek_last_error(), NULL);
|
||||
+ ERR_clear_error();
|
||||
+
|
||||
+ return PyString_FromString(errstr); /* Can be NULL */
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+set_evp_exception(void)
|
||||
+{
|
||||
+ char *errstr;
|
||||
+
|
||||
+ errstr = ERR_error_string(ERR_peek_last_error(), NULL);
|
||||
+ ERR_clear_error();
|
||||
+
|
||||
+ PyErr_SetString(PyExc_ValueError, errstr);
|
||||
+}
|
||||
+
|
||||
+
|
||||
/* Internal methods for a hash object */
|
||||
|
||||
static void
|
||||
@@ -315,14 +367,15 @@
|
||||
static int
|
||||
EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
- static char *kwlist[] = {"name", "string", NULL};
|
||||
+ static char *kwlist[] = {"name", "string", "usedforsecurity", NULL};
|
||||
PyObject *name_obj = NULL;
|
||||
+ int usedforsecurity = 1;
|
||||
Py_buffer view = { 0 };
|
||||
char *nameStr;
|
||||
const EVP_MD *digest;
|
||||
|
||||
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s*:HASH", kwlist,
|
||||
- &name_obj, &view)) {
|
||||
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s*i:HASH", kwlist,
|
||||
+ &name_obj, &view, &usedforsecurity)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -338,7 +391,12 @@
|
||||
PyBuffer_Release(&view);
|
||||
return -1;
|
||||
}
|
||||
- EVP_DigestInit(&self->ctx, digest);
|
||||
+ mc_ctx_init(&self->ctx, usedforsecurity);
|
||||
+ if (!EVP_DigestInit_ex(&self->ctx, digest, NULL)) {
|
||||
+ set_evp_exception();
|
||||
+ PyBuffer_Release(&view);
|
||||
+ return -1;
|
||||
+ }
|
||||
|
||||
self->name = name_obj;
|
||||
Py_INCREF(self->name);
|
||||
@@ -422,7 +480,8 @@
|
||||
static PyObject *
|
||||
EVPnew(PyObject *name_obj,
|
||||
const EVP_MD *digest, const EVP_MD_CTX *initial_ctx,
|
||||
- const unsigned char *cp, Py_ssize_t len)
|
||||
+ const unsigned char *cp, Py_ssize_t len,
|
||||
+ int usedforsecurity)
|
||||
{
|
||||
EVPobject *self;
|
||||
|
||||
@@ -437,7 +496,12 @@
|
||||
if (initial_ctx) {
|
||||
EVP_MD_CTX_copy(&self->ctx, initial_ctx);
|
||||
} else {
|
||||
- EVP_DigestInit(&self->ctx, digest);
|
||||
+ mc_ctx_init(&self->ctx, usedforsecurity);
|
||||
+ if (!EVP_DigestInit_ex(&self->ctx, digest, NULL)) {
|
||||
+ set_evp_exception();
|
||||
+ Py_DECREF(self);
|
||||
+ return NULL;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (cp && len) {
|
||||
@@ -461,20 +525,28 @@
|
||||
An optional string argument may be provided and will be\n\
|
||||
automatically hashed.\n\
|
||||
\n\
|
||||
-The MD5 and SHA1 algorithms are always supported.\n");
|
||||
+The MD5 and SHA1 algorithms are always supported.\n\
|
||||
+\n\
|
||||
+An optional \"usedforsecurity=True\" keyword argument is provided for use in\n\
|
||||
+environments that enforce FIPS-based restrictions. Some implementations of\n\
|
||||
+OpenSSL can be configured to prevent the usage of non-secure algorithms (such\n\
|
||||
+as MD5). If you have a non-security use for these algorithms (e.g. a hash\n\
|
||||
+table), you can override this argument by marking the callsite as\n\
|
||||
+\"usedforsecurity=False\".");
|
||||
|
||||
static PyObject *
|
||||
EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
|
||||
{
|
||||
- static char *kwlist[] = {"name", "string", NULL};
|
||||
+ static char *kwlist[] = {"name", "string", "usedforsecurity", NULL};
|
||||
PyObject *name_obj = NULL;
|
||||
Py_buffer view = { 0 };
|
||||
PyObject *ret_obj;
|
||||
char *name;
|
||||
const EVP_MD *digest;
|
||||
+ int usedforsecurity = 1;
|
||||
|
||||
- if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*:new", kwlist,
|
||||
- &name_obj, &view)) {
|
||||
+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*i:new", kwlist,
|
||||
+ &name_obj, &view, &usedforsecurity)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -487,7 +559,7 @@
|
||||
digest = EVP_get_digestbyname(name);
|
||||
|
||||
ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf,
|
||||
- view.len);
|
||||
+ view.len, usedforsecurity);
|
||||
PyBuffer_Release(&view);
|
||||
|
||||
return ret_obj;
|
||||
@@ -713,51 +785,111 @@
|
||||
|
||||
|
||||
/*
|
||||
- * This macro generates constructor function definitions for specific
|
||||
- * hash algorithms. These constructors are much faster than calling
|
||||
- * the generic one passing it a python string and are noticably
|
||||
- * faster than calling a python new() wrapper. Thats important for
|
||||
+ * This macro and function generates a family of constructor function
|
||||
+ * definitions for specific hash algorithms. These constructors are much
|
||||
+ * faster than calling the generic one passing it a python string and are
|
||||
+ * noticably faster than calling a python new() wrapper. That's important for
|
||||
* code that wants to make hashes of a bunch of small strings.
|
||||
*/
|
||||
#define GEN_CONSTRUCTOR(NAME) \
|
||||
static PyObject * \
|
||||
- EVP_new_ ## NAME (PyObject *self, PyObject *args) \
|
||||
+ EVP_new_ ## NAME (PyObject *self, PyObject *args, PyObject *kwdict) \
|
||||
{ \
|
||||
- Py_buffer view = { 0 }; \
|
||||
- PyObject *ret_obj; \
|
||||
- \
|
||||
- if (!PyArg_ParseTuple(args, "|s*:" #NAME , &view)) { \
|
||||
- return NULL; \
|
||||
- } \
|
||||
- \
|
||||
- ret_obj = EVPnew( \
|
||||
- CONST_ ## NAME ## _name_obj, \
|
||||
- NULL, \
|
||||
- CONST_new_ ## NAME ## _ctx_p, \
|
||||
- (unsigned char*)view.buf, view.len); \
|
||||
- PyBuffer_Release(&view); \
|
||||
- return ret_obj; \
|
||||
+ return implement_specific_EVP_new(self, args, kwdict, \
|
||||
+ "|s*i:" #NAME, \
|
||||
+ &cached_info_ ## NAME ); \
|
||||
}
|
||||
|
||||
+static PyObject *
|
||||
+implement_specific_EVP_new(PyObject *self, PyObject *args, PyObject *kwdict,
|
||||
+ const char *format,
|
||||
+ EVPCachedInfo *cached_info)
|
||||
+{
|
||||
+ static char *kwlist[] = {"string", "usedforsecurity", NULL};
|
||||
+ Py_buffer view = { 0 };
|
||||
+ int usedforsecurity = 1;
|
||||
+ int idx;
|
||||
+ PyObject *ret_obj = NULL;
|
||||
+
|
||||
+ assert(cached_info);
|
||||
+
|
||||
+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, format, kwlist,
|
||||
+ &view, &usedforsecurity)) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ idx = usedforsecurity ? 1 : 0;
|
||||
+
|
||||
+ /*
|
||||
+ * If an error occurred during creation of the global content, the ctx_ptr
|
||||
+ * will be NULL, and the error_msg will hopefully be non-NULL:
|
||||
+ */
|
||||
+ if (cached_info->ctx_ptrs[idx]) {
|
||||
+ /* We successfully initialized this context; copy it: */
|
||||
+ ret_obj = EVPnew(cached_info->name_obj,
|
||||
+ NULL,
|
||||
+ cached_info->ctx_ptrs[idx],
|
||||
+ (unsigned char*)view.buf, view.len,
|
||||
+ usedforsecurity);
|
||||
+ } else {
|
||||
+ /* Some kind of error happened initializing the global context for
|
||||
+ this (digest, usedforsecurity) pair.
|
||||
+ Raise an exception with the saved error message: */
|
||||
+ if (cached_info->error_msgs[idx]) {
|
||||
+ PyErr_SetObject(PyExc_ValueError, cached_info->error_msgs[idx]);
|
||||
+ } else {
|
||||
+ PyErr_SetString(PyExc_ValueError, "Error initializing hash");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ PyBuffer_Release(&view);
|
||||
+
|
||||
+ return ret_obj;
|
||||
+}
|
||||
+
|
||||
/* a PyMethodDef structure for the constructor */
|
||||
#define CONSTRUCTOR_METH_DEF(NAME) \
|
||||
- {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \
|
||||
+ {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, \
|
||||
+ METH_VARARGS |METH_KEYWORDS, \
|
||||
PyDoc_STR("Returns a " #NAME \
|
||||
" hash object; optionally initialized with a string") \
|
||||
}
|
||||
|
||||
-/* used in the init function to setup a constructor: initialize OpenSSL
|
||||
- constructor constants if they haven't been initialized already. */
|
||||
-#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \
|
||||
- if (CONST_ ## NAME ## _name_obj == NULL) { \
|
||||
- CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \
|
||||
- if (EVP_get_digestbyname(#NAME)) { \
|
||||
- CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \
|
||||
- EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \
|
||||
- } \
|
||||
- } \
|
||||
+/*
|
||||
+ Macro/function pair to set up the constructors.
|
||||
+
|
||||
+ Try to initialize a context for each hash twice, once with
|
||||
+ EVP_MD_CTX_FLAG_NON_FIPS_ALLOW and once without.
|
||||
+
|
||||
+ Any that have errors during initialization will end up wit a NULL ctx_ptrs
|
||||
+ entry, and err_msgs will be set (unless we're very low on memory)
|
||||
+*/
|
||||
+#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \
|
||||
+ init_constructor_constant(&cached_info_ ## NAME, #NAME); \
|
||||
} while (0);
|
||||
|
||||
+static void
|
||||
+init_constructor_constant(EVPCachedInfo *cached_info, const char *name)
|
||||
+{
|
||||
+ assert(cached_info);
|
||||
+ cached_info->name_obj = PyString_FromString(name);
|
||||
+ if (EVP_get_digestbyname(name)) {
|
||||
+ int i;
|
||||
+ for (i=0; i<2; i++) {
|
||||
+ mc_ctx_init(&cached_info->ctxs[i], i);
|
||||
+ if (EVP_DigestInit_ex(&cached_info->ctxs[i],
|
||||
+ EVP_get_digestbyname(name), NULL)) {
|
||||
+ /* Success: */
|
||||
+ cached_info->ctx_ptrs[i] = &cached_info->ctxs[i];
|
||||
+ } else {
|
||||
+ /* Failure: */
|
||||
+ cached_info->ctx_ptrs[i] = NULL;
|
||||
+ cached_info->error_msgs[i] = error_msg_for_last_error();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
GEN_CONSTRUCTOR(md5)
|
||||
GEN_CONSTRUCTOR(sha1)
|
||||
#ifdef _OPENSSL_SUPPORTS_SHA2
|
||||
@@ -794,14 +926,11 @@
|
||||
{
|
||||
PyObject *m, *openssl_md_meth_names;
|
||||
|
||||
+ SSL_load_error_strings();
|
||||
+ SSL_library_init();
|
||||
OpenSSL_add_all_digests();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
- /* TODO build EVP_functions openssl_* entries dynamically based
|
||||
- * on what hashes are supported rather than listing many
|
||||
- * but having some be unsupported. Only init appropriate
|
||||
- * constants. */
|
||||
-
|
||||
Py_TYPE(&EVPtype) = &PyType_Type;
|
||||
if (PyType_Ready(&EVPtype) < 0)
|
||||
return;
|
@ -1,41 +0,0 @@
|
||||
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
|
||||
--- a/Lib/multiprocessing/connection.py
|
||||
+++ b/Lib/multiprocessing/connection.py
|
||||
@@ -41,6 +41,10 @@
|
||||
# A very generous timeout when it comes to local connections...
|
||||
CONNECTION_TIMEOUT = 20.
|
||||
|
||||
+# The hmac module implicitly defaults to using MD5.
|
||||
+# Support using a stronger algorithm for the challenge/response code:
|
||||
+HMAC_DIGEST_NAME='sha256'
|
||||
+
|
||||
_mmap_counter = itertools.count()
|
||||
|
||||
default_family = 'AF_INET'
|
||||
@@ -700,12 +704,16 @@
|
||||
WELCOME = b'#WELCOME#'
|
||||
FAILURE = b'#FAILURE#'
|
||||
|
||||
+def get_digestmod_for_hmac():
|
||||
+ import hashlib
|
||||
+ return getattr(hashlib, HMAC_DIGEST_NAME)
|
||||
+
|
||||
def deliver_challenge(connection, authkey):
|
||||
import hmac
|
||||
assert isinstance(authkey, bytes)
|
||||
message = os.urandom(MESSAGE_LENGTH)
|
||||
connection.send_bytes(CHALLENGE + message)
|
||||
- digest = hmac.new(authkey, message).digest()
|
||||
+ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest()
|
||||
response = connection.recv_bytes(256) # reject large message
|
||||
if response == digest:
|
||||
connection.send_bytes(WELCOME)
|
||||
@@ -719,7 +727,7 @@
|
||||
message = connection.recv_bytes(256) # reject large message
|
||||
assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
|
||||
message = message[len(CHALLENGE):]
|
||||
- digest = hmac.new(authkey, message).digest()
|
||||
+ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest()
|
||||
connection.send_bytes(digest)
|
||||
response = connection.recv_bytes(256) # reject large message
|
||||
if response != WELCOME:
|
@ -1,249 +0,0 @@
|
||||
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
|
||||
index 5021ebf..63c763a 100644
|
||||
--- a/Lib/ensurepip/__init__.py
|
||||
+++ b/Lib/ensurepip/__init__.py
|
||||
@@ -7,6 +7,7 @@ import pkgutil
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
+from ensurepip import rewheel
|
||||
|
||||
|
||||
__all__ = ["version", "bootstrap"]
|
||||
@@ -29,6 +30,8 @@ def _run_pip(args, additional_paths=None):
|
||||
|
||||
# Install the bundled software
|
||||
import pip._internal
|
||||
+ if args[0] in ["install", "list", "wheel"]:
|
||||
+ args.append('--pre')
|
||||
return pip._internal.main(args)
|
||||
|
||||
|
||||
@@ -93,21 +96,40 @@ def _bootstrap(root=None, upgrade=False, user=False,
|
||||
# omit pip and easy_install
|
||||
os.environ["ENSUREPIP_OPTIONS"] = "install"
|
||||
|
||||
+ whls = []
|
||||
+ rewheel_dir = None
|
||||
+ # try to see if we have system-wide versions of _PROJECTS
|
||||
+ dep_records = rewheel.find_system_records([p[0] for p in _PROJECTS])
|
||||
+ # TODO: check if system-wide versions are the newest ones
|
||||
+ # if --upgrade is used?
|
||||
+ if all(dep_records):
|
||||
+ # if we have all _PROJECTS installed system-wide, we'll recreate
|
||||
+ # wheels from them and install those
|
||||
+ rewheel_dir = tempfile.mkdtemp()
|
||||
+ for dr in dep_records:
|
||||
+ new_whl = rewheel.rewheel_from_record(dr, rewheel_dir)
|
||||
+ whls.append(os.path.join(rewheel_dir, new_whl))
|
||||
+ else:
|
||||
+ # if we don't have all the _PROJECTS installed system-wide,
|
||||
+ # let's just fall back to bundled wheels
|
||||
+ for project, version in _PROJECTS:
|
||||
+ whl = os.path.join(
|
||||
+ os.path.dirname(__file__),
|
||||
+ "_bundled",
|
||||
+ "{}-{}-py2.py3-none-any.whl".format(project, version)
|
||||
+ )
|
||||
+ whls.append(whl)
|
||||
+
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
try:
|
||||
# Put our bundled wheels into a temporary directory and construct the
|
||||
# additional paths that need added to sys.path
|
||||
additional_paths = []
|
||||
- for project, version in _PROJECTS:
|
||||
- wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
|
||||
- whl = pkgutil.get_data(
|
||||
- "ensurepip",
|
||||
- "_bundled/{}".format(wheel_name),
|
||||
- )
|
||||
- with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
|
||||
- fp.write(whl)
|
||||
-
|
||||
- additional_paths.append(os.path.join(tmpdir, wheel_name))
|
||||
+ for whl in whls:
|
||||
+ shutil.copy(whl, tmpdir)
|
||||
+ additional_paths.append(os.path.join(tmpdir, os.path.basename(whl)))
|
||||
+ if rewheel_dir:
|
||||
+ shutil.rmtree(rewheel_dir)
|
||||
|
||||
# Construct the arguments to be passed to the pip command
|
||||
args = ["install", "--no-index", "--find-links", tmpdir]
|
||||
diff --git a/Lib/ensurepip/rewheel/__init__.py b/Lib/ensurepip/rewheel/__init__.py
|
||||
new file mode 100644
|
||||
index 0000000..75c2094
|
||||
--- /dev/null
|
||||
+++ b/Lib/ensurepip/rewheel/__init__.py
|
||||
@@ -0,0 +1,158 @@
|
||||
+import argparse
|
||||
+import codecs
|
||||
+import csv
|
||||
+import email.parser
|
||||
+import os
|
||||
+import io
|
||||
+import re
|
||||
+import site
|
||||
+import subprocess
|
||||
+import sys
|
||||
+import zipfile
|
||||
+
|
||||
+def run():
|
||||
+ parser = argparse.ArgumentParser(description='Recreate wheel of package with given RECORD.')
|
||||
+ parser.add_argument('record_path',
|
||||
+ help='Path to RECORD file')
|
||||
+ parser.add_argument('-o', '--output-dir',
|
||||
+ help='Dir where to place the wheel, defaults to current working dir.',
|
||||
+ dest='outdir',
|
||||
+ default=os.path.curdir)
|
||||
+
|
||||
+ ns = parser.parse_args()
|
||||
+ retcode = 0
|
||||
+ try:
|
||||
+ print(rewheel_from_record(**vars(ns)))
|
||||
+ except BaseException as e:
|
||||
+ print('Failed: {}'.format(e))
|
||||
+ retcode = 1
|
||||
+ sys.exit(1)
|
||||
+
|
||||
+def find_system_records(projects):
|
||||
+ """Return list of paths to RECORD files for system-installed projects.
|
||||
+
|
||||
+ If a project is not installed, the resulting list contains None instead
|
||||
+ of a path to its RECORD
|
||||
+ """
|
||||
+ records = []
|
||||
+ # get system site-packages dirs
|
||||
+ if hasattr(sys, 'real_prefix'):
|
||||
+ #we are in python2 virtualenv and sys.real_prefix is the original sys.prefix
|
||||
+ _orig_prefixes = site.PREFIXES
|
||||
+ setattr(site, 'PREFIXES', [sys.real_prefix]*2)
|
||||
+ sys_sitepack = site.getsitepackages()
|
||||
+ setattr(site, 'PREFIXES', _orig_prefixes)
|
||||
+ elif hasattr(sys, 'base_prefix'): # python3 venv doesn't inject real_prefix to sys
|
||||
+ # we are on python3 and base(_exec)_prefix is unchanged in venv
|
||||
+ sys_sitepack = site.getsitepackages([sys.base_prefix, sys.base_exec_prefix])
|
||||
+ else:
|
||||
+ # we are in python2 without virtualenv
|
||||
+ sys_sitepack = site.getsitepackages()
|
||||
+
|
||||
+ sys_sitepack = [sp for sp in sys_sitepack if os.path.exists(sp)]
|
||||
+ # try to find all projects in all system site-packages
|
||||
+ for project in projects:
|
||||
+ path = None
|
||||
+ for sp in sys_sitepack:
|
||||
+ dist_info_re = os.path.join(sp, project) + '-[^\{0}]+\.dist-info'.format(os.sep)
|
||||
+ candidates = [os.path.join(sp, p) for p in os.listdir(sp)]
|
||||
+ # filter out candidate dirs based on the above regexp
|
||||
+ filtered = [c for c in candidates if re.match(dist_info_re, c)]
|
||||
+ # if we have 0 or 2 or more dirs, something is wrong...
|
||||
+ if len(filtered) == 1:
|
||||
+ path = filtered[0]
|
||||
+ if path is not None:
|
||||
+ records.append(os.path.join(path, 'RECORD'))
|
||||
+ else:
|
||||
+ records.append(None)
|
||||
+ return records
|
||||
+
|
||||
+def rewheel_from_record(record_path, outdir):
|
||||
+ """Recreates a whee of package with given record_path and returns path
|
||||
+ to the newly created wheel."""
|
||||
+ site_dir = os.path.dirname(os.path.dirname(record_path))
|
||||
+ record_relpath = record_path[len(site_dir):].strip(os.path.sep)
|
||||
+ to_write, to_omit = get_records_to_pack(site_dir, record_relpath)
|
||||
+ new_wheel_name = get_wheel_name(record_path)
|
||||
+ new_wheel_path = os.path.join(outdir, new_wheel_name + '.whl')
|
||||
+
|
||||
+ new_wheel = zipfile.ZipFile(new_wheel_path, mode='w', compression=zipfile.ZIP_DEFLATED)
|
||||
+ # we need to write a new record with just the files that we will write,
|
||||
+ # e.g. not binaries and *.pyc/*.pyo files
|
||||
+ if sys.version_info[0] < 3:
|
||||
+ new_record = io.BytesIO()
|
||||
+ else:
|
||||
+ new_record = io.StringIO()
|
||||
+ writer = csv.writer(new_record)
|
||||
+
|
||||
+ # handle files that we can write straight away
|
||||
+ for f, sha_hash, size in to_write:
|
||||
+ new_wheel.write(os.path.join(site_dir, f), arcname=f)
|
||||
+ writer.writerow([f, sha_hash,size])
|
||||
+
|
||||
+ # rewrite the old wheel file with a new computed one
|
||||
+ writer.writerow([record_relpath, '', ''])
|
||||
+ new_wheel.writestr(record_relpath, new_record.getvalue())
|
||||
+
|
||||
+ new_wheel.close()
|
||||
+
|
||||
+ return new_wheel.filename
|
||||
+
|
||||
+def get_wheel_name(record_path):
|
||||
+ """Return proper name of the wheel, without .whl."""
|
||||
+
|
||||
+ wheel_info_path = os.path.join(os.path.dirname(record_path), 'WHEEL')
|
||||
+ with codecs.open(wheel_info_path, encoding='utf-8') as wheel_info_file:
|
||||
+ wheel_info = email.parser.Parser().parsestr(wheel_info_file.read().encode('utf-8'))
|
||||
+
|
||||
+ metadata_path = os.path.join(os.path.dirname(record_path), 'METADATA')
|
||||
+ with codecs.open(metadata_path, encoding='utf-8') as metadata_file:
|
||||
+ metadata = email.parser.Parser().parsestr(metadata_file.read().encode('utf-8'))
|
||||
+
|
||||
+ # construct name parts according to wheel spec
|
||||
+ distribution = metadata.get('Name')
|
||||
+ version = metadata.get('Version')
|
||||
+ build_tag = '' # nothing for now
|
||||
+ lang_tag = []
|
||||
+ for t in wheel_info.get_all('Tag'):
|
||||
+ lang_tag.append(t.split('-')[0])
|
||||
+ lang_tag = '.'.join(lang_tag)
|
||||
+ abi_tag, plat_tag = wheel_info.get('Tag').split('-')[1:3]
|
||||
+ # leave out build tag, if it is empty
|
||||
+ to_join = filter(None, [distribution, version, build_tag, lang_tag, abi_tag, plat_tag])
|
||||
+ return '-'.join(list(to_join))
|
||||
+
|
||||
+def get_records_to_pack(site_dir, record_relpath):
|
||||
+ """Accepts path of sitedir and path of RECORD file relative to it.
|
||||
+ Returns two lists:
|
||||
+ - list of files that can be written to new RECORD straight away
|
||||
+ - list of files that shouldn't be written or need some processing
|
||||
+ (pyc and pyo files, scripts)
|
||||
+ """
|
||||
+ record_file_path = os.path.join(site_dir, record_relpath)
|
||||
+ with codecs.open(record_file_path, encoding='utf-8') as record_file:
|
||||
+ record_contents = record_file.read()
|
||||
+ # temporary fix for https://github.com/pypa/pip/issues/1376
|
||||
+ # we need to ignore files under ".data" directory
|
||||
+ data_dir = os.path.dirname(record_relpath).strip(os.path.sep)
|
||||
+ data_dir = data_dir[:-len('dist-info')] + 'data'
|
||||
+
|
||||
+ to_write = []
|
||||
+ to_omit = []
|
||||
+ for l in record_contents.splitlines():
|
||||
+ spl = l.split(',')
|
||||
+ if len(spl) == 3:
|
||||
+ # new record will omit (or write differently):
|
||||
+ # - abs paths, paths with ".." (entry points),
|
||||
+ # - pyc+pyo files
|
||||
+ # - the old RECORD file
|
||||
+ # TODO: is there any better way to recognize an entry point?
|
||||
+ if os.path.isabs(spl[0]) or spl[0].startswith('..') or \
|
||||
+ spl[0].endswith('.pyc') or spl[0].endswith('.pyo') or \
|
||||
+ spl[0] == record_relpath or spl[0].startswith(data_dir):
|
||||
+ to_omit.append(spl)
|
||||
+ else:
|
||||
+ to_write.append(spl)
|
||||
+ else:
|
||||
+ pass # bad RECORD or empty line
|
||||
+ return to_write, to_omit
|
||||
diff --git a/Makefile.pre.in b/Makefile.pre.in
|
||||
index 877698c..2c43611 100644
|
||||
--- a/Makefile.pre.in
|
||||
+++ b/Makefile.pre.in
|
||||
@@ -1065,7 +1065,7 @@ LIBSUBDIRS= lib-tk lib-tk/test lib-tk/test/test_tkinter \
|
||||
test/tracedmodules \
|
||||
encodings compiler hotshot \
|
||||
email email/mime email/test email/test/data \
|
||||
- ensurepip ensurepip/_bundled \
|
||||
+ ensurepip ensurepip/_bundled ensurepip/rewheel\
|
||||
json json/tests \
|
||||
sqlite3 sqlite3/test \
|
||||
logging bsddb bsddb/test csv importlib wsgiref \
|
@ -1,52 +0,0 @@
|
||||
--- Python-2.7.15-orig/Python/pythonrun.c
|
||||
+++ Python-2.7.15/Python/pythonrun.c
|
||||
@@ -180,6 +182,49 @@
|
||||
char buf[128];
|
||||
#endif
|
||||
extern void _Py_ReadyTypes(void);
|
||||
+ char *py2_allow_flag = getenv("RHEL_ALLOW_PYTHON2_FOR_BUILD");
|
||||
+
|
||||
+ // Fail unless a specific workaround is applied
|
||||
+ if ((!py2_allow_flag || strcmp(py2_allow_flag, "1") != 0)
|
||||
+ && (strstr(Py_GetProgramName(), "for-tests") == NULL)
|
||||
+ ) {
|
||||
+ fprintf(stderr,
|
||||
+ "\n"
|
||||
+ "ERROR: Python 2 is disabled in RHEL8.\n"
|
||||
+ "\n"
|
||||
+ "- For guidance on porting to Python 3, see the\n"
|
||||
+ " Conservative Python3 Porting Guide:\n"
|
||||
+ " http://portingguide.readthedocs.io/\n"
|
||||
+ "\n"
|
||||
+ "- If you need Python 2 at runtime:\n"
|
||||
+ " - Use the python27 module\n"
|
||||
+ "\n"
|
||||
+ "- If you do not have access to BZ#1533919:\n"
|
||||
+ " - Use the python27 module\n"
|
||||
+ "\n"
|
||||
+ "- If you need to use Python 2 only at RPM build time:\n"
|
||||
+ " - File a bug blocking BZ#1533919:\n"
|
||||
+ " https://bugzilla.redhat.com/show_bug.cgi?id=1533919\n"
|
||||
+ " - Set the environment variable RHEL_ALLOW_PYTHON2_FOR_BUILD=1\n"
|
||||
+ " (Note that if you do not file the bug as above,\n"
|
||||
+ " this workaround will break without warning in the future.)\n"
|
||||
+ "\n"
|
||||
+ "- If you need to use Python 2 only for tests:\n"
|
||||
+ " - File a bug blocking BZ#1533919:\n"
|
||||
+ " https://bugzilla.redhat.com/show_bug.cgi?id=1533919\n"
|
||||
+ " (If your test tool does not have a Bugzilla component,\n"
|
||||
+ " feel free to use `python2`.)\n"
|
||||
+ " - Use /usr/bin/python2-for-tests instead of python2 to run\n"
|
||||
+ " your tests.\n"
|
||||
+ " (Note that if you do not file the bug as above,\n"
|
||||
+ " this workaround will break without warning in the future.)\n"
|
||||
+ "\n"
|
||||
+ "For details, see https://hurl.corp.redhat.com/rhel8-py2\n"
|
||||
+ "\n"
|
||||
+ );
|
||||
+ fflush(stderr);
|
||||
+ Py_FatalError("Python 2 is disabled");
|
||||
+ }
|
||||
|
||||
if (initialized)
|
||||
return;
|
@ -1,21 +0,0 @@
|
||||
diff -urN Python-2.7.13/Modules/Setup.dist Python-2.7.13_modul/Modules/Setup.dist
|
||||
--- Python-2.7.13/Modules/Setup.dist 2017-04-21 14:57:13.767444374 +0200
|
||||
+++ Python-2.7.13_modul/Modules/Setup.dist 2017-04-21 14:56:49.658953833 +0200
|
||||
@@ -326,7 +326,7 @@
|
||||
# every system.
|
||||
|
||||
# *** Always uncomment this (leave the leading underscore in!):
|
||||
-_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT \
|
||||
+#_tkinter _tkinter.c tkappinit.c -DWITH_APPINIT \
|
||||
# *** Uncomment and edit to reflect where your Tcl/Tk libraries are:
|
||||
# -L/usr/local/lib \
|
||||
# *** Uncomment and edit to reflect where your Tcl/Tk headers are:
|
||||
@@ -345,7 +345,7 @@
|
||||
# *** Uncomment and edit for TOGL extension only:
|
||||
# -DWITH_TOGL togl.c \
|
||||
# *** Uncomment and edit to reflect your Tcl/Tk versions:
|
||||
- -ltk -ltcl \
|
||||
+# -ltk -ltcl \
|
||||
# *** Uncomment and edit to reflect where your X11 libraries are:
|
||||
# -L/usr/X11R6/lib \
|
||||
# *** Or uncomment this for Solaris:
|
1
dead.package
Normal file
1
dead.package
Normal file
@ -0,0 +1 @@
|
||||
python2 package is retired on branch c10s for CS-2551
|
@ -5,15 +5,17 @@
|
||||
# Note that the bcond macros are named for the CLI option they create.
|
||||
# "%%bcond_without" means "ENABLE by default and create a --without option"
|
||||
|
||||
# Ability to reuse RPM-installed pip using rewheel
|
||||
%bcond_with rewheel
|
||||
# Whether to use RPM build wheels from the python2-{pip,setuptools}-wheel package
|
||||
# Uses upstream bundled prebuilt wheels otherwise
|
||||
%bcond_without rpmwheels
|
||||
|
||||
# Extra build for debugging the interpreter or C-API extensions
|
||||
# (the -debug subpackages)
|
||||
%bcond_with debug_build
|
||||
%bcond_without debug_build
|
||||
|
||||
# Remove extra packages
|
||||
%bcond_with tk_and_tools
|
||||
# Only use this when bootstrapping python3
|
||||
# Needed to build setuptools for the first time
|
||||
%bcond_with python3_bootstrap
|
||||
|
||||
%global unicode ucs4
|
||||
|
||||
@ -46,7 +48,7 @@
|
||||
%global with_systemtap 1
|
||||
|
||||
# some arches don't have valgrind so we need to disable its support on them
|
||||
%ifarch %{valgrind_arches}
|
||||
%ifnarch s390 %{mips} riscv64
|
||||
%global with_valgrind 1
|
||||
%else
|
||||
%global with_valgrind 0
|
||||
@ -54,25 +56,16 @@
|
||||
|
||||
%global with_gdbm 1
|
||||
|
||||
%if 0%{?_module_build}
|
||||
%global with_valgrind 0
|
||||
%global with_systemtap 0
|
||||
|
||||
# (Don't) Run the test suite in %%check
|
||||
%bcond_with tests
|
||||
%else
|
||||
# Run the test suite in %%check
|
||||
%bcond_without tests
|
||||
%endif
|
||||
|
||||
# Disable automatic bytecompilation. The python2.7 binary is not yet
|
||||
# available in /usr/bin when Python is built. Also, the bytecompilation fails
|
||||
# on files that test invalid syntax.
|
||||
%undefine __brp_python_bytecompile
|
||||
# The above is broken now
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1597664
|
||||
# This is an older non-standard way to disable the brp script, as a workaround
|
||||
%undefine py_auto_byte_compile
|
||||
# This doesn't work now, see https://bugzilla.redhat.com/show_bug.cgi?id=1597664
|
||||
# %%undefine __brp_python_bytecompile
|
||||
# … and this is working workaround
|
||||
%define __brp_python_bytecompile %{nil}
|
||||
|
||||
# We need to get a newer configure generated out of configure.in for the following
|
||||
# patches:
|
||||
@ -110,12 +103,11 @@
|
||||
Summary: An interpreted, interactive, object-oriented programming language
|
||||
Name: %{python}
|
||||
# Remember to also rebase python2-docs when changing this:
|
||||
Version: 2.7.17
|
||||
Release: 1%{?dist}
|
||||
Version: 2.7.18
|
||||
Release: 11%{?dist}
|
||||
License: Python
|
||||
Group: Development/Languages
|
||||
Requires: %{python}-libs%{?_isa} = %{version}-%{release}
|
||||
Requires: %{python}-for-tests%{?_isa} = %{version}-%{release}
|
||||
Provides: python(abi) = %{pybasever}
|
||||
|
||||
|
||||
@ -126,11 +118,10 @@ Provides: python(abi) = %{pybasever}
|
||||
# (keep this list alphabetized)
|
||||
|
||||
BuildRequires: autoconf
|
||||
%if ! 0%{?_module_build}
|
||||
BuildRequires: bluez-libs-devel
|
||||
%endif
|
||||
BuildRequires: bzip2
|
||||
BuildRequires: bzip2-devel
|
||||
BuildRequires: git-core
|
||||
BuildRequires: glibc-devel
|
||||
BuildRequires: gmp-devel
|
||||
BuildRequires: libdb-devel
|
||||
@ -140,9 +131,7 @@ BuildRequires: openssl-devel
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: readline-devel
|
||||
BuildRequires: sqlite-devel
|
||||
%if %{with tk_and_tools}
|
||||
BuildRequires: tcl-devel
|
||||
%endif
|
||||
|
||||
# For the nis module
|
||||
BuildRequires: libnsl2-devel
|
||||
@ -158,10 +147,8 @@ BuildRequires: gcc-c++
|
||||
# ABI change without soname bump, reverted
|
||||
BuildRequires: gdbm-devel >= 1:1.13
|
||||
%endif
|
||||
%if %{with tk_and_tools}
|
||||
BuildRequires: libGL-devel
|
||||
BuildRequires: libX11-devel
|
||||
%endif #{with tk_and_tools}
|
||||
|
||||
%if 0%{?with_systemtap}
|
||||
BuildRequires: systemtap-sdt-devel
|
||||
@ -171,10 +158,8 @@ BuildRequires: systemtap-sdt-devel
|
||||
%endif # with_systemtap
|
||||
|
||||
BuildRequires: tar
|
||||
%if %{with tk_and_tools}
|
||||
BuildRequires: tix-devel
|
||||
BuildRequires: tk-devel
|
||||
%endif #{with tk_and_tools}
|
||||
|
||||
%if 0%{?with_valgrind}
|
||||
BuildRequires: valgrind-devel
|
||||
@ -182,16 +167,21 @@ BuildRequires: valgrind-devel
|
||||
|
||||
BuildRequires: zlib-devel
|
||||
|
||||
%if %{with rewheel}
|
||||
BuildRequires: python2-setuptools
|
||||
Requires: python2-setuptools
|
||||
%if %{with rpmwheels}
|
||||
BuildRequires: python2-setuptools-wheel
|
||||
BuildRequires: python2-pip-wheel
|
||||
%endif
|
||||
|
||||
%if ! 0%{?_module_build}
|
||||
BuildRequires: python2-pip
|
||||
Requires: python2-pip
|
||||
%endif # !module_build
|
||||
%endif # rewheel
|
||||
# Require alternatives version that implements the --keep-foreign flag
|
||||
Requires: alternatives >= 1.19.1-1
|
||||
Requires(post): alternatives >= 1.19.1-1
|
||||
Requires(postun): alternatives >= 1.19.1-1
|
||||
|
||||
# Previously, this was required for our rewheel patch to work.
|
||||
# This is technically no longer needed, but we keep it recommended
|
||||
# for the developer experience.
|
||||
Recommends: python2-setuptools
|
||||
Recommends: python2-pip
|
||||
|
||||
|
||||
# =======================
|
||||
@ -577,20 +567,15 @@ Patch144: 00144-no-gdbm.patch
|
||||
|
||||
# 00146 #
|
||||
# Support OpenSSL FIPS mode (e.g. when OPENSSL_FORCE_FIPS_MODE=1 is set)
|
||||
# - handle failures from OpenSSL (e.g. on attempts to use MD5 in a
|
||||
# - handle some failures from OpenSSL (e.g. on attempts to use MD5 in a
|
||||
# FIPS-enforcing environment)
|
||||
# - add a new "usedforsecurity" keyword argument to the various digest
|
||||
# algorithms in hashlib so that you can whitelist a callsite with
|
||||
# "usedforsecurity=False"
|
||||
# (sent upstream for python 3 as http://bugs.python.org/issue9216; this is a
|
||||
# backport to python 2.7; see RHEL6 patch 119)
|
||||
# - enforce usage of the _hashlib implementation: don't fall back to the _md5
|
||||
# and _sha* modules (leading to clearer error messages if fips selftests
|
||||
# fail)
|
||||
# - don't build the _md5 and _sha* modules; rely on the _hashlib implementation
|
||||
# of hashlib (for example, md5.py will use _hashlib's implementation of MD5,
|
||||
# if permitted by the FIPS setting)
|
||||
# (rhbz#563986)
|
||||
# Resolves: rhbz#1734126
|
||||
Patch146: 00146-hashlib-fips.patch
|
||||
|
||||
# 00147 #
|
||||
@ -630,14 +615,6 @@ Patch165: 00165-crypt-module-salt-backport.patch
|
||||
# Not yet sent upstream
|
||||
Patch167: 00167-disable-stack-navigation-tests-when-optimized-in-test_gdb.patch
|
||||
|
||||
# 00169 #
|
||||
# Use SHA-256 rather than implicitly using MD5 within the challenge handling
|
||||
# in multiprocessing.connection
|
||||
#
|
||||
# Sent upstream as http://bugs.python.org/issue17258
|
||||
# (rhbz#879695)
|
||||
Patch169: 00169-avoid-implicit-usage-of-md5-in-multiprocessing.patch
|
||||
|
||||
# 00170 #
|
||||
# In debug builds, try to print repr() when a C-level assert fails in the
|
||||
# garbage collector (typically indicating a reference-counting error
|
||||
@ -684,6 +661,11 @@ Patch185: 00185-urllib2-honors-noproxy-for-ftp.patch
|
||||
# symbol)
|
||||
Patch187: 00187-add-RPATH-to-pyexpat.patch
|
||||
|
||||
# 00189 #
|
||||
# Instead of bundled wheels, use our RPM packaged wheels from
|
||||
# /usr/share/python2-wheels
|
||||
Patch189: 00189-use-rpm-wheels.patch
|
||||
|
||||
# 00191 #
|
||||
# Disabling NOOP test as it fails without internet connection
|
||||
Patch191: 00191-disable-NOOP.patch
|
||||
@ -695,28 +677,136 @@ Patch191: 00191-disable-NOOP.patch
|
||||
# Patch provided by John C. Peterson
|
||||
Patch193: 00193-enable-loading-sqlite-extensions.patch
|
||||
|
||||
# 00198 #
|
||||
Patch198: 00198-add-rewheel-module.patch
|
||||
|
||||
# 00257 #
|
||||
# Python's threading library doesn't use the monotonic clock when handling wait timeouts,
|
||||
# so when the system clock is set backwards, the wait doesn't return after the timeout,
|
||||
# causing deadlocks.
|
||||
# This patch works around the issue.
|
||||
# Resolves: rhbz#1565560
|
||||
# Resolves: rhbz#1653754
|
||||
# DOWNSTREAM ONLY PATCH
|
||||
Patch257: 00257-threading-wait-clamp-remaining-time.patch
|
||||
|
||||
# 00288 #
|
||||
# Adds a warning when /usr/bin/python is invoked during rpmbuild
|
||||
# See https://fedoraproject.org/wiki/Changes/Avoid_usr_bin_python_in_RPM_Build
|
||||
Patch288: 00288-disable-python2.patch
|
||||
Patch288: 00288-ambiguous-python-version-rpmbuild-warn.patch
|
||||
|
||||
# 00289 #
|
||||
# Disable automatic detection for the nis module
|
||||
# (we handle it it in Setup.dist, see Patch0)
|
||||
Patch289: 00289-disable-nis-detection.patch
|
||||
|
||||
# 00351 #
|
||||
# Avoid infinite loop when reading specially crafted TAR files using the tarfile module
|
||||
# (CVE-2019-20907).
|
||||
# See: https://bugs.python.org/issue39017
|
||||
Patch351: 00351-cve-2019-20907-fix-infinite-loop-in-tarfile.patch
|
||||
|
||||
# 00354 #
|
||||
# Reject control chars in HTTP method in httplib.putrequest to prevent
|
||||
# HTTP header injection
|
||||
#
|
||||
# Backported from Python 3.5-3.10 (and adjusted for py2's single-module httplib):
|
||||
# - https://bugs.python.org/issue39603
|
||||
Patch354: 00354-cve-2020-26116-http-request-method-crlf-injection-in-httplib.patch
|
||||
|
||||
# 00355 #
|
||||
# No longer call eval() on content received via HTTP in the CJK codec tests
|
||||
# Backported from the python3 branches upstream: https://bugs.python.org/issue41944
|
||||
# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1889886
|
||||
Patch355: 00355-CVE-2020-27619.patch
|
||||
|
||||
# 00357 #
|
||||
# Security fix for CVE-2021-3177
|
||||
# Stack-based buffer overflow in PyCArg_repr in _ctypes/callproc.c
|
||||
# Backported from the upstream python3 branches: https://bugs.python.org/issue42938
|
||||
Patch357: 00357-CVE-2021-3177.patch
|
||||
|
||||
# 00359 #
|
||||
# CVE-2021-23336 python: Web Cache Poisoning via urllib.parse.parse_qsl and
|
||||
# urllib.parse.parse_qs by using a semicolon in query parameters
|
||||
# Upstream: https://bugs.python.org/issue42967
|
||||
# Main BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1928904
|
||||
Patch359: 00359-CVE-2021-23336.patch
|
||||
|
||||
# 00366 # e76b05ea3313854adf80e290c07d5b38fef606bb
|
||||
# CVE-2021-3733: Fix ReDoS in urllib AbstractBasicAuthHandler
|
||||
#
|
||||
# Fix Regular Expression Denial of Service (ReDoS) vulnerability in
|
||||
# urllib2.AbstractBasicAuthHandler. The ReDoS-vulnerable regex
|
||||
# has quadratic worst-case complexity and it allows cause a denial of
|
||||
# service when identifying crafted invalid RFCs. This ReDoS issue is on
|
||||
# the client side and needs remote attackers to control the HTTP server.
|
||||
#
|
||||
# Backported from Python 3 together with another backward-compatible
|
||||
# improvement of the regex from fix for CVE-2020-8492.
|
||||
#
|
||||
# Upstream: https://bugs.python.org/issue43075
|
||||
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=1995234
|
||||
Patch366: 00366-CVE-2021-3733.patch
|
||||
|
||||
# 00368 # 10dcf6732fb101ce89ad506a89365c6b1ff8c4e4
|
||||
# CVE-2021-3737: http client infinite line reading (DoS) after a HTTP 100 Continue
|
||||
#
|
||||
# Fixes http.client potential denial of service where it could get stuck reading
|
||||
# lines from a malicious server after a 100 Continue response.
|
||||
#
|
||||
# Backported from Python 3.
|
||||
#
|
||||
# Upstream: https://bugs.python.org/issue44022
|
||||
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=1995162
|
||||
Patch368: 00368-CVE-2021-3737.patch
|
||||
|
||||
# 00372 #
|
||||
# CVE-2021-4189: ftplib should not use the host from the PASV response
|
||||
# Upstream: https://bugs.python.org/issue43285
|
||||
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=2036020
|
||||
Patch372: 00372-CVE-2021-4189.patch
|
||||
|
||||
# 00377 #
|
||||
# CVE-2022-0391: urlparse does not sanitize URLs containing ASCII newline and tabs
|
||||
#
|
||||
# ASCII newline and tab characters are stripped from the URL.
|
||||
#
|
||||
# Backported from Python 3.
|
||||
#
|
||||
# Upstream: https://bugs.python.org/issue43882
|
||||
# Tracking bug: https://bugzilla.redhat.com/show_bug.cgi?id=2047376
|
||||
Patch377: 00377-CVE-2022-0391.patch
|
||||
|
||||
# 00378 #
|
||||
# Support expat 2.4.5
|
||||
#
|
||||
# Curly brackets were never allowed in namespace URIs
|
||||
# according to RFC 3986, and so-called namespace-validating
|
||||
# XML parsers have the right to reject them a invalid URIs.
|
||||
#
|
||||
# libexpat >=2.4.5 has become strcter in that regard due to
|
||||
# related security issues; with ET.XML instantiating a
|
||||
# namespace-aware parser under the hood, this test has no
|
||||
# future in CPython.
|
||||
#
|
||||
# References:
|
||||
# - https://datatracker.ietf.org/doc/html/rfc3968
|
||||
# - https://www.w3.org/TR/xml-names/
|
||||
#
|
||||
# Also, test_minidom.py: Support Expat >=2.4.5
|
||||
#
|
||||
# Upstream: https://bugs.python.org/issue46811
|
||||
#
|
||||
# Backported from Python 3.
|
||||
Patch378: 00378-support-expat-2-4-5.patch
|
||||
|
||||
# 00382 #
|
||||
# Make mailcap refuse to match unsafe filenames/types/params (GH-91993)
|
||||
#
|
||||
# Upstream: https://github.com/python/cpython/issues/68966
|
||||
#
|
||||
# Tracker bug: https://bugzilla.redhat.com/show_bug.cgi?id=2075390
|
||||
#
|
||||
# Backported from python3.
|
||||
Patch382: 00382-cve-2015-20107.patch
|
||||
|
||||
# (New patches go here ^^^)
|
||||
#
|
||||
# When adding new patches to "python2" and "python3" in Fedora, EL, etc.,
|
||||
@ -730,9 +820,6 @@ Patch289: 00289-disable-nis-detection.patch
|
||||
# %%{regenerate_autotooling_patch}
|
||||
# above:
|
||||
|
||||
# Disable tk for modularity builds to break up build dependencies
|
||||
Patch04000: 04000-modularity-disable-tk.patch
|
||||
|
||||
Patch5000: 05000-autotool-intermediates.patch
|
||||
|
||||
# ======================================================
|
||||
@ -743,6 +830,10 @@ Patch5000: 05000-autotool-intermediates.patch
|
||||
# alongside the system one e.g. python26, python33 etc
|
||||
Provides: python27 = %{version}-%{release}
|
||||
|
||||
# When the user tries to `yum install python`, yum will list this package among
|
||||
# the possible alternatives
|
||||
Provides: alternative-for(python)
|
||||
|
||||
|
||||
URL: https://www.python.org/
|
||||
|
||||
@ -758,19 +849,7 @@ package.
|
||||
|
||||
This package provides the "python2" executable; most of the actual
|
||||
implementation is within the "python2-libs" package.
|
||||
|
||||
%package for-tests
|
||||
Summary: The python2-for-tests-command
|
||||
Requires: %{python}-libs%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description for-tests
|
||||
This package provides the "python2-for-tests" executable, a working
|
||||
Python 2 interpreter intended for use only in test harnesses that
|
||||
were not ported to Python 3 yet.
|
||||
|
||||
Install this package, but not "python2", to approximate a system that
|
||||
lacks Python 2 entirely.
|
||||
|
||||
For the unversioned "python" executable, see manual page "unversioned-python".
|
||||
|
||||
%package libs
|
||||
Summary: Runtime libraries for Python 2
|
||||
@ -793,8 +872,13 @@ Requires: glibc%{?_isa} >= 2.24.90-26
|
||||
Requires: gdbm%{?_isa} >= 1:1.13
|
||||
%endif
|
||||
|
||||
Provides: python-libs = %{version}-%{release}
|
||||
Provides: python-libs%{?_isa} = %{version}-%{release}
|
||||
%if %{with rpmwheels}
|
||||
Requires: python2-setuptools-wheel
|
||||
Requires: python2-pip-wheel
|
||||
%else
|
||||
Provides: bundled(python2-pip) = 18.1
|
||||
Provides: bundled(python2-setuptools) = 40.6.2
|
||||
%endif
|
||||
|
||||
%description libs
|
||||
This package contains files used to embed Python 2 into applications.
|
||||
@ -805,9 +889,33 @@ Group: Development/Libraries
|
||||
Requires: %{python}%{?_isa} = %{version}-%{release}
|
||||
Requires: python-rpm-macros
|
||||
Requires: python2-rpm-macros
|
||||
Requires: python3-rpm-generators
|
||||
Requires: pkgconfig
|
||||
|
||||
%if %{without python3_bootstrap}
|
||||
# When bootstrapping python3, we need to build setuptools
|
||||
# But setuptools BR python2-devel and that brings in python3-rpm-generators
|
||||
# python3-rpm-generators needs python3-setuptools, so we cannot have it yet
|
||||
Requires: python3-rpm-generators
|
||||
%endif
|
||||
|
||||
# This is not "API" (packages that need setuptools should still BuildRequire it)
|
||||
# However some packages apparently can build both with and without setuptools
|
||||
# producing egg-info as file or directory (depending on setuptools presence).
|
||||
# Directory-to-file updates are problematic in RPM, so we ensure setuptools is
|
||||
# installed when -devel is required.
|
||||
# See https://bugzilla.redhat.com/show_bug.cgi?id=1623922
|
||||
# See https://fedoraproject.org/wiki/Packaging:Directory_Replacement
|
||||
#
|
||||
# This is not necessary when rebuilding when we're bundling the python2 stack
|
||||
# into a Flatpak containe with prefix=/app, because we never upgrade packages
|
||||
# in the Flatpak context. We want to avoid
|
||||
# python2-setuptools => BuildRequires => python2-devel => Requires python2-setuptools
|
||||
# since the old python2-setuptools will be the /usr version not the /app version.
|
||||
|
||||
%if !0%{?flatpak}
|
||||
Requires: python2-setuptools
|
||||
%endif
|
||||
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1217376
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1496757
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1218294
|
||||
@ -818,23 +926,16 @@ Requires: redhat-rpm-config
|
||||
# package
|
||||
Conflicts: %{python} < %{version}-%{release}
|
||||
|
||||
Provides: python-devel = %{version}-%{release}
|
||||
Provides: python-devel%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description devel
|
||||
This package contains libraries and header files used to build applications
|
||||
with and native libraries for Python 2
|
||||
|
||||
%if %{with tk_and_tools}
|
||||
%package tools
|
||||
Summary: A collection of development tools included with Python 2
|
||||
Group: Development/Tools
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
Requires: %{python}-tkinter = %{version}-%{release}
|
||||
|
||||
Provides: python-tools = %{version}-%{release}
|
||||
Provides: python-tools%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description tools
|
||||
This package includes several tools to help with the development of Python 2
|
||||
programs, including IDLE (an IDE with editing and debugging facilities), a
|
||||
@ -845,12 +946,8 @@ Summary: A graphical user interface for the Python 2 scripting language
|
||||
Group: Development/Languages
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
|
||||
Provides: tkinter = %{version}-%{release}
|
||||
Provides: tkinter%{?_isa} = %{version}-%{release}
|
||||
Provides: tkinter2 = %{version}-%{release}
|
||||
Provides: tkinter2%{?_isa} = %{version}-%{release}
|
||||
Provides: python-tkinter = %{version}-%{release}
|
||||
Provides: python-tkinter%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description tkinter
|
||||
|
||||
@ -859,16 +956,12 @@ the Python 2 scripting language.
|
||||
|
||||
You should install the python2tkinter package if you'd like to use a graphical
|
||||
user interface for Python 2 programming.
|
||||
%endif %{with tk_and_tools}
|
||||
|
||||
%package test
|
||||
Summary: The test modules from the main python2 package
|
||||
Group: Development/Languages
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
|
||||
Provides: python-test = %{version}-%{release}
|
||||
Provides: python-test%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description test
|
||||
|
||||
The test modules from the main python2 package: %{name}
|
||||
@ -890,13 +983,8 @@ Requires: %{name}%{?_isa} = %{version}-%{release}
|
||||
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
|
||||
Requires: %{name}-devel%{?_isa} = %{version}-%{release}
|
||||
Requires: %{name}-test%{?_isa} = %{version}-%{release}
|
||||
%if %{with tk_and_tools}
|
||||
Requires: %{python}-tkinter%{?_isa} = %{version}-%{release}
|
||||
Requires: %{name}-tools%{?_isa} = %{version}-%{release}
|
||||
%endif #{with tk_and_tools}
|
||||
|
||||
Provides: python-debug = %{version}-%{release}
|
||||
Provides: python-debug%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description debug
|
||||
python2-debug provides a version of the Python 2 runtime with numerous debugging
|
||||
@ -1014,32 +1102,45 @@ rm -r Modules/zlib || exit 1
|
||||
%if !%{with_gdbm}
|
||||
%patch144 -p1
|
||||
%endif
|
||||
#patch146 -p1
|
||||
%patch146 -p1
|
||||
%patch147 -p1
|
||||
%patch155 -p1
|
||||
%patch156 -p1
|
||||
%patch165 -p1
|
||||
mv Modules/cryptmodule.c Modules/_cryptmodule.c
|
||||
%patch167 -p1
|
||||
%patch169 -p1
|
||||
%patch170 -p1
|
||||
%patch174 -p1 -b .fix-for-usr-move
|
||||
%patch180 -p1
|
||||
%patch181 -p1
|
||||
%patch185 -p1
|
||||
%patch187 -p1
|
||||
|
||||
%if %{with rpmwheels}
|
||||
%patch189 -p1
|
||||
rm Lib/ensurepip/_bundled/*.whl
|
||||
%endif
|
||||
|
||||
%patch191 -p1
|
||||
%patch193 -p1
|
||||
%if %{with rewheel}
|
||||
%patch198 -p1
|
||||
%endif
|
||||
%patch257 -p1
|
||||
%patch288 -p1
|
||||
%patch289 -p1
|
||||
|
||||
%if ! %{with tk_and_tools}
|
||||
%patch4000 -p1
|
||||
%endif
|
||||
# Patch 351 adds binary file for testing. We need to apply it using Git.
|
||||
git apply %{PATCH351}
|
||||
|
||||
%patch354 -p1
|
||||
%patch355 -p1
|
||||
%patch357 -p1
|
||||
%patch359 -p1
|
||||
%patch366 -p1
|
||||
%patch368 -p1
|
||||
%patch372 -p1
|
||||
%patch377 -p1
|
||||
%patch378 -p1
|
||||
%patch382 -p1
|
||||
|
||||
|
||||
# This shouldn't be necesarry, but is right now (2.2a3)
|
||||
find -name "*~" |xargs rm -f
|
||||
@ -1056,7 +1157,6 @@ find -name "*~" |xargs rm -f
|
||||
# ======================================================
|
||||
|
||||
%build
|
||||
export RHEL_ALLOW_PYTHON2_FOR_BUILD=1
|
||||
topdir=$(pwd)
|
||||
export CFLAGS="$RPM_OPT_FLAGS -D_GNU_SOURCE -fPIC -fwrapv"
|
||||
export CXXFLAGS="$RPM_OPT_FLAGS -D_GNU_SOURCE -fPIC -fwrapv"
|
||||
@ -1193,7 +1293,6 @@ BuildPython optimized \
|
||||
# ======================================================
|
||||
|
||||
%install
|
||||
export RHEL_ALLOW_PYTHON2_FOR_BUILD=1
|
||||
topdir=$(pwd)
|
||||
rm -rf %{buildroot}
|
||||
mkdir -p %{buildroot}%{_prefix} %{buildroot}%{_mandir}
|
||||
@ -1236,8 +1335,8 @@ make install DESTDIR=%{buildroot}
|
||||
# but doing so generated noise when ldconfig was rerun (rhbz:562980)
|
||||
#
|
||||
%if 0%{?with_gdb_hooks}
|
||||
DirHoldingGdbPy=%{_prefix}/lib/debug/%{_libdir}
|
||||
PathOfGdbPy=$DirHoldingGdbPy/$PyInstSoName.debug-gdb.py
|
||||
DirHoldingGdbPy=%{_usr}/lib/debug/%{_libdir}
|
||||
PathOfGdbPy=$DirHoldingGdbPy/$PyInstSoName-%{version}-%{release}.%{_arch}.debug-gdb.py
|
||||
|
||||
mkdir -p %{buildroot}$DirHoldingGdbPy
|
||||
cp $topdir/Tools/gdb/libpython.py %{buildroot}$PathOfGdbPy
|
||||
@ -1348,7 +1447,6 @@ ln -s ./pynche2 %{buildroot}%{_bindir}/pynche
|
||||
|
||||
mv %{buildroot}%{_bindir}/pydoc %{buildroot}%{_bindir}/pydoc%{pybasever}
|
||||
ln -s ./pydoc%{pybasever} %{buildroot}%{_bindir}/pydoc2
|
||||
ln -s ./pydoc2 %{buildroot}%{_bindir}/pydoc
|
||||
|
||||
mv %{buildroot}%{_bindir}/pygettext.py %{buildroot}%{_bindir}/pygettext%{pybasever}.py
|
||||
ln -s ./pygettext%{pybasever}.py %{buildroot}%{_bindir}/pygettext2.py
|
||||
@ -1479,19 +1577,34 @@ find %{buildroot} -type f -a -name "*.py" -print0 | \
|
||||
/usr/bin/chmod 755 %{buildroot}%{_libdir}/libpython%{pybasever}.so.1.0
|
||||
%if %{with debug_build}
|
||||
/usr/bin/chmod 755 %{buildroot}%{_libdir}/libpython%{pybasever}_d.so.1.0
|
||||
%endif # with debug_build
|
||||
%endif
|
||||
|
||||
# Add an executable for tests
|
||||
cp %{buildroot}%{_bindir}/python%{pybasever} %{buildroot}%{_bindir}/python2-for-tests
|
||||
# Remove unversioned executables
|
||||
# Remove pyc/pyo files from /usr/bin
|
||||
# They are not needed, and due to them, the resulting RPM is not multilib-clean
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1703575
|
||||
rm %{buildroot}%{_bindir}/*.py{c,o}
|
||||
|
||||
# Remove the /usr/bin/python executable so that the user can chose where it
|
||||
# points to by installing streams of the @python module
|
||||
rm %{buildroot}%{_bindir}/python
|
||||
# The same with the symlink to man page
|
||||
rm %{buildroot}%{_mandir}/man1/python.1
|
||||
|
||||
# Remove unversioned executables in /usr/bin/ so that there is no "default"
|
||||
# Python 2 or Python 3 version
|
||||
rm %{buildroot}%{_bindir}/idle
|
||||
rm %{buildroot}%{_bindir}/pydoc
|
||||
rm %{buildroot}%{_bindir}/msgfmt.py
|
||||
rm %{buildroot}%{_bindir}/pygettext.py
|
||||
rm %{buildroot}%{_bindir}/pynche
|
||||
rm %{buildroot}%{_bindir}/smtpd.py
|
||||
rm %{buildroot}%{_bindir}/python-config
|
||||
# Remove unversioned manpage
|
||||
rm %{buildroot}%{_mandir}/man1/python.*
|
||||
|
||||
# All ghost files controlled by alternatives need to exist for the files
|
||||
# section check to succeed
|
||||
# - Don't list /usr/bin/python as a ghost file so `yum install /usr/bin/python`
|
||||
# doesn't install this package
|
||||
touch %{buildroot}%{_bindir}/unversioned-python
|
||||
touch %{buildroot}%{_mandir}/man1/python.1.gz
|
||||
|
||||
|
||||
# ======================================================
|
||||
@ -1499,7 +1612,6 @@ rm %{buildroot}%{_mandir}/man1/python.*
|
||||
# ======================================================
|
||||
|
||||
%check
|
||||
export RHEL_ALLOW_PYTHON2_FOR_BUILD=1
|
||||
topdir=$(pwd)
|
||||
CheckPython() {
|
||||
ConfName=$1
|
||||
@ -1567,20 +1679,39 @@ CheckPython \
|
||||
# Cleaning up
|
||||
# ======================================================
|
||||
|
||||
%post
|
||||
# Alternative for /usr/bin/python -> /usr/bin/python2 + man page
|
||||
alternatives --install %{_bindir}/unversioned-python \
|
||||
python \
|
||||
%{_bindir}/python2 \
|
||||
200 \
|
||||
--slave %{_bindir}/python \
|
||||
unversioned-python \
|
||||
%{_bindir}/python2 \
|
||||
--slave %{_mandir}/man1/python.1.gz \
|
||||
unversioned-python-man \
|
||||
%{_mandir}/man1/python2.1.gz
|
||||
|
||||
%postun
|
||||
# Do this only during uninstall process (not during update)
|
||||
if [ $1 -eq 0 ]; then
|
||||
alternatives --keep-foreign --remove python \
|
||||
%{_bindir}/python2
|
||||
fi
|
||||
|
||||
%files
|
||||
%defattr(-, root, root, -)
|
||||
%{!?_licensedir:%global license %%doc}
|
||||
%license LICENSE
|
||||
%doc README
|
||||
%{_bindir}/pydoc*
|
||||
%{_bindir}/pydoc2*
|
||||
%{_bindir}/%{python}
|
||||
%{_bindir}/python%{pybasever}
|
||||
%{_mandir}/*/*
|
||||
%{_mandir}/man1/python2.1*
|
||||
%{_mandir}/man1/python2.7.1*
|
||||
%ghost %{_bindir}/unversioned-python
|
||||
%ghost %{_mandir}/man1/python.1*
|
||||
|
||||
%files for-tests
|
||||
%license LICENSE
|
||||
%{_bindir}/python2-for-tests
|
||||
|
||||
%files libs
|
||||
%defattr(-,root,root,-)
|
||||
@ -1681,7 +1812,6 @@ CheckPython \
|
||||
%{pylibdir}/distutils/*.py*
|
||||
%{pylibdir}/distutils/README
|
||||
%{pylibdir}/distutils/command
|
||||
%exclude %{pylibdir}/distutils/command/wininst-*.exe
|
||||
%dir %{pylibdir}/email
|
||||
%{pylibdir}/email/*.py*
|
||||
%{pylibdir}/email/mime
|
||||
@ -1734,11 +1864,11 @@ CheckPython \
|
||||
|
||||
%dir %{pylibdir}/ensurepip/
|
||||
%{pylibdir}/ensurepip/*.py*
|
||||
%if %{with rpmwheels}
|
||||
%exclude %{pylibdir}/ensurepip/_bundled
|
||||
|
||||
%if %{with rewheel}
|
||||
%dir %{pylibdir}/ensurepip/rewheel/
|
||||
%{pylibdir}/ensurepip/rewheel/*.py*
|
||||
%else
|
||||
%dir %{pylibdir}/ensurepip/_bundled
|
||||
%{pylibdir}/ensurepip/_bundled/*.whl
|
||||
%endif
|
||||
|
||||
|
||||
@ -1749,7 +1879,6 @@ CheckPython \
|
||||
%{_libdir}/pkgconfig/python2.pc
|
||||
%{pylibdir}/config/*
|
||||
%exclude %{pylibdir}/config/Makefile
|
||||
%exclude %{pylibdir}/distutils/command/wininst-*.exe
|
||||
%{_includedir}/python%{pybasever}/*.h
|
||||
%exclude %{_includedir}/python%{pybasever}/%{_pyconfig_h}
|
||||
%doc Misc/README.valgrind Misc/valgrind-python.supp Misc/gdbinit
|
||||
@ -1757,20 +1886,19 @@ CheckPython \
|
||||
%{_bindir}/python%{pybasever}-config
|
||||
%{_libdir}/libpython%{pybasever}.so
|
||||
|
||||
%if %{with tk_and_tools}
|
||||
%files tools
|
||||
%defattr(-,root,root,755)
|
||||
%doc Tools/pynche/README.pynche
|
||||
%{site_packages}/pynche
|
||||
%{_bindir}/smtpd*.py*
|
||||
%{_bindir}/smtpd2*.py*
|
||||
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1111275
|
||||
%exclude %{_bindir}/2to3*
|
||||
|
||||
%{_bindir}/idle*
|
||||
%{_bindir}/pynche*
|
||||
%{_bindir}/pygettext*.py*
|
||||
%{_bindir}/msgfmt*.py*
|
||||
%{_bindir}/idle2*
|
||||
%{_bindir}/pynche2*
|
||||
%{_bindir}/pygettext2*.py*
|
||||
%{_bindir}/msgfmt2*.py*
|
||||
%{tools_dir}
|
||||
%{demo_dir}
|
||||
%{pylibdir}/Doc
|
||||
@ -1780,27 +1908,6 @@ CheckPython \
|
||||
%{pylibdir}/lib-tk
|
||||
%{dynload_dir}/_tkinter.so
|
||||
|
||||
%else #{with tk_and_tools}
|
||||
|
||||
%exclude %{site_packages}/pynche
|
||||
%exclude %{_bindir}/smtpd*.py*
|
||||
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1111275
|
||||
%exclude %{_bindir}/2to3*
|
||||
|
||||
%exclude %{_bindir}/idle*
|
||||
%exclude %{_bindir}/pynche*
|
||||
%exclude %{_bindir}/pygettext*.py*
|
||||
%exclude %{_bindir}/msgfmt*.py*
|
||||
%exclude %{tools_dir}
|
||||
%exclude %{demo_dir}
|
||||
%exclude %{pylibdir}/Doc
|
||||
|
||||
%exclude %{pylibdir}/lib-tk
|
||||
%exclude %{dynload_dir}/_tkinter.so
|
||||
|
||||
%endif
|
||||
|
||||
%files test
|
||||
%defattr(-, root, root, -)
|
||||
%{pylibdir}/bsddb/test
|
||||
@ -1942,10 +2049,8 @@ CheckPython \
|
||||
# None for now; we could build precanned versions that have the appropriate
|
||||
# shebang if needed
|
||||
|
||||
%if ! 0%{?_module_build}
|
||||
# Analog of the tkinter subpackage's files:
|
||||
%{dynload_dir}/_tkinter_d.so
|
||||
%endif
|
||||
|
||||
# Analog of the -test subpackage's files:
|
||||
%{dynload_dir}/_ctypes_test_d.so
|
||||
@ -1974,58 +2079,148 @@ CheckPython \
|
||||
# ======================================================
|
||||
|
||||
%changelog
|
||||
* Fri Jun 17 2022 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-11
|
||||
- Security fix for CVE-2015-20107
|
||||
- Fix the test suite support for Expat >= 2.4.5
|
||||
Resolves: rhbz#2075390
|
||||
|
||||
* Tue Feb 08 2022 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-10
|
||||
- Security fix for CVE-2022-0391: urlparse does not sanitize URLs containing ASCII newline and tabs
|
||||
Resolves: rhbz#2047376
|
||||
|
||||
* Wed Jan 12 2022 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-9
|
||||
- Security fix for CVE-2021-4189: ftplib should not use the host from the PASV response
|
||||
Resolves: rhbz#2036020
|
||||
|
||||
* Tue Sep 21 2021 Lumír Balhar <lbalhar@redhat.com> - 2.7.18-8
|
||||
- Security fixes for CVE-2021-3737 and CVE-2021-3733
|
||||
Resolves: rhbz#1995162 and rhbz#1995234
|
||||
|
||||
* Thu Aug 05 2021 Tomas Orsava <torsava@redhat.com> - 2.7.18-7
|
||||
- Adjusted the postun scriptlets to enable upgrading to RHEL 9
|
||||
- Resolves: rhbz#1933055
|
||||
|
||||
* Wed May 12 2021 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-6
|
||||
- Security fix for CVE-2020-27619: eval() call on content received via HTTP in the CJK codec tests
|
||||
Resolves: rhbz#1889886
|
||||
|
||||
* Fri Apr 16 2021 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-5
|
||||
- Fix for CVE-2021-23336
|
||||
Resolves: rhbz#1928904
|
||||
|
||||
* Fri Jan 22 2021 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-4
|
||||
- Security fix for CVE-2021-3177
|
||||
Resolves: rhbz#1919163
|
||||
|
||||
* Wed Jan 13 2021 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-3
|
||||
- Fixes for bundling prefix=/app build in gimp/inkscape containers
|
||||
Resolves: rhbz#1907592
|
||||
|
||||
* Fri Oct 09 2020 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-2
|
||||
- Security fix for CVE-2020-26116: Reject control chars in HTTP method in httplib.putrequest
|
||||
Resolves: rhbz#1883258
|
||||
|
||||
* Fri Oct 09 2020 Charalampos Stratakis <cstratak@redhat.com> - 2.7.18-1
|
||||
- Update to 2.7.18
|
||||
Resolves: rhbz#1886754
|
||||
|
||||
* Mon Aug 17 2020 Tomas Orsava <torsava@redhat.com> - 2.7.17-2
|
||||
- Avoid infinite loop when reading specially crafted TAR files (CVE-2019-20907)
|
||||
Resolves: rhbz#1856481
|
||||
|
||||
* Wed Oct 23 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.17-1
|
||||
- Update to 2.7.17
|
||||
Resolves: rhbz#1759946
|
||||
Resolves: rhbz#1759944
|
||||
|
||||
* Tue Jun 11 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-8
|
||||
* Tue Sep 03 2019 Tomas Orsava <torsava@redhat.com> - 2.7.16-12
|
||||
- Adding FIPS compliance to Python 2 in RHEL8:
|
||||
- Updated patch 146 with a new version of the FIPS patch
|
||||
- Patch 169 has been removed, obsoleted by the updated patch 146
|
||||
Resolves: rhbz#1734126
|
||||
|
||||
* Tue Jul 02 2019 Miro Hrončok <mhroncok@redhat.com> - 2.7.16-11
|
||||
- Use RPM built wheels of pip and setuptools in ensurepip instead of our rewheel patch
|
||||
- Require python2-setuptools from python2-devel to prevent packaging errors
|
||||
Resolves: rhbz#1718398
|
||||
|
||||
* Tue Jun 11 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-10
|
||||
- Fix urlparse.urlsplit() error message for Unicode URL
|
||||
Resolves: rhbz#1689328
|
||||
Resolves: rhbz#1689327
|
||||
|
||||
* Fri Jun 07 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-7
|
||||
* Fri Jun 07 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-9
|
||||
- Security fix for CVE-2019-10160
|
||||
Resolves: rhbz#1689328
|
||||
Resolves: rhbz#1689327
|
||||
|
||||
* Thu May 30 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-6
|
||||
* Thu May 30 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-8
|
||||
- Security fix for CVE-2019-9948
|
||||
Resolves: rhbz#1704177
|
||||
Resolves: rhbz#1704176
|
||||
|
||||
* Thu May 30 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-5
|
||||
* Thu May 30 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-7
|
||||
- Disallow control chars in http URLs
|
||||
- Fixes CVE-2019-9740 and CVE-2019-9947
|
||||
Resolves: rhbz#1704369 and rhbz#1703537
|
||||
Resolves: rhbz#1703539 and rhbz#1704367
|
||||
|
||||
* Fri May 03 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-4
|
||||
* Tue May 21 2019 Tomas Orsava <torsava@redhat.com> - 2.7.16-6
|
||||
- Remove pyc/pyo files from /usr/bin
|
||||
Resolves: rhbz#1696741
|
||||
|
||||
* Fri May 03 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-5
|
||||
- Updated fix for CVE-2019-9636
|
||||
Resolves: rhbz#1689328
|
||||
Resolves: rhbz#1689327
|
||||
|
||||
* Thu Apr 25 2019 Tomas Orsava <torsava@redhat.com> - 2.7.16-4
|
||||
- Bumping due to problems with modular RPM upgrade path
|
||||
- Resolves: rhbz#1695587
|
||||
|
||||
* Fri Apr 12 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-3
|
||||
- Fix coverity scan static analysis issues
|
||||
Resolves: rhbz#1602667
|
||||
Resolves: rhbz#1690919
|
||||
|
||||
* Wed Apr 3 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-2
|
||||
- Security fix for CVE-2019-9636 (rhbz#1689328)
|
||||
- Security fix for CVE-2019-9636 (rhbz#1689327)
|
||||
|
||||
* Mon Apr 1 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-1
|
||||
* Tue Feb 19 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-1
|
||||
- Update to 2.7.16
|
||||
Resolves: rhbz#1680964
|
||||
Resolves: rhbz#1680967
|
||||
|
||||
* Thu Nov 29 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-16
|
||||
- Bump NVR to redo CI gating tests, because the "update test" was
|
||||
malfunctioning and had to be fixed
|
||||
Resolves: rhbz#1565560
|
||||
* Wed Dec 12 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-21
|
||||
- Fix Tkinter
|
||||
- Remove wininst exe files, that are no longer included, from the files section
|
||||
- Resolves: rhbz#1656488
|
||||
|
||||
* Wed Nov 28 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-15
|
||||
* Wed Dec 12 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-20
|
||||
- Fix launcher of pynche
|
||||
- Resolves: rhbz#1656479
|
||||
|
||||
* Tue Dec 11 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-19
|
||||
- Remove remaining unversioned executables (idle, msgfmt.py, pygettext.py,
|
||||
pynche, smtpd.py, python-config)
|
||||
Resolves: rhbz#1656511
|
||||
|
||||
* Wed Nov 28 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-18
|
||||
- Workaround Python's threading library issue with non returning wait, for signals with timeout
|
||||
Resolves: rhbz#1565560
|
||||
Resolves: rhbz#1653754
|
||||
|
||||
* Tue Nov 13 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-14
|
||||
* Tue Nov 13 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-17
|
||||
- Add choices for sort option of cProfile for better output
|
||||
Resolves: rhbz#1565101
|
||||
Resolves: rhbz#1649473
|
||||
|
||||
* Wed Nov 07 2018 Lumír Balhar <lbalhar@redhat.com> - 2.7.15-13
|
||||
* Wed Nov 07 2018 Lumír Balhar <lbalhar@redhat.com> - 2.7.15-16
|
||||
- Bring audiotest.au back to package. It's not copyrighted anymore.
|
||||
- Resolves: rhbz#1643970
|
||||
- Resolves: rhbz#1647692
|
||||
|
||||
* Tue Oct 16 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-15
|
||||
- Slightly edit the description
|
||||
- Related: rhbz#1633537
|
||||
|
||||
* Sun Oct 14 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-14
|
||||
- Add Requires (/post/postun) on /usr/sbin/alternatives
|
||||
- Resolves: rhbz#1633537
|
||||
|
||||
* Fri Oct 12 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-13
|
||||
- Don't list /usr/bin/python as a ghost file so `yum install /usr/bin/python`
|
||||
doesn't install this package
|
||||
- Resolves: rhbz#1633537
|
||||
|
||||
* Fri Oct 12 2018 Petr Viktorin <pviktori@redhat.com> - 2.7.15-12
|
||||
- Remove Windows binaries from the source archive
|
||||
@ -2033,41 +2228,43 @@ Resolves: rhbz#1565101
|
||||
|
||||
* Fri Oct 12 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-11
|
||||
- Fix test_dbm_gnu for gdbm 1.15 which fails on ppc64le
|
||||
Resolves: rhbz#1638710
|
||||
Resolves: rhbz#1638716
|
||||
|
||||
* Mon Sep 24 2018 Miro Hrončok <mhroncok@redhat.com> - 2.7.15-10
|
||||
* Thu Oct 11 2018 Miro Hrončok <mhroncok@redhat.com> - 2.7.15-10
|
||||
- Security fix for CVE-2018-14647
|
||||
Resolves: rhbz#1632095
|
||||
|
||||
* Sat Aug 04 2018 Petr Viktorin <pviktori@redhat.com> - 2.7.15-9
|
||||
- Disable Python 2 at the C level
|
||||
* Mon Oct 08 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-9
|
||||
- Set a special Provides tag that advertises the `python2` package as an
|
||||
alternative to the non-existing `python` package
|
||||
- Resolves: rhbz#1633559
|
||||
|
||||
* Fri Aug 3 2018 Florian Weimer <fweimer@redhat.com> - 2.7.15-8
|
||||
- Honor %%{valgrind_arches}
|
||||
* Thu Oct 04 2018 Lumír Balhar <lbalhar@redhat.com> - 2.7.15-8
|
||||
- Remove unversioned provides
|
||||
- Resolves: rhbz#1628242
|
||||
|
||||
* Thu Aug 02 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-7
|
||||
* Tue Oct 02 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-7
|
||||
- Implement the alternatives system for Python in RHEL8
|
||||
- Resolves: rhbz#1633537
|
||||
|
||||
* Thu Aug 09 2018 Lumír Balhar <lbalhar@redhat.com> - 2.7.15-6
|
||||
- Remove unversioned symlink to manual page and to pydoc binary.
|
||||
They are both available in streams of `python` module.
|
||||
- Resolves: rhbz#1613343
|
||||
|
||||
* Thu Aug 02 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-5
|
||||
- Disable optimizations
|
||||
- Disable ssl related tests for now
|
||||
|
||||
* Wed Jul 25 2018 Petr Kubat <pkubat@redhat.com> - 2.7.15-6
|
||||
- Rebuilt for gdbm
|
||||
* Wed Aug 01 2018 Lumír Balhar <lbalhar@redhat.com> - 2.7.15-4
|
||||
- Hotfix issue with byte compilation macro - rhbz#1597664
|
||||
|
||||
* Mon Jul 09 2018 Petr Viktorin <pviktori@redhat.com> - 2.7.15-5
|
||||
- Don't build the tkinter and tools subpackages
|
||||
* Thu May 24 2018 Tomas Orsava <torsava@redhat.com> - 2.7.15-3
|
||||
- Remove the /usr/bin/python executable so that the user can chose where it
|
||||
points to by installing various streams of the `python` module
|
||||
|
||||
* Thu Jun 28 2018 Petr Viktorin <pviktori@redhat.com> - 2.7.15-4
|
||||
- Disable Python 2
|
||||
- Exclude the unversioned commands in /usr/bin
|
||||
- Exclude the unversioned man page
|
||||
- No longer Provide unversioned "python"
|
||||
|
||||
* Tue Jun 26 2018 Petr Viktorin <pviktori@redhat.com> - 2.7.15-3
|
||||
- Bump release
|
||||
|
||||
* Fri Jun 22 2018 Petr Viktorin <pviktori@redhat.com> - 2.7.15-2
|
||||
- Provide the python2-for-tests package
|
||||
- Disable rewheel & ensurepip
|
||||
- Disable the debug build
|
||||
* Tue May 15 2018 Charalampos Stratakis <cstratak@redhat.com> - 2.7.15-2
|
||||
- Fix loading of the gdb python plugin (rhbz#1578001)
|
||||
|
||||
* Tue May 01 2018 Miro Hrončok <mhroncok@redhat.com> - 2.7.15-1
|
||||
- Update to version 2.7.15
|
1
sources
Normal file
1
sources
Normal file
@ -0,0 +1 @@
|
||||
SHA512 (Python-2.7.18-noexe.tar.xz) = c74b0fcf4cf01ae33b1f9cdfc104d8679bd4718daa093db72c2664688d966ece736fd6d82c195a227d8de85bc7d9d149aaa1d52bc3dbe7d3413076adae9fb5b6
|
Loading…
Reference in New Issue
Block a user