dpkg-sig-0.13.1+nmu1/0000755000000000000000000000000012232531261011040 5ustar dpkg-sig-0.13.1+nmu1/dpkg-sig0000755000000000000000000015566412232531261012514 0ustar #!/usr/bin/perl -w # # dpkg-sig signs deb-files in a standard way # # (c) Andreas Barth 2003 # (c) Marc Brockschmidt 2004 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Upstream web site is http://dpkg-sig.turmzimmer.net/ =pod =head1 NAME B - Debian package archive (.deb) signature generation and verification tool =head1 DESCRIPTION This is the description of the source code, trying to help people to understand how B works. =head1 SYNOPSIS =cut use strict; use Getopt::Long; use Data::Dumper; use IPC::Open2; use IPC::Open3; use File::Temp qw(tempdir); use File::Copy qw(move); use File::Basename qw(dirname basename); $| = 1; #Global variables (used for configuration and/or command line stuff) my ($sign, $list, $verify, $verify_role, $verify_exact, $client, $cache_pass, $pass_file, $key, $maintainer, $maintainer_pr, $verbose, %config, $tempdir, %part_cache, $check_v2_sig, $check_v3_sig, $batch, $gpgoptions, $passphrase, $remote_dpkg_sig, %ssh_connections, $sign_changes, $get_hashes, $sign_hashes, $write_signature, $help, $DEBUG, $verify_pattern, $remote_ssh_port); my @configfiles = qw(/etc/devscripts.conf ~/.devscripts); my $DPKG_SIG_VERSION = 0.13; &process_cli_options(); #If wanted, print only help and exit: if ($help) { &help; exit; } #In client mode, we wait for commands and STDIN, we don't need the rest: if ($client) { print "Welcome. This is dpkg-sig in client mode. Protocol version 6\n"; &read_cmds(); exit; } #Only load Config::File if we're not in the client mode: eval { require Config::File; import Config::File qw(read_config_file); }; _die($@) if $@; $tempdir = tempdir("debsigs-ng.XXXXXX", CLEANUP => 1, TMPDIR => 1); &load_config(@configfiles); $remote_dpkg_sig ||= "dpkg-sig"; my @files = @ARGV; if ($sign_hashes || $write_signature) { for (@files) { unless (/\.dpkg-sig-hashes$/) { die _die("$_: Make sure all files were generated by dpkg-sig --get-hashes file\n"); } } for my $file (@files) { if ($sign_hashes) { print "Processing $file...\n"; sign_hashes($file); print "Signed hashes in $file...\n"; } else { print "Processing $file...\n"; my @done = write_signature($file); print "Added signature to $_\n" for (@done); } } exit; } if (grep { ! /(?:deb|changes)$/ } @files) { die _die("We can only work on debs and changes files."); } if ($sign) { if (length($sign) > 9) { die _die("The signing name '$sign' is too long."); } elsif ($sign !~ /^[a-z]+$/) { die _die("The signing name '$sign' is not valid. Please use only letters."); } } if ($verify_role) { $verify_pattern = "^_gpg".$verify_role."[0-9A-Z]?\$"; } elsif ($verify_exact) { $verify_pattern = "^_gpg$verify_exact"; } else { $verify_pattern = "^_gpg.+"; } #The main loop: for my $exp (@files) { my @globbed_files = glob_exp($exp); die _die("Cannot find $exp: no such file") if (! @globbed_files); for my $file (@globbed_files) { if ($file =~ /\.deb$/) { #Yay! That's easy! print "Processing $file...\n"; if ($sign) { sign_deb($sign, $file); print "Signed deb $file\n" if ! $batch; } elsif ($verify || $verify_role || $verify_exact) { my @verify_output = verify_deb($file, $verify_pattern); print @verify_output; exit 2 if grep { /^BADSIG/ } @verify_output; exit 3 if grep { /^UNKNOWNSIG/ } @verify_output; } elsif ($list) { for (get_deb_parts($file)) { print "$1\n" if ($_->[0] =~ /_gpg(.+)/); } } elsif ($get_hashes) { write_deb_info($get_hashes, $file); unlink "$file.dpkg-sig-hashes"; add_part_to_ar_archive ("$file.dpkg-sig-hashes", "deb\n$get_hashes\n$file ".get_file_md5sum($file)."\n" , "control"); add_part_to_ar_archive ("$file.dpkg-sig-hashes", _read_file("$tempdir/digests"), "deb0"); } } else { print "--- Processing changes file $file:\n"; my $changes_signed = 0; my (%new_debs, $sums_control_data, @deb_md5sums, $maintainer_from_changes); #Get default from *changes: unless ($maintainer || $key) { $maintainer_from_changes = 1; chomp($maintainer = `grep ^Changed-By: $file | cut -d " " -f 2-`); $maintainer = quotemeta($maintainer); } if ($get_hashes) { unlink "$file.dpkg-sig-hashes"; $sums_control_data = "changes $file\n$get_hashes\n"; } for my $deb (get_debs_from_changes($file, \$changes_signed)) { print "Processing $deb...\n"; if ($sign) { my $r = sign_deb($sign, $deb); $new_debs{$r->[2]} = $r; print "Signed deb $deb\n" if ! $batch; } elsif ($verify || $verify_role || $verify_exact) { my @verify_output = verify_deb($deb, $verify_pattern); print @verify_output; exit 2 if grep { /^BADSIG/ } @verify_output; exit 3 if grep { /^UNKNOWNSIG/ } @verify_output; } elsif ($list) { for (get_deb_parts($deb)) { print "$1\n" if ($_->[0] =~ /_gpg(.+)/); } } elsif ($get_hashes) { $sums_control_data .= $deb." ".get_file_md5sum($deb)."\n"; write_deb_info($get_hashes, $deb); push @deb_md5sums, _read_file("$tempdir/digests"); } } if ($sign) { correct_changes_file($file, \%new_debs); sign_control_files($file) if ($sign_changes ne "no" && ! ($sign_changes eq "auto" && ! $changes_signed)); } elsif ($get_hashes) { add_part_to_ar_archive ("$file.dpkg-sig-hashes", $sums_control_data , "control"); for (my $i=0; $i<@deb_md5sums; $i++) { add_part_to_ar_archive ("$file.dpkg-sig-hashes", $deb_md5sums[$i], "deb$i"); } } undef $maintainer if ($maintainer_from_changes); } } } #Clean our ssh connections: for (values %ssh_connections) { my ($pid, $readerfh, $writerfh) = @$_; print $writerfh "quit\n"; sleep 1; kill $pid; } exit; =pod =head2 I<\@file_info> = sign_deb (I<$signing_role>, I<$file>) Does everything needed to add a signature to I<$file>: =over 4 =item * Verifies existing signatures =item * Creates the meta-data that is actually signed =item * Calls gpg to sign the meta-data. =item * Adds the signature to I<$file> =back Returns a reference to an array containing the new md5sum, the new size and the name of the signed deb. =cut sub sign_deb { my ($sig_name, $file) = @_; #Check the existing signatures: my @verify = verify_deb($file, "^_gpg.+"); if (grep { /^BADSIG/ } @verify) { print STDERR "Can't sign $file, some signatures are invalid:\n".(join "", grep { /^BADSIG/ } @verify)."\n"; exit 2; } #This also chooses the right sig name: $sig_name = write_deb_info($sig_name, $file); sign_file ("$tempdir/digests", "$tempdir/digests.asc", "no_detach"); #Read sig: my $sig = _read_file ($tempdir."/digests.asc"); return add_sig_to_deb($file, $sig, $sig_name); } =pod =head2 I<$signature_name> = write_deb_info (I<$signing_role>, I<$file>) Creates a digests.asc file with the meta-data of I<$file> in dpkg-sig's tempdir: =over 4 =item * Gets the needed information from I<$file> =item * Chooses the name of the signature =item * Writes a file in a RFC822-like format containing the meta-data =back Returns the name that should be used to add the file to the deb. =cut sub write_deb_info { my ($sig_name, $deb) = @_; #Get digests: my $digests = get_deb_digests($deb); #Get name for our new signature part of the archive $sig_name = get_sig_name($sig_name, $digests, $deb); #Create digests file unlink ($tempdir."/digests.asc"); my $signer_name = $key || $maintainer; if ($signer_name =~ /^(0x)?[0-9A-F]{8}$/i) { my $uidline = (grep /^uid:/, qx/gpg --list-keys --with-colons --fixed-list-mode $signer_name/)[0]; $signer_name = (split /:/, $uidline)[9] if defined $uidline; chomp($signer_name); } my @data; push @data, "Version: 4\n"; push @data, "Signer: $signer_name\n"; push @data, "Date: " . localtime() . "\n"; push @data, "Role: $1\n" if $sig_name =~ /^_gpg(\S+?)[A-Z0-9]?$/; push @data, "Files: \n"; for my $part_info (@$digests) { push @data, "\t" . join (" ", reverse @$part_info) . "\n"; } _write_file("$tempdir/digests", @data); return $sig_name } =pod =head2 sign_hashes (I<$file>) Signs a .dpkg-sig-hashes I<$file> containing the digests of a deb/changes file: =over 4 =item * Checks the .dpkg-sig-hashes file to see if it really was created by us =item * Creates a new archive, containing the old control file =item * Signs the digests and adds the clearsigned data to the the new archive =item * Substitutes the old file by the new, signed one. =back =cut sub sign_hashes { my ($file) = @_; unlink ($tempdir."/digests"); unlink ($tempdir."/digests.asc"); unlink ($tempdir."/hashes.signed"); #We don't need the control data, we just want to check if this is real #dpkg-sig generated hashes archiv: my $control = get_archive_part($file, "control"); if ($control !~ /^(deb|changes)/) { die _die("$file seems not to be a dpkg-sig hash archive"); } add_part_to_ar_archive($tempdir."/hashes.signed", $control, "control"); #Now sign all hashes: my $num = 0; for (get_deb_parts($file)) { my $part_name = $_->[0]; if ($part_name !~ /^(deb\d+|control)$/) { print STDERR "W: $file contains $part_name, which shouldn't happen in dpkg-sig hash archive\n"; } elsif ($part_name =~ /^deb\d+/) { my $data = get_archive_part($file, $part_name); if ($data =~ /-----BEGIN PGP SIGNATURE-----/) { die _die("$file seems to be already signed!\n"); } _write_file($tempdir."/digests", $data); sign_file("$tempdir/digests", "$tempdir/digests.asc", "no_detach"); my $s_data = _read_file($tempdir."/digests.asc"); add_part_to_ar_archive($tempdir."/hashes.signed", $s_data, "deb".$num++); } } move($tempdir."/hashes.signed", $file); } =pod =head2 I<@changed_files> = write_signature (I<$file>) Adds the signatures from a signed .dpkg-sig-hashes I<$file> to the signed debs: =over 4 =item * Checks the .dpkg-sig-hashes file to see if it really was created by us =item * Tries to find out where we find the debs that have sigs in the .dpkg-sig-hashes =item * Checks if the debs were changed since they were signed =item * Adds signatures from the .dpkg-sig-hashes file to the debs =item * If needed, it corrects the changes file to reflect the new sizes/md5sums of the debs =back Returns the pathes of the debs that were changed. =cut sub write_signature { my ($file) = @_; my @done; unlink ($tempdir."/digests"); unlink ($tempdir."/digests.asc"); unlink ($tempdir."/hashes.signed"); #Get control data: my @control = split (/\n/, get_archive_part($file, "control")); if ($control[0] !~ /^(deb|changes)/) { die _die("$file seems not to be a dpkg-sig hash archive"); } chomp(my $sig_name = $control[1]); my ($num, %new_debs) = (0); for (get_ar_parts($file)) { my $part_name = $_->[0]; if ($part_name !~ /^(deb\d+|control)$/) { print STDERR "W: $file contains $part_name, which shouldn't happen in dpkg-sig hash archive\n"; } elsif ($part_name =~ /^deb\d+/) { my $sig = get_archive_part($file, $part_name); if ($sig !~ /-----BEGIN PGP SIGNATURE-----/) { die _die("$file seems to be unsigned!\n"); } #deb$num is the detached sig for the deb named in control line $num + 1 #Get the name and the md5sum: my ($name, $md5sum) = split / /, $control[$num + 2]; my $path; #Try to find the deb in this dir: if (file_readable(basename($name)) && get_file_md5sum(basename($name)) eq $md5sum) { $path = basename($name); #Now try the path in the hashes file: } elsif (file_readable($name) && get_file_md5sum($name) eq $md5sum) { $path = $name; #Wrong md5sum } elsif (! (get_file_md5sum(basename($name)) eq $md5sum || get_file_md5sum($name) eq $md5sum)) { die _die("The md5sum for $name is wrong. Please use an archive of signed hashes of the version of the file existing now."); #We don't find the damn file! } else { die _die("Can't find $name. Please start dpkg-sig either in the dir with the debs to sign or in the dir where you got the hashes."); } push @done, $path; $sig_name = get_sig_name($sig_name, [get_deb_parts($path)], $path); my $r = add_sig_to_deb($path, $sig, $sig_name); $new_debs{$r->[2]} = $r; $num++; } } if ($control[0] =~ /^changes (.+)$/) { if (file_readable(basename($1))) { correct_changes_file(basename($1), \%new_debs); print "Corrected changes file ".basename($1)."\n"; } elsif (file_readable($1)) { correct_changes_file($1, \%new_debs); print "Corrected changes file $1\n"; } else { print STDERR "Can't find changes file $1, so won't correct it.\n"; } } return @done; } =pod =head2 I<@output> = verify_deb (I<$deb>, I<$verify_pattern>) Verifies all signatures in I<$deb> with names matching I<$verify_pattern>: =over 4 =item * Gets the digests of all parts of I<$deb>. =item * Skips all signatures that don't match I<$verify_pattern>. =item * Writes the signatures to $tempdir/digests.asc. =item * Calls a function to check if $tempdir/digests.asc is valid in the v4 format, then tries v3 and v2. =back Returns its output. This is needed to achieve a "silent" verification when signing a deb. =cut sub verify_deb { my ($deb, $verify_pattern) = @_; my @return; #Get MD5 sums: my $digests = get_deb_digests($deb); for (my $n=0;$n<@$digests;$n++) { my ($part_name, $size, $sha1sum, $md5sum) = @{@$digests[$n]}; next if $part_name !~ /$verify_pattern/; unlink ($tempdir."/digests.asc"); unlink ($tempdir."/digests"); my $sig = get_archive_part($deb, $part_name); _write_file($tempdir."/digests.asc", $sig); my ($status, @info); if ($sig =~ /BEGIN PGP SIGNED MESSAGE/) { $status = verify_deb_sig_v4($part_name, $n, $digests, \@info, \@return); } if ($check_v3_sig && (!$status || $status eq "BAD")) { $status = verify_deb_sig_v3($part_name, $n, $digests, \@info, \@return); } if ($check_v2_sig && (!$status || $status eq "BAD")) { $status = verify_deb_sig_v2($part_name, $n, $digests, \@info, \@return); } if ($status && $status eq "GOOD") { push @return, "GOODSIG $part_name $info[0] $info[2]\n"; } elsif ($status && $status eq "UNKNOWN" && (! $batch || $batch >= 2)) { push @return, "UNKNOWNSIG $part_name $info[0]\n"; } else { push @return, "BADSIG $part_name\n" } } return @return; } =pod =head2 I<$verification_status> = verify_deb_sig_v4 (I<$part_name>, I<$part_number>, I<\@digests>, I<\@info>, I<\@return>) Verifies if $tempdir/digests is a valid (version 4) signature for the deb described with I<\@digests>: =over 4 =item * Calls gpg to verify the OpenPGP signature in $tempdir/digests.asc itself. =item * Parses the signature to get the digests that were actually signed =item * Compare the digests of the deb and those extracted from the signature to see if the deb was changed. =item * Check that the name in the ar archive matches the "Role" field in the signature. =item * DON'T check the Signer- and Date-Fiels. =item * Check that at least the digests for control.tar.gz, data.tar.gz and debian-binary were signed. =back Returns if the the signature is good, by an unknown key, or bad. =cut sub verify_deb_sig_v4 { my ($part_name, $part_number, $digests, $info, $return) = @_; #Check signature: my @cmdline = qw(gpg --openpgp --decrypt --no-auto-check-trustdb --batch --no-tty --status-fd 1 2>&1); push @cmdline, "--output", "$tempdir/digests", "$tempdir/digests.asc"; my $res=qx/@cmdline/; @$info = split(/ /, $1 ) if $res =~ /^\[GNUPG:\] VALIDSIG (.*)$/m; if ($res =~ /^\[GNUPG:\] NO_PUBKEY \S{8}(\S{8})/m) { $info->[0] = $1; return "UNKNOWN"; } return "FORCE_BAD" unless $res =~ /^\[GNUPG:\] GOOD/m; #Now find out if the deb contains the data that was signed: open (FH, "<", "$tempdir/digests") || die "Can't open $tempdir/digests: $!"; my (%data, $field_name); while () { if (/^(\S+):\s*(.*)$/) { $field_name = lc($1); $data{$field_name} = $2 || ""; } elsif (/^\s+(.+)$/ && $field_name) { $data{$field_name} .= ($data{$field_name} ? "\n" : "") . $1; } } close FH; if ($data{version} > 4) { push @$return, "$part_name: v$data{version} signature, dpkg-sig is too old to check it.\n"; return "FORCE_BAD"; } my %seen_files; for my $file_info (split /\n/, $data{files}) { my ($md5sum, $sha1sum, $size, $name) = split /\s+/, $file_info; $seen_files{$name} = 1; my $checked_something=0; for my $member_info (@$digests) { if ($member_info->[0] eq $name) { $checked_something = 1; if ($member_info->[1] ne $size) { push @$return, "$part_name: ${name}'s size differs from signed size.\n" if $verbose; return "FORCE_BAD"; } elsif ($member_info->[2] ne $sha1sum) { push @$return, "$part_name: ${name}'s sha1sum differs from signed size.\n" if $verbose; return "FORCE_BAD"; } elsif ($member_info->[3] ne $md5sum) { push @$return, "$part_name: ${name}'s md5sum differs from signed size.\n" if $verbose; return "FORCE_BAD"; } } } unless ($checked_something) { push @$return, "$part_name: ${name} signed, but not in the deb.\n" if $verbose; return "FORCE_BAD"; } } $part_name =~ /^_gpg(\S+?)[A-Z0-9]?$/; if ($data{role} ne $1) { push @$return, "$part_name: signature name and signed role differ.\n" if $verbose; return "FORCE_BAD"; } return "FORCE_BAD" unless ($seen_files{"control.tar.gz"} && $seen_files{"data.tar.gz"} && $seen_files{"debian-binary"}); return "GOOD"; } =pod =head2 I<$verification_status> = verify_deb_sig_v3 (I<$part_name>, I<$part_number>, I<\@digests>, I<\@info>, I<\@return>) Verifies if $tempdir/digests is a valid (version 3) signature for the deb described with I<\@digests>: =over 4 =item * Creates a file in $tempdir/digests that contains the signing role and the digests from the current deb. =item * Calls gpg to verify that the detached OpenPGP signature in $tempdir/digests.asc is valid for $tempdir/digests. =back Returns if the the signature is good, by an unknown key, or bad. =cut sub verify_deb_sig_v3 { my ($part_name, $part_number, $digests, $info, $return) = @_; my @cmdline = qw(gpg --openpgp --verify --no-auto-check-trustdb --batch --no-tty --status-fd 1 2>&1); push @cmdline, "$tempdir/digests.asc", "$tempdir/digests"; push @$return, "$part_name: Invalid v4 sig ... Trying v3\n" if $verbose; $part_name =~ s/^_gpg(\S+?)[A-Z0-9]?$/$1/; open (FH, ">", $tempdir."/digests") || die _die("Couldn't open $tempdir/digests: $!"); print FH $part_name, "\n"; print FH join "\n", map { $_->[3] . " " . $_->[0] } @$digests[0..$part_number-1]; print FH "\n"; close FH; my $res=qx/@cmdline/; @$info = split(/ /, $1 ) if $res =~ /^\[GNUPG:\] VALIDSIG (.*)$/m; if ($res =~ /^\[GNUPG:\] NO_PUBKEY \S{8}(\S{8})/m) { $info->[0] = $1; return "UNKNOWN"; } return "BAD" unless $res =~ /^\[GNUPG:\] GOOD/m; return "GOOD"; } =pod =head2 I<$verification_status> = verify_deb_sig_v2 (I<$part_name>, I<$part_number>, I<\@digests>, I<\@info>, I<\@return>) Verifies if $tempdir/digests is a valid (version 2) signature for the deb described with I<\@digests>: =over 4 =item * Creates a file in $tempdir/digests that contains the digests from the current deb. =item * Calls gpg to verify that the detached OpenPGP signature in $tempdir/digests.asc is valid for $tempdir/digests. =back Returns if the the signature is good, by an unknown key, or bad. =cut sub verify_deb_sig_v2 { my ($part_name, $part_number, $digests, $info, $return) = @_; my @cmdline = qw(gpg --openpgp --verify --no-auto-check-trustdb --batch --no-tty --status-fd 1 2>&1); push @cmdline, "$tempdir/digests.asc", "$tempdir/digests"; push @$return, "$part_name: Invalid v3 sig ... Trying v2\n" if $verbose; open (FH, ">", $tempdir."/digests") || die _die("Couldn't open $tempdir/digests: $!"); print FH join "\n", map { $_->[3] . " " . $_->[0] } @$digests[0..$part_number-1]; print FH "\n"; close FH; my $res= qx/@cmdline/; @$info = split(/ /, $1 ) if $res =~ /^\[GNUPG:\] VALIDSIG (.*)$/m; if ($res =~ /^\[GNUPG:\] NO_PUBKEY \S{8}(\S{8})/m) { $info->[0] = $1; return "UNKNOWN"; } return "BAD" unless $res =~ /^\[GNUPG:\] GOOD/m; return "GOOD"; } =pod =head2 I<$sig_name> = get_sig_name (I<$sig_name>, I<\@parts>, I<$deb>) Tries to find a filename for the signature. Receives the role and constructs a name not already present in I<$deb>. Returns the final name or dies if it wasn't possible to construct a name. =cut sub get_sig_name { my ($sig_name, $parts, $deb) = @_; $sig_name = "_gpg".$sig_name; if (grep { $_->[0] eq $sig_name } @$parts) { my $changed = 0; for my $ext (0..9, "A" .. "Z") { if (! grep { $_->[0] eq $sig_name.$ext} @$parts) { $sig_name .= $ext; ++$changed; last; } } die _die("$deb: Couldn't get a name for the signature part") if ! $changed; } return $sig_name; } =pod =head2 correct_changes_file (I<$changes>, I<\%new_deb_info>) Receives a path to a changes file I<$changes> and a hash reference I<\%new_deb_info> containing new sizes and md5sums of debs in that changes file. It'll parse the changes file, replace the old values by the new ones. If the file is signed, the signature will be stripped (as it would be invalid anyway). =cut sub correct_changes_file { my ($changes, $new_deb_info) = @_; if ($changes =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($changes); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); print $writerfh "correct_changes_file $file\n"; my ($response, $t); $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response =~ /^300 /) { for (keys %$new_deb_info) { print $writerfh join (" ", @{$new_deb_info->{$_}}), "\n"; } print $writerfh ".\n"; $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } } else { die _die("remote dpkg-sig on $host seems to be weird. Can't parse \"$response\""); } } else { my ($new_changes, $in_files) = ('', 0); open (CHANGES, "+<", $changes) || die _die("$changes: Can't open file: $!"); while () { if (/^-----BEGIN PGP SIGNED MESSAGE-----$/) { while () { last if /^\s*$/ }; next } if ($in_files) { chomp; last if ! s/^ //; my ($md5sum, $size, $section, $priority, $file_name) = split / /, $_; if ($new_deb_info->{$file_name}) { $md5sum = $new_deb_info->{$file_name}->[0]; $size = $new_deb_info->{$file_name}->[1]; chomp($md5sum); } $new_changes .= " " . join (" ", ($md5sum, $size, $section, $priority, $file_name)). "\n"; } else { $new_changes .= $_; } $in_files = "yes" if /^Files:/; } seek(CHANGES, 0, 0) || die _die("$changes: Can't rewind file: $!"); truncate(CHANGES, 0) || die _die("$changes: Can't truncate file: $!"); print CHANGES $new_changes; close CHANGES; } } =pod =head2 I<\@new_file_info> = add_part_to_ar_archive (I<$file>, I<$new_data>, I<$new_name>) =head2 I<\@new_file_info> = add_sig_to_deb (I<$file>, I<$new_data>, I<$new_name>) Adds I<$new_data> to I<$file> as new ar archiv part, using $new_name as filename. If I<$file> doesn't exist, a new ar archive is created. Returns the new md5sum and size of I<$file>. =cut sub add_part_to_ar_archive { return add_sig_to_deb(@_); } sub add_sig_to_deb { my ($deb, $sig, $sig_name) = @_; my ($new_md5sum, $new_file_size); if ($deb =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($deb); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); print $writerfh "add_sig_to_deb $sig_name $file\n"; my ($response, $t); $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response =~ /^300 /) { for (split /\n/, $sig) { s/^\./../g; print $writerfh $_, "\n"; } print $writerfh ".\n"; $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } else { $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); ($new_md5sum, $new_file_size) = split (/ /, $response); } } else { die _die("remote dpkg-sig on $host seems to be weird. Can't parse \"$response\""); } } else { die _die("$deb: Arch member name $sig_name too long!") if (length($sig_name) > 14); my $new_part = sprintf("%-16s%-12s%-6s%-6s%-8s%-10s`\n%s", $sig_name, time, 0, 0, 100644, length($sig), $sig . (length($sig)%2 ? "\n":"")); if (!stat($deb)) { open (DEB, ">", (glob $deb)[0]) || die _die("Couldn't open ".(glob $deb)[0].": $!"); print DEB "!\n"; } else { open (DEB, ">>", (glob $deb)[0]) || die _die("Couldn't open ".(glob $deb)[0].": $!"); } print DEB $new_part || die _die("Couldn write to $deb: $!"); close DEB; $new_md5sum = get_file_md5sum($deb); $new_file_size = (stat($deb))[7]; } return [$new_md5sum, $new_file_size, basename($deb)]; } =pod =head2 I<@parts> = get_ar_parts (I<$file>) =head2 I<@parts> = get_deb_parts (I<$file>) Parses I<$file> as ar archive and returns all filenames included in the archive. =cut sub get_ar_parts { get_deb_part(@_) } sub get_deb_parts { my ($deb) = shift; my @parts; if ($deb =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($deb); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); print $writerfh "get_deb_parts $file\n"; my $response = <$readerfh>; chomp($response); if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } else { while (<$readerfh>) { last if (/^\.$/); s/^\.\././; chomp; push @parts, [$_]; } } } else { open(DEB, "<", (glob $deb)[0]) || die _die("Couldn't open $deb: $!"); if (read(DEB, $_, 8) != 8) { die _die("Couldn't open $deb: ar format b0rken [Couldn't read first 8 bytes]"); } elsif ($_ ne "!\n") { die _die("Couldn't open $deb: ar format b0rken"); } do { my $line = ; if ($line =~ /\S/) { #This should help with additional newlines #debian-binary 1075243548 0 0 100644 4 ` my $name = substr($line, 0, 16); $name =~ s/\s*//g; my $length = substr($line, 48, 10); $length =~ s/\s*//g; next if (!$name && $length && $length =~ /^\d+\s*$/); seek (DEB, $length, 1) or die _die("Couldn't read $name in $deb: File too short!"); if ($length % 2) { seek (DEB, 1, 1) or die _die("Couldn't read $name in $deb: File too short!"); } push @parts, [$name]; } } while (!eof(DEB)); close DEB; } return @parts; } =pod =head2 I<@debs> = get_debs_from_changes (I<$file>, I<\$changes_signed>) Parses I<$file> as Debian .changes file and returns all listed debs. The dirname of I<$file> is prepended to the debs, which means that the returned URIs should exist. If I<$file> is signed, I<$changes_signed> is set to "yes". =cut sub get_debs_from_changes { my ($changes, $changes_signed) = @_; my $changes_path = dirname($changes); my @debs; if ($changes =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($changes); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); print $writerfh "get_debs_from_changes $file\n"; my $response = <$readerfh>; chomp($response); if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } else { $$changes_signed = "yes" if $response =~ /^200 ok debs in signed/; while (<$readerfh>) { last if (/^\.$/); s/^\.\././; chomp; if (defined ($user)) { push @debs, "ssh://$user\@$host:$_"; } else { push @debs, "ssh://$host:$_"; } } } } else { open (CHANGES, "<", $changes) || die _die("$changes: Can't open file: $!"); while () { $$changes_signed = "yes" if /-----BEGIN PGP SIGNED MESSAGE-----/; last if /^Files:/ } while () { chomp; if (/^ [^ ]+ \d+ [^ ]+ [^ ]+ (.+)$/) { push @debs, $changes_path."/".$1 if $1 =~ /^(.+\.deb)$/; } elsif (/^\s*$/) { last; } else { print STDERR "$changes corrupted\n"; } } close CHANGES; } return @debs; } =pod =head2 I<\@digests> = get_deb_digests (I<$deb>) Parses I<$deb> and returns the meta-data of the included files. The read data is piped to md5sums and sha1sums, which create the respective digests. The digests, the filename and the size are put in an anymous array looking like this: [B<$name>, B<$size>, B<$sha1sum>, B<$md5sum>]. One of these arrays is pushed to I<@digests> for every file in I<$deb>. =cut sub get_deb_digests { my $deb = shift; my @digests; if ($deb =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($deb); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); print $writerfh "get_deb_digests $file\n"; my $response = <$readerfh>; chomp($response); if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } else { while (<$readerfh>) { last if (/^\.$/); s/^\.\././; chomp; my ($name, $size, $sha1sum, $md5sum) = split / /, $_; push @digests, [$name, $size, $sha1sum, $md5sum]; } } } else { open(DEB, "<", (glob $deb)[0]) || die _die("Couldn't open $deb: $!"); if (read(DEB, $_, 8) != 8) { die _die("Couldn't open $deb: ar format b0rken [Couldn't read first 8 bytes]"); } elsif ($_ ne "!\n") { die _die("Couldn't open $deb: ar format b0rken"); } do { my $line = ; if ($line =~ /\S/) { #This should help with additional newlines my ($name, $size, $md5sum, $sha1sum); #debian-binary 1075243548 0 0 100644 4 ` $name = substr($line, 0, 16); $name =~ s/\s*//g; my $length = substr($line, 48, 10); $length =~ s/\s*//g; next if (!$name && $length && $length =~ /^\d+\s*$/); $size = $length; my ($part, $read_length, $md5sum_input, $md5sum_output, $sha1sum_input, $sha1sum_output); open2($md5sum_output, $md5sum_input, qq{md5sum}); if (-x "/usr/bin/sha1sum") { open2($sha1sum_output, $sha1sum_input, qq{sha1sum}); } elsif (-x "/usr/bin/gpg") { #We need this for woody *sigh*: open2($sha1sum_output, $sha1sum_input, qq{gpg --print-md sha1 | tr 'A-Z' 'a-z' | sed 's/ //g'}); } else { die _die("Can't compute sha1sum, please install sha1sum or gpg"); } do { $read_length = ($length > 4096) ? 4096 : $length; $length -= $read_length; if (read (DEB, $part, $read_length) != $read_length) { die _die("Couldn't read $name in $deb: File too short!"); } if ($read_length % 2 && read (DEB, $_, 1) != 1) { die _die("Couldn't read $name in $deb: File too short!"); } print $md5sum_input $part; print $sha1sum_input $part; } while ($length > 0); close $md5sum_input; close $sha1sum_input; ($md5sum = <$md5sum_output>) =~ s/[\s\n\r-]//g; ($sha1sum = <$sha1sum_output>) =~ s/[\s\n\r-]//g; push @digests, [$name, $size, $sha1sum, $md5sum]; } } while (!eof(DEB)); close DEB; } return \@digests; } =pod =head2 I<$md5sum> = get_file_md5sum (I<$file>) Returns the md5sum for I<$file>. =cut sub get_file_md5sum { my $file = shift; my $md5sum; if ($file =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($file); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); if ($prot_version < 5) { die _die("remote dpkg-sig on $host is too old and can't return the needed md5sum of a file."); } print $writerfh "get_file_md5sum $file\n"; my ($response, $t); $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response =~ /^200 ok md5sum is (\S+)/) { $md5sum = $1; } else { die _die("remote dpkg-sig on $host returned \"$response\""); } } else { chomp ($md5sum = `md5sum $file | cut -d " " -f 1`); } return $md5sum; } =pod =head2 I<$part_data> = get_archive_part (I<$archive>, I<$part_name>) Returns the content of I<$part_name> in the ar archive I<$archive>. =cut sub get_archive_part { my ($deb, $part_name) = @_; my $part = ''; if ($deb =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($deb); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); print $writerfh "get_archive_part $part_name $file\n"; my $response = <$readerfh>; if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } else { while (<$readerfh>) { last if (/^\.$/); s/^\.\././; chomp; $part .= "$_\n"; } } } else { open(DEB, "<", $deb) || die _die("Couldn't open $deb: $!"); if (read(DEB, $_, 8) != 8) { die _die("Couldn't open $deb: ar format b0rken [Couldn't read first 8 bytes]"); } elsif ($_ ne "!\n") { die _die("Couldn't open $deb: ar format b0rken"); } while (!eof(DEB)) { my $line = ; #debian-binary 1075243548 0 0 100644 4 ` my $name = substr($line, 0, 16); my $length = substr($line, 48, 10); next if (!$name && $length && $length =~ /^\d+\s*$/); my $tmp_part; if (read (DEB, $tmp_part, $length) != $length) { die _die("Couldn't read $name in $deb: File too short!"); } if ($length % 2 && read (DEB, $_, 1) != 1) { die _die("Couldn't read $name in $deb: File too short!"); } if ($name =~ /^$part_name\s*$/) { $part = $tmp_part; last; } } close DEB; } return $part; } =pod =head2 I<@file_data> = read_control_file (I<$file>) Returns the content of I<$file> as array with one line per element. =cut sub read_control_file { my $file = shift; my @file_data; die _die("This only returns debian control files (ending with .changes or dsc)") if $file !~ /\.(?:dsc|changes)$/; if ($file =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($file); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); if ($prot_version < 3) { die _die("remote dpkg-sig on $host is too old and can't return the needed control file data."); } print $writerfh "read_control_file $file\n"; my $response = <$readerfh>; chomp($response); if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } else { while (<$readerfh>) { last if (/^\.$/); s/^\.\././; push @file_data, $_; } } } else { open (FH, $file) or die _die("Can't open $file: $!"); @file_data = ; close FH; } return @file_data; } =pod =head2 I<@file_info> = write_control_file (I<$file>, I<\@data>) Writes contents of I<\@data> to I<$file>. Returns new md5sum and size of I<$file>. =cut sub write_control_file { my ($file, $data) = @_; my ($response, $t, $new_md5sum, $new_file_size); die _die("This only writes debian control files (ending with .changes or dsc)") if $file !~ /\.(?:dsc|changes)$/; if ($file =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($file); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); if ($prot_version < 3) { die _die("remote dpkg-sig on $host is too old and can't return the needed control file data."); } print $writerfh "write_control_file $file\n"; $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response =~ /^300 /) { print $writerfh @$data; print $writerfh ".\n"; $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response =~ /^200 .+New md5sum, size: ([^ ]+) (\d+)/) { $new_md5sum = $1; $new_file_size = $2; } else { die _die("remote dpkg-sig on $host returned \"$response\""); } } else { die _die("remote dpkg-sig on $host seems to be weird. Can't parse \"$response\""); } } else { _write_file($file, @$data); chomp ($new_md5sum = `md5sum $file | cut -d " " -f 1`); $new_file_size = (stat($file))[7]; } return ($new_md5sum, $new_file_size); } =pod =head2 I<@files> = glob_exp (I<$exp>) Returns the result of globbing I<$exp> as array. =cut sub glob_exp { my $exp = shift; my @files; if ($exp =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($exp); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); print $writerfh "glob_exp $file\n"; my $response = <$readerfh>; if ($response !~ /^200 /) { die _die("remote dpkg-sig on $host returned \"$response\""); } else { while (<$readerfh>) { last if (/^\.$/); s/^\.\././; chomp; if (defined ($user)) { push @files, "ssh://$user\@$host:$_"; } else { push @files, "ssh://$host:$_"; } } } } else { push @files, glob($exp); } return @files; } =pod =head2 I<$file_readable> = file_readable (I<$file>) Returns a true value if I<$file> is readable. =cut sub file_readable { my $file = shift; if ($file =~ m!^ssh://!) { my ($user, $host, $file) = split_ssh_uri($file); my ($readerfh, $writerfh, $prot_version) = get_ssh_connection($user, $host); if ($prot_version < 4) { print "W: remote dpkg-sig on $host is too old and can't return the needed data. .dsc not signed"; return 0; } print $writerfh "file_readable $file\n"; my ($response, $t); $response = ''; do { read($readerfh, $t, 1); $response .= $t } while ($t ne "\n"); chomp($response); if ($response =~ /^200 /) { return 1; } elsif ($response =~ /^400 /) { return 0; } else { die _die("remote dpkg-sig on $host returned \"$response\""); } } else { return -r $file; } } =pod =head2 I<@ssh_uri_parts> = split_ssh_uri (I<$uri>) Splits an ssh URI $uri into a B<$user>, B<$host> and B<$path> part. =cut sub split_ssh_uri { my ($uri) = @_; my ($user, $host, $path); #ssh://$USER@$HOST:$PATH if ($uri =~ m!^ssh://(?:([^@\s]+)@)?(\S+):(.+)!) { ($user, $host, $path) = ($1, $2, $3); $user ||= undef; die _die("$uri: Please specify at least a host to connect to.") if !$host; die _die("$uri: Please specify a path on the remote host.") if !$path; } else { die _die("$uri is no ssh uri!"); } return ($user, $host, $path); } =pod =head2 I<@ssh_connection_info> = get_ssh_connection (I<$user>, I<$host>) Opens a ssh connection to I<$host> as user I<$user>, directly calling B. It checks if the remote B is compatible to the current version and returns the B<$pid>, the Read-Filehandle B<$readerfh> and the Write-Filehandle B<$writerfh>. =cut sub get_ssh_connection { my ($user, $host) = @_; my $connection_id = (defined ($user))?"$user\@$host":$host; $remote_ssh_port ||= ""; $remote_ssh_port =~ s/^\s*(\d+)\s*$/-p $&/; if (! $ssh_connections{$connection_id} ) { my ($readerfh, $writerfh); die _die("No ssh installed, we need it to connect to the remote host.") if (not `which ssh`); my $pid = open2($readerfh, $writerfh, qq{ssh $remote_ssh_port $connection_id '$remote_dpkg_sig --client $DEBUG 2>/dev/null || echo "No dpkg-sig available"' 2>&1}); my $response = <$readerfh>; if ($response && $response !~ /protocol version/i) { if ($response && $response =~ /No dpkg-sig available/) { die _die("No $remote_dpkg_sig on remote host installed."); } else { if ($response) { die _die("ssh returned $response"); } else { die _die("Some problem with the ssh connection $connection_id occured"); } } } if ($response !~ /protocol version (\d+)$/i || $1 < 6) { die _die("dpkg-sig on $host is too old (we need protocol version 6)"); } $ssh_connections{$connection_id} = [$pid, $readerfh, $writerfh, $1]; } return (@{$ssh_connections{$connection_id}}[1,2,3]); } =pod =head2 sign_control_files (I<$changes_file>) This works like debsign: =over 4 =item Checks if a .dsc exists. =item If the .dsc should be signed, it tries to do so.. =item Writes the new .dsc with the new signature. =item Reads I<$changes_file> and puts in the new size/md5sum of the .dsc. =item Signs I<$changes_file> and write the signed copy back. =back =cut sub sign_control_files { my $file = shift; my $sign_dsc = $sign_changes =~ /full$/ ? 1 : 0; my ($dsc, $new_dsc_md5sum, $new_dsc_size); $dsc = "$1.dsc" if ($file =~ /^(.+)_[^ _]+.changes/ && file_readable("$1.dsc")); #Clean the tempdir: unlink ($tempdir."/dsc.unsigned"); unlink ($tempdir."/dsc.signed"); unlink ($tempdir."/changes.unsigned"); unlink ($tempdir."/changes.signed"); if ($sign_dsc && $dsc) { open (DSC, ">", $tempdir."/dsc.unsigned") || die _die("Can't open $tempdir/dsc.unsigned: $!"); my @data = read_control_file($dsc); for (my $i=0;$i<@data;$i++) { if ($data[$i] =~ /^-----BEGIN PGP SIGNED MESSAGE-----$/) { if ($sign_changes eq "force_full") { $sign_dsc = 1; } elsif (! $batch) { print "The .dsc file is already signed.\nWould you like to use the current signature? [Yn] "; chomp(my $answer = lc()); $sign_dsc = 0 unless ($answer eq "n" || $answer eq "no"); } else { $sign_dsc = 0; } while(defined $data[$i]) { last if $data[$i++] =~ /^\s*$/ } } elsif ($data[$i] =~ /^\s*$/) { last; } print DSC $data[$i]; } print DSC "\n"; close DSC; if ($sign_dsc) { #Sign it: sign_file($tempdir."/dsc.unsigned",$tempdir."/dsc.signed", "no_detach") if $sign_dsc; #Read and write them to the fitting location: open (DSC, $tempdir."/dsc.signed") || die _die("Can't open $tempdir/dsc.signed: $!"); @data = ; close DSC; ($new_dsc_md5sum, $new_dsc_size) = write_control_file($dsc, \@data); print "Signed .dsc $dsc\n" unless $batch; } } #Now the changes file: open (CHANGES, ">", $tempdir."/changes.unsigned") || die _die("Can't open $tempdir/changes.unsigned: $!"); my $basename_dsc = basename($dsc) if $dsc; for (read_control_file($file), "\n") { #If we've changed the .dsc file, we have to use the new values in the .changes: if ($basename_dsc && $new_dsc_md5sum && $new_dsc_size && $_ =~ /\Q$basename_dsc\E$/) { s/^ [^ ]+ \d+ (.+)$/ $new_dsc_md5sum $new_dsc_size $1/; } print CHANGES $_; } close CHANGES; sign_file($tempdir."/changes.unsigned",$tempdir."/changes.signed", "no_detach"); my @data; open (CHANGES, $tempdir."/changes.signed") || die _die("Can't open $tempdir/changes.signed: $!"); @data = ; close CHANGES; write_control_file($file, \@data); print "Signed .changes $file\n" unless $batch; } =pod =head2 sign_file (I<$in_file>, I<$out_file>, I<$no_detach>) Signs I<$in_file> with gpg and puts the detached signature in I<$out_file>. If I<$no_detach> is true, I<$out_file> is a clearsigned copy of I<$in_file>. =cut sub sign_file { my ($in_file, $out_file, $no_detach) = @_; my @cmdline = ("gpg", "--openpgp", "--armor", "--output", $out_file); if ($no_detach) { push @cmdline, "--clearsign"; } else { push @cmdline, "--detach-sign"; } if ($key) { push (@cmdline, "--default-key", "'$key'"); print "Default key: $key\n" if $verbose; } elsif ($maintainer) { push (@cmdline, "--default-key", "'$maintainer'"); } if ($pass_file) { push (@cmdline, "--no-tty", "--batch", "--passphrase-fd", "42", "42<$pass_file"); print "Using passphrase from $pass_file\n" if $verbose; } elsif ($passphrase) { push (@cmdline, "--no-tty", "--batch", "--passphrase-fd", "0"); print "Using cached passphrase\n" if $verbose; } push (@cmdline, $gpgoptions) if $gpgoptions; print "Signing $in_file with key ".($key || "of $maintainer")."\n" if $verbose; push (@cmdline, $in_file, "2>&1"); open (GPG, "| ".join " ", @cmdline) || die _die("Signing failed: $!"); print GPG $passphrase, "\n" if $passphrase; close GPG; die _die("Signing failed. Error code: $?") if $?; } sub read_cmds { $DEBUG && (open (LOG, ">", "/tmp/dpkg-sig.log") || die _die("Couldn't open log: $!")); $DEBUG && select LOG; $|=1; $DEBUG && select STDOUT; sub send { print STDOUT @_; $DEBUG && print LOG "Sent: ", @_; } sub read { $_ = ; $DEBUG && print LOG "Received: ", $_; return $_ } ; while ($_ = &read()) { chomp; if (/^get_deb_digests (.+)$/) { my $r = eval { get_deb_digests ($1) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send ("500 error: $@\n"); } else { &send("200 ok digests for $1 follow\n"); &send(join (" ", @$_), "\n") for @$r; &send(".\n"); } } elsif (/^get_deb_parts (.+)$/) { my @r = eval { get_deb_parts ($1) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send ("500 error: $@\n"); } else { &send("200 ok parts of $1 follow\n"); &send($_->[0] . "\n") for @r; &send(".\n"); } } elsif (/^get_archive_part ([^ ]+) (.+)$/) { my $r = eval { get_archive_part ($2, $1) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: $@\n"); } else { &send("200 ok part $1 of $2 follows\n"); for (split (/\n/, $r)) { s/^\./../; &send("$_\n"); } &send(".\n"); } } elsif (/^read_control_file (.+)$/) { my @r = eval { read_control_file ($1) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: $@\n"); } else { &send("200 ok file $1 follows\n"); for (@r) { s/^\./../; &send("$_"); } &send(".\n"); } } elsif (/^get_debs_from_changes (.+)$/) { my $changes_signed = 0; my @r = eval { get_debs_from_changes ($1, \$changes_signed) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: $@\n"); } else { if ($changes_signed) { &send("200 ok debs in signed $1 follow\n"); } else { &send("200 ok debs in $1 follow\n"); } for (@r) { s/^\./../; &send("$_\n"); } &send(".\n"); } } elsif (/^glob_exp (.+)$/) { my @r = eval { glob_exp ($1) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: $@\n"); } else { &send("200 ok files matching \"$1\" follow\n"); for (@r) { s/^\./../; &send("$_\n"); } &send(".\n"); } } elsif (/^file_readable (.+)$/) { my $r = eval { file_readable ($1) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: $@\n"); } else { if ($r) { &send("200 ok file readable\n"); } else { &send("400 not ok file not readable\n"); } } } elsif (/^get_file_md5sum (.+)$/) { my $r = eval { get_file_md5sum ($1) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: $@\n"); } else { &send("200 ok md5sum is $r\n"); } } elsif (/^add_sig_to_deb ([^ ]+) (.+)$/) { my ($sig_name, $deb, $sig) = ($1, $2, ''); &send("300 ok waiting for data\n"); while ($_ = &read()) { last if (/^\.$/); s/^\.\././; $sig .= $_; } my $r = eval { add_sig_to_deb ($deb, $sig, $sig_name) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: "); &send($@, "\n"); } else { &send("200 ok added sig to $deb. New data follows\n"); &send(join (" ", @$r), "\n"); } } elsif (/^correct_changes_file (.+)$/) { my ($changes, $new_changes_data) = ($1, {}); &send("300 ok waiting for data\n"); while ($_ = &read()) { last if (/^\.$/); s/^\.\././; chomp; my ($md5sum, $size, $name) = split (/ /, $_, 3); $new_changes_data->{$name} = [$md5sum, $size, $name]; } my $r = eval { correct_changes_file ($changes, $new_changes_data) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: "); &send($@, "\n"); } else { &send("200 ok $changes corrected\n"); } } elsif (/^write_control_file (.+)$/) { my ($file, @data) = ($1, ()); &send("300 ok waiting for data\n"); while ($_ = &read()) { last if (/^\.$/); s/^\.\././; push @data, $_; } my @r = eval { write_control_file ($file, \@data) }; if ($@) { chomp($@); $@ =~ s/\n/\t/g; &send("500 error: "); &send($@, "\n"); } else { &send("200 ok $file written. New md5sum, size: $r[0] $r[1]\n"); } } elsif (/^quit\s*$/) { &send("200 ok Bye!\n"); exit; } else { &send("501 unknown command ".(split / /, $_)[0]."\n"); } } $DEBUG && close LOG; } sub process_cli_options { exit 1 unless GetOptions("sign|s=s" => \$sign, "list|l|t" => \$list, "verify|check|c" => \$verify, "verify-role=s" => \$verify_role, "verify-exact=s" => \$verify_exact, "get-hashes=s" => \$get_hashes, "sign-hashes" => \$sign_hashes, "write-signature" => \$write_signature, "client" => \$client, "help" => \$help, #Options: "default-key|k=s" => \$key, "cache-passphrase|p" => \$cache_pass, "passphrase-file|f=s" => \$pass_file, "m=s" => \$maintainer, "e=s" => \$maintainer_pr, "verbose|v+" => \$verbose, "also-v2-sig" => \$check_v2_sig, "also-v3-sig" => \$check_v2_sig, "sign-changes|a:s" => \$sign_changes, "batch:i" => \$batch, "gpg-options|g=s" => \$gpgoptions, "remote-dpkg-sig|r=s" => \$remote_dpkg_sig, "remote-ssh-port|o=i" => \$remote_ssh_port, ); $check_v2_sig = ($check_v2_sig && $check_v2_sig eq "false"?0:"yes"); $check_v3_sig = ($check_v3_sig && $check_v3_sig eq "false"?0:"yes"); die _die('Please use only one of --sign, --list, --verify[-role|-exact], --get-hashes, --write-signature, --help and --client!') if (! !$sign + ! !$list + ! !$verify + ! !$verify_role + ! !$verify_exact + ! !$client + ! !$get_hashes + ! !$sign_hashes + ! !$write_signature + ! ! $help> 1); $maintainer_pr && ($maintainer = $maintainer_pr); if (!$sign && !$list && !$verify && !$verify_role && !$verify_exact && !$client && !$get_hashes && !$sign_hashes && !$write_signature && !$help) { if (@ARGV) { $verify = 1; print "I: No action requested, verifying files.\n"; } else { $help = 1; } } } sub load_config { my @configfiles = @_; for my $configfile (@configfiles) { $configfile = (glob($configfile))[0]; if ($configfile && -r $configfile) { %config = %{read_config_file($configfile) || {}}; } } ($maintainer = ($config{'DEBSIGN_MAINT'} || "")) =~ s/^"(.+)"$/$1/ if ! $maintainer; ($key = ($config{'DPKGSIG_KEYID'} || $config{'DEBSIGN_KEYID'} || "")) =~ s/^"(.+)"$/$1/ if ! $key; ($cache_pass = ($config{'DPKGSIG_CACHE_PASS'} || "")) =~ s/^"(.+)"$/$1/ if ! $cache_pass; ($sign_changes=($config{'DPKGSIG_SIGN_CHANGES'} ||""))=~ s/^"(.+)"$/$1/ if ! $sign_changes; if (! $sign_changes) { $sign_changes = "auto"; } elsif (! grep {$sign_changes eq $_} qw(no auto yes full force_full)) { if ($sign_changes =~ /(?:deb|changes)$/) { push @files, $sign_changes; $sign_changes = "yes"; } else { print "W: Unrecognized argument to --sign-changes, using \"auto\": $sign_changes\n"; $sign_changes = "auto"; } } if ($sign && $cache_pass && ! $pass_file) { eval { require Term::ReadKey; }; if ($@) { print STDERR "Couldn't load Term::ReadKey. Please install. Passphrase caching disabled.\n"; } else { my $passphrase_valid = 0; while (! $passphrase_valid) { print "The passphrase for ".($key || "your default key").": "; Term::ReadKey::ReadMode("noecho"); chomp($passphrase = Term::ReadKey::ReadLine(0)); Term::ReadKey::ReadMode("restore"); print "\n"; #Try to use the key: open (TMP, ">", $tempdir . '/tmp-file') or die "Can't open $tempdir/tmp-file: $!"; print TMP "Foobar"; close TMP; my $error; eval { my @sign_cmd = ("gpg", "--clearsign"); push @sign_cmd, ("--default-key", $key) if $key; push @sign_cmd, ("--no-tty", "--batch", "--passphrase-fd", "0"); push @sign_cmd, $gpgoptions if $gpgoptions; push @sign_cmd, "$tempdir/tmp-file"; my ($write_handle, $read_handle); open3 ($write_handle, $read_handle, undef, @sign_cmd) || die _die("Signing failed: $!"); print $write_handle $passphrase, "\n"; close $write_handle; $error = join "", <$read_handle>; close $read_handle; }; if ($error && $error =~ /^gpg:.+bad passphrase\n/) { $passphrase_valid = 0; print STDERR "Wrong passphrase for " . (($key)?"key $key":"default key") . "!\n"; } else { $passphrase_valid = 1; } } } } } sub help { print < Sign files -c,--verify Verify signatures on files -l,--list List signatures on files --get-hashes Get hashes file for files --sign-hashes Sign hashes file --write-hashes Write sigs from signed hashes file Options: -m,-e Specify maintainer name to use when signing -k Specify keyid to use when signing -v,--verbose Makes dpkg-sig more verbose --also-v2-sig Verify sigs from dpkg-sig 0.2 and earlier --also-v3-sig Verify sigs from dpkg-sig 0.3-0.10 -a,--sign-changes Tells whether also sign the changes and dsc-files. The default is auto. -g,--gpgoptions DANGEROUS: Specify custom gpg options. -p,--cache-passphrase INSECURE: Caches gpg passphrase in dpkg-sig -f,--passphrase-file INSECURE: Let gpg use passphrase from EOH } sub _die { chomp(my $msg = shift || "No error msg given! This is a bug, hurt the author!"); my $i = 0; while ($_ = (caller($i++))[3]) { if ($_ && $_ eq "(eval)") { return $msg; } } my $code = shift || 1; my $line = (caller)[2]; print STDERR "E: $msg\n"; exit $code; } sub _read_file { my $file = shift; my $content; open (FH, $file) or die _die "Can't open $file: $!"; $content = join "", ; close FH; return $content; } sub _write_file { my $file = shift; my @content = @_; open (FH, ">", $file) or die _die "Can't open $file for writing: $!"; print FH @content; close FH; } =pod =head1 AUTHOR B and this manpage were written by Andreas Barth und Marc Brockschmidt. They are Copyright (C) 2003, 2004 by them and released under the GNU General Public Licence version 2 or later; there is NO WARRANTY. See F and F for details. =cut # vim:set shiftwidth=4: # vim:set tabstop=4: # vim:set noet: # vim:set shiftround: dpkg-sig-0.13.1+nmu1/dpkg-sig.pod0000644000000000000000000002366410377444400013272 0ustar =pod =head1 NAME B - Debian package archive (.deb) signature generation and verification tool =head1 SYNOPSIS B B<[options]> B<--sign> I I<[archive|changes]>+ B B<[options]> B<--verify> I<[archive]>+ B B<[options]> B<--verify-role> I I<[archive]>+ B B<[options]> B<--verify-exact> I I<[archive]>+ B B<[options]> B<--list> I<[archive]>+ B B<[options]> B<--get-hashes> I I<[archive|changes]>+ B B<[options]> B<--sign-hashes> I<[hashes-archive]>+ B B<[options]> B<--write-signature> I<[hashes-archive]>+ =head1 DESCRIPTION B creates and verifies signatures on Debian archives (.deb-files). Use higher-level tools to install and remove packages from your system, and to verify a signature as acceptable for your system. A usage example can be found at the end of this man page. =head1 ACTION OPTIONS =over 4 =item B<--sign>, B<-s> I Signs a standard-conforming Debian archive. I gives the name of the signature (usually 'builder' for the builder of the .deb). The signature is made using your default key, unless specified via any explicit or implicit option (see below). If one or more .changes-files are given, the md5sums inside the .changes file(s) are also updated. If a .changes file was gpg-signed, the signature is removed when updating the md5sums. =item B<--verify>, B<-c>; B<--verify-role>; B<--verify-exact> Verifies a signature on the given archive file. B<--verify> and B<-c> just check all signatures; B<--verify-role> verifies all signatures with a given role, and B<--verify-exact> wants the exact name of the archive member (without the leading _gpg). However, both commands also accept perl regular expressions as the name. All verify variants output (in turn for each signature) either a line consisting of GOODSIG, role, gpg-fingerprint and signature time (in seconds since 1970-1-1 0:00:00 UTC), or BADSIG. Starting from version 0.12, B returns 2 if a bad signature was found when trying to verify. If an unknown key was used to sign a .deb, B returns 3. =item B<--list>, B<-l>, B<-t> Lists all names inside the deb that look like a signature. =item B<--get-hashes>, B<--sign-hashes>, B<--write-signature> B<--get-hashes> creates an B(1) archive containing a control file part and files with the digests of all the .debs specified on the command-line or named in the .changes file(s) specified on the command-line. After that, you can transfer this (small) file to another machine, for example an offline system containing your gpg keys. (Yep, that's paranoid!) B<--sign-hashes> then signs this file containing the digests (in fact, it replaces the digests parts with their signatures). Now transfer the signed file back to the machine where you created the hashes and use B<--write-signature> to add the signatures from the archive to the deb. =back =head1 OPTIONS =over 4 =item B<-m> I Specify the maintainer name to be used for signing. =item B<-e> I Same as B<-m> but takes precedence. =item B<-k> I Specify the key ID to be used for signing; overrides any B<-e> or B<-m> option. =item B<--verbose> Get some more details. =item B<--batch=1> Gurantees that the non-verbose output will not change. Use this if you want to parse the output. =item B<--also-v3-sig> The signature format changed between version 0.10 and 0.11. If you want to verify old signatures too, try this switch. =item B<--also-v2-sig> The signature format changed between version 0.2 and 0.3. If you want to verify old signatures too, try this switch. =item B<--cache-passphrase>, B<-p> Caches the gpg-passphrase inside B. This needs the suggested package C. Be warned: Doing this is insecure, B doesn't protect the memory it uses to store the passphrase. =item B<--sign-changes>, B<-a> [ no | auto | yes | full | force_full ] Tells whether also sign the .changes and .dsc-files. The default is I, which means that the .changes-file is re-signed if it was signed before. The other values are I (don't sign .changes, and remove an existing signature), I (always add a signature to .changes), I (always add a signature to .changes, and also sign the .dsc-file if there was no previous signature; otherwise ask) and I (always add a signature to both the .changes and .dsc files). =item B<--remote-dpkg-sig>, B<-r> I Use this if you want to specify where B can find the B executable on the remote machine. This is useful if you're not able/allowed to install B as a .deb. To do that, copy the script to something like F<~/bin/dpkg-sig> on the remote system. After that, you can call your local B with something like the following to use the remote signing/verifying features: C =item B<--remote-ssh-port>, B<-o> I Port of the B on the remote host. Default value is 22. =back =head1 MORE OPTIONS These options should normally not be used, but are here for completeness. Be warned: Use them only if you really know what you are doing. =over 4 =item B<--gpgoptions>, B<-g> I Use this to pass arbitrary options to B(1) whenever a file is signed. As this can lead to broken signatures, test your changes carefully. =item B<--passphrase-file>, B<-f> I Tells gpg to use the passphrase in I to sign. Be warned: Doing this is insecure, DON'T use this feature. However, in some cases (e.g. automatic signing on a buildd) this could be useful, and is still better than using a gpg-key without passphrase. You can gain at least some security by putting this file on a ramdisk, but it would be better to use B(1). =back =head1 CONFIGURATION VARIABLES The two configuration files F and F<~/.devscripts> are sourced in that order to set configuration variables. Command line options can be used to override configuration file settings. Environment variable settings are ignored for this purpose. The currently recognised variables are: =over 4 =item B This is the B<-m> option. =item B, B This is the B<-k> option, and B has most precedence. =item B This is the B<--sign-changes> option. Valid values are I, I, I, I and I. =item B This is the B<--cache-passphrase> option. Set this to a true value to enable it. =back =head1 SIGNATURE FORMAT The signatures created by B are added in a strict standard-conforming way to the .deb archive file. The signature itself is made on a file formatted like a Debian control file. The fields of this file are: Version, specifying a B file version number; Signer, giving the name of the signer; Date and Role, and finally Files, which gives the digests of the prior contents of the .deb archive file. Note that this includes any prior signatures made by B. Thus it is possible to verify any signature by hand with just B(1), B(1), B(1) and B(1). Signing a list of digests has the advantage that it is possible to perform remote signatures without transferring the whole archive file. This does require one to trust the remote machine, though! Further details can be found at L. =head1 REMOTE SIGNING B can sign remote files using B(1) without transferring the whole file to the local machine, or the key to the remote machine. Simply specify the file with C, and have B installed on the remote machine. (See also the B<--remote-dpkg-sig> option above.) Remote signing supports the usual filename globbing. Remote signing has been tested, but is at the moment considered a more experimental feature. =head1 BUGS, TODO B should be able to also verify signatures made by older code. This may be added in a later version. B assumes that any given archive is strictly standard-compatible. This is valid for archives created by B - but if you're not sure about a archive, verify this yourself, or live with the risk of a bad signature. More documentation about the signature format should be added. Deal better with expired etc. keys and signatures. Better inclusion into the other tools like B. And of course: Still missing is testing, testing and testing B. =head1 USAGE EXAMPLE A typical use is to sign packages before a (maintainer-)upload. This can be done by running B and afterwards calling C. If you want to do all signing with B you could run C and afterwards call C. If you do this, there is no need to call B any more, as B does all the signing for you. If you don't want to type in your passphrase multiple times, then you could add the option B<--cache-passphrase>. The options B<--sign-changes> and B<--cache-passphrase> could be replaced with setting the variables B respectivly B (set the later one set to a true value) in F<~/.devscripts>. The key-id is automatically set from F and F<~/.devscripts>, but could be overriden via the B<-m>, B<-e> or B<-k> command line options (see above). =head1 SEE ALSO B(5), B(1), B(8), F =head1 AUTHOR B and this manpage were written by Andreas Barth and Marc Brockschmidt. They are Copyright (C) 2003-2006 by them and released under the GNU General Public Licence version 2 or later; there is NO WARRANTY. See F and F for details. Some parts of this manpage are taken from debsign. =cut dpkg-sig-0.13.1+nmu1/TODO0000644000000000000000000000177610034063347011546 0ustar * Understand the way debsign works and verify its signatures. * What happens with expired keys? * Get about 1000 DDs to test the thing and report bugs. * Create patches for katie, the buildds and dpkg to sign and verify signatures everytime a package is touched. * We need a way to check if the signature is done by a DD ... errr ... Reworded: To check if the package is signed with a DD's gpg key (ATM we don't see the difference between someone who rebuilt the package with his h4x0r3d sk111z signing as "builder" and a DD who's supposed to do so). Perhaps something with debian-keyring, gpg --no-default-keyring --keyring, but that won't work with NMs. Do we need a wanna-be-dd-keyring? Perhaps one with all keys of people with a package in the archive. Oh, and for non-Debian sources we would need a --accept-people-not-associated-with-debian switch. *sigh* We have to discuss this at least with the debian keyring manager, and perhaps some more DDs (#debian-devel? debian-devel@l.d.o?) dpkg-sig-0.13.1+nmu1/copyright0000644000000000000000000000203610377603155013006 0ustar This package was written by Andreas Barth und Marc Brockschmidt. It is Copyright (C) 2003, 2004 by them. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Upstream web site is http://dpkg-sig.turmzimmer.net/ See /usr/share/common-licenses/GPL for the full list of conditions. It is politly asked (but not legally required) that changes of this code are forwarded to the author, and that the upstream web site is mentioned. dpkg-sig-0.13.1+nmu1/debian/0000755000000000000000000000000012232531553012266 5ustar dpkg-sig-0.13.1+nmu1/debian/rules0000755000000000000000000000225510377603357013364 0ustar #!/usr/bin/make -f build: clean: dh_testdir dh_testroot dh_clean binary-arch: binary-indep: dh_testdir dh_testroot dh_clean mkdir -p debian/dpkg-sig/usr/share/man/man1 mkdir -p debian/dpkg-sig/usr/share/man/man7 mkdir -p debian/dpkg-sig/usr/share/doc/dpkg-sig mkdir -p debian/dpkg-sig/usr/share/lintian/overrides mkdir -p debian/dpkg-sig/usr/bin cp dpkg-sig debian/dpkg-sig/usr/bin pod2man --section=1 --release="Debian Project" --center="Debian GNU/Linux manual" dpkg-sig.pod debian/dpkg-sig/usr/share/man/man1/dpkg-sig.1 pod2man --section=7 --release="Debian Project" --center="Debian GNU/Linux manual" dpkg-sig debian/dpkg-sig/usr/share/man/man7/dpkg-sig.7 cp copyright debian/changelog debian/dpkg-sig/usr/share/doc/dpkg-sig cp debian/lintian.overrides debian/dpkg-sig/usr/share/lintian/overrides/dpkg-sig dh_installexamples dh_compress dh_gencontrol dh_md5sums #dh_md5sums excludes everything with DEBIAN in its path... cd debian/dpkg-sig && \ md5sum >> DEBIAN/md5sums \ usr/share/doc/dpkg-sig/examples/sample/debian/tmp/DEBIAN/control \ usr/share/doc/dpkg-sig/examples/sample/debian/tmp/DEBIAN/md5sums dh_fixperms dh_builddeb binary: binary-indep dpkg-sig-0.13.1+nmu1/debian/control0000644000000000000000000000121012232531511013655 0ustar Source: dpkg-sig Section: devel Priority: optional Uploaders: Andreas Barth Maintainer: Marc 'HE' Brockschmidt Build-Depends: debhelper (>= 4), perl Standards-Version: 3.6.2 Package: dpkg-sig Architecture: all Depends: perl, gnupg, libdigest-md5-perl, libconfig-file-perl Suggests: ssh, libterm-readkey-perl Description: create and verify signatures on .deb-files dpkg-sig is a low-level tool for creation and verification of signature on Debian binary packages (.deb-files). . The created signed packages are strict compatible with dpkg and the apt-utils. . Website is http://dpkg-sig.turmzimmer.net/ dpkg-sig-0.13.1+nmu1/debian/lintian.overrides0000644000000000000000000000027310377603237015661 0ustar dpkg-sig: zero-byte-file-in-doc-directory usr/share/doc/dpkg-sig/examples/sample/debian/tmp/tmp/test dpkg-sig: zero-byte-file-in-doc-directory usr/share/doc/dpkg-sig/examples/sample/test dpkg-sig-0.13.1+nmu1/debian/changelog0000644000000000000000000002526212232531553014147 0ustar dpkg-sig (0.13.1+nmu1) unstable; urgency=low * Non-maintainer upload. * Fix "FTBFS with perl 5.18: POD errors": fix POD by adding missing '=back'. (Closes: #723867) * debian/control: replace Build-Depends-Indep with Build-Depends. Both debhelper and perl are needed during clean (lintian error). -- gregor herrmann Fri, 25 Oct 2013 20:07:08 +0200 dpkg-sig (0.13.1) unstable; urgency=low * Non-Maintainer Upload by Gunnar Wolf * Updated dependency on libconfigfile-perl to libconfig-file-perl, updated the invocation of ConfigFile to Config::File, to avoid the warning for the deprecated call (Closes: #387016) -- Gunnar Wolf Mon, 20 Nov 2006 09:10:47 -0600 dpkg-sig (0.13) unstable; urgency=low * HE - dpkg-sig: + Fix -k option so that the maintainer name is still retrieved from the gpg key. Patch from Julian Gilbey , thanks. (Closes: #352727) + Remove --debug option (but leave code in so that manual code changes can reactivate the debug stuff). (Closes: #352723) - dpkg-sig.pod: + Update whole manpage, based on patch from Julian Gilbey . Thanks! (Closes: #352730) - examples/README: + Replace a left-over debsigs-ng.sample/md5sums.asc by digests.asc. Thanks for the note, Julian. (Closes: #342473) - debian/control: + Updated Standards-Version to 3.6.2 + Make me Maintainer, aba Co-Maint (mostly to get dpkg-sig on my QA overview page without co-maintained packages). - Make package lintian clean (includes overrides for empty files, they are empty for a reason). -- Marc 'HE' Brockschmidt Fri, 24 Feb 2006 01:05:53 +0100 dpkg-sig (0.12) unstable; urgency=low * HE - dpkg-sig: + [1-9A-F] describes possible hex digits not as well as [0-9A-F] (Yes, i'm a brainless idiot *sigh*). This should fix the Signer field for people using a 0x\S+0\S+ key id. + If passphrase caching enabled, we now check the passphrase at the beginning (and prompt again if something isn't ok) + Properly quote the $key and $maintainer arguments to gpg. (Closes: #308049) + Let ssh choose the username for a host if it wasn't specified in the URL, this allows the ssh config to do it's job. Thanks for the report, Marc. (Closes: #331122) + Handle ssh errors a *wee* *little* bit better. Almost no improvement, but enough to thank Marc for the report. (Closes: #331123) + Add missing chomps all over the place. + Replace '*changes' in a exec(grep) because that's crap and we know it (the .changes, not the crap). + Use another exit code when trying to verify and finding a bad signature. BAD SIG! GO TO YOUR ROOM! (Closes: #280559) + Check if an unknown key was used to create a signature when verifying a .deb. Output UNKNOWNSIG for those and exit with exitcode 3. + Don't use /usr/bin/perl -W, but /usr/bin/perl -w, which doesn't output stupid warnings. + die gracefully if a file couldn't be found (and don't end the show with a exit 0). -- Marc 'HE' Brockschmidt Tue, 8 Feb 2005 20:57:21 +0100 dpkg-sig (0.11) unstable; urgency=low * HE - dpkg-sig: + Change order of return values of get_md5sums. + Renamed get_md5sums to get_deb_digests. + Added get_deb_parts to allow faster listing of sigs. + Use md5sum(1) instead of Digest::MD5 + Splitted the verify_deb into one sub per signature version, this should allow to add new versions more easily. + Implemented new signature format suggested by weasel (thanks for that!). We now sign sha1sums, md5sums and the size of every part in the deb archive. The new format also transports more meta-information (Signer, Date and Role). (Closes: #276557) + Restructured dpkg-sig a bit to shorten the things done in main:: + Added a lot of documentation as POD. + Added --remote-ssh-port|-o as cli option to allow people to specify the remote sshd port. (Closes: #271454) - debian/rules: + Generate dpkg-sig.7 from the POD documentation in dpkg-sig. -- Marc 'HE' Brockschmidt Fri, 15 Oct 2004 17:07:16 +0200 dpkg-sig (0.10) unstable; urgency=high * HE - urgency=high is IMHO needed, as #268376 would be a serious problems for developers using stable. - dpkg-sig: + Really really really work without ConfigFile in client mode. (Closes: #268376) + Don't include the damn CVS dirs in the source. (Closes: #263106) -- Marc 'HE' Brockschmidt Fri, 27 Aug 2004 17:01:04 +0200 dpkg-sig (0.9) unstable; urgency=low * HE: - dpkg-sig: + Move the use statement for ConfigFile in a way that we don't need it in --client mode. Useful if you want to install the thing in your home on a remote machine. + Add --help to give a brief help statement. (Closes: #257262) + Use value from Changed-By as default key when signing. -- Marc 'HE' Brockschmidt Fri, 2 Jul 2004 23:30:24 +0200 dpkg-sig (0.8) unstable; urgency=low * HE: - debian/control: + Update the Uploaders field, i'm a DD now! + Depend on perl, not perl-base. - debian/rules: + Fix a dh_md5sum problem, it excludes all files with DEBIAN in their path (and we have such files as examples) - dpkg-sig: + Fixed the "Signed deb *changes" output to print out the signed deb, not the changes file it was referenced from. + Be a bit more verbose when signing .dscs and .changes. + Verify deb before signing it. [This lead also to a change on the return value of get_md5sums internally] -- Marc 'HE' Brockschmidt Thu, 11 Mar 2004 18:24:45 +0100 dpkg-sig (0.7) unstable; urgency=low * HE: - dpkg-sig: + Config file handling: Strip enclosing "s. + Die if gpg fails to sign. (Closes: #236183) + Rename get_md5_sums to get_md5sums. + Don't die when parsing a deb and reading a newline where we actually expected an ar header (can happen if people change stuff with vi). (Closes: #236185) + Cache md5sums in get_md5sums. + Don't ask for a passphrase if both -p/DPKGSIG_CACHE_PASS and -f are used (use the passphrase from the file specified with -f in that case) + Don't use die() if not needed for the weird stuff in eval{}. Created the cute _die() function to do that. + Added a "--remote-dpkg-sig" (also available as "-r") switch to allow users to specify where the dpkg-sig executable is. This is useful if you're not able to get the remote admin to install dpkg-sig. + Rearranged the main routine to drop duplicated code + Aliased add_file_to_ar_archive to add_sig_to_deb + add_sig_to_deb now starts a new ar archive if the file it should write to hasn't existed before. + New switches --get-hashes, --sign-hashes and --write-signature (also available as -h, -d and -w) The first one creates an archiv of hashes of a deb/changes, the second signs it (wherever you want) and the third one adds the signatures to the deb(s and corrects the changes file). + Make standard output more verbose. + Notice when people try dpkg-sig --sign foo --sign-changes *deb (--sign-changes uses an optional argument). Assume that this wasn't meant, choose yes for --sign-changes and process the files as other cli arguments. - dpkg-sig.1: + Document the new --remote-dpkg-sig stuff. + Document the new --get-hashes, --sign-hashes and --write-signature options. + Converted to POD, now available as dpkg-sig.pod - dpkg-sig.pod: + Added (converted from dpkg-sig.1) - debian/control: + Build-Depend on perl, we need pod2man to create the manpage. - debian/rules: + Create manpage from dpkg-sig.pod -- Marc 'HE' Brockschmidt Thu, 11 Mar 2004 14:04:35 +0100 dpkg-sig (0.6) unstable; urgency=high * he: - dpkg-sig: + Fixed bug with cached passphrases leading to invalid signatures. We don't use GnuPG anymore (we now talk to GPG directly). + Added a "--gpgoptions" (also available as "-g") to allow users to give gpg arbitrary options. + Don't use only -r to check if a .dsc file is readable. Replaced with file_readable, which can handle a ssh://HOST:FILE path. - debian/control: Remove the libgnupg-perl suggestion. - dpkg-sig.1: + We don't need the GnuPG perl module to read the passphrase from a file. + We don't need the GnuPG to use the cached passphrase. + Document the new "--gpgoptions" stuff. -- Marc 'HE' Brockschmidt Mon, 1 Mar 2004 19:08:40 +0100 dpkg-sig (0.5) unstable; urgency=low * he: - Added new switch "--passphrase-file" (also available as "-f"). Allows signers to store their gpg passphrase in a file. - Now signs *changes and *dsc without the help of debsign. This is very useful for people signing many debs, as it uses the cached passphrase or the file with the passphrase. - Added example of the signing process, with a sample package and all temp files. Have fun. - debian/{control,rules}: Changed to install the examples. Now uses debhelper version 4. * aba: - added the new options to the man page, and added an usage example. -- Andreas Barth Sun, 29 Feb 2004 16:41:09 +0100 dpkg-sig (0.4) unstable; urgency=high * Fixed the caching of the gpg passphrase. * Added support to (re)sign changes files with debsign. * Fixed a bug in the remote signing code (content of changes files deleted) * Added support for a --debug switch, leading to a log of the communication between master and client in the remote /tmp/dpkg-sig.log. Use with care. -- Marc Brockschmidt Tue, 17 Feb 2004 00:36:23 +0100 dpkg-sig (0.3) unstable; urgency=low * Marc 'HE' Brockschmidt is now Co-Maintainer (and sprinkled some perl-magic on it) * Remote signing now possible. * Change the signature format to V3. * Fix error with devscripts containing @. Closes: #229545 -- Andreas Barth Sun, 1 Feb 2004 22:18:38 +0100 dpkg-sig (0.2) unstable; urgency=low * Create temporary directory only once * Can now sign also changes-files. -- Andreas Barth Wed, 14 Jan 2004 21:53:08 +0100 dpkg-sig (0.1) unstable; urgency=low * Initial release. Closes: #223311 -- Andreas Barth Sun, 14 Dec 2003 22:36:49 +0100 dpkg-sig-0.13.1+nmu1/debian/compat0000644000000000000000000000000210020337237013460 0ustar 4 dpkg-sig-0.13.1+nmu1/debian/dpkg-sig.examples0000644000000000000000000000001310020337237015521 0ustar examples/* dpkg-sig-0.13.1+nmu1/examples/0000755000000000000000000000000010377602746012675 5ustar dpkg-sig-0.13.1+nmu1/examples/dpkg-sig-example_0.1.dsc0000644000000000000000000000106510134511572017071 0ustar -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Format: 1.0 Source: dpkg-sig-example Version: 0.1 Binary: dpkg-sig-example Maintainer: Andreas Barth Architecture: all Standards-Version: 3.6.1.0 Build-Depends-Indep: debhelper Uploaders: Marc Brockschmidt Files: a7beeef85a612c4cb3cf7e1e26f64739 951 dpkg-sig-example_0.1.tar.gz -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.5 (GNU/Linux) iEYEARECAAYFAkFykPAACgkQmO5zOp3h7rFa6ACghdIZg5GDNTmm795IJQ0KSCb7 ks4An1Cb72wbx5RHZdLgNLeBy6mYz2C3 =Waai -----END PGP SIGNATURE----- dpkg-sig-0.13.1+nmu1/examples/dpkg-sig-example_0.1_all.deb0000644000000000000000000000255210134511572017704 0ustar ! debian-binary 1098027154 0 0 100644 4 ` 2.0 control.tar.gz 1098027154 0 0 100644 366 ` AO0)`AŃ{0֥-~zD=Yӭk׽xoԮ{H*L$\1'X=Q❋?t*UX‘?A3dB?R2/i9LsΧ\fCW0Y'5X#%_I?{],uefTvjl50z5=kgSLwy1 yfnWMn6)Ӷ}3~FF>tUb\}UWƄnuE>bF _>C(nCu8 )^p>ś(data.tar.gz 1098027154 0 0 100644 136 ` 1 @Q "88K4M[J UpE8DJLEVս`]w8ٻ~ 6Ls[Ş|jR`(_gpgbuilder 1098027247 0 0 100644 631 ` -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Version: 4 Signer: Marc 'HE' Brockschmidt Date: Sun Oct 17 17:34:05 2004 Role: builder Files: 3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad 4 debian-binary 79bb73dbb522dc1a2dd1b9c2ec89fc79 26d29d15aad5c0e051d07571e28da2bc0009707e 366 control.tar.gz e1a6e48c95a760170029ef7872cec994 e02ed99e5c4fd847bde12b4c2c30dd814b26ec27 136 data.tar.gz -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.5 (GNU/Linux) iEYEARECAAYFAkFykO0ACgkQmO5zOp3h7rEW7gCgg7jKhJKn/xh+39jR8omgU3FV uucAoI7NX+t5QCyjKWh8ron+IAFvN2+a =j0nX -----END PGP SIGNATURE----- dpkg-sig-0.13.1+nmu1/examples/dpkg-sig-example_0.1.tar.gz0000644000000000000000000000166710134511572017535 0ustar rAߓ6z+Ӈi-M3%oqCw4qGcw#~i)XQYYWJx#CV.DFZZ{f`a6 YFVv.B󿳛?B[ײoЪ$!<ҵGgLlr+rJ~1!eJcOp|L ? +٪0d3#dmk{^JH%u γb6v*'u8pƔ)[ZWKyT\tv[{4J19kE;uuΛa%ʰ:gV4tw)!YaJalZeYUs3%'= måT풯_}Pv1:K|jEmنsAȺS^JHVqV$/%̅9ҁ}oc/5S׎pޔDOd4]o7r5еFqB W_nCՅcW;gn?Ev~c2GAAAAAAAAdE6Pdpkg-sig-0.13.1+nmu1/examples/README0000644000000000000000000000273610377442452013561 0ustar This dir contains a sample package, the dpkg-sig tempdir (named debsigs-ng.sample) and all generated files. What happened: sample$ dpkg-buildpackage -rfakeroot -uc -us [...] sample$ cd .. examples$ dpkg-sig --sign-changes full --sign builder dpkg-sig-example_0.1_i386.changes [...] The used dpkg-sig version was slightly modified to make the temporary files available. dpkg-sig did the following things: 1) Parse the .changes to get all binaries that need to be signed. 2) Get the md5sums, sha1sums, sizes and names of all members of the deb (which is an ar archive) 3) Write this information + some meta-data (Date, Role, Signer) in a RFC822-compliant format to debsigs-ng.sample/digests. 4) Clearsign debsigs-ng.sample/digests, the signed version is in debsigs-ng.sample/digests.asc. 5) debsigs-ng.sample/digests.asc is added to the deb (as new ar member) with an unique name reflecting the signing role. 6) The new md5sum and size of the deb are put in the .changes file. As this invalidates an existing gpg signature, it is stripped. [This is an optional feature of dpkg-sig: 7) The .dsc is signed (an unsigned copy is in debsigs-ng/dsc, the clearsigned copy is in debsigs-ng/dsc.asc and then moved to the original location) 8) The new .dsc size and md5sum are put in the .changes file. 9) The new .changes is signed (an unsigned copy is in debsigs-ng/changes, the clearsigned copy is in debsigs-ng/changes.asc and then moved to the original location) ] dpkg-sig-0.13.1+nmu1/examples/sample/0000755000000000000000000000000010377602746014156 5ustar dpkg-sig-0.13.1+nmu1/examples/sample/test0000644000000000000000000000000010020337070015022 0ustar dpkg-sig-0.13.1+nmu1/examples/sample/debian/0000755000000000000000000000000010377602746015400 5ustar dpkg-sig-0.13.1+nmu1/examples/sample/debian/rules0000755000000000000000000000042010020337070016430 0ustar #!/usr/bin/make -f build: clean: dh_testdir dh_testroot dh_clean binary-arch: binary-indep: dh_testdir dh_testroot dh_clean mkdir -p debian/tmp/tmp cp test debian/tmp/tmp dh_compress dh_gencontrol dh_md5sums dh_fixperms dh_builddeb binary: binary-indep dpkg-sig-0.13.1+nmu1/examples/sample/debian/control0000644000000000000000000000051410020337070016757 0ustar Source: dpkg-sig-example Section: devel Priority: optional Maintainer: Andreas Barth Uploaders: Marc Brockschmidt Build-Depends-Indep: debhelper Standards-Version: 3.6.1.0 Package: dpkg-sig-example Architecture: all Description: sample package This is a example to show how dpkg-sig works. dpkg-sig-0.13.1+nmu1/examples/sample/debian/changelog0000644000000000000000000000020410020337070017222 0ustar dpkg-sig-example (0.1) unstable; urgency=low * Foo. -- Marc 'HE' Brockschmidt Sat, 28 Feb 2004 20:51:00 +0100 dpkg-sig-0.13.1+nmu1/examples/sample/debian/tmp/0000755000000000000000000000000010377602747016201 5ustar dpkg-sig-0.13.1+nmu1/examples/sample/debian/tmp/DEBIAN/0000755000000000000000000000000010377602746017122 5ustar dpkg-sig-0.13.1+nmu1/examples/sample/debian/tmp/DEBIAN/control0000644000000000000000000000035110020337070020500 0ustar Package: dpkg-sig-example Version: 0.1 Section: devel Priority: optional Architecture: all Installed-Size: 12 Maintainer: Andreas Barth Description: sample package This is a example to show how dpkg-sig works. dpkg-sig-0.13.1+nmu1/examples/sample/debian/tmp/DEBIAN/md5sums0000644000000000000000000000005310020337070020414 0ustar d41d8cd98f00b204e9800998ecf8427e tmp/test dpkg-sig-0.13.1+nmu1/examples/sample/debian/tmp/tmp/0000755000000000000000000000000010377602747017001 5ustar dpkg-sig-0.13.1+nmu1/examples/sample/debian/tmp/tmp/test0000644000000000000000000000000010020337070017644 0ustar dpkg-sig-0.13.1+nmu1/examples/sample/debian/files0000644000000000000000000000005410020337070016400 0ustar dpkg-sig-example_0.1_all.deb devel optional dpkg-sig-0.13.1+nmu1/examples/dpkg-sig-example_0.1_i386.changes0000644000000000000000000000156610134511572020507 0ustar -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Format: 1.7 Date: Sat, 28 Feb 2004 20:51:00 +0100 Source: dpkg-sig-example Binary: dpkg-sig-example Architecture: source all Version: 0.1 Distribution: unstable Urgency: low Maintainer: Andreas Barth Changed-By: Marc 'HE' Brockschmidt Description: dpkg-sig-example - sample package Changes: dpkg-sig-example (0.1) unstable; urgency=low . * Foo. Files: 34a85c5305f88a78b3e7d6339c4121b1 565 devel optional dpkg-sig-example_0.1.dsc a7beeef85a612c4cb3cf7e1e26f64739 951 devel optional dpkg-sig-example_0.1.tar.gz 16b320195066f99fd82ea7c6da550484 1386 devel optional dpkg-sig-example_0.1_all.deb -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.5 (GNU/Linux) iEYEARECAAYFAkFykPEACgkQmO5zOp3h7rEZEQCfbyaaDgOLBfFKnR3qhApDaQL1 sqMAn2ixLMp+47vuv/bOHoMa+7JDpoCs =6vBZ -----END PGP SIGNATURE----- dpkg-sig-0.13.1+nmu1/examples/debsigs-ng.sample/0000755000000000000000000000000010377602746016177 5ustar dpkg-sig-0.13.1+nmu1/examples/debsigs-ng.sample/digests.asc0000644000000000000000000000116710134511572020321 0ustar -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Version: 4 Signer: Marc 'HE' Brockschmidt Date: Sun Oct 17 17:34:05 2004 Role: builder Files: 3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad 4 debian-binary 79bb73dbb522dc1a2dd1b9c2ec89fc79 26d29d15aad5c0e051d07571e28da2bc0009707e 366 control.tar.gz e1a6e48c95a760170029ef7872cec994 e02ed99e5c4fd847bde12b4c2c30dd814b26ec27 136 data.tar.gz -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.5 (GNU/Linux) iEYEARECAAYFAkFykO0ACgkQmO5zOp3h7rEW7gCgg7jKhJKn/xh+39jR8omgU3FV uucAoI7NX+t5QCyjKWh8ron+IAFvN2+a =j0nX -----END PGP SIGNATURE----- dpkg-sig-0.13.1+nmu1/examples/debsigs-ng.sample/digests0000644000000000000000000000060310134511572017546 0ustar Version: 4 Signer: Marc 'HE' Brockschmidt Date: Sun Oct 17 17:34:05 2004 Role: builder Files: 3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad 4 debian-binary 79bb73dbb522dc1a2dd1b9c2ec89fc79 26d29d15aad5c0e051d07571e28da2bc0009707e 366 control.tar.gz e1a6e48c95a760170029ef7872cec994 e02ed99e5c4fd847bde12b4c2c30dd814b26ec27 136 data.tar.gz dpkg-sig-0.13.1+nmu1/examples/debsigs-ng.sample/changes.signed0000644000000000000000000000156610134511572020775 0ustar -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Format: 1.7 Date: Sat, 28 Feb 2004 20:51:00 +0100 Source: dpkg-sig-example Binary: dpkg-sig-example Architecture: source all Version: 0.1 Distribution: unstable Urgency: low Maintainer: Andreas Barth Changed-By: Marc 'HE' Brockschmidt Description: dpkg-sig-example - sample package Changes: dpkg-sig-example (0.1) unstable; urgency=low . * Foo. Files: 34a85c5305f88a78b3e7d6339c4121b1 565 devel optional dpkg-sig-example_0.1.dsc a7beeef85a612c4cb3cf7e1e26f64739 951 devel optional dpkg-sig-example_0.1.tar.gz 16b320195066f99fd82ea7c6da550484 1386 devel optional dpkg-sig-example_0.1_all.deb -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.5 (GNU/Linux) iEYEARECAAYFAkFykPEACgkQmO5zOp3h7rEZEQCfbyaaDgOLBfFKnR3qhApDaQL1 sqMAn2ixLMp+47vuv/bOHoMa+7JDpoCs =6vBZ -----END PGP SIGNATURE----- dpkg-sig-0.13.1+nmu1/examples/debsigs-ng.sample/changes.unsigned0000644000000000000000000000120210134511572021323 0ustar Format: 1.7 Date: Sat, 28 Feb 2004 20:51:00 +0100 Source: dpkg-sig-example Binary: dpkg-sig-example Architecture: source all Version: 0.1 Distribution: unstable Urgency: low Maintainer: Andreas Barth Changed-By: Marc 'HE' Brockschmidt Description: dpkg-sig-example - sample package Changes: dpkg-sig-example (0.1) unstable; urgency=low . * Foo. Files: 34a85c5305f88a78b3e7d6339c4121b1 565 devel optional dpkg-sig-example_0.1.dsc a7beeef85a612c4cb3cf7e1e26f64739 951 devel optional dpkg-sig-example_0.1.tar.gz 16b320195066f99fd82ea7c6da550484 1386 devel optional dpkg-sig-example_0.1_all.deb dpkg-sig-0.13.1+nmu1/examples/debsigs-ng.sample/dsc.unsigned0000644000000000000000000000050110134511572020465 0ustar Format: 1.0 Source: dpkg-sig-example Version: 0.1 Binary: dpkg-sig-example Maintainer: Andreas Barth Architecture: all Standards-Version: 3.6.1.0 Build-Depends-Indep: debhelper Uploaders: Marc Brockschmidt Files: a7beeef85a612c4cb3cf7e1e26f64739 951 dpkg-sig-example_0.1.tar.gz dpkg-sig-0.13.1+nmu1/examples/debsigs-ng.sample/dsc.signed0000644000000000000000000000106510134511572020130 0ustar -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Format: 1.0 Source: dpkg-sig-example Version: 0.1 Binary: dpkg-sig-example Maintainer: Andreas Barth Architecture: all Standards-Version: 3.6.1.0 Build-Depends-Indep: debhelper Uploaders: Marc Brockschmidt Files: a7beeef85a612c4cb3cf7e1e26f64739 951 dpkg-sig-example_0.1.tar.gz -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.5 (GNU/Linux) iEYEARECAAYFAkFykPAACgkQmO5zOp3h7rFa6ACghdIZg5GDNTmm795IJQ0KSCb7 ks4An1Cb72wbx5RHZdLgNLeBy6mYz2C3 =Waai -----END PGP SIGNATURE-----