Skip to content

Commit

Permalink
Implement targetpw
Browse files Browse the repository at this point in the history
  • Loading branch information
bjorn3 committed Mar 3, 2025
1 parent 5478630 commit 76ab7af
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/common/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ impl AuthUser {
User::from_uid(UserId::ROOT)?.ok_or(Error::UserNotFound("root".to_string()))?,
))
}

pub fn from_user_for_targetpw(user: User) -> Self {
Self(user)
}
}

impl ops::Deref for AuthUser {
Expand Down
1 change: 1 addition & 0 deletions src/defaults/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ defaults! {
pwfeedback = false
env_editor = true
rootpw = false
targetpw = false

passwd_tries = 3 [0..=1000]

Expand Down
3 changes: 3 additions & 0 deletions src/sudo/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ impl<Auth: AuthPlugin> Pipeline<Auth> {
AuthUser::from_current_user(context.current_user.clone())
}
AuthenticatingUser::Root => AuthUser::resolve_root_for_rootpw()?,
AuthenticatingUser::TargetUser => {
AuthUser::from_user_for_targetpw(context.target_user.clone())
}
};

self.authenticator.init(context, auth_user)?;
Expand Down
5 changes: 4 additions & 1 deletion src/sudoers/policy.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::Sudoers;

use super::Judgement;
use crate::common::{SudoPath, HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1};
use crate::common::{SudoPath, HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2};
use crate::system::{time::Duration, Hostname, User};
/// Data types and traits that represent what the "terms and conditions" are after a succesful
/// permission check.
Expand Down Expand Up @@ -37,6 +37,8 @@ impl super::Settings {
pwfeedback: self.pwfeedback(),
credential: if self.rootpw() {
AuthenticatingUser::Root
} else if self.targetpw() {
AuthenticatingUser::TargetUser
} else {
AuthenticatingUser::InvokingUser
},
Expand Down Expand Up @@ -68,6 +70,7 @@ pub enum DirChange<'a> {
pub enum AuthenticatingUser {
InvokingUser = HARDENED_ENUM_VALUE_0,
Root = HARDENED_ENUM_VALUE_1,
TargetUser = HARDENED_ENUM_VALUE_2,
}

impl Judgement {
Expand Down
86 changes: 86 additions & 0 deletions test-framework/sudo-compliance-tests/src/sudo/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,89 @@ fn rootpw_option_doesnt_affect_authorization() -> Result<()> {

Ok(())
}

#[test]
fn targetpw_option_works() -> Result<()> {
const PASSWORD: &str = "passw0rd";
const PASSWORD2: &str = "notr00t";

let env = Env(format!(
"Defaults targetpw\nDefaults passwd_tries=1\n{USERNAME} ALL=(ALL:ALL) ALL"
))
.user(User(USERNAME).password(PASSWORD))
.user(User("user2").password(PASSWORD2))
.build()?;

// User password is not accepted when targetpw is enabled
let output = Command::new("sh")
.arg("-c")
.arg(format!("echo {PASSWORD} | sudo -S -u user2 true"))
.as_user(USERNAME)
.output(&env)?;
assert!(!output.status().success());

// Target user password is accepted when targetpw is enabled
let output = Command::new("sh")
.arg("-c")
.arg(format!("echo {PASSWORD2} | sudo -S -u user2 true"))
.as_user(USERNAME)
.output(&env)?;
assert!(output.status().success());

Ok(())
}

#[test]
fn targetpw_option_doesnt_affect_authorization() -> Result<()> {
const PASSWORD: &str = "passw0rd";
const PASSWORD2: &str = "notr00t";

let env = Env(format!("Defaults targetpw\nuser2 ALL=(ALL:ALL) ALL"))
.user(User(USERNAME).password(PASSWORD))
.user(User("user2").password(PASSWORD2))
.build()?;

// Even though we accept the target user password when targetpw is enabled,
// we still check that the actual invoking user is authorized to run the command.
let output = Command::new("sh")
.arg("-c")
.arg(format!("echo {PASSWORD2} | sudo -S -u user2 true"))
.as_user(USERNAME)
.output(&env)?;
assert!(!output.status().success());

Ok(())
}

#[test]
fn rootpw_takes_priority_over_targetpw() -> Result<()> {
const PASSWORD: &str = "passw0rd";
const PASSWORD2: &str = "notr00t";
const ROOT_PASSWORD: &str = "r00t";

let env = Env(format!(
"Defaults rootpw, targetpw\nDefaults passwd_tries=1\n{USERNAME} ALL=(ALL:ALL) ALL"
))
.user_password("root", ROOT_PASSWORD)
.user(User(USERNAME).password(PASSWORD))
.user(User("user2").password(PASSWORD2))
.build()?;

// Root password is accepted when targetpw is enabled
let output = Command::new("sh")
.arg("-c")
.arg(format!("echo {ROOT_PASSWORD} | sudo -S -u user2 true"))
.as_user(USERNAME)
.output(&env)?;
assert!(output.status().success());

// Target user password is not accepted when targetpw is enabled
let output = Command::new("sh")
.arg("-c")
.arg(format!("echo {PASSWORD2} | sudo -S -u user2 true"))
.as_user(USERNAME)
.output(&env)?;
assert!(!output.status().success());

Ok(())
}

0 comments on commit 76ab7af

Please sign in to comment.