summaryrefslogtreecommitdiffstats
path: root/scripts/samhainadmin-sig.pl.in
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/samhainadmin-sig.pl.in')
-rwxr-xr-xscripts/samhainadmin-sig.pl.in636
1 files changed, 636 insertions, 0 deletions
diff --git a/scripts/samhainadmin-sig.pl.in b/scripts/samhainadmin-sig.pl.in
new file mode 100755
index 0000000..10bb6ea
--- /dev/null
+++ b/scripts/samhainadmin-sig.pl.in
@@ -0,0 +1,636 @@
+#! /usr/bin/perl
+
+# Copyright Rainer Wichmann (2004)
+#
+# License Information:
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+use warnings;
+use strict;
+use Getopt::Long;
+use File::Basename;
+use File::Copy;
+use File::stat;
+use File::Temp qw/ tempfile tempdir unlink0 /;
+use IO::Handle;
+use Fcntl qw(:DEFAULT :flock);
+use Tie::File;
+
+# Do I/O to the data file in binary mode (so it
+# wouldn't complain about invalid UTF-8 characters).
+use bytes;
+
+File::Temp->safe_level( File::Temp::HIGH );
+
+my %opts = ();
+my $action;
+my $file1;
+my $file2;
+my $passphrase;
+my $secretkey;
+my $return_from_sign = 0;
+my $no_print_examine = 0;
+my $no_remove_lock = 0;
+my $base = basename($0);
+
+my $cfgfile = "@myconffile@";
+my $datafile = "@mydatafile@";
+my $daemon = "@sbindir@/@install_name@";
+my $signify = "@mysignify@";
+
+my $SIGDIR = "$ENV{'HOME'}/.signify";
+my $KEYID = "@install_name@";
+
+$cfgfile =~ s/^REQ_FROM_SERVER//;
+$datafile =~ s/^REQ_FROM_SERVER//;
+
+$signify = "signify-openbsd" if ($signify eq "");
+
+sub usage() {
+ print "Usage:\n";
+ print " $base { -m F | --create-cfgfile } [options] [in.cfgfile]\n";
+ print " Sign the configuration file. If in.cfgfile is given, sign it\n";
+ print " and install it as configuration file.\n\n";
+
+ print " $base { -m f | --print-cfgfile } [options] \n";
+ print " Print the configuration file to stdout. Signatures are removed.\n\n";
+
+ print " $base { -m D | --create-datafile } [options] [in.datafile]\n";
+ print " Sign the database file. If in.datafile is given, sign it\n";
+ print " and install it as database file.\n\n";
+
+ print " $base { -m d | --print-datafile } [options] \n";
+ print " Print the database file to stdout. Signatures are removed. Use\n";
+ print " option --list to list files in database rather than printing the raw file.\n\n";
+
+ print " $base { -m R | --remove-signature } [options] file1 [file2 ...]\n";
+ print " Remove cleartext signature from input file(s). The file\n";
+ print " is replaced by the non-signed file.\n\n";
+
+ print " $base { -m E | --sign } [options] file1 [file2 ...]\n";
+ print " Sign file(s) with a cleartext signature. The file\n";
+ print " is replaced by the signed file.\n\n";
+
+ print " $base { -m e | --examine } [options] file1 [file2 ...]\n";
+ print " Report signature status of file(s).\n\n";
+
+ print " $base { -m G | --generate-keys } [options] \n";
+ print " Generate a signify keypair to use for signing.\n\n";
+
+ print "Options:\n";
+ print " -c cfgfile --cfgfile cfgfile\n";
+ print " Select an alternate configuration file.\n\n";
+
+ print " -d datafile --datafile datafile\n";
+ print " Select an alternate database file.\n\n";
+
+ print " -p passphrase --passphrase passphrase\n";
+ print " Set the passphrase for signify. By default, signify will ask.\n\n";
+
+ print " -s signify_dir --signify-dir signify_dir\n";
+ print " Select an alternate directory to locate the secret keyring.\n";
+ print " Will use '$ENV{'HOME'}/.signify/' by default.\n\n";
+
+ print " -k keyid --keyid keyid\n";
+ print " Select the keyid to use for signing.\n\n";
+
+ print " -l --list\n";
+ print " List the files in database rather than printing the raw file.\n\n";
+
+ print " -v --verbose\n";
+ print " Verbose output.\n\n";
+ return;
+}
+
+sub check_signify_uid () {
+ if (0 != $>) {
+ print "--------------------------------------------------\n";
+ print "\n";
+ print " You are not root. Please remember that samhain/yule\n";
+ print " will use the public key of root to verify a signature.\n";
+ print "\n";
+ print "--------------------------------------------------\n";
+ } else {
+ if (!("@yulectl_prg@" =~ //)) {
+ print "--------------------------------------------------\n";
+ print "\n";
+ print " Please remember that yule will drop root after startup. Signature\n";
+ print " verification on SIGHUP will fail if you do not import the public key\n";
+ print " into the ~/.signify/ directory of the non-root yule user.\n";
+ print "\n";
+ print "--------------------------------------------------\n";
+ }
+ }
+}
+
+sub check_signify_sign () {
+ if ( defined($secretkey)) {
+ if ( (!-d "$secretkey")){
+ print "--------------------------------------------------\n";
+ print "\n";
+ print " Secret key $secretkey not found!\n";
+ print "\n";
+ print " Please check the path/name of the alternate secret key.\n";
+ print "\n";
+ print "--------------------------------------------------\n";
+ print "\n";
+ exit;
+ }
+ } else {
+ if ( (!-d "$SIGDIR") || (!-e "${SIGDIR}/${KEYID}.sec")) {
+ print "--------------------------------------------------\n";
+ print "\n";
+ if (!-d "$SIGDIR") {
+ print " Directory $SIGDIR not found!\n";
+ } else {
+ print " Secret key ${SIGDIR}/${KEYID}.sec not found!\n";
+ }
+ print "\n";
+ print " This indicates that you have never created a \n";
+ print " public/private keypair, and thus cannot sign.\n";
+ print " \n";
+ print " Please use $0 --generate-keys or\n";
+ print " $signify -G -s ${SIGDIR}/${KEYID}.sec -p ${SIGDIR}/${KEYID}.pub\n";
+ print " to generate a public/private keypair first.\n";
+ print "\n";
+ print "--------------------------------------------------\n";
+ print "\n";
+ exit;
+ }
+ }
+}
+
+sub check_signify_verify () {
+ if ( (!-d "${SIGDIR}") || (!-e "${SIGDIR}/${KEYID}.pub")) {
+ print "--------------------------------------------------\n";
+ print "\n";
+ if (!-d "$SIGDIR") {
+ print " Directory $SIGDIR not found!\n";
+ } else {
+ print " Public key ${SIGDIR}/${KEYID}.pub not found!\n";
+ }
+ print "\n";
+ print " This indicates that you have no public key\n";
+ print " to verify signatures.\n";
+ print " \n";
+ print " Please copy the public key ${KEYID}.pub of\n";
+ print " the user who is signing the configuration/database files\n";
+ print " into the directory $SIGDIR.\n";
+ print "\n";
+ print "--------------------------------------------------\n";
+ print "\n";
+ exit;
+ }
+}
+
+
+sub generate () {
+ my $command = "$signify -G -s ${SIGDIR}/${KEYID}.sec -p ${SIGDIR}/${KEYID}.pub";
+ if (!-d "${SIGDIR}") {
+ unless(mkdir "$SIGDIR", 0750) {
+ die "Creating directory $SIGDIR failed: $?";
+ }
+ }
+ check_signify_uid();
+ system ($command) == 0
+ or die "system $command failed: $?";
+ exit;
+}
+
+sub examine () {
+ my $iscfg = 0;
+ my $have_fp = 0;
+ my $have_sig = 0;
+ my $message = '';
+ my $retval = 9;
+ my $fh;
+ my $filename;
+
+ if (!($file1 =~ /^\-$/)) {
+ die ("Cannot open $file1 for read: $!") unless ((-e $file1) && (-r _));
+ }
+ open FIN, "<$file1" or die "Cannot open $file1 for read: $!";
+
+ my $dir = tempdir( CLEANUP => 1 );
+ $filename = $dir . "/exa_jhfdbilw." . $$;
+ open $fh, ">$filename" or die "Cannot open $filename";
+ autoflush $fh 1;
+
+ while (<FIN>) {
+ print $fh $_;
+ if ($_ =~ /^\s*\[Misc\]/) {
+ $iscfg = 1;
+ }
+ }
+ if ($iscfg == 1) {
+ $message .= "File $file1 is a configuration file\n\n";
+ } else {
+ $message .= "File $file1 is a database file\n\n";
+ }
+
+
+ my $command = "$signify -Vem /dev/null -p ${SIGDIR}/${KEYID}.pub ";
+ $command .= "-x $filename ";
+ if (defined($opts{'v'})) {
+ $command .= "2>&1";
+ } else {
+ $command .= "2>/dev/null";
+ }
+
+ print STDOUT "Using: $command\n\n" if (defined($opts{'v'}));
+ open SIGIN, "$command |" or die "Cannot fork: $!";
+
+ while (<SIGIN>) {
+ chomp ($_);
+ if ($_ =~ /^Signature Verified$/) {
+ $message .= "GOOD signature with key: ${SIGDIR}/${KEYID}.pub\n";
+ $have_sig = 1;
+ $retval = 0;
+ }
+ print STDOUT $_ if (defined($opts{'v'}));
+ }
+ close (SIGIN);
+ print STDOUT "\n" if (defined($opts{'v'}));
+ if ($have_sig == 0) {
+ $message .= "NO valid signature found\n";
+ }
+ close (FIN);
+ if ($no_print_examine == 0) {
+ print STDOUT $message;
+ }
+ unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
+ return $retval;
+}
+
+sub wstrip ($) {
+ $_ = shift;
+ $_ =~ s/\s+//g;
+ return $_;
+}
+
+sub remove () {
+ my $bodystart = 1;
+ my $sigstart = 0;
+ my $sigend = 0;
+ my $filename = "";
+ my $fh;
+ my $stats;
+
+ open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
+ if (!($file1 =~ /^\-$/)) {
+ flock(FH, LOCK_EX) unless ($no_remove_lock == 1);
+ my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
+ $filename = $dir . "/rem_iqegBCQb." . $$;
+ open $fh, ">$filename" or die "Cannot open $filename";
+ $stats = stat($file1);
+ } else {
+ open $fh, ">$file1" or die "Cannot open file $file1 for write: $!";
+ }
+ autoflush $fh 1;
+ while (<FH>) {
+ if ($_ =~ /^untrusted comment: /) {
+ $sigstart = 1;
+ $bodystart = 0;
+ next;
+ } elsif (($sigstart == 1) && (wstrip($_) =~ m{^(?: [A-Za-z0-9+/]{4} )*(?:[A-Za-z0-9+/]{2} [AEIMQUYcgkosw048]=|[A-Za-z0-9+/][AQgw]==)?$}xm )) {
+ $sigstart = 0;
+ $bodystart = 1;
+ next;
+ } elsif (($sigstart == 1) && ($bodystart == 0)) {
+ # comment NOT followed by signature
+ $sigstart = 0;
+ next;
+ }
+
+ if ($bodystart == 1) {
+ print $fh $_;
+ }
+ }
+ if (!($file1 =~ /^\-$/)) {
+ copy("$filename", "$file1")
+ or die "Copy $filename to $file1 failed: $!";
+ chmod $stats->mode, $file1;
+ chown $stats->uid, $stats->gid, $file1;
+ flock(FH, LOCK_UN) unless ($no_remove_lock == 1);
+ close FH;
+ }
+ unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
+ return;
+}
+
+sub print_cfgfile () {
+ my $bodystart = 0;
+ my $sigstart = 0;
+
+ if (!defined($file2)) {
+ $file2 = '-';
+ }
+
+ open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
+ open FO, ">$file2" or die "Cannot open file $file2 for write: $!";
+ while (<FH>) {
+ if ($_ =~ /^untrusted comment: /) {
+ $sigstart = 1;
+ $bodystart = 0;
+ next;
+ } elsif (($sigstart == 1) && (wstrip($_) =~ m{^(?: [A-Za-z0-9+/]{4} )*(?:[A-Za-z0-9+/]{2} [AEIMQUYcgkosw048]=|[A-Za-z0-9+/][AQgw]==)?$}xm )) {
+ $sigstart = 0;
+ $bodystart = 1;
+ next;
+ } elsif (($sigstart == 1) && ($bodystart == 0)) {
+ # comment NOT followed by signature
+ $sigstart = 0;
+ next;
+ }
+ if ($bodystart == 1) {
+ print FO $_;
+ }
+ }
+ exit;
+}
+
+sub print_datafile () {
+ die ("Cannot find program $daemon")
+ unless (-e $daemon);
+ if (defined($opts{'v'})) {
+ open FH, "$daemon --full-detail -d $datafile |"
+ or die "Cannot open datafile $datafile for read: $!";
+ } else {
+ open FH, "$daemon -d $datafile |"
+ or die "Cannot open datafile $datafile for read: $!";
+ }
+ while (<FH>) {
+ print $_;
+ }
+ exit;
+}
+
+sub sign_file () {
+
+ my $fileout = '';
+ my $bodystart = 1;
+ my $sigstart = 0;
+ my $sigend = 0;
+ my $stats;
+ my $fh1;
+ my $filename1;
+ my $flag1 = 0;
+
+ check_signify_uid();
+
+ if (!defined($file2)) {
+ $file2 = $file1;
+ }
+
+ if ($file1 =~ /^\-$/) {
+ my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
+ $filename1 = $dir . "/sig_vs8827sd." . $$;
+ open $fh1, ">$filename1" or die "Cannot open $filename1";
+ $flag1 = 1;
+ # my ($fh1, $filename1) = tempfile(UNLINK => 1);
+
+ while (<STDIN>) {
+ if ($_ =~ /^untrusted comment: /) {
+ $sigstart = 1;
+ $bodystart = 0;
+ next;
+ } elsif (($sigstart == 1) && (wstrip($_) =~ m{^(?: [A-Za-z0-9+/]{4} )*(?:[A-Za-z0-9+/]{2} [AEIMQUYcgkosw048]=|[A-Za-z0-9+/][AQgw]==)?$}xm )) {
+ $sigstart = 0;
+ $bodystart = 1;
+ next;
+ } elsif (($sigstart == 1) && ($bodystart == 0)) {
+ #comment NOT followed by signature
+ $sigstart = 0;
+ next;
+ }
+
+ if ($bodystart == 1) {
+ print $fh1 $_;
+ }
+ }
+ $file1 = $filename1;
+ $fileout = '-';
+ } else {
+ open (LOCKFILE, "<$file1") or die "Cannot open $file1: $!";
+ flock(LOCKFILE, LOCK_EX);
+ $no_print_examine = 1;
+ $no_remove_lock = 1;
+ if (examine() < 2) {
+ remove();
+ }
+ $fileout = $file1 . ".sig";
+ $stats = stat($file1)
+ or die "No file $file1: $!";
+ }
+
+ my $command = "$signify -Se ";
+ $command .= "-s ${SIGDIR}/${KEYID}.sec ";
+ $command .= "-x ${fileout} ";
+ $command .= "-m $file1";
+
+ if (defined($passphrase)) {
+ local $SIG{PIPE} = 'IGNORE';
+ open (FH, "|$command") or die "can't fork: $!";
+ print FH "$passphrase" or die "can't write: $!";
+ close FH or die "can't close: status=$?";
+ } else {
+ system("$command") == 0
+ or die "system $command failed: $?";
+ }
+
+ if (!($fileout =~ /^\-$/)) {
+ my $st_old = stat($file1)
+ or die "No file $file1: $!";
+ my $st_new = stat($fileout)
+ or die "No file $fileout: $!";
+ die ("Signed file is smaller than unsigned file")
+ unless ($st_new->size > $st_old->size);
+ move("$fileout", "$file2")
+ or die "Move $fileout to $file2 failed: $!";
+ chmod $stats->mode, $file2;
+ chown $stats->uid, $stats->gid, $file2;
+ flock(LOCKFILE, LOCK_UN);
+ }
+
+ if ($flag1 == 1) {
+ unlink0( $fh1, $filename1 ) or die "Cannot unlink $filename1 safely";
+ }
+ if ($return_from_sign == 1) {
+ return;
+ }
+ exit;
+}
+
+Getopt::Long::Configure ("posix_default");
+Getopt::Long::Configure ("bundling");
+# Getopt::Long::Configure ("debug");
+
+GetOptions (\%opts, 'm=s', 'h|help', 'v|verbose', 'l|list',
+ 'c|cfgfile=s',
+ 'd|datafile=s',
+ 'p|passphrase=s',
+ 's|secretkey=s',
+ 'k|keyid=s',
+ 'create-cfgfile', # -m F
+ 'print-cfgfile', # -m f
+ 'create-datafile', # -m D
+ 'print-datafile', # -m d
+ 'remove-signature',# -m R
+ 'sign', # -m E
+ 'examine', # -m e
+ 'generate-keys'); # -m G
+
+if (defined ($opts{'h'})) {
+ usage();
+ exit;
+}
+
+if (defined($opts{'k'})) {
+ $KEYID = $opts{'k'};
+}
+if (defined($opts{'c'})) {
+ $cfgfile = $opts{'c'};
+}
+if (defined($opts{'d'})) {
+ $datafile = $opts{'d'};
+}
+if (defined($opts{'p'})) {
+ $passphrase = $opts{'p'};
+}
+if (defined($opts{'s'})) {
+ $SIGDIR = $opts{'s'};
+}
+
+if (defined ($opts{'m'}) && ($opts{'m'} =~ /[FfDdREeG]{1}/) ) {
+ $action = $opts{'m'};
+}
+elsif (defined ($opts{'create-cfgfile'})) {
+ $action = 'F';
+}
+elsif (defined ($opts{'print-cfgfile'})) {
+ $action = 'f';
+}
+elsif (defined ($opts{'create-datafile'})) {
+ $action = 'D';
+}
+elsif (defined ($opts{'print-datafile'})) {
+ $action = 'd';
+}
+elsif (defined ($opts{'remove-signature'})) {
+ $action = 'R';
+}
+elsif (defined ($opts{'sign'})) {
+ $action = 'E';
+}
+elsif (defined ($opts{'examine'})) {
+ $action = 'e';
+}
+elsif (defined ($opts{'generate-keys'})) {
+ $action = 'G';
+}
+else {
+ usage();
+ die ("No valid action specified !");
+}
+
+if (defined($ARGV[0])) {
+ $file1 = $ARGV[0];
+}
+if (defined($ARGV[1])) {
+ $file2 = $ARGV[1];
+}
+
+
+if (($action =~ /[REe]{1}/) && !defined($file1)) {
+ usage();
+ die("Option -m $action requires a filename (or '-' for stdio)\n");
+}
+
+if ($action =~ /^F$/) {
+ if (!defined($file1)) {
+ $file1 = $cfgfile;
+ }
+ $file2 = $cfgfile;
+ sign_file ();
+}
+
+if ($action =~ /^D$/) {
+ if (!defined($file1)) {
+ $file1 = $datafile;
+ }
+ $file2 = $datafile;
+ sign_file ();
+}
+
+if ($action =~ /^R$/) {
+ # $file1 defined
+ my $i = 0;
+ while (defined($ARGV[$i])) {
+ $file1 = $ARGV[$i];
+ remove ();
+ ++$i;
+ }
+}
+
+if ($action =~ /^E$/) {
+ # $file1 defined
+ # default: $file2 = $file1
+ check_signify_sign();
+ my $i = 0;
+ while (defined($ARGV[$i])) {
+ $file1 = $ARGV[$i];
+ $file2 = $file1;
+ $return_from_sign = 1;
+ sign_file ();
+ ++$i;
+ }
+}
+
+if ($action =~ /^e$/) {
+ # $file1 defined
+ # default: $file2 = stdout
+ check_signify_verify();
+ my $i = 0;
+ my $ret = 0;
+ while (defined($ARGV[$i])) {
+ print "\n";
+ $file1 = $ARGV[$i];
+ $ret += examine ();
+ ++$i;
+ print "\n--------------------------------\n" if (defined($ARGV[$i]));
+ }
+ exit($ret);
+}
+
+if ($action =~ /^f$/) {
+ $file1 = $cfgfile;
+ $file2 = "-";
+ print_cfgfile ();
+}
+
+if ($action =~ /^d$/) {
+ # $file1 irrelevant
+ if (defined($opts{'l'})) {
+ print_datafile ();
+ } else {
+ $file1 = $datafile;
+ $file2 = "-";
+ print_cfgfile ();
+ }
+}
+
+
+