2 ["/home/#{$live_user}/.claws-mail",
3 "/home/#{$live_user}/.gconf/system/networking/connections",
4 "/home/#{$live_user}/.gnome2/keyrings",
5 "/home/#{$live_user}/.gnupg",
6 "/home/#{$live_user}/.mozilla/firefox/bookmarks",
7 "/home/#{$live_user}/.purple",
8 "/home/#{$live_user}/.ssh",
9 "/home/#{$live_user}/Persistent",
10 "/var/cache/apt/archives",
14 Given /^I create a new (\d+) ([[:alpha:]]+) USB drive named "([^"]+)"$/ do |size, unit, name|
15 next if @skip_steps_while_restoring_background
16 @vm.storage.create_new_disk(name, {:size => size, :unit => unit})
19 Given /^I clone USB drive "([^"]+)" to a new USB drive "([^"]+)"$/ do |from, to|
20 next if @skip_steps_while_restoring_background
21 @vm.storage.clone_to_new_disk(from, to)
24 Given /^I unplug USB drive "([^"]+)"$/ do |name|
25 next if @skip_steps_while_restoring_background
26 @vm.unplug_drive(name)
29 Given /^the computer is set to boot from the old Tails DVD$/ do
30 next if @skip_steps_while_restoring_background
31 @vm.set_cdrom_boot($old_tails_iso)
34 class ISOHybridUpgradeNotSupported < StandardError
37 def usb_install_helper(name)
38 @screen.wait('USBCreateLiveUSB.png', 10)
40 # Here we'd like to select USB drive using #{name}, but Sikuli's
41 # OCR seems to be too unreliable.
42 # @screen.wait('USBTargetDevice.png', 10)
43 # match = @screen.find('USBTargetDevice.png')
45 # region_y = match.y + match.height
46 # region_w = match.width*3
47 # region_h = match.height*2
48 # ocr = Sikuli::Region.new(region_x, region_y, region_w, region_h).text
50 # # Unfortunately this results in almost garbage, like "|]dev/sdm"
51 # # when it should be /dev/sda1
53 @screen.wait_and_click('USBCreateLiveUSB.png', 10)
55 if @screen.find("USBSuggestsInstall.png")
56 raise ISOHybridUpgradeNotSupported
58 rescue Sikuli::ImageNotFound
59 # we didn't get the warning, so we can proceed with the install
62 @screen.wait_and_click('USBCreateLiveUSBNext.png', 10)
64 @screen.wait('USBInstallationComplete.png', 60*60)
65 @screen.type(Sikuli::KEY_RETURN)
66 @screen.type(Sikuli::KEY_F4, Sikuli::KEY_ALT)
69 When /^I "Clone & Install" Tails to USB drive "([^"]+)"$/ do |name|
70 next if @skip_steps_while_restoring_background
71 step "I run \"liveusb-creator-launcher\""
72 @screen.wait_and_click('USBCloneAndInstall.png', 30)
73 usb_install_helper(name)
76 When /^I "Clone & Upgrade" Tails to USB drive "([^"]+)"$/ do |name|
77 next if @skip_steps_while_restoring_background
78 step "I run \"liveusb-creator-launcher\""
79 @screen.wait_and_click('USBCloneAndUpgrade.png', 30)
80 usb_install_helper(name)
83 When /^I try a "Clone & Upgrade" Tails to USB drive "([^"]+)"$/ do |name|
84 next if @skip_steps_while_restoring_background
86 step "I \"Clone & Upgrade\" Tails to USB drive \"#{name}\""
87 rescue ISOHybridUpgradeNotSupported
88 # this is what we expect
90 raise "The USB installer should not succeed"
94 When /^I am suggested to do a "Clone & Upgrade"$/ do
95 next if @skip_steps_while_restoring_background
96 @screen.find("USBSuggestsInstall.png")
99 def shared_iso_dir_on_guest
103 Given /^I setup a filesystem share containing the Tails ISO$/ do
104 next if @skip_steps_while_restoring_background
105 @vm.add_share(File.dirname($tails_iso), shared_iso_dir_on_guest)
108 When /^I do a "Upgrade from ISO" on USB drive "([^"]+)"$/ do |name|
109 next if @skip_steps_while_restoring_background
110 step "I run \"liveusb-creator-launcher\""
111 @screen.wait_and_click('USBUpgradeFromISO.png', 10)
112 @screen.wait('USBUseLiveSystemISO.png', 10)
113 match = @screen.find('USBUseLiveSystemISO.png')
114 pos_x = match.x + match.width/2
115 pos_y = match.y + match.height*2
116 @screen.click(pos_x, pos_y)
117 @screen.wait('USBSelectISO.png', 10)
118 @screen.wait_and_click('GnomeFileDiagTypeFilename.png', 10)
119 iso = "#{shared_iso_dir_on_guest}/#{File.basename($tails_iso)}"
120 @screen.type(iso + Sikuli::KEY_RETURN)
121 usb_install_helper(name)
124 Given /^I enable all persistence presets$/ do
125 next if @skip_steps_while_restoring_background
126 @screen.wait('PersistenceWizardPresets.png', 20)
127 # Mark first non-default persistence preset
129 # Check all non-default persistence presets
133 @screen.wait_and_click('PersistenceWizardSave.png', 10)
134 @screen.wait('PersistenceWizardDone.png', 20)
135 @screen.type(Sikuli::KEY_F4, Sikuli::KEY_ALT)
138 Given /^I create a persistent partition with password "([^"]+)"$/ do |pwd|
139 next if @skip_steps_while_restoring_background
140 step "I run \"tails-persistence-setup\""
141 @screen.wait('PersistenceWizardWindow.png', 20)
142 @screen.wait('PersistenceWizardStart.png', 20)
143 @screen.type(pwd + "\t" + pwd + Sikuli::KEY_RETURN)
144 @screen.wait('PersistenceWizardPresets.png', 300)
145 step "I enable all persistence presets"
148 def check_part_integrity(name, dev, usage, type, scheme, label)
149 info = @vm.execute("udisks --show-info #{dev}").stdout
150 info_split = info.split("\n partition:\n")
151 dev_info = info_split[0]
152 part_info = info_split[1]
153 assert(dev_info.match("^ usage: +#{usage}$"),
154 "Unexpected device field 'usage' on USB drive '#{name}', '#{dev}'")
155 assert(dev_info.match("^ type: +#{type}$"),
156 "Unexpected device field 'type' on USB drive '#{name}', '#{dev}'")
157 assert(part_info.match("^ scheme: +#{scheme}$"),
158 "Unexpected partition scheme on USB drive '#{name}', '#{dev}'")
159 assert(part_info.match("^ label: +#{label}$"),
160 "Unexpected partition label on USB drive '#{name}', '#{dev}'")
163 Then /^Tails is installed on USB drive "([^"]+)"$/ do |name|
164 next if @skip_steps_while_restoring_background
165 dev = @vm.disk_dev(name) + "1"
166 check_part_integrity(name, dev, "filesystem", "vfat", "gpt", "Tails")
168 old_root = "/lib/live/mount/medium"
169 new_root = "/mnt/new"
170 @vm.execute("mkdir -p #{new_root}")
171 @vm.execute("mount #{dev} #{new_root}")
173 c = @vm.execute("diff -qr '#{old_root}/live' '#{new_root}/live'")
175 "USB drive '#{name}' has differences in /live:\n#{c.stdout}")
177 loader = boot_device_type == "usb" ? "syslinux" : "isolinux"
178 syslinux_files = @vm.execute("ls -1 #{new_root}/syslinux").stdout.chomp.split
179 # We deal with these files separately
180 ignores = ["syslinux.cfg", "exithelp.cfg", "ldlinux.sys"]
181 for f in syslinux_files - ignores do
182 c = @vm.execute("diff -q '#{old_root}/#{loader}/#{f}' " +
183 "'#{new_root}/syslinux/#{f}'")
184 assert(c.success?, "USB drive '#{name}' has differences in " +
188 # The main .cfg is named differently vs isolinux
189 c = @vm.execute("diff -q '#{old_root}/#{loader}/#{loader}.cfg' " +
190 "'#{new_root}/syslinux/syslinux.cfg'")
191 assert(c.success?, "USB drive '#{name}' has differences in " +
192 "'/syslinux/syslinux.cfg'")
194 # We have to account for the different path vs isolinux
195 old_exithelp = @vm.execute("cat '#{old_root}/#{loader}/exithelp.cfg'").stdout
196 new_exithelp = @vm.execute("cat '#{new_root}/syslinux/exithelp.cfg'").stdout
197 new_exithelp_undiffed = new_exithelp.sub("kernel /syslinux/vesamenu.c32",
198 "kernel /#{loader}/vesamenu.c32")
199 assert(new_exithelp_undiffed == old_exithelp,
200 "USB drive '#{name}' has unexpected differences in " +
201 "'/syslinux/exithelp.cfg'")
203 @vm.execute("umount #{new_root}")
207 Then /^there is no persistence partition on USB drive "([^"]+)"$/ do |name|
208 next if @skip_steps_while_restoring_background
209 data_part_dev = @vm.disk_dev(name) + "2"
210 assert(!@vm.execute("test -b #{data_part_dev}").success?,
211 "USB drive #{name} has a partition '#{data_part_dev}'")
214 Then /^a Tails persistence partition with password "([^"]+)" exists on USB drive "([^"]+)"$/ do |pwd, name|
215 next if @skip_steps_while_restoring_background
216 dev = @vm.disk_dev(name) + "2"
217 check_part_integrity(name, dev, "crypto", "crypto_LUKS", "gpt", "TailsData")
219 c = @vm.execute("echo #{pwd} | cryptsetup luksOpen #{dev} #{name}")
220 assert(c.success?, "Couldn't open LUKS device '#{dev}' on drive '#{name}'")
221 luks_dev = "/dev/mapper/#{name}"
223 # Adapting check_part_integrity() seems like a bad idea so here goes
224 info = @vm.execute("udisks --show-info #{luks_dev}").stdout
225 assert info.match("^ cleartext luks device:$")
226 assert info.match("^ usage: +filesystem$")
227 assert info.match("^ type: +ext[34]$")
228 assert info.match("^ label: +TailsData$")
230 mount_dir = "/mnt/#{name}"
231 @vm.execute("mkdir -p #{mount_dir}")
232 c = @vm.execute("mount #{luks_dev} #{mount_dir}")
234 "Couldn't mount opened LUKS device '#{dev}' on drive '#{name}'")
236 @vm.execute("umount #{mount_dir}")
238 @vm.execute("cryptsetup luksClose #{name}")
241 Given /^I enable persistence with password "([^"]+)"$/ do |pwd|
242 next if @skip_steps_while_restoring_background
243 match = @screen.find('TailsGreeterPersistence.png')
244 pos_x = match.x + match.width/2
245 # height*2 may seem odd, but we want to click the button below the
246 # match. This may even work accross different screen resolutions.
247 pos_y = match.y + match.height*2
248 @screen.click(pos_x, pos_y)
249 @screen.wait('TailsGreeterPersistencePassphrase.png', 10)
250 match = @screen.find('TailsGreeterPersistencePassphrase.png')
251 pos_x = match.x + match.width*2
252 pos_y = match.y + match.height/2
253 @screen.click(pos_x, pos_y)
257 Given /^persistence is not enabled$/ do
258 next if @skip_steps_while_restoring_background
259 data_part_dev = boot_device + "2"
260 assert(!@vm.execute("grep -q '^#{data_part_dev} ' /proc/mounts").success?,
261 "Partition '#{data_part_dev}' from the boot device is mounted")
264 Given /^I enable read-only persistence with password "([^"]+)"$/ do |pwd|
265 step "I enable persistence with password \"#{pwd}\""
266 next if @skip_steps_while_restoring_background
267 @screen.wait_and_click('TailsGreeterPersistenceReadOnly.png', 10)
270 Given /^persistence has been enabled$/ do
271 next if @skip_steps_while_restoring_background
272 try_for(120, :msg => "Some persistent dir was not mounted") {
273 mount = @vm.execute("mount").stdout.chomp
274 persistent_dirs.each do |dir|
275 if ! mount.include? "on #{dir} "
276 raise "persistent dir #{dir} missing"
283 # Approach borrowed from
284 # config/chroot_local_includes/lib/live/config/998-permissions
285 boot_dev_id = @vm.execute("udevadm info --device-id-of-file=/lib/live/mount/medium").stdout.chomp
286 boot_dev = @vm.execute("readlink -f /dev/block/'#{boot_dev_id}'").stdout.chomp
291 # Approach borrowed from
292 # config/chroot_local_includes/lib/live/config/998-permissions
293 boot_dev_info = @vm.execute("udevadm info --query=property --name='#{boot_device}'").stdout.chomp
294 boot_dev_type = (boot_dev_info.split("\n").select { |x| x.start_with? "ID_BUS=" })[0].split("=")[1]
298 Then /^Tails is running from USB drive "([^"]+)"$/ do |name|
299 next if @skip_steps_while_restoring_background
300 assert(boot_device_type == "usb",
301 "Got device type '#{boot_device_type}' while expecting 'usb'")
302 actual_dev = boot_device
303 # The boot partition differs between a "normal" install using the
304 # USB installer and isohybrid installations
305 expected_dev_normal = @vm.disk_dev(name) + "1"
306 expected_dev_isohybrid = @vm.disk_dev(name) + "4"
307 assert(actual_dev == expected_dev_normal ||
308 actual_dev == expected_dev_isohybrid,
309 "We are running from device #{actual_dev}, but for USB drive " +
310 "'#{name}' we expected to run from either device " +
311 "#{expected_dev_normal} (when installed via the USB installer) " +
312 "or #{expected_dev_normal} (when installed from an isohybrid)")
315 Then /^the boot device has safe access rights$/ do
316 next if @skip_steps_while_restoring_background
318 super_boot_dev = boot_device.sub(/[[:digit:]]+$/, "")
319 devs = @vm.execute("ls -1 #{super_boot_dev}*").stdout.chomp.split
320 assert(devs.size > 0, "Could not determine boot device")
321 all_users = @vm.execute("cut -d':' -f1 /etc/passwd").stdout.chomp.split
322 all_users_with_groups = all_users.collect do |user|
323 groups = @vm.execute("groups #{user}").stdout.chomp.sub(/^#{user} : /, "").split(" ")
327 dev_owner = @vm.execute("stat -c %U #{dev}").stdout.chomp
328 dev_group = @vm.execute("stat -c %G #{dev}").stdout.chomp
329 dev_perms = @vm.execute("stat -c %a #{dev}").stdout.chomp
330 assert(dev_owner == "root",
331 "Boot device '#{dev}' owned by user '#{dev_owner}', expected 'root'")
332 assert(dev_group == "disk" || dev_group == "root",
333 "Boot device '#{dev}' owned by group '#{dev_group}', expected " +
334 "'disk' or 'root'. We are probably affected by Debian bug #645466.")
335 assert(dev_perms == "660",
336 "Boot device '#{dev}' has permissions '#{dev_perms}', expected '660'")
337 for user, groups in all_users_with_groups do
338 next if user == "root"
339 assert(!(groups.include?(dev_group)),
340 "Unprivileged user '#{user}' is in group '#{dev_group}' which " +
341 "owns boot device '#{dev}'")
346 When /^I write some files expected to persist$/ do
347 next if @skip_steps_while_restoring_background
348 persistent_dirs.each do |dir|
349 owner = @vm.execute("stat -c %U #{dir}").stdout.chomp
350 assert(@vm.execute("touch #{dir}/XXX_persist", user=owner).success?,
351 "Could not create file in persistent directory #{dir}")
355 When /^I remove some files expected to persist$/ do
356 next if @skip_steps_while_restoring_background
357 persistent_dirs.each do |dir|
358 assert(@vm.execute("rm #{dir}/XXX_persist").success?,
359 "Could not remove file in persistent directory #{dir}")
363 When /^I write some files not expected to persist$/ do
364 next if @skip_steps_while_restoring_background
365 persistent_dirs.each do |dir|
366 owner = @vm.execute("stat -c %U #{dir}").stdout.chomp
367 assert(@vm.execute("touch #{dir}/XXX_gone", user=owner).success?,
368 "Could not create file in persistent directory #{dir}")
372 Then /^the expected persistent files are present in the filesystem$/ do
373 next if @skip_steps_while_restoring_background
374 persistent_dirs.each do |dir|
375 assert(@vm.execute("test -e #{dir}/XXX_persist").success?,
376 "Could not find expected file in persistent directory #{dir}")
377 assert(!@vm.execute("test -e #{dir}/XXX_gone").success?,
378 "Found file that should not have persisted in persistent directory #{dir}")
382 Then /^only the expected files should persist on USB drive "([^"]+)"$/ do |name|
383 next if @skip_steps_while_restoring_background
385 step "the computer is set to boot from USB drive \"#{name}\""
386 step "the network is unplugged"
387 step "I start the computer"
388 step "the computer boots Tails"
389 step "I enable read-only persistence with password \"asdf\""
390 step "I log in to a new session"
391 step "persistence has been enabled"
392 step "GNOME has started"
393 step "I have closed all annoying notifications"
394 step "the expected persistent files are present in the filesystem"
395 step "I completely shutdown Tails"
398 When /^I delete the persistent partition$/ do
399 next if @skip_steps_while_restoring_background
400 step "I run \"tails-persistence-setup --step delete\""
401 @screen.wait("PersistenceWizardWindow.png", 20)
402 @screen.wait("PersistenceWizardDeletionStart.png", 20)
404 @screen.wait("PersistenceWizardDone.png", 120)