|
Line 0
Link Here
|
|
|
1 |
package Koha::Passwords; |
| 2 |
|
| 3 |
# Copyright 2013 Catalyst IT Ltd. |
| 4 |
# |
| 5 |
# This file is part of Koha. |
| 6 |
# |
| 7 |
# Koha is free software; you can redistribute it and/or modify it under the |
| 8 |
# terms of the GNU General Public License as published by the Free Software |
| 9 |
# Foundation; either version 2 of the License, or (at your option) any later |
| 10 |
# version. |
| 11 |
# |
| 12 |
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY |
| 13 |
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR |
| 14 |
# A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 15 |
# |
| 16 |
# You should have received a copy of the GNU General Public License along |
| 17 |
# with Koha; if not, write to the Free Software Foundation, Inc., |
| 18 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 19 |
|
| 20 |
=head1 NAME |
| 21 |
|
| 22 |
Koha::Passwords - A central point to handle user passwords |
| 23 |
|
| 24 |
=head1 SYNOPSIS |
| 25 |
|
| 26 |
use Koha::Passwords; |
| 27 |
my $hashed1 = Koha::Passwords::hash_password($password1); |
| 28 |
my $hashed2 = Koha::Passwords::hash_password($password2, $hashed1); |
| 29 |
print '$password1 is the same as $password2' if $hashed1 eq $hashed2; |
| 30 |
|
| 31 |
=head1 DESCRIPTION |
| 32 |
|
| 33 |
This provides the hashing function that should be used whenever you want to |
| 34 |
do something with a password. |
| 35 |
|
| 36 |
=head1 FUNCTIONS |
| 37 |
|
| 38 |
=cut |
| 39 |
|
| 40 |
use Fcntl qw/O_RDONLY/; # O_RDONLY is used in generate_salt |
| 41 |
use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64); |
| 42 |
use Modern::Perl; |
| 43 |
|
| 44 |
require Exporter; |
| 45 |
|
| 46 |
use vars qw(@ISA @EXPORT @EXPORT_OK); |
| 47 |
|
| 48 |
BEGIN { |
| 49 |
@ISA = qw(Exporter); |
| 50 |
@EXPORT = qw(); |
| 51 |
@EXPORT_OK = qw(hash_password); |
| 52 |
} |
| 53 |
|
| 54 |
=head2 hash_password |
| 55 |
|
| 56 |
$hashed = hash_password($password, [$settings]); |
| 57 |
|
| 58 |
This hashes a password using whatever the secure algorithm of the day is. |
| 59 |
The optional C<$settings> parameter describes what an existing password is |
| 60 |
hashed with, and is attached to the start of the returned string. This means |
| 61 |
that you can test a password by passing in the password you were given, |
| 62 |
the hash you already have, and compare the hash you get out to the one you |
| 63 |
have. |
| 64 |
|
| 65 |
This function is exportable. |
| 66 |
|
| 67 |
=cut |
| 68 |
|
| 69 |
sub hash_password { |
| 70 |
my $password = shift; |
| 71 |
# In case it gets called with -> instead of :: |
| 72 |
$password = shift if $password eq 'Koha::Passwords'; |
| 73 |
|
| 74 |
# Generate a salt if one is not passed |
| 75 |
my $settings = shift; |
| 76 |
unless ( defined $settings ) |
| 77 |
{ # if there are no settings, we need to create a salt and append settings |
| 78 |
# Set the cost to 8 and append a NULL |
| 79 |
$settings = '$2a$08$' . en_base64( generate_salt( 'weak', 16 ) ); |
| 80 |
} |
| 81 |
|
| 82 |
# Encrypt it |
| 83 |
return bcrypt( $password, $settings ); |
| 84 |
} |
| 85 |
|
| 86 |
=head2 generate_salt |
| 87 |
|
| 88 |
my $salt = generate_salt($strength, $length); |
| 89 |
|
| 90 |
=over |
| 91 |
|
| 92 |
=item strength |
| 93 |
|
| 94 |
For general password salting a C<$strength> of C<weak> is recommend, |
| 95 |
For generating a server-salt a C<$strength> of C<strong> is recommended |
| 96 |
|
| 97 |
'strong' uses /dev/random which may block until sufficient entropy is acheived. |
| 98 |
'weak' uses /dev/urandom and is non-blocking. |
| 99 |
|
| 100 |
=item length |
| 101 |
|
| 102 |
C<$length> is a positive integer which specifies the desired length of the returned string |
| 103 |
|
| 104 |
=back |
| 105 |
|
| 106 |
This function is not exportable. |
| 107 |
|
| 108 |
=cut |
| 109 |
|
| 110 |
# the implementation of generate_salt is loosely based on Crypt::Random::Provider::File |
| 111 |
sub generate_salt { |
| 112 |
|
| 113 |
# strength is 'strong' or 'weak' |
| 114 |
# length is number of bytes to read, positive integer |
| 115 |
my ( $strength, $length ) = @_; |
| 116 |
|
| 117 |
my $source; |
| 118 |
|
| 119 |
if ( $length < 1 ) { |
| 120 |
die |
| 121 |
"non-positive strength of '$strength' passed to Koha::Passwords::generate_salt\n"; |
| 122 |
} |
| 123 |
|
| 124 |
if ( $strength eq "strong" ) { |
| 125 |
$source = '/dev/random'; # blocking |
| 126 |
} |
| 127 |
else { |
| 128 |
unless ( $strength eq 'weak' ) { |
| 129 |
warn |
| 130 |
"unsuppored strength of '$strength' passed to Koha::Passwords::generate_salt, defaulting to 'weak'\n"; |
| 131 |
} |
| 132 |
$source = '/dev/urandom'; # non-blocking |
| 133 |
} |
| 134 |
|
| 135 |
sysopen SOURCE, $source, O_RDONLY |
| 136 |
or die |
| 137 |
"failed to open source '$source' in Koha:Passwords::generate_salt\n"; |
| 138 |
|
| 139 |
# $bytes is the bytes just read |
| 140 |
# $string is the concatenation of all the bytes read so far |
| 141 |
my ( $bytes, $string ) = ( "", "" ); |
| 142 |
|
| 143 |
# keep reading until we have $length bytes in $strength |
| 144 |
while ( length($string) < $length ) { |
| 145 |
|
| 146 |
# return the number of bytes read, 0 (EOF), or -1 (ERROR) |
| 147 |
my $return = sysread SOURCE, $bytes, $length - length($string); |
| 148 |
|
| 149 |
# if no bytes were read, keep reading (if using /dev/random it is possible there was insufficient entropy so this may block) |
| 150 |
next unless $return; |
| 151 |
if ( $return == -1 ) { |
| 152 |
die |
| 153 |
"error while reading from $source in Koha::Passwords::generate_salt\n"; |
| 154 |
} |
| 155 |
|
| 156 |
$string .= $bytes; |
| 157 |
} |
| 158 |
|
| 159 |
close SOURCE; |
| 160 |
return $string; |
| 161 |
} |
| 162 |
|