]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
b2bb40ce | 2 | /* |
b2bb40ce | 3 | * Port to systemd-boot |
96b2fb93 | 4 | * Copyright © 2017 Max Resch <resch.max@gmail.com> |
b2bb40ce MR |
5 | * |
6 | * Security Policy Handling | |
96b2fb93 | 7 | * Copyright © 2012 <James.Bottomley@HansenPartnership.com> |
b2bb40ce MR |
8 | * https://github.com/mjg59/efitools |
9 | */ | |
10 | ||
11 | #include <efi.h> | |
12 | #include <efilib.h> | |
13 | ||
a69702c5 | 14 | #include "missing_efi.h" |
b2bb40ce MR |
15 | #include "util.h" |
16 | #include "shim.h" | |
17 | ||
82a0fb32 DS |
18 | #if defined(__x86_64__) || defined(__i386__) |
19 | #define __sysv_abi__ __attribute__((sysv_abi)) | |
20 | #else | |
21 | #define __sysv_abi__ | |
22 | #endif | |
23 | ||
b2bb40ce | 24 | struct ShimLock { |
db4122d1 | 25 | EFI_STATUS __sysv_abi__ (*shim_verify) (void *buffer, uint32_t size); |
b2bb40ce MR |
26 | |
27 | /* context is actually a struct for the PE header, but it isn't needed so void is sufficient just do define the interface | |
28 | * see shim.c/shim.h and PeHeader.h in the github shim repo */ | |
db4122d1 | 29 | EFI_STATUS __sysv_abi__ (*generate_hash) (void *data, uint32_t datasize, void *context, uint8_t *sha256hash, uint8_t *sha1hash); |
b2bb40ce | 30 | |
db4122d1 | 31 | EFI_STATUS __sysv_abi__ (*read_header) (void *data, uint32_t datasize, void *context); |
b2bb40ce MR |
32 | }; |
33 | ||
7d2ebb6f | 34 | #define SHIM_LOCK_GUID \ |
c0ad07b1 | 35 | &(const EFI_GUID) { 0x605dab50, 0xe046, 0x4300, { 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } } |
b2bb40ce MR |
36 | |
37 | BOOLEAN shim_loaded(void) { | |
38 | struct ShimLock *shim_lock; | |
39 | ||
2a5e4fe4 | 40 | return BS->LocateProtocol((EFI_GUID*) SHIM_LOCK_GUID, NULL, (void**) &shim_lock) == EFI_SUCCESS; |
b2bb40ce MR |
41 | } |
42 | ||
db4122d1 | 43 | static BOOLEAN shim_validate(void *data, uint32_t size) { |
b2bb40ce MR |
44 | struct ShimLock *shim_lock; |
45 | ||
46 | if (!data) | |
47 | return FALSE; | |
48 | ||
2a5e4fe4 | 49 | if (BS->LocateProtocol((EFI_GUID*) SHIM_LOCK_GUID, NULL, (void**) &shim_lock) != EFI_SUCCESS) |
b2bb40ce MR |
50 | return FALSE; |
51 | ||
52 | if (!shim_lock) | |
53 | return FALSE; | |
54 | ||
2a5e4fe4 | 55 | return shim_lock->shim_verify(data, size) == EFI_SUCCESS; |
b2bb40ce MR |
56 | } |
57 | ||
b2bb40ce MR |
58 | /* Handle to the original authenticator for security1 protocol */ |
59 | static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL; | |
60 | ||
61 | /* Handle to the original authenticator for security2 protocol */ | |
62 | static EFI_SECURITY2_FILE_AUTHENTICATION es2fa = NULL; | |
63 | ||
64 | /* | |
65 | * Perform shim/MOK and Secure Boot authentication on a binary that's already been | |
66 | * loaded into memory. This function does the platform SB authentication first | |
67 | * but preserves its return value in case of its failure, so that it can be | |
68 | * returned in case of a shim/MOK authentication failure. This is done because | |
69 | * the SB failure code seems to vary from one implementation to another, and I | |
70 | * don't want to interfere with that at this time. | |
71 | */ | |
72 | static EFIAPI EFI_STATUS security2_policy_authentication (const EFI_SECURITY2_PROTOCOL *this, | |
73 | const EFI_DEVICE_PATH_PROTOCOL *device_path, | |
70cd15e9 | 74 | void *file_buffer, UINTN file_size, BOOLEAN boot_policy) { |
8599bdb6 | 75 | EFI_STATUS err; |
b2bb40ce | 76 | |
508df915 | 77 | assert(this); |
de829ff5 | 78 | /* device_path and file_buffer may be NULL */ |
508df915 | 79 | |
b2bb40ce | 80 | /* Chain original security policy */ |
8599bdb6 | 81 | err = es2fa(this, device_path, file_buffer, file_size, boot_policy); |
b2bb40ce MR |
82 | |
83 | /* if OK, don't bother with MOK check */ | |
2a5e4fe4 | 84 | if (err == EFI_SUCCESS) |
8599bdb6 | 85 | return err; |
b2bb40ce MR |
86 | |
87 | if (shim_validate(file_buffer, file_size)) | |
88 | return EFI_SUCCESS; | |
89 | ||
8599bdb6 | 90 | return err; |
b2bb40ce MR |
91 | } |
92 | ||
93 | /* | |
94 | * Perform both shim/MOK and platform Secure Boot authentication. This function loads | |
95 | * the file and performs shim/MOK authentication first simply to avoid double loads | |
96 | * of Linux kernels, which are much more likely to be shim/MOK-signed than platform-signed, | |
97 | * since kernels are big and can take several seconds to load on some computers and | |
98 | * filesystems. This also has the effect of returning whatever the platform code is for | |
99 | * authentication failure, be it EFI_ACCESS_DENIED, EFI_SECURITY_VIOLATION, or something | |
100 | * else. (This seems to vary between implementations.) | |
101 | */ | |
db4122d1 | 102 | static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROTOCOL *this, uint32_t authentication_status, |
b2bb40ce | 103 | const EFI_DEVICE_PATH_PROTOCOL *device_path_const) { |
8599bdb6 | 104 | EFI_STATUS err; |
3639d1b0 | 105 | _cleanup_free_ char16_t *dev_path_str = NULL; |
b2bb40ce | 106 | EFI_HANDLE h; |
07d0fde4 | 107 | _cleanup_free_ char *file_buffer = NULL; |
b2bb40ce | 108 | UINTN file_size; |
b2bb40ce | 109 | |
508df915 | 110 | assert(this); |
508df915 | 111 | |
b2bb40ce MR |
112 | if (!device_path_const) |
113 | return EFI_INVALID_PARAMETER; | |
114 | ||
e17fd553 | 115 | EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path_const; |
8599bdb6 | 116 | err = BS->LocateDevicePath(&FileSystemProtocol, &dp, &h); |
2a5e4fe4 | 117 | if (err != EFI_SUCCESS) |
8599bdb6 | 118 | return err; |
b2bb40ce | 119 | |
f747ca3e | 120 | _cleanup_(file_closep) EFI_FILE *root = NULL; |
8599bdb6 JJ |
121 | err = open_volume(h, &root); |
122 | if (err != EFI_SUCCESS) | |
123 | return err; | |
e1e086d1 | 124 | |
41b74a18 | 125 | dev_path_str = DevicePathToStr(dp); |
9f048123 JJ |
126 | if (!dev_path_str) |
127 | return EFI_OUT_OF_RESOURCES; | |
b2bb40ce | 128 | |
8599bdb6 | 129 | err = file_read(root, dev_path_str, 0, 0, &file_buffer, &file_size); |
2a5e4fe4 | 130 | if (err != EFI_SUCCESS) |
8599bdb6 | 131 | return err; |
b2bb40ce MR |
132 | |
133 | if (shim_validate(file_buffer, file_size)) | |
a42d7cf1 | 134 | return EFI_SUCCESS; |
b2bb40ce | 135 | |
a42d7cf1 | 136 | /* Try using the platform's native policy.... */ |
12f32748 | 137 | return esfas(this, authentication_status, device_path_const); |
b2bb40ce MR |
138 | } |
139 | ||
140 | EFI_STATUS security_policy_install(void) { | |
141 | EFI_SECURITY_PROTOCOL *security_protocol; | |
142 | EFI_SECURITY2_PROTOCOL *security2_protocol = NULL; | |
8599bdb6 | 143 | EFI_STATUS err; |
b2bb40ce MR |
144 | |
145 | /* Already Installed */ | |
146 | if (esfas) | |
147 | return EFI_ALREADY_STARTED; | |
148 | ||
149 | /* | |
bceda88b ZJS |
150 | * Don't bother with status here. The call is allowed |
151 | * to fail, since SECURITY2 was introduced in PI 1.2.1. | |
152 | * Use security2_protocol == NULL as indicator. | |
b2bb40ce | 153 | */ |
12f32748 | 154 | BS->LocateProtocol((EFI_GUID*) SECURITY_PROTOCOL2_GUID, NULL, (void**) &security2_protocol); |
b2bb40ce | 155 | |
8599bdb6 | 156 | err = BS->LocateProtocol((EFI_GUID*) SECURITY_PROTOCOL_GUID, NULL, (void**) &security_protocol); |
b2bb40ce | 157 | /* This one is mandatory, so there's a serious problem */ |
2a5e4fe4 | 158 | if (err != EFI_SUCCESS) |
8599bdb6 | 159 | return err; |
b2bb40ce | 160 | |
bceda88b ZJS |
161 | esfas = security_protocol->FileAuthenticationState; |
162 | security_protocol->FileAuthenticationState = security_policy_authentication; | |
163 | ||
164 | if (security2_protocol) { | |
b2bb40ce MR |
165 | es2fa = security2_protocol->FileAuthentication; |
166 | security2_protocol->FileAuthentication = security2_policy_authentication; | |
167 | } | |
168 | ||
b2bb40ce MR |
169 | return EFI_SUCCESS; |
170 | } |