#!/usr/bin/env perl use strict; use warnings; #man{{{ =head1 NAME tails-restricted-network-detector =head1 VERSION Version X.XX =head1 AUTHOR Tails dev team See https://tails.boum.org/. =cut #}}} use File::Tail; use Parse::Syslog; use IPC::System::Simple qw(runx); use Locale::gettext; use I18N::Langinfo qw{langinfo CODESET}; use Encode qw{decode find_encoding}; use POSIX; setlocale(LC_MESSAGES, ""); textdomain("tails"); sub notify_maybe_blocked { my $encoding = find_encoding(langinfo(CODESET())); my $summary = $encoding->decode(gettext('Network connection blocked?')); my $body = $encoding->decode(gettext( 'It looks like you are blocked from the network. This may be ' . 'related to the MAC spoofing feature. For more information, see the ' . 'MAC spoofing ' . 'documentation.')); # We can't use Desktop::Notify since this script is supposed to be run # as root (for access to syslog), started in an env without DESKTOP etc, # which also causes issues with opening links in the text body. # All this works fine with tails-notify-user. runx('/usr/local/sbin/tails-notify-user', ($summary, $body, '30000')); } my %state; my $syslog = File::Tail->new(name => "/var/log/syslog", maxinterval => 1, interval => 1); my $parser = Parse::Syslog->new($syslog, allow_future => 1); while(my $sl = $parser->next) { next if !($sl->{program} eq "NetworkManager"); my $text = $sl->{text}; if ($text =~ /Activation \(([^)]+)\) starting connection/) { # The beginning of *all* (not only wireless) new # connections. We drop any previous state so it won't # interfere. $state{$1} = ""; } elsif ($text =~ /\(([^)]+)\): supplicant (?:connection|interface) state: \S+ -> (\S+)/ || $text =~ /\(([^)]+)\): device state change: \S+ -> (\S+)/) { # NetworkManager logs state transitions with the above # messages, but the really important part is that we # accurately log the state changes *to* and *from* # "associating" (for the next case). Hence the safest bet # seems to be to deal with all observed types of transitions # that NetworkManager logs. $state{$1} = $2; } elsif ($text =~ /Activation \(([^)]+)\/[^)]*\): association took too long/) { # Wireless connection failure. If it happens during # "associating" it *may* indicate that the AP is blocking the # MAC address in use. if ($state{$1} eq "associating") { notify_maybe_blocked(); } } }