Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ pacman -S sudo-rs
```
This will offer the functionality using the commands `sudo-rs`, `sudoedit-rs`, `visudo-rs` and `su-rs` to avoid conflicts.

**Warning**: Due to a packaging issue, uninstalling sudo after installing sudo-rs will result in the deletion of `/etc/sudoers` and `/etc/pam.d/sudo`, rendering sudo-rs non-functional. To avoid this issue, either:

- Back up `/etc/sudoers` and `/etc/pam.d/sudo` before uninstalling sudo, then restore these files afterward.
- Uninstall sudo before installing sudo-rs and add back `/etc/pam.d/sudo` manually afterwards (of course, ensure you have another way to gain root access first).

The sudo-rs package on Arch Linux is typically up-to-date.

### Fedora
Expand Down
13 changes: 11 additions & 2 deletions src/common/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ pub enum Error {
Authorization(String),
InteractionRequired,
EnvironmentVar(Vec<String>),
Configuration(String),
Configuration {
message: String,
path: Option<PathBuf>,
},
Options(String),
Pam(PamError),
Io(Option<PathBuf>, std::io::Error),
Expand Down Expand Up @@ -78,7 +81,13 @@ impl fmt::Display for Error {
}
Ok(())
}
Error::Configuration(e) => write!(f, "invalid configuration: {e}"),
Error::Configuration { message, path } => {
if let Some(path) = path {
write!(f, "invalid configuration at {}: {message}", path.display())
} else {
write!(f, "invalid configuration: {message}")
}
}
Error::Options(e) => write!(f, "{e}"),
Error::Pam(e) => write!(f, "{e}"),
Error::Io(location, e) => {
Expand Down
53 changes: 49 additions & 4 deletions src/sudo/pam.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,53 @@
use std::ffi::OsString;
use std::{ffi::OsString, path::PathBuf};

use crate::common::context::LaunchType;
use crate::common::error::Error;
use crate::log::{dev_info, user_warn};
use crate::pam::{PamContext, PamError, PamErrorType, PamResult};
use crate::pam::{PamContext, PamError, PamErrorType};
use crate::system::{term::current_tty_name, time::Duration};

#[cfg(target_os = "freebsd")]
const PAM_SERVICE_DIRS: &[&str] = &["/usr/local/etc/pam.d", "/etc/pam.d"];

#[cfg(not(target_os = "freebsd"))]
const PAM_SERVICE_DIRS: &[&str] = &["/etc/pam.d"];

fn pam_service_candidates(service_name: &str) -> Vec<PathBuf> {
PAM_SERVICE_DIRS
.iter()
.map(|dir| PathBuf::from(dir).join(service_name))
.collect()
}

fn ensure_pam_service_config(service_name: &str) -> Result<(), Error> {
let candidates = pam_service_candidates(service_name);

if candidates.iter().any(|path| path.exists()) {
return Ok(());
}

let message = if candidates.len() == 1 {
format!(
"PAM configuration for service '{service_name}' not found at {}; This file is needed to run sudo-rs. Uninstalling sudo after installing sudo-rs might cause this problem on some distributions like arch linux due to a packaging issue. See https://github.com/trifectatechfoundation/sudo-rs/issues/1182",
candidates[0].display()
)
} else {
let searched = candidates
.iter()
.map(|path| path.display().to_string())
.collect::<Vec<_>>()
.join(", ");
format!(
"PAM configuration for service '{service_name}' not found in any of: {searched}; This file is needed to run sudo-rs. Uninstalling sudo after installing sudo-rs might cause this problem on some distributions like arch linux due to a packaging issue. See https://github.com/trifectatechfoundation/sudo-rs/issues/1182"
)
};

Err(Error::Configuration {
message,
path: candidates.first().cloned(),
})
}

pub(super) struct InitPamArgs<'a> {
pub(super) launch: LaunchType,
pub(super) use_stdin: bool,
Expand Down Expand Up @@ -34,11 +76,13 @@ pub(super) fn init_pam(
target_user,
hostname,
}: InitPamArgs,
) -> PamResult<PamContext> {
) -> Result<PamContext, Error> {
let service_name = match launch {
LaunchType::Login if cfg!(feature = "pam-login") => "sudo-i",
LaunchType::Login | LaunchType::Shell | LaunchType::Direct => "sudo",
};

ensure_pam_service_config(service_name)?;
let mut pam = PamContext::new_cli(
"sudo",
service_name,
Expand All @@ -48,7 +92,8 @@ pub(super) fn init_pam(
password_feedback,
password_timeout,
Some(auth_user),
)?;
)
.map_err(Error::from)?;
pam.mark_silent(matches!(launch, LaunchType::Direct));
pam.mark_allow_null_auth_token(false);
pam.set_requesting_user(requesting_user)?;
Expand Down
20 changes: 15 additions & 5 deletions src/sudo/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,28 @@ mod edit;
pub(super) use edit::run_edit;

fn read_sudoers() -> Result<Sudoers, Error> {
let sudoers_path = &super::candidate_sudoers_file();

let (sudoers, syntax_errors) =
Sudoers::open(sudoers_path).map_err(|e| Error::Configuration(format!("{e}")))?;
let sudoers_path = super::candidate_sudoers_file();

let (sudoers, syntax_errors) = Sudoers::open(&sudoers_path).map_err(|e| {
let mut message = e.to_string();
if e.kind() == std::io::ErrorKind::NotFound {
message = format!(
"{message}; sudo-rs looks for /etc/sudoers-rs first and falls back to /etc/sudoers. Create one of these files before running sudo-rs. Uninstalling sudo after installing sudo-rs might cause this problem on some distributions like arch linux due to a packaging issue."
);
}
Error::Configuration {
message,
path: Some(sudoers_path.clone()),
}
})?;

for crate::sudoers::Error {
source,
location,
message,
} in syntax_errors
{
let path = source.as_deref().unwrap_or(sudoers_path);
let path = source.as_deref().unwrap_or(&sudoers_path);
diagnostic::diagnostic!("{message}", path @ location);
}

Expand Down
Loading