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 ###########################################################################
28 binmode(STDOUT
, ":utf8");
35 use Digest
::MD5
qw(md5_hex);
37 use Getopt
::Long
qw(:config bundling);
44 my $VERSION = "2.0.0";
45 my $CONFIGDIR = $ENV{CLIVEPASS_CONFIGDIR
}
46 || File
::Spec
->catfile($ENV{HOME
}, ".config/clivepass");
47 my $PASSWDFILE = File
::Spec
->catfile($CONFIGDIR, "passwd");
51 'create|c', 'add|a=s', 'get|g=s', 'edit|e=s', 'delete|d=s',
52 'show|s', 'manual|m', 'help|h', 'version|v' => \
&print_version
,
55 pod2usage
(-exitstatus
=> 0, -verbose
=> 1) if $opts{help
};
56 pod2usage
(-exitstatus
=> 0, -verbose
=> 2) if $opts{manual
};
61 mkpath
( [$CONFIGDIR], 1, 0700 );
63 if ( $opts{add
} ) { add_login
(); }
64 elsif ( $opts{create
} ) { create_passwd
(); }
65 elsif ( $opts{edit
} ) { edit_login
(); }
66 elsif ( $opts{delete} ) { delete_login
(); }
67 elsif ( $opts{get
} ) { get_login
(); }
68 elsif ( $opts{show
} ) { show_logins
(); }
70 pod2usage
(-exitstatus
=> 0, -verbose
=> 1);
75 if ( -e
$PASSWDFILE ) {
76 print "WARN: $PASSWDFILE exists already.\n"
77 . "WARN: You are about to overwrite the existing file.\n"
78 . "WARN: Hit ctrl-c now if that's not your intention.\n";
80 print "Creating $PASSWDFILE.\n";
83 $phrase = getpass
("Enter a new passphrase: ") while ( ! $phrase );
84 print "WARN: Consider using a longer passphrase.\n"
85 if ( length($phrase) < 8 );
86 $again = getpass
("Re-enter the passphrase: ") while ( ! $again );
88 print STDERR
"error: passphrases did not match\n" and exit
89 unless $phrase eq $again;
91 my $passwd = Config
::Tiny
->new;
92 $passwd->{_
}->{phrase
} = unix_md5_crypt
($phrase, salt
(8));
93 $passwd->write($PASSWDFILE);
95 return ( passwd
=> $passwd, phrase
=> $phrase );
99 my ($phrase_hash) = @_;
101 print STDERR
"error: $PASSWDFILE: phrase hash not found\n" and exit
105 $phrase = getpass
("Enter passphrase: ") while ( ! $phrase );
107 if ( unix_md5_crypt
($phrase, $phrase_hash) ne $phrase_hash ) {
108 print STDERR
"error: invalid passphrase\n";
115 my ($dupl_user) = @_;
117 print STDERR
"error: $PASSWDFILE does not exist, use --create\n"
118 and exit if ( ! -e
$PASSWDFILE );
120 my $passwd = Config
::Tiny
->read($PASSWDFILE);
123 my ($id, $pwd) = lookup_login
($passwd, $dupl_user);
124 print STDERR
qq/error: login with the "$dupl_user" /
125 . "username exists already\n" and exit if $pwd;
128 my $phrase = verify_phrase
($passwd->{_
}->{phrase
});
129 my $key = md5_hex
($phrase);
131 return ($key, $passwd);
140 chomp(my $passwd = <STDIN
>);
150 my ($key, $passwd, $user) = @_;
153 $pwd = getpass
("Enter password for $user: ") while ( ! $pwd );
154 $again = getpass
("Re-enter the password: ") while ( ! $again );
156 print STDERR
"error: passwords did not match\n" and exit
157 unless $pwd eq $again;
159 my $c = Crypt
::Twofish2
->new($key, Crypt
::Twofish2
::MODE_CBC
);
161 $passwd->{login
}->{$user} =
162 encode_base64
( $c->encrypt( pack('a16',$pwd) ) );
164 $passwd->write($PASSWDFILE);
168 my ($key, $passwd) = get_key
($opts{add
});
169 new_login
($key, $passwd, $opts{add
});
173 my ($key, $passwd) = get_key
();
174 my ($id, $pwd) = lookup_login
($passwd, $opts{edit
});
176 print STDERR
qq/error: no such login with the "$opts{edit}" /
177 . "username exists\n" and exit unless $pwd;
179 print "WARN: Changing password for the login "
180 . qq/with the username "$id".\n/;
182 new_login
($key, $passwd, $id);
186 my ($key, $passwd) = get_key
();
187 my ($id, $pwd) = lookup_login
($passwd, $opts{get
});
189 print STDERR
qq/error: no such login with the "$opts{get}" /
190 . "username exists\n" and exit unless $pwd;
192 my $c = Crypt
::Twofish2
->new($key, Crypt
::Twofish2
::MODE_CBC
);
193 print "login: " .$opts{get
}."=". $c->decrypt(decode_base64
($pwd)) ."\n";
197 my $passwd = Config
::Tiny
->read($PASSWDFILE);
198 foreach ( $passwd->{login
} ) {
199 while ( my ($id, $pwd) = each(%{$_}) ) {
200 printf "%20s = %-32s\n", $id, $pwd;
206 my ($key, $passwd) = get_key
();
207 my ($id, $pwd) = lookup_login
($passwd, $opts{delete});
209 print STDERR
qq/error: no such login with the "$opts{delete}" /
210 . "username exists\n" and exit unless $pwd;
212 print "WARN: About to delete the login with the username "
213 . qq/"$id".\n/ ."> Confirm delete (y/N): ";
215 chomp(my $confirm = <STDIN
>);
216 exit unless $confirm eq "y";
218 delete $passwd->{login
}->{$id};
219 $passwd->write($PASSWDFILE);
223 my ($passwd, $user) = @_;
225 foreach ( $passwd->{login
} ) {
226 while ( my ($id, $pwd) = each(%{$_}) ) {
227 if ( $id eq $user ) {
235 my $perl_v = sprintf "%vd", $^V
;
237 "clivepass version $VERSION. Copyright (C) 2008 Toni Gundogdu.
241 * Crypt::PasswdMD5/$Crypt::PasswdMD5::VERSION\t* Crypt::Twofish2/$Crypt::Twofish2::VERSION
242 * Crypt::Salt/$Crypt::Salt::VERSION\t\t* Config::Tiny/$Config::Tiny::VERSION
245 * Getopt::Long/$Getopt::Long::VERSION\t\t* Encode/$Encode::VERSION
246 * File::Spec/$File::Spec::VERSION\t\t* File::Find/$File::Find::VERSION
247 * Pod::Usage/$Pod::Usage::VERSION\t\t* Digest::MD5/$Digest::MD5::VERSION
248 * MIME::Base64/$MIME::Base64::VERSION
250 This program comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of
251 clivepass under the terms of the GNU General Public License as published by the
252 Free Software Foundation, either version 3 of the License, or (at your option)
253 any later version. You should have received a copy of the General Public License
254 along with this program. If not, see http://www.gnu.org/licenses/.
262 clivepass - the login password utility for clive
266 clivepass [option]...
270 clivepass is an utility that can be used to create and change passwords
271 for websites used by L<clive(1)>. The passwords are encrypted and saved
272 along with the username information. Access is restricted by using a
275 Historically, a similar utility, clive-passwd, was part of clive 1.x but
276 was written in Python instead of Perl.
288 =item B<-v --version>
290 Show version and exit.
294 Create a new passwd file. See also L</FILES>.
296 =item B<-a --add=>I<username>
298 Add a new login with the I<username>.
300 =item B<-e --edit=>I<username>
302 Change login password for I<username>.
304 =item B<-g --get=>I<username>
306 Print the decrypted login password for the I<username>
311 Show all saved login usernames with encrypted passwords.
313 =item B<-d --delete=>I<username>
315 Delete the login with the I<username>.
323 =item % clivepass -a myusername
325 =item % clivepass -g myusername
331 By default, clivepass searches the ~/.config/clivepass directory for the
332 password file. The B<CLIVEPASS_CONFIGDIR> environment variable can be used
333 to override this behaviour.
337 =item ~/.config/clivepass/passwd
339 Password file. Contains the salted passphrase hash and login usernames and
344 L<clive(1)> L<clivescan(1)> L<clivefeed(1)>
348 Project: http://googlecode.com/p/clive-utils/
350 A clive-utils development repository can be obtained from:
352 % git clone git://repo.or.cz/clive-utils.git
358 Written by Toni Gundogdu <legatvs@gmail.com>