!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 mkpasswd — Build-time ``/etc/passwd`` Generation
------------------------------------------------- -------------------------------------------------
``tools/mkpasswd`` is a C host tool (compiled from ``tools/mkpasswd.c``) that ``tools/mkpasswd`` is a host tool (``tools/mkpasswd.c``) that generates a
generates a single ``/etc/passwd`` entry at build time. It is invoked single ``/etc/passwd`` entry at build time. It runs automatically when
automatically by the ROMFS build step when ``CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y``.
``CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y`` is set.
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 Setup
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.
For improved baseline security, the configured password must be at least 1. Run ``make menuconfig`` and configure:
8 characters long.
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 2. Run ``make``.
``CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD``.
2. The password is hashed using the Tiny Encryption Algorithm (TEA) — the TEA encryption keys
same implementation used at runtime in ~~~~~~~~~~~~~~~~~~~
``libs/libc/misc/lib_tea_encrypt.c`` — with custom base64 encoding
matching ``apps/fsutils/passwd/passwd_encrypt.c``. Keys must match between the build (``mkpasswd``) and the firmware
3. The resulting hashed entry is written to (``CONFIG_FSUTILS_PASSWD_KEY1..4``). Choose one option:
``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 Kconfig options
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Enable the feature and configure credentials via ``make menuconfig``:
.. code:: kconfig .. code:: kconfig
CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y
CONFIG_NSH_CONSOLE_LOGIN=y # required to enforce login prompt CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD="<secret>"
CONFIG_BOARD_ETC_ROMFS_PASSWD_USER="root" # default: root CONFIG_BOARD_ETC_ROMFS_PASSWD_RANDOMIZE_KEYS=y
CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD="<secret>" # required, min length 8 CONFIG_NSH_CONSOLE_LOGIN=y
CONFIG_BOARD_ETC_ROMFS_PASSWD_UID=0 CONFIG_NSH_LOGIN_PASSWD=y
CONFIG_BOARD_ETC_ROMFS_PASSWD_GID=0 CONFIG_FSUTILS_PASSWD=y
CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME="/"
The TEA encryption keys can be changed from their defaults via How it works
``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 ``/etc/passwd`` file format
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: text .. code:: text
user:x:uid:gid:home user:encrypted_hash: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
Notes on ``savedefconfig`` Notes on ``savedefconfig``
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
To avoid leaking credentials into board defconfigs, ``make savedefconfig`` ``make savedefconfig`` does not save these options:
does not save the following options in the generated defconfig:
* ``CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD`` * ``CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD``
* ``CONFIG_FSUTILS_PASSWD_KEY1`` * ``CONFIG_FSUTILS_PASSWD_KEY1`` through ``CONFIG_FSUTILS_PASSWD_KEY4``
* ``CONFIG_FSUTILS_PASSWD_KEY2``
* ``CONFIG_FSUTILS_PASSWD_KEY3``
* ``CONFIG_FSUTILS_PASSWD_KEY4``
If you need these values for local development, add them manually to your Do not copy them into a defconfig or commit them to version control.
local defconfig after running ``make savedefconfig``.

View file

@ -31,6 +31,7 @@ ifeq ($(wildcard .config),)
@echo " tools/configure.sh -L" @echo " tools/configure.sh -L"
else else
include .config include .config
include tools/passwd_keys.mk
# Include the correct Makefile for the selected architecture. # 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 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))) $(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 ($(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) mkdir -p $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT)
$(Q) $(TOPDIR)$(DELIM)tools$(DELIM)mkpasswd$(HOSTEXEEXT) \ $(Q) $(TOPDIR)$(DELIM)tools$(DELIM)mkpasswd$(HOSTEXEEXT) \
--user $(CONFIG_BOARD_ETC_ROMFS_PASSWD_USER) \ --user $(CONFIG_BOARD_ETC_ROMFS_PASSWD_USER) \
@ -46,10 +43,10 @@ endif
--uid $(CONFIG_BOARD_ETC_ROMFS_PASSWD_UID) \ --uid $(CONFIG_BOARD_ETC_ROMFS_PASSWD_UID) \
--gid $(CONFIG_BOARD_ETC_ROMFS_PASSWD_GID) \ --gid $(CONFIG_BOARD_ETC_ROMFS_PASSWD_GID) \
--home $(CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME) \ --home $(CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY1),--key1 $(CONFIG_FSUTILS_PASSWD_KEY1)) \ --key1 $(CONFIG_FSUTILS_PASSWD_KEY1) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY2),--key2 $(CONFIG_FSUTILS_PASSWD_KEY2)) \ --key2 $(CONFIG_FSUTILS_PASSWD_KEY2) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY3),--key3 $(CONFIG_FSUTILS_PASSWD_KEY3)) \ --key3 $(CONFIG_FSUTILS_PASSWD_KEY3) \
$(if $(CONFIG_FSUTILS_PASSWD_KEY4),--key4 $(CONFIG_FSUTILS_PASSWD_KEY4)) \ --key4 $(CONFIG_FSUTILS_PASSWD_KEY4) \
-o $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT)$(DELIM)passwd -o $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT)$(DELIM)passwd
endif endif
$(Q) genromfs -f romfs.img -d $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT) -V "NSHInitVol" $(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 default n
depends on ETC_ROMFS depends on ETC_ROMFS
---help--- ---help---
Generate the /etc/passwd file at build time from a user-supplied Generate /etc/passwd at build time. The password is hashed with TEA
password. This avoids shipping a hard-coded default password by tools/mkpasswd; the plaintext is not stored in the firmware.
(CWE-798). When enabled, the build will fail if no password
is configured, forcing each build to set its own credentials.
The password is hashed at build time by the host tool Before building, set:
tools/mkpasswd (compiled from tools/mkpasswd.c) using the Tiny 1. Admin password (below)
Encryption Algorithm (TEA) — the same algorithm used at runtime 2. TEA keys — enable random generation below, or set
in libs/libc/misc/lib_tea_encrypt.c. The plaintext password is CONFIG_FSUTILS_PASSWD_KEY1..4 in Application Configuration ->
never stored in the firmware image. 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 if BOARD_ETC_ROMFS_PASSWD_ENABLE
comment "--- Step 1: set the admin password below ---"
config BOARD_ETC_ROMFS_PASSWD_USER config BOARD_ETC_ROMFS_PASSWD_USER
string "Admin username" string "Admin username"
default "root" default "root"
@ -5614,14 +5616,39 @@ config BOARD_ETC_ROMFS_PASSWD_USER
The username for the auto-generated /etc/passwd entry. The username for the auto-generated /etc/passwd entry.
config BOARD_ETC_ROMFS_PASSWD_PASSWORD config BOARD_ETC_ROMFS_PASSWD_PASSWORD
string "Admin password (required)" string "Admin password (required, no default)"
default "Administrator"
---help--- ---help---
The plaintext password for the auto-generated /etc/passwd entry. The plaintext password for the /etc/passwd entry. No default is
This value is hashed with TEA at build time; the plaintext is NOT provided. The build fails if this is empty or shorter than 8
stored in the firmware image. The build will fail if this is left characters.
empty or shorter than 8 characters. Set this via
'make menuconfig'. 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 config BOARD_ETC_ROMFS_PASSWD_UID
int "Admin user ID" int "Admin user ID"

View file

@ -287,9 +287,17 @@ function(process_all_directory_romfs)
if("${CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD}" STREQUAL "") if("${CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD}" STREQUAL "")
message( message(
FATAL_ERROR FATAL_ERROR
"CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD must be set when " "\n"
"BOARD_ETC_ROMFS_PASSWD_ENABLE is enabled. Run 'make menuconfig' " " BUILD ERROR: Admin password not set.\n"
"to set a password.") "\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() endif()
# Determine host executable suffix (.exe on Windows, empty elsewhere) # 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}) add_custom_target(build_host_mkpasswd DEPENDS ${MKPASSWD_BIN})
endif() endif()
# Pass TEA key overrides when the user has changed them from defaults set(GENPASSWD_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/etc/passwd)
set(MKPASSWD_KEY_ARGS "")
if(CONFIG_FSUTILS_PASSWD_KEY1) # Delegate detection and generation to the shell helpers so the logic is
list(APPEND MKPASSWD_KEY_ARGS --key1 ${CONFIG_FSUTILS_PASSWD_KEY1}) # testable outside of cmake. check_passwd_keys.sh prints "yes" when keys
endif() # are absent or at insecure defaults; gen_passwd_keys.sh writes fresh
if(CONFIG_FSUTILS_PASSWD_KEY2) # /dev/urandom values in-place.
list(APPEND MKPASSWD_KEY_ARGS --key2 ${CONFIG_FSUTILS_PASSWD_KEY2}) #
endif() # RANDOMIZE_KEYS single-invocation path: 1. gen_passwd_keys.sh writes new
if(CONFIG_FSUTILS_PASSWD_KEY3) # keys to .config at configure time. 2. We re-read .config below so
list(APPEND MKPASSWD_KEY_ARGS --key3 ${CONFIG_FSUTILS_PASSWD_KEY3}) # CONFIG_FSUTILS_PASSWD_KEY1..4 carry the new values for the rest of this
endif() # cmake configure run. 3. add_custom_command is registered with the updated
if(CONFIG_FSUTILS_PASSWD_KEY4) # key values, so both mkpasswd (passwd hash) and the firmware use the same
list(APPEND MKPASSWD_KEY_ARGS --key4 ${CONFIG_FSUTILS_PASSWD_KEY4}) # 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() 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( add_custom_command(
OUTPUT ${GENPASSWD_OUTPUT} OUTPUT ${GENPASSWD_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/etc 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 --password "${CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD}" --uid
${CONFIG_BOARD_ETC_ROMFS_PASSWD_UID} --gid ${CONFIG_BOARD_ETC_ROMFS_PASSWD_UID} --gid
${CONFIG_BOARD_ETC_ROMFS_PASSWD_GID} --home ${CONFIG_BOARD_ETC_ROMFS_PASSWD_GID} --home
"${CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME}" ${MKPASSWD_KEY_ARGS} -o "${CONFIG_BOARD_ETC_ROMFS_PASSWD_HOME}" --key1
${GENPASSWD_OUTPUT} ${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 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_custom_target(generate_passwd DEPENDS ${GENPASSWD_OUTPUT})
add_dependencies(generate_passwd build_host_mkpasswd) add_dependencies(generate_passwd build_host_mkpasswd)
list(APPEND RCRAWS ${GENPASSWD_OUTPUT}) 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') 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; return 1;
} }
@ -541,30 +544,35 @@ int main(int argc, char **argv)
return 1; 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) if (strcmp(password, "Administrator") == 0)
{ {
fprintf(stderr, fprintf(stderr,
">>>> WARNING: YOU ARE USING THE DEFAULT ADMIN PASSWORD " "mkpasswd: ERROR: password \"Administrator\" is not allowed.\n"
"(CONFIG_BOARD_ETC_ROMFS_" " Set a unique password in menuconfig: Board Selection -> "
"PASSWD_PASSWORD=\"Administrator\")!!! PLEASE CHANGE " "Auto-generate /etc/passwd -> Admin password\n");
"IT!!! <<<<\n"); return 1;
} }
/* Warn when the user has not changed the default TEA keys. /* Reject the default TEA keys. Using the published defaults means any
* The default values are identical across all NuttX builds, so any * attacker who has a copy of the NuttX source can decrypt the password
* attacker with access to the firmware image can recover the plaintext * hash directly from the firmware image (CWE-321).
* password. This is a warning only; the build is not aborted.
*/ */
if (key[0] == DEFAULT_KEY1 && key[1] == DEFAULT_KEY2 && if (key[0] == DEFAULT_KEY1 && key[1] == DEFAULT_KEY2 &&
key[2] == DEFAULT_KEY3 && key[3] == DEFAULT_KEY4) key[2] == DEFAULT_KEY3 && key[3] == DEFAULT_KEY4)
{ {
fprintf(stderr, fprintf(stderr,
">>>> WARNING: YOU ARE USING DEFAULT PASSWORD KEYS " "mkpasswd: ERROR: default TEA encryption keys "
"(CONFIG_FSUTILS_" "are not allowed.\n"
"PASSWD_KEY1-4)!!! PLEASE CHANGE IT!!! <<<<\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. /* 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