!tools/mkpasswd: PBKDF2 host tool and ROMFS passwd build integration

Add standalone host PBKDF2-HMAC-SHA256 mkpasswd, board_romfs_mkpasswd.sh,
and promptpasswd.sh with confirm-password support. Integrate ROMFS passwd
generation in Board.mk and CMake. Drop TEA key checks from passwd_keys.mk.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit is contained in:
Abhishek Mishra 2026-07-15 15:44:37 +00:00 committed by Xiang Xiao
parent 82ab33aed6
commit 4761f15b3e
11 changed files with 807 additions and 505 deletions

View file

@ -107,7 +107,7 @@ ifdef HOSTEXEEXT
mkversion: mkversion$(HOSTEXEEXT)
endif
# mkpasswd - Generate a NuttX /etc/passwd entry with TEA-encrypted password
# mkpasswd - Generate a NuttX /etc/passwd entry with PBKDF2-HMAC-SHA256 hash
mkpasswd$(HOSTEXEEXT): mkpasswd.c
$(Q) $(HOSTCC) $(HOSTCFLAGS) -o mkpasswd$(HOSTEXEEXT) mkpasswd.c

View file

@ -780,7 +780,7 @@ 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.bak -e '/^CONFIG_FSUTILS_PASSWD_KEY[0-9]/d' defconfig.tmp
$(Q) sed -i.bak -e '/^CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS=/d' defconfig.tmp
$(Q) sed -i.bak -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
@ -803,7 +803,7 @@ savedefconfig: apps_preconfig
$(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: CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS was not saved in defconfig."; \
echo "WARNING: This is intentional to avoid leaking credentials. Add them manually in local defconfig if needed."; \
fi

62
tools/board_romfs_mkpasswd.sh Executable file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env bash
# tools/board_romfs_mkpasswd.sh
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.
#
# Ensure the ROMFS root password is configured, then run mkpasswd.
# Arguments:
# board_romfs_mkpasswd.sh <nuttx-topdir> <passfile> <mkpasswd> <output> [mkpasswd args...]
set -e
TOPDIR=$1
PASSFILE=$2
MKPASSWD=$3
OUTPUT=$4
shift 4
CONFIG_FILE="${TOPDIR}/.config"
read_int_config() {
local symbol=$1
local default=$2
local value
value=$(grep "^${symbol}=" "${CONFIG_FILE}" 2>/dev/null | cut -d= -f2- | tr -d '"')
if [ -z "${value}" ]; then
echo "${default}"
else
echo "${value}"
fi
}
ITERATIONS=$(read_int_config CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS 10000)
"${TOPDIR}/tools/promptpasswd.sh" \
--min 8 \
--config CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD \
--config-file "${CONFIG_FILE}" \
--update-config \
--prompt "ROMFS root password (min 8 characters): " \
--output-file "${PASSFILE}"
PASSWORD=$(cat "${PASSFILE}")
"${MKPASSWD}" --password "${PASSWORD}" \
--iterations "${ITERATIONS}" \
"$@" -o "${OUTPUT}"
rm -f "${PASSFILE}"

View file

@ -356,7 +356,7 @@ echo "CONFIG_BASE_DEFCONFIG=\"$posboardconfig\"" >> "${dest_config}"
${TOPDIR}/tools/sethost.sh $host $*
# Supply ROMFS admin password from NUTTX_ROMFS_PASSWD_PASSWORD when absent
# Supply ROMFS root password from NUTTX_ROMFS_PASSWD_PASSWORD when absent
"${TOPDIR}/tools/update_romfs_password.sh" "${dest_config}"
# Save the original configuration file without CONFIG_BASE_DEFCONFIG

File diff suppressed because it is too large Load diff

View file

@ -3,13 +3,9 @@
#
# 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.
# ROMFS password validation. Included from the top-level Makefile
# immediately after .config is loaded, BEFORE tools/Unix.mk builds
# include/nuttx/config.h.
############################################################################
TOPDIR ?= .
@ -28,6 +24,30 @@ else
_PASSWD_ENFORCE := $(if $(filter-out $(PASSWD_SKIP_GOALS),$(MAKECMDGOALS)),y,)
endif
ifeq ($(_PASSWD_ENFORCE),y)
# Reject removed fixed-login symbols left in stale .config or defconfig files.
ifneq ($(shell grep -c '^CONFIG_NSH_LOGIN_FIXED=y' $(TOPDIR)/.config 2>/dev/null),0)
$(error CONFIG_NSH_LOGIN_FIXED was removed. Enable CONFIG_FSUTILS_PASSWD and CONFIG_NSH_LOGIN_PASSWD, or use CONFIG_NSH_LOGIN_PLATFORM with platform_user_verify().)
endif
ifneq ($(shell grep -c '^CONFIG_NSH_LOGIN_PASSWORD=' $(TOPDIR)/.config 2>/dev/null),0)
$(error CONFIG_NSH_LOGIN_PASSWORD was removed. Set CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD in menuconfig or export NUTTX_ROMFS_PASSWD_PASSWORD.)
endif
ifeq ($(CONFIG_NSH_CONSOLE_LOGIN),y)
ifeq ($(CONFIG_FSUTILS_PASSWD),)
$(error NSH console login requires CONFIG_FSUTILS_PASSWD. Fixed login was removed; enable password file support and CONFIG_NSH_LOGIN_PASSWD.)
endif
endif
ifeq ($(CONFIG_NSH_TELNET_LOGIN),y)
ifeq ($(CONFIG_FSUTILS_PASSWD),)
$(error NSH telnet login requires CONFIG_FSUTILS_PASSWD. Fixed login was removed; enable password file support and CONFIG_NSH_LOGIN_PASSWD.)
endif
endif
endif
ifeq ($(CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE),y)
ifeq ($(_PASSWD_ENFORCE),y)
@ -35,43 +55,24 @@ ifeq ($(_PASSWD_ENFORCE),y)
$(shell $(TOPDIR)/tools/update_romfs_password.sh $(TOPDIR)/.config >/dev/null 2>&1)
include $(TOPDIR)/.config
# --- password check ---
ifeq ($(strip $(patsubst "%",%,$(CONFIG_BOARD_ETC_ROMFS_PASSWD_PASSWORD))),)
_PASSWD_HAS_TTY := $(shell test -r /dev/tty && test -w /dev/tty && echo 1)
ifneq ($(_PASSWD_HAS_TTY),1)
$(info )
$(info BUILD ERROR: Admin password not set.)
$(info BUILD ERROR: Root password not set.)
$(info )
$(info Run make menuconfig and set:)
$(info Board Selection -> Auto-generate /etc/passwd -> Admin password)
$(info Board Selection -> Auto-generate /etc/passwd -> Root 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 For CI or scripted builds, export NUTTX_ROMFS_PASSWD_PASSWORD)
$(info (see tools/update_romfs_password.sh).)
$(info )
$(info Password and keys are not saved in defconfig.)
$(info Password is 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)
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
# Interactive builds: board_romfs_mkpasswd.sh / promptpasswd.sh will prompt.
endif
endif
endif

180
tools/promptpasswd.sh Executable file
View file

@ -0,0 +1,180 @@
#!/usr/bin/env bash
# tools/promptpasswd.sh
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.
#
# Prompt for a Kconfig password when it is unset or invalid. If no
# terminal is available, print an error and exit.
#
# Usage:
# promptpasswd.sh --min <n> [--config <symbol>] [--config-file <file>]
# [--update-config] [--prompt <text>] [--output-file <file>]
set -e
MIN=8
VALUE=""
PROMPT="Password: "
CONFIG_SYMBOL=""
CONFIG_FILE=".config"
UPDATE_CONFIG=0
OUTPUT_FILE=""
while [ $# -gt 0 ]; do
case "$1" in
--min)
MIN=$2
shift 2
;;
--value)
VALUE=$2
shift 2
;;
--prompt)
PROMPT=$2
shift 2
;;
--config)
CONFIG_SYMBOL=$2
shift 2
;;
--config-file)
CONFIG_FILE=$2
shift 2
;;
--update-config)
UPDATE_CONFIG=1
shift
;;
--output-file)
OUTPUT_FILE=$2
shift 2
;;
*)
echo "promptpasswd.sh: unknown option: $1" >&2
exit 1
;;
esac
done
validate_password() {
local pw="$1"
local ok=0
if [ ${#pw} -lt "${MIN}" ]; then
echo "Error: password must be at least ${MIN} characters" >&2
ok=1
fi
if ! printf '%s' "$pw" | grep -q '[A-Z]'; then
echo "Error: password must contain at least one uppercase letter (A-Z)" >&2
ok=1
fi
if ! printf '%s' "$pw" | grep -q '[a-z]'; then
echo "Error: password must contain at least one lowercase letter (a-z)" >&2
ok=1
fi
if ! printf '%s' "$pw" | grep -q '[0-9]'; then
echo "Error: password must contain at least one digit (0-9)" >&2
ok=1
fi
if ! printf '%s' "$pw" | grep -q '[^a-zA-Z0-9]'; then
echo "Error: password must contain at least one special character" \
"(!@#\$%^&*()_+-=[]{}|;:,.<>?)" >&2
ok=1
fi
return "${ok}"
}
if [ -n "${CONFIG_SYMBOL}" ] && [ -z "${VALUE}" ] && [ -f "${CONFIG_FILE}" ]; then
VALUE=$(grep "^${CONFIG_SYMBOL}=" "${CONFIG_FILE}" 2>/dev/null | cut -d= -f2- | tr -d '"')
fi
if [ -n "${VALUE}" ] && validate_password "${VALUE}"; then
if [ -n "${OUTPUT_FILE}" ]; then
umask 077
printf '%s' "${VALUE}" > "${OUTPUT_FILE}"
else
printf '%s' "${VALUE}"
fi
exit 0
fi
# Make recipe shells are not connected to the terminal on stdin, so test /dev/tty
# instead of [ -t 0 ] when deciding whether an interactive prompt is possible.
INTERACTIVE=0
if [ -r /dev/tty ] && [ -w /dev/tty ]; then
INTERACTIVE=1
fi
if [ "${INTERACTIVE}" -eq 0 ]; then
echo "" >&2
if [ -n "${CONFIG_SYMBOL}" ]; then
echo "ERROR: ${CONFIG_SYMBOL} must be at least ${MIN} characters and" >&2
echo "contain uppercase, lowercase, digit, and special character." >&2
else
echo "ERROR: Password must be at least ${MIN} characters and contain" >&2
echo "uppercase, lowercase, digit, and special character." >&2
fi
echo "Set it with 'make menuconfig' or edit .config, then rebuild." >&2
exit 1
fi
PASSWORD=""
while true; do
printf '%s' "${PROMPT}" >/dev/tty
IFS= read -r -s PASSWORD </dev/tty
echo "" >/dev/tty
if ! validate_password "${PASSWORD}"; then
echo "Please try again." >&2
PASSWORD=""
continue
fi
while true; do
printf 'Confirm password: ' >/dev/tty
IFS= read -r -s PASSWORD2 </dev/tty
echo "" >/dev/tty
if [ "${PASSWORD}" = "${PASSWORD2}" ]; then
break
fi
echo "Passwords do not match. Please try again." >&2
PASSWORD=""
break
done
if [ -n "${PASSWORD}" ]; then
break
fi
done
if [ "${UPDATE_CONFIG}" -eq 1 ] && [ -n "${CONFIG_SYMBOL}" ]; then
kconfig-tweak --file "${CONFIG_FILE}" --set-str "${CONFIG_SYMBOL}" "${PASSWORD}"
fi
if [ -n "${OUTPUT_FILE}" ]; then
umask 077
printf '%s' "${PASSWORD}" > "${OUTPUT_FILE}"
else
printf '%s' "${PASSWORD}"
fi

View file

@ -19,7 +19,7 @@
# Usage:
# update_romfs_password.sh <path-to-.config>
#
# When CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y and the admin password is not
# When CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y and the root password is not
# set in .config, copy NUTTX_ROMFS_PASSWD_PASSWORD into .config. This is the
# supported way to supply build-time credentials that must not live in defconfig
# (CI, automation, local scripts). No-op when the password is already set or