!boards: enforce secure ROMFS passwd and TEA key setup

Remove implicit default credentials and add build-time validation.
Add check_passwd_keys.sh and gen_passwd_keys.sh; run key setup via
passwd_keys.mk before config.h is generated. Mirror the same logic in
cmake/nuttx_add_romfs.cmake for CMake builds.

BREAKING CHANGE: Builds with CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y now
require an explicit admin password and non-default TEA keys. The
Kconfig default password "Administrator" and default TEA keys are no
longer accepted. Fix: run make menuconfig, set Admin password under
Board Selection -> Auto-generate /etc/passwd, enable random TEA keys or
set CONFIG_FSUTILS_PASSWD_KEY1..4 manually, and use NSH login with
Encrypted password file verification.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit is contained in:
Abhishek Mishra 2026-07-05 19:59:44 +00:00 committed by Xiang Xiao
parent a38fd1603c
commit ffa6ba222f
9 changed files with 449 additions and 135 deletions

View file

@ -17,107 +17,77 @@ and host C programs that are important parts of the NuttX build system:
mkpasswd — Build-time ``/etc/passwd`` Generation
-------------------------------------------------
``tools/mkpasswd`` is a C host tool (compiled from ``tools/mkpasswd.c``) that
generates a single ``/etc/passwd`` entry at build time. It is invoked
automatically by the ROMFS build step when
``CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y`` is set.
``tools/mkpasswd`` is a host tool (``tools/mkpasswd.c``) that generates a
single ``/etc/passwd`` entry at build time. It runs automatically when
``CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y``.
Why build-time generation?
~~~~~~~~~~~~~~~~~~~~~~~~~~
The plaintext password is hashed with TEA and is not stored in the firmware.
The build fails if the password is empty or uses known insecure defaults.
Shipping a hard-coded default password in firmware is a well-known security
weakness (CWE-798). By generating the ``/etc/passwd`` entry from a
user-supplied plaintext password at build time, each firmware image carries
unique credentials. The build will fail if the password is left empty,
preventing accidental deployments with no credentials.
Setup
~~~~~
For improved baseline security, the configured password must be at least
8 characters long.
1. Run ``make menuconfig`` and configure:
How it works
~~~~~~~~~~~~
* **Board Selection** → Auto-generate /etc/passwd at build time
* Set the admin password (required, minimum 8 characters)
* Enable random TEA key generation, or set keys manually (see below)
* **Application Configuration** → NSH Library → Console Login
* Set verification method to **Encrypted password file**
* **Application Configuration** → File System Utilities → Password file support
* Enable password file support
1. The host tool reads the plaintext password from
``CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD``.
2. The password is hashed using the Tiny Encryption Algorithm (TEA) — the
same implementation used at runtime in
``libs/libc/misc/lib_tea_encrypt.c`` — with custom base64 encoding
matching ``apps/fsutils/passwd/passwd_encrypt.c``.
3. The resulting hashed entry is written to
``etctmp/<mountpoint>/passwd`` and then embedded into the ROMFS image.
4. The **plaintext password is never stored in the firmware image**.
2. Run ``make``.
TEA encryption keys
~~~~~~~~~~~~~~~~~~~
Keys must match between the build (``mkpasswd``) and the firmware
(``CONFIG_FSUTILS_PASSWD_KEY1..4``). Choose one option:
* **Random generation** (``CONFIG_BOARD_ETC_ROMFS_PASSWD_RANDOMIZE_KEYS=y``):
On the first build, four keys are generated from ``/dev/urandom`` and
written to ``.config``. Key values are not printed in the build log.
Search ``.config`` for ``CONFIG_FSUTILS_PASSWD_KEY`` to view them.
* **Manual**: Set ``CONFIG_FSUTILS_PASSWD_KEY1..4`` under Password file
support to unique non-zero values.
Kconfig options
~~~~~~~~~~~~~~~
Enable the feature and configure credentials via ``make menuconfig``:
.. code:: kconfig
CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y
CONFIG_NSH_CONSOLE_LOGIN=y # required to enforce login prompt
CONFIG_BOARD_ETC_ROMFS_PASSWD_USER="root" # default: root
CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD="<secret>" # required, min length 8
CONFIG_BOARD_ETC_ROMFS_PASSWD_UID=0
CONFIG_BOARD_ETC_ROMFS_PASSWD_GID=0
CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME="/"
CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD="<secret>"
CONFIG_BOARD_ETC_ROMFS_PASSWD_RANDOMIZE_KEYS=y
CONFIG_NSH_CONSOLE_LOGIN=y
CONFIG_NSH_LOGIN_PASSWD=y
CONFIG_FSUTILS_PASSWD=y
The TEA encryption keys can be changed from their defaults via
``CONFIG_FSUTILS_PASSWD_KEY1..4``.
How it works
~~~~~~~~~~~~
1. ``tools/passwd_keys.mk`` checks the password and TEA keys before
``config.h`` is generated.
2. If random generation is enabled and keys are not set, ``gen_passwd_keys.sh``
writes new keys to ``.config``.
3. ``mkpasswd`` hashes the password with the configured TEA keys.
4. The entry is embedded in the ROMFS image as ``/etc/passwd``.
``/etc/passwd`` file format
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: text
user:x:uid:gid:home
Where:
* ``user`` — user name
* ``x`` — TEA-hashed, base64-encoded password
* ``uid`` — numeric user ID
* ``gid`` — numeric group ID
* ``home`` — login directory
Verifying the generated entry
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After enabling ``CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE`` and setting a
password, rebuild and verify:
1. **Configure and build**:
.. code:: console
$ make menuconfig # enable BOARD_ETC_ROMFS_PASSWD_ENABLE and set password
$ make
2. **Inspect the generated passwd line** (written to the board build tree):
.. code:: console
$ cat boards/<arch>/<chip>/<board>/src/etctmp/etc/passwd
root:8Tv+Hbmr3pLVb5HHZgd26D:0:0:/
3. **Verify the plaintext is absent from firmware**:
.. code:: console
$ grep <your-password> boards/<arch>/<chip>/<board>/src/etctmp.c
# must print nothing
user:encrypted_hash:uid:gid:home
Notes on ``savedefconfig``
~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid leaking credentials into board defconfigs, ``make savedefconfig``
does not save the following options in the generated defconfig:
``make savedefconfig`` does not save these options:
* ``CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD``
* ``CONFIG_FSUTILS_PASSWD_KEY1``
* ``CONFIG_FSUTILS_PASSWD_KEY2``
* ``CONFIG_FSUTILS_PASSWD_KEY3``
* ``CONFIG_FSUTILS_PASSWD_KEY4``
* ``CONFIG_FSUTILS_PASSWD_KEY1`` through ``CONFIG_FSUTILS_PASSWD_KEY4``
If you need these values for local development, add them manually to your
local defconfig after running ``make savedefconfig``.
Do not copy them into a defconfig or commit them to version control.

View file

@ -31,6 +31,7 @@ ifeq ($(wildcard .config),)
@echo " tools/configure.sh -L"
else
include .config
include tools/passwd_keys.mk
# Include the correct Makefile for the selected architecture.

View file

@ -36,9 +36,6 @@ $(ETCSRC): $(foreach raw,$(RCRAWS), $(if $(wildcard $(BOARD_DIR)$(DELIM)src$(DEL
$(shell mkdir -p $(dir $(ETCDIR)$(DELIM)$(raw))) \
$(shell cp -rfp $(if $(wildcard $(BOARD_DIR)$(DELIM)src$(DELIM)$(raw)), $(BOARD_DIR)$(DELIM)src$(DELIM)$(raw), $(if $(wildcard $(BOARD_COMMON_DIR)$(DELIM)$(raw)), $(BOARD_COMMON_DIR)$(DELIM)$(raw), $(BOARD_DIR)$(DELIM)src$(DELIM)$(raw))) $(ETCDIR)$(DELIM)$(raw)))
ifeq ($(CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE),y)
ifeq ($(strip $(patsubst "%",%,$(CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD))),)
$(error CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD must be set when BOARD_ETC_ROMFS_PASSWD_ENABLE is enabled. Run 'make menuconfig' and select a password at: Board Selection ---> Auto-generate /etc/passwd at build time ---> Admin password)
endif
$(Q) mkdir -p $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT)
$(Q) $(TOPDIR)$(DELIM)tools$(DELIM)mkpasswd$(HOSTEXEEXT) \
--user $(CONFIG_BOARD_ETC_ROMFS_PASSWD_USER) \
@ -46,10 +43,10 @@ endif
--uid $(CONFIG_BOARD_ETC_ROMFS_PASSWD_UID) \
--gid $(CONFIG_BOARD_ETC_ROMFS_PASSWD_GID) \
--home $(CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY1),--key1 $(CONFIG_FSUTILS_PASSWD_KEY1)) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY2),--key2 $(CONFIG_FSUTILS_PASSWD_KEY2)) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY3),--key3 $(CONFIG_FSUTILS_PASSWD_KEY3)) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY4),--key4 $(CONFIG_FSUTILS_PASSWD_KEY4)) \
--key1 $(CONFIG_FSUTILS_PASSWD_KEY1) \
--key2 $(CONFIG_FSUTILS_PASSWD_KEY2) \
--key3 $(CONFIG_FSUTILS_PASSWD_KEY3) \
--key4 $(CONFIG_FSUTILS_PASSWD_KEY4) \
-o $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT)$(DELIM)passwd
endif
$(Q) genromfs -f romfs.img -d $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT) -V "NSHInitVol"

View file

@ -5592,21 +5592,23 @@ config BOARD_ETC_ROMFS_PASSWD_ENABLE
default n
depends on ETC_ROMFS
---help---
Generate the /etc/passwd file at build time from a user-supplied
password. This avoids shipping a hard-coded default password
(CWE-798). When enabled, the build will fail if no password
is configured, forcing each build to set its own credentials.
Generate /etc/passwd at build time. The password is hashed with TEA
by tools/mkpasswd; the plaintext is not stored in the firmware.
The password is hashed at build time by the host tool
tools/mkpasswd (compiled from tools/mkpasswd.c) using the Tiny
Encryption Algorithm (TEA) — the same algorithm used at runtime
in libs/libc/misc/lib_tea_encrypt.c. The plaintext password is
never stored in the firmware image.
Before building, set:
1. Admin password (below)
2. TEA keys — enable random generation below, or set
CONFIG_FSUTILS_PASSWD_KEY1..4 in Application Configuration ->
File System Utilities -> Password file support
See Documentation/components/passwd_autogen.rst for details.
Also enable NSH login with "Encrypted password file" verification.
Password and keys are not saved in defconfig. Do not commit them.
if BOARD_ETC_ROMFS_PASSWD_ENABLE
comment "--- Step 1: set the admin password below ---"
config BOARD_ETC_ROMFS_PASSWD_USER
string "Admin username"
default "root"
@ -5614,14 +5616,39 @@ config BOARD_ETC_ROMFS_PASSWD_USER
The username for the auto-generated /etc/passwd entry.
config BOARD_ETC_ROMFS_PASSWD_PASSWORD
string "Admin password (required)"
default "Administrator"
string "Admin password (required, no default)"
---help---
The plaintext password for the auto-generated /etc/passwd entry.
This value is hashed with TEA at build time; the plaintext is NOT
stored in the firmware image. The build will fail if this is left
empty or shorter than 8 characters. Set this via
'make menuconfig'.
The plaintext password for the /etc/passwd entry. No default is
provided. The build fails if this is empty or shorter than 8
characters.
Not saved in defconfig.
comment "--- Step 2: choose how to supply the TEA encryption keys ---"
config BOARD_ETC_ROMFS_PASSWD_RANDOMIZE_KEYS
bool "Generate random TEA encryption keys automatically"
default n
---help---
Generate four random TEA keys from /dev/urandom and write them
to .config on the first build. The same keys are used for the
/etc/passwd hash and the firmware.
make menuconfig # set password, enable this option
make
Key values are not printed in the build log. Search .config for
CONFIG_FSUTILS_PASSWD_KEY to view them.
Do not copy keys or the password into a defconfig or commit
them to version control.
If disabled, set CONFIG_FSUTILS_PASSWD_KEY1..4 manually under
Application Configuration -> File System Utilities ->
Password file support.
comment " If not randomizing: set KEY1..4 at App Config -> File System Utilities -> Password file support"
depends on !BOARD_ETC_ROMFS_PASSWD_RANDOMIZE_KEYS
config BOARD_ETC_ROMFS_PASSWD_UID
int "Admin user ID"

View file

@ -287,9 +287,17 @@ function(process_all_directory_romfs)
if("${CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD}" STREQUAL "")
message(
FATAL_ERROR
"CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD must be set when "
"BOARD_ETC_ROMFS_PASSWD_ENABLE is enabled. Run 'make menuconfig' "
"to set a password.")
"\n"
" BUILD ERROR: Admin password not set.\n"
"\n"
" Run make menuconfig and set:\n"
" Board Selection -> Auto-generate /etc/passwd -> Admin password\n"
"\n"
" For TEA keys, either enable random generation in the same menu,\n"
" or set CONFIG_FSUTILS_PASSWD_KEY1..4 under Application Configuration\n"
" -> File System Utilities -> Password file support.\n"
"\n"
" Password and keys are not saved in defconfig.\n")
endif()
# Determine host executable suffix (.exe on Windows, empty elsewhere)
@ -317,22 +325,77 @@ function(process_all_directory_romfs)
add_custom_target(build_host_mkpasswd DEPENDS ${MKPASSWD_BIN})
endif()
# Pass TEA key overrides when the user has changed them from defaults
set(MKPASSWD_KEY_ARGS "")
if(CONFIG_FSUTILS_PASSWD_KEY1)
list(APPEND MKPASSWD_KEY_ARGS --key1 ${CONFIG_FSUTILS_PASSWD_KEY1})
endif()
if(CONFIG_FSUTILS_PASSWD_KEY2)
list(APPEND MKPASSWD_KEY_ARGS --key2 ${CONFIG_FSUTILS_PASSWD_KEY2})
endif()
if(CONFIG_FSUTILS_PASSWD_KEY3)
list(APPEND MKPASSWD_KEY_ARGS --key3 ${CONFIG_FSUTILS_PASSWD_KEY3})
endif()
if(CONFIG_FSUTILS_PASSWD_KEY4)
list(APPEND MKPASSWD_KEY_ARGS --key4 ${CONFIG_FSUTILS_PASSWD_KEY4})
set(GENPASSWD_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/etc/passwd)
# Delegate detection and generation to the shell helpers so the logic is
# testable outside of cmake. check_passwd_keys.sh prints "yes" when keys
# are absent or at insecure defaults; gen_passwd_keys.sh writes fresh
# /dev/urandom values in-place.
#
# RANDOMIZE_KEYS single-invocation path: 1. gen_passwd_keys.sh writes new
# keys to .config at configure time. 2. We re-read .config below so
# CONFIG_FSUTILS_PASSWD_KEY1..4 carry the new values for the rest of this
# cmake configure run. 3. add_custom_command is registered with the updated
# key values, so both mkpasswd (passwd hash) and the firmware use the same
# keys login works without a second cmake invocation.
#
# Note: config.h is regenerated at build time from .config (its dependency),
# so the firmware uses the correct keys automatically.
execute_process(
COMMAND "${NUTTX_DIR}/tools/check_passwd_keys.sh" "${NUTTX_DIR}/.config"
OUTPUT_VARIABLE _passwd_keys_need_setup
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _check_rc)
if(NOT _check_rc EQUAL 0)
message(
FATAL_ERROR
"check_passwd_keys.sh failed check ${NUTTX_DIR}/tools/check_passwd_keys.sh"
)
endif()
set(GENPASSWD_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/etc/passwd)
if(_passwd_keys_need_setup STREQUAL "yes")
if(CONFIG_BOARD_ETC_ROMFS_PASSWD_RANDOMIZE_KEYS)
# Generate keys and write to .config (no key values printed here)
execute_process(
COMMAND "${NUTTX_DIR}/tools/gen_passwd_keys.sh" "${NUTTX_DIR}/.config"
RESULT_VARIABLE _gen_rc
OUTPUT_QUIET)
if(NOT _gen_rc EQUAL 0)
message(
FATAL_ERROR
"gen_passwd_keys.sh failed check ${NUTTX_DIR}/.config permissions"
)
endif()
message(
STATUS
"[passwd] TEA keys written to .config (search for CONFIG_FSUTILS_PASSWD_KEY to view)"
)
# Re-read .config so the new key values are live for this configure run
# (mirrors Board.mk's second -include).
file(STRINGS "${NUTTX_DIR}/.config" _fresh_config
REGEX "^CONFIG_FSUTILS_PASSWD_KEY[1-4]=")
foreach(_line ${_fresh_config})
if(_line MATCHES "^CONFIG_FSUTILS_PASSWD_KEY([1-4])=(.+)$")
set(CONFIG_FSUTILS_PASSWD_KEY${CMAKE_MATCH_1} "${CMAKE_MATCH_2}")
endif()
endforeach()
else()
message(
FATAL_ERROR
"\n"
" BUILD ERROR: TEA encryption keys not configured.\n"
"\n"
" Run make menuconfig and either:\n"
" - enable Generate random TEA keys automatically, or\n"
" - set CONFIG_FSUTILS_PASSWD_KEY1..4 under Application Configuration\n"
" -> File System Utilities -> Password file support\n")
endif()
endif()
# At this point KEY1..4 are guaranteed to be correct (either freshly
# generated above, or manually set by the user).
add_custom_command(
OUTPUT ${GENPASSWD_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/etc
@ -341,10 +404,13 @@ function(process_all_directory_romfs)
--password "${CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD}" --uid
${CONFIG_BOARD_ETC_ROMFS_PASSWD_UID} --gid
${CONFIG_BOARD_ETC_ROMFS_PASSWD_GID} --home
"${CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME}" ${MKPASSWD_KEY_ARGS} -o
${GENPASSWD_OUTPUT}
"${CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME}" --key1
${CONFIG_FSUTILS_PASSWD_KEY1} --key2 ${CONFIG_FSUTILS_PASSWD_KEY2}
--key3 ${CONFIG_FSUTILS_PASSWD_KEY3} --key4
${CONFIG_FSUTILS_PASSWD_KEY4} -o ${GENPASSWD_OUTPUT}
DEPENDS ${MKPASSWD_BIN} ${NUTTX_DIR}/.config
COMMENT "Generating /etc/passwd from Kconfig values")
COMMENT "Generating /etc/passwd from .config TEA keys")
add_custom_target(generate_passwd DEPENDS ${GENPASSWD_OUTPUT})
add_dependencies(generate_passwd build_host_mkpasswd)
list(APPEND RCRAWS ${GENPASSWD_OUTPUT})

82
tools/check_passwd_keys.sh Executable file
View file

@ -0,0 +1,82 @@
#!/usr/bin/env sh
# tools/check_passwd_keys.sh
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Usage:
# check_passwd_keys.sh <path-to-.config>
#
# Prints "yes" to stdout when CONFIG_FSUTILS_PASSWD_KEY1..4 need setup:
# - any key is absent or "is not set"
# - all four equal the known-insecure Kconfig defaults
# Prints "no" when all four are present and non-default.
# Idempotent; no side effects.
set -e
CONFIG="${1}"
if [ -z "${CONFIG}" ]; then
printf 'Usage: check_passwd_keys.sh <path-to-.config>\n' >&2
exit 1
fi
if [ ! -f "${CONFIG}" ]; then
printf 'check_passwd_keys: file not found: %s\n' "${CONFIG}" >&2
exit 1
fi
# Known-insecure placeholder/default values — must not be used in a build.
DEFAULT1=0x12345678
DEFAULT2=0x9abcdef0
DEFAULT3=0x12345678
DEFAULT4=0x9abcdef0
# Return the assigned value of CONFIG_FSUTILS_PASSWD_KEY<n>, or empty
# string when the line is absent or commented as "is not set".
get_val() {
grep -E "^CONFIG_FSUTILS_PASSWD_KEY${1}=" "${CONFIG}" 2>/dev/null \
| tail -n 1 \
| sed 's/^[^=]*=//'
}
# True when the value is unset or still at the Kconfig placeholder (0).
is_placeholder() {
case "$1" in
''|0|0x0|0x00000000) return 0 ;;
*) return 1 ;;
esac
}
K1=$(get_val 1)
K2=$(get_val 2)
K3=$(get_val 3)
K4=$(get_val 4)
# Any key absent, empty, or placeholder → needs setup
if is_placeholder "${K1}" || is_placeholder "${K2}" || \
is_placeholder "${K3}" || is_placeholder "${K4}"; then
echo yes
exit 0
fi
# All four at the known insecure defaults → needs setup
if [ "${K1}" = "${DEFAULT1}" ] && [ "${K2}" = "${DEFAULT2}" ] && \
[ "${K3}" = "${DEFAULT3}" ] && [ "${K4}" = "${DEFAULT4}" ]; then
echo yes
exit 0
fi
echo no

89
tools/gen_passwd_keys.sh Executable file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env sh
# tools/gen_passwd_keys.sh
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Usage:
# gen_passwd_keys.sh <path-to-.config>
#
# Generates four random 32-bit TEA key values from /dev/urandom and
# writes/replaces CONFIG_FSUTILS_PASSWD_KEY1..4 in the target .config file.
# Handles both "line exists (replace)" and "line missing (append)" cases.
#
# Key values are NOT printed to stdout or stderr. Search the .config file
# for CONFIG_FSUTILS_PASSWD_KEY to view them if needed.
#
# Exit 0 on success, non-zero with a message on failure.
set -e
CONFIG="${1}"
if [ -z "${CONFIG}" ]; then
printf 'Usage: gen_passwd_keys.sh <path-to-.config>\n' >&2
exit 1
fi
if [ ! -f "${CONFIG}" ]; then
printf 'gen_passwd_keys: file not found: %s\n' "${CONFIG}" >&2
exit 1
fi
# Generate a random 32-bit hex value via /dev/urandom.
# Uses od -tx4 which always produces exactly 8 hex digits.
rand_key() {
dd if=/dev/urandom bs=4 count=1 2>/dev/null \
| od -An -tx4 \
| tr -d ' \n'
}
K1="0x$(rand_key)"
K2="0x$(rand_key)"
K3="0x$(rand_key)"
K4="0x$(rand_key)"
# Remove any existing definitions (assigned or "is not set") everywhere.
sed -i \
'/^# CONFIG_FSUTILS_PASSWD_KEY[1-4] is not set/d;
/^CONFIG_FSUTILS_PASSWD_KEY[1-4]=/d' \
"${CONFIG}" || {
printf 'gen_passwd_keys: ERROR: failed to edit %s (check permissions)\n' \
"${CONFIG}" >&2
exit 1
}
# Insert keys in the FSUTILS_PASSWD section when it exists so menuconfig
# and mkconfig see a single canonical copy (not orphaned lines at EOF).
if grep -q '^CONFIG_FSUTILS_PASSWD_IOBUFFER_SIZE=' "${CONFIG}"; then
sed -i "/^CONFIG_FSUTILS_PASSWD_IOBUFFER_SIZE=/a\\
CONFIG_FSUTILS_PASSWD_KEY1=${K1}\\
CONFIG_FSUTILS_PASSWD_KEY2=${K2}\\
CONFIG_FSUTILS_PASSWD_KEY3=${K3}\\
CONFIG_FSUTILS_PASSWD_KEY4=${K4}" "${CONFIG}" || {
printf 'gen_passwd_keys: ERROR: failed to insert keys into %s\n' \
"${CONFIG}" >&2
exit 1
}
else
printf 'CONFIG_FSUTILS_PASSWD_KEY1=%s\nCONFIG_FSUTILS_PASSWD_KEY2=%s\nCONFIG_FSUTILS_PASSWD_KEY3=%s\nCONFIG_FSUTILS_PASSWD_KEY4=%s\n' \
"${K1}" "${K2}" "${K3}" "${K4}" >> "${CONFIG}" || {
printf 'gen_passwd_keys: ERROR: failed to append keys to %s\n' \
"${CONFIG}" >&2
exit 1
}
fi
printf 'TEA keys generated and written to %s.\nSearch %s for CONFIG_FSUTILS_PASSWD_KEY to view if needed.\n' \
"${CONFIG}" "${CONFIG}"

View file

@ -529,7 +529,10 @@ int main(int argc, char **argv)
if (password[0] == '\0')
{
fprintf(stderr, "mkpasswd: --password must not be empty\n");
fprintf(stderr,
"mkpasswd: ERROR: password must not be empty.\n"
" Set it in menuconfig: Board Selection -> "
"Auto-generate /etc/passwd -> Admin password\n");
return 1;
}
@ -541,30 +544,35 @@ int main(int argc, char **argv)
return 1;
}
/* Warn if the board Kconfig default password is still being used. */
/* Reject the well-known default password. The build system should have
* caught this already; mkpasswd is the last line of defence.
*/
if (strcmp(password, "Administrator") == 0)
{
fprintf(stderr,
">>>> WARNING: YOU ARE USING THE DEFAULT ADMIN PASSWORD "
"(CONFIG_BOARD_ETC_ROMFS_"
"PASSWD_PASSWORD=\"Administrator\")!!! PLEASE CHANGE "
"IT!!! <<<<\n");
"mkpasswd: ERROR: password \"Administrator\" is not allowed.\n"
" Set a unique password in menuconfig: Board Selection -> "
"Auto-generate /etc/passwd -> Admin password\n");
return 1;
}
/* Warn when the user has not changed the default TEA keys.
* The default values are identical across all NuttX builds, so any
* attacker with access to the firmware image can recover the plaintext
* password. This is a warning only; the build is not aborted.
/* Reject the default TEA keys. Using the published defaults means any
* attacker who has a copy of the NuttX source can decrypt the password
* hash directly from the firmware image (CWE-321).
*/
if (key[0] == DEFAULT_KEY1 && key[1] == DEFAULT_KEY2 &&
key[2] == DEFAULT_KEY3 && key[3] == DEFAULT_KEY4)
{
fprintf(stderr,
">>>> WARNING: YOU ARE USING DEFAULT PASSWORD KEYS "
"(CONFIG_FSUTILS_"
"PASSWD_KEY1-4)!!! PLEASE CHANGE IT!!! <<<<\n");
"mkpasswd: ERROR: default TEA encryption keys "
"are not allowed.\n"
" Set keys in menuconfig: Application Configuration -> "
"File System Utilities -> Password file support\n"
" Or enable random key generation under Board Selection -> "
"Auto-generate /etc/passwd\n");
return 1;
}
/* Encrypt the password using TEA + custom base64.

74
tools/passwd_keys.mk Normal file
View file

@ -0,0 +1,74 @@
############################################################################
# tools/passwd_keys.mk
#
# SPDX-License-Identifier: Apache-2.0
#
# Passwd / TEA-key validation and generation. Included from the top-level
# Makefile immediately after .config is loaded, BEFORE tools/Unix.mk builds
# include/nuttx/config.h. This ordering guarantees that freshly generated
# keys are present in .config when config.h is created, so the firmware and
# mkpasswd always agree on the same key values in a single make invocation.
#
# Board.mk only consumes CONFIG_FSUTILS_PASSWD_KEY1..4 in the ROMFS recipe.
############################################################################
TOPDIR ?= .
# Skip enforcement during configuration-only make invocations (menuconfig,
# olddefconfig, clean_context, etc.) so the user can set the password first.
PASSWD_SKIP_GOALS := config menuconfig oldconfig olddefconfig savedefconfig \
nconfig qconfig gconfig clean_context apps_preconfig \
apps_config apps_menuconfig apps_oldconfig apps_olddefconfig \
apps_savedefconfig apps_nconfig apps_qconfig apps_gconfig \
distclean clean
ifeq ($(MAKECMDGOALS),)
_PASSWD_ENFORCE := y
else
_PASSWD_ENFORCE := $(if $(filter-out $(PASSWD_SKIP_GOALS),$(MAKECMDGOALS)),y,)
endif
ifeq ($(CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE),y)
ifeq ($(_PASSWD_ENFORCE),y)
# --- password check ---
ifeq ($(strip $(patsubst "%",%,$(CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD))),)
$(info )
$(info BUILD ERROR: Admin password not set.)
$(info )
$(info Run make menuconfig and set:)
$(info Board Selection -> Auto-generate /etc/passwd -> Admin password)
$(info )
$(info For TEA keys, either enable random generation in the same menu,)
$(info or set CONFIG_FSUTILS_PASSWD_KEY1..4 under Application Configuration)
$(info -> File System Utilities -> Password file support.)
$(info )
$(info Password and keys are not saved in defconfig.)
$(info )
$(error Aborting: CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD is not set)
endif
# --- TEA key check / generation ---
_PASSWD_KEYS_NEED_SETUP := $(shell \
$(TOPDIR)/tools/check_passwd_keys.sh $(TOPDIR)/.config 2>/dev/null)
ifneq ($(_PASSWD_KEYS_NEED_SETUP),no)
ifeq ($(CONFIG_BOARD_ETC_ROMFS_PASSWD_RANDOMIZE_KEYS),y)
$(shell $(TOPDIR)/tools/gen_passwd_keys.sh $(TOPDIR)/.config >/dev/null)
$(info [passwd] TEA keys written to .config (search for CONFIG_FSUTILS_PASSWD_KEY to view))
include $(TOPDIR)/.config
else
$(info )
$(info BUILD ERROR: TEA encryption keys not configured.)
$(info )
$(info Run make menuconfig and either:)
$(info - enable Generate random TEA keys automatically, or)
$(info - set CONFIG_FSUTILS_PASSWD_KEY1..4 under Application Configuration)
$(info -> File System Utilities -> Password file support)
$(info )
$(error Aborting: CONFIG_FSUTILS_PASSWD_KEY1..4 must be set to non-default values)
endif
endif
endif
endif