windows-erg 0.1.0

Ergonomic, idiomatic Rust wrappers for Windows APIs
Documentation
use std::ffi::c_void;

use windows::Win32::Foundation::{HLOCAL, LocalFree};
use windows::Win32::Security::Authorization::{
    ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1,
};
use windows::Win32::Security::{PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES};
use windows::core::PCWSTR;

use crate::error::{Error, PipeCreateError, PipeError};
use crate::utils::to_utf16_nul;

use super::types::PipeSecurityOptions;

pub(crate) struct NativePipeSecurityAttributes {
    attrs: SECURITY_ATTRIBUTES,
    descriptor: Option<PSECURITY_DESCRIPTOR>,
    include_for_call: bool,
}

impl NativePipeSecurityAttributes {
    pub(crate) fn from_options(
        options: &PipeSecurityOptions,
        resource: &str,
    ) -> crate::Result<Self> {
        let mut descriptor = None;
        let mut attrs = SECURITY_ATTRIBUTES {
            nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
            bInheritHandle: options.inherit_handle.into(),
            lpSecurityDescriptor: std::ptr::null_mut::<c_void>(),
        };

        if let Some(security_descriptor) = &options.security_descriptor {
            let sddl = descriptor_to_sddl(security_descriptor);
            let sddl_wide = to_utf16_nul(&sddl);

            let mut raw_descriptor = PSECURITY_DESCRIPTOR::default();
            let mut descriptor_size = 0u32;
            unsafe {
                ConvertStringSecurityDescriptorToSecurityDescriptorW(
                    PCWSTR(sddl_wide.as_ptr()),
                    SDDL_REVISION_1,
                    &mut raw_descriptor as *mut _,
                    Some(&mut descriptor_size),
                )
            }
            .map_err(|e| {
                Error::Pipe(PipeError::Create(PipeCreateError::with_code(
                    resource.to_string(),
                    "security_descriptor",
                    e.code().0,
                )))
            })?;

            attrs.lpSecurityDescriptor = raw_descriptor.0;
            descriptor = Some(raw_descriptor);
        }

        Ok(Self {
            attrs,
            descriptor,
            include_for_call: options.inherit_handle || options.security_descriptor.is_some(),
        })
    }

    pub(crate) fn as_option_ptr(&self) -> Option<*const SECURITY_ATTRIBUTES> {
        if self.include_for_call {
            Some(&self.attrs as *const SECURITY_ATTRIBUTES)
        } else {
            None
        }
    }
}

impl Drop for NativePipeSecurityAttributes {
    fn drop(&mut self) {
        if let Some(descriptor) = self.descriptor {
            unsafe {
                let _ = LocalFree(HLOCAL(descriptor.0));
            }
        }
    }
}

fn descriptor_to_sddl(descriptor: &crate::security::SecurityDescriptor) -> String {
    let mut sddl = String::new();

    if let Some(owner) = descriptor.owner() {
        sddl.push_str("O:");
        sddl.push_str(owner.as_str());
    }

    if let Some(group) = descriptor.group() {
        sddl.push_str("G:");
        sddl.push_str(group.as_str());
    }

    sddl.push_str("D:");
    for ace in descriptor.dacl().entries() {
        let ace_type = match ace.ace_type {
            crate::security::AceType::Allow => "A",
            crate::security::AceType::Deny => "D",
        };

        let mut flags = String::new();
        if ace.inheritance.object_inherit {
            flags.push_str("OI");
        }
        if ace.inheritance.container_inherit {
            flags.push_str("CI");
        }
        if ace.inheritance.inherit_only {
            flags.push_str("IO");
        }
        if ace.inheritance.no_propagate_inherit {
            flags.push_str("NP");
        }
        if ace.inherited {
            flags.push_str("ID");
        }

        sddl.push_str(&format!(
            "({};{};0x{:X};;;{})",
            ace_type,
            flags,
            ace.access_mask.bits(),
            ace.trustee.as_str()
        ));
    }

    sddl
}