X-Git-Url: https://git.sesse.net/?p=pr0n;a=blobdiff_plain;f=perl%2FSesse%2Fpr0n%2FCommon.pm;h=8ea7fca1330bcb4be7faaa90909b8cd2841c5e8f;hp=a9ee2119db8751d3c97770f5cb6f7aa68f9864e0;hb=c41996944dd4d5b818e6b69150db1341d6483701;hpb=9b45ad1ecd9f1eace384f825a7dc6b2b7920af65 diff --git a/perl/Sesse/pr0n/Common.pm b/perl/Sesse/pr0n/Common.pm index a9ee211..8ea7fca 100644 --- a/perl/Sesse/pr0n/Common.pm +++ b/perl/Sesse/pr0n/Common.pm @@ -19,7 +19,7 @@ use DBD::Pg; use Image::Magick; use POSIX; use Digest::MD5; -use Digest::SHA1; +use Digest::SHA; use Digest::HMAC_SHA1; use MIME::Base64; use MIME::Types; @@ -29,6 +29,7 @@ use Image::ExifTool; use HTML::Entities; use URI::Escape; use File::Basename; +use Crypt::Eksblowfish::Bcrypt; BEGIN { use Exporter (); @@ -39,7 +40,7 @@ BEGIN { require Sesse::pr0n::Config_local; }; - $VERSION = "v2.72"; + $VERSION = "v2.81"; @ISA = qw(Exporter); @EXPORT = qw(&error &dberror); %EXPORT_TAGS = qw(); @@ -96,7 +97,7 @@ sub header { $quote = LWP::Simple::get("http://itk.samfundet.no/include/quotes.cli.php"); $quote = "Error: Could not fetch quotes." if (!defined($quote)); } - Sesse::pr0n::Templates::print_template($r, "header", { title => $title, quotes => Encode::decode_utf8($quote) }); + Sesse::pr0n::Templates::print_template($r, "header", { title => $title, quotes => $quote }); } sub footer { @@ -233,7 +234,7 @@ sub get_cache_location { sub ensure_disk_location_exists { my ($r, $id) = @_; - my $dir = POSIX::floor($id / 256); + my $dir = POSIX::floor($id / 256); my $img_dir = get_base($r) . "/images/$dir/"; if (! -d $img_dir) { @@ -308,6 +309,10 @@ sub update_image_info { # Tags my @tags = $exiftool->GetValue('Keywords', 'ValueConv'); + if (scalar @tags == 0) { + # This is XMP-dc:Subject, an RDF bag of tags. + @tags = $exiftool->GetValue('Subject', 'ValueConv'); + } $dbh->do('DELETE FROM tags WHERE image=?', undef, $id) or die "Couldn't delete old tag information in SQL: $!"; @@ -363,11 +368,11 @@ sub output_401 { # proxies etc. are being used), and we use HMAC instead of simple # hashing simply because that's a better signing method. # - # NOTE: For some weird reason, Digest::HMAC_SHA1 doesn't like taking + # NOTE: For some weird reason, Digest::HMAC_SHA doesn't like taking # the output from time directly (it gives a different response), so we # forcefully stringify the argument. my $ts = time; - my $nonce = Digest::HMAC_SHA1->hmac_sha1_hex($ts . "", $Sesse::pr0n::Config::db_password); + my $nonce = Digest::HMAC_SHA->hmac_sha1_hex($ts . "", $Sesse::pr0n::Config::db_password); my $stale_nonce_text = ""; $stale_nonce_text = ", stale=\"true\"" if ($options{'StaleNonce'} // 0); @@ -386,10 +391,18 @@ sub check_basic_auth { my ($raw_user, $pass) = split /:/, MIME::Base64::decode_base64($auth); my ($user, $takenby) = extract_takenby($raw_user); - - my $ref = $dbh->selectrow_hashref('SELECT sha1password,digest_ha1_hex FROM users WHERE username=? AND vhost=?', + + my $ref = $dbh->selectrow_hashref('SELECT sha1password,cryptpassword,digest_ha1_hex FROM users WHERE username=? AND vhost=?', undef, $user, $r->get_server_name); - if (!defined($ref) || $ref->{'sha1password'} ne Digest::SHA1::sha1_base64($pass)) { + my ($sha1_matches, $bcrypt_matches) = (0, 0); + if (defined($ref) && defined($ref->{'sha1password'})) { + $sha1_matches = (Digest::SHA::sha1_base64($pass) eq $ref->{'sha1password'}); + } + if (defined($ref) && defined($ref->{'cryptpassword'})) { + $bcrypt_matches = (Crypt::Eksblowfish::Bcrypt::bcrypt($pass, $ref->{'cryptpassword'}) eq $ref->{'cryptpassword'}); + } + + if (!defined($ref) || (!$sha1_matches && !$bcrypt_matches)) { $r->content_type('text/plain; charset=utf-8'); $r->log->warn("Authentication failed for $user/$takenby"); output_401($r); @@ -406,9 +419,39 @@ sub check_basic_auth { $r->log->info("Updated Digest auth hash for for $user"); } + # Make sure we can use bcrypt authentication in the future with this password. + # Also remove old-style SHA1 password when we migrate. + if (!$bcrypt_matches) { + my $salt = get_pseudorandom_bytes(16); # Doesn't need to be cryptographically secur. + my $hash = "\$2a\$07\$" . Crypt::Eksblowfish::Bcrypt::en_base64($salt); + my $cryptpassword = Crypt::Eksblowfish::Bcrypt::bcrypt($pass, $hash); + $dbh->do('UPDATE users SET sha1password=NULL,cryptpassword=? WHERE username=? AND vhost=?', + undef, $cryptpassword, $user, $r->get_server_name) + or die "Couldn't update: " . $dbh->errstr; + $r->log->info("Updated bcrypt hash for $user"); + } + return ($user, $takenby); } +sub get_pseudorandom_bytes { + my $num_left = shift; + my $bytes = ""; + open my $randfh, "<", "/dev/urandom" + or die "/dev/urandom: $!"; + binmode $randfh; + while ($num_left > 0) { + my $tmp; + if (sysread($randfh, $tmp, $num_left) == -1) { + die "sysread(/dev/urandom): $!"; + } + $bytes .= $tmp; + $num_left -= length($bytes); + } + close $randfh; + return $bytes; +} + sub check_digest_auth { my ($r, $auth) = @_; @@ -1117,8 +1160,6 @@ sub purge_cache { } $regex .= "(\\?.*)?\$"; $r->headers_out->{'X-Pr0n-Purge'} = $regex; - - $r->log->info($r->headers_out->{'X-Pr0n-Purge'}); } # Find a list of all cache URLs for a given image, given what we have on disk.