crypto: support FIPS mode of OpenSSL
authorFedor Indutny <fedor@indutny.com>
Mon, 1 Jun 2015 21:49:43 +0000 (23:49 +0200)
committerFedor Indutny <fedor@indutny.com>
Wed, 10 Jun 2015 23:49:20 +0000 (01:49 +0200)
Support building and running with FIPS-compliant OpenSSL. The process is
following:

1. Download and verify `openssl-fips-x.x.x.tar.gz` from
   https://www.openssl.org/source/
2. Extract source to `openssl-fips` folder
3. ``cd openssl-fips && ./config fipscanisterbuild --prefix=`pwd`/out``
   (NOTE: On OS X, you may want to run
    ``./Configure darwin64-x86_64-cc --prefix=`pwd`/out`` if you are going to
    build x64-mode io.js)
4. `make -j && make install`
5. Get into io.js checkout folder
6. `./configure --openssl-fips=/path/to/openssl-fips/out`
7. Build io.js with `make -j`
8. Verify with `node -p "process.versions.openssl"` (`1.0.2a-fips`)

Fix: https://github.com/joyent/node/issues/25463
PR-URL: https://github.com/nodejs/io.js/pull/1890
Reviewed-By: Rod Vagg <rod@vagg.org>
Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
README.md
common.gypi
configure
deps/openssl/fips/fipscc [new file with mode: 0755]
deps/openssl/fips/fipsld [new file with mode: 0755]
deps/openssl/openssl.gyp
node.gyp
src/node_crypto.cc

index 9b7f5b0..9121581 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+
 io.js
 =====
 
@@ -249,6 +250,28 @@ as `deps/icu` (You'll have: `deps/icu/source/...`)
 > vcbuild full-icu
 ```
 
+# Building io.js with FIPS-compliant OpenSSL
+
+NOTE: Windows is not yet supported
+
+It is possible to build io.js with
+[OpenSSL FIPS module](https://www.openssl.org/docs/fips/fipsnotes.html).
+
+Instructions:
+
+1. Download and verify `openssl-fips-x.x.x.tar.gz` from
+   https://www.openssl.org/source/
+2. Extract source to `openssl-fips` folder
+3. ``cd openssl-fips && ./config fipscanisterbuild --prefix=`pwd`/out``
+   (NOTE: On OS X, you may want to run
+    ``./Configure darwin64-x86_64-cc --prefix=`pwd`/out`` if you are going to
+    build x64-mode io.js)
+4. `make -j && make install`
+5. Get into io.js checkout folder
+6. `./configure --openssl-fips=/path/to/openssl-fips/out`
+7. Build io.js with `make -j`
+8. Verify with `node -p "process.versions.openssl"` (`1.0.2a-fips`)
+
 ## Resources for Newcomers
 
 * [CONTRIBUTING.md](./CONTRIBUTING.md)
index ea7779e..4c1b90b 100644 (file)
         'OBJ_DIR': '<(PRODUCT_DIR)/obj.target',
         'V8_BASE': '<(PRODUCT_DIR)/obj.target/deps/v8/tools/gyp/libv8_base.a',
       }],
+      ['openssl_fips != ""', {
+        'OPENSSL_PRODUCT': 'libcrypto.a',
+      }, {
+        'OPENSSL_PRODUCT': 'libopenssl.a',
+      }],
       ['OS=="mac"', {
         'clang%': 1,
       }, {
index 5c9b29b..ae994e5 100755 (executable)
--- a/configure
+++ b/configure
@@ -88,6 +88,11 @@ parser.add_option("--openssl-no-asm",
     dest="openssl_no_asm",
     help="Do not build optimized assembly for OpenSSL")
 
+parser.add_option('--openssl-fips',
+    action='store',
+    dest='openssl_fips',
+    help='Build OpenSSL using FIPS canister .o file in supplied folder')
+
 shared_optgroup.add_option('--shared-http-parser',
     action='store_true',
     dest='shared_http_parser',
@@ -720,6 +725,16 @@ def configure_openssl(o):
   o['variables']['node_use_openssl'] = b(not options.without_ssl)
   o['variables']['node_shared_openssl'] = b(options.shared_openssl)
   o['variables']['openssl_no_asm'] = 1 if options.openssl_no_asm else 0
+  if options.openssl_fips:
+    o['variables']['openssl_fips'] = options.openssl_fips
+    fips_dir = os.path.join(root_dir, 'deps', 'openssl', 'fips')
+    fips_ld = os.path.abspath(os.path.join(fips_dir, 'fipsld'))
+    o['make_global_settings'] = [
+      ['LINK', fips_ld + ' <(openssl_fips)/bin/fipsld'],
+    ]
+  else:
+    o['variables']['openssl_fips'] = ''
+
 
   if options.without_ssl:
     return
@@ -1025,10 +1040,21 @@ configure_fullystatic(output)
 # move everything else to target_defaults
 variables = output['variables']
 del output['variables']
+
+# make_global_settings should be a root level element too
+if 'make_global_settings' in output:
+  make_global_settings = output['make_global_settings']
+  del output['make_global_settings']
+else:
+  make_global_settings = False
+
 output = {
   'variables': variables,
-  'target_defaults': output
+  'target_defaults': output,
 }
+if make_global_settings:
+  output['make_global_settings'] = make_global_settings
+
 pprint.pprint(output, indent=2)
 
 write('config.gypi', do_not_edit +
diff --git a/deps/openssl/fips/fipscc b/deps/openssl/fips/fipscc
new file mode 100755 (executable)
index 0000000..af30629
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+ARGS=
+CXX=${CXX:-g++}
+
+while [ "x$1" != "x" ]; do
+  ARG=$1
+  shift
+  case $ARG in
+    *fips_premain.c) ARGS="$ARGS -x c $ARG -x none";;
+    *) ARGS="$ARGS $ARG";;
+  esac
+done
+
+echo $CXX $ARGS
+${CXX} $ARGS
diff --git a/deps/openssl/fips/fipsld b/deps/openssl/fips/fipsld
new file mode 100755 (executable)
index 0000000..b261376
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# NOTE: Just a wrapper around normal fipsld
+FIPSLD=$1
+shift
+
+DIR=`dirname $0`
+FIPSLD_CC=$DIR/fipscc $FIPSLD $@
index 5a3dc9b..d5bb16e 100644 (file)
@@ -9,6 +9,7 @@
     'openssl_no_asm%': 0,
     'llvm_version%': 0,
     'gas_version%': 0,
+    'openssl_fips%': 'false',
   },
   'targets': [
     {
         ['exclude', 'store/.*$']
       ],
       'conditions': [
+        # FIPS
+        ['openssl_fips != ""', {
+          'defines': [
+            'OPENSSL_FIPS',
+          ],
+          'include_dirs': [
+            '<(openssl_fips)/include',
+          ],
+
+          # Trick fipsld, it expects to see libcrypto.a
+          'product_name': 'crypto',
+
+          'direct_dependent_settings': {
+            'defines': [
+              'OPENSSL_FIPS',
+            ],
+            'include_dirs': [
+              '<(openssl_fips)/include',
+            ],
+          },
+        }],
+
         ['openssl_no_asm!=0', {
           # Disable asm
           'defines': [
index cbc536d..2b530f1 100644 (file)
--- a/node.gyp
+++ b/node.gyp
                 [ 'node_target_type!="static_library"', {
                   'xcode_settings': {
                     'OTHER_LDFLAGS': [
-                      '-Wl,-force_load,<(PRODUCT_DIR)/libopenssl.a',
+                      '-Wl,-force_load,<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)',
                     ],
                   },
                   'conditions': [
                     ['OS in "linux freebsd"', {
                       'ldflags': [
-                        '-Wl,--whole-archive <(PRODUCT_DIR)/libopenssl.a',
+                        '-Wl,--whole-archive <(PRODUCT_DIR)/<(OPENSSL_PRODUCT)',
                         '-Wl,--no-whole-archive',
                       ],
                     }],
index 51914b8..3888a84 100644 (file)
@@ -5097,6 +5097,15 @@ void InitCryptoOnce() {
   CRYPTO_set_locking_callback(crypto_lock_cb);
   CRYPTO_THREADID_set_callback(crypto_threadid_cb);
 
+#ifdef OPENSSL_FIPS
+  if (!FIPS_mode_set(1)) {
+    int err = ERR_get_error();
+    fprintf(stderr, "openssl fips failed: %s\n", ERR_error_string(err, NULL));
+    UNREACHABLE();
+  }
+#endif  // OPENSSL_FIPS
+
+
   // Turn off compression. Saves memory and protects against CRIME attacks.
 #if !defined(OPENSSL_NO_COMP)
 #if OPENSSL_VERSION_NUMBER < 0x00908000L