]> git.sthu.org Git - pgp-tools.git/blob - gpgdir/test/data-dir/dir3/dir4/gpgdir-copy.pl
Update d.changelog to mention previous changes made to keyanalyze
[pgp-tools.git] / gpgdir / test / data-dir / dir3 / dir4 / gpgdir-copy.pl
1 #!/usr/bin/perl -w
2 #
3 ###########################################################################
4 #
5 # File: gpgdir
6 #
7 # URL: http://www.cipherdyne.org/gpgdir/
8 #
9 # Purpose: To encrypt/decrypt whole directories
10 #
11 # Author: Michael Rash (mbr@cipherdyne.com)
12 #
13 # Version: 1.7
14 #
15 # Copyright (C) 2002-2007 Michael Rash (mbr@cipherdyne.org)
16 #
17 # License (GNU General Public License):
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 # USA
28 #
29 ###########################################################################
30 #
31 # $Id: gpgdir 246 2008-02-18 14:29:16Z mbr $
32 #
33
34 use lib '/usr/lib/gpgdir';
35 use File::Find;
36 use File::Copy;
37 use Term::ReadKey;
38 use GnuPG::Interface;
39 use IO::File;
40 use IO::Handle;
41 use Getopt::Long;
42 use Cwd;
43 use strict;
44
45 ### set the current gpgdir version and file revision numbers
46 my $version = '1.7';
47 my $revision_svn = '$Revision: 246 $';
48 my $rev_num = '1';
49 ($rev_num) = $revision_svn =~ m|\$Rev.*:\s+(\S+)|;
50
51 ### establish some defaults
52 my $encrypt_user = '';
53 my $gpg_homedir = '';
54 my $dir = '';
55 my $pw = '';
56 my $encrypt_dir = '';
57 my $decrypt_dir = '';
58 my $homedir = '';
59 my $exclude_pat = '';
60 my $exclude_file = '';
61 my $include_pat = '';
62 my $include_file = '';
63 my $total_encrypted = 0;
64 my $total_decrypted = 0;
65 my $norecurse = 0;
66 my $printver = 0;
67 my $no_delete = 0;
68 my $no_fs_times = 0;
69 my $test_and_exit = 0;
70 my $trial_run = 0;
71 my $skip_test_mode = 0;
72 my $verbose = 0;
73 my $quiet = 0;
74 my $use_gpg_agent = 0; ### use gpg-agent for passwords
75 my $gpg_agent_info = '';
76 my $force_mode = 0;
77 my $help = 0;
78 my $wipe_mode = 0;
79 my $encrypt_mode = 0;
80 my $use_default_key = 0;
81 my $pw_file = '';
82 my $wipe_cmd = '/usr/bin/wipe';
83 my $wipe_cmdline = '';
84 my $wipe_interactive = 0;
85 my $interactive_mode = 0;
86 my $ascii_armor_mode = 0;
87 my @exclude_patterns = ();
88 my @include_patterns = ();
89 my %files = ();
90 my %options = ();
91 my %obfuscate_ctrs = ();
92 my %obfuscated_dirs = ();
93 my $have_obfuscated_file = 0;
94 my $cmdline_no_password = 0;
95 my $obfuscate_mode = 0;
96 my $obfuscate_map_filename = '.gpgdir_map_file';
97 my $overwrite_encrypted = 0;
98 my $overwrite_decrypted = 0;
99 my $symmetric_mode = 0;
100 my $DEL_SOURCE_FILE = 1;
101 my $NO_DEL_SOURCE_FILE = 0;
102
103 ### for user answers
104 my $ACCEPT_YES_DEFAULT = 1;
105 my $ACCEPT_NO_DEFAULT = 2;
106
107 unless ($< == $>) {
108 die "[*] Real and effective uid must be the same. Make sure\n",
109 " gpgdir has not been installed as a SUID binary.\n",
110 "Exiting.";
111 }
112
113 my @args_cp = @ARGV;
114
115 ### make Getopts case sensitive
116 Getopt::Long::Configure('no_ignore_case');
117
118 die "[-] Use --help for usage information.\n" unless(GetOptions (
119 'encrypt=s' => \$encrypt_dir, # Encrypt files in this directory.
120 'decrypt=s' => \$decrypt_dir, # Decrypt files in this directory.
121 'gnupg-dir=s' => \$gpg_homedir, # Path to /path/to/.gnupg directory.
122 'pw-file=s' => \$pw_file, # Read password out of this file.
123 'agent' => \$use_gpg_agent, # Use gpg-agent for passwords.
124 'Agent-info=s' => \$gpg_agent_info, # Specify GnuPG agent connection
125 # information.
126 'Wipe' => \$wipe_mode, # Securely delete unencrypted files.
127 'wipe-path=s' => \$wipe_cmd, # Path to wipe command.
128 'wipe-interactive' => \$wipe_interactive, # Disable "wipe -I"
129 'wipe-cmdline=s' => \$wipe_cmdline, # Specify wipe command line.
130 'Obfuscate-filenames' => \$obfuscate_mode, # substitute real filenames
131 # with manufactured ones.
132 'obfuscate-map-file=s' => \$obfuscate_map_filename, # path to mapping file.
133 'Force' => \$force_mode, # Continue if files can't be deleted.
134 'overwrite-encrypted' => \$overwrite_encrypted, # Overwrite encrypted files
135 # even if they exist.
136 'overwrite-decrypted' => \$overwrite_decrypted, # Overwrite decrypted files
137 # even if they exist.
138 'Exclude=s' => \$exclude_pat, # Exclude a pattern from encrypt/decrypt
139 # cycle.
140 'Exclude-from=s' => \$exclude_file, # Exclude patterns in <file> from
141 # encrypt decrypt cycle.
142 'Include=s' => \$include_pat, # Specify a pattern used to restrict
143 # encrypt/decrypt operation to.
144 'Include-from=s' => \$include_file, # Specify a file of include patterns to
145 # restrict all encrypt/decrypt
146 # operations to.
147 'test-mode' => \$test_and_exit, # Run encrypt -> decrypt test only and
148 # exit.
149 'Trial-run' => \$trial_run, # Don't modify any files; just show what
150 # would have happened.
151 'quiet' => \$quiet, # Print as little as possible to
152 # stdout.
153 'Interactive' => \$interactive_mode, # Query the user before encrypting/
154 # decrypting/deleting any files.
155 'Key-id=s' => \$encrypt_user, # Specify encrypt/decrypt key
156 'Default-key' => \$use_default_key, # Assume that default-key is set within
157 # ~/.gnupg/options.
158 'Symmetric' => \$symmetric_mode, # encrypt using symmetric cipher.
159 # (this option is not required to
160 # also decrypt, GnuPG handles
161 # that automatically).
162 'Plain-ascii' => \$ascii_armor_mode, # Ascii armor mode (creates non-binary
163 # encrypted files).
164 'skip-test' => \$skip_test_mode, # Skip encrypt -> decrypt test.
165 'no-recurse' => \$norecurse, # Don't encrypt/decrypt files in
166 # subdirectories.
167 'no-delete' => \$no_delete, # Don't delete files once they have
168 # been encrypted.
169 'no-password' => \$cmdline_no_password, # Do not query for a password (only
170 # useful for when the gpg literally
171 # has no password).
172 'user-homedir=s' => \$homedir, # Path to home directory.
173 'no-preserve-times' => \$no_fs_times, # Don't preserve mtimes or atimes.
174 'verbose' => \$verbose, # Verbose mode.
175 'Version' => \$printver, # Print version
176 'help' => \$help # Print help
177 ));
178 &usage_and_exit() if $help;
179
180 print "[+] gpgdir v$version (file revision: $rev_num)\n",
181 " by Michael Rash <mbr\@cipherdyne.org>\n"
182 and exit 0 if $printver;
183
184 if ($symmetric_mode and ($use_gpg_agent or $gpg_agent_info)) {
185 die "[*] gpg-agent incompatible with --Symmetric mode";
186 }
187
188 if ($encrypt_dir and $overwrite_decrypted) {
189 die "[*] The -e and --overwrite-decrypted options are incompatible.";
190 }
191 if ($decrypt_dir and $overwrite_encrypted) {
192 die "[*] The -d and --overwrite-encrypted options are incompatible.";
193 }
194
195 if ($wipe_mode) {
196 unless (-e $wipe_cmd) {
197 die "[*] Can't find wipe command at: $wipe_cmd,\n",
198 " use --wipe-path to specify path.";
199 }
200 unless (-e $wipe_cmd) {
201 die "[*] Can't execute $wipe_cmd";
202 }
203 }
204
205 ### build up GnuPG options hash
206 if ($verbose) {
207 %options = ('homedir' => $gpg_homedir);
208 } else {
209 %options = (
210 'batch' => 1,
211 'homedir' => $gpg_homedir
212 );
213 }
214
215 $options{'armor'} = 1 if $ascii_armor_mode;
216
217 ### get the path to the user's home directory
218 $homedir = &get_homedir() unless $homedir;
219
220 unless ($symmetric_mode) {
221 if ($gpg_homedir) { ### specified on the command line with --gnupg-dir
222 unless ($gpg_homedir =~ /\.gnupg$/) {
223 die "[*] Must specify the path to a user .gnupg directory ",
224 "e.g. /home/username/.gnupg\n";
225 }
226 } else {
227 if (-d "${homedir}/.gnupg") {
228 $gpg_homedir = "${homedir}/.gnupg";
229 }
230 }
231 unless (-d $gpg_homedir) {
232 die "[*] GnuPG directory: ${homedir}/.gnupg does not exist. Please\n",
233 " create it by executing: \"gpg --gen-key\". Exiting.\n";
234 }
235
236 ### get the key identifier from ~/.gnupg
237 $encrypt_user = &get_key() unless $encrypt_user or $use_default_key;
238 }
239
240 if ($decrypt_dir and $encrypt_dir) {
241 die "[*] You cannot encrypt and decrypt the same directory.\n";
242 &usage_and_exit();
243 }
244
245 unless ($decrypt_dir or $encrypt_dir or $test_and_exit) {
246 print "[*] Please specify -e <dir>, -d <dir>, or --test-mode\n";
247 &usage_and_exit();
248 }
249
250 ### exclude file pattern
251 push @exclude_patterns, $exclude_pat if $exclude_pat;
252
253 if ($exclude_file) {
254 open P, "< $exclude_file" or die "[*] Could not open file: $exclude_file";
255 my @lines = <P>;
256 close P;
257 for my $line (@lines) {
258 next unless $line =~ /\S/;
259 chomp $line;
260 push @exclude_patterns, qr{$line};
261 }
262 }
263
264 ### include file pattern
265 push @include_patterns, $include_pat if $include_pat;
266
267 if ($include_file) {
268 open P, "< $include_file" or die "[*] Could not open file: $include_file";
269 my @lines = <P>;
270 close P;
271 for my $line (@lines) {
272 next unless $line =~ /\S/;
273 chomp $line;
274 push @include_patterns, qr{$line};
275 }
276 }
277
278 if ($encrypt_dir) {
279 $dir = $encrypt_dir;
280 $encrypt_mode = 1;
281 } elsif ($decrypt_dir) {
282 $dir = $decrypt_dir;
283 $encrypt_mode = 0;
284 }
285
286 if ($dir) {
287 die "[*] Directory does not exist: $dir" unless -e $dir;
288 die "[*] Not a directory: $dir" unless -d $dir;
289 }
290
291 ### don't need to test encrypt/decrypt ability if we are running
292 ### in --Trial-run mode.
293 $skip_test_mode = 1 if $trial_run;
294
295 my $initial_dir = cwd or die "[*] Could not get CWD: $!";
296
297 if ($symmetric_mode) {
298 &get_password();
299 } else {
300 &get_password() unless $encrypt_mode and $skip_test_mode;
301 }
302
303 if ($dir eq '.') {
304 $dir = $initial_dir;
305 } elsif ($dir !~ m|^/|) {
306 $dir = $initial_dir . '/' . $dir;
307 }
308 $dir =~ s|/$||; ### remove any trailing slash
309
310 ### run a test to make sure gpgdir and encrypt and decrypt a file
311 unless ($skip_test_mode) {
312 my $rv = &test_mode();
313 exit $rv if $test_and_exit;
314 }
315
316 if ($encrypt_mode) {
317 print "[+] Encrypting directory: $dir\n" unless $quiet;
318 } else {
319 print "[+] Decrypting directory: $dir\n" unless $quiet;
320 }
321
322 ### build a hash of file paths to work against
323 &get_files($dir);
324
325 ### perform the gpg operation (encrypt/decrypt)
326 &gpg_operation();
327
328 &obfuscated_mapping_files() if $obfuscate_mode;
329
330 unless ($obfuscate_mode) {
331 if ($have_obfuscated_file) {
332 print "[-] Obfuscated filenames detected, try decrypting with -O.\n"
333 unless $quiet;
334 }
335 }
336
337 if ($encrypt_mode) {
338 print "[+] Total number of files encrypted: " .
339 "$total_encrypted\n" unless $quiet;
340 } else {
341 print "[+] Total number of files decrypted: " .
342 "$total_decrypted\n" unless $quiet;
343 }
344
345 exit 0;
346 #==================== end main =====================
347
348 sub encrypt_file() {
349 my ($in_file, $out_file, $del_flag) = @_;
350
351 my $gpg = GnuPG::Interface->new();
352 $gpg->options->hash_init(%options);
353
354 die "[*] Could not create new gpg object with ",
355 "homedir: $gpg_homedir" unless $gpg;
356
357 unless ($symmetric_mode or $use_default_key) {
358 $gpg->options->default_key($encrypt_user);
359 $gpg->options->push_recipients($encrypt_user);
360 }
361
362 my ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) =
363 (IO::File->new($in_file),
364 IO::File->new("> $out_file"),
365 IO::Handle->new(),
366 IO::Handle->new(),
367 IO::Handle->new());
368
369 my $handles = GnuPG::Handles->new(
370 stdin => $input_fh,
371 stdout => $output_fh,
372 stderr => $error_fh,
373 passphrase => $pw_fh,
374 status => $status_fh
375 );
376 $handles->options('stdin')->{'direct'} = 1;
377 $handles->options('stdout')->{'direct'} = 1;
378
379 my $pid;
380
381 if ($use_gpg_agent or $gpg_agent_info) {
382
383 ### set environment explicitly if --Agent was specified
384 if ($gpg_agent_info) {
385 $ENV{'GPG_AGENT_INFO'} = $gpg_agent_info;
386 }
387
388 $pid = $gpg->encrypt('handles' => $handles,
389 'command_args' => [ qw( --use-agent ) ]);
390
391 } else {
392 if ($symmetric_mode) {
393 $pid = $gpg->encrypt_symmetrically('handles' => $handles);
394 } else {
395 $pid = $gpg->encrypt('handles' => $handles);
396 }
397 }
398
399 print $pw_fh $pw;
400 close $pw_fh;
401
402 my @errors = <$error_fh>;
403
404 if ($verbose) {
405 print for @errors;
406 } else {
407 for (@errors) {
408 print if /bad\s+pass/;
409 }
410 }
411
412 close $input_fh;
413 close $output_fh;
414 close $error_fh;
415 close $status_fh;
416
417 waitpid $pid, 0;
418
419 if (-s $out_file == 0) {
420 &delete_file($out_file);
421 &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE;
422 if ($use_gpg_agent) {
423 die "[*] Created zero-size file: $out_file\n",
424 " Maybe gpg-agent does not yet have the password for that key?\n",
425 " Try re-running with -v.";
426 } else {
427 die "[*] Created zero-size file: $out_file\n",
428 " Bad password? Try re-running with -v.";
429 }
430 }
431
432 return;
433 }
434
435 sub decrypt_file() {
436 my ($in_file, $out_file, $del_flag) = @_;
437
438 my $gpg = GnuPG::Interface->new();
439 $gpg->options->hash_init(%options);
440
441 die "[*] Could not create new gpg object with ",
442 "homedir: $gpg_homedir" unless $gpg;
443
444 unless ($symmetric_mode or $use_default_key) {
445 $gpg->options->default_key($encrypt_user);
446 $gpg->options->push_recipients($encrypt_user);
447 }
448
449 my ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) =
450 (IO::File->new($in_file),
451 IO::File->new("> $out_file"),
452 IO::Handle->new(),
453 IO::Handle->new(),
454 IO::Handle->new());
455
456 my $handles = GnuPG::Handles->new(
457 stdin => $input_fh,
458 stdout => $output_fh,
459 stderr => $error_fh,
460 passphrase => $pw_fh,
461 status => $status_fh
462 );
463 $handles->options('stdin')->{'direct'} = 1;
464 $handles->options('stdout')->{'direct'} = 1;
465
466 my $pid;
467
468 if ($use_gpg_agent) {
469 $pid = $gpg->decrypt('handles' => $handles,
470 'command_args' => [ qw( --use-agent ) ]);
471 } else {
472 $pid = $gpg->decrypt('handles' => $handles);
473 }
474
475 print $pw_fh $pw;
476 close $pw_fh;
477
478 my @errors = <$error_fh>;
479
480 if ($verbose) {
481 print for @errors;
482 } else {
483 for (@errors) {
484 print if /bad\s+pass/;
485 }
486 }
487
488 close $input_fh;
489 close $output_fh;
490 close $error_fh;
491 close $status_fh;
492
493 waitpid $pid, 0;
494
495 if (-s $out_file == 0) {
496 &delete_file($out_file);
497 &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE;
498 if ($use_gpg_agent) {
499 die "[*] Created zero-size file: $out_file\n",
500 " Maybe gpg-agent does not yet have the password for that key?\n",
501 " Try re-running with -v.";
502 } else {
503 die "[*] Created zero-size file: $out_file\n",
504 " Bad password? Try re-running with -v.";
505 }
506 }
507 return;
508 }
509
510 sub delete_file() {
511 my $file = shift;
512
513 return if $no_delete;
514 return unless -e $file;
515
516 if ($wipe_mode) {
517 my $cmd = $wipe_cmd;
518 if ($wipe_cmdline) {
519 $cmd .= " $wipe_cmdline ";
520 } else {
521 if ($wipe_interactive) {
522 $cmd .= ' -i ';
523 } else {
524 $cmd .= ' -I -s ';
525 }
526 }
527 $cmd .= $file;
528 if ($verbose) {
529 print " Executing: $cmd\n";
530 }
531
532 ### wipe the file
533 system $cmd;
534
535 } else {
536 unlink $file;
537 }
538
539 if (-e $file) {
540 my $msg = "[-] Could not delete file: $file\n";
541 if ($force_mode) {
542 print $msg unless $quiet;
543 } else {
544 die $msg unless $quiet;
545 }
546 }
547 return;
548 }
549
550 sub gpg_operation() {
551
552 ### sort by oldest to youngest mtime
553 FILE: for my $file (sort
554 {$files{$a}{'mtime'} <=> $files{$b}{'mtime'}} keys %files) {
555
556 ### see if we have an exclusion pattern that implies
557 ### we should skip this file
558 if (@exclude_patterns and &exclude_file($file)) {
559 print "[+] Skipping excluded file: $file\n"
560 if $verbose and not $quiet;
561 next FILE;
562 }
563
564 ### see if we have an inclusion pattern that implies
565 ### we should process this file
566 if (@include_patterns and not &include_file($file)) {
567 print "[+] Skipping non-included file: $file\n"
568 if $verbose and not $quiet;
569 next FILE;
570 }
571
572 ### dir is always a full path
573 my ($dir, $filename) = ($file =~ m|(.*)/(.*)|);
574
575 unless (chdir($dir)) {
576 print "[-] Could not chdir $dir, skipping.\n" unless $quiet;
577 next FILE;
578 }
579
580 my $mtime = $files{$file}{'mtime'};
581 my $atime = $files{$file}{'atime'};
582
583 if ($encrypt_mode) {
584
585 my $encrypt_filename = "$filename.gpg";
586
587 if ($obfuscate_mode) {
588
589 unless (defined $obfuscate_ctrs{$dir}) {
590
591 ### create a new gpgdir mapping file for obfuscated file
592 ### names, but preserve any previously encrypted file
593 ### name mappings
594 &handle_old_obfuscated_map_file();
595
596 ### make obfuscated file names start at 1 for each
597 ### directory
598 $obfuscate_ctrs{$dir} = 1;
599 }
600
601 $encrypt_filename = 'gpgdir_' . $$ . '_'
602 . $obfuscate_ctrs{$dir} . '.gpg';
603 }
604
605 if ($ascii_armor_mode) {
606 $encrypt_filename = "$filename.asc";
607 }
608
609 if (-e $encrypt_filename and not $overwrite_encrypted) {
610 print "[-] Encrypted file $dir/$encrypt_filename already ",
611 "exists, skipping.\n" unless $quiet;
612 next FILE;
613 }
614
615 if ($interactive_mode) {
616 next FILE unless (&query_yes_no(
617 " Encrypt: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT));
618 }
619
620 print "[+] Encrypting: $file\n" unless $quiet;
621
622 unless ($trial_run) {
623
624 &encrypt_file($filename, $encrypt_filename,
625 $NO_DEL_SOURCE_FILE);
626
627 if (-e $encrypt_filename && -s $encrypt_filename != 0) {
628 ### set the atime and mtime to be the same as the
629 ### original file.
630 unless ($no_fs_times) {
631 if (defined $mtime and $mtime and
632 defined $atime and $atime) {
633 utime $atime, $mtime, $encrypt_filename;
634 }
635 }
636 ### only delete the original file if
637 ### the encrypted one exists
638 if ($wipe_mode and not $quiet) {
639 print " Securely deleting file: $file\n";
640 }
641 &delete_file($filename);
642
643 if ($obfuscate_mode) {
644
645 ### record the original file name mapping
646 &append_obfuscated_mapping($filename,
647 $encrypt_filename);
648
649 $obfuscate_ctrs{$dir}++;
650 }
651
652 $total_encrypted++;
653
654 } else {
655 print "[-] Could not encrypt file: $file\n" unless $quiet;
656 next FILE;
657 }
658 }
659
660 } else {
661
662 ### allow filenames with spaces
663 my $decrypt_filename = '';
664 if ($filename =~ /^(.+)\.gpg$/) {
665 $decrypt_filename = $1;
666 } elsif ($filename =~ /^(.+)\.asc$/) {
667 $decrypt_filename = $1;
668 }
669
670 if ($obfuscate_mode) {
671
672 &import_obfuscated_file_map($dir)
673 unless defined $obfuscated_dirs{$dir};
674
675 if (defined $obfuscated_dirs{$dir}{$filename}) {
676 $decrypt_filename = $obfuscated_dirs{$dir}{$filename};
677 } else {
678 ###
679 print "[-] Obfuscated file map does not exist for $filename in\n",
680 " $obfuscate_map_filename, skipping.\n";
681 next FILE;
682 }
683
684 } else {
685 if (not $force_mode and $file =~ /gpgdir_\d+_\d+.gpg/) {
686 ### be careful not to decrypt obfuscated file unless we
687 ### are running in -O mode. This ensures that the
688 ### original file names will be acquired from the
689 ### /some/dir/.gpgdir_map_file
690 $have_obfuscated_file = 1;
691 next FILE;
692 }
693 }
694
695 ### length() allows files named "0"
696 next FILE unless length($decrypt_filename) > 0;
697
698 ### don't decrypt a file on top of a normal file of
699 ### the same name
700 if (-e $decrypt_filename and not $overwrite_decrypted) {
701 print "[-] Decrypted file $dir/$decrypt_filename ",
702 "already exists. Skipping.\n" unless $quiet;
703 next FILE;
704 }
705
706 if ($interactive_mode) {
707 next FILE unless (&query_yes_no(
708 " Decrypt: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT));
709 }
710
711 unless ($trial_run) {
712
713 print "[+] Decrypting: $dir/$filename\n" unless $quiet;
714 &decrypt_file($filename, $decrypt_filename,
715 $NO_DEL_SOURCE_FILE);
716
717 if (-e $decrypt_filename && -s $decrypt_filename != 0) {
718 ### set the atime and mtime to be the same as the
719 ### original file.
720 unless ($no_fs_times) {
721 if (defined $mtime and $mtime and
722 defined $atime and $atime) {
723 utime $atime, $mtime, $decrypt_filename;
724 }
725 }
726 if ($wipe_mode and not $quiet) {
727 print " Securely deleting file: $file\n";
728 }
729 ### only delete the original encrypted
730 ### file if the decrypted one exists
731 &delete_file($filename);
732
733 $total_decrypted++;
734
735 } else {
736 print "[-] Could not decrypt file: $file\n" unless $quiet;
737 next FILE;
738 }
739 }
740 }
741 }
742 print "\n" unless $quiet;
743 chdir $initial_dir or die "[*] Could not chdir: $initial_dir\n";
744 return;
745 }
746
747 sub get_files() {
748 my $dir = shift;
749
750 print "[+] Building file list...\n" unless $quiet;
751 if ($norecurse) {
752 opendir D, $dir or die "[*] Could not open $dir: $!";
753 my @files = readdir D;
754 closedir D;
755
756 for my $file (@files) {
757 next if $file eq '.';
758 next if $file eq '..';
759 &check_file_criteria("$dir/$file");
760 }
761 } else {
762 ### get all files in all subdirectories
763 find(\&find_files, $dir);
764 }
765 return;
766 }
767
768 sub exclude_file() {
769 my $file = shift;
770 for my $pat (@exclude_patterns) {
771 if ($file =~ m|$pat|) {
772 print "[+] Skipping $file (matches exclude pattern: $pat)\n"
773 if $verbose and not $quiet;
774 return 1;
775 }
776 }
777 return 0;
778 }
779
780 sub include_file() {
781 my $file = shift;
782 for my $pat (@include_patterns) {
783 if ($file =~ m|$pat|) {
784 print "[+] Including $file (matches include pattern: $pat)\n"
785 if $verbose and not $quiet;
786 return 1;
787 }
788 }
789 return 0;
790 }
791
792 sub obfuscated_mapping_files() {
793 my $dirs_href;
794
795 if ($encrypt_mode) {
796 $dirs_href = \%obfuscate_ctrs;
797 } else {
798 $dirs_href = \%obfuscated_dirs;
799 }
800
801 DIR: for my $dir (keys %$dirs_href) {
802 unless (chdir($dir)) {
803 print "[-] Could not chdir $dir, skipping.\n" unless $quiet;
804 next DIR;
805 }
806
807 if ($encrypt_mode) {
808 next DIR unless -e $obfuscate_map_filename;
809 ### encrypt the map file now that we have encrypted
810 ### the directory
811 print "[+] Encrypting mapping file: ",
812 "$dir/$obfuscate_map_filename\n" unless $quiet;
813 unless ($trial_run) {
814 &encrypt_file($obfuscate_map_filename,
815 "$obfuscate_map_filename.gpg", $NO_DEL_SOURCE_FILE);
816
817 unlink $obfuscate_map_filename;
818 }
819 } else {
820 next DIR unless -e "$obfuscate_map_filename.gpg";
821 ### delete the map file since we have decrypted
822 ### the directory
823 print "[+] Decrypting mapping file: ",
824 "$dir/$obfuscate_map_filename.gpg\n" unless $quiet;
825 unless ($trial_run) {
826 &decrypt_file("$obfuscate_map_filename.gpg",
827 $obfuscate_map_filename, $NO_DEL_SOURCE_FILE);
828
829 unlink "$obfuscate_map_filename.gpg";
830 }
831 }
832 }
833 return;
834 }
835
836 sub handle_old_obfuscated_map_file() {
837 return unless -e "$obfuscate_map_filename.gpg";
838
839 &decrypt_file("$obfuscate_map_filename.gpg",
840 $obfuscate_map_filename, $NO_DEL_SOURCE_FILE);
841
842 unlink "$obfuscate_map_filename.gpg";
843
844 my @existing_obfuscated_files = ();
845
846 open F, "< $obfuscate_map_filename" or die "[*] Could not open ",
847 "$obfuscate_map_filename: $!";
848 while (<F>) {
849 if (/^\s*.*\s+(gpgdir_\d+_\d+.gpg)/) {
850 if (-e $1) {
851 push @existing_obfuscated_files, $_;
852 }
853 }
854 }
855 close F;
856
857 if (@existing_obfuscated_files) {
858 ### there are some obfuscated files from a previous gpgdir
859 ### execution
860 open G, "> $obfuscate_map_filename" or die "[*] Could not open ",
861 "$obfuscate_map_filename: $!";
862 print G for @existing_obfuscated_files;
863 close G;
864 }
865 return;
866 }
867
868 sub append_obfuscated_mapping() {
869 my ($filename, $encrypt_filename) = @_;
870
871 open G, ">> $obfuscate_map_filename" or die "[*] Could not open ",
872 "$obfuscate_map_filename: $!";
873 print G "$filename $encrypt_filename\n";
874 close G;
875 return;
876 }
877
878 sub import_obfuscated_file_map() {
879 my $dir = shift;
880
881 $obfuscated_dirs{$dir} = {};
882
883 return unless -e "$obfuscate_map_filename.gpg";
884
885 &decrypt_file("$obfuscate_map_filename.gpg",
886 $obfuscate_map_filename, $NO_DEL_SOURCE_FILE);
887
888 open G, "< $obfuscate_map_filename" or die "[*] Could not open ",
889 "$obfuscate_map_filename: $!";
890 while (<G>) {
891 if (/^\s*(.*)\s+(gpgdir_\d+_\d+.gpg)/) {
892 $obfuscated_dirs{$dir}{$2} = $1;
893 }
894 }
895 close G;
896
897 return;
898 }
899
900 sub get_homedir() {
901 my $uid = $<;
902 my $homedir = '';
903 if (-e '/etc/passwd') {
904 open P, '< /etc/passwd' or
905 die "[*] Could not open /etc/passwd. Exiting.\n";
906 my @lines = <P>;
907 close P;
908 for my $line (@lines) {
909 ### mbr:x:222:222:Michael Rash:/home/mbr:/bin/bash
910 chomp $line;
911 if ($line =~ /^(?:.*:){2}$uid:(?:.*:){2}(\S+):/) {
912 $homedir = $1;
913 last;
914 }
915 }
916 } else {
917 $homedir = $ENV{'HOME'} if defined $ENV{'HOME'};
918 }
919 die "[*] Could not determine home directory. Use the -u <homedir> option."
920 unless $homedir;
921 return $homedir;
922 }
923
924 sub get_key() {
925 if (-e "${homedir}/.gpgdirrc") {
926 open F, "< ${homedir}/.gpgdirrc" or die "[*] Could not open ",
927 "${homedir}/.gpgdirrc. Exiting.\n";
928 my @lines = <F>;
929 close F;
930 my $key = '';
931 for my $line (@lines) {
932 chomp $line;
933 if ($line =~ /^\s*default_key/) {
934 ### prefer to use the default GnuPG key
935 $use_default_key = 1;
936 return '';
937 } elsif ($line =~ /^\s*use_key\s+(.*)$/) {
938 ### GnuPG accepts strings to match the key, so we don't
939 ### have to strictly require a key ID... just a string
940 ### that matches the key
941 return $1;
942 }
943 }
944 die
945 "[*] Please edit ${homedir}/.gpgdirrc to include your gpg key identifier\n",
946 " (e.g. \"D4696445\"; see the output of \"gpg --list-keys\"), or use the\n",
947 " default GnuPG key defined in ~/.gnupg/options";
948 }
949 print "[+] Creating gpgdir rc file: $homedir/.gpgdirrc\n";
950 open F, "> ${homedir}/.gpgdirrc" or die "[*] Could not open " .
951 "${homedir}/.gpgdirrc. Exiting.\n";
952
953 print F <<_CONFIGRC_;
954 # Config file for gpgdir.
955 #
956 # Set the key to use to encrypt files with "use_key <key>", e.g.
957 # "use_key D4696445". See "gpg --list-keys" for a list of keys on your
958 # GnuPG key ring. Alternatively, if you want gpgdir to always use the
959 # default key that is defined by the "default-key" variable in
960 # ~/.gnupg/options, then uncomment the "default_key" line below.
961
962 # Uncomment to use the GnuPG default key defined in ~/.gnupg/options:
963 #default_key
964
965 # If you want to use a specific GnuPG key, Uncomment the next line and
966 # replace "KEYID" with your real key id:
967 #use_key KEYID
968 _CONFIGRC_
969
970 close F;
971 print
972 "[*] Please edit $homedir/.gpgdirrc to include your gpg key identifier,\n",
973 " or use the default GnuPG key defined in ~/.gnupg/options. Exiting.\n";
974 exit 0;
975 }
976
977 sub find_files() {
978 my $file = $File::Find::name;
979 &check_file_criteria($file);
980 return;
981 }
982
983 sub check_file_criteria() {
984 my $file = shift;
985 ### skip all links, zero size files, all hidden
986 ### files (includes .gnupg files), etc.
987 return if -d $file;
988 if (-e $file and not -l $file and -s $file != 0
989 and $file !~ m|/\.|) {
990 if ($encrypt_mode) {
991 if ($file =~ m|\.gpg| or $file =~ m|\.asc|) {
992 print "[-] Skipping encrypted file: $file\n" unless $quiet;
993 return;
994 }
995 } else {
996 unless ($file =~ m|\.gpg| or $file =~ m|\.asc|) {
997 print "[-] Skipping unencrypted file: $file\n" unless $quiet;
998 return;
999 }
1000 }
1001 my ($atime, $mtime) = (stat($file))[8,9];
1002 $files{$file}{'atime'} = $atime;
1003 $files{$file}{'mtime'} = $mtime;
1004 } else {
1005 print "[-] Skipping file: $file\n"
1006 if $verbose and not $quiet;
1007 }
1008 return;
1009 }
1010
1011 sub get_password() {
1012
1013 ### this is only useful if the gpg key literally has no password
1014 ### (usually this is not the case, but gpgdir will support it if
1015 ### so).
1016 return if $cmdline_no_password;
1017
1018 ### if we are using gpg-agent for passwords, then return
1019 return if $use_gpg_agent;
1020
1021 if ($pw_file) {
1022 open PW, "< $pw_file" or die "[*] Could not open $pw_file: $!";
1023 $pw = <PW>;
1024 close PW;
1025 chomp $pw;
1026 } else {
1027 print "[+] Executing: gpgdir @args_cp\n" unless $quiet;
1028 if ($symmetric_mode) {
1029 print " [Symmetric mode]\n" unless $quiet;
1030 } else {
1031 if ($use_default_key) {
1032 print " Using default GnuPG key.\n" unless $quiet;
1033 } else {
1034 print " Using GnuPG key: $encrypt_user\n" unless $quiet;
1035 }
1036 }
1037 if ($test_and_exit) {
1038 print " *** test_mode() ***\n" unless $quiet;
1039 }
1040 if ($encrypt_mode) {
1041 print ' Enter password (for initial ' .
1042 "encrypt/decrypt test)\n" unless $quiet;
1043 }
1044 my $msg = 'Password: ';
1045 ### get the password without echoing the chars back to the screen
1046 ReadMode 'noecho';
1047 while (! $pw) {
1048 print $msg;
1049 $pw = ReadLine 0;
1050 chomp $pw;
1051 }
1052 ReadMode 'normal';
1053 if ($quiet) {
1054 print "\n";
1055 } else {
1056 print "\n\n";
1057 }
1058 }
1059 return;
1060 }
1061
1062 sub test_mode() {
1063 chdir $dir or die "[*] Could not chdir($dir): $!";
1064
1065 my $test_file = "gpgdir_test.$$";
1066 print "[+] test_mode(): Encrypt/Decrypt test of $test_file\n"
1067 if (($test_and_exit or $verbose) and not $quiet);
1068
1069 if (-e $test_file) {
1070 &delete_file($test_file) or
1071 die "[*] test_mode(): Could not remove $test_file: $!";
1072 }
1073 if (-e "$test_file.gpg") {
1074 &delete_file("$test_file.gpg") or
1075 die "[*] test_mode(): Could not remove $test_file.gpg: $!";
1076 }
1077
1078 open G, "> $test_file" or
1079 die "[*] test_mode(): Could not create $test_file: $!";
1080 print G "gpgdir test\n";
1081 close G;
1082
1083 if (-e $test_file) {
1084 print "[+] test_mode(): Created $test_file\n"
1085 if (($test_and_exit or $verbose) and not $quiet);
1086 } else {
1087 die "[*] test_mode(): Could not create $test_file\n";
1088 }
1089
1090 &encrypt_file($test_file, "${test_file}.gpg", $DEL_SOURCE_FILE);
1091
1092 if (-e "$test_file.gpg" and (-s $test_file != 0)) {
1093 print "[+] test_mode(): Successful encrypt of $test_file\n"
1094 if (($test_and_exit or $verbose) and not $quiet);
1095 &delete_file($test_file) if -e $test_file;
1096 } else {
1097 die "[*] test_mode(): not encrypt $test_file (try adding -v).\n";
1098 }
1099
1100 &decrypt_file("${test_file}.gpg", $test_file, $DEL_SOURCE_FILE);
1101
1102 if (-e $test_file and (-s $test_file != 0)) {
1103 print "[+] test_mode(): Successful decrypt of $test_file\n"
1104 if (($test_and_exit or $verbose) and not $quiet);
1105 } else {
1106 die "[*] test_mode(): Could not decrypt $test_file.gpg ",
1107 "(try adding -v).\n";
1108 }
1109 open F, "< $test_file" or
1110 die "[*] test_mode(): Could not open $test_file: $!";
1111 my $line = <F>;
1112 close F;
1113
1114 if (defined $line and $line =~ /\S/) {
1115 chomp $line;
1116 if ($line eq 'gpgdir test') {
1117 print "[+] test_mode(): Decrypted content matches original.\n",
1118 "[+] test_mode(): Success!\n\n"
1119 if (($test_and_exit or $verbose) and not $quiet);
1120 } else {
1121 die "[*] test_mode(): Decrypted content does not match ",
1122 "original (try adding -v).";
1123 }
1124 } else {
1125 die "[*] test_mode(): Fail (try adding -v).\n";
1126 }
1127 &delete_file($test_file) if -e $test_file;
1128 &delete_file("$test_file.gpg") if -e "$test_file.gpg";
1129
1130 chdir $initial_dir or die "[*] Could not chdir($initial_dir)";
1131
1132 return 1;
1133 }
1134
1135 sub query_yes_no() {
1136 my ($msg, $style) = @_;
1137 my $ans = '';
1138 while ($ans ne 'y' and $ans ne 'n') {
1139 print $msg;
1140 $ans = lc(<STDIN>);
1141 if ($style == $ACCEPT_YES_DEFAULT) {
1142 return 1 if $ans eq "\n";
1143 } elsif ($style == $ACCEPT_NO_DEFAULT) {
1144 return 0 if $ans eq "\n";
1145 }
1146 chomp $ans;
1147 }
1148 return 1 if $ans eq 'y';
1149 return 0;
1150 }
1151
1152 sub usage_and_exit() {
1153 print <<_HELP_;
1154
1155 gpgdir; Recursive direction encryption and decryption with GnuPG
1156
1157 [+] Version: $version (file revision: $rev_num)
1158 By Michael Rash (mbr\@cipherdyne.org)
1159 URL: http://www.cipherdyne.org/gpgdir/
1160
1161 Usage: gpgdir -e|-d <directory> [options]
1162
1163 Options:
1164 -e, --encrypt <directory> - Encrypt <directory> and all of its
1165 subdirectories.
1166 -d, --decrypt <directory> - Decrypt <directory> and all of its
1167 subdirectories.
1168 -a, --agent - Acquire password information from a
1169 running instance of gpg-agent.
1170 -A, --Agent-info <info> - Specify the value for the GPG_AGENT_INFO
1171 environment variable as returned by
1172 'gpg-agent --daemon'.
1173 -g, --gnupg-dir <dir> - Specify a path to a .gnupg directory for
1174 gpg keys (the default is ~/.gnupg if this
1175 option is not used).
1176 -p, --pw-file <file> - Read password in from <file>.
1177 -s, --skip-test - Skip encrypt -> decrypt test.
1178 -t, --test-mode - Run encrypt -> decrypt test and exit.
1179 -T, --Trial-run - Show what filesystem actions would take
1180 place without actually doing them.
1181 -P, --Plain-ascii - Ascii armor mode (creates non-binary
1182 encrypted files).
1183 --Interactive - Query the user before encrypting,
1184 decrypting, or deleting any files.
1185 --Exclude <pattern> - Skip all filenames that match <pattern>.
1186 --Exclude-from <file> - Skip all filenames that match any pattern
1187 contained within <file>.
1188 --Include <pattern> - Include only those filenames that match
1189 <pattern>.
1190 --Include-from <file> - Include only those filenames that match a
1191 pattern contained within <file>.
1192 -K, --Key-id <id> - Specify GnuPG key ID, or key-matching
1193 string. This overrides the use_key value
1194 in ~/.gpgdirrc
1195 -D, --Default-key - Use the key that GnuPG defines as the
1196 default (i.e. the key that is specified
1197 by the default-key option in
1198 ~/.gnupg/options).
1199 -O, --Obfuscate-filenames - Substitute all real filenames in a
1200 directory with manufactured ones (the
1201 original filenames are preserved in a
1202 mapping file and restored when the
1203 directory is decrypted).
1204 --obfuscate-map_file <file> - Specify path to obfuscated mapping file
1205 (in -O mode).
1206 -F, --Force - Continue to run even if files cannot be
1207 deleted (because of permissions problems
1208 for example).
1209 --overwrite-encrypted - Overwrite encrypted files even if a
1210 previous <file>.gpg file already exists.
1211 --overwrite-decrypted - Overwrite decrypted files even if the
1212 previous unencrypted file already exists.
1213 -q, --quiet - Print as little to the screen as possible
1214 -W, --Wipe - Use the 'wipe' command to securely delete
1215 unencrypted copies of files after they
1216 have been encrypted.
1217 --wipe-path <path> - Specify path to the wipe command.
1218 --wipe-interactive - Force interactive mode with the wipe
1219 command.
1220 --wipe-cmdline <args> - Manually specify command line arguments
1221 to the wipe command.
1222 --no-recurse - Don't recursively encrypt/decrypt
1223 subdirectories.
1224 --no-delete - Don't delete original unencrypted files.
1225 --no-preserve-times - Don't preserve original mtime and atime
1226 values on encrypted/decrypted files.
1227 --no-password - Assume the gpg key has no password at all
1228 (this is not common).
1229 -u, --user-homedir <dir> - Path to home directory.
1230 -v, --verbose - Run in verbose mode.
1231 -V, --Version - print version.
1232 -h, --help - print help.
1233 _HELP_
1234 exit 0;
1235 }