!build: add build-time password generation with mkpasswd tool.

Introduce mkpasswd, a pure-C host tool for generating encrypted password
files at build time using TEA encryption. This enables secure,
credential-free firmware images while allowing build-time password
configuration.

Changes:
* Add mkpasswd.c host tool for TEA-based password hashing and encryption
* Integrate mkpasswd into Make build system (tools/Makefile.host)
* Add CMake support for mkpasswd compilation and ROMFS passwd generation
* Add CONFIG_BOARD_ETC_ROMFS_PASSWD_* configuration options to Kconfig
* Implement credential exclusion from defconfig to prevent password leaking
* Update savedefconfig.cmake to strip sensitive credentials
* Fix mkdir() portability for Windows Native builds (CONFIG_WINDOWS_NATIVE)
* Change default username from "admin" to "root" (POSIX convention)
* Improve build-failure error message with full menuconfig navigation path

BREAKING CHANGE: Boards enabling CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE
must set CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD to a non-empty string
of at least 8 characters. The build now fails with an explicit error if
this config is left empty. To fix: run 'make menuconfig' and navigate to:
  Board Selection --->
    Auto-generate /etc/passwd at build time --->
      Admin password

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit is contained in:
Abhishek Mishra
2026-03-20 13:54:21 +00:00
committed by Xiang Xiao
parent ea161e4fa9
commit ab6b1fd6f9
9 changed files with 813 additions and 8 deletions
+1
View File
@@ -9,6 +9,7 @@
*.dSYM
*.elf
*.exe
etctmp/
*.gcno
*.gcda
*.hex
+18 -1
View File
@@ -30,11 +30,28 @@ $(RCOBJS): $(ETCDIR)$(DELIM)%: %
$(Q) mkdir -p $(dir $@)
$(call PREPROCESS, $<, $@)
$(ETCSRC): $(foreach raw,$(RCRAWS), $(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)))) $(RCOBJS)
$(ETCSRC): $(foreach raw,$(RCRAWS), $(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)))) $(RCOBJS) $(TOPDIR)$(DELIM).config
$(foreach raw, $(RCRAWS), \
$(shell rm -rf $(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)))
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) \
--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) \
$(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)) \
-o $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT)$(DELIM)passwd
endif
$(Q) genromfs -f romfs.img -d $(ETCDIR)$(DELIM)$(CONFIG_ETC_ROMFSMOUNTPT) -V "NSHInitVol"
$(Q) echo "#include <nuttx/compiler.h>" > $@
$(Q) xxd -i romfs.img | sed -e "s/^unsigned char/const unsigned char aligned_data(4)/g" >> $@
+50
View File
@@ -5446,3 +5446,53 @@ config BOARD_MEMORY_RANGE
end: end address of memory range
flags: Executable 0x1, Writable 0x2, Readable 0x4
example:{0x1000,0x2000,0x4},{0x2000,0x3000,0x6},{0x3000,0x4000,0x7} ... {0x0,0x0,0x0}
config BOARD_ETC_ROMFS_PASSWD_ENABLE
bool "Auto-generate /etc/passwd at build time"
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.
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.
See Documentation/components/passwd_autogen.rst for details.
if BOARD_ETC_ROMFS_PASSWD_ENABLE
config BOARD_ETC_ROMFS_PASSWD_USER
string "Admin username"
default "root"
---help---
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'.
config BOARD_ETC_ROMFS_PASSWD_UID
int "Admin user ID"
default 0
config BOARD_ETC_ROMFS_PASSWD_GID
int "Admin group ID"
default 0
config BOARD_ETC_ROMFS_PASSWD_HOME
string "Admin home directory"
default "/"
endif # BOARD_ETC_ROMFS_PASSWD_ENABLE
+69
View File
@@ -282,6 +282,75 @@ function(process_all_directory_romfs)
list(PREPEND RCSRCS ${board_rcsrcs} ${dyn_rcsrcs})
list(PREPEND RCRAWS ${board_rcraws} ${dyn_rcraws})
# Auto-generate /etc/passwd at build time if configured
if(CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE)
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.")
endif()
# Determine host executable suffix (.exe on Windows, empty elsewhere)
if(CMAKE_HOST_WIN32)
set(HOST_EXE_SUFFIX .exe)
else()
set(HOST_EXE_SUFFIX "")
endif()
# Locate a host C compiler to build the mkpasswd tool
find_program(HOST_CC NAMES cc gcc clang REQUIRED)
# Build mkpasswd.c as a host binary in the CMake build directory and keep
# the source tree clean.
set(MKPASSWD_SRC ${NUTTX_DIR}/tools/mkpasswd.c)
set(MKPASSWD_BIN ${CMAKE_BINARY_DIR}/tools/mkpasswd${HOST_EXE_SUFFIX})
if(NOT TARGET build_host_mkpasswd)
add_custom_command(
OUTPUT ${MKPASSWD_BIN}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/tools
COMMAND ${HOST_CC} -o ${MKPASSWD_BIN} ${MKPASSWD_SRC}
DEPENDS ${MKPASSWD_SRC}
COMMENT "Building host tool: mkpasswd")
add_custom_target(build_host_mkpasswd DEPENDS ${MKPASSWD_BIN})
endif()
# Pass TEA key overrides when the user has changed them from defaults
set(MKPASSWD_KEY_ARGS "")
if(CONFIG_FSUTILS_PASSWD_KEY1)
list(APPEND MKPASSWD_KEY_ARGS --key1 ${CONFIG_FSUTILS_PASSWD_KEY1})
endif()
if(CONFIG_FSUTILS_PASSWD_KEY2)
list(APPEND MKPASSWD_KEY_ARGS --key2 ${CONFIG_FSUTILS_PASSWD_KEY2})
endif()
if(CONFIG_FSUTILS_PASSWD_KEY3)
list(APPEND MKPASSWD_KEY_ARGS --key3 ${CONFIG_FSUTILS_PASSWD_KEY3})
endif()
if(CONFIG_FSUTILS_PASSWD_KEY4)
list(APPEND MKPASSWD_KEY_ARGS --key4 ${CONFIG_FSUTILS_PASSWD_KEY4})
endif()
set(GENPASSWD_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/etc/passwd)
add_custom_command(
OUTPUT ${GENPASSWD_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/etc
COMMAND
${MKPASSWD_BIN} --user "${CONFIG_BOARD_ETC_ROMFS_PASSWD_USER}"
--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}
DEPENDS ${MKPASSWD_BIN} ${NUTTX_DIR}/.config
COMMENT "Generating /etc/passwd from Kconfig values")
add_custom_target(generate_passwd DEPENDS ${GENPASSWD_OUTPUT})
add_dependencies(generate_passwd build_host_mkpasswd)
list(APPEND RCRAWS ${GENPASSWD_OUTPUT})
list(APPEND dyn_deps generate_passwd)
endif()
# init dynamic dependencies
get_property(
+16 -1
View File
@@ -27,10 +27,14 @@ set(TARGET_FILE ${CMAKE_ARGV4})
file(STRINGS ${SOURCE_FILE} ConfigContents)
encode_brackets(ConfigContents)
set(PASSWD_AUTOGEN_ENABLED FALSE)
foreach(NameAndValue ${ConfigContents})
decode_brackets(NameAndValue)
encode_semicolon(NameAndValue)
if("${NameAndValue}" MATCHES "^CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y$")
set(PASSWD_AUTOGEN_ENABLED TRUE)
endif()
if("${NameAndValue}" MATCHES "CONFIG_ARCH="
OR "${NameAndValue}" MATCHES "^CONFIG_ARCH_CHIP_"
OR "${NameAndValue}" MATCHES "CONFIG_ARCH_CHIP="
@@ -72,9 +76,20 @@ list(SORT LINES)
foreach(LINE IN LISTS LINES)
decode_brackets(LINE)
decode_semicolon(LINE)
file(APPEND ${OUTPUT_FILE} "${LINE}\n")
if(NOT "${LINE}" MATCHES "^CONFIG_FSUTILS_PASSWD_KEY[0-9]"
AND NOT "${LINE}" MATCHES "^CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD=")
file(APPEND ${OUTPUT_FILE} "${LINE}\n")
endif()
endforeach()
if(PASSWD_AUTOGEN_ENABLED)
message(
WARNING
"CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD and CONFIG_FSUTILS_PASSWD_KEY1-4 "
"were intentionally excluded from defconfig by savedefconfig. Add them "
"manually in local defconfig if needed.")
endif()
# Converts the newline style for the output file.
configure_file(${OUTPUT_FILE} ${OUTPUT_FILE} @ONLY NEWLINE_STYLE LF)
+1
View File
@@ -14,6 +14,7 @@
/mksymtab
/mksyscall
/mkversion
/mkpasswd
/nxstyle
/rmcr
/incdir
+16 -4
View File
@@ -35,8 +35,9 @@ endif
all: b16$(HOSTEXEEXT) bdf-converter$(HOSTEXEEXT) cmpconfig$(HOSTEXEEXT) \
configure$(HOSTEXEEXT) mkconfig$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) \
mksymtab$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkversion$(HOSTEXEEXT) \
cnvwindeps$(HOSTEXEEXT) nxstyle$(HOSTEXEEXT) initialconfig$(HOSTEXEEXT) \
gencromfs$(HOSTEXEEXT) convert-comments$(HOSTEXEEXT) lowhex$(HOSTEXEEXT) \
mkpasswd$(HOSTEXEEXT) cnvwindeps$(HOSTEXEEXT) nxstyle$(HOSTEXEEXT) \
initialconfig$(HOSTEXEEXT) gencromfs$(HOSTEXEEXT) \
convert-comments$(HOSTEXEEXT) lowhex$(HOSTEXEEXT) \
detab$(HOSTEXEEXT) rmcr$(HOSTEXEEXT) incdir$(HOSTEXEEXT) \
jlink-nuttx$(HOSTDYNEXT)
default: mkconfig$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) \
@@ -44,8 +45,8 @@ default: mkconfig$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) \
ifdef HOSTEXEEXT
.PHONY: b16 bdf-converter cmpconfig clean configure kconfig2html mkconfig \
mkdeps mksymtab mksyscall mkversion cnvwindeps nxstyle initialconfig \
gencromfs convert-comments lowhex detab rmcr incdir
mkdeps mksymtab mksyscall mkversion mkpasswd cnvwindeps nxstyle \
initialconfig gencromfs convert-comments lowhex detab rmcr incdir
endif
ifdef HOSTDYNEXT
.PHONY: jlink-nuttx
@@ -106,6 +107,15 @@ ifdef HOSTEXEEXT
mkversion: mkversion$(HOSTEXEEXT)
endif
# mkpasswd - Generate a NuttX /etc/passwd entry with TEA-encrypted password
mkpasswd$(HOSTEXEEXT): mkpasswd.c
$(Q) $(HOSTCC) $(HOSTCFLAGS) -o mkpasswd$(HOSTEXEEXT) mkpasswd.c
ifdef HOSTEXEEXT
mkpasswd: mkpasswd$(HOSTEXEEXT)
endif
# mksyscall - Convert a CSV file into syscall stubs and proxies
mksyscall$(HOSTEXEEXT): mksyscall.c csvparser.c
@@ -268,6 +278,8 @@ clean:
$(call DELFILE, mksyscall.exe)
$(call DELFILE, mkversion)
$(call DELFILE, mkversion.exe)
$(call DELFILE, mkpasswd)
$(call DELFILE, mkpasswd.exe)
$(call DELFILE, nxstyle)
$(call DELFILE, nxstyle.exe)
$(call DELFILE, rmcr)
+17 -2
View File
@@ -282,6 +282,9 @@ tools/mkdeps$(HOSTEXEEXT):
tools/cnvwindeps$(HOSTEXEEXT):
$(Q) $(MAKE) -C tools -f Makefile.host cnvwindeps$(HOSTEXEEXT)
tools/mkpasswd$(HOSTEXEEXT):
$(Q) $(MAKE) -C tools -f Makefile.host mkpasswd$(HOSTEXEEXT)
# .dirlinks, and helpers
#
# Directories links. Most of establishing the NuttX configuration involves
@@ -661,12 +664,17 @@ host_info: checkpython3
# pass1dep: Create pass1 build dependencies
# pass2dep: Create pass2 build dependencies
pass1dep: context tools/mkdeps$(HOSTEXEEXT) tools/cnvwindeps$(HOSTEXEEXT)
PASSWD_TOOL_DEP =
ifeq ($(CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE),y)
PASSWD_TOOL_DEP += tools/mkpasswd$(HOSTEXEEXT)
endif
pass1dep: context tools/mkdeps$(HOSTEXEEXT) tools/cnvwindeps$(HOSTEXEEXT) $(PASSWD_TOOL_DEP)
$(Q) for dir in $(USERDEPDIRS) ; do \
$(MAKE) -C $$dir depend || exit; \
done
pass2dep: context tools/mkdeps$(HOSTEXEEXT) tools/cnvwindeps$(HOSTEXEEXT)
pass2dep: context tools/mkdeps$(HOSTEXEEXT) tools/cnvwindeps$(HOSTEXEEXT) $(PASSWD_TOOL_DEP)
$(Q) for dir in $(KERNDEPDIRS) ; do \
$(MAKE) -C $$dir EXTRAFLAGS="$(KDEFINE) $(EXTRAFLAGS)" depend || exit; \
done
@@ -759,6 +767,8 @@ savedefconfig: apps_preconfig
$(Q) ${KCONFIG_ENV} ${KCONFIG_SAVEDEFCONFIG}
$(Q) $(call kconfig_tweak_disable,defconfig.tmp,CONFIG_APPS_DIR)
$(Q) $(call kconfig_tweak_disable,defconfig.tmp,CONFIG_BASE_DEFCONFIG)
$(Q) sed -i -e '/^CONFIG_FSUTILS_PASSWD_KEY[0-9]/d' defconfig.tmp
$(Q) sed -i -e '/^CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD=/d' defconfig.tmp
$(Q) grep "CONFIG_ARCH=" .config >> defconfig.tmp
$(Q) grep "^CONFIG_ARCH_CHIP_" .config >> defconfig.tmp; true
$(Q) grep "CONFIG_ARCH_CHIP=" .config >> defconfig.tmp; true
@@ -778,6 +788,11 @@ savedefconfig: apps_preconfig
$(Q) rm -f warning.tmp
$(Q) rm -f defconfig.tmp
$(Q) rm -f sortedconfig.tmp
$(Q) if grep -q '^CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y' .config; then \
echo "WARNING: CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD was not saved in defconfig."; \
echo "WARNING: CONFIG_FSUTILS_PASSWD_KEY1-4 were not saved in defconfig."; \
echo "WARNING: This is intentional to avoid leaking credentials. Add them manually in local defconfig if needed."; \
fi
# export
#
+625
View File
File diff suppressed because it is too large Load Diff