#!/usr/bin/perl # # ks [option] name [[from] to] # ks -c name from [to] # ks -x # # Key Storage Control for various Encryption methods. User passwords are used # to decrypt a cryptographic key files that not only stores the master key # but all the other details needed to 'unlock' or 'decrypt' some data store. # # Mulitple 'key store' directories can be used, and are typically stored in # a "ks" sub-directory of some mounted USB device, though it does nto have to # be. The given 'name' is hashed so as to look like a 'encfs' datafile, and # determines the specific 'key file' which is then decrypted to extract the # details needed. # # The encrypted keyfile contains information such as # * Highly Random Binary Master Password (to decrypt actual data) # * The command to run to decrypt or display the data store # * Location of the Data Store (optional) # * Where to mount the decrypted data (optional) # * Any extra configuration or data needed (such as a EncFS config file) # # The command in the 'key file' is run with optional locations placed in the # command arguments with password being feed to command via standard input, # and optionally a file pipe to feed the extra data, given as the environment # variables 'DATA' and 'ENCFS_CONFIG'. This is perfect to use with the # "encfs" command but should be usable by just about any other encryption # method. # # Options... # Normal usage # -x Use X Windows to collect arguments and passwords (no tty) # -u umount (or specify a directory name containing a '/') # Initialization # -c create a EncFS mounting key file for given 'name', 'from' args # -F Mount even if "data store" or the "data mount" is empty # Administration # -p change the users password (used to decrypt the 'key file; # -r rename the key_file to a new 'name', with same password # -m change stored 'from' and 'to' mount paths in key file # -d delete this key file (DANGER DANGER DANGER) # Low Level Administration # -l look at raw command/from/to settings found in 'key file' # -e edit the 'key file' (requires extra setup for editor) # -f output the real filename of 'key file' in any 'key store' # -kf output the full pathe to the 'key file' that will be used # -ks use this specific 'key store' rather than searching for one # # For example to create a new EncFS data store # Create a key file... # ks -c name /encrypted/data/store/directory # Now we can mount the empty data store for the first time. The -F flag is # needed to turn off the "Data Store is empty" check. For empty data stores # you can check that it actually mounted using "df". # ks -F name # df # Create or Copy at least one file into mounted decrypted data store, so # "ks" will know it has successfully mounted, and that encfs is decrypting # at least one file. # vi name/README # # You can now unmount. # ks name/ # You can either unmount using the -u option, or add a '/' on end of key # name, which lets you unmount using the -x (X windows only) option. # # From this point you can mount and unmount that EncFS data store using... # ks -x # # It is recommended that you add the command "ks -x" to a window manager # menu, hotkey, or some other GUI launcher. The option will pop up windows to # securely ask for the key name and password and if used with EncFS, should # cause the decrypted data to mount as a sub-directory of your home, (the # current directory of the window manager). # # The -x option will avoid the command and thus the 'key file' names from # being recorded in the 'shell history log' making its usage harder to track # by others. # # # Example, just a data file # Create and/or edit a encrypted key file # ks -e my_secrets # # This requires vim to be setup to decrypt a file with a ".enc" suffix # using a program called "encrypt" which uses the same encryption method # "ks" is using. (see script comments) # ### # # Software Requirements (minimal)... # # This program requires two perl modules for encryption of the 'key files' # "perl-Crypt-CBC" "perl-Crypt-OpenSSL-AES" # # Other than that everything else is optional, however... # # Linux commands "zenity", and "Xdialog", "notify-send" provides X window # dialog windows, fo rthe "ks -x" option user interaction. This avoids # command line shells and its history logging. This very important for # a encryption program, and a recommended addition. # # Optionally, the external perl script "encrypt" can be used to decrypt and # edit the keyfiles directly. # http://www.ict.griffith.edu.au/~anthony/software/#encrypt # # This uses the exact same file encryption used by this script for 'key files' # but designed with 'pipelining' of the data stream (much like "aespipe"). # This program can be merged with a text editor (like "vim") to allow editing # of encrypted files, including 'key files' and more specifically the 'DATA' # section within. # # See that command for details on how to configure your ".vimrc" so that # the "ks -e" can be used to edit 'key files' directly. # # You may also like to set the PAGER and XPAGER environment variables to point # to appropriate 'paging' programs for terminal and X window usage # respectfully. This allows secure handling of data output (as stored in a key # file) so as to display it as needed. Such data is useful for storing a small # amounts of data such as passwords (for web sites or accounts), or bank # account details, without requiring some type of file system mount. # # And finally I recommend you arrange to automatically unmount encrypted file # systems (passwords should not be needed for this) when you logout, as a # precaution against accidentally leave them logged in when you are not # around. # # # Design notes... # # This program uses principles found in a Paper by Clemens Fruhwirth, "TKS1, # An anti-forensic, two level, and iterated key setup scheme." # # http://clemens.endorphin.org/TKS1-draft.pdf # # The main features being that the users generally low entropy pass-phrase # only decrypts the high entory (purely random) master key (and any other data # needed for decryption purposes) using the iterative PBKDF2 (RFC 2898) to # make brute force attacks slow and thus unfeasible. It also provides the # ability for users to change their passwords without needing to re-encrypt # the data store. Only a re-encryption of the key file. This even allows # you to set up multiple pass-phrases to decrypt the same data store, by # duplicating and encrypting key files using different names and password # pairs. This is particularly useful for multi-user access to encrypted data. # # This scheme is simular to but expanded apon the method implemented in the # Linux "LUKS" dm-crypt Disk Encryption scheme (see linux "cryptsetup' # command), but without the same pass-phrase being applied to multiple 'key # slots', and not limited to a single specific encrypted data store. # # This scheme also allows you to completely seperate the 'key files' from the # actual encrypted data. This means the encrypted 'keys' needed to unlock # data can be saved to a completely different device such as a small USB key, # allowing you to add physical security to the encrypted data. The 'key' also # only needs to be available when the encryption is setup, and can be removed # once unlocked. If power is removed the data should automatically be # 'locked' providing instant security. # # As the command needed is also stored with the master key, it means that the # 'key file' could be used for just about any encryption scheme, from disk # partitions (dm_crypt), filesystem directory (encfs), or a just a file of # information (configuration details). # # That is all the information needed to decrypt a 'data store' regardless of # the actual encryption method used, can be saved in the 'key file' as # a separable entity. That includes all the supposedly 'public' information, # such as salts, iteration counts, etc. # # The normal format of the decrypted keyfile is of the form # COMMAND={the command to feed the password to - see below} # PASSWORD={The master password, ascii characters up to a newline} # FROM={optional: absolute path to encrypted data (first %s in command)} # TO={optional: where to mount data (second %s in command)} # __DATA__ # {Any other optional data also encrypted in the key file. # This could be text information, binary data, or a configuration # file needed for the command (such as an encfs config file)} # # The FROM and TO options could be overrided from the command line. # (See EncFS usage below). # # The 'command' being executted doed not have to actually decrypt any 'data # store', but could just simply output the master password, or the 'extra # data' that is stored in the 'key file', for other purposes. For example # I often use a 'key file' to hold 'private information' such as internet # passwords, and banking details. # # Example commands to use in 'key files'... # # * Just output the Random Base64 Master Password (no decryption command), # so you can pipe that master password into something else. # COMMAND=cat - # # That is the key file is only used to hold a master password so you can # feed it into something else. For example use ks output to initialise # the Gnome KeyRing Daemon... # ks -x gnome_key | /usr/libexec/pam-keyring-tool -u -s --keyring=default # # * You can also initialise the Gnome Key Ring directly... # COMMAND=/usr/libexec/pam-keyring-tool -u -s --keyring=default # # * Output the DATA section of the encrypted 'key file' (password ignored) # COMMAND=cat $DATA # # * Or show the data in a 'pager' program (like 'less' or 'xless'). # COMMAND=$PAGER $DATA # # The pager to use is specified using $PAGER environment variable (default # to less), or if a "ks -x" command is used, PAGER is replaced by the # contents of $XPAGER environment variable, (default to 'xterm' running # $PAGER) # # This is a good way to have some encrypted data 'popup' when reqested # on your X window display, but not have it recorded unencrypted on disk. # # * Mount a EncFS encrypted directory (%s passes the 'from' and 'to' args) # COMMAND=encfs --stdinpass --anykey %s %s # # This is the more normal setup and what is automatically set when you # use the -c option (as described above). The -c option will also # generates a completely random 'master passowrd' but also create and # save a configuration file for EncFS, which is then stored in the DATA # area of the 'key file'. All that is then nessary is to specify the # source directory and optionally the destination directory # # The Encfs command requires a FROM path, which is generally set within # the 'key file'. The TO (mount) path is optional and may be an absolute # path, or relative to the current directory. If not provided the 'key # name' is used. A sub-directory is created, and the FROM path is # encfs mounted at that point. # # If the input 'key name' ends in a '/' or a '-u' option is given, the # endfs file system attached to the specified directory will be umounted # and the empty directory removed. # # * Or run the data section as a shell script # COMMAND=/bin/sh $DATA # # This allows you to create a very complex sequence of secret operations # such as re-organise your mounts, setup remote file system stores, or # decrypt multiple sets of encrypted data stores. # # Basically any command can be run for a given 'name' and 'password' pair, # including larger scripts, that can perform any action, while keeping the # command used an encrypted secret. # # A command can be designed to use any encrypted data method, or even remove # or shread something, to make an encrypted 'data store' useless if a wrong # password, or key store was used. # # Only those that know the 'name' and 'password' pair can execute, look-at or # modify that command, whcih could even run a script stored in the encrypted # key file. That is encrypted commands is also posible. # # Basically the key store scheme is specifically designed to make # differentiating between chaff, fake keys, fake data stores, and the real # encrypted data practially imposible. Especially in situations where rubber # hose attacks are a posibility. # # Chaff Keys and Data Stores containing fake/random data that is encrypted # with a forgotten random pad should always be added. Better still the files # such data should be 'touched' on occasion, by some means to keep dates # randomized. # # See Also # "TKS1, An anti-forensic, two level, and iterated key setup scheme." # http://clemens.endorphin.org/TKS1-draft.pdf # "Dual Password Encryption with EncFS" # http://magazine.redhat.com/2007/06/13/ # # Anthony Thyssen 18 November 2009 A.Thyssen@griffith.edu.au # use strict; use FindBin; my $PROGNAME = $FindBin::Script; use File::Spec; use File::Temp qw(tempfile tempdir); use IPC::Open2; use Crypt::CBC; use Digest::SHA1 qw(sha1_base64); # used to implement a PBKDF2 use Digest::HMAC_SHA1 qw(hmac_sha1); # iterative hashing function. # Selection of the cryptography method to use. my $CryptModule = "Crypt::OpenSSL::AES"; # Advanced Encryption Standard #my $CryptModule = "Crypt::Rijndael"; # Original 'AES' method name #my $CryptModule = "Crypt::Blowfish"; # Blowfish 'XOR' Encryption #my $CryptModule = "Crypt::DES"; # DES encryption #my $CryptModule = "Crypt::IDEA"; # The 'IDEA' encryption # Magic Prefix - if you really must identify encrypted files internally #my $magic = ''; #my $magic = "KeepOut_"; my $magic = "PBKDF2__"; # make compatible to my personal "encrypt" program # ---------------------------- # Error Handling my $xw = 0; # Use X windows my $rt = 0; # Retry on failure my @retry_args = @ARGV; # Repeat with these args of retry # Optional: Subroutine for X window prompting sub Set_Focus { my ( $title, $jiggle ) = @_; $ENV{LANG}="C"; # Prevent "Warning: Unable to load any usable fontset" my $jiggle = $jiggle ? 'jiggle_window -id $id' : ''; system("sh -c 'if id=`xwin_find 10 \"$title\"`; then xwit -focus -id \$id $jiggle xwit -focus -id \$id fi' &"); } # Shell Script Usage Method ( report and exit ) sub Usage { if ( $xw ) { Set_Focus('KS Failure', 0); # use "Xdialog", as it provides more control than "zenity" # and less configuation than "xmessage" system("Xdialog --title 'KS Failure' ". "--ok-label 'Quit' --msgbox '@_' 0x0 &" ); #system("xmessage -name KS -title 'KS Failure' -center ". # "-buttons 'Cancel:0' -default 'Cancel' ". # "-xrm '*message.borderWidth: 0' ". # "-xrm '*message.scrollVertical: Never' ". # "@_ &"); #system("zenity --title 'KS Failure' ". # "--error --text='@_' &" ); exit 10; } print STDERR @_, "\n"; @ARGV = ( "$FindBin::Bin/$PROGNAME" ); while( <> ) { next if 1 .. 2; last if /^###/; s/^#$//; s/^# //; print STDERR "Usage: " if 3 .. 3; print STDERR; } exit 10; } # Report Failure (under X ask to 'Try Again') sub Fail { if ( $xw ) { Set_Focus('KS Failure', 0); # use Xdialog, as it provides better control than zenity system("Xdialog --title 'KS Failure' ". "--ok-label 'Try Again' --yesno '@_' 0x0" ); #system("zenity --title 'KS Failure' ". # "--question --text='@_'" ); #system("xmessage -name KS -title 'KS failure' -center ". # "-buttons 'Cancel:0,Try Again:10' -default 'Try Again' ". # "-xrm '*message.borderWidth: 0' ". # "-xrm '*message.scrollVertical: Never' ". # "@_"); my $result=$?; # If user selected the default 'try Again' restart program if ( $result == 0 ) { # default - try again selected exec("$0", @retry_args) } exit 10; # Precaution } die ( "@_\n" ); # Just die! } # Notify user with temporary unintrusive message sub Notify { if ( $xw ) { #system("notify-send", "@_"); # auto-backgrounds (from libnotify package) system("notify_message", "@_"); # alturnative message notifier script } } # ---------------------------- # Argument handling my ( $um, $fm, $fn, $kf, $kc, $kl, $ke, $kr, $kd, $km, $kp ) = map 0, 0..20; my ( $ks ) = map undef, 0..10; OPTION: # Multi-switch option handling while( @ARGV && $ARGV[0] =~ s/^-(?=.)// ) { $_ = shift; { m/^$/ && do { next }; # Next option m/^-$/ && do { last }; # End of options '--' m/^\?/ && do { Usage }; # Usage Help '-?' s/^ks// && do { $ks = $_ || shift; next }; # set keystore to use s/^x// && do { $xw++; next }; # collect password via X windows s/^F// && do { $fm++; next }; # force mount (if datastore empty) s/^u// && do { $um++; next }; # unmount this encryption s/^c// && do { $kc++; next }; # create keyfile s/^p// && do { $kp++; next }; # change password of keyfile s/^r// && do { $kr++; next }; # rename the keyfile name s/^d// && do { $kd++; next }; # delete the keyfile s/^f// && do { $fn++; next }; # filename of keyfile s/^kf// && do { $kf++; next }; # actual keyfile found s/^l// && do { $kl++; next }; # list keyfile settings s/^e// && do { $ke++; next }; # edit keyfile and data part s/^m// && do { $km++; next }; # change mount from/to paths Usage( "$PROGNAME: Unknown Option \"-$_\"" ); } continue { next OPTION }; last OPTION; } # No more arguments and X windows falgs - Get Key Name # This can be used for mounting and unmounting, # but not for options requiring two or more arguments if ( @ARGV == 0 && $xw ) { # read the key name (including spaces in name) Set_Focus('KS', 1); $ARGV[0] = `zenity --title=KS --entry --text='Key Name:'`; #`Xdialog --title KS --stdout --inputbox 'Key Name:' 0x0`; chomp($ARGV[0]); exit 1 if $?; } Usage( "$PROGNAME: Options mutually exclusive" ) if $um+$fn+$kf+$kc+$kl+$ke+$kp+$kr+$kd+$km > 1; Usage( "$PROGNAME: Too Few Arguments" ) if @ARGV < 1 # min 1 arg || @ARGV < 2 && $km+$kr; # need 2 or more args Usage( "$PROGNAME: Too Many Arguments" ) if @ARGV > 1 && $um+$fn+$kf+$kl+$ke+$kp+$kd # one arg only || @ARGV > 2 && $kr # two ars only || @ARGV > 3; # max 3 args my ($nm, $mf, $mt); $nm = shift; $mf = shift if @ARGV && ( @ARGV > 1 || $kc ); $mt = shift if @ARGV; Usage( "$PROGNAME: FROM argument must be an absolute path" ) if $mf && $mf !~ /^\//; # Temporary directoyr, used for EncFS config creation only my $tmpdir = File::Spec->tmpdir(); # ---------------------------- # Other Subroutines # Are we on a TTY, if so get stty args chomp( my $stty_reset = qx(stty -g 2>/dev/null) ); sub prompt_read { print @_ if length $stty_reset; # TTY? chomp( my $i = ); return $i; } sub passwd_read { if ( $xw ) { Set_Focus('KS', 0); chomp( my $p = `zenity --title=KS --entry --text="@_" --hide-text` ); #`Xdialog --title KS --stdout --password --inputbox "@_" 0x0` ); Fail("Zero length password not allowed!") unless length $p; return $p; } if ( length $stty_reset ) { # TTY input? system('stty', '-echo'); # turn off echo print @_; } chomp( my $p = ); if ( length $stty_reset ) { system('stty', $stty_reset); print "\n"; } Fail("Zero length password not allowed!") unless length $p; return $p; } sub generate_random_key { chomp( my $k=`openssl rand 45 -base64` ); # base64 length = 60 bytes return $k; } sub fname { # convert binary to EncFS look-a-like filename my $fn = substr(sha1_base64(shift),2,24); $fn =~ tr%+/%,-%; return $fn; } sub umount { system("fusermount -u '$_[0]' 2>/dev/null"); } sub generate_random_cf { # EncFS 1.6 has a --standard option for easier FS creation, and avoiding # the need to pipe input during creation. But you still need tmp directories! # Left like this for greater backward compatibility. my $d1 = tempdir( "ks-XXXXXX", DIR => $tmpdir, CLEANUP => 1 ); my $d2 = tempdir( "ks-XXXXXX", DIR => $tmpdir, CLEANUP => 1 ); my $k = generate_random_key; # it is not used! #print "DEBUG: encfs --stdinpass '$d1' '$d2' >/dev/null\n"; open(E, '|-') or exec "encfs --stdinpass '$d1' '$d2' >/dev/null"; print E "\n"; # request default parameters print E "$k\n"; # now give it the password close E; umount($d2); rmdir($d2); local $/ = undef; open(C, "$d1/.encfs6.xml"); my $cf = ; close C; unlink("$d1/.encfs6.xml"); rmdir($d1); Fail("Volume key build failure") unless length $cf > 1024; # This speeds up actual encfs mounts (as we do the password check). # But it also completely destroys encfs password check for this config. # However we don't use that password anyway, only the other config settings, # so it does not matter in any case. The generated config is only for use # in key files, so no 'cracker' would see this slight change, only EncFS. $cf =~ s/\d+1 from the # Palm::Keyring perl module. Found on the PerlMonks Forum # http://www.perlmonks.org/?node_id=631963 # # Usage pbkdf2(password, salt, iter, keylen, prf) sub pbkdf2 { my ($password, $salt, $iter, $keylen, $prf) = @_; my ($k, $t, $u, $ui, $i); $t = ""; for ($k = 1; length($t) < $keylen; $k++) { $u = $ui = &$prf($salt.pack('N', $k), $password); for ($i = 1; $i < $iter; $i++) { $ui = &$prf($ui, $password); $u ^= $ui; } $t .= $u; } return substr($t, 0, $keylen); } # Wrapper around the above specifing the "hmac_sha1" hashing function to use. # # Alternativally you can use an external "pbkdf2" to access the # OpenSSL libraries PKCS5_PBKDF2_HMAC_SHA1() function. # Source for program download from # http://www.ict.griffith.edu.au/~anthony/software/#pbkdf2 # # Usage pbkdf2_sha1( password, salt, iter ) sub pbkdf2_sha1 { #print STDERR "DEBUG: pbkdf2 ", unpack("H*", $_[1]), " $_[2]\n"; my $k; if( 1 ) { # Use the above function, specify final key length and 'sha1' hashing $k = pbkdf2(@_, 32+16, \&hmac_sha1); } else { # Use the command line (break out of OpenSSL function) local( *O, *I ); my $pid = open2( \*O, \*I, 'pbkdf2', unpack("H*", $_[1]), $_[2] ); print I $_[0]; close I; chomp( $k = ); close 0; waitpid($pid, 0); Fail( "Program 'pbkdf2' returns bad status : ", ($? >> 8), "\n" ) if $?; $k = pack("H*", $k); } #print STDERR unpack("H*",$k), "\n"; return( wantarray ? (substr($k,0,32), substr($k,32,16) ) : $k ); } sub encrypt { # $encrypted_data = encrypt($data, $password); # Encrypt data exactly as per the perl "encrypt" script. # This could call the external "encrypt" program instead. my $salt=`openssl rand 16`; # Create random salt (16 bits) my $ic = int(rand 30000) + 30000; # Create a Large Iterative Count my ( $k, $iv ) = pbkdf2_sha1($_[1], $salt, $ic); return( $magic . # file magic - if you really need it $salt . # salt as 16 byte raw binary string pack("N", $ic) . # count as 4 byte network number Crypt::CBC->new( -cipher => $CryptModule, -header => 'none', -literal_key => 1, -key => $k, -iv => $iv,) ->encrypt($_[0]) ); } sub decrypt { # $data = decrypt($encrypted_data, $password); # Decrypt data that was encrypted using the previous function # This could call the external "encrypt" program instead. my $lm = length($magic); if ( $lm > 0 ) { my $fmagic = substr($_[0], 0, $lm); return( '' ) if $lm > 0 && $fmagic ne $magic; # file magic failure } my $salt = substr($_[0], $lm+0, 16); # salt as binary my $ic = unpack("N", substr($_[0], $lm+16, 4) ); # count as a number my ( $k, $iv ) = pbkdf2_sha1($_[1], $salt, $ic); return( Crypt::CBC->new( -cipher => $CryptModule, -header => 'none', -literal_key => 1, -key => $k, -iv => $iv,) ->decrypt( substr($_[0], $lm+20) ) ); } # File Encryption testing # my $p = passwd_read "Password: "; # print decrypt(encrypt("The encryption has worked!\n", $p), $p); exit 0; # ---------------------------- # Main Proceedure chomp ( my $cwd = `pwd` ); # # direct encfs directory mount request # if ( $nm =~ /\// && -f "$nm/.encfs6.xml" && defined $mt && ! defined $mf ) { # mkdir($mt); # exec 'encfs', "$nm", "$mt"; # Fail("Direct Exec Failure : $!"); # } if ( $nm =~ /\// ) { # key name is a directory path - try to unmount it Fail("Directory not found, can not unmount") unless -d $nm; umount($nm); rmdir($nm) || Fail("Failed to unmount!"); exit 0; } # locate a key store directory # This may be on a USB stick or mounted file system my $f = fname($nm); if ( $fn ) { print "$f\n"; exit(0); } for ( defined $ks ? $ks : (), map { glob } qw( /media/*/ks /mnt/*/ks ~/misc/ks ), ) { $ks=$_,last if length && -f "$_/$f"; } $ks ||= glob("~/misc/ks"); # fall back if no plugin keystore is found # create a new encfs key if ( $kc ) { my $p; while ( 1 ) { $p = passwd_read "Password: "; my $p2 = passwd_read "Password Again: "; last if $p eq $p2; print STDERR "Password Mismatch -- Try Again!\n"; } Fail("Volume key already present!") if -f "$ks/$f"; my $kf = join("\n", "COMMAND=encfs --stdinpass --anykey %s %s", "PASSWORD=". generate_random_key, "FROM=".($mf||''), "TO=".($mt||''), "__DATA__", generate_random_cf ); open(K, ">$ks/$f") or Fail("Failed to write key file : $!"); print K encrypt($kf, $p); close(K); exit(0); } if ( $kf ) { print "$ks/$f\n"; exit(0); } # This probably should read the password and junk it Fail( "Error decoding volume key, password incorrect!!!!" ) unless ( defined $ks && -f "$ks/$f" ); if ( $kd ) { # Shred the start of the keyfile - encryption handles the rest. # Ready if the password is forgotten, the file is irrecoverable anyway. system('shred', '--verbose', '--size=256', '--force', "$ks/$f"); unlink("$ks/$f") or Fail("Error volume key delete failure: $!"); exit(0); } if ( $kr ) { my $f2 = fname($mt); link("$ks/$f", "$ks/$f2") and unlink("$ks/$f") or Fail("Key store rename failed."); exit(0); } if ( $ke ) { (undef, my $edit) = tempfile( "ks_XXXXXX", DIR => $tmpdir, SUFFIX => ".enc", UNLINK => 1 ); unlink( $edit ); symlink("$ks/$f", $edit) or Fail("Volume Key symlink failed: $!"); system( $ENV{VISUAL}||$ENV{EDITOR}, $edit); unlink($edit); exit(0); } my $p = passwd_read "Password: "; $/ = undef; open(K, "$ks/$f") or Fail("Failed to read key file : $!"); my $kf = decrypt(, $p); close(K); $/="\n"; my ( $k, $cm, $ef, $et ); ( $cm ) = $kf =~ /^COMMAND=(.*).*/m; ( $k ) = $kf =~ /^PASSWORD=(.*).*/m; ( $ef ) = $kf =~ /^FROM=(.*).*/m; ( $et ) = $kf =~ /^TO=(.*)/m; Fail("Error decoding volume key, password incorrect!") unless length($cm) && length($k); if ( $kl ) { print "COMMAND=$cm\n"; print "FROM=$ef\n"; print "TO=$et\n"; exit(0); } if ( $km ) { $kf =~ s/^FROM=.*/FROM=$mf/m if defined $mf; $kf =~ s/^TO=.*/TO=$mt/m; open(K, ">$ks/$f") or Fail("Failed to write key file : $!"); print K encrypt($kf, $p); close(K); exit(0); } $p = "The Users Password Is No Longer Needed!!!"; if ( $kp ) { my $p; while ( 1 ) { $p = passwd_read "New Password: "; my $p2 = passwd_read "Password Again: "; last if $p eq $p2; print STDERR "Password Mismatch -- Try Again!\n"; } open(K, ">$ks/$f") or Fail("Failed to write key file : $!"); print K encrypt($kf, $p); close(K); exit(0); } $ef = $mf||$ef; $et = $mt||$et||$nm; $et = "$cwd/$et" unless $et =~ /^\//; umount($et) if length $et; exit 0 if $um; $cm = sprintf($cm, $ef, $et); #print "Command: $cm\n"; $kf =~ s/^.*?\n__DATA__\n?//s; # remove header, leaving just the data if ( length($ef) ) { Fail('Data Store not found') unless -d $ef || -f $ef; # directory or file Fail('Data Store is empty') if -d $ef && ! length(`ls -A '$ef'`) && !$fm; } # ------------------------------------------------------- # DO IT # Is command "$PAGER"? Setup command, and modification as appropriate if ( $cm =~ /^\$PAGER\b/ ) { $ENV{PAGER} = "cat" unless defined $ENV{PAGER}; #$ENV{PAGER} .= ' -f' if $ENV{PAGER} =~ /^[\/\w]\/less$/; # just "less" if ( $xw ) { if ( defined $ENV{XPAGER} ) { $ENV{PAGER} = $ENV{XPAGER}; # User defined X window pager } else { # stop gap measure, run PAGER in a terminal window $ENV{PAGER} = "xterm -g 80x50 -T pager -e $ENV{PAGER}"; } } } # Initialize data pipline and mount points ( undef, my $data_pipe ) = tempfile("ks_XXXXXX", DIR=>$tmpdir, UNLINK=>1 ); unlink($data_pipe); # we want a more secure named pipe, not a plain file # FUTURE add trap to clean up on error exit if ( $cm =~ /^encfs\s/ ) { # special handling Fail("Source directory not found!") unless -d $ef; mkdir($et) unless -d $et; Fail("Unable to create mount point") unless -d $et; Fail("Mount directory not empty") if length(`ls -A '$et'`); system('mkfifo', '--mode=600', $data_pipe); #system('mknod', $data_pipe, 'p'); $ENV{ENCFS6_CONFIG} = $data_pipe; } elsif ( length($kf) > 1 ) { # copy data to tmp file # Provide an actual filename for a DATA feed (cat, PAGER, XPAGER commands) system('mkfifo', '--mode=600', $data_pipe); #system('mknod', $data_pipe, 'p'); $ENV{DATA} = $data_pipe; } # The order for passing master password and data for "encfs" is very # important. First run command, then send DATA, and finally the password. # Run the command open(E, '|-') or exec $cm or exit(1); # Send DATA if ( length($kf) > 1 ) { # copy data (encfs config) to tmp file open(D, ">$data_pipe" ); print D $kf; close(D); } # send master password print E "$k\n"; close(E); my $stat = $?>>8; # cleanup - use shread, just in case. system( 'shread', '-uz', $data_pipe ); unlink($data_pipe); if ($stat) { Fail("KS \"$nm\" failed") } else { Notify("KS \"$nm\" success"); } exit $stat