auth.rs
// Copyright 2016 Pierre-Étienne Meunier
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
use cryptovec::CryptoVec;
use std::sync::Arc;
use thrussh_keys::encoding;
use thrussh_keys::key;
use tokio::io::{AsyncRead, AsyncWrite};
bitflags! {
/// Set of methods, represented by bit flags.
pub struct MethodSet: u32 {
/// The SSH `none` method (no authentication).
const NONE = 1;
/// The SSH `password` method (plaintext passwords).
const PASSWORD = 2;
/// The SSH `publickey` method (sign a challenge sent by the
/// server).
const PUBLICKEY = 4;
/// The SSH `hostbased` method (certain hostnames are allowed
/// by the server).
const HOSTBASED = 8;
/// The SSH `keyboard-interactive` method (answer to a
/// challenge, where the "challenge" can be a password prompt,
/// a bytestring to sign with a smartcard, or something else).
const KEYBOARD_INTERACTIVE = 16;
}
}
macro_rules! iter {
( $y:expr, $x:expr ) => {{
if $y.contains($x) {
$y.remove($x);
return Some($x);
}
}};
}
impl Iterator for MethodSet {
type Item = MethodSet;
fn next(&mut self) -> Option<MethodSet> {
iter!(self, MethodSet::NONE);
iter!(self, MethodSet::PASSWORD);
iter!(self, MethodSet::PUBLICKEY);
iter!(self, MethodSet::HOSTBASED);
iter!(self, MethodSet::KEYBOARD_INTERACTIVE);
None
}
}
pub trait Signer: Sized {
type Error: From<crate::SendError>;
type Future: futures::Future<Output = (Self, Result<CryptoVec, Self::Error>)> + Send;
fn auth_publickey_sign(self, key: &key::PublicKey, to_sign: CryptoVec) -> Self::Future;
}
#[derive(Debug, Error)]
pub enum AgentAuthError {
#[error(transparent)]
Send(#[from] crate::SendError),
#[error(transparent)]
Key(#[from] thrussh_keys::Error),
}
impl<R: AsyncRead + AsyncWrite + Unpin + Send + 'static> Signer
for thrussh_keys::agent::client::AgentClient<R>
{
type Error = AgentAuthError;
type Future = std::pin::Pin<
Box<dyn futures::Future<Output = (Self, Result<CryptoVec, Self::Error>)> + Send>,
>;
fn auth_publickey_sign(self, key: &key::PublicKey, to_sign: CryptoVec) -> Self::Future {
let fut = self.sign_request(key, to_sign);
futures::FutureExt::boxed(async move {
let (a, b) = fut.await;
(a, b.map_err(AgentAuthError::Key))
})
}
}
#[derive(Debug)]
pub enum Method {
// None,
Password { password: String },
PublicKey { key: Arc<key::KeyPair> },
FuturePublicKey { key: key::PublicKey },
// Hostbased,
}
impl encoding::Bytes for MethodSet {
fn bytes(&self) -> &'static [u8] {
match *self {
MethodSet::NONE => b"none",
MethodSet::PASSWORD => b"password",
MethodSet::PUBLICKEY => b"publickey",
MethodSet::HOSTBASED => b"hostbased",
MethodSet::KEYBOARD_INTERACTIVE => b"keyboard-interactive",
_ => b"",
}
}
}
impl MethodSet {
pub(crate) fn from_bytes(b: &[u8]) -> Option<MethodSet> {
match b {
b"none" => Some(MethodSet::NONE),
b"password" => Some(MethodSet::PASSWORD),
b"publickey" => Some(MethodSet::PUBLICKEY),
b"hostbased" => Some(MethodSet::HOSTBASED),
b"keyboard-interactive" => Some(MethodSet::KEYBOARD_INTERACTIVE),
_ => None,
}
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct AuthRequest {
pub methods: MethodSet,
pub partial_success: bool,
pub current: Option<CurrentRequest>,
pub rejection_count: usize,
}
#[doc(hidden)]
#[derive(Debug)]
pub enum CurrentRequest {
PublicKey {
key: CryptoVec,
algo: CryptoVec,
sent_pk_ok: bool,
},
KeyboardInteractive {
submethods: String,
},
}