From a228a229186bbfaef56346c2dbd904c5768bced0 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Tue, 13 Jun 2023 09:23:18 +0200 Subject: [PATCH] auth-api: set PAM_RHOST during pam authentication Signed-off-by: Wolfgang Bumiller --- proxmox-auth-api/src/api/access.rs | 10 +++--- proxmox-auth-api/src/api/mod.rs | 9 +++++- proxmox-auth-api/src/pam_authenticator.rs | 39 ++++++++++++++++++++--- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/proxmox-auth-api/src/api/access.rs b/proxmox-auth-api/src/api/access.rs index 9d87ddd6..428d22a8 100644 --- a/proxmox-auth-api/src/api/access.rs +++ b/proxmox-auth-api/src/api/access.rs @@ -3,6 +3,7 @@ use anyhow::{bail, format_err, Error}; use serde_json::{json, Value}; +use proxmox_rest_server::RestEnvironment; use proxmox_router::{http_err, Permission, RpcEnvironment}; use proxmox_schema::{api, api_types::PASSWORD_SCHEMA}; use proxmox_tfa::api::TfaChallenge; @@ -90,14 +91,12 @@ pub async fn create_ticket( tfa_challenge: Option, rpcenv: &mut dyn RpcEnvironment, ) -> Result { - use proxmox_rest_server::RestEnvironment; - let env: &RestEnvironment = rpcenv .as_any() .downcast_ref::() .ok_or_else(|| format_err!("detected wrong RpcEnvironment type"))?; - match authenticate_user(&username, &password, path, privs, port, tfa_challenge).await { + match authenticate_user(&username, &password, path, privs, port, tfa_challenge, env).await { Ok(AuthResult::Success) => Ok(json!({ "username": username })), Ok(AuthResult::CreateTicket) => { let auth_context = auth_context()?; @@ -139,6 +138,7 @@ async fn authenticate_user( privs: Option, port: Option, tfa_challenge: Option, + rpcenv: &RestEnvironment, ) -> Result { let auth_context = auth_context()?; let prefix = auth_context.auth_prefix(); @@ -170,12 +170,14 @@ async fn authenticate_user( } } + let client_ip = rpcenv.get_client_ip().map(|sa| sa.ip()); + #[allow(clippy::let_unit_value)] { let _: () = auth_context .lookup_realm(userid.realm()) .ok_or_else(|| format_err!("unknown realm {:?}", userid.realm().as_str()))? - .authenticate_user(userid.name(), password) + .authenticate_user(userid.name(), password, client_ip.as_ref()) .await?; } diff --git a/proxmox-auth-api/src/api/mod.rs b/proxmox-auth-api/src/api/mod.rs index fbcf69c7..129462ff 100644 --- a/proxmox-auth-api/src/api/mod.rs +++ b/proxmox-auth-api/src/api/mod.rs @@ -1,4 +1,5 @@ use std::future::Future; +use std::net::IpAddr; use std::pin::Pin; use std::sync::Mutex; @@ -27,10 +28,16 @@ pub trait Authenticator { &'a self, username: &'a UsernameRef, password: &'a str, + client_ip: Option<&'a IpAddr>, ) -> Pin> + Send + 'a>>; /// Change a user's password. - fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error>; + fn store_password( + &self, + username: &UsernameRef, + password: &str, + client_ip: Option<&IpAddr>, + ) -> Result<(), Error>; /// Remove a user. fn remove_password(&self, username: &UsernameRef) -> Result<(), Error>; diff --git a/proxmox-auth-api/src/pam_authenticator.rs b/proxmox-auth-api/src/pam_authenticator.rs index 745b13ef..fb8e7e7e 100644 --- a/proxmox-auth-api/src/pam_authenticator.rs +++ b/proxmox-auth-api/src/pam_authenticator.rs @@ -1,9 +1,12 @@ -use std::ffi::{c_int, c_void, CStr}; +use std::ffi::{c_int, c_void, CStr, CString}; use std::future::Future; +use std::net::IpAddr; use std::pin::Pin; -use anyhow::{bail, Error}; -use pam_sys::types::{PamHandle, PamMessage, PamMessageStyle, PamResponse, PamReturnCode}; +use anyhow::{bail, format_err, Error}; +use pam_sys::types::{ + PamHandle, PamItemType, PamMessage, PamMessageStyle, PamResponse, PamReturnCode, +}; use crate::types::UsernameRef; @@ -23,6 +26,7 @@ impl crate::api::Authenticator for Pam { &'a self, username: &'a UsernameRef, password: &'a str, + client_ip: Option<&'a IpAddr>, ) -> Pin> + Send + 'a>> { Box::pin(async move { let mut password_conv = PasswordConv { @@ -46,6 +50,17 @@ impl crate::api::Authenticator for Pam { result: PamReturnCode::SUCCESS, }; + if let Some(ip) = client_ip { + let ip = ip.to_string(); + let ip = CString::new(ip).map_err(|_| format_err!("nul-byte in client ip"))?; + let ip = unsafe { &*(ip.as_ptr() as *const libc::c_void) }; + + let err = pam_sys::wrapped::set_item(handle.handle, PamItemType::RHOST, ip); + if err != PamReturnCode::SUCCESS { + bail!("error setting PAM_RHOST - {err}"); + } + } + handle.result = pam_sys::wrapped::authenticate(handle.handle, pam_sys::types::PamFlag::NONE); if handle.result != PamReturnCode::SUCCESS { @@ -56,7 +71,12 @@ impl crate::api::Authenticator for Pam { }) } - fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error> { + fn store_password( + &self, + username: &UsernameRef, + password: &str, + client_ip: Option<&IpAddr>, + ) -> Result<(), Error> { let mut password_conv = PasswordConv { login: username.as_str(), password, @@ -78,6 +98,17 @@ impl crate::api::Authenticator for Pam { result: PamReturnCode::SUCCESS, }; + if let Some(ip) = client_ip { + let ip = ip.to_string(); + let ip = CString::new(ip).map_err(|_| format_err!("nul-byte in client ip"))?; + let ip = unsafe { &*(ip.as_ptr() as *const libc::c_void) }; + + let err = pam_sys::wrapped::set_item(handle.handle, PamItemType::RHOST, ip); + if err != PamReturnCode::SUCCESS { + bail!("error setting PAM_RHOST - {err}"); + } + } + /* * we assume we're root and don't need to authenticate handle.result =