0

I am using exchangelib for some end-to-end testing purposes of our mail system. Currently, when making a request, I am returned a 501.

# Tell exchangelib to use this adapter class instead of the default
BaseProtocol.HTTP_ADAPTER_CLS = NoVerifyHTTPAdapter

...

version = Version(api_version="Exchange2013_SP1", build=Build(15, 0, 847))
credentials = Credentials(username=outlook_profile['Username'], password=outlook_profile['Password'])
config = Configuration(server="<SERVICEURL>", credentials=credentials, auth_type=NTLM, version=version)
account = Account(primary_smtp_address="<USER>@<DOMAIN>", config=config, autodiscover=False, access_type=DELEGATE)

print(account.inbox.total_count)

Error:

WARNING:exchangelib.services.common:EWS https://<SERVICEURL>/EWS/Exchange.asmx, account <USER>@<DOMAIN>: Exception in _get_elements: Traceback (most recent call last):
  File "C:\Python38\lib\site-packages\cached_property.py", line 69, in __get__
    return obj_dict[name]
KeyError: 'root'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python38\lib\site-packages\exchangelib\services\common.py", line 84, in _get_elements
    response = self._get_response_xml(payload=payload)
  File "C:\Python38\lib\site-packages\exchangelib\services\common.py", line 150, in _get_response_xml
    r, session = post_ratelimited(
  File "C:\Python38\lib\site-packages\exchangelib\util.py", line 770, in post_ratelimited
    _raise_response_errors(r, protocol, log_msg, log_vals)  # Always raises an exception
  File "C:\Python38\lib\site-packages\exchangelib\util.py", line 851, in _raise_response_errors
    raise TransportError(str('Unknown failure\n') + log_msg % log_vals)
exchangelib.errors.TransportError: Unknown failure
Retry: 0
Waited: 10
Timeout: 120
Session: 24770
Thread: 34952
Auth type: <requests_ntlm.requests_ntlm.HttpNtlmAuth object at 0x000001B2F697A310>
URL: https://<SERVICEURL>/EWS/Exchange.asmx
HTTP adapter: <exchangelib.protocol.NoVerifyHTTPAdapter object at 0x000001B2F697A0A0>
Allow redirects: False
Streaming: False
Response time: 0.0779999999795109
Status code: 501
Request headers: {'User-Agent': 'exchangelib/3.2.0 (python-requests/2.23.0)', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'Keep-Alive', 'Content-Type': 'text/xml; charset=utf-8', 'X-AnchorMailbox': '<USER>@<DOMAIN>', 'Content-Length': '1304', 'Authorization': 'NTLM <HASH>'}
Response headers: {'Date': 'Wed, 10 Jun 2020 21:38:09 GMT', 'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Connection': 'close', 'Content-Length': '87', 'Content-Type': 'text/html'}
Request data: b'<?xml version=\'1.0\' encoding=\'utf-8\'?>\n<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"><s:Header><t:RequestServerVersion Version="Exchange2013_SP1"/><t:ExchangeImpersonation><t:ConnectingSID><t:PrimarySmtpAddress><USER>@<DOMAIN></t:PrimarySmtpAddress></t:ConnectingSID></t:ExchangeImpersonation><t:TimeZoneContext><t:TimeZoneDefinition Id="UTC"/></t:TimeZoneContext></s:Header><s:Body><m:GetFolder><m:FolderShape><t:BaseShape>IdOnly</t:BaseShape><t:AdditionalProperties><t:FieldURI FieldURI="folder:FolderId"/><t:FieldURI FieldURI="folder:ChildFolderCount"/><t:FieldURI FieldURI="folder:EffectiveRights"/><t:FieldURI FieldURI="folder:FolderClass"/><t:FieldURI FieldURI="folder:DisplayName"/><t:FieldURI FieldURI="folder:ParentFolderId"/><t:FieldURI FieldURI="folder:TotalCount"/><t:FieldURI FieldURI="folder:UnreadCount"/></t:AdditionalProperties></m:FolderShape><m:FolderIds><t:DistinguishedFolderId Id="root"><t:Mailbox><t:EmailAddress><USER>@<DOMAIN></t:EmailAddress><t:RoutingType>SMTP</t:RoutingType><t:MailboxType>Mailbox</t:MailboxType></t:Mailbox></t:DistinguishedFolderId></m:FolderIds></m:GetFolder></s:Body></s:Envelope>'
Response data: b'<html><head><title>501 Invalid Request</title></head><body>Invalid Request: ??</body>\r\n'

However, when I connect (successfully) to my email system using a powershell EWS library, I can see that my Request body looks quite different, especially in terms of the soap:Envelope tag:

POST https://<SERVICEURL>/EWS/Exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept: text/xml
User-Agent: ExchangeServicesClient/15.00.0847.030
Accept-Encoding: gzip,deflate
Authorization: NTLM <HASH>
Host: <SERVICEURL>
Content-Length: 673
Expect: 100-continue

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2013_SP1" />
  </soap:Header>
  <soap:Body>
    <m:GetFolder>
      <m:FolderShape>
        <t:BaseShape>AllProperties</t:BaseShape>
      </m:FolderShape>
      <m:FolderIds>
        <t:DistinguishedFolderId Id="inbox" />
      </m:FolderIds>
    </m:GetFolder>
  </soap:Body>
</soap:Envelope>

Also, if I instead target the autodiscover URL using exchangelib, I can resolve the server correctly, but still get the same 501 error. So the credentials are correctly configured.

I can provide other information if needed. Any help would be greatly welcome.

Update 1

Instead of using the exchangelib module, I was able to correctly authenticate and query the server by splitting up the requests:

session = requests.Session()
session.mount('https://', adapter)
session.headers.update({'Content-Type': 'text/xml; charset=utf-8', 'Expect': '100-continue'})
session.auth = HttpNtlmAuth(domain\\username, password)
session.post('https://SERVICEURL/EWS/Exchange.asmx', verify=False)
session.post('https://SERVICEURL/EWS/Exchange.asmx', data=body, verify=False)

Here, body is the exact request body excahngelib generated to query the inbox:

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">' \
  <s:Header>
    <t:RequestServerVersion Version="Exchange2013_SP1"/>
    <t:TimeZoneContext>
      <t:TimeZoneDefinition Id="UTC"/>
    </t:TimeZoneContext>
  </s:Header>
  <s:Body>
    <m:GetFolder>
      <m:FolderShape>
        <t:BaseShape>IdOnly</t:BaseShape>
        <t:AdditionalProperties>
          <t:FieldURI FieldURI="folder:FolderId"/>
          <t:FieldURI FieldURI="folder:ChildFolderCount"/>
          <t:FieldURI FieldURI="folder:EffectiveRights"/>
          <t:FieldURI FieldURI="folder:FolderClass"/>
          <t:FieldURI FieldURI="folder:DisplayName"/>
          <t:FieldURI FieldURI="folder:ParentFolderId"/>
          <t:FieldURI FieldURI="folder:TotalCount"/>
          <t:FieldURI FieldURI="folder:UnreadCount"/>
        </t:AdditionalProperties>
      </m:FolderShape>
      <m:FolderIds>
        <t:DistinguishedFolderId Id="root">
          <t:Mailbox>
            <t:EmailAddress>username@domain</t:EmailAddress>      
            <t:RoutingType>SMTP</t:RoutingType> 
            <t:MailboxType>Mailbox</t:MailboxType>
          </t:Mailbox>
        </t:DistinguishedFolderId>
      </m:FolderIds>
    </m:GetFolder>
  </s:Body>
</s:Envelope>
  • Are you sure you set `access_type=DELEGATE` when you got the above output? The request XML from exchangelib is trying to do impersonation, while your PowerShell example is delegate. – Erik Cederstrand Jun 11 '20 at 06:54
  • I see what you mean. In the above error I guess I was not setting the access type to delegate. But, when I do set `access_type=DELEGATE` in `Account(...,access_type=DELEGATE)`, I get a very similar error, and still a 501: https://pastebin.com/NJDqf456 – Alex Knowlton Jun 11 '20 at 17:46
  • I am using a Kemp Load Balancer in front of Exchange. Should that cause any issues? – Alex Knowlton Jun 18 '20 at 18:07
  • I have no idea. The request XML is not invalid, AFAICS. My best suggestion is to debug this by extracting the exchangelib request into a separate script that just calls `requests.post()` with the same headers, NTLM auth and data that was logged by exchangelib, and change it gradually to match your succeeding request, until you get a successful response. If you find the reason, please post here or file a ticket with exchangelib. – Erik Cederstrand Jun 19 '20 at 05:06
  • Combing through Kemp logs as we speak. Finding some strange results -- will update when we find a solution. It could very well be a configuration issue. Just weird how requests using `Microsoft.Exchange.WebServices.dll` work, and neither `exchangelib` or `ews-java-api` do. – Alex Knowlton Jun 19 '20 at 13:49
  • I was able to find a solution. Please see the update above. Not sure why I needed to split the auth request from the actual request to exchange -- `Microsoft.Exchange.WebServices.dll` does not exhibit this behavior – Alex Knowlton Jun 19 '20 at 19:29
  • That seems like a problem with your proxy not handling the NTLM auth protocol correctly. There is a series of requests going on in the background to secure the connection. Maybe something is proxied incorrectly in that Series. The MS code is able to recover somehow. – Erik Cederstrand Jun 20 '20 at 05:19
  • Perhaps -- I will look further into the NTLM handshake. The error on the server is, during the final request in the handshake, Kemp errors on parsing the request body (but it throws the exception as if it is coming from issues with the header). So I will speak to Kemp to see if they have a solution. For now, the implementation above works fine -- I just need to write my own soap requests, which is a pain. – Alex Knowlton Jun 22 '20 at 15:14

0 Answers0