mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
logparse.pl: add verbose dumping for transport protocol.
This includes picking apart the various asymmetric crypto formats (public keys, signatures, elliptic-curve point encodings) as far as possible, but since the verbose decoder system in logparse.pl currently has to work without benefit of statefulness, it's not always possible - some of the ECC formats depend for their decoding on everyone remembering _which_ ECC protocol was negotiated by the KEXINITs.
This commit is contained in:
parent
1c4f122525
commit
ec29d35403
@ -542,13 +542,31 @@ my %packets = (
|
||||
},
|
||||
);
|
||||
|
||||
our %disc_reasons = {
|
||||
1 => "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT",
|
||||
2 => "SSH_DISCONNECT_PROTOCOL_ERROR",
|
||||
3 => "SSH_DISCONNECT_KEY_EXCHANGE_FAILED",
|
||||
4 => "SSH_DISCONNECT_RESERVED",
|
||||
5 => "SSH_DISCONNECT_MAC_ERROR",
|
||||
6 => "SSH_DISCONNECT_COMPRESSION_ERROR",
|
||||
7 => "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE",
|
||||
8 => "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED",
|
||||
9 => "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE",
|
||||
10 => "SSH_DISCONNECT_CONNECTION_LOST",
|
||||
11 => "SSH_DISCONNECT_BY_APPLICATION",
|
||||
12 => "SSH_DISCONNECT_TOO_MANY_CONNECTIONS",
|
||||
13 => "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER",
|
||||
14 => "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE",
|
||||
15 => "SSH_DISCONNECT_ILLEGAL_USER_NAME",
|
||||
};
|
||||
|
||||
my %verbose_packet_dump_functions = (
|
||||
'SSH2_MSG_KEXINIT' => sub {
|
||||
my ($data) = @_;
|
||||
my ($cookie0, $cookie1, $cookie2, $cookie3,
|
||||
$kex, $hostkey, $cscipher, $sccipher, $csmac, $scmac,
|
||||
$cscompress, $sccompress, $cslang, $sclang, $guess) =
|
||||
&parse("uuuussssssssssb", $data);
|
||||
$cscompress, $sccompress, $cslang, $sclang, $guess, $reserved) =
|
||||
&parse("uuuussssssssssbu", $data);
|
||||
printf(" cookie: %08x%08x%08x%08x\n",
|
||||
$cookie0, $cookie1, $cookie2, $cookie3);
|
||||
my $print_namelist = sub {
|
||||
@ -567,6 +585,127 @@ my %verbose_packet_dump_functions = (
|
||||
$print_namelist->("client->server language", $cslang);
|
||||
$print_namelist->("server->client language", $sclang);
|
||||
printf " first kex packet follows: %s\n", $guess;
|
||||
printf " reserved field: %#x\n", $reserved;
|
||||
},
|
||||
'SSH2_MSG_KEXDH_INIT' => sub {
|
||||
my ($data) = @_;
|
||||
my ($e) = &parse("m", $data);
|
||||
printf " e: %s\n", $e;
|
||||
},
|
||||
'SSH2_MSG_KEX_DH_GEX_REQUEST' => sub {
|
||||
my ($data) = @_;
|
||||
my ($min, $pref, $max) = &parse("uuu", $data);
|
||||
printf " min bits: %d\n", $min;
|
||||
printf " preferred bits: %d\n", $pref;
|
||||
printf " max bits: %d\n", $max;
|
||||
},
|
||||
'SSH2_MSG_KEX_DH_GEX_GROUP' => sub {
|
||||
my ($data) = @_;
|
||||
my ($p, $g) = &parse("mm", $data);
|
||||
printf " p: %s\n", $p;
|
||||
printf " g: %s\n", $g;
|
||||
},
|
||||
'SSH2_MSG_KEX_DH_GEX_INIT' => sub {
|
||||
my ($data) = @_;
|
||||
my ($e) = &parse("m", $data);
|
||||
printf " e: %s\n", $e;
|
||||
},
|
||||
'SSH2_MSG_KEX_ECDH_INIT' => sub {
|
||||
my ($data) = @_;
|
||||
my ($cpv) = &parse("s", $data);
|
||||
# Public values in ECDH depend for their interpretation on the
|
||||
# selected curve, and this script doesn't cross-analyse the
|
||||
# two KEXINIT packets to independently figure out what that
|
||||
# curve is. So the best we can do is just dump the raw data.
|
||||
printf " client public value: %s\n", (unpack "H*", $cpv);
|
||||
},
|
||||
'SSH2_MSG_KEXDH_REPLY' => sub {
|
||||
my ($data) = @_;
|
||||
my ($hostkeyblob, $f, $sigblob) = &parse("sms", $data);
|
||||
my ($hktype, @hostkey) = &parse_public_key($hostkeyblob);
|
||||
printf " host key: %s\n", $hktype;
|
||||
while (@hostkey) {
|
||||
my ($key, $value) = splice @hostkey, 0, 2;
|
||||
printf " $key: $value\n";
|
||||
}
|
||||
printf " f: %s\n", $f;
|
||||
printf " signature:\n";
|
||||
my @signature = &parse_signature($sigblob, $hktype);
|
||||
while (@signature) {
|
||||
my ($key, $value) = splice @signature, 0, 2;
|
||||
printf " $key: $value\n";
|
||||
}
|
||||
},
|
||||
'SSH2_MSG_KEX_DH_GEX_REPLY' => sub {
|
||||
my ($data) = @_;
|
||||
my ($hostkeyblob, $f, $sigblob) = &parse("sms", $data);
|
||||
my ($hktype, @hostkey) = &parse_public_key($hostkeyblob);
|
||||
printf " host key: %s\n", $hktype;
|
||||
while (@hostkey) {
|
||||
my ($key, $value) = splice @hostkey, 0, 2;
|
||||
printf " $key: $value\n";
|
||||
}
|
||||
printf " f: %s\n", $f;
|
||||
printf " signature:\n";
|
||||
my @signature = &parse_signature($sigblob, $hktype);
|
||||
while (@signature) {
|
||||
my ($key, $value) = splice @signature, 0, 2;
|
||||
printf " $key: $value\n";
|
||||
}
|
||||
},
|
||||
'SSH2_MSG_KEX_ECDH_REPLY' => sub {
|
||||
my ($data) = @_;
|
||||
my ($hostkeyblob, $spv, $sigblob) = &parse("sss", $data);
|
||||
my ($hktype, @hostkey) = &parse_public_key($hostkeyblob);
|
||||
printf " host key: %s\n", $hktype;
|
||||
while (@hostkey) {
|
||||
my ($key, $value) = splice @hostkey, 0, 2;
|
||||
printf " $key: $value\n";
|
||||
}
|
||||
printf " server public value: %s\n", (unpack "H*", $spv);
|
||||
printf " signature:\n";
|
||||
my @signature = &parse_signature($sigblob, $hktype);
|
||||
while (@signature) {
|
||||
my ($key, $value) = splice @signature, 0, 2;
|
||||
printf " $key: $value\n";
|
||||
}
|
||||
},
|
||||
'SSH2_MSG_NEWKEYS' => sub {},
|
||||
'SSH2_MSG_SERVICE_REQUEST' => sub {
|
||||
my ($data) = @_;
|
||||
my ($servname) = &parse("s", $data);
|
||||
printf " service name: %s\n", $servname;
|
||||
},
|
||||
'SSH2_MSG_SERVICE_ACCEPT' => sub {
|
||||
my ($data) = @_;
|
||||
my ($servname) = &parse("s", $data);
|
||||
printf " service name: %s\n", $servname;
|
||||
},
|
||||
'SSH2_MSG_DISCONNECT' => sub {
|
||||
my ($data) = @_;
|
||||
my ($reason, $desc, $lang) = &parse("uss", $data);
|
||||
printf(" reason code: %d%s\n", $reason,
|
||||
defined $disc_reasons{$reason} ?
|
||||
" ($disc_reasons{$reason})" : "" );
|
||||
printf " description: '%s'\n", $desc;
|
||||
printf " language tag: '%s'\n", $lang;
|
||||
},
|
||||
'SSH2_MSG_DEBUG' => sub {
|
||||
my ($data) = @_;
|
||||
my ($display, $desc, $lang) = &parse("bss", $data);
|
||||
printf " always display: %s\n", $display;
|
||||
printf " description: '%s'\n", $desc;
|
||||
printf " language tag: '%s'\n", $lang;
|
||||
},
|
||||
'SSH2_MSG_IGNORE' => sub {
|
||||
my ($data) = @_;
|
||||
my ($payload) = &parse("s", $data);
|
||||
printf " data: %s\n", unpack "H*", $payload;
|
||||
},
|
||||
'SSH2_MSG_UNIMPLEMENTED' => sub {
|
||||
my ($data) = @_;
|
||||
my ($seq) = &parse("u", $data);
|
||||
printf " sequence number: %d\n", $seq;
|
||||
},
|
||||
);
|
||||
|
||||
@ -815,14 +954,25 @@ while (<>) {
|
||||
$recording = 0;
|
||||
my $fullseq = "$direction$ourseq";
|
||||
print "$fullseq: $type ";
|
||||
|
||||
my ($verbose_dump, $verbose_data) = undef;
|
||||
if (defined $verbose_packet_dump_functions{$type} &&
|
||||
($verbose_all || defined $verbose_packet{$type})) {
|
||||
$verbose_dump = $verbose_packet_dump_functions{$type};
|
||||
$verbose_data = [ @$data ];
|
||||
}
|
||||
|
||||
if (defined $packets{$type}) {
|
||||
$packets{$type}->($direction, $fullseq, $data);
|
||||
} else {
|
||||
printf "raw %s\n", join "", map { sprintf "%02x", $_ } @$data;
|
||||
}
|
||||
if (defined $verbose_packet_dump_functions{$type} &&
|
||||
($verbose_all || defined $verbose_packet{$type})) {
|
||||
$verbose_packet_dump_functions{$type}->($data);
|
||||
if (defined $verbose_dump) {
|
||||
$verbose_dump->($verbose_data);
|
||||
if (@$verbose_data) {
|
||||
printf(" trailing bytes: %s\n",
|
||||
unpack "H*", pack "C*", @$verbose_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -856,6 +1006,13 @@ if ($dumpchannels) {
|
||||
}
|
||||
}
|
||||
|
||||
sub format_unsigned_hex_integer {
|
||||
my $abs = join "", map { sprintf "%02x", $_ } @_;
|
||||
$abs =~ s!^0*!!g;
|
||||
$abs = "0" if $abs eq "";
|
||||
return "0x" . $abs;
|
||||
}
|
||||
|
||||
sub parseone {
|
||||
my ($type, $data) = @_;
|
||||
if ($type eq "u") { # uint32
|
||||
@ -896,7 +1053,7 @@ sub parseone {
|
||||
}
|
||||
$str = "-";
|
||||
}
|
||||
$str .= "0x" . join "", map { sprintf "%02x", $_ } @bytes;
|
||||
$str .= &format_unsigned_hex_integer(@bytes);
|
||||
return $str;
|
||||
} else {
|
||||
return pack "C*", @bytes;
|
||||
@ -1001,6 +1158,80 @@ sub sftp_parse_attrs {
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub parse_public_key {
|
||||
my ($blob) = @_;
|
||||
my $data = [ unpack "C*", $blob ];
|
||||
my @toret;
|
||||
my ($type) = &parse("s", $data);
|
||||
push @toret, $type;
|
||||
if ($type eq "ssh-rsa") {
|
||||
my ($e, $n) = &parse("mm", $data);
|
||||
push @toret, "e", $e, "n", $n;
|
||||
} elsif ($type eq "ssh-dss") {
|
||||
my ($p, $q, $g, $y) = &parse("mmmm", $data);
|
||||
push @toret, "p", $p, "q", $q, "g", $g, "y", $y;
|
||||
} elsif ($type eq "ssh-ed25519") {
|
||||
my ($xyblob) = &parse("s", $data);
|
||||
my @y = unpack "C*", $xyblob;
|
||||
push @toret, "hibit(x)", $y[$#y] & 1;
|
||||
$y[$#y] &= ~1;
|
||||
push @toret, "y & ~1", &format_unsigned_hex_integer(@y);
|
||||
} elsif ($type =~ m!^ecdsa-sha2-nistp(256|384|521)$!) {
|
||||
my ($curvename, $blob) = &parse("ss", $data);
|
||||
push @toret, "curve name", $curvename;
|
||||
my @blobdata = unpack "C*", $blob;
|
||||
my ($fmt) = &parse("B", \@blobdata);
|
||||
push @toret, "format byte", $fmt;
|
||||
if ($fmt == 4) {
|
||||
push @toret, "x", &format_unsigned_hex_integer(
|
||||
@blobdata[0..($#blobdata+1)/2-1]);
|
||||
push @toret, "y", &format_unsigned_hex_integer(
|
||||
@blobdata[($#blobdata+1)/2..$#blobdata]);
|
||||
}
|
||||
} else {
|
||||
push @toret, "undecoded data", unpack "H*", pack "C*", @$data;
|
||||
}
|
||||
return @toret;
|
||||
};
|
||||
|
||||
sub parse_signature {
|
||||
my ($blob, $keytype) = @_;
|
||||
my $data = [ unpack "C*", $blob ];
|
||||
my @toret;
|
||||
if ($keytype eq "ssh-rsa") {
|
||||
my ($type, $s) = &parse("ss", $data);
|
||||
push @toret, "sig type", $type;
|
||||
push @toret, "s", &format_unsigned_hex_integer(unpack "C*", $s);
|
||||
} elsif ($keytype eq "ssh-dss") {
|
||||
my ($type, $subblob) = &parse("ss", $data);
|
||||
push @toret, "sig type", $type;
|
||||
push @toret, "r", &format_unsigned_hex_integer(
|
||||
unpack "C*", substr($subblob, 0, 20));
|
||||
push @toret, "s", &format_unsigned_hex_integer(
|
||||
unpack "C*", substr($subblob, 20, 40));
|
||||
} elsif ($keytype eq "ssh-ed25519") {
|
||||
my ($type, $rsblob) = &parse("ss", $data);
|
||||
push @toret, "sig type", $type;
|
||||
my @ry = unpack "C*", $rsblob;
|
||||
my @sy = splice @ry, 32, 32;
|
||||
push @toret, "hibit(r.x)", $ry[$#ry] & 1;
|
||||
$ry[$#ry] &= ~1;
|
||||
push @toret, "r.y & ~1", &format_unsigned_hex_integer(@ry);
|
||||
push @toret, "hibit(s.x)", $sy[$#sy] & 1;
|
||||
$sy[$#sy] &= ~1;
|
||||
push @toret, "s.y & ~1", &format_unsigned_hex_integer(@sy);
|
||||
} elsif ($keytype =~ m!^ecdsa-sha2-nistp(256|384|521)$!) {
|
||||
my ($sigtype, $subblob) = &parse("ss", $data);
|
||||
push @toret, "sig type", $sigtype;
|
||||
my @sbdata = unpack "C*", $subblob;
|
||||
my ($r, $s) = &parse("mm", \@sbdata);
|
||||
push @toret, "r", $r, "s", $s;
|
||||
} else {
|
||||
push @toret, "undecoded data", unpack "H*", pack "C*", @$data;
|
||||
}
|
||||
return @toret;
|
||||
};
|
||||
|
||||
sub stringescape {
|
||||
my ($str) = @_;
|
||||
$str =~ s!\\!\\\\!g;
|
||||
|
Loading…
Reference in New Issue
Block a user