3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-12-23 19:22:40 +01:00

Add weak/strong also-known-as linking

PBot will now use weak links if the ip address portion of a hostmask
hasn't been seen in the last 48 hours in order to prevent false-positive
linking of dynamic ip addresses.

Weak links are excluded from ban-evasion logic.

Weak links can be manually upgraded to strong links with the `akalink`
command if a human confirms they are the same person through the `aka -w`
command.

Individuals with matching nicks or matching nickserv accounts, etc, will
automatically be strongly linked, as usual.
This commit is contained in:
Pragmatic Software 2015-07-15 00:18:57 -07:00
parent 6a6eff1150
commit 850c8a0525
3 changed files with 106 additions and 52 deletions

View File

@ -601,6 +601,11 @@ sub check_bans {
foreach my $alias (keys %aliases) { foreach my $alias (keys %aliases) {
next if $alias =~ /^Guest\d+(?:!.*)?$/; next if $alias =~ /^Guest\d+(?:!.*)?$/;
if ($aliases{$alias}->{type} == $self->{messagehistory}->{alias_type}->{WEAK}) {
$self->{pbot}->{logger}->log("anti-flood: [check-bans] skipping WEAK alias $alias in channel $channel\n") if $debug_checkban >= 2;
next;
}
$self->{pbot}->{logger}->log("anti-flood: [check-bans] checking blacklist for $alias in channel $channel\n") if $debug_checkban >= 5; $self->{pbot}->{logger}->log("anti-flood: [check-bans] checking blacklist for $alias in channel $channel\n") if $debug_checkban >= 5;
if ($self->{pbot}->{blacklist}->check_blacklist($alias, $channel)) { if ($self->{pbot}->{blacklist}->check_blacklist($alias, $channel)) {
my $baninfo = {}; my $baninfo = {};

View File

@ -74,10 +74,12 @@ sub rebuild_aliases {
sub aka_link { sub aka_link {
my ($self, $from, $nick, $user, $host, $arguments) = @_; my ($self, $from, $nick, $user, $host, $arguments) = @_;
my ($id, $alias) = split /\s+/, $arguments; my ($id, $alias, $type) = split /\s+/, $arguments;
$type = $self->{messagehistory}->{alias_type}->{STRONG} if not defined $type;
if (not $id or not $alias) { if (not $id or not $alias) {
return "Usage: link <target id> <alias id>"; return "Usage: link <target id> <alias id> [type]";
} }
my $source = $self->{database}->find_most_recent_hostmask($id); my $source = $self->{database}->find_most_recent_hostmask($id);
@ -91,8 +93,8 @@ sub aka_link {
return "No such id $alias found."; return "No such id $alias found.";
} }
if ($self->{database}->link_alias($id, $alias)) { if ($self->{database}->link_alias($id, $alias, $type)) {
return "$source linked to $target."; return "$source " . ($type == $self->{messagehistory}->{alias_type}->{WEAK} ? "weakly" : "strongly") . " linked to $target.";
} else { } else {
return "Link failed."; return "Link failed.";
} }
@ -128,7 +130,7 @@ sub aka_unlink {
sub list_also_known_as { sub list_also_known_as {
my ($self, $from, $nick, $user, $host, $arguments) = @_; my ($self, $from, $nick, $user, $host, $arguments) = @_;
my $usage = "Usage: aka [-h] [-i] [-n] [-r] <nick>; -h show hostmasks; -i show ids; -n show nickserv accounts; -r show relationships"; my $usage = "Usage: aka [-h] [-i] [-n] [-r] [-w] <nick>; -h show hostmasks; -i show ids; -n show nickserv accounts; -r show relationships; -w show weak links";
if(not length $arguments) { if(not length $arguments) {
return $usage; return $usage;
@ -140,11 +142,12 @@ sub list_also_known_as {
chomp $getopt_error; chomp $getopt_error;
}; };
my ($show_hostmasks, $show_nickserv, $show_id, $show_relationship, $dont_use_aliases_table); my ($show_hostmasks, $show_nickserv, $show_id, $show_relationship, $show_weak, $dont_use_aliases_table);
my ($ret, $args) = GetOptionsFromString($arguments, my ($ret, $args) = GetOptionsFromString($arguments,
'h' => \$show_hostmasks, 'h' => \$show_hostmasks,
'n' => \$show_nickserv, 'n' => \$show_nickserv,
'r' => \$show_relationship, 'r' => \$show_relationship,
'w' => \$show_weak,
'nt' => \$dont_use_aliases_table, 'nt' => \$dont_use_aliases_table,
'i' => \$show_id); 'i' => \$show_id);
@ -161,6 +164,7 @@ sub list_also_known_as {
my $sep = ""; my $sep = "";
foreach my $aka (sort keys %akas) { foreach my $aka (sort keys %akas) {
next if $aka =~ /^Guest\d+(?:!.*)?$/; next if $aka =~ /^Guest\d+(?:!.*)?$/;
next if $akas{$aka}->{type} == $self->{messagehistory}->{alias_type}->{WEAK} && not $show_weak;
if (not $show_hostmasks) { if (not $show_hostmasks) {
my ($nick) = $aka =~ m/([^!]+)/; my ($nick) = $aka =~ m/([^!]+)/;
@ -182,6 +186,8 @@ sub list_also_known_as {
$result .= " [$akas{$aka}->{id}]"; $result .= " [$akas{$aka}->{id}]";
} }
$result .= " [WEAK]" if $akas{$aka}->{type} == $self->{messagehistory}->{alias_type}->{WEAK};
if ($show_hostmasks or $show_nickserv or $show_id or $show_relationship) { if ($show_hostmasks or $show_nickserv or $show_id or $show_relationship) {
$sep = ",\n"; $sep = ",\n";
} else { } else {

View File

@ -44,6 +44,9 @@ sub initialize {
$self->{pbot}->{registry}->get_value('messagehistory', 'sqlite_commit_interval'), $self->{pbot}->{registry}->get_value('messagehistory', 'sqlite_commit_interval'),
'messagehistory_sqlite_commit_interval' 'messagehistory_sqlite_commit_interval'
); );
$self->{alias_type}->{WEAK} = 0;
$self->{alias_type}->{STRONG} = 1;
} }
sub sqlite_commit_interval_trigger { sub sqlite_commit_interval_trigger {
@ -122,13 +125,14 @@ SQL
$self->{dbh}->do(<<SQL); $self->{dbh}->do(<<SQL);
CREATE TABLE IF NOT EXISTS Aliases ( CREATE TABLE IF NOT EXISTS Aliases (
id INTEGER, id INTEGER,
alias INTEGER alias INTEGER,
type INTEGER
) )
SQL SQL
$self->{dbh}->do('CREATE INDEX IF NOT EXISTS MsgIdx1 ON Messages(id, channel, mode)'); $self->{dbh}->do('CREATE INDEX IF NOT EXISTS MsgIdx1 ON Messages(id, channel, mode)');
$self->{dbh}->do('CREATE INDEX IF NOT EXISTS AliasIdx1 ON Aliases(id, alias)'); $self->{dbh}->do('CREATE INDEX IF NOT EXISTS AliasIdx1 ON Aliases(id, alias, type)');
$self->{dbh}->do('CREATE INDEX IF NOT EXISTS AliasIdx2 ON Aliases(alias, id)'); $self->{dbh}->do('CREATE INDEX IF NOT EXISTS AliasIdx2 ON Aliases(alias, id, type)');
$self->{dbh}->begin_work(); $self->{dbh}->begin_work();
}; };
@ -826,6 +830,24 @@ sub link_aliases {
my %ids; my %ids;
if ($hostmask) { if ($hostmask) {
my ($host) = $hostmask =~ /(\@.*)$/;
my $sth = $self->{dbh}->prepare('SELECT id, last_seen FROM Hostmasks WHERE hostmask LIKE ?');
$sth->bind_param(1, "\%$host");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
my $now = gettimeofday;
foreach my $row (@$rows) {
if ($now - $row->{last_seen} <= 60 * 60 * 48) {
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG} };
$self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for host [$host]\n") if $debug_link;
} else {
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{WEAK} };
$self->{pbot}->{logger}->log("found WEAK matching id $row->{id} for host [$host]\n") if $debug_link;
}
}
my ($nick) = $hostmask =~ m/([^!]+)/; my ($nick) = $hostmask =~ m/([^!]+)/;
unless ($nick =~ m/^Guest\d+$/) { unless ($nick =~ m/^Guest\d+$/) {
my $qnick = quotemeta $nick; my $qnick = quotemeta $nick;
@ -837,21 +859,10 @@ sub link_aliases {
my $rows = $sth->fetchall_arrayref({}); my $rows = $sth->fetchall_arrayref({});
foreach my $row (@$rows) { foreach my $row (@$rows) {
$ids{$row->{id}} = $row->{id}; $ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG} };
$self->{pbot}->{logger}->log("found matching id $row->{id} for nick [$qnick]\n") if $debug_link; $self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for nick [$qnick]\n") if $debug_link;
} }
} }
my ($host) = $hostmask =~ /(\@.*)$/;
my $sth = $self->{dbh}->prepare('SELECT id FROM Hostmasks WHERE hostmask LIKE ?');
$sth->bind_param(1, "\%$host");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
foreach my $row (@$rows) {
$ids{$row->{id}} = $row->{id};
$self->{pbot}->{logger}->log("found matching id $row->{id} for host [$host]\n") if $debug_link;
}
} }
if ($nickserv) { if ($nickserv) {
@ -861,22 +872,30 @@ sub link_aliases {
my $rows = $sth->fetchall_arrayref({}); my $rows = $sth->fetchall_arrayref({});
foreach my $row (@$rows) { foreach my $row (@$rows) {
$ids{$row->{id}} = $row->{id}; $ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG} };
$self->{pbot}->{logger}->log("found matching id $row->{id} for nickserv [$nickserv]\n") if $debug_link; $self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for nickserv [$nickserv]\n") if $debug_link;
} }
} }
my $sth = $self->{dbh}->prepare('INSERT INTO Aliases SELECT ?, ? WHERE NOT EXISTS (SELECT 1 FROM Aliases WHERE id = ? AND alias = ?)'); my $sth = $self->{dbh}->prepare('REPLACE INTO Aliases (id, alias, type) VALUES (?, ?, ?)');
$sth->bind_param(1, $account);
$sth->bind_param(3, $account);
foreach my $id (sort keys %ids) { foreach my $id (sort keys %ids) {
next if $account == $id; next if $account == $id;
$sth->bind_param(1, $account);
$sth->bind_param(2, $id); $sth->bind_param(2, $id);
$sth->bind_param(4, $id); $sth->bind_param(3, $ids{$id}->{type});
$sth->execute(); $sth->execute();
if ($sth->rows) { if ($sth->rows) {
$self->{pbot}->{logger}->log("Linked $account to $id\n") if $debug_link; $self->{pbot}->{logger}->log("Linked $account to $id [$ids{$id}->{type}]\n") if $debug_link;
$self->{new_entries}++;
}
$sth->bind_param(1, $id);
$sth->bind_param(2, $account);
$sth->bind_param(3, $ids{$id}->{type});
$sth->execute();
if ($sth->rows) {
$self->{pbot}->{logger}->log("Linked $id to $account [$ids{$id}->{type}]\n") if $debug_link;
$self->{new_entries}++; $self->{new_entries}++;
} }
} }
@ -885,31 +904,55 @@ sub link_aliases {
} }
sub link_alias { sub link_alias {
my ($self, $id, $alias) = @_; my ($self, $id, $alias, $type) = @_;
my $ret = eval { my $ret = eval {
my $ret = 0; my $ret = 0;
my $sth = $self->{dbh}->prepare('INSERT INTO Aliases SELECT ?, ? WHERE NOT EXISTS (SELECT 1 FROM Aliases WHERE id = ? AND alias = ?)');
my $sth = $self->{dbh}->prepare('INSERT INTO Aliases SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM Aliases WHERE id = ? AND alias = ?)');
$sth->bind_param(1, $alias); $sth->bind_param(1, $alias);
$sth->bind_param(2, $id); $sth->bind_param(2, $id);
$sth->bind_param(3, $alias); $sth->bind_param(3, $type);
$sth->bind_param(4, $id);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
}
$sth->bind_param(1, $id);
$sth->bind_param(2, $alias);
$sth->bind_param(3, $id);
$sth->bind_param(4, $alias); $sth->bind_param(4, $alias);
$sth->bind_param(5, $id);
$sth->execute(); $sth->execute();
if ($sth->rows) { if ($sth->rows) {
$self->{new_entries}++; $self->{new_entries}++;
$ret = 1; $ret = 1;
} else { } else {
$ret = 0; $sth = $self->{dbh}->prepare('UPDATE Aliases SET type = ? WHERE id = ? AND alias = ?');
$sth->bind_param(1, $type);
$sth->bind_param(2, $id);
$sth->bind_param(3, $alias);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
}
}
$sth = $self->{dbh}->prepare('INSERT INTO Aliases SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM Aliases WHERE id = ? AND alias = ?)');
$sth->bind_param(1, $id);
$sth->bind_param(2, $alias);
$sth->bind_param(3, $type);
$sth->bind_param(4, $id);
$sth->bind_param(5, $alias);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
} else {
$sth = $self->{dbh}->prepare('UPDATE Aliases SET type = ? WHERE id = ? AND alias = ?');
$sth->bind_param(1, $type);
$sth->bind_param(2, $alias);
$sth->bind_param(3, $id);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
} else {
$ret = 0;
}
} }
return $ret; return $ret;
}; };
@ -1008,22 +1051,22 @@ sub get_also_known_as {
return %akas; return %akas;
} }
$ids{$id} = $id; $ids{$id} = { id => $id, type => $self->{alias_type}->{STRONG} };
$self->{pbot}->{logger}->log("Adding $id -> $id\n") if $debug; $self->{pbot}->{logger}->log("Adding $id -> $id\n") if $debug;
my $sth = $self->{dbh}->prepare('SELECT alias FROM Aliases WHERE id = ?'); my $sth = $self->{dbh}->prepare('SELECT alias, type FROM Aliases WHERE id = ?');
$sth->bind_param(1, $id); $sth->bind_param(1, $id);
$sth->execute(); $sth->execute();
my $rows = $sth->fetchall_arrayref({}); my $rows = $sth->fetchall_arrayref({});
foreach my $row (@$rows) { foreach my $row (@$rows) {
$ids{$row->{alias}} = $id; $ids{$row->{alias}} = { id => $id, type => $row->{type} };
$self->{pbot}->{logger}->log("[$id] Adding $row->{alias} -> $id\n") if $debug; $self->{pbot}->{logger}->log("[$id] Adding $row->{alias} -> $id [type $row->{type}]\n") if $debug;
} }
my %seen_id; my %seen_id;
$sth = $self->{dbh}->prepare('SELECT id FROM Aliases WHERE alias = ?'); $sth = $self->{dbh}->prepare('SELECT id, type FROM Aliases WHERE alias = ?');
while (1) { while (1) {
my $new_aliases = 0; my $new_aliases = 0;
@ -1037,9 +1080,9 @@ sub get_also_known_as {
foreach my $row (@$rows) { foreach my $row (@$rows) {
next if exists $ids{$row->{id}}; next if exists $ids{$row->{id}};
$ids{$row->{id}} = $id; $ids{$row->{id}} = { id => $id, type => $row->{type} };
$new_aliases++; $new_aliases++;
$self->{pbot}->{logger}->log("[$id] Adding $row->{id} -> $id\n") if $debug; $self->{pbot}->{logger}->log("[$id] Adding $row->{id} -> $id [type $row->{type}]\n") if $debug;
} }
} }
last if not $new_aliases; last if not $new_aliases;
@ -1054,8 +1097,8 @@ sub get_also_known_as {
$rows = $hostmask_sth->fetchall_arrayref({}); $rows = $hostmask_sth->fetchall_arrayref({});
foreach my $row (@$rows) { foreach my $row (@$rows) {
$akas{$row->{hostmask}} = { hostmask => $row->{hostmask}, id => $id, alias => $ids{$id} }; $akas{$row->{hostmask}} = { hostmask => $row->{hostmask}, id => $id, alias => $ids{$id}->{id}, type => $ids{$id}->{type} };
$self->{pbot}->{logger}->log("[$id] Adding hostmask $row->{hostmask} -> $ids{$id}\n") if $debug; $self->{pbot}->{logger}->log("[$id] Adding hostmask $row->{hostmask} -> $ids{$id}->{id} [type $ids{$id}->{type}]\n") if $debug;
} }
$nickserv_sth->bind_param(1, $id); $nickserv_sth->bind_param(1, $id);