summaryrefslogtreecommitdiffstats
path: root/features/step_definitions/checks.rb
diff options
context:
space:
mode:
authorPhilip Hands <phil@hands.com>2016-03-14 15:36:16 +0100
committerHolger Levsen <holger@layer-acht.org>2016-04-28 21:52:10 +0200
commitda080c472fc415b0ce918f4dd4a1ab143bb1bca4 (patch)
treebf63179f32f0eda0c2d5796e3e31c18c3c1185cf /features/step_definitions/checks.rb
parent26a9e8ec2bcae03db4d663d87b44d8708d64fdc2 (diff)
downloadjenkins.debian.net-da080c472fc415b0ce918f4dd4a1ab143bb1bca4.tar.xz
rough attempt to grab the good cucumber bits from recent tails
Diffstat (limited to 'features/step_definitions/checks.rb')
-rw-r--r--features/step_definitions/checks.rb253
1 files changed, 181 insertions, 72 deletions
diff --git a/features/step_definitions/checks.rb b/features/step_definitions/checks.rb
index 76cfe670..423b8390 100644
--- a/features/step_definitions/checks.rb
+++ b/features/step_definitions/checks.rb
@@ -1,40 +1,55 @@
-Then /^the shipped Tails signing key is not outdated$/ do
- # "old" here is w.r.t. the one we fetch from Tails' website
- next if @skip_steps_while_restoring_background
- sig_key_fingerprint = "0D24B36AA9A2A651787876451202821CBE2CD9C1"
- fresh_sig_key = "/tmp/tails-signing.key"
- tmp_keyring = "/tmp/tmp-keyring.gpg"
- key_url = "https://tails.boum.org/tails-signing.key"
- @vm.execute("curl --silent --socks5-hostname localhost:9062 " +
- "#{key_url} -o #{fresh_sig_key}", $live_user)
- @vm.execute("gpg --batch --no-default-keyring --keyring #{tmp_keyring} " +
- "--import #{fresh_sig_key}", $live_user)
- fresh_sig_key_info =
- @vm.execute("gpg --batch --no-default-keyring --keyring #{tmp_keyring} " +
- "--list-key #{sig_key_fingerprint}", $live_user).stdout
- shipped_sig_key_info = @vm.execute("gpg --batch --list-key #{sig_key_fingerprint}",
- $live_user).stdout
- assert_equal(fresh_sig_key_info, shipped_sig_key_info,
- "The Tails signing key shipped inside Tails is outdated:\n" +
- "Shipped key:\n" +
- shipped_sig_key_info +
- "Newly fetched key from #{key_url}:\n" +
- fresh_sig_key_info)
+def shipped_openpgp_keys
+ 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
+
+Then /^the OpenPGP keys shipped with Tails will be valid for the next (\d+) months$/ do |months|
+ invalid = Array.new
+ shipped_openpgp_keys.each do |key|
+ begin
+ step "the shipped OpenPGP key #{key} will be valid for the next #{months} months"
+ rescue Test::Unit::AssertionFailedError
+ invalid << key
+ next
+ end
+ end
+ assert(invalid.empty?, "The following key(s) will not be valid in #{months} months: #{invalid.join(', ')}")
+end
+
+Then /^the shipped (?:Debian repository key|OpenPGP key ([A-Z0-9]+)) will be valid for the next (\d+) months$/ do |fingerprint, max_months|
+ if fingerprint
+ cmd = 'gpg'
+ user = LIVE_USER
+ else
+ fingerprint = TAILS_DEBIAN_REPO_KEY
+ cmd = 'apt-key adv'
+ user = 'root'
+ end
+ 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])
+ assert((expiration_date << max_months.to_i) > DateTime.now,
+ "The shipped key #{fingerprint} will not be valid #{max_months} months from now.")
+ end
+end
+
+Then /^I double-click the Report an Error launcher on the desktop$/ do
+ @screen.wait_and_double_click('DesktopReportAnError.png', 30)
end
Then /^the live user has been setup by live\-boot$/ do
- next if @skip_steps_while_restoring_background
- assert(@vm.execute("test -e /var/lib/live/config/user-setup").success?,
+ assert($vm.execute("test -e /var/lib/live/config/user-setup").success?,
"live-boot failed its user-setup")
- actual_username = @vm.execute(". /etc/live/config/username.conf; " +
+ actual_username = $vm.execute(". /etc/live/config/username.conf; " +
"echo $LIVE_USERNAME").stdout.chomp
- assert_equal($live_user, actual_username)
+ assert_equal(LIVE_USER, actual_username)
end
Then /^the live user is a member of only its own group and "(.*?)"$/ do |groups|
- next if @skip_steps_while_restoring_background
- expected_groups = groups.split(" ") << $live_user
- actual_groups = @vm.execute("groups #{$live_user}").stdout.chomp.sub(/^#{$live_user} : /, "").split(" ")
+ expected_groups = groups.split(" ") << LIVE_USER
+ actual_groups = $vm.execute("groups #{LIVE_USER}").stdout.chomp.sub(/^#{LIVE_USER} : /, "").split(" ")
unexpected = actual_groups - expected_groups
missing = expected_groups - actual_groups
assert_equal(0, unexpected.size,
@@ -44,26 +59,17 @@ Then /^the live user is a member of only its own group and "(.*?)"$/ do |groups|
end
Then /^the live user owns its home dir and it has normal permissions$/ do
- next if @skip_steps_while_restoring_background
- home = "/home/#{$live_user}"
- assert(@vm.execute("test -d #{home}").success?,
+ home = "/home/#{LIVE_USER}"
+ assert($vm.execute("test -d #{home}").success?,
"The live user's home doesn't exist or is not a directory")
- owner = @vm.execute("stat -c %U:%G #{home}").stdout.chomp
- perms = @vm.execute("stat -c %a #{home}").stdout.chomp
- assert_equal("#{$live_user}:#{$live_user}", owner)
+ owner = $vm.execute("stat -c %U:%G #{home}").stdout.chomp
+ perms = $vm.execute("stat -c %a #{home}").stdout.chomp
+ assert_equal("#{LIVE_USER}:#{LIVE_USER}", owner)
assert_equal("700", perms)
end
-Given /^I wait between (\d+) and (\d+) seconds$/ do |min, max|
- next if @skip_steps_while_restoring_background
- time = rand(max.to_i - min.to_i + 1) + min.to_i
- puts "Slept for #{time} seconds"
- sleep(time)
-end
-
Then /^no unexpected services are listening for network connections$/ do
- next if @skip_steps_while_restoring_background
- netstat_cmd = @vm.execute("netstat -ltupn")
+ netstat_cmd = $vm.execute("netstat -ltupn")
assert netstat_cmd.success?
for line in netstat_cmd.stdout.chomp.split("\n") do
splitted = line.split(/[[:blank:]]+/)
@@ -79,8 +85,8 @@ Then /^no unexpected services are listening for network connections$/ do
proc = splitted[proc_index].split("/")[1]
# Services listening on loopback is not a threat
if /127(\.[[:digit:]]{1,3}){3}/.match(laddr).nil?
- if $services_expected_on_all_ifaces.include? [proc, laddr, lport] or
- $services_expected_on_all_ifaces.include? [proc, laddr, "*"]
+ if SERVICES_EXPECTED_ON_ALL_IFACES.include? [proc, laddr, lport] or
+ SERVICES_EXPECTED_ON_ALL_IFACES.include? [proc, laddr, "*"]
puts "Service '#{proc}' is listening on #{laddr}:#{lport} " +
"but has an exception"
else
@@ -91,53 +97,156 @@ Then /^no unexpected services are listening for network connections$/ do
end
When /^Tails has booted a 64-bit kernel$/ do
- next if @skip_steps_while_restoring_background
- assert(@vm.execute("uname -r | grep -qs 'amd64$'").success?,
+ assert($vm.execute("uname -r | grep -qs 'amd64$'").success?,
"Tails has not booted a 64-bit kernel.")
end
+Then /^there is no screenshot in the live user's Pictures directory$/ do
+ pictures_directory = "/home/#{LIVE_USER}/Pictures"
+ assert($vm.execute(
+ "find '#{pictures_directory}' -name 'Screenshot*.png' -maxdepth 1"
+ ).stdout.empty?,
+ "Existing screenshots were found in the live user's Pictures directory.")
+end
+
+Then /^a screenshot is saved to the live user's Pictures directory$/ do
+ pictures_directory = "/home/#{LIVE_USER}/Pictures"
+ try_for(10, :msg=> "No screenshot was created in #{pictures_directory}") do
+ !$vm.execute(
+ "find '#{pictures_directory}' -name 'Screenshot*.png' -maxdepth 1"
+ ).stdout.empty?
+ end
+end
+
Then /^the VirtualBox guest modules are available$/ do
- next if @skip_steps_while_restoring_background
- assert(@vm.execute("modinfo vboxguest").success?,
+ assert($vm.execute("modinfo vboxguest").success?,
"The vboxguest module is not available.")
end
-def shared_pdf_dir_on_guest
- "/tmp/shared_pdf_dir"
+Given /^I setup a filesystem share containing a sample PDF$/ do
+ shared_pdf_dir_on_host = "#{$config["TMPDIR"]}/shared_pdf_dir"
+ @shared_pdf_dir_on_guest = "/tmp/shared_pdf_dir"
+ FileUtils.mkdir_p(shared_pdf_dir_on_host)
+ Dir.glob("#{MISC_FILES_DIR}/*.pdf") do |pdf_file|
+ FileUtils.cp(pdf_file, shared_pdf_dir_on_host)
+ end
+ add_after_scenario_hook { FileUtils.rm_r(shared_pdf_dir_on_host) }
+ $vm.add_share(shared_pdf_dir_on_host, @shared_pdf_dir_on_guest)
end
-Given /^I setup a filesystem share containing a sample PDF$/ do
- next if @skip_steps_while_restoring_background
- @vm.add_share($misc_files_dir, shared_pdf_dir_on_guest)
+Then /^the support documentation page opens in Tor Browser$/ do
+ @screen.wait("SupportDocumentation#{@language}.png", 120)
end
Then /^MAT can clean some sample PDF file$/ do
- next if @skip_steps_while_restoring_background
- for pdf_on_host in Dir.glob("#{$misc_files_dir}/*.pdf") do
+ for pdf_on_host in Dir.glob("#{MISC_FILES_DIR}/*.pdf") do
pdf_name = File.basename(pdf_on_host)
- 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}\""
- @vm.execute("mat --display '#{pdf_on_guest}'",
- $live_user).stdout
- check_before = @vm.execute("mat --check '#{pdf_on_guest}'",
- $live_user).stdout
- if check_before.include?("#{pdf_on_guest} is clean")
- STDERR.puts "warning: '#{pdf_on_host}' is already clean so it is a " +
- "bad candidate for testing MAT"
- end
- @vm.execute("mat '#{pdf_on_guest}'", $live_user)
- check_after = @vm.execute("mat --check '#{pdf_on_guest}'",
- $live_user).stdout
+ 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}'",
+ :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}'", :user => LIVE_USER)
+ check_after = $vm.execute_successfully("mat --check '#{pdf_on_guest}'",
+ :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}'")
end
end
Then /^AppArmor is enabled$/ do
- assert(@vm.execute("aa-status").success?, "AppArmor is not enabled")
+ assert($vm.execute("aa-status").success?, "AppArmor is not enabled")
end
Then /^some AppArmor profiles are enforced$/ do
- assert(@vm.execute("aa-status --enforced").stdout.chomp.to_i > 0,
+ assert($vm.execute("aa-status --enforced").stdout.chomp.to_i > 0,
"No AppArmor profile is enforced")
end
+
+def get_seccomp_status(process)
+ assert($vm.has_process?(process), "Process #{process} not running.")
+ pid = $vm.pidof(process)[0]
+ status = $vm.file_content("/proc/#{pid}/status")
+ return status.match(/^Seccomp:\s+([0-9])/)[1].chomp.to_i
+end
+
+def get_apparmor_status(pid)
+ apparmor_status = $vm.file_content("/proc/#{pid}/attr/current").chomp
+ if apparmor_status.include?(')')
+ # matches something like /usr/sbin/cupsd (enforce)
+ # and only returns what's in the parentheses
+ return apparmor_status.match(/[^\s]+\s+\((.+)\)$/)[1].chomp
+ else
+ return apparmor_status
+ end
+end
+
+Then /^the running process "(.+)" is confined with AppArmor in (complain|enforce) mode$/ do |process, mode|
+ if process == 'i2p'
+ $vm.execute_successfully('service i2p status')
+ pid = $vm.file_content('/run/i2p/i2p.pid').chomp
+ else
+ assert($vm.has_process?(process), "Process #{process} not running.")
+ pid = $vm.pidof(process)[0]
+ end
+ assert_equal(mode, get_apparmor_status(pid))
+end
+
+Then /^the running process "(.+)" is confined with Seccomp in (filter|strict) mode$/ do |process,mode|
+ status = get_seccomp_status(process)
+ if mode == 'strict'
+ assert_equal(1, status, "#{process} not confined with Seccomp in strict mode")
+ elsif mode == 'filter'
+ assert_equal(2, status, "#{process} not confined with Seccomp in filter mode")
+ else
+ raise "Unsupported mode #{mode} passed"
+ end
+end
+
+Then /^tails-debugging-info is not susceptible to symlink attacks$/ do
+ secret_file = '/secret'
+ secret_contents = 'T0P S3Cr1t -- 3yEs oN1y'
+ $vm.file_append(secret_file, secret_contents)
+ $vm.execute_successfully("chmod u=rw,go= #{secret_file}")
+ $vm.execute_successfully("chown root:root #{secret_file}")
+ script_path = '/usr/local/sbin/tails-debugging-info'
+ script_lines = $vm.file_content(script_path).split("\n")
+ script_lines.grep(/^debug_file\s+/).each do |line|
+ _, user, debug_file = line.split
+ # root can always mount symlink attacks
+ next if user == 'root'
+ # Remove quoting around the file
+ debug_file.gsub!(/["']/, '')
+ # Skip files that do not exist, or cannot be removed (e.g. the
+ # ones in /proc).
+ next if not($vm.execute("rm #{debug_file}").success?)
+ # Check what would happen *if* the amnesia user managed to replace
+ # the debugging file with a symlink to the secret.
+ $vm.execute_successfully("ln -s #{secret_file} #{debug_file}")
+ $vm.execute_successfully("chown --no-dereference #{LIVE_USER}:#{LIVE_USER} #{debug_file}")
+ if $vm.execute("sudo /usr/local/sbin/tails-debugging-info | " +
+ "grep '#{secret_contents}'",
+ :user => LIVE_USER).success?
+ raise "The secret was leaked by tails-debugging-info via '#{debug_file}'"
+ end
+ # Remove the secret so it cannot possibly interfere with the
+ # following iterations (even though it should not).
+ $vm.execute_successfully("echo > #{debug_file}")
+ end
+end
+
+When /^I disable all networking in the Tails Greeter$/ do
+ begin
+ @screen.click('TailsGreeterDisableAllNetworking.png')
+ rescue FindFailed
+ @screen.type(Sikuli::Key.PAGE_DOWN)
+ @screen.click('TailsGreeterDisableAllNetworking.png')
+ end
+end
+
+Then /^the Tor Status icon tells me that Tor is( not)? usable$/ do |not_usable|
+ picture = not_usable ? 'TorStatusNotUsable' : 'TorStatusUsable'
+ @screen.find("#{picture}.png")
+end