File: test_server.py

package info (click to toggle)
httpx-ntlm 1.4.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 120 kB
  • sloc: python: 475; makefile: 4
file content (72 lines) | stat: -rw-r--r-- 2,849 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import base64
import struct

from flask import Flask, request
from tests.test_utils import domain, username, password

app = Flask(__name__)


@app.route("/ntlm")
def ntlm_auth():
    return get_auth_response('NTLM')


@app.route("/negotiate")
def negotiate_auth():
    return get_auth_response('Negotiate')


@app.route("/both")
def negotiate_and_ntlm_auth():
    return get_auth_response('NTLM', advertise_nego_and_ntlm=True)


def get_auth_response(auth_type, advertise_nego_and_ntlm=False):
    # Get the actual header that is returned by httpx_ntlm
    actual_header = request.headers.get('Authorization', '')

    # Check what the message type is from the header
    if actual_header == '':
        # This is the initial connection, need to return a 401
        response_headers = {'WWW-Authenticate': auth_type if not advertise_nego_and_ntlm else 'Negotiate, NTLM'}
        status_code = 401
        response = "auth with '%s\\%s':'%s'" % (domain, username, password)
    else:
        # Set human readable names for message types
        # see https://msdn.microsoft.com/en-us/library/cc236639.aspx for more details
        expected_signature = b'NTLMSSP\x00'
        negotiate_message_type = 1
        authenticate_message_type = 3

        msg = base64.b64decode(actual_header[len(auth_type):])
        signature = msg[0:8]
        if signature != expected_signature:
            raise ValueError("Mismatch on NTLM message signature, expecting: %s, actual: %s" % (expected_signature,
                                                                                                signature))
        # Get the NTLM version number (bytes 9 - 12)
        message_type = struct.unpack("<I", msg[8:12])[0]

        if message_type == negotiate_message_type:
            # Initial NTLM message from client, attach challenge token
            challenge_response = ('TlRMTVNTUAACAAAAAwAMADgAAAAzgoriASNFZ4mrze8AAAA'
                                  'AAAAAACQAJABEAAAABgBwFwAAAA9TAGUAcgB2AGUAcgACAA'
                                  'wARABvAG0AYQBpAG4AAQAMAFMAZQByAHYAZQByAAAAAAA=')
            challenge_header = auth_type + ' ' + challenge_response
            response_headers = {'WWW-Authenticate': challenge_header}
            response = "auth with '%s\\%s':'%s'" % (domain, username, password)
            status_code = 401
        elif message_type == authenticate_message_type:
            # Received final NTLM message, return 200
            response_headers = {}
            status_code = 200
            response = 'authed'
        else:
            # Should only ever receive a negotiate (1) or auth (3) message from httpx_ntlm
            raise ValueError("Mismatch on NTLM message type, expecting: 1 or 3, actual: %d" % message_type)

    return response, status_code, response_headers


if __name__ == "__main__":
    app.run()