# How Ventoy Implements Password Protection for GRUB2 Boot Menu Options

> Discover how Ventoy implements GRUB2 password protection by parsing JSON, utilizing global variables, and verifying input via ventoy_check_password() for secure boot menu access.

- Repository: [longpanda/Ventoy](https://github.com/ventoy/Ventoy)
- Tags: internals
- Published: 2026-03-01

---

**Ventoy implements password protection by parsing JSON configurations into a `vtoy_password` structure, storing them in global variables like `g_boot_pwd`, and verifying user input through `ventoy_check_password()` before displaying the GRUB2 boot menu.**

Ventoy secures access to its GRUB2 boot menu through a dedicated password plugin integrated into the bootloader's initialization sequence. This system parses configuration files to support plain text, MD5, and salted MD5 authentication methods that can protect the entire menu or specific file types.

## JSON Password Parsing

When the GRUB2 module loads, `ventoy_plugin_init()` reads the plugin JSON and calls `ventoy_plugin_parse_json()` to process password definitions. In [`GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_plugin.c) (lines 926–1013), the function `ventoy_plugin_parse_pwdstr()` converts password strings into a `vtoy_password` structure.

### Supported Password Formats

The parser recognizes three distinct formats defined in the JSON configuration:

- **`txt#<plain-text>`** — Stores the password as raw text
- **`md5#<32-hex-chars>`** — Stores a 16-byte MD5 hash of the password
- **`md5#<salt>#<32-hex-chars>`** — Stores a salted MD5 hash where the salt is prepended before hashing

In lines 49–89 of [`ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_plugin.c), the code examines the prefix using `grub_strncmp` and converts hex characters to bytes via `grub_strtoul` for MD5 formats. If the format is invalid, the function returns non-zero and logs an error.

### Example Configuration

```json
{
    "bootpwd": "txt#mySecret",
    "isopwd": "md5#5f4dcc3b5aa765d61d8327deb882cf99",
    "menupwd": [
        {
            "file": "/Ubuntu/ubuntu-20.04.iso",
            "pwd": "md5#mysalt#e10adc3949ba59abbe56e057f20f883e"
        }
    ]
}

```

## Global Password Storage

After parsing, Ventoy stores passwords in global variables accessible throughout the boot process. The loop at lines 1080–1086 in [`ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_plugin.c) populates these structures:

- **`g_boot_pwd`** — Contains the boot-level password protecting the entire menu
- **`g_file_type_pwd[]`** — An array storing per-file-type passwords (ISO, WIM, IMG, EFI, VHD, VTOY)
- **`g_pwd_head`** — A linked list of `menu_password` structures for path-specific protection under the `"menupwd"` directive

These global variables persist from plugin initialization through the boot menu display, ensuring authentication state is maintained across the GRUB2 module lifecycle.

## Boot-Time Verification

After JSON parsing completes, [`ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_plugin.c) (lines 38–48) checks if a boot password exists and triggers verification:

```c
if (g_boot_pwd.type) {
    grub_printf("\n\n======= %s ======\n\n", grub_env_get("VTOY_TEXT_MENU_VER"));
    if (ventoy_check_password(&g_boot_pwd, 3)) {
        grub_printf("\n!!! Password check failed, will exit after 5 seconds. !!!\n");
        grub_sleep(5);
        grub_exit();
    }
}

```

### The Check Function

The core verification logic resides in `ventoy_check_password()` within [`GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c`](https://github.com/ventoy/Ventoy/blob/main/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c) (lines 306–340). This function accepts a `vtoy_password` pointer and a retry count, then implements the following comparison logic:

- **Plain text** — Uses `grub_strcmp()` for direct string comparison
- **MD5** — Hashes the user input using `grub_crypto_hash(GRUB_MD_MD5, ...)` and compares the 16-byte digest with `grub_memcmp()`
- **Salted MD5** — Concatenates the stored salt with the entered password before hashing and comparison

If verification fails after the specified number of retries (typically 3), the function returns non-zero, triggering `grub_sleep(5)` and `grub_exit()` to abort the boot sequence.

### Input Handling

User input is captured by `ventoy_get_password()` (lines 2192–2203 in [`ventoy_cmd.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_cmd.c)), which checks the `VTOY_SHOW_PASSWORD_ASTERISK` environment variable to determine whether to display asterisks for each character entered. The function calls either `grub_password_get()` or `ventoy_password_get()` based on this configuration.

## Plugson Web Interface Integration

Ventoy provides a web-based configuration tool called **Plugson** to simplify password management without manual JSON editing. The interface defined in [`Plugson/www/plugson_password.html`](https://github.com/ventoy/Ventoy/blob/main/Plugson/www/plugson_password.html) communicates with backend handlers in [`Plugson/src/Web/ventoy_http.c`](https://github.com/ventoy/Ventoy/blob/main/Plugson/src/Web/ventoy_http.c).

When users save passwords through the web UI, the `ventoy_api_password_add` handler (line 2293) receives the JSON payload and populates a `data_password` structure defined in [`ventoy_http.h`](https://github.com/ventoy/Ventoy/blob/main/ventoy_http.h). This structure mirrors the fields used by the GRUB2 module (`bootpwd[256]`, file-type passwords), ensuring consistency between the configuration interface and the boot-time verification logic.

## Summary

- **Parsing** — `ventoy_plugin_parse_pwdstr()` in [`ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_plugin.c) converts JSON password strings into structured `vtoy_password` objects supporting plain text, MD5, and salted MD5 formats
- **Storage** — Global variables `g_boot_pwd` and `g_file_type_pwd[]` store authentication data for boot-level and per-file-type protection
- **Verification** — `ventoy_check_password()` performs hash comparisons using `grub_crypto_hash()` and allows 3 retry attempts before aborting
- **Abort sequence** — Failed authentication triggers a 5-second sleep followed by `grub_exit()`, preventing unauthorized boot menu access

## Frequently Asked Questions

### What password hash formats does Ventoy support?

Ventoy supports three formats defined in [`ventoy_def.h`](https://github.com/ventoy/Ventoy/blob/main/ventoy_def.h): plain text (`VTOY_PASSWORD_TXT`), unsalted MD5 (`VTOY_PASSWORD_MD5`), and salted MD5 (`VTOY_PASSWORD_SALT_MD5`). The parser in [`ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_plugin.c) identifies these via the `txt#`, `md5#`, and `md5#<salt>#` prefixes respectively.

### How many password attempts does Ventoy allow before locking out?

The boot-time check in [`ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_plugin.c) passes a retry count of `3` to `ventoy_check_password()`. After three failed attempts, the function returns non-zero, causing the system to sleep for 5 seconds and then execute `grub_exit()`, effectively halting the boot process.

### Can different passwords be set for specific ISO files or file types?

Yes. The `g_file_type_pwd[]` array stores separate passwords for each file type (ISO, WIM, IMG, EFI, VHD, VTOY), while the `g_pwd_head` linked list manages path-specific passwords configured under the `"menupwd"` JSON key. This allows distinct passwords for individual disk images or entire directories.

### Where is the password verification logic located in the source code?

The verification implementation spans two primary files: [`GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c`](https://github.com/ventoy/Ventoy/blob/main/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c) contains `ventoy_check_password()` (lines 306–340) and `ventoy_get_password()` (lines 2192–2203), while [`ventoy_plugin.c`](https://github.com/ventoy/Ventoy/blob/main/ventoy_plugin.c) handles the initial check trigger (lines 38–48) and JSON parsing (lines 926–1013).