From 8778f482bd36890c04d6aba03315de8382a144ca Mon Sep 17 00:00:00 2001 From: Felipe Moura Date: Mon, 8 Jun 2026 13:09:06 -0300 Subject: [PATCH] netutils/dropbear: initial Dropbear SSH server port for NuttX Ports the Dropbear SSH server to NuttX targeting RISC-V (ESP32-C3). Companion to the NuttX kernel PR that adds the ESP32-C3 dropbear defconfig and ChaCha20 stream helpers. The port provides a fully integrated SSH daemon that authenticates users against the NuttX passwd file (FSUTILS_PASSWD) via a pluggable nuttx_auth.c layer, generates and persists an ECDSA P-256 host key on SPIFFS, and spawns an interactive NSH session on a PTY for each authenticated connection. Build system: - Makefile/CMakeLists.txt: download the upstream Dropbear tarball at build time, compile it with bundled libtomcrypt/libtommath, and link into the NuttX image. - Uses gpatch on macOS (CONFIG_HOST_MACOS) since Apple patch 2.0 lacks support for diff --git format. All patch invocations use -i flag instead of stdin redirect to avoid conflicts under parallel make. - patch/0001-0007: series of patches applied to the upstream tree to replace autoconf-generated headers, redirect crypto primitives to NuttX kernel APIs, fix compiler warnings, and guard the environ declaration for NuttX. Patches 0003-0006 strip diff --git/index headers for BSD patch compatibility. Port layer (netutils/dropbear/port/): - nuttx_config.h/localoptions.h: trim algorithm set to ECDSA, ChaCha20-Poly1305, Curve25519, AES-128-CTR, and HMAC-SHA256. - nuttx_hostkey.c: generate and store ECDSA host key on first run. - nuttx_auth.c: password verification against NuttX passwd file. - dropbear_chachapoly.c: wires ChaCha20-Poly1305 to chacha20_stream_* helpers from the kernel PR. - dropbear_ltc_aes.c/hmac_sha256.c/sha256.c: redirect libtomcrypt calls to NuttX kernel crypto APIs. - nuttx_compat.c: minimal POSIX shims. Application layer: - dropbear_main.c: foreground accept loop with configurable listen retries; setjmp/longjmp recovery fence per session. - dropbear_nshsession.c: ChanType handler opening a PTY pair, spawning NSH on the slave end, threading I/O, delivering SIGINT on Ctrl-C. NSH integration (nshlib/): - nsh_dropbear.c/nsh_init.c: optionally autostart dropbear at NSH boot when CONFIG_NSH_DROPBEAR is enabled. fsutils/passwd: - passwd_adduser.c: create passwd file if missing before appending first user, preventing error on fresh filesystem. Co-Authored-By: Claude Sonnet 4.6 --- fsutils/passwd/passwd_adduser.c | 11 + fsutils/passwd/passwd_append.c | 2 +- netutils/dropbear/.gitignore | 6 + netutils/dropbear/CMakeLists.txt | 207 ++++++ netutils/dropbear/Kconfig | 102 +++ netutils/dropbear/Make.defs | 25 + netutils/dropbear/Makefile | 177 +++++ netutils/dropbear/README.md | 113 +++ netutils/dropbear/dropbear_main.c | 305 ++++++++ netutils/dropbear/dropbear_nshsession.c | 615 ++++++++++++++++ .../patch/0001-use-nuttx-unused-macro.patch | 291 ++++++++ .../0002-use-nuttx-ecdsa-hostkey-sign.patch | 103 +++ ...-guard-environ-declaration-for-nuttx.patch | 14 + .../0004-fix-nuttx-compile-warnings.patch | 107 +++ .../patch/0005-use-nuttx-sha256-hmac.patch | 45 ++ .../0006-use-nuttx-chachapoly-state.patch | 84 +++ .../patch/0007-use-nuttx-passwd-auth.patch | 87 +++ netutils/dropbear/port/config.h | 20 + .../dropbear/port/default_options_guard.h | 693 ++++++++++++++++++ netutils/dropbear/port/dropbear_chachapoly.c | 172 +++++ netutils/dropbear/port/dropbear_crypto.c | 87 +++ netutils/dropbear/port/dropbear_crypto.h | 47 ++ netutils/dropbear/port/dropbear_curve25519.c | 24 + netutils/dropbear/port/dropbear_ltc_aes.c | 300 ++++++++ .../dropbear/port/dropbear_ltc_hmac_sha256.c | 103 +++ netutils/dropbear/port/dropbear_ltc_sha256.c | 75 ++ netutils/dropbear/port/dropbear_utils.c | 139 ++++ netutils/dropbear/port/dropbear_utils.h | 31 + netutils/dropbear/port/localoptions.h | 21 + netutils/dropbear/port/nuttx_auth.c | 109 +++ netutils/dropbear/port/nuttx_compat.c | 78 ++ netutils/dropbear/port/nuttx_config.h | 139 ++++ netutils/dropbear/port/nuttx_hostkey.c | 303 ++++++++ netutils/dropbear/port/nuttx_hostkey.h | 30 + netutils/dropbear/port/nuttx_localoptions.h | 58 ++ nshlib/CMakeLists.txt | 4 + nshlib/Kconfig | 22 + nshlib/Makefile | 4 + nshlib/nsh.h | 4 + nshlib/nsh_dropbear.c | 76 ++ nshlib/nsh_init.c | 12 + 41 files changed, 4844 insertions(+), 1 deletion(-) create mode 100644 netutils/dropbear/.gitignore create mode 100644 netutils/dropbear/CMakeLists.txt create mode 100644 netutils/dropbear/Kconfig create mode 100644 netutils/dropbear/Make.defs create mode 100644 netutils/dropbear/Makefile create mode 100644 netutils/dropbear/README.md create mode 100644 netutils/dropbear/dropbear_main.c create mode 100644 netutils/dropbear/dropbear_nshsession.c create mode 100644 netutils/dropbear/patch/0001-use-nuttx-unused-macro.patch create mode 100644 netutils/dropbear/patch/0002-use-nuttx-ecdsa-hostkey-sign.patch create mode 100644 netutils/dropbear/patch/0003-guard-environ-declaration-for-nuttx.patch create mode 100644 netutils/dropbear/patch/0004-fix-nuttx-compile-warnings.patch create mode 100644 netutils/dropbear/patch/0005-use-nuttx-sha256-hmac.patch create mode 100644 netutils/dropbear/patch/0006-use-nuttx-chachapoly-state.patch create mode 100644 netutils/dropbear/patch/0007-use-nuttx-passwd-auth.patch create mode 100644 netutils/dropbear/port/config.h create mode 100644 netutils/dropbear/port/default_options_guard.h create mode 100644 netutils/dropbear/port/dropbear_chachapoly.c create mode 100644 netutils/dropbear/port/dropbear_crypto.c create mode 100644 netutils/dropbear/port/dropbear_crypto.h create mode 100644 netutils/dropbear/port/dropbear_curve25519.c create mode 100644 netutils/dropbear/port/dropbear_ltc_aes.c create mode 100644 netutils/dropbear/port/dropbear_ltc_hmac_sha256.c create mode 100644 netutils/dropbear/port/dropbear_ltc_sha256.c create mode 100644 netutils/dropbear/port/dropbear_utils.c create mode 100644 netutils/dropbear/port/dropbear_utils.h create mode 100644 netutils/dropbear/port/localoptions.h create mode 100644 netutils/dropbear/port/nuttx_auth.c create mode 100644 netutils/dropbear/port/nuttx_compat.c create mode 100644 netutils/dropbear/port/nuttx_config.h create mode 100644 netutils/dropbear/port/nuttx_hostkey.c create mode 100644 netutils/dropbear/port/nuttx_hostkey.h create mode 100644 netutils/dropbear/port/nuttx_localoptions.h create mode 100644 nshlib/nsh_dropbear.c diff --git a/fsutils/passwd/passwd_adduser.c b/fsutils/passwd/passwd_adduser.c index 3a14ac8124a..4136d0ffd7c 100644 --- a/fsutils/passwd/passwd_adduser.c +++ b/fsutils/passwd/passwd_adduser.c @@ -26,6 +26,7 @@ #include #include +#include #include "fsutils/passwd.h" #include "passwd.h" @@ -55,6 +56,7 @@ int passwd_adduser(FAR const char *username, FAR const char *password) { struct passwd_s passwd; PASSWD_SEM_DECL(sem); + FAR FILE *stream; int ret; /* Get exclusive access to the /etc/passwd file */ @@ -67,6 +69,15 @@ int passwd_adduser(FAR const char *username, FAR const char *password) /* Check if the username already exists */ + stream = fopen(CONFIG_FSUTILS_PASSWD_PATH, "a"); + if (stream == NULL) + { + ret = -errno; + goto errout_with_lock; + } + + fclose(stream); + ret = passwd_find(username, &passwd); if (ret >= 0) { diff --git a/fsutils/passwd/passwd_append.c b/fsutils/passwd/passwd_append.c index be77c9aed65..125a4cfe671 100644 --- a/fsutils/passwd/passwd_append.c +++ b/fsutils/passwd/passwd_append.c @@ -69,7 +69,7 @@ int passwd_append(FAR const char *username, FAR const char *password) { int errcode = errno; DEBUGASSERT(errcode > 0); - return errcode; + return -errcode; } /* The format of the password file is: diff --git a/netutils/dropbear/.gitignore b/netutils/dropbear/.gitignore new file mode 100644 index 00000000000..1dc4834a11d --- /dev/null +++ b/netutils/dropbear/.gitignore @@ -0,0 +1,6 @@ +/dropbear +/*.zip +*.o +.built +.depend +Make.dep diff --git a/netutils/dropbear/CMakeLists.txt b/netutils/dropbear/CMakeLists.txt new file mode 100644 index 00000000000..46aa20a2665 --- /dev/null +++ b/netutils/dropbear/CMakeLists.txt @@ -0,0 +1,207 @@ +# ############################################################################## +# apps/netutils/dropbear/CMakeLists.txt +# +# 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. +# +# ############################################################################## + +if(CONFIG_NETUTILS_DROPBEAR) + + set(DROPBEAR_COMMIT "${CONFIG_NETUTILS_DROPBEAR_COMMIT}") + string(REPLACE "\"" "" DROPBEAR_COMMIT "${DROPBEAR_COMMIT}") + + set(DROPBEAR_ZIP "${DROPBEAR_COMMIT}.zip") + set(DROPBEAR_URL "https://github.com/mkj/dropbear/archive") + set(DROPBEAR_UNPACKNAME "dropbear") + + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}") + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_ZIP}") + message(STATUS "Downloading Dropbear: ${DROPBEAR_URL}/${DROPBEAR_ZIP}") + file(DOWNLOAD "${DROPBEAR_URL}/${DROPBEAR_ZIP}" + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_ZIP}") + endif() + message(STATUS "Unpacking Dropbear: ${DROPBEAR_ZIP}") + execute_process( + COMMAND unzip -q -o "${DROPBEAR_ZIP}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE result) + if(result EQUAL 0) + file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/dropbear-${DROPBEAR_COMMIT}" + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0001-use-nuttx-unused-macro.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0002-use-nuttx-ecdsa-hostkey-sign.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0003-guard-environ-declaration-for-nuttx.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0004-fix-nuttx-compile-warnings.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0005-use-nuttx-sha256-hmac.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0006-use-nuttx-chachapoly-state.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0007-use-nuttx-passwd-auth.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + endif() + + set(PROGNAME "${CONFIG_NETUTILS_DROPBEAR_PROGNAME}") + string(REPLACE "\"" "" PROGNAME "${PROGNAME}") + + set(DROPBEAR_SRCS + dropbear_nshsession.c + port/nuttx_auth.c + port/nuttx_compat.c + port/dropbear_chachapoly.c + port/dropbear_crypto.c + port/dropbear_curve25519.c + port/dropbear_ltc_aes.c + port/dropbear_ltc_hmac_sha256.c + port/dropbear_ltc_sha256.c + port/dropbear_utils.c + port/nuttx_hostkey.c + dropbear/src/dbutil.c + dropbear/src/buffer.c + dropbear/src/dbhelpers.c + dropbear/src/bignum.c + dropbear/src/signkey.c + dropbear/src/dbrandom.c + dropbear/src/queue.c + dropbear/src/atomicio.c + dropbear/src/compat.c + dropbear/src/fake-rfc2553.c + dropbear/src/ltc_prng.c + dropbear/src/ecc.c + dropbear/src/ecdsa.c + dropbear/src/crypto_desc.c + dropbear/src/dbmalloc.c + dropbear/src/gensignkey.c + dropbear/src/common-session.c + dropbear/src/packet.c + dropbear/src/common-algo.c + dropbear/src/common-kex.c + dropbear/src/common-channel.c + dropbear/src/common-chansession.c + dropbear/src/termcodes.c + dropbear/src/tcp-accept.c + dropbear/src/listener.c + dropbear/src/process-packet.c + dropbear/src/common-runopts.c + dropbear/src/circbuffer.c + dropbear/src/list.c + dropbear/src/netio.c + dropbear/src/gcm.c + dropbear/src/kex-x25519.c + dropbear/src/svr-kex.c + dropbear/src/svr-auth.c + dropbear/src/svr-authpasswd.c + dropbear/src/svr-session.c + dropbear/src/svr-service.c + dropbear/src/svr-runopts.c + dropbear/src/svr-tcpfwd.c + dropbear/src/svr-authpam.c) + + file(GLOB LIBTOMMATH_SRCS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtommath/*.c") + list(APPEND DROPBEAR_SRCS ${LIBTOMMATH_SRCS}) + + file(GLOB_RECURSE LIBTOMCRYPT_SRCS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtomcrypt/src/*.c") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/pk/ecc/ecc_make_key\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/pk/ecc/ecc_encrypt_key\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/pk/ecc/ecc_decrypt_key\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/pk/ecc/ecc_shared_secret\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/pk/ecc/ecc_sign_hash\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/pk/ecc/ecc_verify_hash\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/pk/ecc/ecc_test\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/ciphers/aes/aes\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/ciphers/aes/aes_tab\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/hmac/hmac_done\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/hmac/hmac_init\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/hmac/hmac_process\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/poly1305/.*\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/encauth/chachapoly/.*\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/prngs/chacha20\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/stream/chacha/.*\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/hashes/sha2/sha256\\.c$") + list(APPEND DROPBEAR_SRCS ${LIBTOMCRYPT_SRCS}) + + nuttx_add_application( + NAME + ${PROGNAME} + SRCS + ${DROPBEAR_SRCS} + dropbear_main.c + STACKSIZE + ${CONFIG_NETUTILS_DROPBEAR_STACKSIZE} + PRIORITY + ${CONFIG_NETUTILS_DROPBEAR_PRIORITY} + DEPENDS + ${DROPBEAR_UNPACKNAME}) + + target_include_directories( + ${PROGNAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/port + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear/src + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtomcrypt/src/headers + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtommath + ${CMAKE_CURRENT_SOURCE_DIR}/../../nshlib) + + target_compile_definitions( + ${PROGNAME} + PRIVATE LOCALOPTIONS_H_EXISTS=1 DROPBEAR_NUTTX=1 + DROPBEAR_NUTTX_CHACHAPOLY=1 DROPBEAR_NUTTX_HMAC_SHA256=1 + DROPBEAR_NUTTX_PASSWD=1 DROPBEAR_NUTTX_SHA256=1) + + set_source_files_properties( + dropbear_nshsession.c + PROPERTIES COMPILE_DEFINITIONS + "Channel=dropbear_channel;ChanType=dropbear_chantype") + + # LTC_SOURCE must be set only for libtomcrypt sources. + set_source_files_properties(${LIBTOMCRYPT_SRCS} PROPERTIES COMPILE_DEFINITIONS + LTC_SOURCE=1) + + target_compile_options(${PROGNAME} PRIVATE -Wno-pointer-sign -Wno-format) + + target_sources(apps PRIVATE ${DROPBEAR_SRCS}) + +endif() diff --git a/netutils/dropbear/Kconfig b/netutils/dropbear/Kconfig new file mode 100644 index 00000000000..8283f34f0a4 --- /dev/null +++ b/netutils/dropbear/Kconfig @@ -0,0 +1,102 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig NETUTILS_DROPBEAR + tristate "Dropbear SSH server" + default n + depends on NET && NET_TCP + depends on !DISABLE_PSEUDOFS_OPERATIONS + depends on !DISABLE_PTHREAD + depends on SCHED_WAITPID + depends on NSH_LIBRARY + depends on FSUTILS_PASSWD + depends on PSEUDOTERM + depends on SERIAL + depends on ARCH_HAVE_RNG + select CRYPTO + select CRYPTO_RANDOM_POOL + select DEV_RANDOM + select DEV_URANDOM + select LIBC_NETDB + select LIBC_GAISTRERROR + ---help--- + Enable a minimal Dropbear SSH server port for NuttX. This initial + port is based on the ESP-IDF MCU test port and provides a single + foreground SSH server process with SSH sessions backed by NSH. + +if NETUTILS_DROPBEAR + +config NETUTILS_DROPBEAR_STACKSIZE + int "Dropbear main stack size" + default 65536 if ARCH_CHIP_ESP32C3 + default 32768 + ---help--- + Stack size for the Dropbear server built-in. The ESP32-C3 test + results showed key exchange needs more than 32 KiB on RISC-V, so + 64 KiB is the conservative default for ESP32-C3. + +config NETUTILS_DROPBEAR_PRIORITY + int "Dropbear main priority" + default 100 + +config NETUTILS_DROPBEAR_SHELL_PRIORITY + int "Dropbear NSH session priority" + default 100 + +config NETUTILS_DROPBEAR_PROGNAME + string "Dropbear program name" + default "dropbear" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config NETUTILS_DROPBEAR_LISTEN_RETRIES + int "Dropbear listen retries" + default 0 + ---help--- + Number of times to retry listen setup when no listen socket could + be opened. Zero means to retry forever. + +config NETUTILS_DROPBEAR_LISTEN_RETRY_MAX + int "Dropbear maximum listen retry interval" + default 120 + range 1 3600 + ---help--- + Maximum number of seconds to wait between listen setup retries. + The retry delay starts at one second and doubles until it reaches + this value. + +config NETUTILS_DROPBEAR_SHELL_STACKSIZE + int "Dropbear NSH session task stack size" + default 8192 + +config NETUTILS_DROPBEAR_PORT + int "Dropbear listen port" + default 2222 + +config NETUTILS_DROPBEAR_HOSTKEY_PATH + string "Dropbear ECDSA P-256 host key path" + default "/etc/dropbear/dropbear_ecdsa_host_key" + ---help--- + Path to the persistent ECDSA P-256 host key used by the Dropbear + server. The file stores the private scalar and public point in a + NuttX-specific text format: + nuttx-ecdsa-p256-v1:d_hex:x_hex:y_hex + +config NETUTILS_DROPBEAR_GENERATE_HOSTKEY + bool "Generate host key if missing" + default y + ---help--- + Generate an ECDSA P-256 host key with NuttX crypto on first boot + when NETUTILS_DROPBEAR_HOSTKEY_PATH does not exist. Product builds + can disable this and provision the host key externally. + +config NETUTILS_DROPBEAR_COMMIT + string "Dropbear upstream commit" + default "75f699bfe2c234418056776c4d9f651a07a76de6" + ---help--- + Upstream Dropbear revision used by the ESP-IDF validation repo. + +endif diff --git a/netutils/dropbear/Make.defs b/netutils/dropbear/Make.defs new file mode 100644 index 00000000000..9fcb635f7a4 --- /dev/null +++ b/netutils/dropbear/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/netutils/dropbear/Make.defs +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_NETUTILS_DROPBEAR),) +CONFIGURED_APPS += $(APPDIR)/netutils/dropbear +endif diff --git a/netutils/dropbear/Makefile b/netutils/dropbear/Makefile new file mode 100644 index 00000000000..38e510772f4 --- /dev/null +++ b/netutils/dropbear/Makefile @@ -0,0 +1,177 @@ +############################################################################ +# apps/netutils/dropbear/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +ifneq ($(CONFIG_NETUTILS_DROPBEAR),) + +DROPBEAR_COMMIT = $(patsubst "%",%,$(strip $(CONFIG_NETUTILS_DROPBEAR_COMMIT))) +DROPBEAR_ZIP = $(DROPBEAR_COMMIT).zip +DROPBEAR_UNPACKNAME = dropbear +DROPBEAR_URL = https://github.com/mkj/dropbear/archive +UNPACK ?= unzip -q -o +ifeq ($(CONFIG_HOST_MACOS),y) +PATCH ?= gpatch +else +PATCH ?= patch +endif + +MODULE = $(CONFIG_NETUTILS_DROPBEAR) +PROGNAME = $(CONFIG_NETUTILS_DROPBEAR_PROGNAME) +PRIORITY = $(CONFIG_NETUTILS_DROPBEAR_PRIORITY) +STACKSIZE = $(CONFIG_NETUTILS_DROPBEAR_STACKSIZE) + +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)port" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear$(DELIM)src" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear$(DELIM)libtomcrypt$(DELIM)src$(DELIM)headers" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear$(DELIM)libtommath" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)nshlib" +CFLAGS += ${DEFINE_PREFIX}LOCALOPTIONS_H_EXISTS=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX_CHACHAPOLY=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX_HMAC_SHA256=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX_PASSWD=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX_SHA256=1 +CFLAGS += -Wno-pointer-sign -Wno-format + +dropbear_nshsession.c_CFLAGS += ${DEFINE_PREFIX}Channel=dropbear_channel +dropbear_nshsession.c_CFLAGS += ${DEFINE_PREFIX}ChanType=dropbear_chantype + +CSRCS = dropbear_nshsession.c +CSRCS += port/nuttx_auth.c +CSRCS += port/nuttx_compat.c +CSRCS += port/dropbear_chachapoly.c +CSRCS += port/dropbear_crypto.c +CSRCS += port/dropbear_curve25519.c +CSRCS += port/dropbear_ltc_aes.c +CSRCS += port/dropbear_ltc_hmac_sha256.c +CSRCS += port/dropbear_ltc_sha256.c +CSRCS += port/dropbear_utils.c +CSRCS += port/nuttx_hostkey.c + +CSRCS += \ + dropbear/src/dbutil.c \ + dropbear/src/buffer.c \ + dropbear/src/dbhelpers.c \ + dropbear/src/bignum.c \ + dropbear/src/signkey.c \ + dropbear/src/dbrandom.c \ + dropbear/src/queue.c \ + dropbear/src/atomicio.c \ + dropbear/src/compat.c \ + dropbear/src/fake-rfc2553.c \ + dropbear/src/ltc_prng.c \ + dropbear/src/ecc.c \ + dropbear/src/ecdsa.c \ + dropbear/src/crypto_desc.c \ + dropbear/src/dbmalloc.c \ + dropbear/src/gensignkey.c \ + dropbear/src/common-session.c \ + dropbear/src/packet.c \ + dropbear/src/common-algo.c \ + dropbear/src/common-kex.c \ + dropbear/src/common-channel.c \ + dropbear/src/common-chansession.c \ + dropbear/src/termcodes.c \ + dropbear/src/tcp-accept.c \ + dropbear/src/listener.c \ + dropbear/src/process-packet.c \ + dropbear/src/common-runopts.c \ + dropbear/src/circbuffer.c \ + dropbear/src/list.c \ + dropbear/src/netio.c \ + dropbear/src/gcm.c \ + dropbear/src/kex-x25519.c \ + dropbear/src/svr-kex.c \ + dropbear/src/svr-auth.c \ + dropbear/src/svr-authpasswd.c \ + dropbear/src/svr-session.c \ + dropbear/src/svr-service.c \ + dropbear/src/svr-runopts.c \ + dropbear/src/svr-tcpfwd.c \ + dropbear/src/svr-authpam.c + +TOMMATH_SRCS = $(shell if [ -d "$(DROPBEAR_UNPACKNAME)/libtommath" ]; then find "$(DROPBEAR_UNPACKNAME)/libtommath" -name "*.c"; fi) +TOMCRYPT_SRCS = $(shell if [ -d "$(DROPBEAR_UNPACKNAME)/libtomcrypt/src" ]; then find "$(DROPBEAR_UNPACKNAME)/libtomcrypt/src" -name "*.c" ! -name "sober128tab.c"; fi) +TOMCRYPT_SRCS := $(filter-out \ + %/pk/ecc/ecc_make_key.c \ + %/pk/ecc/ecc_encrypt_key.c \ + %/pk/ecc/ecc_decrypt_key.c \ + %/pk/ecc/ecc_shared_secret.c \ + %/pk/ecc/ecc_sign_hash.c \ + %/pk/ecc/ecc_verify_hash.c \ + %/pk/ecc/ecc_test.c \ + %/ciphers/aes/aes.c \ + %/ciphers/aes/aes_tab.c \ + %/mac/hmac/hmac_done.c \ + %/mac/hmac/hmac_init.c \ + %/mac/hmac/hmac_process.c \ + $(DROPBEAR_UNPACKNAME)/libtomcrypt/src/mac/poly1305/% \ + $(DROPBEAR_UNPACKNAME)/libtomcrypt/src/encauth/chachapoly/% \ + $(DROPBEAR_UNPACKNAME)/libtomcrypt/src/prngs/chacha20.c \ + $(DROPBEAR_UNPACKNAME)/libtomcrypt/src/stream/chacha/% \ + %/hashes/sha2/sha256.c, \ + $(TOMCRYPT_SRCS)) + +CSRCS += $(TOMMATH_SRCS) +CSRCS += $(TOMCRYPT_SRCS) + +# Match the ESP-IDF port behavior: LTC_SOURCE is only for libtomcrypt sources. +$(foreach src,$(TOMCRYPT_SRCS),$(eval $(src)_CFLAGS += ${DEFINE_PREFIX}LTC_SOURCE=1)) + +MAINSRC = dropbear_main.c + +$(DROPBEAR_ZIP): + @echo "Downloading: $(DROPBEAR_ZIP)" + $(Q) curl -L -o $(DROPBEAR_ZIP) $(DROPBEAR_URL)/$(DROPBEAR_ZIP) + +$(DROPBEAR_UNPACKNAME): $(DROPBEAR_ZIP) + @echo "Unpacking: $(DROPBEAR_ZIP) -> $(DROPBEAR_UNPACKNAME)" + $(Q) $(UNPACK) $(DROPBEAR_ZIP) + $(Q) rm -rf $(DROPBEAR_UNPACKNAME) + $(Q) mv dropbear-$(DROPBEAR_COMMIT) $(DROPBEAR_UNPACKNAME) + @echo "Patching $(DROPBEAR_UNPACKNAME)" + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0001-use-nuttx-unused-macro.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0002-use-nuttx-ecdsa-hostkey-sign.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0003-guard-environ-declaration-for-nuttx.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0004-fix-nuttx-compile-warnings.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0005-use-nuttx-sha256-hmac.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0006-use-nuttx-chachapoly-state.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0007-use-nuttx-passwd-auth.patch; true + $(Q) touch $(DROPBEAR_UNPACKNAME) + +ifeq ($(wildcard $(DROPBEAR_UNPACKNAME)/.git),) +context:: $(DROPBEAR_UNPACKNAME) + +clean:: + $(call DELFILE, port$(DELIM)*.o) + +distclean:: + $(call DELDIR, $(DROPBEAR_UNPACKNAME)) + $(call DELFILE, $(DROPBEAR_ZIP)) +endif + +endif + +include $(APPDIR)/Application.mk diff --git a/netutils/dropbear/README.md b/netutils/dropbear/README.md new file mode 100644 index 00000000000..72e08eab74c --- /dev/null +++ b/netutils/dropbear/README.md @@ -0,0 +1,113 @@ +# Dropbear SSH Server for NuttX + +This is the initial NuttX port scaffold for the MCU Dropbear server validated in +the Windows test repository. It tracks upstream Dropbear commit +`75f699bfe2c234418056776c4d9f651a07a76de6` and keeps the platform-specific +code under `apps/netutils/dropbear`. + +The first target is ESP32-C3. The ESP32-C3 test results showed key exchange +needs a larger stack than the usual NuttX built-in default, so the Kconfig +default for `CONFIG_NETUTILS_DROPBEAR_STACKSIZE` is 64 KiB on ESP32-C3. + +Minimum configuration: + +```text +CONFIG_NET=y +CONFIG_NET_TCP=y +CONFIG_DEV_RANDOM=y +CONFIG_BUILTIN=y +CONFIG_SCHED_WAITPID=y +CONFIG_SERIAL=y +CONFIG_NSH_LIBRARY=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_PSEUDOTERM=y +CONFIG_PSEUDOTERM_SUSV1=y +CONFIG_PSEUDOTERM_RXBUFSIZE=1024 +CONFIG_PSEUDOTERM_TXBUFSIZE=2048 +CONFIG_TTY_SIGINT=y +CONFIG_TTY_SIGINT_CHAR=0x03 +CONFIG_CRYPTO=y +CONFIG_NETUTILS_DROPBEAR=y +CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRIES=0 +CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX=120 +CONFIG_NETUTILS_DROPBEAR_STACKSIZE=65536 +CONFIG_NETUTILS_DROPBEAR_SHELL_PRIORITY=100 +CONFIG_NETUTILS_DROPBEAR_SHELL_STACKSIZE=8192 +CONFIG_NETUTILS_DROPBEAR_PORT=2222 +CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH="/data/dropbear_ecdsa_host_key" +CONFIG_NETUTILS_DROPBEAR_GENERATE_HOSTKEY=y +CONFIG_FSUTILS_PASSWD=y +CONFIG_FSUTILS_PASSWD_PATH="/data/passwd" +CONFIG_NSH_DROPBEAR=y +``` + +With `CONFIG_NSH_DROPBEAR=y`, NSH starts the Dropbear task automatically after +the NSH network bring-up. The server attempts to create its listen sockets +immediately and retries with exponential backoff if networking is not ready yet. +`CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRIES=0` keeps retrying forever. +`CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX` caps the retry interval in seconds. +This is independent of the network device type; Wi-Fi, Ethernet, or another IP +interface can come up later. + +To run manually when autostart is disabled: + +```text +nsh> dropbear & +``` + +Connect from the host: + +```sh +ssh -o StrictHostKeyChecking=no -p 2222 root@ +``` + +After authentication, the SSH session starts a real NSH session over the SSH +channel. The Dropbear port does not fork and exec `/bin/sh`; it adapts the SSH +session channel to `nsh_session()` with `NSH_LOGIN_NONE`, relying on SSH +password authentication for login. + +The SSH session is backed by a NuttX pseudoterminal. NSH and applications +launched by NSH inherit the PTY slave as stdin, stdout, and stderr. + +Host key: + +```text +CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH="/data/dropbear_ecdsa_host_key" +CONFIG_NETUTILS_DROPBEAR_GENERATE_HOSTKEY=y +``` + +The server loads its ECDSA P-256 host key from +`CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH`. When +`CONFIG_NETUTILS_DROPBEAR_GENERATE_HOSTKEY=y` and the file does not exist, +Dropbear generates it with NuttX `crypto/` ECDSA support. The file uses a +NuttX-specific text format: + +```text +nuttx-ecdsa-p256-v1:d_hex:x_hex:y_hex +``` + +ESP32-C3 uses a flat file under `/data` because that is the board's default +SPIFFS mount point and SPIFFS does not support directories. + +Password file: + +```text +CONFIG_FSUTILS_PASSWD=y +CONFIG_FSUTILS_PASSWD_PATH="/data/passwd" +``` + +The server verifies SSH passwords with NuttX `fsutils/passwd`, the same +password backend used by NSH login when `CONFIG_NSH_LOGIN_PASSWD=y`. Dropbear +does not create a private password file. Provision users through the NuttX +passwd support, for example from the serial NSH console: + +```text +nsh> useradd root dropbear +``` + +Initial bring-up credentials: + +```text +user: root +password: dropbear +``` diff --git a/netutils/dropbear/dropbear_main.c b/netutils/dropbear/dropbear_main.c new file mode 100644 index 00000000000..d27fdd02c9d --- /dev/null +++ b/netutils/dropbear/dropbear_main.c @@ -0,0 +1,305 @@ +/**************************************************************************** + * apps/netutils/dropbear/dropbear_main.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "includes.h" +#include "algo.h" +#include "crypto_desc.h" +#define dropbear_main dropbear_multi_entry +#include "dbutil.h" +#undef dropbear_main +#include "dbrandom.h" +#include "netio.h" +#include "runopts.h" +#include "session.h" +#include "ssh.h" +#include "nuttx_hostkey.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_PORT_STRING_HELPER(n) #n +#define DROPBEAR_PORT_STRING(n) DROPBEAR_PORT_STRING_HELPER(n) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef void (*dropbear_exit_handler_t)(int exitcode, FAR const char *format, + va_list param) ATTRIB_NORETURN; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static jmp_buf g_session_exit_jmp; +static int g_session_exitcode; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void dropbear_session_exit(int exitcode, FAR const char *format, + va_list param) noreturn_function; +static void dropbear_session_exit(int exitcode, FAR const char *format, + va_list param) +{ + char exitmsg[150]; + char fullmsg[300]; + char fromaddr[60]; + int signal_pipe[2]; + + vsnprintf(exitmsg, sizeof(exitmsg), format, param); + + fromaddr[0] = '\0'; + + if (svr_ses.addrstring != NULL) + { + snprintf(fromaddr, sizeof(fromaddr), " from <%s>", svr_ses.addrstring); + } + + if (!ses.init_done) + { + snprintf(fullmsg, sizeof(fullmsg), "Early exit%s: %s", fromaddr, + exitmsg); + } + else if (ses.authstate.authdone) + { + snprintf(fullmsg, sizeof(fullmsg), "Exit (%s)%s: %s", + ses.authstate.pw_name, fromaddr, exitmsg); + } + else if (ses.authstate.pw_name != NULL) + { + snprintf(fullmsg, sizeof(fullmsg), + "Exit before auth%s: (user '%s', %u fails): %s", + fromaddr, ses.authstate.pw_name, ses.authstate.failcount, + exitmsg); + } + else + { + snprintf(fullmsg, sizeof(fullmsg), "Exit before auth%s: %s", fromaddr, + exitmsg); + } + + dropbear_log(LOG_INFO, "%s", fullmsg); + + signal_pipe[0] = ses.signal_pipe[0]; + signal_pipe[1] = ses.signal_pipe[1]; + session_cleanup(); + + if (signal_pipe[0] > STDERR_FILENO) + { + m_close(signal_pipe[0]); + } + + if (signal_pipe[1] > STDERR_FILENO && signal_pipe[1] != signal_pipe[0]) + { + m_close(signal_pipe[1]); + } + + memset(&ses, 0, sizeof(ses)); + memset(&svr_ses, 0, sizeof(svr_ses)); + + g_session_exitcode = exitcode; + longjmp(g_session_exit_jmp, 1); + + while (1) + { + } +} + +static void dropbear_setup(FAR const char *port) +{ + FAR char *argv[] = + { + "dropbear", + "-F", + "-p", + (FAR char *)port, + NULL + }; + + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; + + disallow_core(); + svr_getopts(4, argv); + seedrandom(); + crypto_init(); + + if (dropbear_hostkey_initialize() < 0) + { + dropbear_exit("failed to initialize host key"); + } + + if (dropbear_auth_initialize() < 0) + { + dropbear_exit("failed to initialize password auth"); + } +} + +static void dropbear_run_session(int childsock) +{ + dropbear_exit_handler_t saved_exit; + + saved_exit = _dropbear_exit; + _dropbear_exit = dropbear_session_exit; + + if (setjmp(g_session_exit_jmp) == 0) + { + svr_session(childsock, -1); + } + + _dropbear_exit = saved_exit; + + if (g_session_exitcode != EXIT_SUCCESS) + { + dropbear_log(LOG_WARNING, "session exited with status %d", + g_session_exitcode); + } +} + +static size_t dropbear_listen_sockets(FAR int *socks, size_t sockcount, + FAR int *maxfd) +{ + FAR char *errstring = NULL; + size_t sockpos = 0; + unsigned int i; + + for (i = 0; i < svr_opts.portcount; i++) + { + int nsock; + unsigned int n; + + nsock = dropbear_listen(svr_opts.addresses[i], svr_opts.ports[i], + &socks[sockpos], sockcount - sockpos, + &errstring, maxfd, svr_opts.interface); + + if (nsock < 0) + { + dropbear_log(LOG_WARNING, "failed listening on '%s': %s", + svr_opts.ports[i], + errstring != NULL ? errstring : "unknown error"); + m_free(errstring); + errstring = NULL; + continue; + } + + for (n = 0; n < (unsigned int)nsock; n++) + { + set_sock_priority(socks[sockpos + n], DROPBEAR_PRIO_LOWDELAY); + } + + sockpos += (size_t)nsock; + } + + return sockpos; +} + +static size_t dropbear_wait_listen_sockets(FAR int *socks, size_t sockcount, + FAR int *maxfd) +{ + int retries = 0; + int retry_delay = 1; + + while (1) + { + size_t count; + + *maxfd = -1; + count = dropbear_listen_sockets(socks, sockcount, maxfd); + + if (count > 0) + { + return count; + } + +#if CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRIES > 0 + if (retries++ >= CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRIES) + { + dropbear_exit("no listening ports available"); + } +#else + retries++; +#endif + + dropbear_log(LOG_WARNING, "Retry %d in %d seconds...", retries, + retry_delay); + sleep(retry_delay); + + retry_delay *= 2; + + if (retry_delay > CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX) + { + retry_delay = CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR const char *port = DROPBEAR_PORT_STRING(CONFIG_NETUTILS_DROPBEAR_PORT); + int listensocks[MAX_LISTEN_ADDR]; + int maxfd = -1; + + if (argc > 1) + { + port = argv[1]; + } + + dropbear_setup(port); + + dropbear_wait_listen_sockets(listensocks, MAX_LISTEN_ADDR, &maxfd); + + printf("dropbear: listening on port %s\n", port); + + while (1) + { + struct sockaddr_storage remoteaddr; + socklen_t remoteaddrlen = sizeof(remoteaddr); + FAR char *remote_host = NULL; + FAR char *remote_port = NULL; + int childsock; + + childsock = accept(listensocks[0], (FAR struct sockaddr *)&remoteaddr, + &remoteaddrlen); + + if (childsock < 0) + { + continue; + } + + getaddrstring(&remoteaddr, &remote_host, &remote_port, 0); + dropbear_log(LOG_INFO, "connection from %s:%s", + remote_host != NULL ? remote_host : "?", + remote_port != NULL ? remote_port : "?"); + m_free(remote_host); + m_free(remote_port); + + seedrandom(); + dropbear_run_session(childsock); + close(childsock); + } + + return EXIT_SUCCESS; +} diff --git a/netutils/dropbear/dropbear_nshsession.c b/netutils/dropbear/dropbear_nshsession.c new file mode 100644 index 00000000000..6eee89b971d --- /dev/null +++ b/netutils/dropbear/dropbear_nshsession.c @@ -0,0 +1,615 @@ +/**************************************************************************** + * apps/netutils/dropbear/dropbear_nshsession.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "includes.h" +#include "channel.h" +#include "chansession.h" +#include "dbutil.h" +#include "session.h" + +#include "nsh.h" +#include "nsh_console.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct dropbear_nshsession_s +{ + int pty_readfd; + int pty_writefd; + pid_t nsh_pid; + volatile bool done; + pthread_t waiter; + bool waiter_started; + bool have_winsize; + struct winsize win; +}; + +struct dropbear_signal_name_s +{ + FAR const char *name; + int signo; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct dropbear_signal_name_s g_dropbear_signals[] = +{ + { "ABRT", SIGABRT }, + { "ALRM", SIGALRM }, + { "FPE", SIGFPE }, + { "HUP", SIGHUP }, + { "ILL", SIGILL }, + { "INT", SIGINT }, + { "KILL", SIGKILL }, + { "PIPE", SIGPIPE }, + { "QUIT", SIGQUIT }, + { "SEGV", SIGSEGV }, + { "TERM", SIGTERM }, + { "USR1", SIGUSR1 }, + { "USR2", SIGUSR2 }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_nsh_main(int argc, FAR char *argv[]) +{ + FAR struct console_stdio_s *pstate; + FAR char *nsh_argv[] = + { + "nsh", + NULL + }; + + int ret; + + (void)argc; + (void)argv; + + pstate = nsh_newconsole(true); + if (pstate == NULL) + { + dropbear_log(LOG_WARNING, "failed to create NSH console"); + return -ENOMEM; + } + + ret = nsh_session(pstate, NSH_LOGIN_NONE, 1, nsh_argv); + dropbear_log(LOG_INFO, "NSH session exited: %d", ret); + + nsh_exit(&pstate->cn_vtbl, ret); + return ret; +} + +static FAR void *dropbear_nsh_waiter(FAR void *arg) +{ + FAR struct dropbear_nshsession_s *sess = arg; + unsigned char ch = 0; + int status; + int ret; + + ret = waitpid(sess->nsh_pid, &status, 0); + if (ret < 0) + { + dropbear_log(LOG_WARNING, "NSH session wait failed: %s", + strerror(errno)); + } + + sess->nsh_pid = -1; + sess->done = true; + + /* Match Dropbear's SIGCHLD wakeup path. The session loop checks channel + * close conditions when this pipe becomes readable. + */ + + if (ses.signal_pipe[1] >= 0) + { + write(ses.signal_pipe[1], &ch, sizeof(ch)); + } + + return NULL; +} + +static int dropbear_newchansess(FAR struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess; + + sess = m_malloc(sizeof(*sess)); + memset(sess, 0, sizeof(*sess)); + sess->pty_readfd = -1; + sess->pty_writefd = -1; + sess->nsh_pid = -1; + + channel->typedata = sess; + channel->prio = DROPBEAR_PRIO_LOWDELAY; + return 0; +} + +static int dropbear_sesscheckclose(FAR struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess = channel->typedata; + + return sess != NULL && sess->done; +} + +static int dropbear_setup_spawn_attrs(FAR posix_spawnattr_t *attr, + FAR const char **errmsg) +{ + struct sched_param param; + int rc; + + param.sched_priority = CONFIG_NETUTILS_DROPBEAR_SHELL_PRIORITY; + *errmsg = "spawn priority setup"; + rc = posix_spawnattr_setschedparam(attr, ¶m); + if (rc != 0) + { + return rc; + } + + *errmsg = "spawn stack setup"; + rc = posix_spawnattr_setstacksize( + attr, CONFIG_NETUTILS_DROPBEAR_SHELL_STACKSIZE); + if (rc != 0) + { + return rc; + } + + *errmsg = "spawn flags setup"; + return posix_spawnattr_setflags(attr, POSIX_SPAWN_SETSCHEDPARAM); +} + +static int +dropbear_setup_spawn_stdio(FAR posix_spawn_file_actions_t *actions, + int slavefd) +{ + static const int stdio_fds[] = + { + STDIN_FILENO, + STDOUT_FILENO, + STDERR_FILENO + }; + + int i; + int rc; + + for (i = 0; i < nitems(stdio_fds); i++) + { + rc = posix_spawn_file_actions_adddup2(actions, slavefd, stdio_fds[i]); + if (rc != 0) + { + return rc; + } + } + + return 0; +} + +static int +dropbear_setup_spawn_close(FAR posix_spawn_file_actions_t *actions, + int fd) +{ + if (fd <= STDERR_FILENO) + { + return 0; + } + + return posix_spawn_file_actions_addclose(actions, fd); +} + +static int dropbear_start_nsh(FAR struct dropbear_channel *channel, + FAR struct dropbear_nshsession_s *sess) +{ + posix_spawn_file_actions_t actions; + posix_spawnattr_t attr; + FAR const char *errmsg; + FAR char * const argv[] = + { + "nsh", + NULL + }; + + int masterfd; + int slavefd; + int writefd; + int rc; + + if (sess->nsh_pid >= 0) + { + dropbear_log(LOG_WARNING, "NSH session already running"); + return DROPBEAR_FAILURE; + } + + rc = openpty(&masterfd, &slavefd, NULL, NULL, + sess->have_winsize ? &sess->win : NULL); + if (rc < 0) + { + dropbear_log(LOG_WARNING, "openpty failed: %s", strerror(errno)); + return DROPBEAR_FAILURE; + } + + writefd = dup(masterfd); + if (writefd < 0) + { + dropbear_log(LOG_WARNING, "pty dup failed: %s", strerror(errno)); + close(masterfd); + close(slavefd); + return DROPBEAR_FAILURE; + } + + rc = posix_spawn_file_actions_init(&actions); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn actions init failed: %s", + strerror(rc)); + close(masterfd); + close(writefd); + close(slavefd); + return DROPBEAR_FAILURE; + } + + rc = posix_spawnattr_init(&attr); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn attr init failed: %s", strerror(rc)); + goto err_with_actions; + } + + rc = dropbear_setup_spawn_attrs(&attr, &errmsg); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "%s failed: %s", errmsg, strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_stdio(&actions, slavefd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stdio setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, masterfd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn master close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, writefd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn write close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, slavefd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn slave close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + sess->nsh_pid = task_spawn("dropbear nsh", dropbear_nsh_main, &actions, + &attr, argv, NULL); + if (sess->nsh_pid < 0) + { + dropbear_log(LOG_WARNING, "failed to create NSH task: %s", + strerror(-sess->nsh_pid)); + goto err_with_attr; + } + + close(slavefd); + slavefd = -1; + + rc = pthread_create(&sess->waiter, NULL, dropbear_nsh_waiter, sess); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "failed to create NSH waiter: %s", + strerror(rc)); + close(masterfd); + close(writefd); + masterfd = -1; + writefd = -1; + kill(sess->nsh_pid, SIGTERM); + waitpid(sess->nsh_pid, NULL, 0); + sess->nsh_pid = -1; + goto err_with_attr; + } + + sess->waiter_started = true; + sess->pty_readfd = masterfd; + sess->pty_writefd = writefd; + + channel->readfd = masterfd; + channel->writefd = writefd; + channel->bidir_fd = 0; + + setnonblocking(channel->readfd); + setnonblocking(channel->writefd); + ses.maxfd = MAX(ses.maxfd, channel->readfd); + ses.maxfd = MAX(ses.maxfd, channel->writefd); + + posix_spawnattr_destroy(&attr); + posix_spawn_file_actions_destroy(&actions); + + dropbear_log(LOG_INFO, "NSH PTY session started"); + return DROPBEAR_SUCCESS; + +err_with_attr: + posix_spawnattr_destroy(&attr); + +err_with_actions: + posix_spawn_file_actions_destroy(&actions); + + if (masterfd >= 0) + { + close(masterfd); + } + + if (writefd >= 0) + { + close(writefd); + } + + if (slavefd >= 0) + { + close(slavefd); + } + + sess->nsh_pid = -1; + return DROPBEAR_FAILURE; +} + +static void dropbear_parse_winsize(FAR struct dropbear_nshsession_s *sess) +{ + unsigned int cols = buf_getint(ses.payload); + unsigned int rows = buf_getint(ses.payload); + unsigned int width = buf_getint(ses.payload); + unsigned int height = buf_getint(ses.payload); + + sess->win.ws_col = cols; + sess->win.ws_row = rows; + sess->win.ws_xpixel = width; + sess->win.ws_ypixel = height; + sess->have_winsize = true; + + if (sess->pty_readfd >= 0) + { + ioctl(sess->pty_readfd, TIOCSWINSZ, (unsigned long)&sess->win); + } +} + +static int dropbear_handle_pty_req(FAR struct dropbear_nshsession_s *sess) +{ + unsigned int len; + FAR char *term; + FAR char *modes; + + term = buf_getstring(ses.payload, &len); + dropbear_parse_winsize(sess); + modes = buf_getstring(ses.payload, &len); + + m_free(term); + m_free(modes); + return DROPBEAR_SUCCESS; +} + +static int dropbear_signal_from_name(FAR const char *name) +{ + int i; + + for (i = 0; i < nitems(g_dropbear_signals); i++) + { + if (strcmp(name, g_dropbear_signals[i].name) == 0) + { + return g_dropbear_signals[i].signo; + } + } + + return -EINVAL; +} + +static int dropbear_write_terminal_signal( + FAR struct dropbear_nshsession_s *sess, + int signo) +{ +#ifdef CONFIG_TTY_SIGINT + unsigned char ch; + ssize_t nwritten; + + if (signo == SIGINT && sess->pty_writefd >= 0) + { + ch = CONFIG_TTY_SIGINT_CHAR; + nwritten = write(sess->pty_writefd, &ch, sizeof(ch)); + if (nwritten == sizeof(ch)) + { + return DROPBEAR_SUCCESS; + } + + dropbear_log(LOG_WARNING, "SSH terminal INT failed: %s", + strerror(errno)); + return DROPBEAR_FAILURE; + } +#endif + + return DROPBEAR_FAILURE; +} + +static int dropbear_handle_signal(FAR struct dropbear_nshsession_s *sess) +{ + unsigned int len; + FAR char *name; + int signo; + int ret; + + name = buf_getstring(ses.payload, &len); + signo = dropbear_signal_from_name(name); + if (signo < 0) + { + dropbear_log(LOG_WARNING, "unsupported SSH signal '%s'", name); + m_free(name); + return DROPBEAR_FAILURE; + } + + ret = dropbear_write_terminal_signal(sess, signo); + if (ret == DROPBEAR_SUCCESS) + { + dropbear_log(LOG_INFO, "SSH signal '%s' sent to PTY", name); + m_free(name); + return DROPBEAR_SUCCESS; + } + + if (sess->nsh_pid <= 0) + { + dropbear_log(LOG_WARNING, "SSH signal '%s' has no NSH session", name); + m_free(name); + return DROPBEAR_FAILURE; + } + + ret = kill(sess->nsh_pid, signo); + if (ret < 0) + { + dropbear_log(LOG_WARNING, "SSH signal '%s' failed: %s", + name, strerror(errno)); + m_free(name); + return DROPBEAR_FAILURE; + } + + dropbear_log(LOG_INFO, "SSH signal '%s' sent to NSH session", name); + m_free(name); + return DROPBEAR_SUCCESS; +} + +static void dropbear_chansessionrequest(FAR struct dropbear_channel *channel) +{ + unsigned int typelen; + FAR char *type = buf_getstring(ses.payload, &typelen); + unsigned char wantreply = buf_getbool(ses.payload); + FAR struct dropbear_nshsession_s *sess = channel->typedata; + int ret = DROPBEAR_FAILURE; + + TRACE(("dropbear_chansessionrequest: type='%s'", type)) + + if (strcmp(type, "pty-req") == 0) + { + ret = dropbear_handle_pty_req(sess); + } + else if (strcmp(type, "shell") == 0) + { + ret = dropbear_start_nsh(channel, sess); + } + else if (strcmp(type, "exec") == 0) + { + dropbear_log(LOG_WARNING, "SSH exec requests are not supported"); + } + else if (strcmp(type, "window-change") == 0) + { + dropbear_parse_winsize(sess); + ret = DROPBEAR_SUCCESS; + } + else if (strcmp(type, "signal") == 0) + { + ret = dropbear_handle_signal(sess); + } + else if (strcmp(type, "break") == 0) + { + ret = DROPBEAR_SUCCESS; + } + else + { + TRACE(("dropbear_chansessionrequest: unhandled type '%s'", type)) + } + + if (wantreply) + { + if (ret == DROPBEAR_SUCCESS) + { + send_msg_channel_success(channel); + } + else + { + send_msg_channel_failure(channel); + } + } + + m_free(type); +} + +static void +dropbear_closechansess(FAR const struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess = channel->typedata; + + if (sess != NULL) + { + sess->done = true; + } +} + +static void +dropbear_cleanupchansess(FAR const struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess = channel->typedata; + + if (sess == NULL) + { + return; + } + + sess->done = true; + + if (sess->nsh_pid > 0) + { + kill(sess->nsh_pid, SIGTERM); + } + + if (sess->waiter_started) + { + pthread_join(sess->waiter, NULL); + sess->waiter_started = false; + } + + m_free(sess); +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct dropbear_chantype svrchansess = +{ + "session", + dropbear_newchansess, + dropbear_sesscheckclose, + dropbear_chansessionrequest, + dropbear_closechansess, + dropbear_cleanupchansess, +}; diff --git a/netutils/dropbear/patch/0001-use-nuttx-unused-macro.patch b/netutils/dropbear/patch/0001-use-nuttx-unused-macro.patch new file mode 100644 index 00000000000..ad41c67ec0b --- /dev/null +++ b/netutils/dropbear/patch/0001-use-nuttx-unused-macro.patch @@ -0,0 +1,291 @@ +--- a/src/chachapoly.c ++++ b/src/chachapoly.c +@@ -43,11 +43,15 @@ + const struct dropbear_cipher dropbear_chachapoly = + {&dummy, CHACHA20_KEY_LEN*2, CHACHA20_BLOCKSIZE}; + +-static int dropbear_chachapoly_start(int UNUSED(cipher), const unsigned char* UNUSED(IV), ++static int dropbear_chachapoly_start(int cipher, const unsigned char *IV, + const unsigned char *key, int keylen, +- int UNUSED(num_rounds), dropbear_chachapoly_state *state) { ++ int num_rounds, dropbear_chachapoly_state *state) { + int err; + ++ UNUSED(cipher); ++ UNUSED(IV); ++ UNUSED(num_rounds); ++ + TRACE2(("enter dropbear_chachapoly_start")) + + if (keylen != CHACHA20_KEY_LEN*2) { +--- a/src/common-algo.c ++++ b/src/common-algo.c +@@ -40,16 +40,25 @@ + * decide which ciphers/hashes/compression/signing to use during key exchange*/ + + static int void_cipher(const unsigned char* in, unsigned char* out, +- unsigned long len, void* UNUSED(cipher_state)) { ++ unsigned long len, void *cipher_state) { ++ UNUSED(cipher_state); ++ + if (in != out) { + memmove(out, in, len); + } + return CRYPT_OK; + } + +-static int void_start(int UNUSED(cipher), const unsigned char* UNUSED(IV), +- const unsigned char* UNUSED(key), +- int UNUSED(keylen), int UNUSED(num_rounds), void* UNUSED(cipher_state)) { ++static int void_start(int cipher, const unsigned char *IV, ++ const unsigned char *key, ++ int keylen, int num_rounds, void *cipher_state) { ++ UNUSED(cipher); ++ UNUSED(IV); ++ UNUSED(key); ++ UNUSED(keylen); ++ UNUSED(num_rounds); ++ UNUSED(cipher_state); ++ + return CRYPT_OK; + } + +--- a/src/common-channel.c ++++ b/src/common-channel.c +@@ -410,7 +410,9 @@ + + #ifndef HAVE_WRITEV + static int writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf, +- const unsigned char *UNUSED(moredata), unsigned int *morelen) { ++ const unsigned char *moredata, unsigned int *morelen) { ++ UNUSED(moredata); ++ + + unsigned char *circ_p1, *circ_p2; + unsigned int circ_len1, circ_len2; +--- a/src/common-kex.c ++++ b/src/common-kex.c +@@ -420,11 +420,15 @@ + && ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY); + } + +-static void* dropbear_zalloc(void* UNUSED(opaque), uInt items, uInt size) { ++static void *dropbear_zalloc(void *opaque, uInt items, uInt size) { ++ UNUSED(opaque); ++ + return m_calloc(items, size); + } + +-static void dropbear_zfree(void* UNUSED(opaque), void* ptr) { ++static void dropbear_zfree(void *opaque, void *ptr) { ++ UNUSED(opaque); ++ + m_free(ptr); + } + +--- a/src/dbutil.c ++++ b/src/dbutil.c +@@ -133,8 +133,10 @@ + dropbear_exit("Failed assertion (%s:%d): `%s'", file, line, expr); + } + +-static void generic_dropbear_log(int UNUSED(priority), const char* format, ++static void generic_dropbear_log(int priority, const char* format, + va_list param) { ++ UNUSED(priority); ++ + + char printbuf[1024]; + +--- a/src/gcm.c ++++ b/src/gcm.c +@@ -37,9 +37,11 @@ + + static int dropbear_gcm_start(int cipher, const unsigned char *IV, + const unsigned char *key, int keylen, +- int UNUSED(num_rounds), dropbear_gcm_state *state) { ++ int num_rounds, dropbear_gcm_state *state) { + int err; + ++ UNUSED(num_rounds); ++ + TRACE2(("enter dropbear_gcm_start")) + + if ((err = gcm_init(&state->gcm, cipher, key, keylen)) != CRYPT_OK) { +@@ -51,13 +53,15 @@ + return CRYPT_OK; + } + +-static int dropbear_gcm_crypt(unsigned int UNUSED(seq), ++static int dropbear_gcm_crypt(unsigned int seq, + const unsigned char *in, unsigned char *out, + unsigned long len, unsigned long taglen, + dropbear_gcm_state *state, int direction) { + unsigned char *iv, tag[GHASH_LEN]; + int i, err; + ++ UNUSED(seq); ++ + TRACE2(("enter dropbear_gcm_crypt")) + + if (len < 4 || taglen != GHASH_LEN) { +@@ -97,9 +101,12 @@ + return CRYPT_OK; + } + +-static int dropbear_gcm_getlength(unsigned int UNUSED(seq), ++static int dropbear_gcm_getlength(unsigned int seq, + const unsigned char *in, unsigned int *outlen, +- unsigned long len, dropbear_gcm_state* UNUSED(state)) { ++ unsigned long len, dropbear_gcm_state *state) { ++ UNUSED(seq); ++ UNUSED(state); ++ + TRACE2(("enter dropbear_gcm_getlength")) + + if (len < 4) { +--- a/src/ltc_prng.c ++++ b/src/ltc_prng.c +@@ -32,8 +32,10 @@ + @param prng [out] The PRNG state to initialize + @return CRYPT_OK if successful + */ +-int dropbear_prng_start(prng_state* UNUSED(prng)) ++int dropbear_prng_start(prng_state *prng) + { ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -44,8 +46,12 @@ + @param prng PRNG state to update + @return CRYPT_OK if successful + */ +-int dropbear_prng_add_entropy(const unsigned char* UNUSED(in), unsigned long UNUSED(inlen), prng_state* UNUSED(prng)) ++int dropbear_prng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng) + { ++ UNUSED(in); ++ UNUSED(inlen); ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -54,8 +60,10 @@ + @param prng The PRNG to make active + @return CRYPT_OK if successful + */ +-int dropbear_prng_ready(prng_state* UNUSED(prng)) ++int dropbear_prng_ready(prng_state *prng) + { ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -66,8 +74,10 @@ + @param prng The active PRNG to read from + @return Number of octets read + */ +-unsigned long dropbear_prng_read(unsigned char* out, unsigned long outlen, prng_state* UNUSED(prng)) ++unsigned long dropbear_prng_read(unsigned char* out, unsigned long outlen, prng_state *prng) + { ++ UNUSED(prng); ++ + LTC_ARGCHK(out != NULL); + genrandom(out, outlen); + return outlen; +@@ -78,8 +88,10 @@ + @param prng The PRNG to terminate + @return CRYPT_OK if successful + */ +-int dropbear_prng_done(prng_state* UNUSED(prng)) ++int dropbear_prng_done(prng_state *prng) + { ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -90,8 +102,11 @@ + @param prng The PRNG to export + @return CRYPT_OK if successful + */ +-int dropbear_prng_export(unsigned char* UNUSED(out), unsigned long* outlen, prng_state* UNUSED(prng)) ++int dropbear_prng_export(unsigned char *out, unsigned long* outlen, prng_state *prng) + { ++ UNUSED(out); ++ UNUSED(prng); ++ + LTC_ARGCHK(outlen != NULL); + + *outlen = 0; +@@ -105,8 +120,12 @@ + @param prng The PRNG to import + @return CRYPT_OK if successful + */ +-int dropbear_prng_import(const unsigned char* UNUSED(in), unsigned long UNUSED(inlen), prng_state* UNUSED(prng)) ++int dropbear_prng_import(const unsigned char *in, unsigned long inlen, prng_state *prng) + { ++ UNUSED(in); ++ UNUSED(inlen); ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +--- a/src/netio.c ++++ b/src/netio.c +@@ -47,7 +47,10 @@ + } + } + +-static void cancel_callback(int result, int sock, void* UNUSED(data), const char* UNUSED(errstring)) { ++static void cancel_callback(int result, int sock, void *data, const char *errstring) { ++ UNUSED(data); ++ UNUSED(errstring); ++ + if (result == DROPBEAR_SUCCESS) + { + m_close(sock); +--- a/src/svr-agentfwd.c ++++ b/src/svr-agentfwd.c +@@ -100,10 +100,12 @@ + /* accepts a connection on the forwarded socket and opens a new channel for it + * back to the client */ + /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +-static void agentaccept(const struct Listener *UNUSED(listener), int sock) { ++static void agentaccept(const struct Listener *listener, int sock) { + + int fd; + ++ UNUSED(listener); ++ + fd = accept(sock, NULL, NULL); + if (fd < 0) { + TRACE(("accept failed")) +--- a/src/svr-kex.c ++++ b/src/svr-kex.c +@@ -114,7 +114,7 @@ + + #if DROPBEAR_DELAY_HOSTKEY + +-static void svr_ensure_hostkey() { ++static void svr_ensure_hostkey(void) { + + const char* fn = NULL; + char *expand_fn = NULL; +--- a/src/signkey.h ++++ b/src/signkey.h +@@ -150,9 +150,9 @@ + void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf); + #if DROPBEAR_SIGNKEY_VERIFY + int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf); + int sk_buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf, char* app, unsigned int applen); +-char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); + #endif ++char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); + int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, + const unsigned char* algoname, unsigned int algolen, + const buffer * line, char ** fingerprint); diff --git a/netutils/dropbear/patch/0002-use-nuttx-ecdsa-hostkey-sign.patch b/netutils/dropbear/patch/0002-use-nuttx-ecdsa-hostkey-sign.patch new file mode 100644 index 00000000000..eb7938be272 --- /dev/null +++ b/netutils/dropbear/patch/0002-use-nuttx-ecdsa-hostkey-sign.patch @@ -0,0 +1,103 @@ +--- a/src/ecdsa.c ++++ b/src/ecdsa.c +@@ -4,6 +4,9 @@ + #include "ecc.h" + #include "ecdsa.h" + #include "signkey.h" ++#ifdef DROPBEAR_NUTTX ++#include "nuttx_hostkey.h" ++#endif + + #if DROPBEAR_ECDSA + +@@ -67,10 +70,14 @@ + , bit_size); + } + ++#ifdef DROPBEAR_NUTTX ++ dropbear_exit("ECDSA key generation uses NuttX host key storage"); ++#else + new_key = m_malloc(sizeof(*new_key)); + if (ecc_make_key_ex(NULL, dropbear_ltc_prng, new_key, dp) != CRYPT_OK) { + dropbear_exit("ECC error"); + } ++#endif + return new_key; + } + +@@ -164,8 +171,13 @@ + /* Based on libtomcrypt's ecc_sign_hash but without the asn1 */ + int err = DROPBEAR_FAILURE; + struct dropbear_ecc_curve *curve = NULL; ++#ifndef DROPBEAR_NUTTX + hash_state hs; + unsigned char hash[64]; ++#endif ++#ifdef DROPBEAR_NUTTX ++ unsigned char nuttx_r[32], nuttx_s[32]; ++#endif + void *e = NULL, *p = NULL, *s = NULL, *r; + char key_ident[30]; + buffer *sigbuf = NULL; +@@ -177,6 +189,31 @@ + goto out; + } + ++#ifdef DROPBEAR_NUTTX ++#if DROPBEAR_ECC_256 ++ if (curve == &ecc_curve_nistp256) { ++ if (dropbear_hostkey_ecdsa_p256_sign(data_buf->data, data_buf->len, ++ nuttx_r, nuttx_s) != DROPBEAR_SUCCESS) { ++ explicit_bzero(nuttx_r, sizeof(nuttx_r)); ++ explicit_bzero(nuttx_s, sizeof(nuttx_s)); ++ goto out; ++ } ++ ++ if (mp_from_ubin(r, nuttx_r, sizeof(nuttx_r)) != MP_OKAY || ++ mp_from_ubin(s, nuttx_s, sizeof(nuttx_s)) != MP_OKAY) { ++ explicit_bzero(nuttx_r, sizeof(nuttx_r)); ++ explicit_bzero(nuttx_s, sizeof(nuttx_s)); ++ goto out; ++ } ++ ++ explicit_bzero(nuttx_r, sizeof(nuttx_r)); ++ explicit_bzero(nuttx_s, sizeof(nuttx_s)); ++ goto put_sig; ++ } ++#endif ++#endif ++ ++#ifndef DROPBEAR_NUTTX + curve->hash_desc->init(&hs); + curve->hash_desc->process(&hs, data_buf->data, data_buf->len); + curve->hash_desc->done(&hs, hash); +@@ -227,7 +264,11 @@ + break; + } + } ++#else ++ dropbear_exit("Unsupported NuttX ECDSA curve"); ++#endif + ++put_sig: + snprintf(key_ident, sizeof(key_ident), "ecdsa-sha2-%s", curve->name); + buf_putstring(buf, key_ident, strlen(key_ident)); + /* enough for nistp521 */ +--- a/src/sysoptions.h ++++ b/src/sysoptions.h +@@ -161,9 +161,15 @@ + #define LTM_DESC + #endif + ++#ifndef DROPBEAR_ECC_256 + #define DROPBEAR_ECC_256 (DROPBEAR_ECC) ++#endif ++#ifndef DROPBEAR_ECC_384 + #define DROPBEAR_ECC_384 (DROPBEAR_ECC) ++#endif ++#ifndef DROPBEAR_ECC_521 + #define DROPBEAR_ECC_521 (DROPBEAR_ECC) ++#endif + + /* Only include necessary ECC curves building libtomcrypt */ + #define LTC_NO_CURVES diff --git a/netutils/dropbear/patch/0003-guard-environ-declaration-for-nuttx.patch b/netutils/dropbear/patch/0003-guard-environ-declaration-for-nuttx.patch new file mode 100644 index 00000000000..425a435b794 --- /dev/null +++ b/netutils/dropbear/patch/0003-guard-environ-declaration-for-nuttx.patch @@ -0,0 +1,14 @@ +--- a/src/includes.h ++++ b/src/includes.h +@@ -184,7 +184,11 @@ typedef u_int32_t uint32_t; + #include + #endif + ++#ifdef DROPBEAR_NUTTX ++/* NuttX provides environ as a macro in stdlib.h. */ ++#else + extern char** environ; ++#endif + + #include "fake-rfc2553.h" + diff --git a/netutils/dropbear/patch/0004-fix-nuttx-compile-warnings.patch b/netutils/dropbear/patch/0004-fix-nuttx-compile-warnings.patch new file mode 100644 index 00000000000..aa04e46d876 --- /dev/null +++ b/netutils/dropbear/patch/0004-fix-nuttx-compile-warnings.patch @@ -0,0 +1,107 @@ +--- a/src/common-kex.c ++++ b/src/common-kex.c +@@ -117,7 +117,7 @@ void send_msg_kexinit() { + + } + +-static void switch_keys() { ++static void switch_keys(void) { + TRACE2(("enter switch_keys")) + if (!(ses.kexstate.sentkexinit && ses.kexstate.recvkexinit)) { + dropbear_exit("Unexpected newkeys message"); +--- a/src/common-session.c ++++ b/src/common-session.c +@@ -503,7 +503,7 @@ void ignore_recv_response() { + TRACE(("Ignored msg_request_response")) + } + +-static void send_msg_keepalive() { ++static void send_msg_keepalive(void) { + time_t old_time_idle = ses.last_packet_time_idle; + struct Channel *chan = get_any_ready_channel(); + +--- a/src/compat.c ++++ b/src/compat.c +@@ -81,6 +81,10 @@ + + #include "includes.h" + ++#ifdef DROPBEAR_NUTTX ++int setsid(void); ++#endif ++ + #ifndef HAVE_GETUSERSHELL + static char **curshell, **shells, *strings; + static char **initshells(); +--- a/src/dbrandom.c ++++ b/src/dbrandom.c +@@ -129,7 +129,7 @@ static void addrandom(const unsigned char * buf, unsigned int len) { + sha256_done(&hs, hashpool); + } + +-static void write_urandom() ++static void write_urandom(void) + { + #if DROPBEAR_FUZZ + if (fuzz.fuzzing) { +--- a/src/dbutil.c ++++ b/src/dbutil.c +@@ -66,6 +66,10 @@ + #include "session.h" + #include "atomicio.h" + ++#ifdef DROPBEAR_NUTTX ++int execv(const char *path, char * const argv[]); ++#endif ++ + #define MAX_FMT 100 + + static void generic_dropbear_exit(int exitcode, const char* format, +--- a/src/packet.c ++++ b/src/packet.c +@@ -475,7 +475,7 @@ int packet_is_okay_for_queues(unsigned char type) { + return 1; + } + +-static void enqueue_reply_packet() { ++static void enqueue_reply_packet(void) { + struct packetlist * new_item = NULL; + new_item = m_malloc(sizeof(struct packetlist)); + new_item->next = NULL; +--- a/src/svr-runopts.c ++++ b/src/svr-runopts.c +@@ -38,7 +38,7 @@ svr_runopts svr_opts; /* GLOBAL */ + static void printhelp(const char * progname); + static void addportandaddress(const char* spec); + static void loadhostkey(const char *keyfile, int fatal_duplicate); + static void addhostkey(const char *keyfile); +-static void load_banner(); ++static void load_banner(void); + + static void printhelp(const char * progname) { + +@@ -729,7 +729,7 @@ void load_all_hostkeys() { + } + } + +-static void load_banner() { ++static void load_banner(void) { + struct stat buf; + if (stat(svr_opts.bannerfile, &buf) != 0) { + dropbear_log(LOG_WARNING, "Error opening banner file '%s'", +--- a/src/svr-tcpfwd.c ++++ b/src/svr-tcpfwd.c +@@ -53,9 +53,13 @@ void recv_msg_global_request_remotetcp() { + /* */ + #endif /* !DROPBEAR_SVR_REMOTETCPFWD */ + ++#if DROPBEAR_SVR_REMOTETCPFWD + static int svr_cancelremotetcp(void); + static int svr_remotetcpreq(int *allocated_listen_port); ++#endif ++#if DROPBEAR_SVR_LOCALTCPFWD + static int newtcpdirect(struct Channel * channel); ++#endif + #if DROPBEAR_SVR_LOCALSTREAMFWD + static int newstreamlocal(struct Channel * channel); + #endif diff --git a/netutils/dropbear/patch/0005-use-nuttx-sha256-hmac.patch b/netutils/dropbear/patch/0005-use-nuttx-sha256-hmac.patch new file mode 100644 index 00000000000..6aa1e174437 --- /dev/null +++ b/netutils/dropbear/patch/0005-use-nuttx-sha256-hmac.patch @@ -0,0 +1,45 @@ +--- a/libtomcrypt/src/headers/tomcrypt_hash.h ++++ b/libtomcrypt/src/headers/tomcrypt_hash.h +@@ -30,11 +30,18 @@ struct sha512_state { + #endif + + #ifdef LTC_SHA256 ++#ifdef DROPBEAR_NUTTX_SHA256 ++#include ++struct sha256_state { ++ SHA2_CTX ctx; ++}; ++#else + struct sha256_state { + ulong64 length; + ulong32 state[8], curlen; + unsigned char buf[64]; + }; ++#endif + #endif + + #ifdef LTC_SHA1 +--- a/libtomcrypt/src/headers/tomcrypt_mac.h ++++ b/libtomcrypt/src/headers/tomcrypt_mac.h +@@ -7,6 +7,13 @@ + */ + + #ifdef LTC_HMAC ++#ifdef DROPBEAR_NUTTX_HMAC_SHA256 ++#include ++typedef struct Hmac_state { ++ HMAC_SHA256_CTX ctx; ++ int hash; ++} hmac_state; ++#else + typedef struct Hmac_state { + hash_state md; + int hash; +@@ -14,6 +21,7 @@ typedef struct Hmac_state { + unsigned char *key; + } hmac_state; + ++#endif + int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen); + int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen); + int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen); diff --git a/netutils/dropbear/patch/0006-use-nuttx-chachapoly-state.patch b/netutils/dropbear/patch/0006-use-nuttx-chachapoly-state.patch new file mode 100644 index 00000000000..f8e4c89d759 --- /dev/null +++ b/netutils/dropbear/patch/0006-use-nuttx-chachapoly-state.patch @@ -0,0 +1,84 @@ +--- a/libtomcrypt/src/headers/tomcrypt_custom.h ++++ b/libtomcrypt/src/headers/tomcrypt_custom.h +@@ -212,7 +212,9 @@ + #define LTC_CAMELLIA + + /* stream ciphers */ ++#ifndef DROPBEAR_NUTTX_CHACHAPOLY + #define LTC_CHACHA ++#endif + #define LTC_RC4_STREAM + #define LTC_SOBER128_STREAM + +@@ -283,7 +285,9 @@ + #define LTC_XCBC + #define LTC_F9_MODE + #define LTC_PELICAN ++#ifndef DROPBEAR_NUTTX_CHACHAPOLY + #define LTC_POLY1305 ++#endif + #define LTC_BLAKE2SMAC + #define LTC_BLAKE2BMAC + +@@ -295,7 +299,9 @@ + #define LTC_OCB3_MODE + #define LTC_CCM_MODE + #define LTC_GCM_MODE ++#ifndef DROPBEAR_NUTTX_CHACHAPOLY + #define LTC_CHACHA20POLY1305_MODE ++#endif + + /* Use 64KiB tables */ + #ifndef LTC_NO_TABLES +@@ -323,7 +329,9 @@ + #define LTC_RC4 + + /* The ChaCha20 stream cipher based PRNG */ ++#ifndef DROPBEAR_NUTTX_CHACHAPOLY + #define LTC_CHACHA20_PRNG ++#endif + + /* Fortuna PRNG */ + #define LTC_FORTUNA +--- a/libtomcrypt/src/headers/tomcrypt_mac.h ++++ b/libtomcrypt/src/headers/tomcrypt_mac.h +@@ -113,6 +113,9 @@ void pmac_shift_xor(pmac_state *pmac); + #endif /* PMAC */ + + #ifdef LTC_POLY1305 ++#ifdef DROPBEAR_NUTTX_CHACHAPOLY ++#include ++#else + typedef struct { + ulong32 r[5]; + ulong32 h[5]; +@@ -130,6 +133,7 @@ int poly1305_memory(const unsigned char *key, unsigned long keylen, const unsign + int poly1305_memory_multi(const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen, const unsigned char *in, unsigned long inlen, ...); + int poly1305_file(const char *fname, const unsigned char *key, unsigned long keylen, unsigned char *mac, unsigned long *maclen); + int poly1305_test(void); ++#endif + #endif /* LTC_POLY1305 */ + + #ifdef LTC_BLAKE2SMAC +--- a/src/chachapoly.h ++++ b/src/chachapoly.h +@@ -31,10 +31,19 @@ + + #if DROPBEAR_CHACHA20POLY1305 + ++#ifdef DROPBEAR_NUTTX_CHACHAPOLY ++#include ++ ++typedef struct { ++ struct chacha20_stream_ctx chacha; ++ struct chacha20_stream_ctx header; ++} dropbear_chachapoly_state; ++#else + typedef struct { + chacha_state chacha; + chacha_state header; + } dropbear_chachapoly_state; ++#endif + + extern const struct dropbear_cipher dropbear_chachapoly; + extern const struct dropbear_cipher_mode dropbear_mode_chachapoly; diff --git a/netutils/dropbear/patch/0007-use-nuttx-passwd-auth.patch b/netutils/dropbear/patch/0007-use-nuttx-passwd-auth.patch new file mode 100644 index 00000000000..5e71d73daba --- /dev/null +++ b/netutils/dropbear/patch/0007-use-nuttx-passwd-auth.patch @@ -0,0 +1,87 @@ +--- a/src/svr-authpasswd.c ++++ b/src/svr-authpasswd.c +@@ -33,6 +33,77 @@ + + #if DROPBEAR_SVR_PASSWORD_AUTH + ++#if DROPBEAR_NUTTX_PASSWD ++ ++/* Process a password auth request, sending success or failure messages as ++ * appropriate */ ++void svr_auth_password(int valid_user) { ++ ++ char * password = NULL; ++ unsigned int passwordlen; ++ unsigned int changepw; ++ int auth_ok = 0; ++ ++ /* check if client wants to change password */ ++ changepw = buf_getbool(ses.payload); ++ if (changepw) { ++ /* not implemented by this server */ ++ send_msg_userauth_failure(0, 1); ++ return; ++ } ++ ++ password = buf_getstring(ses.payload, &passwordlen); ++ if (valid_user && passwordlen <= DROPBEAR_MAX_PASSWORD_LEN && ++ strlen(password) == passwordlen) { ++ auth_ok = dropbear_verify_password(ses.authstate.pw_name, password); ++ } ++ m_burn(password, passwordlen); ++ m_free(password); ++ ++ /* After we have got the payload contents we can exit if the username ++ is invalid. Invalid users have already been logged. */ ++ if (!valid_user) { ++ send_msg_userauth_failure(0, 1); ++ return; ++ } ++ ++ if (passwordlen > DROPBEAR_MAX_PASSWORD_LEN) { ++ dropbear_log(LOG_WARNING, ++ "Too-long password attempt for '%s' from %s", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ send_msg_userauth_failure(0, 1); ++ return; ++ } ++ ++ if (auth_ok == DROPBEAR_SUCCESS) { ++ if (svr_opts.multiauthmethod && (ses.authstate.authtypes & ~AUTH_TYPE_PASSWORD)) { ++ /* successful password authentication, but extra auth required */ ++ dropbear_log(LOG_NOTICE, ++ "Password auth succeeded for '%s' from %s, extra auth required", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ ses.authstate.authtypes &= ~AUTH_TYPE_PASSWORD; /* password auth ok, delete the method flag */ ++ send_msg_userauth_failure(1, 0); /* Send partial success */ ++ } else { ++ /* successful authentication */ ++ dropbear_log(LOG_NOTICE, ++ "Password auth succeeded for '%s' from %s", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ send_msg_userauth_success(); ++ } ++ } else { ++ dropbear_log(LOG_WARNING, ++ "Bad password attempt for '%s' from %s", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ send_msg_userauth_failure(0, 1); ++ } ++} ++ ++#else ++ + /* not constant time when strings are differing lengths. + string content isn't leaked, and crypt hashes are predictable length. */ + static int constant_time_strcmp(const char* a, const char* b) { +@@ -131,4 +202,6 @@ + } + } + ++#endif /* DROPBEAR_NUTTX_PASSWD */ ++ + #endif diff --git a/netutils/dropbear/port/config.h b/netutils/dropbear/port/config.h new file mode 100644 index 00000000000..0f5355fd7d9 --- /dev/null +++ b/netutils/dropbear/port/config.h @@ -0,0 +1,20 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/config.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_CONFIG_H +#define __APPS_NETUTILS_DROPBEAR_PORT_CONFIG_H + +/* Dropbear looks for a file named config.h. Keep this wrapper name stable; + * NuttX-specific autoconf replacements live in nuttx_config.h. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "nuttx_config.h" + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_CONFIG_H */ diff --git a/netutils/dropbear/port/default_options_guard.h b/netutils/dropbear/port/default_options_guard.h new file mode 100644 index 00000000000..7f3d6383511 --- /dev/null +++ b/netutils/dropbear/port/default_options_guard.h @@ -0,0 +1,693 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/default_options_guard.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Generated from upstream Dropbear src/default_options.h. + * Do not edit this file; local customisation goes in localoptions.h. + ****************************************************************************/ + +#ifndef DROPBEAR_DEFAULT_OPTIONS_H_ +#define DROPBEAR_DEFAULT_OPTIONS_H_ + +/* default_options.h documents compile-time options and provides default + * values. + * + * Local customisation should be added to localoptions.h which is used if + * it exists in the build directory. Options defined there will override + * any options in this file. + * + * Customisations will also be taken from src/distrooptions.h if it exists. + * + * Options can also be defined with -DDROPBEAR_XXX=[0,1] in Makefile CFLAGS + * + * IMPORTANT: Some options will require "make clean" after changes. + */ + +#ifndef DROPBEAR_DEFPORT +#define DROPBEAR_DEFPORT "22" +#endif + +/* Listen on all interfaces */ + +#ifndef DROPBEAR_DEFADDRESS +#define DROPBEAR_DEFADDRESS "" +#endif + +/* Default hostkey paths - these can be specified on the command line. + * Homedir is prepended if path begins with ~/ + */ + +#ifndef DSS_PRIV_FILENAME +#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key" +#endif +#ifndef RSA_PRIV_FILENAME +#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" +#endif +#ifndef ECDSA_PRIV_FILENAME +#define ECDSA_PRIV_FILENAME "/etc/dropbear/dropbear_ecdsa_host_key" +#endif +#ifndef ED25519_PRIV_FILENAME +#define ED25519_PRIV_FILENAME "/etc/dropbear/dropbear_ed25519_host_key" +#endif + +/* Set NON_INETD_MODE if you require daemon functionality (ie Dropbear + * listens on chosen ports and keeps accepting connections). This is the + * default. + * + * Set INETD_MODE if you want to be able to run Dropbear with inetd (or + * similar), where it will use stdin/stdout for connections, and each + * process lasts for a single connection. Dropbear should be invoked with + * the -i flag for inetd, and can only accept IPv4 connections. + * + * Both of these flags can be defined at once, don't compile without at + * least one of them. + */ + +#ifndef NON_INETD_MODE +#define NON_INETD_MODE 1 +#endif +#ifndef INETD_MODE +#define INETD_MODE 1 +#endif + +/* By default Dropbear will re-execute itself for each incoming connection + * so that memory layout may be re-randomised (ASLR) - exploiting + * vulnerabilities becomes harder. Re-exec causes slightly more memory use + * per connection. This option is ignored on non-Linux platforms. + */ + +#ifndef DROPBEAR_REEXEC +#define DROPBEAR_REEXEC 1 +#endif + +/* Include verbose debug output, enabled with -v at runtime (repeat to + * increase). Define which level of debug output you compile in. + * Level 0 = disabled + * Level 1-3 = approx 4 Kb (connection, remote identity, algos, auth info) + * Level 4 = approx 17 Kb (detailed before connection) + * Level 5 = approx 8 Kb (detailed after connection) + */ + +#ifndef DEBUG_TRACE +#define DEBUG_TRACE 0 +#endif + +/* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can + * save several kB in binary size however will make the symmetrical ciphers + * and hashes slower, perhaps by 50%. Recommended for small systems that + * aren't doing much traffic. + */ + +#ifndef DROPBEAR_SMALL_CODE +#define DROPBEAR_SMALL_CODE 1 +#endif + +/* Enable X11 Forwarding - server only */ + +#ifndef DROPBEAR_X11FWD +#define DROPBEAR_X11FWD 0 +#endif + +/* Enable TCP Fowarding. + * 'Local' is "-L" style (client listening port forwarded via server). + * 'Remote' is "-R" style (server listening port forwarded via client). + */ + +#ifndef DROPBEAR_CLI_LOCALTCPFWD +#define DROPBEAR_CLI_LOCALTCPFWD 1 +#endif +#ifndef DROPBEAR_CLI_REMOTETCPFWD +#define DROPBEAR_CLI_REMOTETCPFWD 1 +#endif + +#ifndef DROPBEAR_SVR_LOCALTCPFWD +#define DROPBEAR_SVR_LOCALTCPFWD 1 +#endif +#ifndef DROPBEAR_SVR_REMOTETCPFWD +#define DROPBEAR_SVR_REMOTETCPFWD 1 +#endif +#ifndef DROPBEAR_SVR_LOCALSTREAMFWD +#define DROPBEAR_SVR_LOCALSTREAMFWD 1 +#endif + +/* Enable Authentication Agent Forwarding */ + +#ifndef DROPBEAR_SVR_AGENTFWD +#define DROPBEAR_SVR_AGENTFWD 1 +#endif +#ifndef DROPBEAR_CLI_AGENTFWD +#define DROPBEAR_CLI_AGENTFWD 1 +#endif + +/* Note: Both DROPBEAR_CLI_PROXYCMD and DROPBEAR_CLI_NETCAT must be set to + * allow multihop dbclient connections. + */ + +/* Allow using -J to run the connection through a pipe to a + * program, rather the normal TCP connection. + */ + +#ifndef DROPBEAR_CLI_PROXYCMD +#define DROPBEAR_CLI_PROXYCMD 1 +#endif + +/* Enable "Netcat mode" option. This will forward standard input/output + * to a remote TCP-forwarded connection. + */ + +#ifndef DROPBEAR_CLI_NETCAT +#define DROPBEAR_CLI_NETCAT 1 +#endif + +/* Whether to support "-c" and "-m" flags to choose ciphers/MACs at + * runtime. + */ + +#ifndef DROPBEAR_USER_ALGO_LIST +#define DROPBEAR_USER_ALGO_LIST 1 +#endif + +/* Encryption - at least one required. + * AES should be enabled, some very old implementations might only + * support 3DES. + * Including both AES keysize variants (128 and 256) will result in + * a minimal size increase. + */ + +#ifndef DROPBEAR_AES128 +#define DROPBEAR_AES128 1 +#endif +#ifndef DROPBEAR_AES256 +#define DROPBEAR_AES256 1 +#endif +#ifndef DROPBEAR_3DES +#define DROPBEAR_3DES 0 +#endif + +/* Enable Chacha20-Poly1305 authenticated encryption mode. This is + * generally faster than AES256 on CPU w/o dedicated AES instructions, + * having the same key size. Recommended. + * Compiling in will add ~5,5kB to binary size on x86-64. + */ + +#ifndef DROPBEAR_CHACHA20POLY1305 +#define DROPBEAR_CHACHA20POLY1305 1 +#endif + +/* Enable "Counter Mode" for ciphers. Recommended. */ + +#ifndef DROPBEAR_ENABLE_CTR_MODE +#define DROPBEAR_ENABLE_CTR_MODE 1 +#endif + +/* Enable CBC mode for ciphers. This has security issues though may be + * required for compatibility with old implementations. + */ + +#ifndef DROPBEAR_ENABLE_CBC_MODE +#define DROPBEAR_ENABLE_CBC_MODE 0 +#endif + +/* Enable "Galois/Counter Mode" for ciphers. This authenticated encryption + * mode is a combination of CTR mode and GHASH. Recommended for security + * and forwards compatibility, but slower than CTR on CPU w/o dedicated + * AES/GHASH instructions. + * Compiling in will add ~6kB to binary size on x86-64. + */ + +#ifndef DROPBEAR_ENABLE_GCM_MODE +#define DROPBEAR_ENABLE_GCM_MODE 0 +#endif + +/* Message integrity. sha2-256 is recommended as a default, sha1 for + * compatibility. + */ + +#ifndef DROPBEAR_SHA1_HMAC +#define DROPBEAR_SHA1_HMAC 0 +#endif +#ifndef DROPBEAR_SHA2_256_HMAC +#define DROPBEAR_SHA2_256_HMAC 1 +#endif +#ifndef DROPBEAR_SHA2_512_HMAC +#define DROPBEAR_SHA2_512_HMAC 0 +#endif +#ifndef DROPBEAR_SHA1_96_HMAC +#define DROPBEAR_SHA1_96_HMAC 0 +#endif + +/* Hostkey/public key algorithms - at least one required, these are used + * for hostkey as well as for verifying signatures with pubkey auth. + * RSA is recommended. + * + * See: RSA_PRIV_FILENAME and DSS_PRIV_FILENAME + */ + +#ifndef DROPBEAR_RSA +#define DROPBEAR_RSA 1 +#endif + +/* Newer SSH implementations use SHA256 for RSA signatures. SHA1 support + * is required to communicate with some older implementations. + * It is disabled by default. + */ + +#ifndef DROPBEAR_RSA_SHA1 +#define DROPBEAR_RSA_SHA1 0 +#endif + +/* DSS may be necessary to connect to some systems but is not recommended + * for new keys (1024 bits is small, and it uses SHA1). + * RSA key generation will be faster with bundled libtommath if + * DROPBEAR_DSS is disabled. + */ + +#ifndef DROPBEAR_DSS +#define DROPBEAR_DSS 0 +#endif + +/* ECDSA is significantly faster than RSA or DSS. Compiling in ECC code + * (either ECDSA or ECDH) increases binary size - around 30kB on x86-64. + * See: ECDSA_PRIV_FILENAME + */ + +#ifndef DROPBEAR_ECDSA +#define DROPBEAR_ECDSA 1 +#endif + +/* Ed25519 is faster than ECDSA. Compiling in Ed25519 code increases + * binary size - around 7,5kB on x86-64. + * See: ED25519_PRIV_FILENAME + */ + +#ifndef DROPBEAR_ED25519 +#define DROPBEAR_ED25519 1 +#endif + +/* Allow U2F security keys for public key auth, with + * sk-ecdsa-sha2-nistp256@openssh.com or sk-ssh-ed25519@openssh.com keys. + * The corresponding DROPBEAR_ECDSA or DROPBEAR_ED25519 also needs to be + * set. This is currently server-only. + */ + +#ifndef DROPBEAR_SK_KEYS +#define DROPBEAR_SK_KEYS 1 +#endif + +/* RSA must be >=1024. + * DSS is always 1024. + * ECDSA defaults to largest size configured, usually 521. + * Ed25519 is always 256. + */ + +#ifndef DROPBEAR_DEFAULT_RSA_SIZE +#define DROPBEAR_DEFAULT_RSA_SIZE 2048 +#endif + +/* Add runtime flag "-R" to generate hostkeys as-needed when the first + * connection using that key type occurs. This avoids the need to otherwise + * run "dropbearkey" and avoids some problems with badly seeded /dev/urandom + * when systems first boot. + */ + +#ifndef DROPBEAR_DELAY_HOSTKEY +#define DROPBEAR_DELAY_HOSTKEY 1 +#endif + +/* Key exchange algorithm. + * + * group14_sha1 - 2048 bit, sha1 + * group14_sha256 - 2048 bit, sha2-256 + * group16 - 4096 bit, sha2-512 + * group1 - 1024 bit, sha1 + * curve25519 - elliptic curve DH + * ecdh - NIST elliptic curve DH (256, 384, 521) + * sntrup761 - post-quantum hybrid with x25519 + * mlkem768 - post-quantum hybrid with x25519 + * + * group1 is too small for security though is necessary if you need + * compatibility with some implementations such as Dropbear < 0.53. + * group14 is supported by most implementations. + * group16 provides a greater strength level but is slower and increases + * binary size. + * curve25519 and ecdh algorithms are faster than non-elliptic curve methods. + * curve25519 increases binary size by ~2,5kB on x86-64. + * Including either ECDH or ECDSA increases binary size by ~30kB on x86-64. + * + * sntrup761 and mlkem768 are recommended to avoid possible decryption + * by future quantum computers. On systems with sufficient space both are + * recommended. sntrup761 is the most widely supported at time of writing, + * recommended when space is limited. Both are fast. + * sntrup uses ~9kB code size, mlkem uses ~34kB code size (32-bit armv7). + */ + +#ifndef DROPBEAR_DH_GROUP14_SHA1 +#define DROPBEAR_DH_GROUP14_SHA1 0 +#endif +#ifndef DROPBEAR_DH_GROUP14_SHA256 +#define DROPBEAR_DH_GROUP14_SHA256 1 +#endif +#ifndef DROPBEAR_DH_GROUP16 +#define DROPBEAR_DH_GROUP16 0 +#endif +#ifndef DROPBEAR_CURVE25519 +#define DROPBEAR_CURVE25519 1 +#endif +#ifndef DROPBEAR_SNTRUP761 +#define DROPBEAR_SNTRUP761 1 +#endif +#ifndef DROPBEAR_MLKEM768 +#define DROPBEAR_MLKEM768 1 +#endif +#ifndef DROPBEAR_ECDH +#define DROPBEAR_ECDH 1 +#endif +#ifndef DROPBEAR_DH_GROUP1 +#define DROPBEAR_DH_GROUP1 0 +#endif + +/* When group1 is enabled it will only be allowed by Dropbear client, not + * as a server, due to concerns over its strength. Set to 0 to allow + * group1 in Dropbear server too. + */ + +#ifndef DROPBEAR_DH_GROUP1_CLIENTONLY +#define DROPBEAR_DH_GROUP1_CLIENTONLY 1 +#endif + +/* Control the memory/performance/compression tradeoff for zlib. + * Set windowBits=8 for least memory usage, see your system's zlib.h for + * full details. + * Default settings (windowBits=15) will use 256kB for compression. + * windowBits=8 will use 129kB for compression. + * Both modes will use ~35kB for decompression (using windowBits=15 for + * interoperability). + */ + +#ifndef DROPBEAR_ZLIB_WINDOW_BITS +#define DROPBEAR_ZLIB_WINDOW_BITS 15 +#endif + +/* Whether to do reverse DNS lookups. */ + +#ifndef DO_HOST_LOOKUP +#define DO_HOST_LOOKUP 0 +#endif + +/* Whether to print the message of the day (MOTD). */ + +#ifndef DO_MOTD +#define DO_MOTD 1 +#endif +#ifndef MOTD_FILENAME +#define MOTD_FILENAME "/etc/motd" +#endif +#ifndef MOTD_MAXSIZE +#define MOTD_MAXSIZE 2000 +#endif + +/* Authentication Types - at least one required. + * RFC Draft requires pubkey auth, and recommends password. + */ + +#ifndef DROPBEAR_SVR_PASSWORD_AUTH +#define DROPBEAR_SVR_PASSWORD_AUTH 1 +#endif + +/* Note: PAM auth is quite simple and only works for PAM modules which + * just do a simple "Login: " "Password: " (you can edit the strings in + * svr-authpam.c). It's useful for systems like OS X where standard + * password crypts don't work but there's an interface via a PAM module. + * It won't work for more complex PAM challenge/response. + * You can't enable both PASSWORD and PAM. + */ + +#ifndef DROPBEAR_SVR_PAM_AUTH +#define DROPBEAR_SVR_PAM_AUTH 0 +#endif + +/* ~/.ssh/authorized_keys authentication. + * You must define DROPBEAR_SVR_PUBKEY_AUTH in order to use plugins. + */ + +#ifndef DROPBEAR_SVR_PUBKEY_AUTH +#define DROPBEAR_SVR_PUBKEY_AUTH 1 +#endif + +/* Whether to take public key options in authorized_keys file into + * account. + */ + +#ifndef DROPBEAR_SVR_PUBKEY_OPTIONS +#define DROPBEAR_SVR_PUBKEY_OPTIONS 1 +#endif + +/* Set this to 0 if your system does not have multiple user support. + * (Linux kernel CONFIG_MULTIUSER option) + * The resulting binary will not run on a normal system. + */ + +#ifndef DROPBEAR_SVR_MULTIUSER +#define DROPBEAR_SVR_MULTIUSER 1 +#endif + +/* Client authentication options */ + +#ifndef DROPBEAR_CLI_PASSWORD_AUTH +#define DROPBEAR_CLI_PASSWORD_AUTH 1 +#endif +#ifndef DROPBEAR_CLI_PUBKEY_AUTH +#define DROPBEAR_CLI_PUBKEY_AUTH 1 +#endif + +/* A default argument for dbclient -i . + * Homedir is prepended if path begins with ~/ + */ + +#ifndef DROPBEAR_DEFAULT_CLI_AUTHKEY +#define DROPBEAR_DEFAULT_CLI_AUTHKEY "~/.ssh/id_dropbear" +#endif + +/* Per client configuration file. */ + +#ifndef DROPBEAR_USE_SSH_CONFIG +#define DROPBEAR_USE_SSH_CONFIG 0 +#endif + +/* Allow specifying the password for dbclient via the DROPBEAR_PASSWORD + * environment variable. + */ + +#ifndef DROPBEAR_USE_PASSWORD_ENV +#define DROPBEAR_USE_PASSWORD_ENV 1 +#endif + +/* Define this (as well as DROPBEAR_CLI_PASSWORD_AUTH) to allow the use of + * a helper program for the ssh client. The helper program should be + * specified in the SSH_ASKPASS environment variable, and dbclient should + * be run with DISPLAY set and no tty. The program should return the + * password on standard output. + */ + +#ifndef DROPBEAR_CLI_ASKPASS_HELPER +#define DROPBEAR_CLI_ASKPASS_HELPER 0 +#endif + +/* Save a network roundtrip by sending a real auth request immediately + * after sending a query for the available methods. This is not yet + * enabled by default since it could cause problems with non-compliant + * servers. + */ + +#ifndef DROPBEAR_CLI_IMMEDIATE_AUTH +#define DROPBEAR_CLI_IMMEDIATE_AUTH 0 +#endif + +/* Set this to use PRNGD or EGD instead of /dev/urandom */ + +#ifndef DROPBEAR_USE_PRNGD +#define DROPBEAR_USE_PRNGD 0 +#endif +#ifndef DROPBEAR_PRNGD_SOCKET +#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng" +#endif + +/* Specify the number of clients we will allow to be connected but not yet + * authenticated. After this limit, connections are rejected. + * The first setting is per-IP, to avoid denial of service. + */ + +#ifndef MAX_UNAUTH_PER_IP +#define MAX_UNAUTH_PER_IP 5 +#endif + +/* And then a global limit to avoid chewing memory if connections come + * from many IPs. + */ + +#ifndef MAX_UNAUTH_CLIENTS +#define MAX_UNAUTH_CLIENTS 30 +#endif + +/* Default maximum number of failed authentication tries (server option). + * -T server option overrides. + */ + +#ifndef MAX_AUTH_TRIES +#define MAX_AUTH_TRIES 10 +#endif + +/* Maximum number of public key queries per session. + * Public key queries aren't a risk for brute forcing authentication, but + * can be a user enumeration/privacy concern if an attacker attempts to + * iterate known public keys such as those published by github. + * + * This limit has a trade-off. Having a smaller limit reduces the number of + * legitimate public keys that can be presented by a client/ssh agent. + * Assuming a 100ms session establishment time, + * MAX_UNAUTH_CLIENTS * MAX_PUBKEY_QUERIES / 0.1 = 750 queries/sec. + * That is still a risk against a single host, but this limit may deter + * internet-wide scanning. + * + * If -T argument or MAX_AUTH_TRIES is larger that will be used instead. + */ + +#ifndef MAX_PUBKEY_QUERIES +#define MAX_PUBKEY_QUERIES 15 +#endif + +/* Change server process to user privileges after authentication. + * Default is enabled. Should only be disabled if platforms are + * incompatible. + */ + +#ifndef DROPBEAR_SVR_DROP_PRIVS +#ifndef DROPBEAR_SVR_DROP_PRIVS +#define DROPBEAR_SVR_DROP_PRIVS DROPBEAR_SVR_MULTIUSER +#endif +#endif + +/* Delay introduced before closing an unauthenticated session (seconds). + * Disabled by default, can be set to say 30 seconds to reduce the speed + * of password brute forcing. Note that there is a risk of denial of + * service by setting this. + */ + +#ifndef UNAUTH_CLOSE_DELAY +#define UNAUTH_CLOSE_DELAY 0 +#endif + +/* The default file to store the daemon's process ID, for shutdown + * scripts etc. This can be overridden with the -P flag. + * Homedir is prepended if path begins with ~/ + */ + +#ifndef DROPBEAR_PIDFILE +#define DROPBEAR_PIDFILE "/var/run/dropbear.pid" +#endif + +/* The command to invoke for xauth when using X11 forwarding. + * "-q" for quiet. + */ + +#ifndef XAUTH_COMMAND +#define XAUTH_COMMAND "/usr/bin/xauth -q" +#endif + +/* If you want to enable running an sftp server (such as the one included + * with OpenSSH), set the path below and set DROPBEAR_SFTPSERVER. + * The sftp-server program is not provided by Dropbear itself. + * Homedir is prepended if path begins with ~/ + */ + +#ifndef DROPBEAR_SFTPSERVER +#define DROPBEAR_SFTPSERVER 1 +#endif +#ifndef SFTPSERVER_PATH +#define SFTPSERVER_PATH "/usr/libexec/sftp-server" +#endif + +/* This is used by the scp binary when used as a client binary. If you're + * not using the Dropbear client, you'll need to change it. + */ + +#ifndef DROPBEAR_PATH_SSH_PROGRAM +#define DROPBEAR_PATH_SSH_PROGRAM "/usr/bin/dbclient" +#endif + +/* Whether to log commands executed by a client. This only logs the + * (single) command sent to the server, not what a user did in a + * shell/sftp session etc. + */ + +#ifndef LOG_COMMANDS +#define LOG_COMMANDS 0 +#endif + +/* Window size limits. These tend to be a trade-off between memory usage + * and network performance. + */ + +/* Size of the network receive window. This amount of memory is allocated + * as a per-channel receive buffer. Increasing this value can make a + * significant difference to network performance. 24kB was empirically + * chosen for a 100mbit ethernet network. The value can be altered at + * runtime with the -W argument. + */ + +#ifndef DEFAULT_RECV_WINDOW +#define DEFAULT_RECV_WINDOW 24576 +#endif + +/* Maximum size of a received SSH data packet - this _MUST_ be >= 32768 + * in order to interoperate with other implementations. + */ + +#ifndef RECV_MAX_PAYLOAD_LEN +#define RECV_MAX_PAYLOAD_LEN 32768 +#endif + +/* Maximum size of a transmitted data packet - this can be any value, + * though increasing it may not make a significant difference. + */ + +#ifndef TRANS_MAX_PAYLOAD_LEN +#define TRANS_MAX_PAYLOAD_LEN 16384 +#endif + +/* Ensure that data is transmitted every KEEPALIVE seconds. This can be + * overridden at runtime with -K. 0 disables keepalives. + */ + +#ifndef DEFAULT_KEEPALIVE +#define DEFAULT_KEEPALIVE 0 +#endif + +/* If this many KEEPALIVES are sent with no packets received from the + * other side, exit. Not run-time configurable - if you have a need for + * runtime configuration please mail the Dropbear list. + */ + +#ifndef DEFAULT_KEEPALIVE_LIMIT +#define DEFAULT_KEEPALIVE_LIMIT 3 +#endif + +/* Ensure that data is received within IDLE_TIMEOUT seconds. This can be + * overridden at runtime with -I. 0 disables idle timeouts. + */ + +#ifndef DEFAULT_IDLE_TIMEOUT +#define DEFAULT_IDLE_TIMEOUT 0 +#endif + +/* The default path. This will often get replaced by the shell. */ + +#ifndef DEFAULT_PATH +#define DEFAULT_PATH "/usr/bin:/bin" +#endif +#ifndef DEFAULT_ROOT_PATH +#define DEFAULT_ROOT_PATH "/usr/sbin:/usr/bin:/sbin:/bin" +#endif + +#endif /* DROPBEAR_DEFAULT_OPTIONS_H_ */ diff --git a/netutils/dropbear/port/dropbear_chachapoly.c b/netutils/dropbear/port/dropbear_chachapoly.c new file mode 100644 index 00000000000..c4f220d6e06 --- /dev/null +++ b/netutils/dropbear/port/dropbear_chachapoly.c @@ -0,0 +1,172 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_chachapoly.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "includes.h" + +#include + +#include "algo.h" +#include "dbutil.h" +#include "chachapoly.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CHACHA20_KEY_LEN 32 +#define CHACHA20_BLOCKSIZE 8 +#define POLY1305_KEY_LEN 32 +#define POLY1305_TAG_LEN 16 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct ltc_cipher_descriptor g_dropbear_chachapoly_dummy = +{ + .name = NULL +}; + +static const struct dropbear_hash g_dropbear_chachapoly_mac = +{ + NULL, + POLY1305_KEY_LEN, + POLY1305_TAG_LEN +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct dropbear_cipher dropbear_chachapoly = +{ + &g_dropbear_chachapoly_dummy, + CHACHA20_KEY_LEN * 2, + CHACHA20_BLOCKSIZE +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_chachapoly_start(int cipher, + const unsigned char *iv, + const unsigned char *key, + int keylen, + int num_rounds, + dropbear_chachapoly_state *state) +{ + UNUSED(cipher); + UNUSED(iv); + + if (keylen != CHACHA20_KEY_LEN * 2 || num_rounds != 0) + { + return CRYPT_ERROR; + } + + chacha20_stream_setkey(&state->chacha, key); + chacha20_stream_setkey(&state->header, key + CHACHA20_KEY_LEN); + return CRYPT_OK; +} + +static void dropbear_chachapoly_set_packet_iv( + struct chacha20_stream_ctx *ctx, + unsigned int seq, + uint64_t counter) +{ + unsigned char seqbuf[8]; + + STORE64H((uint64_t)seq, seqbuf); + chacha20_stream_ivctr64(ctx, seqbuf, counter); +} + +static int dropbear_chachapoly_crypt(unsigned int seq, + const unsigned char *in, + unsigned char *out, + unsigned long len, + unsigned long taglen, + dropbear_chachapoly_state *state, + int direction) +{ + poly1305_state poly; + unsigned char key[POLY1305_KEY_LEN]; + unsigned char tag[POLY1305_TAG_LEN]; + unsigned char zero[POLY1305_KEY_LEN]; + + if (len < 4 || taglen != POLY1305_TAG_LEN) + { + return CRYPT_ERROR; + } + + memset(zero, 0, sizeof(zero)); + dropbear_chachapoly_set_packet_iv(&state->chacha, seq, 0); + chacha20_stream_crypt(&state->chacha, zero, key, sizeof(key)); + + poly1305_begin(&poly, key); + if (direction == LTC_DECRYPT) + { + poly1305_update(&poly, in, len); + poly1305_finish(&poly, tag); + + if (constant_time_memcmp(in + len, tag, sizeof(tag)) != 0) + { + return CRYPT_ERROR; + } + } + + dropbear_chachapoly_set_packet_iv(&state->header, seq, 0); + chacha20_stream_crypt(&state->header, in, out, 4); + + dropbear_chachapoly_set_packet_iv(&state->chacha, seq, 1); + chacha20_stream_crypt(&state->chacha, in + 4, out + 4, len - 4); + + if (direction == LTC_ENCRYPT) + { + poly1305_update(&poly, out, len); + poly1305_finish(&poly, out + len); + } + + zeromem(key, sizeof(key)); + zeromem(tag, sizeof(tag)); + return CRYPT_OK; +} + +static int dropbear_chachapoly_getlength(unsigned int seq, + const unsigned char *in, + unsigned int *outlen, + unsigned long len, + dropbear_chachapoly_state *state) +{ + unsigned char buf[4]; + + if (len < sizeof(buf)) + { + return CRYPT_ERROR; + } + + dropbear_chachapoly_set_packet_iv(&state->header, seq, 0); + chacha20_stream_crypt(&state->header, in, buf, sizeof(buf)); + LOAD32H(*outlen, buf); + return CRYPT_OK; +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct dropbear_cipher_mode dropbear_mode_chachapoly = +{ + (void *)dropbear_chachapoly_start, + NULL, + NULL, + (void *)dropbear_chachapoly_crypt, + (void *)dropbear_chachapoly_getlength, + &g_dropbear_chachapoly_mac +}; diff --git a/netutils/dropbear/port/dropbear_crypto.c b/netutils/dropbear/port/dropbear_crypto.c new file mode 100644 index 00000000000..acd48ebb477 --- /dev/null +++ b/netutils/dropbear/port/dropbear_crypto.c @@ -0,0 +1,87 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_crypto.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/* This file is the adapter between Dropbear's port layer and the NuttX + * crypto APIs. It does not implement cryptographic primitives itself. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include + +#include +#include + +#include "dropbear_crypto.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int dropbear_nuttx_sha256(FAR const uint8_t *data, size_t datalen, + FAR uint8_t out[DROPBEAR_NUTTX_SHA256_LEN]) +{ + SHA2_CTX ctx; + + if (data == NULL || out == NULL) + { + return -EINVAL; + } + + sha256init(&ctx); + sha256update(&ctx, data, datalen); + sha256final(out, &ctx); + explicit_bzero(&ctx, sizeof(ctx)); + return OK; +} + +int dropbear_nuttx_ecdsa_p256_genkey( + FAR uint8_t d[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t x[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t y[DROPBEAR_NUTTX_ECDSA_P256_LEN]) +{ + if (d == NULL || x == NULL || y == NULL) + { + return -EINVAL; + } + + if (ecc_make_key_uncomp(x, y, d) == 0) + { + return -EIO; + } + + return OK; +} + +int dropbear_nuttx_ecdsa_p256_sign( + FAR const uint8_t d[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR const uint8_t hash[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t r[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t s[DROPBEAR_NUTTX_ECDSA_P256_LEN]) +{ + uint8_t signature[DROPBEAR_NUTTX_ECDSA_P256_LEN * 2]; + + if (d == NULL || hash == NULL || r == NULL || s == NULL) + { + return -EINVAL; + } + + if (ecdsa_sign(d, hash, signature) == 0) + { + return -EIO; + } + + memcpy(r, signature, DROPBEAR_NUTTX_ECDSA_P256_LEN); + memcpy(s, signature + DROPBEAR_NUTTX_ECDSA_P256_LEN, + DROPBEAR_NUTTX_ECDSA_P256_LEN); + explicit_bzero(signature, sizeof(signature)); + return OK; +} diff --git a/netutils/dropbear/port/dropbear_crypto.h b/netutils/dropbear/port/dropbear_crypto.h new file mode 100644 index 00000000000..dccf8dc08dc --- /dev/null +++ b/netutils/dropbear/port/dropbear_crypto.h @@ -0,0 +1,47 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_crypto.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_CRYPTO_H +#define __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_CRYPTO_H + +/* Adapter API for Dropbear code that needs NuttX crypto primitives. */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_NUTTX_SHA256_LEN SHA256_DIGEST_LENGTH +#define DROPBEAR_NUTTX_ECDSA_P256_LEN 32 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int dropbear_nuttx_sha256(FAR const uint8_t *data, size_t datalen, + FAR uint8_t out[DROPBEAR_NUTTX_SHA256_LEN]); + +int dropbear_nuttx_ecdsa_p256_genkey( + FAR uint8_t d[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t x[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t y[DROPBEAR_NUTTX_ECDSA_P256_LEN]); + +int dropbear_nuttx_ecdsa_p256_sign( + FAR const uint8_t d[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR const uint8_t hash[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t r[DROPBEAR_NUTTX_ECDSA_P256_LEN], + FAR uint8_t s[DROPBEAR_NUTTX_ECDSA_P256_LEN]); + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_CRYPTO_H */ diff --git a/netutils/dropbear/port/dropbear_curve25519.c b/netutils/dropbear/port/dropbear_curve25519.c new file mode 100644 index 00000000000..a6dfc9a9417 --- /dev/null +++ b/netutils/dropbear/port/dropbear_curve25519.c @@ -0,0 +1,24 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_curve25519.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "includes.h" + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void dropbear_curve25519_scalarmult(unsigned char *q, + const unsigned char *n, + const unsigned char *p) +{ + curve25519(q, n, p); +} diff --git a/netutils/dropbear/port/dropbear_ltc_aes.c b/netutils/dropbear/port/dropbear_ltc_aes.c new file mode 100644 index 00000000000..9adc238d40c --- /dev/null +++ b/netutils/dropbear/port/dropbear_ltc_aes.c @@ -0,0 +1,300 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_ltc_aes.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/* LibTomCrypt-compatible AES descriptor backed by NuttX crypto/rijndael. + * Dropbear uses the descriptor from its CTR mode implementation, so keeping + * this API lets aes128-ctr use NuttX AES without changing Dropbear + * internals. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "includes.h" + +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_ltc_aes_setup(const unsigned char *key, int keylen, + int num_rounds, symmetric_key *skey) +{ + rijndael_ctx *ctx; + + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(skey != NULL); + + if (num_rounds != 0) + { + return CRYPT_INVALID_ROUNDS; + } + + if (keylen != 16 && keylen != 24 && keylen != 32) + { + return CRYPT_INVALID_KEYSIZE; + } + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + { + return CRYPT_MEM; + } + + if (rijndael_set_key(ctx, key, keylen * 8) != 0) + { + free(ctx); + return CRYPT_INVALID_KEYSIZE; + } + + skey->data = ctx; + return CRYPT_OK; +} + +static int dropbear_ltc_aes_ecb_encrypt(const unsigned char *pt, + unsigned char *ct, + symmetric_key *skey) +{ + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(skey != NULL); + LTC_ARGCHK(skey->data != NULL); + + rijndael_encrypt(skey->data, pt, ct); + return CRYPT_OK; +} + +static int dropbear_ltc_aes_ecb_decrypt(const unsigned char *ct, + unsigned char *pt, + symmetric_key *skey) +{ + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(skey != NULL); + LTC_ARGCHK(skey->data != NULL); + + rijndael_decrypt(skey->data, ct, pt); + return CRYPT_OK; +} + +static void dropbear_ltc_aes_done(symmetric_key *skey) +{ + if (skey != NULL && skey->data != NULL) + { + zeromem(skey->data, sizeof(rijndael_ctx)); + free(skey->data); + skey->data = NULL; + } +} + +static int dropbear_ltc_aes_keysize(int *keysize) +{ + LTC_ARGCHK(keysize != NULL); + + if (*keysize < 16) + { + return CRYPT_INVALID_KEYSIZE; + } + else if (*keysize < 24) + { + *keysize = 16; + } + else if (*keysize < 32) + { + *keysize = 24; + } + else + { + *keysize = 32; + } + + return CRYPT_OK; +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct ltc_cipher_descriptor rijndael_desc = +{ + "rijndael", + 6, + 16, + 32, + 16, + 10, + rijndael_setup, + rijndael_ecb_encrypt, + rijndael_ecb_decrypt, + rijndael_test, + rijndael_done, + rijndael_keysize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +const struct ltc_cipher_descriptor aes_desc = +{ + "aes", + 6, + 16, + 32, + 16, + 10, + rijndael_setup, + rijndael_ecb_encrypt, + rijndael_ecb_decrypt, + rijndael_test, + rijndael_done, + rijndael_keysize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +const struct ltc_cipher_descriptor rijndael_enc_desc = +{ + "rijndael", + 6, + 16, + 32, + 16, + 10, + rijndael_enc_setup, + rijndael_enc_ecb_encrypt, + NULL, + NULL, + rijndael_enc_done, + rijndael_enc_keysize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +const struct ltc_cipher_descriptor aes_enc_desc = +{ + "aes", + 6, + 16, + 32, + 16, + 10, + rijndael_enc_setup, + rijndael_enc_ecb_encrypt, + NULL, + NULL, + rijndael_enc_done, + rijndael_enc_keysize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, + symmetric_key *skey) +{ + return dropbear_ltc_aes_setup(key, keylen, num_rounds, skey); +} + +int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, + symmetric_key *skey) +{ + return dropbear_ltc_aes_ecb_encrypt(pt, ct, skey); +} + +int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, + symmetric_key *skey) +{ + return dropbear_ltc_aes_ecb_decrypt(ct, pt, skey); +} + +int rijndael_test(void) +{ + return CRYPT_NOP; +} + +void rijndael_done(symmetric_key *skey) +{ + dropbear_ltc_aes_done(skey); +} + +int rijndael_keysize(int *keysize) +{ + return dropbear_ltc_aes_keysize(keysize); +} + +int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, + symmetric_key *skey) +{ + return dropbear_ltc_aes_setup(key, keylen, num_rounds, skey); +} + +int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, + symmetric_key *skey) +{ + return dropbear_ltc_aes_ecb_encrypt(pt, ct, skey); +} + +void rijndael_enc_done(symmetric_key *skey) +{ + dropbear_ltc_aes_done(skey); +} + +int rijndael_enc_keysize(int *keysize) +{ + return dropbear_ltc_aes_keysize(keysize); +} diff --git a/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c b/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c new file mode 100644 index 00000000000..bae7ab3916a --- /dev/null +++ b/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c @@ -0,0 +1,103 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/* LibTomCrypt-compatible HMAC API backed by NuttX crypto/hmac. The NuttX + * Dropbear configuration only enables hmac-sha2-256. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "includes.h" + +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_hmac_hash_is_sha256(int hash) +{ + int ret; + + ret = hash_is_valid(hash); + if (ret != CRYPT_OK) + { + return ret; + } + + if (hash_descriptor[hash].hashsize != SHA256_DIGEST_LENGTH || + hash_descriptor[hash].blocksize != SHA256_BLOCK_LENGTH) + { + return CRYPT_INVALID_HASH; + } + + return CRYPT_OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, + unsigned long keylen) +{ + int ret; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(key != NULL); + + if (keylen == 0) + { + return CRYPT_INVALID_KEYSIZE; + } + + ret = dropbear_hmac_hash_is_sha256(hash); + if (ret != CRYPT_OK) + { + return ret; + } + + hmac->hash = hash; + hmac_sha256_init(&hmac->ctx, key, (u_int)keylen); + return CRYPT_OK; +} + +int hmac_process(hmac_state *hmac, const unsigned char *in, + unsigned long inlen) +{ + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(in != NULL || inlen == 0); + + hmac_sha256_update(&hmac->ctx, in, (u_int)inlen); + return CRYPT_OK; +} + +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen) +{ + uint8_t digest[SHA256_DIGEST_LENGTH]; + unsigned long copylen; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if (dropbear_hmac_hash_is_sha256(hmac->hash) != CRYPT_OK) + { + return CRYPT_INVALID_HASH; + } + + hmac_sha256_final(digest, &hmac->ctx); + + copylen = MIN(*outlen, SHA256_DIGEST_LENGTH); + memcpy(out, digest, copylen); + *outlen = copylen; + + zeromem(digest, sizeof(digest)); + zeromem(hmac, sizeof(*hmac)); + return CRYPT_OK; +} diff --git a/netutils/dropbear/port/dropbear_ltc_sha256.c b/netutils/dropbear/port/dropbear_ltc_sha256.c new file mode 100644 index 00000000000..3e889fa453e --- /dev/null +++ b/netutils/dropbear/port/dropbear_ltc_sha256.c @@ -0,0 +1,75 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_ltc_sha256.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/* LibTomCrypt-compatible SHA256 descriptor backed by NuttX crypto/sha2. + * Dropbear still expects the libtomcrypt hash API for KEX and HMAC. This + * adapter keeps that API while avoiding the bundled SHA256 implementation. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "includes.h" + +#include + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct ltc_hash_descriptor sha256_desc = +{ + "sha256", + 0, + SHA256_DIGEST_LENGTH, + SHA256_BLOCK_LENGTH, + { 2, 16, 840, 1, 101, 3, 4, 2, 1 }, + 9, + + sha256_init, + sha256_process, + sha256_done, + sha256_test, + NULL +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int sha256_init(hash_state *md) +{ + LTC_ARGCHK(md != NULL); + + sha256init(&md->sha256.ctx); + return CRYPT_OK; +} + +int sha256_process(hash_state *md, const unsigned char *in, + unsigned long inlen) +{ + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(in != NULL || inlen == 0); + + sha256update(&md->sha256.ctx, in, inlen); + return CRYPT_OK; +} + +int sha256_done(hash_state *md, unsigned char *out) +{ + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + sha256final(out, &md->sha256.ctx); + zeromem(md, sizeof(*md)); + return CRYPT_OK; +} + +int sha256_test(void) +{ + return CRYPT_NOP; +} diff --git a/netutils/dropbear/port/dropbear_utils.c b/netutils/dropbear/port/dropbear_utils.c new file mode 100644 index 00000000000..91d2927e752 --- /dev/null +++ b/netutils/dropbear/port/dropbear_utils.c @@ -0,0 +1,139 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_utils.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "dropbear_utils.h" + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_hex_value(char ch) +{ + if (ch >= '0' && ch <= '9') + { + return ch - '0'; + } + + if (ch >= 'a' && ch <= 'f') + { + return ch - 'a' + 10; + } + + if (ch >= 'A' && ch <= 'F') + { + return ch - 'A' + 10; + } + + return -1; +} + +static int dropbear_try_mkdir(FAR const char *path) +{ + if (mkdir(path, 0700) < 0 && errno != EEXIST) + { + return -errno; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void dropbear_hex_encode(FAR char *dst, FAR const uint8_t *src, + size_t srclen) +{ + static const char hex[] = "0123456789abcdef"; + size_t i; + + for (i = 0; i < srclen; i++) + { + dst[i * 2] = hex[src[i] >> 4]; + dst[i * 2 + 1] = hex[src[i] & 0x0f]; + } + + dst[srclen * 2] = '\0'; +} + +int dropbear_hex_decode(FAR const char *src, size_t srclen, + FAR uint8_t *dst, size_t dstlen) +{ + size_t i; + + if (srclen != dstlen * 2) + { + return -EINVAL; + } + + for (i = 0; i < dstlen; i++) + { + int hi = dropbear_hex_value(src[i * 2]); + int lo = dropbear_hex_value(src[i * 2 + 1]); + + if (hi < 0 || lo < 0) + { + return -EINVAL; + } + + dst[i] = (uint8_t)((hi << 4) | lo); + } + + return OK; +} + +int dropbear_try_prepare_parent(FAR const char *path) +{ + char dir[PATH_MAX]; + struct stat st; + FAR char *slash; + FAR char *p; + int ret; + + if (strlcpy(dir, path, sizeof(dir)) >= sizeof(dir)) + { + return -ENAMETOOLONG; + } + + slash = strrchr(dir, '/'); + if (slash == NULL || slash == dir) + { + return OK; + } + + *slash = '\0'; + if (stat(dir, &st) == 0) + { + return OK; + } + + for (p = dir + 1; *p != '\0'; p++) + { + if (*p == '/') + { + *p = '\0'; + ret = dropbear_try_mkdir(dir); + if (ret < 0) + { + return ret; + } + + *p = '/'; + } + } + + return dropbear_try_mkdir(dir); +} diff --git a/netutils/dropbear/port/dropbear_utils.h b/netutils/dropbear/port/dropbear_utils.h new file mode 100644 index 00000000000..3e7d2722e7d --- /dev/null +++ b/netutils/dropbear/port/dropbear_utils.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_utils.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_UTILS_H +#define __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_UTILS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void dropbear_hex_encode(FAR char *dst, FAR const uint8_t *src, + size_t srclen); + +int dropbear_hex_decode(FAR const char *src, size_t srclen, + FAR uint8_t *dst, size_t dstlen); + +int dropbear_try_prepare_parent(FAR const char *path); + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_UTILS_H */ diff --git a/netutils/dropbear/port/localoptions.h b/netutils/dropbear/port/localoptions.h new file mode 100644 index 00000000000..f6e6c011129 --- /dev/null +++ b/netutils/dropbear/port/localoptions.h @@ -0,0 +1,21 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/localoptions.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_LOCALOPTIONS_H +#define __APPS_NETUTILS_DROPBEAR_PORT_LOCALOPTIONS_H + +/* Dropbear looks for a file named localoptions.h when + * LOCALOPTIONS_H_EXISTS is defined. Keep this wrapper name stable and place + * the NuttX-specific option choices in nuttx_localoptions.h. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "nuttx_localoptions.h" + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_LOCALOPTIONS_H */ diff --git a/netutils/dropbear/port/nuttx_auth.c b/netutils/dropbear/port/nuttx_auth.c new file mode 100644 index 00000000000..83becdcae91 --- /dev/null +++ b/netutils/dropbear/port/nuttx_auth.c @@ -0,0 +1,109 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_auth.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "fsutils/passwd.h" + +#include "dbutil.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct passwd g_dropbear_pw; +static char g_dropbear_name[64]; +static char g_dropbear_dir[] = "/"; +static char g_dropbear_shell[] = "/bin/sh"; +static char g_dropbear_password_marker[] = "x"; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static FAR struct passwd *dropbear_fill_pw(FAR const char *name, uid_t uid) +{ + memset(&g_dropbear_pw, 0, sizeof(g_dropbear_pw)); + + snprintf(g_dropbear_name, sizeof(g_dropbear_name), "%s", name); + + g_dropbear_pw.pw_uid = uid; + g_dropbear_pw.pw_gid = 0; + g_dropbear_pw.pw_name = g_dropbear_name; + g_dropbear_pw.pw_passwd = g_dropbear_password_marker; + g_dropbear_pw.pw_gecos = g_dropbear_name; + g_dropbear_pw.pw_dir = g_dropbear_dir; + g_dropbear_pw.pw_shell = g_dropbear_shell; + + return &g_dropbear_pw; +} + +FAR struct passwd *dropbear_getpwuid(uid_t uid) +{ + return dropbear_fill_pw("root", uid); +} + +FAR struct passwd *dropbear_getpwnam(FAR const char *name) +{ + if (name == NULL || name[0] == '\0') + { + return NULL; + } + + return dropbear_fill_pw(name, 0); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +uid_t dropbear_getuid(void) +{ + return 0; +} + +uid_t dropbear_geteuid(void) +{ + return 0; +} + +int dropbear_auth_initialize(void) +{ + dropbear_log(LOG_INFO, "using NuttX passwd auth at %s", + CONFIG_FSUTILS_PASSWD_PATH); + return OK; +} + +int dropbear_verify_password(FAR const char *username, + FAR const char *password) +{ + int ret; + + ret = passwd_verify(username, password); + if (PASSWORD_VERIFY_MATCH(ret)) + { + return DROPBEAR_SUCCESS; + } + + if (PASSWORD_VERIFY_ERROR(ret) && ret != -ENOENT) + { + dropbear_log(LOG_WARNING, "passwd_verify failed for '%s': %d", + username, ret); + } + + return DROPBEAR_FAILURE; +} diff --git a/netutils/dropbear/port/nuttx_compat.c b/netutils/dropbear/port/nuttx_compat.c new file mode 100644 index 00000000000..ed97e8b9a42 --- /dev/null +++ b/netutils/dropbear/port/nuttx_compat.c @@ -0,0 +1,78 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_compat.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char g_dropbear_shell[] = "/bin/sh"; +static int g_shell_returned; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void dropbear_setusershell(void) +{ + g_shell_returned = 0; +} + +FAR char *dropbear_getusershell(void) +{ + if (g_shell_returned == 0) + { + g_shell_returned = 1; + return g_dropbear_shell; + } + + return NULL; +} + +void dropbear_endusershell(void) +{ + g_shell_returned = 0; +} + +int dropbear_getgroups(int size, gid_t list[]) +{ + (void)size; + (void)list; + + errno = ENOSYS; + return -1; +} + +int link(FAR const char *path1, FAR const char *path2) +{ + (void)path1; + (void)path2; + + /* NuttX may not provide hard links depending on FS/config. + * Return ENOSYS so Dropbear falls back to non-atomic write path. + */ + + errno = ENOSYS; + return -1; +} + +void svr_chansessinitialise(void) +{ +} + +void svr_chansess_checksignal(void) +{ +} diff --git a/netutils/dropbear/port/nuttx_config.h b/netutils/dropbear/port/nuttx_config.h new file mode 100644 index 00000000000..89c43ea73f9 --- /dev/null +++ b/netutils/dropbear/port/nuttx_config.h @@ -0,0 +1,139 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_config.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_CONFIG_H +#define __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_CONFIG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_SERVER 1 +#define BUNDLED_LIBTOM 1 + +#define DISABLE_LASTLOG 1 +#define DISABLE_PAM 1 +#define DISABLE_PUTUTLINE 1 +#define DISABLE_PUTUTXLINE 1 +#define DISABLE_SYSLOG 1 +#define DISABLE_UTMP 1 +#define DISABLE_UTMPX 1 +#define DISABLE_WTMP 1 +#define DISABLE_WTMPX 1 +#define DISABLE_ZLIB 1 + +#define DROPBEAR_FUZZ 0 +#define DROPBEAR_PLUGIN 0 + +#define HAVE_BASENAME 1 +#define HAVE_CLOCK_GETTIME 1 +#define HAVE_CONST_GAI_STRERROR_PROTO 1 +#define HAVE_CRYPT 1 +#define HAVE_DECL_HTOLE64 1 +#define HAVE_ENDIAN_H 1 +#define HAVE_EXPLICIT_BZERO 1 +#define HAVE_FREEADDRINFO 1 +#define HAVE_GAI_STRERROR 1 +#define HAVE_GETADDRINFO 1 +#define HAVE_GETNAMEINFO 1 +#define HAVE_GETRANDOM 1 +#define HAVE_GETUSERSHELL 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LIBGEN_H 1 +#define HAVE_NETDB_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_NETINET_TCP_H 1 +#define HAVE_PATHS_H 1 +#define HAVE_PUTENV 1 +#define HAVE_STATIC_ASSERT 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDIO_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRUCT_ADDRINFO 1 +#define HAVE_STRUCT_IN6_ADDR 1 +#define HAVE_STRUCT_SOCKADDR_IN6 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +#define HAVE_SYS_RANDOM_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_UIO_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_UINT16_T 1 +#define HAVE_UINT32_T 1 +#define HAVE_UINT8_T 1 +#define HAVE_U_INT16_T 1 +#define HAVE_U_INT32_T 1 +#define HAVE_U_INT8_T 1 +#define HAVE_UNDERSCORE_STATIC_ASSERT 1 +#define HAVE_UNISTD_H 1 +/* NuttX exposes writev(), but keep this port on Dropbear's simpler write() + * path until the SSH-to-NSH channel bridge is validated with vectored + * writes. + */ + +#undef HAVE_WRITEV +#define STDC_HEADERS 1 + +#define PACKAGE_BUGREPORT "" +#define PACKAGE_NAME "" +#define PACKAGE_STRING "" +#define PACKAGE_TARNAME "" +#define PACKAGE_URL "" +#define PACKAGE_VERSION "" + +#define SELECT_TYPE_ARG1 int +#define SELECT_TYPE_ARG234 (fd_set *) +#define SELECT_TYPE_ARG5 (struct timeval *) + +#ifndef PF_UNIX +# define PF_UNIX AF_UNIX +#endif + +#ifndef GRND_NONBLOCK +# define GRND_NONBLOCK O_NONBLOCK +#endif + +#define IPPORT_RESERVED 1024 + +#define getuid dropbear_getuid +#define geteuid dropbear_geteuid +#define getpwuid dropbear_getpwuid +#define getpwnam dropbear_getpwnam +#define getgroups dropbear_getgroups +#define setusershell dropbear_setusershell +#define getusershell dropbear_getusershell +#define endusershell dropbear_endusershell + +uid_t getuid(void); +uid_t geteuid(void); +struct passwd *getpwuid(uid_t uid); +struct passwd *getpwnam(const char *name); +int dropbear_auth_initialize(void); +int dropbear_verify_password(const char *username, const char *password); +int dropbear_hostkey_initialize(void); +int getgroups(int size, gid_t list[]); +void setusershell(void); +char *getusershell(void); +void endusershell(void); + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_CONFIG_H */ diff --git a/netutils/dropbear/port/nuttx_hostkey.c b/netutils/dropbear/port/nuttx_hostkey.c new file mode 100644 index 00000000000..08daba6b0e3 --- /dev/null +++ b/netutils/dropbear/port/nuttx_hostkey.c @@ -0,0 +1,303 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_hostkey.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "dbutil.h" +#include "ecc.h" +#include "runopts.h" +#include "signkey.h" + +#include "dropbear_crypto.h" +#include "dropbear_utils.h" +#include "nuttx_hostkey.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_HOSTKEY_MAGIC "nuttx-ecdsa-p256-v1" +#define DROPBEAR_HOSTKEY_FIELD_HEX_LEN \ + (DROPBEAR_NUTTX_ECDSA_P256_LEN * 2) +#define DROPBEAR_HOSTKEY_LINE_MAX \ + (sizeof(DROPBEAR_HOSTKEY_MAGIC) + 1 + \ + (DROPBEAR_HOSTKEY_FIELD_HEX_LEN * 3) + 4) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static uint8_t g_hostkey_d[DROPBEAR_NUTTX_ECDSA_P256_LEN]; +static bool g_hostkey_ready; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +FAR ecc_key *new_ecc_key(void); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_hostkey_set_ltc(FAR const uint8_t d[32], + FAR const uint8_t x[32], + FAR const uint8_t y[32]) +{ + FAR ecc_key *key; + + key = new_ecc_key(); + key->type = PK_PRIVATE; + key->idx = -1; + key->dp = ecc_curve_nistp256.dp; + + if (mp_from_ubin(key->pubkey.x, x, DROPBEAR_NUTTX_ECDSA_P256_LEN) != + MP_OKAY || + mp_from_ubin(key->pubkey.y, y, DROPBEAR_NUTTX_ECDSA_P256_LEN) != + MP_OKAY || + mp_from_ubin(key->k, d, DROPBEAR_NUTTX_ECDSA_P256_LEN) != MP_OKAY) + { + ecc_free(key); + m_free(key); + return -EINVAL; + } + + mp_set(key->pubkey.z, 1); + + if (svr_opts.hostkey == NULL) + { + svr_opts.hostkey = new_sign_key(); + } + + if (svr_opts.hostkey->ecckey256 != NULL) + { + ecc_free(svr_opts.hostkey->ecckey256); + m_free(svr_opts.hostkey->ecckey256); + } + + svr_opts.hostkey->ecckey256 = key; + memcpy(g_hostkey_d, d, sizeof(g_hostkey_d)); + g_hostkey_ready = true; + return OK; +} + +static int dropbear_hostkey_parse_line(FAR char *line, + FAR uint8_t d[32], + FAR uint8_t x[32], + FAR uint8_t y[32]) +{ + FAR char *saveptr; + FAR char *magic; + FAR char *d_hex; + FAR char *x_hex; + FAR char *y_hex; + + magic = strtok_r(line, ":\r\n", &saveptr); + d_hex = strtok_r(NULL, ":\r\n", &saveptr); + x_hex = strtok_r(NULL, ":\r\n", &saveptr); + y_hex = strtok_r(NULL, ":\r\n", &saveptr); + + if (magic == NULL || d_hex == NULL || x_hex == NULL || y_hex == NULL || + strcmp(magic, DROPBEAR_HOSTKEY_MAGIC) != 0) + { + return -EINVAL; + } + + if (dropbear_hex_decode(d_hex, strlen(d_hex), d, + DROPBEAR_NUTTX_ECDSA_P256_LEN) < 0 || + dropbear_hex_decode(x_hex, strlen(x_hex), x, + DROPBEAR_NUTTX_ECDSA_P256_LEN) < 0 || + dropbear_hex_decode(y_hex, strlen(y_hex), y, + DROPBEAR_NUTTX_ECDSA_P256_LEN) < 0) + { + return -EINVAL; + } + + return OK; +} + +static int dropbear_hostkey_read(FAR const char *path, + FAR uint8_t d[32], + FAR uint8_t x[32], + FAR uint8_t y[32]) +{ + char line[DROPBEAR_HOSTKEY_LINE_MAX]; + FAR FILE *fp; + int ret; + + fp = fopen(path, "r"); + if (fp == NULL) + { + return -errno; + } + + if (fgets(line, sizeof(line), fp) == NULL) + { + ret = -EIO; + } + else + { + ret = dropbear_hostkey_parse_line(line, d, x, y); + } + + fclose(fp); + return ret; +} + +static int dropbear_hostkey_write(FAR const char *path, + FAR const uint8_t d[32], + FAR const uint8_t x[32], + FAR const uint8_t y[32]) +{ + char d_hex[DROPBEAR_HOSTKEY_FIELD_HEX_LEN + 1]; + char x_hex[DROPBEAR_HOSTKEY_FIELD_HEX_LEN + 1]; + char y_hex[DROPBEAR_HOSTKEY_FIELD_HEX_LEN + 1]; + FAR FILE *fp; + int ret; + + ret = dropbear_try_prepare_parent(path); + if (ret < 0) + { + return ret; + } + + fp = fopen(path, "w"); + if (fp == NULL) + { + return -errno; + } + + dropbear_hex_encode(d_hex, d, DROPBEAR_NUTTX_ECDSA_P256_LEN); + dropbear_hex_encode(x_hex, x, DROPBEAR_NUTTX_ECDSA_P256_LEN); + dropbear_hex_encode(y_hex, y, DROPBEAR_NUTTX_ECDSA_P256_LEN); + + ret = fprintf(fp, "%s:%s:%s:%s\n", DROPBEAR_HOSTKEY_MAGIC, + d_hex, x_hex, y_hex); + explicit_bzero(d_hex, sizeof(d_hex)); + + if (ret < 0) + { + ret = -EIO; + } + else if (fclose(fp) < 0) + { + ret = -errno; + fp = NULL; + } + else + { + fp = NULL; + ret = OK; + } + + if (fp != NULL) + { + fclose(fp); + } + + return ret; +} + +static int dropbear_hostkey_generate(FAR const char *path, + FAR uint8_t d[32], + FAR uint8_t x[32], + FAR uint8_t y[32]) +{ + int ret; + + dropbear_log(LOG_INFO, "generating ECDSA P-256 host key at %s", path); + + ret = dropbear_nuttx_ecdsa_p256_genkey(d, x, y); + if (ret < 0) + { + dropbear_log(LOG_ERR, "failed to generate ECDSA P-256 host key: %d", + ret); + return ret; + } + + ret = dropbear_hostkey_write(path, d, x, y); + if (ret < 0) + { + dropbear_log(LOG_ERR, "failed to write host key %s: %d", path, ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int dropbear_hostkey_initialize(void) +{ + FAR const char *path = CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH; + uint8_t d[DROPBEAR_NUTTX_ECDSA_P256_LEN]; + uint8_t x[DROPBEAR_NUTTX_ECDSA_P256_LEN]; + uint8_t y[DROPBEAR_NUTTX_ECDSA_P256_LEN]; + int ret; + + ret = dropbear_hostkey_read(path, d, x, y); + if (ret < 0) + { +#ifdef CONFIG_NETUTILS_DROPBEAR_GENERATE_HOSTKEY + if (ret == -ENOENT) + { + ret = dropbear_hostkey_generate(path, d, x, y); + } +#endif + + if (ret < 0) + { + explicit_bzero(d, sizeof(d)); + return ret; + } + } + + ret = dropbear_hostkey_set_ltc(d, x, y); + explicit_bzero(d, sizeof(d)); + if (ret < 0) + { + return ret; + } + + dropbear_log(LOG_INFO, "loaded ECDSA P-256 host key from %s", path); + return OK; +} + +int dropbear_hostkey_ecdsa_p256_sign(FAR const uint8_t *data, + size_t datalen, + FAR uint8_t r[32], + FAR uint8_t s[32]) +{ + uint8_t hash[DROPBEAR_NUTTX_SHA256_LEN]; + int ret; + + if (!g_hostkey_ready) + { + return DROPBEAR_FAILURE; + } + + ret = dropbear_nuttx_sha256(data, datalen, hash); + if (ret == OK) + { + ret = dropbear_nuttx_ecdsa_p256_sign(g_hostkey_d, hash, r, s); + } + + explicit_bzero(hash, sizeof(hash)); + return ret == OK ? DROPBEAR_SUCCESS : DROPBEAR_FAILURE; +} diff --git a/netutils/dropbear/port/nuttx_hostkey.h b/netutils/dropbear/port/nuttx_hostkey.h new file mode 100644 index 00000000000..df0702eb3f9 --- /dev/null +++ b/netutils/dropbear/port/nuttx_hostkey.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_hostkey.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_HOSTKEY_H +#define __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_HOSTKEY_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int dropbear_hostkey_initialize(void); + +int dropbear_hostkey_ecdsa_p256_sign(FAR const uint8_t *data, + size_t datalen, + FAR uint8_t r[32], + FAR uint8_t s[32]); + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_HOSTKEY_H */ diff --git a/netutils/dropbear/port/nuttx_localoptions.h b/netutils/dropbear/port/nuttx_localoptions.h new file mode 100644 index 00000000000..790f290d08d --- /dev/null +++ b/netutils/dropbear/port/nuttx_localoptions.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_localoptions.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_LOCALOPTIONS_H +#define __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_LOCALOPTIONS_H + +#define DROPBEAR_SVR_DROP_PRIVS 0 +#define DROPBEAR_SVR_MULTIUSER 0 +#define DROPBEAR_SVR_PASSWORD_AUTH 1 +#define DROPBEAR_SVR_PUBKEY_AUTH 0 +#define DROPBEAR_SVR_PUBKEY_OPTIONS 0 + +#define DROPBEAR_REEXEC 0 +#define DROPBEAR_SMALL_CODE 1 +#define DROPBEAR_USER_ALGO_LIST 0 + +#define DROPBEAR_X11FWD 0 +#define DROPBEAR_SVR_AGENTFWD 0 +#define DROPBEAR_SVR_LOCALTCPFWD 0 +#define DROPBEAR_SVR_REMOTETCPFWD 0 +#define DROPBEAR_SVR_LOCALSTREAMFWD 0 + +#define DROPBEAR_DSS 0 +#define DROPBEAR_RSA 0 +#define DROPBEAR_ECDSA 1 +#define DROPBEAR_ED25519 0 +#define DROPBEAR_SK_KEYS 0 +#define DROPBEAR_ECC_256 1 +#define DROPBEAR_ECC_384 0 +#define DROPBEAR_ECC_521 0 + +#define DROPBEAR_AES128 1 +#define DROPBEAR_AES256 0 +#define DROPBEAR_CHACHA20POLY1305 1 +#define DROPBEAR_ENABLE_CBC_MODE 0 +#define DROPBEAR_ENABLE_GCM_MODE 0 + +#define DROPBEAR_SHA1_HMAC 0 +#define DROPBEAR_SHA2_256_HMAC 1 +#define DROPBEAR_SHA2_512_HMAC 0 +#define DROPBEAR_SHA1_96_HMAC 0 + +#define DROPBEAR_CURVE25519 1 +#define DROPBEAR_DH_GROUP14_SHA1 0 +#define DROPBEAR_DH_GROUP14_SHA256 0 +#define DROPBEAR_DH_GROUP16 0 +#define DROPBEAR_DH_GROUP1 0 +#define DROPBEAR_ECDH 0 +#define DROPBEAR_SNTRUP761 0 +#define DROPBEAR_MLKEM768 0 + +#define DROPBEAR_DEFAULT_CLI_AUTHKEY "/etc/dropbear/authorized_keys" +#define DROPBEAR_SFTPSERVER 0 + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_LOCALOPTIONS_H */ diff --git a/nshlib/CMakeLists.txt b/nshlib/CMakeLists.txt index 86b1b06ae4a..f8f88d11ec7 100644 --- a/nshlib/CMakeLists.txt +++ b/nshlib/CMakeLists.txt @@ -97,6 +97,10 @@ if(CONFIG_NSH_LIBRARY) endif() endif() + if(CONFIG_NSH_DROPBEAR) + list(APPEND CSRCS nsh_dropbear.c) + endif() + if(NOT CONFIG_NSH_DISABLESCRIPT) list(APPEND CSRCS nsh_test.c) endif() diff --git a/nshlib/Kconfig b/nshlib/Kconfig index b9a51038330..7812a9f9e86 100644 --- a/nshlib/Kconfig +++ b/nshlib/Kconfig @@ -1168,6 +1168,28 @@ config NSH_DISABLE_TELNETSTART endmenu # Telnet Configuration +menu "Dropbear Configuration" + +config NSH_DROPBEAR + bool "Start Dropbear SSH server" + default n + depends on NETUTILS_DROPBEAR + depends on NSH_BUILTIN_APPS + depends on !NSH_DISABLEBG + select NSH_LOGIN + ---help--- + If NSH_DROPBEAR is set to 'y', then NSH starts the Dropbear SSH + server automatically. + +config NSH_DISABLE_DROPBEARSTART + bool "Disable to start Dropbear" + default n + depends on NSH_DROPBEAR + ---help--- + Determines if NSH starts Dropbear automatically. + +endmenu # Dropbear Configuration + config NSH_LOGIN bool default n diff --git a/nshlib/Makefile b/nshlib/Makefile index 26dbbd0de70..73ebc363fc0 100644 --- a/nshlib/Makefile +++ b/nshlib/Makefile @@ -82,6 +82,10 @@ CSRCS += nsh_telnetlogin.c endif endif +ifeq ($(CONFIG_NSH_DROPBEAR),y) +CSRCS += nsh_dropbear.c +endif + ifneq ($(CONFIG_NSH_DISABLESCRIPT),y) CSRCS += nsh_test.c endif diff --git a/nshlib/nsh.h b/nshlib/nsh.h index 8ea334d077e..680b1b70133 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -850,6 +850,10 @@ int nsh_login(FAR struct console_stdio_s *pstate); int nsh_telnetlogin(FAR struct console_stdio_s *pstate); #endif +#if defined(CONFIG_NSH_DROPBEAR) && !defined(CONFIG_NSH_DISABLE_DROPBEARSTART) +int nsh_dropbearstart(void); +#endif + /* Application interface */ int nsh_command(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char *argv[]); diff --git a/nshlib/nsh_dropbear.c b/nshlib/nsh_dropbear.c new file mode 100644 index 00000000000..9e7753f11a0 --- /dev/null +++ b/nshlib/nsh_dropbear.c @@ -0,0 +1,76 @@ +/**************************************************************************** + * apps/nshlib/nsh_dropbear.c + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "nsh.h" +#include "nsh_console.h" + +#ifdef CONFIG_NSH_DROPBEAR + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_dropbearstart + * + * Description: + * nsh_dropbearstart() starts the Dropbear SSH server. This function + * returns immediately after the daemon has been started. + * + * Returned Values: + * Zero is returned if Dropbear was started. A negated errno value will be + * returned on failure. + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_DROPBEARSTART +int nsh_dropbearstart(void) +{ + FAR struct console_stdio_s *pstate = nsh_newconsole(false); + char cmdline[] = CONFIG_NETUTILS_DROPBEAR_PROGNAME " &"; + int ret; + + DEBUGASSERT(pstate != NULL); + + ninfo("Starting the Dropbear SSH server\n"); + + ret = nsh_parse(&pstate->cn_vtbl, cmdline); + if (ret < 0) + { + nerr("ERROR: Failed to start Dropbear: %d\n", ret); + } + + nsh_release(&pstate->cn_vtbl); + return ret; +} +#endif + +#endif /* CONFIG_NSH_DROPBEAR */ diff --git a/nshlib/nsh_init.c b/nshlib/nsh_init.c index 4846ff15b97..f2e3d9d5898 100644 --- a/nshlib/nsh_init.c +++ b/nshlib/nsh_init.c @@ -187,4 +187,16 @@ void nsh_initialize(void) nsh_telnetstart(AF_UNSPEC); #endif + +#if defined(CONFIG_NSH_DROPBEAR) && \ + !defined(CONFIG_NSH_DISABLE_DROPBEARSTART) && \ + !defined(CONFIG_NETINIT_NETLOCAL) + /* If Dropbear is selected as an SSH front-end, then start the daemon + * UNLESS network initialization is deferred via CONFIG_NETINIT_NETLOCAL. + * In that case, Dropbear must be started manually with the dropbear + * command after the network has been initialized. + */ + + nsh_dropbearstart(); +#endif }