netutils/dropbear: add scp support via SSH exec requests

Add SCP support do the dropbear server.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
This commit is contained in:
Felipe Moura 2026-07-04 13:32:17 -03:00
parent 8e00b4d672
commit b5705ea7a8
6 changed files with 430 additions and 18 deletions

View file

@ -137,6 +137,10 @@ if(CONFIG_NETUTILS_DROPBEAR)
list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/prngs/sober128tab\\.c$")
list(APPEND DROPBEAR_SRCS ${LIBTOMCRYPT_SRCS})
if(CONFIG_NETUTILS_DROPBEAR_SCP)
list(APPEND DROPBEAR_SRCS dropbear/src/scpmisc.c port/nuttx_scp.c)
endif()
nuttx_add_application(
NAME
${PROGNAME}
@ -150,6 +154,20 @@ if(CONFIG_NETUTILS_DROPBEAR)
DEPENDS
${DROPBEAR_UNPACKNAME})
if(CONFIG_NETUTILS_DROPBEAR_SCP)
nuttx_add_application(
NAME
scp
SRCS
dropbear/src/scp.c
STACKSIZE
${CONFIG_NETUTILS_DROPBEAR_SCP_STACKSIZE}
PRIORITY
${CONFIG_NETUTILS_DROPBEAR_SCP_PRIORITY}
DEPENDS
${DROPBEAR_UNPACKNAME})
endif()
target_include_directories(
${PROGNAME}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
@ -160,6 +178,14 @@ if(CONFIG_NETUTILS_DROPBEAR)
${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtommath
${CMAKE_CURRENT_SOURCE_DIR}/../../nshlib)
if(CONFIG_NETUTILS_DROPBEAR_SCP)
target_include_directories(
scp
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/port
${CMAKE_CURRENT_SOURCE_DIR}/dropbear
${CMAKE_CURRENT_SOURCE_DIR}/dropbear/src)
endif()
if(CONFIG_NETUTILS_DROPBEAR_COMPRESSION)
target_include_directories(
${PROGNAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../system/zlib/zlib)
@ -169,17 +195,46 @@ if(CONFIG_NETUTILS_DROPBEAR)
${PROGNAME} PRIVATE LOCALOPTIONS_H_EXISTS=1 DROPBEAR_NUTTX=1
DROPBEAR_NUTTX_PASSWD=1)
if(CONFIG_NETUTILS_DROPBEAR_SCP)
target_compile_definitions(
scp PRIVATE LOCALOPTIONS_H_EXISTS=1 DROPBEAR_NUTTX=1
DROPBEAR_NUTTX_PASSWD=1)
endif()
set_source_files_properties(
dropbear_nshsession.c
PROPERTIES COMPILE_DEFINITIONS
"Channel=dropbear_channel;ChanType=dropbear_chantype")
if(CONFIG_NETUTILS_DROPBEAR_SCP)
set_source_files_properties(
dropbear/src/scp.c
PROPERTIES
COMPILE_DEFINITIONS
"xmalloc=dropbear_scp_xmalloc;xrealloc=dropbear_scp_xrealloc;xfree=dropbear_scp_xfree;execvp=dropbear_scp_execvp"
)
set_source_files_properties(
dropbear/src/scpmisc.c
PROPERTIES
COMPILE_DEFINITIONS
"xmalloc=dropbear_scp_xmalloc;xrealloc=dropbear_scp_xrealloc;xfree=dropbear_scp_xfree"
)
endif()
# 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)
if(CONFIG_NETUTILS_DROPBEAR_SCP)
target_compile_options(
scp
PRIVATE -Wno-pointer-sign -Wno-format -include
${CMAKE_CURRENT_SOURCE_DIR}/port/nuttx_scp.h
-Wno-strict-prototypes)
endif()
target_sources(apps PRIVATE ${DROPBEAR_SRCS})
endif()

View file

@ -70,6 +70,26 @@ config NETUTILS_DROPBEAR_SHELL_STACKSIZE
int "Dropbear NSH session task stack size"
default 8192
config NETUTILS_DROPBEAR_SCP
bool "Enable scp remote copy helper"
default y
depends on PIPES
---help---
Build Dropbear's scp program and allow SSH exec requests so a host
scp client can copy files to and from NuttX using the legacy scp
protocol. This does not build a full SSH client for initiating scp
transfers from the target.
config NETUTILS_DROPBEAR_SCP_STACKSIZE
int "Dropbear scp stack size"
default 32768
depends on NETUTILS_DROPBEAR_SCP
config NETUTILS_DROPBEAR_SCP_PRIORITY
int "Dropbear scp priority"
default 100
depends on NETUTILS_DROPBEAR_SCP
config NETUTILS_DROPBEAR_PORT
int "Dropbear listen port"
default 2222

View file

@ -40,6 +40,12 @@ PROGNAME = $(CONFIG_NETUTILS_DROPBEAR_PROGNAME)
PRIORITY = $(CONFIG_NETUTILS_DROPBEAR_PRIORITY)
STACKSIZE = $(CONFIG_NETUTILS_DROPBEAR_STACKSIZE)
ifneq ($(CONFIG_NETUTILS_DROPBEAR_SCP),)
PROGNAME += scp
PRIORITY += $(CONFIG_NETUTILS_DROPBEAR_SCP_PRIORITY)
STACKSIZE += $(CONFIG_NETUTILS_DROPBEAR_SCP_STACKSIZE)
endif
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"
@ -116,11 +122,31 @@ TOMCRYPT_SRCS = $(shell if [ -d "$(DROPBEAR_UNPACKNAME)/libtomcrypt/src" ]; then
CSRCS += $(TOMMATH_SRCS)
CSRCS += $(TOMCRYPT_SRCS)
ifneq ($(CONFIG_NETUTILS_DROPBEAR_SCP),)
CSRCS += dropbear/src/scpmisc.c
CSRCS += port/nuttx_scp.c
endif
# 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
ifneq ($(CONFIG_NETUTILS_DROPBEAR_SCP),)
MAINSRC += dropbear/src/scp.c
dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}xmalloc=dropbear_scp_xmalloc
dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}xrealloc=dropbear_scp_xrealloc
dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}xfree=dropbear_scp_xfree
dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}execvp=dropbear_scp_execvp
dropbear/src/scp.c_CFLAGS += -include port/nuttx_scp.h
dropbear/src/scp.c_CFLAGS += -Wno-strict-prototypes
dropbear/src/scpmisc.c_CFLAGS += ${DEFINE_PREFIX}xmalloc=dropbear_scp_xmalloc
dropbear/src/scpmisc.c_CFLAGS += ${DEFINE_PREFIX}xrealloc=dropbear_scp_xrealloc
dropbear/src/scpmisc.c_CFLAGS += ${DEFINE_PREFIX}xfree=dropbear_scp_xfree
endif
$(DROPBEAR_ZIP):
@echo "Downloading: $(DROPBEAR_ZIP)"
$(Q) curl -L -o $(DROPBEAR_ZIP) $(DROPBEAR_URL)/$(DROPBEAR_ZIP)

View file

@ -43,6 +43,7 @@ struct dropbear_nshsession_s
pthread_t waiter;
bool waiter_started;
bool have_winsize;
bool has_pty;
struct winsize win;
};
@ -80,17 +81,8 @@ static const struct dropbear_signal_name_s g_dropbear_signals[] =
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)
{
@ -98,7 +90,7 @@ static int dropbear_nsh_main(int argc, FAR char *argv[])
return -ENOMEM;
}
ret = nsh_session(pstate, NSH_LOGIN_NONE, 1, nsh_argv);
ret = nsh_session(pstate, NSH_LOGIN_NONE, argc, argv);
dropbear_log(LOG_INFO, "NSH session exited: %d", ret);
nsh_exit(&pstate->cn_vtbl, ret);
@ -208,6 +200,29 @@ dropbear_setup_spawn_stdio(FAR posix_spawn_file_actions_t *actions,
return 0;
}
#ifdef CONFIG_NETUTILS_DROPBEAR_SCP
static int
dropbear_setup_spawn_stdio3(FAR posix_spawn_file_actions_t *actions,
int stdinfd, int stdoutfd, int stderrfd)
{
int rc;
rc = posix_spawn_file_actions_adddup2(actions, stdinfd, STDIN_FILENO);
if (rc != 0)
{
return rc;
}
rc = posix_spawn_file_actions_adddup2(actions, stdoutfd, STDOUT_FILENO);
if (rc != 0)
{
return rc;
}
return posix_spawn_file_actions_adddup2(actions, stderrfd, STDERR_FILENO);
}
#endif /* CONFIG_NETUTILS_DROPBEAR_SCP */
static int
dropbear_setup_spawn_close(FAR posix_spawn_file_actions_t *actions,
int fd)
@ -220,18 +235,21 @@ dropbear_setup_spawn_close(FAR posix_spawn_file_actions_t *actions,
return posix_spawn_file_actions_addclose(actions, fd);
}
static void dropbear_close_fd(FAR int *fd)
{
if (*fd >= 0)
{
close(*fd);
*fd = -1;
}
}
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;
@ -318,7 +336,7 @@ static int dropbear_start_nsh(FAR struct dropbear_channel *channel,
}
sess->nsh_pid = task_spawn("dropbear nsh", dropbear_nsh_main, &actions,
&attr, argv, NULL);
&attr, NULL, NULL);
if (sess->nsh_pid < 0)
{
dropbear_log(LOG_WARNING, "failed to create NSH task: %s",
@ -345,6 +363,7 @@ static int dropbear_start_nsh(FAR struct dropbear_channel *channel,
}
sess->waiter_started = true;
sess->has_pty = true;
sess->pty_readfd = masterfd;
sess->pty_writefd = writefd;
@ -388,6 +407,201 @@ err_with_actions:
return DROPBEAR_FAILURE;
}
#ifdef CONFIG_NETUTILS_DROPBEAR_SCP
static int dropbear_start_exec(FAR struct dropbear_channel *channel,
FAR struct dropbear_nshsession_s *sess,
FAR char *cmd)
{
posix_spawn_file_actions_t actions;
posix_spawnattr_t attr;
FAR const char *errmsg;
FAR char * const argv[] =
{
"-c",
cmd,
NULL
};
int inpipe[2] =
{
-1,
-1
};
int outpipe[2] =
{
-1,
-1
};
int errpipe[2] =
{
-1,
-1
};
int rc;
if (sess->nsh_pid >= 0)
{
dropbear_log(LOG_WARNING, "NSH exec already running");
return DROPBEAR_FAILURE;
}
if (pipe(inpipe) < 0 || pipe(outpipe) < 0 || pipe(errpipe) < 0)
{
dropbear_log(LOG_WARNING, "exec pipe setup failed: %s",
strerror(errno));
goto err_with_pipes;
}
rc = posix_spawn_file_actions_init(&actions);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn actions init failed: %s",
strerror(rc));
goto err_with_pipes;
}
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_stdio3(&actions, inpipe[0], outpipe[1],
errpipe[1]);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn stdio setup failed: %s",
strerror(rc));
goto err_with_attr;
}
rc = dropbear_setup_spawn_close(&actions, inpipe[0]);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn stdin close setup failed: %s",
strerror(rc));
goto err_with_attr;
}
rc = dropbear_setup_spawn_close(&actions, inpipe[1]);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn stdin writer close setup failed: %s",
strerror(rc));
goto err_with_attr;
}
rc = dropbear_setup_spawn_close(&actions, outpipe[0]);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn stdout reader close setup failed: %s",
strerror(rc));
goto err_with_attr;
}
rc = dropbear_setup_spawn_close(&actions, outpipe[1]);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn stdout close setup failed: %s",
strerror(rc));
goto err_with_attr;
}
rc = dropbear_setup_spawn_close(&actions, errpipe[0]);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn stderr reader close setup failed: %s",
strerror(rc));
goto err_with_attr;
}
rc = dropbear_setup_spawn_close(&actions, errpipe[1]);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "spawn stderr close setup failed: %s",
strerror(rc));
goto err_with_attr;
}
sess->nsh_pid = task_spawn("dropbear exec", dropbear_nsh_main, &actions,
&attr, argv, NULL);
if (sess->nsh_pid < 0)
{
dropbear_log(LOG_WARNING, "failed to create NSH exec task: %s",
strerror(-sess->nsh_pid));
goto err_with_attr;
}
dropbear_close_fd(&inpipe[0]);
dropbear_close_fd(&outpipe[1]);
dropbear_close_fd(&errpipe[1]);
rc = pthread_create(&sess->waiter, NULL, dropbear_nsh_waiter, sess);
if (rc != 0)
{
dropbear_log(LOG_WARNING, "failed to create NSH exec waiter: %s",
strerror(rc));
dropbear_close_fd(&inpipe[1]);
dropbear_close_fd(&outpipe[0]);
dropbear_close_fd(&errpipe[0]);
kill(sess->nsh_pid, SIGTERM);
waitpid(sess->nsh_pid, NULL, 0);
sess->nsh_pid = -1;
goto err_with_attr;
}
sess->waiter_started = true;
sess->has_pty = false;
sess->pty_readfd = -1;
sess->pty_writefd = -1;
channel->readfd = outpipe[0];
channel->writefd = inpipe[1];
channel->errfd = errpipe[0];
channel->bidir_fd = 0;
setnonblocking(channel->readfd);
setnonblocking(channel->writefd);
setnonblocking(channel->errfd);
ses.maxfd = MAX(ses.maxfd, channel->readfd);
ses.maxfd = MAX(ses.maxfd, channel->writefd);
ses.maxfd = MAX(ses.maxfd, channel->errfd);
posix_spawnattr_destroy(&attr);
posix_spawn_file_actions_destroy(&actions);
dropbear_log(LOG_INFO, "NSH exec started: %s", cmd);
return DROPBEAR_SUCCESS;
err_with_attr:
posix_spawnattr_destroy(&attr);
err_with_actions:
posix_spawn_file_actions_destroy(&actions);
err_with_pipes:
dropbear_close_fd(&inpipe[0]);
dropbear_close_fd(&inpipe[1]);
dropbear_close_fd(&outpipe[0]);
dropbear_close_fd(&outpipe[1]);
dropbear_close_fd(&errpipe[0]);
dropbear_close_fd(&errpipe[1]);
sess->nsh_pid = -1;
return DROPBEAR_FAILURE;
}
#endif /* CONFIG_NETUTILS_DROPBEAR_SCP */
static void dropbear_parse_winsize(FAR struct dropbear_nshsession_s *sess)
{
unsigned int cols = buf_getint(ses.payload);
@ -445,7 +659,7 @@ static int dropbear_write_terminal_signal(
unsigned char ch;
ssize_t nwritten;
if (signo == SIGINT && sess->pty_writefd >= 0)
if (signo == SIGINT && sess->has_pty && sess->pty_writefd >= 0)
{
ch = CONFIG_TTY_SIGINT_CHAR;
nwritten = write(sess->pty_writefd, &ch, sizeof(ch));
@ -528,7 +742,16 @@ static void dropbear_chansessionrequest(FAR struct dropbear_channel *channel)
}
else if (strcmp(type, "exec") == 0)
{
#ifdef CONFIG_NETUTILS_DROPBEAR_SCP
unsigned int cmdlen;
FAR char *cmd;
cmd = buf_getstring(ses.payload, &cmdlen);
ret = dropbear_start_exec(channel, sess, cmd);
m_free(cmd);
#else
dropbear_log(LOG_WARNING, "SSH exec requests are not supported");
#endif
}
else if (strcmp(type, "window-change") == 0)
{

View file

@ -0,0 +1,49 @@
/****************************************************************************
* apps/netutils/dropbear/port/nuttx_scp.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 <nuttx/config.h>
#include "nuttx_scp.h"
#include <errno.h>
#include <sys/types.h>
/****************************************************************************
* Public Functions
****************************************************************************/
/* scp only runs in server mode (-t/-f) on NuttX; execvp would only be
* reached in client mode to spawn a local ssh, which is not supported.
*/
int dropbear_scp_execvp(FAR const char *file, FAR char * const argv[])
{
(void)file;
(void)argv;
set_errno(ENOSYS);
return ERROR;
}

View file

@ -0,0 +1,39 @@
/****************************************************************************
* apps/netutils/dropbear/port/nuttx_scp.h
*
* 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.
*
****************************************************************************/
#ifndef __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_SCP_H
#define __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_SCP_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
int dropbear_scp_execvp(FAR const char *file, FAR char * const argv[]);
#endif /* __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_SCP_H */