aboutsummaryrefslogtreecommitdiffhomepage
path: root/oxish/src
diff options
context:
space:
mode:
Diffstat (limited to 'oxish/src')
-rw-r--r--oxish/src/authentication.rs153
1 files changed, 77 insertions, 76 deletions
diff --git a/oxish/src/authentication.rs b/oxish/src/authentication.rs
index da64a36..ff238eb 100644
--- a/oxish/src/authentication.rs
+++ b/oxish/src/authentication.rs
@@ -19,7 +19,7 @@ use std::{
fd::{AsRawFd, FromRawFd},
unix::ffi::OsStrExt,
},
- path::{Path, PathBuf},
+ path::PathBuf,
str,
};
@@ -272,7 +272,7 @@ impl DefaultStore {
0 => Box::new(SystemStore) as Box<dyn UserStore>,
uid => {
let data = User::lookup(UserLookup::Id(uid))?;
- let keys = authorized_keys(&data.home_dir, data.id, provider);
+ let keys = data.authorized_keys(provider);
Box::new(SingleUser(CachedUser { data, keys }))
}
})
@@ -294,7 +294,7 @@ impl UserStore for SystemStore {
}
fn keys(&self, user: &User, provider: &dyn CryptoProvider) -> Vec<AuthorizedKey> {
- authorized_keys(&user.home_dir, user.id, provider)
+ user.authorized_keys(provider)
}
fn drop_privileges(&self) -> bool {
@@ -468,6 +468,80 @@ impl User {
})
}
+ /// Read and parse the `authorized_keys` file for a user
+ ///
+ /// This is pretty finicky because we need to check that
+ ///
+ /// - None of the path components have group or other write permissions
+ /// - Each of the path components are owned by root or the target user
+ /// - Avoid TOCTOU issues when opening each path component
+ fn authorized_keys(&self, provider: &dyn CryptoProvider) -> Vec<AuthorizedKey> {
+ let home_dir = &self.home_dir;
+ let home = match File::open(home_dir) {
+ Ok(file) => file,
+ Err(error) => {
+ warn!(%error, ?home_dir, "failed to open home directory");
+ return Vec::new();
+ }
+ };
+
+ match check_permissions(&home, self.id, "home directory") {
+ ControlFlow::Continue(()) => {}
+ ControlFlow::Break(()) => {
+ warn!(?home_dir, "bad permissions on home directory");
+ return Vec::new();
+ }
+ };
+
+ let ssh_dir = match open_in_dir(&home, ".ssh", O_DIRECTORY) {
+ Ok(file) => file,
+ Err(error) => {
+ warn!(%error, ?home_dir, "failed to open .ssh directory");
+ return Vec::new();
+ }
+ };
+
+ match check_permissions(&ssh_dir, self.id, ".ssh directory") {
+ ControlFlow::Continue(()) => {}
+ ControlFlow::Break(()) => {
+ warn!(?home_dir, "bad permissions on .ssh directory");
+ return Vec::new();
+ }
+ };
+
+ let mut key_file = match open_in_dir(&ssh_dir, "authorized_keys", O_RDONLY) {
+ Ok(file) => file,
+ Err(error) => {
+ warn!(%error, ?home_dir, "failed to open authorized keys file");
+ return Vec::new();
+ }
+ };
+
+ match check_permissions(&key_file, self.id, "authorized keys file") {
+ ControlFlow::Continue(()) => {}
+ ControlFlow::Break(()) => {
+ warn!(?home_dir, "bad permissions on authorized keys file");
+ return Vec::new();
+ }
+ };
+
+ let mut contents = String::new();
+ if let Err(error) = key_file.read_to_string(&mut contents) {
+ warn!(%error, ?home_dir, "failed to read authorized keys file");
+ return Vec::new();
+ };
+
+ let mut keys = Vec::new();
+ for (line, key) in contents.lines().enumerate() {
+ match AuthorizedKey::from_str(key, provider) {
+ Some(key) => keys.push(key),
+ None => debug!(line = line + 1, "no valid authorized key found on line"),
+ }
+ }
+
+ keys
+ }
+
const FAKE_HOME: *const c_char = c"/var/empty".as_ptr().cast::<c_char>();
const DEFAULT_SHELL: *const c_char = c"/bin/sh".as_ptr().cast::<c_char>();
}
@@ -527,79 +601,6 @@ enum UserLookup {
Id(u32),
}
-/// Read and parse the `authorized_keys` file for a user
-///
-/// This is pretty finicky because we need to check that
-///
-/// - None of the path components have group or other write permissions
-/// - Each of the path components are owned by root or the target user
-/// - Avoid TOCTOU issues when opening each path component
-fn authorized_keys(home_dir: &Path, uid: u32, provider: &dyn CryptoProvider) -> Vec<AuthorizedKey> {
- let home = match File::open(home_dir) {
- Ok(file) => file,
- Err(error) => {
- warn!(%error, ?home_dir, "failed to open home directory");
- return Vec::new();
- }
- };
-
- match check_permissions(&home, uid, "home directory") {
- ControlFlow::Continue(()) => {}
- ControlFlow::Break(()) => {
- warn!(?home_dir, "bad permissions on home directory");
- return Vec::new();
- }
- };
-
- let ssh_dir = match open_in_dir(&home, ".ssh", O_DIRECTORY) {
- Ok(file) => file,
- Err(error) => {
- warn!(%error, ?home_dir, "failed to open .ssh directory");
- return Vec::new();
- }
- };
-
- match check_permissions(&ssh_dir, uid, ".ssh directory") {
- ControlFlow::Continue(()) => {}
- ControlFlow::Break(()) => {
- warn!(?home_dir, "bad permissions on .ssh directory");
- return Vec::new();
- }
- };
-
- let mut key_file = match open_in_dir(&ssh_dir, "authorized_keys", O_RDONLY) {
- Ok(file) => file,
- Err(error) => {
- warn!(%error, ?home_dir, "failed to open authorized keys file");
- return Vec::new();
- }
- };
-
- match check_permissions(&key_file, uid, "authorized keys file") {
- ControlFlow::Continue(()) => {}
- ControlFlow::Break(()) => {
- warn!(?home_dir, "bad permissions on authorized keys file");
- return Vec::new();
- }
- };
-
- let mut contents = String::new();
- if let Err(error) = key_file.read_to_string(&mut contents) {
- warn!(%error, ?home_dir, "failed to read authorized keys file");
- return Vec::new();
- };
-
- let mut keys = Vec::new();
- for (line, key) in contents.lines().enumerate() {
- match AuthorizedKey::from_str(key, provider) {
- Some(key) => keys.push(key),
- None => debug!(line = line + 1, "no valid authorized key found on line"),
- }
- }
-
- keys
-}
-
fn open_in_dir(dir: &File, name: &str, flags: libc::c_int) -> Result<File, io::Error> {
let c_name = CString::new(name)?;
match unsafe { libc::openat(dir.as_raw_fd(), c_name.as_ptr(), flags) } {