#!/usr/bin/perl # Simple command-line password manager. # # Copyright 2010 Alessandro Ghedini # -------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): # Alessandro Ghedini wrote this file. As long as you retain this # notice you can do whatever you want with this stuff. If we # meet some day, and you think this stuff is worth it, you can # buy me a beer in return. # -------------------------------------------------------------- # # Portions written by ikegami are public domain. use strict; use warnings; use Crypt::CBC; use constant SIG => "SUCCESS"; die "For info type 'perldoc $0'\n" unless $#ARGV >= 0; my $default_pwd; if ($^O eq 'MSWin32') { $default_pwd = $ENV{USERPROFILE}."\\passwd.txt"; } else { $default_pwd = $ENV{HOME}."/.passwd"; } my $pwdfile = $ENV{PWD_FILE} ne "" ? $ENV{PWD_FILE} : $default_pwd; print("Password: "); system('stty','-echo') if $^O eq 'linux'; chop(my $passphrase = ); system('stty','echo') if $^O eq 'linux'; print "\n"; my $cipher = Crypt::CBC->new( -cipher => 'Blowfish' -key => $passphrase, ); my $action = $ARGV[0]; if ($action eq 'add') { my $data = read_file($crypt, $pwdfile); print "Account Name: "; chop(my $account = ); print "Account Login: "; chop(my $login = ); print "Account Password: "; chop(my $password = ); my $new_string = "$account - $password ($login)\n"; $data .= $new_string; write_file($crypt, $pwdfile, $data); } elsif ($action eq 'ls') { my $grep = $ARGV[1] ne "" ? $ARGV[1] : "(.*?)"; my $data = read_file($crypt, $pwdfile); my @pwds = sort split("\n", $data); foreach (@pwds) { next unless $_ =~ m/$grep/; $_ =~ m/(.*?) - (.*?) \((.*?)\)/; my $title = $1; my $user = $3; my $pwd = $2; print $title."\nUser: ".$user."\nPassword: ".$pwd."\n"; } } elsif ($action eq 'rm') { my $grep = $ARGV[1] ne "" ? $ARGV[1] : ""; my $data = read_file($crypt, $pwdfile); my @pwds = split "\n", $data; my $i = -1; foreach (@pwds) { $i++; next unless $_ =~ m/$grep/; $_ =~ m/(.*?) - (.*?) \((.*?)\)/; print "Delete '$1'? [y/n] "; chop(my $command = ); next unless $command eq 'y'; splice(@pwds, $i, 1); } $data = (join "\n", @pwds)."\n"; write_file($crypt, $pwdfile, $data); } else { print "ERROR: Invalid action '$action'.\n"; print "Type 'perldoc $0' for manual\n"; } sub read_file { my ($cipher, $qfn) = @_; open(my $fh, '<:bytes', $qfn") or die("Cannot open $qfn: $!\n"); my $file; { local $/; $file = <$fh>; } $file = $cipher->decrypt($file); die("Wrong password\n") if substr($file, 0, length(SIG), '') ne SIG; return $file; } sub write_file { my ($cipher, $qfn, $file) = @_; open(my $fh, '>:bytes', $qfn") or die("Cannot create file $qfn: $!\n"); print($fh $cipher->encrypt(SIG . $file)); } __END__ =head1 NAME Password - A simple command-line password manager. =head1 USAGE B I I =head1 ACTIONS =over =item B Add a new account in the password file. The user will be asked interactively for all the information (account name, login and password) needed. =item B Remove the ACCOUNT account. =item B Show accounts that contain TERM. If no TERM specified, the entire list is showed. =back =head1 CONFIGURATION Set PWD_FILE environment variable, to your password file location. Default ~/.passwd. =head1 ENCRYPTION Blowfish encryption is used, with proper salting and chaining. =cut