Added clivepass utility.
[clive-utils.git] / clivepass
blobfde02b7342f3a3619fdf73322e8364183ef7c0c2
1 #!/usr/bin/env perl
2 # -*- coding: ascii -*-
3 ###########################################################################
4 # clivepass, the login password utility for clive
5 # Copyright (C) 2008 Toni Gundogdu.
7 # This file is part of clive-utils.
9 # clivepass is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # clivepass is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with clivepass. If not, see <http://www.gnu.org/licenses/>.
21 ###########################################################################
23 # Keep it simple.
25 use warnings;
26 use strict;
28 binmode(STDOUT, ":utf8");
30 use Digest::MD5 qw(md5_hex); #!bsd
31 use Crypt::PasswdMD5;
32 use Crypt::Twofish2;
33 use MIME::Base64; #!bsd
34 use Config::Tiny;
35 use Crypt::Salt;
36 # Core modules:
37 use Getopt::Long qw(:config bundling);
38 use File::Spec;
39 use File::Find;
40 use Pod::Usage;
41 use Encode;
43 # TODO: Add --manual and __END__
44 # TODO: Add module prerequisites to INSTALL
46 my $VERSION = "2.0beta3";
47 my $CONFIGDIR = $ENV{CLIVEPASS_CONFIGDIR}
48 || File::Spec->catfile($ENV{HOME}, ".config/clivepass");
49 my $PASSWDFILE = File::Spec->catfile($CONFIGDIR, "passwd");
51 my %opts;
52 $opts{add} = 0;
53 $opts{manual}= 0;
54 $opts{help} = 0;
56 GetOptions(\%opts,
57 'add|a', 'get|g=s', 'manual|m', 'help|h', 'version|v' => \&print_version,
58 ) or pod2usage(1);
60 pod2usage(-exitstatus => 0, -verbose => 1) if $opts{help};
61 pod2usage(-exitstatus => 0, -verbose => 2) if $opts{manual};
63 main();
65 sub main {
66 my $passwd = Config::Tiny->read($PASSWDFILE);
67 my $phrase_hash = $passwd->{_}->{phrase};
69 my %ctx = ! $phrase_hash
70 ? create_passwd($phrase_hash)
71 : verify_phrase($passwd, $phrase_hash);
73 my $key = md5_hex($ctx{phrase});
75 if ( $opts{add} ) {
76 add_login(\%ctx, $key);
77 } elsif ( $opts{get} ) {
78 my $pwd = get_login(\%ctx, $key, $opts{get});
79 if ( $pwd ) {
80 print "found: $opts{get}=$pwd\n";
81 } else {
82 print STDERR "error: login not found\n";
84 } else {
85 list_logins(\%ctx, $key);
89 sub create_passwd {
90 my ( $phrase_hash ) = @_;
92 my $phrase;
93 $phrase = getpass("Enter passphrase: ") while ( ! $phrase );
95 my $passwd = Config::Tiny->new;
96 $passwd->{_}->{phrase} = unix_md5_crypt($phrase, salt(8));
97 $passwd->write($PASSWDFILE);
99 return ( passwd => $passwd, phrase => $phrase );
102 sub verify_phrase {
103 my ( $passwd, $phrase_hash ) = @_;
105 my $phrase;
106 $phrase = getpass("Enter passphrase: ") while ( ! $phrase );
108 if ( unix_md5_crypt($phrase, $phrase_hash) ne $phrase_hash ) {
109 print STDERR "invalid passphrase\n";
110 exit;
112 return ( passwd => $passwd, phrase => $phrase );
115 sub getpass {
116 system "stty -echo";
117 print shift;
118 chomp(my $passwd = <STDIN>);
119 print "\n";
120 system "stty echo";
121 return $passwd;
124 sub add_login {
125 my ($ctx, $key) = @_;
127 print "Enter username: ";
128 chomp(my $user = <STDIN>);
130 my $pwd = getpass("Enter password for $user: ");
131 $pwd = pack('a16', $pwd);
133 my $c = Crypt::Twofish2->new($key, Crypt::Twofish2::MODE_CBC);
135 $ctx->{passwd}->{login}->{$user} = encode_base64($c->encrypt($pwd));
136 $ctx->{passwd}->write($PASSWDFILE);
139 sub get_login {
140 my ($ctx, $key, $login) = @_;
141 foreach ( $ctx->{passwd}->{login} ) {
142 while ( my ($id, $pwd) = each(%{$_}) ) {
143 if ( $id eq $login ) {
144 my $c = Crypt::Twofish2->new($key, Crypt::Twofish2::MODE_CBC);
145 return $c->decrypt(decode_base64($pwd));
151 sub list_logins {
152 my ($ctx, $key) = @_;
154 foreach ( $ctx->{passwd}->{login} ) {
155 while ( my ($id, $pwd) = each(%{$_}) ) {
156 my $c = Crypt::Twofish2->new($key, Crypt::Twofish2::MODE_CBC);
157 print "$id = " . $c->decrypt(decode_base64($pwd)) . "\n";
162 sub print_version {
163 my $perl_v = sprintf "%vd", $^V;
164 print
165 "clivepass version $VERSION. Copyright (C) 2008 Toni Gundogdu.
167 Perl: $perl_v ($^O)
168 Modules:
169 * MIME::Base64/$MIME::Base64::VERSION\t\t* Config::Tiny/$Config::Tiny::VERSION
170 * Crypt::Salt/$Crypt::Salt::VERSION\t\t* Digest::MD5/$Digest::MD5::VERSION
171 * Crypt::PasswdMD5/$Crypt::PasswdMD5::VERSION\t* Crypt::Twofish2/$Crypt::Twofish2::VERSION
173 Core modules:
174 * Getopt::Long/$Getopt::Long::VERSION\t\t* Encode/$Encode::VERSION
175 * File::Spec/$File::Spec::VERSION\t\t* File::Find/$File::Find::VERSION
176 * Pod::Usage/$Pod::Usage::VERSION
178 This program comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of
179 clivepass under the terms of the GNU General Public License as published by the
180 Free Software Foundation, either version 3 of the License, or (at your option)
181 any later version. You should have received a copy of the General Public License
182 along with this program. If not, see http://www.gnu.org/licenses/.
183 "; exit;
186 __END__
188 =head1 NAME
190 clivepass - the login password utility for clive
192 =head1 SYNOPSIS
194 clivepass [option]...
196 =head1 DESCRIPTION
198 clivepass is an utility that can be used to create and change passwords
199 for websites used by L<clive(1)>. The passwords are encrypted and saved
200 along with the username information. The access is restricted by using a
201 passphrase.
203 =head1 OPTIONS
205 If the utility is started without any options, it will prompt for the
206 passphrase and list all decrypted passwords to stdout.
208 B<Basic Options>
210 =over 4
212 =item B<-h --help>
214 Show help and exit.
216 =item B<-v --version>
218 Show version and exit.
220 =item B<-a --add>
222 Add a new password to the list.
224 =item B<-g --get=>I<username>
226 Return a decrypted password for the I<username> from the list.
227 The information is printed to stdout.
229 =back
231 =head1 EXAMPLES
233 =over 4
235 =item % clivepass -a
237 =item % clivepass -g myusername
239 =back
241 =head1 FILES
243 By default, clivepass searches the ~/.config/clivepass directory for the
244 password file. The B<CLIVEPASS_CONFIGDIR> environment variable can be used
245 to override this behaviour.
247 =over 4
249 =item ~/.config/clivepass/passwd
251 Password file. Contains the salted passphrase hash and login usernames and
252 encrypted passwords.
254 =head1 SEE ALSO
256 L<clive(1)> L<clivescan(1)> L<clivefeed(1)>
258 =head1 OTHER
260 Project: http://googlecode.com/p/clive-utils/
262 A clive-utils development repository can be obtained from:
264 % git clone git://repo.or.cz/clive-utils.git
266 Patches welcome.
268 =head1 AUTHOR
270 Written by Toni Gundogdu <legatvs@gmail.com>
272 =cut