summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2014-08-29 11:50:41 +0200
committerYorhel <git@yorhel.nl>2014-08-29 11:50:41 +0200
commit1cf4252f2d313bc1e3f460a1d379a49b751d1170 (patch)
tree4385788e5553fe7376e4063961e8c7a9cbce7663 /lib
parent9a1bd46a568094ff62cfc85bc488b116042718b8 (diff)
API: Make sure to honor the new login throttle
Diffstat (limited to 'lib')
-rw-r--r--lib/Multi/API.pm36
-rw-r--r--lib/VNDB/Handler/Users.pm4
-rw-r--r--lib/VNDB/Util/Auth.pm2
-rw-r--r--lib/VNDB/Util/Misc.pm23
-rw-r--r--lib/VNDBUtil.pm21
5 files changed, 53 insertions, 33 deletions
diff --git a/lib/Multi/API.pm b/lib/Multi/API.pm
index 1e4700d9..3dfaa2c9 100644
--- a/lib/Multi/API.pm
+++ b/lib/Multi/API.pm
@@ -14,7 +14,7 @@ use POE::Filter::VNDBAPI 'encode_filters';
use Digest::SHA 'sha256_hex';
use Encode 'encode_utf8';
use Time::HiRes 'time'; # important for throttling
-use VNDBUtil 'normalize_query';
+use VNDBUtil 'normalize_query', 'norm_ip';
use JSON::XS;
@@ -40,7 +40,7 @@ sub spawn {
package_states => [
$p => [qw|
_start shutdown log server_error client_connect client_error client_input
- login login_res dbstats dbstats_res get_results get_vn get_vn_res
+ login login_throttle login_res dbstats dbstats_res get_results get_vn get_vn_res
get_release get_release_res get_producer get_producer_res get_character
get_character_res get_votelist get_votelist_res get_vnlist
get_vnlist_res get_wishlist get_wishlist_res set_votelist set_vnlist
@@ -420,9 +420,9 @@ sub login {
return cerr $c, badarg => 'Invalid client version', field => 'clientver' if $arg->{clientver} !~ /^[a-zA-Z0-9_.\/-]{1,25}$/;
if(exists $arg->{username}) {
- # fetch user info
- $_[KERNEL]->post(pg => query => "SELECT id, salt, encode(passwd, 'hex') as passwd FROM users WHERE username = ?",
- [ $arg->{username} ], 'login_res', [ $c, $arg ]);
+ # check login throttle
+ $_[KERNEL]->post(pg => query => "SELECT timeout FROM login_throttle WHERE ip = ?",
+ [ norm_ip($c->{ip}) ], 'login_throttle', [ $c, $arg ]);
} else {
$c->{client} = $arg->{client};
$c->{clientver} = $arg->{clientver};
@@ -432,14 +432,36 @@ sub login {
}
-sub login_res { # num, res, [ c, arg ]
+sub login_throttle {
my($num, $res, $c, $arg) = (@_[ARG0, ARG1], $_[ARG2][0], $_[ARG2][1]);
+ my $tm = @$res && $res->[0]{timeout} > time ? $res->[0]{timeout} : time;
+ $tm = int $tm;
+ return cerr $c, auth => "Too many failed login attempts"
+ if $tm-time() > $VNDB::S{login_throttle}[1];
+
+ # fetch user info
+ $_[KERNEL]->post(pg => query => "SELECT id, salt, encode(passwd, 'hex') as passwd FROM users WHERE username = ?",
+ [ $arg->{username} ], 'login_res', [ $c, $arg, $tm ]);
+}
+
+
+sub login_res { # num, res, [ c, arg, tm ]
+ my($num, $res, $c, $arg, $tm) = (@_[ARG0, ARG1], $_[ARG2][0], $_[ARG2][1], $_[ARG2][2]);
+
return cerr $c, auth => "No user with the name '$arg->{username}'" if $num == 0;
return cerr $c, auth => "Account disabled" if $res->[0]{salt} =~ /^ +$/;
my $encrypted = sha256_hex($VNDB::S{global_salt}.encode_utf8($arg->{password}).encode_utf8($res->[0]{salt}));
- return cerr $c, auth => "Wrong password for user '$arg->{username}'" if lc($encrypted) ne lc($res->[0]{passwd});
+
+ if(lc($encrypted) ne lc($res->[0]{passwd})) {
+ $tm += $VNDB::S{login_throttle}[0];
+ $_[KERNEL]->post(pg => do => 'UPDATE login_throttle SET timeout = ? WHERE ip = ?', [ $tm, $c->{ip} ]);
+ $_[KERNEL]->post(pg => do =>
+ 'INSERT INTO login_throttle (ip, timeout) SELECT ?, ? WHERE NOT EXISTS(SELECT 1 FROM login_throttle WHERE ip = ?)',
+ [ $c->{ip}, $tm, $c->{ip} ]);
+ return cerr $c, auth => "Wrong password for user '$arg->{username}'";
+ }
$c->{uid} = $res->[0]{id};
$c->{username} = $arg->{username};
diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm
index 78c4103d..680e50a0 100644
--- a/lib/VNDB/Handler/Users.pm
+++ b/lib/VNDB/Handler/Users.pm
@@ -140,7 +140,7 @@ sub login {
return $self->resRedirect('/') if $self->authInfo->{id};
- my $tm = $self->dbThrottleGet($self->normIP);
+ my $tm = $self->dbThrottleGet(norm_ip($self->reqIP));
if($tm-time() > $self->{login_throttle}[1]) {
$self->htmlHeader(title => mt '_login_title');
div class => 'mainbox';
@@ -167,7 +167,7 @@ sub login {
if(!$frm->{_err}) {
return if $self->authLogin($frm->{usrname}, $frm->{usrpass}, $ref);
$frm->{_err} = [ 'login_failed' ];
- $self->dbThrottleSet($self->normIP, $tm+$self->{login_throttle}[0]);
+ $self->dbThrottleSet(norm_ip($self->reqIP), $tm+$self->{login_throttle}[0]);
}
}
diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm
index a1fa9b4d..89cbd215 100644
--- a/lib/VNDB/Util/Auth.pm
+++ b/lib/VNDB/Util/Auth.pm
@@ -165,7 +165,7 @@ sub authGetCode {
my $self = shift;
my $id = shift;
my $time = (shift || time)/3600; # accuracy of an hour
- my $uid = encode_utf8($self->{_auth} ? $self->{_auth}{id} : $self->normIP());
+ my $uid = encode_utf8($self->{_auth} ? $self->{_auth}{id} : norm_ip($self->reqIP()));
return lc substr sha1_hex($self->{form_salt} . $uid . encode_utf8($id||'') . pack('N', int $time)), 0, 16;
}
diff --git a/lib/VNDB/Util/Misc.pm b/lib/VNDB/Util/Misc.pm
index 7ee0701b..b7503c24 100644
--- a/lib/VNDB/Util/Misc.pm
+++ b/lib/VNDB/Util/Misc.pm
@@ -6,9 +6,8 @@ use warnings;
use Exporter 'import';
use TUWF ':html';
use VNDB::Func;
-use Socket 'inet_pton', 'inet_ntop', 'AF_INET6';
-our @EXPORT = qw|filFetchDB ieCheck normIP|;
+our @EXPORT = qw|filFetchDB ieCheck|;
my %filfields = (
@@ -143,25 +142,5 @@ sub ieCheck {
return 0;
}
-
-# Normalized IP address to use for duplicate detection/throttling. For IPv4
-# this is just the normal address, but for IPv6 this is the /48 subnet, with
-# the rest of the address zero'd.
-sub normIP {
- my $s = shift;
- my $ip = $s->reqIP();
- return $ip if $ip !~ /:/;
-
- # There's a whole bunch of IPv6 manipulation modules on CPAN, but many seem
- # quite bloated and still don't offer the functionality to return an IP
- # with its mask applied (admittedly not a common operation). The libc
- # socket functions will do fine in parsing and formatting IPv6 addresses,
- # and the actual masking is quite trivial in binary form.
- $ip = inet_pton AF_INET6, $ip;
- return '::' if !$ip;
- $ip =~ s/^(.{6}).+$/$1 . "\0"x10/e;
- return inet_ntop AF_INET6, $ip;
-}
-
1;
diff --git a/lib/VNDBUtil.pm b/lib/VNDBUtil.pm
index e9df0a2c..febce0c0 100644
--- a/lib/VNDBUtil.pm
+++ b/lib/VNDBUtil.pm
@@ -7,8 +7,9 @@ use warnings;
use Exporter 'import';
use Encode 'encode_utf8';
use Unicode::Normalize 'NFKD';
+use Socket 'inet_pton', 'inet_ntop', 'AF_INET6';
-our @EXPORT = qw|shorten bb2html gtintype normalize normalize_titles normalize_query imgsize|;
+our @EXPORT = qw|shorten bb2html gtintype normalize normalize_titles normalize_query imgsize norm_ip|;
sub shorten {
@@ -244,5 +245,23 @@ sub imgsize {
}
+# Normalized IP address to use for duplicate detection/throttling. For IPv4
+# this is just the normal address, but for IPv6 this is the /48 subnet, with
+# the rest of the address zero'd.
+sub norm_ip {
+ my $ip = shift;
+ return $ip if $ip !~ /:/;
+
+ # There's a whole bunch of IPv6 manipulation modules on CPAN, but many seem
+ # quite bloated and still don't offer the functionality to return an IP
+ # with its mask applied (admittedly not a common operation). The libc
+ # socket functions will do fine in parsing and formatting IPv6 addresses,
+ # and the actual masking is quite trivial in binary form.
+ $ip = inet_pton AF_INET6, $ip;
+ return '::' if !$ip;
+ $ip =~ s/^(.{6}).+$/$1 . "\0"x10/e;
+ return inet_ntop AF_INET6, $ip;
+}
+
1;