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

Reply via email to