python-3.xlinuxsmbcifscacls

Getting owner of file from smb share, by using python on linux


I need to find out for a script I'm writing who is the true owner of a file in an smb share (mounted using mount -t cifs of course on my server and using net use through windows machines).

Turns out it is a real challenge finding this information out using python on a linux server.

I tried using many many smb libraries (such as smbprotocol, smbclient and others), nothing worked.
I find few solutions for windows, they all use pywin32 or another windows specific package.
And I also managed to do it from bash using smbcalcs but couldn't do it cleanly but using subprocess.popen('smbcacls')..

Any idea on how to solve it?


Solution

  • This was unbelievably not a trivial task, and unfortunately the answer isn't simple as I hoped it would be..

    I'm posting this answer if someone will be stuck with this same problem in the future, but hope maybe someone would post a better solution earlier

    In order to find the owner I used this library with its examples:

    from smb.SMBConnection import SMBConnection
    
    conn = SMBConnection(username='<username>', password='<password>', domain='<domain>', my_name='<some pc name>', remote_name='<server name>')
    conn.connect('<server name>')
    
    sec_att = conn.getSecurity('<share name>', r'\some\file\path')
    owner_sid = sec_att.owner
    

    The problem is that pysmb package will only give you the owner's SID and not his name.
    In order to get his name you need to make an ldap query like in this answer (reposting the code):

    from ldap3 import Server, Connection, ALL
    from ldap3.utils.conv import escape_bytes
    
    s = Server('my_server', get_info=ALL)
    c = Connection(s, 'my_user', 'my_password')
    c.bind()
    
    binary_sid = b'....'  # your sid must be in binary format
    
    c.search('my_base', '(objectsid=' + escape_bytes(binary_sid) + ')', attributes=['objectsid', 'samaccountname'])
    print(c.entries)
    

    But of course nothing will be easy, it took me hours to find a way to convert a string SID to binary SID in python, and in the end this solved it:

    # posting the needed functions and omitting the class part
    def byte(strsid):
        '''
        Convert a SID into bytes
            strdsid - SID to convert into bytes
        '''
        sid = str.split(strsid, '-')
        ret = bytearray()
        sid.remove('S')
        for i in range(len(sid)):
            sid[i] = int(sid[i])
        sid.insert(1, len(sid)-2)
        ret += longToByte(sid[0], size=1)
        ret += longToByte(sid[1], size=1)
        ret += longToByte(sid[2], False, 6)
        for i in range(3, len(sid)):
            ret += cls.longToByte(sid[i])
        return ret
    
    def byteToLong(byte, little_endian=True):
        '''
        Convert bytes into a Python integer
            byte - bytes to convert
            little_endian - True (default) or False for little or big endian
        '''
        if len(byte) > 8:
            raise Exception('Bytes too long. Needs to be <= 8 or 64bit')
        else:
            if little_endian:
                a = byte.ljust(8, b'\x00')
                return struct.unpack('<q', a)[0]
            else:
                a = byte.rjust(8, b'\x00')
                return struct.unpack('>q', a)[0]  
    

    ... AND finally you have the full solution! enjoy :(