IdentityPython/pysaml2

SignatureError at /saml2/login/ (failed to load external entity)

Amr-Es opened this issue · 6 comments

I'm using DjangoSaml2 on windows which use pysaml2, i download xmlsec1 and the needed dlls, below error is received when running

Traceback (most recent call last):
  File "C:\Program Files\Python310\lib\site-packages\saml2\sigver.py", line 860, in sign_statement
    (stdout, stderr, output) = self._run_xmlsec(com_list, [tmp.name])
  File "C:\Program Files\Python310\lib\site-packages\saml2\sigver.py", line 933, in _run_xmlsec
    raise XmlsecError(errmsg)

During handling of the above exception (returncode=1
error=I/O error : Permission denied
I/O error : Permission denied
I/O warning : failed to load external entity "C:\Users\u\AppData\Local\Temp\tmpi9vlw7gi.xml"
Error: failed to parse xml file "C:\Users\u\AppData\Local\Temp\tmpi9vlw7gi.xml"
Error: failed to load template "C:\Users\u\AppData\Local\Temp\tmpi9vlw7gi.xml"
Error: failed to sign file "C:\Users\u\AppData\Local\Temp\tmpi9vlw7gi.xml"

output=), another exception occurred:
  File "C:\Program Files\Python310\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Program Files\Python310\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Program Files\Python310\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Program Files\Python310\lib\site-packages\django\views\generic\base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "C:\Program Files\Python310\lib\site-packages\djangosaml2\views.py", line 348, in get
    session_id, request_xml = client.create_authn_request(
  File "C:\Program Files\Python310\lib\site-packages\saml2\client_base.py", line 481, in create_authn_request
    msg = self._message(
  File "C:\Program Files\Python310\lib\site-packages\saml2\entity.py", line 606, in _message
    signed_req = self.sign(
  File "C:\Program Files\Python310\lib\site-packages\saml2\entity.py", line 542, in sign
    return signed_instance_factory(msg, self.sec, to_sign)
  File "C:\Program Files\Python310\lib\site-packages\saml2\sigver.py", line 344, in signed_instance_factory
    signed_xml = seccont.sign_statement(
  File "C:\Program Files\Python310\lib\site-packages\saml2\sigver.py", line 1786, in sign_statement
    return self.crypto.sign_statement(
  File "C:\Program Files\Python310\lib\site-packages\saml2\sigver.py", line 862, in sign_statement
    raise SignatureError(com_list)

Exception Type: SignatureError at /saml2/login/
Exception Value: ['xmlsec1.exe', '--sign', '--privkey-pem', 'key.pem', '--id-attr:ID', 'urn:oasis:names:tc:SAML:2.0:protocol:AuthnRequest', '--node-id', 'id-Dihqjb9ADFOjG3mvp', '--output', 'C:\\Users\\u\\AppData\\Local\\Temp\\tmpz91m2e_u.xml', 'C:\\Users\\u\\AppData\\Local\\Temp\\tmpi9vlw7gi.xml']

hello, as with IdentityPython/pyXMLSecurity#71 it looks as if the process is not allowed to read files from temporary storage

error=I/O error : Permission denied
I/O error : Permission denied
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\Amr\\AppData\\Local\\Temp\\tmps_abq_kj.pem'
XML parse error: [Errno 13] Permission denied: 'C:\\Users\\Amr\\AppData\\Local\\Temp\\tmps_abq_kj.pem'

Is the process allowed to read/write to the filesystem?

@c00kiemon5ter it can write to the temp directory, as I can see the file is created but it can't open it for read, when I copy this file from temp dir to another dir I can open it, i tried to run as admin but still same problem

ok, I think this goes back to the generic issue of the NamedTemporaryFile in Python on Windows.
Related MR and discussion: #665

I can confirm that I have the same problem "SignatureError" when running djangosaml2 on Windows server 2016.

I see two problems, a general problem is that installing xmlsec1.exe in windows is a bit complex as there are very few builds.

The second problem is that the NamedTemporaryFile function only works in Windows when the temporary files are used by the current Python program and not when calling external programs such as xmlsec1.exe with Popen. This is due to file locks in Windows.

Has anyone come up with a good solution to get this working under Windows?

I am guessing everything should work within WSL2 (Windows Subsystem for Linux)

WSL is not always an option, specifically for embedded python in windows applications.

Until a solution is found, I have monkey patched the _run_xmlsec method to avoid the current issue:

from saml2.sigver import CryptoBackendXmlSec1, XmlsecError, logger
from tempfile import NamedTemporaryFile
from subprocess import Popen
from subprocess import PIPE

def _run_xmlsec(self, com_list, extra_args):
    """
    Common code to invoke xmlsec and parse the output.
    :param com_list: Key-value parameter list for xmlsec
    :param extra_args: Positional parameters to be appended after all
        key-value parameters
    :result: Whatever xmlsec wrote to an --output temporary file
    """
    with NamedTemporaryFile(suffix='.xml', delete=False) as ntf:
        com_list.extend(['--output', ntf.name])
        com_list += extra_args

        logger.debug('xmlsec command: %s', ' '.join(com_list))

        pof = Popen(com_list, stderr=PIPE, stdout=PIPE)
        p_out, p_err = pof.communicate()
        p_out = p_out.decode()
        p_err = p_err.decode()

        if pof.returncode != 0:
            errmsg = "returncode={code}\nerror={err}\noutput={out}".format(
                code=pof.returncode, err=p_err, out=p_out
            )
            logger.error(errmsg)
            raise XmlsecError(errmsg)

        ntf.seek(0)
        return p_out, p_err, ntf.read()

CryptoBackendXmlSec1._run_xmlsec = _run_xmlsec