diff --git a/Anvil/Tools/Account.pm b/Anvil/Tools/Account.pm index 953a52f9..567b698c 100755 --- a/Anvil/Tools/Account.pm +++ b/Anvil/Tools/Account.pm @@ -13,6 +13,7 @@ my $THIS_FILE = "Account.pm"; ### Methods; # encrypt_password +# read_details # validate_password @@ -80,10 +81,10 @@ This takes a string (a new password from a user), generates a salt, appends the This method returns a hash reference with the following keys; -* hash: The final encrypted hash. -* salt: The salt created (or used) to generate the hash. -* algorithm: The algorithm used to compute the hash. -* loops: The number of re-encryptions of the initial hash. +* user_password_hash: The final encrypted hash. +* user_salt: The salt created (or used) to generate the hash. +* user_algorithm: The algorithm used to compute the hash. +* user_hash_count: The number of re-encryptions of the initial hash. If anything goes wrong, all four keys will have empty strings. @@ -93,10 +94,6 @@ Parameters This is the password (string) to encrypt. -=head3 salt (optional) - -If passed, this string will be appended to the password to salt the string. If this is not passed, a random, new hash - =cut sub encrypt_password { @@ -105,109 +102,200 @@ sub encrypt_password my $anvil = $self->parent; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; - my $password = defined $parameter->{password} ? $parameter->{password} : ""; - my $salt = defined $parameter->{salt} ? $parameter->{target} : ""; - my $hash = ""; - my $loops = $anvil->data->{sys}{password}{hash_count} =~ /^\d+$/ ? $anvil->data->{sys}{password}{hash_count} : 500000; - my $algorithm = $anvil->data->{sys}{password}{algorithm} ? $anvil->data->{sys}{password}{algorithm} : "sha512"; + my $user_password_hash = defined $parameter->{password} ? $parameter->{password} : ""; + my $user_hash_count = $anvil->data->{sys}{password}{hash_count} =~ /^\d+$/ ? $anvil->data->{sys}{password}{hash_count} : 500000; + my $user_algorithm = $anvil->data->{sys}{password}{algorithm} ? $anvil->data->{sys}{password}{algorithm} : "sha512"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - password => $anvil->Log->secure ? $password : "--", - salt => $salt, + password => $anvil->Log->secure ? $user_password_hash : "--", }}); # We'll fill these out below if we succeed. my $answer = { - hash => "", - salt => "", - loops => "", - algorithm => "", + user_password_hash => "", + user_salt => "", + user_hash_count => "", + user_algorithm => "", }; # Make sure we got a string - if (not $password) + if (not $user_password_hash) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Account->encrypt_password()", parameter => "password" }}); return($answer); } - # If we weren't passed a salt, generate one node. - if (not $salt) - { - $salt = $anvil->Get->_salt; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { salt => $salt }}); - } + # Generate a salt. + my $user_salt = $anvil->Get->_salt; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_salt => $user_salt }}); ### TODO: Look at using/support bcrypt as the default algorithm. Needed RPMs are already in the el7 AN!Repo. # We support sha256, sha384 and sha512, possible new ones later. - if ($algorithm eq "sha256" ) + if ($user_algorithm eq "sha256" ) { - $hash = sha256_base64($password.$salt); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { hash => $hash }}); + $user_password_hash = sha256_base64($user_password_hash.$user_salt); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_password_hash => $user_password_hash }}); - if ($loops > 0) + if ($user_hash_count > 0) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { loops => $loops }}); - for (1..$loops) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_hash_count => $user_hash_count }}); + for (1..$user_hash_count) { - $hash = sha256_base64($hash); + $user_password_hash = sha256_base64($user_password_hash); } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { hash => $hash }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_password_hash => $user_password_hash }}); } } - elsif ($algorithm eq "sha384" ) + elsif ($user_algorithm eq "sha384" ) { - $hash = sha384_base64($password.$salt); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { hash => $hash }}); + $user_password_hash = sha384_base64($user_password_hash.$user_salt); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_password_hash => $user_password_hash }}); - if ($loops > 0) + if ($user_hash_count > 0) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { loops => $loops }}); - for (1..$loops) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_hash_count => $user_hash_count }}); + for (1..$user_hash_count) { - $hash = sha384_base64($hash); + $user_password_hash = sha384_base64($user_password_hash); } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { hash => $hash }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_password_hash => $user_password_hash }}); } } - elsif ($algorithm eq "sha512" ) + elsif ($user_algorithm eq "sha512" ) { - $hash = sha512_base64($password.$salt); - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { hash => $hash }}); + $user_password_hash = sha512_base64($user_password_hash.$user_salt); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_password_hash => $user_password_hash }}); - if ($loops > 0) + if ($user_hash_count > 0) { - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { loops => $loops }}); - for (1..$loops) + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_hash_count => $user_hash_count }}); + for (1..$user_hash_count) { - $hash = sha512_base64($hash); + $user_password_hash = sha512_base64($user_password_hash); } - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { hash => $hash }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_password_hash => $user_password_hash }}); } } else { # Bash algorith. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0171", variables => { algorithm => $algorithm }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0171", variables => { user_algorithm => $user_algorithm }}); return($answer); } $answer = { - hash => $hash, - salt => $salt, - loops => $loops, - algorithm => $algorithm, + user_password_hash => $user_password_hash, + user_salt => $user_salt, + user_hash_count => $user_hash_count, + user_algorithm => $user_algorithm, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 'answer->hash' => $answer->{hash}, - 'answer->salt' => $answer->{salt}, - 'answer->loops' => $answer->{loops}, - 'answer->algorithm' => $answer->{algorithm}, + 'answer->user_password_hash' => $answer->{user_password_hash}, + 'answer->user_salt' => $answer->{user_salt}, + 'answer->user_hash_count' => $answer->{user_hash_count}, + 'answer->user_algorithm' => $answer->{user_algorithm}, }}); return($answer); } +=head2 read_details + +This method takes a user name and, if the user is found, reads in the details and sets C<< sys::user:: >>. If the user is found, C<< 1 >> is returned. If not, C<< 0 >> is returned. + +Parameters; + +=head3 user_name (required) + +This is the user name being searched for. It is case sensitive. + +=cut +sub read_details +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + + my $user_name = defined $parameter->{user_name} ? $parameter->{user_name} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_name => $user_name }}); + + my $query = " +SELECT + user_uuid, + user_password_hash, + user_salt, + user_algorithm, + user_hash_count, + user_language, + user_is_admin, + user_is_experienced, + user_is_trusted +FROM + users +WHERE + user_name = ".$anvil->data->{sys}{use_db_fh}->quote($user_name)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + if (not $count) + { + # User doesn't exist. + return(0); + } + my $user_uuid = $results->[0]->[0]; + my $user_password_hash = $results->[0]->[1]; + my $user_salt = $results->[0]->[2]; + my $user_algorithm = $results->[0]->[3]; + my $user_hash_count = $results->[0]->[4]; + my $user_language = $results->[0]->[5]; + my $user_is_admin = $results->[0]->[6]; + my $user_is_experienced = $results->[0]->[7]; + my $user_is_trusted = $results->[0]->[8]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + user_uuid => $user_uuid, + user_password_hash => $user_password_hash, + user_salt => $user_salt, + user_algorithm => $user_algorithm, + user_hash_count => $user_hash_count, + user_language => $user_language, + user_is_admin => $user_is_admin, + user_is_experienced => $user_is_experienced, + user_is_trusted => $user_is_trusted, + }}); + + $anvil->data->{sys}{user}{user_name} = $user_name; + $anvil->data->{sys}{user}{user_uuid} = $user_uuid; + $anvil->data->{sys}{user}{user_password_hash} = $user_password_hash, + $anvil->data->{sys}{user}{user_salt} = $user_salt, + $anvil->data->{sys}{user}{user_algorithm} = $user_algorithm, + $anvil->data->{sys}{user}{user_hash_count} = $user_hash_count, + $anvil->data->{sys}{user}{user_language} = $user_language, + $anvil->data->{sys}{user}{user_is_admin} = $user_is_admin, + $anvil->data->{sys}{user}{user_is_experienced} = $user_is_experienced, + $anvil->data->{sys}{user}{user_is_trusted} = $user_is_trusted, + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + 'sys::user::user_name' => $anvil->data->{sys}{user}{user_name}, + 'sys::user::user_uuid' => $anvil->data->{sys}{user}{user_uuid}, + 'sys::user::user_password_hash' => $anvil->data->{sys}{user}{user_password_hash}, + 'sys::user::user_salt' => $anvil->data->{sys}{user}{user_salt}, + 'sys::user::user_algorithm' => $anvil->data->{sys}{user}{user_algorithm}, + 'sys::user::user_hash_count' => $anvil->data->{sys}{user}{user_hash_count}, + 'sys::user::user_language' => $anvil->data->{sys}{user}{user_language}, + 'sys::user::user_is_admin' => $anvil->data->{sys}{user}{user_is_admin}, + 'sys::user::user_is_experienced' => $anvil->data->{sys}{user}{user_is_experienced}, + 'sys::user::user_is_trusted' => $anvil->data->{sys}{user}{user_is_trusted}, + }}); + + return(1); +} + =head2 validate_password This method takes a user name and password and checks to see if the password matches. @@ -254,7 +342,7 @@ sub validate_password my $query = " SELECT - user_password, + user_password_hash, user_salt, user_algorithm, user_hash_count @@ -278,16 +366,16 @@ WHERE $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0172", variables => { user => $user }}); return($valid); } - - my $user_password = $results->[0]->[0]; - my $user_salt = $results->[0]->[1]; - my $user_algorithm = $results->[0]->[2]; - my $user_hash_count = $results->[0]->[3]; + + my $user_password_hash = $results->[0]->[0]; + my $user_salt = $results->[0]->[1]; + my $user_algorithm = $results->[0]->[2]; + my $user_hash_count = $results->[0]->[3]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - user_password => $user_password, - user_salt => $user_salt, - user_algorithm => $user_algorithm, - user_hash_count => $user_hash_count, + user_password_hash => $user_password_hash, + user_salt => $user_salt, + user_algorithm => $user_algorithm, + user_hash_count => $user_hash_count, }}); if ($user_algorithm eq "sha256" ) @@ -343,7 +431,7 @@ WHERE } # Test. - if ($hash eq $user_password) + if ($hash eq $user_password_hash) { # Good password. $valid = 1; diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index c32b9742..6d55f511 100755 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -2528,19 +2528,19 @@ Is passed, the associated record will be updated. This is the user's name they type when logging into Striker. -=head3 user_password (required) +=head3 user_password_hash (required) This is either the B<< hash >> of the user's password, or the raw password. Which it is will be determined by whether C<< user_salt >> is passed in. If it is, C<< user_algorithm >> and C<< user_hash_count >> will also be required. If not, the password will be hashed (and a salt generated) using the default algorithm and hash count. -=head3 user_salt (optional, see 'user_password') +=head3 user_salt (optional, see 'user_password_hash') This is the random salt used to generate the password hash. -=head3 user_algorithm (optional, see 'user_password') +=head3 user_algorithm (optional, see 'user_password_hash') This is the algorithm used to create the password hash (with the salt appended to the password). -=head3 user_hash_count (optional, see 'user_password') +=head3 user_hash_count (optional, see 'user_password_hash') This is how many times the initial hash is re-encrypted. @@ -2572,7 +2572,7 @@ sub insert_or_update_users my $line = defined $parameter->{line} ? $parameter->{line} : ""; my $user_uuid = defined $parameter->{user_uuid} ? $parameter->{user_uuid} : ""; my $user_name = defined $parameter->{user_name} ? $parameter->{user_name} : ""; - my $user_password = defined $parameter->{user_password} ? $parameter->{user_password} : ""; + my $user_password_hash = defined $parameter->{user_password_hash} ? $parameter->{user_password_hash} : ""; my $user_salt = defined $parameter->{user_salt} ? $parameter->{user_salt} : ""; my $user_algorithm = defined $parameter->{user_algorithm} ? $parameter->{user_algorithm} : ""; my $user_hash_count = defined $parameter->{user_hash_count} ? $parameter->{user_hash_count} : ""; @@ -2586,7 +2586,7 @@ sub insert_or_update_users line => $line, user_uuid => $user_uuid, user_name => $user_name, - user_password => (($anvil->Log->secure) or ($user_salt)) ? $user_password : "--" , + user_password_hash => (($anvil->Log->secure) or ($user_salt)) ? $user_password_hash : "--" , user_salt => $user_salt, user_algorithm => $user_algorithm, user_hash_count => $user_hash_count, @@ -2602,10 +2602,10 @@ sub insert_or_update_users $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_users()", parameter => "user_name" }}); return(""); } - if (not $user_password) + if (not $user_password_hash) { # Throw an error and exit. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_users()", parameter => "user_password" }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Database->insert_or_update_users()", parameter => "user_password_hash" }}); return(""); } @@ -2630,17 +2630,23 @@ sub insert_or_update_users else { # No salt given, we'll generate a hash now. - my $answer = $anvil->Account->encrypt_password({password => $user_password}); - $user_password = $answer->{hash}; - $user_salt = $answer->{salt}; - $user_algorithm = $answer->{algorithm}; - $user_hash_count = $answer->{loops}; + my $answer = $anvil->Account->encrypt_password({password => $user_password_hash}); + $user_password_hash = $answer->{user_password_hash}; + $user_salt = $answer->{user_salt}; + $user_algorithm = $answer->{user_algorithm}; + $user_hash_count = $answer->{user_hash_count}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - user_password => (($anvil->Log->secure) or ($user_salt)) ? $user_password : "--" , - user_salt => $user_salt, - user_algorithm => $user_algorithm, - user_hash_count => $user_hash_count, + user_password_hash => (($anvil->Log->secure) or ($user_salt)) ? $user_password_hash : "--" , + user_salt => $user_salt, + user_algorithm => $user_algorithm, + user_hash_count => $user_hash_count, }}); + + if (not $user_salt) + { + # Something went wrong. + return(""); + } } # If we don't have a UUID, see if we can find one for the given user server name. @@ -2713,7 +2719,7 @@ INSERT INTO ( user_uuid, user_name, - user_password, + user_password_hash, user_salt, user_algorithm, user_hash_count, @@ -2725,7 +2731,7 @@ INSERT INTO ) VALUES ( ".$anvil->data->{sys}{use_db_fh}->quote($user_uuid).", ".$anvil->data->{sys}{use_db_fh}->quote($user_name).", - ".$anvil->data->{sys}{use_db_fh}->quote($user_password).", + ".$anvil->data->{sys}{use_db_fh}->quote($user_password_hash).", ".$anvil->data->{sys}{use_db_fh}->quote($user_salt).", ".$anvil->data->{sys}{use_db_fh}->quote($user_algorithm).", ".$anvil->data->{sys}{use_db_fh}->quote($user_hash_count).", @@ -2746,7 +2752,7 @@ INSERT INTO my $query = " SELECT user_name, - user_password, + user_password_hash, user_salt, user_algorithm, user_hash_count, @@ -2770,7 +2776,7 @@ WHERE foreach my $row (@{$results}) { my $old_user_name = $row->[0]; - my $old_user_password = $row->[1]; + my $old_user_password_hash = $row->[1]; my $old_user_salt = $row->[2]; my $old_user_algorithm = $row->[3]; my $old_user_hash_count = $row->[4]; @@ -2780,7 +2786,7 @@ WHERE my $old_user_is_trusted = $row->[8]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { old_user_name => $old_user_name, - old_user_password => $old_user_password, + old_user_password_hash => $old_user_password_hash, old_user_salt => $old_user_salt, old_user_algorithm => $old_user_algorithm, old_user_hash_count => $old_user_hash_count, @@ -2803,7 +2809,7 @@ WHERE # Anything change? if (($old_user_name ne $user_name) or ($old_user_name ne $user_name) or - ($old_user_password ne $user_password) or + ($old_user_password_hash ne $user_password_hash) or ($old_user_salt ne $user_salt) or ($old_user_algorithm ne $user_algorithm) or ($old_user_hash_count ne $user_hash_count) or @@ -2818,7 +2824,7 @@ UPDATE users SET user_name = ".$anvil->data->{sys}{use_db_fh}->quote($user_name).", - user_password = ".$anvil->data->{sys}{use_db_fh}->quote($user_password).", + user_password_hash = ".$anvil->data->{sys}{use_db_fh}->quote($user_password_hash).", user_salt = ".$anvil->data->{sys}{use_db_fh}->quote($user_salt).", user_algorithm = ".$anvil->data->{sys}{use_db_fh}->quote($user_algorithm).", user_hash_count = ".$anvil->data->{sys}{use_db_fh}->quote($user_hash_count).", diff --git a/cgi-bin/home b/cgi-bin/home index f5de8517..03ec9d04 100755 --- a/cgi-bin/home +++ b/cgi-bin/home @@ -177,7 +177,7 @@ sub check_availability my $count = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; $count = 0 if not defined $count; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'say::maintenance' => $anvil->data->{say}{maintenance} }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { count => $count }}); if ($count) { # We're waiting for the network configuration diff --git a/tools/anvil-change-password b/tools/anvil-change-password index 1c3f8932..b8833b4d 100755 --- a/tools/anvil-change-password +++ b/tools/anvil-change-password @@ -63,7 +63,7 @@ my $password = "Initial2"; # my $user_uuid = $anvil->Database->insert_or_update_users({ # debug => 2, # user_name => $user, -# user_password => $password, +# user_password_hash => $password, # user_salt => "", # user_algorithm => "", # user_hash_count => "", @@ -72,6 +72,7 @@ my $password = "Initial2"; # user_is_trusted => 1, # }); # print "User name: [".$user."], UUID: [".$user_uuid."]\n"; +# die; my $valid = $anvil->Account->validate_password({ debug => 2, user => $user, diff --git a/tools/anvil.sql b/tools/anvil.sql index e2a417ce..dbcde29f 100644 --- a/tools/anvil.sql +++ b/tools/anvil.sql @@ -43,7 +43,7 @@ $$; CREATE TABLE users ( user_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here. user_name text not null, - user_password text not null, -- A user without a password is disabled. + user_password_hash text not null, -- A user without a password is disabled. user_salt text not null, -- This is used to enhance the security of the user's password. user_algorithm text not null, -- This is the algorithm used to encrypt the password and salt. user_hash_count text not null, -- This is the number of times that the password+salt was re-hashed through the algorithm. @@ -59,7 +59,7 @@ CREATE TABLE history.users ( history_id bigserial, user_uuid uuid, user_name text, - user_password text, + user_password_hash text, user_salt text, user_algorithm text, user_hash_count text, @@ -80,7 +80,7 @@ BEGIN INSERT INTO history.users (user_uuid, user_name, - user_password, + user_password_hash, user_salt, user_algorithm, user_hash_count, @@ -92,7 +92,7 @@ BEGIN VALUES (history_users.user_uuid, history_users.user_name, - history_users.user_password, + history_users.user_password_hash, history_users.user_salt, history_users.user_algorithm, history_users.user_hash_count,