2 # -*- coding: ascii -*-
3 ###########################################################################
4 # clivepass, the login password utility for clive
6 # Copyright (c) 2008, 2009 Toni Gundogdu <legatvs@gmail.com>
8 # Permission to use, copy, modify, and distribute this software for any
9 # purpose with or without fee is hereby granted, provided that the above
10 # copyright notice and this permission notice appear in all copies.
12 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 ###########################################################################
24 use constant VERSION
=> "2.1.7";
26 binmode( STDOUT
, ":utf8" );
27 use Getopt
::Long
qw(:config bundling);
32 my $CONFIGDIR = $ENV{CLIVEPASS_HOME
}
33 || File
::Spec
->catfile( $ENV{HOME
}, ".config/clive-utils" );
34 my $PASSWDFILE = File
::Spec
->catfile( $CONFIGDIR, "passwd" );
37 GetOptions
( \
%opts, 'create|c', 'add|a=s', 'get|g=s', 'edit|e=s', 'delete|d=s',
38 'show|s', 'help|h', 'version|v' => \
&print_version
, )
43 Pod
::Usage
::pod2usage
( -exitstatus
=> 0, -verbose
=> 1 );
50 File
::Path
::mkpath
( [$CONFIGDIR], 0, 0700 );
52 require Crypt
::PasswdMD5
;
53 require Crypt
::Twofish
;
55 if ( $opts{add
} ) { add_login
(); }
56 elsif ( $opts{create
} ) { create_passwd
(); }
57 elsif ( $opts{edit
} ) { edit_login
(); }
58 elsif ( $opts{delete} ) { delete_login
(); }
59 elsif ( $opts{get
} ) { get_login
(); }
60 elsif ( $opts{show
} ) { show_logins
(); }
62 print STDERR
"Try --help for more info.\n";
69 ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' )
70 [ map { rand 64 } ( 1 .. $l ) ];
74 if ( -e
$PASSWDFILE ) {
75 print "WARN: $PASSWDFILE exists already.\n"
76 . "WARN: You are about to overwrite the existing file.\n"
77 . "WARN: Hit ctrl-c now if that's not your intention.\n";
79 print "Creating $PASSWDFILE.\n";
81 my ( $phrase, $again );
82 $phrase = getpass
("Enter a new passphrase: ") while ( !$phrase );
83 print "WARN: Consider using a longer passphrase.\n"
84 if ( length($phrase) < 8 );
85 $again = getpass
("Re-enter the passphrase: ") while ( !$again );
87 print STDERR
"error: passphrases did not match\n" and exit
88 unless $phrase eq $again;
90 my $passwd = Config
::Tiny
->new;
91 $passwd->{_
}->{phrase
} =
92 Crypt
::PasswdMD5
::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 ( Crypt
::PasswdMD5
::unix_md5_crypt
( $phrase, $phrase_hash ) ne
110 print STDERR
"error: invalid passphrase\n";
117 my ($dupl_user) = @_;
119 print STDERR
"error: $PASSWDFILE does not exist, use --create\n"
121 if ( !-e
$PASSWDFILE );
123 my $passwd = Config
::Tiny
->read($PASSWDFILE);
126 my ( $id, $pwd ) = lookup_login
( $passwd, $dupl_user );
127 print STDERR
qq/error: login "$dupl_user" / . "exists already\n"
132 my $phrase = verify_phrase
( $passwd->{_
}->{phrase
} );
134 my $key = Digest
::MD5
::md5_hex
($phrase);
136 return ( $key, $passwd );
144 chomp( my $passwd = <STDIN
> );
154 my ( $key, $passwd, $user ) = @_;
157 $pwd = getpass
("Enter password for $user: ") while ( !$pwd );
158 $again = getpass
("Re-enter the password: ") while ( !$again );
160 print STDERR
"error: passwords did not match\n" and exit
161 unless $pwd eq $again;
163 my $c = Crypt
::Twofish
->new($key);
165 $passwd->{login
}->{$user} =
166 MIME
::Base64
::encode_base64
( $c->encrypt( pack( 'a16', $pwd ) ) );
168 $passwd->write($PASSWDFILE);
172 my ( $key, $passwd ) = get_key
( $opts{add
} );
173 new_login
( $key, $passwd, $opts{add
} );
177 my ( $key, $passwd ) = get_key
();
178 my ( $id, $pwd ) = lookup_login
( $passwd, $opts{edit
} );
180 print STDERR
"error: no such login\n" and exit unless $pwd;
181 print qq/WARN: Changing password for the login "$id".\n/;
183 new_login
( $key, $passwd, $id );
187 my ( $key, $passwd ) = get_key
();
188 my ( $id, $pwd ) = lookup_login
( $passwd, $opts{get
} );
190 print STDERR
"error: no such login\n" and exit unless $pwd;
192 my $c = Crypt
::Twofish
->new($key);
195 . $c->decrypt( MIME
::Base64
::decode_base64
($pwd) ) . "\n";
199 my $passwd = Config
::Tiny
->read($PASSWDFILE);
200 foreach ( $passwd->{login
} ) {
201 while ( my ( $id, $pwd ) = each( %{$_} ) ) {
202 printf "%20s = %-32s\n", $id, $pwd;
208 my ( $key, $passwd ) = get_key
();
209 my ( $id, $pwd ) = lookup_login
( $passwd, $opts{delete} );
211 print STDERR
"error: no such login\n" and exit unless $pwd;
213 print qq/WARN: About to delete the login "$id". /
214 . "Confirm delete (y/N): ";
216 chomp( my $confirm = <STDIN
> );
217 exit unless $confirm eq "y";
219 delete $passwd->{login
}->{$id};
220 $passwd->write($PASSWDFILE);
224 my ( $passwd, $user ) = @_;
226 foreach ( $passwd->{login
} ) {
227 while ( my ( $id, $pwd ) = each( %{$_} ) ) {
228 if ( $id eq $user ) {
229 return ( $id, $pwd );
237 my $perl_v = sprintf( "--with-perl=%vd", $^V
);
239 sprintf( "clivepass version %s [%s].\n"
240 . "Copyright (c) 2008-2009 Toni Gundogdu "
241 . "<legatvs\@gmail.com>.\n\n",
243 $str .= "\t$perl_v\n";
245 "\nclivepass is licensed under the ISC license which is "
246 . "functionally\nequivalent to the 2-clause BSD licence.\n"
247 . "\tReport bugs to <http://code.google.com/p/clive-utils/issues/>.\n";
248 return $str if $noexit;
257 clivepass [option]...
261 -h, --help print help and exit
262 -v, --version print version and exit
263 -c, --create create new passwd file
264 -a, --add=USERNAME add new login for USERNAME
265 -e, --edit=USERNAME change login password for USERNAME
266 -g, --get=USERNAME dump USERNAME decrypted login password to stdout
267 -s, --show dump all saved login usernames to stdout
268 -d, --delete=USERNAME delete USERNAME from passwd file