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..c3187a32dff --- /dev/null +++ b/netutils/dropbear/CMakeLists.txt @@ -0,0 +1,185 @@ +# ############################################################################## +# 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-guard-platform-declarations.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-passwd-auth.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-allow-localoptions-to-override-tracking-malloc.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-use-nuttx-unused-macro.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + message(STATUS "Generating default_options_guard.h") + execute_process( + COMMAND + sh + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}/src/ifndef_wrapper.sh" + INPUT_FILE + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}/src/default_options.h" + OUTPUT_FILE + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}/src/default_options_guard.h" + 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/dropbear_utils.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/curve25519.c + dropbear/src/chachapoly.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-forward.c + dropbear/src/svr-streamfwd.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 ".*/prngs/sober128tab\\.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) + + if(CONFIG_NETUTILS_DROPBEAR_COMPRESSION) + target_include_directories( + ${PROGNAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../system/zlib/zlib) + endif() + + target_compile_definitions( + ${PROGNAME} PRIVATE LOCALOPTIONS_H_EXISTS=1 DROPBEAR_NUTTX=1 + DROPBEAR_NUTTX_PASSWD=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..507c8506834 --- /dev/null +++ b/netutils/dropbear/Kconfig @@ -0,0 +1,120 @@ +# +# 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 + depends on DEV_RANDOM + depends on DEV_URANDOM + depends on LIBC_NETDB + depends on LIBC_GAISTRERROR + select CRYPTO + select CRYPTO_RANDOM_POOL + ---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 + ---help--- + Stack size for the Dropbear server built-in. + This is architecture-specific, so adjust it according to your setup. + +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 is stored in Dropbear's native host key format. + +config NETUTILS_DROPBEAR_GENERATE_HOSTKEY + bool "Generate host key if missing" + default y + ---help--- + Pass -R so Dropbear generates an ECDSA P-256 host key on demand and + persists it at NETUTILS_DROPBEAR_HOSTKEY_PATH when it does not exist. + Product builds can disable this and provision the host key + externally (loaded with -r). + +config NETUTILS_DROPBEAR_COMPRESSION + bool "Enable SSH compression (zlib)" + default n + depends on LIB_ZLIB + ---help--- + Enable zlib compression for SSH sessions. Requires the zlib + library (LIB_ZLIB). When disabled, Dropbear is built with + DISABLE_ZLIB and negotiates no compression. + + WARNING: each session allocates a zlib deflate state of about + 256 KiB (DROPBEAR_ZLIB_WINDOW_BITS=15, DROPBEAR_ZLIB_MEM_LEVEL=8), + and the state is allocated even for the delayed zlib@openssh.com + method, right after key exchange. + +config NETUTILS_DROPBEAR_SYSLOG + bool "Log via syslog" + default n + ---help--- + Route Dropbear log messages through syslog(). When disabled, + Dropbear is built with DISABLE_SYSLOG. + +config NETUTILS_DROPBEAR_COMMIT + string "Dropbear upstream commit" + default "54ef47adf8c99b422be6a8f694f2866e62f88b9e" + ---help--- + Upstream Dropbear (mkj/dropbear) commit to download and build. + +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..039b0d0b35c --- /dev/null +++ b/netutils/dropbear/Makefile @@ -0,0 +1,157 @@ +############################################################################ +# 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" + +ifneq ($(CONFIG_NETUTILS_DROPBEAR_COMPRESSION),) +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)system$(DELIM)zlib$(DELIM)zlib" +endif + +CFLAGS += ${DEFINE_PREFIX}LOCALOPTIONS_H_EXISTS=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX_PASSWD=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/dropbear_utils.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/curve25519.c \ + dropbear/src/chachapoly.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-forward.c \ + dropbear/src/svr-streamfwd.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) + +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-guard-platform-declarations.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0002-use-nuttx-passwd-auth.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0003-allow-localoptions-to-override-tracking-malloc.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0004-use-nuttx-unused-macro.patch; true + @echo "Generating default_options_guard.h" + $(Q) sh $(DROPBEAR_UNPACKNAME)$(DELIM)src$(DELIM)ifndef_wrapper.sh \ + < $(DROPBEAR_UNPACKNAME)$(DELIM)src$(DELIM)default_options.h \ + > $(DROPBEAR_UNPACKNAME)$(DELIM)src$(DELIM)default_options_guard.h + $(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/dropbear_main.c b/netutils/dropbear/dropbear_main.c new file mode 100644 index 00000000000..798ba2b554f --- /dev/null +++ b/netutils/dropbear/dropbear_main.c @@ -0,0 +1,329 @@ +/**************************************************************************** + * 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 "signkey.h" +#include "gensignkey.h" +#include "ssh.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) +{ + /* Load the host key with -r so it lives in the persistent (epoch 0) + * allocation rather than being created lazily inside a per-session malloc + * epoch (-R), which would free it when the first session ends and leave a + * dangling svr_opts.hostkey for the next connection. + */ + + FAR char *argv[] = + { + "dropbear", + "-F", + "-p", + (FAR char *)port, + "-r", + CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH, + NULL + }; + + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; + + disallow_core(); + svr_getopts(6, argv); + seedrandom(); + crypto_init(); + +#ifdef CONFIG_NETUTILS_DROPBEAR_GENERATE_HOSTKEY + /* Generate the ECDSA host key now if it is missing. signkey_generate() + * with skip_exist=1 silently succeeds when the file already exists. + */ + + if (signkey_generate(DROPBEAR_SIGNKEY_ECDSA_NISTP256, 0, + CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH, 1) + == DROPBEAR_FAILURE) + { + dropbear_exit("failed to generate host key"); + } +#endif + + load_all_hostkeys(); + + 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; + + m_malloc_set_epoch(1); + + if (setjmp(g_session_exit_jmp) == 0) + { + svr_session(childsock, -1); + } + + m_malloc_free_epoch(1, 1); + m_malloc_set_epoch(0); + + _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..3b2e6cdbd68 --- /dev/null +++ b/netutils/dropbear/dropbear_nshsession.c @@ -0,0 +1,633 @@ +/**************************************************************************** + * 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 Functions + ****************************************************************************/ + +/* Chansession lifecycle hooks invoked from svr-session.c. Upstream + * svr-chansession.c uses these to set up and reap the global childpids[] / + * SIGCHLD machinery; the NSH bridge tracks each session's child through its + * own waiter thread (dropbear_nsh_waiter), so both are no-ops here. + */ + +void svr_chansessinitialise(void) +{ +} + +void svr_chansess_checksignal(void) +{ +} + +/**************************************************************************** + * 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-guard-platform-declarations.patch b/netutils/dropbear/patch/0001-guard-platform-declarations.patch new file mode 100644 index 00000000000..7eb1dfcd23a --- /dev/null +++ b/netutils/dropbear/patch/0001-guard-platform-declarations.patch @@ -0,0 +1,26 @@ +--- 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/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-passwd-auth.patch b/netutils/dropbear/patch/0002-use-nuttx-passwd-auth.patch new file mode 100644 index 00000000000..5e71d73daba --- /dev/null +++ b/netutils/dropbear/patch/0002-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/patch/0003-allow-localoptions-to-override-tracking-malloc.patch b/netutils/dropbear/patch/0003-allow-localoptions-to-override-tracking-malloc.patch new file mode 100644 index 00000000000..b1dc3e52c29 --- /dev/null +++ b/netutils/dropbear/patch/0003-allow-localoptions-to-override-tracking-malloc.patch @@ -0,0 +1,24 @@ +--- a/src/sysoptions.h ++++ b/src/sysoptions.h +@@ -183,7 +183,9 @@ defined(__has_feature) + #define LTC_ECC521 + #endif + +-#define DROPBEAR_LTC_PRNG (DROPBEAR_ECC) ++#ifndef DROPBEAR_LTC_PRNG ++#define DROPBEAR_LTC_PRNG (DROPBEAR_ECC) ++#endif + + /* RSA can be vulnerable to timing attacks which use the time required for + * signing to guess the private key. Blinding avoids this attack, though makes +@@ -436,7 +438,9 @@ defined(__has_feature) + #define DROPBEAR_CLIENT_TCP_FAST_OPEN 0 + #endif + +-#define DROPBEAR_TRACKING_MALLOC (DROPBEAR_FUZZ) ++#ifndef DROPBEAR_TRACKING_MALLOC ++#define DROPBEAR_TRACKING_MALLOC (DROPBEAR_FUZZ) ++#endif + + /* Used to work around Memory Sanitizer false positives */ + #if defined(__has_feature) diff --git a/netutils/dropbear/patch/0004-use-nuttx-unused-macro.patch b/netutils/dropbear/patch/0004-use-nuttx-unused-macro.patch new file mode 100644 index 00000000000..2ea58805739 --- /dev/null +++ b/netutils/dropbear/patch/0004-use-nuttx-unused-macro.patch @@ -0,0 +1,202 @@ +--- a/src/compat.c ++++ b/src/compat.c +@@ -89,6 +89,6 @@ + #ifndef HAVE_GETUSERSHELL + static char **curshell, **shells, *strings; +-static char **initshells(); ++static char **initshells(void); + #endif + + #ifndef HAVE_STRLCPY +@@ -234,7 +234,7 @@ + curshell = initshells(); + } + +-static char **initshells() { ++static char **initshells(void) { + static const char *okshells[] = { COMPAT_USER_SHELLS, NULL }; + register char **sp, *cp; + register FILE *fp; +--- a/src/common-algo.c ++++ b/src/common-algo.c +@@ -39,16 +39,25 @@ + /* This file (algo.c) organises the ciphers which can be used, and is used to + * 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)) { ++static int void_cipher(const unsigned char* in, unsigned char* out, ++ 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/dbutil.c ++++ b/src/dbutil.c +@@ -138,7 +138,9 @@ + } + +-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/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/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/chachapoly.c ++++ b/src/chachapoly.c +@@ -43,9 +43,13 @@ + 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) { ++ UNUSED(cipher); ++ UNUSED(IV); ++ UNUSED(num_rounds); ++ + int err; + + TRACE2(("enter dropbear_chachapoly_start")) 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/dropbear_utils.c b/netutils/dropbear/port/dropbear_utils.c new file mode 100644 index 00000000000..73619cbe750 --- /dev/null +++ b/netutils/dropbear/port/dropbear_utils.c @@ -0,0 +1,149 @@ +/**************************************************************************** + * 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); +} + +#ifndef CONFIG_SCHED_USER_IDENTITY +int dropbear_getgroups(int size, gid_t list[]) +{ + (void)size; + (void)list; + set_errno(ENOSYS); + return ERROR; +} +#endif 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..28ac5a3b5b4 --- /dev/null +++ b/netutils/dropbear/port/localoptions.h @@ -0,0 +1,81 @@ +/**************************************************************************** + * 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 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_TRACKING_MALLOC 1 + +/* Persist the native Dropbear ECDSA host key at the configured path. */ + +#define ECDSA_PRIV_FILENAME CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH + +#define DROPBEAR_SVR_DROP_PRIVS 0 + +#ifdef CONFIG_SCHED_USER_IDENTITY +# define DROPBEAR_SVR_MULTIUSER 1 +#else +# define DROPBEAR_SVR_MULTIUSER 0 +#endif + +#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_SVR_REMOTESTREAMFWD 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_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_config.h b/netutils/dropbear/port/nuttx_config.h new file mode 100644 index 00000000000..d91709d1e90 --- /dev/null +++ b/netutils/dropbear/port/nuttx_config.h @@ -0,0 +1,142 @@ +/**************************************************************************** + * 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 +#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_UTMP 1 +#define DISABLE_UTMPX 1 +#define DISABLE_WTMP 1 +#define DISABLE_WTMPX 1 + +#ifndef CONFIG_NETUTILS_DROPBEAR_SYSLOG +# define DISABLE_SYSLOG 1 +#endif + +#ifndef CONFIG_NETUTILS_DROPBEAR_COMPRESSION +# define DISABLE_ZLIB 1 +#endif + +#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_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 +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); + +#ifndef CONFIG_SCHED_USER_IDENTITY +/* NuttX has no supplementary-group support, so getgroups() does not exist. */ + +int dropbear_getgroups(int size, gid_t list[]); + +# define getgroups dropbear_getgroups +#endif + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_CONFIG_H */ diff --git a/nshlib/CMakeLists.txt b/nshlib/CMakeLists.txt index 8adab316892..866f8bb6c9f 100644 --- a/nshlib/CMakeLists.txt +++ b/nshlib/CMakeLists.txt @@ -101,6 +101,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 80476e7f56e..01103ee29a8 100644 --- a/nshlib/Kconfig +++ b/nshlib/Kconfig @@ -1201,6 +1201,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 d15ca4aa311..4aba9e3a827 100644 --- a/nshlib/Makefile +++ b/nshlib/Makefile @@ -86,6 +86,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 f20fcf7732e..872c1e4af2a 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -844,6 +844,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 aea095e34cd..49c1488e6bf 100644 --- a/nshlib/nsh_init.c +++ b/nshlib/nsh_init.c @@ -175,4 +175,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 }