Search code examples
node.jsrefnode-ffi

Using Native Windows Function GetNamedSecurityInfoA in node.js


I would like to get the permissions on a folder. I'm using the libraries ffi and ref to invoke native Windows functions. I'm calling the function GetNamedSecurityInfoA to get the DACL but the returned struct contains wrong values. Could you please help me? Thanks!

Edit 20191208: Function GetAce seems to work though...

Some Structures and Definitions:


/*typedef struct _ACE_HEADER {
  BYTE AceType;
  BYTE AceFlags;
  WORD AceSize;
} ACE_HEADER;*/

export const ACE_HEADER = struct({
   AceType:win32types.UCHAR,
   AceFlags:win32types.UCHAR,
   AceSize:win32types.WORD
});

/*typedef struct _ACCESS_ALLOWED_ACE {
    ACE_HEADER  Header;
    ACCESS_MASK Mask;
    DWORD       SidStart;
} ACCESS_ALLOWED_ACE;*/

export const ACCESS_ALLOWED_ACL = struct({
    Header:ACE_HEADER,
    Mask:win32types.DWORD,
    SidStart:win32types.DWORD
});


export const ACL = struct({
    AclRevision:win32types.UCHAR,
    Sbz1:win32types.UCHAR,
    AclSize:win32types.USHORT,
    AceCount:win32types.USHORT,
    Sbz2:win32types.USHORT
});

/*
typedef struct _SECURITY_DESCRIPTOR {
  BYTE                        Revision;
  BYTE                        Sbz1;
  SECURITY_DESCRIPTOR_CONTROL Control;
  PSID                        Owner;
  PSID                        Group;
  PACL                        Sacl;
  PACL                        Dacl;
} SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
**/
export const SECURITY_DESCRIPTOR = struct({
    Revision:win32types.UCHAR,
    Sbz1:win32types.UCHAR,
    Control:win32types.WORD,
    Owner:win32types.PVOID,
    Group:win32types.PVOID,
    Sacl:win32types.PVOID,
    Dacl:win32types.PVOID
});

const PACL = ref.refType(ACL);
const PSECURITY_DESCRIPTOR = ref.refType(SECURITY_DESCRIPTOR);
const PACE = ref.refType(ACCESS_ALLOWED_ACL);

export const advApi = ffi.Library('Advapi32', {
    /*
    DWORD GetNamedSecurityInfoW(
    LPCWSTR              pObjectName,
    SE_OBJECT_TYPE       ObjectType,
    SECURITY_INFORMATION SecurityInfo,
    PSID                 *ppsidOwner,
    PSID                 *ppsidGroup,
    PACL                 *ppDacl,
    PACL                 *ppSacl,
    PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
     */
    GetNamedSecurityInfoW:[win32types.DWORD,[win32types.LPCTSTR,'int','uint', 'pointer', 'pointer', PACL, PACL, PSECURITY_DESCRIPTOR]],
    /*
    BOOL GetAce(
    PACL   pAcl,
    DWORD  dwAceIndex,
    LPVOID *pAce);
     */
    GetAce:[win32types.BOOL, [ACL, win32types.DWORD, ref.refType(PACE)]],
});

How the function is called:


import {advApi} from "../win32";
import {ACL as defACL, SECURITY_DESCRIPTOR as defSecDesc, ACCESS_ALLOWED_ACL as ace} from "../win32defs"

                let dAclRef = new defACL().ref();
                let securityDescriptorRef = new defSecDesc().ref();

                let childPathBuffer = Buffer.from(newChild.path+'\0', 'ucs-2');

                await advApi.GetNamedSecurityInfoW
                    .async(childPathBuffer, 1, 4, null, null, dAclRef, null, securityDescriptorRef,
                        (err, res) => {
                        if(err) throw err;


                        if(res === 0)
                        {
                             let aclValue = dAclRef.deref();
                             let i = 0;
                             let success = true;

                            while(i<aclValue.AceCount && success)
                            {
                                let aceRefRef = new ace().ref().ref();
                                success = advApi.GetAce(aclValue, i, aceRefRef);
                                if(success)
                                {
                                }

                                i++;
                            }
                        };
        });

the returned ACL Struct contains wrong values, with AceCount > 500.


Solution

  • Finally I was able to retrieve the permissions (that wasn't dead easy).

    Still I don't know why AceCount is that big.

    Here some Code excerpts:

    
    /*typedef struct _ACE_HEADER {
      BYTE AceType;
      BYTE AceFlags;
      WORD AceSize;
    } ACE_HEADER;*/
    
    export const ACE_HEADER = struct({
       AceType:win32types.UCHAR,
       AceFlags:win32types.UCHAR,
       AceSize:win32types.WORD
    });
    
    /*typedef struct _ACCESS_ALLOWED_ACE {
        ACE_HEADER  Header;
        ACCESS_MASK Mask;
        DWORD       SidStart;
    } ACCESS_ALLOWED_ACE;*/
    
    export const ACCESS_ALLOWED_ACL = struct({
        Header:ACE_HEADER,
        Mask:win32types.DWORD,
        SidStart:win32types.DWORD
    });
    
    
    export const ACL = struct({
        AclRevision:win32types.UCHAR,
        Sbz1:win32types.UCHAR,
        AclSize:win32types.USHORT,
        AceCount:win32types.USHORT,
        Sbz2:win32types.USHORT
    });
    
    /*
    typedef struct _SECURITY_DESCRIPTOR {
      BYTE                        Revision;
      BYTE                        Sbz1;
      SECURITY_DESCRIPTOR_CONTROL Control;
      PSID                        Owner;
      PSID                        Group;
      PACL                        Sacl;
      PACL                        Dacl;
    } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
    **/
    export const SECURITY_DESCRIPTOR = struct({
        Revision:win32types.UCHAR,
        Sbz1:win32types.UCHAR,
        Control:win32types.WORD,
        Owner:win32types.PVOID,
        Group:win32types.PVOID,
        Sacl:win32types.PVOID,
        Dacl:win32types.PVOID
    });
    
    const PACL = ref.refType(ACL);
    const PSECURITY_DESCRIPTOR = ref.refType(SECURITY_DESCRIPTOR);
    const PACE = ref.refType(ACCESS_ALLOWED_ACL);
    
    export const advApi = ffi.Library('Advapi32', {
        /*
        DWORD GetNamedSecurityInfoW(
        LPCWSTR              pObjectName,
        SE_OBJECT_TYPE       ObjectType,
        SECURITY_INFORMATION SecurityInfo,
        PSID                 *ppsidOwner,
        PSID                 *ppsidGroup,
        PACL                 *ppDacl,
        PACL                 *ppSacl,
        PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
         */
        GetNamedSecurityInfoW:[win32types.DWORD,[win32types.LPCTSTR,'int','uint', 'pointer', 'pointer', PACL, PACL, PSECURITY_DESCRIPTOR]],
        /*
        BOOL GetAce(
        PACL   pAcl,
        DWORD  dwAceIndex,
        LPVOID *pAce);
         */
        GetAce:[win32types.BOOL, [ACL, win32types.DWORD, ref.refType(PACE)]],
        /*BOOL IsValidSid(
         PSID pSid
        )*/
        IsValidSid:[win32types.BOOL, [win32types.LPVOID]],
    
        /*BOOL LookupAccountSidW(
        LPCWSTR       lpSystemName,
        PSID          Sid,
        LPWSTR        Name,
        LPDWORD       cchName,
        LPWSTR        ReferencedDomainName,
        LPDWORD       cchReferencedDomainName,
        PSID_NAME_USE peUse
        );*/
        LookupAccountSidW:[win32types.BOOL, [win32types.LPCTSTR, win32types.LPVOID, win32types.LPCTSTR, win32types.LPDWORD, win32types.LPCTSTR, win32types.LPDWORD, ref.refType('int')]],
    });
    
    

    How the function is called. The important bit here is this one: let sidBuffer = ref.reinterpret(aceRef, 4, 8).

    
    import {advApi} from "../win32";
    import {ACL as defACL, SECURITY_DESCRIPTOR as defSecDesc, ACCESS_ALLOWED_ACL as ace} from "../win32defs"
    
                    let dAclRef = new defACL().ref();
                    let securityDescriptorRef = new defSecDesc().ref();
    
                    let childPathBuffer = Buffer.from(newChild.path+'\0', 'ucs-2');
    
                    await advApi.GetNamedSecurityInfoW
                        .async(childPathBuffer, 1, 4, null, null, dAclRef, null, securityDescriptorRef,
                            (err, res) => {
                            if(err) throw err;
    
    
                            if(res === 0)
                            {
                                let aclValue = dAclRef.deref();
                                let i = 0;
                                let success = true;
    
                                while(i<aclValue.AceCount && success)
                                {
                                    let aceRefRef = new ace().ref().ref();
                                    success = advApi.GetAce(aclValue, i, aceRefRef);
                                    if(success)
                                    {
                                        let aceRef = aceRefRef.deref();
                                        let sidBuffer = ref.reinterpret(aceRef, 4, 8);
    
                                        if(advApi.IsValidSid(sidBuffer))
                                        {
                                            let cchNameRef = ref.alloc(win32types.DWORD, new Buffer(ref.sizeof.pointer));
                                            let cchReferencedDomainNameRef = ref.alloc(win32types.DWORD, new Buffer(ref.sizeof.pointer));
                                            let peUseRef = ref.alloc('int', new Buffer(ref.sizeof.pointer));
    
                                            advApi.LookupAccountSidW(null, sidBuffer, null, cchNameRef, null, cchReferencedDomainNameRef, peUseRef);
    
                                            let nameRef = Buffer.alloc(cchNameRef.deref()*2);
                                            let domainNameRef = Buffer.alloc(cchReferencedDomainNameRef.deref()*2);
    
    
                                            let accessArray = [];
    
                                            if(advApi.LookupAccountSidW(null, sidBuffer, nameRef, cchNameRef, domainNameRef, cchReferencedDomainNameRef, peUseRef))
                                            {
                                                let aceValue = aceRef.deref();
    
                                                ...
                                            }
    
                                        }
                                    }
                                    i++;
                                }
                            };