Removed obsolete TODO line.
[clive-utils.git] / clivepass
blob804aede6a7326039618a1c9633df745c16e18507
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 module prerequisites to INSTALL
45 my $VERSION = "2.0beta3";
46 my $CONFIGDIR = $ENV{CLIVEPASS_CONFIGDIR}
47 || File::Spec->catfile($ENV{HOME}, ".config/clivepass");
48 my $PASSWDFILE = File::Spec->catfile($CONFIGDIR, "passwd");
50 my %opts;
51 $opts{add} = 0;
52 $opts{manual}= 0;
53 $opts{help} = 0;
55 GetOptions(\%opts,
56 'add|a', 'get|g=s', 'manual|m', 'help|h', 'version|v' => \&print_version,
57 ) or pod2usage(1);
59 pod2usage(-exitstatus => 0, -verbose => 1) if $opts{help};
60 pod2usage(-exitstatus => 0, -verbose => 2) if $opts{manual};
62 main();
64 sub main {
65 my $passwd = Config::Tiny->read($PASSWDFILE);
66 my $phrase_hash = $passwd->{_}->{phrase};
68 my %ctx = ! $phrase_hash
69 ? create_passwd($phrase_hash)
70 : verify_phrase($passwd, $phrase_hash);
72 my $key = md5_hex($ctx{phrase});
74 if ( $opts{add} ) {
75 add_login(\%ctx, $key);
76 } elsif ( $opts{get} ) {
77 my $pwd = get_login(\%ctx, $key, $opts{get});
78 if ( $pwd ) {
79 print "found: $opts{get}=$pwd\n";
80 } else {
81 print STDERR "error: login not found\n";
83 } else {
84 list_logins(\%ctx, $key);
88 sub create_passwd {
89 my ( $phrase_hash ) = @_;
91 my $phrase;
92 $phrase = getpass("Enter passphrase: ") while ( ! $phrase );
94 my $passwd = Config::Tiny->new;
95 $passwd->{_}->{phrase} = unix_md5_crypt($phrase, salt(8));
96 $passwd->write($PASSWDFILE);
98 return ( passwd => $passwd, phrase => $phrase );
101 sub verify_phrase {
102 my ( $passwd, $phrase_hash ) = @_;
104 my $phrase;
105 $phrase = getpass("Enter passphrase: ") while ( ! $phrase );
107 if ( unix_md5_crypt($phrase, $phrase_hash) ne $phrase_hash ) {
108 print STDERR "invalid passphrase\n";
109 exit;
111 return ( passwd => $passwd, phrase => $phrase );
114 sub getpass {
115 system "stty -echo";
116 print shift;
117 chomp(my $passwd = <STDIN>);
118 print "\n";
119 system "stty echo";
120 return $passwd;
123 sub add_login {
124 my ($ctx, $key) = @_;
126 print "Enter username: ";
127 chomp(my $user = <STDIN>);
129 my $pwd = getpass("Enter password for $user: ");
130 $pwd = pack('a16', $pwd);
132 my $c = Crypt::Twofish2->new($key, Crypt::Twofish2::MODE_CBC);
134 $ctx->{passwd}->{login}->{$user} = encode_base64($c->encrypt($pwd));
135 $ctx->{passwd}->write($PASSWDFILE);
138 sub get_login {
139 my ($ctx, $key, $login) = @_;
140 foreach ( $ctx->{passwd}->{login} ) {
141 while ( my ($id, $pwd) = each(%{$_}) ) {
142 if ( $id eq $login ) {
143 my $c = Crypt::Twofish2->new($key, Crypt::Twofish2::MODE_CBC);
144 return $c->decrypt(decode_base64($pwd));
150 sub list_logins {
151 my ($ctx, $key) = @_;
153 foreach ( $ctx->{passwd}->{login} ) {
154 while ( my ($id, $pwd) = each(%{$_}) ) {
155 my $c = Crypt::Twofish2->new($key, Crypt::Twofish2::MODE_CBC);
156 print "$id = " . $c->decrypt(decode_base64($pwd)) . "\n";
161 sub print_version {
162 my $perl_v = sprintf "%vd", $^V;
163 print
164 "clivepass version $VERSION. Copyright (C) 2008 Toni Gundogdu.
166 Perl: $perl_v ($^O)
167 Modules:
168 * MIME::Base64/$MIME::Base64::VERSION\t\t* Config::Tiny/$Config::Tiny::VERSION
169 * Crypt::Salt/$Crypt::Salt::VERSION\t\t* Digest::MD5/$Digest::MD5::VERSION
170 * Crypt::PasswdMD5/$Crypt::PasswdMD5::VERSION\t* Crypt::Twofish2/$Crypt::Twofish2::VERSION
172 Core modules:
173 * Getopt::Long/$Getopt::Long::VERSION\t\t* Encode/$Encode::VERSION
174 * File::Spec/$File::Spec::VERSION\t\t* File::Find/$File::Find::VERSION
175 * Pod::Usage/$Pod::Usage::VERSION
177 This program comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of
178 clivepass under the terms of the GNU General Public License as published by the
179 Free Software Foundation, either version 3 of the License, or (at your option)
180 any later version. You should have received a copy of the General Public License
181 along with this program. If not, see http://www.gnu.org/licenses/.
182 "; exit;
185 __END__
187 =head1 NAME
189 clivepass - the login password utility for clive
191 =head1 SYNOPSIS
193 clivepass [option]...
195 =head1 DESCRIPTION
197 clivepass is an utility that can be used to create and change passwords
198 for websites used by L<clive(1)>. The passwords are encrypted and saved
199 along with the username information. The access is restricted by using a
200 passphrase.
202 =head1 OPTIONS
204 If the utility is started without any options, it will prompt for the
205 passphrase and list all decrypted passwords to stdout.
207 B<Basic Options>
209 =over 4
211 =item B<-h --help>
213 Show help and exit.
215 =item B<-v --version>
217 Show version and exit.
219 =item B<-a --add>
221 Add a new password to the list.
223 =item B<-g --get=>I<username>
225 Return a decrypted password for the I<username> from the list.
226 The information is printed to stdout.
228 =back
230 =head1 EXAMPLES
232 =over 4
234 =item % clivepass -a
236 =item % clivepass -g myusername
238 =back
240 =head1 FILES
242 By default, clivepass searches the ~/.config/clivepass directory for the
243 password file. The B<CLIVEPASS_CONFIGDIR> environment variable can be used
244 to override this behaviour.
246 =over 4
248 =item ~/.config/clivepass/passwd
250 Password file. Contains the salted passphrase hash and login usernames and
251 encrypted passwords.
253 =head1 SEE ALSO
255 L<clive(1)> L<clivescan(1)> L<clivefeed(1)>
257 =head1 OTHER
259 Project: http://googlecode.com/p/clive-utils/
261 A clive-utils development repository can be obtained from:
263 % git clone git://repo.or.cz/clive-utils.git
265 Patches welcome.
267 =head1 AUTHOR
269 Written by Toni Gundogdu <legatvs@gmail.com>
271 =cut