diff options
author | Georg Brandl <georg@python.org> | 2012-02-20 20:31:46 (GMT) |
---|---|---|
committer | Georg Brandl <georg@python.org> | 2012-02-20 20:31:46 (GMT) |
commit | 09a7c72cad48f568e0781541167cf9ea6a3f0760 (patch) | |
tree | d925894bfc3662e33c03ff7b6b2c5e9e38749b73 /Modules | |
parent | fee358b0df547e9451cfb0b3d25980e6cc7177cc (diff) | |
parent | 2daf6ae2495c862adf8bc717bfe9964081ea0b10 (diff) | |
download | cpython-09a7c72cad48f568e0781541167cf9ea6a3f0760.zip cpython-09a7c72cad48f568e0781541167cf9ea6a3f0760.tar.gz cpython-09a7c72cad48f568e0781541167cf9ea6a3f0760.tar.bz2 |
Merge from 3.1: Issue #13703: add a way to randomize the hash values of basic types (str, bytes, datetime)
in order to make algorithmic complexity attacks on (e.g.) web apps much more complicated.
The environment variable PYTHONHASHSEED and the new command line flag -R control this
behavior.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_datetimemodule.c | 4 | ||||
-rw-r--r-- | Modules/main.c | 20 | ||||
-rw-r--r-- | Modules/posixmodule.c | 124 |
3 files changed, 38 insertions, 110 deletions
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 6ee5317..85c5c4d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -2785,10 +2785,12 @@ generic_hash(unsigned char *data, int len) register Py_hash_t x; p = (unsigned char *) data; - x = *p << 7; + x = _Py_HashSecret.prefix; + x ^= *p << 7; while (--len >= 0) x = (1000003*x) ^ *p++; x ^= len; + x ^= _Py_HashSecret.suffix; if (x == -1) x = -2; diff --git a/Modules/main.c b/Modules/main.c index 5a985a5..ed84aa0 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -46,7 +46,7 @@ static wchar_t **orig_argv; static int orig_argc; /* command line options */ -#define BASE_OPTS L"bBc:dEhiJm:OqsStuvVW:xX:?" +#define BASE_OPTS L"bBc:dEhiJm:OqRsStuvVW:xX:?" #define PROGRAM_OPTS BASE_OPTS @@ -72,6 +72,9 @@ static char *usage_2 = "\ -O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x\n\ -OO : remove doc-strings in addition to the -O optimizations\n\ -q : don't print version and copyright messages on interactive startup\n\ +-R : use a pseudo-random salt to make hash() values of various types be\n\ + unpredictable between separate invocations of the interpreter, as\n\ + a defence against denial-of-service attacks\n\ -s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\ -S : don't imply 'import site' on initialization\n\ "; @@ -99,8 +102,14 @@ static char *usage_5 = "PYTHONHOME : alternate <prefix> directory (or <prefix>%c<exec_prefix>).\n" " The default module search path uses %s.\n" "PYTHONCASEOK : ignore case in 'import' statements (Windows).\n" -"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n" -; +"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n\ +"; +static char *usage_6 = "\ +PYTHONHASHSEED: if this variable is set to ``random``, the effect is the same \n\ + as specifying the :option:`-R` option: a random value is used to seed the\n\ + hashes of str, bytes and datetime objects. It can also be set to an integer\n\ + in the range [0,4294967295] to get hash values with a predictable seed.\n\ +"; static int usage(int exitcode, wchar_t* program) @@ -116,6 +125,7 @@ usage(int exitcode, wchar_t* program) fputs(usage_3, f); fprintf(f, usage_4, DELIM); fprintf(f, usage_5, DELIM, PYTHONHOMEHELP); + fputs(usage_6, f); } #if defined(__VMS) if (exitcode == 0) { @@ -429,6 +439,10 @@ Py_Main(int argc, wchar_t **argv) Py_QuietFlag++; break; + case 'R': + Py_HashRandomizationFlag++; + break; + /* This space reserved for other options */ default: diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 0afab3c..9f57673 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7614,82 +7614,6 @@ posix_getloadavg(PyObject *self, PyObject *noargs) } #endif -#ifdef MS_WINDOWS - -PyDoc_STRVAR(win32_urandom__doc__, -"urandom(n) -> str\n\n\ -Return n random bytes suitable for cryptographic use."); - -typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\ - LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\ - DWORD dwFlags ); -typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\ - BYTE *pbBuffer ); - -static CRYPTGENRANDOM pCryptGenRandom = NULL; -/* This handle is never explicitly released. Instead, the operating - system will release it when the process terminates. */ -static HCRYPTPROV hCryptProv = 0; - -static PyObject* -win32_urandom(PyObject *self, PyObject *args) -{ - int howMany; - PyObject* result; - - /* Read arguments */ - if (! PyArg_ParseTuple(args, "i:urandom", &howMany)) - return NULL; - if (howMany < 0) - return PyErr_Format(PyExc_ValueError, - "negative argument not allowed"); - - if (hCryptProv == 0) { - HINSTANCE hAdvAPI32 = NULL; - CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; - - /* Obtain handle to the DLL containing CryptoAPI - This should not fail */ - hAdvAPI32 = GetModuleHandle("advapi32.dll"); - if(hAdvAPI32 == NULL) - return win32_error("GetModuleHandle", NULL); - - /* Obtain pointers to the CryptoAPI functions - This will fail on some early versions of Win95 */ - pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress( - hAdvAPI32, - "CryptAcquireContextA"); - if (pCryptAcquireContext == NULL) - return PyErr_Format(PyExc_NotImplementedError, - "CryptAcquireContextA not found"); - - pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress( - hAdvAPI32, "CryptGenRandom"); - if (pCryptGenRandom == NULL) - return PyErr_Format(PyExc_NotImplementedError, - "CryptGenRandom not found"); - - /* Acquire context */ - if (! pCryptAcquireContext(&hCryptProv, NULL, NULL, - PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - return win32_error("CryptAcquireContext", NULL); - } - - /* Allocate bytes */ - result = PyBytes_FromStringAndSize(NULL, howMany); - if (result != NULL) { - /* Get random data */ - memset(PyBytes_AS_STRING(result), 0, howMany); /* zero seed */ - if (! pCryptGenRandom(hCryptProv, howMany, (unsigned char*) - PyBytes_AS_STRING(result))) { - Py_DECREF(result); - return win32_error("CryptGenRandom", NULL); - } - } - return result; -} -#endif - PyDoc_STRVAR(device_encoding__doc__, "device_encoding(fd) -> str\n\n\ Return a string describing the encoding of the device\n\ @@ -7727,41 +7651,35 @@ device_encoding(PyObject *self, PyObject *args) return Py_None; } -#ifdef __VMS -/* Use openssl random routine */ -#include <openssl/rand.h> -PyDoc_STRVAR(vms_urandom__doc__, +PyDoc_STRVAR(posix_urandom__doc__, "urandom(n) -> str\n\n\ Return n random bytes suitable for cryptographic use."); -static PyObject* -vms_urandom(PyObject *self, PyObject *args) +static PyObject * +posix_urandom(PyObject *self, PyObject *args) { - int howMany; - PyObject* result; + Py_ssize_t size; + PyObject *result; + int ret; - /* Read arguments */ - if (! PyArg_ParseTuple(args, "i:urandom", &howMany)) + /* Read arguments */ + if (!PyArg_ParseTuple(args, "n:urandom", &size)) return NULL; - if (howMany < 0) + if (size < 0) return PyErr_Format(PyExc_ValueError, "negative argument not allowed"); + result = PyBytes_FromStringAndSize(NULL, size); + if (result == NULL) + return NULL; - /* Allocate bytes */ - result = PyBytes_FromStringAndSize(NULL, howMany); - if (result != NULL) { - /* Get random data */ - if (RAND_pseudo_bytes((unsigned char*) - PyBytes_AS_STRING(result), - howMany) < 0) { - Py_DECREF(result); - return PyErr_Format(PyExc_ValueError, - "RAND_pseudo_bytes"); - } + ret = _PyOS_URandom(PyBytes_AS_STRING(result), + PyBytes_GET_SIZE(result)); + if (ret == -1) { + Py_DECREF(result); + return NULL; } return result; } -#endif #ifdef HAVE_SETRESUID PyDoc_STRVAR(posix_setresuid__doc__, @@ -8137,12 +8055,7 @@ static PyMethodDef posix_methods[] = { #ifdef HAVE_GETLOADAVG {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__}, #endif - #ifdef MS_WINDOWS - {"urandom", win32_urandom, METH_VARARGS, win32_urandom__doc__}, - #endif - #ifdef __VMS - {"urandom", vms_urandom, METH_VARARGS, vms_urandom__doc__}, - #endif + {"urandom", posix_urandom, METH_VARARGS, posix_urandom__doc__}, #ifdef HAVE_SETRESUID {"setresuid", posix_setresuid, METH_VARARGS, posix_setresuid__doc__}, #endif @@ -8155,7 +8068,6 @@ static PyMethodDef posix_methods[] = { #ifdef HAVE_GETRESGID {"getresgid", posix_getresgid, METH_NOARGS, posix_getresgid__doc__}, #endif - {NULL, NULL} /* Sentinel */ }; |