Home > database >  windows-rs GetNamedSecurityInfoW error 87
windows-rs GetNamedSecurityInfoW error 87

Time:05-16

I'm trying to use windows-rs to use GetNamedSecurityInfoW microsoft api docs to read file permission information, but I keep getting error code 87 corresponds to ERROR_INVALID_PARAMETER. What have I done wrong? (I'm not experienced with rust or the windows api)

#[cfg(windows)]
pub unsafe fn get_file_perms(file: String) -> Result<()> {
    use windows_sys::core::PCWSTR;
    use windows_sys::Win32::Security::Authorization::GetNamedSecurityInfoW;

    let file_u16 = file.encode_utf16().collect::<Vec<u16>>();
    let lpfile: PCWSTR = file_u16.as_ptr() as PCWSTR;
    let acl: *mut *mut windows_sys::Win32::Security::ACL = std::ptr::null_mut();
    let security_descriptor: *mut windows_sys::Win32::Security::PSECURITY_DESCRIPTOR = std::ptr::null_mut();
    let err = GetNamedSecurityInfoW(
        lpfile,
        windows_sys::Win32::Security::Authorization::SE_FILE_OBJECT,
        windows_sys::Win32::Security::DACL_SECURITY_INFORMATION,
        std::ptr::null_mut(),
        std::ptr::null_mut(),
        acl,
        std::ptr::null_mut(),
        security_descriptor,
    );
    if err != 0
    {
        println!("{}", err);
        return Err(anyhow!("Failed to get file permissions"));
    }

    Ok(())
}`

CodePudding user response:

GetNamedSecurityInfoW is an API call with somewhat complex semantics. Besides a description of the object, there's

  • a bitmask (SecurityInfo) describing the requested information
  • a set of output parameters (ppsidOwner, ppsidGroup, ppDacl, ppSacl) that provide access to structured data
  • the actual buffer holding the data (ppSecurityDescriptor).

On successful return, the system allocates memory, and transfers ownership to the caller through the final parameter. Depending on the requested information (DACL_SECURITY_INFORMATION) you will have to pass the addresses of pointers to the structured data (ppDacl in this case).

With that fixed, there are two more issues left: Making sure that the object name (pObjectName) is zero-terminated, and cleaning up the buffer the system allocated for us with a call to LocalFree. Note that any one of ppsidOwner, ppsidGroup, ppDacl, and ppSacl are valid only for as long as ppSecurityDescriptor is valid.

The following code fixes the immediate issue:

pub unsafe fn get_file_perms(file: String) -> Result<()> {
    use windows_sys::Win32::Security::Authorization::GetNamedSecurityInfoW;

    let file_u16 = file.encode_utf16().collect::<Vec<u16>>();
    // Pointers that receive the output arguments
    let mut acl = std::ptr::null_mut();
    let mut security_descriptor = std::ptr::null_mut();
    let err = GetNamedSecurityInfoW(
        file_u16.as_ptr(),
        windows_sys::Win32::Security::Authorization::SE_FILE_OBJECT,
        windows_sys::Win32::Security::DACL_SECURITY_INFORMATION,
        std::ptr::null_mut(),
        std::ptr::null_mut(),
        // Pass the *address* of the pointer
        std::ptr::addr_of_mut!(acl),
        std::ptr::null_mut(),
        // Same here
        std::ptr::addr_of_mut!(security_descriptor),
    );
    if err != 0 {
        println!("{}", err);
        return Err("Failed to get file permissions".into());
    }

    // At this point `acl` points into an access control list

    // Cleanup up resources (should really be bound to a struct with a `Drop` impl)
    windows_sys::Win32::System::Memory::LocalFree(security_descriptor as _);

    Ok(())
}

As far as the interface goes, you should consider taking a Path/PathBuf instead. Since you are dealing with path names, a String will unduly restrict the input to the point of not being able to encode all potential paths.

Adding zero-termination, the function can be rewritten to this:

pub unsafe fn get_file_perms(file: impl AsRef<Path>) -> Result<()> {

    let file_u16 = file
        .as_ref()
        .as_os_str()
        .encode_wide()
        .chain(once(0))
        .collect::<Vec<_>>();

    // ...
  • Related