Skip to contentSkip to conversation

Added support for OpenSSL FIPS Object Module v2.0 validated encryption

PATCH In review [email protected]... 2 participants 3 messages Dec 4, 2017 – Jan 21, 2018 Kbuild

lore
Share & tools
lore
#0 lore raw

PATCH 1/2 Added support for OpenSSL FIPS Object Module v2.0 validated encryption

From: Jim Carroll <[email protected]...>

---
 INSTALL                      | 78 ++++++++++++++++++++++++++++++++++++++++++++
 Makefile.am                  |  5 +++
 configure.ac                 | 41 +++++++++++++++++++++++
 src/openvpn/crypto.c         |  2 +-
 src/openvpn/crypto_backend.h |  3 +-
 src/openvpn/crypto_openssl.c | 16 ++++++++-
 src/openvpn/crypto_openssl.h |  8 +++++
 src/openvpn/ntlm.c           |  2 +-
 src/openvpn/openvpn.c        |  9 +++++
 src/openvpn/options.c        | 18 ++++++++++
 src/openvpn/options.h        |  3 ++
 src/openvpn/ssl.c            | 12 +++++--
 src/openvpn/ssl.h            |  4 +++
 13 files changed, 195 insertions(+), 6 deletions(-)
INSTALL +78 -0
index 3a31e6f..b001cb1 100644
@@ -305,6 +305,84 @@ TUN/TAP Driver Configuration:
 
 *************************************************************************
 
+OpenSSL FIPS Object Module v2.0 Configuration:
+
+These instructions were adapted from
+
+    https://www.openssl.org/docs/fipsnotes.html
+
+Requirements:
+
+    * OpenSSL 1.0.2m
+    * openssl-fips-2.0.2
+
+WARNING
+
+To install FIPS Validated encryption, you must follow the instructions in the
+FIPS 2.0 User's Guide precisely. You are not permitted to modify any of the FIPS
+build artifacts, makefiles or scripts. The FIPS 2.0 module is only compatible with
+OpenSSL 1.0.1 and 1.0.2.
+
+These instructions describe the use of OpenSSL 1.0.2m.
+
+PRE-INSTALLATION CHECKUP:
+
+    The INSTALLATION procecure describes how to install an OpenSSL library that
+    is built with FIPS support. If your platform already provides a FIPS
+    enabled library you can skip to step 6 (build OpenVPN).
+
+INSTALLATION:
+
+    1. Surf to https://www.openssl.org/source/
+    2. Download source AND validate the download was correct (preferably using PGP)
+    3. Untar and uncompress tarball
+    4. You must build using this precise command (do NOT choose any other options):
+
+            # ./config && make install
+
+            (you may optionslly pass 'no-asm' to config)
+
+       If the above procedure does not build on your system -- STOP. You are not
+       building on a FIPS supported platform, and therefore will not have a
+       FIPS validated encryption environment. See chapter 3 of the FIPS 2.0
+       User's Guide for the complete list of supported platforms:
+
+            https://openssl.org/docs/fips/UserGuide-2.0.pdf
+
+    5. Download, build & install openssl 1.0.2m (you are permitted to
+       modify this step to suite your preferences):
+
+            # git clone https://github.com/openssl/openssl.git
+            # (cd openssl && \
+                    git checkout OpenSSL_1_0_2m && \
+                    ./config fips && \
+                    make depend && \
+                    make install)
+
+    6. Now build openvpn and tell it where to find you recently installed OpenSSL
+
+            # ./configure --enable-fips-mode \
+                    OPENSSL_CFLAGS=-I/usr/local/ssl/include \
+                    OPENSSL_LIBS="-ldl -L/usr/local/ssl/lib -lssl -lcrypto"
+
+            # make install
+
+    7. You can confirm FIPS mode is available with the command
+
+            # ./openvpn --version | grep 'library version'
+            library versions: OpenSSL 1.0.2m-fips  2 Nov 2017, LZO 2.08
+
+USAGE:
+
+The above adds a new '--enable-fips-mode' command line option to OpenVPN. Add this to your
+invocation statement. If you've successfully configured OpenVPN for FIPS mode, check your
+OpenVPN logs for the statement:
+
+    *** FIPS MODE ENABLE ***
+
+
+*************************************************************************
+
 CAVEATS & BUGS:
 
 * I have noticed cases where TCP sessions tunneled over the Linux
Makefile.am +5 -0
index 773b786..6d571ec 100644
@@ -83,6 +83,11 @@ rootdir=$(prefix)
 root_DATA = version.sh
 endif
 
+if FIPSMODE
+export CC
+export FIPSLD_CC
+endif
+
 config-version.h:
 	@CONFIGURE_GIT_CHFILES="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) diff-files --name-status -r --ignore-submodules --quiet -- || echo \"+\"`"; \
 	CONFIGURE_GIT_UNCOMMITTED="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) diff-index --cached  --quiet --ignore-submodules HEAD || echo \"*\"`"; \
configure.ac +41 -0

59 lines not on this page · raw message

src/openvpn/crypto.c +1 -1
index 3096f3b..97d117b 100644
@@ -852,7 +852,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key,
     if (kt->digest && kt->hmac_length > 0)
     {
         ctx->hmac = hmac_ctx_new();
-        hmac_ctx_init(ctx->hmac, key->hmac, kt->hmac_length, kt->digest);
+        hmac_ctx_init(ctx->hmac, key->hmac, kt->hmac_length, kt->digest, false);
 
         msg(D_HANDSHAKE,
             "%s: Using %d bit message hash '%s' for HMAC authentication",
src/openvpn/crypto_backend.h +2 -1
index 567fd9b..8790ca5 100644
@@ -604,10 +604,11 @@ void hmac_ctx_free(hmac_ctx_t *ctx);
  * @param key           The key to use for the HMAC
  * @param key_len       The key length to use
  * @param kt            Static message digest parameters
+ * @param prf_use       Intended use for PRF in TLS protocol
  *
  */
 void hmac_ctx_init(hmac_ctx_t *ctx, const uint8_t *key, int key_length,
-                   const md_kt_t *kt);
+                   const md_kt_t *kt, bool prf_use);
 
 /*
  * Free the given HMAC context.
src/openvpn/crypto_openssl.c +15 -1
index 20a519e..fe4cef3 100644
@@ -159,6 +159,18 @@ crypto_init_lib(void)
 #endif
 }
 
+int
+crypto_enable_fips_mode(int mode)
+{
+    if (!FIPS_mode_set(mode))
+    {
+        ERR_print_errors_fp(stderr);
+        return 1;
+    }
+    msg(M_INFO, "*** FIPS MODE ENABLED ***");
+    return 0;
+}
+
 void
 crypto_uninit_lib(void)
 {
@@ -926,11 +938,13 @@ hmac_ctx_free(HMAC_CTX *ctx)
 
 void
 hmac_ctx_init(HMAC_CTX *ctx, const uint8_t *key, int key_len,
-              const EVP_MD *kt)
+              const EVP_MD *kt, bool prf_use)
 {
     ASSERT(NULL != kt && NULL != ctx);
 
     HMAC_CTX_reset(ctx);
+    if (kt == EVP_md5() && prf_use)
+        HMAC_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
     HMAC_Init_ex(ctx, key, key_len, kt, NULL);
 
     /* make sure we used a big enough key */
src/openvpn/crypto_openssl.h +8 -0
index 60a2812..fbc8b2a 100644
@@ -102,4 +102,12 @@ void crypto_print_openssl_errors(const unsigned int flags);
     } while (false)
 
 
+/**
+ * Enable FIPS Mode. Returns non-zero to indicate an error.
+ *
+ * @param mode         Should be 1. Future versions of OpenSSL FIPS
+ *                     code are expected to accept extended modes.
+ */
+int crypto_enable_fips_mode(int mode);
+
 #endif /* CRYPTO_OPENSSL_H_ */
src/openvpn/ntlm.c +1 -1
index 077fa3e..fe39ab1 100644
@@ -88,7 +88,7 @@ gen_hmac_md5(const uint8_t *data, int data_len, const uint8_t *key, int key_len,
     const md_kt_t *md5_kt = md_kt_get("MD5");
     hmac_ctx_t *hmac_ctx = hmac_ctx_new();
 
-    hmac_ctx_init(hmac_ctx, key, key_len, md5_kt);
+    hmac_ctx_init(hmac_ctx, key, key_len, md5_kt, false);
     hmac_ctx_update(hmac_ctx, data, data_len);
     hmac_ctx_final(hmac_ctx, result);
     hmac_ctx_cleanup(hmac_ctx);
src/openvpn/openvpn.c +9 -0
index e237ee5..da8e852 100644
@@ -210,6 +210,15 @@ openvpn_main(int argc, char *argv[])
             /* parse command line options, and read configuration file */
             parse_argv(&c.options, argc, argv, M_USAGE, OPT_P_DEFAULT, NULL, c.es);
 
+#if ENABLE_FIPS
+            if (c.options.fips_mode)
+            {
+                if (enable_fips_mode(c.options.fips_mode))
+                {
+                    break;
+                }
+            }
+#endif
 #ifdef ENABLE_PLUGIN
             /* plugins may contribute options configuration */
             init_verb_mute(&c, IVM_LEVEL_1);
src/openvpn/options.c +18 -0
index 7be5f38..99d3ccc 100644
@@ -517,6 +517,11 @@ static const char usage_message[] =
     "\n"
     "Data Channel Encryption Options (must be compatible between peers):\n"
     "(These options are meaningful for both Static Key & TLS-mode)\n"
+#ifdef ENABLE_FIPS
+	"--enable-fips-mode : Enable OpenSSL FIPS Object Module v2.0.\n"
+	"                  Setting this on the server will enforce FIPS validated\n"
+	"                  encryption on both client and server.\n"
+#endif
     "--secret f [d]  : Enable Static Key encryption mode (non-TLS).\n"
     "                  Use shared secret file f, generate with --genkey.\n"
     "                  The optional d parameter controls key directionality.\n"
@@ -847,6 +852,9 @@ init_options(struct options *o, const bool init_gc)
     o->scheduled_exit_interval = 5;
 #endif
     o->ciphername = "BF-CBC";
+#ifdef ENABLE_FIPS
+	o->fips_mode = 0;
+#endif
 #ifdef HAVE_AEAD_CIPHER_MODES /* IV_NCP=2 requires GCM support */
     o->ncp_enabled = true;
 #else
@@ -1550,6 +1558,9 @@ show_settings(const struct options *o)
     SHOW_INT(persist_mode);
 #endif
 
+#ifdef ENABLE_FIPS
+    SHOW_INT(fips_mode);
+#endif
     SHOW_BOOL(show_ciphers);
     SHOW_BOOL(show_digests);
     SHOW_BOOL(show_engines);
@@ -7389,6 +7400,13 @@ add_option(struct options *options,
         }
     }
 #endif /* USE_COMP */
+#ifdef ENABLE_FIPS
+    else if (streq(p[0], "enable-fips-mode") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_GENERAL);
+        options->fips_mode = 1;
+    }
+#endif
     else if (streq(p[0], "show-ciphers") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +3 -0
index f70760c..6ffe646 100644
@@ -186,6 +186,9 @@ struct options
     bool persist_config;
     int persist_mode;
 
+#ifdef ENABLE_FIPS
+	int fips_mode;
+#endif
     const char *key_pass_file;
     bool show_ciphers;
     bool show_digests;
src/openvpn/ssl.c +10 -2
index 7b42845..3992f4d 100644
@@ -352,6 +352,14 @@ init_ssl_lib(void)
     crypto_init_lib();
 }
 
+#if ENABLE_FIPS
+int
+enable_fips_mode(int mode)
+{
+	return crypto_enable_fips_mode(mode);
+}
+#endif
+
 void
 free_ssl_lib(void)
 {
@@ -1638,8 +1646,8 @@ tls1_P_hash(const md_kt_t *md_kt,
     chunk = md_kt_size(md_kt);
     A1_len = md_kt_size(md_kt);
 
-    hmac_ctx_init(ctx, sec, sec_len, md_kt);
-    hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt);
+    hmac_ctx_init(ctx, sec, sec_len, md_kt, true);
+    hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt, true);
 
     hmac_ctx_update(ctx,seed,seed_len);
     hmac_ctx_final(ctx, A1);
src/openvpn/ssl.h +4 -0
index dd1ab0f..f251765 100644
@@ -598,4 +598,8 @@ bool is_hard_reset(int op, int key_method);
 
 void delayed_auth_pass_purge(void);
 
+#if ENABLE_FIPS
+int enable_fips_mode(int mode);
+#endif
+
 #endif /* ifndef OPENVPN_SSL_H */
Signature · 12 lines
2.1.4


From 85a23fd158f36c444e59ff41c35d0750917898b1 Mon Sep 17 00:00:00 2001
From: Jim Carroll <[email protected]...>
Date: Mon, 4 Dec 2017 16:12:42 -0500
Subject: [PATCH 2/2] fixed typo

Signed-off-by: Jim Carroll <[email protected]...>
---
 INSTALL | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
INSTALL +1 -1
index b001cb1..0bb25b4 100644
@@ -327,7 +327,7 @@ These instructions describe the use of OpenSSL 1.0.2m.
 
 PRE-INSTALLATION CHECKUP:
 
-    The INSTALLATION procecure describes how to install an OpenSSL library that
+    The INSTALLATION procedure describes how to install an OpenSSL library that
     is built with FIPS support. If your platform already provides a FIPS
     enabled library you can skip to step 6 (build OpenVPN).
 
Signature · 1 line
2.1.4
#1 lore raw

Hi,

Sorry for taking so long to review. At least some early review comments:

Quoted 149 lines · On 04-12-17 22:16, [email protected]... wrote:

From: Jim Carroll <[email protected]...>

---
 INSTALL                      | 78 ++++++++++++++++++++++++++++++++++++++++++++
 Makefile.am                  |  5 +++
 configure.ac                 | 41 +++++++++++++++++++++++
 src/openvpn/crypto.c         |  2 +-
 src/openvpn/crypto_backend.h |  3 +-
 src/openvpn/crypto_openssl.c | 16 ++++++++-
 src/openvpn/crypto_openssl.h |  8 +++++
 src/openvpn/ntlm.c           |  2 +-
 src/openvpn/openvpn.c        |  9 +++++
 src/openvpn/options.c        | 18 ++++++++++
 src/openvpn/options.h        |  3 ++
 src/openvpn/ssl.c            | 12 +++++--
 src/openvpn/ssl.h            |  4 +++
 13 files changed, 195 insertions(+), 6 deletions(-)
INSTALL +78 -0
index 3a31e6f..b001cb1 100644
@@ -305,6 +305,84 @@ TUN/TAP Driver Configuration:
 
 *************************************************************************
 
+OpenSSL FIPS Object Module v2.0 Configuration:
+
+These instructions were adapted from
+
+    https://www.openssl.org/docs/fipsnotes.html
+
+Requirements:
+
+    * OpenSSL 1.0.2m
+    * openssl-fips-2.0.2
+
+WARNING
+
+To install FIPS Validated encryption, you must follow the instructions in the
+FIPS 2.0 User's Guide precisely. You are not permitted to modify any of the FIPS
+build artifacts, makefiles or scripts. The FIPS 2.0 module is only compatible with
+OpenSSL 1.0.1 and 1.0.2.
+
+These instructions describe the use of OpenSSL 1.0.2m.
+
+PRE-INSTALLATION CHECKUP:
+
+    The INSTALLATION procecure describes how to install an OpenSSL library that
+    is built with FIPS support. If your platform already provides a FIPS
+    enabled library you can skip to step 6 (build OpenVPN).
+
+INSTALLATION:
+
+    1. Surf to https://www.openssl.org/source/
+    2. Download source AND validate the download was correct (preferably using PGP)
+    3. Untar and uncompress tarball
+    4. You must build using this precise command (do NOT choose any other options):
+
+            # ./config && make install
+
+            (you may optionslly pass 'no-asm' to config)
+
+       If the above procedure does not build on your system -- STOP. You are not
+       building on a FIPS supported platform, and therefore will not have a
+       FIPS validated encryption environment. See chapter 3 of the FIPS 2.0
+       User's Guide for the complete list of supported platforms:
+
+            https://openssl.org/docs/fips/UserGuide-2.0.pdf
+
+    5. Download, build & install openssl 1.0.2m (you are permitted to
+       modify this step to suite your preferences):
+
+            # git clone https://github.com/openssl/openssl.git
+            # (cd openssl && \
+                    git checkout OpenSSL_1_0_2m && \
+                    ./config fips && \
+                    make depend && \
+                    make install)
+
+    6. Now build openvpn and tell it where to find you recently installed OpenSSL
+
+            # ./configure --enable-fips-mode \
+                    OPENSSL_CFLAGS=-I/usr/local/ssl/include \
+                    OPENSSL_LIBS="-ldl -L/usr/local/ssl/lib -lssl -lcrypto"
+
+            # make install
+
+    7. You can confirm FIPS mode is available with the command
+
+            # ./openvpn --version | grep 'library version'
+            library versions: OpenSSL 1.0.2m-fips  2 Nov 2017, LZO 2.08
+
+USAGE:
+
+The above adds a new '--enable-fips-mode' command line option to OpenVPN. Add this to your
+invocation statement. If you've successfully configured OpenVPN for FIPS mode, check your
+OpenVPN logs for the statement:
+
+    *** FIPS MODE ENABLE ***
+
+
+*************************************************************************
+
 CAVEATS & BUGS:
 
 * I have noticed cases where TCP sessions tunneled over the Linux
Makefile.am +5 -0
index 773b786..6d571ec 100644
@@ -83,6 +83,11 @@ rootdir=$(prefix)
 root_DATA = version.sh
 endif
 
+if FIPSMODE
+export CC
+export FIPSLD_CC
+endif
+
 config-version.h:
 	@CONFIGURE_GIT_CHFILES="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) diff-files --name-status -r --ignore-submodules --quiet -- || echo \"+\"`"; \
 	CONFIGURE_GIT_UNCOMMITTED="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) diff-index --cached  --quiet --ignore-submodules HEAD || echo \"*\"`"; \
configure.ac +11 -0
index b4fd1b3..dc74230 100644
@@ -286,6 +286,17 @@ AC_ARG_WITH(
 	[with_crypto_library="openssl"]
 )
 
+AC_ARG_ENABLE(
+    [fips-mode],
+    [AS_HELP_STRING([--enable-fips-mode], [OpenSSL FIPS Object Module 2.0 @<:@default=no@:>@])],
+    [
+        if test "${with_crypto_library}" != "openssl"; then
+            AC_MSG_ERROR([enable_fips_mode requires --with_crypto_library=openssl])
+        fi
+    ],
+    [enable_fips_mode="no"]
+)
+
 AC_ARG_VAR([PLUGINDIR], [Path of plug-in directory @<:@default=LIBDIR/openvpn/plugins@:>@])
 if test -n "${PLUGINDIR}"; then
 	plugindir="${PLUGINDIR}"
@@ -948,6 +959,35 @@ if test "${with_crypto_library}" = "openssl"; then
 		]
 	)

configure.ac if test "${with_crypto_library}" = "openssl"; then · line 970

Quoted 10 lines
configure.ac +9 -0
 
+    if test "${enable_fips_mode}" = "yes"; then
+        AC_CHECK_FUNCS(
+            [ \
+                FIPS_mode \
+                FIPS_mode_set \
+                SSLeay_version
+            ],
+            [],
+            [AC_MSG_ERROR([Incorrect version of OpenSSL, require 1.0.2])]

This isn't really the version check that fails, right? Something like "Couldn't find functions required for FIPS" sounds more accurate.

configure.ac if test "${with_crypto_library}" = "openssl"; then · line 980

Quoted 10 lines
configure.ac +10 -0
+            )
+        AC_RUN_IFELSE(
+            [AC_LANG_PROGRAM(
+                [[#include <openssl/crypto.h>]],
+                [[printf("%s\n", SSLeay_version(SSLEAY_DIR));]])
+            ],
+            [AC_SUBST(OPENSSLDIR,
+                [[`./conftest$EXEEXT | $SED -n 's/.*"\(.*\)".*/\1/p'`]])
+            ]
+        )

Instead of calling SSLeay_version(), consider using the OPENSSL_VERSION define, like we already do elsewhere in configure.ac. That will not break as soon as there is a FIPS-compliant 1.1 release (which the openssl devs say there will be "soon").

Quoted 39 lines
configure.ac +11 -0
+        if ! test -x "${OPENSSLDIR}/fips-2.0/bin/fipsld"; then
+            AC_MSG_ERROR([Incomplete OpenSSL FIPS installation; missing fipsld])
+        fi
+        AC_SUBST([FIPSLD_CC], ["${CC}"])
+        AC_SUBST([CC], ["${OPENSSLDIR}/fips-2.0/bin/fipsld"])
+        export CC
+        export FIPSLD_CC
+        AC_DEFINE([ENABLE_FIPS], [1], [Enable OpenSSL FIPS 2.0 Options])
+    fi
+
 	CFLAGS="${saved_CFLAGS}"
 	LIBS="${saved_LIBS}"
 
@@ -1359,6 +1399,7 @@ AM_CONDITIONAL([GIT_CHECKOUT], [test "${GIT_CHECKOUT}" = "yes"])
 AM_CONDITIONAL([ENABLE_PLUGIN_AUTH_PAM], [test "${enable_plugin_auth_pam}" = "yes"])
 AM_CONDITIONAL([ENABLE_PLUGIN_DOWN_ROOT], [test "${enable_plugin_down_root}" = "yes"])
 AM_CONDITIONAL([HAVE_LD_WRAP_SUPPORT], [test "${have_ld_wrap_support}" = "yes"])
+AM_CONDITIONAL([FIPSMODE], [test "${enable_fips_mode}" = "yes"])
 
 sampledir="\$(docdir)/sample"
 AC_SUBST([plugindir])
src/openvpn/crypto.c +1 -1
index 3096f3b..97d117b 100644
@@ -852,7 +852,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key,
     if (kt->digest && kt->hmac_length > 0)
     {
         ctx->hmac = hmac_ctx_new();
-        hmac_ctx_init(ctx->hmac, key->hmac, kt->hmac_length, kt->digest);
+        hmac_ctx_init(ctx->hmac, key->hmac, kt->hmac_length, kt->digest, false);
 
         msg(D_HANDSHAKE,
             "%s: Using %d bit message hash '%s' for HMAC authentication",
src/openvpn/crypto_backend.h +0 -0
index 567fd9b..8790ca5 100644

src/openvpn/crypto_backend.h void hmac_ctx_free(hmac_ctx_t *ctx); · line 611

Quoted 9 lines
src/openvpn/crypto_backend.h +2 -1
@@ -604,10 +604,11 @@ void hmac_ctx_free(hmac_ctx_t *ctx);
  * @param key           The key to use for the HMAC
  * @param key_len       The key length to use
  * @param kt            Static message digest parameters
+ * @param prf_use       Intended use for PRF in TLS protocol
  *
  */
 void hmac_ctx_init(hmac_ctx_t *ctx, const uint8_t *key, int key_length,
-                   const md_kt_t *kt);
+                   const md_kt_t *kt, bool prf_use);

Instead of adding this parameter, I'd prefer to add a hmac_ctx_set_fips_prf() function. That way all the non-prf hmac calls don't have to change.

Quoted 27 lines
src/openvpn/crypto_backend.h +0 -0
 /*
  * Free the given HMAC context.
src/openvpn/crypto_openssl.c +12 -0
index 20a519e..fe4cef3 100644
@@ -159,6 +159,18 @@ crypto_init_lib(void)
 #endif
 }
 
+int
+crypto_enable_fips_mode(int mode)
+{
+    if (!FIPS_mode_set(mode))
+    {
+        ERR_print_errors_fp(stderr);
+        return 1;
+    }
+    msg(M_INFO, "*** FIPS MODE ENABLED ***");
+    return 0;
+}
+
 void
 crypto_uninit_lib(void)
 {
@@ -926,11 +938,13 @@ hmac_ctx_free(HMAC_CTX *ctx)
 

src/openvpn/crypto_openssl.c hmac_ctx_free(HMAC_CTX *ctx) · line 947

Quoted 10 lines
src/openvpn/crypto_openssl.c +3 -1
 void
 hmac_ctx_init(HMAC_CTX *ctx, const uint8_t *key, int key_len,
-              const EVP_MD *kt)
+              const EVP_MD *kt, bool prf_use)
 {
     ASSERT(NULL != kt && NULL != ctx);
 
     HMAC_CTX_reset(ctx);
+    if (kt == EVP_md5() && prf_use)
+        HMAC_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);

Please always use braces for a branch.

Quoted 108 lines
src/openvpn/crypto_openssl.c +0 -0
     HMAC_Init_ex(ctx, key, key_len, kt, NULL);
 
     /* make sure we used a big enough key */
src/openvpn/crypto_openssl.h +8 -0
index 60a2812..fbc8b2a 100644
@@ -102,4 +102,12 @@ void crypto_print_openssl_errors(const unsigned int flags);
     } while (false)
 
 
+/**
+ * Enable FIPS Mode. Returns non-zero to indicate an error.
+ *
+ * @param mode         Should be 1. Future versions of OpenSSL FIPS
+ *                     code are expected to accept extended modes.
+ */
+int crypto_enable_fips_mode(int mode);
+
 #endif /* CRYPTO_OPENSSL_H_ */
src/openvpn/ntlm.c +1 -1
index 077fa3e..fe39ab1 100644
@@ -88,7 +88,7 @@ gen_hmac_md5(const uint8_t *data, int data_len, const uint8_t *key, int key_len,
     const md_kt_t *md5_kt = md_kt_get("MD5");
     hmac_ctx_t *hmac_ctx = hmac_ctx_new();
 
-    hmac_ctx_init(hmac_ctx, key, key_len, md5_kt);
+    hmac_ctx_init(hmac_ctx, key, key_len, md5_kt, false);
     hmac_ctx_update(hmac_ctx, data, data_len);
     hmac_ctx_final(hmac_ctx, result);
     hmac_ctx_cleanup(hmac_ctx);
src/openvpn/openvpn.c +9 -0
index e237ee5..da8e852 100644
@@ -210,6 +210,15 @@ openvpn_main(int argc, char *argv[])
             /* parse command line options, and read configuration file */
             parse_argv(&c.options, argc, argv, M_USAGE, OPT_P_DEFAULT, NULL, c.es);
 
+#if ENABLE_FIPS
+            if (c.options.fips_mode)
+            {
+                if (enable_fips_mode(c.options.fips_mode))
+                {
+                    break;
+                }
+            }
+#endif
 #ifdef ENABLE_PLUGIN
             /* plugins may contribute options configuration */
             init_verb_mute(&c, IVM_LEVEL_1);
src/openvpn/options.c +18 -0
index 7be5f38..99d3ccc 100644
@@ -517,6 +517,11 @@ static const char usage_message[] =
     "\n"
     "Data Channel Encryption Options (must be compatible between peers):\n"
     "(These options are meaningful for both Static Key & TLS-mode)\n"
+#ifdef ENABLE_FIPS
+	"--enable-fips-mode : Enable OpenSSL FIPS Object Module v2.0.\n"
+	"                  Setting this on the server will enforce FIPS validated\n"
+	"                  encryption on both client and server.\n"
+#endif
     "--secret f [d]  : Enable Static Key encryption mode (non-TLS).\n"
     "                  Use shared secret file f, generate with --genkey.\n"
     "                  The optional d parameter controls key directionality.\n"
@@ -847,6 +852,9 @@ init_options(struct options *o, const bool init_gc)
     o->scheduled_exit_interval = 5;
 #endif
     o->ciphername = "BF-CBC";
+#ifdef ENABLE_FIPS
+	o->fips_mode = 0;
+#endif
 #ifdef HAVE_AEAD_CIPHER_MODES /* IV_NCP=2 requires GCM support */
     o->ncp_enabled = true;
 #else
@@ -1550,6 +1558,9 @@ show_settings(const struct options *o)
     SHOW_INT(persist_mode);
 #endif
 
+#ifdef ENABLE_FIPS
+    SHOW_INT(fips_mode);
+#endif
     SHOW_BOOL(show_ciphers);
     SHOW_BOOL(show_digests);
     SHOW_BOOL(show_engines);
@@ -7389,6 +7400,13 @@ add_option(struct options *options,
         }
     }
 #endif /* USE_COMP */
+#ifdef ENABLE_FIPS
+    else if (streq(p[0], "enable-fips-mode") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_GENERAL);
+        options->fips_mode = 1;
+    }
+#endif
     else if (streq(p[0], "show-ciphers") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +0 -0
index f70760c..6ffe646 100644

src/openvpn/options.h struct options · line 191

Quoted 6 lines
src/openvpn/options.h +3 -0
@@ -186,6 +186,9 @@ struct options
     bool persist_config;
     int persist_mode;
 
+#ifdef ENABLE_FIPS
+	int fips_mode;
+#endif

Use spaces for indentation. Also, sounds like this should be bool, rather than an int?

Quoted 39 lines
src/openvpn/options.h +0 -0
     const char *key_pass_file;
     bool show_ciphers;
     bool show_digests;
src/openvpn/ssl.c +10 -2
index 7b42845..3992f4d 100644
@@ -352,6 +352,14 @@ init_ssl_lib(void)
     crypto_init_lib();
 }
 
+#if ENABLE_FIPS
+int
+enable_fips_mode(int mode)
+{
+	return crypto_enable_fips_mode(mode);
+}
+#endif
+
 void
 free_ssl_lib(void)
 {
@@ -1638,8 +1646,8 @@ tls1_P_hash(const md_kt_t *md_kt,
     chunk = md_kt_size(md_kt);
     A1_len = md_kt_size(md_kt);
 
-    hmac_ctx_init(ctx, sec, sec_len, md_kt);
-    hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt);
+    hmac_ctx_init(ctx, sec, sec_len, md_kt, true);
+    hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt, true);
 
     hmac_ctx_update(ctx,seed,seed_len);
     hmac_ctx_final(ctx, A1);
src/openvpn/ssl.h +0 -0
index dd1ab0f..f251765 100644

src/openvpn/ssl.h bool is_hard_reset(int op, int key_method); · line 605

Quoted 8 lines
src/openvpn/ssl.h +4 -0
@@ -598,4 +598,8 @@ bool is_hard_reset(int op, int key_method);
 
 void delayed_auth_pass_purge(void);
 
+#if ENABLE_FIPS
+int enable_fips_mode(int mode);
+#endif
+
 #endif /* ifndef OPENVPN_SSL_H */

I hope to look into this patch more, and run some test later.

Thanks so far,
-Steffan

#2 lore raw
Quoted 9 lines

Review

Quoted 9 lines
(quoted diff) +9 -0
+    if test "${enable_fips_mode}" = "yes"; then
+        AC_CHECK_FUNCS(
+            [ \
+                FIPS_mode \
+                FIPS_mode_set \
+                SSLeay_version
+            ],
+            [],
+            [AC_MSG_ERROR([Incorrect version of OpenSSL, require
Quoted 4 lines

1.0.2])]

This isn't really the version check that fails, right? Something like "Couldn't find functions required for FIPS" sounds more accurate.

The intention was to ensure the right version is being used. SSLeay_version() doesn't exists in 1.1+. If they are using a newer version of OpenSSL, the FIPS module is not valid. We also need the SSLeay_version() function so we can extract the openssl installation directory.

Quoted 8 lines

Review

Quoted 8 lines
(quoted diff) +8 -0
+            )
+        AC_RUN_IFELSE(
+            [AC_LANG_PROGRAM(
+                [[#include <openssl/crypto.h>]],
+                [[printf("%s\n", SSLeay_version(SSLEAY_DIR));]])
+            ],
+            [AC_SUBST(OPENSSLDIR,
+                [[`./conftest$EXEEXT | $SED -n
Quoted 8 lines

's/.*"\(.*\)".*/\1/p'`]])

Review

Quoted 2 lines
(quoted diff) +2 -0
+            ]
+        )

Instead of calling SSLeay_version(), consider using the OPENSSL_VERSION define, like we already do elsewhere in configure.ac. That will not break as soon as there is a FIPS-compliant 1.1 release (which the openssl devs say there will be "soon").

We are not interested in the version here -- we are trying to set OPENSSLDIR to the directory where openssl is installed, which we capture from the output of SSLeay_version(). We need the openssldir so we can find fipld and use this during the build process.

Quoted 8 lines
Quoted 1 line

void hmac_ctx_init(hmac_ctx_t *ctx, const uint8_t *key, int

key_length,

Review

Quoted 2 lines
(quoted diff) +1 -1
-                   const md_kt_t *kt);
+                   const md_kt_t *kt, bool prf_use);

Instead of adding this parameter, I'd prefer to add a hmac_ctx_set_fips_prf() function. That way all the non-prf hmac calls don't have to change.

The function is called from three places (all of which we have patched). You'd prefer to add create a largely duplicate function and then if/else tests to those three calls rather than just add a parameter?

Quoted 5 lines

Review

Quoted 3 lines
     HMAC_CTX_reset(ctx);
(quoted diff) +2 -0
+    if (kt == EVP_md5() && prf_use)
+        HMAC_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);

Please always use braces for a branch.

Agreed. I tried to follow the coding conventions in the project, but missed this one.

Quoted 6 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+#ifdef ENABLE_FIPS
+	int fips_mode;
+#endif

Use spaces for indentation. Also, sounds like this should be bool, rather than an int?

It is strongly advised to use an int. Future versions of OpenSSL FIPS_mode_set() are expected to accept extended modes. From
https://wiki.openssl.org/index.php/FIPS_mode_set()

Currently all non-zero values of ONOFF enable FIPS mode. In the future other values may specify additional actions beyond enabling FIPS mode, such as a value of 2 to designate an additional restriction to Suite B algorithms. To avoid further compatibility issues, a program is encouraged to call FIPS_mode_set() with a ONOFF value of 1 (rather than an arbitrary non-zero value).

Quoted 4 lines

I hope to look into this patch more, and run some test later.

Thanks so far,
-Steffan

Thanks Steffan

BTW: I just remembered, there are two more small changes I would like to suggest. One is to fix a typo in the documentation, and the second was to test the returned value from FIPS_mode_set() and to clean up error reporting:

INSTALL +0 -0
index 0bb25b4..62a2bee 100644
@@ -378,7 +378,7 @@ The above adds a new '--enable-fips-mode' command line

option to OpenVPN. Add th
invocation statement. If you've successfully configured OpenVPN for FIPS
mode, check your
OpenVPN logs for the statement:

- *** FIPS MODE ENABLE ***
+ *** FIPS MODE ENABLED ***

*************************************************************************

src/openvpn/crypto_openssl.c +2 -2
index fe4cef3..b55bad8 100644
@@ -162,9 +162,9 @@ crypto_init_lib(void)
 int
 crypto_enable_fips_mode(int mode)
 {
-    if (!FIPS_mode_set(mode))
+    if (FIPS_mode_set(mode) != mode)
     {
-        ERR_print_errors_fp(stderr);
+        crypto_msg(M_FATAL, "Unable to set fips mode");
         return 1;
     }
     msg(M_INFO, "*** FIPS MODE ENABLED ***");

begin 666 smime.p7s
M,( &"2J&2(;W#0$'J" ,( "0$Q"S )!@4K#@,"&@4,( &"2J&2(;W#0$' M0``H((.$3""!#8P@@,>H ,"0("0$P#08)*H9(AO<-0$%!0P;S$+, D& MU4$!A,"4T4Q%# 2!@-5! H3"T%D9%1R=7-T($%",28P) 8#500+$QU!9&14 M<G5S="!%>'1E<FYA;"!45% @3F5T=V]R:S$B," &U4$`Q,9061D5')U<W0@

M17AT97)[email protected]...$@4F]O=# >%PTP,# U,S Q,#0X,SA:%PTR,# U,S Q,#0X
M,SA:,&\Q"S )!@-5! 83`E-%,10P$@8#500*$PM!9&14<G5S="!!0C$F,"0&
M`U4$"Q,=061D5')U<[email protected]...)[email protected]...%10($YE='=O<FLQ(C @!@-5! ,3
M&4%D9%1R=7-T($5X=&5R;F%L($-!(%)O;W0P@@$B, T&"2J&2(;W#0$!`04`
M`X(!#P`P@@$*`H(!`0"W]QHSYO(`!"TYX$Y;[1^\; _-M?HCML[>FQ$SEZ0I
M3'V3G[U*O)/M`QKCC\_E;5!:UI<IE%J L$EZVRZ5_;C*OS<X+1X^D4&M<%;'
M\$\_Z#*>=,K(D%3IQE\/>)V:0#P.K&&J7A2/GH>A:E#<UYI.KP6SIG&4G'&S
M4& *QQ.=. >&`JCIJ&DF&)"K3+!/(ZLZ3X38W\Z?X6EON]="UVM$Y,>M[FU!
M7W):<[email protected]...%[email protected]...#?W`"\-PI)RVM X<ML4J$7$72I]M[36Q.ZLS1-$M\DK
MW4,`)?IAN6EJ6",1MZ<SCU9U6?7-*==&MPHK9;;30F\5LKA[^^_I75/5-%HG
M`@,!``&C@=PP@=DP'08#51T.!!8$%*V]F'HTM";W^L0F5.\#O> DRU0:, L&
M`U4=#P0$`P(!!C /!@-5'1,!`?\$!3 #`0'_,(&9!@-5'2,$@[email protected]... %*V]
MF'HTM";W^L0F5.\#O> DRU0:H7.D<3!O,0LP"08#500&$P)313$4,!(&`U4$
M"A,+061D5')U<[email protected]...(Q)C D!@-5! L3'4%D9%1R=7-T($5X=&5R;F%L(%14
M4"!.971W;W)K,2(P( 8#500#$QE!9&14<G5S="!%>'1E<FYA;"!#02!2;V]T
M@@$!, T&"2J&2(;W#0$!!04``X(!`0"PF^"%)<+6(^(/E@:2G4&8G-F$>8'9
M'EL4!R,V98^PV'>[K$%L1V"#4;#Y,CWG_/8F$\> %J6_6OR'SWAYB2&:XDP'
M"H8UO/+>4<32EK?<?D[N</T<.>L,`E$4+8Z]%N#!WT9UYR2M[/1"M(63<!!G
MNIT&-4H8TRMZS%%"H7ICT>:[H<4KPC:^$PWFO6-^>7NG"0U JVK=CXK#]O:,
M&D(%4=1%]9^G8B%H%2!#/)GG?+TDV*F1%W.(/U8;,[email protected]...'$/FLW(#IZ.+AOA
MC)B#RQ\Q\41,[email protected]...@#\?XO1> :R[IS$P.6IIY#R *+M6>8R8>59*4V((7
M6GO0O,>/3H8$,(($KS""`Y>@`P(!`@(1`. [email protected]...)K6%N>E1G:R$P#08)
M*H9(AO<-`0$+!0`P;S$+, D&`U4$!A,"4T4Q%# 2!@-5! H3"T%D9%1R=7-T
M($%",28P) 8#500+$QU!9&14<G5S="!%>'1E<FYA;"!45% @3F5T=V]R:S$B
M," &`U4$`Q,9061D5')U<[email protected]...)[email protected]...$@4F]O=# >%PTQ-#$R,C(P
M,# P,#!:%PTR,# U,S Q,#0X,SA:,(&;,0LP"08#500&$P)'0C$;,!D&`U4$
M"!,21W)E871E<B!-86YC:&5S=&5R,1 P#@8#500'$P=386QF;W)D,1HP& 8#
M500*$Q%#3TU/1$\@0T$@3&EM:71E9#%!,#\&`U4$`Q,X0T]-3T1/(%-(02TR
[email protected]...($%U=&AE;G1I8V%T:6]N(&%N9"!396-U<[email protected]...:[email protected]...$P
M@@$B, T&"2J&2(;W#0$!`04``X(!#P`P@@$*`H(!`0")L0W:>E,93G!2';Q6
[email protected]...[A)X);G4:OQ\%H3216CM(P;8+QZ44*G>8RD(M\784Z1U78C"A332@)_
MMAT)@&ZE!#W9NKL6_J&'J2Y#4D,6?*\R4,BF3UKI"-C/DR6<>XCH,&3FI/A6
M@/TJ)!0S%YFL1.5IBZ-&!DO",]3I0)\&L+&LDT"YM0B3.IPJ4Z,[email protected]...
M`X[93G8E`B$I^J-\<79/[N%?@>G[5(#;PWLU4K>$WB(]+# M,7]9O5(WL#-I
M+4/K^M:E\9=W9U&,V>XGZ[RE!SAVC*2I./_?C/4#K$F^RO=SF3H/,JN<E3H3
M/0Y&.E=T85"^QD _R^3BGZ(A`@,!``&C@@$7,((!$S ?!@-5'2,$&# 6@!2M
MO9AZ-+0F]_K$)E3O`[W@),M4&C =!@-5'0X$%@04DF%[email protected]...&BH*I/[&?QPJ/W
MM( `P>PP#@8#51T/`0'_! 0#`@&&,!(&`U4=$P$!_P0(, 8!`?\"`0`P'08#
M51TE!!8P% 8(*P8!!04'`P(&""L&`04%!P,$,!$&`U4=( 0*, @P!@8$51T@
M`#!$!@-5'1\$/3 [,#[email protected]... UAC-H='1P.B\O8W)L+G5S97)T<G5S="YC;VTO
M061D5')U<W1%>'1E<FYA;$-!4F]O="YC<FPP-08(*P8!!04'`0$$*3 G,"4&
M""L&`04%!S !AAEH='1P.B\O;V-S<"YU<V5R=')U<W0N8V]M, T&"2J&2(;W
M#0$!"P4``X(!`0`;*FZL5<$ZJXC%V.W-5?.J:V$KP D0(YD/Q69J;['UM+5W
M7@\"80#??07^$K.D@( `_/L=6VIR`@I!O 6ZP5C5)L+JU4V$^_Z"F,]8&^,B
M8YQ2^+L%-JM]6*7>JSMCY=K5<^_LX/M[XJ/_\$(CG,JVC4T^Y$L8`[*H+=38
MNT)+D&F%$-NF-S3H>^ !$*6<RCK'GT^(-&Z*9= :BKNIW,K*-M'T_,)D*36O
MUK&G<1'2`T.QCSZ:[)XR4_1VDLJ&- >Y+,KF'$K8F0W!AN*0DOM:0FHC(1#I
M9<?UU;M^ZHR%( )BZM$Z!RQ9Q9DS\CB)Y;;I%GH?>13V2A :)OI\BON;,((%
M(#""! B@`P(!`@(1`(+-2':;XZ#&U!G>^P&7OJPP#08)*H9(AO<-`0$+!0`P
[email protected]..."S )!@-5! 83`D=",1LP&08#500($Q)'<F5A=&5R($UA;F-H97-T97(Q
M$# .!@-5! <3!U-A;&9O<F0Q&C 8!@-5! H3$4-/34]$3R!#02!,:6UI=&5D
M,4$P/P8#500#$SA#3TU/1$\@4TA!+3(U-B!#;&EE;[email protected]...:&5N=&EC871I
M;[email protected]...(%-E8W5R92!%;6%I;"!#03 >%PTQ-S Q,[email protected]...,# P,#!:%PTR,# Q
M,[email protected]...,S4Y-3E:," Q'C <!@DJADB&]PT!"0$6#VII;4!C87)R;VQL+F-O;3""
M`2(P#08)*H9(AO<-`0$!!0`#@@$/`#""`0H"@@$!`+>;EY]ERMD$,P;8"I7X
M?^ /F69VS8/L_HEV(J#N= $S];)Z)NSNCEKQQL*V\)X*[3:U#K&RT"]7`^(9
M/]<<)D]QC$]^CQB)W6&GH=]!S)"#BX#@1Q)ZRNK%`>L!^4T4%*7SH4#L9W*4
MT$5<*UR%\T_56N':G<DYHV-9.='BNEU"'%TY+[]LYZ=B>CA]ND5XQ(/B6G)$
M+.45284OBU4BNP&F^_G1,%^"$.,_`")HQ!K_IW :.=0"J38^R&!!*H#$V>"H
M\A*[L =HF*&;G2L'<8*^T%BY+7S.3D[>P=G4O*#;+C4>,]Q8'[email protected]...$#LC
M'5]WL4$YZ?=VZ+H"B_33;&T"`P$``:."`=<P@@'3,!\&`U4=(P08,!: %))A
M:X+AHJ"J3^QG\<*C][2 `,'L,!T&`U4=#@06!!2S>P/T;X" O18ML([email protected]...
M(7<123 .!@-5'0\!`?\$! ,"!: P# 8#51T3`0'_! (P`# =!@-5'24$%C 4
M!@@K!@$%!0<#! 8(*P8!!04'`P([email protected]...#51T@!#\P/3 [!@PK!@$$`;(Q`0(!
M`P4P*S I!@@K!@$%!0<"`18=:'1T<',Z+R]S96-U<F4N8V]M;V1O+FYE="]#
M4%,P708#51T?!%8P5#!2H%"@3H9,:'1T<#HO+V-R;"YC;VUO9&]C82YC;VTO
M0T]-3T1/4TA!,C4V0VQI96YT075T:&5N=&EC871I;VYA;F1396-U<F5%;6%I
M;$-!+F-R;#"!D 8(*P8!!04'`0$$@8,[email protected]... P6 8(*P8!!04', *&3&AT=' Z
M+R]C<G0N8V]M;V1O8V$N8V]M+T-/34]$3U-(03(U-D-L:65N=$%U=&AE;G1I
M8V%T:6]N86YD4V5C=7)E16UA:6Q#02YC<G0P) 8(*P8!!04', &&&&AT=' Z
M+R]O8W-P+F-O;6]D;V-A+F-O;3 :!@-5'1$$$S [email protected]...]J:6U 8V%R<F]L;"YC
M;VTP#08)*H9(AO<-`0$+!0`#@@$!`'W33 4"-'Y3RQ+/>:[email protected]... 1V
MH"=?*@"]NZ7+1X&;TMD)/=KS<3BZN1X<*%FAF5TT8@\9&Y)W7&M28.,GF/5,
MV'!"E<-"(+ZQU&<SFY>52)<8(H0D6.RB<5"5L#EHH-[8IB]:W7W0)9<VHV>A
M3OF,X]<-(XS5!1:<VY>/Q-+\ZA; \X:\/*#59:$JD)8S.TZ20:Q(!CUZ+E[A
M!-V6+Y9+9_#2CX G4C7LD,*>D_T?J?X.7^&M(*ZE=+5X$_)7^:4-5GJCO[GY
MC*7/4G($(0<G(B/8FO<=,%\M\-V**+[KKM<%[email protected]...,=HU Y_GU3PBE![J;
MWYTVZ97^?=PQ@@0C,(($'P(!`3"!L3"!FS$+, D&`U4$!A,"1T(Q&S 9!@-5
M! @3$D=R96%T97(@36%N8VAE<W1E<C$0, X&`U4$!Q,'4V%L9F]R9#$:,!@&
M`U4$"A,10T]-3T1/($-!($QI;6ET960Q03 _!@-5! ,3.$-/34]$3R!32$$M
M,C4V($-L:65N="!!=71H96YT:6-A=&EO;B!A;[email protected]...=7)E($5M86EL($-!
M`A$`@LU(=IOCH,;4&=[[`9>^K# )!@4K#@,"&@4`H(("1C 8!@DJADB&]PT!
M"0,Q"P8)*H9(AO<-`0<!,!P&"2J&2(;W#0$)!3$/%PTQ.# Q,C$Q-C,W,3=:
M,",&"2J&2(;W#0$)!#$6!!2(2QGD8INMP%+TJF-J.[*+:SM#23!;!@DJADB&
M]PT!"0\Q3C!,, H&""J&2(;W#0,', X&""J&2(;W#0,"`@(`@# -!@@JADB&
M]PT#`@(!0# '!@4K#@,"!S -!@@JADB&]PT#`@(!*# '!@4K#@,"&C"[email protected]...)
M*P8!! &"-Q $,8&T,(&Q,(&;,0LP"08#500&$P)'0C$;,!D&`U4$"!,21W)E
M871E<B!-86YC:&5S=&5R,1 P#@8#500'$P=386QF;W)D,1HP& 8#500*$Q%#
M3TU/1$\@0T$@3&EM:71E9#%!,#\&`U4$`Q,X0T]-3T1/(%-([email protected]...
M96YT($%U=&AE;G1I8V%T:6]N(&%N9"!396-U<[email protected]...:[email protected]...$"$0""S4AV
MF^[email protected]...!E[ZL,('$!@LJADB&]PT!"1 ""S&!M*"!L3"!FS$+, D&`U4$
M!A,"1T(Q&S 9!@-5! @3$D=R96%T97(@36%N8VAE<W1E<C$0, X&`U4$!Q,'
M4V%L9F]R9#$:,!@&`U4$"A,10T]-3T1/($-!($QI;6ET960Q03 _!@-5! ,3
M.$-/34]$3R!32$$M,C4V($-L:65N="!!=71H96YT:6-A=&EO;B!A;[email protected]...
M=7)E($5M86EL($-!`A$`@LU(=IOCH,;4&=[[`9>^K# -!@DJADB&]PT!`0$%
M``2"`0"G<36):2:'LF4&CFW*R[&09 'LQ['W1%FZ%E5R"@(-GVD<4*8."3Q?
MI>;4"7U4;9) &8G A%AO-S?Y3EO3@"\?1#-K(V$>-+O0GDLH_3B0ZX=NTEXG
M5MW:#VT;%KJ]!2\<>6/D8O)+:<*\FQ4J`%0<U%#F'9DCZQ[1K$"ATK7Y@+O9
M-5F$+_C5J;A\UO[!DMRF&O&R!#R<X+Q]A.^^CV[]\61J!OM(/2^,; `FRMYS
[email protected]...:5"[I593HUV+1"/<A/\Z0KNUI<CWN34Y>)/:6Q96@@=/(FE<INF+T8ZW
J(M&D&2&:5@,@7=/JI!\(&A&LRF2C-VFUFTZE%9,#-H3U3=??````````
`
end