diff --git a/.gitignore b/.gitignore index e42baaf..62b0a25 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /v6.4.1.tar.gz +/v6.4.2.tar.gz diff --git a/0001-Improve-error-handling-when-doing-signature-verifica.patch b/0001-Improve-error-handling-when-doing-signature-verifica.patch deleted file mode 100644 index 078b048..0000000 --- a/0001-Improve-error-handling-when-doing-signature-verifica.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 90811cc0df4f32fbf9e5389cca15813e2f6395cb Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Fri, 3 Jun 2022 22:01:15 -0300 -Subject: [PATCH 1/5] Improve error handling when doing signature verification - -This makes verify_signature_from_file() more consistent in that it will -always raise an exception informing the signature verification failed, -when this situation happens. - -As it is, verify_signature() can raise a few different exceptions, and -those were not handled by verify_signature_from_file(). - -Signed-off-by: Sergio Correia ---- - keylime/signing.py | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/keylime/signing.py b/keylime/signing.py -index 71f8be0..1353c1e 100644 ---- a/keylime/signing.py -+++ b/keylime/signing.py -@@ -30,7 +30,13 @@ def verify_signature_from_file(key_file, filename, sig_file, file_description): - with open(filename, "rb") as file_f: - file = file_f.read() - -- if verify_signature(key, sig, file): -+ verified = False -+ try: -+ verified = verify_signature(key, sig, file) -+ except Exception as e: -+ logger.warning("Unable to verify signature: %s", e) -+ -+ if verified: - logger.debug("%s passed signature verification", file_description.capitalize()) - else: - raise Exception( --- -2.35.1 - diff --git a/0002-revocation_notifier-Factor-out-revocation-message-pr.patch b/0002-revocation_notifier-Factor-out-revocation-message-pr.patch deleted file mode 100644 index 474551e..0000000 --- a/0002-revocation_notifier-Factor-out-revocation-message-pr.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 09db3fe88b22c0e1522343c14f184ea610883fcf Mon Sep 17 00:00:00 2001 -From: Daiki Ueno -Date: Mon, 21 Mar 2022 11:06:45 +0100 -Subject: [PATCH 2/5] revocation_notifier: Factor out revocation message - processing - -This moves the revocation message processing logic out of -await_notifications, so it can be called directly from the POST -handler. - -Signed-off-by: Daiki Ueno ---- - keylime/revocation_notifier.py | 45 ++++++++++++++++++---------------- - 1 file changed, 24 insertions(+), 21 deletions(-) - -diff --git a/keylime/revocation_notifier.py b/keylime/revocation_notifier.py -index 4854a99..7cfe0e5 100644 ---- a/keylime/revocation_notifier.py -+++ b/keylime/revocation_notifier.py -@@ -165,6 +165,29 @@ def notify_webhook(tosend): - cert_key = None - - -+def process_revocation(revocation, callback, cert_path): -+ global cert_key -+ -+ if cert_key is None: -+ # load up the CV signing public key -+ if cert_path is not None and os.path.exists(cert_path): -+ logger.info("Lazy loading the revocation certificate from %s", cert_path) -+ with open(cert_path, "rb") as f: -+ certpem = f.read() -+ cert_key = crypto.x509_import_pubkey(certpem) -+ -+ if cert_key is None: -+ logger.warning("Unable to check signature of revocation message: %s not available", cert_path) -+ elif "signature" not in revocation or revocation["signature"] == "none": -+ logger.warning("No signature on revocation message from server") -+ elif not crypto.rsa_verify(cert_key, revocation["msg"].encode("utf-8"), revocation["signature"].encode("utf-8")): -+ logger.error("Invalid revocation message siganture %s", revocation) -+ else: -+ message = json.loads(revocation["msg"]) -+ logger.debug("Revocation signature validated for revocation: %s", message) -+ callback(message) -+ -+ - def await_notifications(callback, revocation_cert_path): - # keep old typo "listen_notfications" around for a few versions - assert config.getboolean("cloud_agent", "listen_notifications", fallback=False) or config.getboolean( -@@ -175,8 +198,6 @@ def await_notifications(callback, revocation_cert_path): - except ImportError as error: - raise Exception("install PyZMQ for 'listen_notifications' option") from error - -- global cert_key -- - if revocation_cert_path is None: - raise Exception("must specify revocation_cert_path") - -@@ -197,25 +218,7 @@ def await_notifications(callback, revocation_cert_path): - while True: - rawbody = mysock.recv() - body = json.loads(rawbody) -- -- if cert_key is None: -- # load up the CV signing public key -- if revocation_cert_path is not None and os.path.exists(revocation_cert_path): -- logger.info("Lazy loading the revocation certificate from %s", revocation_cert_path) -- with open(revocation_cert_path, "rb") as f: -- certpem = f.read() -- cert_key = crypto.x509_import_pubkey(certpem) -- -- if cert_key is None: -- logger.warning("Unable to check signature of revocation message: %s not available", revocation_cert_path) -- elif "signature" not in body or body["signature"] == "none": -- logger.warning("No signature on revocation message from server") -- elif not crypto.rsa_verify(cert_key, body["msg"].encode("utf-8"), body["signature"].encode("utf-8")): -- logger.error("Invalid revocation message siganture %s", body) -- else: -- message = json.loads(body["msg"]) -- logger.debug("Revocation signature validated for revocation: %s", message) -- callback(message) -+ process_revocation(body, callback, revocation_cert_path) - - - def main(): --- -2.35.1 - diff --git a/0003-keylime_agent-Support-notifications-revocation-REST-.patch b/0003-keylime_agent-Support-notifications-revocation-REST-.patch deleted file mode 100644 index 9105bf5..0000000 --- a/0003-keylime_agent-Support-notifications-revocation-REST-.patch +++ /dev/null @@ -1,175 +0,0 @@ -From 3defa4f6c399a4965dbea8b8188992bb470c2215 Mon Sep 17 00:00:00 2001 -From: Daiki Ueno -Date: Mon, 21 Mar 2022 11:46:08 +0100 -Subject: [PATCH 3/5] keylime_agent: Support /notifications/revocation REST - method - -Signed-off-by: Daiki Ueno ---- - keylime/keylime_agent.py | 109 ++++++++++++++++++++++++--------------- - 1 file changed, 68 insertions(+), 41 deletions(-) - -diff --git a/keylime/keylime_agent.py b/keylime/keylime_agent.py -index f8ea0f5..9ee9797 100644 ---- a/keylime/keylime_agent.py -+++ b/keylime/keylime_agent.py -@@ -210,17 +210,13 @@ class Handler(BaseHTTPRequestHandler): - rest_params = web_util.get_restful_params(self.path) - - if rest_params is None: -- web_util.echo_json_response(self, 405, "Not Implemented: Use /keys/ interface") -+ web_util.echo_json_response(self, 405, "Not Implemented: Use /keys/ or /notifications/ interface") - return - - if not rest_params["api_version"]: - web_util.echo_json_response(self, 400, "API Version not supported") - return - -- if rest_params.get("keys", None) not in ["ukey", "vkey"]: -- web_util.echo_json_response(self, 400, "Only /keys/ukey or /keys/vkey are supported") -- return -- - content_length = int(self.headers.get("Content-Length", 0)) - if content_length <= 0: - logger.warning("POST returning 400 response, expected content in message. url: %s", self.path) -@@ -230,6 +226,26 @@ class Handler(BaseHTTPRequestHandler): - post_body = self.rfile.read(content_length) - try: - json_body = json.loads(post_body) -+ except Exception as e: -+ logger.warning("POST returning 400 response, could not parse body data: %s", e) -+ web_util.echo_json_response(self, 400, "content is invalid") -+ return -+ -+ if "notifications" in rest_params: -+ if rest_params["notifications"] == "revocation": -+ revocation_notifier.process_revocation( -+ json_body, perform_actions, cert_path=self.server.revocation_cert_path -+ ) -+ web_util.echo_json_response(self, 200, "Success") -+ else: -+ web_util.echo_json_response(self, 400, "Only /notifications/revocation is supported") -+ return -+ -+ if rest_params.get("keys", None) not in ["ukey", "vkey"]: -+ web_util.echo_json_response(self, 400, "Only /keys/ukey or /keys/vkey are supported") -+ return -+ -+ try: - b64_encrypted_key = json_body["encrypted_key"] - decrypted_key = crypto.rsa_decrypt(self.server.rsaprivatekey, base64.b64decode(b64_encrypted_key)) - except (ValueError, KeyError, TypeError) as e: -@@ -392,6 +408,7 @@ class CloudAgentHTTPServer(ThreadingMixIn, HTTPServer): - rsakey_path = None - mtls_cert_enabled = False - mtls_cert = None -+ revocation_cert_path = None - done = threading.Event() - auth_tag = None - payload = None -@@ -456,6 +473,13 @@ class CloudAgentHTTPServer(ThreadingMixIn, HTTPServer): - self.mtls_cert = None - logger.info("WARNING: mTLS disabled, Tenant and Verifier will reach out to agent via HTTP") - -+ self.revocation_cert_path = config.get("cloud_agent", "revocation_cert") -+ if self.revocation_cert_path == "default": -+ self.revocation_cert_path = os.path.join(secdir, "unzipped/RevocationNotifier-cert.crt") -+ elif self.revocation_cert_path[0] != "/": -+ # if it is a relative, convert to absolute in work_dir -+ self.revocation_cert_path = os.path.abspath(os.path.join(config.WORK_DIR, self.revocation_cert_path)) -+ - # attempt to get a U value from the TPM NVRAM - nvram_u = tpm_instance.read_key_nvram() - if nvram_u is not None: -@@ -549,6 +573,45 @@ class CloudAgentHTTPServer(ThreadingMixIn, HTTPServer): - return False - - -+# Execute revocation action -+def perform_actions(revocation): -+ actionlist = [] -+ -+ # load the actions from inside the keylime module -+ actionlisttxt = config.get("cloud_agent", "revocation_actions") -+ if actionlisttxt.strip() != "": -+ actionlist = actionlisttxt.split(",") -+ actionlist = [f"revocation_actions.{i}" % i for i in actionlist] -+ -+ # load actions from unzipped -+ secdir = secure_mount.mount() -+ action_list_path = os.path.join(secdir, "unzipped/action_list") -+ if os.path.exists(action_list_path): -+ with open(action_list_path, encoding="utf-8") as f: -+ actionlisttxt = f.read() -+ if actionlisttxt.strip() != "": -+ localactions = actionlisttxt.strip().split(",") -+ for action in localactions: -+ if not action.startswith("local_action_"): -+ logger.warning("Invalid local action: %s. Must start with local_action_", action) -+ else: -+ actionlist.append(action) -+ -+ uzpath = os.path.join(secdir, "unzipped") -+ if uzpath not in sys.path: -+ sys.path.append(uzpath) -+ -+ for action in actionlist: -+ logger.info("Executing revocation action %s", action) -+ try: -+ module = importlib.import_module(action) -+ execute = getattr(module, "execute") -+ loop = asyncio.new_event_loop() -+ loop.run_until_complete(execute(revocation)) -+ except Exception as e: -+ logger.warning("Exception during execution of revocation action %s: %s", action, e) -+ -+ - def revocation_listener(): - """ - This configures and starts the revocation listener. It is designed to be started in a separate process. -@@ -573,42 +636,6 @@ def revocation_listener(): - # if it is a relative, convert to absolute in work_dir - cert_path = os.path.abspath(os.path.join(config.WORK_DIR, cert_path)) - -- # Callback function handling the revocations -- def perform_actions(revocation): -- actionlist = [] -- -- # load the actions from inside the keylime module -- actionlisttxt = config.get("cloud_agent", "revocation_actions") -- if actionlisttxt.strip() != "": -- actionlist = actionlisttxt.split(",") -- actionlist = [f"revocation_actions.{i}" % i for i in actionlist] -- -- # load actions from unzipped -- action_list_path = os.path.join(secdir, "unzipped/action_list") -- if os.path.exists(action_list_path): -- with open(action_list_path, encoding="utf-8") as f: -- actionlisttxt = f.read() -- if actionlisttxt.strip() != "": -- localactions = actionlisttxt.strip().split(",") -- for action in localactions: -- if not action.startswith("local_action_"): -- logger.warning("Invalid local action: %s. Must start with local_action_", action) -- else: -- actionlist.append(action) -- -- uzpath = os.path.join(secdir, "unzipped") -- if uzpath not in sys.path: -- sys.path.append(uzpath) -- -- for action in actionlist: -- logger.info("Executing revocation action %s", action) -- try: -- module = importlib.import_module(action) -- execute = getattr(module, "execute") -- asyncio.get_event_loop().run_until_complete(execute(revocation)) -- except Exception as e: -- logger.warning("Exception during execution of revocation action %s: %s", action, e) -- - try: - while True: - try: --- -2.35.1 - diff --git a/0004-cloud_verifier-Support-notifications-revocation-REST.patch b/0004-cloud_verifier-Support-notifications-revocation-REST.patch deleted file mode 100644 index ba7069c..0000000 --- a/0004-cloud_verifier-Support-notifications-revocation-REST.patch +++ /dev/null @@ -1,356 +0,0 @@ -From 16d3a31145e3d0001e6c6621adb6dbaf831cb03f Mon Sep 17 00:00:00 2001 -From: Daiki Ueno -Date: Mon, 21 Mar 2022 13:08:29 +0100 -Subject: [PATCH 4/5] cloud_verifier: Support /notifications/revocation REST - API - -Signed-off-by: Daiki Ueno ---- - keylime.conf | 22 ++++-- - keylime/cloud_verifier_common.py | 16 +---- - keylime/cloud_verifier_tornado.py | 108 +++++++++++++++++++++++------- - keylime/revocation_notifier.py | 18 ++++- - 4 files changed, 119 insertions(+), 45 deletions(-) - -diff --git a/keylime.conf b/keylime.conf -index fbd1119..c393e22 100644 ---- a/keylime.conf -+++ b/keylime.conf -@@ -269,9 +269,21 @@ max_retries = 5 - # will done as fast as possible. Floating point values accepted here. - quote_interval = 2 - --# Whether to turn on the zero mq based revocation notifier system. --# Currently this only works if you are using keylime-CA. --revocation_notifier = True -+# Enable listed revocation notification methods. -+# -+# Available methods are: -+# -+# "zeromq": Enable the ZeroMQ based revocation notification method; -+# revocation_notifier_ip and revocation_notifier_port options must be -+# set. Currently this only works if you are using keylime-CA. -+# -+# "webhook": Send notification via webhook. The endpoint URL must be -+# configured with webhook_url option. This can be used to notify other -+# systems that do not have a Keylime agent running. -+# -+# "agent": Deliver notification directly to the agent via the REST -+# protocol. -+revocation_notifiers = zeromq - - # The binding address and port of the revocation notifier service. - # If the 'revocation_notifier' option is set to "true", then the verifier -@@ -279,10 +291,6 @@ revocation_notifier = True - revocation_notifier_ip = 127.0.0.1 - revocation_notifier_port = 8992 - --# Enable revocation notifications via webhook. This can be used to notify other --# systems that do not have a Keylime agent running. --revocation_notifier_webhook = False -- - # Webhook url for revocation notifications. - webhook_url = '' - -diff --git a/keylime/cloud_verifier_common.py b/keylime/cloud_verifier_common.py -index 52d6908..ab00768 100644 ---- a/keylime/cloud_verifier_common.py -+++ b/keylime/cloud_verifier_common.py -@@ -7,7 +7,7 @@ import ast - import base64 - import time - --from keylime import config, crypto, json, keylime_logging, revocation_notifier -+from keylime import config, crypto, json, keylime_logging - from keylime.agentstates import AgentAttestStates - from keylime.common import algorithms, validators - from keylime.failure import Component, Failure -@@ -268,14 +268,7 @@ def process_get_status(agent): - - - # sign a message with revocation key. telling of verification problem -- -- --def notify_error(agent, msgtype="revocation", event=None): -- send_mq = config.getboolean("cloud_verifier", "revocation_notifier") -- send_webhook = config.getboolean("cloud_verifier", "revocation_notifier_webhook", fallback=False) -- if not (send_mq or send_webhook): -- return -- -+def prepare_error(agent, msgtype="revocation", event=None): - # prepare the revocation message: - revocation = { - "type": msgtype, -@@ -300,10 +293,7 @@ def notify_error(agent, msgtype="revocation", event=None): - - else: - tosend["signature"] = "none" -- if send_mq: -- revocation_notifier.notify(tosend) -- if send_webhook: -- revocation_notifier.notify_webhook(tosend) -+ return tosend - - - def validate_agent_data(agent_data): -diff --git a/keylime/cloud_verifier_tornado.py b/keylime/cloud_verifier_tornado.py -index 60abf37..a8c08d2 100644 ---- a/keylime/cloud_verifier_tornado.py -+++ b/keylime/cloud_verifier_tornado.py -@@ -9,6 +9,7 @@ import os - import signal - import sys - import traceback -+from concurrent.futures import ThreadPoolExecutor - from multiprocessing import Process - - import tornado.ioloop -@@ -35,6 +36,9 @@ from keylime.failure import MAX_SEVERITY_LABEL, Component, Failure - - logger = keylime_logging.init_logging("cloudverifier") - -+# mTLS configuration to connect to the agent -+mtls_options = None -+ - - try: - engine = DBEngineManager().make_engine("cloud_verifier") -@@ -232,11 +236,6 @@ class VersionHandler(BaseHandler): - - - class AgentsHandler(BaseHandler): -- mtls_options = None # Stores the cert, key and password used by the verifier for mTLS connections -- -- def initialize(self, mtls_options): -- self.mtls_options = mtls_options -- - def head(self): - """HEAD not supported""" - web_util.echo_json_response(self, 405, "HEAD not supported") -@@ -496,9 +495,7 @@ class AgentsHandler(BaseHandler): - mtls_cert = agent_data["mtls_cert"] - agent_data["ssl_context"] = None - if agent_mtls_cert_enabled and mtls_cert: -- agent_data["ssl_context"] = web_util.generate_agent_mtls_context( -- mtls_cert, self.mtls_options -- ) -+ agent_data["ssl_context"] = web_util.generate_agent_mtls_context(mtls_cert, mtls_options) - - if agent_data["ssl_context"] is None: - logger.warning("Connecting to agent without mTLS: %s", agent_id) -@@ -566,7 +563,7 @@ class AgentsHandler(BaseHandler): - if not isinstance(agent, dict): - agent = _from_db_obj(agent) - if agent["mtls_cert"]: -- agent["ssl_context"] = web_util.generate_agent_mtls_context(agent["mtls_cert"], self.mtls_options) -+ agent["ssl_context"] = web_util.generate_agent_mtls_context(agent["mtls_cert"], mtls_options) - agent["operational_state"] = states.START - asyncio.ensure_future(process_agent(agent, states.GET_QUOTE)) - web_util.echo_json_response(self, 200, "Success") -@@ -860,6 +857,70 @@ async def invoke_provide_v(agent): - asyncio.ensure_future(process_agent(agent, states.GET_QUOTE)) - - -+async def invoke_notify_error(agent, tosend): -+ if agent is None: -+ logger.warning("Agent deleted while being processed") -+ return -+ kwargs = { -+ "data": tosend, -+ } -+ if agent["ssl_context"]: -+ kwargs["context"] = agent["ssl_context"] -+ res = tornado_requests.request( -+ "POST", -+ f"http://{agent['ip']}:{agent['port']}/v{agent['supported_version']}/notifications/revocation", -+ **kwargs, -+ ) -+ response = await res -+ -+ if response is None: -+ logger.warning( -+ "Empty Notify Revocation response from cloud agent %s", -+ agent["agent_id"], -+ ) -+ elif response.status_code != 200: -+ logger.warning( -+ "Unexpected Notify Revocation response error for cloud agent %s, Error: %s", -+ agent["agent_id"], -+ response.status_code, -+ ) -+ -+ -+async def notify_error(agent, msgtype="revocation", event=None): -+ notifiers = revocation_notifier.get_notifiers() -+ if len(notifiers) == 0: -+ return -+ -+ tosend = cloud_verifier_common.prepare_error(agent, msgtype, event) -+ if "webhook" in notifiers: -+ revocation_notifier.notify_webhook(tosend) -+ if "zeromq" in notifiers: -+ revocation_notifier.notify(tosend) -+ if "agent" in notifiers: -+ verifier_id = config.get( -+ "cloud_verifier", "cloudverifier_id", fallback=cloud_verifier_common.DEFAULT_VERIFIER_ID -+ ) -+ session = get_session() -+ agents = session.query(VerfierMain).filter_by(verifier_id=verifier_id).all() -+ futures = [] -+ loop = asyncio.get_event_loop() -+ # Notify all agents asynchronously through a thread pool -+ with ThreadPoolExecutor() as pool: -+ for agent_db_obj in agents: -+ if agent_db_obj.agent_id != agent["agent_id"]: -+ agent = _from_db_obj(agent_db_obj) -+ if agent["mtls_cert"]: -+ agent["ssl_context"] = web_util.generate_agent_mtls_context(agent["mtls_cert"], mtls_options) -+ func = functools.partial(invoke_notify_error, agent, tosend) -+ futures.append(await loop.run_in_executor(pool, func)) -+ # Wait for all tasks complete in 60 seconds -+ try: -+ for f in asyncio.as_completed(futures, timeout=60): -+ await f -+ except asyncio.TimeoutError as e: -+ logger.error("Timeout during notifying error to agents: %s", e) -+ -+ - async def process_agent(agent, new_operational_state, failure=Failure(Component.INTERNAL, ["verifier"])): - # Convert to dict if the agent arg is a db object - if not isinstance(agent, dict): -@@ -900,7 +961,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component. - - # issue notification for invalid quotes - if new_operational_state == states.INVALID_QUOTE: -- cloud_verifier_common.notify_error(agent, event=failure.highest_severity_event) -+ await notify_error(agent, event=failure.highest_severity_event) - - # When the failure is irrecoverable we stop polling the agent - if not failure.recoverable or failure.highest_severity == MAX_SEVERITY_LABEL: -@@ -975,9 +1036,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component. - ) - failure.add_event("not_reachable", "agent was not reachable from verifier", False) - if agent["first_verified"]: # only notify on previously good agents -- cloud_verifier_common.notify_error( -- agent, msgtype="comm_error", event=failure.highest_severity_event -- ) -+ await notify_error(agent, msgtype="comm_error", event=failure.highest_severity_event) - else: - logger.debug("Communication error for new agent. No notification will be sent") - await process_agent(agent, states.FAILED, failure) -@@ -1004,7 +1063,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component. - maxr, - ) - failure.add_event("not_reachable_v", "agent was not reachable to provide V", False) -- cloud_verifier_common.notify_error(agent, msgtype="comm_error", event=failure.highest_severity_event) -+ await notify_error(agent, msgtype="comm_error", event=failure.highest_severity_event) - await process_agent(agent, states.FAILED, failure) - else: - agent["operational_state"] = states.PROVIDE_V -@@ -1031,7 +1090,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component. - await process_agent(agent, states.FAILED, failure) - - --async def activate_agents(verifier_id, verifier_ip, verifier_port, mtls_options): -+async def activate_agents(verifier_id, verifier_ip, verifier_port): - session = get_session() - aas = get_AgentAttestStates() - try: -@@ -1100,7 +1159,7 @@ def main(): - # print out API versions we support - keylime_api_version.log_api_versions(logger) - -- context, mtls_options = web_util.init_mtls(logger=logger) -+ context, server_mtls_options = web_util.init_mtls(logger=logger) - - # Check for user defined CA to connect to agent - agent_mtls_cert = config.get("cloud_verifier", "agent_mtls_cert", fallback=None) -@@ -1108,12 +1167,15 @@ def main(): - agent_mtls_private_key_pw = config.get("cloud_verifier", "agent_mtls_private_key_pw", fallback=None) - - # Only set custom options if the cert should not be the same as used by the verifier -- if agent_mtls_cert != "CV": -+ global mtls_options -+ if agent_mtls_cert == "CV": -+ mtls_options = server_mtls_options -+ else: - mtls_options = (agent_mtls_cert, agent_mtls_private_key, agent_mtls_private_key_pw) - - app = tornado.web.Application( - [ -- (r"/v?[0-9]+(?:\.[0-9]+)?/agents/.*", AgentsHandler, {"mtls_options": mtls_options}), -+ (r"/v?[0-9]+(?:\.[0-9]+)?/agents/.*", AgentsHandler), - (r"/v?[0-9]+(?:\.[0-9]+)?/allowlists/.*", AllowlistHandler), - (r"/versions?", VersionHandler), - (r".*", MainHandler), -@@ -1149,17 +1211,17 @@ def main(): - server.start() - if task_id == 0: - # Reactivate agents -- asyncio.ensure_future( -- activate_agents(cloudverifier_id, cloudverifier_host, cloudverifier_port, mtls_options) -- ) -+ asyncio.ensure_future(activate_agents(cloudverifier_id, cloudverifier_host, cloudverifier_port)) - tornado.ioloop.IOLoop.current().start() - logger.debug("Server %s stopped.", task_id) - sys.exit(0) - - processes = [] - -+ run_revocation_notifier = "zeromq" in revocation_notifier.get_notifiers() -+ - def sig_handler(*_): -- if config.getboolean("cloud_verifier", "revocation_notifier"): -+ if run_revocation_notifier: - revocation_notifier.stop_broker() - for p in processes: - p.join() -@@ -1167,7 +1229,7 @@ def main(): - - signal.signal(signal.SIGINT, sig_handler) - signal.signal(signal.SIGTERM, sig_handler) -- if config.getboolean("cloud_verifier", "revocation_notifier"): -+ if run_revocation_notifier: - logger.info( - "Starting service for revocation notifications on port %s", - config.getint("cloud_verifier", "revocation_notifier_port"), -diff --git a/keylime/revocation_notifier.py b/keylime/revocation_notifier.py -index 7cfe0e5..0628a64 100644 ---- a/keylime/revocation_notifier.py -+++ b/keylime/revocation_notifier.py -@@ -22,8 +22,22 @@ broker_proc: Optional[Process] = None - _SOCKET_PATH = "/var/run/keylime/keylime.verifier.ipc" - - -+# return the revocation notification methods for cloud verifier -+def get_notifiers(): -+ notifiers = set(config.get("cloud_verifier", "revocation_notifiers", fallback="").split(",")) -+ if ("zeromq" not in notifiers) and config.getboolean("cloud_verifier", "revocation_notifier", fallback=False): -+ logger.warning("Warning: 'revocation_notifier' option is deprecated; use 'revocation_notifiers'") -+ notifiers.add("zeromq") -+ if ("webhook" not in notifiers) and config.getboolean( -+ "cloud_verifier", "revocation_notifier_webhook", fallback=False -+ ): -+ logger.warning("Warning: 'revocation_notifier_webhook' option is deprecated; use 'revocation_notifiers'") -+ notifiers.add("webhook") -+ return notifiers.intersection({"zeromq", "webhook", "agent"}) -+ -+ - def start_broker(): -- assert config.getboolean("cloud_verifier", "revocation_notifier") -+ assert "zeromq" in get_notifiers() - try: - import zmq # pylint: disable=import-outside-toplevel - except ImportError as error: -@@ -78,7 +92,7 @@ def stop_broker(): - - - def notify(tosend): -- assert config.getboolean("cloud_verifier", "revocation_notifier") -+ assert "zeromq" in get_notifiers() - try: - import zmq # pylint: disable=import-outside-toplevel - except ImportError as error: --- -2.35.1 - diff --git a/0005-Use-python3-gpg-instead-of-python3-gnupg.patch b/0005-Use-python3-gpg-instead-of-python3-gnupg.patch deleted file mode 100644 index 35676aa..0000000 --- a/0005-Use-python3-gpg-instead-of-python3-gnupg.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 49bc0a3afbbe3740bb857b530440364b021a865f Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Fri, 17 Jun 2022 19:57:17 -0300 -Subject: [PATCH 5/5] Use python3-gpg instead of python3-gnupg - -The former uses GPGME and is the recommended way of using GnuPG from -applications, by the GnuPG initiative, as it provides a better -documented API [1][2]. - -python-gpg is also already present in some distros, e.g. Fedora and -CentOS Stream, as it is a dependency of their package manager (dnf). - -It is also available in other major distros such as Debian, Ubuntu, -OpenSUSE, so no disruptions are to be expected regarding packaging. - -[1] https://gnupg.org/software/gpgme/index.html -[2] https://wiki.python.org/moin/GnuPrivacyGuard ---- - installer.sh | 6 +++--- - keylime/signing.py | 27 +++++++++++++-------------- - requirements.txt | 3 +-- - 3 files changed, 17 insertions(+), 19 deletions(-) - -diff --git a/installer.sh b/installer.sh -index 6618e1d..4355b33 100755 ---- a/installer.sh -+++ b/installer.sh -@@ -58,7 +58,7 @@ case "$ID" in - echo "${ID} selected." - PACKAGE_MGR=$(command -v apt-get) - PYTHON_PREIN="git patch" -- PYTHON_DEPS="python3 python3-pip python3-dev python3-setuptools python3-zmq python3-tornado python3-cryptography python3-requests python3-psutil gcc g++ libssl-dev swig python3-yaml python3-gnupg python3-lark wget" -+ PYTHON_DEPS="python3 python3-pip python3-dev python3-setuptools python3-zmq python3-tornado python3-cryptography python3-requests python3-psutil gcc g++ libssl-dev swig python3-yaml python3-gpg python3-lark wget" - if [ "$(uname -m)" = "x86_64" ]; then - PYTHON_DEPS+=" libefivar-dev" - fi -@@ -96,7 +96,7 @@ case "$ID" in - PACKAGE_MGR=$(command -v dnf) - NEED_EPEL=1 - PYTHON_PREIN="python3 python3-devel python3-setuptools python3-pip" -- PYTHON_DEPS="gcc gcc-c++ openssl-devel python3-yaml python3-requests swig python3-cryptography wget git python3-tornado python3-zmq python3-gnupg python3-psutil" -+ PYTHON_DEPS="gcc gcc-c++ openssl-devel python3-yaml python3-requests swig python3-cryptography wget git python3-tornado python3-zmq python3-gpg python3-psutil" - if [ "$(uname -m)" = "x86_64" ]; then - PYTHON_DEPS+=" efivar-libs" - fi -@@ -116,7 +116,7 @@ case "$ID" in - echo "${ID} selected." - PACKAGE_MGR=$(command -v dnf) - PYTHON_PREIN="python3 python3-devel python3-setuptools git wget patch" -- PYTHON_DEPS="python3-pip gcc gcc-c++ openssl-devel swig python3-pyyaml python3-zmq python3-cryptography python3-tornado python3-requests python3-gnupg yaml-cpp-devel procps-ng python3-psutil python3-lark-parser" -+ PYTHON_DEPS="python3-pip gcc gcc-c++ openssl-devel swig python3-pyyaml python3-zmq python3-cryptography python3-tornado python3-requests python3-gpg yaml-cpp-devel procps-ng python3-psutil python3-lark-parser" - if [ "$(uname -m)" = "x86_64" ]; then - PYTHON_DEPS+=" efivar-devel" - fi -diff --git a/keylime/signing.py b/keylime/signing.py -index 1353c1e..a1be9c7 100644 ---- a/keylime/signing.py -+++ b/keylime/signing.py -@@ -5,7 +5,7 @@ Copyright 2017 Massachusetts Institute of Technology. - - import tempfile - --import gnupg -+import gpg - from cryptography.exceptions import InvalidSignature - from cryptography.hazmat.primitives import hashes - from cryptography.hazmat.primitives.asymmetric import ec -@@ -55,19 +55,18 @@ def verify_signature(key, sig, file): - - # PGP - if key_header == "-----BEGIN PGP PUBLIC KEY BLOCK-----": -- gpg = gnupg.GPG() -- logger.debug("Importing GPG key") -- gpg_imported = gpg.import_keys(key.decode("utf-8")) -- if gpg_imported.count == 1: # pylint: disable=E1101 -- logger.debug("GPG key successfully imported") -- else: -- raise Exception("Unable to import GPG key") -- -- # The Python PGP library won't let you read a signature from memory, hence this hack. -- with tempfile.NamedTemporaryFile() as temp_sig: -- temp_sig.write(sig) -- temp_sig.flush() -- verified = gpg.verify_data(temp_sig.name, file) -+ verified = False -+ with tempfile.TemporaryDirectory() as gpg_homedir: -+ ctx = gpg.Context(home_dir=gpg_homedir) -+ try: -+ logger.debug("Importing GPG key") -+ result = ctx.key_import(key) -+ except Exception as e: -+ raise Exception("Unable to import GPG key") from e -+ -+ if result is not None and hasattr(result, "considered") is True: -+ _, result = ctx.verify(file, sig) -+ verified = result.signatures[0].status == 0 - - # OpenSSL - elif key_header == "-----BEGIN PUBLIC KEY-----": -diff --git a/requirements.txt b/requirements.txt -index d31eabc..ca3fac3 100644 ---- a/requirements.txt -+++ b/requirements.txt -@@ -10,8 +10,7 @@ pyyaml>=3.11 # MIT - requests>=2.6 # Apache-2.0 - sqlalchemy>=1.3 # MIT - alembic>=1.1.0 # MIT --python-gnupg>=0.4.6 # BSD - packaging>=20.0 #BSD - psutil>=5.4.2 # BSD - # Note that lark was renamed from lark-parser with 1.0.0 release --lark>=1.0.0 # MIT -\ No newline at end of file -+lark>=1.0.0 # MIT --- -2.35.1 - diff --git a/keylime.spec b/keylime.spec index 0c03c36..2dec35d 100644 --- a/keylime.spec +++ b/keylime.spec @@ -1,8 +1,8 @@ %global srcname keylime Name: keylime -Version: 6.4.1 -Release: %{?autorelease}%{!?autorelease:1%{?dist}} +Version: 6.4.2 +Release: 1%{?dist} Summary: Open source TPM software for Bootstrapping and Maintaining Trust BuildArch: noarch @@ -11,14 +11,6 @@ URL: https://github.com/keylime/keylime Source0: https://github.com/keylime/keylime/archive/refs/tags/v%{version}.tar.gz Source1: %{srcname}.sysusers -Patch: 0001-Improve-error-handling-when-doing-signature-verifica.patch -# Agent-Local revocation. -Patch: 0002-revocation_notifier-Factor-out-revocation-message-pr.patch -Patch: 0003-keylime_agent-Support-notifications-revocation-REST-.patch -Patch: 0004-cloud_verifier-Support-notifications-revocation-REST.patch -# python-gpg. -Patch: 0005-Use-python3-gpg-instead-of-python3-gnupg.patch - License: ASL 2.0 and MIT BuildRequires: git-core @@ -144,14 +136,8 @@ rm -rf %{buildroot}%{python3_sitelib}/%{srcname}/static/ rm -f %{buildroot}/%{_bindir}/%{srcname}_ima_emulator rm -f %{buildroot}/%{_bindir}/%{srcname}_userdata_encrypt -# Disable zeromq revocation notifier, as there is no zeromq in RHEL. -# 6.4.1. -# Agent-Local revocation. -sed -e 's/^revocation_notifiers[[:space:]]*=.*/revocation_notifiers = agent/g' \ - -i %{srcname}.conf - -# Setting up the agent to use keylime:tss user/group after dropping privileges. -sed -e 's/^run_as[[:space:]]*=.*/run_as = keylime:tss/g' -i %{srcname}.conf +# Setting up the agent to use keylime:keylime user/group after dropping privileges. +sed -e 's/^run_as[[:space:]]*=.*/run_as = keylime:keylime/g' -i %{srcname}.conf # Using sha256 for tpm_hash_alg. sed -e 's/^tpm_hash_alg[[:space:]]*=.*/tpm_hash_alg = sha256/g' -i %{srcname}.conf @@ -228,7 +214,7 @@ exit 0 %{python3_sitelib}/%{srcname} %files base -%license LICENSE keylime/static/icons/ICON-LICENSE +%license LICENSE %doc README.md %config(noreplace) %attr(600,%{srcname},%{srcname}) %{_sysconfdir}/%{srcname}.conf %attr(700,%{srcname},%{srcname}) %dir %{_rundir}/%{srcname} @@ -241,4 +227,10 @@ exit 0 %license LICENSE %changelog -%autochangelog +* Thu Jul 07 2022 Sergio Correia - 6.4.2-1 +- Update to 6.4.2 + Related: rhbz#2082989 + +* Tue Jun 21 2022 Sergio Correia - 6.4.1-1 +- Add keylime to RHEL-9 + Resolves: rhbz#2082989 diff --git a/sources b/sources index 9c77940..e890003 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (v6.4.1.tar.gz) = 1075eacb45f27df36e16e68b6486cfb32060c86ddbf0f40b28ab59ce4a76db183c65a8d76896fe49451b5b2ba84be1b39e758d42b943fd9ec66e659be2f1d89f +SHA512 (v6.4.2.tar.gz) = 7bc365b17b719c03aad76796f63c103de06c7c8a0ac1e9741cd0be460110d4da9d44c2caebb5eb1390f577d3a082d4a3d6a565bdccb46bd5c9ec060dae2bc161