Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs, bump version to 0.3.0 #10

Merged
merged 1 commit into from
Aug 13, 2023
Merged
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pam-ssh-agent"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/nresare/pam-ssh-agent"
Expand Down
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,47 @@ remote `ssh-agent`.
One scenario that this module can be used in is to grant escalated privileges on a remote
system with the `sudo` command where the identity of the user is confirmed by their ability
to provide a signature made with a local ssh-agent and a private key that never leaves the
designated hardware.
designated hardware. I use the [Secretive](https://github.com/maxgoedjen/secretive) app on
macOS for this purpose.

This project is re-implementation of the [pam_ssh_agent_auth](https://github.com/jbeverly/pam_ssh_agent_auth)
module but does not share any code with that project. The eventual goal of this module is to be
functionally equivalent and a drop-in replacement instead of pam_ssh_agent_auth.
functionally equivalent and a drop-in replacement for `pam_ssh_agent_auth`.

This project is currently in a usable state, and has been tested with Ubuntu 22.04. As of now,
the path expansion patterns that pam_ssh_agent_auth provides are not implemented. In other
words a single authorized_keys file is expected to be used.

## Project goals

Since this is security sensitive software and a bug could easily result in undue privilege
escalation, the main goal of this project is to be robust and easy to follow for would-be
reviewers.

The implementation leans heavily on modules available in the Rust ecosystem that implements
The implementation leans heavily on crates available in the Rust ecosystem that implements
the different parts needed for the overall functionality, most notably the pam, ssh-key,
and ssh-agent-client-rs crates. Using upstream libraries directly is intended to make it
easier to ensure that implementation issues with security implication gets addressed in a
timely manner. A secondary benefit is that supporting a wide range of algorithms is easier.

## Current state
timely manner. A secondary benefit is that it is easier to support a wide range of algorithms.

The pam module integration is in a very rough state, but at least it can be used as a proof
of concept. To try it out, these are the rough steps:
## Usage

* use `debuild -b` to build a `.deb` package with the shared object and install it with `dpkg`
* install `doas`, to ensure that you have a differnt way of elevating your privileges. You will
need to add a `permit` line in /etc/doas.conf for it to work
* Replace the common-auth include in `/etc/pam.d/sudo` with `auth required pam_ssh_agent.so`
* install `doas`, to ensure that you have a different way of elevating your privileges than sudo.
You will need to add a `permit` line in `/etc/doas.conf` for it to work
* Replace the `common-auth` include in `/etc/pam.d/sudo` with `auth required pam_ssh_agent.so`
* Configure `sudo` to not drop the `SSH_AUTH_SOCK` environment variable by
adding `Defaults env_keep += "SSH_AUTH_SOCK` to the file `/etc/sudoers.d/ssh_agent_env`
* Add the public key from you to `/etc/sudo_ssh_keys`
* Add the public key that your ssh-agent knows about to `/etc/security/authorized_keys`

## Configuration options

PAM modules can be configured using space separated options after `pam_ssh_agent.so` in the applicable
configuration file in `/etc/pam.d`. pam_ssh_agent currently understands the following options

* `debug` This will increase log output to the AUTHPRIV syslog facility
* `file=/file/name` This will modify the file holding the authorized public keys instead of the
default `/etc/security/authorized_keys`.

## License

Expand Down
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
pam-ssh-agent (0.3.0) UNRELEASED; urgency=medium

* 0.3.0

-- Noa Resare <[email protected]> Sun, 13 Aug 2023 12:17:15 +0000

pam-ssh-agent (0.2.0) UNRELEASED; urgency=medium

* 0.2.0
Expand Down
2 changes: 2 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use std::ffi::CStr;

/// Argument parsing.
#[derive(Debug, Eq, PartialEq)]
pub struct Args {
pub debug: bool,
pub file: String,
}

impl Args {
/// Parses args and returns an Args instance with the parsed arguments
pub fn parse(args: Vec<&CStr>) -> Self {
let mut debug = false;
let mut file: String = "/etc/security/authorized_keys".into();
Expand Down
9 changes: 7 additions & 2 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ use std::path::Path;

const CHALLENGE_SIZE: usize = 32;

/// Finds the first key, if any, that the ssh-agent knows about that is also present
/// in the file referenced by keys_file_path, sends a random message to be signed and
/// verifies the signature with the public key.
///
/// Returns Ok(true) if a key was found and the signature was correct, Ok(false) if no
/// key was found, and Err if agent communication or signature verification failed.
pub fn authenticate(
keys_file_path: &str,
mut agent: impl SSHAgent,
Expand All @@ -29,8 +35,7 @@ pub fn authenticate(

fn sign_and_verify(key: PublicKey, mut agent: impl SSHAgent) -> Result<bool> {
let mut data: [u8; CHALLENGE_SIZE] = [0_u8; CHALLENGE_SIZE];
getrandom(data.as_mut_slice()).expect("Failed to obtain random data to sign");

getrandom(data.as_mut_slice())?;
let sig = agent.sign(&key, data.as_ref())?;

key.key_data().verify(data.as_ref(), &sig)?;
Expand Down
2 changes: 1 addition & 1 deletion src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl KeyHolder {
pub(crate) fn from_file(path: &Path) -> Result<Self> {
let keys = Vec::from_iter(
AuthorizedKeys::read_file(path)
.context(format!("Failed to read from '{:?}'", path))?
.context(format!("Failed to read from {:?}", path))?
.into_iter()
.map(|e| e.public_key().key_data().to_owned()),
);
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ struct PamSshAgent;
pam::pam_hooks!(PamSshAgent);

impl PamHooks for PamSshAgent {
/// The authentication method called by pam to authenticate the user. This method
/// will return PAM_SUCCESS if the ssh-agent available through the unix socket path
/// in the PAM_AUTH_SOCK environment variable is able to correctly sign a random
/// message with the private key corresponding to one of the public keys in in
/// /etc/security/authorized_key. Otherwise this function returns PAM_AUTH_ERR.
///
/// This method logs diagnostic output to the AUTHPRIV facility.
fn sm_authenticate(
pam_handle: &mut PamHandle,
args: Vec<&CStr>,
Expand Down Expand Up @@ -68,6 +75,8 @@ fn do_authenticate(log: &mut impl Log, args: &Args) -> Result<()> {
}
}

/// Fetch the name of the current service, i.e. the software that uses pam for authentication
/// using the PamHandle::get_item() method.
fn get_service(pam_handle: &PamHandle) -> String {
let service = match pam_handle.get_item::<Service>() {
Ok(Some(service)) => service,
Expand Down