Open chipsenkbeil opened 1 year ago
NOTE: The pam-client and other crate that use the pam sys crate do NOT support OpenPAM. We'd have to write our own bindings!
For windows, could use powershell to do this as well: https://serverfault.com/questions/596602/powershell-test-user-credentials-in-ad-with-password-reset
For windows using the LogonUserW function:
use std::ptr;
use windows::{ErrorCode, Result};
use windows::win32::security::LogonUserW;
use windows::win32::windows_programming::CloseHandle;
fn authenticate_user(username: &str, password: &str) -> Result<bool> {
let mut token_handle = ptr::null_mut();
// NOTE: If we provide NULL to the domain, the username is expected to be in UPN
// format, which looks like User@DNSDomainName
//
// We can instead provide a domain of "." for a local account
let success = unsafe {
LogonUserW(
windows::wstring::from_str(username)?.as_ptr(),
ptr::null_mut(),
windows::wstring::from_str(password)?.as_ptr(),
LogonUserW::LOGON32_LOGON_NETWORK,
LogonUserW::LOGON32_PROVIDER_DEFAULT,
&mut token_handle,
).is_ok()
};
let result = success.map_err(|error| error.code())?;
if success {
// authentication succeeded, do something with the token handle
// (e.g. use it to impersonate the authenticated user)
unsafe { CloseHandle(token_handle) };
}
Ok(result)
}
For MacOS, we can use the security-framework crate:
use security_framework::{
auth::*, base::SecKeychainItem, import::SecImportOptions, item::SecItem,
};
use std::os::raw::c_void;
fn authenticate(username: &str, password: &str) -> Result<(), String> {
let auth_context = AuthContext::new()?;
let auth_context = auth_context.set_option(AuthOption::UserName(username))?;
let auth_context = auth_context.set_option(AuthOption::Password(password))?;
let result = auth_context.evaluate()?;
if result == AuthResult::Success {
Ok(())
} else {
Err(format!("Authentication failed with result: {:?}", result))
}
}
The security-framework example isn't real (chatGPT failed me). I did figure out a way to do this from the commandline that works for a non-root user is dscl . -authonly {username} {password}
. Providing this on the commandline is bad because it'll have the password visible and in the history! You can provide it without the password, but it doesn't appear that I can feed in the password over stdin?
Note that dscl /Login/Default -authonly {username} {password}
also works and appears to be what .
defaults to on my machine.
I've now written https://crates.io/crates/pwcheck to do basic password authentication that leverages su
for Unix systems including MacOS, and the LogonUserW
via the windows
crate for Windows.
It's a bit of a hack and may not be the best in terms of security, but it does work on all three platforms: Linux, MacOS, and Windows. It's untested on FreeBSD, NetBSD, and termux.
I'd like to move the authentication methods (outside of none) to a new crate, distant-auth
, which will provide the methods we can use as features. This way, the static key can always be available and we can provide password
as a feature to use the pwcheck
crate.
Once pam
is available, that can be made as a feature as well.
auth_userokay
LogonUserA