Skip to content

Commit

Permalink
Fix deserialization of unknown extensions
Browse files Browse the repository at this point in the history
OpenSSH 8.9 ssh client started sending EXTENSION commands. Unfortunately
ssh-agent.rs didn't deserialize them correctly and even though these
extensions were optional the agent didn't work.

This patch adjusts the deserialization of extension contents to properly
capture all bytes and includes a test code observed in the wild.

Fixes #30.
  • Loading branch information
wiktor-k committed Mar 18, 2022
1 parent 5a5dfa1 commit c1616f9
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 5 deletions.
6 changes: 4 additions & 2 deletions src/proto/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ pub fn from_bytes<'a, T: Deserialize<'a>>(bytes: &[u8]) -> ProtoResult<T> {
impl<'de, 'a, R: io::Read> de::Deserializer<'de> for &'a mut Deserializer<R> {
type Error = ProtoError;

fn deserialize_any<V: Visitor<'de>>(self, _visitor: V) -> ProtoResult<V::Value> {
unimplemented!()
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> ProtoResult<V::Value> {
let mut bytes = vec![];
self.reader.read_to_end(&mut bytes)?;
visitor.visit_byte_buf(bytes)
}

fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> ProtoResult<V::Value> {
Expand Down
39 changes: 37 additions & 2 deletions src/proto/message.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use serde::de::{Deserializer, Visitor};

use super::private_key::PrivateKey;

Expand Down Expand Up @@ -52,8 +53,42 @@ pub struct AddSmartcardKeyConstrained {

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Extension {
extension_type: String,
extension_contents: Vec<u8>
pub extension_type: String,
pub extension_contents: ExtensionContents,
}

#[derive(Debug, PartialEq, Clone)]
pub struct ExtensionContents(pub Vec<u8>);

impl Serialize for ExtensionContents {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer {
serializer.serialize_bytes(&self.0)
}
}

impl<'de> Deserialize<'de> for ExtensionContents {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ContentsVisitor;

impl<'de> Visitor<'de> for ContentsVisitor {
type Value = ExtensionContents;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("raw bytes buffer")
}

fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E> {
Ok(ExtensionContents(v))
}
}

deserializer.deserialize_any(ContentsVisitor)
}
}

pub type Passphrase = String;
Expand Down
9 changes: 8 additions & 1 deletion src/proto/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{to_bytes, from_bytes, Blob};
use super::public_key::*;
use super::private_key::*;
use super::message::{Message, SignRequest, Identity};
use super::message::{Message, SignRequest, Identity, Extension};
use super::signature::Signature;

#[test]
Expand Down Expand Up @@ -163,3 +163,10 @@ fn raw_protocol_test() {

assert_eq!(response_bytes.as_slice(), &include_bytes!("id_ans_response.bin")[..]);
}

#[test]
fn test_extension() {
let extension_bytes: &[u8] = &[0, 0, 0, 24, 115, 101, 115, 115, 105, 111, 110, 45, 98, 105, 110, 100, 64, 111, 112, 101, 110, 115, 115, 104, 46, 99, 111, 109, 0, 0, 0, 51, 0, 0, 0, 11, 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 32, 177, 185, 198, 92, 165, 45, 127, 95, 202, 195, 226, 63, 6, 115, 10, 104, 18, 137, 172, 240, 153, 154, 174, 74, 83, 7, 1, 204, 14, 177, 153, 40, 0, 0, 0, 32, 175, 96, 42, 133, 218, 171, 58, 220, 97, 78, 155, 114, 20, 67, 90, 133, 24, 185, 156, 71, 128, 163, 234, 195, 202, 15, 160, 177, 130, 229, 114, 164, 0, 0, 0, 83, 0, 0, 0, 11, 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 64, 4, 235, 93, 135, 144, 110, 220, 24, 17, 150, 40, 11, 143, 37, 207, 58, 215, 159, 23, 233, 95, 218, 115, 22, 205, 101, 55, 159, 146, 42, 121, 190, 229, 82, 75, 174, 143, 199, 121, 141, 52, 155, 73, 215, 119, 220, 104, 241, 116, 83, 96, 129, 184, 12, 93, 93, 33, 243, 171, 236, 201, 123, 17, 1, 0];
let extension: Extension = from_bytes(&extension_bytes).unwrap();
assert_eq!(extension.extension_type, "session-bind@openssh.com");
}

0 comments on commit c1616f9

Please sign in to comment.