diff --git a/README.md b/README.md index a5d59e80e709c1..3852ec5e432be5 100644 --- 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) diff --git a/common.gypi b/common.gypi index ea7779ee097a69..4c1b90b29ad2ca 100644 --- a/common.gypi +++ b/common.gypi @@ -38,6 +38,11 @@ '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, }, { diff --git a/configure b/configure index 5c9b29bf2c2d40..ae994e5d49f27f 100755 --- 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 index 00000000000000..af30629e3319de --- /dev/null +++ b/deps/openssl/fips/fipscc @@ -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 index 00000000000000..b261376757fdeb --- /dev/null +++ b/deps/openssl/fips/fipsld @@ -0,0 +1,8 @@ +#!/bin/sh + +# NOTE: Just a wrapper around normal fipsld +FIPSLD=$1 +shift + +DIR=`dirname $0` +FIPSLD_CC=$DIR/fipscc $FIPSLD $@ diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index 5a3dc9b6c74210..d5bb16e5e388f6 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -9,6 +9,7 @@ 'openssl_no_asm%': 0, 'llvm_version%': 0, 'gas_version%': 0, + 'openssl_fips%': 'false', }, 'targets': [ { @@ -21,6 +22,28 @@ ['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': [ diff --git a/node.gyp b/node.gyp index de0e47ec2e400d..e098e08906f802 100644 --- a/node.gyp +++ b/node.gyp @@ -233,13 +233,13 @@ [ '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', ], }], diff --git a/src/node_crypto.cc b/src/node_crypto.cc index e2c478a510be84..29de05aaf0f8e4 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5071,6 +5071,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