Skip Menu |

This queue is for tickets about the File-KeePass CPAN distribution.

Report information
The Basics
Id: 85012
Status: open
Priority: 0/
Queue: File-KeePass

People
Owner: Nobody in particular
Requestors: gmail [...] beyerscoetzee.com
joern.clausen [...] uni-bielefeld.de
peter.ohlerich [...] uni-bielefeld.de
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 2.03
Fixed in: (no value)



Subject: Endianness Bug in Module File::KeePass
Date: Fri, 03 May 2013 09:03:51 +0200
To: bug-File-KeePass [...] rt.cpan.org
From: Peter Ohlerich <peter.ohlerich [...] uni-bielefeld.de>
Hello Paul! Yesterday I files a bug at Sourceforge for kpcli and Lester told me the problem is not in kpcli, but in File::KeePass and requested me to send this to you. So here's my problem (copied from [ kpcli-Bugs-3612469 ] ): I tried to open a KDBX-file with kpcli-1.7 under Solaris 10 Sparc and got a signature error: Couldn't load the file facebook_fvdmb.kdbx: File signature (sig1) did not match (x != y) When I converted both values to HEX I saw that the values were equal but with wrong byte order. Can you please fix this? With regards, Peter. -- ---------------------------------------------------------------------- HRZ der Universitaet Bielefeld Phone: +49 521 106-00 Dipl.-Inf. Peter Ohlerich Email: Peter.Ohlerich@uni-bielefeld.de P.O.Box 100131 WWW: http://www.uni-bielefeld.de/hrz/ D-33501 Bielefeld (Germany)
The attached patch got this working for me on Solaris 11 SPARC.
Subject: big-endian.diff
diff --git a/File-KeePass-2.03/lib/File/KeePass.pm b/File-KeePass/lib/File/KeePass.pm index 3c414e5..9bcb2d0 100644 --- a/File-KeePass-2.03/lib/File/KeePass.pm +++ b/File-KeePass/lib/File/KeePass.pm @@ -122,7 +122,7 @@ sub parse_db { sub parse_header { my ($self, $buffer) = @_; - my ($sig1, $sig2) = unpack 'LL', $buffer; + my ($sig1, $sig2) = unpack 'VV', $buffer; die "File signature (sig1) did not match ($sig1 != ".DB_SIG_1().")\n" if $sig1 != DB_SIG_1; return $self->_parse_v1_header($buffer) if $sig2 eq DB_SIG_2_v1; return $self->_parse_v2_header($buffer) if $sig2 eq DB_SIG_2_v2; @@ -135,7 +135,7 @@ sub _parse_v1_header { die "File was smaller than db header ($size < ".DB_HEADSIZE_V1().")\n" if $size < DB_HEADSIZE_V1; my %h = (version => 1, header_size => DB_HEADSIZE_V1); my @f = qw(sig1 sig2 flags ver seed_rand enc_iv n_groups n_entries checksum seed_key rounds); - my $t = 'L L L L a16 a16 L L a32 a32 L'; + my $t = '( L L L L a16 a16 L L a32 a32 L )<'; @h{@f} = unpack $t, $buffer; die "Unsupported file version ($h{'ver'}).\n" if $h{'ver'} & 0xFFFFFF00 != DB_VER_DW_V1 & 0xFFFFFF00; $h{'enc_type'} = ($h{'flags'} & DB_FLAG_RIJNDAEL) ? 'rijndael' @@ -147,12 +147,12 @@ sub _parse_v1_header { sub _parse_v2_header { my ($self, $buffer) = @_; my %h = (version => 2, enc_type => 'rijndael'); - @h{qw(sig1 sig2 ver)} = unpack 'L3', $buffer; + @h{qw(sig1 sig2 ver)} = unpack 'V3', $buffer; die "Unsupported file version2 ($h{'ver'}).\n" if $h{'ver'} & 0xFFFF0000 > 0x00020000 & 0xFFFF0000; my $pos = 12; while (1) { - my ($type, $size) = unpack "\@$pos CS", $buffer; + my ($type, $size) = unpack "\@$pos Cv", $buffer; $pos += 3; my $val = substr $buffer, $pos, $size; # #my ($val) = unpack "\@$pos a$size", $buffer; if (!$type) { @@ -177,7 +177,7 @@ sub _parse_v2_header { warn "Length of seed key was not 32\n" if length($val) != 32; $h{'seed_key'} = $val; } elsif ($type == 6) { - $h{'rounds'} = unpack 'L', $val; + $h{'rounds'} = unpack 'V', $val; } elsif ($type == 7) { warn "Length of encryption IV was not 16\n" if length($val) != 16; $h{'enc_iv'} = $val; @@ -761,7 +761,7 @@ sub _gen_v1_header { die "Length of $_ was not 32 (".length($head->{$_}).")\n" for grep {length($head->{$_}) != 32} qw(seed_key checksum); die "Length of $_ was not 16 (".length($head->{$_}).")\n" for grep {length($head->{$_}) != 16} qw(enc_iv seed_rand); my @f = qw(sig1 sig2 flags ver seed_rand enc_iv n_groups n_entries checksum seed_key rounds); - my $t = 'L L L L a16 a16 L L a32 a32 L'; + my $t = '( L L L L a16 a16 L L a32 a32 L )<'; my $header = pack $t, @$head{@f}; die "Invalid generated header\n" if length($header) != DB_HEADSIZE_V1; return $header; @@ -1009,15 +1009,15 @@ sub _gen_v2_header { die "Length of $_ was not 32 (".length($head->{$_}).")\n" for grep {length($head->{$_}) != 32} qw(seed_rand seed_key protected_stream_key start_bytes); die "Length of enc_iv was not 16\n" if length($head->{'enc_iv'}) != 16; - my $buffer = pack 'L3', @$head{qw(sig1 sig2 ver)}; + my $buffer = pack 'V3', @$head{qw(sig1 sig2 ver)}; - my $pack = sub { my ($type, $str) = @_; $buffer .= pack('C S', $type, length($str)) . $str }; + my $pack = sub { my ($type, $str) = @_; $buffer .= pack('C v', $type, length($str)) . $str }; $pack->(1, $head->{'comment'}) if defined($head->{'comment'}) && length($head->{'comment'}); $pack->(2, "\x31\xc1\xf2\xe6\xbf\x71\x43\x50\xbe\x58\x05\x21\x6a\xfc\x5a\xff"); # aes cipher $pack->(3, pack 'V', $head->{'compression'} ? 1 : 0); $pack->(4, $head->{'seed_rand'}); $pack->(5, $head->{'seed_key'}); - $pack->(6, pack 'LL', $head->{'rounds'}, 0); # a little odd to be double the length but not used + $pack->(6, pack 'VV', $head->{'rounds'}, 0); # a little odd to be double the length but not used $pack->(7, $head->{'enc_iv'}); $pack->(8, $head->{'protected_stream_key'}); $pack->(9, $head->{'start_bytes'}); @@ -1064,7 +1064,7 @@ sub unchunksum { my ($self, $buffer) = @_; my ($new, $pos) = ('', 0); while ($pos < length($buffer)) { - my ($index, $hash, $size) = unpack "\@$pos L a32 i", $buffer; + my ($index, $hash, $size) = unpack "( \@$pos L a32 i )<", $buffer; $pos += 40; if ($size == 0) { warn "Found mismatch for 0 chunksize\n" if $hash ne "\0"x32; @@ -1087,11 +1087,11 @@ sub chunksum { my $pos = 0; while ($pos < length($buffer)) { my $chunk = substr($buffer, $pos, $chunk_size); - $new .= pack "L a32 i", $index++, sha256($chunk), length($chunk); + $new .= pack "( L a32 i )<", $index++, sha256($chunk), length($chunk); $new .= $chunk; $pos += length($chunk); } - $new .= pack "L a32 i", $index++, "\0"x32, 0; + $new .= pack "( L a32 i )<", $index++, "\0"x32, 0; return $new; } diff --git a/File-KeePass/t/06_sample.t b/File-KeePass/t/06_sample.t new file mode 100644 index 0000000..9ecf92a --- /dev/null +++ b/File-KeePass/t/06_sample.t @@ -0,0 +1,129 @@ +#!/usr/bin/perl + +=head1 NAME + +06_sample.t - Check interoperability with a sample database created by KeePass. + +=cut + +use strict; +use warnings; +use Test::More tests => 44; +use File::Basename; + +if (!eval { + require MIME::Base64; + require XML::Parser; + require Compress::Raw::Zlib; + require utf8; +}) { + diag "Failed to load library: $@"; + SKIP: { skip "Missing necessary libraries.\n", 44 }; + exit; +} + +use_ok('File::KeePass'); + +my $pass = "qwe123"; +my $filename = dirname(__FILE__). "/NewDatabase.kdbx"; + +my $obj = File::KeePass->new; +ok($obj->load_db($filename, $pass), "Database load"); + +compare( + 'group', [ + { + 'title' => 'NewDatabase', + 'icon' => 49, + 'level' => 0, + }, + { + 'title' => 'General', + 'icon' => 48, + 'level' => 1, + }, + { + 'title' => 'Windows', + 'icon' => 38, + 'level' => 1, + }, + { + 'title' => 'Network', + 'icon' => 3, + 'level' => 1, + }, + { + 'title' => 'Internet', + 'icon' => 1, + 'level' => 1, + }, + { + 'title' => 'eMail', + 'icon' => 19, + 'level' => 1, + }, + { + 'title' => 'Homebanking', + 'icon' => 37, + 'level' => 1, + }, + ] +); +compare( + 'entry', [ + { + 'title' => 'Sample Entry', + 'icon' => '0', + 'username' => 'User Name', + 'url' => 'http://keepass.info/', + 'password' => 'Password', + }, + { + 'title' => 'Sample Entry #2', + 'icon' => '0', + 'username' => 'Michael321', + 'url' => 'http://keepass.info/help/kb/testform.html', + 'password' => '12345', + }, + ] +); + +sub compare { + my $type = shift; + my @expected_items = @{shift()}; + my $find_all_method = { + 'group' => 'find_groups', + 'entry' => 'find_entries', + }->{$type}; + my $find_one_method = { + 'group' => 'find_group', + 'entry' => 'find_entry', + }->{$type}; + + my @items = $obj->$find_all_method({}); + is($#items, $#expected_items, "\u$type count"); + + foreach my $expected_item ( @expected_items ) { + my $item = $obj->$find_one_method({ + 'title' => $expected_item->{'title'} + }); + ok($item, "Found $type ". $expected_item->{'title'}); + foreach my $property ( keys %$expected_item ) { + if ( $property eq 'password' ) { + is( + $obj->locked_entry_password($item), + $expected_item->{$property}, + 'Retrieved password' + ); + } + else { + is( + $item->{$property}, + $expected_item->{$property}, + "\u$type ". $expected_item->{'title'}. + " has expected $property" + ); + } + } + } +} diff --git a/File-KeePass/t/NewDatabase.kdbx b/File-KeePass/t/NewDatabase.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..3ca08a42f246b14bfcd94802f1578d5ccee0d77e GIT binary patch literal 2078 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 IPxc>m`Ak&z1poj5 literal 0 HcmV?d00001
Subject: Endianess Bug in File::KeePass
Date: Thu, 04 Aug 2016 09:14:40 +0200
To: bug-File-KeePass [...] rt.cpan.org
From: Jörn Clausen <joern.clausen [...] uni-bielefeld.de>
Hello Paul! I would like to follow up on Bug 85021, which my colleague Peter opened in 2013. I tried registering with Bitcard, but did not receive a confirmation mail. I have tried the patch you attached to the bug report in 2014 and can confirm, that it allows reading 2.x KeePass files on Solaris 11/sparc. Trying to read a 1.x file results in Couldn't load the file Database2.kdb: Group header offset is out of range. (6, 1375731712) at /opt/pkg-hrz/20160711/lib/perl5/vendor_perl/5.24.0/File/KeePass.pm line 410. I assume that the changes make the code work only on big endian and now break it on little endian, right? So it would be great if you could make the module work with both endianesses. Best Regards, Joern -- Jörn Clausen Plattformen & Serverdienste Hochschulrechenzentrum Universität Bielefeld Universitätsstraße 25 33615 Bielefeld Telefon: +49 521 106-12601 E-Mail: joern.clausen@uni-bielefeld.de http://www.uni-bielefeld.de/hrz
Subject: Endianness Bug in File::KeePass - Followup
Date: Tue, 26 Nov 2019 14:10:25 +0200
To: bug-File-KeePass [...] rt.cpan.org
From: Beyers Coetzee <gmail [...] beyerscoetzee.com>
In the last week or so I bumped my head against this selfsame bug, in trying to open KeePass databases generated on Red Hat on an IBM Mainframe architecture (s390x - big-endian) in Red Hat on a desktop architecture (x86_64 little-endian). And vica-versa too, of course. I had a look at the patch submitted in 2014, but it did not address KeePass v1 databases at all, and more importantly, it contained quite a lot of patching I couldn't figure out at all. So I generated this uber-simple patch that *only* addresses the endian problem, both for V1 and V2 databases. All it does is to replace the template order/type specifiers for pack and unpack to little-endian specific ones: L -> V    (Unsigned long value to unsigned long (32-bit) in little-endian order) S -> v    (Unsigned short value to unsigned short (16-bit) in little-endian order) i -> V    (signed integer value to unsigned long (32-bit) in little-endian order) The first two replacements should theoretically cause no problems that I am aware of. The integer replacement is a little worrying, as I am not sure why it was used in the first place. It leaves the implementation vulnerable to the integer length that was compiled into the operating system, at least according to my understanding. I did not exhaustively test it for all control paths, only for generating- and parsing databases on the two architectures to my disposal, so no guarantees. I hope though that you will find it useful if you are faced with the same problem.

Message body is not shown because sender requested not to inline it.

Subject: Re: [rt.cpan.org #131088] Endianness Bug in File::KeePass - Followup
Date: Sat, 30 Nov 2019 20:50:25 -0700
To: bug-File-KeePass [...] rt.cpan.org
From: Paul Seamons <paul [...] seamons.com>
I'll look at getting it applied this week

On Nov 26, 2019 5:38 AM, Beyers Coetzee via RT <bug-File-KeePass@rt.cpan.org> wrote:
Show quoted text

Tue Nov 26 07:38:45 2019: Request 131088 was acted upon.
Transaction: Ticket created by gmail@beyerscoetzee.com
       Queue: File-KeePass
     Subject: Endianness Bug in File::KeePass - Followup
   Broken in: (no value)
    Severity: (no value)
       Owner: Nobody
  Requestors: gmail@beyerscoetzee.com
      Status: new
Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=131088 >


In the last week or so I bumped my head against this selfsame bug, in
trying to open KeePass databases generated on Red Hat on an IBM
Mainframe architecture (s390x - big-endian) in Red Hat on a desktop
architecture (x86_64 little-endian). And vica-versa too, of course.

I had a look at the patch submitted in 2014, but it did not address
KeePass v1 databases at all, and more importantly, it contained quite a
lot of patching I couldn't figure out at all. So I generated this
uber-simple patch that *only* addresses the endian problem, both for V1
and V2 databases. All it does is to replace the template order/type
specifiers for pack and unpack to little-endian specific ones:

L -> V    (Unsigned long value to unsigned long (32-bit) in
little-endian order)
S -> v    (Unsigned short value to unsigned short (16-bit) in
little-endian order)
i -> V    (signed integer value to unsigned long (32-bit) in
little-endian order)

The first two replacements should theoretically cause no problems that I
am aware of. The integer replacement is a little worrying, as I am not
sure why it was used in the first place. It leaves the implementation
vulnerable to the integer length that was compiled into the operating
system, at least according to my understanding.

I did not exhaustively test it for all control paths, only for
generating- and parsing databases on the two architectures to my
disposal, so no guarantees. I hope though that you will find it useful
if you are faced with the same problem.