soppakirja.pl


#!/usr/bin/perl -w
use strict;
use Fcntl ':mode';
use Getopt::Long;

#
# $Id: soppakirja.pl,v 1.12 2004-12-04 15:59:15+02 qvr Exp qvr $
#
# This will try to keep your passwords etc safe.
# You'll need a working pgp installation and a pgp private key
# with a good password.
#
# Run soppakirja.pl and it will create its home directory and a default config file.
# Edit the config to your liking and run soppakirja.pl again, remember
# to save after editing the file and voila, your passwords and whatnot are safe.
#
# If you don't trust root, don't use this. If you think your server is compromised,
# don't use this. If you don't understand this, dont use this.
# You should not leave this running on a screen when you dont need it, as that
# kinda defeats the whole point of this. While this is running, the only thing
# keeping your passwords safe are directory permissions.
#
# Making backups of the encrypted file (and your keyrings) on to a 
# physically different location is a smart move.
# 
# If an error occurs, the script will do its best not to leave open password
# file hanging around.
# 
# copyright 2004 matti@hiljanen.com
#

# User settings are now read from the config, 
# end-user should not edit anything here

my $workdir = $ENV{HOME} . "/.soppakirja";
my ($force_encrypt, $help, $verbose, $extraconf);
my (@configs, $decrypted, $encrypt_to, @editor, $encrypted, @decrypt, @encrypt, @edit, @ls);

our ($opt_workdir, $opt_decrypted, $opt_encrypt_to);
GetOptions(
      "help|h" => \$help,
      "verbose|v" => \$verbose,
      "config|C=s" => \$extraconf,
      "workdir=s",
      "decrypted=s",
      "encrypt-to=s" => \$opt_encrypt_to,
      "force-encrypt" => \$force_encrypt);

$workdir = $opt_workdir if ($opt_workdir);

print '$Id: soppakirja.pl,v 1.12 2004-12-04 15:59:15+02 qvr Exp qvr $' . "\n";

if ($help) {
   print <<EOF;
Usage: $0 [OPTION]...

Options:
  (no options)            open soppakirja normally
  --force-encrypt         encrypt an open (unencrypted) soppakirja
  --workdir=DIR           the working directory, needs secure permissions
  --encrypt-to=EMAIL      the encryption key to use
  --decrypted=FILE        the unencrypted soppakirja, under workdir
  -C, --config=FILE       config file to read
  -h, --help              this help
EOF
      exit 0;
}

# config parsing
my $configured = 0;
push(@configs, "$extraconf") if $extraconf;
push(@configs, "$workdir/config.pl");
push(@configs, $ENV{HOME} . "/.soppakirja/config.pl");
push(@configs, $ENV{HOME} . "/.soppakirjarc");

foreach my $config (@configs) {
   next if $configured;
   if (-f $config && -r $config && -s $config) {
      eval(`cat $config`);
      die "FATAL configuration error: $@" if $@;
      print "config loaded from $config\n";
      $configured = $config;
   }
}

$decrypted = $opt_decrypted if ($opt_decrypted);
$workdir = $opt_workdir if ($opt_workdir);
$encrypt_to = $opt_encrypt_to if ($opt_encrypt_to);

if (!$configured) {
    print "no config found, creating it for you...\n";
    
    my $file;
    if (-d $workdir && -w $workdir) {
       $file = "$workdir/config.pl";
    } else {
       $file = $ENV{HOME} . "/.soppakirjarc";
    }

    open(CONFIG, "> $file")
       or die "FATAL: couldn't write config: $@!\n";
    print CONFIG <<EOF;
### This is the configuration file for soppakirja, the friendly password manager ###

# \$workdir = \$ENV{HOME} . "/.soppakirja";
\$decrypted = "soppakirja"; # the secret file, this is under \$workdir
\$encrypt_to = ''; # your gpg encrypting key (aka. email address)

# if you change this, make sure the editor doesn't use swap files and such
\@editor = ("vim", "-n");
EOF
    close(CONFIG);
    print "done. please edit $file and then try again\n";
    print "exiting...\n";
    exit 1;
} else { # let's check for config validity
   if ($workdir && $decrypted && $encrypt_to && @editor) {
      if ($decrypted =~ /[^a-z]/i) {
         print "FATAL: bad characters in \$decrypted! Please edit config file $configured.\nExiting...\n"; 
         exit 1;
      }
      $decrypted = "$workdir/$decrypted";
      $encrypted = $decrypted . ".asc";
      @decrypt = ("gpg", "--decrypt-files", "$encrypted");
      @encrypt = ("gpg", "-a", "-v", "-r", "$encrypt_to", ,"-o", "$encrypted", "--encrypt", "$decrypted");
      @edit = (@editor, "$decrypted");
      @ls = ("ls", "-l", "$workdir");
      #print "soppakirja is at $decrypted\nwe will encrypt to $encrypt_to\nand editor is $editor[0]\n";
      #exit 1;
   } else {
      print "config file loaded but not it's not valid, please edit $configured\n";
      print "exiting...\n";
      exit 1;
   }
}

if (!-d $workdir) {
   if (mkdir $workdir, 0700) {
      print "$workdir created\n";
   } else {
      print <<EOF;
could not create $workdir
create it manually first
exiting...
EOF
      exit 1;
   }
}

my $dirmode = (stat($workdir))[2];
if ($dirmode & S_IRWXO || $dirmode & S_IRWXG) {
   print <<EOF;
$workdir is not secure!
chmod 0700 $workdir and try again
exiting...
EOF
   exit 1;
}

if ($force_encrypt) {
   system(@encrypt) == 0
      or die "FATAL: encrypting failed, aborting!";
   unlink($decrypted) == 1
      or die "FATAL: unlink failed: $!!";
   print "Soppakirja is now locked.\n";
   exit 0;
}

if (-e $decrypted) {
   print <<EOF;
soppakirja already open!
close the other running process OR encrypt it with $0 --force-encrypt
exiting...
EOF
   exit 1;
}

sub try {
   my @cmd = @_;
   system(@cmd) == 0 
      or cleanup(@cmd);
   return;
}

sub cleanup {
   my @cmd = @_;
   print STDERR "ERROR: '@cmd' failed! Trying to pick up the pieces...\n";
   if (-e $decrypted) {
      print STDERR "Decrypted file still open! not good...\n";
      system(@encrypt) == 0 
         or die "FATAL: encrypting failed, aborting!";
      unlink($decrypted) == 1
         or die "FATAL: unlink failed: $!!";
   }
   print STDERR "Cleanup done (seemingly successfully). Exiting...\n";
   system(@ls);
   exit 1;
}

if (-e $encrypted) {
   try(@decrypt);
   unlink($encrypted) == 1 
      or cleanup("unlink($encrypted)");
} else {
   print "$encrypted does not exist, creating a new soppakirja\n";
   print "CTRL-C to cancel, ENTER to continue\n"; 
   my $foo; read(STDIN,$foo,1);
}

try(@edit);

try("clear");
try(@encrypt);
unlink($decrypted) == 1
   or cleanup("unlink($decrypted)");

print "Soppakirja is now locked.\n";