OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
HttpBootCallback.c
Go to the documentation of this file.
1
9
10#include <IndustryStandard/Http11.h>
11#include <Library/HttpLib.h>
12#include <Protocol/Http.h>
13
14#define HTTP_CONTENT_TYPE_APP_EFI "application/efi"
15
17
18STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback;
19STATIC BOOLEAN mUriValidated;
20
21//
22// Abort if we are loading a .dmg and these are banned, or if underlying drivers have
23// allowed http:// in URL but user setting for OpenNetworkBoot does not allow it.
24// If PcdAllowHttpConnections was not set (via NETWORK_ALLOW_HTTP_CONNECTIONS compilation
25// flag) then both HttpDxe and HttpBootDxe will enforce https:// before we get to here.
26//
27EFI_STATUS
29 CHAR16 *Uri,
30 BOOLEAN ShowLog,
31 BOOLEAN *HasDmgExtension
32 )
33{
34 CHAR8 *Match;
35 CHAR8 *Uri8;
36 UINTN UriSize;
37
38 if (gRequireHttpsUri && !HasHttpsUri (Uri)) {
39 //
40 // Do not return ACCESS_DENIED as this will attempt to add authentication to the request.
41 //
42 if (ShowLog) {
43 DEBUG ((DEBUG_INFO, "NETB: Invalid URI https:// is required\n"));
44 }
45
46 return EFI_UNSUPPORTED;
47 }
48
49 UriSize = StrSize (Uri);
50 Uri8 = AllocatePool (UriSize);
51 if (Uri8 == NULL) {
52 return EFI_OUT_OF_RESOURCES;
53 }
54
55 UnicodeStrToAsciiStrS (Uri, Uri8, UriSize);
56
57 Match = ".dmg";
58 *HasDmgExtension = UriFileHasExtension (Uri8, Match);
59 if (!*HasDmgExtension) {
60 Match = ".chunklist";
61 *HasDmgExtension = UriFileHasExtension (Uri8, Match);
62 }
63
64 FreePool (Uri8);
65
67 if (*HasDmgExtension) {
68 if (ShowLog) {
69 DEBUG ((DEBUG_INFO, "NETB: %a file is requested while DMG loading is disabled\n", Match));
70 }
71
72 return EFI_UNSUPPORTED;
73 }
74 }
75
76 return EFI_SUCCESS;
77}
78
79STATIC
80EFI_STATUS
81EFIAPI
83 IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This,
84 IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType,
85 IN BOOLEAN Received,
86 IN UINT32 DataLength,
87 IN VOID *Data OPTIONAL
88 )
89{
90 EFI_STATUS Status;
91 EFI_HTTP_MESSAGE *HttpMessage;
92 EFI_HTTP_HEADER *Header;
93 STATIC BOOLEAN HasDmgExtension = FALSE;
94
95 switch (DataType) {
96 //
97 // Abort if http:// is specified but only https:// is allowed.
98 // Since the URI can come from DHCP boot options this is important for security.
99 // This will fail to be enforced if this callback doesn't get registered, or
100 // isn't used by a given implementation of HTTP Boot (it is used by ours, ofc).
101 // Therefore if a file is returned from HTTP Boot, we check that this
102 // was really hit, and won't load it otherwise. (Which is less ideal than
103 // not even fetching the file, as will happen when this callback is hit.)
104 //
105 // Also abort early if .dmg or .chunklist is found when DmgLoading is disabled.
106 // This is a convenience, we could allow these to load and they would be
107 // rejected eventually anyway.
108 //
109 case HttpBootHttpRequest:
110 if (Data != NULL) {
111 HttpMessage = (EFI_HTTP_MESSAGE *)Data;
112 if (HttpMessage->Data.Request->Url != NULL) {
113 //
114 // Print log messages once on initial access with HTTP HEAD, don't
115 // log on subsequent GET which is an attempt to get file size by
116 // pre-loading entire file for case of chunked encoding (where file
117 // size is not known until it has been transferred).
118 //
119 Status = ValidateDmgAndHttps (
120 HttpMessage->Data.Request->Url,
121 HttpMessage->Data.Request->Method == HttpMethodHead,
122 &HasDmgExtension
123 );
124 if (EFI_ERROR (Status)) {
125 return Status;
126 }
127
128 mUriValidated = TRUE;
129 }
130 }
131
132 break;
133
134 //
135 // Provide fake MIME type of 'application/efi' for .dmg and .chunklist.
136 // This is also a convenience of sorts, in that as long as the user
137 // sets 'application/efi' MIME type for these files on their web server,
138 // they would work anyway.
139 //
140 case HttpBootHttpResponse:
141 if ((Data != NULL) && HasDmgExtension) {
142 //
143 // We do not need to keep modifying Content-Type for subsequent packets.
144 //
145 HasDmgExtension = FALSE;
146
147 HttpMessage = (EFI_HTTP_MESSAGE *)Data;
148 Header = HttpFindHeader (HttpMessage->HeaderCount, HttpMessage->Headers, HTTP_HEADER_CONTENT_TYPE);
149
150 if (Header == NULL) {
151 Header = HttpMessage->Headers;
152 ++HttpMessage->HeaderCount;
153 HttpMessage->Headers = AllocatePool (HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0]));
154 if (HttpMessage->Headers == NULL) {
155 return EFI_OUT_OF_RESOURCES;
156 }
157
158 CopyMem (HttpMessage->Headers, Header, HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0]));
159 Header = &HttpMessage->Headers[HttpMessage->HeaderCount - 1];
160 Header->FieldValue = NULL;
161 Header->FieldName = AllocateCopyPool (L_STR_SIZE (HTTP_HEADER_CONTENT_TYPE), HTTP_HEADER_CONTENT_TYPE);
162 if (Header->FieldName == NULL) {
163 return EFI_OUT_OF_RESOURCES;
164 }
165 } else {
166 ASSERT (Header->FieldValue != NULL);
167 if (AsciiStrCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) != 0) {
168 FreePool (Header->FieldValue);
169 Header->FieldValue = NULL;
170 }
171 }
172
173 if (Header->FieldValue == NULL) {
174 Header->FieldValue = AllocateCopyPool (L_STR_SIZE (HTTP_CONTENT_TYPE_APP_EFI), HTTP_CONTENT_TYPE_APP_EFI);
175 if (Header->FieldValue == NULL) {
176 return EFI_OUT_OF_RESOURCES;
177 }
178 }
179 }
180
181 break;
182
183 default:
184 break;
185 }
186
188 This,
189 DataType,
190 Received,
191 DataLength,
192 Data
193 );
194}
195
196STATIC
197VOID
198EFIAPI
200 IN EFI_EVENT Event,
201 IN VOID *Context
202 )
203{
204 EFI_STATUS Status;
205 EFI_HANDLE LoadFileHandle;
206 EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;
207
208 LoadFileHandle = Context;
209
210 Status = gBS->HandleProtocol (
211 LoadFileHandle,
212 &gEfiHttpBootCallbackProtocolGuid,
213 (VOID **)&HttpBootCallback
214 );
215
216 if (!EFI_ERROR (Status)) {
217 //
218 // Due to a bug in EDK 2 HttpBootUninstallCallback, they do not uninstall
219 // this protocol when they try to. This is okay as long as there is only
220 // one consumer of the protocol, because our hooked version stays installed
221 // and gets reused (found as the already installed protocol) on second
222 // and subsequent tries in HttpBootInstallCallback.
223 // REF: https://edk2.groups.io/g/devel/message/117469
224 // TODO: Add edk2 bugzilla issue.
225 // TODO: To safely allow for more than one consumer while allowing for
226 // their bug, we would need to store multiple original callbacks, one
227 // per http boot callback protocol address. (Otherwise using consumer
228 // A then consumer B, so that we are already installed on both handles,
229 // then consumer A again, will use the original callback for B.)
230 //
231 mOriginalHttpBootCallback = HttpBootCallback->Callback;
232 HttpBootCallback->Callback = OpenNetworkBootHttpBootCallback;
233 }
234}
235
236BOOLEAN
238 VOID
239 )
240{
241 return mUriValidated;
242}
243
244EFI_EVENT
246 EFI_HANDLE LoadFileHandle
247 )
248{
249 EFI_STATUS Status;
250 EFI_EVENT Event;
251 VOID *Registration;
252
253 //
254 // Everything in our callback except https validation is convenient but optional.
255 // So we can make our driver fully usable with some (hypothetical?) http boot
256 // implementation which never hits our callback, as long as we treat the URI as
257 // already validated (even if the callback is never hit) when https validation
258 // is not turned on.
259 //
261
262 Status = gBS->CreateEvent (
263 EVT_NOTIFY_SIGNAL,
264 TPL_CALLBACK,
266 LoadFileHandle,
267 &Event
268 );
269
270 if (EFI_ERROR (Status)) {
271 return NULL;
272 }
273
274 Status = gBS->RegisterProtocolNotify (
275 &gEfiHttpBootCallbackProtocolGuid,
276 Event,
277 &Registration
278 );
279
280 if (EFI_ERROR (Status)) {
281 gBS->CloseEvent (Event);
282 return NULL;
283 }
284
285 return Event;
286}
VENDOR_DEVICE_PATH Header
OC_DMG_LOADING_SUPPORT gDmgLoading
EFI_STATUS ValidateDmgAndHttps(CHAR16 *Uri, BOOLEAN ShowLog, BOOLEAN *HasDmgExtension)
STATIC EFI_STATUS EFIAPI OpenNetworkBootHttpBootCallback(IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This, IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType, IN BOOLEAN Received, IN UINT32 DataLength, IN VOID *Data OPTIONAL)
BOOLEAN UriWasValidated(VOID)
STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback
STATIC VOID EFIAPI NotifyInstallHttpBootCallback(IN EFI_EVENT Event, IN VOID *Context)
STATIC BOOLEAN mUriValidated
EFI_EVENT MonitorHttpBootCallback(EFI_HANDLE LoadFileHandle)
#define HTTP_CONTENT_TYPE_APP_EFI
BOOLEAN UriFileHasExtension(IN CHAR8 *Uri, IN CHAR8 *Ext)
Definition Uri.c:222
BOOLEAN HasHttpsUri(CHAR16 *Uri)
Definition Uri.c:20
BOOLEAN gRequireHttpsUri
OC_DMG_LOADING_SUPPORT
@ OcDmgLoadingDisabled
EFI_BOOT_SERVICES * gBS
#define L_STR_SIZE(String)
Definition OcStringLib.h:35
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
#define ASSERT(x)
Definition coder.h:55