Commit 354ea98b authored by anonym's avatar anonym
Browse files

Merge remote-tracking branch 'origin/devel' into bugfix/10333-tails-debugging-info-leak

parents ebd8fc40 dfa1d43d
......@@ -128,7 +128,7 @@ if [ -n "$JENKINS_URL" ] && [ -z "$GIT_TAG" ] \
fi
# build the doc wiki
./build-wiki
./build-website
# refresh translations of our programs
./refresh-translations || fatal "refresh-translations failed ($?)."
......
......@@ -66,6 +66,10 @@ Package: poedit
Pin: release o=Debian Backports,n=wheezy-backports
Pin-Priority: 999
Package: firmware-amd-graphics
Pin: release o=Debian,a=unstable
Pin-Priority: 999
Package: firmware-atheros
Pin: release o=Debian,a=unstable
Pin-Priority: 999
......@@ -106,7 +110,7 @@ Package: firmware-linux-nonfree
Pin: release o=Debian,a=unstable
Pin-Priority: 999
Package: firmware-ralink
Package: firmware-misc-nonfree
Pin: release o=Debian,a=unstable
Pin-Priority: 999
......
......@@ -21,6 +21,14 @@ nic_is_up() {
# The following "nic"-related functions require that the argument is a
# NIC that exists
nic_ipv4_addr() {
ip addr show "${1}" | sed -n 's,^\s*inet \([0-9\.]\+\)/.*$,\1,p'
}
nic_ipv6_addr() {
ip addr show "${1}" | sed -n 's,^\s*inet6 \([0-9a-fA-F:]\+\)/.*$,\1,p'
}
# Will just output nothing on failure
get_current_mac_of_nic() {
local mac
......@@ -74,5 +82,7 @@ mod_rev_dep() {
# Unloads module $1, and all modules that (transatively) depends on
# $1 (i.e. its reverse dependencies).
unload_module_and_rev_deps() {
/sbin/modprobe -r $(mod_rev_dep ${1})
for mod in $(mod_rev_dep ${1}); do
/sbin/rmmod ${mod}
done
}
......@@ -34,6 +34,11 @@ show_notification() {
pgrep --full /usr/lib/notification-daemon/notification-daemon >/dev/null; do
sleep 1
done
# The above doesn't seem to be enough. The best we can do seems to
# be to statically wait a bit longer. The amount chosen is just
# arbitrarily picked, and may not work on slow hardware or even
# DVD boot, but should at least work in our automated test suite.
sleep 10
/usr/local/sbin/tails-notify-user "${1}" "${2}" 0
}
......
......@@ -250,6 +250,7 @@ xserver-xorg-input-vmmouse
#endif
### Firmwares
firmware-amd-graphics
firmware-atheros
firmware-brcm80211
firmware-ipw2x00
......@@ -258,7 +259,7 @@ firmware-libertas
firmware-linux
firmware-linux-free
firmware-linux-nonfree
firmware-ralink
firmware-misc-nonfree
firmware-realtek
firmware-zd1211
b43-fwcutter
......
......@@ -21,6 +21,7 @@ Feature: Various checks
When Tails has booted a 64-bit kernel
Then the VirtualBox guest modules are available
@fragile
Scenario: The shipped Tails OpenPGP keys are up-to-date
Given I have started Tails from DVD without network and logged in
Then the OpenPGP keys shipped with Tails will be valid for the next 3 months
......
@product
Feature: Spoofing MAC addresses
In order to not reveal information about the physical location
As a Tails user
I want to be able to control whether my network devices MAC addresses should be spoofed
And I want this feature to fail safe and notify me in case of errors
Background:
Given I have started Tails from DVD without network and stopped at Tails Greeter's login screen
And I capture all network traffic
And the network is plugged
Scenario: MAC address spoofing is disabled
When I enable more Tails Greeter options
And I disable MAC spoofing in Tails Greeter
And I log in to a new session
And the Tails desktop is ready
And Tor is ready
Then 1 network interface is enabled
And the network device has its default MAC address configured
And the real MAC address was leaked
Scenario: MAC address spoofing is successful
When I log in to a new session
And the Tails desktop is ready
And Tor is ready
Then 1 network interface is enabled
And the network device has a spoofed MAC address configured
And the real MAC address was not leaked
Scenario: MAC address spoofing fails and macchanger returns false
Given macchanger will fail by not spoofing and always returns false
When I log in to a new session
And see the "Network card disabled" notification
And the Tails desktop is ready
Then no network interfaces are enabled
And the real MAC address was not leaked
Scenario: MAC address spoofing fails and macchanger returns true
Given macchanger will fail by not spoofing and always returns true
When I log in to a new session
And see the "Network card disabled" notification
And the Tails desktop is ready
Then no network interfaces are enabled
And the real MAC address was not leaked
Scenario: MAC address spoofing fails and the module is not removed
Given MAC spoofing will fail, and the module cannot be unloaded
When I log in to a new session
And see the "All networking disabled" notification
And the Tails desktop is ready
Then 1 network interface is enabled
But the MAC spoofing panic mode disabled networking
And the real MAC address was not leaked
Scenario: MAC address spoofing causes a network failure
Given the network is unplugged
When I log in to a new session
Then the Tails desktop is ready
Given a wireless NIC's MAC address is blocked by the network
Then I see the "Network connection blocked?" notification
Scenario: The MAC address is not leaked when booting Tails
Given a computer
And I capture all network traffic
When I start the computer
Then the computer boots Tails
And no network interfaces are enabled
And the real MAC address was not leaked
......@@ -51,7 +51,7 @@ Feature: Chatting anonymously using Pidgin
When I say something to my friend
Then I receive a response from my friend
@check_tor_leaks
@check_tor_leaks @fragile
Scenario: Connecting to the #tails IRC channel with the pre-configured account
Given I have started Tails from DVD and logged in and the network is connected
And Pidgin has the expected accounts configured with random nicknames
......
......@@ -14,7 +14,7 @@ end
When /^I update APT using apt-get$/ do
Timeout::timeout(30*60) do
$vm.execute_successfully("echo #{@sudo_password} | " +
"sudo -S apt-get update", LIVE_USER)
"sudo -S apt-get update", :user => LIVE_USER)
end
end
......@@ -22,7 +22,8 @@ Then /^I should be able to install a package using apt-get$/ do
package = "cowsay"
Timeout::timeout(120) do
$vm.execute_successfully("echo #{@sudo_password} | " +
"sudo -S apt-get install #{package}", LIVE_USER)
"sudo -S apt-get install #{package}",
:user => LIVE_USER)
end
step "package \"#{package}\" is installed"
end
......
def shipped_openpgp_keys
shipped_gpg_keys = $vm.execute_successfully('gpg --batch --with-colons --fingerprint --list-key', LIVE_USER).stdout
shipped_gpg_keys = $vm.execute_successfully('gpg --batch --with-colons --fingerprint --list-key', :user => LIVE_USER).stdout
openpgp_fingerprints = shipped_gpg_keys.scan(/^fpr:::::::::([A-Z0-9]+):$/).flatten
return openpgp_fingerprints
end
......@@ -26,7 +26,7 @@ Then /^the shipped (?:Debian repository key|OpenPGP key ([A-Z0-9]+)) will be val
cmd = 'apt-key adv'
user = 'root'
end
shipped_sig_key_info = $vm.execute_successfully("#{cmd} --batch --list-key #{fingerprint}", user).stdout
shipped_sig_key_info = $vm.execute_successfully("#{cmd} --batch --list-key #{fingerprint}", :user => user).stdout
m = /\[expire[ds]: ([0-9-]*)\]/.match(shipped_sig_key_info)
if m
expiration_date = Date.parse(m[1])
......@@ -105,7 +105,8 @@ Then /^GNOME Screenshot is configured to save files to the live user's home dire
home = "/home/#{LIVE_USER}"
save_path = $vm.execute_successfully(
"gsettings get org.gnome.gnome-screenshot auto-save-directory",
LIVE_USER).stdout.chomp.tr("'","")
:user => LIVE_USER
).stdout.chomp.tr("'","")
assert_equal("file://#{home}", save_path,
"The GNOME screenshot auto-save-directory is not set correctly.")
end
......@@ -149,12 +150,12 @@ Then /^MAT can clean some sample PDF file$/ do
pdf_on_guest = "/home/#{LIVE_USER}/#{pdf_name}"
step "I copy \"#{@shared_pdf_dir_on_guest}/#{pdf_name}\" to \"#{pdf_on_guest}\" as user \"#{LIVE_USER}\""
check_before = $vm.execute_successfully("mat --check '#{pdf_on_guest}'",
LIVE_USER).stdout
:user => LIVE_USER).stdout
assert(check_before.include?("#{pdf_on_guest} is not clean"),
"MAT failed to see that '#{pdf_on_host}' is dirty")
$vm.execute_successfully("mat '#{pdf_on_guest}'", LIVE_USER)
$vm.execute_successfully("mat '#{pdf_on_guest}'", :user => LIVE_USER)
check_after = $vm.execute_successfully("mat --check '#{pdf_on_guest}'",
LIVE_USER).stdout
:user => LIVE_USER).stdout
assert(check_after.include?("#{pdf_on_guest} is clean"),
"MAT failed to clean '#{pdf_on_host}'")
$vm.execute_successfully("rm '#{pdf_on_guest}'")
......
......@@ -37,8 +37,8 @@ def deactivate_filesystem_shares
#end
end
def notification_helper(notification_image, time_to_wait)
# notifiction-daemon may abort during start-up, causing the tests that look for
def notification_popup_wait(notification_image, time_to_wait)
# notification-daemon may abort during start-up, causing the tests that look for
# desktop notifications to fail (ticket #8686)
begin
@screen.wait(notification_image, time_to_wait)
......@@ -48,6 +48,41 @@ def notification_helper(notification_image, time_to_wait)
end
end
# This helper requires that the notification image is the one shown in
# the notification applet's list, not the notification pop-up.
def robust_notification_wait(notification_image, time_to_wait)
error_msg = "Didn't not see notification '#{notification_image}'"
try_for(time_to_wait, :delay => 0, :msg => error_msg) do
@screen.hide_cursor
@screen.click('GnomeNotificationApplet.png')
# Sanity check that the applet's list of notifications were
# opened. Sometimes the applet is moved when other systray
# elements are added, causing a race between Sikuli's mouse
# movement and the appearance of the new element.
@screen.wait('GnomeNotificationAppletClearAllButton.png', 5)
begin
return @screen.find(notification_image)
rescue FindFailed => e
# It could be that too many notifications are in the list, so
# the one we're looking for is not visible. Let's clear one
# notification and retry by re-raising the exception we just
# caught. This should not interfere with anything except if we
# later are interested in any of these older notifications that
# we may close.
# It's worth noting that the "close button" picture below has
# carefully been sized to include more than just the close
# button, so much that it ensures that the notification header
# must also be shown. That way we won't close any half-seen
# notification that may be the one we're looking for.
@screen.click('GnomeNotificationAppletCloseButton.png')
raise e
end
end
rescue Timeout::Error => e
step 'process "notification-daemon" is running'
raise e
end
def post_snapshot_restore_hook
$vm.wait_until_remote_shell_is_up
post_vm_start_hook
......@@ -292,7 +327,7 @@ Then /^Tails seems to have booted normally$/ do
end
When /^I see the 'Tor is ready' notification$/ do
notification_helper('GnomeTorIsReady.png', 300)
notification_popup_wait('GnomeTorIsReady.png', 300)
@screen.waitVanish("GnomeTorIsReady.png", 15)
end
......@@ -499,9 +534,8 @@ end
def xul_application_info(application)
binary = $vm.execute_successfully(
'. /usr/local/lib/tails-shell-library/tor-browser.sh; ' +
'echo ${TBB_INSTALL}/firefox'
).stdout.chomp
'echo ${TBB_INSTALL}/firefox', :libs => 'tor-browser'
).stdout.chomp
case application
when "Tor Browser"
user = LIVE_USER
......@@ -576,9 +610,8 @@ def xul_app_shared_lib_check(pid, chroot)
absent_tbb_libs = []
unwanted_native_libs = []
tbb_libs = $vm.execute_successfully(
". /usr/local/lib/tails-shell-library/tor-browser.sh; " +
"ls -1 #{chroot}${TBB_INSTALL}/*.so"
).stdout.split
"ls -1 #{chroot}${TBB_INSTALL}/*.so", :libs => 'tor-browser'
).stdout.split
firefox_pmap_info = $vm.execute("pmap #{pid}").stdout
for lib in tbb_libs do
lib_name = File.basename lib
......@@ -698,7 +731,7 @@ When /^the directory "([^"]+)" does not exist$/ do |directory|
end
When /^I copy "([^"]+)" to "([^"]+)" as user "([^"]+)"$/ do |source, destination, user|
c = $vm.execute("cp \"#{source}\" \"#{destination}\"", LIVE_USER)
c = $vm.execute("cp \"#{source}\" \"#{destination}\"", :user => LIVE_USER)
assert(c.success?, "Failed to copy file:\n#{c.stdout}\n#{c.stderr}")
end
......@@ -818,7 +851,7 @@ Then /^there is no GNOME bookmark for the persistent Tor Browser directory$/ do
end
def pulseaudio_sink_inputs
pa_info = $vm.execute_successfully('pacmd info', LIVE_USER).stdout
pa_info = $vm.execute_successfully('pacmd info', :user => LIVE_USER).stdout
sink_inputs_line = pa_info.match(/^\d+ sink input\(s\) available\.$/)[0]
return sink_inputs_line.match(/^\d+/)[0].to_i
end
......@@ -947,7 +980,7 @@ EOF
# accessing this server matters, like when testing the Tor Browser..
try_for(30, :msg => "Something is wrong with the LAN web server") do
msg = $vm.execute_successfully("curl #{@web_server_url}",
LIVE_USER).stdout.chomp
:user => LIVE_USER).stdout.chomp
web_server_hello_msg == msg
end
end
......@@ -985,7 +1018,7 @@ def force_new_tor_circuit(with_vidalia=nil)
@screen.wait('VidaliaNewIdentityNotification.png', 20)
@screen.waitVanish('VidaliaNewIdentityNotification.png', 60)
else
$vm.execute_successfully('. /usr/local/lib/tails-shell-library/tor.sh; tor_control_send "signal NEWNYM"')
$vm.execute_successfully('tor_control_send "signal NEWNYM"', :libs => 'tor')
end
end
......
......@@ -24,9 +24,10 @@ Given /^I generate an OpenPGP key named "([^"]+)" with password "([^"]+)"$/ do |
%commit
EOF
gpg_key_recipie.split("\n").each do |line|
$vm.execute("echo '#{line}' >> /tmp/gpg_key_recipie", LIVE_USER)
$vm.execute("echo '#{line}' >> /tmp/gpg_key_recipie", :user => LIVE_USER)
end
c = $vm.execute("gpg --batch --gen-key < /tmp/gpg_key_recipie", LIVE_USER)
c = $vm.execute("gpg --batch --gen-key < /tmp/gpg_key_recipie",
:user => LIVE_USER)
assert(c.success?, "Failed to generate OpenPGP key:\n#{c.stderr}")
end
......
......@@ -122,7 +122,7 @@ Given /^I fill the guest's memory with a known pattern(| without verifying)$/ do
# unnecessarily.
instances = (@detected_ram_m.to_f/(2**10)).ceil
instances.times do
$vm.spawn('/usr/local/sbin/fillram; killall fillram', LIVE_USER)
$vm.spawn('/usr/local/sbin/fillram; killall fillram', :user => LIVE_USER)
end
# We make sure that all fillram processes have started...
try_for(10, :msg => "all fillram processes didn't start", :delay => 0.1) do
......
......@@ -40,17 +40,17 @@ Given(/^I disable Tails' firewall$/) do
end
When(/^I do a TCP DNS lookup of "(.*?)"$/) do |host|
lookup = $vm.execute("host -T #{host} #{SOME_DNS_SERVER}", LIVE_USER)
lookup = $vm.execute("host -T #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
assert(lookup.success?, "Failed to resolve #{host}:\n#{lookup.stdout}")
end
When(/^I do a UDP DNS lookup of "(.*?)"$/) do |host|
lookup = $vm.execute("host #{host} #{SOME_DNS_SERVER}", LIVE_USER)
lookup = $vm.execute("host #{host} #{SOME_DNS_SERVER}", :user => LIVE_USER)
assert(lookup.success?, "Failed to resolve #{host}:\n#{lookup.stdout}")
end
When(/^I send some ICMP pings$/) do
# We ping an IP address to avoid a DNS lookup
ping = $vm.execute("ping -c 5 #{SOME_DNS_SERVER}", LIVE_USER)
ping = $vm.execute("ping -c 5 #{SOME_DNS_SERVER}", :user => LIVE_USER)
assert(ping.success?, "Failed to ping #{SOME_DNS_SERVER}:\n#{ping.stderr}")
end
Then /^the Git repository "([\S]+)" has been cloned successfully$/ do |repo|
assert($vm.directory_exist?("/home/#{LIVE_USER}/#{repo}/.git"))
assert($vm.file_exist?("/home/#{LIVE_USER}/#{repo}/.git/config"))
$vm.execute_successfully("cd '/home/#{LIVE_USER}/#{repo}/' && git status", LIVE_USER)
$vm.execute_successfully("cd '/home/#{LIVE_USER}/#{repo}/' && git status",
:user => LIVE_USER)
end
......@@ -6,8 +6,7 @@ end
Given /^the I2P router console is ready$/ do
try_for(120) do
$vm.execute('. /usr/local/lib/tails-shell-library/i2p.sh; ' +
'i2p_router_console_is_ready').success?
$vm.execute('i2p_router_console_is_ready', :libs => 'i2p').success?
end
end
......
def all_ethernet_nics
$vm.execute_successfully(
"get_all_ethernet_nics", :libs => 'hardware'
).stdout.split
end
When /^I disable MAC spoofing in Tails Greeter$/ do
@screen.wait_and_click("TailsGreeterMACSpoofing.png", 30)
end
Then /^the network device has (its default|a spoofed) MAC address configured$/ do |mode|
is_spoofed = (mode == "a spoofed")
nic = "eth0"
assert_equal([nic], all_ethernet_nics,
"We only expected NIC #{nic} but these are present: " +
all_ethernet_nics.join(", "))
nic_real_mac = $vm.real_mac
nic_current_mac = $vm.execute_successfully(
"get_current_mac_of_nic #{nic}", :libs => 'hardware'
).stdout.chomp
if is_spoofed
if nic_real_mac == nic_current_mac
save_pcap_file
raise "The MAC address was expected to be spoofed but wasn't"
end
else
if nic_real_mac != nic_current_mac
save_pcap_file
raise "The MAC address is spoofed but was expected to not be"
end
end
end
Then /^the real MAC address was (not )?leaked$/ do |mode|
is_leaking = mode.nil?
leaks = FirewallLeakCheck.new(@sniffer.pcap_file)
mac_leaks = leaks.mac_leaks
if is_leaking
if !mac_leaks.include?($vm.real_mac)
save_pcap_file
raise "The real MAC address was expected to leak but didn't. We " +
"observed the following MAC addresses: #{mac_leaks}"
end
else
if mac_leaks.include?($vm.real_mac)
save_pcap_file
raise "The real MAC address was leaked but was expected not to. We " +
"observed the following MAC addresses: #{mac_leaks}"
end
end
end
Given /^macchanger will fail by not spoofing and always returns ([\S]+)$/ do |mode|
$vm.execute_successfully("mv /usr/bin/macchanger /usr/bin/macchanger.orig")
$vm.execute_successfully("ln -s /bin/#{mode} /usr/bin/macchanger")
end
Given /^MAC spoofing will fail, and the module cannot be unloaded$/ do
step "macchanger will fail by not spoofing and always returns true"
$vm.execute_successfully("mv /sbin/rmmod /sbin/rmmod.orig")
$vm.execute_successfully("ln -s /bin/false /sbin/rmmod")
end
When /^see the "Network card disabled" notification$/ do
robust_notification_wait("MACSpoofNetworkCardDisabled.png", 60)
end
When /^see the "All networking disabled" notification$/ do
robust_notification_wait("MACSpoofNetworkingDisabled.png", 60)
end
Then /^I see the "Network connection blocked\?" notification$/ do
robust_notification_wait("MACSpoofNetworkBlocked.png", 60)
end
Then /^(\d+|no) network interface(?:s)? (?:is|are) enabled$/ do |expected_nr_nics|
# note that "no".to_i => 0 in Ruby.
expected_nr_nics = expected_nr_nics.to_i
nr_nics = all_ethernet_nics.size
assert_equal(expected_nr_nics, nr_nics)
end
Then /^the MAC spoofing panic mode disabled networking$/ do
nm_is_disabled = not($vm.file_exist?("/etc/init.d/network-manager")) &&
not($vm.file_exist?("/usr/sbin/NetworkManager"))
assert(nm_is_disabled, "NetworkManager was not disabled")
all_ethernet_nics.each do |nic|
["nic_ipv4_addr", "nic_ipv6_addr"].each do |function|
addr = $vm.execute_successfully(
"#{function} #{nic}", :libs => 'hardware'
).stdout.chomp
assert_equal("", addr, "NIC #{nic} was assigned address #{addr}")
end
end
end
Given /^a wireless NIC's MAC address is blocked by the network$/ do
device = 'wlan0'
test_ssid = 'test-ssid'
# The below log was recorded from Tails based on Debian Wheezy. We
# should update it and this comment whenever we rebase Tails on a
# different version of Debian, or install a new version of
# NetworkManager.
network_manager_info_log_entries = <<-EOF
Activation (#{device}) starting connection '#{test_ssid}'
(#{device}): device state change: disconnected -> prepare (reason 'none') [30 40 0]
Activation (#{device}) Stage 1 of 5 (Device Prepare) scheduled...
Activation (#{device}) Stage 1 of 5 (Device Prepare) started...
Activation (#{device}) Stage 2 of 5 (Device Configure) scheduled...
Activation (#{device}) Stage 1 of 5 (Device Prepare) complete.
Activation (#{device}) Stage 2 of 5 (Device Configure) starting...
(#{device}): device state change: prepare -> config (reason 'none') [40 50 0]
Activation (#{device}/wireless): access point '#{test_ssid}' has security, but secrets are required.
(#{device}): device state change: config -> need-auth (reason 'none') [50 60 0]
Activation (#{device}) Stage 2 of 5 (Device Configure) complete.
get_secret_flags: assertion `is_secret_prop (setting, secret_name, error)' failed
Activation (#{device}) Stage 1 of 5 (Device Prepare) scheduled...
Activation (#{device}) Stage 1 of 5 (Device Prepare) started...
(#{device}): device state change: need-auth -> prepare (reason 'none') [60 40 0]
Activation (#{device}) Stage 2 of 5 (Device Configure) scheduled...
Activation (#{device}) Stage 1 of 5 (Device Prepare) complete.
Activation (#{device}) Stage 2 of 5 (Device Configure) starting...
(#{device}): device state change: prepare -> config (reason 'none') [40 50 0]
Activation (#{device}/wireless): connection '#{test_ssid}' has security, and secrets exist. No new secrets needed.
Config: added 'ssid' value '#{test_ssid}'
Config: added 'scan_ssid' value '1'
Config: added 'key_mgmt' value 'WPA-PSK'
Config: added 'auth_alg' value 'OPEN'
Config: added 'psk' value '<omitted>'
Activation (#{device}) Stage 2 of 5 (Device Configure) complete.
Config: set interface ap_scan to 1
(#{device}): supplicant interface state: inactive -> scanning
(#{device}): supplicant interface state: scanning -> authenticating
(#{device}): supplicant interface state: authenticating -> associating #{device}: link becomes ready
Activation (#{device}/wireless): association took too long.
EOF
tag = 'NetworkManager[666]'
network_manager_info_log_entries.split("\n").each do |line|
line.lstrip!
line.gsub!(/(\"|\`)/) { |match| "\\" + match }
$vm.execute_successfully("logger -t \"#{tag}\" \"<info> #{line}\"")
end
end
......@@ -150,7 +150,7 @@ When /^I join some empty multi-user chat$/ do
@screen.type("a", Sikuli::KeyModifier.CTRL)