use std::{fmt::Display, str::FromStr};
#[cfg(feature = "user")]
use windows::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
use windows::Win32::Foundation::{HLOCAL, LocalFree};
use windows::Win32::Security::Authorization::{ConvertSidToStringSidW, ConvertStringSidToSidW};
use windows::Win32::Security::{CopySid, GetLengthSid, IsValidSid, PSID};
#[cfg(feature = "user")]
use windows::Win32::Security::{LookupAccountSidW, SidTypeUnknown};
use windows::core::{PCWSTR, PWSTR};
use crate::sys::utils::to_utf8_str;
#[doc = include_str!("../../md_doc/sid.md")]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Sid {
sid: Vec<u8>,
}
impl Sid {
pub(crate) unsafe fn from_psid(psid: PSID) -> Option<Self> {
if psid.is_invalid() {
return None;
}
if unsafe { !IsValidSid(psid).as_bool() } {
return None;
}
let length = unsafe { GetLengthSid(psid) };
let mut sid = vec![0; length as usize];
if unsafe { CopySid(length, PSID(sid.as_mut_ptr().cast()), psid).is_err() } {
sysinfo_debug!("CopySid failed: {:?}", std::io::Error::last_os_error());
return None;
}
assert_eq!(sid[0], 1, "Expected SID revision to be 1");
Some(Self { sid })
}
#[cfg(feature = "user")]
pub(crate) fn account_name(&self) -> Option<String> {
unsafe {
let mut name_len = 0;
let mut domain_len = 0;
let mut name_use = SidTypeUnknown;
let sid = PSID((self.sid.as_ptr() as *mut u8).cast());
if let Err(err) = LookupAccountSidW(
PCWSTR::null(),
sid,
None,
&mut name_len,
None,
&mut domain_len,
&mut name_use,
) && err.code() != ERROR_INSUFFICIENT_BUFFER.to_hresult()
{
sysinfo_debug!("LookupAccountSidW failed: {:?}", err);
return None;
}
let mut name = vec![0; name_len as usize];
domain_len = 0;
if LookupAccountSidW(
PCWSTR::null(),
sid,
Some(PWSTR::from_raw(name.as_mut_ptr())),
&mut name_len,
None,
&mut domain_len,
&mut name_use,
)
.is_err()
{
sysinfo_debug!(
"LookupAccountSidW failed: {:?}",
std::io::Error::last_os_error()
);
return None;
}
Some(to_utf8_str(PWSTR::from_raw(name.as_mut_ptr())))
}
}
}
impl Display for Sid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unsafe fn convert_sid_to_string_sid(sid: PSID) -> Option<String> {
let mut string_sid = PWSTR::null();
unsafe {
if let Err(_err) = ConvertSidToStringSidW(sid, &mut string_sid) {
sysinfo_debug!("ConvertSidToStringSidW failed: {:?}", _err);
return None;
}
let result = to_utf8_str(string_sid);
let _err = LocalFree(Some(HLOCAL(string_sid.0 as _)));
Some(result)
}
}
let string_sid =
unsafe { convert_sid_to_string_sid(PSID((self.sid.as_ptr() as *mut u8).cast())) };
let string_sid = string_sid.ok_or(std::fmt::Error)?;
write!(f, "{string_sid}")
}
}
impl FromStr for Sid {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
unsafe {
let mut string_sid: Vec<u16> = s.encode_utf16().collect();
string_sid.push(0);
let mut psid = PSID::default();
if let Err(err) =
ConvertStringSidToSidW(PCWSTR::from_raw(string_sid.as_ptr()), &mut psid)
{
return Err(format!("ConvertStringSidToSidW failed: {err:?}"));
}
let sid = Self::from_psid(psid);
let _err = LocalFree(Some(HLOCAL(psid.0 as _)));
Ok(sid.expect("ConvertStringSidToSidW should have worked"))
}
}
}