10

I'm using the pysmb library to query shares/directory structures on SMB/CIFS network shares.

def ListShares(Server, Username=None, Password=None, Domain=None):
    Ip = socket.gethostbyname(Server)
    conn = SMBConnection(Username,
                         Password,
                         'MyApp',
                         Server,
                         Domain,
                         use_ntlm_v2=True,
                         sign_options=SMBConnection.SIGN_WHEN_SUPPORTED,
                         is_direct_tcp=True)
    assert conn.connect(Ip)

    Response = conn.listShares(timeout=30)

    return [{'Name': x.name,
             'Type': x.type,
             'IsTemporary': x.isTemporary,
             'Comments': x.comments} for x in Response if not x.isSpecial]

When connecting to a linux box running samba, I can connect okay and everything works. When I try to connect to a Win7/SBS2008/Server2008 share, I get an error.

If is_direct_tcp=True, I get an Invalid protocol header for Direct TCP session message

File ".../MyApp/Managers/SmbHelper.py", line 38, in ListShares assert conn.connect(Ip) 
File "/opt/pyenv/lib/python3.3/site-packages/smb/SMBConnection.py", line 111, in connect self._pollForNetBIOSPacket(timeout) 
File "/opt/pyenv/lib/python3.3/site-packages/smb/SMBConnection.py", line 504, in _pollForNetBIOSPacket self.feedData(data) 
File "/opt/pyenv/lib/python3.3/site-packages/nmb/base.py", line 49, in feedData length = self.data_nmb.decode(self.data_buf, offset) 
File "/opt/pyenv/lib/python3.3/site-packages/nmb/nmb_structs.py", line 60, in decode raise NMBError("Invalid protocol header for Direct TCP session message") 

If is_direct_tcp=False, I get a NotConnectedError

File ".../MyApp/Managers/SmbHelper.py", line 38, in ListShares assert conn.connect(Ip) 
File "/opt/pyenv/lib/python3.3/lib/site-packages/smb/SMBConnection.py", line 111, in connect self._pollForNetBIOSPacket(timeout) 
File "/opt/pyenv/lib/python3.3/lib/site-packages/smb/SMBConnection.py", line 466, in _pollForNetBIOSPacket raise NotConnectedError 

I'm hitting a bit of a brick wall. How can I work out what exactly is wrong and fix it?

Further diagnostics...

smbclient -L linux.domain.local   -U MyUsername -W domain //Works
smbclient -L linux.domain.local   -U MyUsername@domain    //Doesn't work (Auth failed)
smbclient -L windows.domain.local -U MyUsername -W domain //Doesn't work (Auth failed)
smbclient -L windows.domain.local -U MyUsername@domain    //Works

smbclient -L [either].domain.local -U MyUsername@domain -W domain //Works, despite redundancy

So it seems Linux gets the domain from the -W parameter, Windows gets it from the Username@Domain syntax and providing both makes the smbclient call succeed to either server. Unfortunately, connecting to Windows doesn't succeed succeed from within pysmb even if I use the @Domain syntax

Solution

There were 3 problems... Firstly, when use_direct_tcp = True, port needs to be 445. When it's False, port should be 139. There was also a bug when using the module from Python3 (bytes were being incorrectly encoded). Finally, there was a problem with the way it was communicating with the server (at least when connecting to Windows boxes rather than a linux samba server).

Michael Teo, author of the module has developed a fix which we've tested and works. He's planning to update the package shortly.

Community
  • 1
  • 1
Basic
  • 26,321
  • 24
  • 115
  • 201

1 Answers1

9

I'm not sure if this helps in your case, but it works for me:

  1. The 3rd parameter to the SmbConnection should be (I think) the client_machine_name, so I pass there what I get from socket.gethostname().

  2. I'm not using the sign_options and is_direct_tcp I just leave the default values.

This works for me with both samba and windows shares (I just have to pass a different port number sometimes).

Here is the code I use:

class Smb(object):
    def __init__(self, username, password, server, share, port=139):
        # split username if it contains a domain (domain\username)
        domain, username = username.split('\\') if username.count('\\') == 1 else ('', username)
        # setup data
        self.domain    = str(domain)
        self.username  = str(username)
        self.password  = str(password)
        self.client    = socket.gethostname()
        self.server    = str(server)
        self.server_ip = socket.gethostbyname(server)
        self.share     = str(share)
        self.port      = port
        self.conn      = None
        self.connected = False
        # SMB.SMBConnection logs too much
        smb_logger = logging.getLogger('SMB.SMBConnection')
        smb_logger.setLevel(logging.WARNING)

    def connect(self):
        try:
            self.conn = SMBConnection(self.username, self.password,
                                      self.client, self.server,
                                      use_ntlm_v2=True, domain=self.domain)
            self.connected = self.conn.connect(self.server_ip, self.port)
            logger.info('Connected to %s' % self.server)
            return self.connected
        except Exception, e:
            logger.error('Connect failed. Reason: %s', e)
            return False

And use it as:

smb = Smb('domain\\user', 'password', 'server', 'share_name')
Basic
  • 26,321
  • 24
  • 115
  • 201
Viktor Kerkez
  • 45,070
  • 12
  • 104
  • 85
  • Thanks, let me try that and see what happens... Hmmm that just takes me back to the `NotConnectedError` – Basic Sep 12 '13 at 11:28
  • It is indeed. I've updated the Q with more diagnostic info using smbclient. In addition, I can telnet to either machine on 139 and will get an "unrecognised request" when I type garbage so something's there and listening – Basic Sep 12 '13 at 11:59
  • Interestingly, your code works for the samba box but not the windows one... `File "...site-packages\smb\base.py", line 331, in _handleSessionChallenge_SMB2\n self.signing_session_key = (session_key + '\\0'*16)[:16]\n", "TypeError: can't concat bytes to str\n"` – Basic Sep 12 '13 at 12:57
  • What version of Python are you running? – Basic Sep 12 '13 at 13:18
  • 1
    @Basic sorry this is for python 2.7 :-/ Missed the python3 tag. I don't even remember why I have `str` everywhere. I think the module raised an exception if I passed a unicode string. The code is several months old... – Viktor Kerkez Sep 12 '13 at 13:27
  • No worries Viktor, thanks for taking the time. I think the exception above is a bug (concat of str and byte - changing it to `(session_key + ('\\0'*16).encode('utf-8'))` lets me get further but I have no idea if it's right. I'll post if I ever get this working. – Basic Sep 12 '13 at 15:06
  • 1
    I'm accepting as this actually pointed me in the right direction. I was using an incorrectly formatted server name (I was using `srv.domain.local` instead of just `srv`). I've since hit another problem (buffer overflow) but that's a different question. Thanks for your time – Basic Sep 13 '13 at 10:45