Merge remote-tracking branch 'origin/master'
[tails.git] / features / step_definitions / checks.rb
blob8ed375bd62cf544a1270bca3b00532cc8a6ad772
1 def shipped_openpgp_keys
2   shipped_gpg_keys = $vm.execute_successfully('gpg --batch --with-colons --fingerprint --list-key', :user => LIVE_USER).stdout
3   openpgp_fingerprints = shipped_gpg_keys.scan(/^fpr:::::::::([A-Z0-9]+):$/).flatten
4   return openpgp_fingerprints
5 end
7 Then /^the OpenPGP keys shipped with Tails will be valid for the next (\d+) months$/ do |months|
8   invalid = Array.new
9   shipped_openpgp_keys.each do |key|
10     begin
11       step "the shipped OpenPGP key #{key} will be valid for the next #{months} months"
12     rescue Test::Unit::AssertionFailedError
13       invalid << key
14       next
15     end
16   end
17   assert(invalid.empty?, "The following key(s) will not be valid in #{months} months: #{invalid.join(', ')}")
18 end
20 Then /^the shipped (?:Debian repository key|OpenPGP key ([A-Z0-9]+)) will be valid for the next (\d+) months$/ do |fingerprint, max_months|
21   if fingerprint
22     cmd = 'gpg'
23     user = LIVE_USER
24   else
25     fingerprint = TAILS_DEBIAN_REPO_KEY
26     cmd = 'apt-key adv'
27     user = 'root'
28   end
29   shipped_sig_key_info = $vm.execute_successfully("#{cmd} --batch --list-key #{fingerprint}", :user => user).stdout
30   m = /\[expire[ds]: ([0-9-]*)\]/.match(shipped_sig_key_info)
31   if m
32     expiration_date = Date.parse(m[1])
33     assert((expiration_date << max_months.to_i) > DateTime.now,
34            "The shipped key #{fingerprint} will not be valid #{max_months} months from now.")
35   end
36 end
38 Then /^the live user has been setup by live\-boot$/ do
39   assert($vm.execute("test -e /var/lib/live/config/user-setup").success?,
40          "live-boot failed its user-setup")
41   actual_username = $vm.execute(". /etc/live/config/username.conf; " +
42                                 "echo $LIVE_USERNAME").stdout.chomp
43   assert_equal(LIVE_USER, actual_username)
44 end
46 Then /^the live user is a member of only its own group and "(.*?)"$/ do |groups|
47   expected_groups = groups.split(" ") << LIVE_USER
48   actual_groups = $vm.execute("groups #{LIVE_USER}").stdout.chomp.sub(/^#{LIVE_USER} : /, "").split(" ")
49   unexpected = actual_groups - expected_groups
50   missing = expected_groups - actual_groups
51   assert_equal(0, unexpected.size,
52          "live user in unexpected groups #{unexpected}")
53   assert_equal(0, missing.size,
54          "live user not in expected groups #{missing}")
55 end
57 Then /^the live user owns its home dir and it has normal permissions$/ do
58   home = "/home/#{LIVE_USER}"
59   assert($vm.execute("test -d #{home}").success?,
60          "The live user's home doesn't exist or is not a directory")
61   owner = $vm.execute("stat -c %U:%G #{home}").stdout.chomp
62   perms = $vm.execute("stat -c %a #{home}").stdout.chomp
63   assert_equal("#{LIVE_USER}:#{LIVE_USER}", owner)
64   assert_equal("700", perms)
65 end
67 Then /^no unexpected services are listening for network connections$/ do
68   for line in $vm.execute_successfully("ss -ltupn").stdout.chomp.split("\n") do
69     splitted = line.split(/[[:blank:]]+/)
70     proto = splitted[0]
71     next unless ['tcp', 'udp'].include?(proto)
72     laddr, lport = splitted[4].split(":")
73     proc = /users:\(\("([^"]+)"/.match(splitted[6])[1]
74     # Services listening on loopback is not a threat
75     if /127(\.[[:digit:]]{1,3}){3}/.match(laddr).nil?
76       if SERVICES_EXPECTED_ON_ALL_IFACES.include? [proc, laddr, lport] or
77          SERVICES_EXPECTED_ON_ALL_IFACES.include? [proc, laddr, "*"]
78         puts "Service '#{proc}' is listening on #{laddr}:#{lport} " +
79              "but has an exception"
80       else
81         raise "Unexpected service '#{proc}' listening on #{laddr}:#{lport}"
82       end
83     end
84   end
85 end
87 When /^Tails has booted a 64-bit kernel$/ do
88   assert($vm.execute("uname -r | grep -qs 'amd64$'").success?,
89          "Tails has not booted a 64-bit kernel.")
90 end
92 Then /^the VirtualBox guest modules are available$/ do
93   assert($vm.execute("modinfo vboxguest").success?,
94          "The vboxguest module is not available.")
95 end
97 Then /^the support documentation page opens in Tor Browser$/ do
98    if @language == 'German'
99      expected_title = 'Tails - Hilfe & Support'
100      expected_heading = 'Die Dokumentation durchsuchen'
101    else
102      expected_title = 'Tails - Support'
103      expected_heading = 'Search the documentation'
104    end
105    step "\"#{expected_title}\" has loaded in the Tor Browser"
106    if @language == 'German'
107      browser_name = 'Tor-Browser'
108    else
109      browser_name = 'Tor Browser'
110    end
111    headings = @torbrowser
112               .child(expected_title + " - #{browser_name}", roleName: 'frame')
113               .children(roleName: 'heading')
114    assert(
115      headings.any? { |heading| heading.text == expected_heading }
116    )
117  end
119 Given /^I plug and mount a USB drive containing a sample PNG$/ do
120   @png_dir = share_host_files(Dir.glob("#{MISC_FILES_DIR}/*.png"))
123 Then /^MAT can clean some sample PNG file$/ do
124   for png_on_host in Dir.glob("#{MISC_FILES_DIR}/*.png") do
125     png_name = File.basename(png_on_host)
126     png_on_guest = "/home/#{LIVE_USER}/#{png_name}"
127     step "I copy \"#{@png_dir}/#{png_name}\" to \"#{png_on_guest}\" as user \"#{LIVE_USER}\""
128     raw_check_cmd = "grep --quiet --fixed-strings --text " +
129                     "'Created with GIMP' '#{png_on_guest}'"
130     assert($vm.execute(raw_check_cmd, user: LIVE_USER).success?,
131            'The comment is not present in the PNG')
132     check_before = $vm.execute_successfully("mat --check '#{png_on_guest}'",
133                                             :user => LIVE_USER).stdout
134     assert(check_before.include?("#{png_on_guest} is not clean"),
135            "MAT failed to see that '#{png_on_host}' is dirty")
136     $vm.execute_successfully("mat '#{png_on_guest}'", :user => LIVE_USER)
137     check_after = $vm.execute_successfully("mat --check '#{png_on_guest}'",
138                                            :user => LIVE_USER).stdout
139     assert(check_after.include?("#{png_on_guest} is clean"),
140            "MAT failed to clean '#{png_on_host}'")
141     assert($vm.execute(raw_check_cmd, user: LIVE_USER).failure?,
142            'The comment is still present in the PNG')
143     $vm.execute_successfully("rm '#{png_on_guest}'")
144   end
149 Then /^AppArmor is enabled$/ do
150   assert($vm.execute("aa-status").success?, "AppArmor is not enabled")
153 Then /^some AppArmor profiles are enforced$/ do
154   assert($vm.execute("aa-status --enforced").stdout.chomp.to_i > 0,
155          "No AppArmor profile is enforced")
158 def get_seccomp_status(process)
159   assert($vm.has_process?(process), "Process #{process} not running.")
160   pid = $vm.pidof(process)[0]
161   status = $vm.file_content("/proc/#{pid}/status")
162   return status.match(/^Seccomp:\s+([0-9])/)[1].chomp.to_i
165 def get_apparmor_status(pid)
166   apparmor_status = $vm.file_content("/proc/#{pid}/attr/current").chomp
167   if apparmor_status.include?(')')
168     # matches something like     /usr/sbin/cupsd (enforce)
169     # and only returns what's in the parentheses
170     return apparmor_status.match(/[^\s]+\s+\((.+)\)$/)[1].chomp
171   else
172     return apparmor_status
173   end
176 Then /^the running process "(.+)" is confined with AppArmor in (complain|enforce) mode$/ do |process, mode|
177   assert($vm.has_process?(process), "Process #{process} not running.")
178   pid = $vm.pidof(process)[0]
179   assert_equal(mode, get_apparmor_status(pid))
182 Then /^the running process "(.+)" is confined with Seccomp in (filter|strict) mode$/ do |process,mode|
183   status = get_seccomp_status(process)
184   if mode == 'strict'
185     assert_equal(1, status, "#{process} not confined with Seccomp in strict mode")
186   elsif mode == 'filter'
187     assert_equal(2, status, "#{process} not confined with Seccomp in filter mode")
188   else
189     raise "Unsupported mode #{mode} passed"
190   end
193 When /^I disable all networking in the Tails Greeter$/ do
194   open_greeter_additional_settings()
195   @screen.wait_and_click('TailsGreeterNetworkConnection.png', 30)
196   @screen.wait_and_click('TailsGreeterDisableAllNetworking.png', 10)
197   @screen.wait_and_click("TailsGreeterAdditionalSettingsAdd.png", 10)
200 Then /^the Tor Status icon tells me that Tor is( not)? usable$/ do |not_usable|
201   picture = not_usable ? 'TorStatusNotUsable' : 'TorStatusUsable'
202   @screen.find("#{picture}.png")