diff options
author | Michael Brown <mcb30@ipxe.org> | 2022-01-17 16:17:17 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2022-01-17 16:17:17 +0000 |
commit | 7d27190e9948b1184286714ded580e4852fd830a (patch) | |
tree | 380f3b12f48512d833abdd50e268df7004d92c4f | |
parent | 461e02d371d9b7a31ac0d91eee38a9ff7c07a8be (diff) | |
download | ipxe-autoexec_pxe.tar.gz |
[efi] Attempt to fetch autoexec script via TFTPautoexec_pxe
Attempt to fetch the autoexec.ipxe script via TFTP using the PXE base
code protocol installed on the loaded image's device handle, if
present.
This provides a generic alternative to the use of an embedded script
for chainloaded binaries, which is particularly useful in a UEFI
Secure Boot environment since it allows the script to be modified
without the need to sign a new binary.
As a side effect, this also provides a third method for breaking the
PXE chainloading loop (as an alternative to requiring an embedded
script or custom DHCP server configuration).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/interface/efi/efi_autoexec.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c index 881c30c7e..7180fb842 100644 --- a/src/interface/efi/efi_autoexec.c +++ b/src/interface/efi/efi_autoexec.c @@ -24,11 +24,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> +#include <stdlib.h> #include <errno.h> #include <ipxe/image.h> #include <ipxe/init.h> +#include <ipxe/in.h> +#include <ipxe/settings.h> +#include <ipxe/cachedhcp.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_autoexec.h> +#include <ipxe/efi/Protocol/PxeBaseCode.h> #include <ipxe/efi/Protocol/SimpleFileSystem.h> #include <ipxe/efi/Guid/FileInfo.h> @@ -170,6 +175,146 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { } /** + * Load autoexec script from TFTP server + * + * @v device Device handle + * @ret rc Return status code + */ +static int efi_autoexec_tftp ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + void *interface; + EFI_PXE_BASE_CODE_PROTOCOL *pxe; + } u; + union { + struct in_addr in; + EFI_IP_ADDRESS ip; + } server; + struct settings *settings; + size_t filename_len; + char *filename; + char *sep; + int len; + UINT64 size; + VOID *data; + EFI_STATUS efirc; + int rc; + + /* Open PXE base code protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_pxe_base_code_protocol_guid, + &u.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s has no PXE base code instance: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_pxe; + } + + /* Identify settings block containing cached filename, if any */ + len = fetch_setting ( &cachedhcp_settings, &filename_setting, + &settings, NULL, NULL, 0 ); + if ( len < 0 ) { + rc = len; + DBGC ( device, "EFI %s has no PXE boot filename: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_settings; + } + + /* Allocate filename */ + filename_len = ( len + ( sizeof ( efi_autoexec_name ) - 1 /* NUL */ ) + + 1 /* NUL */ ); + filename = malloc ( filename_len ); + if ( ! filename ) { + rc = -ENOMEM; + goto err_filename; + } + + /* Fetch filename and TFTP server address */ + fetch_string_setting ( settings, &filename_setting, filename, + filename_len ); + memset ( &server, 0, sizeof ( server ) ); + fetch_ipv4_setting ( settings, &next_server_setting, &server.in ); + + /* Update filename to autoexec script name */ + sep = strrchr ( filename, '/' ); + if ( ! sep ) + sep = strrchr ( filename, '\\' ); + if ( ! sep ) + sep = ( filename - 1 ); + strcpy ( ( sep + 1 ), efi_autoexec_name ); + + /* Get file size */ + if ( ( efirc = u.pxe->Mtftp ( u.pxe, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, FALSE, &size, NULL, &server.ip, + ( ( UINT8 * ) filename ), NULL, + FALSE ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not get size of %s:%s: %s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename, strerror ( rc ) ); + goto err_size; + } + + /* Ignore zero-length files */ + if ( ! size ) { + rc = -EINVAL; + DBGC ( device, "EFI %s has zero-length %s:%s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename ); + goto err_empty; + } + + /* Allocate temporary copy */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size, + &data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not allocate %s:%s: %s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename, strerror ( rc ) ); + goto err_alloc; + } + + /* Download file */ + if ( ( efirc = u.pxe->Mtftp ( u.pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE, + data, FALSE, &size, NULL, &server.ip, + ( ( UINT8 * ) filename ), NULL, + FALSE ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not download %s:%s: %s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename, strerror ( rc ) ); + goto err_download; + } + + /* Record autoexec script */ + efi_autoexec = data; + efi_autoexec_len = size; + data = NULL; + DBGC ( device, "EFI %s found %s:%s\n", efi_handle_name ( device ), + inet_ntoa ( server.in ), filename ); + + /* Success */ + rc = 0; + + err_download: + if ( data ) + bs->FreePool ( data ); + err_alloc: + err_empty: + err_size: + free ( filename ); + err_filename: + err_settings: + bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid, + efi_image_handle, device ); + err_pxe: + return rc; +} + +/** * Load autoexec script * * @v device Device handle @@ -186,6 +331,10 @@ int efi_autoexec_load ( EFI_HANDLE device ) { if ( ( rc = efi_autoexec_filesystem ( device ) ) == 0 ) return 0; + /* Try loading via TFTP, if supported */ + if ( ( rc = efi_autoexec_tftp ( device ) ) == 0 ) + return 0; + return -ENOENT; } |