Reputation: 64
I use winapi NetUserAdd to add a user,account added succsess,execute command net user
,shows as below picture and cannot find users by control panel.
What wrong with LPSWTR or USER_INFO_1 struct to LPBYTE?
use winapi::um::lmaccess::{USER_INFO_1,NetUserAdd,UF_SCRIPT};
use std::iter::{once};
use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;
pub fn winstr(value: &str) -> Vec<u16> {
OsStr::new(value).encode_wide().chain(once(0)).collect()
}
fn main() {
let username:String = "Test".to_string();
let password:String = "Test******".to_string();
let mut user = USER_INFO_1{
usri1_name:winstr(&username).as_mut_ptr(),
usri1_password:winstr(&password).as_mut_ptr(),
usri1_priv:1,
usri1_password_age: 0,
usri1_home_dir: std::ptr::null_mut(),
usri1_comment: std::ptr::null_mut(),
usri1_flags:UF_SCRIPT,
usri1_script_path: std::ptr::null_mut(),
};
let mut error = 0 ;
unsafe{
NetUserAdd(std::ptr::null_mut(),1,&mut user as *mut _ as _,&mut error);
}
println!("{}",error);//result is 0,means success.
}
Upvotes: 1
Views: 498
Reputation: 5645
I’d recommend this excellent resource https://github.com/secur30nly/netuser-rs. It is a rust-based cli/library to manage Windows users and groups.
My solution is built on top of it but is slightly different: I used USER_INFO_2
instead of USER_INFO_1
, which allows for the user's full name, and LOCALGROUP_MEMBER_INFO_3
instead of LOCALGROUP_MEMBER_INFO_1
, which is a simpler API.
#[cfg(windows)]
fn create_account_win32<S: AsRef<str> + std::fmt::Display>(
user_name: S,
full_name: S,
password: S,
group: &str,
) -> anyhow::Result<()> {
use windows::Win32::NetworkManagement::NetManagement::{
NetUserAdd, UF_NORMAL_ACCOUNT, UF_SCRIPT, USER_ACCOUNT_FLAGS, USER_INFO_2, USER_PRIV_USER,
};
// use level 2 i.e. USER_INFO_2 in NetUserAdd
// https://learn.microsoft.com/en-us/windows/win32/api/lmaccess/ns-lmaccess-user_info_2
let mut user = USER_INFO_2::default();
let (user_name_w, _v1) = str_to_pwstr(user_name.as_ref());
user.usri2_name = user_name_w;
let (password_w, _v2) = str_to_pwstr(password.as_ref());
user.usri2_password = password_w;
let (full_name_w, _v3) = str_to_pwstr(full_name.as_ref());
user.usri2_full_name = full_name_w;
user.usri2_priv = USER_PRIV_USER;
user.usri2_flags = UF_SCRIPT | USER_ACCOUNT_FLAGS(UF_NORMAL_ACCOUNT);
user.usri2_acct_expires = u32::MAX;
tracing::debug!("Creating user {user:?}");
let status: u32 = unsafe { NetUserAdd(None, 2, &user as *const _ as _, None) };
if status == 0 {
println!("✓ Successfully created account.");
} else {
eprintln!("⛌ Failed to create account!");
}
// Now add user to a localgroup. This group must already exist in the system.
manage_group_users_win32(user_name.as_ref(), group, false)?;
Ok(())
}
/// Add user to a specified local group.
/// If the `deletion` param is `true` - delete the specified user account from the local
/// group.
#[cfg(windows)]
unsafe fn manage_group_users_win32(
username: &str,
groupname: &str,
deletion: bool,
) -> anyhow::Result<()> {
use windows::Win32::NetworkManagement::NetManagement::{
NetLocalGroupAddMembers, NetLocalGroupDelMembers, LOCALGROUP_MEMBERS_INFO_3,
};
let (wide_groupname_nul, _buf) = str_to_pwstr(groupname);
let (wide_username_nul, _buf) = str_to_pwstr(username);
tracing::debug!("Mangaing groups of user {username}: group={groupname} , delete={deletion}.");
let group_members = LOCALGROUP_MEMBERS_INFO_3 {
lgrmi3_domainandname: wide_username_nul,
};
let rc = if deletion {
NetLocalGroupDelMembers(
None,
wide_groupname_nul,
3,
&group_members as *const _ as *const u8,
1,
)
} else {
NetLocalGroupAddMembers(
None,
wide_groupname_nul,
3,
&group_members as *const _ as *const u8,
1,
)
};
if rc != 0 {
anyhow::bail!("Failed to manage user group. Error {rc}.");
}
Ok(())
}
/// Convert &str into PWSTR (null terminated).
pub fn str_to_pwstr(s: &str) -> (PWSTR, Vec<u16>) {
let mut encoded = s.encode_utf16().chain([0u16]).collect::<Vec<u16>>();
(PWSTR(encoded.as_mut_ptr()), encoded)
}
Upvotes: 0
Reputation: 60472
You're sending dangling pointers.
Your winstr(...).as_mut_ptr()
calls create a Vec<u16>
, gets a pointer to its data, and drops the Vec<u16>
since it was a temporary value. You need to keep those values at least until the call to NetUserAdd
has finished:
let mut username = winstr("Test");
let mut password = winstr("Test******");
let mut user = USER_INFO_1{
usri1_name: username.as_mut_ptr(),
usri1_password: password.as_mut_ptr(),
usri1_priv: 1,
usri1_password_age: 0,
usri1_home_dir: std::ptr::null_mut(),
usri1_comment: std::ptr::null_mut(),
usri1_flags: UF_SCRIPT,
usri1_script_path: std::ptr::null_mut(),
};
Upvotes: 4