From 82e50a3fda52978fbbfa2ba40d699577e18f32cb Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 10 Feb 2014 21:25:24 +0000 Subject: [PATCH] release 2.023000 --- Netdisco/Changes | 2 +- Netdisco/MANIFEST | 24 +- Netdisco/META.yml | 6 +- Netdisco/Makefile.PL | 6 +- Netdisco/bin/netdisco-deploy | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- .../App/Netdisco/Web/Auth/Provider/DBIC.pm | 2 +- .../Netdisco/Web/Plugin/AdminTask/Users.pm | 2 +- .../lib/App/Netdisco/Web/Plugin/Passphrase.pm | 753 ------------------ 9 files changed, 27 insertions(+), 772 deletions(-) delete mode 100644 Netdisco/lib/App/Netdisco/Web/Plugin/Passphrase.pm diff --git a/Netdisco/Changes b/Netdisco/Changes index 0c087e45..bf4a0f9a 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,4 +1,4 @@ -2.023000 - +2.023000 - 2014-02-10 [NEW FEATURES] diff --git a/Netdisco/MANIFEST b/Netdisco/MANIFEST index 90e05700..a172ab6c 100644 --- a/Netdisco/MANIFEST +++ b/Netdisco/MANIFEST @@ -22,6 +22,7 @@ lib/App/Netdisco.pm lib/App/Netdisco/Core/Arpnip.pm lib/App/Netdisco/Core/Discover.pm lib/App/Netdisco/Core/Macsuck.pm +lib/App/Netdisco/Core/Nbtstat.pm lib/App/Netdisco/Daemon/DB.pm lib/App/Netdisco/Daemon/DB/Result/Admin.pm lib/App/Netdisco/Daemon/Queue.pm @@ -37,6 +38,7 @@ lib/App/Netdisco/Daemon/Worker/Poller/Common.pm lib/App/Netdisco/Daemon/Worker/Poller/Device.pm lib/App/Netdisco/Daemon/Worker/Poller/Expiry.pm lib/App/Netdisco/Daemon/Worker/Poller/Macsuck.pm +lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm lib/App/Netdisco/Daemon/Worker/Scheduler.pm lib/App/Netdisco/DB.pm lib/App/Netdisco/DB/ExplicitLocking.pm @@ -71,6 +73,7 @@ lib/App/Netdisco/DB/Result/Virtual/ActiveNode.pm lib/App/Netdisco/DB/Result/Virtual/ActiveNodeWithAge.pm lib/App/Netdisco/DB/Result/Virtual/ApRadioChannelPower.pm lib/App/Netdisco/DB/Result/Virtual/CidrIps.pm +lib/App/Netdisco/DB/Result/Virtual/DeviceDnsMismatch.pm lib/App/Netdisco/DB/Result/Virtual/DeviceLinks.pm lib/App/Netdisco/DB/Result/Virtual/DuplexMismatch.pm lib/App/Netdisco/DB/Result/Virtual/NodeWithAge.pm @@ -89,10 +92,10 @@ lib/App/Netdisco/DB/ResultSet/Device.pm lib/App/Netdisco/DB/ResultSet/DevicePort.pm lib/App/Netdisco/DB/ResultSet/Node.pm lib/App/Netdisco/DB/ResultSet/NodeIp.pm +lib/App/Netdisco/DB/ResultSet/NodeNbt.pm lib/App/Netdisco/DB/ResultSet/NodeWireless.pm lib/App/Netdisco/DB/ResultSet/Subnet.pm lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-1-2-PostgreSQL.sql -lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-1-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-10-11-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-11-12-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-12-13-PostgreSQL.sql @@ -100,13 +103,10 @@ lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-13-14-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-14-15-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-15-16-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-16-17-PostgreSQL.sql -lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-16-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-17-18-PostgreSQL.sql -lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-17-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-18-19-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-19-20-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-2-3-PostgreSQL.sql -lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-2-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-20-21-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-21-22-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-22-23-PostgreSQL.sql @@ -122,12 +122,15 @@ lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-30-31-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-31-32-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-32-33-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-33-34-PostgreSQL.sql +lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-34-35-PostgreSQL.sql +lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-35-36-PostgreSQL.sql +lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-36-37-PostgreSQL.sql +lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-37-38-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-4-5-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-5-6-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-6-7-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-7-8-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-8-9-PostgreSQL.sql -lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-8-PostgreSQL.sql lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-9-10-PostgreSQL.sql lib/App/Netdisco/Manual/Configuration.pod lib/App/Netdisco/Manual/Deployment.pod @@ -136,6 +139,7 @@ lib/App/Netdisco/Manual/ReleaseNotes.pod lib/App/Netdisco/Manual/WritingPlugins.pod lib/App/Netdisco/Util/Device.pm lib/App/Netdisco/Util/DNS.pm +lib/App/Netdisco/Util/Node.pm lib/App/Netdisco/Util/Noop.pm lib/App/Netdisco/Util/Port.pm lib/App/Netdisco/Util/PortMAC.pm @@ -170,10 +174,12 @@ lib/App/Netdisco/Web/Plugin/Report/ApClients.pm lib/App/Netdisco/Web/Plugin/Report/ApRadioChannelPower.pm lib/App/Netdisco/Web/Plugin/Report/DeviceAddrNoDNS.pm lib/App/Netdisco/Web/Plugin/Report/DeviceByLocation.pm +lib/App/Netdisco/Web/Plugin/Report/DeviceDnsMismatch.pm lib/App/Netdisco/Web/Plugin/Report/DevicePoeStatus.pm lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm lib/App/Netdisco/Web/Plugin/Report/HalfDuplex.pm lib/App/Netdisco/Web/Plugin/Report/IpInventory.pm +lib/App/Netdisco/Web/Plugin/Report/Netbios.pm lib/App/Netdisco/Web/Plugin/Report/NodeMultiIPs.pm lib/App/Netdisco/Web/Plugin/Report/PhonesDiscovered.pm lib/App/Netdisco/Web/Plugin/Report/PortAdminDown.pm @@ -241,6 +247,7 @@ share/public/images/tango_sweep.png share/public/images/vaga_copy.png share/public/javascripts/bootstrap.min.js share/public/javascripts/d3.min.js +share/public/javascripts/daterangepicker.js share/public/javascripts/jquery-deserialize.js share/public/javascripts/jquery-history.js share/public/javascripts/jquery-latest.min.js @@ -248,6 +255,7 @@ share/public/javascripts/jquery-ui.custom.min.js share/public/javascripts/jquery.cookie.js share/public/javascripts/jquery.floatThead.js share/public/javascripts/jquery.qtip.min.js +share/public/javascripts/moment.min.js share/public/javascripts/netdisco.js share/public/javascripts/netdisco_portcontrol.js share/public/javascripts/toastr.js @@ -281,6 +289,8 @@ share/views/ajax/report/deviceaddrnodns.tt share/views/ajax/report/deviceaddrnodns_csv.tt share/views/ajax/report/devicebylocation.tt share/views/ajax/report/devicebylocation_csv.tt +share/views/ajax/report/devicednsmismatch.tt +share/views/ajax/report/devicednsmismatch_csv.tt share/views/ajax/report/devicepoestatus.tt share/views/ajax/report/devicepoestatus_csv.tt share/views/ajax/report/duplexmismatch.tt @@ -289,6 +299,7 @@ share/views/ajax/report/halfduplex.tt share/views/ajax/report/halfduplex_csv.tt share/views/ajax/report/ipinventory.tt share/views/ajax/report/ipinventory_csv.tt +share/views/ajax/report/netbios.tt share/views/ajax/report/nodemultiips.tt share/views/ajax/report/nodemultiips_csv.tt share/views/ajax/report/phonesdiscovered.tt @@ -321,9 +332,7 @@ share/views/inventory.tt share/views/js/admintask.js share/views/js/bootstrap-tree.js share/views/js/common.js -share/views/js/daterangepicker.js share/views/js/device.js -share/views/js/moment.min.js share/views/js/report.js share/views/js/search.js share/views/layouts/main.tt @@ -334,6 +343,7 @@ share/views/sidebar/admintask/portlog.tt share/views/sidebar/device/netmap.tt share/views/sidebar/device/ports.tt share/views/sidebar/report/ipinventory.tt +share/views/sidebar/report/netbios.tt share/views/sidebar/report/subnets.tt share/views/sidebar/search/device.tt share/views/sidebar/search/node.tt diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 03af017f..26bb4e16 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -34,7 +34,7 @@ requires: Dancer: 1.3112 Dancer::Plugin::Auth::Extensible: 0.3 Dancer::Plugin::DBIC: 0.1803 - Dancer::Plugin::Passphrase: 2 + Dancer::Plugin::Passphrase: 2.0.1 File::ShareDir: 1.03 Guard: 1.022 HTML::Parser: 3.7 @@ -53,7 +53,7 @@ requires: Plack: 1.0023 Plack::Middleware::Expires: 0.03 Role::Tiny: 1.002005 - SNMP::Info: 3.11 + SNMP::Info: 3.12 SQL::Translator: 0.11016 Socket6: 0.23 Starman: 0.4008 @@ -74,4 +74,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.022000 +version: 2.023000 diff --git a/Netdisco/Makefile.PL b/Netdisco/Makefile.PL index 8fb35f4e..aa98b83e 100644 --- a/Netdisco/Makefile.PL +++ b/Netdisco/Makefile.PL @@ -18,9 +18,7 @@ requires 'Daemon::Control' => 0.001000; requires 'Dancer' => 1.3112; requires 'Dancer::Plugin::DBIC' => 0.1803; requires 'Dancer::Plugin::Auth::Extensible' => 0.30; -requires 'Data::Entropy::Algorithms' => 0.007; -requires 'Digest::Bcrypt' => 1.0.1; -requires 'Digest::SHA' => 5.86; +requires 'Dancer::Plugin::Passphrase' => '2.0.1'; requires 'File::ShareDir' => 1.03; requires 'Guard' => 1.022; requires 'HTML::Parser' => 3.70; @@ -41,7 +39,7 @@ requires 'Plack::Middleware::Expires' => 0.03; requires 'Role::Tiny' => 1.002005; requires 'Socket6' => 0.23; requires 'Starman' => 0.4008; -requires 'SNMP::Info' => 3.11; +requires 'SNMP::Info' => 3.12; requires 'SQL::Translator' => 0.11016; requires 'Template' => 2.24; requires 'Template::Plugin::CSV' => 0.04; diff --git a/Netdisco/bin/netdisco-deploy b/Netdisco/bin/netdisco-deploy index 0a79a8f1..cda994ae 100755 --- a/Netdisco/bin/netdisco-deploy +++ b/Netdisco/bin/netdisco-deploy @@ -37,7 +37,7 @@ BEGIN { use App::Netdisco; use Dancer ':script'; use Dancer::Plugin::DBIC 'schema'; -use App::Netdisco::Web::Plugin::Passphrase; +use Dancer::Plugin::Passphrase; info "App::Netdisco version $App::Netdisco::VERSION loaded."; diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index b4fa2961..ecfe6d7c 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -7,7 +7,7 @@ use 5.010_000; use File::ShareDir 'dist_dir'; use Path::Class; -our $VERSION = '2.022000'; +our $VERSION = '2.023000'; BEGIN { if (not ($ENV{DANCER_APPDIR} || '') diff --git a/Netdisco/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm b/Netdisco/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm index 7954a493..c90719f7 100644 --- a/Netdisco/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm +++ b/Netdisco/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm @@ -10,7 +10,7 @@ use base 'Dancer::Plugin::Auth::Extensible::Provider::Base'; use Dancer ':syntax'; use Dancer::Plugin::DBIC; -use App::Netdisco::Web::Plugin::Passphrase; +use Dancer::Plugin::Passphrase; use Digest::MD5; sub authenticate_user { diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm index 6ab0bfc3..ec2f79e3 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm @@ -4,7 +4,7 @@ use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; use Dancer::Plugin::Auth::Extensible; -use App::Netdisco::Web::Plugin::Passphrase; +use Dancer::Plugin::Passphrase; use App::Netdisco::Web::Plugin; use Digest::MD5 (); diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Passphrase.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Passphrase.pm deleted file mode 100644 index 27d14f4a..00000000 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Passphrase.pm +++ /dev/null @@ -1,753 +0,0 @@ -package App::Netdisco::Web::Plugin::Passphrase; - -# ABSTRACT: Passphrases and Passwords as objects for Dancer - -=head1 NAME - -Dancer::Plugin::Passphrase - Passphrases and Passwords as objects for Dancer - -=head1 SYNOPSIS - -This plugin manages the hashing of passwords for Dancer apps, allowing -developers to follow cryptography best practices without having to -become a cryptography expert. - -It uses the bcrypt algorithm as the default, while also supporting any -hashing function provided by L - -=head1 USAGE - - package MyWebService; - use Dancer ':syntax'; - use Dancer::Plugin::Passphrase; - - post '/login' => sub { - my $phrase = passphrase( param('my password') )->generate; - - # $phrase is now an object that contains RFC 2307 representation - # of the hashed passphrase, along with the salt, and other metadata - - # You should store $phrase->rfc2307() for use later - }; - - get '/protected' => sub { - # Retrieve $stored_rfc_2307_string, like we created above. - # IT MUST be a valid RFC 2307 string - - if ( passphrase( param('my password') )->matches( $stored_rfc_2307 ) ) { - # Passphrase matches! - } - }; - - get '/generate_new_password' => sub { - return passphrase->generate_random; - }; - -=cut - -use strict; -use feature 'switch'; - -use Dancer::Plugin; - -use Carp qw(carp croak); -use Data::Entropy::Algorithms qw(rand_bits rand_int); -use Digest; -use MIME::Base64 qw(decode_base64 encode_base64); -use Scalar::Util qw(blessed); - -our $VERSION = '2.0.0'; - -# Auto stringifies and returns the RFC 2307 representation -# of the object unless we are calling a method on it -use overload ( - '""' => sub { - if (blessed($_[0]) && $_[0]->isa('App::Netdisco::Web::Plugin::Passphrase')) { - $_[0]->rfc2307(); - } - }, - fallback => 1, -); - -register passphrase => \&passphrase; - - -=head1 KEYWORDS - -=head2 passphrase - -Given a plaintext password, it returns a Dancer::Plugin::Passphrase -object that you can generate a new hash from, or match against a stored hash. - -=cut - -sub passphrase { - # Dancer 2 keywords receive a reference to the DSL object as a first param. - # We don't need it, so get rid of it, and just get the plaintext - shift if blessed($_[0]) && $_[0]->isa('Dancer::Core::DSL'); - - my $plaintext = $_[0]; - - return bless { - plaintext => $plaintext - }, 'App::Netdisco::Web::Plugin::Passphrase'; -} - - - -=head1 MAIN METHODS - -=head2 generate - -Generates an RFC 2307 representation of the hashed passphrase -that is suitable for storage in a database. - - my $pass = passphrase('my passphrase')->generate; - -You should store C<$phrase->rfc_2307()> in your database. For convenience -the object will automagically return the RFC 2307 representation when no -method is called on it. - -Accepts a hashref of options to specify what kind of hash should be -generated. All options settable in the config file are valid. - -If you specify only the algorithm, the default settings for that algorithm will be used. - -A cryptographically random salt is used if salt is not defined. -Only if you specify the empty string will an empty salt be used -This is not recommended, and should only be used to upgrade old insecure hashes - - my $phrase = passphrase('my password')->generate({ - algorithm => '', # What algorithm is used to generate the hash - cost => '', # Cost / Work Factor if using bcrypt - salt => '', # Manually specify salt if using a salted digest - }); - -=cut - -sub generate { - my ($self, $options) = @_; - - $self->_get_settings($options); - $self->_calculate_hash; - - return $self; -} - -sub generate_hash { - carp "generate_hash method is deprecated"; - return shift->generate(); -} - - -=head2 matches - -Matches a plaintext password against a stored hash. -Returns 1 if the hash of the password matches the stored hash. -Returns undef if they don't match or if there was an error -Fail-Secure, rather than Fail-Safe. - - passphrase('my password')->matches($stored_rfc_2307_string); - -$stored_rfc_2307_string B be a valid RFC 2307 string, -as created by L - -An RFC 2307 string is made up of a scheme identifier, followed by a -base64 encoded string. The base64 encoded string should contain -the password hash and the salt concatenated together - in that order. - - '{'.$scheme.'}'.encode_base64($hash . $salt, ''); - -Where C<$scheme> can be any of the following and their unsalted variants, -which have the leading S removed. CRYPT will be Bcrypt. - - SMD5 SSHA SSHA224 SSHA256 SSHA384 SSHA512 CRYPT - -A complete RFC2307 string looks like this: - - {SSHA}K3LAbIjRL5CpLzOlm3/HzS3qt/hUaGVTYWx0 - -This is the format created by L - -=cut - -sub matches { - my ($self, $stored_hash) = @_; - - # Force auto stringification in case we were passed an object. - ($stored_hash) = ($stored_hash =~ m/(.*)/s); - - my $new_hash = $self->_extract_settings($stored_hash)->_calculate_hash->rfc2307; - - return ($new_hash eq $stored_hash) ? 1 : undef; -} - - - -=head2 generate_random - -Generates and returns any number of cryptographically random -characters from the url-safe base64 charater set. - - my $rand_pass = passphrase->generate_random; - -The passwords generated are suitable for use as -temporary passwords or one-time authentication tokens. - -You can configure the length and the character set -used by passing a hashref of options. - - my $rand_pass = passphrase->generate_random({ - length => 32, - charset => ['a'..'z', 'A'..'Z'], - }); - -=cut - -sub generate_random { - my ($self, $options) = @_; - - # Default is 16 URL-safe base64 chars. Supported everywhere and a reasonable length - my $length = $options->{length} || 16; - my $charset = $options->{charset} || ['a'..'z', 'A'..'Z', '0'..'9', '-', '_']; - - return join '', map { @$charset[rand_int scalar @$charset] } 1..$length; -} - - - -=head1 ADDITIONAL METHODS - -The methods are only applicable once you have called C - - passphrase( 'my password' )->generate->rfc2307; # CORRECT - - passphrase( 'my password' )->rfc2307; # INCORRECT, Returns undef - - -=head2 rfc2307 - -Returns the rfc2307 representation from a C object. - - passphrase('my password')->generate->rfc2307; - -=cut - -sub rfc2307 { - return shift->{rfc2307} || undef; -} - -sub as_rfc2307 { - carp "as_rfc2307 method is deprecated"; - return shift->rfc2307(); -} - - - -=head2 scheme - -Returns the scheme name from a C object. - -This is the scheme name as used in the RFC 2307 representation - - passphrase('my password')->generate->scheme; - -The scheme name can be any of the following, and will always be capitalized - - SMD5 SSHA SSHA224 SSHA256 SSHA384 SSHA512 CRYPT - MD5 SHA SHA224 SHA256 SHA384 SHA512 - -=cut - -sub scheme { - return shift->{scheme} || undef; -} - - -=head2 algorithm - -Returns the algorithm name from a C object. - -The algorithm name can be anything that is accepted by Cnew($alg)> -This includes any modules in the C Namespace - - passphrase('my password')->generate->algorithm; - -=cut - -sub algorithm { - return shift->{algorithm} || undef; -} - - -=head2 cost - -Returns the bcrypt cost from a C object. -Only works when using the bcrypt algorithm, returns undef for other algorithms - - passphrase('my password')->generate->cost; - -=cut - -sub cost { - return shift->{cost} || undef; -} - - -=head2 salt_raw - -Returns the raw salt from a C object. - - passphrase('my password')->generate->salt_raw; - -Can be defined, but false - The empty string is technically a valid salt. - -Returns C if there is no salt. - -=cut - -sub salt_raw { - return shift->{salt} // undef; -} - -sub raw_salt { - carp "raw_salt method is deprecated"; - return shift->salt_raw(); -} - -=head2 hash_raw - -Returns the raw hash from a C object. - - passphrase('my password')->generate->hash_raw; - -=cut - -sub hash_raw { - return shift->{hash} || undef; -} - -sub raw_hash { - carp "raw_hash method is deprecated"; - return shift->hash_raw(); -} - - -=head2 salt_hex - -Returns the hex-encoded salt from a C object. - -Can be defined, but false - The empty string is technically a valid salt. -Returns C if there is no salt. - - passphrase('my password')->generate->salt_hex; - -=cut - -sub salt_hex { - return unpack("H*", shift->{salt}) // undef; -} - - -=head2 hash_hex - -Returns the hex-encoded hash from a C object. - - passphrase('my password')->generate->hash_hex; - -=cut - -sub hash_hex { - return unpack("H*", shift->{hash}) || undef; -} - - -=head2 salt_base64 - -Returns the base64 encoded salt from a C object. - -Can be defined, but false - The empty string is technically a valid salt. -Returns C if there is no salt. - - passphrase('my password')->generate->salt_base64; - -=cut - -sub salt_base64 { - return encode_base64(shift->{salt}, '') // undef; -} - - -=head2 hash_base64 - -Returns the base64 encoded hash from a C object. - - passphrase('my password')->generate->hash_base64; - -=cut - -sub hash_base64 { - return encode_base64(shift->{hash}, '') || undef; -} - -=head2 plaintext - -Returns the plaintext password as originally supplied to the L keyword. - - passphrase('my password')->generate->plaintext; - -=cut - -sub plaintext { - return shift->{plaintext} || undef; -} - - - -# Actual generation of the hash, using the provided settings -sub _calculate_hash { - my $self = shift; - - my $hasher = Digest->new( $self->algorithm ); - - given ($self->algorithm) { - when ('Bcrypt') { - $hasher->add($self->{plaintext}); - $hasher->salt($self->salt_raw); - $hasher->cost($self->cost); - - $self->{hash} = $hasher->digest; - $self->{rfc2307} - = '{CRYPT}$' - . $self->{type} . '$' - . $self->cost . '$' - . _en_bcrypt_base64($self->salt_raw) - . _en_bcrypt_base64($self->hash_raw); - } - default { - $hasher->add($self->{plaintext}); - $hasher->add($self->{salt}); - - $self->{hash} = $hasher->digest; - $self->{rfc2307} - = '{' . $self->{scheme} . '}' - . encode_base64($self->hash_raw . $self->salt_raw, ''); - } - } - - return $self; -} - - -# Extracts the settings from an RFC 2307 string -sub _extract_settings { - my ($self, $rfc2307_string) = @_; - - my ($scheme, $settings) = ($rfc2307_string =~ m/^{(\w+)}(.*)/s); - - unless ($scheme && $settings) { - croak "An RFC 2307 compliant string must be passed to matches()"; - } - - if ($scheme eq 'CRYPT'){ - given ($settings) { - when (/^\$2(?:a|x|y)\$/) { - $scheme = 'Bcrypt'; - $settings =~ m{\A\$(2a|2x|2y)\$([0-9]{2})\$([./A-Za-z0-9]{22})}x; - - ($self->{type}, $self->{cost}, $self->{salt}) = ($1, $2, _de_bcrypt_base64($3)); - } - default { croak "Unknown CRYPT format: $_"; } - } - } - - my $scheme_meta = { - 'MD5' => { algorithm => 'MD5', octets => 128 / 8 }, - 'SMD5' => { algorithm => 'MD5', octets => 128 / 8 }, - 'SHA' => { algorithm => 'SHA-1', octets => 160 / 8 }, - 'SSHA' => { algorithm => 'SHA-1', octets => 160 / 8 }, - 'SHA224' => { algorithm => 'SHA-224', octets => 224 / 8 }, - 'SSHA224' => { algorithm => 'SHA-224', octets => 224 / 8 }, - 'SHA256' => { algorithm => 'SHA-256', octets => 256 / 8 }, - 'SSHA256' => { algorithm => 'SHA-256', octets => 256 / 8 }, - 'SHA384' => { algorithm => 'SHA-384', octets => 384 / 8 }, - 'SSHA384' => { algorithm => 'SHA-384', octets => 384 / 8 }, - 'SHA512' => { algorithm => 'SHA-512', octets => 512 / 8 }, - 'SSHA512' => { algorithm => 'SHA-512', octets => 512 / 8 }, - 'Bcrypt' => { algorithm => 'Bcrypt', octets => 128 / 8 }, - }; - - $self->{scheme} = $scheme; - $self->{algorithm} = $scheme_meta->{$scheme}->{algorithm}; - - if (!defined $self->{salt}) { - $self->{salt} = substr(decode_base64($settings), $scheme_meta->{$scheme}->{octets}); - } - - return $self; -} - - -# Gets the settings from config.yml, and merges them with any custom -# settings given to the constructor -sub _get_settings { - my ($self, $options) = @_; - - $self->{algorithm} = $options->{algorithm} || - plugin_setting->{algorithm} || - 'Bcrypt'; - - my $plugin_setting = plugin_setting->{$self->algorithm}; - - # Specify empty string to get an unsalted hash - # Leaving it undefs results in 128 random bits being used as salt - # bcrypt requires this amount, and is reasonable for other algorithms - $self->{salt} = $options->{salt} // - $plugin_setting->{salt} // - rand_bits(128); - - # RFC 2307 scheme is based on the algorithm, with a prefixed 'S' for salted - $self->{scheme} = join '', $self->algorithm =~ /[\w]+/g; - $self->{scheme} = 'S'.$self->{scheme} if $self->{salt}; - - given ($self->{scheme}) { - when ('SHA1') { $self->{scheme} = 'SHA'; } - when ('SSHA1') { $self->{scheme} = 'SSHA'; } - } - - # Bcrypt requires a cost parameter - if ($self->algorithm eq 'Bcrypt') { - $self->{scheme} = 'CRYPT'; - $self->{type} = '2a'; - $self->{cost} = $options->{cost} || - $plugin_setting->{cost} || - 4; - - $self->{cost} = 31 if $self->cost > 31; - $self->{cost} = sprintf("%02d", $self->cost); - } - - return $self; -} - - -# From Crypt::Eksblowfish::Bcrypt. -# Bcrypt uses it's own variation on base64 -sub _en_bcrypt_base64 { - my ($octets) = @_; - my $text = encode_base64($octets, ''); - $text =~ tr{A-Za-z0-9+/=}{./A-Za-z0-9}d; - return $text; -} - - -# And the decoder of bcrypt's custom base64 -sub _de_bcrypt_base64 { - my ($text) = @_; - $text =~ tr{./A-Za-z0-9}{A-Za-z0-9+/}; - $text .= "=" x (3 - (length($text) + 3) % 4); - return decode_base64($text); -} - - -register_plugin for_versions => [ 1, 2 ]; - -1; - - -=head1 MORE INFORMATION - -=head2 Purpose - -The aim of this module is to help you store new passwords in a secure manner, -whilst still being able to verify and upgrade older passwords. - -Cryptography is a vast and complex field. Many people try to roll their own -methods for securing user data, but succeed only in coming up with -a system that has little real security. - -This plugin provides a simple way of managing that complexity, allowing -developers to follow crypto best practice without having to become an expert. - - -=head2 Rationale - -The module defaults to hashing passwords using the bcrypt algorithm, returning them -in RFC 2307 format. - -RFC 2307 describes an encoding system for passphrase hashes, as used in the "userPassword" -attribute in LDAP databases. It encodes hashes as ASCII text, and supports several -passphrase schemes by starting the encoding with an alphanumeric scheme identifier enclosed -in braces. - -RFC 2307 only specifies the C, and C schemes - however in real-world usage, -schemes that are salted are widely supported, and are thus provided by this module. - -Bcrypt is an adaptive hashing algorithm that is designed to resist brute -force attacks by including a cost (aka work factor). This cost increases -the computational effort it takes to compute the hash. - -SHA and MD5 are designed to be fast, and modern machines compute a billion -hashes a second. With computers getting faster every day, brute forcing -SHA hashes is a very real problem that cannot be easily solved. - -Increasing the cost of generating a bcrypt hash is a trivial way to make -brute forcing ineffective. With a low cost setting, bcrypt is just as secure -as a more traditional SHA+salt scheme, and just as fast. Increasing the cost -as computers become more powerful keeps you one step ahead - -For a more detailed description of why bcrypt is preferred, see this article: -L - - -=head2 Configuration - -In your applications config file, you can set the default hashing algorithm, -and the default settings for every supported algorithm. Calls to -L will use the default settings -for that algorithm specified in here. - -You can override these defaults when you call L. - -If you do no configuration at all, the default is to bcrypt with a cost of 4, and -a strong psuedo-random salt. - - plugins: - Passphrase: - default: Bcrypt - - Bcrypt: - cost: 8 - - -=head2 Storage in a database - -You should be storing the RFC 2307 string in your database, it's the easiest way -to use this module. You could store the C, C, and C -separately, but this strongly discouraged. RFC 2307 strings are specifically -designed for storing hashed passwords, and should be used wherever possible. - -The length of the string produced by L can -vary dependent on your settings. Below is a table of the lengths generated -using default settings. - -You will need to make sure your database columns are at least this long. -If the string gets truncated, the password can I be validated. - - ALGORITHM LENGTH EXAMPLE RFC 2307 STRING - - Bcrypt 68 {CRYPT}$2a$04$MjkMhQxasFQod1qq56DXCOvWu6YTWk9X.EZGnmSSIbbtyEBIAixbS - SHA-512 118 {SSHA512}lZG4dZ5EU6dPEbJ1kBPPzEcupFloFSIJjiXCwMVxJXOy/x5qhBA5XH8FiUWj7u59onQxa97xYdqje/fwY5TDUcW1Urplf3KHMo9NO8KO47o= - SHA-384 98 {SSHA384}SqZF5YYyk4NdjIM8YgQVfRieXDxNG0dKH4XBcM40Eblm+ribCzdyf0JV7i2xJvVHZsFSQNcuZPKtiTMzDyOU+w== - SHA-256 74 {SSHA256}xsJHNzPlNCpOZ41OkTfQOU35ZY+nRyZFaM8lHg5U2pc0xT3DKNlGW2UTY0NPYsxU - SHA-224 70 {SSHA224}FTHNkvKOdyX1d6f45iKLVxpaXZiHel8pfilUT1dIZ5u+WIUyhDGxLnx72X0= - SHA-1 55 {SSHA}Qsaao/Xi/bYTRMQnpHuD3y5nj02wbdcw5Cek2y2nLs3pIlPh - MD5 51 {SMD5}bgfLiUQWgzUm36+nBhFx62bi0xdwTp+UpEeNKDxSLfM= - -=head2 Common Mistakes - -Common mistakes people make when creating their own solution. If any of these -seem familiar, you should probably be using this module - -=over - -=item Passwords are stored as plain text for a reason - -There is never a valid reason to store a password as plain text. -Passwords should be reset and not emailed to customers when they forget. -Support people should be able to login as a user without knowing the users password. -No-one except the user should know the password - that is the point of authentication. - -=item No-one will ever guess our super secret algorithm! - -Unless you're a cryptography expert with many years spent studying -super-complex maths, your algorithm is almost certainly not as secure -as you think. Just because it's hard for you to break doesn't mean -it's difficult for a computer. - -=item Our application-wide salt is "Sup3r_S3cret_L0ng_Word" - No-one will ever guess that. - -This is common misunderstanding of what a salt is meant to do. The purpose of a -salt is to make sure the same password doesn't always generate the same hash. -A fresh salt needs to be created each time you hash a password. It isn't meant -to be a secret key. - -=item We generate our random salt using C. - -C isn't actually random, it's a non-unform pseudo-random number generator, -and not suitable for cryptographic applications. Whilst this module also defaults to -a PRNG, it is better than the one provided by C. Using a true RNG is a config -option away, but is not the default as it it could potentially block output if the -system does not have enough entropy to generate a truly random number - -=item We use C, and the salt is from C - -MD5 has been broken for many years. Commodity hardware can find a -hash collision in seconds, meaning an attacker can easily generate -the correct MD5 hash without using the correct password. - -=item We use C, and the salt is from C - -SHA isn't quite as broken as MD5, but it shares the same theoretical -weaknesses. Even without hash collisions, it is vulnerable to brute forcing. -Modern hardware is so powerful it can try around a billion hashes a second. -That means every 7 chracter password in the range [A-Za-z0-9] can be cracked -in one hour on your average desktop computer. - -=item If the only way to break the hash is to brute-force it, it's secure enough - -It is unlikely that your database will be hacked and your hashes brute forced. -However, in the event that it does happen, or SHA512 is broken, using this module -gives you an easy way to change to a different algorithm, while still allowing -you to validate old passphrases - -=back - - -=head1 KNOWN ISSUES - -If you see errors like this - - Wide character in subroutine entry - -or - - Input must contain only octets - -The C, C, and C algorithms can't handle chracters with an ordinal -value above 255, producing errors like this if they encounter them. -It is not possible for this plugin to automagically work out the correct -encoding for a given string. - -If you see errors like this, then you probably need to use the L module -to encode your text as UTF-8 (or whatever encoding it is) before giving it -to C. - -Text encoding is a bag of hurt, and errors like this are probably indicitive -of deeper problems within your app's code. - -You will save yourself a lot of trouble if you read up on the -L module sooner rather than later. - -For further reading on UTF-8, unicode, and text encoding in perl, -see L - - -=head1 SEE ALSO - -L, L, L, L - - -=head1 AUTHOR - -James Aitken - - -=head1 COPYRIGHT AND LICENSE - -This software is copyright (c) 2012 by James Aitken. - -This is free software; you can redistribute it and/or modify it under -the same terms as the Perl 5 programming language system itself. - -=cut