4f8d33
# HG changeset patch
4f8d33
# User John Dennis <jdennis@redhat.com>
4f8d33
# Date 1434146324 14400
4f8d33
#      Fri Jun 12 17:58:44 2015 -0400
4f8d33
# Node ID 6096d0660e2abefcee12e502fed029663d435c54
4f8d33
# Parent  a5e07e90e9c8cb08c5fb46eba16bdb43b94d67c3
4f8d33
Some unit tests fail in FIPS mode
4f8d33
4f8d33
Resolves bug
4f8d33
https://bugzilla.redhat.com/show_bug.cgi?id=1194349
4f8d33
4f8d33
Essentially there were 2 problems:
4f8d33
4f8d33
1. The DB password 'db_passwd' was not complex enough for FIPS
4f8d33
   changing it to 'DB_passwd' with upper case was sufficient.
4f8d33
   Also changed the pkcs password 'pk12_passwd' to 'PK12_passwd'
4f8d33
4f8d33
2. NSS adds a random salt in FIPS mode for the PKCS12 operations.
4f8d33
   The presence of this salt was causing a comparision to fail,
4f8d33
   the exact salt is insignificant to the operation of the test
4f8d33
   so it was stripped out prior to comparision.
4f8d33
4f8d33
At the same time setup_certs.py was augmented to accept a --fips
4f8d33
parameter to put the DB into fips mode and setup_certs.py also
4f8d33
now reports the status of the system FIPS mode and DB FIPS mode in
4f8d33
addtion to the existing info displayed at its conclusion.
4f8d33
4f8d33
diff --git a/test/setup_certs.py b/test/setup_certs.py
4f8d33
--- a/test/setup_certs.py
4f8d33
+++ b/test/setup_certs.py
4f8d33
@@ -4,6 +4,7 @@
4f8d33
 import atexit
4f8d33
 import logging
4f8d33
 import os
4f8d33
+import re
4f8d33
 import shutil
4f8d33
 import subprocess
4f8d33
 import sys
4f8d33
@@ -11,6 +12,12 @@
4f8d33
 import tempfile
4f8d33
 
4f8d33
 #-------------------------------------------------------------------------------
4f8d33
+logger = None
4f8d33
+
4f8d33
+FIPS_SWITCH_FAILED_ERR = 11
4f8d33
+FIPS_ALREADY_ON_ERR = 12
4f8d33
+FIPS_ALREADY_OFF_ERR = 13
4f8d33
+
4f8d33
 
4f8d33
 class CmdError(Exception):
4f8d33
     def __init__(self, cmd_args, returncode, message=None, stdout=None, stderr=None):
4f8d33
@@ -41,7 +48,7 @@
4f8d33
         returncode = p.returncode
4f8d33
         if returncode != 0:
4f8d33
             raise CmdError(cmd_args, returncode,
4f8d33
-                           'failed %s' % (', '.join(cmd_args)),
4f8d33
+                           'failed %s' % (' '.join(cmd_args)),
4f8d33
                            stdout, stderr)
4f8d33
         return stdout, stderr
4f8d33
     except OSError as e:
4f8d33
@@ -319,9 +326,75 @@
4f8d33
     run_cmd(cmd_args)
4f8d33
     return name
4f8d33
 
4f8d33
+def parse_fips_enabled(string):
4f8d33
+    if re.search('FIPS mode disabled', string):
4f8d33
+        return False
4f8d33
+    if re.search('FIPS mode enabled', string):
4f8d33
+        return True
4f8d33
+    raise ValueError('unknown fips enabled string: "%s"' % string)
4f8d33
+
4f8d33
+def get_system_fips_enabled():
4f8d33
+    fips_path = '/proc/sys/crypto/fips_enabled'
4f8d33
+
4f8d33
+    try:
4f8d33
+        with open(fips_path) as f:
4f8d33
+            data = f.read()
4f8d33
+    except Exception as e:
4f8d33
+        logger.warning("Unable to determine system FIPS mode: %s" % e)
4f8d33
+        data = '0'
4f8d33
+
4f8d33
+    value = int(data)
4f8d33
+    if value:
4f8d33
+        return True
4f8d33
+    else:
4f8d33
+        return False
4f8d33
+        
4f8d33
+
4f8d33
+def get_db_fips_enabled(db_name):
4f8d33
+    cmd_args = ['/usr/bin/modutil',
4f8d33
+                '-dbdir', db_name,               # NSS database
4f8d33
+                '-chkfips', 'true',              # enable/disable fips
4f8d33
+                ]
4f8d33
+
4f8d33
+    try:
4f8d33
+        stdout, stderr = run_cmd(cmd_args)
4f8d33
+        return parse_fips_enabled(stdout)
4f8d33
+    except CmdError as e:
4f8d33
+        if e.returncode == FIPS_SWITCH_FAILED_ERR:
4f8d33
+            return parse_fips_enabled(e.stdout)
4f8d33
+        else:
4f8d33
+            raise
4f8d33
+        
4f8d33
+def set_fips_mode(options):
4f8d33
+    if options.fips:
4f8d33
+        state = 'true'
4f8d33
+    else:
4f8d33
+        if get_system_fips_enabled():
4f8d33
+            logger.warning("System FIPS enabled, cannot disable FIPS")
4f8d33
+            return
4f8d33
+        state = 'false'
4f8d33
+
4f8d33
+    logging.info('setting fips: %s', state)
4f8d33
+
4f8d33
+    cmd_args = ['/usr/bin/modutil',
4f8d33
+                '-dbdir', options.db_name,       # NSS database
4f8d33
+                '-fips', state,                  # enable/disable fips
4f8d33
+                '-force'
4f8d33
+                ]
4f8d33
+
4f8d33
+    try:
4f8d33
+        stdout, stderr = run_cmd(cmd_args)
4f8d33
+    except CmdError as e:
4f8d33
+        if options.fips and e.returncode == FIPS_ALREADY_ON_ERR:
4f8d33
+            pass
4f8d33
+        elif not options.fips and e.returncode == FIPS_ALREADY_OFF_ERR:
4f8d33
+            pass
4f8d33
+        else:
4f8d33
+            raise
4f8d33
 #-------------------------------------------------------------------------------
4f8d33
 
4f8d33
 def setup_certs(args):
4f8d33
+    global logger
4f8d33
 
4f8d33
     # --- cmd ---
4f8d33
     parser = argparse.ArgumentParser(description='create certs for testing',
4f8d33
@@ -393,6 +466,9 @@
4f8d33
     parser.add_argument('--serial-file', dest='serial_file',
4f8d33
                         help='name of file used to track next serial number')
4f8d33
 
4f8d33
+    parser.add_argument('--db-fips', action='store_true',
4f8d33
+                        help='enable FIPS mode on NSS Database')
4f8d33
+
4f8d33
     parser.set_defaults(verbose = False,
4f8d33
                         debug = False,
4f8d33
                         quiet = False,
4f8d33
@@ -402,7 +478,7 @@
4f8d33
                         hostname = os.uname()[1],
4f8d33
                         db_type = 'sql',
4f8d33
                         db_dir = 'pki',
4f8d33
-                        db_passwd = 'db_passwd',
4f8d33
+                        db_passwd = 'DB_passwd',
4f8d33
                         ca_subject = 'CN=Test CA',
4f8d33
                         ca_nickname = 'test_ca',
4f8d33
                         server_subject =  'CN=${hostname}',
4f8d33
@@ -416,6 +492,7 @@
4f8d33
                         valid_months = 12,
4f8d33
                         ca_path_len = 2,
4f8d33
                         serial_file = '${db_dir}/serial',
4f8d33
+                        fips = False,
4f8d33
                         )
4f8d33
 
4f8d33
 
4f8d33
@@ -468,6 +545,7 @@
4f8d33
 
4f8d33
     try:
4f8d33
         create_database(options)
4f8d33
+        set_fips_mode(options)
4f8d33
         cert_nicknames.append(create_ca_cert(options))
4f8d33
         cert_nicknames.append(create_server_cert(options))
4f8d33
         cert_nicknames.append(create_client_cert(options))
4f8d33
@@ -488,6 +566,8 @@
4f8d33
     logging.info('---------- Summary ----------')
4f8d33
     logging.info('NSS database name="%s", password="%s"',
4f8d33
                  options.db_name, options.db_passwd)
4f8d33
+    logging.info('system FIPS mode=%s', get_system_fips_enabled());
4f8d33
+    logging.info('DB FIPS mode=%s', get_db_fips_enabled(options.db_name));
4f8d33
     logging.info('CA nickname="%s", CA subject="%s"',
4f8d33
                  options.ca_nickname, options.ca_subject)
4f8d33
     logging.info('server nickname="%s", server subject="%s"',
4f8d33
diff --git a/test/test_client_server.py b/test/test_client_server.py
4f8d33
--- a/test/test_client_server.py
4f8d33
+++ b/test/test_client_server.py
4f8d33
@@ -22,7 +22,7 @@
4f8d33
 
4f8d33
 verbose = False
4f8d33
 info = False
4f8d33
-password = 'db_passwd'
4f8d33
+password = 'DB_passwd'
4f8d33
 use_ssl = True
4f8d33
 client_cert_action = NO_CLIENT_CERT
4f8d33
 db_name = 'sql:pki'
4f8d33
diff --git a/test/test_pkcs12.py b/test/test_pkcs12.py
4f8d33
--- a/test/test_pkcs12.py
4f8d33
+++ b/test/test_pkcs12.py
4f8d33
@@ -15,8 +15,8 @@
4f8d33
 
4f8d33
 verbose = False
4f8d33
 db_name = 'sql:pki'
4f8d33
-db_passwd = 'db_passwd'
4f8d33
-pk12_passwd = 'pk12_passwd'
4f8d33
+db_passwd = 'DB_passwd'
4f8d33
+pk12_passwd = 'PK12_passwd'
4f8d33
 
4f8d33
 cert_nickname = 'test_user'
4f8d33
 pk12_filename = '%s.p12' % cert_nickname
4f8d33
@@ -115,6 +115,9 @@
4f8d33
         raise ValueError('Could not file Key section in pk12 listing')
4f8d33
     return text[match.start(0):]
4f8d33
 
4f8d33
+def strip_salt_from_pk12_listing(text):
4f8d33
+    return re.sub(r'\s+Salt:\s*\n.*', '', text)
4f8d33
+
4f8d33
 #-------------------------------------------------------------------------------
4f8d33
 
4f8d33
 def load_tests(loader, tests, pattern):
4f8d33
@@ -206,9 +209,11 @@
4f8d33
 
4f8d33
         pk12_listing = list_pk12(pk12_filename)
4f8d33
         pk12_listing = strip_key_from_pk12_listing(pk12_listing)
4f8d33
+        pk12_listing = strip_salt_from_pk12_listing(pk12_listing)
4f8d33
 
4f8d33
         exported_pk12_listing = list_pk12(exported_pk12_filename)
4f8d33
         exported_pk12_listing = strip_key_from_pk12_listing(exported_pk12_listing)
4f8d33
+        exported_pk12_listing = strip_salt_from_pk12_listing(exported_pk12_listing)
4f8d33
 
4f8d33
         self.assertEqual(pk12_listing, exported_pk12_listing)
4f8d33