Skip to content

Commit 4c1ca33

Browse files
committed
Check that the expected NSS token matches the current FIPS state
In order to be FIPS-compliant, and actually be able to renew certificates, the NSS token name needs to match the expectation based on the current FIPS state. The token name will change depending on the state. So if a user installs IPA and then switches to FIPS mode: 1. The system is non-compliant 2. Renewals will fail because the NSS Certificate DB token is not present in the database. Fixes: #342 Signed-off-by: Rob Crittenden <rcritten@redhat.com>
1 parent 0b3151a commit 4c1ca33

2 files changed

Lines changed: 237 additions & 3 deletions

File tree

src/ipahealthcheck/ipa/certs.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from ipalib.install import certmonger
2323
from ipalib.constants import RENEWAL_CA_NAME, IPA_CA_RECORD
2424
from ipaplatform.paths import paths
25+
from ipaplatform.tasks import tasks
2526
from ipaserver.install import certs
2627
from ipaserver.install import dsinstance
2728
from ipaserver.install import krainstance
@@ -1568,3 +1569,68 @@ def check(self):
15681569

15691570
if len(requests) == 0:
15701571
yield Result(self, constants.SUCCESS, key='no_stuck')
1572+
1573+
1574+
@registry
1575+
class CertmongerFIPTokensCheck(IPAPlugin):
1576+
"""Check if the current FIPS state doesn't match the certmonger
1577+
token name. This only applies if an HSM is not used for now.
1578+
1579+
We can only test this on certificates stored in NSS dbs.
1580+
"""
1581+
requires = ('dirsrv',)
1582+
1583+
@duration
1584+
def check(self):
1585+
# token names we care about
1586+
candidates = ('NSS FIPS 140-2 Certificate DB', 'NSS Certificate DB')
1587+
1588+
if tasks.is_fips_enabled():
1589+
expected_token = 'NSS FIPS 140-2 Certificate DB'
1590+
else:
1591+
expected_token = 'NSS Certificate DB'
1592+
1593+
# Getting the requests this way instead of get_expected_requests
1594+
# because we need to be able to modify the list in tests and this
1595+
# is only currently possible doing it this way.
1596+
1597+
cm = certmonger._certmonger()
1598+
requests = cm.obj_if.get_requests()
1599+
failed = False
1600+
for request in requests:
1601+
req = certmonger._cm_dbus_object(cm.bus, cm, request,
1602+
certmonger.DBUS_CM_REQUEST_IF,
1603+
certmonger.DBUS_CM_IF, True)
1604+
request_id = str(req.prop_if.Get(certmonger.DBUS_CM_REQUEST_IF,
1605+
'nickname'))
1606+
if request_id is None:
1607+
# we could log here but other checks already do. Don't spam.
1608+
continue
1609+
1610+
storage = certmonger.get_request_value(request_id,
1611+
'key-storage')
1612+
1613+
if storage != 'NSSDB':
1614+
logger.debug('Skipping non-NSSDB request %s',
1615+
request_id)
1616+
continue
1617+
1618+
token = certmonger.get_request_value(request_id,
1619+
'key-token')
1620+
if token not in candidates:
1621+
logger.debug('Skipping token %s in request %s',
1622+
str(token), request_id)
1623+
continue
1624+
1625+
if token != expected_token:
1626+
yield Result(self, constants.ERROR,
1627+
key=request_id,
1628+
token=token,
1629+
expected_token=expected_token,
1630+
msg='certmonger request {key} has token {token} '
1631+
'but based on the current FIPS state the '
1632+
'expected token is {expected_token}')
1633+
failed = True
1634+
1635+
if not failed:
1636+
yield Result(self, constants.SUCCESS, key='fipstokencheck')

tests/test_ipa_tracking.py

Lines changed: 171 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77

88
from ipahealthcheck.core import constants, config
99
from ipahealthcheck.ipa.plugin import registry
10-
from ipahealthcheck.ipa.certs import IPACertTracking, CertmongerStuckCheck
11-
from unittest.mock import Mock
10+
from ipahealthcheck.ipa.certs import (
11+
IPACertTracking,
12+
CertmongerStuckCheck,
13+
CertmongerFIPTokensCheck
14+
)
15+
from unittest.mock import Mock, patch
1216
from mock_certmonger import create_mock_dbus, _certmonger
1317
from mock_certmonger import get_expected_requests, set_requests
1418

@@ -106,7 +110,7 @@ def test_none_stuck(self):
106110
self.results = capture_results(f)
107111

108112
assert len(self.results) == 1
109-
result = self.results.results[0]
113+
result = self.results.results[-1]
110114
assert result.result == constants.SUCCESS
111115

112116
def test_one_stuck(self):
@@ -128,3 +132,167 @@ def test_one_stuck(self):
128132
result = self.results.results[0]
129133
assert result.result == constants.WARNING
130134
assert result.kw.get('key') == '7777'
135+
136+
137+
class TestFIPSTokens(BaseTest):
138+
"""Test the combination of FIPS configurations with tokens and
139+
also that when HSM is used the check is skipped.
140+
"""
141+
patches = {
142+
'ipahealthcheck.ipa.certs.get_expected_requests':
143+
Mock(return_value=get_expected_requests()),
144+
'ipalib.install.certmonger._cm_dbus_object':
145+
Mock(side_effect=create_mock_dbus),
146+
'ipalib.install.certmonger._certmonger':
147+
Mock(return_value=_certmonger())
148+
}
149+
150+
nss_tracking = {
151+
'nickname': '9876',
152+
'ca-name': 'dogtag-ipa-ca-renew-agent',
153+
'template_profile': 'caIPAserviceCert',
154+
'cert-storage': 'NSSDB',
155+
'cert-storage_location': '/etc/pki/pki-tomcat/alias',
156+
'cert-token': 'NSS Certificate DB',
157+
'key-storage': 'NSSDB',
158+
'key-storage_location': '/etc/pki/pki-tomcat/alias',
159+
'key-token': 'NSS Certificate DB',
160+
}
161+
162+
@patch('ipaplatform.tasks.tasks.is_fips_enabled')
163+
@patch('ipalib.install.certmonger.get_request_value')
164+
# @patch('ipahealthcheck.ipa.certs.get_expected_requests')
165+
# @patch('ipalib.install.certmonger._cm_dbus_object')
166+
def test_nonfips_token_correct(self, mock_value, mock_fips):
167+
mock_value.side_effect = [
168+
'FILE',
169+
'FILE',
170+
'NSSDB', 'NSS Certificate DB',
171+
]
172+
mock_fips.return_value = False
173+
set_requests(add=self.nss_tracking)
174+
# mock_requests.return_value=get_expected_requests()
175+
# mock_obj.side_effect=create_mock_dbus
176+
177+
framework = object()
178+
registry.initialize(framework, config.Config)
179+
f = CertmongerFIPTokensCheck(registry)
180+
181+
self.results = capture_results(f)
182+
183+
assert len(self.results) == 1
184+
result = self.results.results[0]
185+
assert result.result == constants.SUCCESS
186+
187+
@patch('ipaplatform.tasks.tasks.is_fips_enabled')
188+
@patch('ipalib.install.certmonger.get_request_value')
189+
def test_nonfips_token_wrong(self, mock_value, mock_fips):
190+
mock_value.side_effect = [
191+
'FILE',
192+
'FILE',
193+
'NSSDB', 'NSS FIPS 140-2 Certificate DB',
194+
]
195+
mock_fips.return_value = False
196+
set_requests(add=self.nss_tracking)
197+
198+
framework = object()
199+
registry.initialize(framework, config.Config)
200+
f = CertmongerFIPTokensCheck(registry)
201+
202+
self.results = capture_results(f)
203+
204+
assert len(self.results) == 1
205+
result = self.results.results[0]
206+
assert result.result == constants.ERROR
207+
assert result.kw.get('key') == '9876'
208+
assert result.kw.get('token') == 'NSS FIPS 140-2 Certificate DB'
209+
210+
@patch('ipaplatform.tasks.tasks.is_fips_enabled')
211+
@patch('ipalib.install.certmonger.get_request_value')
212+
def test_fips_token_correct(self, mock_value, mock_fips):
213+
mock_value.side_effect = [
214+
'FILE',
215+
'FILE',
216+
'NSSDB', 'NSS FIPS 140-2 Certificate DB',
217+
]
218+
mock_fips.return_value = True
219+
set_requests(add=self.nss_tracking)
220+
221+
framework = object()
222+
registry.initialize(framework, config.Config)
223+
f = CertmongerFIPTokensCheck(registry)
224+
225+
self.results = capture_results(f)
226+
227+
assert len(self.results) == 1
228+
result = self.results.results[0]
229+
assert result.result == constants.SUCCESS
230+
231+
@patch('ipaplatform.tasks.tasks.is_fips_enabled')
232+
@patch('ipalib.install.certmonger.get_request_value')
233+
def test_fips_token_wrong(self, mock_value, mock_fips):
234+
mock_value.side_effect = [
235+
'FILE',
236+
'FILE',
237+
'NSSDB', 'NSS Certificate DB',
238+
]
239+
mock_fips.return_value = True
240+
set_requests(add=self.nss_tracking)
241+
242+
framework = object()
243+
registry.initialize(framework, config.Config)
244+
f = CertmongerFIPTokensCheck(registry)
245+
246+
self.results = capture_results(f)
247+
248+
assert len(self.results) == 1
249+
result = self.results.results[0]
250+
assert result.result == constants.ERROR
251+
assert result.kw.get('key') == '9876'
252+
assert result.kw.get('token') == 'NSS Certificate DB'
253+
assert result.kw.get('expected_token') == \
254+
'NSS FIPS 140-2 Certificate DB'
255+
256+
@patch('ipaplatform.tasks.tasks.is_fips_enabled')
257+
@patch('ipalib.install.certmonger.get_request_value')
258+
def test_hsm_token_non_fips(self, mock_value, mock_fips):
259+
"""FIPS shouldn't make a difference as HSM tokens should be skipped"""
260+
mock_value.side_effect = [
261+
'FILE',
262+
'FILE',
263+
'NSSDB', 'ipa_token',
264+
]
265+
mock_fips.return_value = False
266+
set_requests(add=self.nss_tracking)
267+
268+
framework = object()
269+
registry.initialize(framework, config.Config)
270+
f = CertmongerFIPTokensCheck(registry)
271+
272+
self.results = capture_results(f)
273+
274+
assert len(self.results) == 1
275+
result = self.results.results[0]
276+
assert result.result == constants.SUCCESS
277+
278+
@patch('ipaplatform.tasks.tasks.is_fips_enabled')
279+
@patch('ipalib.install.certmonger.get_request_value')
280+
def test_hsm_token_fips(self, mock_value, mock_fips):
281+
"""FIPS shouldn't make a difference as HSM tokens should be skipped"""
282+
mock_value.side_effect = [
283+
'FILE',
284+
'FILE',
285+
'NSSDB', 'ipa_token',
286+
]
287+
mock_fips.return_value = True
288+
set_requests(add=self.nss_tracking)
289+
290+
framework = object()
291+
registry.initialize(framework, config.Config)
292+
f = CertmongerFIPTokensCheck(registry)
293+
294+
self.results = capture_results(f)
295+
296+
assert len(self.results) == 1
297+
result = self.results.results[0]
298+
assert result.result == constants.SUCCESS

0 commit comments

Comments
 (0)