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
|
#ifdef DOWNLOAD_PROTO_SLAM
|
||||||
REQUIRE_OBJECT ( slam );
|
REQUIRE_OBJECT ( slam );
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef DOWNLOAD_PROTO_DATA
|
||||||
|
REQUIRE_OBJECT ( datauri );
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Drag in all requested SAN boot protocols
|
* Drag in all requested SAN boot protocols
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ FILE_SECBOOT ( PERMITTED );
|
|||||||
//#define DOWNLOAD_PROTO_FTP /* File Transfer Protocol */
|
//#define DOWNLOAD_PROTO_FTP /* File Transfer Protocol */
|
||||||
//#define DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */
|
//#define DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */
|
||||||
//#define DOWNLOAD_PROTO_NFS /* Network File System Protocol */
|
//#define DOWNLOAD_PROTO_NFS /* Network File System Protocol */
|
||||||
|
#define DOWNLOAD_PROTO_DATA /* Inline Data */
|
||||||
|
|
||||||
/* Protocols supported only on platforms with filesystem abstractions */
|
/* Protocols supported only on platforms with filesystem abstractions */
|
||||||
#if defined ( PLATFORM_efi )
|
#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_spcr ( ERRFILE_CORE | 0x00330000 )
|
||||||
#define ERRFILE_disklog ( ERRFILE_CORE | 0x00340000 )
|
#define ERRFILE_disklog ( ERRFILE_CORE | 0x00340000 )
|
||||||
#define ERRFILE_efi_disklog ( ERRFILE_CORE | 0x00350000 )
|
#define ERRFILE_efi_disklog ( ERRFILE_CORE | 0x00350000 )
|
||||||
|
#define ERRFILE_datauri ( ERRFILE_CORE | 0x00360000 )
|
||||||
|
|
||||||
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
|
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
|
||||||
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
|
#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 ( hkdf_test );
|
||||||
REQUIRE_OBJECT ( ffdhe_test );
|
REQUIRE_OBJECT ( ffdhe_test );
|
||||||
REQUIRE_OBJECT ( mime_test );
|
REQUIRE_OBJECT ( mime_test );
|
||||||
|
REQUIRE_OBJECT ( datauri_test );
|
||||||
|
|||||||
Reference in New Issue
Block a user