Looking at the source code, I noticed that since Python 3.5, the ${PYTHON_SRC_DIR}/Modules/_ssl.c structure has changed (got "a little bit" more structured and also contains generated code), compared to the 3.4 version that I submitted the patch to ([Python.Bugs]: FIPS_mode() and FIPS_mode_set() functions in Python (ssl)).
Basically, every entry in the PySSL_methods array is no longer defined on the spot, but indirectly, via a preprocessor macro.
You acted according to the new standard, but forgot to define the macros.
In order to fix the problem:
Define the 2 macros for the PyMethodDef entries (inside #ifdef EXPORT_FIPSMODE_FUNCS
):
// ...
}
#define _SSL_FIPS_MODE_METHODDEF \
{"FIPS_mode", (PyCFunction)_ssl_FIPS_mode_impl, METH_NOARGS, NULL},
#define _SSL_FIPS_MODE_SET_METHODDEF \
{"FIPS_mode_set", (PyCFunction)_ssl_FIPS_mode_set_impl, METH_O, NULL},
#endif // EXPORT_FIPSMODE_FUNCS
// ...
"Protect" the new macros by an #ifdef clause (similarly to where they were defined) when defining PySSL_methods (so that if the EXPORT_FIPSMODE_FUNCS macro isn't defined, you won't get a compile error):
// ...
_SSL_NID2OBJ_METHODDEF
#ifdef EXPORT_FIPSMODE_FUNCS
_SSL_FIPS_MODE_METHODDEF
_SSL_FIPS_MODE_SET_METHODDEF
#endif // EXPORT_FIPSMODE_FUNCS
{NULL, NULL} /* Sentinel */
// ...
Notes:
Since you didn't define the docstrings, I filled them with NULLs (PyMethodDef's last member). That will result in syntactically correct code, but I'm not sure how will it behave at runtime, when you'll run help()
on any of the 2 functions. If it segfaults, copy the definitions from the patch or define some dummy strings using PyDoc_STRVAR (and recompile, of course)
Although this is closer to the standard than the original patch, it's not quite there, as OP (@HussainAliAkbar) shown in the (2nd) edit. Things are a little bit more complicated (also involving ${PYTHON_SRC_DIR}/Modules/clinic/_ssl.c.h). See the next update section below
Update #0
I took some time to check what's the deal with that clinic folder. It's [Python.Docs]: Argument Clinic How-To. I consider it a major improvement because it spares the developer writing the argument parsing code (involving PyArg_*, Py*_Check funcs) in every function ("monkey work")
I spent some hours, but I managed to do things "by the book" (at least, I think so)
Python-3.6.4-ssl_fips.diff:
--- Python-3.6.4/Modules/_ssl.c.orig 2018-07-27 19:10:06.131999999 +0300
+++ Python-3.6.4/Modules/_ssl.c 2018-07-27 20:32:15.531999999 +0300
@@ -4789,6 +4789,46 @@
return result;
}
+#if defined(EXPORT_FIPSMODE_FUNCS)
+
+unsigned char _fips_table_sig[0x0C] = {0x21, 0x7A, 0x65, 0x6C, 0x75, 0x72, 0x20, 0x49, 0x54, 0x41, 0x46, 0x00};
+
+/*[clinic input]
+_ssl.FIPS_mode
+
+Return 1 (!=0) if FIPS mode is enabled, 0 otherwise.
+[clinic start generated code]*/
+
+static PyObject *
+_ssl_FIPS_mode_impl(PyObject *module)
+/*[clinic end generated code: output=89f5a88ec715a291 input=52e4e5fdd1f555c7]*/
+{
+ return PyLong_FromLong(FIPS_mode());
+}
+
+/*[clinic input]
+_ssl.FIPS_mode_set
+ mode: int
+ /
+
+Try to set the FIPS mode to 'mode' (int).
+
+Return nothing. Raise SSLError when enabling FIPS mode fails.
+[clinic start generated code]*/
+
+static PyObject *
+_ssl_FIPS_mode_set_impl(PyObject *module, int mode)
+/*[clinic end generated code: output=70e3e9f3bb4fce65 input=899c21a986720235]*/
+{
+if (FIPS_mode_set(mode) == 0) {
+ _setSSLError(ERR_error_string(ERR_get_error(), NULL), 0, __FILE__, __LINE__);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+#endif // EXPORT_FIPSMODE_FUNCS
+
#ifdef _MSC_VER
static PyObject*
@@ -5055,6 +5095,8 @@
_SSL_ENUM_CRLS_METHODDEF
_SSL_TXT2OBJ_METHODDEF
_SSL_NID2OBJ_METHODDEF
+ _SSL_FIPS_MODE_METHODDEF
+ _SSL_FIPS_MODE_SET_METHODDEF
{NULL, NULL} /* Sentinel */
};
--- Python-3.6.4/Modules/clinic/_ssl.c.h.orig 2018-07-27 19:10:48.067999999 +0300
+++ Python-3.6.4/Modules/clinic/_ssl.c.h 2018-07-27 20:31:04.507999999 +0300
@@ -1062,6 +1062,61 @@
return return_value;
}
+#if defined(EXPORT_FIPSMODE_FUNCS)
+
+PyDoc_STRVAR(_ssl_FIPS_mode__doc__,
+"FIPS_mode($module, /)\n"
+"--\n"
+"\n"
+"Return 1 (!=0) if FIPS mode is enabled, 0 otherwise.");
+
+#define _SSL_FIPS_MODE_METHODDEF \
+ {"FIPS_mode", (PyCFunction)_ssl_FIPS_mode, METH_NOARGS, _ssl_FIPS_mode__doc__},
+
+static PyObject *
+_ssl_FIPS_mode_impl(PyObject *module);
+
+static PyObject *
+_ssl_FIPS_mode(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _ssl_FIPS_mode_impl(module);
+}
+
+#endif /* defined(EXPORT_FIPSMODE_FUNCS) */
+
+#if defined(EXPORT_FIPSMODE_FUNCS)
+
+PyDoc_STRVAR(_ssl_FIPS_mode_set__doc__,
+"FIPS_mode_set($module, mode, /)\n"
+"--\n"
+"\n"
+"Try to set the FIPS mode to \'mode\' (int).\n"
+"\n"
+"Return nothing. Raise SSLError when enabling FIPS mode fails.");
+
+#define _SSL_FIPS_MODE_SET_METHODDEF \
+ {"FIPS_mode_set", (PyCFunction)_ssl_FIPS_mode_set, METH_O, _ssl_FIPS_mode_set__doc__},
+
+static PyObject *
+_ssl_FIPS_mode_set_impl(PyObject *module, int mode);
+
+static PyObject *
+_ssl_FIPS_mode_set(PyObject *module, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int mode;
+
+ if (!PyArg_Parse(arg, "i:FIPS_mode_set", &mode)) {
+ goto exit;
+ }
+ return_value = _ssl_FIPS_mode_set_impl(module, mode);
+
+exit:
+ return return_value;
+}
+
+#endif /* defined(EXPORT_FIPSMODE_FUNCS) */
+
#if defined(_MSC_VER)
PyDoc_STRVAR(_ssl_enum_certificates__doc__,
@@ -1161,6 +1216,14 @@
#define _SSL_RAND_EGD_METHODDEF
#endif /* !defined(_SSL_RAND_EGD_METHODDEF) */
+#ifndef _SSL_FIPS_MODE_METHODDEF
+ #define _SSL_FIPS_MODE_METHODDEF
+#endif /* !defined(_SSL_FIPS_MODE_METHODDEF) */
+
+#ifndef _SSL_FIPS_MODE_SET_METHODDEF
+ #define _SSL_FIPS_MODE_SET_METHODDEF
+#endif /* !defined(_SSL_FIPS_MODE_SET_METHODDEF) */
+
#ifndef _SSL_ENUM_CERTIFICATES_METHODDEF
#define _SSL_ENUM_CERTIFICATES_METHODDEF
#endif /* !defined(_SSL_ENUM_CERTIFICATES_METHODDEF) */
@@ -1168,4 +1231,4 @@
#ifndef _SSL_ENUM_CRLS_METHODDEF
#define _SSL_ENUM_CRLS_METHODDEF
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
-/*[clinic end generated code: output=a8b184655068c238 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=94d0ec4213d44124 input=a9049054013a1b77]*/
--- Python-3.6.4/Lib/ssl.py.orig 2018-07-27 19:10:29.827999999 +0300
+++ Python-3.6.4/Lib/ssl.py 2018-03-28 23:30:35.065667344 +0300
@@ -114,6 +114,11 @@
# LibreSSL does not provide RAND_egd
pass
+try:
+ from _ssl import FIPS_mode, FIPS_mode_set
+except ImportError:
+ # Compiled without FIPS functions support
+ pass
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3
from _ssl import _OPENSSL_API_VERSION
--- Python-3.6.4/setup.py.orig 2018-07-27 19:09:33.763999999 +0300
+++ Python-3.6.4/setup.py 2018-03-28 23:30:35.061667315 +0300
@@ -862,6 +862,7 @@
ssl_libs is not None):
exts.append( Extension('_ssl', ['_ssl.c'],
include_dirs = ssl_incs,
+ define_macros = [("EXPORT_FIPSMODE_FUNCS", None)],
library_dirs = ssl_libs,
libraries = ['ssl', 'crypto'],
depends = ['socketmodule.h']), )
Notes:
That is a diff (note that it's outside ${PYTHON_SRC_DIR}). See [SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer) (Patching utrunner section) for how to apply patches on Win (basically, every line that starts with one "+" sign goes in, and every line that starts with one "-" sign goes out)
Source baseline is v3.6.4 (as pointed by the file name)
4 files are modified:
${PYTHON_SRC_DIR}/Modules/_ssl.c - contains both manual and generated (as you guessed, by the Argument Clinic) changes (generated pieces easy to spot from comments)
${PYTHON_SRC_DIR}/Modules/clinic/_ssl.c.h - contains generated code only
The other 2 contain manual changes
I also tested the changes (at one point I used the OpenSSL version that I've built for [SO]: How to enable FIPS mode for libcrypto and libssl packaged with Python? (@CristiFati's answer))
Output:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049493537]> LD_LIBRARY_PATH=./py364-nofips/lib ./py364-nofips/bin/python3 -c "import ssl;print(ssl.FIPS_mode())"
Traceback (most recent call last):
File "<string>", line 1, in <module>
AttributeError: module 'ssl' has no attribute 'FIPS_mode'
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049493537]> LD_LIBRARY_PATH=py364-fips/lib py364-fips/bin/python3 -c "import ssl;print(ssl.FIPS_mode(), ssl.FIPS_mode_set(0), ssl.FIPS_mode());print(ssl.FIPS_mode_set(1), ssl.FIPS_mode())"
0 None 0
Traceback (most recent call last):
File "<string>", line 1, in <module>
ssl.SSLError: error:0F06D065:common libcrypto routines:FIPS_mode_set:fips mode not supported (_ssl.c:4822)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049493537]> LD_LIBRARY_PATH=py364-fips/lib:../q049320993/ssl/build/lib py364-fips/bin/python3 -c "import ssl;print(ssl.FIPS_mode(), ssl.FIPS_mode_set(0), ssl.FIPS_mode());print(ssl.FIPS_mode_set(1), ssl.FIPS_mode())"
0 None 0
None 1
Might also look over [SO]: OpenSSL FIPS_mode_set not working in Python cryptography library (@CristiFati's answer).