Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 513e16bb80
Fetching contributors…

Cannot retrieve contributors at this time

executable file 161 lines (136 sloc) 4.561 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
#!/usr/bin/env perl
#
# notes:
# gpg --passphrase-file only reads the first line so we xor the keyfile with
# 0x0a to remove newlines

use warnings;
use strict;

use POSIX 'setsid';
use POE qw/Wheel::FollowTail/;
use MIME::Base64;

my $killdir = "/etc/killallthehumans";
my $gpgkey = "${killdir}/humans.key";
my $ramfs = "/mnt/ramfs";
my $mntpoint = "/mnt/shadow";
my $shadow = "${mntpoint}/shadow.gpg";
my $shadow_decrypted = "${ramfs}/shadow";
my $shadow_original = "/etc/shadow";
my $file = "/var/log/syslog";
my $pidfile = "/var/run/killallthehumansd.pid";
my $setup = 0;

$SIG{INT} = \&_cleanup;
$SIG{TERM} = \&_cleanup;


sub daemonize {
  chdir '/' or die "Can't chdir to /: $!";
  open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
  open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
  defined(my $pid = fork) or die "Can't fork: $!";

  if ($pid) {
    open(PIDFILE, ">${pidfile}");
    print PIDFILE $pid;
    close(PIDFILE);
    exit 0;
  }

  setsid or die "Can't start a new session: $!";
  open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
}

sub _do_cleanup {
  system("shred -u -n 1 ${shadow_decrypted} &> /dev/null");
  system("umount ${ramfs} 2>/dev/null");
  system("umount ${mntpoint} 2>/dev/null");
}

sub _do_mounts {
  my $dev = shift;

  system("mount -t ramfs -o size=1m ramfs ${ramfs}");
  system("mount -t auto /dev/${dev} ${mntpoint}");
}

sub _cleanup {
  _do_cleanup();
  exit 0;
}

sub _do_setup {
  my $dev = shift;

  # make sure directories exist
  mkdir($ramfs, 0700) unless (-d $ramfs);
  mkdir($mntpoint, 0700) unless (-d $mntpoint);
  mkdir($killdir, 0700) unless (-d $killdir);

  # create keyfile if it does not exist
  # set keyfile to immutable to try preventing modification/removal
  # gpg --passphrase-file only reads the first line so we xor the keyfile
  # with 0x0a to remove newlines
  if (! -f $gpgkey) {
    system("dd if=/dev/urandom bs=64 count=1 of=${gpgkey}.tmp 2>/dev/null");
    my $keybuffer;
    open(TMP, "${gpgkey}.tmp");
    open(KEY, ">${gpgkey}");
    binmode(TMP);
    binmode(KEY);
    read(TMP, $keybuffer, 64, 0);
    print KEY encode_base64($keybuffer);
    close(KEY);
    close(TMP);
    system("shred -u -n 1 ${gpgkey}.tmp");
    system("chattr +i ${gpgkey}");
  }

  # check if shadow file is a symlink, and if not:
  ## set up mounts
  ## encrypt shadow file to $shadow
  ## shred original shadow file
  ## create symlink to $shadow
  if (! -l $shadow_original) {
    _do_mounts($dev);
    system("shred -u -n 1 ${shadow} &> /dev/null");
    system("gpg -c -q --batch --no-use-agent --cipher-algo AES256 " .
           "-o ${shadow} --passphrase-file ${gpgkey} < ${shadow_original}");
    system("shred -u -n 1 ${shadow_decrypted} &> /dev/null");
    system("mv ${shadow_original} ${shadow_original}.killallthehumansd");
    system("ln -s ${shadow_decrypted} ${shadow_original}");
  }

  # switch setup flag to TRUE
  $setup = 1;
}

daemonize();

POE::Session->create(
  inline_states => {
    _start => sub {
      $_[HEAP]->{wheel} = POE::Wheel::FollowTail->new(
        Filename => $file,
        InputEvent => 'got_line',
        ErrorEvent => 'got_error',
      );
    },
    got_line => sub {
      my ($line, $wheel_id) = @_[ARG0, ARG1];
      my $child = $_[HEAP]{children_by_wid}{$wheel_id};
      if ($line =~ /sd[a-z]: (sd[a-z][0-9]+)/) {
        my $dev = $1;
        # print "${line}\n";

        # make sure everything is setup for the first time
        if ($setup == 0) {
            _do_setup($dev);
        }

        # now we need to clean up and unmount old ramfs if it exists
        _do_cleanup();

        # make new ramfs and mount
        # XXX: we run this in _do_setup(), make sure it's okay to run twice in
        # that case, or if there is a better place to run this.
        _do_mounts($dev);

        # once device is mounted do a decrypt and write data out to ramfs
        system("gpg -d -q --batch --no-use-agent --cipher-algo AES256 " .
               "-o ${shadow_decrypted} --passphrase-file ${gpgkey} < " .
               "${shadow}");
      } elsif ($line =~ /USB disconnect/) {
        # print "${line}\n";
        _do_cleanup();
      }
    },
    got_error => sub {
      my ($line, $wheel_id) = @_[ARG0, ARG1];
      my $child = $_[HEAP]{children_by_wid}{$wheel_id};
      warn "${line}\n"
    },
  },
  args => [$file],
);

POE::Kernel->run();

exit 0;
Something went wrong with that request. Please try again.