-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathtest_uefistored_sb.py
More file actions
243 lines (206 loc) · 9.37 KB
/
Copy pathtest_uefistored_sb.py
File metadata and controls
243 lines (206 loc) · 9.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import pytest
import logging
from lib.commands import SSHCommandFailed
from lib.common import wait_for
from lib.efi import EFIAuth
from lib.snapshot import Snapshot
from lib.vm import VM
from .utils import (
VM_SECURE_BOOT_FAILED,
_test_key_exchanges,
boot_and_check_no_sb_errors,
boot_and_check_sb_failed,
boot_and_check_sb_succeeded,
generate_keys,
revert_vm_state,
sign_efi_bins,
)
from typing import Generator
# These tests check the behaviour of XAPI and uefistored as they are in XCP-ng 8.2
# For XCP-ng 8.3 or later, see test_varstored_sb.py
# Requirements:
# On the test runner:
# - See requirements documented in the project's README.md for Guest UEFI Secure Boot tests
# From --hosts parameter:
# - host: XCP-ng host 8.2.x only (+ updates)
# with UEFI certs either absent, or present and consistent (state will be saved and restored)
# From --vm parameter
# - A UEFI VM to import
# Some tests are Linux-only and some tests are Windows-only.
pytestmark = pytest.mark.default_vm('mini-linux-x86_64-uefi')
@pytest.mark.small_vm
@pytest.mark.usefixtures("host_less_than_8_3")
@pytest.mark.usefixtures("pool_without_uefi_certs", "skip_if_not_unix_vm")
class TestGuestLinuxUEFISecureBoot:
PK: EFIAuth
KEK: EFIAuth
db: EFIAuth
dbx: EFIAuth
@pytest.fixture(autouse=True)
def setup_and_cleanup(self, uefi_vm_and_snapshot: tuple[VM, Snapshot]) -> Generator[None, None, None]:
vm, snapshot = uefi_vm_and_snapshot
self.PK, self.KEK, self.db, self.dbx = generate_keys()
yield
revert_vm_state(vm, snapshot)
# clear pool certs for next test
vm.host.pool.clear_uefi_certs()
@pytest.mark.multi_vms # test that SB works on various UEFI unix/linux VMs, not just on `small_vm`
def test_boot_success_when_pool_db_set_and_images_signed(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.host.pool.install_custom_uefi_certs([self.PK, self.KEK, self.db])
sign_efi_bins(vm, self.db)
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_succeeded(vm)
def test_boot_success_when_vm_db_set_and_images_signed(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.install_uefi_certs([self.PK, self.KEK, self.db])
sign_efi_bins(vm, self.db)
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_succeeded(vm)
def test_boot_fails_when_pool_db_set_and_images_unsigned(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.host.pool.install_custom_uefi_certs([self.PK, self.KEK, self.db])
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_failed(vm)
def test_boot_fails_when_vm_db_set_and_images_unsigned(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.install_uefi_certs([self.PK, self.KEK, self.db])
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_failed(vm)
def test_boot_succeeds_when_pool_certs_set_and_sb_disabled(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.host.pool.install_custom_uefi_certs([self.PK, self.KEK, self.db])
vm.param_set('platform', False, key='secureboot')
boot_and_check_no_sb_errors(vm)
def test_boot_succeeds_when_vm_certs_set_and_sb_disabled(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.install_uefi_certs([self.PK, self.KEK, self.db])
vm.param_set('platform', False, key='secureboot')
boot_and_check_no_sb_errors(vm)
def test_boot_fails_when_pool_dbx_revokes_signed_images(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.host.pool.install_custom_uefi_certs([self.PK, self.KEK, self.db, self.dbx])
sign_efi_bins(vm, self.db)
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_failed(vm)
def test_boot_fails_when_vm_dbx_revokes_signed_images(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.install_uefi_certs([self.PK, self.KEK, self.db, self.dbx])
sign_efi_bins(vm, self.db)
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_failed(vm)
def test_boot_success_when_initial_pool_keys_not_signed_by_parent(self, uefi_vm: VM) -> None:
vm = uefi_vm
PK, KEK, db, _ = generate_keys(self_signed=True)
vm.host.pool.install_custom_uefi_certs([PK, KEK, db])
sign_efi_bins(vm, db)
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_succeeded(vm)
def test_boot_success_when_initial_vm_keys_not_signed_by_parent(self, uefi_vm: VM) -> None:
vm = uefi_vm
PK, KEK, db, _ = generate_keys(self_signed=True)
vm.install_uefi_certs([PK, KEK, db])
sign_efi_bins(vm, db)
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_succeeded(vm)
def test_sb_off_really_means_off(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.install_uefi_certs([self.PK, self.KEK, self.db])
sign_efi_bins(vm, self.db)
vm.param_set('platform', False, key='secureboot')
vm.start()
vm.wait_for_vm_running_and_ssh_up()
logging.info("Check that SB is NOT enabled according to the OS.")
assert not vm.booted_with_secureboot()
@pytest.mark.usefixtures("host_less_than_8_3")
@pytest.mark.usefixtures("pool_without_uefi_certs", "skip_if_not_windows_vm")
class TestGuestWindowsUEFISecureBoot:
@pytest.fixture(autouse=True)
def setup_and_cleanup(self, uefi_vm_and_snapshot: tuple[VM, Snapshot]) -> Generator[None, None, None]:
vm, snapshot = uefi_vm_and_snapshot
yield
revert_vm_state(vm, snapshot)
# clear pool certs for next test
vm.host.pool.clear_uefi_certs()
@pytest.mark.small_vm # test on the smallest Windows VM, if that means anything with Windows
def test_windows_fails(self, uefi_vm: VM) -> None:
vm = uefi_vm
PK, KEK, db, _ = generate_keys(self_signed=True)
vm.host.pool.install_custom_uefi_certs([PK, KEK, db])
vm.param_set('platform', True, key='secureboot')
boot_and_check_sb_failed(vm)
@pytest.mark.multi_vms # test that SB works on every Windows VM we have
def test_windows_succeeds(self, uefi_vm: VM) -> None:
vm = uefi_vm
vm.param_set('platform', True, key='secureboot')
# Install default certs. This requires internet access from the host.
logging.info("Install default certs on pool with secureboot-certs install")
vm.host.ssh('secureboot-certs install')
boot_and_check_sb_succeeded(vm)
@pytest.mark.small_vm
@pytest.mark.usefixtures("host_less_than_8_3")
@pytest.mark.usefixtures("pool_without_uefi_certs")
class TestCertsMissingAndSbOn:
PK: EFIAuth
KEK: EFIAuth
db: EFIAuth
dbx: EFIAuth
@pytest.fixture(autouse=True)
def setup_and_cleanup(self, uefi_vm_and_snapshot: tuple[VM, Snapshot]) -> Generator[None, None, None]:
vm, snapshot = uefi_vm_and_snapshot
vm.param_set('platform', True, key='secureboot')
yield
revert_vm_state(vm, snapshot)
# clear pool certs for next test
vm.host.pool.clear_uefi_certs()
def check_vm_start_fails_and_uefistored_dies(self, vm: VM) -> None:
with pytest.raises(SSHCommandFailed) as excinfo:
vm.start()
assert 'An emulator required to run this VM failed to start' in excinfo.value.stdout
logging.info('Verified that uefistored killed itself to prevent the VM start')
wait_for(
lambda: vm.get_messages(VM_SECURE_BOOT_FAILED),
'Wait for message %s' % VM_SECURE_BOOT_FAILED,
)
# Just in case it managed to start somehow, be it in UEFI shell only
assert vm.is_halted()
def test_no_certs_but_sb_on(self, uefi_vm: VM) -> None:
vm = uefi_vm
self.check_vm_start_fails_and_uefistored_dies(vm)
def test_only_pk_present_but_sb_on(self, uefi_vm: VM) -> None:
vm = uefi_vm
PK, _, _, _ = generate_keys()
vm.install_uefi_certs([PK])
self.check_vm_start_fails_and_uefistored_dies(vm)
def test_only_pk_and_kek_present_but_sb_on(self, uefi_vm: VM) -> None:
vm = uefi_vm
PK, KEK, _, _ = generate_keys()
vm.install_uefi_certs([PK, KEK])
self.check_vm_start_fails_and_uefistored_dies(vm)
def test_only_kek_and_db_present_but_sb_on(self, uefi_vm: VM) -> None:
vm = uefi_vm
_, KEK, db, _ = generate_keys()
vm.install_uefi_certs([KEK, db])
self.check_vm_start_fails_and_uefistored_dies(vm)
def test_only_pk_and_db_present_but_sb_on(self, uefi_vm: VM) -> None:
vm = uefi_vm
PK, _, db, _ = generate_keys()
vm.install_uefi_certs([PK, db])
self.check_vm_start_fails_and_uefistored_dies(vm)
def test_only_db_present_but_sb_on(self, uefi_vm: VM) -> None:
vm = uefi_vm
_, _, db, _ = generate_keys()
vm.install_uefi_certs([db])
self.check_vm_start_fails_and_uefistored_dies(vm)
@pytest.mark.small_vm
@pytest.mark.usefixtures("host_less_than_8_3")
@pytest.mark.usefixtures("pool_without_uefi_certs", "unix_vm")
class TestUEFIKeyExchange:
@pytest.fixture(autouse=True)
def setup_and_cleanup(self, uefi_vm_and_snapshot: tuple[VM, Snapshot]) -> Generator[None, None, None]:
vm, snapshot = uefi_vm_and_snapshot
yield
revert_vm_state(vm, snapshot)
def test_key_exchanges(self, uefi_vm: VM) -> None:
vm = uefi_vm
_test_key_exchanges(vm)