dropbear: backport security fixes
Some checks are pending
Build all core packages / Build all core packages for selected target (push) Waiting to run

This fixes the following security problems:
CVE-2025-14282: Avoid privilege escalation via unix stream forwarding in Dropbear server.
CVE-2019-6111: This allowed a malicious server to overwrite arbitrary local files.

This backports two upstream merged PRs:
https://github.com/mkj/dropbear/pull/391
https://github.com/mkj/dropbear/pull/394
and this upstream commit:
48a17cff6a

Link: https://github.com/openwrt/openwrt/pull/21192
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
Hauke Mehrtens 2025-12-17 00:41:30 +01:00
parent 58a0211f82
commit 0f52a05723
8 changed files with 880 additions and 4 deletions

View file

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=dropbear
PKG_VERSION:=2024.86
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL:= \

View file

@ -0,0 +1,262 @@
From 61cfbc66aefcb047534210713f3aac097100e5f5 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 9 Dec 2025 15:08:06 +0900
Subject: Drop privileges after user authentication
Instead of switching user privileges after forking to a shell, switch
to the user immediately upon successful authentication.
This will require further commits to fix utmp and hostkey handling.
The DROPBEAR_SVR_DROP_PRIVS configuration option controls this
behaviour. This should generally be enabled, but can be set to 0 for
incompatible platforms. In future it may become non-optional, those
platforms should be investigated.
Most uses of DROPBEAR_SVR_MULTIUSER have been replaced by
!DROPBEAR_SVR_DROP_PRIVS.
(cherry picked from commit e0251be2354e1a5c6eccfc2cf4b64243625dafcc)
---
.github/workflows/build.yml | 2 ++
src/auth.h | 1 +
src/default_options.h | 6 +++++
src/svr-agentfwd.c | 14 ++++++++----
src/svr-auth.c | 45 +++++++++++++++++++++++++++++++++++++
src/svr-authpubkey.c | 6 +++--
src/svr-chansession.c | 26 ++-------------------
src/sysoptions.h | 3 +++
8 files changed, 73 insertions(+), 30 deletions(-)
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -217,6 +217,8 @@ jobs:
echo "#define DROPBEAR_SVR_PASSWORD_AUTH 0" >> localoptions.h
# 1 second timeout is too short
sed -i "s/DEFAULT_IDLE_TIMEOUT 1/DEFAULT_IDLE_TIMEOUT 99/" localoptions.h
+ # DROPBEAR_SVR_DROP_PRIVS is on by default, turn it off
+ echo "#define DROPBEAR_SVR_DROP_PRIVS 0" >> localoptions.h
- name: make
run: |
--- a/src/auth.h
+++ b/src/auth.h
@@ -40,6 +40,7 @@ void send_msg_userauth_banner(const buff
void svr_auth_password(int valid_user);
void svr_auth_pubkey(int valid_user);
void svr_auth_pam(int valid_user);
+void svr_switch_user(void);
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
int svr_pubkey_allows_agentfwd(void);
--- a/src/default_options.h
+++ b/src/default_options.h
@@ -297,6 +297,12 @@ group1 in Dropbear server too */
/* -T server option overrides */
#define MAX_AUTH_TRIES 10
+/* Change server process to user privileges after authentication. */
+#ifndef DROPBEAR_SVR_DROP_PRIVS
+/* Default is enabled. Should only be disabled if platforms are incompatible */
+#define DROPBEAR_SVR_DROP_PRIVS DROPBEAR_SVR_MULTIUSER
+#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
--- a/src/svr-agentfwd.c
+++ b/src/svr-agentfwd.c
@@ -151,7 +151,7 @@ void svr_agentcleanup(struct ChanSess *
if (chansess->agentfile != NULL && chansess->agentdir != NULL) {
-#if DROPBEAR_SVR_MULTIUSER
+#if !DROPBEAR_SVR_DROP_PRIVS
/* Remove the dir as the user. That way they can't cause problems except
* for themselves */
uid = getuid();
@@ -160,6 +160,9 @@ void svr_agentcleanup(struct ChanSess *
(seteuid(ses.authstate.pw_uid)) < 0) {
dropbear_exit("Failed to set euid");
}
+#else
+ (void)uid;
+ (void)gid;
#endif
/* 2 for "/" and "\0" */
@@ -172,7 +175,7 @@ void svr_agentcleanup(struct ChanSess *
rmdir(chansess->agentdir);
-#if DROPBEAR_SVR_MULTIUSER
+#if !DROPBEAR_SVR_DROP_PRIVS
if ((seteuid(uid)) < 0 ||
(setegid(gid)) < 0) {
dropbear_exit("Failed to revert euid");
@@ -219,7 +222,7 @@ static int bindagent(int fd, struct Chan
gid_t gid;
int ret = DROPBEAR_FAILURE;
-#if DROPBEAR_SVR_MULTIUSER
+#if !DROPBEAR_SVR_DROP_PRIVS
/* drop to user privs to make the dir/file */
uid = getuid();
gid = getgid();
@@ -227,6 +230,9 @@ static int bindagent(int fd, struct Chan
(seteuid(ses.authstate.pw_uid)) < 0) {
dropbear_exit("Failed to set euid");
}
+#else
+ (void)uid;
+ (void)gid;
#endif
memset((void*)&addr, 0x0, sizeof(addr));
@@ -267,7 +273,7 @@ bindsocket:
out:
-#if DROPBEAR_SVR_MULTIUSER
+#if !DROPBEAR_SVR_DROP_PRIVS
if ((seteuid(uid)) < 0 ||
(setegid(gid)) < 0) {
dropbear_exit("Failed to revert euid");
--- a/src/svr-auth.c
+++ b/src/svr-auth.c
@@ -456,12 +456,22 @@ void send_msg_userauth_success() {
/* authdone must be set after encrypt_packet() for
* delayed-zlib mode */
ses.authstate.authdone = 1;
+
+#if DROPBEAR_DROP_PRIVS
+ svr_switch_user();
+#endif
ses.connect_time = 0;
+#if DROPBEAR_DROP_PRIVS
+ /* If running as the user, we can rely on the OS
+ * to limit allowed ports */
+ ses.allowprivport = 1;
+#else
if (ses.authstate.pw_uid == 0) {
ses.allowprivport = 1;
}
+#endif
/* Remove from the list of pre-auth sockets. Should be m_close(), since if
* we fail, we might end up leaking connection slots, and disallow new
@@ -471,3 +481,38 @@ void send_msg_userauth_success() {
TRACE(("leave send_msg_userauth_success"))
}
+
+/* Switch to the ses.authstate user.
+ * Fails if not running as root and the user differs.
+ *
+ * This may be called either after authentication, or
+ * after shell/command fork if DROPBEAR_SVR_DROP_PRIVS is unset.
+ */
+void svr_switch_user(void) {
+ assert(ses.authstate.authdone);
+
+ /* We can only change uid/gid as root ... */
+ if (getuid() == 0) {
+
+ if ((setgid(ses.authstate.pw_gid) < 0) ||
+ (initgroups(ses.authstate.pw_name,
+ ses.authstate.pw_gid) < 0)) {
+ dropbear_exit("Error changing user group");
+ }
+ if (setuid(ses.authstate.pw_uid) < 0) {
+ dropbear_exit("Error changing user");
+ }
+ } else {
+ /* ... but if the daemon is the same uid as the requested uid, we don't
+ * need to */
+
+ /* XXX - there is a minor issue here, in that if there are multiple
+ * usernames with the same uid, but differing groups, then the
+ * differing groups won't be set (as with initgroups()). The solution
+ * is for the sysadmin not to give out the UID twice */
+ if (getuid() != ses.authstate.pw_uid) {
+ dropbear_exit("Couldn't change user as non-root");
+ }
+ }
+}
+
--- a/src/svr-authpubkey.c
+++ b/src/svr-authpubkey.c
@@ -444,12 +444,14 @@ static int checkpubkey(const char* keyal
buffer * line = NULL;
unsigned int len;
int line_num;
+#if !DROPBEAR_SVR_DROP_PRIVS
uid_t origuid;
gid_t origgid;
+#endif
TRACE(("enter checkpubkey"))
-#if DROPBEAR_SVR_MULTIUSER
+#if !DROPBEAR_SVR_DROP_PRIVS
/* access the file as the authenticating user. */
origuid = getuid();
origgid = getgid();
@@ -476,7 +478,7 @@ static int checkpubkey(const char* keyal
TRACE(("checkpubkey: failed opening %s: %s", filename, strerror(errno)))
}
}
-#if DROPBEAR_SVR_MULTIUSER
+#if !DROPBEAR_SVR_DROP_PRIVS
if ((seteuid(origuid)) < 0 ||
(setegid(origgid)) < 0) {
dropbear_exit("Failed to revert euid");
--- a/src/svr-chansession.c
+++ b/src/svr-chansession.c
@@ -980,30 +980,8 @@ static void execchild(const void *user_d
#endif /* DEBUG_VALGRIND */
}
-#if DROPBEAR_SVR_MULTIUSER
- /* We can only change uid/gid as root ... */
- if (getuid() == 0) {
-
- if ((setgid(ses.authstate.pw_gid) < 0) ||
- (initgroups(ses.authstate.pw_name,
- ses.authstate.pw_gid) < 0)) {
- dropbear_exit("Error changing user group");
- }
- if (setuid(ses.authstate.pw_uid) < 0) {
- dropbear_exit("Error changing user");
- }
- } else {
- /* ... but if the daemon is the same uid as the requested uid, we don't
- * need to */
-
- /* XXX - there is a minor issue here, in that if there are multiple
- * usernames with the same uid, but differing groups, then the
- * differing groups won't be set (as with initgroups()). The solution
- * is for the sysadmin not to give out the UID twice */
- if (getuid() != ses.authstate.pw_uid) {
- dropbear_exit("Couldn't change user as non-root");
- }
- }
+#if !DROPBEAR_SVR_DROP_PRIVS
+ svr_switch_user();
#endif
/* set env vars */
--- a/src/sysoptions.h
+++ b/src/sysoptions.h
@@ -403,6 +403,9 @@
#define DROPBEAR_MULTI 0
#endif
+#if !DROPBEAR_SVR_MULTIUSER && DROPBEAR_SVR_DROP_PRIVS
+#error DROPBEAR_SVR_DROP_PRIVS needs DROPBEAR_SVR_MULTIUSER
+#endif
/* Fuzzing expects all key types to be enabled */
#if DROPBEAR_FUZZ
#if defined(DROPBEAR_DSS)

View file

@ -0,0 +1,91 @@
From febe8493782965b05025b016091a8bffa5d03563 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 9 Dec 2025 09:04:04 +0900
Subject: Remove return code from login_login
Previously this was always 0, so not useful.
(cherry picked from commit b47fe5df58f0b459bb49accdd8cb961d969209fb)
---
src/loginrec.c | 19 +++++--------------
src/loginrec.h | 6 +++---
2 files changed, 8 insertions(+), 17 deletions(-)
--- a/src/loginrec.c
+++ b/src/loginrec.c
@@ -193,32 +193,24 @@ int wtmpx_get_entry(struct logininfo *li
*
* Call with a pointer to a struct logininfo initialised with
* login_init_entry() or login_alloc_entry()
- *
- * Returns:
- * >0 if successful
- * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
*/
-int
+void
login_login (struct logininfo *li)
{
li->type = LTYPE_LOGIN;
- return login_write(li);
+ login_write(li);
}
/* login_logout(struct logininfo *) - Record a logout
*
* Call as with login_login()
- *
- * Returns:
- * >0 if successful
- * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
*/
-int
+void
login_logout(struct logininfo *li)
{
li->type = LTYPE_LOGOUT;
- return login_write(li);
+ login_write(li);
}
@@ -309,7 +301,7 @@ login_set_current_time(struct logininfo
** login_write: Call low-level recording functions based on autoconf
** results
**/
-int
+void
login_write (struct logininfo *li)
{
#ifndef HAVE_CYGWIN
@@ -340,7 +332,6 @@ login_write (struct logininfo *li)
#ifdef USE_WTMPX
wtmpx_write_entry(li);
#endif
- return 0;
}
#ifdef LOGIN_NEEDS_UTMPX
--- a/src/loginrec.h
+++ b/src/loginrec.h
@@ -161,8 +161,8 @@ int login_init_entry(struct logininfo *l
void login_set_current_time(struct logininfo *li);
/* record the entry */
-int login_login (struct logininfo *li);
-int login_logout(struct logininfo *li);
+void login_login (struct logininfo *li);
+void login_logout(struct logininfo *li);
#ifdef LOGIN_NEEDS_UTMPX
int login_utmp_only(struct logininfo *li);
#endif
@@ -170,7 +170,7 @@ int login_utmp_only(struct logininfo *li
/** End of public functions */
/* record the entry */
-int login_write (struct logininfo *li);
+void login_write (struct logininfo *li);
int login_log_entry(struct logininfo *li);
/* produce various forms of the line filename */

View file

@ -0,0 +1,260 @@
From b20e7823a0c6a4480aa473e0a46d3ccab4b9b102 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 9 Dec 2025 09:05:30 +0900
Subject: Retain utmp saved group when dropping privileges
utmp is required to record logout. The saved group
is reset by the OS for the executed user shell.
This requires setresgid() function which is not available on all
platforms. Notable platforms are netbsd and macos. Those platforms will
have to set DROPBEAR_SVR_DROP_PRIVS 0 unless an alternative approach is
found.
(cherry picked from commit 73e4e70ea8e6b890c3918b52bb2e647313a09faa)
---
.github/workflows/build.yml | 6 ++++
configure | 7 +++++
configure.ac | 1 +
src/auth.h | 2 ++
src/config.h.in | 3 ++
src/loginrec.c | 6 ----
src/session.h | 6 ++++
src/svr-auth.c | 61 +++++++++++++++++++++++++++++++++++--
src/svr-chansession.c | 8 +++++
src/sysoptions.h | 4 +++
10 files changed, 96 insertions(+), 8 deletions(-)
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -72,6 +72,9 @@ jobs:
# fails with:
# .../ranlib: file: libtomcrypt.a(cbc_setiv.o) has no symbols
ranlib: ranlib -no_warning_for_no_symbols
+ # macos doesn't have setresgid
+ localoptions: |
+ #define DROPBEAR_SVR_DROP_PRIVS 0
- name: macos 15
os: macos-15
@@ -84,6 +87,9 @@ jobs:
# fails with:
# .../ranlib: file: libtomcrypt.a(cbc_setiv.o) has no symbols
ranlib: ranlib -no_warning_for_no_symbols
+ # macos doesn't have setresgid
+ localoptions: |
+ #define DROPBEAR_SVR_DROP_PRIVS 0
# Check that debug code doesn't bitrot
- name: DEBUG_TRACE
--- a/configure
+++ b/configure
@@ -7597,6 +7597,13 @@ then :
fi
+ac_fn_c_check_func "$LINENO" "setresgid" "ac_cv_func_setresgid"
+if test "x$ac_cv_func_setresgid" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETRESGID 1" >>confdefs.h
+
+fi
+
# POSIX monotonic time
ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime"
--- a/configure.ac
+++ b/configure.ac
@@ -545,6 +545,7 @@ AC_CHECK_FUNCS(utmpname)
AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline )
AC_CHECK_FUNCS(setutxent utmpxname)
AC_CHECK_FUNCS(logout updwtmp logwtmp)
+AC_CHECK_FUNCS(setresgid)
# POSIX monotonic time
AC_CHECK_FUNCS(clock_gettime)
--- a/src/auth.h
+++ b/src/auth.h
@@ -41,6 +41,8 @@ void svr_auth_password(int valid_user);
void svr_auth_pubkey(int valid_user);
void svr_auth_pam(int valid_user);
void svr_switch_user(void);
+void svr_raise_gid_utmp(void);
+void svr_restore_gid(void);
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
int svr_pubkey_allows_agentfwd(void);
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -222,6 +222,9 @@
/* Define to 1 if you have the <security/pam_appl.h> header file. */
#undef HAVE_SECURITY_PAM_APPL_H
+/* Define to 1 if you have the `setresgid' function. */
+#undef HAVE_SETRESGID
+
/* Define to 1 if you have the `setutent' function. */
#undef HAVE_SETUTENT
--- a/src/loginrec.c
+++ b/src/loginrec.c
@@ -304,12 +304,6 @@ login_set_current_time(struct logininfo
void
login_write (struct logininfo *li)
{
-#ifndef HAVE_CYGWIN
- if ((int)geteuid() != 0) {
- return 1;
- }
-#endif
-
/* set the timestamp */
login_set_current_time(li);
#ifdef USE_LOGIN
--- a/src/session.h
+++ b/src/session.h
@@ -271,6 +271,12 @@ struct serversession {
/* The instance created by the plugin_new function */
struct PluginInstance *plugin_instance;
#endif
+
+#if DROPBEAR_SVR_DROP_PRIVS
+ /* Set to 1 when utmp_gid is valid */
+ int have_utmp_gid;
+ gid_t utmp_gid;
+#endif
};
typedef enum {
--- a/src/svr-auth.c
+++ b/src/svr-auth.c
@@ -457,13 +457,14 @@ void send_msg_userauth_success() {
* delayed-zlib mode */
ses.authstate.authdone = 1;
-#if DROPBEAR_DROP_PRIVS
+#if DROPBEAR_SVR_DROP_PRIVS
+ /* Drop privileges as soon as authentication has happened. */
svr_switch_user();
#endif
ses.connect_time = 0;
-#if DROPBEAR_DROP_PRIVS
+#if DROPBEAR_SVR_DROP_PRIVS
/* If running as the user, we can rely on the OS
* to limit allowed ports */
ses.allowprivport = 1;
@@ -482,6 +483,20 @@ void send_msg_userauth_success() {
}
+#if DROPBEAR_SVR_DROP_PRIVS
+/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int utmp_gid(gid_t *ret_gid) {
+ struct group *utmp_gr = getgrnam("utmp");
+ if (!utmp_gr) {
+ TRACE(("No utmp group"));
+ return DROPBEAR_FAILURE;
+ }
+
+ *ret_gid = utmp_gr->gr_gid;
+ return DROPBEAR_SUCCESS;
+}
+#endif
+
/* Switch to the ses.authstate user.
* Fails if not running as root and the user differs.
*
@@ -499,6 +514,25 @@ void svr_switch_user(void) {
ses.authstate.pw_gid) < 0)) {
dropbear_exit("Error changing user group");
}
+
+#if DROPBEAR_SVR_DROP_PRIVS
+ /* Retain utmp saved group so that wtmp/utmp can be written */
+ int ret = utmp_gid(&svr_ses.utmp_gid);
+ if (ret == DROPBEAR_SUCCESS) {
+ /* Set saved gid to utmp so that it can be
+ * restored for login_logout() etc. This saved
+ * group is cleared by the OS on execve() */
+ int rc = setresgid(-1, -1, svr_ses.utmp_gid);
+ if (rc == 0) {
+ svr_ses.have_utmp_gid = 1;
+ } else {
+ /* Will not attempt to switch to utmp gid.
+ * login() etc may fail. */
+ TRACE(("utmp setresgid failed"));
+ }
+ }
+#endif
+
if (setuid(ses.authstate.pw_uid) < 0) {
dropbear_exit("Error changing user");
}
@@ -516,3 +550,26 @@ void svr_switch_user(void) {
}
}
+void svr_raise_gid_utmp(void) {
+#if DROPBEAR_SVR_DROP_PRIVS
+ if (!svr_ses.have_utmp_gid) {
+ return;
+ }
+
+ if (setegid(svr_ses.utmp_gid) != 0) {
+ dropbear_log(LOG_WARNING, "failed setegid");
+ }
+#endif
+}
+
+void svr_restore_gid(void) {
+#if DROPBEAR_SVR_DROP_PRIVS
+ if (!svr_ses.have_utmp_gid) {
+ return;
+ }
+
+ if (setegid(getgid()) != 0) {
+ dropbear_log(LOG_WARNING, "failed setegid");
+ }
+#endif
+}
--- a/src/svr-chansession.c
+++ b/src/svr-chansession.c
@@ -326,7 +326,11 @@ static void cleanupchansess(const struct
if (chansess->tty) {
/* write the utmp/wtmp login record */
li = chansess_login_alloc(chansess);
+
+ svr_raise_gid_utmp();
login_logout(li);
+ svr_restore_gid();
+
login_free_entry(li);
pty_release(chansess->tty);
@@ -847,7 +851,11 @@ static int ptycommand(struct Channel *ch
* terminal used for stdout with the dup2 above, otherwise
* the wtmp login will not be recorded */
li = chansess_login_alloc(chansess);
+
+ svr_raise_gid_utmp();
login_login(li);
+ svr_restore_gid();
+
login_free_entry(li);
/* Can now dup2 stderr. Messages from login_login() have gone
--- a/src/sysoptions.h
+++ b/src/sysoptions.h
@@ -318,6 +318,10 @@
#error "At least one hostkey or public-key algorithm must be enabled; RSA is recommended."
#endif
+#if DROPBEAR_SVR_DROP_PRIVS && !defined(HAVE_SETRESGID)
+ #error "DROPBEAR_SVR_DROP_PRIVS requires setresgid()."
+#endif
+
/* Source for randomness. This must be able to provide hundreds of bytes per SSH
* connection without blocking. */
#ifndef DROPBEAR_URANDOM_DEV

View file

@ -0,0 +1,64 @@
From ae7f99902161237542f2c71089ff68b8f694006b Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 9 Dec 2025 09:08:37 +0900
Subject: Limit rekey to current hostkey type
During rekey dropbear process may be running with user privileges, that
can't write a new hostkey when auto-generating keys.
Only offer the original hostkey when rekeying, also for non-autogenerate
case.
(cherry picked from commit a4043dac4e0e0237255200603672ddb0122017a4)
---
src/runopts.h | 1 +
src/svr-kex.c | 8 ++++++++
src/svr-runopts.c | 11 +++++++++++
3 files changed, 20 insertions(+)
--- a/src/runopts.h
+++ b/src/runopts.h
@@ -66,6 +66,7 @@ extern runopts opts;
int readhostkey(const char * filename, sign_key * hostkey,
enum signkey_type *type);
void load_all_hostkeys(void);
+void disable_sig_except(enum signature_type sig_type);
typedef struct svr_runopts {
--- a/src/svr-kex.c
+++ b/src/svr-kex.c
@@ -96,6 +96,14 @@ void recv_msg_kexdh_init() {
}
#endif
+ if (!ses.kexstate.donesecondkex) {
+ /* Disable other signature types.
+ * During future rekeying, privileges may have been dropped
+ * so other keys won't be loadable.
+ * This must occur after send_msg_ext_info() which uses the hostkey list */
+ disable_sig_except(ses.newkeys->algo_signature);
+ }
+
ses.requirenext = SSH_MSG_NEWKEYS;
TRACE(("leave recv_msg_kexdh_init"))
}
--- a/src/svr-runopts.c
+++ b/src/svr-runopts.c
@@ -502,6 +502,17 @@ static void disablekey(enum signature_ty
}
}
+void disable_sig_except(enum signature_type allow_type) {
+ int i;
+ TRACE(("Disabling other sigs except %d", allow_type));
+ for (i = 0; sigalgs[i].name != NULL; i++) {
+ enum signature_type sig_type = sigalgs[i].val;
+ if (sig_type != allow_type) {
+ sigalgs[i].usable = 0;
+ }
+ }
+}
+
static void loadhostkey_helper(const char *name, void** src, void** dst, int fatal_duplicate) {
if (*dst) {
if (fatal_duplicate) {

View file

@ -0,0 +1,41 @@
From 3e487854fcf09a5ef57a620ebe606f586a6efdc3 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Fri, 12 Dec 2025 12:31:40 +0900
Subject: Restore seteuid for authorized_keys
Authorized_keys reading is pre-authentication so should not be
modified in the post-auth drop-privilege change.
Fixes: e0251be2354e ("Drop privileges after user authentication")
(cherry picked from commit d193731630a62482855b450daa1d5a5e13a90125)
---
src/svr-authpubkey.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
--- a/src/svr-authpubkey.c
+++ b/src/svr-authpubkey.c
@@ -444,14 +444,12 @@ static int checkpubkey(const char* keyal
buffer * line = NULL;
unsigned int len;
int line_num;
-#if !DROPBEAR_SVR_DROP_PRIVS
uid_t origuid;
gid_t origgid;
-#endif
TRACE(("enter checkpubkey"))
-#if !DROPBEAR_SVR_DROP_PRIVS
+#if DROPBEAR_SVR_MULTIUSER
/* access the file as the authenticating user. */
origuid = getuid();
origgid = getgid();
@@ -478,7 +476,7 @@ static int checkpubkey(const char* keyal
TRACE(("checkpubkey: failed opening %s: %s", filename, strerror(errno)))
}
}
-#if !DROPBEAR_SVR_DROP_PRIVS
+#if DROPBEAR_SVR_MULTIUSER
if ((seteuid(origuid)) < 0 ||
(setegid(origgid)) < 0) {
dropbear_exit("Failed to revert euid");

View file

@ -0,0 +1,153 @@
From 5938a084b0aad887c0f7fa62bf3644d029e4d47d Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 9 Dec 2025 22:59:19 +0900
Subject: scp CVE-2019-6111 fix
Cherry-pick from OpenSSH portable
391ffc4b9d31 ("upstream: check in scp client that filenames sent during")
upstream: check in scp client that filenames sent during
remote->local directory copies satisfy the wildcard specified by the user.
This checking provides some protection against a malicious server
sending unexpected filenames, but it comes at a risk of rejecting wanted
files due to differences between client and server wildcard expansion rules.
For this reason, this also adds a new -T flag to disable the check.
reported by Harry Sintonen
fix approach suggested by markus@;
has been in snaps for ~1wk courtesy deraadt@
(cherry picked from commit 48a17cff6aa104b8e806ddb2191f83f1024060f1)
---
src/scp.c | 38 +++++++++++++++++++++++++++++---------
1 file changed, 29 insertions(+), 9 deletions(-)
--- a/src/scp.c
+++ b/src/scp.c
@@ -76,6 +76,8 @@
#include "includes.h"
/*RCSID("$OpenBSD: scp.c,v 1.130 2006/01/31 10:35:43 djm Exp $");*/
+#include <fnmatch.h>
+
#include "atomicio.h"
#include "compat.h"
#include "scpmisc.h"
@@ -291,14 +293,14 @@ void verifydir(char *);
uid_t userid;
int errs, remin, remout;
-int pflag, iamremote, iamrecursive, targetshouldbedirectory;
+int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
#define CMDNEEDS 64
char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
int response(void);
void rsource(char *, struct stat *);
-void sink(int, char *[]);
+void sink(int, char *[], const char *);
void source(int, char *[]);
void tolocal(int, char *[]);
void toremote(char *, int, char *[]);
@@ -325,8 +327,8 @@ main(int argc, char **argv)
args.list = NULL;
addargs(&args, "%s", ssh_program);
- fflag = tflag = 0;
- while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1)
+ fflag = Tflag = tflag = 0;
+ while ((ch = getopt(argc, argv, "dfl:prtTvBCc:i:P:q1246S:o:F:")) != -1)
switch (ch) {
/* User-visible flags. */
case '1':
@@ -389,9 +391,12 @@ main(int argc, char **argv)
setmode(0, O_BINARY);
#endif
break;
+ case 'T':
+ Tflag = 1;
+ break;
default:
usage();
- }
+ }
argc -= optind;
argv += optind;
@@ -409,7 +414,7 @@ main(int argc, char **argv)
}
if (tflag) {
/* Receive data. */
- sink(argc, argv);
+ sink(argc, argv, NULL);
exit(errs != 0);
}
if (argc < 2)
@@ -589,7 +594,7 @@ tolocal(int argc, char **argv)
continue;
}
xfree(bp);
- sink(1, argv + argc - 1);
+ sink(1, argv + argc - 1, src);
(void) close(remin);
remin = remout = -1;
}
@@ -822,7 +827,7 @@ bwlimit(int amount)
}
void
-sink(int argc, char **argv)
+sink(int argc, char **argv, const char *src)
{
static BUF buffer;
struct stat stb;
@@ -836,6 +841,7 @@ sink(int argc, char **argv)
off_t size, statbytes;
int setimes, targisdir, wrerrno = 0;
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
+ char *src_copy = NULL, *restrict_pattern = NULL;
struct timeval tv[2];
#define atime tv[0]
@@ -857,6 +863,17 @@ sink(int argc, char **argv)
(void) atomicio(vwrite, remout, "", 1);
if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
targisdir = 1;
+ if (src != NULL && !iamrecursive && !Tflag) {
+ /*
+ * Prepare to try to restrict incoming filenames to match
+ * the requested destination file glob.
+ */
+ if ((src_copy = strdup(src)) == NULL)
+ fatal("strdup failed");
+ if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) {
+ *restrict_pattern++ = '\0';
+ }
+ }
for (first = 1;; first = 0) {
cp = buf;
if (atomicio(read, remin, cp, 1) != 1)
@@ -939,6 +956,9 @@ sink(int argc, char **argv)
run_err("error: unexpected filename: %s", cp);
exit(1);
}
+ if (restrict_pattern != NULL &&
+ fnmatch(restrict_pattern, cp, 0) != 0)
+ SCREWUP("filename does not match request");
if (targisdir) {
static char *namebuf = NULL;
static size_t cursize = 0;
@@ -977,7 +997,7 @@ sink(int argc, char **argv)
goto bad;
}
vect[0] = xstrdup(np);
- sink(1, vect);
+ sink(1, vect, src);
if (setimes) {
setimes = 0;
if (utimes(vect[0], tv) < 0)

View file

@ -1,6 +1,6 @@
--- a/src/svr-chansession.c
+++ b/src/svr-chansession.c
@@ -984,12 +984,12 @@ static void execchild(const void *user_d
--- a/src/svr-auth.c
+++ b/src/svr-auth.c
@@ -509,9 +509,9 @@ void svr_switch_user(void) {
/* We can only change uid/gid as root ... */
if (getuid() == 0) {
@ -11,6 +11,11 @@
+ ses.authstate.pw_gid) < 0))) {
dropbear_exit("Error changing user group");
}
@@ -533,7 +533,7 @@ void svr_switch_user(void) {
}
#endif
- if (setuid(ses.authstate.pw_uid) < 0) {
+ if ((ses.authstate.pw_uid != 0) && (setuid(ses.authstate.pw_uid) < 0)) {
dropbear_exit("Error changing user");