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.0beta3";
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 passphrase: ") while ( ! $phrase );
84 $again = getpass
("Enter passphrase again: ") while ( ! $again );
86 print STDERR
"[error] passphrases did not match\n" and exit
87 unless $phrase eq $again;
89 my $passwd = Config
::Tiny
->new;
90 $passwd->{_
}->{phrase
} = unix_md5_crypt
($phrase, salt
(8));
91 $passwd->write($PASSWDFILE);
93 return ( passwd
=> $passwd, phrase
=> $phrase );
97 my ($phrase_hash) = @_;
99 print STDERR
"[error] $PASSWDFILE: phrase hash not found\n" and exit
103 $phrase = getpass
("Enter passphrase: ") while ( ! $phrase );
105 if ( unix_md5_crypt
($phrase, $phrase_hash) ne $phrase_hash ) {
106 print STDERR
"[error] invalid passphrase\n";
113 my ($dupl_user) = @_;
115 print STDERR
"[error] $PASSWDFILE does not exist, use --create\n"
116 and exit if ( ! -e
$PASSWDFILE );
118 my $passwd = Config
::Tiny
->read($PASSWDFILE);
121 my ($id, $pwd) = lookup_login
($passwd, $dupl_user);
122 print STDERR
qq/[error] login with the "$dupl_user" /
123 . "username exists already\n" and exit if $pwd;
126 my $phrase = verify_phrase
($passwd->{_
}->{phrase
});
127 my $key = md5_hex
($phrase);
129 return ($key, $passwd);
138 chomp(my $passwd = <STDIN
>);
148 my ($key, $passwd, $user) = @_;
151 $pwd = getpass
("Enter password for $user: ") while ( ! $pwd );
152 $again = getpass
("Re-enter the password: ") while ( ! $again );
154 print STDERR
"[error] passwords did not match\n" and exit
155 unless $pwd eq $again;
157 my $c = Crypt
::Twofish2
->new($key, Crypt
::Twofish2
::MODE_CBC
);
159 $passwd->{login
}->{$user} =
160 encode_base64
( $c->encrypt( pack('a16',$pwd) ) );
162 $passwd->write($PASSWDFILE);
166 my ($key, $passwd) = get_key
($opts{add
});
167 new_login
($key, $passwd, $opts{add
});
171 my ($key, $passwd) = get_key
();
172 my ($id, $pwd) = lookup_login
($passwd, $opts{edit
});
174 print STDERR
qq/[error] no such login with the "$opts{edit}" /
175 . "username exists\n" and exit unless $pwd;
177 print "WARN: Changing password for the login "
178 . qq/with the username "$id".\n/;
180 new_login
($key, $passwd, $id);
184 my ($key, $passwd) = get_key
();
185 my ($id, $pwd) = lookup_login
($passwd, $opts{get
});
187 print STDERR
qq/[error] no such login with the "$opts{get}" /
188 . "username exists\n" and exit unless $pwd;
190 my $c = Crypt
::Twofish2
->new($key, Crypt
::Twofish2
::MODE_CBC
);
191 print "login: " .$opts{get
}."=". $c->decrypt(decode_base64
($pwd)) ."\n";
195 my $passwd = Config
::Tiny
->read($PASSWDFILE);
196 foreach ( $passwd->{login
} ) {
197 while ( my ($id, $pwd) = each(%{$_}) ) {
198 printf "%20s = %-32s\n", $id, $pwd;
204 my ($key, $passwd) = get_key
();
205 my ($id, $pwd) = lookup_login
($passwd, $opts{delete});
207 print STDERR
qq/[error] no such login with the "$opts{delete}" /
208 . "username exists\n" and exit unless $pwd;
210 print "WARN: About to delete the login with the username "
211 . qq/"$id".\n/ ."> Confirm delete (y/N): ";
213 chomp(my $confirm = <STDIN
>);
214 exit unless $confirm eq "y";
216 delete $passwd->{login
}->{$id};
217 $passwd->write($PASSWDFILE);
221 my ($passwd, $user) = @_;
223 foreach ( $passwd->{login
} ) {
224 while ( my ($id, $pwd) = each(%{$_}) ) {
225 if ( $id eq $user ) {
233 my $perl_v = sprintf "%vd", $^V
;
235 "clivepass version $VERSION. Copyright (C) 2008 Toni Gundogdu.
239 * Crypt::PasswdMD5/$Crypt::PasswdMD5::VERSION\t* Crypt::Twofish2/$Crypt::Twofish2::VERSION
240 * Crypt::Salt/$Crypt::Salt::VERSION\t\t* Config::Tiny/$Config::Tiny::VERSION
243 * Getopt::Long/$Getopt::Long::VERSION\t\t* Encode/$Encode::VERSION
244 * File::Spec/$File::Spec::VERSION\t\t* File::Find/$File::Find::VERSION
245 * Pod::Usage/$Pod::Usage::VERSION\t\t* Digest::MD5/$Digest::MD5::VERSION
246 * MIME::Base64/$MIME::Base64::VERSION
248 This program comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of
249 clivepass under the terms of the GNU General Public License as published by the
250 Free Software Foundation, either version 3 of the License, or (at your option)
251 any later version. You should have received a copy of the General Public License
252 along with this program. If not, see http://www.gnu.org/licenses/.
260 clivepass - the login password utility for clive
264 clivepass [option]...
268 clivepass is an utility that can be used to create and change passwords
269 for websites used by L<clive(1)>. The passwords are encrypted and saved
270 along with the username information. Access is restricted by using a
273 Historically, a similar utility, clive-passwd, was part of clive 1.x but
274 was written in Python instead of Perl.
286 =item B<-v --version>
288 Show version and exit.
292 Create a new passwd file. See also L</FILES>.
294 =item B<-a --add=>I<username>
296 Add a new login with the I<username>.
298 =item B<-e --edit=>I<username>
300 Change login password for I<username>.
302 =item B<-g --get=>I<username>
304 Print the decrypted login password for the I<username>
309 Show all saved login usernames with encrypted passwords.
311 =item B<-d --delete=>I<username>
313 Delete the login with the I<username>.
321 =item % clivepass -a myusername
323 =item % clivepass -g myusername
329 By default, clivepass searches the ~/.config/clivepass directory for the
330 password file. The B<CLIVEPASS_CONFIGDIR> environment variable can be used
331 to override this behaviour.
335 =item ~/.config/clivepass/passwd
337 Password file. Contains the salted passphrase hash and login usernames and
342 L<clive(1)> L<clivescan(1)> L<clivefeed(1)>
346 Project: http://googlecode.com/p/clive-utils/
348 A clive-utils development repository can be obtained from:
350 % git clone git://repo.or.cz/clive-utils.git
356 Written by Toni Gundogdu <legatvs@gmail.com>