]>
git.sthu.org Git - pgp-tools.git/blob - caff/pgp-fixkey
3 # caff -- CA - Fire and Forget
4 # $Id: caff 37 2005-02-28 23:20:15Z weasel $
6 # Copyright (c) 2004, 2005 Peter Palfrader <peter@palfrader.org>
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions
13 # 1. Redistributions of source code must retain the above copyright
14 # notice, this list of conditions and the following disclaimer.
15 # 2. Redistributions in binary form must reproduce the above copyright
16 # notice, this list of conditions and the following disclaimer in the
17 # documentation and/or other materials provided with the distribution.
18 # 3. The name of the author may not be used to endorse or promote products
19 # derived from this software without specific prior written permission.
21 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 pgp-fixkey -- remove broken packets from keys
42 =item B<pgp-fixkey> I<keyid> [I<keyid> ...]
48 B<pgp-fixkey> removes broken packets from keys in the GnuPG keyring that make
49 GnuPG spew ugly warnings. It takes a list of keyids on the command line and
50 only cleans those keys.
66 =item $HOME/.gnupg/pubring.gpg - default GnuPG keyring
76 Peter Palfrader <peter@palfrader.org>
88 my $REVISION = '$Rev: 37 $';
89 my ($REVISION_NUMER) = $REVISION =~ /(\d+)/;
90 my $VERSION = "0.0.0.$REVISION_NUMER";
94 print STDERR
"[NOTICE] $line\n";
98 print STDERR
"[INFO] $line\n";
102 print STDERR
"[DEBUG] $line\n";
106 #print STDERR "[trace] $line\n";
110 #print STDERR "[trace2] $line\n";
115 stdin
=> IO
::Handle
->new(),
116 stdout
=> IO
::Handle
->new(),
117 stderr
=> IO
::Handle
->new(),
118 status
=> IO
::Handle
->new() );
119 my $handles = GnuPG
::Handles
->new( %fds );
120 return ($fds{'stdin'}, $fds{'stdout'}, $fds{'stderr'}, $fds{'status'}, $handles);
123 sub readwrite_gpg
($$$$$%) {
124 my ($in, $inputfd, $stdoutfd, $stderrfd, $statusfd, %options) = @_;
126 trace
("Entering readwrite_gpg.");
128 my ($first_line, $dummy) = split /\n/, $in;
129 debug
("readwrite_gpg sends ".(defined $first_line ?
$first_line : "<nothing>"));
131 local $INPUT_RECORD_SEPARATOR = undef;
132 my $sout = IO
::Select
->new();
133 my $sin = IO
::Select
->new();
136 trace
("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".(defined $statusfd ?
$statusfd : 'undef').".");
138 $inputfd->blocking(0);
139 $stdoutfd->blocking(0);
140 $statusfd->blocking(0) if defined $statusfd;
141 $stderrfd->blocking(0);
142 $sout->add($stdoutfd);
143 $sout->add($stderrfd);
144 $sout->add($statusfd) if defined $statusfd;
147 my ($stdout, $stderr, $status) = ("", "", "");
148 my $exitwhenstatusmatches = $options{'exitwhenstatusmatches'};
149 trace
("doing stuff until we find $exitwhenstatusmatches") if defined $exitwhenstatusmatches;
151 my $readwrote_stuff_this_time = 0;
152 my $do_not_wait_on_select = 0;
153 my ($readyr, $readyw, $written);
154 while ($sout->count() > 0 || (defined($sin) && ($sin->count() > 0))) {
155 if (defined $exitwhenstatusmatches) {
156 if ($status =~ /$exitwhenstatusmatches/m) {
157 trace
("readwrite_gpg found match on $exitwhenstatusmatches");
158 if ($readwrote_stuff_this_time) {
159 trace
("read/write some more\n");
160 $do_not_wait_on_select = 1;
162 trace
("that's it in our while loop.\n");
168 $readwrote_stuff_this_time = 0;
169 trace
("select waiting for ".($sout->count())." fds.");
170 ($readyr, $readyw, undef) = IO
::Select
::select($sout, $sin, undef, $do_not_wait_on_select ?
0 : 1);
171 trace
("ready: write: ".(defined $readyw ?
scalar @
$readyw : 0 )."; read: ".(defined $readyr ?
scalar @
$readyr : 0));
172 for my $wfd (@
$readyw) {
173 $readwrote_stuff_this_time = 1;
174 if (length($in) != $offset) {
175 trace
("writing to $wfd.");
176 $written = $wfd->syswrite($in, length($in) - $offset, $offset);
179 if ($offset == length($in)) {
180 trace
("writing to $wfd done.");
181 unless ($options{'nocloseinput'}) {
183 trace
("$wfd closed.");
190 next unless (defined(@
$readyr)); # Wait some more.
192 for my $rfd (@
$readyr) {
193 $readwrote_stuff_this_time = 1;
195 trace
("reading from $rfd done.");
200 trace
("reading from $rfd.");
201 if ($rfd == $stdoutfd) {
203 trace2
("stdout is now $stdout\n================");
206 if (defined $statusfd && $rfd == $statusfd) {
208 trace2
("status is now $status\n================");
211 if ($rfd == $stderrfd) {
213 trace2
("stderr is now $stderr\n================");
218 trace
("readwrite_gpg done.");
219 return ($stdout, $stderr, $status);
222 my $KEYEDIT_PROMPT = '^\[GNUPG:\] GET_LINE keyedit.prompt';
223 my $KEYEDIT_DELUID_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.uid.okay';
224 my $KEYEDIT_DELSIG_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.delsig';
225 my $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT = '^\[GNUPG:\] (GET_BOOL keyedit.delsig|GET_LINE keyedit.prompt)';
226 my $KEYEDIT_DELSUBKEY_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.subkey';
230 print STDERR
"pgp-fixkey $VERSION - (c) 2004, 2005 Peter Palfrader\n";
231 print STDERR
"Usage: $PROGRAM_NAME <keyid> [<keyid> ...]\n";
235 usage
() unless scalar @ARGV >= 1;
237 for my $keyid (@ARGV) {
239 unless ($keyid =~ /^[A-Za-z0-9]{8}([A-Za-z0-9]{8})?$/) {
240 print STDERR
"$keyid is not a keyid.\n";
243 push @KEYIDS, uc($keyid);
248 for my $keyid (@KEYIDS) {
251 my $gpg = GnuPG
::Interface
->new();
252 $gpg->options->meta_interactive( 0 );
253 my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds
();
254 $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode' ] );
255 my $pid = $gpg->list_public_keys(handles
=> $handles, command_args
=> [ $keyid ]);
256 my ($stdout, $stderr, $status) = readwrite_gpg
('', $inputfd, $stdoutfd, $stderrfd, $statusfd);
259 warn ("No data from gpg for list-key $keyid\n");
262 my $keyinfo = $stdout;
263 my @publine = grep { /^pub/ } (split /\n/, $stdout);
264 my ($dummy1, $dummy2, $dummy3, $dummy4, $longkeyid, $dummy6, $dummy7, $dummy8, $dummy9, $dummy10, $dummy11, $flags) = split /:/, pop @publine;
265 my $can_encrypt = $flags =~ /E/;
266 unless (defined $longkeyid) {
267 warn ("Didn't find public keyid in edit dialog of key $keyid.\n");
273 my $this_uid_text = '';
275 debug
("Doing key $keyid, uid $uid_number");
279 $gpg = GnuPG
::Interface
->new();
280 $gpg->options->hash_init(
281 'extra_args' => [ '--with-colons', '--fixed-list-mode', '--command-fd=0', '--no-tty' ] );
282 ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds
();
283 $pid = $gpg->wrap_call(
284 commands
=> [ '--edit' ],
285 command_args
=> [ $keyid ],
286 handles
=> $handles );
288 debug
("Starting edit session");
289 ($stdout, $stderr, $status) = readwrite_gpg
('', $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches
=> $KEYEDIT_PROMPT, nocloseinput
=> 1);
293 my $number_of_subkeys = 0;
298 debug
("Parsing stdout output.");
299 for my $line (split /\n/, $stdout) {
300 debug
("Checking line $line");
301 my ($type, $dummy2, $dummy3, $dummy4, $dummy5, $dummy6, $dummy7, $dummy8, $dummy9, $uidtext) = split /:/, $line;
302 if ($type eq 'sub') {
303 $number_of_subkeys++;
305 next unless ($type eq 'uid' || $type eq 'uat');
306 debug
("line is interesting.");
308 readwrite_gpg
("$i\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches
=> $KEYEDIT_PROMPT, nocloseinput
=> 1);
311 debug
("Parsing stdout output done.");
315 ($stdout, $stderr, $status) =
316 readwrite_gpg
("delsig\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches
=> $KEYEDIT_DELSIG_PROMPT, nocloseinput
=> 1);
318 while($status =~ /$KEYEDIT_DELSIG_PROMPT/m) {
319 # sig:?::17:EA2199412477CAF8:1058095214:::::13x:
320 my @sigline = grep { /^sig/ } (split /\n/, $stdout);
321 $stdout =~ s/\n/\\n/g;
322 notice
("[sigremoval] why are there ".(scalar @sigline)." siglines in that part of the dialog!? got: $stdout") if scalar @sigline >= 2; # XXX
323 my $line = pop @sigline;
325 if (defined $line) { # only if we found a sig here - we never remove revocation packets for instance
326 debug
("[sigremoval] doing line $line.");
327 my ($dummy1, $dummy2, $dummy3, $dummy4, $signer, $created, $dummy7, $dummy8, $dummy9) = split /:/, $line;
328 if ($signer eq ('-1' x
16)) {
329 debug
("[sigremoval] not interested in that sig ($signer).");
333 debug
("[sigremoval] no sig line here, only got: ".$stdout);
335 ($stdout, $stderr, $status) =
336 readwrite_gpg
($answer."\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches
=> $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput
=> 1);
338 readwrite_gpg
("save\n", $inputfd, $stdoutfd, $stderrfd, $statusfd);