Unverified Commit aba1c515 authored by cclauss's avatar cclauss Committed by Daniel Stenberg
Browse files

tests: make Impacket (SMB server) Python 3 compatible

Closes #3731
Fixes #3289
parent cd3edb08
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ problems may have been fixed or changed somewhat since this was written!
 4.5 Improve --data-urlencode space encoding

 5. Build and portability issues
 5.1 tests not compatible with python3
 5.2 curl-config --libs contains private details
 5.3 curl compiled on OSX 10.13 failed to run on OSX 10.10
 5.4 Cannot compile against a static build of OpenLDAP
@@ -372,12 +371,6 @@ problems may have been fixed or changed somewhat since this was written!

5. Build and portability issues

5.1 tests not compatible with python3

 The smb test server still needs python2.

 See https://github.com/curl/curl/issues/3289

5.2 curl-config --libs contains private details

 "curl-config --libs" will include details set in LDFLAGS when configure is
+24 −22
Original line number Diff line number Diff line
from __future__ import print_function
from __future__ import absolute_import
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
@@ -40,7 +42,7 @@ from random import randint
from struct import pack, unpack
import time

from structure import Structure
from .structure import Structure

CVS_REVISION = '$Revision: 526 $'

@@ -454,7 +456,7 @@ class NetBIOS:
            except socket.error:
                pass
        if not has_bind:
            raise NetBIOSError, ( 'Cannot bind to a good UDP port', ERRCLASS_OS, errno.EAGAIN )
            raise NetBIOSError( 'Cannot bind to a good UDP port', ERRCLASS_OS, errno.EAGAIN)
        self.__sock = s

    # Set the default NetBIOS domain nameserver.
@@ -531,15 +533,15 @@ class NetBIOS:
                            if res.get_rcode() == 0x03:
                                return None
                            else:
                                raise NetBIOSError, ( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode() )
                                raise NetBIOSError( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode())
                        
                        if res.get_ancount() != 1:
                            raise NetBIOSError( 'Malformed response')
                        
                        return NBPositiveNameQueryResponse(res.get_answers())
            except select.error, ex:
            except select.error as ex:
                if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
                    raise NetBIOSError, ( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0] )
                    raise NetBIOSError( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0])
                raise


@@ -570,25 +572,25 @@ class NetBIOS:
                else:
                    try:
                        data, _ = self.__sock.recvfrom(65536, 0)
                    except Exception, e:
                        raise NetBIOSError, "recvfrom error: %s" % str(e)
                    except Exception as e:
                        raise NetBIOSError("recvfrom error: %s" % str(e))
                    self.__sock.close()
                    res = NetBIOSPacket(data)
                    if res.get_trn_id() == p.get_trn_id():
                        if res.get_rcode():
                            if res.get_rcode() == 0x03:
                                # I'm just guessing here
                                raise NetBIOSError, "Cannot get data from server"
                                raise NetBIOSError("Cannot get data from server")
                            else:
                                raise NetBIOSError, ( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode() )
                                raise NetBIOSError( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode())
                        answ = NBNodeStatusResponse(res.get_answers())
                        self.mac = answ.get_mac()
                        return answ.get_node_names()
            except select.error, ex:
            except select.error as ex:
                if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
                    raise NetBIOSError, ( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0] )
            except socket.error, ex:
                raise NetBIOSError, 'Connection error: %s' % str(ex)
                    raise NetBIOSError( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0])
            except socket.error as ex:
                raise NetBIOSError('Connection error: %s' % str(ex))

# Perform first and second level encoding of name as specified in RFC 1001 (Section 4)
def encode_name(name, type, scope):
@@ -841,7 +843,7 @@ class NetBIOSTCPSession(NetBIOSSession):
            af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_STREAM)[0]
            sock = socket.socket(af, socktype, proto)
            sock.connect(sa)
        except socket.error, e:
        except socket.error as e:
            raise socket.error("Connection error (%s:%s)" % (peer[0], peer[1]), e)
        return sock

@@ -866,7 +868,7 @@ class NetBIOSTCPSession(NetBIOSSession):
        while 1:
            p = self.recv_packet(timeout)
            if p.get_type() == NETBIOS_SESSION_NEGATIVE_RESPONSE:
                raise NetBIOSError, ( 'Cannot request session', ERRCLASS_SESSION, ord(p.get_trailer()[0]) )
                raise NetBIOSError( 'Cannot request session', ERRCLASS_SESSION, ord(p.get_trailer()[0]))
            elif p.get_type() == NETBIOS_SESSION_POSITIVE_RESPONSE:
                break
            else:
@@ -896,13 +898,13 @@ class NetBIOSTCPSession(NetBIOSSession):

                received = self._sock.recv(bytes_left)
                if len(received) == 0:
                    raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None)
                    raise NetBIOSError( 'Error while reading from remote', ERRCLASS_OS, None)

                data = data + received
                bytes_left = read_length - len(data)
            except select.error, ex:
            except select.error as ex:
                if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
                    raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] )
                    raise NetBIOSError( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0])

        return data

@@ -919,13 +921,13 @@ class NetBIOSTCPSession(NetBIOSSession):

                received = self._sock.recv(bytes_left)
                if len(received) == 0:
                    raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None)
                    raise NetBIOSError( 'Error while reading from remote', ERRCLASS_OS, None)

                data = data + received
                bytes_left = read_length - len(data)
            except select.error, ex:
            except select.error as ex:
                if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
                    raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] )
                    raise NetBIOSError( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0])

        return data

@@ -974,7 +976,7 @@ def main():
                
    
    n = get_netbios_host_by_name("some-host")
    print n
    print(n)

if __name__ == '__main__':
    main()
+40 −38
Original line number Diff line number Diff line
from __future__ import print_function
# Copyright (c) 2003-2016 CORE Security Technologies:
#
# This software is provided under under a slightly modified version
@@ -209,7 +210,7 @@ class AV_PAIRS():
        self.fields[key] = (len(value),value)

    def __getitem__(self, key):
        if self.fields.has_key(key):
        if key in self.fields:
           return self.fields[key]
        return None

@@ -236,10 +237,10 @@ class AV_PAIRS():

    def dump(self):
        for i in self.fields.keys():
            print "%s: {%r}" % (i,self[i])
            print("%s: {%r}" % (i,self[i]))

    def getData(self):
        if self.fields.has_key(NTLMSSP_AV_EOL):
        if NTLMSSP_AV_EOL in self.fields:
            del self.fields[NTLMSSP_AV_EOL]
        ans = ''
        for i in self.fields.keys():
@@ -611,7 +612,7 @@ def getNTLMSSPType3(type1, type2, user, password, domain, lmhash = '', nthash =
    # method we will create a valid ChallengeResponse
    ntlmChallengeResponse = NTLMAuthChallengeResponse(user, password, ntlmChallenge['challenge'])

    clientChallenge = "".join([random.choice(string.digits+string.letters) for i in xrange(8)])
    clientChallenge = "".join([random.choice(string.digits+string.letters) for i in range(8)])

    serverName = ntlmChallenge['TargetInfoFields']

@@ -647,7 +648,7 @@ def getNTLMSSPType3(type1, type2, user, password, domain, lmhash = '', nthash =
    if ntlmChallenge['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH:
       # not exactly what I call random tho :\
       # exportedSessionKey = this is the key we should use to sign
       exportedSessionKey = "".join([random.choice(string.digits+string.letters) for i in xrange(16)])
       exportedSessionKey = "".join([random.choice(string.digits+string.letters) for i in range(16)])
       #exportedSessionKey = "A"*16
       #print "keyExchangeKey %r" % keyExchangeKey
       # Let's generate the right session key based on the challenge flags
@@ -738,6 +739,8 @@ def compute_nthash(password):
    # This is done according to Samba's encryption specification (docs/html/ENCRYPTION.html)
    try:
        password = unicode(password).encode('utf_16le')
    except NameError:  # unicode() was removed in Python 3
        password = str(password).encode('utf_16le')
    except UnicodeDecodeError:
        import sys
        password = password.decode(sys.getfilesystemencoding()).encode('utf_16le')
@@ -968,4 +971,3 @@ class NTLM_HTTP_AuthChallengeResponse(NTLM_HTTP, NTLMAuthChallengeResponse):

    def __init__(self):
        NTLMAuthChallengeResponse.__init__(self)
+11 −11
Original line number Diff line number Diff line
@@ -629,9 +629,9 @@ class SharedFile:
    @staticmethod
    def __convert_smbtime(t):
        x = t >> 32
        y = t & 0xffffffffL
        y = t & 0xffffffff
        geo_cal_offset = 11644473600.0  # = 369.0 * 365.25 * 24 * 60 * 60 - (3.0 * 24 * 60 * 60 + 6.0 * 60 * 60)
        return (x * 4.0 * (1 << 30) + (y & 0xfff00000L)) * 1.0e-7 - geo_cal_offset
        return (x * 4.0 * (1 << 30) + (y & 0xfff00000)) * 1.0e-7 - geo_cal_offset


# Contain information about a SMB machine
@@ -676,12 +676,12 @@ class NewSMBPacket(Structure):
    def __init__(self, **kargs):
        Structure.__init__(self, **kargs)

        if self.fields.has_key('Flags2') is False:
        if ('Flags2' in self.fields) is False:
             self['Flags2'] = 0
        if self.fields.has_key('Flags1') is False:
        if ('Flags1' in self.fields) is False:
             self['Flags1'] = 0

        if not kargs.has_key('data'):
        if 'data' not in kargs:
            self['Data'] = []

    def addCommand(self, command):
@@ -709,9 +709,9 @@ class NewSMBPacket(Structure):
                return 1
            elif self.isMoreProcessingRequired():
                return 1
            raise SessionError, ("SMB Library Error", self['ErrorClass'] + (self['_reserved'] << 8), self['ErrorCode'], self['Flags2'] & SMB.FLAGS2_NT_STATUS)
            raise SessionError("SMB Library Error", self['ErrorClass'] + (self['_reserved'] << 8), self['ErrorCode'], self['Flags2'] & SMB.FLAGS2_NT_STATUS)
        else:
            raise UnsupportedFeature, ("Unexpected answer from server: Got %d, Expected %d" % (self['Command'], cmd))
            raise UnsupportedFeature("Unexpected answer from server: Got %d, Expected %d" % (self['Command'], cmd))


class SMBCommand(Structure):
@@ -2550,7 +2550,7 @@ class SMB:
                    if s.get_error_class() == 0x00 and s.get_error_code() == 0x00:
                        return 1
                    else:
                        raise SessionError, ( "SMB Library Error", s.get_error_class()+ (s.get_reserved() << 8), s.get_error_code() , s.get_flags2() & SMB.FLAGS2_NT_STATUS )
                        raise SessionError( "SMB Library Error", s.get_error_class()+ (s.get_reserved() << 8), s.get_error_code() , s.get_flags2() & SMB.FLAGS2_NT_STATUS)
                else:
                    break
        return 0
@@ -2583,7 +2583,7 @@ class SMB:
                        self.__server_name = self._dialects_data['ServerName']

                    if self._dialects_parameters['DialectIndex'] == 0xffff:
                        raise UnsupportedFeature,"Remote server does not know NT LM 0.12"
                        raise UnsupportedFeature("Remote server does not know NT LM 0.12")
                    return 1
            else:
                return 0
@@ -2734,7 +2734,7 @@ class SMB:
        self._SigningSessionKey = key

    def get_encryption_key(self):
        if self._dialects_data.fields.has_key('Challenge'):
        if 'Challenge' in self._dialects_data.fields:
            return self._dialects_data['Challenge']
        else:
            return None
@@ -3241,7 +3241,7 @@ class SMB:
                       pass

            # Parse Version to know the target Operating system name. Not provided elsewhere anymore
            if ntlmChallenge.fields.has_key('Version'):
            if 'Version' in ntlmChallenge.fields:
                version = ntlmChallenge['Version']

                if len(version) >= 4:
+35 −34
Original line number Diff line number Diff line
from __future__ import print_function
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
@@ -231,13 +232,13 @@ class SMB3:
            self.negotiateSession(preferredDialect)

    def printStatus(self):
        print "CONNECTION"
        print("CONNECTION")
        for i in self._Connection.items():
            print "%-40s : %s" % i
        print
        print "SESSION"
            print("%-40s : %s" % i)
        print()
        print("SESSION")
        for i in self._Session.items():
            print "%-40s : %s" % i
            print("%-40s : %s" % i)

    def getServerName(self):
        return self._Session['ServerName']
@@ -308,7 +309,7 @@ class SMB3:
        packet['SessionID'] = self._Session['SessionID']

        # Default the credit charge to 1 unless set by the caller
        if packet.fields.has_key('CreditCharge') is False:
        if ('CreditCharge' in packet.fields) is False:
            packet['CreditCharge'] = 1

        # Standard credit request after negotiating protocol
@@ -318,7 +319,7 @@ class SMB3:
        messageId = packet['MessageID']

        if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2:
            if packet['TreeID'] > 0 and self._Session['TreeConnectTable'].has_key(packet['TreeID']) is True:
            if packet['TreeID'] > 0 and (packet['TreeID'] in self._Session['TreeConnectTable']) is True:
                if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False:
                    packet['Flags'] = SMB2_FLAGS_SIGNED
                    self.signSMB(packet)
@@ -350,7 +351,7 @@ class SMB3:

    def recvSMB(self, packetID = None):
        # First, verify we don't have the packet already
        if self._Connection['OutstandingResponses'].has_key(packetID):
        if packetID in self._Connection['OutstandingResponses']:
            return self._Connection['OutstandingResponses'].pop(packetID) 

        data = self._NetBIOSSession.recv_packet(self._timeout) 
@@ -727,7 +728,7 @@ class SMB3:
                       pass 

                # Parse Version to know the target Operating system name. Not provided elsewhere anymore
                if ntlmChallenge.fields.has_key('Version'):
                if 'Version' in ntlmChallenge.fields:
                    version = ntlmChallenge['Version']

                    if len(version) >= 4:
@@ -785,7 +786,7 @@ class SMB3:

        #print self._Session['TreeConnectTable']
        share = share.split('\\')[-1]
        if self._Session['TreeConnectTable'].has_key(share):
        if share in self._Session['TreeConnectTable']:
            # Already connected, no need to reconnect
            treeEntry =  self._Session['TreeConnectTable'][share]
            treeEntry['NumberOfUses'] += 1
@@ -837,10 +838,10 @@ class SMB3:
           return packet['TreeID'] 

    def disconnectTree(self, treeId):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        if self._Session['TreeConnectTable'].has_key(treeId):
        if treeId in self._Session['TreeConnectTable']:
            # More than 1 use? descrease it and return, if not, send the packet
            if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1:
                treeEntry =  self._Session['TreeConnectTable'][treeId]
@@ -862,7 +863,7 @@ class SMB3:
            return True

    def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        fileName = string.replace(fileName, '/', '\\')
@@ -885,7 +886,7 @@ class SMB3:
           # Is this file NOT on the root directory?
           if len(fileName.split('\\')) > 2:
               parentDir = ntpath.dirname(pathName)
           if self.GlobalFileTable.has_key(parentDir):
           if parentDir in self.GlobalFileTable:
               LOG.critical("Don't know what to do now! :-o")
               raise
           else:
@@ -957,9 +958,9 @@ class SMB3:
            return str(createResponse['FileID'])

    def close(self, treeId, fileId):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -988,9 +989,9 @@ class SMB3:
        # This function should NOT be used for reading files directly, but another higher
        # level function should be used that will break the read into smaller pieces

        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1030,9 +1031,9 @@ class SMB3:
        # This function should NOT be used for writing directly to files, but another higher
        # level function should be used that will break the writes into smaller pieces

        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1071,9 +1072,9 @@ class SMB3:
            return bytesWritten

    def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1124,12 +1125,12 @@ class SMB3:
        self.sendSMB(packet)

    def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '',  maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if fileId is None:
            fileId = '\xff'*16
        else:
            if self._Session['OpenTable'].has_key(fileId) is False:
            if (fileId in self._Session['OpenTable']) is False:
                raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1165,9 +1166,9 @@ class SMB3:
            return smbIoctlResponse['Buffer']

    def flush(self,treeId, fileId):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1186,9 +1187,9 @@ class SMB3:
            return True

    def lock(self, treeId, fileId, locks, lockSequence = 0):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1248,9 +1249,9 @@ class SMB3:
            return True

    def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1280,9 +1281,9 @@ class SMB3:
            return queryResponse['Buffer']

    def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ):
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if self._Session['OpenTable'].has_key(fileId) is False:
        if (fileId in self._Session['OpenTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)

        packet = self.SMB_PACKET()
@@ -1385,7 +1386,7 @@ class SMB3:
                        files.append(smb.SharedFile(fileInfo['CreationTime'],fileInfo['LastAccessTime'],fileInfo['LastChangeTime'],fileInfo['EndOfFile'],fileInfo['AllocationSize'],fileInfo['ExtFileAttributes'],fileInfo['FileName'].decode('utf-16le'), fileInfo['FileName'].decode('utf-16le')))
                        nextOffset = fileInfo['NextEntryOffset']
                        res = res[nextOffset:]
                except SessionError, e:
                except SessionError as e:
                    if (e.get_error_code()) != STATUS_NO_MORE_FILES:
                        raise
                    break 
@@ -1512,7 +1513,7 @@ class SMB3:

    def waitNamedPipe(self, treeId, pipename, timeout = 5):
        pipename = ntpath.basename(pipename)
        if self._Session['TreeConnectTable'].has_key(treeId) is False:
        if (treeId in self._Session['TreeConnectTable']) is False:
            raise SessionError(STATUS_INVALID_PARAMETER)
        if len(pipename) > 0xffff:
            raise SessionError(STATUS_INVALID_PARAMETER)
Loading