Skip to content

Commit 91c7d2e

Browse files
authored
Fixed CRL type detection on get_intel_pcs_x509_crl (#499)
- Added and updated unit tests
1 parent 67a5c8f commit 91c7d2e

File tree

3 files changed

+68
-32
lines changed

3 files changed

+68
-32
lines changed

middleware/admin/x509_utils.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,31 @@ def get_intel_pcs_x509_crl(url):
3838
raise RuntimeError(f"Error fetching CRL from {url}")
3939

4040
try:
41-
# Parse CRL
42-
ctype = ra_res.headers["Content-Type"]
43-
if ctype in ["application/x-pem-file"]:
44-
crl = x509.load_pem_x509_crl(ra_res.content)
45-
elif ctype in ["application/pkix-crl", "application/x-x509-ca-cert"]:
46-
crl = x509.load_der_x509_crl(ra_res.content)
41+
# Parse CRL. PCS endpoints may return generic MIME types (e.g.
42+
# application/octet-stream) even when the payload is DER.
43+
raw_ctype = ra_res.headers.get("Content-Type", "")
44+
ctype = raw_ctype.split(";", 1)[0].strip().lower()
45+
pem_types = {
46+
"application/x-pem-file"
47+
}
48+
der_types = {
49+
"application/pkix-crl",
50+
"application/x-x509-ca-cert",
51+
}
52+
content = ra_res.content
53+
content_stripped = content.lstrip()
54+
pem_hint = \
55+
content_stripped.startswith(b"-----BEGIN") or \
56+
url.lower().endswith(".pem")
57+
der_hint = \
58+
url.lower().endswith(".der")
59+
60+
if ctype in pem_types or pem_hint:
61+
crl = x509.load_pem_x509_crl(content)
62+
elif ctype in der_types or der_hint:
63+
crl = x509.load_der_x509_crl(content)
4764
else:
48-
raise RuntimeError(f"Unknown CRL encoding: {ctype}")
65+
raise RuntimeError(f"Unable to parse CRL with content-type <{raw_ctype}>")
4966

5067
# Parse certification chain (if any)
5168
issuer_chain = ra_res.headers.get("SGX-PCK-CRL-Issuer-Chain")

middleware/admin/x509_validator.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,22 @@ def get_crl_info(self, subject):
3939
if len(crldps) == 0:
4040
raise RuntimeError("No CRL distribution points found in certificate")
4141

42+
errors = []
4243
for crldp in crldps:
4344
url = crldp.full_name[0].value
4445
try:
4546
crl_info = crl_getter(url)
4647
break
47-
except RuntimeError:
48-
pass
48+
except RuntimeError as e:
49+
errors.append(str(e))
4950

5051
if crl_info is None:
52+
error_str = ""
53+
if len(errors) > 0:
54+
error_str = " The following errors were recorded: " + \
55+
"; ".join(errors)
5156
raise RuntimeError("None of the distribution points "
52-
"provided a valid CRL")
57+
f"provided a valid CRL.{error_str}")
5358

5459
return crl_info
5560
except Exception as e:

middleware/tests/admin/test_x509_utils.py

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,38 +72,46 @@ def test_certs_pref_suf(self):
7272
@patch("admin.x509_utils.x509.load_pem_x509_crl")
7373
@patch("admin.x509_utils.requests")
7474
class TestGetIntelPcsX509CRL(TestCase):
75-
def test_ok_pem(self, requests, load_pem, load_der, split, unquote):
75+
@parameterized.expand([
76+
("ctype", "a-url", "application/x-pem-file", b"the-content"),
77+
("content", "a-url", "the-ctype", b"-----BEGIN CERTIFICATE etc etc"),
78+
("url", "a-url.pem", "the-ctype", b"the-content"),
79+
])
80+
def test_ok_pem(self, requests, load_pem, load_der, split, unquote,
81+
_, url, ctype, ctnt):
7682
res = Mock()
7783
requests.get.return_value = res
7884

7985
res.status_code = 200
80-
res.content = "the-crl-content"
86+
res.content = ctnt
8187
res.headers = {
82-
"Content-Type": "application/x-pem-file"
88+
"Content-Type": ctype
8389
}
8490
load_pem.return_value = "the-parsed-certificate"
8591

8692
self.assertEqual({
8793
"crl": "the-parsed-certificate",
8894
"issuer_chain": None,
8995
"warning": None,
90-
}, get_intel_pcs_x509_crl("the-crl-url"))
96+
}, get_intel_pcs_x509_crl(url))
9197

92-
load_pem.assert_called_with("the-crl-content")
98+
load_pem.assert_called_with(ctnt)
9399
load_der.assert_not_called()
94100
split.assert_not_called()
95101
unquote.assert_not_called()
96102

97103
@parameterized.expand([
98-
("header 1", "application/pkix-crl"),
99-
("header 2", "application/x-x509-ca-cert"),
104+
("ctype 1", "a-url", "application/pkix-crl"),
105+
("ctype 2", "a-url", "application/x-x509-ca-cert"),
106+
("url", "a-url.der", "the-ctype"),
100107
])
101-
def test_ok_der(self, requests, load_pem, load_der, split, unquote, _, ctype):
108+
def test_ok_der(self, requests, load_pem, load_der, split, unquote,
109+
_, url, ctype):
102110
res = Mock()
103111
requests.get.return_value = res
104112

105113
res.status_code = 200
106-
res.content = "the-crl-content"
114+
res.content = b"the-crl-content"
107115
res.headers = {
108116
"Content-Type": ctype,
109117
}
@@ -113,9 +121,9 @@ def test_ok_der(self, requests, load_pem, load_der, split, unquote, _, ctype):
113121
"crl": "the-parsed-certificate",
114122
"issuer_chain": None,
115123
"warning": None,
116-
}, get_intel_pcs_x509_crl("the-crl-url"))
124+
}, get_intel_pcs_x509_crl(url))
117125

118-
load_der.assert_called_with("the-crl-content")
126+
load_der.assert_called_with(b"the-crl-content")
119127
load_pem.assert_not_called()
120128
split.assert_not_called()
121129
unquote.assert_not_called()
@@ -125,7 +133,7 @@ def test_ok_warning(self, requests, load_pem, load_der, split, unquote):
125133
requests.get.return_value = res
126134

127135
res.status_code = 200
128-
res.content = "the-crl-content"
136+
res.content = b"the-crl-content"
129137
res.headers = {
130138
"Content-Type": "application/x-pem-file",
131139
"warning": "this-is-a-warning",
@@ -138,7 +146,7 @@ def test_ok_warning(self, requests, load_pem, load_der, split, unquote):
138146
"warning": "Getting the-crl-url: this-is-a-warning",
139147
}, get_intel_pcs_x509_crl("the-crl-url"))
140148

141-
load_pem.assert_called_with("the-crl-content")
149+
load_pem.assert_called_with(b"the-crl-content")
142150
load_der.assert_not_called()
143151
split.assert_not_called()
144152
unquote.assert_not_called()
@@ -149,7 +157,7 @@ def test_ok_issuer_chain(self, loadcer, requests, load_pem, load_der, split, unq
149157
requests.get.return_value = res
150158

151159
res.status_code = 200
152-
res.content = "the-crl-content"
160+
res.content = b"the-crl-content"
153161
res.headers = {
154162
"Content-Type": "application/x-x509-ca-cert",
155163
"SGX-PCK-CRL-Issuer-Chain": "chain0-chain1-chain2",
@@ -169,7 +177,7 @@ def test_ok_issuer_chain(self, loadcer, requests, load_pem, load_der, split, unq
169177
"warning": None,
170178
}, get_intel_pcs_x509_crl("the-crl-url"))
171179

172-
load_der.assert_called_with("the-crl-content")
180+
load_der.assert_called_with(b"the-crl-content")
173181
load_pem.assert_not_called()
174182
unquote.assert_called_with("chain0-chain1-chain2")
175183
split.assert_called_with("chain0,chain1,chain2")
@@ -198,29 +206,35 @@ def test_error_unknown_ctype(self, requests, load_pem, load_der, split, unquote)
198206
res.headers = {
199207
"Content-Type": "not-known"
200208
}
209+
res.content = b"this is not a certificate"
201210

202211
with self.assertRaises(RuntimeError) as e:
203212
get_intel_pcs_x509_crl("the-crl-url")
204213
self.assertIn("While", str(e.exception))
205-
self.assertIn("Unknown", str(e.exception))
214+
self.assertIn("the-crl-url", str(e.exception))
215+
self.assertIn("Unable", str(e.exception))
216+
self.assertIn("not-known", str(e.exception))
206217

207218
load_pem.assert_not_called()
208219
load_der.assert_not_called()
209220
split.assert_not_called()
210221
unquote.assert_not_called()
211222

212223
@parameterized.expand([
213-
("header 1", "application/x-pem-file", "pem"),
214-
("header 2", "application/pkix-crl", "der"),
215-
("header 3", "application/x-x509-ca-cert", "der"),
224+
("header 1", "a-url", "application/x-pem-file", b"the-content", "pem"),
225+
("header 2", "a-url", "application/pkix-crl", b"the-content", "der"),
226+
("header 3", "a-url", "application/x-x509-ca-cert", b"the-content", "der"),
227+
("url 1", "a-url.der", "the-ctype", b"the-content", "der"),
228+
("url 2", "a-url.pem", "the-ctype", b"the-content", "pem"),
229+
("content", "a-url", "the-ctype", b"-----BEGIN CERT etc etc", "pem"),
216230
])
217231
def test_error_parsing(self, requests, load_pem, load_der,
218-
split, unquote, _, ctype, errct):
232+
split, unquote, _, url, ctype, ctnt, errct):
219233
res = Mock()
220234
requests.get.return_value = res
221235

222236
res.status_code = 200
223-
res.content = "some-content"
237+
res.content = ctnt
224238
res.headers = {
225239
"Content-Type": ctype,
226240
}
@@ -229,7 +243,7 @@ def test_error_parsing(self, requests, load_pem, load_der,
229243
load_der.side_effect = ValueError("der parsing issue")
230244

231245
with self.assertRaises(RuntimeError) as e:
232-
get_intel_pcs_x509_crl("the-crl-url")
246+
get_intel_pcs_x509_crl(url)
233247
self.assertIn("While", str(e.exception))
234248
self.assertIn(f"{errct} parsing", str(e.exception))
235249

0 commit comments

Comments
 (0)