-
-
Notifications
You must be signed in to change notification settings - Fork 340
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #930 from mbikovitsky/windows-sid
Use SIDs as UIDs on Windows
- Loading branch information
Showing
9 changed files
with
336 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Opaque type encapsulating a Windows SID. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// Take a look at the license at the top of the repository in the LICENSE file. | ||
|
||
use std::{fmt::Display, str::FromStr}; | ||
|
||
use winapi::{ | ||
shared::{ | ||
sddl::{ConvertSidToStringSidW, ConvertStringSidToSidW}, | ||
winerror::ERROR_INSUFFICIENT_BUFFER, | ||
}, | ||
um::{ | ||
errhandlingapi::GetLastError, | ||
securitybaseapi::{CopySid, GetLengthSid, IsValidSid}, | ||
winbase::{LocalFree, LookupAccountSidW}, | ||
winnt::{SidTypeUnknown, LPWSTR, PSID}, | ||
}, | ||
}; | ||
|
||
use crate::sys::utils::to_str; | ||
|
||
#[doc = include_str!("../../md_doc/sid.md")] | ||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct Sid { | ||
sid: Vec<u8>, | ||
} | ||
|
||
impl Sid { | ||
/// Creates an `Sid` by making a copy of the given raw SID. | ||
pub(crate) unsafe fn from_psid(psid: PSID) -> Option<Self> { | ||
if psid.is_null() { | ||
return None; | ||
} | ||
|
||
if IsValidSid(psid) == 0 { | ||
return None; | ||
} | ||
|
||
let length = GetLengthSid(psid); | ||
|
||
let mut sid = vec![0; length as usize]; | ||
|
||
if CopySid(length, sid.as_mut_ptr() as *mut _, psid) == 0 { | ||
sysinfo_debug!("CopySid failed: {:?}", GetLastError()); | ||
return None; | ||
} | ||
|
||
// We are making assumptions about the SID internal structure, | ||
// and these only hold if the revision is 1 | ||
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid | ||
// Namely: | ||
// 1. SIDs can be compared directly (memcmp). | ||
// 2. Following from this, to hash a SID we can just hash its bytes. | ||
// These are the basis for deriving PartialEq, Eq, and Hash. | ||
// And since we also need PartialOrd and Ord, we might as well derive them | ||
// too. The default implementation will be consistent with Eq, | ||
// and we don't care about the actual order, just that there is one. | ||
// So it should all work out. | ||
// Why bother with this? Because it makes the implementation that | ||
// much simpler :) | ||
assert_eq!(sid[0], 1, "Expected SID revision to be 1"); | ||
|
||
Some(Self { sid }) | ||
} | ||
|
||
/// Retrieves the account name of this SID. | ||
pub(crate) fn account_name(&self) -> Option<String> { | ||
unsafe { | ||
let mut name_len = 0; | ||
let mut domain_len = 0; | ||
let mut name_use = SidTypeUnknown; | ||
|
||
if LookupAccountSidW( | ||
std::ptr::null_mut(), | ||
self.sid.as_ptr() as *mut _, | ||
std::ptr::null_mut(), | ||
&mut name_len, | ||
std::ptr::null_mut(), | ||
&mut domain_len, | ||
&mut name_use, | ||
) == 0 | ||
{ | ||
let error = GetLastError(); | ||
if error != ERROR_INSUFFICIENT_BUFFER { | ||
sysinfo_debug!("LookupAccountSidW failed: {:?}", error); | ||
return None; | ||
} | ||
} | ||
|
||
let mut name = vec![0; name_len as usize]; | ||
|
||
// Reset length to 0 since we're still passing a NULL pointer | ||
// for the domain. | ||
domain_len = 0; | ||
|
||
if LookupAccountSidW( | ||
std::ptr::null_mut(), | ||
self.sid.as_ptr() as *mut _, | ||
name.as_mut_ptr(), | ||
&mut name_len, | ||
std::ptr::null_mut(), | ||
&mut domain_len, | ||
&mut name_use, | ||
) == 0 | ||
{ | ||
sysinfo_debug!("LookupAccountSidW failed: {:?}", GetLastError()); | ||
return None; | ||
} | ||
|
||
Some(to_str(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: LPWSTR = std::ptr::null_mut(); | ||
if ConvertSidToStringSidW(sid, &mut string_sid) == 0 { | ||
sysinfo_debug!("ConvertSidToStringSidW failed: {:?}", GetLastError()); | ||
return None; | ||
} | ||
let result = to_str(string_sid); | ||
LocalFree(string_sid as *mut _); | ||
Some(result) | ||
} | ||
|
||
let string_sid = unsafe { convert_sid_to_string_sid(self.sid.as_ptr() as *mut _) }; | ||
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 = std::ptr::null_mut(); | ||
if ConvertStringSidToSidW(string_sid.as_ptr(), &mut psid) == 0 { | ||
return Err(format!( | ||
"ConvertStringSidToSidW failed: {:?}", | ||
GetLastError() | ||
)); | ||
} | ||
let sid = Self::from_psid(psid); | ||
LocalFree(psid as *mut _); | ||
|
||
// Unwrapping because ConvertStringSidToSidW should've performed | ||
// all the necessary validations. If it returned an invalid SID, | ||
// we better fail fast. | ||
Ok(sid.unwrap()) | ||
} | ||
} | ||
} |
Oops, something went wrong.