Skip to content

Commit

Permalink
refactor(windows): use user_utils crate to get user and group names
Browse files Browse the repository at this point in the history
This commit also uses user_utils crate for:
* creating world sid for `get_accessmask` function
* replace passing of raw PSIDs with `BorrowedPsid<'_>` struct
  • Loading branch information
bydlw98 committed Apr 7, 2024
1 parent d65e531 commit 1c891bd
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 226 deletions.
147 changes: 16 additions & 131 deletions src/os/windows/accounts.rs
Original file line number Diff line number Diff line change
@@ -1,173 +1,58 @@
use std::io;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::Mutex;

use once_cell::sync::Lazy;

use super::sys_prelude::*;
use super::utf16_until_null_to_string_lossy;
use user_utils::windows::*;

use crate::config::Config;

pub fn get_accountname_by_sid_ptr(sid_ptr: c::PSID, config: &Config) -> String {
pub fn get_accountname_by_psid(psid: BorrowedPsid<'_>, config: &Config) -> String {
static ACCOUNTS_CACHE: Lazy<Mutex<Vec<Account>>> =
Lazy::new(|| Mutex::new(Vec::with_capacity(2)));
let mut accounts_cache = ACCOUNTS_CACHE.lock().unwrap();
let cache_index_option = accounts_cache
.iter()
.position(|account| account.sid_buf == sid_ptr);
.position(|account| account.psid == psid);

match cache_index_option {
Some(cache_index) => accounts_cache[cache_index].name.clone(),
None => match Account::from_sid_ptr(sid_ptr, config) {
None => match Account::get_by_psid(psid, config) {
Some(account) => {
let accountname = account.name.clone();
log::debug!("account '{}' is not found in ACCOUNTS_CACHE", accountname);
accounts_cache.push(account);

accountname
}
None => internal_get_accountname_by_sid_ptr(sid_ptr, config),
None => internal_get_accountname_by_psid(psid, config),
},
}
}

fn internal_get_accountname_by_sid_ptr(sid_ptr: c::PSID, config: &Config) -> String {
fn internal_get_accountname_by_psid(psid: BorrowedPsid<'_>, config: &Config) -> String {
if config.numeric_uid_gid {
convert_to_string_sid(sid_ptr)
psid.convert_to_string_sid().unwrap_or(String::from("?"))
} else {
lookup_accountname(sid_ptr)
}
}

fn lookup_accountname(sid_ptr: c::PSID) -> String {
let mut wide_name_length: u32 = 32;
let mut wide_domain_length: u32 = 32;
let mut wide_name_buf: [u16; 32] = [0; 32];
let mut wide_domain_buf: [u16; 32] = [0; 32];
let mut sid_name_use = c::SidTypeUnknown;

unsafe {
let return_code = c::LookupAccountSidW(
ptr::null(),
sid_ptr,
wide_name_buf.as_mut_ptr(),
&mut wide_name_length,
wide_domain_buf.as_mut_ptr(),
&mut wide_domain_length,
&mut sid_name_use,
);

// If LookupAccountSidW succeeds, return_code is non-zero
if return_code != 0 {
format!(
"{}\\{}",
utf16_until_null_to_string_lossy(&wide_domain_buf),
utf16_until_null_to_string_lossy(&wide_name_buf)
)
}
// If GetLastError() returns ERROR_NONE_MAPPED, means
// unable to get the name of SID
else if c::GetLastError() == c::ERROR_NONE_MAPPED {
log::debug!("no SID is mapped");
convert_to_string_sid(sid_ptr)
} else {
// Retry lookup SID name with correct size
let mut wide_name = vec![0; wide_name_length as usize];
let mut wide_domain = vec![0; wide_domain_length as usize];

let return_code = c::LookupAccountSidW(
ptr::null(),
sid_ptr,
wide_name.as_mut_ptr(),
&mut wide_name_length,
wide_domain.as_mut_ptr(),
&mut wide_domain_length,
&mut sid_name_use,
);
if return_code != 0 {
format!(
"{}\\{}",
utf16_until_null_to_string_lossy(&wide_domain),
utf16_until_null_to_string_lossy(&wide_name)
)
} else {
log::debug!(
"unable to lookup accountname: {}",
io::Error::last_os_error()
);

String::from('?')
}
}
}
}

fn convert_to_string_sid(sid_ptr: c::PSID) -> String {
unsafe {
let mut wide_cstring_ptr = MaybeUninit::<*mut u16>::uninit();
let return_code = c::ConvertSidToStringSidW(sid_ptr, wide_cstring_ptr.as_mut_ptr());

// On success, return_code is non-zero
if return_code != 0 {
let wide_cstring_ptr = wide_cstring_ptr.assume_init();
let wide_cstring_len = c::wcslen(wide_cstring_ptr);
let wide_cstring_array = std::slice::from_raw_parts(wide_cstring_ptr, wide_cstring_len);
let string_sid = String::from_utf16_lossy(wide_cstring_array);
c::LocalFree(wide_cstring_ptr as c::HLOCAL);

string_sid
} else {
String::from('?')
match psid.lookup_accountname() {
Ok(accountname) => accountname.to_string_lossy().to_string(),
Err(_) => psid.convert_to_string_sid().unwrap_or(String::from("?")),
}
}
}

#[derive(Debug, Default)]
#[derive(Debug)]
struct Account {
name: String,
sid_buf: SidBuf,
psid: OwnedPsid,
}

impl Account {
fn from_sid_ptr(sid_ptr: c::PSID, config: &Config) -> Option<Self> {
let sid_buf = SidBuf::from_sid_ptr(sid_ptr)?;
let accountname = internal_get_accountname_by_sid_ptr(sid_ptr, config);
fn get_by_psid(psid: BorrowedPsid<'_>, config: &Config) -> Option<Self> {
let sid_buf = psid.try_clone_to_owned().ok()?;
let accountname = internal_get_accountname_by_psid(psid, config);

Some(Self {
name: accountname,
sid_buf: sid_buf,
psid: sid_buf,
})
}
}

#[derive(Debug, Default)]
struct SidBuf(Vec<u8>);

impl SidBuf {
pub fn as_ptr(&self) -> c::PSID {
self.0.as_ptr() as c::PSID
}

pub fn from_sid_ptr(sid_ptr: c::PSID) -> Option<Self> {
unsafe {
let sid_length = c::GetLengthSid(sid_ptr);
let mut buf: Vec<u8> = vec![0; sid_length as usize];
let return_code = c::CopySid(sid_length, buf.as_mut_ptr() as c::PSID, sid_ptr);

// On success, return_code is non-zero
if return_code != 0 {
Some(Self(buf))
} else {
None
}
}
}
}

impl PartialEq<c::PSID> for SidBuf {
fn eq(&self, other: &c::PSID) -> bool {
unsafe { c::EqualSid(self.as_ptr(), *other) != 0 }
}
}
8 changes: 3 additions & 5 deletions src/os/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use nls_term_grid::{Alignment, GridCell};
use crate::config::{AllocatedSizeBlocks, Config};
use crate::output::GridCellExts;

use accounts::get_accountname_by_sid_ptr;
use accounts::get_accountname_by_psid;
use permissions::get_rwx_permissions;
use security_info::SecurityInfo;

Expand Down Expand Up @@ -86,12 +86,10 @@ impl WindowsMetadata {
self.rwx_permissions = get_rwx_permissions(&security_info, config);
}
if config.list_owner {
self.owner_string =
get_accountname_by_sid_ptr(security_info.sid_owner_ptr(), config);
self.owner_string = get_accountname_by_psid(security_info.owner_psid(), config);
}
if config.list_group {
self.group_string =
get_accountname_by_sid_ptr(security_info.sid_group_ptr(), config);
self.group_string = get_accountname_by_psid(security_info.group_psid(), config);
}
}
Err(err) => {
Expand Down
87 changes: 30 additions & 57 deletions src/os/windows/permissions.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::io;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::OnceLock;

use once_cell::sync::OnceCell;
use user_utils::windows::{AsPsid, AsRawPsid, BorrowedPsid, OwnedPsid};

use super::security_info::SecurityInfo;
use super::sys_prelude::*;
Expand All @@ -10,10 +11,10 @@ use crate::config::Config;
use crate::utils::HasMaskSetExt;

pub fn get_rwx_permissions(security_info: &SecurityInfo, config: &Config) -> String {
static OTHERS_SIDBUF: OnceLock<Option<Vec<u8>>> = OnceLock::new();
static WORLD_PSID: OnceCell<Option<OwnedPsid>> = OnceCell::new();
let mut permissions_buf = String::with_capacity(9);

match get_accessmask(security_info.dacl_ptr(), security_info.sid_owner_ptr()) {
match get_accessmask(security_info.dacl_ptr(), security_info.owner_psid()) {
Ok(owner_accessmask) => {
permissions_buf.push_str(&accessmask_to_rwx(owner_accessmask, config))
}
Expand All @@ -23,7 +24,7 @@ pub fn get_rwx_permissions(security_info: &SecurityInfo, config: &Config) -> Str
}
}

match get_accessmask(security_info.dacl_ptr(), security_info.sid_group_ptr()) {
match get_accessmask(security_info.dacl_ptr(), security_info.group_psid()) {
Ok(group_accessmask) => {
permissions_buf.push_str(&accessmask_to_rwx(group_accessmask, config))
}
Expand All @@ -33,48 +34,21 @@ pub fn get_rwx_permissions(security_info: &SecurityInfo, config: &Config) -> Str
}
}

match OTHERS_SIDBUF.get_or_init(|| create_others_sidbuf()) {
Some(others_sidbuf) => {
match get_accessmask(security_info.dacl_ptr(), others_sidbuf.as_ptr() as c::PSID) {
Ok(others_accessmask) => {
permissions_buf.push_str(&accessmask_to_rwx(others_accessmask, config))
}
Err(err) => {
eprintln!("nls: unable to get others permissions: {}", err);
permissions_buf.push_str("???")
}
match WORLD_PSID.get_or_init(|| OwnedPsid::world().ok()) {
Some(world_psid) => match get_accessmask(security_info.dacl_ptr(), world_psid.as_psid()) {
Ok(others_accessmask) => {
permissions_buf.push_str(&accessmask_to_rwx(others_accessmask, config))
}
}
Err(err) => {
eprintln!("nls: unable to get others permissions: {}", err);
permissions_buf.push_str("???")
}
},
None => permissions_buf.push_str("???"),
}
permissions_buf
}

pub fn create_others_sidbuf() -> Option<Vec<u8>> {
unsafe {
let mut world_sid_len = c::GetSidLengthRequired(1);
let mut others_sidbuf: Vec<u8> = vec![0; world_sid_len as usize];

let return_code = c::CreateWellKnownSid(
c::WinWorldSid,
ptr::null_mut(),
others_sidbuf.as_mut_ptr() as c::PSID,
&mut world_sid_len,
);

// On success, return_code is 0
if return_code != 0 {
Some(others_sidbuf)
} else {
log::debug!(
"unable to create others SID: {}",
io::Error::last_os_error()
);
None
}
}
}

fn accessmask_to_rwx(accessmask: u32, config: &Config) -> String {
let mut rwx_string = String::with_capacity(32);
let theme = &config.theme;
Expand All @@ -100,22 +74,21 @@ fn accessmask_to_rwx(accessmask: u32, config: &Config) -> String {
rwx_string
}

pub fn get_accessmask(dacl_ptr: *const c::ACL, sid_ptr: c::PSID) -> Result<u32, io::Error> {
unsafe {
let mut trustee = MaybeUninit::<c::TRUSTEE_W>::uninit();
c::BuildTrusteeWithSidW(trustee.as_mut_ptr(), sid_ptr);
let trustee = trustee.assume_init();

let mut accessmask: u32 = 0;
let return_code = c::GetEffectiveRightsFromAclW(dacl_ptr, &trustee, &mut accessmask);

// On success, return_code is ERROR_SUCCESS
// Else return_code is a raw os error
if return_code == c::ERROR_SUCCESS {
Ok(accessmask)
} else {
Err(io::Error::from_raw_os_error(return_code as i32))
}
pub fn get_accessmask(dacl_ptr: *const c::ACL, psid: BorrowedPsid<'_>) -> Result<u32, io::Error> {
let raw_psid = psid.as_raw_psid();
let mut trustee = MaybeUninit::<c::TRUSTEE_W>::uninit();
unsafe { c::BuildTrusteeWithSidW(trustee.as_mut_ptr(), raw_psid) };
let trustee = unsafe { trustee.assume_init() };

let mut accessmask: u32 = 0;
let return_code = unsafe { c::GetEffectiveRightsFromAclW(dacl_ptr, &trustee, &mut accessmask) };

// On success, return_code is ERROR_SUCCESS
// Else return_code is a raw os error
if return_code == c::ERROR_SUCCESS {
Ok(accessmask)
} else {
Err(io::Error::from_raw_os_error(return_code as i32))
}
}

Expand Down

0 comments on commit 1c891bd

Please sign in to comment.