Compare commits

...

3 Commits

Author SHA1 Message Date
e7595fe88d [menu] Allow a post-activity timeout to be defined
Allow the "--retimeout" option to be used to specify a timeout value
that will be (re)applied after each keypress activity.  This allows
script authors to ensure that a single (potentially accidental)
keypress will not pause the boot process indefinitely.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2025-02-19 13:12:29 +00:00
ccd6200549 [crypto] Start up RBG on demand if needed
The ANS X9.82 specification implicitly assumes that the RBG_Startup
function will be called before it is needed, and includes checks to
make sure that Generate_function fails if this has not happened.
However, there is no well-defined point at which the RBG_Startup
function is to be called: it's just assumed that this happens as part
of system startup.

We currently call RBG_Startup to instantiate the DRBG as an iPXE
startup function, with the corresponding shutdown function
uninstantiating the DRBG.  This works for most use cases, and avoids
an otherwise unexpected user-visible delay when a caller first
attempts to use the DRBG (e.g. by attempting an HTTPS download).

The download of autoexec.ipxe for UEFI is triggered by the EFI root
bus probe in efi_probe().  Both the root bus probe and the RBG startup
function run at STARTUP_NORMAL, so there is no defined ordering
between them.  If the base URI for autoexec.ipxe uses HTTPS, then this
may cause random bits to be requested before the RBG has been started.

Extend the logic in rbg_generate() to automatically start up the RBG
if startup has not already been attempted.  If startup fails
(e.g. because the entropy source is broken), then do not automatically
retry since this could result in extremely long delays waiting for
entropy that will never arrive.

Reported-by: Michael Niehaus <niehaus@live.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2025-02-18 15:38:54 +00:00
b35300fc67 [efi] Increase download timeout for autoexec.ipxe
In almost all cases, the download timeout for autoexec.ipxe is
irrelevant: the operation will either succeed or fail relatively
quickly (e.g. due to a nonexistent file).  The overall download
timeout exists only to ensure that an unattended or headless system
will not wait indefinitely in the case of a degenerate network
response (e.g. an HTTP server that returns an endless trickle of data
using chunked transfer encoding without ever reaching the end of the
file).

The current download timeout is too short if PeerDist content encoding
is enabled, since the overall download will abort before the first
peer discovery attempt has completed, and without allowing sufficient
time for an origin server range request.

The single timeout value is currently used for both the download
timeout and the sync timeout.  The latter timeout exists only to allow
network communication to be gracefully quiesced before removing the
temporary MNP network device, and may safely be shortened without
affecting functionality.

Fix by increasing the download timeout from two seconds to 30 seconds,
and defining a separate one-second timeout for the sync operation.

Reported-by: Michael Niehaus <niehaus@live.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2025-02-17 13:30:27 +00:00
6 changed files with 68 additions and 34 deletions

View File

@ -75,6 +75,9 @@ static int rbg_startup ( void ) {
int len;
int rc;
/* Record that startup has been attempted (even if unsuccessful) */
rbg.started = 1;
/* Try to obtain system UUID for use as personalisation
* string, in accordance with ANS X9.82 Part 3-2007 Section
* 8.5.2. If no UUID is available, proceed without a
@ -97,6 +100,33 @@ static int rbg_startup ( void ) {
return 0;
}
/**
* Generate bits using RBG
*
* @v additional Additional input
* @v additional_len Length of additional input
* @v prediction_resist Prediction resistance is required
* @v data Output buffer
* @v len Length of output buffer
* @ret rc Return status code
*
* This is the RBG_Generate function defined in ANS X9.82 Part 4
* (April 2011 Draft) Section 9.1.2.2.
*/
int rbg_generate ( const void *additional, size_t additional_len,
int prediction_resist, void *data, size_t len ) {
/* Attempt startup, if not already attempted */
if ( ! rbg.started )
rbg_startup();
/* Generate bits. The DRBG will itself return an error if it
* is not valid (e.g. due to an instantiation failure).
*/
return drbg_generate ( &rbg.state, additional, additional_len,
prediction_resist, data, len );
}
/**
* Shut down RBG
*
@ -105,16 +135,21 @@ static void rbg_shutdown ( void ) {
/* Uninstantiate DRBG */
drbg_uninstantiate ( &rbg.state );
/* Clear startup attempted flag */
rbg.started = 0;
}
/** RBG startup function */
static void rbg_startup_fn ( void ) {
/* Start up RBG. There is no way to report an error at this
* stage, but a failed startup will result in an invalid DRBG
* that refuses to generate bits.
/* Start up RBG (if not already started on demand). There is
* no way to report an error at this stage, but a failed
* startup will result in an invalid DRBG that refuses to
* generate bits.
*/
rbg_startup();
if ( ! rbg.started )
rbg_startup();
}
/** RBG shutdown function */

View File

@ -207,8 +207,10 @@ static int item_exec ( int argc, char **argv ) {
struct choose_options {
/** Dynamic user interface name */
char *dynui;
/** Timeout */
/** Initial timeout */
unsigned long timeout;
/** Post-activity timeout */
unsigned long retimeout;
/** Default selection */
char *select;
/** Keep dynamic user interface */
@ -223,6 +225,8 @@ static struct option_descriptor choose_opts[] = {
struct choose_options, select, parse_string ),
OPTION_DESC ( "timeout", 't', required_argument,
struct choose_options, timeout, parse_timeout ),
OPTION_DESC ( "retimeout", 'r', required_argument,
struct choose_options, retimeout, parse_timeout ),
OPTION_DESC ( "keep", 'k', no_argument,
struct choose_options, keep, parse_flag ),
};
@ -259,8 +263,8 @@ static int choose_exec ( int argc, char **argv ) {
goto err_parse_dynui;
/* Show as menu */
if ( ( rc = show_menu ( dynui, opts.timeout, opts.select,
&item ) ) != 0 )
if ( ( rc = show_menu ( dynui, opts.timeout, opts.retimeout,
opts.select, &item ) ) != 0 )
goto err_show_menu;
/* Apply default type if necessary */

View File

@ -53,8 +53,10 @@ struct menu_ui {
struct dynamic_ui *dynui;
/** Jump scroller */
struct jump_scroller scroll;
/** Timeout (0=indefinite) */
/** Remaining timeout (0=indefinite) */
unsigned long timeout;
/** Post-activity timeout (0=indefinite) */
unsigned long retimeout;
};
/**
@ -180,8 +182,8 @@ static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) {
if ( ui->timeout == 0 )
chosen = 1;
} else {
/* Cancel any timeout */
ui->timeout = 0;
/* Reset timeout after activity */
ui->timeout = ui->retimeout;
/* Handle scroll keys */
move = jump_scroll_key ( &ui->scroll, key );
@ -241,12 +243,14 @@ static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) {
* Show menu
*
* @v dynui Dynamic user interface
* @v timeout Timeout period, in ticks (0=indefinite)
* @v timeout Initial timeout period, in ticks (0=indefinite)
* @v retimeout Post-activity timeout period, in ticks (0=indefinite)
* @ret selected Selected item
* @ret rc Return status code
*/
int show_menu ( struct dynamic_ui *dynui, unsigned long timeout,
const char *select, struct dynamic_item **selected ) {
unsigned long retimeout, const char *select,
struct dynamic_item **selected ) {
struct dynamic_item *item;
struct menu_ui ui;
char buf[ MENU_COLS + 1 /* NUL */ ];
@ -258,6 +262,8 @@ int show_menu ( struct dynamic_ui *dynui, unsigned long timeout,
ui.dynui = dynui;
ui.scroll.rows = MENU_ROWS;
ui.timeout = timeout;
ui.retimeout = retimeout;
list_for_each_entry ( item, &dynui->items, list ) {
if ( item->name ) {
if ( ! named_count )

View File

@ -60,7 +60,8 @@ extern struct dynamic_item * dynui_item ( struct dynamic_ui *dynui,
extern struct dynamic_item * dynui_shortcut ( struct dynamic_ui *dynui,
int key );
extern int show_menu ( struct dynamic_ui *dynui, unsigned long timeout,
const char *select, struct dynamic_item **selected );
unsigned long retimeout, const char *select,
struct dynamic_item **selected );
extern int show_form ( struct dynamic_ui *dynui );
#endif /* _IPXE_DYNUI_H */

View File

@ -16,28 +16,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
struct random_bit_generator {
/** DRBG state */
struct drbg_state state;
/** Startup has been attempted */
int started;
};
extern struct random_bit_generator rbg;
/**
* Generate bits using RBG
*
* @v additional Additional input
* @v additional_len Length of additional input
* @v prediction_resist Prediction resistance is required
* @v data Output buffer
* @v len Length of output buffer
* @ret rc Return status code
*
* This is the RBG_Generate function defined in ANS X9.82 Part 4
* (April 2011 Draft) Section 9.1.2.2.
*/
static inline int rbg_generate ( const void *additional, size_t additional_len,
int prediction_resist, void *data,
size_t len ) {
return drbg_generate ( &rbg.state, additional, additional_len,
prediction_resist, data, len );
}
extern int rbg_generate ( const void *additional, size_t additional_len,
int prediction_resist, void *data, size_t len );
#endif /* _IPXE_RBG_H */

View File

@ -43,7 +43,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/** Timeout for autoexec script downloads */
#define EFI_AUTOEXEC_TIMEOUT ( 2 * TICKS_PER_SEC )
#define EFI_AUTOEXEC_TIMEOUT ( 30 * TICKS_PER_SEC )
/** Timeout for autoexec pending operation completion */
#define EFI_AUTOEXEC_SYNC_TIMEOUT ( 1 * TICKS_PER_SEC )
/** Autoexec script image name */
#define EFI_AUTOEXEC_NAME "autoexec.ipxe"
@ -136,7 +139,7 @@ static int efi_autoexec_network ( EFI_HANDLE handle, struct image **image ) {
}
/* Ensure network exchanges have completed */
sync ( EFI_AUTOEXEC_TIMEOUT );
sync ( EFI_AUTOEXEC_SYNC_TIMEOUT );
err_open:
err_cwuri: