This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit ffa6ba222f2a7ef6c0fcff9017f1c80c496b57c8 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 55089c56ece..e26daf68c4f 100644 --- a/boards/Kconfig +++ b/boards/Kconfig @@ -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" - ---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
