Expand native auth and benchmark gates
This commit is contained in:
+21
-18
@@ -10,9 +10,10 @@ use dosh::config::{expand_tilde, load_client_config, load_hosts_config, load_ser
|
||||
use dosh::crypto;
|
||||
use dosh::native::{
|
||||
EnvVar, ForwardingKind, ForwardingRequest, KnownHostStatus, TrustResult,
|
||||
derive_native_session_key, generate_native_ephemeral, host_fingerprint, load_ed25519_identity,
|
||||
load_ed25519_identity_with_passphrase, parse_host_public_key_line, remove_trusted_host,
|
||||
sign_user_auth, trust_host, verify_known_host, verify_server_hello,
|
||||
derive_native_session_key, generate_native_ephemeral, host_fingerprint, load_native_identity,
|
||||
load_native_identity_with_passphrase, parse_host_public_key_line, remove_trusted_host,
|
||||
sign_user_auth_with_private_key, supported_user_key_algorithms, trust_host, verify_known_host,
|
||||
verify_server_hello,
|
||||
};
|
||||
use dosh::protocol::{
|
||||
self, AttachOk, AttachReject, BootstrapAttachRequest, CLIENT_TO_SERVER, Frame, Input,
|
||||
@@ -1486,7 +1487,7 @@ async fn try_native_auth(
|
||||
requested_mode: mode.to_string(),
|
||||
terminal_size: (cols, rows),
|
||||
supported_aead: vec!["chacha20poly1305".to_string()],
|
||||
supported_user_key_algorithms: vec!["ssh-ed25519".to_string()],
|
||||
supported_user_key_algorithms: supported_user_key_algorithms(),
|
||||
cached_host_key_fingerprint: None,
|
||||
attach_ticket_envelope: None,
|
||||
requested_env,
|
||||
@@ -1642,7 +1643,7 @@ async fn try_native_auth_check(
|
||||
requested_mode: "doctor".to_string(),
|
||||
terminal_size: (80, 24),
|
||||
supported_aead: vec!["chacha20poly1305".to_string()],
|
||||
supported_user_key_algorithms: vec!["ssh-ed25519".to_string()],
|
||||
supported_user_key_algorithms: supported_user_key_algorithms(),
|
||||
cached_host_key_fingerprint: None,
|
||||
attach_ticket_envelope: None,
|
||||
requested_env: Vec::new(),
|
||||
@@ -1766,7 +1767,9 @@ fn sign_native_user_auth(
|
||||
}
|
||||
|
||||
match load_first_native_identity(config, cli_identity, ssh_config) {
|
||||
Ok(identity) => sign_user_auth(&identity, hello, server_hello, requested_forwardings),
|
||||
Ok(identity) => {
|
||||
sign_user_auth_with_private_key(&identity, hello, server_hello, requested_forwardings)
|
||||
}
|
||||
Err(err) => {
|
||||
errors.push(format!("identity files: {err:#}"));
|
||||
Err(anyhow!(
|
||||
@@ -1781,7 +1784,7 @@ fn load_first_native_identity(
|
||||
config: &dosh::config::ClientConfig,
|
||||
cli_identity: Option<&Path>,
|
||||
ssh_config: &SshConfig,
|
||||
) -> Result<ed25519_dalek::SigningKey> {
|
||||
) -> Result<ssh_key::PrivateKey> {
|
||||
load_first_native_identity_with_prompt(
|
||||
config,
|
||||
cli_identity,
|
||||
@@ -1795,7 +1798,7 @@ fn load_first_native_identity_with_prompt<F>(
|
||||
cli_identity: Option<&Path>,
|
||||
ssh_config: &SshConfig,
|
||||
mut prompt: F,
|
||||
) -> Result<ed25519_dalek::SigningKey>
|
||||
) -> Result<ssh_key::PrivateKey>
|
||||
where
|
||||
F: FnMut(&Path) -> Result<String>,
|
||||
{
|
||||
@@ -1817,11 +1820,11 @@ where
|
||||
if !path.exists() {
|
||||
continue;
|
||||
}
|
||||
match load_ed25519_identity(&path) {
|
||||
match load_native_identity(&path) {
|
||||
Ok(identity) => return Ok(identity),
|
||||
Err(err) if err.to_string().contains(" is encrypted") => {
|
||||
match prompt(&path).and_then(|passphrase| {
|
||||
load_ed25519_identity_with_passphrase(&path, Some(&passphrase))
|
||||
load_native_identity_with_passphrase(&path, Some(&passphrase))
|
||||
}) {
|
||||
Ok(identity) => return Ok(identity),
|
||||
Err(err) => errors.push(format!("{}: {err:#}", path.display())),
|
||||
@@ -1831,10 +1834,10 @@ where
|
||||
}
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Err(anyhow!("no usable ssh-ed25519 identity found"))
|
||||
Err(anyhow!("no usable native identity found"))
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"no usable ssh-ed25519 identity found: {}",
|
||||
"no usable native identity found: {}",
|
||||
errors.join("; ")
|
||||
))
|
||||
}
|
||||
@@ -4222,8 +4225,8 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
VerifyingKey::from(&loaded).to_bytes(),
|
||||
VerifyingKey::from(&signing).to_bytes()
|
||||
loaded.public_key().key_data().ed25519().unwrap().as_ref(),
|
||||
VerifyingKey::from(&signing).as_bytes()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4243,7 +4246,7 @@ mod tests {
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(err.to_string().contains("no usable ssh-ed25519 identity"));
|
||||
assert!(err.to_string().contains("no usable native identity"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4263,7 +4266,7 @@ mod tests {
|
||||
})
|
||||
.unwrap_err();
|
||||
|
||||
assert!(err.to_string().contains("no usable ssh-ed25519 identity"));
|
||||
assert!(err.to_string().contains("no usable native identity"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4285,8 +4288,8 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
VerifyingKey::from(&loaded).to_bytes(),
|
||||
VerifyingKey::from(&signing).to_bytes()
|
||||
loaded.public_key().key_data().ed25519().unwrap().as_ref(),
|
||||
VerifyingKey::from(&signing).as_bytes()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+94
-2
@@ -878,9 +878,11 @@ async fn handle_native_client_hello(
|
||||
.hello
|
||||
.supported_user_key_algorithms
|
||||
.iter()
|
||||
.any(|algorithm| algorithm == "ssh-ed25519")
|
||||
.any(|algorithm| dosh::native::is_supported_user_signature_algorithm(algorithm))
|
||||
{
|
||||
Err(anyhow!("native auth requires ssh-ed25519"))
|
||||
Err(anyhow!(
|
||||
"native auth requires a supported user key algorithm"
|
||||
))
|
||||
} else {
|
||||
let current_user = std::env::var("USER").unwrap_or_else(|_| "unknown".to_string());
|
||||
if req.hello.requested_user != current_user {
|
||||
@@ -3266,6 +3268,96 @@ mod tests {
|
||||
assert_eq!(data.bytes, b"hello");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocked_stream_data_does_not_block_terminal_frames() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [8u8; 16];
|
||||
let session_key = [10u8; 32];
|
||||
let receiver = UdpSocket::bind("127.0.0.1:0").await.unwrap();
|
||||
let sender = Arc::new(UdpSocket::bind("127.0.0.1:0").await.unwrap());
|
||||
let endpoint = receiver.local_addr().unwrap();
|
||||
state.sessions.insert(
|
||||
"test".to_string(),
|
||||
Session {
|
||||
pty: None,
|
||||
parser: vt100::Parser::new(24, 80, 100),
|
||||
clients: HashMap::from([(
|
||||
client_id,
|
||||
ClientState {
|
||||
endpoint,
|
||||
mode: "read-write".to_string(),
|
||||
session_key,
|
||||
last_acked: 0,
|
||||
replay: ReplayWindow::default(),
|
||||
send_seq: 1,
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
last_seen: Instant::now(),
|
||||
pending: VecDeque::new(),
|
||||
allowed_forwardings: Vec::new(),
|
||||
stream_writers: HashMap::new(),
|
||||
opened_streams: HashSet::from([42]),
|
||||
stream_send_credit: HashMap::from([(42, 0)]),
|
||||
stream_pending_data: HashMap::new(),
|
||||
epoch: 0,
|
||||
epoch_started: Instant::now(),
|
||||
epoch_packets: 0,
|
||||
previous_session_key: None,
|
||||
},
|
||||
)]),
|
||||
output_seq: 0,
|
||||
recent: VecDeque::new(),
|
||||
empty_since: None,
|
||||
holder_control: None,
|
||||
persistent: false,
|
||||
bytes_since_persist: 0,
|
||||
last_persisted_seq: 0,
|
||||
},
|
||||
);
|
||||
state.client_index.insert(client_id, "test".to_string());
|
||||
let state = Arc::new(Mutex::new(state));
|
||||
|
||||
queue_or_send_stream_data_to_client(
|
||||
&state,
|
||||
&sender,
|
||||
client_id,
|
||||
42,
|
||||
b"blocked-forward-bytes".to_vec(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
let locked = state.lock().unwrap();
|
||||
let client = locked.sessions["test"].clients.get(&client_id).unwrap();
|
||||
assert_eq!(client.stream_pending_data[&42].len(), 1);
|
||||
assert_eq!(client.stream_send_credit[&42], 0);
|
||||
}
|
||||
|
||||
broadcast_output(
|
||||
&state,
|
||||
&sender,
|
||||
PtyOutput {
|
||||
session: "test".to_string(),
|
||||
bytes: b"terminal-priority".to_vec(),
|
||||
exited: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut buf = [0u8; 2048];
|
||||
let (n, _) = tokio::time::timeout(Duration::from_secs(1), receiver.recv_from(&mut buf))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let packet = protocol::decode(&buf[..n]).unwrap();
|
||||
assert_eq!(packet.header.kind, PacketKind::Frame);
|
||||
let plain = protocol::decrypt_body(&packet, &session_key, SERVER_TO_CLIENT).unwrap();
|
||||
let frame: Frame = protocol::from_body(&plain).unwrap();
|
||||
assert_eq!(frame.bytes, b"terminal-priority");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_non_loopback_bind_requires_explicit_config() {
|
||||
let config = ServerConfig::default();
|
||||
|
||||
Reference in New Issue
Block a user