From 39cf4846829e2aaf7c240357e05398683367c033 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 9 May 2023 05:30:25 +0000 Subject: [PATCH] import keylime-6.5.2-4.el9 --- .gitignore | 3 +- .keylime.metadata | 3 +- ...t-values-that-need-reading-the-conf.patch} | 8 +- ...uation-on-quick-succession-execution.patch | 42 -- ...itch-to-sha256-hashes-for-signatures.patch | 67 ++ ...ch-Get-DevicePath-length-from-Length.patch | 70 -- SOURCES/0003-Backport-upsteam-PR-1156.patch | 672 ------------------ ...ove-option-to-log-into-separate-file.patch | 136 ++++ SOURCES/keylime.fc | 24 - SOURCES/keylime.if | 37 - SOURCES/keylime.te | 140 ---- SPECS/keylime.spec | 53 +- 12 files changed, 235 insertions(+), 1020 deletions(-) rename SOURCES/{0004-Do-not-use-default-values-that-need-reading-the-conf.patch => 0001-Do-not-use-default-values-that-need-reading-the-conf.patch} (95%) delete mode 100644 SOURCES/0001-ima-Fix-log-evaluation-on-quick-succession-execution.patch create mode 100644 SOURCES/0002-Switch-to-sha256-hashes-for-signatures.patch delete mode 100644 SOURCES/0002-tpm_bootlog_enrich-Get-DevicePath-length-from-Length.patch delete mode 100644 SOURCES/0003-Backport-upsteam-PR-1156.patch create mode 100644 SOURCES/0003-logging-remove-option-to-log-into-separate-file.patch delete mode 100644 SOURCES/keylime.fc delete mode 100644 SOURCES/keylime.if delete mode 100644 SOURCES/keylime.te diff --git a/.gitignore b/.gitignore index aecd0c5..333b933 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -SOURCES/v6.5.1.tar.gz +SOURCES/keylime-selinux-1.0.0.tar.gz +SOURCES/v6.5.2.tar.gz diff --git a/.keylime.metadata b/.keylime.metadata index 8e3e3cf..d577a83 100644 --- a/.keylime.metadata +++ b/.keylime.metadata @@ -1 +1,2 @@ -3205e290b09550a994237f067c41a72aab5289a9 SOURCES/v6.5.1.tar.gz +a1154fc19d2ae6f52b6b77a39e62d2420c0f4c5e SOURCES/keylime-selinux-1.0.0.tar.gz +1c311bc1d3ab6c8050fd819410c593392187c2fa SOURCES/v6.5.2.tar.gz diff --git a/SOURCES/0004-Do-not-use-default-values-that-need-reading-the-conf.patch b/SOURCES/0001-Do-not-use-default-values-that-need-reading-the-conf.patch similarity index 95% rename from SOURCES/0004-Do-not-use-default-values-that-need-reading-the-conf.patch rename to SOURCES/0001-Do-not-use-default-values-that-need-reading-the-conf.patch index 54324d3..2c4ace1 100644 --- a/SOURCES/0004-Do-not-use-default-values-that-need-reading-the-conf.patch +++ b/SOURCES/0001-Do-not-use-default-values-that-need-reading-the-conf.patch @@ -1,7 +1,7 @@ -From 76cbd7bbcce1793db9a3d64d962cfdb518ef4eff Mon Sep 17 00:00:00 2001 +From d6dd71e3a3fe8e822fbcaa0d88f19a0c3332cacd Mon Sep 17 00:00:00 2001 From: Sergio Correia Date: Tue, 15 Nov 2022 07:09:13 -0300 -Subject: [PATCH 4/4] Do not use default values that need reading the config in +Subject: [PATCH] Do not use default values that need reading the config in methods Following up from the recent refactoring that moved the EK validation @@ -52,7 +52,7 @@ index d2fc54d..3576c64 100644 """ try: diff --git a/keylime/tenant.py b/keylime/tenant.py -index dd9c09c..118f8c4 100644 +index b574d04..076b849 100644 --- a/keylime/tenant.py +++ b/keylime/tenant.py @@ -430,7 +430,7 @@ class Tenant: @@ -78,7 +78,7 @@ index ff41837..df6222c 100644 @abstractmethod diff --git a/keylime/tpm/tpm_main.py b/keylime/tpm/tpm_main.py -index 35f0a2f..09af0d0 100644 +index e1d1cf8..e244dfa 100644 --- a/keylime/tpm/tpm_main.py +++ b/keylime/tpm/tpm_main.py @@ -776,12 +776,12 @@ class tpm(tpm_abstract.AbstractTPM): diff --git a/SOURCES/0001-ima-Fix-log-evaluation-on-quick-succession-execution.patch b/SOURCES/0001-ima-Fix-log-evaluation-on-quick-succession-execution.patch deleted file mode 100644 index 5e56e39..0000000 --- a/SOURCES/0001-ima-Fix-log-evaluation-on-quick-succession-execution.patch +++ /dev/null @@ -1,42 +0,0 @@ -From de8bbb63dca836bcf07586186218c3227749d2e7 Mon Sep 17 00:00:00 2001 -From: Stefan Berger -Date: Fri, 4 Nov 2022 11:20:15 -0400 -Subject: [PATCH] ima: Fix log evaluation on quick-succession execution of - scripts - -In case the attested-to host quickly executes files measured by IMA we may -run into the case that the keylime agent retrieved the state of the PCR at -'state n' but then IMA appended the log with several entries leading to a -log representing 'state n + x' (with x>=1), which may not just be the -previously anticipated single additional entry (state n+1). Therefore, -remove the check for the number of entries in the log and always compare -the running_hash that iterative attestation was resumed with against the -provided PCR value from 'state n'. - -Signed-off-by: Stefan Berger ---- - keylime/ima/ima.py | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/keylime/ima/ima.py b/keylime/ima/ima.py -index b88b1af..c4c2ae6 100644 ---- a/keylime/ima/ima.py -+++ b/keylime/ima/ima.py -@@ -299,9 +299,11 @@ def _process_measurement_list( - - # Iterative attestation may send us no log [len(lines) == 1]; compare last know PCR 10 state - # against current PCR state. -- # Since IMA log append and PCR extend is not atomic, we may get a quote that does not yet take -- # into account the next appended measurement's [len(lines) == 2] PCR extension. -- if not found_pcr and len(lines) <= 2: -+ # Since IMA's append to the log and PCR extend as well as Keylime's retrieval of the quote, reading -+ # of PCR 10 and retrieval of the log are not atomic, we may get a quote that does not yet take into -+ # account the next-appended measurements' [len(lines) >= 2] PCR extension(s). In fact, the value of -+ # the PCR may lag the log by several entries. -+ if not found_pcr: - found_pcr = running_hash == pcrval_bytes - - for linenum, line in enumerate(lines): --- -2.37.3 - diff --git a/SOURCES/0002-Switch-to-sha256-hashes-for-signatures.patch b/SOURCES/0002-Switch-to-sha256-hashes-for-signatures.patch new file mode 100644 index 0000000..6401048 --- /dev/null +++ b/SOURCES/0002-Switch-to-sha256-hashes-for-signatures.patch @@ -0,0 +1,67 @@ +From 1f9ee7437f5b712a892c6d13ac8d75e128c1a16f Mon Sep 17 00:00:00 2001 +From: Stefan Berger +Date: Tue, 22 Nov 2022 10:56:43 -0500 +Subject: [PATCH] tests: Switch to sha256 hashes for signatures + +Resolves: https://github.com/keylime/keylime/issues/1202 +Signed-off-by: Stefan Berger +--- + test/test_ima_ast.py | 4 ++-- + test/test_ima_verification.py | 12 ++++++------ + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/test/test_ima_ast.py b/test/test_ima_ast.py +index cd54f95f9..e7d3841a7 100644 +--- a/test/test_ima_ast.py ++++ b/test/test_ima_ast.py +@@ -14,11 +14,11 @@ + VALID_ENTRIES = { + "ima-sig-rsa": ( + ast.ImaSig, +- "10 50873c47693cf9458e87eb4a02dd4f594f7a0c0f ima-sig sha1:1350320e5f7f51553bac8aa403489a1b135bc101 /usr/bin/dd 030202f3452d23010084c2a6cf7de1aeefa119220df0da265a7c44d34380f0d97002e7c778d09cfcf88c018e6595df3ee70eda926851f159332f852e7981a8fca1bc5e959958d06d234a0896861f13cc60da825905c8ed234df26c1deecfa816d5aa9bfb11b905e2814084a86b588be60423afada8dd0dd5a143774c6d890b64195ac42fb47ef5a9a00f0d6c80711d8e0c2b843ec38c02f60fd46bc46c7b4c329ad2dbb1b7625293703f9c739dc4c2bf0769126a2f3cb2cd031d1881cd0af64bf20fd474a993b48620f103a5c14999a2f17d60721bcc019a896b4138a688a59f50cb6cd94a4cfe3b8052e82dec025fef4feabb08c7ce412e3de850f903797e293ec27c329f57fd84e0", ++ "10 1e70a3e1af66f42826ad63b761b4cb9c4df195e1 ima-sig sha256:d33d5d13792292e202dbf69a6f1b07bc8a02f01424db8489ba7bb7d43c0290ef /usr/bin/dd 030204f3452d2301009dd340c852f37e35748363586939d4199b6684be27e7c1236ca1528f708372ed9cd52a0d991f66448790f5616ed5bd7f9bbd22193b1e3e54f6bf29a1497945a34d1b418b24f4cbeaef897bf3cebca27065ebb8761b46bc2662fe76f141245b9186a5ac8493c7f4976cf0d6dfc085c3e503e3f771bc3ccb121230db76fd8aba4f45f060ad64ab3afd99b4e52824b9eba12e93e46f9dcb2fa01d9cef89f298a0da02a82a4fb56924afd3e3c277a1302d99f770d488449df2d43eb5b174a0a528827e6877b965c2f0b7c89cf1aa26a7417a892df4c2294e2872d62748b72ea04ecb0689b5d792e615a9bf9d56f6e0f298560bf9441df0a22729c5f23389f028c25f", + ), + "ima-sig-ec": ( + ast.ImaSig, +- "10 06e804489a77ddab51b9ef27e17053c0e5d503bd ima-sig sha1:1cb84b12db45d7da8de58ba6744187db84082f0e /usr/bin/zmore 030202531f402500483046022100bff9c02dc7b270c83cc94bfec10eecd42831de2cdcb04f024369a14623bc3a91022100cc4d015ae932fb98d6846645ed7d1bb1afd4621ec9089bc087126f191886dd31", ++ "10 5d4d5141ccd5066d50dc3f21d79ba02fedc24256 ima-sig sha256:b8ae0b8dd04a5935cd8165aa2260cd11b658bd71629bdb52256a675a1f73907b /usr/bin/zmore 030204531f402500483046022100fe24678d21083ead47660e1a2d553a592d777c478d1b0466de6ed484b54956b3022100cad3adb37f277bbb03544d6107751b4cd4f2289d8353fa36257400a99334d5c3", + ), + "ima-sig-missing": ( + ast.ImaSig, +diff --git a/test/test_ima_verification.py b/test/test_ima_verification.py +index bdb929c9c..d2fc9ef16 100644 +--- a/test/test_ima_verification.py ++++ b/test/test_ima_verification.py +@@ -27,8 +27,8 @@ + "/lib/modules/5.4.48-openpower1/kernel/drivers/gpu/drm/drm_panel_orientation_quirks.ko": [ + "cd026b58efdf66658685430ff526490d54a430a3f0066a35ac26a8acab66c55d" + ], +- "/usr/bin/dd": ["1350320e5f7f51553bac8aa403489a1b135bc101"], +- "/usr/bin/zmore": ["1cb84b12db45d7da8de58ba6744187db84082f0e"], ++ "/usr/bin/dd": ["d33d5d13792292e202dbf69a6f1b07bc8a02f01424db8489ba7bb7d43c0290ef"], ++ "/usr/bin/zmore": ["b8ae0b8dd04a5935cd8165aa2260cd11b658bd71629bdb52256a675a1f73907b"], + "/usr/bin/zless": ["233ad3a8e77c63a7d9a56063ec2cad1eafa58850"], + }, + "keyrings": { +@@ -50,8 +50,8 @@ + "version": 1, + }, + "hashes": { +- "/usr/bin/dd": ["1350320e5f7f51553bac8aa403489a1b135bc102"], +- "/usr/bin/zmore": ["1cb84b12db45d7da8de58ba6744187db84082f01"], ++ "/usr/bin/dd": ["bad05d13792292e202dbf69a6f1b07bc8a02f01424db8489ba7bb7d43c0290ef"], ++ "/usr/bin/zmore": ["bad00b8dd04a5935cd8165aa2260cd11b658bd71629bdb52256a675a1f73907b"], + }, + } + +@@ -73,8 +73,8 @@ + # 1st signature: RSA + # 2nd signature: EC + SIGNATURES = ( +- "10 50873c47693cf9458e87eb4a02dd4f594f7a0c0f ima-sig sha1:1350320e5f7f51553bac8aa403489a1b135bc101 /usr/bin/dd 030202f3452d23010084c2a6cf7de1aeefa119220df0da265a7c44d34380f0d97002e7c778d09cfcf88c018e6595df3ee70eda926851f159332f852e7981a8fca1bc5e959958d06d234a0896861f13cc60da825905c8ed234df26c1deecfa816d5aa9bfb11b905e2814084a86b588be60423afada8dd0dd5a143774c6d890b64195ac42fb47ef5a9a00f0d6c80711d8e0c2b843ec38c02f60fd46bc46c7b4c329ad2dbb1b7625293703f9c739dc4c2bf0769126a2f3cb2cd031d1881cd0af64bf20fd474a993b48620f103a5c14999a2f17d60721bcc019a896b4138a688a59f50cb6cd94a4cfe3b8052e82dec025fef4feabb08c7ce412e3de850f903797e293ec27c329f57fd84e0\n" +- "10 06e804489a77ddab51b9ef27e17053c0e5d503bd ima-sig sha1:1cb84b12db45d7da8de58ba6744187db84082f0e /usr/bin/zmore 030202531f402500483046022100bff9c02dc7b270c83cc94bfec10eecd42831de2cdcb04f024369a14623bc3a91022100cc4d015ae932fb98d6846645ed7d1bb1afd4621ec9089bc087126f191886dd31\n" ++ "10 1e70a3e1af66f42826ad63b761b4cb9c4df195e1 ima-sig sha256:d33d5d13792292e202dbf69a6f1b07bc8a02f01424db8489ba7bb7d43c0290ef /usr/bin/dd 030204f3452d2301009dd340c852f37e35748363586939d4199b6684be27e7c1236ca1528f708372ed9cd52a0d991f66448790f5616ed5bd7f9bbd22193b1e3e54f6bf29a1497945a34d1b418b24f4cbeaef897bf3cebca27065ebb8761b46bc2662fe76f141245b9186a5ac8493c7f4976cf0d6dfc085c3e503e3f771bc3ccb121230db76fd8aba4f45f060ad64ab3afd99b4e52824b9eba12e93e46f9dcb2fa01d9cef89f298a0da02a82a4fb56924afd3e3c277a1302d99f770d488449df2d43eb5b174a0a528827e6877b965c2f0b7c89cf1aa26a7417a892df4c2294e2872d62748b72ea04ecb0689b5d792e615a9bf9d56f6e0f298560bf9441df0a22729c5f23389f028c25f\n" ++ "10 5d4d5141ccd5066d50dc3f21d79ba02fedc24256 ima-sig sha256:b8ae0b8dd04a5935cd8165aa2260cd11b658bd71629bdb52256a675a1f73907b /usr/bin/zmore 030204531f402500483046022100fe24678d21083ead47660e1a2d553a592d777c478d1b0466de6ed484b54956b3022100cad3adb37f277bbb03544d6107751b4cd4f2289d8353fa36257400a99334d5c3\n" + ) + + COMBINED = MEASUREMENTS + SIGNATURES diff --git a/SOURCES/0002-tpm_bootlog_enrich-Get-DevicePath-length-from-Length.patch b/SOURCES/0002-tpm_bootlog_enrich-Get-DevicePath-length-from-Length.patch deleted file mode 100644 index 922be54..0000000 --- a/SOURCES/0002-tpm_bootlog_enrich-Get-DevicePath-length-from-Length.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 2fee03637d3a1d0c9c004b958af69f4b0e4b57f3 Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Fri, 4 Nov 2022 17:41:31 +0100 -Subject: [PATCH 2/2] tpm_bootlog_enrich: Get DevicePath length from - LengthOfDevicePath - -In enrich_device_path(), get the length of DevicePath from the field -LengthOfDevicePath instead of calculating the length from the bytes -array. - -This avoids a segmentation fault when processing the measured boot event -log in create_mb_refstate script. - -This is called for the events "EV_EFI_BOOT_SERVICES_APPLICATION", -"EV_EFI_BOOT_SERVICES_DRIVER", and "EV_EFI_RUNTIME_SERVICES_DRIVER". - -Fixes: #1153 - -Signed-off-by: Anderson Toshiyuki Sasaki ---- - keylime/tpm_bootlog_enrich.py | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -diff --git a/keylime/tpm_bootlog_enrich.py b/keylime/tpm_bootlog_enrich.py -index ef8e9f7..621bc67 100644 ---- a/keylime/tpm_bootlog_enrich.py -+++ b/keylime/tpm_bootlog_enrich.py -@@ -46,14 +46,14 @@ yaml.add_representer(hexint, representer) - efivarlib_functions = CDLL(config.LIBEFIVAR) - - --def getDevicePath(b): -- ret = efivarlib_functions.efidp_format_device_path(0, 0, b, len(b)) -+def getDevicePath(b, l): -+ ret = efivarlib_functions.efidp_format_device_path(0, 0, b, l) - if ret < 0: - raise Exception(f"getDevicePath: efidp_format_device_path({b}) returned {ret}") - - s = create_string_buffer(ret + 1) - -- ret = efivarlib_functions.efidp_format_device_path(s, ret + 1, b, len(b)) -+ ret = efivarlib_functions.efidp_format_device_path(s, ret + 1, b, l) - if ret < 0: - raise Exception(f"getDevicePath: efidp_format_device_path({b}) returned {ret}") - -@@ -174,7 +174,7 @@ def getVar(event, b): - c = w.decode("utf-16", errors="ignore") - description += c - r["Description"] = description -- devicePath = getDevicePath(b[i:]) -+ devicePath = getDevicePath(b[i:], len(b[i:])) - r["DevicePath"] = devicePath - return r - return None -@@ -184,10 +184,11 @@ def enrich_device_path(d: dict) -> None: - if isinstance(d.get("DevicePath"), str): - try: - b = bytes.fromhex(d["DevicePath"]) -+ l = int(d["LengthOfDevicePath"]) - except Exception: - return - try: -- p = getDevicePath(b) -+ p = getDevicePath(b, l) - # Deal with garbage devicePath - except Exception: - return --- -2.38.1 - diff --git a/SOURCES/0003-Backport-upsteam-PR-1156.patch b/SOURCES/0003-Backport-upsteam-PR-1156.patch deleted file mode 100644 index 60e0f4e..0000000 --- a/SOURCES/0003-Backport-upsteam-PR-1156.patch +++ /dev/null @@ -1,672 +0,0 @@ -From 57c67e2b359e9544ecd5a0ba264adf7aa7c67991 Mon Sep 17 00:00:00 2001 -From: rpm-build -Date: Mon, 14 Nov 2022 21:47:40 -0300 -Subject: [PATCH 3/3] Backport upsteam PR#1156 - -From: https://github.com/keylime/keylime/pull/1156 - -We had partially addressed the issue we encountered when parsing some -certificates with python-cryptography by writing cert_utils module that -provided a single helper to parse the pubkey from a certificate. - -However, we still were doing the EK validation in tpm_main using -python-cryptography, which means we would fall into the same parsing -problem again, since during the validation it would read all the certs -in the tpm cert store to check if it is signing the presented EK. - -We address this issue by moving the EK cert verification to cert_utils: -we use the same approach as before, i.e. we parse the cert with pyasn1 -when python-cryptography fails, and then use python-cryptography to do -the actual signature verification, as before. - -By moving the method to cert_utils, it also becomes simpler to test it, -so in this commit we more tests to verify the methods work as expected. - -Additionally, the updated EK verification is also capable of handling -ECDSA signatures ---- - keylime.conf | 2 + - keylime/cert_utils.py | 144 ++++++++++++++++++++++++++++++++++-- - keylime/registrar_common.py | 7 +- - keylime/tenant.py | 21 ++---- - keylime/tpm/tpm_main.py | 47 +----------- - keylime/tpm_ek_ca.py | 10 +-- - scripts/ek-openssl-verify | 98 ++++++++++++++++++++++++ - test/run_tests.sh | 8 +- - test/test_cert_utils.py | 142 ++++++++++++++++++++++++++++++----- - 9 files changed, 380 insertions(+), 99 deletions(-) - create mode 100755 scripts/ek-openssl-verify - -diff --git a/keylime.conf b/keylime.conf -index 331e57a..d896f9f 100644 ---- a/keylime.conf -+++ b/keylime.conf -@@ -501,6 +501,8 @@ require_ek_cert = True - # PROVKEYS - contains a json document containing EK, EKcert, and AIK from the - # provider. EK and AIK are in PEM format. The EKcert is in base64 encoded - # DER format. -+# TPM_CERT_STORE - contains the path to the TPM certificates store, e.g.: -+# "/var/lib/keylime/tpm_cert_store". - # - # Set to blank to disable this check. See warning above if require_ek_cert - # is "False". -diff --git a/keylime/cert_utils.py b/keylime/cert_utils.py -index d014aed..d2fc54d 100644 ---- a/keylime/cert_utils.py -+++ b/keylime/cert_utils.py -@@ -1,13 +1,143 @@ --from cryptography.hazmat.primitives.serialization import load_der_public_key -+import io -+import os.path -+import subprocess -+import sys -+ -+from cryptography import exceptions as crypto_exceptions -+from cryptography import x509 -+from cryptography.hazmat.backends import default_backend -+from cryptography.hazmat.primitives.asymmetric import ec, padding -+from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey -+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey - from pyasn1.codec.der import decoder, encoder --from pyasn1_modules import rfc2459 -+from pyasn1_modules import pem, rfc2459 - -+from keylime import config, keylime_logging, tpm_ek_ca - - # Issue #944 -- python-cryptography won't parse malformed certs, - # such as some Nuvoton ones we have encountered in the field. - # Unfortunately, we still have to deal with such certs anyway. --# Let's read the EK cert with pyasn1 instead of python-cryptography. --def read_x509_der_cert_pubkey(der_cert_data): -- """Returns the public key of a DER-encoded X.509 certificate""" -- der509 = decoder.decode(der_cert_data, asn1Spec=rfc2459.Certificate())[0] -- return load_der_public_key(encoder.encode(der509["tbsCertificate"]["subjectPublicKeyInfo"])) -+ -+# Here we provide some helpers that use pyasn1 to parse the certificates -+# when parsing them with python-cryptography fails, and in this case, we -+# try to read the parsed certificate again into python-cryptograhy. -+ -+logger = keylime_logging.init_logging("cert_utils") -+ -+ -+def x509_der_cert(der_cert_data: bytes): -+ """Load an x509 certificate provided in DER format -+ :param der_cert_data: the DER bytes of the certificate -+ :type der_cert_data: bytes -+ :returns: cryptography.x509.Certificate -+ """ -+ try: -+ return x509.load_der_x509_certificate(data=der_cert_data, backend=default_backend()) -+ except Exception as e: -+ logger.warning("Failed to parse DER data with python-cryptography: %s", e) -+ pyasn1_cert = decoder.decode(der_cert_data, asn1Spec=rfc2459.Certificate())[0] -+ return x509.load_der_x509_certificate(data=encoder.encode(pyasn1_cert), backend=default_backend()) -+ -+ -+def x509_pem_cert(pem_cert_data: str): -+ """Load an x509 certificate provided in PEM format -+ :param pem_cert_data: the base-64 encoded PEM certificate -+ :type pem_cert_data: str -+ :returns: cryptography.x509.Certificate -+ """ -+ try: -+ return x509.load_pem_x509_certificate(data=pem_cert_data.encode("utf-8"), backend=default_backend()) -+ except Exception as e: -+ logger.warning("Failed to parse PEM data with python-cryptography: %s", e) -+ # Let's read the DER bytes from the base-64 PEM. -+ der_data = pem.readPemFromFile(io.StringIO(pem_cert_data)) -+ # Now we can load it as we do in x509_der_cert(). -+ pyasn1_cert = decoder.decode(der_data, asn1Spec=rfc2459.Certificate())[0] -+ return x509.load_der_x509_certificate(data=encoder.encode(pyasn1_cert), backend=default_backend()) -+ -+ -+def verify_ek(ekcert, tpm_cert_store=config.get("tenant", "tpm_cert_store")): -+ """Verify that the provided EK certificate is signed by a trusted root -+ :param ekcert: The Endorsement Key certificate in DER format -+ :returns: True if the certificate can be verified, False otherwise -+ """ -+ try: -+ trusted_certs = tpm_ek_ca.cert_loader(tpm_cert_store) -+ except Exception as e: -+ logger.warning("Error loading trusted certificates from the TPM cert store: %s", e) -+ return False -+ -+ try: -+ ek509 = x509_der_cert(ekcert) -+ for cert_file, pem_cert in trusted_certs.items(): -+ signcert = x509_pem_cert(pem_cert) -+ if ek509.issuer != signcert.subject: -+ continue -+ -+ signcert_pubkey = signcert.public_key() -+ try: -+ if isinstance(signcert_pubkey, RSAPublicKey): -+ signcert_pubkey.verify( -+ ek509.signature, -+ ek509.tbs_certificate_bytes, -+ padding.PKCS1v15(), -+ ek509.signature_hash_algorithm, -+ ) -+ elif isinstance(signcert_pubkey, EllipticCurvePublicKey): -+ signcert_pubkey.verify( -+ ek509.signature, -+ ek509.tbs_certificate_bytes, -+ ec.ECDSA(ek509.signature_hash_algorithm), -+ ) -+ else: -+ logger.warning("Unsupported public key type: %s", type(signcert_pubkey)) -+ continue -+ except crypto_exceptions.InvalidSignature: -+ continue -+ -+ logger.debug("EK cert matched cert: %s", cert_file) -+ return True -+ except Exception as e: -+ # Log the exception so we don't lose the raw message -+ logger.exception(e) -+ raise Exception("Error processing ek/ekcert. Does this TPM have a valid EK?").with_traceback(sys.exc_info()[2]) -+ -+ logger.error("No Root CA matched EK Certificate") -+ return False -+ -+ -+def verify_ek_script(script, env, cwd): -+ if script is None: -+ logger.warning("External check script (%s) not specified", script) -+ return False -+ -+ script_path = os.path.abspath(script) -+ if not os.path.isfile(script_path): -+ if cwd is None or not os.path.isfile(os.path.abspath(os.path.join(cwd, script))): -+ logger.warning("External check script (%s) not found; please make sure its path is correct", script) -+ return False -+ script_path = os.path.abspath(os.path.join(cwd, script)) -+ -+ try: -+ proc = subprocess.run( -+ [script_path], -+ env=env, -+ shell=False, -+ cwd=cwd, -+ stderr=subprocess.STDOUT, -+ stdout=subprocess.PIPE, -+ check=False, -+ ) -+ if proc.returncode != 0: -+ errmsg = "" -+ if proc.stdout is not None: -+ errmsg = proc.stdout.decode("utf-8") -+ logger.error("External check script failed to validate EK: %s", errmsg) -+ return False -+ logger.debug("External check script successfully to validated EK") -+ if proc.stdout is not None: -+ logger.info("ek_check output: %s", proc.stdout.decode("utf-8")) -+ except subprocess.CalledProcessError as e: -+ logger.error("Error while trying to run external check script to validate EK: %s", e) -+ return False -+ return True -diff --git a/keylime/registrar_common.py b/keylime/registrar_common.py -index 2c32d19..fb37e5b 100644 ---- a/keylime/registrar_common.py -+++ b/keylime/registrar_common.py -@@ -261,11 +261,8 @@ class UnprotectedHandler(BaseHTTPRequestHandler, SessionManager): - # Note, we don't validate the EKCert here, other than the implicit - # "is it a valid x509 cert" check. So it's still untrusted. - # This will be validated by the tenant. -- ek_tpm = base64.b64encode( -- tpm2_objects.ek_low_tpm2b_public_from_pubkey( -- cert_utils.read_x509_der_cert_pubkey(base64.b64decode(ekcert)) -- ) -- ).decode() -+ cert = cert_utils.x509_der_cert(base64.b64decode(ekcert)) -+ ek_tpm = base64.b64encode(tpm2_objects.ek_low_tpm2b_public_from_pubkey(cert.public_key())).decode() - - aik_attrs = tpm2_objects.get_tpm2b_public_object_attributes( - base64.b64decode(aik_tpm), -diff --git a/keylime/tenant.py b/keylime/tenant.py -index cc53623..dd9c09c 100644 ---- a/keylime/tenant.py -+++ b/keylime/tenant.py -@@ -5,7 +5,6 @@ import io - import json - import logging - import os --import subprocess - import sys - import tempfile - import time -@@ -15,7 +14,7 @@ import requests - from cryptography.hazmat.primitives import serialization as crypto_serialization - - from keylime import api_version as keylime_api_version --from keylime import ca_util, config, crypto, keylime_logging, registrar_client, signing, web_util -+from keylime import ca_util, cert_utils, config, crypto, keylime_logging, registrar_client, signing, web_util - from keylime.agentstates import AgentAttestState - from keylime.cli import options, policies - from keylime.cmd import user_data_encrypt -@@ -516,19 +515,11 @@ class Tenant: - env["EK_CERT"] = "" - - env["PROVKEYS"] = json.dumps(self.registrar_data.get("provider_keys", {})) -- with subprocess.Popen( -- script, env=env, shell=True, cwd=config.WORK_DIR, stdout=subprocess.PIPE, stderr=subprocess.STDOUT -- ) as proc: -- retval = proc.wait() -- if retval != 0: -- raise UserError("External check script failed to validate EK") -- logger.debug("External check script successfully to validated EK") -- while True: -- line = proc.stdout.readline().decode() -- if line == "": -- break -- logger.debug("ek_check output: %s", line.strip()) -- return True -+ -+ # Define the TPM cert store for the external script. -+ env["TPM_CERT_STORE"] = config.get("tenant", "tpm_cert_store") -+ -+ return cert_utils.verify_ek_script(script, env, config.WORK_DIR) - - def do_cv(self): - """Initiate v, agent_id and ip and initiate the cloudinit sequence""" -diff --git a/keylime/tpm/tpm_main.py b/keylime/tpm/tpm_main.py -index 7bab4fd..35f0a2f 100644 ---- a/keylime/tpm/tpm_main.py -+++ b/keylime/tpm/tpm_main.py -@@ -12,14 +12,10 @@ import time - import typing - import zlib - --from cryptography import exceptions as crypto_exceptions --from cryptography import x509 --from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import serialization as crypto_serialization --from cryptography.hazmat.primitives.asymmetric import padding - from packaging.version import Version - --from keylime import cmd_exec, config, keylime_logging, secure_mount, tpm_ek_ca -+from keylime import cert_utils, cmd_exec, config, keylime_logging, secure_mount - from keylime.agentstates import TPMClockInfo - from keylime.common import algorithms, retry - from keylime.failure import Component, Failure -@@ -785,46 +781,7 @@ class tpm(tpm_abstract.AbstractTPM): - :param ekcert: The Endorsement Key certificate in DER format - :returns: True if the certificate can be verified, false otherwise - """ -- # openssl x509 -inform der -in certificate.cer -out certificate.pem -- try: -- tpm_ek_ca.check_tpm_cert_store() -- -- ek509 = x509.load_der_x509_certificate( -- data=ekcert, -- backend=default_backend(), -- ) -- -- trusted_certs = tpm_ek_ca.cert_loader() -- for cert in trusted_certs: -- signcert = x509.load_pem_x509_certificate( -- data=cert.encode(), -- backend=default_backend(), -- ) -- -- if ek509.issuer.rfc4514_string() != signcert.subject.rfc4514_string(): -- continue -- -- try: -- signcert.public_key().verify( -- ek509.signature, -- ek509.tbs_certificate_bytes, -- padding.PKCS1v15(), -- ek509.signature_hash_algorithm, -- ) -- except crypto_exceptions.InvalidSignature: -- continue -- -- logger.debug("EK cert matched cert: %s", cert) -- return True -- except Exception as e: -- # Log the exception so we don't lose the raw message -- logger.exception(e) -- raise Exception("Error processing ek/ekcert. Does this TPM have a valid EK?").with_traceback( -- sys.exc_info()[2] -- ) -- -- logger.error("No Root CA matched EK Certificate") -- return False -+ return cert_utils.verify_ek(ekcert) - - def get_tpm_manufacturer(self, output=None): - vendorStr = None -diff --git a/keylime/tpm_ek_ca.py b/keylime/tpm_ek_ca.py -index 3695f0b..fb66c07 100644 ---- a/keylime/tpm_ek_ca.py -+++ b/keylime/tpm_ek_ca.py -@@ -7,8 +7,7 @@ logger = keylime_logging.init_logging("tpm_ek_ca") - trusted_certs = {} - - --def check_tpm_cert_store(): -- tpm_cert_store = config.get("tenant", "tpm_cert_store") -+def check_tpm_cert_store(tpm_cert_store=config.get("tenant", "tpm_cert_store")): - if not os.path.isdir(tpm_cert_store): - logger.error("The directory %s does not exist.", tpm_cert_store) - raise Exception(f"The directory {tpm_cert_store} does not exist.") -@@ -21,11 +20,10 @@ def check_tpm_cert_store(): - raise Exception(f"The directory {tpm_cert_store} does not contain " f"any .pem files") - - --def cert_loader(): -- tpm_cert_store = config.get("tenant", "tpm_cert_store") -+def cert_loader(tpm_cert_store=config.get("tenant", "tpm_cert_store")): - file_list = glob.glob(os.path.join(tpm_cert_store, "*.pem")) -- my_trusted_certs = [] -+ my_trusted_certs = {} - for file_path in file_list: - with open(file_path, encoding="utf-8") as f_input: -- my_trusted_certs.append(f_input.read()) -+ my_trusted_certs[file_path] = f_input.read() - return my_trusted_certs -diff --git a/scripts/ek-openssl-verify b/scripts/ek-openssl-verify -new file mode 100755 -index 0000000..f91e9b5 ---- /dev/null -+++ b/scripts/ek-openssl-verify -@@ -0,0 +1,98 @@ -+#!/bin/sh -+ -+# This script can be used as the `ek_check_script' (tenant configuration), -+# to attempt to verify a provided EK_CERT via env var using openssl. -+ -+# EK - contains a PEM-encoded version of the public EK -+# EK_CERT - contains a base64 DER-encoded EK certificate if one is -+# available. -+# PROVKEYS - contains a json document containing EK, EKcert, and AIK -+# from the provider. EK and AIK are in PEM format. The -+# EKcert is in base64-encoded DER format -+# TPM_CERT_STORE - contains the path of the TPM certificate store. -+EK=${EK:-} -+EK_CERT=${EK_CERT:-} -+PROVKEYS=${PROVKEYS:-} -+ -+EK_VERIFICATION_LOG=${EK_VERIFICATION_LOG:-/var/log/keylime/ek-verification.log} -+LOG="${EK_VERIFICATION_LOG}" -+ -+# Setting log fallback in case we cannot write to the specified file. -+touch "${LOG}" 2>/dev/null || LOG=/dev/stderr -+ -+log() { -+ _stderr=${2:-} -+ echo "[$(date)] ${1}" >&2 >> "${LOG}" -+ [ -n "${_stderr}" ] && [ "${LOG}" != '/dev/stderr' ] && echo "${1}" >&2 -+} -+ -+die() { -+ log "ERROR: ${1}" _ -+ exit 1 -+} -+ -+command -v openssl >/dev/null \ -+ || die "openssl CLI was not found in the PATH; please make sure it is installed" -+ -+[ -n "${EK_CERT}" ] || die "EK_CERT was not provided as an env var" -+ -+# Cert store directory. If one is not provided via TPM_CERT_STORE env var, -+# we start by attempting to read tenant.conf. -+CERT_STORE=${TPM_CERT_STORE:-} -+ -+if [ -z "${CERT_STORE}" ]; then -+ KEYLIME_CONFIG_DIR=${KEYLIME_CONFIG_DIR:-/etc/keylime} -+ [ -d "${KEYLIME_CONFIG_DIR}" ] \ -+ || die "KEYLIME_CONFIG_DIR (${KEYLIME_CONFIG_DIR}) does not seem to exist" -+ -+ if [ -r "${KEYLIME_CONFIG_DIR}"/tenant.conf ]; then -+ CERT_STORE="$(grep -w ^tpm_cert_store "${KEYLIME_CONFIG_DIR}"/tenant.conf \ -+ | tail -1 | cut -d'=' -f2 | tr -d "[:blank:]")" -+ fi -+ -+ # Next we try to read any snippets in tenant.conf.d/ -+ if [ -d "${KEYLIME_CONFIG_DIR}"/tenant.conf.d ]; then -+ for _s in "${KEYLIME_CONFIG_DIR}"/tenant.conf.d/*.conf; do -+ [ -e "${_s}" ] || continue -+ _store="$(grep -w ^tpm_cert_store "${_s}" \ -+ | tail -1 | cut -d'=' -f2 | tr -d "[:blank:]")" -+ [ -n "${_store}" ] && CERT_STORE="${_store}" -+ done -+ fi -+fi -+ -+[ -n "${CERT_STORE}" ] \ -+ || die "It was not possible to determine the TPM cert store dir from tenant.conf or tenant.conf.d/ snippets" -+[ -d "${CERT_STORE}" ] \ -+ || die "TPM cert store is not a valid directory (${CERT_STORE})" -+ -+EK_VERIFICATION_TMPDIR= -+ek_verification_cleanup() { -+ [ -d "${EK_VERIFICATION_TMPDIR}" ] || return 0 -+ rm -rf "${EK_VERIFICATION_TMPDIR}" -+} -+trap ek_verification_cleanup EXIT -+ -+mkdir -p "${TMPDIR:-/tmp}" -+EK_VERIFICATION_TMPDIR="$(mktemp -d)" || \ -+ die "Creating a temp dir for EK verification failed" -+ -+EK_CERT_FILE_DER="${EK_VERIFICATION_TMPDIR}"/ek.der -+EK_CERT_FILE_PEM="${EK_VERIFICATION_TMPDIR}"/ek.pem -+ -+printf '%s' "${EK_CERT}" > "${EK_CERT_FILE_DER}".b64 -+base64 -d "${EK_CERT_FILE_DER}".b64 > "${EK_CERT_FILE_DER}" -+openssl x509 -inform der -in "${EK_CERT_FILE_DER}" > "${EK_CERT_FILE_PEM}" -+ -+for c in "${CERT_STORE}"/*.pem; do -+ [ -e "${c}" ] || continue -+ log "Checking if ${c} is the issuer of EK cert..." -+ if openssl verify -partial_chain -CAfile "${c}" "${EK_CERT_FILE_PEM}" \ -+ >>"${LOG}" 2>>"${LOG}"; then -+ log "${EK_CERT} successfully verified by $(basename "${c}")" _ -+ exit 0 -+ fi -+done -+ -+die "EK signature did not match certificates from TPM cert store" -+# vim:set ts=2 sw=2 et: -diff --git a/test/run_tests.sh b/test/run_tests.sh -index fc43113..81a6dff 100755 ---- a/test/run_tests.sh -+++ b/test/run_tests.sh -@@ -107,19 +107,19 @@ fi - # Set correct dependencies - # Fedora - if [ $PACKAGE_MGR = "dnf" ]; then -- PYTHON_PREIN="python3" -+ PYTHON_PREIN="python3 openssl" - PYTHON_DEPS="python3-pip python3-dbus" - # RHEL / CentOS etc - elif [ $PACKAGE_MGR = "yum" ]; then -- PYTHON_PREIN="epel-release python36" -+ PYTHON_PREIN="epel-release python36 openssl" - PYTHON_DEPS="python36-pip python36-dbus" - # Ubuntu / Debian - elif [ $PACKAGE_MGR = "apt-get" ]; then -- PYTHON_PREIN="python3" -+ PYTHON_PREIN="python3 openssl" - PYTHON_DEPS="python3-pip python3-dbus" - # SUSE - elif [ $PACKAGE_MGR = "zypper" ]; then -- PYTHON_PREIN="python3" -+ PYTHON_PREIN="python3 openssl" - PYTHON_DEPS="python3-pip python3-dbus" - else - echo "No recognized package manager found on this system!" 1>&2 -diff --git a/test/test_cert_utils.py b/test/test_cert_utils.py -index 4666c0f..bdf6090 100644 ---- a/test/test_cert_utils.py -+++ b/test/test_cert_utils.py -@@ -4,17 +4,19 @@ Copyright 2022 Red Hat, Inc. - """ - - import base64 -+import os - import unittest - --from keylime import cert_utils -+import cryptography - -+from keylime import cert_utils, tpm_ek_ca - --class Cert_Utils_Test(unittest.TestCase): -- def test_read_x509_der_cert_pubkey(self): -- # The certificate listed in issue #944, from Nuvoton. It fails to -- # be parsed by python-cryptography with the following error: -- # ValueError: error parsing asn1 value: ParseError { kind: InvalidSetOrdering, location: ["RawCertificate::tbs_cert", "TbsCertificate::issuer", "0", "2"] } -- nuvoton_ecdsa_sha256_der = """\ -+CERT_STORE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "tpm_cert_store")) -+ -+# The certificate listed in issue #944, from Nuvoton. It fails to -+# be parsed by python-cryptography with the following error: -+# ValueError: error parsing asn1 value: ParseError { kind: InvalidSetOrdering, location: ["RawCertificate::tbs_cert", "TbsCertificate::issuer", "0", "2"] } -+nuvoton_ecdsa_sha256_der = """\ - MIICBjCCAaygAwIBAgIIP5MvnZk8FrswCgYIKoZIzj0EAwIwVTFTMB8GA1UEAxMYTnV2b3RvbiBU - UE0gUm9vdCBDQSAyMTEwMCUGA1UEChMeTnV2b3RvbiBUZWNobm9sb2d5IENvcnBvcmF0aW9uMAkG - A1UEBhMCVFcwHhcNMTUxMDE5MDQzMjAwWhcNMzUxMDE1MDQzMjAwWjBVMVMwHwYDVQQDExhOdXZv -@@ -26,10 +28,10 @@ ajW+9zAfBgNVHSMEGDAWgBSfu3mqD1JieL7RUJKacXHpajW+9zAKBggqhkjOPQQDAgNIADBFAiEA - /jiywhOKpiMOUnTfDmXsXfDFokhKVNTXB6Xtqm7J8L4CICjT3/Y+rrSnf8zrBXqWeHDh8Wi41+w2 - ppq6Ev9orZFI - """ -- # This cert from STMicroelectronics presents a different issue when -- # parsed by python-cryptography: -- # ValueError: error parsing asn1 value: ParseError { kind: ExtraData } -- st_sha256_with_rsa_der = """\ -+# This cert from STMicroelectronics presents a different issue when -+# parsed by python-cryptography: -+# ValueError: error parsing asn1 value: ParseError { kind: ExtraData } -+st_sha256_with_rsa_der = """\ - MIIEjTCCA3WgAwIBAgIUTL0P5h7nYu2yjVCyaPw1hv89XoIwDQYJKoZIhvcNAQELBQAwVTELMAkG - A1UEBhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBOVjEmMCQGA1UEAxMdU1RNIFRQ - TSBFSyBJbnRlcm1lZGlhdGUgQ0EgMDUwHhcNMTgwNzExMDAwMDAwWhcNMjgwNzExMDAwMDAwWjAA -@@ -60,10 +62,116 @@ rTJ1x4NA2ZtQMYyT29Yy1UlkjocAaXL5u0m3Hvz///////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// - /////w== - """ -- certs = [nuvoton_ecdsa_sha256_der, st_sha256_with_rsa_der] -- for c in certs: -+ -+st_ecdsa_sha256_der = """\ -+MIIDAzCCAqmgAwIBAgIUIymn2ai+UaVx1bM26/wU7I+sJd8wCgYIKoZIzj0EAwIwVjELMAkGA1UE -+BhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBOVjEnMCUGA1UEAxMeU1RNIFRQTSBF -+Q0MgSW50ZXJtZWRpYXRlIENBIDAxMB4XDTE4MDcyNjAwMDAwMFoXDTI4MDcyNjAwMDAwMFowADBZ -+MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBsTz5y2cedVZxG/GsbXQ9bL6EQylWNjx1b/SSp2EHlN -+aJjtn43iz2zb+qot2UOhQIwPxS5hMCXhasw4XsFXgnijggGpMIIBpTAfBgNVHSMEGDAWgBR+uDbO -++9+KY3H/czP5utcUYWyWyzBCBgNVHSAEOzA5MDcGBFUdIAAwLzAtBggrBgEFBQcCARYhaHR0cDov -+L3d3dy5zdC5jb20vVFBNL3JlcG9zaXRvcnkvMFkGA1UdEQEB/wRPME2kSzBJMRYwFAYFZ4EFAgEM -+C2lkOjUzNTQ0RDIwMRcwFQYFZ4EFAgIMDFNUMzNIVFBIQUhCNDEWMBQGBWeBBQIDDAtpZDowMDQ5 -+MDAwNDBmBgNVHQkEXzBdMBYGBWeBBQIQMQ0wCwwDMi4wAgEAAgF0MEMGBWeBBQISMTowOAIBAAEB -+/6ADCgEBoQMKAQCiAwoBAKMQMA4WAzMuMQoBBAoBAgEB/6QPMA0WBTE0MC0yCgECAQEAMAwGA1Ud -+EwEB/wQCMAAwEAYDVR0lBAkwBwYFZ4EFCAEwDgYDVR0PAQH/BAQDAgMIMEsGCCsGAQUFBwEBBD8w -+PTA7BggrBgEFBQcwAoYvaHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNvbS9zdG10cG1lY2NpbnQw -+MS5jcnQwCgYIKoZIzj0EAwIDSAAwRQIgcNiZkn7poyk6J8Y1Cnwz4nV7YGPb5pBesBg6bk9n6KIC -+IQCE/jkHb/aPP/T3GtfLNHAdHL4JnofAbsDEuLQxAseeZA== -+""" -+ -+ -+def has_strict_x509_parsing(): -+ """Indicates whether python-cryptography has strict x509 parsing.""" -+ -+ # Major release where python-cryptography started being strict -+ # when parsing x509 certificates. -+ PYCRYPTO_STRICT_X509_MAJOR = 35 -+ return int(cryptography.__version__.split(".", maxsplit=1)[0]) >= PYCRYPTO_STRICT_X509_MAJOR -+ -+ -+def expectedFailureIf(condition): -+ """The test is marked as an expectedFailure if the condition is satisfied.""" -+ -+ def wrapper(func): -+ if condition: -+ return unittest.expectedFailure(func) -+ return func -+ -+ return wrapper -+ -+ -+class Cert_Utils_Test(unittest.TestCase): -+ def test_tpm_cert_store(self): -+ tpm_ek_ca.check_tpm_cert_store(CERT_STORE_DIR) -+ my_trusted_certs = tpm_ek_ca.cert_loader(CERT_STORE_DIR) -+ -+ self.assertNotEqual(len(my_trusted_certs), 0) -+ -+ def test_cert_store_certs(self): -+ my_trusted_certs = tpm_ek_ca.cert_loader(CERT_STORE_DIR) -+ for fname, pem_cert in my_trusted_certs.items(): - try: -- pubkey = cert_utils.read_x509_der_cert_pubkey(base64.b64decode(c)) -- except Exception: -- self.fail("read_x509_der_cert_pubkey() is not expected to raise an exception here") -- self.assertIsNotNone(pubkey) -+ cert = cert_utils.x509_pem_cert(pem_cert) -+ except Exception as e: -+ self.fail(f"Failed to load certificate {fname}: {e}") -+ self.assertIsNotNone(cert) -+ -+ def test_verify_ek(self): -+ tests = [ -+ {"cert": st_sha256_with_rsa_der, "expected": True}, # RSA, signed by STM_RSA_05I.pem. -+ {"cert": st_ecdsa_sha256_der, "expected": True}, # ECC, signed by STM_ECC_01I.pem. -+ ] -+ for t in tests: -+ self.assertEqual( -+ cert_utils.verify_ek(base64.b64decode(t["cert"]), CERT_STORE_DIR), -+ t["expected"], -+ msg=f"Test failed for cert {t['cert']}; expected: {t['expected']}", -+ ) -+ -+ @expectedFailureIf(has_strict_x509_parsing()) -+ def test_verify_ek_expected_failures(self): -+ # The following certificates are not compliant, and will fail the -+ # signature verification with python-cryptography, even though they -+ # should validate. Marking as expected failure for now. -+ tests = [ -+ {"cert": nuvoton_ecdsa_sha256_der, "expected": True}, # ECC, signed by NUVO_2110.pem. -+ ] -+ for t in tests: -+ self.assertEqual( -+ cert_utils.verify_ek(base64.b64decode(t["cert"]), CERT_STORE_DIR), -+ t["expected"], -+ msg=f"Test failed for cert {t['cert']}; expected: {t['expected']}", -+ ) -+ -+ def test_verify_ek_script(self): -+ # We will be using `nuvoton_ecdsa_sha256_der', which is signed by -+ # NUVO_2110.pem but fails verification when using python-cryptography -+ # as it is a malformed cert -- it is the same one we use in -+ # test_verify_ek_expected_failures(). -+ # With an external script `ek_script_check' that uses openssl, the -+ # validation works. -+ cert = nuvoton_ecdsa_sha256_der.replace("\n", "") -+ -+ self.assertFalse(cert_utils.verify_ek_script(None, None, None)) -+ self.assertFalse(cert_utils.verify_ek_script("/foo/bar", None, None)) -+ -+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "scripts", "ek-openssl-verify")) -+ # Testing ek-openssl-verify script, but without specifying the -+ # EK_CERT env var. -+ self.assertFalse(cert_utils.verify_ek_script(script, None, None)) -+ -+ # Now let's specify the EK_CERT. -+ env = os.environ.copy() -+ env["EK_CERT"] = cert -+ env["TPM_CERT_STORE"] = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "tpm_cert_store")) -+ self.assertTrue(cert_utils.verify_ek_script(script, env, None)) -+ -+ # Now, let us specify the ek_check_script with a relative path. -+ script = "ek-openssl-verify" -+ cwd = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "scripts")) -+ self.assertTrue(cert_utils.verify_ek_script(script, env, cwd)) -+ -+ # And now we try a bad TPM cert store. -+ env["TPM_CERT_STORE"] = "/some/bad/directory" -+ self.assertFalse(cert_utils.verify_ek_script(script, env, cwd)) --- -2.38.1 - diff --git a/SOURCES/0003-logging-remove-option-to-log-into-separate-file.patch b/SOURCES/0003-logging-remove-option-to-log-into-separate-file.patch new file mode 100644 index 0000000..e7e3672 --- /dev/null +++ b/SOURCES/0003-logging-remove-option-to-log-into-separate-file.patch @@ -0,0 +1,136 @@ +From eb5112dd597336b566378b3a157e76fe3cbbbfee Mon Sep 17 00:00:00 2001 +From: Thore Sommer +Date: Mon, 16 Jan 2023 07:26:08 -0300 +Subject: [PATCH 3/3] logging: remove option to log into separate file + +The implementation had the issue that only the main loggers were added and that +the permissions were not set strict enough. Users should use the logging +provided by systemd instead. + +Signed-off-by: Thore Sommer +--- + keylime.conf | 10 ---------- + keylime/keylime_logging.py | 31 ------------------------------ + scripts/templates/2.0/registrar.j2 | 9 --------- + scripts/templates/2.0/verifier.j2 | 9 --------- + 4 files changed, 59 deletions(-) + +diff --git a/keylime.conf b/keylime.conf +index d896f9f..043b6a8 100644 +--- a/keylime.conf ++++ b/keylime.conf +@@ -342,11 +342,6 @@ tomtou_errors = False + # signature check before storing them in the database. + require_allow_list_signatures = False + +-# Destination for log output, in addition to console. Values can be 'file', +-# with the file being named after the "service" - cloud_verifier - created under +-# /var/log/keylime), 'stream' or it can be left empty (which results in +-# logging to console only, recommended when running inside a container) +-log_destination = file + + #============================================================================= + [tenant] +@@ -595,11 +590,6 @@ auto_migrate_db = True + # The file to use for SQLite persistence of provider hypervisor data. + prov_db_filename = provider_reg_data.sqlite + +-# Destination for log output, in addition to console. Values can be 'file', +-# with the file being named after the "service" - registrar - created under +-# /var/log/keylime), 'stream' or it can be left empty (which results in +-# logging to console only, recommended when running inside a container) +-log_destination = file + + #============================================================================= + [ca] +diff --git a/keylime/keylime_logging.py b/keylime/keylime_logging.py +index bc8a11d..f7c7a8f 100644 +--- a/keylime/keylime_logging.py ++++ b/keylime/keylime_logging.py +@@ -1,17 +1,10 @@ + import logging +-import os + from logging import Logger + from logging import config as logging_config + from typing import Any, Callable, Dict + + from keylime import config + +-LOG_TO_FILE = set() +-LOG_TO_STREAM = set() +-LOGDIR = os.getenv("KEYLIME_LOGDIR", "/var/log/keylime") +-# not clear that this works right. console logging may not work +-LOGSTREAM = os.path.join(LOGDIR, "keylime-stream.log") +- + logging_config.fileConfig(config.get_config("logging")) + + +@@ -50,31 +43,7 @@ def log_http_response(logger: Logger, loglevel: int, response_body: Dict[str, An + + + def init_logging(loggername: str) -> Logger: +- +- if loggername in ("verifier", "registrar"): +- logdest = config.get(loggername, "log_destination", fallback="") +- if logdest == "file": +- LOG_TO_FILE.add(loggername) +- if logdest == "stream": +- LOG_TO_STREAM.add(loggername) +- + logger = logging.getLogger(f"keylime.{loggername}") + logging.getLogger("requests").setLevel(logging.WARNING) +- mainlogger = logging.getLogger("keylime") +- basic_formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s") +- if loggername in LOG_TO_FILE: +- logfilename = os.path.join(LOGDIR, f"{loggername}.log") +- if not os.path.exists(LOGDIR): +- os.makedirs(LOGDIR, 0o750) +- fh = logging.FileHandler(logfilename) +- fh.setLevel(logger.getEffectiveLevel()) +- fh.setFormatter(basic_formatter) +- mainlogger.addHandler(fh) +- +- if loggername in LOG_TO_STREAM: +- fh = logging.FileHandler(filename=LOGSTREAM, mode="w") +- fh.setLevel(logger.getEffectiveLevel()) +- fh.setFormatter(basic_formatter) +- mainlogger.addHandler(fh) + + return logger +diff --git a/scripts/templates/2.0/registrar.j2 b/scripts/templates/2.0/registrar.j2 +index 3d92303..8de7a50 100644 +--- a/scripts/templates/2.0/registrar.j2 ++++ b/scripts/templates/2.0/registrar.j2 +@@ -71,12 +71,3 @@ auto_migrate_db = {{ registrar.auto_migrate_db }} + + # The file to use for SQLite persistence of provider hypervisor data. + prov_db_filename: {{ registrar.prov_db_filename }} +- +-# Destination for log output, in addition to console. If left empty, the log +-# output will only be printed to console (recommended for containers to avoid +-# filling data storage). The accepted values are: +-# 'file': The log output will also be written to a file named after the +-# component in '/var/log/keylime/registrar.log' +-# 'stream': The log output will be written to a common file in +-# 'var/log/keylime/keylime-stream.log' +-log_destination = {{ registrar.log_destination }} +diff --git a/scripts/templates/2.0/verifier.j2 b/scripts/templates/2.0/verifier.j2 +index d1584df..7a66cb1 100644 +--- a/scripts/templates/2.0/verifier.j2 ++++ b/scripts/templates/2.0/verifier.j2 +@@ -196,12 +196,3 @@ zmq_port = {{ verifier.zmq_port }} + + # Webhook url for revocation notifications. + webhook_url = {{ verifier.webhook_url }} +- +-# Destination for log output, in addition to console. If left empty, the log +-# output will only be printed to console (recommended for containers to avoid +-# filling data storage). The accepted values are: +-# 'file': The log output will also be written to a file named after the +-# component in '/var/log/keylime/verifier.log' +-# 'stream': The log output will be written to a common file in +-# 'var/log/keylime/keylime-stream.log' +-log_destination = {{ verifier.log_destination }} +-- +2.38.1 + diff --git a/SOURCES/keylime.fc b/SOURCES/keylime.fc deleted file mode 100644 index 5114c47..0000000 --- a/SOURCES/keylime.fc +++ /dev/null @@ -1,24 +0,0 @@ -/usr/bin/keylime_agent -- gen_context(system_u:object_r:keylime_agent_exec_t,s0) -/usr/bin/keylime_ima_emulator -- gen_context(system_u:object_r:keylime_agent_exec_t,s0) -/usr/bin/keylime_userdata_encrypt -- gen_context(system_u:object_r:keylime_agent_exec_t,s0) - -/usr/bin/keylime_ca -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/bin/keylime_migrations_apply -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/bin/keylime_registrar -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/bin/keylime_verifier -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/bin/keylime_tenant -- gen_context(system_u:object_r:keylime_server_exec_t,s0) - -/usr/local/bin/keylime_agent -- gen_context(system_u:object_r:keylime_agent_exec_t,s0) -/usr/local/bin/keylime_ima_emulator -- gen_context(system_u:object_r:keylime_agent_exec_t,s0) -/usr/local/bin/keylime_userdata_encrypt -- gen_context(system_u:object_r:keylime_agent_exec_t,s0) - -/usr/local/bin/keylime_ca -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/local/bin/keylime_migrations_apply -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/local/bin/keylime_registrar -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/local/bin/keylime_verifier -- gen_context(system_u:object_r:keylime_server_exec_t,s0) -/usr/local/bin/keylime_tenant -- gen_context(system_u:object_r:keylime_server_exec_t,s0) - -/var/lib/keylime(/.*)? gen_context(system_u:object_r:keylime_var_lib_t,s0) -/var/lib/keylime-agent(/.*)? gen_context(system_u:object_r:keylime_var_lib_t,s0) - -/var/log/keylime(/.*)? gen_context(system_u:object_r:keylime_log_t,s0) diff --git a/SOURCES/keylime.if b/SOURCES/keylime.if deleted file mode 100644 index 1614a33..0000000 --- a/SOURCES/keylime.if +++ /dev/null @@ -1,37 +0,0 @@ -## policy for keylime - -######################################## -## -## Add to specified type to keylime_type attribute . -## -## -## -## Type to be used for keylime domains. -## -## -# -interface(`keylime_use_keylime_domain',` - gen_require(` - attribute keylime_domain; - ') - - typeattribute $1 keylime_domain; -') - -######################################## -## -## Mounton keylime lib directory. -## -## -## -## Domain allowed access. -## -## -# -interface(`keylime_mounton_var_lib',` - gen_require(` - type keylime_var_lib_t; - ') - - allow $1 keylime_var_lib_t:dir mounton; -') diff --git a/SOURCES/keylime.te b/SOURCES/keylime.te deleted file mode 100644 index cd02baf..0000000 --- a/SOURCES/keylime.te +++ /dev/null @@ -1,140 +0,0 @@ -policy_module(keylime, 1.0.0) - -######################################## -# -# Declarations -# - -attribute keylime_domain; - -type keylime_agent_t; -keylime_use_keylime_domain(keylime_agent_t) -type keylime_agent_exec_t; -init_daemon_domain(keylime_agent_t, keylime_agent_exec_t) - -type keylime_server_t; -keylime_use_keylime_domain(keylime_server_t) -type keylime_server_exec_t; -init_daemon_domain(keylime_server_t, keylime_server_exec_t) - -type keylime_log_t; -logging_log_file(keylime_log_t) - -type keylime_var_lib_t; -files_type(keylime_var_lib_t) - -type keylime_tmp_t; -files_tmp_file(keylime_tmp_t) - -######################################## -# -# keylime domain policy -# - -allow keylime_domain self:tcp_socket create_stream_socket_perms; - -manage_dirs_pattern(keylime_domain, keylime_tmp_t, keylime_tmp_t) -manage_files_pattern(keylime_domain, keylime_tmp_t, keylime_tmp_t) -files_tmp_filetrans(keylime_domain, keylime_tmp_t, { dir file }) - -manage_dirs_pattern(keylime_domain, keylime_var_lib_t, keylime_var_lib_t) -manage_files_pattern(keylime_domain, keylime_var_lib_t, keylime_var_lib_t) -files_var_lib_filetrans(keylime_domain, keylime_var_lib_t, { dir file lnk_file }) - -corecmd_exec_bin(keylime_domain) - -corenet_tcp_bind_generic_node(keylime_domain) -corenet_tcp_bind_all_ports(keylime_domain) -corenet_tcp_connect_all_unreserved_ports(keylime_domain) - -dev_read_sysfs(keylime_domain) - -fs_tmpfs_filetrans(keylime_domain, keylime_var_lib_t, { dir file }) - -init_named_socket_activation(keylime_domain, keylime_var_lib_t, "keylime") - -miscfiles_read_generic_certs(keylime_domain) - -sysnet_read_config(keylime_domain) - -userdom_exec_user_tmp_files(keylime_domain) -userdom_manage_user_tmp_dirs(keylime_domain) -userdom_manage_user_tmp_files(keylime_domain) - -######################################## -# -# keylime server policy -# - -allow keylime_server_t self:netlink_route_socket { create_stream_socket_perms nlmsg_read }; -allow keylime_server_t self:udp_socket create_stream_socket_perms; - -manage_dirs_pattern(keylime_server_t, keylime_log_t, keylime_log_t) -manage_files_pattern(keylime_server_t, keylime_log_t, keylime_log_t) - -fs_rw_inherited_tmpfs_files(keylime_server_t) - -optional_policy(` - gpg_exec(keylime_server_t) -') - -optional_policy(` - kerberos_read_config(keylime_server_t) - kerberos_read_keytab(keylime_server_t) -') - -optional_policy(` - sssd_run_stream_connect(keylime_server_t) -') - - -######################################## -# -# keylime agent policy -# -#work with /var/lib/keylime/secure -allow keylime_agent_t self:capability { chown dac_override dac_read_search setgid setuid sys_nice sys_ptrace }; -allow keylime_agent_t self:chr_file getattr; - -#FIX ME, add to tabrmd policy interface related with this -allow keylime_agent_t domain:unix_stream_socket rw_stream_socket_perms; #selint-disable:W-001 - -dev_rw_tpm(keylime_agent_t) - -exec_files_pattern(keylime_agent_t, keylime_var_lib_t, keylime_var_lib_t) -files_read_var_lib_files(keylime_agent_t) - -fs_dontaudit_search_cgroup_dirs(keylime_agent_t) -fs_getattr_cgroup(keylime_agent_t) -fs_mount_tmpfs(keylime_agent_t) -fs_setattr_tmpfs_dirs(keylime_agent_t) - -init_dontaudit_stream_connect(keylime_agent_t) - -kernel_read_all_proc(keylime_agent_t) - -userdom_dontaudit_search_user_home_dirs(keylime_agent_t) - -auth_read_passwd(keylime_agent_t) - -keylime_mounton_var_lib(keylime_agent_t) - -mount_domtrans(keylime_agent_t) - -selinux_read_policy(keylime_agent_t) - -optional_policy(` - #FIX ME, add to tabrmd policy interface related with this - #https://github.com/tpm2-software/tpm2-abrmd/blob/master/selinux - dbus_chat_system_bus(keylime_agent_t) -') - -optional_policy(` - dbus_stream_connect_system_dbusd(keylime_agent_t) - dbus_system_bus_client(keylime_agent_t) -') - -optional_policy(` - systemd_userdbd_stream_connect(keylime_agent_t) - systemd_machined_stream_connect(keylime_agent_t) -') diff --git a/SPECS/keylime.spec b/SPECS/keylime.spec index 7708fcd..8a47a55 100644 --- a/SPECS/keylime.spec +++ b/SPECS/keylime.spec @@ -1,4 +1,5 @@ %global srcname keylime +%global policy_version 1.0.0 %global with_selinux 1 %global selinuxtype targeted @@ -7,21 +8,18 @@ %global debug_package %{nil} Name: keylime -Version: 6.5.1 -Release: 1%{?dist}.4 +Version: 6.5.2 +Release: 4%{?dist} Summary: Open source TPM software for Bootstrapping and Maintaining Trust URL: https://github.com/keylime/keylime Source0: https://github.com/keylime/keylime/archive/refs/tags/v%{version}.tar.gz Source1: %{srcname}.sysusers -Source2: %{srcname}.te -Source3: %{srcname}.if -Source4: %{srcname}.fc +Source2: https://github.com/RedHat-SP-Security/%{name}-selinux/archive/v%{policy_version}/keylime-selinux-%{policy_version}.tar.gz -Patch: 0001-ima-Fix-log-evaluation-on-quick-succession-execution.patch -Patch: 0002-tpm_bootlog_enrich-Get-DevicePath-length-from-Length.patch -Patch: 0003-Backport-upsteam-PR-1156.patch -Patch: 0004-Do-not-use-default-values-that-need-reading-the-conf.patch +Patch: 0001-Do-not-use-default-values-that-need-reading-the-conf.patch +Patch: 0002-Switch-to-sha256-hashes-for-signatures.patch +Patch: 0003-logging-remove-option-to-log-into-separate-file.patch License: ASL 2.0 and MIT @@ -145,15 +143,12 @@ Requires: python3-%{srcname} = %{version}-%{release} The Keylime Tenant can be used to provision a Keylime Agent. %prep -%autosetup -S git -n %{srcname}-%{version} +%autosetup -S git -n %{srcname}-%{version} -a2 %if 0%{?with_selinux} # SELinux policy (originally from selinux-policy-contrib) # this policy module will override the production module mkdir selinux -cp -p %{SOURCE2} selinux/ -cp -p %{SOURCE3} selinux/ -cp -p %{SOURCE4} selinux/ make -f %{_datadir}/selinux/devel/Makefile %{srcname}.pp bzip2 -9 %{srcname}.pp @@ -197,7 +192,7 @@ done %if 0%{?with_selinux} install -D -m 0644 %{srcname}.pp.bz2 %{buildroot}%{_datadir}/selinux/packages/%{selinuxtype}/%{srcname}.pp.bz2 -install -D -p -m 0644 selinux/%{srcname}.if %{buildroot}%{_datadir}/selinux/devel/include/distributed/%{srcname}.if +install -D -p -m 0644 keylime-selinux-%{policy_version}/%{srcname}.if %{buildroot}%{_datadir}/selinux/devel/include/distributed/%{srcname}.if %endif @@ -325,6 +320,7 @@ fi %{python3_sitelib}/%{srcname} %{_datadir}/%{srcname}/scripts/create_mb_refstate %{_datadir}/%{srcname}/scripts/create_policy +%{_bindir}/keylime_convert_ima_policy %files base %license LICENSE @@ -346,25 +342,24 @@ fi %license LICENSE %changelog -* Tue Nov 15 2022 Sergio Correia - 6.5.1-1.4 -- Do not use default values that need reading the config in methods - Resolves: rhbz#2142033 - Registrar may crash during EK validation when require_ek_cert is enabled [rhel-9.1.0.z] +* Fri Jan 13 2023 Sergio Correia - 6.5.2-4 +- Backport upstream PR#1240 - logging: remove option to log into separate file + Resolves: rhbz#2154584 - keylime verifier is not logging to /var/log/keylime -* Mon Nov 14 2022 Sergio Correia - 6.5.1-1.3 -- Backport upstream PR#1156 - Resolves: rhbz#2142033 - Registrar may crash during EK validation when require_ek_cert is enabled [rhel-9.1.0.z] +* Thu Dec 1 2022 Sergio Correia - 6.5.2-3 +- Remove leftover policy file + Related: rhbz#2152135 -* Mon Nov 14 2022 Sergio Correia - 6.5.1-1.2 -- Segmentation fault in create_mb_refstate script - Resolves: rhbz#2142034 - Segmentation fault in /usr/share/keylime/create_mb_refstate script [rhel-9.1.0.z] +* Thu Dec 1 2022 Patrik Koncity - 6.5.2-2 +- Use keylime selinux policy from upstream. + Resolves: rhbz#2152135 -* Mon Nov 14 2022 Sergio Correia - 6.5.1-1.1 -- ima: Fix log evaluation on quick-succession execution of scripts - Resolves: rhbz#2142032 - agent fails IMA attestation when one scripts is executed quickly after the other [rhel-9.1.0.z] - -* Thu Oct 13 2022 Sergio Correia - 6.5.1-1 -- Update to 6.5.1 +* Mon Nov 14 2022 Sergio Correia - 6.5.2-1 +- Update to 6.5.2 Resolves: CVE-2022-3500 + Resolves: rhbz#2138167 - agent fails IMA attestation when one scripts is executed quickly after the other + Resolves: rhbz#2140670 - Segmentation fault in /usr/share/keylime/create_mb_refstate script + Resolves: rhbz#142009 - Registrar may crash during EK validation when require_ek_cert is enabled * Tue Sep 13 2022 Sergio Correia - 6.5.0-1 - Update to 6.5.0