Some past security reviews carried out for UEFI Secure Boot signing submissions have covered specific drivers or functional areas of iPXE. Mark all of the files comprising these areas as permitted for UEFI Secure Boot. Signed-off-by: Michael Brown <mcb30@ipxe.org>
291 lines
7.5 KiB
C
291 lines
7.5 KiB
C
/*
|
|
* Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*
|
|
* You can also choose to distribute this program under the terms of
|
|
* the Unmodified Binary Distribution Licence (as given in the file
|
|
* COPYING.UBDL), provided that you have satisfied its requirements.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|
FILE_SECBOOT ( PERMITTED );
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ipxe/acpi.h>
|
|
#include <ipxe/base16.h>
|
|
#include <ipxe/ethernet.h>
|
|
#include <ipxe/if_ether.h>
|
|
#include <ipxe/settings.h>
|
|
#include <ipxe/acpimac.h>
|
|
|
|
/** @file
|
|
*
|
|
* ACPI MAC address
|
|
*
|
|
*/
|
|
|
|
/** Colour for debug messages */
|
|
#define colour FADT_SIGNATURE
|
|
|
|
/** AMAC signature */
|
|
#define AMAC_SIGNATURE ACPI_SIGNATURE ( 'A', 'M', 'A', 'C' )
|
|
|
|
/** MACA signature */
|
|
#define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' )
|
|
|
|
/** RTMA signature */
|
|
#define RTMA_SIGNATURE ACPI_SIGNATURE ( 'R', 'T', 'M', 'A' )
|
|
|
|
/** Maximum number of bytes to skip after ACPI signature
|
|
*
|
|
* This is entirely empirical.
|
|
*/
|
|
#define ACPIMAC_MAX_SKIP 8
|
|
|
|
/** An ACPI MAC extraction mechanism */
|
|
struct acpimac_extractor {
|
|
/** Prefix string */
|
|
const char *prefix;
|
|
/** Encoded MAC length */
|
|
size_t len;
|
|
/** Decode MAC
|
|
*
|
|
* @v mac Encoded MAC
|
|
* @v hw_addr MAC address to fill in
|
|
* @ret rc Return status code
|
|
*/
|
|
int ( * decode ) ( const char *mac, uint8_t *hw_addr );
|
|
};
|
|
|
|
/**
|
|
* Decode Base16-encoded MAC address
|
|
*
|
|
* @v mac Encoded MAC
|
|
* @v hw_addr MAC address to fill in
|
|
* @ret rc Return status code
|
|
*/
|
|
static int acpimac_decode_base16 ( const char *mac, uint8_t *hw_addr ) {
|
|
int len;
|
|
int rc;
|
|
|
|
/* Attempt to base16-decode MAC address */
|
|
len = base16_decode ( mac, hw_addr, ETH_ALEN );
|
|
if ( len < 0 ) {
|
|
rc = len;
|
|
DBGC ( colour, "ACPI could not decode base16 MAC \"%s\": %s\n",
|
|
mac, strerror ( rc ) );
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Decode raw MAC address
|
|
*
|
|
* @v mac Encoded MAC
|
|
* @v hw_addr MAC address to fill in
|
|
* @ret rc Return status code
|
|
*/
|
|
static int acpimac_decode_raw ( const char *mac, uint8_t *hw_addr ) {
|
|
|
|
memcpy ( hw_addr, mac, ETH_ALEN );
|
|
return 0;
|
|
}
|
|
|
|
/** "_AUXMAC_" extraction mechanism */
|
|
static struct acpimac_extractor acpimac_auxmac = {
|
|
.prefix = "_AUXMAC_#",
|
|
.len = ( ETH_ALEN * 2 ),
|
|
.decode = acpimac_decode_base16,
|
|
};
|
|
|
|
/** "_RTXMAC_" extraction mechanism */
|
|
static struct acpimac_extractor acpimac_rtxmac = {
|
|
.prefix = "_RTXMAC_#",
|
|
.len = ETH_ALEN,
|
|
.decode = acpimac_decode_raw,
|
|
};
|
|
|
|
/**
|
|
* Extract MAC address from DSDT/SSDT
|
|
*
|
|
* @v zsdt DSDT or SSDT
|
|
* @v len Length of DSDT/SSDT
|
|
* @v offset Offset of signature within DSDT/SSDT
|
|
* @v data Data buffer
|
|
* @v extractor ACPI MAC address extractor
|
|
* @ret rc Return status code
|
|
*
|
|
* Some vendors provide a "system MAC address" within the DSDT/SSDT,
|
|
* to be used to override the MAC address for a USB docking station.
|
|
*
|
|
* A full implementation would require an ACPI bytecode interpreter,
|
|
* since at least one OEM allows the MAC address to be constructed by
|
|
* executable ACPI bytecode (rather than a fixed data structure).
|
|
*
|
|
* We instead attempt to extract a plausible-looking "_AUXMAC_#.....#"
|
|
* string that appears shortly after an "AMAC" or "MACA" signature.
|
|
* This should work for most implementations encountered in practice.
|
|
*/
|
|
static int acpimac_extract ( const struct acpi_header *zsdt, size_t len,
|
|
size_t offset, void *data,
|
|
struct acpimac_extractor *extractor ) {
|
|
size_t prefix_len = strlen ( extractor->prefix );
|
|
uint8_t *hw_addr = data;
|
|
size_t skip = 0;
|
|
char buf[ prefix_len + extractor->len + 1 /* "#" */ + 1 /* NUL */ ];
|
|
char *mac = &buf[prefix_len];
|
|
int rc;
|
|
|
|
/* Skip signature and at least one tag byte */
|
|
offset += ( 4 /* signature */ + 1 /* tag byte */ );
|
|
|
|
/* Scan for suitable string close to signature */
|
|
for ( skip = 0 ;
|
|
( ( skip < ACPIMAC_MAX_SKIP ) &&
|
|
( offset + skip + sizeof ( buf ) ) <= len ) ;
|
|
skip++ ) {
|
|
|
|
/* Read value */
|
|
memcpy ( buf, ( ( ( const void * ) zsdt ) + offset + skip ),
|
|
sizeof ( buf ) );
|
|
|
|
/* Check for expected format */
|
|
if ( memcmp ( buf, extractor->prefix, prefix_len ) != 0 )
|
|
continue;
|
|
if ( buf[ sizeof ( buf ) - 2 ] != '#' )
|
|
continue;
|
|
if ( buf[ sizeof ( buf ) - 1 ] != '\0' )
|
|
continue;
|
|
DBGC ( colour, "ACPI found MAC:\n" );
|
|
DBGC_HDA ( colour, ( offset + skip ), buf, sizeof ( buf ) );
|
|
|
|
/* Terminate MAC address string */
|
|
mac[extractor->len] = '\0';
|
|
|
|
/* Decode MAC address */
|
|
if ( ( rc = extractor->decode ( mac, hw_addr ) ) != 0 )
|
|
return rc;
|
|
|
|
/* Check MAC address validity */
|
|
if ( ! is_valid_ether_addr ( hw_addr ) ) {
|
|
DBGC ( colour, "ACPI has invalid MAC %s\n",
|
|
eth_ntoa ( hw_addr ) );
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
/**
|
|
* Extract "_AUXMAC_" MAC address from DSDT/SSDT
|
|
*
|
|
* @v zsdt DSDT or SSDT
|
|
* @v len Length of DSDT/SSDT
|
|
* @v offset Offset of signature within DSDT/SSDT
|
|
* @v data Data buffer
|
|
* @ret rc Return status code
|
|
*/
|
|
static int acpimac_extract_auxmac ( const struct acpi_header *zsdt,
|
|
size_t len, size_t offset, void *data ) {
|
|
|
|
return acpimac_extract ( zsdt, len, offset, data, &acpimac_auxmac );
|
|
}
|
|
|
|
/**
|
|
* Extract "_RTXMAC_" MAC address from DSDT/SSDT
|
|
*
|
|
* @v zsdt DSDT or SSDT
|
|
* @v len Length of DSDT/SSDT
|
|
* @v offset Offset of signature within DSDT/SSDT
|
|
* @v data Data buffer
|
|
* @ret rc Return status code
|
|
*/
|
|
static int acpimac_extract_rtxmac ( const struct acpi_header *zsdt,
|
|
size_t len, size_t offset, void *data ) {
|
|
|
|
return acpimac_extract ( zsdt, len, offset, data, &acpimac_rtxmac );
|
|
}
|
|
|
|
/**
|
|
* Extract MAC address from DSDT/SSDT
|
|
*
|
|
* @v hw_addr MAC address to fill in
|
|
* @ret rc Return status code
|
|
*/
|
|
int acpi_mac ( uint8_t *hw_addr ) {
|
|
int rc;
|
|
|
|
/* Look for an "AMAC" address */
|
|
if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr,
|
|
acpimac_extract_auxmac ) ) == 0 )
|
|
return 0;
|
|
|
|
/* Look for a "MACA" address */
|
|
if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr,
|
|
acpimac_extract_auxmac ) ) == 0 )
|
|
return 0;
|
|
|
|
/* Look for a "RTMA" address */
|
|
if ( ( rc = acpi_extract ( RTMA_SIGNATURE, hw_addr,
|
|
acpimac_extract_rtxmac ) ) == 0 )
|
|
return 0;
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
/**
|
|
* Fetch system MAC address setting
|
|
*
|
|
* @v data Buffer to fill with setting data
|
|
* @v len Length of buffer
|
|
* @ret len Length of setting data, or negative error
|
|
*/
|
|
static int sysmac_fetch ( void *data, size_t len ) {
|
|
uint8_t mac[ETH_ALEN];
|
|
int rc;
|
|
|
|
/* Try fetching ACPI MAC address */
|
|
if ( ( rc = acpi_mac ( mac ) ) != 0 )
|
|
return rc;
|
|
|
|
/* Return MAC address */
|
|
if ( len > sizeof ( mac ) )
|
|
len = sizeof ( mac );
|
|
memcpy ( data, mac, len );
|
|
return ( sizeof ( mac ) );
|
|
}
|
|
|
|
/** System MAC address setting */
|
|
const struct setting sysmac_setting __setting ( SETTING_MISC, sysmac ) = {
|
|
.name = "sysmac",
|
|
.description = "System MAC",
|
|
.type = &setting_type_hex,
|
|
.scope = &builtin_scope,
|
|
};
|
|
|
|
/** System MAC address built-in setting */
|
|
struct builtin_setting sysmac_builtin_setting __builtin_setting = {
|
|
.setting = &sysmac_setting,
|
|
.fetch = sysmac_fetch,
|
|
};
|