X-Git-Url: http://git.sthu.org/?a=blobdiff_plain;f=caff%2Fcaff;h=a0a0ed64c270b2268b1430b044b39cab8e3c2a31;hb=a9630bdc99fdda11f2d20409730e777b300fabe9;hp=7583720ee6fdd18bc9d2dc80cfa93ad8bf1d84c9;hpb=ca79cc01ab99c08f7592982bac15ce9b85ab101a;p=pgp-tools.git diff --git a/caff/caff b/caff/caff index 7583720..a0a0ed6 100755 --- a/caff/caff +++ b/caff/caff @@ -97,11 +97,20 @@ Import keys from file. Can be supplied more than once. =item $HOME/.caffrc - configuration file +=item $HOME/.caff/keys/yyyy-mm-dd/ - processed keys + +=item $HOME/.caff/gnupghome/ - caff's working dir for gpg + +=item $HOME/.caff/gnupghome/gpg.conf - gpg configuration + +useful options include use-agent, default-cert-level, etc. + =back =head1 CONFIGURATION FILE OPTIONS The configuration file is a perl script that sets values in the hash B<%CONFIG>. +The file is generated when it does not exist. Example: @@ -226,6 +235,10 @@ The UIDs for which signatures are included in the mail. =back +=item B [string] + +Add a Reply-To: header to messages sent. Default: none. + =item B [string] Address to send blind carbon copies to when sending mail. @@ -247,6 +260,10 @@ Default: none. http://pgp-tools.alioth.debian.org/ +=head1 SEE ALSO + +gpg(1), pgp-clean(1), /usr/share/doc/signing-party/examples/caffrc.sample. + =cut use strict; @@ -266,20 +283,118 @@ my $REVISION = '$Rev$'; my ($REVISION_NUMER) = $REVISION =~ /(\d+)/; my $VERSION = "0.0.0.$REVISION_NUMER"; + + +sub notice($) { + my ($line) = @_; + print "[NOTICE] $line\n"; +}; +sub info($) { + my ($line) = @_; + print "[INFO] $line\n"; +}; +sub debug($) { + my ($line) = @_; + #print "[DEBUG] $line\n"; +}; +sub trace($) { + my ($line) = @_; + #print "[trace] $line\n"; +}; +sub trace2($) { + my ($line) = @_; + #print "[trace2] $line\n"; +}; + + +sub generate_config() { + notice("Error: \$LOGNAME is not set.\n") unless defined $ENV{'LOGNAME'}; + my $gecos = defined $ENV{'LOGNAME'} ? (getpwnam($ENV{LOGNAME}))[6] : undef; + my $email; + my @keys; + my $hostname = `hostname -f`; + chomp $hostname; + my ($Cgecos,$Cemail,$Ckeys) = ('','',''); + if (defined $gecos) { + $gecos =~ s/,.*//; + + my $gpg = GnuPG::Interface->new(); + $gpg->call( 'gpg' ); + $gpg->options->hash_init( + 'extra_args' => [ qw{ --no-auto-check-trustdb --trust-model=always --with-colons --fixed-list-mode } ] ); + $gpg->options->meta_interactive( 0 ); + my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); + my $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $gecos ]); + my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); + waitpid $pid, 0; + + if ($stdout eq '') { + warn ("No data from gpg for list-key\n"); # There should be at least 'tru:' everywhere. + }; + + @keys = ($stdout =~ /^pub:[^r:]*:(?:[^:]*:){2,2}([^:]+):/mg); + unless (scalar @keys) { + info("Error: No keys were found using \"gpg --list-public-keys '$gecos'\"."); + @keys = qw{0123456789abcdef 89abcdef76543210}; + $Ckeys = '#'; + } + ($email) = ($stdout =~ /^uid:.*<(.+?@.+?)>.*:/m); + unless (defined $email) { + info("Error: No email address was found using \"gpg --list-public-keys '$gecos'\"."); + $email = $ENV{'LOGNAME'}.'@'.$hostname; + $Cemail = '#'; + } + } else { + $gecos = 'Unknown Caff User'; + $email = $ENV{'LOGNAME'}.'@'.$hostname; + @keys = qw{0123456789abcdef 89abcdef76543210}; + ($Cgecos,$Cemail,$Ckeys) = ('#','#','#'); + }; + + return < +# +# if you have a v4 key, it will simply be the last 16 digits of +# your fingerprint. +# +# Example: +# \$CONFIG{'keyid'} = [ qw{FEDCBA9876543210} ]; +# or, if you have more than one key: +# \$CONFIG{'keyid'} = [ qw{0123456789ABCDEF 89ABCDEF76543210} ]; + +$Ckeys\$CONFIG{'keyid'} = [ qw{@keys} ]; +EOT +}; + sub load_config() { my $config = $ENV{'HOME'} . '/.caffrc'; - -f $config or die "No file $config present. See caff(1).\n"; + unless (-f $config) { + print "No configfile $config present, I will use this template:\n"; + my $template = generate_config(); + print "$template\nPlease edit $config and run caff again.\n"; + open F, ">$config" or die "$config: $!"; + print F $template; + close F; + exit(1); + } unless (scalar eval `cat $config`) { die "Couldn't parse $config: $EVAL_ERROR\n" if $EVAL_ERROR; }; $CONFIG{'caffhome'}=$ENV{'HOME'}.'/.caff' unless defined $CONFIG{'caffhome'}; - die ("owner is not defined.\n") unless defined $CONFIG{'owner'}; - die ("email is not defined.\n") unless defined $CONFIG{'email'}; - die ("keyid is not defined.\n") unless defined $CONFIG{'keyid'}; - die ("keyid is not an array ref\n") unless (ref $CONFIG{'keyid'} eq 'ARRAY'); + die ("$PROGRAM_NAME: owner is not defined in $config.\n") unless defined $CONFIG{'owner'}; + die ("$PROGRAM_NAME: email is not defined in $config.\n") unless defined $CONFIG{'email'}; + die ("$PROGRAM_NAME: keyid is not defined in $config.\n") unless defined $CONFIG{'keyid'}; + die ("$PROGRAM_NAME: keyid is not an array ref in $config.\n") unless (ref $CONFIG{'keyid'} eq 'ARRAY'); for my $keyid (@{$CONFIG{'keyid'}}) { - $keyid =~ /^[A-F0-9]{16}$/i or die ("key $keyid is not a long (16 digit) keyid.\n"); + $keyid =~ /^[A-F0-9]{16}$/i or die ("$PROGRAM_NAME: key $keyid is not a long (16 digit) keyid in $config.\n"); }; @{$CONFIG{'keyid'}} = map { uc } @{$CONFIG{'keyid'}}; $CONFIG{'export-sig-age'}= 24*60*60 unless defined $CONFIG{'export-sig-age'}; @@ -315,27 +430,6 @@ Regards, EOM }; -sub notice($) { - my ($line) = @_; - print "[NOTICE] $line\n"; -}; -sub info($) { - my ($line) = @_; - print "[INFO] $line\n"; -}; -sub debug($) { - my ($line) = @_; - #print "[DEBUG] $line\n"; -}; -sub trace($) { - my ($line) = @_; - #print "[trace] $line\n"; -}; -sub trace2($) { - my ($line) = @_; - #print "[trace2] $line\n"; -}; - sub make_gpg_fds() { my %fds = ( stdin => IO::Handle->new(), @@ -448,8 +542,9 @@ sub readwrite_gpg($$$$$%) { sub ask($$;$$) { my ($question, $default, $forceyes, $forceno) = @_; my $answer; + my $yn = $default ? '[Y/n]' : '[y/N]'; while (1) { - print $question,' ',($default ? '[Y/n]' : '[y/N]'), ' '; + print $question,' ',$yn, ' '; if ($forceyes && $forceno) { print "$default (from config/command line)\n"; return $default; @@ -464,9 +559,17 @@ sub ask($$;$$) { }; $answer = ; + if (!defined $answer) { + $OUTPUT_AUTOFLUSH = 1; + die "\n\n". + "End of STDIN reached. Are you using xargs? Caff wants to read from STDIN,\n". + "so you can't really use it with xargs. A patch against caff to read from\n". + "the terminal would be appreciated.\n". + "For now instead of cat keys | xargs caff do caff `cat keys`\n"; + }; chomp $answer; - last if ((defined $answer) && (length $answer <= 1)); - print "Error reading from STDIN (are you using caff with xargs?).\n"; + last if ((length $answer == 0) || ($answer =~ m/^[yYnN]$/) ); + print "What about $yn is so hard to understand?\nAnswer with either 'n' or 'y' or just press enter for the default.\n"; sleep 1; }; my $result = $default; @@ -643,6 +746,7 @@ sub send_mail($$$@) { $message_entity->head->add("Subject", "Your signed PGP key 0x$key_id"); $message_entity->head->add("To", $address); $message_entity->head->add("From", '"'.$CONFIG{'owner'}.'" <'.$CONFIG{'email'}.'>'); + $message_entity->head->add("Reply-To", $CONFIG{'reply-to'}) if defined $CONFIG{'reply-to'}; $message_entity->head->add("Bcc", $CONFIG{'bcc'}) if defined $CONFIG{'bcc'}; $message_entity->head->add("User-Agent", $USER_AGENT); $message_entity->send(); @@ -847,6 +951,7 @@ if ($CONFIG{'no-download'}) { # [GNUPG:] NODATA 1 # [GNUPG:] IMPORT_OK 0 25FC1614B8F87B52FF2F99B962AF4031C82E0039 my %local_keyids = map { $_ => 1 } @KEYIDS; + my $had_v3_keys = 0; for my $line (split /\n/, $status) { if ($line =~ /^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{40})/) { my $imported_key = $1; @@ -865,12 +970,16 @@ if ($CONFIG{'no-download'}) { delete $local_keyids{$speced_key}; unshift @keyids_ok, $imported_key; } elsif ($line =~ /^\[GNUPG:\] (NODATA|IMPORT_RES|IMPORTED) /) { + } elsif ($line =~ /^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{32})/) { + my $imported_key = $1; + notice ("Imported key $1 is a version 3 key. Version 3 keys are obsolete, should not be used, and are not and will not be properly supported."); + $had_v3_keys = 1; } else { notice ("got unknown reply from gpg: $line"); } }; if (scalar %local_keyids) { - notice ("Import failed for: ". (join ' ', keys %local_keyids)."."); + notice ("Import failed for: ". (join ' ', keys %local_keyids)."." . ($had_v3_keys ? " (Or maybe it's one of those ugly v3 keys?)" : "")); exit 1 unless ask ("Some keys could not be imported - continue anyway?", 0); } };