From 86e00de5deaeb76c43e26a5548109ad82894d718 Mon Sep 17 00:00:00 2001 From: henrikek Date: Sun, 1 Oct 2023 15:26:52 +0200 Subject: [PATCH 1/3] Use class delete_tmpfiles flag in _run_xmlsec --- src/saml2/sigver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index f3af1ec99..f2fe32320 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -851,7 +851,7 @@ def _run_xmlsec(self, com_list, extra_args): key-value parameters :result: Whatever xmlsec wrote to an --output temporary file """ - with NamedTemporaryFile(suffix=".xml") as ntf: + with NamedTemporaryFile(suffix=".xml", delete=self.delete_tmpfiles) as ntf: com_list.extend(["--output", ntf.name]) if self.version_nums >= (1, 3): com_list.extend(['--lax-key-search']) From 8d12cd32da9e691bc59acea712b92db48bfa763a Mon Sep 17 00:00:00 2001 From: henrikek Date: Mon, 2 Oct 2023 13:03:00 +0200 Subject: [PATCH 2/3] Remove tmpfiles when work is done --- src/saml2/sigver.py | 71 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index f2fe32320..7fcbff037 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -333,7 +333,7 @@ def signed_instance_factory(instance, seccont, elements_to_sign=None): return signed_xml -def make_temp(content, suffix="", decode=True, delete_tmpfiles=True): +def make_temp(content, suffix="", decode=True): """ Create a temporary file with the given content. @@ -345,19 +345,25 @@ def make_temp(content, suffix="", decode=True, delete_tmpfiles=True): suffix in certain circumstances. :param decode: The input content might be base64 coded. If so it must, in some cases, be decoded before being placed in the file. - :param delete_tmpfiles: Whether to keep the tmp files or delete them when they are - no longer in use :return: 2-tuple with file pointer ( so the calling function can close the file) and filename (which is for instance needed by the xmlsec function). """ content_encoded = content.encode("utf-8") if not isinstance(content, bytes) else content content_raw = base64.b64decode(content_encoded) if decode else content_encoded - ntf = NamedTemporaryFile(suffix=suffix, delete=delete_tmpfiles) + ntf = NamedTemporaryFile(suffix=suffix, delete=False) ntf.write(content_raw) ntf.seek(0) return ntf +def delete_filename(filename): + """ + Silent remove filename if exists + """ + try: + os.remove(filename) + except FileNotFoundError: + pass def split_len(seq, length): return [seq[i : i + length] for i in range(0, len(seq), length)] @@ -673,7 +679,7 @@ def encrypt(self, text, recv_key, template, session_key_type, xpath=""): :return: """ logger.debug("Encryption input len: %d", len(text)) - tmp = make_temp(text, decode=False, delete_tmpfiles=self.delete_tmpfiles) + tmp = make_temp(text, decode=False) com_list = [ self.xmlsec, "--encrypt", @@ -693,6 +699,9 @@ def encrypt(self, text, recv_key, template, session_key_type, xpath=""): except XmlsecError as e: raise EncryptError(com_list) from e + if self.delete_tmpfiles: + delete_filename(tmp.name) + return output def encrypt_assertion(self, statement, enc_key, template, key_type="des-192", node_xpath=None, node_id=None): @@ -709,8 +718,8 @@ def encrypt_assertion(self, statement, enc_key, template, key_type="des-192", no if isinstance(statement, SamlBase): statement = pre_encrypt_assertion(statement) - tmp = make_temp(str(statement), decode=False, delete_tmpfiles=self.delete_tmpfiles) - tmp2 = make_temp(str(template), decode=False, delete_tmpfiles=self.delete_tmpfiles) + tmp = make_temp(str(statement), decode=False) + tmp2 = make_temp(str(template), decode=False) if not node_xpath: node_xpath = ASSERT_XPATH @@ -736,6 +745,10 @@ def encrypt_assertion(self, statement, enc_key, template, key_type="des-192", no except XmlsecError as e: raise EncryptError(com_list) from e + if self.delete_tmpfiles: + delete_filename(tmp.name) + delete_filename(tmp2.name) + return output.decode("utf-8") def decrypt(self, enctext, key_file): @@ -747,7 +760,7 @@ def decrypt(self, enctext, key_file): """ logger.debug("Decrypt input len: %d", len(enctext)) - tmp = make_temp(enctext, decode=False, delete_tmpfiles=self.delete_tmpfiles) + tmp = make_temp(enctext, decode=False) com_list = [ self.xmlsec, @@ -763,6 +776,9 @@ def decrypt(self, enctext, key_file): except XmlsecError as e: raise DecryptError(com_list) from e + if self.delete_tmpfiles: + delete_filename(tmp.name) + return output.decode("utf-8") def sign_statement(self, statement, node_name, key_file, node_id): @@ -778,7 +794,7 @@ def sign_statement(self, statement, node_name, key_file, node_id): if isinstance(statement, SamlBase): statement = str(statement) - tmp = make_temp(statement, suffix=".xml", decode=False, delete_tmpfiles=self.delete_tmpfiles) + tmp = make_temp(statement, suffix=".xml", decode=False) com_list = [ self.xmlsec, @@ -797,6 +813,9 @@ def sign_statement(self, statement, node_name, key_file, node_id): except XmlsecError as e: raise SignatureError(com_list) from e + if self.delete_tmpfiles: + delete_filename(tmp.name) + # this does not work if --store-signatures is used if output: return output.decode("utf-8") @@ -818,7 +837,7 @@ def validate_signature(self, signedtext, cert_file, cert_type, node_name, node_i if not isinstance(signedtext, bytes): signedtext = signedtext.encode("utf-8") - tmp = make_temp(signedtext, suffix=".xml", decode=False, delete_tmpfiles=self.delete_tmpfiles) + tmp = make_temp(signedtext, suffix=".xml", decode=False) com_list = [ self.xmlsec, @@ -841,6 +860,9 @@ def validate_signature(self, signedtext, cert_file, cert_type, node_name, node_i except XmlsecError as e: raise SignatureError(com_list) from e + if self.delete_tmpfiles: + delete_filename(tmp.name) + return parse_xmlsec_verify_output(stderr, self.version_nums) def _run_xmlsec(self, com_list, extra_args): @@ -851,7 +873,7 @@ def _run_xmlsec(self, com_list, extra_args): key-value parameters :result: Whatever xmlsec wrote to an --output temporary file """ - with NamedTemporaryFile(suffix=".xml", delete=self.delete_tmpfiles) as ntf: + with NamedTemporaryFile(suffix=".xml", delete=False) as ntf: com_list.extend(["--output", ntf.name]) if self.version_nums >= (1, 3): com_list.extend(['--lax-key-search']) @@ -870,7 +892,12 @@ def _run_xmlsec(self, com_list, extra_args): raise XmlsecError(errmsg) ntf.seek(0) - return p_out, p_err, ntf.read() + ntf_read = ntf.read() + + if self.delete_tmpfiles: + delete_filename(ntf.name) + + return p_out, p_err, ntf_read class CryptoBackendXMLSecurity(CryptoBackend): @@ -1309,10 +1336,15 @@ def decrypt_keys(self, enctext, keys=None): keys_filtered = (key for key in keys if key) keys_encoded = (key.encode("ascii") if not isinstance(key, bytes) else key for key in keys_filtered) - key_files = list(make_temp(key, decode=False, delete_tmpfiles=self.delete_tmpfiles) for key in keys_encoded) + key_files = list(make_temp(key, decode=False) for key in keys_encoded) key_file_names = list(tmp.name for tmp in key_files) dectext = self.decrypt(enctext, key_file=key_file_names) + + if self.delete_tmpfiles: + for tmp in key_files: + delete_filename(tmp.name) + return dectext def decrypt(self, enctext, key_file=None): @@ -1387,7 +1419,7 @@ def _check_signature( for cert_name, cert in _certs: if isinstance(cert, str): content = pem_format(cert) - tmp = make_temp(content, suffix=".pem", decode=False, delete_tmpfiles=self.delete_tmpfiles) + tmp = make_temp(content, suffix=".pem", decode=False) certs.append(tmp) else: certs.append(cert) @@ -1397,7 +1429,7 @@ def _check_signature( if not certs and not self.only_use_keys_in_metadata: logger.debug("==== Certs from instance ====") certs = [ - make_temp(content=pem_format(cert), suffix=".pem", decode=False, delete_tmpfiles=self.delete_tmpfiles) + make_temp(content=pem_format(cert), suffix=".pem", decode=False) for cert in cert_from_instance(item) ] else: @@ -1524,6 +1556,10 @@ def _check_signature( else: raise SignatureError("Failed to verify signature") + if self.delete_tmpfiles: + for tmp in certs: + delete_filename(tmp.name) + return item def check_signature(self, item, node_name=NODE_NAME, origdoc=None, must=False, issuer=None): @@ -1686,12 +1722,15 @@ def sign_statement(self, statement, node_name, key=None, key_file=None, node_id= """ if not key_file and key: content = str(key).encode() - tmp = make_temp(content, suffix=".pem", delete_tmpfiles=self.delete_tmpfiles) + tmp = make_temp(content, suffix=".pem") key_file = tmp.name if not key and not key_file: key_file = self.key_file + if self.delete_tmpfiles: + delete_filename(tmp.name) + return self.crypto.sign_statement( statement, node_name, From 2f1ba6beaef3f6189ace26e0efb5f2d031624e04 Mon Sep 17 00:00:00 2001 From: henrikek Date: Mon, 2 Oct 2023 19:09:03 +0200 Subject: [PATCH 3/3] Close tempfiles before delete --- src/saml2/sigver.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index 7fcbff037..b4fdfae96 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -700,6 +700,7 @@ def encrypt(self, text, recv_key, template, session_key_type, xpath=""): raise EncryptError(com_list) from e if self.delete_tmpfiles: + tmp.close() delete_filename(tmp.name) return output @@ -746,6 +747,8 @@ def encrypt_assertion(self, statement, enc_key, template, key_type="des-192", no raise EncryptError(com_list) from e if self.delete_tmpfiles: + tmp.close() + tmp2.close() delete_filename(tmp.name) delete_filename(tmp2.name) @@ -777,6 +780,7 @@ def decrypt(self, enctext, key_file): raise DecryptError(com_list) from e if self.delete_tmpfiles: + tmp.close() delete_filename(tmp.name) return output.decode("utf-8") @@ -814,6 +818,7 @@ def sign_statement(self, statement, node_name, key_file, node_id): raise SignatureError(com_list) from e if self.delete_tmpfiles: + tmp.close() delete_filename(tmp.name) # this does not work if --store-signatures is used @@ -861,6 +866,7 @@ def validate_signature(self, signedtext, cert_file, cert_type, node_name, node_i raise SignatureError(com_list) from e if self.delete_tmpfiles: + tmp.close() delete_filename(tmp.name) return parse_xmlsec_verify_output(stderr, self.version_nums) @@ -895,6 +901,7 @@ def _run_xmlsec(self, com_list, extra_args): ntf_read = ntf.read() if self.delete_tmpfiles: + ntf.close() delete_filename(ntf.name) return p_out, p_err, ntf_read @@ -1343,6 +1350,7 @@ def decrypt_keys(self, enctext, keys=None): if self.delete_tmpfiles: for tmp in key_files: + tmp.close() delete_filename(tmp.name) return dectext @@ -1558,6 +1566,7 @@ def _check_signature( if self.delete_tmpfiles: for tmp in certs: + tmp.close() delete_filename(tmp.name) return item @@ -1728,7 +1737,8 @@ def sign_statement(self, statement, node_name, key=None, key_file=None, node_id= if not key and not key_file: key_file = self.key_file - if self.delete_tmpfiles: + if 'tmp' in locals() and self.delete_tmpfiles: + tmp.close() delete_filename(tmp.name) return self.crypto.sign_statement(