Blob Blame History Raw
# HG changeset patch
# User John Dennis <jdennis@redhat.com>
# Date 1434146324 14400
#      Fri Jun 12 17:58:44 2015 -0400
# Node ID 6096d0660e2abefcee12e502fed029663d435c54
# Parent  a5e07e90e9c8cb08c5fb46eba16bdb43b94d67c3
Some unit tests fail in FIPS mode

Resolves bug
https://bugzilla.redhat.com/show_bug.cgi?id=1194349

Essentially there were 2 problems:

1. The DB password 'db_passwd' was not complex enough for FIPS
   changing it to 'DB_passwd' with upper case was sufficient.
   Also changed the pkcs password 'pk12_passwd' to 'PK12_passwd'

2. NSS adds a random salt in FIPS mode for the PKCS12 operations.
   The presence of this salt was causing a comparision to fail,
   the exact salt is insignificant to the operation of the test
   so it was stripped out prior to comparision.

At the same time setup_certs.py was augmented to accept a --fips
parameter to put the DB into fips mode and setup_certs.py also
now reports the status of the system FIPS mode and DB FIPS mode in
addtion to the existing info displayed at its conclusion.

diff --git a/test/setup_certs.py b/test/setup_certs.py
--- a/test/setup_certs.py
+++ b/test/setup_certs.py
@@ -4,6 +4,7 @@
 import atexit
 import logging
 import os
+import re
 import shutil
 import subprocess
 import sys
@@ -11,6 +12,12 @@
 import tempfile
 
 #-------------------------------------------------------------------------------
+logger = None
+
+FIPS_SWITCH_FAILED_ERR = 11
+FIPS_ALREADY_ON_ERR = 12
+FIPS_ALREADY_OFF_ERR = 13
+
 
 class CmdError(Exception):
     def __init__(self, cmd_args, returncode, message=None, stdout=None, stderr=None):
@@ -41,7 +48,7 @@
         returncode = p.returncode
         if returncode != 0:
             raise CmdError(cmd_args, returncode,
-                           'failed %s' % (', '.join(cmd_args)),
+                           'failed %s' % (' '.join(cmd_args)),
                            stdout, stderr)
         return stdout, stderr
     except OSError as e:
@@ -319,9 +326,75 @@
     run_cmd(cmd_args)
     return name
 
+def parse_fips_enabled(string):
+    if re.search('FIPS mode disabled', string):
+        return False
+    if re.search('FIPS mode enabled', string):
+        return True
+    raise ValueError('unknown fips enabled string: "%s"' % string)
+
+def get_system_fips_enabled():
+    fips_path = '/proc/sys/crypto/fips_enabled'
+
+    try:
+        with open(fips_path) as f:
+            data = f.read()
+    except Exception as e:
+        logger.warning("Unable to determine system FIPS mode: %s" % e)
+        data = '0'
+
+    value = int(data)
+    if value:
+        return True
+    else:
+        return False
+        
+
+def get_db_fips_enabled(db_name):
+    cmd_args = ['/usr/bin/modutil',
+                '-dbdir', db_name,               # NSS database
+                '-chkfips', 'true',              # enable/disable fips
+                ]
+
+    try:
+        stdout, stderr = run_cmd(cmd_args)
+        return parse_fips_enabled(stdout)
+    except CmdError as e:
+        if e.returncode == FIPS_SWITCH_FAILED_ERR:
+            return parse_fips_enabled(e.stdout)
+        else:
+            raise
+        
+def set_fips_mode(options):
+    if options.fips:
+        state = 'true'
+    else:
+        if get_system_fips_enabled():
+            logger.warning("System FIPS enabled, cannot disable FIPS")
+            return
+        state = 'false'
+
+    logging.info('setting fips: %s', state)
+
+    cmd_args = ['/usr/bin/modutil',
+                '-dbdir', options.db_name,       # NSS database
+                '-fips', state,                  # enable/disable fips
+                '-force'
+                ]
+
+    try:
+        stdout, stderr = run_cmd(cmd_args)
+    except CmdError as e:
+        if options.fips and e.returncode == FIPS_ALREADY_ON_ERR:
+            pass
+        elif not options.fips and e.returncode == FIPS_ALREADY_OFF_ERR:
+            pass
+        else:
+            raise
 #-------------------------------------------------------------------------------
 
 def setup_certs(args):
+    global logger
 
     # --- cmd ---
     parser = argparse.ArgumentParser(description='create certs for testing',
@@ -393,6 +466,9 @@
     parser.add_argument('--serial-file', dest='serial_file',
                         help='name of file used to track next serial number')
 
+    parser.add_argument('--db-fips', action='store_true',
+                        help='enable FIPS mode on NSS Database')
+
     parser.set_defaults(verbose = False,
                         debug = False,
                         quiet = False,
@@ -402,7 +478,7 @@
                         hostname = os.uname()[1],
                         db_type = 'sql',
                         db_dir = 'pki',
-                        db_passwd = 'db_passwd',
+                        db_passwd = 'DB_passwd',
                         ca_subject = 'CN=Test CA',
                         ca_nickname = 'test_ca',
                         server_subject =  'CN=${hostname}',
@@ -416,6 +492,7 @@
                         valid_months = 12,
                         ca_path_len = 2,
                         serial_file = '${db_dir}/serial',
+                        fips = False,
                         )
 
 
@@ -468,6 +545,7 @@
 
     try:
         create_database(options)
+        set_fips_mode(options)
         cert_nicknames.append(create_ca_cert(options))
         cert_nicknames.append(create_server_cert(options))
         cert_nicknames.append(create_client_cert(options))
@@ -488,6 +566,8 @@
     logging.info('---------- Summary ----------')
     logging.info('NSS database name="%s", password="%s"',
                  options.db_name, options.db_passwd)
+    logging.info('system FIPS mode=%s', get_system_fips_enabled());
+    logging.info('DB FIPS mode=%s', get_db_fips_enabled(options.db_name));
     logging.info('CA nickname="%s", CA subject="%s"',
                  options.ca_nickname, options.ca_subject)
     logging.info('server nickname="%s", server subject="%s"',
diff --git a/test/test_client_server.py b/test/test_client_server.py
--- a/test/test_client_server.py
+++ b/test/test_client_server.py
@@ -22,7 +22,7 @@
 
 verbose = False
 info = False
-password = 'db_passwd'
+password = 'DB_passwd'
 use_ssl = True
 client_cert_action = NO_CLIENT_CERT
 db_name = 'sql:pki'
diff --git a/test/test_pkcs12.py b/test/test_pkcs12.py
--- a/test/test_pkcs12.py
+++ b/test/test_pkcs12.py
@@ -15,8 +15,8 @@
 
 verbose = False
 db_name = 'sql:pki'
-db_passwd = 'db_passwd'
-pk12_passwd = 'pk12_passwd'
+db_passwd = 'DB_passwd'
+pk12_passwd = 'PK12_passwd'
 
 cert_nickname = 'test_user'
 pk12_filename = '%s.p12' % cert_nickname
@@ -115,6 +115,9 @@
         raise ValueError('Could not file Key section in pk12 listing')
     return text[match.start(0):]
 
+def strip_salt_from_pk12_listing(text):
+    return re.sub(r'\s+Salt:\s*\n.*', '', text)
+
 #-------------------------------------------------------------------------------
 
 def load_tests(loader, tests, pattern):
@@ -206,9 +209,11 @@
 
         pk12_listing = list_pk12(pk12_filename)
         pk12_listing = strip_key_from_pk12_listing(pk12_listing)
+        pk12_listing = strip_salt_from_pk12_listing(pk12_listing)
 
         exported_pk12_listing = list_pk12(exported_pk12_filename)
         exported_pk12_listing = strip_key_from_pk12_listing(exported_pk12_listing)
+        exported_pk12_listing = strip_salt_from_pk12_listing(exported_pk12_listing)
 
         self.assertEqual(pk12_listing, exported_pk12_listing)