use crate::sys::utils::to_str;
use crate::{
common::{Gid, Uid},
User,
};
use std::ptr::null_mut;
use winapi::shared::lmcons::{MAX_PREFERRED_LENGTH, NET_API_STATUS};
use winapi::shared::minwindef::DWORD;
use winapi::shared::ntstatus::STATUS_SUCCESS;
use winapi::shared::winerror::ERROR_MORE_DATA;
use winapi::um::lmaccess::{NetUserEnum, NetUserGetLocalGroups};
use winapi::um::lmaccess::{
FILTER_NORMAL_ACCOUNT, LG_INCLUDE_INDIRECT, LPLOCALGROUP_USERS_INFO_0, USER_INFO_0,
};
use winapi::um::lmapibuf::NetApiBufferFree;
use winapi::um::ntlsa::{
LsaEnumerateLogonSessions, LsaFreeReturnBuffer, LsaGetLogonSessionData,
PSECURITY_LOGON_SESSION_DATA,
};
use winapi::um::winnt::{LPWSTR, PLUID};
#[allow(non_upper_case_globals)]
const NERR_Success: NET_API_STATUS = 0;
unsafe fn get_groups_for_user(username: LPWSTR) -> Vec<String> {
let mut buf: LPLOCALGROUP_USERS_INFO_0 = null_mut();
let mut nb_entries = 0;
let mut total_entries = 0;
let mut groups;
let status = NetUserGetLocalGroups(
[0u16].as_ptr(),
username,
0,
LG_INCLUDE_INDIRECT,
&mut buf as *mut _ as _,
MAX_PREFERRED_LENGTH,
&mut nb_entries,
&mut total_entries,
);
if status == NERR_Success {
groups = Vec::with_capacity(nb_entries as _);
if !buf.is_null() {
for i in 0..nb_entries {
let tmp = buf.offset(i as _);
if tmp.is_null() {
break;
}
groups.push(to_str((*tmp).lgrui0_name));
}
}
} else {
groups = Vec::new();
sysinfo_debug!("NetUserGetLocalGroups failed with ret code {}", status);
}
if !buf.is_null() {
NetApiBufferFree(buf as *mut _);
}
groups
}
pub unsafe fn get_users() -> Vec<User> {
let mut users = Vec::new();
let mut buffer: *mut USER_INFO_0 = null_mut();
let mut nb_read = 0;
let mut total = 0;
let mut resume_handle: DWORD = 0;
loop {
let status = NetUserEnum(
null_mut(),
0,
FILTER_NORMAL_ACCOUNT,
&mut buffer as *mut _ as *mut _,
MAX_PREFERRED_LENGTH,
&mut nb_read,
&mut total,
&mut resume_handle as *mut _ as *mut _,
);
if status == NERR_Success || status == ERROR_MORE_DATA {
let entries: &[USER_INFO_0] = std::slice::from_raw_parts(buffer, nb_read as _);
for entry in entries {
if entry.usri0_name.is_null() {
continue;
}
let groups = get_groups_for_user(entry.usri0_name);
let name = to_str(entry.usri0_name);
users.push(User {
uid: Uid(name.clone().into_boxed_str()),
gid: Gid(0),
name,
groups,
});
}
} else {
sysinfo_debug!(
"NetUserEnum error: {}",
if status == winapi::shared::winerror::ERROR_ACCESS_DENIED {
"access denied"
} else if status == winapi::shared::winerror::ERROR_INVALID_LEVEL {
"invalid level"
} else {
"unknown error"
}
);
}
if !buffer.is_null() {
NetApiBufferFree(buffer as *mut _);
buffer = null_mut();
}
if status != ERROR_MORE_DATA {
break;
}
}
let mut nb_sessions = 0;
let mut uids: PLUID = null_mut();
if LsaEnumerateLogonSessions(&mut nb_sessions, &mut uids) != STATUS_SUCCESS {
sysinfo_debug!("LsaEnumerateLogonSessions failed");
} else {
for offset in 0..nb_sessions {
let entry = uids.add(offset as _);
let mut data: PSECURITY_LOGON_SESSION_DATA = null_mut();
if LsaGetLogonSessionData(entry, &mut data) == STATUS_SUCCESS && !data.is_null() {
let data = *data;
if data.LogonType == winapi::um::ntlsa::Network {
continue;
}
let name = to_str(data.UserName.Buffer);
if users.iter().any(|u| u.name == name) {
continue;
}
users.push(User {
uid: Uid(name.clone().into_boxed_str()),
gid: Gid(0),
name,
groups: Vec::new(),
});
}
if !data.is_null() {
LsaFreeReturnBuffer(data as *mut _);
}
}
}
users
}