mirror of
https://github.com/ipxe/ipxe
synced 2026-10-07 15:05:42 +03:00
[uri] Add support for "data:" URIs
Add support for "data:" URIs as defined in RFC 2397. These can be used to construct image content under control of an iPXE script. For example: # Inject the message "Hello from iPXE" as /etc/motd initrd -n motd data:,Hello%20from%20iPXE%0A /etc/motd Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -127,6 +127,9 @@ REQUIRE_OBJECT ( nfs_open );
|
||||
#ifdef DOWNLOAD_PROTO_SLAM
|
||||
REQUIRE_OBJECT ( slam );
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_DATA
|
||||
REQUIRE_OBJECT ( datauri );
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Drag in all requested SAN boot protocols
|
||||
|
||||
@@ -46,6 +46,7 @@ FILE_SECBOOT ( PERMITTED );
|
||||
//#define DOWNLOAD_PROTO_FTP /* File Transfer Protocol */
|
||||
//#define DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */
|
||||
//#define DOWNLOAD_PROTO_NFS /* Network File System Protocol */
|
||||
#define DOWNLOAD_PROTO_DATA /* Inline Data */
|
||||
|
||||
/* Protocols supported only on platforms with filesystem abstractions */
|
||||
#if defined ( PLATFORM_efi )
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2026 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 (at your option) 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 <strings.h>
|
||||
#include <errno.h>
|
||||
#include <ipxe/base64.h>
|
||||
#include <ipxe/blob.h>
|
||||
#include <ipxe/open.h>
|
||||
#include <ipxe/datauri.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Data URIs
|
||||
*
|
||||
* The "data" URI scheme is defined in RFC 2397. In the interest of
|
||||
* reducing code size, we support only a practical subset of the
|
||||
* specification. Media types will be ignored (and must not contain a
|
||||
* literal comma character). Base64 encoding is supported.
|
||||
*
|
||||
* URI encoding will already have been stripped by the URI parser.
|
||||
* Special characters may therefore be used in the URI string
|
||||
* (e.g. "hello%20world"). However, URI-encoded NUL bytes (%00) will
|
||||
* not work as expected, since they will be interpreted as terminating
|
||||
* the URI string. If NUL bytes are required, then Base64 encoding
|
||||
* must be used instead.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse data URI
|
||||
*
|
||||
* @v uri Data URI
|
||||
* @v buf Buffer to fill in
|
||||
* @ret len Length of data, or negative error
|
||||
*
|
||||
* The buffer length must be at least the size reported by
|
||||
* datauri_max_len().
|
||||
*/
|
||||
int datauri_parse ( struct uri *uri, void *buf ) {
|
||||
static const char b64marker[7] = ";base64";
|
||||
const char *comma;
|
||||
const char *encoded;
|
||||
size_t prefix_len;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( ! uri->opaque ) {
|
||||
DBGC ( uri, "DATA %p has no opaque part\n", uri );
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Locate encoded string */
|
||||
comma = strchr ( uri->opaque, ',' );
|
||||
if ( ! comma ) {
|
||||
DBGC ( uri, "DATA %p has no comma separator\n", uri );
|
||||
return -EINVAL;
|
||||
}
|
||||
prefix_len = ( comma - uri->opaque );
|
||||
encoded = ( comma + 1 );
|
||||
len = strlen ( encoded );
|
||||
|
||||
/* Decode string */
|
||||
if ( ( prefix_len >= sizeof ( b64marker ) ) &&
|
||||
( strncasecmp ( ( comma - sizeof ( b64marker ) ), b64marker,
|
||||
sizeof ( b64marker ) ) == 0 ) ) {
|
||||
|
||||
/* Decode Base64 string */
|
||||
len = base64_decode ( encoded, buf, len );
|
||||
if ( len < 0 ) {
|
||||
rc = len;
|
||||
DBGC ( uri, "DATA %p could not decode Base64: %s\n",
|
||||
uri, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* Copy raw string */
|
||||
strcpy ( buf, encoded );
|
||||
}
|
||||
|
||||
DBGC ( uri, "DATA %p decoded \"%s\":\n", uri, uri->opaque );
|
||||
DBGC_HDA ( uri, 0, buf, len );
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open data URI
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v uri URI
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int datauri_open ( struct interface *xfer, struct uri *uri ) {
|
||||
void *data;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
/* Allocate space for parsed data */
|
||||
data = malloc ( datauri_max_len ( uri ) );
|
||||
if ( ! data ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
/* Parse data */
|
||||
len = datauri_parse ( uri, data );
|
||||
if ( len < 0 ) {
|
||||
rc = len;
|
||||
goto err_parse;
|
||||
}
|
||||
|
||||
/* Open downloadable blob */
|
||||
if ( ( rc = blob_open ( xfer, data, len ) ) != 0 )
|
||||
goto err_open;
|
||||
|
||||
err_open:
|
||||
err_parse:
|
||||
free ( data );
|
||||
err_alloc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** Data URI opener */
|
||||
struct uri_opener data_uri_opener __uri_opener = {
|
||||
.scheme = "data",
|
||||
.open = datauri_open,
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef _IPXE_DATAURI_H
|
||||
#define _IPXE_DATAURI_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Data URIs
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
FILE_SECBOOT ( PERMITTED );
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ipxe/uri.h>
|
||||
|
||||
/**
|
||||
* Get maximum length of parsed data URI
|
||||
*
|
||||
* @v uri Data URI
|
||||
* @ret max_len Maximum length
|
||||
*/
|
||||
static inline size_t datauri_max_len ( struct uri *uri ) {
|
||||
|
||||
/* Neither URI decoding nor base64 decoding can ever expand data */
|
||||
return ( uri->opaque ? strlen ( uri->opaque ) : 0 );
|
||||
}
|
||||
|
||||
extern int datauri_parse ( struct uri *uri, void *buf );
|
||||
|
||||
#endif /* _IPXE_DATAURI_H */
|
||||
@@ -92,6 +92,7 @@ FILE_SECBOOT ( PERMITTED );
|
||||
#define ERRFILE_spcr ( ERRFILE_CORE | 0x00330000 )
|
||||
#define ERRFILE_disklog ( ERRFILE_CORE | 0x00340000 )
|
||||
#define ERRFILE_efi_disklog ( ERRFILE_CORE | 0x00350000 )
|
||||
#define ERRFILE_datauri ( ERRFILE_CORE | 0x00360000 )
|
||||
|
||||
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
|
||||
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2026 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
|
||||
*
|
||||
* Data URI tests
|
||||
*
|
||||
*/
|
||||
|
||||
/* Forcibly enable assertions */
|
||||
#undef NDEBUG
|
||||
|
||||
#include <string.h>
|
||||
#include <ipxe/uri.h>
|
||||
#include <ipxe/datauri.h>
|
||||
#include <ipxe/test.h>
|
||||
|
||||
/** Define inline data */
|
||||
#define DATA(...) { __VA_ARGS__ }
|
||||
|
||||
/**
|
||||
* Report result of data URI test
|
||||
*
|
||||
* @v uristring URI string
|
||||
* @v expected Expected parsed data
|
||||
* @v expected_len Length of expected parsed data
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
*/
|
||||
static void datauri_okx ( const char *uristring, const void *expected,
|
||||
size_t expected_len, const char *file,
|
||||
unsigned int line ) {
|
||||
uint8_t actual[ strlen ( uristring ) /* more than enough */ ];
|
||||
struct uri *uri;
|
||||
int len;
|
||||
|
||||
/* Parse URI */
|
||||
uri = parse_uri ( uristring );
|
||||
okx ( uri != NULL, file, line );
|
||||
okx ( uri->scheme != NULL, file, line );
|
||||
okx ( uri->opaque != NULL, file, line );
|
||||
okx ( strcmp ( uri->scheme, "data" ) == 0, file, line );
|
||||
|
||||
/* Sanity check */
|
||||
okx ( datauri_max_len ( uri ) <= sizeof ( actual ), file, line );
|
||||
okx ( datauri_max_len ( uri ) >= expected_len, file, line );
|
||||
|
||||
/* Parse data URI */
|
||||
len = datauri_parse ( uri, actual );
|
||||
okx ( len >= 0, file, line );
|
||||
okx ( ( ( size_t ) len ) == expected_len, file, line );
|
||||
okx ( memcmp ( actual, expected, expected_len ) == 0, file, line );
|
||||
|
||||
/* Drop reference */
|
||||
uri_put ( uri );
|
||||
}
|
||||
#define datauri_ok( uristring, EXPECTED ) do { \
|
||||
static const uint8_t expected[] = EXPECTED; \
|
||||
datauri_okx ( uristring, expected, sizeof ( expected ), \
|
||||
__FILE__, __LINE__ ); \
|
||||
} while ( 0 )
|
||||
|
||||
/**
|
||||
* Report result of data URI failure test
|
||||
*
|
||||
* @v uristring URI string
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
*/
|
||||
static void datauri_fail_okx ( const char *uristring, const char *file,
|
||||
unsigned int line ) {
|
||||
uint8_t actual[ strlen ( uristring ) /* more than enough */ ];
|
||||
struct uri *uri;
|
||||
int len;
|
||||
|
||||
/* Parse URI */
|
||||
uri = parse_uri ( uristring );
|
||||
okx ( uri != NULL, file, line );
|
||||
okx ( uri->scheme != NULL, file, line );
|
||||
okx ( uri->opaque != NULL, file, line );
|
||||
okx ( strcmp ( uri->scheme, "data" ) == 0, file, line );
|
||||
|
||||
/* Sanity check */
|
||||
okx ( datauri_max_len ( uri ) <= sizeof ( actual ), file, line );
|
||||
|
||||
/* Parse data URI */
|
||||
len = datauri_parse ( uri, actual );
|
||||
okx ( len < 0, file, line );
|
||||
|
||||
/* Drop reference */
|
||||
uri_put ( uri );
|
||||
}
|
||||
#define datauri_fail_ok( uristring ) \
|
||||
datauri_fail_okx ( uristring, __FILE__, __LINE__ )
|
||||
|
||||
/**
|
||||
* Perform data URI self-test
|
||||
*
|
||||
*/
|
||||
static void datauri_test_exec ( void ) {
|
||||
|
||||
/* Empty data */
|
||||
datauri_ok ( "data:,", DATA() );
|
||||
datauri_ok ( "data:;base64,", DATA() );
|
||||
datauri_ok ( "data:text/plain,", DATA() );
|
||||
|
||||
/* Simple values */
|
||||
datauri_ok ( "data:,hello%20world%21",
|
||||
DATA ( 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l',
|
||||
'd', '!' ) );
|
||||
datauri_ok ( "data:text/plain,simple",
|
||||
DATA ( 's', 'i', 'm', 'p', 'l', 'e' ) );
|
||||
datauri_ok ( "data:text/plain;charset=US-ASCII;base64,ZW5jb2RlZA==",
|
||||
DATA ( 'e', 'n', 'c', 'o', 'd', 'e', 'd' ) );
|
||||
datauri_ok ( "data:application/octet-string;base64,d2l0aABudWw=",
|
||||
DATA ( 'w', 'i', 't', 'h', 0x00, 'n', 'u', 'l' ) );
|
||||
|
||||
/* Invalid values */
|
||||
datauri_fail_ok ( "data:" );
|
||||
datauri_fail_ok ( "data:;base64,INVALID===" );
|
||||
}
|
||||
|
||||
/** Data URI self-test */
|
||||
struct self_test datauri_test __self_test = {
|
||||
.name = "datauri",
|
||||
.exec = datauri_test_exec,
|
||||
};
|
||||
@@ -94,3 +94,4 @@ REQUIRE_OBJECT ( ecdsa_test );
|
||||
REQUIRE_OBJECT ( hkdf_test );
|
||||
REQUIRE_OBJECT ( ffdhe_test );
|
||||
REQUIRE_OBJECT ( mime_test );
|
||||
REQUIRE_OBJECT ( datauri_test );
|
||||
|
||||
Reference in New Issue
Block a user