# Copyright (c) 2003-2014 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# $Id: wkst.py 1117 2014-01-25 19:04:39Z bethus $
#
# Author: Alberto Solino
#
# Description:
#   [MS-WKST] Interface implementation
#
#   Best way to learn how to use these calls is to grab the protocol standard
#   so you understand what the call does, and then read the test case located
#   at https://code.google.com/p/impacket/source/browse/#svn%2Ftrunk%2Fimpacket%2Ftestcases%2FSMB-RPC
#
#   Some calls have helper functions, which makes it even easier to use.
#   They are located at the end of this file. 
#   Helper functions start with "h"<name of the call>.
#   There are test cases for them too. 
#
from impacket.dcerpc.v5 import ndr
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRENUM, NDRUNION, NDRUniConformantArray, NDRUniFixedArray, NDRPOINTER
from impacket.dcerpc.v5.dtypes import *
from impacket import system_errors
from impacket.uuid import uuidtup_to_bin
from impacket.dcerpc.v5.enum import Enum

MSRPC_UUID_WKST   = uuidtup_to_bin(('6BFFD098-A112-3610-9833-46C3F87E345A', '1.0'))

class DCERPCSessionError(Exception):
    def __init__( self, packet = None, error_code = None):
        Exception.__init__(self)
        self.packet = packet
        if packet is not None:
            self.error_code = packet['ErrorCode']
        else:
            self.error_code = error_code
       
    def get_error_code( self ):
        return self.error_code
 
    def get_packet( self ):
        return self.packet

    def __str__( self ):
        key = self.error_code
        if (system_errors.ERROR_MESSAGES.has_key(key)):
            error_msg_short = system_errors.ERROR_MESSAGES[key][0]
            error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] 
            return 'WKST SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
        else:
            return 'WKST SessionError: unknown error code: 0x%x' % (self.error_code)

################################################################################
# CONSTANTS
################################################################################

# 2.2.1.1 JOIN_MAX_PASSWORD_LENGTH
JOIN_MAX_PASSWORD_LENGTH = 256

# 2.2.1.2 JOIN_OBFUSCATOR_LENGTH
JOIN_OBFUSCATOR_LENGTH = 8

# 2.2.1.3 MAX_PREFERRED_LENGTH
MAX_PREFERRED_LENGTH = 0xffffffff

# 2.2.5.22 USE_INFO_1
USE_OK       = 0x00000000
USE_PAUSED   = 0x00000001
USE_SESSLOST = 0x00000002
USE_NETERR   = 0x00000003
USE_CONN     = 0x00000004
USE_RECONN   = 0x00000005

USE_WILDCARD = 0xFFFFFFFF
USE_DISKDEV  = 0x00000000
USE_SPOOLDEV = 0x00000001
USE_CHARDEV  = 0x00000002
USE_IPC      = 0x00000003

# 3.2.4.9 NetrUseDel (Opnum 10)
# Force Level
USE_NOFORCE       = 0x00000000
USE_FORCE         = 0x00000001
USE_LOTS_OF_FORCE = 0x00000002

# 3.2.4.13 NetrJoinDomain2 (Opnum 22)
# Options
NETSETUP_JOIN_DOMAIN           = 0x00000001
NETSETUP_ACCT_CREATE           = 0x00000002
NETSETUP_ACCT_DELETE           = 0x00000004
NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020
NETSETUP_JOIN_UNSECURE         = 0x00000040
NETSETUP_MACHINE_PWD_PASSED    = 0x00000080
NETSETUP_DEFER_SPN_SET         = 0x00000100
NETSETUP_JOIN_DC_ACCOUNT       = 0x00000200
NETSETUP_JOIN_WITH_NEW_NAME    = 0x00000400
NETSETUP_INSTALL_INVOCATION    = 0x00040000

# 3.2.4.14 NetrUnjoinDomain2 (Opnum 23)
# Options
NETSETUP_ACCT_DELETE              = 0x00000004
NETSETUP_IGNORE_UNSUPPORTED_FLAGS = 0x10000000

# 3.2.4.15 NetrRenameMachineInDomain2 (Opnum 24)
# Options
NETSETUP_ACCT_CREATE           = 0x00000002
NETSETUP_DNS_NAME_CHANGES_ONLY = 0x00001000

################################################################################
# STRUCTURES
################################################################################

# 2.2.2.1 WKSSVC_IDENTIFY_HANDLE
class WKSSVC_IDENTIFY_HANDLE(NDRSTRUCT):
    structure =  (
        ('Data', WSTR),
    )

class LPWKSSVC_IDENTIFY_HANDLE(NDRPOINTER):
    referent = (
        ('Data', WKSSVC_IDENTIFY_HANDLE),
    )

# 2.2.2.2 WKSSVC_IMPERSONATE_HANDLE
class WKSSVC_IMPERSONATE_HANDLE(NDRSTRUCT):
    structure =  (
        ('Data',WSTR),
    )

class LPWKSSVC_IMPERSONATE_HANDLE(NDRPOINTER):
    referent = (
        ('Data', WKSSVC_IMPERSONATE_HANDLE),
    )

# 2.2.3.1 NETSETUP_JOIN_STATUS
class NETSETUP_JOIN_STATUS(NDRENUM):
    class enumItems(Enum):
        NetSetupUnknownStatus = 1
        NetSetupUnjoined      = 2
        NetSetupWorkgroupName = 3
        NetSetupDomainName    = 4

# 2.2.3.2 NETSETUP_NAME_TYPE
class NETSETUP_NAME_TYPE(NDRENUM):
    class enumItems(Enum):
        NetSetupUnknown           = 0
        NetSetupMachine           = 1
        NetSetupWorkgroup         = 2
        NetSetupDomain            = 3
        NetSetupNonExistentDomain = 4
        NetSetupDnsMachine        = 5

# 2.2.3.3 NET_COMPUTER_NAME_TYPE
class NET_COMPUTER_NAME_TYPE(NDRENUM):
    class enumItems(Enum):
        NetPrimaryComputerName    = 0
        NetAlternateComputerNames = 1
        NetAllComputerNames       = 2
        NetComputerNameTypeMax    = 3

# 2.2.5.1 WKSTA_INFO_100
class WKSTA_INFO_100(NDRSTRUCT):
    structure = (
        ('wki100_platform_id', ULONG),
        ('wki100_computername', LPWSTR),
        ('wki100_langroup', LPWSTR),
        ('wki100_ver_major', ULONG),
        ('wki100_ver_minor', ULONG),
    )

class LPWKSTA_INFO_100(NDRPOINTER):
    referent = (
        ('Data', WKSTA_INFO_100),
    )

# 2.2.5.2 WKSTA_INFO_101
class WKSTA_INFO_101(NDRSTRUCT):
    structure = (
        ('wki101_platform_id', ULONG),
        ('wki101_computername', LPWSTR),
        ('wki101_langroup', LPWSTR),
        ('wki101_ver_major', ULONG),
        ('wki101_ver_minor', ULONG),
        ('wki101_lanroot', LPWSTR),
    )

class LPWKSTA_INFO_101(NDRPOINTER):
    referent = (
        ('Data', WKSTA_INFO_101),
    )

# 2.2.5.3 WKSTA_INFO_102
class WKSTA_INFO_102(NDRSTRUCT):
    structure = (
        ('wki102_platform_id', ULONG),
        ('wki102_computername', LPWSTR),
        ('wki102_langroup', LPWSTR),
        ('wki102_ver_major', ULONG),
        ('wki102_ver_minor', ULONG),
        ('wki102_lanroot', LPWSTR),
        ('wki102_logged_on_users', ULONG),
    )

class LPWKSTA_INFO_102(NDRPOINTER):
    referent = (
        ('Data', WKSTA_INFO_102),
    )

# 2.2.5.4 WKSTA_INFO_502
class WKSTA_INFO_502(NDRSTRUCT):
    structure = (
        ('wki502_char_wait', ULONG),
        ('wki502_collection_time', ULONG),
        ('wki502_maximum_collection_count', ULONG),
        ('wki502_keep_conn', ULONG),
        ('wki502_max
_cmds', ULONG),
        ('wki502_sess_timeout', ULONG),
        ('wki502_siz_char_buf', ULONG),
        ('wki502_max_threads', ULONG),
        ('wki502_lock_quota', ULONG),
        ('wki502_lock_increment', ULONG),
        ('wki502_lock_maximum', ULONG),
        ('wki502_pipe_increment', ULONG),
        ('wki502_pipe_maximum',
 ULONG),
        ('wki502_cache_file_timeout', ULONG),
        ('wki502_dormant_file_limit', ULONG),
        ('wki502_read_ahead_throughput', ULONG),
        ('wki502_num_mailslot_buffers', ULONG),
        ('wki502_num_srv_announce_buffers', ULONG),
        ('wki502_max_illegal_datagram_events', ULONG),
        ('wki502_illegal_datagram_event_reset_frequency', ULONG),
        ('wki502_log_election_packets', LONG),
        ('wki502_use_opportunistic_locking', LONG),
        ('wki502_use_unlock_behind', LONG),
        ('wki502_use_close_behind', LONG),
        ('wki502_buf_named_pipes', LONG),
        ('wki502_use_lock_read_unlock', LONG),
        ('wki502_utilize_nt_caching', LONG),
        ('wki502_use_raw_read', LONG),
        ('wki502_use_raw_write', LONG),
        ('wki502_use_write_raw_data', LONG),
        ('wki502_use_encryption', LONG),
        ('wki502_buf_files_deny_write', LONG),
        ('wki502_buf_read_only_files', LONG),
        ('wki502_force_core_create_mode', LONG),
        ('wki502_use_512_byte_max_transfer', LONG),
    )

class LPWKSTA_INFO_502(NDRPOINTER):
    referent = (
        ('Data', WKSTA_INFO_502),
    )

# 2.2.5.5 WKSTA_INFO_1013
class WKST