Skip to content

Missing AccessMask flags when requesting DACL from security descriptor #348

Description

@orthose

Consider the following code inspired by https://gist.github.com/jborean93/c4d8db1db14297de1c9a268084e89d36 to get DACL of file or folder:

import os
import smbclient
from smbprotocol.file_info import (
    InfoType,
)
from smbprotocol.open import (
    Open,
    DirectoryAccessMask,
    FilePipePrinterAccessMask,
    SMB2QueryInfoRequest,
    SMB2QueryInfoResponse,
    InfoAdditionalInformation,
)
from smbprotocol.security_descriptor import (
    SMB2CreateSDBuffer,
)


# Not needed when using Kerberos auth, can also be set as kwargs when calling open_file.
smbclient.ClientConfig(username=os.environ["USER"], password=os.environ["PASSWORD"])


def get_sd(fd: Open, info: int) -> SMB2CreateSDBuffer:
    """ Get the Security Descriptor for the opened file. """
    query_req = SMB2QueryInfoRequest()
    query_req['info_type'] = InfoType.SMB2_0_INFO_SECURITY
    query_req['output_buffer_length'] = 65535
    query_req['additional_information'] = info
    query_req['file_id'] = fd.file_id

    req = fd.connection.send(query_req, sid=fd.tree_connect.session.session_id, tid=fd.tree_connect.tree_connect_id)
    resp = fd.connection.receive(req)
    query_resp = SMB2QueryInfoResponse()
    query_resp.unpack(resp['data'].get_value())

    security_descriptor = SMB2CreateSDBuffer()
    security_descriptor.unpack(query_resp['buffer'].get_value())

    return security_descriptor


# File example
with smbclient.open_file(
    r'\\path\to\nas\file.txt',
    mode='rb',
    buffering=0,
    file_type="file",
    desired_access=FilePipePrinterAccessMask.READ_CONTROL) as file:
    sd = get_sd(file.fd, InfoAdditionalInformation.OWNER_SECURTIY_INFORMATION | InfoAdditionalInformation.DACL_SECURITY_INFORMATION)
          
    for ace in sd.get_dacl()['aces']:
        print(str(ace["mask"]), ace["mask"].flag_type)


# Dir example
with smbclient.open_file(
    r"\\path\to\nas\folder", 
    mode='br',
    buffering=0,
    file_type='dir',
    desired_access=DirectoryAccessMask.READ_CONTROL
) as file:
    sd = get_sd(file.fd, InfoAdditionalInformation.OWNER_SECURTIY_INFORMATION | InfoAdditionalInformation.DACL_SECURITY_INFORMATION)
    
    for ace in sd.get_dacl()['aces']:
        print(str(ace["mask"]), ace["mask"].flag_type)
(1179817) READ_CONTROL, SYNCHRONIZE <class 'smbprotocol.security_descriptor.AccessMask'>
(2032127) DELETE, READ_CONTROL, SYNCHRONIZE, WRITE_DACL, WRITE_OWNER, __firstlineno__ <class 'smbprotocol.security_descriptor.AccessMask'>
(2032127) DELETE, READ_CONTROL, SYNCHRONIZE, WRITE_DACL, WRITE_OWNER, __firstlineno__ <class 'smbprotocol.security_descriptor.AccessMask'>
(1179817) READ_CONTROL, SYNCHRONIZE <class 'smbprotocol.security_descriptor.AccessMask'>
(2032127) DELETE, READ_CONTROL, SYNCHRONIZE, WRITE_DACL, WRITE_OWNER, __firstlineno__ <class 'smbprotocol.security_descriptor.AccessMask'>
(1179817) READ_CONTROL, SYNCHRONIZE <class 'smbprotocol.security_descriptor.AccessMask'>
(1179817) READ_CONTROL, SYNCHRONIZE <class 'smbprotocol.security_descriptor.AccessMask'>
(2032127) DELETE, READ_CONTROL, SYNCHRONIZE, WRITE_DACL, WRITE_OWNER, __firstlineno__ <class 'smbprotocol.security_descriptor.AccessMask'>
(2032127) DELETE, READ_CONTROL, SYNCHRONIZE, WRITE_DACL, WRITE_OWNER, __firstlineno__ <class 'smbprotocol.security_descriptor.AccessMask'>
(2032127) DELETE, READ_CONTROL, SYNCHRONIZE, WRITE_DACL, WRITE_OWNER, __firstlineno__ <class 'smbprotocol.security_descriptor.AccessMask'>

I notice that ACE mask has_flag type AccessMask. Some ACE have a value with some bits undefined in AccessMask. For example, 2032127 is 0x1f01ff or 0b111110000000111111111. There is no mask defined for the right-hand side of this number in AccessMask.

The docstring of AccessMask says:

32-bit set of flags that are used to encode the user rights to an object.
This is just a generic setup of access mask flags to set an can vary
from the object being set. When setting the AccessMask on an ACE packet,
any 32-bit value can be used and this is just as a guideline.

But, I wonder if the flag_type of ace["mask"] shouldn't be instead FilePipePrinterAccessMask for a file and DirectoryAccessMask for a directory? These appear to be a subset of AccessMask but define more flags.

class AccessMask:
"""
[MS-DTYP]
2.4.3 ACCESS_MASK
32-bit set of flags that are used to encode the user rights to an object.
This is just a generic setup of access mask flags to set an can vary
from the object being set. When setting the AccessMask on an ACE packet,
any 32-bit value can be used and this is just as a guideline.
"""
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000
MAXIMUM_ALLOWED = 0x02000000
ACCESS_SYSTEM_SECURITY = 0x01000000
SYNCHRONIZE = 0x00100000
WRITE_OWNER = 0x00080000
WRITE_DACL = 0x00040000
READ_CONTROL = 0x00020000
DELETE = 0x00010000

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions