This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch releases/13.0
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/releases/13.0 by this push:
new f5291a8df19 !boards: enforce secure ROMFS passwd and TEA key setup
f5291a8df19 is described below
commit f5291a8df19660fc69992c0763e1a4cc87b7037c
Author: Abhishek Mishra <[email protected]>
AuthorDate: Sun Jul 5 19:59:44 2026 +0000
!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 <[email protected]>
---
Documentation/components/tools/index.rst | 126 ++++++++++++-------------------
Makefile | 1 +
boards/Board.mk | 11 +--
boards/Kconfig | 63 +++++++++++-----
cmake/nuttx_add_romfs.cmake | 104 ++++++++++++++++++++-----
tools/check_passwd_keys.sh | 82 ++++++++++++++++++++
tools/gen_passwd_keys.sh | 89 ++++++++++++++++++++++
tools/mkpasswd.c | 34 +++++----
tools/passwd_keys.mk | 74 ++++++++++++++++++
9 files changed, 449 insertions(+), 135 deletions(-)
diff --git a/Documentation/components/tools/index.rst
b/Documentation/components/tools/index.rst
index 83c868984fb..23a14c174c7 100644
--- a/Documentation/components/tools/index.rst
+++ b/Documentation/components/tools/index.rst
@@ -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
+
+2. Run ``make``.
+
+TEA encryption keys
+~~~~~~~~~~~~~~~~~~~
+
+Keys must match between the build (``mkpasswd``) and the firmware
+(``CONFIG_FSUTILS_PASSWD_KEY1..4``). Choose one option:
-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**.
+* **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
+
+How it works
+~~~~~~~~~~~~
-The TEA encryption keys can be changed from their defaults via
-``CONFIG_FSUTILS_PASSWD_KEY1..4``.
+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``.
\ No newline at end of file
+Do not copy them into a defconfig or commit them to version control.
\ No newline at end of file
diff --git a/Makefile b/Makefile
index ac65abe1121..567d3c6ae8a 100644
--- a/Makefile
+++ b/Makefile
@@ -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.
diff --git a/boards/Board.mk b/boards/Board.mk
index 580a18caf73..a882433aa82 100644
--- a/boards/Board.mk
+++ b/boards/Board.mk
@@ -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"
diff --git a/boards/Kconfig b/boards/Kconfig
index ba2cb60b87f..7dfa36fc3c3 100644
--- a/boards/Kconfig
+++ b/boards/Kconfig
@@ -5544,21 +5544,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"
@@ -5566,14 +5568,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"
- ---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'.
+ string "Admin password (required, no default)"
+ ---help---
+ 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"
diff --git a/cmake/nuttx_add_romfs.cmake b/cmake/nuttx_add_romfs.cmake
index baca00fb8cf..2b5c11d4184 100644
--- a/cmake/nuttx_add_romfs.cmake
+++ b/cmake/nuttx_add_romfs.cmake
@@ -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})
+ 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()
- if(CONFIG_FSUTILS_PASSWD_KEY4)
- list(APPEND MKPASSWD_KEY_ARGS --key4 ${CONFIG_FSUTILS_PASSWD_KEY4})
+
+ 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()
- set(GENPASSWD_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/etc/passwd)
+ # 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})
diff --git a/tools/check_passwd_keys.sh b/tools/check_passwd_keys.sh
new file mode 100755
index 00000000000..e6cbe3bf532
--- /dev/null
+++ b/tools/check_passwd_keys.sh
@@ -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
diff --git a/tools/gen_passwd_keys.sh b/tools/gen_passwd_keys.sh
new file mode 100755
index 00000000000..e744a5a7824
--- /dev/null
+++ b/tools/gen_passwd_keys.sh
@@ -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}"
diff --git a/tools/mkpasswd.c b/tools/mkpasswd.c
index cfde64f3b40..f7952e24e08 100644
--- a/tools/mkpasswd.c
+++ b/tools/mkpasswd.c
@@ -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.
diff --git a/tools/passwd_keys.mk b/tools/passwd_keys.mk
new file mode 100644
index 00000000000..8817f0ddb56
--- /dev/null
+++ b/tools/passwd_keys.mk
@@ -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