mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-01-28 02:27:18 +01:00
dropbear: backport security fixes
Some checks are pending
Build all core packages / Build all core packages for selected target (push) Waiting to run
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:
parent
58a0211f82
commit
0f52a05723
8 changed files with 880 additions and 4 deletions
|
|
@ -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:= \
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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");
|
||||
|
|
@ -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)
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue