source: trunk/scripts/samhainadmin.pl.in@ 1

Last change on this file since 1 was 1, checked in by katerina, 19 years ago

Initial import

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