# This file is part of 'NTLM Authorization Proxy Server'
# Copyright 2001 Dmitry A. Rozmanov <dima@xenon.spb.ru>
#
# NTLM APS is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# NTLM APS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the sofware; see the file COPYING. If not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
#

import ntlm_procs, utils
import base64, string

#---------------------------------------------------------------------
class record:

    def __init__(self, data, offset=0):
        ""
        self.data = data
        self.len = len(data)
        self.offset = 0
        self.next_offset = self.offset + self.len

    #---------------------------------------------------------------------
    # helper function for creation info field in message 3
    def create_record_info(self, offset):
        ""
        self.offset = offset
        len1 = utils.int2chrs(self.len)
        len2 = len1
        data_off = utils.int2chrs(self.offset)
        self.record_info = len1 + len2 + data_off + '\000\000'

        # looks like the length is always = 8 bytes
        self.next_offset = offset + self.len

#---------------------------------------------------------------------
def create_message1(environment_dict):
    ""
    ed = environment_dict
    # overall lenght = 48 bytes
    protocol = 'NTLMSSP\000'    #name
    type = '\001\000'               #type 1
    zeros1 = '\000\000'

    flags = utils.hex2str(ed['FLAGS'])

    zeros2 = '\000\000\000\000\000\000\000\000\000'
    zeros3 = '\000\000\000\000\000\000\000\000\000\000\000'
    smthg1 = '0\000\000\000\000\000\000\000'    # something with chr(48) length?
    smthg2 = '0\000\000\000'                    # something with chr(48) lenght?

    msg1 = protocol + type + zeros1 + flags + zeros2 + zeros3 + smthg1 + smthg2
    msg1 = base64.encodestring(msg1)
    msg1 = string.replace(msg1, '\012', '')

    return msg1

#---------------------------------------------------------------------
def create_message3(nonce, environment_dict):
    ""
    ed = environment_dict

    flags = utils.hex2str(ed['FLAGS'])

    protocol = 'NTLMSSP\000'            #name
    type = '\003\000'                   #type 3
    head = protocol + type + '\000\000'

    domain_rec = record(ed['DOMAIN'])
    user_rec = record(ed['USER'])
    host_rec = record(ed['HOST'])

    additional_rec = record('')

    if ed['LM']:
        lm_rec = record(ntlm_procs.calc_resp(ed['LM_HASHED_PW'], nonce))
    else:
        lm_rec = record('')

    if ed['NT']:
        nt_rec = record(ntlm_procs.calc_resp(ed['NT_HASHED_PW'], nonce))
    else:
        nt_rec = record('')

    # length of the head and five infos for LM, NT, Domain, User, Host
    domain_offset = len(head) + 5 * 8

    # and unknown record info and flags' lenght
    if ed['NTLM_MODE'] == 0:
        domain_offset = domain_offset + 8 + len(flags)

    # create info fields
    domain_rec.create_record_info(domain_offset)
    user_rec.create_record_info(domain_rec.next_offset)
    host_rec.create_record_info(user_rec.next_offset)
    lm_rec.create_record_info(host_rec.next_offset)
    nt_rec.create_record_info(lm_rec.next_offset)
    additional_rec.create_record_info(nt_rec.next_offset)

    # data part of the message 3
    data_part = domain_rec.data + user_rec.data + host_rec.data + lm_rec.data + nt_rec.data

    # build message 3
    m3 = head + lm_rec.record_info + nt_rec.record_info + domain_rec.record_info + \
         user_rec.record_info + host_rec.record_info

    # Experimental feature !!!
    if ed['NTLM_MODE'] == 0:
        m3 = m3 + additional_rec.record_info + flags

    m3 = m3 + data_part

    # Experimental feature !!!
    if ed['NTLM_MODE'] == 0:
        m3 = m3 + additional_rec.data

    # base64 encode
    m3 = base64.encodestring(m3)
    m3 = string.replace(m3, '\012', '')

    return m3

#---------------------------------------------------------------------
def parse_message2(msg2):
    ""
    msg2 = base64.decodestring(msg2)
    # protocol = msg2[0:7]
    # msg_type = msg2[7:9]
    nonce = msg2[24:32]

    return nonce

#---------------------------------------------------------------------
def item(item_str):
    ""
    item = {}
    res = ''
    item['len1'] = utils.bytes2int(item_str[0:2])
    item['len2'] = utils.bytes2int(item_str[2:4])
    item['offset'] = utils.bytes2int(item_str[4:6])

    res = res + '%s\n\nlength (two times), offset, delimiter\n' % (utils.str2hex(item_str))

    res = res + '%s decimal: %3d    # length 1\n' % (utils.int2hex_str(item['len1']), item['len1'])
    res = res + '%s decimal: %3d    # length 2\n' % (utils.int2hex_str(item['len2']), item['len2'])
    res = res + '%s decimal: %3d    # offset\n' % (utils.int2hex_str(item['offset']), item['offset'])
    res = res + '%s                   # delimiter (two zeros)\n\n' % utils.str2hex(item_str[-2:])
    item['string'] = res

    return item

#---------------------------------------------------------------------
def flags(flag_str):
    ""
    res = ''
    res = res + '%s\n\n' % utils.str2hex(flag_str)
    flags = utils.bytes2int(flag_str[0:2])
    res = res + '%s                   # flags\n' % (utils.int2hex_str(flags))
    res = res + 'Binary:\nlayout 87654321 87654321\n'
    res = res + '       %s %s\n' % (utils.byte2bin_str(flag_str[1]), utils.byte2bin_str(flag_str[0]))

    flags2 = utils.bytes2int(flag_str[2:4])
    res = res + '%s                   # more flags ???\n' % (utils.int2hex_str(flags2))
    res = res + 'Binary:\nlayout 87654321 87654321\n'
    res = res + '       %s %s\n' % (utils.byte2bin_str(flag_str[3]), utils.byte2bin_str(flag_str[2]))

    #res = res + '%s                   # delimiter ???\n' % m_hex[(cur + 2) * 2: (cur + 4) * 2]

    return res

#---------------------------------------------------------------------
def unknown_part(bin_str):
    ""
    res = ''
    res = res + 'Hex    :  %s\n' % utils.str2hex(bin_str, '  ')
    res = res + 'String :   %s\n' % utils.str2prn_str(bin_str, '   ')
    res = res + 'Decimal: %s\n' % utils.str2dec(bin_str, ' ')

    return res

#---------------------------------------------------------------------
def debug_message1(msg):
    ""
    m_ = base64.decodestring(msg)
    m_hex = utils.str2hex(m_)

    res = ''
    res = res + '==============================================================\n'
    res = res + 'NTLM Message 1 report:\n'
    res = res + '---------------------------------\n'
    res = res + 'Base64: %s\n' % msg
    res = res + 'String: %s\n' % utils.str2prn_str(m_)
    res = res + 'Hex: %s\n' % m_hex
    cur = 0

    res = res + '---------------------------------\n'
    cur_len = 12
    res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24])
    res = res + '%s\nmethod name 0/8\n%s               # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8]))
    res = res + '0x%s%s                 # message type\n' % (m_hex[18:20], m_hex[16:18])
    res = res + '%s                   # delimiter (zeros)\n' % m_hex[20:24]
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = 4
    res = res + 'Flags %d/%d\n' % (cur, cur_len)
    res = res + flags(m_[cur: cur + cur_len])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = len(m_) - cur
    res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len)
    res = res + unknown_part(m_[cur: cur + cur_len])

    res = res + '\nEnd of message 1 report.\n'

    return res

#---------------------------------------------------------------------
def debug_message2(msg):
    ""
    m_ = base64.decodestring(msg)
    m_hex = utils.str2hex(m_)
    res = ''
    res = res + '==============================================================\n'
    res = res + 'NTLM Message 2 report:\n'
    res = res + '---------------------------------\n'
    res = res + 'Base64: %s\n' % msg
    res = res + 'String: %s\n' % utils.str2prn_str(m_)
    res = res + 'Hex: %s\n' % m_hex
    cur = 0

    res = res + '---------------------------------\n'
    cur_len = 12
    res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24])
    res = res + '%s\nmethod name 0/8\n%s               # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8]))
    res = res + '0x%s%s                 # message type\n' % (m_hex[18:20], m_hex[16:18])
    res = res + '%s                   # delimiter (zeros)\n' % m_hex[20:24]
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = 8
    res = res + 'Lengths and Positions %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2])

    cur_len = 8
    res = res + 'Domain ??? %d/%d\n' % (cur, cur_len)
    dom = item(m_[cur:cur+cur_len])
    res = res + dom['string']
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = 4
    res = res + 'Flags %d/%d\n' % (cur, cur_len)
    res = res + flags(m_[cur: cur + cur_len])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = 8
    res = res + 'NONCE %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = dom['offset'] - cur
    res = res + 'Unknown data %d/%d:\n' % (cur, cur_len)
    res = res + unknown_part(m_[cur: cur + cur_len])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = dom['len1']
    res = res + 'Domain ??? %d/%d:\n' % (cur, cur_len)
    res = res + 'Hex: %s\n' % m_hex[cur * 2: (cur + cur_len) * 2]
    res = res + 'String: %s\n\n' % utils.str2prn_str(m_[cur : cur + cur_len])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = len(m_) - cur
    res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len)
    res = res + unknown_part(m_[cur: cur + cur_len])

    res = res + '\nEnd of message 2 report.\n'

    return res

#---------------------------------------------------------------------
def debug_message3(msg):
    ""
    m_ = base64.decodestring(msg)
    m_hex = utils.str2hex(m_)

    res = ''
    res = res + '==============================================================\n'
    res = res + 'NTLM Message 3 report:\n'
    res = res + '---------------------------------\n'
    res = res + 'Base64: %s\n' % msg
    res = res + 'String: %s\n' % utils.str2prn_str(m_)
    res = res + 'Hex: %s\n' % m_hex
    cur = 0

    res = res + '---------------------------------\n'
    cur_len = 12
    res = res + 'Header %d/%d:\n%s\n\n' % (cur, cur_len, m_hex[0:24])
    res = res + '%s\nmethod name 0/8\n%s               # C string\n\n' % (m_hex[0:16], utils.str2prn_str(m_[0:8]))
    res = res + '0x%s%s                 # message type\n' % (m_hex[18:20], m_hex[16:18])
    res = res + '%s                   # delimiter (zeros)\n' % m_hex[20:24]
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = 48
    res = res + 'Lengths and Positions %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2])

    cur_len = 8
    res = res + 'LAN Manager response %d/%d\n' % (cur, cur_len)
    lmr = item(m_[cur:cur+cur_len])
    res = res + lmr['string']
    cur = cur + cur_len

    cur_len = 8
    res = res + 'NT response %d/%d\n' % (cur, cur_len)
    ntr = item(m_[cur:cur+cur_len])
    res = res + ntr['string']
    cur = cur + cur_len

    cur_len = 8
    res = res + 'Domain string %d/%d\n' % (cur, cur_len)
    dom = item(m_[cur:cur+cur_len])
    res = res + dom['string']
    cur = cur + cur_len

    cur_len = 8
    res = res + 'User string %d/%d\n' % (cur, cur_len)
    user = item(m_[cur:cur+cur_len])
    res = res + user['string']
    cur = cur + cur_len

    cur_len = 8
    res = res + 'Host string %d/%d\n' % (cur, cur_len)
    host = item(m_[cur:cur+cur_len])
    res = res + host['string']
    cur = cur + cur_len

    cur_len = 8
    res = res + 'Unknow item record %d/%d\n' % (cur, cur_len)
    unknown = item(m_[cur:cur+cur_len])
    res = res + unknown['string']
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = 4
    res = res + 'Flags %d/%d\n' % (cur, cur_len)
    res = res + flags(m_[cur: cur + cur_len])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = dom['len1'] + user['len1'] + host['len1']
    res = res + 'Domain, User, Host strings %d/%d\n%s\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2], utils.str2prn_str(m_[cur:cur + cur_len]))

    cur_len = dom['len1']
    res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2]
    res = res + 'Domain name %d/%d:\n' % (cur, cur_len)
    res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)]))
    cur = cur + cur_len

    cur_len = user['len1']
    res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2]
    res = res + 'User name %d/%d:\n' % (cur, cur_len)
    res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)]))
    cur = cur + cur_len

    cur_len = host['len1']
    res = res + '%s\n' % m_hex[cur * 2: (cur + cur_len) * 2]
    res = res + 'Host name %d/%d:\n' % (cur, cur_len)
    res = res + '%s\n\n' % (utils.str2prn_str(m_[cur: (cur + cur_len)]))
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = lmr['len1']
    res = res + 'LAN Manager response %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = ntr['len1']
    res = res + 'NT response %d/%d\n%s\n\n' % (cur, cur_len, m_hex[cur * 2 :(cur + cur_len) * 2])
    cur = cur + cur_len

    res = res + '---------------------------------\n'
    cur_len = len(m_) - cur
    res = res + 'Rest of the message %d/%d:\n' % (cur, cur_len)
    res = res + unknown_part(m_[cur: cur + cur_len])

    res = res + '\nEnd of message 3 report.\n'
    return res

