| [550] | 1 | #! /usr/bin/perl
 | 
|---|
 | 2 | 
 | 
|---|
 | 3 | # Copyright Rainer Wichmann (2004)
 | 
|---|
 | 4 | #
 | 
|---|
 | 5 | # License Information:
 | 
|---|
 | 6 | # This program is free software; you can redistribute it and/or modify
 | 
|---|
 | 7 | # it under the terms of the GNU General Public License as published by
 | 
|---|
 | 8 | # the Free Software Foundation; either version 2 of the License, or
 | 
|---|
 | 9 | # (at your option) any later version.
 | 
|---|
 | 10 | #
 | 
|---|
 | 11 | # This program is distributed in the hope that it will be useful,
 | 
|---|
 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
 | 14 | # GNU General Public License for more details.
 | 
|---|
 | 15 | #
 | 
|---|
 | 16 | # You should have received a copy of the GNU General Public License
 | 
|---|
 | 17 | # along with this program; if not, write to the Free Software
 | 
|---|
 | 18 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
|---|
 | 19 | #
 | 
|---|
 | 20 | 
 | 
|---|
 | 21 | use warnings;
 | 
|---|
 | 22 | use strict;
 | 
|---|
 | 23 | use Getopt::Long;
 | 
|---|
 | 24 | use File::Basename;
 | 
|---|
 | 25 | use File::Copy;
 | 
|---|
 | 26 | use File::stat;
 | 
|---|
 | 27 | use File::Temp qw/ tempfile tempdir unlink0 /;
 | 
|---|
 | 28 | use IO::Handle;
 | 
|---|
 | 29 | use Fcntl qw(:DEFAULT :flock);
 | 
|---|
 | 30 | use Tie::File;
 | 
|---|
 | 31 | 
 | 
|---|
 | 32 | # Do I/O to the data file in binary mode (so it 
 | 
|---|
 | 33 | # wouldn't complain about invalid UTF-8 characters).
 | 
|---|
 | 34 | use bytes;
 | 
|---|
 | 35 | 
 | 
|---|
 | 36 | File::Temp->safe_level( File::Temp::HIGH );
 | 
|---|
 | 37 | 
 | 
|---|
 | 38 | my %opts = ();
 | 
|---|
 | 39 | my $action;
 | 
|---|
 | 40 | my $file1;
 | 
|---|
 | 41 | my $file2;
 | 
|---|
 | 42 | my $passphrase;
 | 
|---|
 | 43 | my $secretkey;
 | 
|---|
 | 44 | my $return_from_sign = 0;
 | 
|---|
 | 45 | my $no_print_examine = 0;
 | 
|---|
 | 46 | my $no_remove_lock   = 0;
 | 
|---|
 | 47 | my $base = basename($0);
 | 
|---|
 | 48 | 
 | 
|---|
 | 49 | my $cfgfile  = "@myconffile@";
 | 
|---|
 | 50 | my $datafile = "@mydatafile@";
 | 
|---|
 | 51 | my $daemon   = "@sbindir@/@install_name@";
 | 
|---|
 | 52 | my $signify  = "@mysignify@";
 | 
|---|
 | 53 | 
 | 
|---|
 | 54 | my $SIGDIR   = "$ENV{'HOME'}/.signify";
 | 
|---|
 | 55 | my $KEYID    = "@install_name@";
 | 
|---|
 | 56 | 
 | 
|---|
 | 57 | $cfgfile  =~ s/^REQ_FROM_SERVER//;
 | 
|---|
 | 58 | $datafile =~ s/^REQ_FROM_SERVER//;
 | 
|---|
 | 59 | 
 | 
|---|
 | 60 | $signify = "signify-openbsd" if ($signify eq "");
 | 
|---|
 | 61 | 
 | 
|---|
 | 62 | sub usage() {
 | 
|---|
 | 63 |     print "Usage:\n";
 | 
|---|
 | 64 |     print "  $base { -m F | --create-cfgfile }    [options] [in.cfgfile]\n";
 | 
|---|
 | 65 |     print "    Sign the configuration file. If in.cfgfile is given, sign it\n";
 | 
|---|
 | 66 |     print "    and install it as configuration file.\n\n";
 | 
|---|
 | 67 | 
 | 
|---|
 | 68 |     print "  $base { -m f | --print-cfgfile }     [options] \n";
 | 
|---|
 | 69 |     print "    Print the configuration file to stdout. Signatures are removed.\n\n";
 | 
|---|
 | 70 | 
 | 
|---|
 | 71 |     print "  $base { -m D | --create-datafile }   [options] [in.datafile]\n";
 | 
|---|
 | 72 |     print "    Sign the database file. If in.datafile is given, sign it\n";
 | 
|---|
 | 73 |     print "    and install it as database file.\n\n";
 | 
|---|
 | 74 | 
 | 
|---|
 | 75 |     print "  $base { -m d | --print-datafile }    [options] \n";
 | 
|---|
 | 76 |     print "    Print the database file to stdout. Signatures are removed. Use\n";
 | 
|---|
 | 77 |     print "    option --list to list files in database rather than printing the raw file.\n\n";
 | 
|---|
 | 78 | 
 | 
|---|
 | 79 |     print "  $base { -m R | --remove-signature }  [options] file1 [file2 ...]\n";
 | 
|---|
 | 80 |     print "    Remove cleartext signature from input file(s). The file\n";
 | 
|---|
 | 81 |     print "    is replaced by the non-signed file.\n\n";
 | 
|---|
 | 82 | 
 | 
|---|
 | 83 |     print "  $base { -m E | --sign }              [options] file1 [file2 ...]\n";
 | 
|---|
 | 84 |     print "    Sign file(s) with a cleartext signature. The file\n";
 | 
|---|
 | 85 |     print "    is replaced by the signed file.\n\n";
 | 
|---|
 | 86 | 
 | 
|---|
 | 87 |     print "  $base { -m e | --examine }           [options] file1 [file2 ...]\n";
 | 
|---|
 | 88 |     print "    Report signature status of file(s).\n\n";
 | 
|---|
 | 89 | 
 | 
|---|
 | 90 |     print "  $base { -m G | --generate-keys }     [options] \n";
 | 
|---|
 | 91 |     print "    Generate a signify keypair to use for signing.\n\n";
 | 
|---|
 | 92 | 
 | 
|---|
 | 93 |     print "Options:\n";
 | 
|---|
 | 94 |     print "  -c cfgfile    --cfgfile cfgfile\n";
 | 
|---|
 | 95 |     print "    Select an alternate configuration file.\n\n";
 | 
|---|
 | 96 | 
 | 
|---|
 | 97 |     print "  -d datafile   --datafile datafile\n";
 | 
|---|
 | 98 |     print "    Select an alternate database file.\n\n";
 | 
|---|
 | 99 | 
 | 
|---|
 | 100 |     print "  -p passphrase --passphrase passphrase\n";
 | 
|---|
 | 101 |     print "    Set the passphrase for signify. By default, signify will ask.\n\n";
 | 
|---|
 | 102 | 
 | 
|---|
 | 103 |     print "  -s signify_dir --signify-dir signify_dir\n";
 | 
|---|
 | 104 |     print "    Select an alternate directory to locate the secret keyring.\n";
 | 
|---|
 | 105 |     print "    Will use '$ENV{'HOME'}/.signify/' by default.\n\n";
 | 
|---|
 | 106 | 
 | 
|---|
 | 107 |     print "  -k keyid      --keyid keyid\n";
 | 
|---|
 | 108 |     print "   Select the keyid to use for signing.\n\n";
 | 
|---|
 | 109 | 
 | 
|---|
 | 110 |     print "  -l            --list\n";
 | 
|---|
 | 111 |     print "    List the files in database rather than printing the raw file.\n\n";
 | 
|---|
 | 112 | 
 | 
|---|
 | 113 |     print "  -v            --verbose\n";
 | 
|---|
 | 114 |     print "    Verbose output.\n\n";
 | 
|---|
 | 115 |     return;
 | 
|---|
 | 116 | }
 | 
|---|
 | 117 | 
 | 
|---|
 | 118 | sub check_signify_uid () {
 | 
|---|
 | 119 |     if (0 != $>) {
 | 
|---|
 | 120 |         print "--------------------------------------------------\n";
 | 
|---|
 | 121 |         print "\n";
 | 
|---|
 | 122 |         print " You are not root. Please remember that samhain/yule\n";
 | 
|---|
 | 123 |         print " will use the public key of root to verify a signature.\n";
 | 
|---|
 | 124 |         print "\n";
 | 
|---|
 | 125 |         print "--------------------------------------------------\n";
 | 
|---|
 | 126 |     } else {
 | 
|---|
 | 127 |         if (!("@yulectl_prg@" =~ //)) {
 | 
|---|
 | 128 |             print "--------------------------------------------------\n";
 | 
|---|
 | 129 |             print "\n";
 | 
|---|
 | 130 |             print " Please remember that yule will drop root after startup. Signature\n";
 | 
|---|
 | 131 |             print " verification on SIGHUP will fail if you do not import the public key\n";
 | 
|---|
 | 132 |             print " into the ~/.signify/ directory of the non-root yule user.\n";
 | 
|---|
 | 133 |             print "\n";
 | 
|---|
 | 134 |             print "--------------------------------------------------\n";
 | 
|---|
 | 135 |         }
 | 
|---|
 | 136 |     }
 | 
|---|
 | 137 | }
 | 
|---|
 | 138 |     
 | 
|---|
 | 139 | sub check_signify_sign () {
 | 
|---|
 | 140 |     if ( defined($secretkey)) {
 | 
|---|
 | 141 |         if ( (!-d "$secretkey")){
 | 
|---|
 | 142 |             print "--------------------------------------------------\n";
 | 
|---|
 | 143 |             print "\n";
 | 
|---|
 | 144 |             print " Secret key $secretkey not found!\n";
 | 
|---|
 | 145 |             print "\n";
 | 
|---|
 | 146 |             print " Please check the path/name of the alternate secret key.\n";
 | 
|---|
 | 147 |             print "\n";
 | 
|---|
 | 148 |             print "--------------------------------------------------\n";
 | 
|---|
 | 149 |             print "\n";
 | 
|---|
 | 150 |             exit;
 | 
|---|
 | 151 |         }
 | 
|---|
 | 152 |     } else {
 | 
|---|
 | 153 |         if ( (!-d "$SIGDIR") || (!-e "${SIGDIR}/${KEYID}.sec")) {
 | 
|---|
 | 154 |             print "--------------------------------------------------\n";
 | 
|---|
 | 155 |             print "\n";
 | 
|---|
 | 156 |             if (!-d "$SIGDIR") {
 | 
|---|
 | 157 |                 print " Directory $SIGDIR not found!\n";
 | 
|---|
 | 158 |             } else {
 | 
|---|
 | 159 |                 print " Secret key ${SIGDIR}/${KEYID}.sec not found!\n";
 | 
|---|
 | 160 |             }
 | 
|---|
 | 161 |             print "\n";
 | 
|---|
 | 162 |             print " This indicates that you have never created a \n";
 | 
|---|
 | 163 |             print " public/private keypair, and thus cannot sign.\n";
 | 
|---|
 | 164 |             print " \n";
 | 
|---|
 | 165 |             print " Please use $0 --generate-keys or\n";
 | 
|---|
 | 166 |             print " $signify -G -s ${SIGDIR}/${KEYID}.sec -p ${SIGDIR}/${KEYID}.pub\n";
 | 
|---|
 | 167 |             print " to generate a public/private keypair first.\n";
 | 
|---|
 | 168 |             print "\n";
 | 
|---|
 | 169 |             print "--------------------------------------------------\n";
 | 
|---|
 | 170 |             print "\n";
 | 
|---|
 | 171 |             exit;
 | 
|---|
 | 172 |         }
 | 
|---|
 | 173 |     }
 | 
|---|
 | 174 | }
 | 
|---|
 | 175 | 
 | 
|---|
 | 176 | sub check_signify_verify () {
 | 
|---|
 | 177 |     if ( (!-d "${SIGDIR}") || (!-e "${SIGDIR}/${KEYID}.pub")) {
 | 
|---|
 | 178 |         print "--------------------------------------------------\n";
 | 
|---|
 | 179 |         print "\n";
 | 
|---|
 | 180 |         if (!-d "$SIGDIR") {
 | 
|---|
 | 181 |             print " Directory $SIGDIR not found!\n";
 | 
|---|
 | 182 |         } else {
 | 
|---|
 | 183 |             print " Public key ${SIGDIR}/${KEYID}.pub not found!\n";
 | 
|---|
 | 184 |         }
 | 
|---|
 | 185 |         print "\n";
 | 
|---|
 | 186 |         print " This indicates that you have no public key\n";
 | 
|---|
 | 187 |         print " to verify signatures.\n";
 | 
|---|
 | 188 |         print " \n";
 | 
|---|
 | 189 |         print " Please copy the public key ${KEYID}.pub of\n";
 | 
|---|
 | 190 |         print " the user who is signing the configuration/database files\n";
 | 
|---|
 | 191 |         print " into the directory $SIGDIR.\n";
 | 
|---|
 | 192 |         print "\n";
 | 
|---|
 | 193 |         print "--------------------------------------------------\n";
 | 
|---|
 | 194 |         print "\n";
 | 
|---|
 | 195 |         exit;
 | 
|---|
 | 196 |     }
 | 
|---|
 | 197 | }
 | 
|---|
 | 198 | 
 | 
|---|
 | 199 | 
 | 
|---|
 | 200 | sub generate () {
 | 
|---|
 | 201 |     my $command = "$signify -G -s ${SIGDIR}/${KEYID}.sec -p ${SIGDIR}/${KEYID}.pub";
 | 
|---|
 | 202 |     if (!-d "${SIGDIR}") {
 | 
|---|
 | 203 |         unless(mkdir "$SIGDIR", 0750) {
 | 
|---|
 | 204 |             die "Creating directory $SIGDIR failed: $?";
 | 
|---|
 | 205 |         }
 | 
|---|
 | 206 |     }
 | 
|---|
 | 207 |     check_signify_uid();
 | 
|---|
 | 208 |     system ($command) == 0 
 | 
|---|
 | 209 |         or die "system $command failed: $?";
 | 
|---|
 | 210 |     exit;
 | 
|---|
 | 211 | }
 | 
|---|
 | 212 | 
 | 
|---|
 | 213 | sub examine () {
 | 
|---|
 | 214 |     my $iscfg = 0;
 | 
|---|
 | 215 |     my $have_fp  = 0;
 | 
|---|
 | 216 |     my $have_sig = 0;
 | 
|---|
 | 217 |     my $message = '';
 | 
|---|
 | 218 |     my $retval  = 9;
 | 
|---|
 | 219 |     my $fh;
 | 
|---|
 | 220 |     my $filename;
 | 
|---|
 | 221 | 
 | 
|---|
 | 222 |     if (!($file1 =~ /^\-$/)) {
 | 
|---|
 | 223 |         die ("Cannot open $file1 for read: $!") unless ((-e $file1) && (-r _));
 | 
|---|
 | 224 |     }
 | 
|---|
 | 225 |     open FIN,  "<$file1" or die "Cannot open $file1 for read: $!";
 | 
|---|
 | 226 | 
 | 
|---|
 | 227 |     my $dir = tempdir( CLEANUP => 1 );
 | 
|---|
 | 228 |     $filename = $dir . "/exa_jhfdbilw." . $$;
 | 
|---|
 | 229 |     open $fh, ">$filename" or die "Cannot open $filename";
 | 
|---|
 | 230 |     autoflush $fh 1;
 | 
|---|
 | 231 | 
 | 
|---|
 | 232 |     while (<FIN>) {
 | 
|---|
 | 233 |         print $fh $_;
 | 
|---|
 | 234 |         if ($_ =~ /^\s*\[Misc\]/) {
 | 
|---|
 | 235 |             $iscfg = 1;
 | 
|---|
 | 236 |         }
 | 
|---|
 | 237 |     }
 | 
|---|
 | 238 |     if ($iscfg == 1) {
 | 
|---|
 | 239 |         $message .=  "File $file1 is a configuration file\n\n";
 | 
|---|
 | 240 |     } else {
 | 
|---|
 | 241 |         $message .=  "File $file1 is a database file\n\n";
 | 
|---|
 | 242 |     }
 | 
|---|
 | 243 | 
 | 
|---|
 | 244 | 
 | 
|---|
 | 245 |     my $command = "$signify -Vem /dev/null -p ${SIGDIR}/${KEYID}.pub ";
 | 
|---|
 | 246 |     $command .= "-x $filename ";
 | 
|---|
 | 247 |     if (defined($opts{'v'})) {
 | 
|---|
 | 248 |         $command .= "2>&1";
 | 
|---|
 | 249 |     } else {
 | 
|---|
 | 250 |         $command .= "2>/dev/null";
 | 
|---|
 | 251 |     }
 | 
|---|
 | 252 | 
 | 
|---|
 | 253 |     print STDOUT "Using: $command\n\n" if (defined($opts{'v'}));
 | 
|---|
 | 254 |     open  SIGIN, "$command |" or die "Cannot fork: $!";
 | 
|---|
 | 255 | 
 | 
|---|
 | 256 |     while (<SIGIN>) {
 | 
|---|
 | 257 |         chomp ($_);
 | 
|---|
 | 258 |         if ($_ =~ /^Signature Verified$/) {
 | 
|---|
 | 259 |             $message .= "GOOD signature with key: ${SIGDIR}/${KEYID}.pub\n";
 | 
|---|
 | 260 |             $have_sig = 1;
 | 
|---|
 | 261 |             $retval   = 0;
 | 
|---|
 | 262 |         }
 | 
|---|
 | 263 |         print STDOUT $_ if (defined($opts{'v'}));
 | 
|---|
 | 264 |     }
 | 
|---|
 | 265 |     close (SIGIN);
 | 
|---|
 | 266 |     print STDOUT "\n" if (defined($opts{'v'}));
 | 
|---|
 | 267 |     if ($have_sig == 0) {
 | 
|---|
 | 268 |         $message .=  "NO valid signature found\n";
 | 
|---|
 | 269 |     }
 | 
|---|
 | 270 |     close (FIN);
 | 
|---|
 | 271 |     if ($no_print_examine == 0) {
 | 
|---|
 | 272 |         print STDOUT $message;
 | 
|---|
 | 273 |     }
 | 
|---|
 | 274 |     unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
 | 
|---|
 | 275 |     return $retval;
 | 
|---|
 | 276 | }
 | 
|---|
 | 277 | 
 | 
|---|
 | 278 | sub wstrip ($) {
 | 
|---|
 | 279 |     $_ = shift;
 | 
|---|
 | 280 |     $_ =~ s/\s+//g;
 | 
|---|
 | 281 |     return $_;
 | 
|---|
 | 282 | }
 | 
|---|
 | 283 | 
 | 
|---|
 | 284 | sub remove () {
 | 
|---|
 | 285 |     my $bodystart = 1;
 | 
|---|
 | 286 |     my $sigstart  = 0;
 | 
|---|
 | 287 |     my $sigend    = 0;
 | 
|---|
 | 288 |     my $filename  = "";
 | 
|---|
 | 289 |     my $fh;
 | 
|---|
 | 290 |     my $stats;
 | 
|---|
 | 291 | 
 | 
|---|
 | 292 |     open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
 | 
|---|
 | 293 |     if (!($file1 =~ /^\-$/)) {
 | 
|---|
 | 294 |         flock(FH, LOCK_EX) unless ($no_remove_lock == 1);
 | 
|---|
 | 295 |         my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
 | 
|---|
 | 296 |         $filename = $dir . "/rem_iqegBCQb." . $$;
 | 
|---|
 | 297 |         open $fh, ">$filename" or die "Cannot open $filename";
 | 
|---|
 | 298 |         $stats = stat($file1);
 | 
|---|
 | 299 |     } else {
 | 
|---|
 | 300 |         open $fh, ">$file1" or die "Cannot open file $file1 for write: $!";
 | 
|---|
 | 301 |     }
 | 
|---|
 | 302 |     autoflush $fh 1;
 | 
|---|
 | 303 |     while (<FH>) {
 | 
|---|
 | 304 |         if ($_ =~ /^untrusted comment: /) {
 | 
|---|
 | 305 |             $sigstart = 1;
 | 
|---|
 | 306 |             $bodystart = 0;
 | 
|---|
 | 307 |             next;
 | 
|---|
 | 308 |         } elsif (($sigstart == 1) && (wstrip($_) =~ m{^(?: [A-Za-z0-9+/]{4} )*(?:[A-Za-z0-9+/]{2} [AEIMQUYcgkosw048]=|[A-Za-z0-9+/][AQgw]==)?$}xm )) {
 | 
|---|
 | 309 |             $sigstart = 0;
 | 
|---|
 | 310 |             $bodystart = 1;
 | 
|---|
 | 311 |             next;
 | 
|---|
 | 312 |         } elsif (($sigstart == 1) && ($bodystart == 0)) {
 | 
|---|
 | 313 |             # comment NOT followed by signature
 | 
|---|
 | 314 |             $sigstart = 0;
 | 
|---|
 | 315 |             next;
 | 
|---|
 | 316 |         }
 | 
|---|
 | 317 | 
 | 
|---|
 | 318 |         if ($bodystart == 1) {
 | 
|---|
 | 319 |             print $fh $_;
 | 
|---|
 | 320 |         }
 | 
|---|
 | 321 |     }
 | 
|---|
 | 322 |     if (!($file1 =~ /^\-$/)) {
 | 
|---|
 | 323 |         copy("$filename", "$file1") 
 | 
|---|
 | 324 |             or die "Copy $filename to $file1 failed: $!";
 | 
|---|
 | 325 |         chmod $stats->mode, $file1;
 | 
|---|
 | 326 |         chown $stats->uid, $stats->gid, $file1;
 | 
|---|
 | 327 |         flock(FH, LOCK_UN) unless ($no_remove_lock == 1);
 | 
|---|
 | 328 |         close FH;
 | 
|---|
 | 329 |     }
 | 
|---|
 | 330 |     unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
 | 
|---|
 | 331 |     return;
 | 
|---|
 | 332 | }
 | 
|---|
 | 333 | 
 | 
|---|
 | 334 | sub print_cfgfile () {
 | 
|---|
 | 335 |     my $bodystart = 0;
 | 
|---|
 | 336 |     my $sigstart  = 0;
 | 
|---|
 | 337 | 
 | 
|---|
 | 338 |     if (!defined($file2)) {
 | 
|---|
 | 339 |         $file2 = '-';
 | 
|---|
 | 340 |     }
 | 
|---|
 | 341 | 
 | 
|---|
 | 342 |     open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
 | 
|---|
 | 343 |     open FO, ">$file2" or die "Cannot open file $file2 for write: $!";
 | 
|---|
 | 344 |     while (<FH>) {
 | 
|---|
 | 345 |         if ($_ =~ /^untrusted comment: /) {
 | 
|---|
 | 346 |             $sigstart = 1;
 | 
|---|
 | 347 |             $bodystart = 0;
 | 
|---|
 | 348 |             next;
 | 
|---|
 | 349 |         } elsif (($sigstart == 1) && (wstrip($_) =~ m{^(?: [A-Za-z0-9+/]{4} )*(?:[A-Za-z0-9+/]{2} [AEIMQUYcgkosw048]=|[A-Za-z0-9+/][AQgw]==)?$}xm )) {
 | 
|---|
 | 350 |             $sigstart = 0;
 | 
|---|
 | 351 |             $bodystart = 1;
 | 
|---|
 | 352 |             next;
 | 
|---|
 | 353 |         } elsif (($sigstart == 1) && ($bodystart == 0)) {
 | 
|---|
 | 354 |             # comment NOT followed by signature 
 | 
|---|
 | 355 |             $sigstart = 0;
 | 
|---|
 | 356 |             next;
 | 
|---|
 | 357 |         }
 | 
|---|
 | 358 |         if ($bodystart == 1) {
 | 
|---|
 | 359 |             print FO $_;
 | 
|---|
 | 360 |         }
 | 
|---|
 | 361 |     }
 | 
|---|
 | 362 |     exit;
 | 
|---|
 | 363 | }
 | 
|---|
 | 364 | 
 | 
|---|
 | 365 | sub print_datafile () {
 | 
|---|
 | 366 |     die ("Cannot find program $daemon") 
 | 
|---|
 | 367 |         unless (-e $daemon);
 | 
|---|
 | 368 |     if (defined($opts{'v'})) {
 | 
|---|
 | 369 |         open FH, "$daemon --full-detail -d $datafile |" 
 | 
|---|
 | 370 |             or die "Cannot open datafile $datafile for read: $!";
 | 
|---|
 | 371 |     } else {
 | 
|---|
 | 372 |         open FH, "$daemon -d $datafile |" 
 | 
|---|
 | 373 |             or die "Cannot open datafile $datafile for read: $!";
 | 
|---|
 | 374 |     }
 | 
|---|
 | 375 |     while (<FH>) {
 | 
|---|
 | 376 |         print $_;
 | 
|---|
 | 377 |     }
 | 
|---|
 | 378 |     exit;
 | 
|---|
 | 379 | }
 | 
|---|
 | 380 | 
 | 
|---|
 | 381 | sub sign_file () {
 | 
|---|
 | 382 | 
 | 
|---|
 | 383 |     my $fileout = '';
 | 
|---|
 | 384 |     my $bodystart = 1;
 | 
|---|
 | 385 |     my $sigstart  = 0;
 | 
|---|
 | 386 |     my $sigend    = 0;
 | 
|---|
 | 387 |     my $stats;
 | 
|---|
 | 388 |     my $fh1;
 | 
|---|
 | 389 |     my $filename1;
 | 
|---|
 | 390 |     my $flag1     = 0;
 | 
|---|
 | 391 | 
 | 
|---|
 | 392 |     check_signify_uid();
 | 
|---|
 | 393 | 
 | 
|---|
 | 394 |     if (!defined($file2)) {
 | 
|---|
 | 395 |         $file2 = $file1;
 | 
|---|
 | 396 |     }
 | 
|---|
 | 397 | 
 | 
|---|
 | 398 |     if ($file1 =~ /^\-$/) {
 | 
|---|
 | 399 |         my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
 | 
|---|
 | 400 |         $filename1 = $dir . "/sig_vs8827sd." . $$;
 | 
|---|
 | 401 |         open $fh1, ">$filename1" or die "Cannot open $filename1";
 | 
|---|
 | 402 |         $flag1 = 1;
 | 
|---|
 | 403 |         # my ($fh1, $filename1) = tempfile(UNLINK => 1);
 | 
|---|
 | 404 | 
 | 
|---|
 | 405 |         while (<STDIN>) {
 | 
|---|
 | 406 |             if ($_ =~ /^untrusted comment: /) {
 | 
|---|
 | 407 |                 $sigstart = 1;
 | 
|---|
 | 408 |                 $bodystart = 0;
 | 
|---|
 | 409 |                 next;
 | 
|---|
 | 410 |             } elsif (($sigstart == 1) && (wstrip($_) =~ m{^(?: [A-Za-z0-9+/]{4} )*(?:[A-Za-z0-9+/]{2} [AEIMQUYcgkosw048]=|[A-Za-z0-9+/][AQgw]==)?$}xm )) {
 | 
|---|
 | 411 |                 $sigstart = 0;
 | 
|---|
 | 412 |                 $bodystart = 1;
 | 
|---|
 | 413 |                 next;
 | 
|---|
 | 414 |             } elsif (($sigstart == 1) && ($bodystart == 0)) {
 | 
|---|
 | 415 |                 #comment NOT followed by signature 
 | 
|---|
 | 416 |                 $sigstart = 0;
 | 
|---|
 | 417 |                 next;
 | 
|---|
 | 418 |             }
 | 
|---|
 | 419 | 
 | 
|---|
 | 420 |             if ($bodystart == 1) {
 | 
|---|
 | 421 |                 print $fh1 $_;
 | 
|---|
 | 422 |             }
 | 
|---|
 | 423 |         }
 | 
|---|
 | 424 |         $file1 = $filename1;
 | 
|---|
 | 425 |         $fileout = '-';
 | 
|---|
 | 426 |     } else {
 | 
|---|
 | 427 |         open (LOCKFILE, "<$file1") or die "Cannot open $file1: $!";
 | 
|---|
 | 428 |         flock(LOCKFILE, LOCK_EX);
 | 
|---|
 | 429 |         $no_print_examine = 1;
 | 
|---|
 | 430 |         $no_remove_lock   = 1;
 | 
|---|
 | 431 |         if (examine() < 2) {
 | 
|---|
 | 432 |             remove();
 | 
|---|
 | 433 |         }
 | 
|---|
 | 434 |         $fileout = $file1 . ".sig";
 | 
|---|
 | 435 |         $stats   = stat($file1)
 | 
|---|
 | 436 |             or die "No file $file1: $!";
 | 
|---|
 | 437 |     }
 | 
|---|
 | 438 | 
 | 
|---|
 | 439 |     my $command = "$signify -Se ";
 | 
|---|
 | 440 |     $command .= "-s ${SIGDIR}/${KEYID}.sec ";
 | 
|---|
 | 441 |     $command .= "-x ${fileout} ";
 | 
|---|
 | 442 |     $command .= "-m $file1";
 | 
|---|
 | 443 | 
 | 
|---|
 | 444 |     if (defined($passphrase)) {
 | 
|---|
 | 445 |         local $SIG{PIPE} = 'IGNORE';
 | 
|---|
 | 446 |         open (FH, "|$command")  or die "can't fork: $!";
 | 
|---|
 | 447 |         print FH "$passphrase"  or die "can't write: $!";
 | 
|---|
 | 448 |         close FH                or die "can't close: status=$?";
 | 
|---|
 | 449 |     } else {
 | 
|---|
 | 450 |         system("$command") == 0 
 | 
|---|
 | 451 |             or die "system $command failed: $?";
 | 
|---|
 | 452 |     }
 | 
|---|
 | 453 | 
 | 
|---|
 | 454 |     if (!($fileout =~ /^\-$/)) {
 | 
|---|
 | 455 |         my $st_old = stat($file1) 
 | 
|---|
 | 456 |             or die "No file $file1: $!";
 | 
|---|
 | 457 |         my $st_new = stat($fileout) 
 | 
|---|
 | 458 |             or die "No file $fileout: $!";
 | 
|---|
 | 459 |         die ("Signed file is smaller than unsigned file") 
 | 
|---|
 | 460 |             unless ($st_new->size > $st_old->size);
 | 
|---|
 | 461 |         move("$fileout", "$file2") 
 | 
|---|
 | 462 |             or die "Move $fileout to $file2 failed: $!";
 | 
|---|
 | 463 |         chmod $stats->mode, $file2;
 | 
|---|
 | 464 |         chown $stats->uid, $stats->gid, $file2;
 | 
|---|
 | 465 |         flock(LOCKFILE, LOCK_UN);
 | 
|---|
 | 466 |     }
 | 
|---|
 | 467 | 
 | 
|---|
 | 468 |     if ($flag1 == 1) {
 | 
|---|
 | 469 |         unlink0( $fh1, $filename1 ) or die "Cannot unlink $filename1 safely";
 | 
|---|
 | 470 |     }
 | 
|---|
 | 471 |     if ($return_from_sign == 1) {
 | 
|---|
 | 472 |         return;
 | 
|---|
 | 473 |     }
 | 
|---|
 | 474 |     exit;
 | 
|---|
 | 475 | }
 | 
|---|
 | 476 | 
 | 
|---|
 | 477 | Getopt::Long::Configure ("posix_default");
 | 
|---|
 | 478 | Getopt::Long::Configure ("bundling");
 | 
|---|
 | 479 | # Getopt::Long::Configure ("debug");
 | 
|---|
 | 480 | 
 | 
|---|
 | 481 | GetOptions (\%opts, 'm=s', 'h|help', 'v|verbose', 'l|list',
 | 
|---|
 | 482 |             'c|cfgfile=s',
 | 
|---|
 | 483 |             'd|datafile=s',
 | 
|---|
 | 484 |             'p|passphrase=s',
 | 
|---|
 | 485 |             's|secretkey=s',
 | 
|---|
 | 486 |             'k|keyid=s',
 | 
|---|
 | 487 |             'create-cfgfile',  # -m F
 | 
|---|
 | 488 |             'print-cfgfile',   # -m f
 | 
|---|
 | 489 |             'create-datafile', # -m D
 | 
|---|
 | 490 |             'print-datafile',  # -m d
 | 
|---|
 | 491 |             'remove-signature',# -m R
 | 
|---|
 | 492 |             'sign',            # -m E
 | 
|---|
 | 493 |             'examine',         # -m e
 | 
|---|
 | 494 |             'generate-keys');  # -m G
 | 
|---|
 | 495 | 
 | 
|---|
 | 496 | if (defined ($opts{'h'})) {
 | 
|---|
 | 497 |     usage();
 | 
|---|
 | 498 |     exit;
 | 
|---|
 | 499 | }
 | 
|---|
 | 500 | 
 | 
|---|
 | 501 | if (defined($opts{'k'})) {
 | 
|---|
 | 502 |     $KEYID = $opts{'k'};
 | 
|---|
 | 503 | }
 | 
|---|
 | 504 | if (defined($opts{'c'})) {
 | 
|---|
 | 505 |     $cfgfile = $opts{'c'};
 | 
|---|
 | 506 | }
 | 
|---|
 | 507 | if (defined($opts{'d'})) {
 | 
|---|
 | 508 |     $datafile = $opts{'d'};
 | 
|---|
 | 509 | }
 | 
|---|
 | 510 | if (defined($opts{'p'})) {
 | 
|---|
 | 511 |     $passphrase = $opts{'p'};
 | 
|---|
 | 512 | }
 | 
|---|
 | 513 | if (defined($opts{'s'})) {
 | 
|---|
 | 514 |     $SIGDIR = $opts{'s'};
 | 
|---|
 | 515 | }
 | 
|---|
 | 516 | 
 | 
|---|
 | 517 | if (defined ($opts{'m'}) && ($opts{'m'} =~ /[FfDdREeG]{1}/) ) {
 | 
|---|
 | 518 |     $action = $opts{'m'};
 | 
|---|
 | 519 | }
 | 
|---|
 | 520 | elsif (defined ($opts{'create-cfgfile'})) {
 | 
|---|
 | 521 |     $action = 'F';
 | 
|---|
 | 522 | }
 | 
|---|
 | 523 | elsif (defined ($opts{'print-cfgfile'})) {
 | 
|---|
 | 524 |     $action = 'f';
 | 
|---|
 | 525 | }
 | 
|---|
 | 526 | elsif (defined ($opts{'create-datafile'})) {
 | 
|---|
 | 527 |     $action = 'D';
 | 
|---|
 | 528 | }
 | 
|---|
 | 529 | elsif (defined ($opts{'print-datafile'})) {
 | 
|---|
 | 530 |     $action = 'd';
 | 
|---|
 | 531 | }
 | 
|---|
 | 532 | elsif (defined ($opts{'remove-signature'})) {
 | 
|---|
 | 533 |     $action = 'R';
 | 
|---|
 | 534 | }
 | 
|---|
 | 535 | elsif (defined ($opts{'sign'})) {
 | 
|---|
 | 536 |     $action = 'E';
 | 
|---|
 | 537 | }
 | 
|---|
 | 538 | elsif (defined ($opts{'examine'})) {
 | 
|---|
 | 539 |     $action = 'e';
 | 
|---|
 | 540 | }
 | 
|---|
 | 541 | elsif (defined ($opts{'generate-keys'})) {
 | 
|---|
 | 542 |     $action = 'G';
 | 
|---|
 | 543 | }
 | 
|---|
 | 544 | else {
 | 
|---|
 | 545 |     usage();
 | 
|---|
 | 546 |     die ("No valid action specified !");
 | 
|---|
 | 547 | }
 | 
|---|
 | 548 | 
 | 
|---|
 | 549 | if (defined($ARGV[0])) {
 | 
|---|
 | 550 |     $file1 = $ARGV[0];
 | 
|---|
 | 551 | } 
 | 
|---|
 | 552 | if (defined($ARGV[1])) {
 | 
|---|
 | 553 |     $file2 = $ARGV[1];
 | 
|---|
 | 554 | } 
 | 
|---|
 | 555 | 
 | 
|---|
 | 556 | 
 | 
|---|
 | 557 | if (($action =~ /[REe]{1}/) && !defined($file1)) {
 | 
|---|
 | 558 |     usage();
 | 
|---|
 | 559 |     die("Option -m $action requires a filename (or '-' for stdio)\n");
 | 
|---|
 | 560 | }
 | 
|---|
 | 561 | 
 | 
|---|
 | 562 | if ($action =~ /^F$/) {
 | 
|---|
 | 563 |     if (!defined($file1)) {
 | 
|---|
 | 564 |         $file1 = $cfgfile;
 | 
|---|
 | 565 |     }
 | 
|---|
 | 566 |     $file2 = $cfgfile;
 | 
|---|
 | 567 |     sign_file ();
 | 
|---|
 | 568 | }
 | 
|---|
 | 569 | 
 | 
|---|
 | 570 | if ($action =~ /^D$/) {
 | 
|---|
 | 571 |     if (!defined($file1)) {
 | 
|---|
 | 572 |         $file1 = $datafile;
 | 
|---|
 | 573 |     }
 | 
|---|
 | 574 |     $file2 = $datafile;
 | 
|---|
 | 575 |     sign_file ();
 | 
|---|
 | 576 | }
 | 
|---|
 | 577 | 
 | 
|---|
 | 578 | if ($action =~ /^R$/) {
 | 
|---|
 | 579 |     # $file1 defined
 | 
|---|
 | 580 |     my $i = 0;
 | 
|---|
 | 581 |     while (defined($ARGV[$i])) {
 | 
|---|
 | 582 |         $file1 = $ARGV[$i];
 | 
|---|
 | 583 |         remove ();
 | 
|---|
 | 584 |         ++$i;
 | 
|---|
 | 585 |     }
 | 
|---|
 | 586 | }
 | 
|---|
 | 587 | 
 | 
|---|
 | 588 | if ($action =~ /^E$/) {
 | 
|---|
 | 589 |     # $file1 defined
 | 
|---|
 | 590 |     # default: $file2 = $file1
 | 
|---|
 | 591 |     check_signify_sign();
 | 
|---|
 | 592 |     my $i = 0;
 | 
|---|
 | 593 |     while (defined($ARGV[$i])) {
 | 
|---|
 | 594 |         $file1 = $ARGV[$i];
 | 
|---|
 | 595 |         $file2 = $file1;
 | 
|---|
 | 596 |         $return_from_sign = 1;
 | 
|---|
 | 597 |         sign_file ();
 | 
|---|
 | 598 |         ++$i;
 | 
|---|
 | 599 |     }
 | 
|---|
 | 600 | }
 | 
|---|
 | 601 | 
 | 
|---|
 | 602 | if ($action =~ /^e$/) {
 | 
|---|
 | 603 |     # $file1 defined
 | 
|---|
 | 604 |     # default: $file2 = stdout
 | 
|---|
 | 605 |     check_signify_verify();
 | 
|---|
 | 606 |     my $i = 0;
 | 
|---|
 | 607 |     my $ret = 0;
 | 
|---|
 | 608 |     while (defined($ARGV[$i])) {
 | 
|---|
 | 609 |         print "\n";
 | 
|---|
 | 610 |         $file1 = $ARGV[$i];
 | 
|---|
 | 611 |         $ret += examine ();
 | 
|---|
 | 612 |         ++$i;
 | 
|---|
 | 613 |         print "\n--------------------------------\n" if (defined($ARGV[$i]));
 | 
|---|
 | 614 |     }
 | 
|---|
 | 615 |     exit($ret);
 | 
|---|
 | 616 | }
 | 
|---|
 | 617 | 
 | 
|---|
 | 618 | if ($action =~ /^f$/) {
 | 
|---|
 | 619 |     $file1 = $cfgfile;
 | 
|---|
 | 620 |     $file2 = "-";
 | 
|---|
 | 621 |     print_cfgfile ();
 | 
|---|
 | 622 | }
 | 
|---|
 | 623 | 
 | 
|---|
 | 624 | if ($action =~ /^d$/) {
 | 
|---|
 | 625 |     # $file1 irrelevant
 | 
|---|
 | 626 |     if (defined($opts{'l'})) {
 | 
|---|
 | 627 |         print_datafile ();
 | 
|---|
 | 628 |     } else {
 | 
|---|
 | 629 |         $file1 = $datafile;
 | 
|---|
 | 630 |         $file2 = "-";
 | 
|---|
 | 631 |         print_cfgfile ();
 | 
|---|
 | 632 |     }
 | 
|---|
 | 633 | }
 | 
|---|
 | 634 | 
 | 
|---|
 | 635 | 
 | 
|---|
 | 636 | 
 | 
|---|