Taylor
Taylor

Reputation: 540

Suds throwing 403 Forbidden on SharePoint

I'm having an issue with getting suds to authenticate using the python-ntlm package against a SharePoint 2010 site. I have tried the solutions linked in this thread with no luck.

I'm running Python 2.7.10, suds 0.4, and python-ntlm 1.1.0.

Here's my code:

from suds.client import *
from suds.transport.https import WindowsHttpAuthenticated

import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
logging.getLogger('ntlm').setLevel(logging.DEBUG)

url = "https://web.site.com/sites/Collection/_vti_bin/Lists.asmx?WSDL"
ntlm = WindowsHttpAuthenticated(username='DOMAIN\UserName',
                                password='Password')
client = Client(url, transport=ntlm)
client.service.GetListCollection()

Here's the debug output:

DEBUG:suds.transport.http:opening (https://web.site.com/sites/Collection/_vti_bin/Lists.asmx?WSDL)
DEBUG:suds.transport.http:opening (http://www.w3.org/2001/XMLSchema.xsd)
DEBUG:suds.transport.http:opening (http://www.w3.org/2001/xml.xsd)
DEBUG:suds.client:sending to (https://web.site.com/sites/Collection/_vti_bin/Lists.asmx)
message:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns0:Body>
      <ns1:GetListCollection/>
   </ns0:Body>
</SOAP-ENV:Envelope>
DEBUG:suds.client:headers = {'SOAPAction': u'"http://schemas.microsoft.com/sharepoint/soap/GetListCollection"', 'Content-Type': 'text/xml; charset=utf-8'}
DEBUG:suds.transport.http:sending:
URL:https://web.site.com/sites/Collection/_vti_bin/Lists.asmx
HEADERS: {'SOAPAction': u'"http://schemas.microsoft.com/sharepoint/soap/GetListCollection"', 'Content-Type': 'text/xml; charset=utf-8', 'Content-type': 'text/xml; charset=utf-8', 'Soapaction': u'"http://schemas.microsoft.com/sharepoint/soap/GetListCollection"'}
MESSAGE:
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><ns0:Body><ns1:GetListCollection/></ns0:Body></SOAP-ENV:Envelope>
ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns0:Body>
      <ns1:GetListCollection/>
   </ns0:Body>
</SOAP-ENV:Envelope>
DEBUG:suds.client:http failed:
403 FORBIDDEN
Traceback (most recent call last):
  File "C:/Users/username/PycharmProjects/Suds/soap.py", line 14, in <module>
    client.service.GetListCollection()
  File "C:\Python27\lib\site-packages\suds\client.py", line 542, in __call__
    return client.invoke(args, kwargs)
  File "C:\Python27\lib\site-packages\suds\client.py", line 602, in invoke
    result = self.send(soapenv)
  File "C:\Python27\lib\site-packages\suds\client.py", line 649, in send
    result = self.failed(binding, e)
  File "C:\Python27\lib\site-packages\suds\client.py", line 708, in failed
    raise Exception((status, reason))
Exception: (403, u'Forbidden')

However, the equivalent works perfectly fine in cURL (formatted for readability)

curl -u 'Username':'Password' --ntlm -X POST \
 -H 'Content-Type: text/xml'\
 -H 'SOAPAction: "http://schemas.microsoft.com/sharepoint/soap/GetListCollection"' \
 "https://web.site.com/sites/Collection/_vti_bin/Lists.asmx" \ 
 --data-binary @soapenvelope.xml

I also have not been able to find a way to force suds to run through a Fiddler proxy while also passing NTLM authentication. Outlined here, it seems to ignore proxy settings anyway, and I can't find a way to mix and match the local proxy through fiddler and have it attempt NTLM authentication against the Lists web service.

Environment Information (to make this easily searchable):

Upvotes: 1

Views: 1788

Answers (1)

Taylor
Taylor

Reputation: 540

After a good amount of reading for the NTLM protocol, I figured out that cURL was sending the NTLM Type 1 message on the first request. However, suds (and PowerShells' New-WebServiceProxy) was not doing that. It was getting the 403 Forbidden, but did not carry on the handshake process since that was unexpected. Using the create_NTLM_NEGOTIATE_MESSAGE() in python-ntlm3, I was able to generate that Type 1 message.

By adding the 'Authorization' header with the Type 1 message, that forced the 401 Unauthorized, and caused the WindowsHttpAuthenticated to complete the handshake as expected.

from ntlm3 import ntlm
from suds.client import Client
from suds.transport.https import WindowsHttpAuthenticated

url = "https://web.site.com/sites/Collection/_vti_bin/Lists.asmx"
wsdl = (url + "?WSDL")
domain = 'DOM'
username = 'USER'
password = 'PASS'

transport = WindowsHttpAuthenticated(username=username,
                                     password=password)
client = Client(url=wsdl,
                location=url,
                transport=transport)

negotiate = "%s\\%s" % (domain, username)
ntlmauth = 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(negotiate).decode('ascii')
header = {'Authorization': ntlmauth}
client.set_options(headers=header)
client.service.GetList('Test')

The magical reason all this is happening is because we use Forms Based Authentication (FBA). Normal authentication requests get routed to an Oracle SSO, which is why (I believe) it is not responding normally and causing the 403 Unauthorized.

Hopefully this helps keep someone from banging their head against the wall.

Upvotes: 1

Related Questions