Difference between revisions of "Fipsld and C++"

From OpenSSLWiki
Jump to: navigation, search
m (Added info on Mac OS X 10.8.)
m (Modifications -> fipsld Modifications.)
Line 185: Line 185:
 
</pre>
 
</pre>
  
== Modifications ==
+
== fipsld Modifications ==
  
 
You have to make 3 modifications to <tt>fipsld++</tt>. To begin, make a copy of <tt>fipsld</tt> and name it <tt>fipsld++</tt>.
 
You have to make 3 modifications to <tt>fipsld++</tt>. To begin, make a copy of <tt>fipsld</tt> and name it <tt>fipsld++</tt>.

Revision as of 21:44, 9 March 2014

This wiki page will cover building a C++ project from the command line that statically links to libssl.a and libcrypto.a. When performing a static link against the OpenSSL library, you have to embed the expected FIPS signature in your executable after final linking. Embedding the FIPS signature in your executable is most of accomplished with fisld.

fisld will take the place of the linker (or compiler if invoking via a compiler driver). If you use fisld to compile a source file, fisld will do nothing and simply invoke the compiler you specify through FIPSLD_CC. When it comes time to link, fisld will compile fips_premain.c, add fipscanister.o, and then perform the final link of your program. Once your program is linked, fisld will then invoke incore to embed the FIPS signature in your program.

The procedure requires three minor modifications to fipsld. The modifications will produce fipsld++ that follows the exiting procedures and re-sues the existing components. After fipsld++ is modified, its copied into fips-2.0 with the other FIPS binary components.

The example below demonstrates the procedures on Debian 7.3 (x64) using OpenSSL FIPS Capable 1.0.1f and the FIPS Object Module 2.0.5. The example demonstrates Clang and Clang++ with -stdlib=libstdc++. However, the procedures work equally well with GCC and G++ (you omit the -stdlib=libstdc++ for GNU compilers). The modified fisld script also works on Mac OS X 10.8.

Background

When attempting to link through the compiler driver when the compiler is C++, you will encounter at least two errors due to fips_premain.c:

/tmp/fips_premain-20db15.o: In function `FINGERPRINT_premain()':
/usr/local/ssl/fips-2.0/lib/fips_premain.c:103: undefined reference to `FIPS_text_start()'
/usr/local/ssl/fips-2.0/lib/fips_premain.c:116: undefined reference to `FIPS_incore_fingerprint(unsigned char*, unsigned int)'

If you link with the -Wl,--no-demangle linker flag, you will see the linker is looking for a C++ mangled name because fips_premain.c was compiled with a C++ compiler:

/tmp/fips_premain-be4611.o: In function `_Z19FINGERPRINT_premainv':
/usr/local/ssl/fips-2.0/lib/fips_premain.c:103: undefined reference to `_Z15FIPS_text_startv'
/usr/local/ssl/fips-2.0/lib/fips_premain.c:116: undefined reference to `_Z23FIPS_incore_fingerprintPhj'

The problem occurs because the symbols are declared extern, and not extern "C" in fips_premain.c (from around line 85):

extern const void         *FIPS_text_start(),  *FIPS_text_end();
extern const unsigned char FIPS_rodata_start[], FIPS_rodata_end[];
extern unsigned char       FIPS_signature[20];
extern unsigned int        FIPS_incore_fingerprint(unsigned char *,unsigned int);

fips_premain.c cannot be changed because it is sequestered under the FIPS process. A different method must be used to link a C++ project.

FIPS Object Module

The FIPS Object Module is built in accordance with the steps detailed in the OpenSSL FIPS 2.0 Security Policy. The OpenSSL Foundation also publishes a user guide at User Guide for the OpenSSL FIPS Object Module v2.0. A grossly simplified digest of the steps is presented below, but the steps are essentially the same steps detailed in the guides and on other pages in the wiki.

# Download and verify signature on openssl-fips-2.0.5.tar.gz
$ wget http://www.openssl.org/source/openssl-fips-2.0.5.tar.gz
$ wget http://www.openssl.org/source/openssl-fips-2.0.5.tar.gz.asc
$ pgp openssl-fips-2.0.5.tar.gz.asc 
pgp: Signature made Thu 20 Jun 2013 03:16:19 PM EDT using RSA key ID F295C759
...

# Build and install the FIPS Object Module
$ tar -xzf openssl-fips-2.0.5.tar.gz 
$ cd openssl-fips-2.0.5
$ ./config
$ make
$ sudo make install

# Copy required files missed by 'make install'
$ find . -iname incore
./util/incore
$ sudo cp util/incore /usr/local/ssl/fips-2.0/bin/
$ find . -iname fipsld
./fips/fipsld
$ sudo cp fips/fipsld /usr/local/ssl/fips-2.0/bin/

FIPS Capable Library

The FIP Capable Library is a version of OpenSSL that uses the FIPS Object Module. The difference between building the standard library and the capable library is simply a switch to config - the fips switch.

# # Download and verify signature on openssl-1.0.1f.tar.gz
$ wget http://www.openssl.org/source/openssl-1.0.1f.tar.gz
$ wget http://www.openssl.org/source/openssl-1.0.1f.tar.gz.asc
$ pgp openssl-1.0.1f.tar.gz.asc
pgp: Signature made Mon 06 Jan 2014 09:36:10 AM EST using RSA key ID F295C759

# Build and install the FIP Capable Library
$ tar -xzf openssl-1.0.1f.tar.gz
$ cd openssl-1.0.1f/
$ ./config fips no-ssl2 <other options>
$ make all
$ sudo make install

The C++ Program

The sample C++ program is named main.cpp and simply enters FIPS mode.

#include <iostream>
using std::cout;
using std::endl;

#include <openssl/evp.h>
#include <openssl/err.h>

int main(int argc, char* argv[])
{
    int rc, mode;

    mode = FIPS_mode();
    if(mode == 0)
    {
        rc = FIPS_mode_set(1);
        if(rc == 0) {
            cout << "Failed to enable FIPS mode, ";
            cout << "error: " << ERR_get_error() << endl;
        } else {
            cout << "Enabled FIPS mode" << endl;
        }
    }
    else
    {
        cout << "Already in FIPS mode" << endl;
    }

    return 0;
}

Build Script

A build script is used to build the sample program. The build script performs the following and is shown below:

  • Sets OPENSSLDIR if its empty
  • Sets FIPS_SIG to point to incore
  • Sets FIPSLIBDIR to point to $OPENSSLDIR/fips-2.0/lib
  • Collects C sources in C_SOURCES
  • Collects C++ sources in CXX_SOURCES
  • Invokes CC on C_SOURCES
  • Invokes CXX on CXX_SOURCES
  • Collects object files in PROG_OBJECTS
  • Sets FIPSLD_CC to point to CXX
  • Sets CXX to point to fipsld
  • Links with fipsld via CXX

Compiling occurs as normal, and includes -std=c++11 (via CXXSTD) and -stdlib=libstdc++ (via CXXSTDLIB). Linking will still use the compiler driver and CXXFLAGS, but it omits -std=c++11 and -stdlib=libstdc++.

#! /bin/bash

################################
# Standard build stuff

CC=/usr/local/bin/clang
CXX=/usr/local/bin/clang++

CFLAGS="-Wall -Wextra"
CXXFLAGS="-Wall -Wextra"
CXXSTD="-std=c++11"
CXXSTDLIB="-stdlib=libstdc++"

C_SOURCES=`ls *.c 2>/dev/null`
CXX_SOURCES=`ls *.cpp 2>/dev/null`

if [ ! -z "$C_SOURCES" ]; then 
  echo "Compiling C sources..."
    (set -x; $CC $CFLAGS $C_SOURCES -c)
fi

if [ ! -z "$CXX_SOURCES" ]; then 
  echo "Compiling C++ sources..."
    (set -x; $CXX $CXXFLAGS $CXXSTD $CXXSTDLIB $CXX_SOURCES -c)
fi

################################
# OpenSSL and fipsld

if [ -z $OPENSSLDIR ] && [ -d /usr/local/ssl ]; then
  OPENSSLDIR=/usr/local/ssl
fi

export FIPS_SIG=`find $OPENSSLDIR/fips-2.0 -iname incore 2>/dev/null`
export FIPSLIBDIR=`find $OPENSSLDIR/fips-2.0 -iname lib 2>/dev/null`

export FIPSLD_CC=$CXX
export CXX=`find $OPENSSLDIR/fips-2.0 -iname fipsld++ 2>/dev/null`

if [ -z "$CXX" ]; then
  echo "Could not locate 'fipsld' in $OPENSSLDIR/fips-2.0"
fi

if [ -z "$FIPS_SIG" ]; then
  echo "Could not locate 'incore' in $OPENSSLDIR/fips-2.0"
fi

if [ -z "$FIPSLIBDIR" ]; then
  echo "Could not locate 'FIPS library directory' in $OPENSSLDIR/fips-2.0"
fi

################################
# Back to linking

PROG_OBJECTS=`ls *.o`

echo "Linking..."
  (set -x; $CXX $CXXFLAGS -o main.exe $PROG_OBJECTS $OPENSSLDIR/lib/libssl.a $OPENSSLDIR/lib/libcrypto.a -ldl)

fipsld Modifications

You have to make 3 modifications to fipsld++. To begin, make a copy of fipsld and name it fipsld++.

First Change

Open fipsld++ and find occurrences where fips_premain.c is compiled. There are four instances around lines 127, 146, 175 and 194. They look like:

${CC}  ${CANISTER_O_CMD:+"${CANISTER_O_CMD}"} \
    "${PREMAIN_C}" \
    ${_WL_PREMAIN} "$@"

Change the lines so that -x c proceeds "${PREMAIN_C}", and -x none follows it. An example is shown below.

${CC}  ${CANISTER_O_CMD:+"${CANISTER_O_CMD}"} \
    -x c "${PREMAIN_C}" -x none \
    ${_WL_PREMAIN} "$@"

If you build now, you will receive an error similar to main.exe is not cross-compiler aware:

:+/usr/local/ssl/fips-2.0/bin/incore ./main.exe
./main.exe is not cross-compiler aware.

Second Change

To fix the main.exe is not cross-compiler aware error, find occurrences where incore is invoked. incore is invoked through {FIPS_SIG}, and there are 2 instances around line 131 and 179. They look like:

if [ "x${FIPS_SIG}" != "x" ]; then
    # embed signature
    "${FIPS_SIG}" "${TARGET}"
    [ $? -ne 42 ] && exit $?
fi

Change the lines so the invocation includes the -exe switch:

if [ "x${FIPS_SIG}" != "x" ]; then
    # embed signature
    "${FIPS_SIG}" -exe "${TARGET}"
    [ $? -ne 42 ] && exit $?
fi

If you build now, you will calculate the expected signature using your program (main.exe below).

:+/usr/local/ssl/fips-2.0/bin/incore -exe ./main.exe
7d0e8dfc1912fca2b02e3e4cc5b0f05ee90fc67d:+[ 0 -ne 42 ]
:+exit 0

But the fipsld++ program exits early so that it only spews the expected signature:

$ ./main.exe 
669f9b0dfd1654fa0ff49574d988b8bfbd17338f

Third Change

To fix the early exit of fipsld++, find the second occurrence of [ $? -ne 42 ] && exit $? and delete it. It happens around line 180:

if [ "x${FIPS_SIG}" != "x" ]; then
    # embed signature
    "${FIPS_SIG}" -exe "${TARGET}"
fi

After deleting the second instance of the early exit, your program will run as expected after building.

Unified Diff

The unified diff between fipsld and fipsld++ is shown below.

/usr/local/ssl/fips-2.0/bin$ diff -u fipsld fipsld++ 
--- fipsld	2014-03-09 14:24:55.868366777 -0400
+++ fipsld++	2014-03-09 17:10:38.069438488 -0400
@@ -124,12 +124,12 @@
 
 	/bin/rm -f "${TARGET}"
 	${CC}	${CANISTER_O_CMD:+"${CANISTER_O_CMD}"} \
-		"${PREMAIN_C}" \
+		-x c "${PREMAIN_C}" -x none \
 		${_WL_PREMAIN} "$@"
 
 	if [ "x${FIPS_SIG}" != "x" ]; then
 		# embed signature
-		"${FIPS_SIG}" "${TARGET}"
+		"${FIPS_SIG}" -exe "${TARGET}"
 		[ $? -ne 42 ] && exit $?
 	fi
 
@@ -143,7 +143,7 @@
 
 	# recompile with signature...
 	${CC}	${CANISTER_O_CMD:+"${CANISTER_O_CMD}"} \
-		-DHMAC_SHA1_SIG=\"${SIG}\" "${PREMAIN_C}" \
+		-DHMAC_SHA1_SIG=\"${SIG}\" -x c "${PREMAIN_C}" -x none \
 		${_WL_PREMAIN} "$@"
 	;;
 
@@ -172,13 +172,12 @@
 
 	/bin/rm -f "${TARGET}"
 	${CC}	${CANISTER_O_CMD:+"${CANISTER_O_CMD}"} \
-		"${PREMAIN_C}" \
+		-x c "${PREMAIN_C}" -x none \
 		${_WL_PREMAIN} "$@"
 
 	if [ "x${FIPS_SIG}" != "x" ]; then
 		# embed signature
-		"${FIPS_SIG}" "${TARGET}"
-		[ $? -ne 42 ] && exit $?
+		"${FIPS_SIG}" -exe "${TARGET}"
 	fi
 
 	# generate signature...
@@ -191,7 +190,7 @@
 
 	# recompile with signature...
 	${CC}	${CANISTER_O_CMD:+"${CANISTER_O_CMD}"} \
-		-DHMAC_SHA1_SIG=\"${SIG}\" "${PREMAIN_C}" \
+		-DHMAC_SHA1_SIG=\"${SIG}\" -x c "${PREMAIN_C}" -x none \
 		${_WL_PREMAIN} "$@"
 	;;
 esac

Program Correctness

You can confirm the program executes successfully with the message Enabled FIPS mode:

$ ./main.exe 
Enabled FIPS mode

In addition, there will be no shared object dependencies on libssl or libcrypto:

$ ldd ./main.exe 
    linux-vdso.so.1 =>  (0x00007fff4d5ff000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f4a81857000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f4a81550000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f4a812cd000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f4a810b7000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4a80d2c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f4a81a72000)

Downloads

fipsld++.tar.gz - the modified fipsld++