blob: 2fef05c3faef762d256f6aa732044f81f8deb367 [file] [log] [blame]
// Take a look at the license at the top of the repository in the LICENSE file.
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};
// FIXME: once this is mreged in winapi, it can be removed.
#[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
}
// FIXME: For now, the Uid is the user name, which is quite bad. Normally, there is `PSID` for
// that. But when getting the `PSID` from the processes, it doesn't match the ones we have for
// the users (`EqualSid`). Anyway, until I have time and motivation to fix this. It'll remain
// like that...
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 mut user: *mut USER_INFO_23 = null_mut();
// if NetUserGetInfo(
// null_mut(),
// entry.usri0_name,
// 23,
// &mut user as *mut _ as *mut _,
// ) == NERR_Success
// {
// let groups = get_groups_for_user((*user).usri23_name);
// users.push(User {
// uid: Uid(name.clone().into_boxed_str()),
// gid: Gid(0),
// name: to_str((*user).usri23_name),
// groups,
// });
// }
// if !user.is_null() {
// NetApiBufferFree(user as *mut _);
// }
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;
}
}
// First part done. Second part now!
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,
// There is no local groups for a non-local user.
groups: Vec::new(),
});
}
if !data.is_null() {
LsaFreeReturnBuffer(data as *mut _);
}
}
}
users
}