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; |
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 |
|