commit f30d169cfa9520a78c07753d99a2eecaf8f95f3f Author: Kevin Christopher Date: Fri Jan 27 15:23:22 2017 -0800 hashlib: in FIPS mode, convert crash to exception OpenSSL's FIPS mode permits EVP functions to return an error during initialization. If this error is ignored, futher use of the EVP_MD_CTX structure will deref NULL and crash. Modify all applicable EVP callsites to check for that return value and raise an exception instead. One complication: the module init function pre-loads several hash functions, and raising an exception during module init is undesirable. Switch pre-loaded hash functions to lazy initialization - the first use will construct the appropriate object, or raise the exception if it fails. Existing code in Lib/hashlib.py (see __get_openssl_constructor) handles that exception by falling back to the _md5 module. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 4995294..c83ebae 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -139,7 +139,10 @@ EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len) process = MUNCH_SIZE; else process = Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int); - EVP_DigestUpdate(self->ctx, (const void*)cp, process); + if (!EVP_DigestUpdate(self->ctx, (const void*)cp, process)) { + _setException(PyExc_ValueError); + break; + } len -= process; cp += process; } @@ -209,7 +212,10 @@ EVP_digest(EVPobject *self, PyObject *unused) return _setException(PyExc_ValueError); } digest_size = EVP_MD_CTX_size(temp_ctx); - EVP_DigestFinal(temp_ctx, digest, NULL); + if (!EVP_DigestFinal(temp_ctx, digest, NULL)) { + _setException(PyExc_ValueError); + return NULL; + } retval = PyBytes_FromStringAndSize((const char *)digest, digest_size); EVP_MD_CTX_free(temp_ctx); @@ -237,7 +243,10 @@ EVP_hexdigest(EVPobject *self, PyObject *unused) return _setException(PyExc_ValueError); } digest_size = EVP_MD_CTX_size(temp_ctx); - EVP_DigestFinal(temp_ctx, digest, NULL); + if (!EVP_DigestFinal(temp_ctx, digest, NULL)) { + _setException(PyExc_ValueError); + return NULL; + } EVP_MD_CTX_free(temp_ctx); @@ -362,7 +371,12 @@ EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds) PyBuffer_Release(&view); return -1; } - EVP_DigestInit(self->ctx, digest); + if (!EVP_DigestInit(self->ctx, digest)) { + _setException(PyExc_ValueError); + if (data_obj) + PyBuffer_Release(&view); + return -1; + } self->name = name_obj; Py_INCREF(self->name); @@ -461,7 +475,11 @@ EVPnew(PyObject *name_obj, if (initial_ctx) { EVP_MD_CTX_copy(self->ctx, initial_ctx); } else { - EVP_DigestInit(self->ctx, digest); + if (!EVP_DigestInit(self->ctx, digest)) { + _setException(PyExc_ValueError); + Py_DECREF(self); + return NULL; + } } if (cp && len) { @@ -902,6 +920,8 @@ 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. + * The first call will lazy-initialize, which reports an exception + * if initialization fails. */ #define GEN_CONSTRUCTOR(NAME) \ static PyObject * \ @@ -919,6 +939,17 @@ generate_hash_name_list(void) return NULL; \ } \ \ + if (CONST_new_ ## NAME ## _ctx_p == NULL) { \ + EVP_MD_CTX *ctx_p = EVP_MD_CTX_new(); \ + if (!EVP_get_digestbyname(#NAME) || \ + !EVP_DigestInit(ctx_p, EVP_get_digestbyname(#NAME))) { \ + _setException(PyExc_ValueError); \ + EVP_MD_CTX_free(ctx_p); \ + return NULL; \ + } \ + CONST_new_ ## NAME ## _ctx_p = ctx_p; \ + } \ + \ if (data_obj) \ GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); \ \ @@ -946,10 +977,6 @@ generate_hash_name_list(void) #define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ if (CONST_ ## NAME ## _name_obj == NULL) { \ CONST_ ## NAME ## _name_obj = PyUnicode_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)); \ - } \ } \ } while (0);