dropbear: backport some security fixes from 2026.90
This cherry picks the following changes:
- Security: server: Fix ability to bypass an authorized_keys "forced_command"
option by an authenticated user, if Dropbear is running with "-t" option.
("-t" is require both password and public key).
c60db64081
Reported by Jeremy Brown
- Security: server: Open authorized_keys non-blocking. This avoids getting stuck
with special files, could allow denial of service from local users.
8b579571b5
Reported by Turistu
- Security: scp: Add missed patch to fix CVE-2019-6111 allowing a malicious server
to overwrite unexpected files.
143291baca
Patch from OpenSSH, reported missing by Asim Viladi Oglu Manizada @manizada
Note breaking change: "-r" is now disallowed when the target directory exists
(an additional change in Dropbear's version). If that's required an alternative
such as rsync could be used.
61bd0e3e65
- Security: scp: Clear setuid/setgid bits on received files.
443ccc1fd1
Patch from OpenSSH, tracked as CVE-2026-35385
- Security: client/server: Fix close() of a file descriptor from an out-of-bounds
read. This seems difficult to exploit but may have unforeseen effects.
067fd3846c
Reported by Ankit Singh and @j499261162
Link: https://github.com/openwrt/openwrt/pull/23980
(cherry picked from commit 2218cb19494609076776ae8c13254de60c5f40f3)
Link: https://github.com/openwrt/openwrt/pull/24296
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
(cherry picked from commit 262c09368fda76c85c519b7554f33bb98fb3263f)
This commit is contained in:
parent
9287454eee
commit
f75c726430
@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dropbear
|
||||
PKG_VERSION:=2024.86
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
|
||||
PKG_SOURCE_URL:= \
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
From 91f4832c0a90fc2c2236465e2937dfbfea03c86d Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Wed, 8 Apr 2026 20:51:47 +0800
|
||||
Subject: [PATCH 1/6] Fix incorrect index closing listeners
|
||||
|
||||
This could read beyond socks[] and close an arbitrary descriptor.
|
||||
The codepath runs when creating TCP listeners for forwarding (client or
|
||||
server).
|
||||
|
||||
(cherry picked from commit 067fd3846c425d1998c7c94d3feafe1f26b514eb)
|
||||
---
|
||||
src/listener.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/src/listener.c
|
||||
+++ b/src/listener.c
|
||||
@@ -95,7 +95,7 @@ struct Listener* new_listener(const int
|
||||
if (ses.listensize > MAX_LISTENERS) {
|
||||
TRACE(("leave newlistener: too many already"))
|
||||
for (j = 0; j < nsocks; j++) {
|
||||
- close(socks[i]);
|
||||
+ close(socks[j]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
From a89c4ce57a0e2d9c06c1ec08f1ac0ea1e0133c0c Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Sat, 2 May 2026 08:17:25 +0800
|
||||
Subject: [PATCH 2/6] scp: clear setuid/setgid bits on received files
|
||||
|
||||
From openssh-portable 487e8ac146f7d6616f65c125d5edb210519b833a
|
||||
Tracked as CVE-2026-35385
|
||||
|
||||
upstream: when downloading files as root in legacy (-O) mode and
|
||||
|
||||
without the -p (preserve modes) flag set, clear setuid/setgid bits from
|
||||
downloaded files as one might expect.
|
||||
|
||||
AFAIK this bug dates back to the original Berkeley rcp program.
|
||||
|
||||
Reported by Christos Papakonstantinou of Cantina and Spearbit.
|
||||
|
||||
OpenBSD-Commit-ID: 49e902fca8dd933a92a9b547ab31f63e86729fa1
|
||||
(cherry picked from commit 443ccc1fd1b9a4e3747f6b5cd644b7af49064308)
|
||||
---
|
||||
src/scp.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/scp.c
|
||||
+++ b/src/scp.c
|
||||
@@ -850,8 +850,10 @@ sink(int argc, char **argv, const char *
|
||||
|
||||
setimes = targisdir = 0;
|
||||
mask = umask(0);
|
||||
- if (!pflag)
|
||||
+ if (!pflag) {
|
||||
+ mask |= 07000;
|
||||
(void) umask(mask);
|
||||
+ }
|
||||
if (argc != 1) {
|
||||
run_err("ambiguous target");
|
||||
exit(1);
|
||||
@ -0,0 +1,39 @@
|
||||
From d05b4fc2f7130630b8715d2b7244f090a3e679a5 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Sat, 2 May 2026 08:43:26 +0800
|
||||
Subject: [PATCH 3/6] scp: Disallow -r to an existing directory
|
||||
|
||||
Checks of received paths don't catch all cases in -r recursive mode, so
|
||||
disallow -r when the destination directory exists. This mitigates
|
||||
CVE-2019-6111, where unexpected files would be accepted from a malicious
|
||||
scp server.
|
||||
|
||||
Alternatives like rsync could be used in that case.
|
||||
|
||||
The new error message may be misleading if parent path components of the
|
||||
destination are also missing, though the command would have failed
|
||||
regardless.
|
||||
|
||||
(cherry picked from commit 61bd0e3e6573277de17359763aa5827a189c8d7f)
|
||||
---
|
||||
src/scp.c | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
--- a/src/scp.c
|
||||
+++ b/src/scp.c
|
||||
@@ -584,6 +584,15 @@ tolocal(int argc, char **argv)
|
||||
if (*suser == '\0')
|
||||
suser = NULL;
|
||||
}
|
||||
+
|
||||
+ char *dest = *(argv + argc - 1);
|
||||
+ if (!(access(dest, F_OK) == 0 && errno == ENOENT)) {
|
||||
+ /* paths sent from the server in recursive mode aren't adequately
|
||||
+ * checked that they match the requested files, so disallow this */
|
||||
+ fprintf(stderr, "-r destination \"%s\" must not already exist\n", dest);
|
||||
+ exit(1);
|
||||
+ }
|
||||
+
|
||||
host = cleanhostname(host);
|
||||
len = strlen(src) + CMDNEEDS + 20;
|
||||
bp = xmalloc(len);
|
||||
@ -0,0 +1,360 @@
|
||||
From 7f15dd66d850a0a785b31c5e6ca1947e431fe4f9 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Mon, 20 Apr 2026 20:57:11 +0800
|
||||
Subject: [PATCH 4/6] scp: more CVE-2019-6111 fix
|
||||
|
||||
Cherrypicked from OpenSSH portable
|
||||
|
||||
commit 3d896c157c722bc47adca51a58dca859225b5874
|
||||
djm Sun Feb 10 11:15:52 2019 +0000
|
||||
|
||||
upstream: when checking that filenames sent by the server side
|
||||
|
||||
match what the client requested, be prepared to handle shell-style brace
|
||||
alternations, e.g. "{foo,bar}".
|
||||
|
||||
"looks good to me" millert@ + in snaps for the last week courtesy
|
||||
deraadt@
|
||||
|
||||
(cherry picked from commit 143291baca6ebb3407a0c25c85bd53bf041a6ab7)
|
||||
---
|
||||
src/scp.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++---
|
||||
1 file changed, 269 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/src/scp.c
|
||||
+++ b/src/scp.c
|
||||
@@ -74,7 +74,6 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
-/*RCSID("$OpenBSD: scp.c,v 1.130 2006/01/31 10:35:43 djm Exp $");*/
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
@@ -609,6 +608,253 @@ tolocal(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
+/* Appends a string to an array; returns 0 on success, -1 on alloc failure */
|
||||
+static int
|
||||
+append(char *cp, char ***ap, size_t *np)
|
||||
+{
|
||||
+ char **tmp;
|
||||
+
|
||||
+ if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL)
|
||||
+ return -1;
|
||||
+ tmp[(*np)] = cp;
|
||||
+ (*np)++;
|
||||
+ *ap = tmp;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Finds the start and end of the first brace pair in the pattern.
|
||||
+ * returns 0 on success or -1 for invalid patterns.
|
||||
+ */
|
||||
+static int
|
||||
+find_brace(const char *pattern, int *startp, int *endp)
|
||||
+{
|
||||
+ int i;
|
||||
+ int in_bracket, brace_level;
|
||||
+
|
||||
+ *startp = *endp = -1;
|
||||
+ in_bracket = brace_level = 0;
|
||||
+ for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
|
||||
+ switch (pattern[i]) {
|
||||
+ case '\\':
|
||||
+ /* skip next character */
|
||||
+ if (pattern[i + 1] != '\0')
|
||||
+ i++;
|
||||
+ break;
|
||||
+ case '[':
|
||||
+ in_bracket = 1;
|
||||
+ break;
|
||||
+ case ']':
|
||||
+ in_bracket = 0;
|
||||
+ break;
|
||||
+ case '{':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ if (pattern[i + 1] == '}') {
|
||||
+ /* Protect a single {}, for find(1), like csh */
|
||||
+ i++; /* skip */
|
||||
+ break;
|
||||
+ }
|
||||
+ if (*startp == -1)
|
||||
+ *startp = i;
|
||||
+ brace_level++;
|
||||
+ break;
|
||||
+ case '}':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ if (*startp < 0) {
|
||||
+ /* Unbalanced brace */
|
||||
+ return -1;
|
||||
+ }
|
||||
+ if (--brace_level <= 0)
|
||||
+ *endp = i;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ /* unbalanced brackets/braces */
|
||||
+ if (*endp < 0 && (*startp >= 0 || in_bracket))
|
||||
+ return -1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Assembles and records a successfully-expanded pattern, returns -1 on
|
||||
+ * alloc failure.
|
||||
+ */
|
||||
+static int
|
||||
+emit_expansion(const char *pattern, int brace_start, int brace_end,
|
||||
+ int sel_start, int sel_end, char ***patternsp, size_t *npatternsp)
|
||||
+{
|
||||
+ char *cp;
|
||||
+ int o = 0, tail_len = strlen(pattern + brace_end + 1);
|
||||
+
|
||||
+ if ((cp = malloc(brace_start + (sel_end - sel_start) +
|
||||
+ tail_len + 1)) == NULL)
|
||||
+ return -1;
|
||||
+
|
||||
+ /* Pattern before initial brace */
|
||||
+ if (brace_start > 0) {
|
||||
+ memcpy(cp, pattern, brace_start);
|
||||
+ o = brace_start;
|
||||
+ }
|
||||
+ /* Current braced selection */
|
||||
+ if (sel_end - sel_start > 0) {
|
||||
+ memcpy(cp + o, pattern + sel_start,
|
||||
+ sel_end - sel_start);
|
||||
+ o += sel_end - sel_start;
|
||||
+ }
|
||||
+ /* Remainder of pattern after closing brace */
|
||||
+ if (tail_len > 0) {
|
||||
+ memcpy(cp + o, pattern + brace_end + 1, tail_len);
|
||||
+ o += tail_len;
|
||||
+ }
|
||||
+ cp[o] = '\0';
|
||||
+ if (append(cp, patternsp, npatternsp) != 0) {
|
||||
+ free(cp);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Expand the first encountered brace in pattern, appending the expanded
|
||||
+ * patterns it yielded to the *patternsp array.
|
||||
+ *
|
||||
+ * Returns 0 on success or -1 on allocation failure.
|
||||
+ *
|
||||
+ * Signals whether expansion was performed via *expanded and whether
|
||||
+ * pattern was invalid via *invalid.
|
||||
+ */
|
||||
+static int
|
||||
+brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
|
||||
+ int *expanded, int *invalid)
|
||||
+{
|
||||
+ int i;
|
||||
+ int in_bracket, brace_start, brace_end, brace_level;
|
||||
+ int sel_start, sel_end;
|
||||
+
|
||||
+ *invalid = *expanded = 0;
|
||||
+
|
||||
+ if (find_brace(pattern, &brace_start, &brace_end) != 0) {
|
||||
+ *invalid = 1;
|
||||
+ return 0;
|
||||
+ } else if (brace_start == -1)
|
||||
+ return 0;
|
||||
+
|
||||
+ in_bracket = brace_level = 0;
|
||||
+ for (i = sel_start = brace_start + 1; i < brace_end; i++) {
|
||||
+ switch (pattern[i]) {
|
||||
+ case '{':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ brace_level++;
|
||||
+ break;
|
||||
+ case '}':
|
||||
+ if (in_bracket)
|
||||
+ break;
|
||||
+ brace_level--;
|
||||
+ break;
|
||||
+ case '[':
|
||||
+ in_bracket = 1;
|
||||
+ break;
|
||||
+ case ']':
|
||||
+ in_bracket = 0;
|
||||
+ break;
|
||||
+ case '\\':
|
||||
+ if (i < brace_end - 1)
|
||||
+ i++; /* skip */
|
||||
+ break;
|
||||
+ }
|
||||
+ if (pattern[i] == ',' || i == brace_end - 1) {
|
||||
+ if (in_bracket || brace_level > 0)
|
||||
+ continue;
|
||||
+ /* End of a selection, emit an expanded pattern */
|
||||
+
|
||||
+ /* Adjust end index for last selection */
|
||||
+ sel_end = (i == brace_end - 1) ? brace_end : i;
|
||||
+ if (emit_expansion(pattern, brace_start, brace_end,
|
||||
+ sel_start, sel_end, patternsp, npatternsp) != 0)
|
||||
+ return -1;
|
||||
+ /* move on to the next selection */
|
||||
+ sel_start = i + 1;
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+ if (in_bracket || brace_level > 0) {
|
||||
+ *invalid = 1;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ /* success */
|
||||
+ *expanded = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* Expand braces from pattern. Returns 0 on success, -1 on failure */
|
||||
+static int
|
||||
+brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
|
||||
+{
|
||||
+ char *cp, *cp2, **active = NULL, **done = NULL;
|
||||
+ size_t i, nactive = 0, ndone = 0;
|
||||
+ int ret = -1, invalid = 0, expanded = 0;
|
||||
+
|
||||
+ *patternsp = NULL;
|
||||
+ *npatternsp = 0;
|
||||
+
|
||||
+ /* Start the worklist with the original pattern */
|
||||
+ if ((cp = strdup(pattern)) == NULL)
|
||||
+ return -1;
|
||||
+ if (append(cp, &active, &nactive) != 0) {
|
||||
+ free(cp);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ while (nactive > 0) {
|
||||
+ cp = active[nactive - 1];
|
||||
+ nactive--;
|
||||
+ if (brace_expand_one(cp, &active, &nactive,
|
||||
+ &expanded, &invalid) == -1) {
|
||||
+ free(cp);
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (invalid)
|
||||
+ fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
|
||||
+ if (expanded) {
|
||||
+ /*
|
||||
+ * Current entry expanded to new entries on the
|
||||
+ * active list; discard the progenitor pattern.
|
||||
+ */
|
||||
+ free(cp);
|
||||
+ continue;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Pattern did not expand; append the finename component to
|
||||
+ * the completed list
|
||||
+ */
|
||||
+ if ((cp2 = strrchr(cp, '/')) != NULL)
|
||||
+ *cp2++ = '\0';
|
||||
+ else
|
||||
+ cp2 = cp;
|
||||
+ if (append(xstrdup(cp2), &done, &ndone) != 0) {
|
||||
+ free(cp);
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ free(cp);
|
||||
+ }
|
||||
+ /* success */
|
||||
+ *patternsp = done;
|
||||
+ *npatternsp = ndone;
|
||||
+ done = NULL;
|
||||
+ ndone = 0;
|
||||
+ ret = 0;
|
||||
+ fail:
|
||||
+ for (i = 0; i < nactive; i++)
|
||||
+ free(active[i]);
|
||||
+ free(active);
|
||||
+ for (i = 0; i < ndone; i++)
|
||||
+ free(done[i]);
|
||||
+ free(done);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
void
|
||||
source(int argc, char **argv)
|
||||
{
|
||||
@@ -850,7 +1096,8 @@ sink(int argc, char **argv, const char *
|
||||
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;
|
||||
+ char **patterns = NULL;
|
||||
+ size_t n, npatterns = 0;
|
||||
struct timeval tv[2];
|
||||
|
||||
#define atime tv[0]
|
||||
@@ -879,16 +1126,13 @@ sink(int argc, char **argv, const char *
|
||||
* 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';
|
||||
- }
|
||||
+ if (brace_expand(src, &patterns, &npatterns) != 0)
|
||||
+ fatal("%s: could not expand pattern", __func__);
|
||||
}
|
||||
for (first = 1;; first = 0) {
|
||||
cp = buf;
|
||||
if (atomicio(read, remin, cp, 1) != 1)
|
||||
- return;
|
||||
+ goto done;
|
||||
if (*cp++ == '\n')
|
||||
SCREWUP("unexpected <newline>");
|
||||
do {
|
||||
@@ -911,7 +1155,7 @@ sink(int argc, char **argv, const char *
|
||||
}
|
||||
if (buf[0] == 'E') {
|
||||
(void) atomicio(vwrite, remout, "", 1);
|
||||
- return;
|
||||
+ goto done;
|
||||
}
|
||||
if (ch == '\n')
|
||||
*--cp = 0;
|
||||
@@ -967,9 +1211,14 @@ sink(int argc, char **argv, const char *
|
||||
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 (npatterns > 0) {
|
||||
+ for (n = 0; n < npatterns; n++) {
|
||||
+ if (fnmatch(patterns[n], cp, 0) == 0)
|
||||
+ break;
|
||||
+ }
|
||||
+ if (n >= npatterns)
|
||||
+ SCREWUP("filename does not match request");
|
||||
+ }
|
||||
if (targisdir) {
|
||||
static char *namebuf = NULL;
|
||||
static size_t cursize = 0;
|
||||
@@ -1134,7 +1383,15 @@ bad: run_err("%s: %s", np, strerror(er
|
||||
break;
|
||||
}
|
||||
}
|
||||
+done:
|
||||
+ for (n = 0; n < npatterns; n++)
|
||||
+ free(patterns[n]);
|
||||
+ free(patterns);
|
||||
+ return;
|
||||
screwup:
|
||||
+ for (n = 0; n < npatterns; n++)
|
||||
+ free(patterns[n]);
|
||||
+ free(patterns);
|
||||
run_err("protocol error: %s", why);
|
||||
exit(1);
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
From ff819590e3d945320faae956343d0a57e3525a02 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Fri, 17 Apr 2026 21:24:08 +0800
|
||||
Subject: [PATCH 5/6] server: open authorized_keys files nonblocking
|
||||
|
||||
This avoids getting stuck with special files.
|
||||
|
||||
(cherry picked from commit 8b579571b5584847caf72345b9073b3b2a0467a7)
|
||||
---
|
||||
src/svr-authpubkey.c | 10 +++++++++-
|
||||
1 file changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/src/svr-authpubkey.c
|
||||
+++ b/src/svr-authpubkey.c
|
||||
@@ -462,6 +462,7 @@ static int checkpubkey(const char* keyal
|
||||
if (checkpubkeyperms() == DROPBEAR_FAILURE) {
|
||||
TRACE(("bad authorized_keys permissions, or file doesn't exist"))
|
||||
} else {
|
||||
+ int fd;
|
||||
/* we don't need to check pw and pw_dir for validity, since
|
||||
* its been done in checkpubkeyperms. */
|
||||
len = strlen(ses.authstate.pw_dir);
|
||||
@@ -471,7 +472,14 @@ static int checkpubkey(const char* keyal
|
||||
snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
|
||||
ses.authstate.pw_dir);
|
||||
|
||||
- authfile = fopen(filename, "r");
|
||||
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
+ if (fd >= 0) {
|
||||
+ authfile = fdopen(fd, "r");
|
||||
+ if (!authfile) {
|
||||
+ /* fdopen could fail with ENOMEM */
|
||||
+ m_close(fd);
|
||||
+ }
|
||||
+ }
|
||||
if (!authfile) {
|
||||
TRACE(("checkpubkey: failed opening %s: %s", filename, strerror(errno)))
|
||||
}
|
||||
@ -0,0 +1,531 @@
|
||||
From d48f79c851110eb673d9738a69a15696ee972767 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Johnston <matt@ucc.asn.au>
|
||||
Date: Thu, 26 Mar 2026 23:03:46 +0800
|
||||
Subject: [PATCH 6/6] Rework pubkey_options struct
|
||||
|
||||
Pubkey info is now included in the restriction struct. A temporary
|
||||
pubkey_options is filled then set in the global session after auth.
|
||||
|
||||
(cherry picked from commit c60db6408178aaca0a8aacd017ac344a2cfb30c8)
|
||||
---
|
||||
fuzz/fuzzer-pubkey.c | 7 +---
|
||||
src/auth.h | 28 ++++++++-----
|
||||
src/fuzz.h | 2 +-
|
||||
src/svr-authpubkey.c | 78 ++++++++++++++++++++----------------
|
||||
src/svr-authpubkeyoptions.c | 80 +++++++++++++++++++------------------
|
||||
src/svr-chansession.c | 5 ++-
|
||||
src/svr-session.c | 4 +-
|
||||
src/sysoptions.h | 4 ++
|
||||
8 files changed, 114 insertions(+), 94 deletions(-)
|
||||
|
||||
--- a/fuzz/fuzzer-pubkey.c
|
||||
+++ b/fuzz/fuzzer-pubkey.c
|
||||
@@ -31,15 +31,10 @@ int LLVMFuzzerTestOneInput(const uint8_t
|
||||
dropbear_exit("fuzzer imagined a bogus algorithm");
|
||||
}
|
||||
|
||||
- int ret = fuzz_checkpubkey_line(line, 5, "/home/me/authorized_keys",
|
||||
+ fuzz_checkpubkey_line(line, 5, "/home/me/authorized_keys",
|
||||
algoname, algolen,
|
||||
keyblob->data, keyblob->len);
|
||||
|
||||
- if (ret == DROPBEAR_SUCCESS) {
|
||||
- /* fuzz_checkpubkey_line() should have cleaned up for failure */
|
||||
- svr_pubkey_options_cleanup();
|
||||
- }
|
||||
-
|
||||
buf_free(line);
|
||||
buf_free(keyblob);
|
||||
m_free(algoname);
|
||||
--- a/src/auth.h
|
||||
+++ b/src/auth.h
|
||||
@@ -44,6 +44,7 @@ void svr_switch_user(void);
|
||||
void svr_raise_gid_utmp(void);
|
||||
void svr_restore_gid(void);
|
||||
|
||||
+struct PubKeyOptions;
|
||||
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
int svr_pubkey_allows_agentfwd(void);
|
||||
int svr_pubkey_allows_tcpfwd(void);
|
||||
@@ -51,8 +52,9 @@ int svr_pubkey_allows_x11fwd(void);
|
||||
int svr_pubkey_allows_pty(void);
|
||||
int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port);
|
||||
void svr_pubkey_set_forced_command(struct ChanSess *chansess);
|
||||
-void svr_pubkey_options_cleanup(void);
|
||||
-int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename);
|
||||
+void svr_pubkey_options_cleanup(struct PubKeyOptions *pubkey_options);
|
||||
+struct PubKeyOptions*
|
||||
+svr_parse_pubkey_options(buffer *options_buf, int line_num, const char* filename);
|
||||
#else
|
||||
/* no option : success */
|
||||
#define svr_pubkey_allows_agentfwd() 1
|
||||
@@ -62,9 +64,14 @@ int svr_add_pubkey_options(buffer *optio
|
||||
static inline int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port)
|
||||
{ (void)host; (void)port; return 1; }
|
||||
|
||||
-static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) { }
|
||||
-static inline void svr_pubkey_options_cleanup(void) { }
|
||||
-#define svr_add_pubkey_options(x,y,z) DROPBEAR_SUCCESS
|
||||
+static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
|
||||
+ (void)chansess;
|
||||
+}
|
||||
+static inline void svr_pubkey_options_cleanup(struct PubKeyOptions *pubkey_options) {
|
||||
+ (void)pubkey_options;
|
||||
+}
|
||||
+struct PubKeyOptions*
|
||||
+svr_parse_pubkey_options(buffer *options_buf, int line_num, const char* filename);
|
||||
#endif
|
||||
|
||||
/* Client functions */
|
||||
@@ -132,14 +139,10 @@ struct AuthState {
|
||||
char *pw_shell;
|
||||
char *pw_name;
|
||||
char *pw_passwd;
|
||||
-#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
struct PubKeyOptions* pubkey_options;
|
||||
- char *pubkey_info;
|
||||
-#endif
|
||||
};
|
||||
|
||||
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
-struct PubKeyOptions;
|
||||
struct PubKeyOptions {
|
||||
/* Flags */
|
||||
int no_port_forwarding_flag;
|
||||
@@ -155,6 +158,13 @@ struct PubKeyOptions {
|
||||
int no_touch_required_flag;
|
||||
int verify_required_flag;
|
||||
#endif
|
||||
+
|
||||
+ /* String from the end of authorized_keys entry, for SSH_PUBKEYINFO
|
||||
+ environment variable. May be omitted (NULL) if
|
||||
+ options contain non-shell-safe characters.
|
||||
+ This is not involved in pubkey option restrictions, but is
|
||||
+ parsed and kept similarly so is stored here. */
|
||||
+ char * info_env;
|
||||
};
|
||||
|
||||
struct PermitTCPFwdEntry {
|
||||
--- a/src/fuzz.h
|
||||
+++ b/src/fuzz.h
|
||||
@@ -30,7 +30,7 @@ const void* fuzz_get_algo(const algo_typ
|
||||
|
||||
// fuzzer functions that intrude into general code
|
||||
void fuzz_kex_fakealgos(void);
|
||||
-int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
+void fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen);
|
||||
extern const char * const * fuzz_signkey_names;
|
||||
--- a/src/svr-authpubkey.c
|
||||
+++ b/src/svr-authpubkey.c
|
||||
@@ -72,7 +72,8 @@
|
||||
#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
|
||||
|
||||
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
|
||||
- const unsigned char* keyblob, unsigned int keybloblen);
|
||||
+ const unsigned char* keyblob, unsigned int keybloblen,
|
||||
+ struct PubKeyOptions **ret_options);
|
||||
static int checkpubkeyperms(void);
|
||||
static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen);
|
||||
@@ -95,7 +96,8 @@ void svr_auth_pubkey(int valid_user) {
|
||||
char* fp = NULL;
|
||||
enum signature_type sigtype;
|
||||
enum signkey_type keytype;
|
||||
- int auth_failure = 1;
|
||||
+ int auth_failure = 1;
|
||||
+ struct PubKeyOptions *pubkey_options = NULL;
|
||||
|
||||
TRACE(("enter pubkeyauth"))
|
||||
|
||||
@@ -148,8 +150,8 @@ void svr_auth_pubkey(int valid_user) {
|
||||
.pos = 0,
|
||||
.size = 0
|
||||
};
|
||||
- int ret = svr_add_pubkey_options(&temp_buf, 0, "N/A");
|
||||
- if (ret == DROPBEAR_FAILURE) {
|
||||
+ pubkey_options = svr_parse_pubkey_options(&temp_buf, 0, "plugin");
|
||||
+ if (pubkey_options == NULL) {
|
||||
/* Fail immediately as the plugin provided wrong options */
|
||||
send_msg_userauth_failure(0, 0);
|
||||
goto out;
|
||||
@@ -160,7 +162,8 @@ void svr_auth_pubkey(int valid_user) {
|
||||
#endif
|
||||
/* check if the key is valid */
|
||||
if (auth_failure) {
|
||||
- auth_failure = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen) == DROPBEAR_FAILURE;
|
||||
+ int status = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen, &pubkey_options);
|
||||
+ auth_failure = (status != DROPBEAR_SUCCESS);
|
||||
}
|
||||
|
||||
if (auth_failure) {
|
||||
@@ -185,13 +188,13 @@ void svr_auth_pubkey(int valid_user) {
|
||||
|
||||
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
|
||||
key->sk_flags_mask = SSH_SK_USER_PRESENCE_REQD;
|
||||
- if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->no_touch_required_flag) {
|
||||
+ if (pubkey_options->no_touch_required_flag) {
|
||||
key->sk_flags_mask &= ~SSH_SK_USER_PRESENCE_REQD;
|
||||
}
|
||||
- if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->verify_required_flag) {
|
||||
+ if (pubkey_options->verify_required_flag) {
|
||||
key->sk_flags_mask |= SSH_SK_USER_VERIFICATION_REQD;
|
||||
}
|
||||
-#endif
|
||||
+#endif /* DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519 */
|
||||
|
||||
/* create the data which has been signed - this a string containing
|
||||
* session_id, concatenated with the payload packet up to the signature */
|
||||
@@ -212,6 +215,10 @@ void svr_auth_pubkey(int valid_user) {
|
||||
/* ... and finally verify the signature */
|
||||
fp = sign_key_fingerprint(keyblob, keybloblen);
|
||||
if (buf_verify(ses.payload, key, sigtype, signbuf) == DROPBEAR_SUCCESS) {
|
||||
+ if (ses.authstate.pubkey_options == NULL) {
|
||||
+ ses.authstate.pubkey_options = pubkey_options;
|
||||
+ pubkey_options = NULL;
|
||||
+ }
|
||||
if (svr_opts.multiauthmethod && (ses.authstate.authtypes & ~AUTH_TYPE_PUBKEY)) {
|
||||
/* successful pubkey authentication, but extra auth required */
|
||||
dropbear_log(LOG_NOTICE,
|
||||
@@ -256,9 +263,9 @@ out:
|
||||
sign_key_free(key);
|
||||
key = NULL;
|
||||
}
|
||||
- /* Retain pubkey options only if auth succeeded */
|
||||
- if (!ses.authstate.authdone) {
|
||||
- svr_pubkey_options_cleanup();
|
||||
+ if (pubkey_options) {
|
||||
+ svr_pubkey_options_cleanup(pubkey_options);
|
||||
+ pubkey_options = NULL;
|
||||
}
|
||||
TRACE(("leave pubkeyauth"))
|
||||
}
|
||||
@@ -281,17 +288,22 @@ static void send_msg_userauth_pk_ok(cons
|
||||
|
||||
}
|
||||
|
||||
-/* Content for SSH_PUBKEYINFO is optionally returned malloced in ret_info (will be
|
||||
- freed if already set */
|
||||
+/* Key options are optionally returned in ret_options.
|
||||
+ Should be passed with *ret_options = NULL, will only be populated
|
||||
+ on success return. */
|
||||
static int checkpubkey_line(buffer* line, int line_num, const char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen,
|
||||
- char ** ret_info) {
|
||||
+ struct PubKeyOptions ** ret_options) {
|
||||
buffer *options_buf = NULL;
|
||||
char *info_str = NULL;
|
||||
unsigned int pos, len, infopos, infolen;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
+ if (ret_options) {
|
||||
+ *ret_options = NULL;
|
||||
+ }
|
||||
+
|
||||
if (line->len < MIN_AUTHKEYS_LINE || line->len > MAX_AUTHKEYS_LINE) {
|
||||
TRACE(("checkpubkey_line: bad line length %d", line->len))
|
||||
goto out;
|
||||
@@ -393,7 +405,7 @@ static int checkpubkey_line(buffer* line
|
||||
if (infolen > 0) {
|
||||
info_str = m_malloc(infolen + 1);
|
||||
buf_setpos(line, infopos);
|
||||
- strncpy(info_str, buf_getptr(line, infolen), infolen);
|
||||
+ strncpy(info_str, buf_getptr(line, infolen), infolen);
|
||||
}
|
||||
|
||||
/* truncate to base64 data length */
|
||||
@@ -405,20 +417,19 @@ static int checkpubkey_line(buffer* line
|
||||
ret = cmp_base64_key(keyblob, keybloblen, (const unsigned char *) algo, algolen, line, NULL);
|
||||
|
||||
/* free pubkey_info if it is filled */
|
||||
- if (ret_info && *ret_info) {
|
||||
- m_free(*ret_info);
|
||||
- *ret_info = NULL;
|
||||
- }
|
||||
-
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
- if (options_buf) {
|
||||
- ret = svr_add_pubkey_options(options_buf, line_num, filename);
|
||||
- }
|
||||
- if (ret_info) {
|
||||
+#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
+ if (ret_options) {
|
||||
+ *ret_options = svr_parse_pubkey_options(options_buf, line_num, filename);
|
||||
+ if (*ret_options == NULL) {
|
||||
+ ret = DROPBEAR_FAILURE;
|
||||
+ goto out;
|
||||
+ }
|
||||
/* take the (optional) public key information */
|
||||
- *ret_info = info_str;
|
||||
+ (*ret_options)->info_env = info_str;
|
||||
info_str = NULL;
|
||||
}
|
||||
+#endif
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -436,7 +447,8 @@ out:
|
||||
* acceptable key for authentication */
|
||||
/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
|
||||
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
|
||||
- const unsigned char* keyblob, unsigned int keybloblen) {
|
||||
+ const unsigned char* keyblob, unsigned int keybloblen,
|
||||
+ struct PubKeyOptions **ret_options) {
|
||||
|
||||
FILE * authfile = NULL;
|
||||
char * filename = NULL;
|
||||
@@ -509,7 +521,7 @@ static int checkpubkey(const char* keyal
|
||||
line_num++;
|
||||
|
||||
ret = checkpubkey_line(line, line_num, filename, keyalgo, keyalgolen,
|
||||
- keyblob, keybloblen, &ses.authstate.pubkey_info);
|
||||
+ keyblob, keybloblen, ret_options);
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
@@ -622,10 +634,12 @@ static int checkfileperm(char * filename
|
||||
}
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
-int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
+void fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen) {
|
||||
- return checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen, NULL);
|
||||
+ struct PubKeyOptions *options = NULL;
|
||||
+ checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen, &options);
|
||||
+ svr_pubkey_options_cleanup(options);
|
||||
}
|
||||
#endif
|
||||
|
||||
--- a/src/svr-authpubkeyoptions.c
|
||||
+++ b/src/svr-authpubkeyoptions.c
|
||||
@@ -132,25 +132,21 @@ void svr_pubkey_set_forced_command(struc
|
||||
}
|
||||
|
||||
/* Free potential public key options */
|
||||
-void svr_pubkey_options_cleanup() {
|
||||
- if (ses.authstate.pubkey_options) {
|
||||
- if (ses.authstate.pubkey_options->forced_command) {
|
||||
- m_free(ses.authstate.pubkey_options->forced_command);
|
||||
- }
|
||||
- if (ses.authstate.pubkey_options->permit_open_destinations) {
|
||||
- m_list_elem *iter = ses.authstate.pubkey_options->permit_open_destinations->first;
|
||||
+void svr_pubkey_options_cleanup(struct PubKeyOptions *pubkey_options) {
|
||||
+ if (pubkey_options) {
|
||||
+ m_free(pubkey_options->forced_command);
|
||||
+ if (pubkey_options->permit_open_destinations) {
|
||||
+ m_list_elem *iter = pubkey_options->permit_open_destinations->first;
|
||||
while (iter) {
|
||||
struct PermitTCPFwdEntry *entry = (struct PermitTCPFwdEntry*)list_remove(iter);
|
||||
m_free(entry->host);
|
||||
m_free(entry);
|
||||
- iter = ses.authstate.pubkey_options->permit_open_destinations->first;
|
||||
+ iter = pubkey_options->permit_open_destinations->first;
|
||||
}
|
||||
- m_free(ses.authstate.pubkey_options->permit_open_destinations);
|
||||
+ m_free(pubkey_options->permit_open_destinations);
|
||||
}
|
||||
- m_free(ses.authstate.pubkey_options);
|
||||
- }
|
||||
- if (ses.authstate.pubkey_info) {
|
||||
- m_free(ses.authstate.pubkey_info);
|
||||
+ m_free(pubkey_options->info_env);
|
||||
+ m_free(pubkey_options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,58 +164,65 @@ static int match_option(buffer *options_
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
-/* Parse pubkey options and set ses.authstate.pubkey_options accordingly.
|
||||
- * Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
|
||||
-int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
|
||||
- int ret = DROPBEAR_FAILURE;
|
||||
+/* Parse pubkey options and return a malloced struct.
|
||||
+ * options_buf may be NULL, treated as empty.
|
||||
+ * Returns NULL on failure */
|
||||
+struct PubKeyOptions*
|
||||
+svr_parse_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
|
||||
+ struct PubKeyOptions *pubkey_options = NULL;
|
||||
|
||||
TRACE(("enter addpubkeyoptions"))
|
||||
|
||||
- ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
|
||||
+ pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
|
||||
+
|
||||
+ if (!options_buf) {
|
||||
+ TRACE(("leave addpubkeyoptions, null options"))
|
||||
+ return pubkey_options;
|
||||
+ }
|
||||
|
||||
buf_setpos(options_buf, 0);
|
||||
while (options_buf->pos < options_buf->len) {
|
||||
if (match_option(options_buf, "no-port-forwarding") == DROPBEAR_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "Port forwarding disabled.");
|
||||
- ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
|
||||
+ pubkey_options->no_port_forwarding_flag = 1;
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_SVR_AGENTFWD
|
||||
dropbear_log(LOG_WARNING, "Agent forwarding disabled.");
|
||||
- ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
|
||||
+ pubkey_options->no_agent_forwarding_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "no-X11-forwarding") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_X11FWD
|
||||
dropbear_log(LOG_WARNING, "X11 forwarding disabled.");
|
||||
- ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
|
||||
+ pubkey_options->no_x11_forwarding_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "Pty allocation disabled.");
|
||||
- ses.authstate.pubkey_options->no_pty_flag = 1;
|
||||
+ pubkey_options->no_pty_flag = 1;
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "restrict") == DROPBEAR_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "Restrict option set");
|
||||
- ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
|
||||
+ pubkey_options->no_port_forwarding_flag = 1;
|
||||
#if DROPBEAR_SVR_AGENTFWD
|
||||
- ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
|
||||
+ pubkey_options->no_agent_forwarding_flag = 1;
|
||||
#endif
|
||||
#if DROPBEAR_X11FWD
|
||||
- ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
|
||||
+ pubkey_options->no_x11_forwarding_flag = 1;
|
||||
#endif
|
||||
- ses.authstate.pubkey_options->no_pty_flag = 1;
|
||||
+ pubkey_options->no_pty_flag = 1;
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) {
|
||||
int escaped = 0;
|
||||
const unsigned char* command_start = buf_getptr(options_buf, 0);
|
||||
|
||||
- if (ses.authstate.pubkey_options->forced_command) {
|
||||
+ if (pubkey_options->forced_command) {
|
||||
/* multiple command= options */
|
||||
goto bad_option;
|
||||
}
|
||||
@@ -228,10 +231,10 @@ int svr_add_pubkey_options(buffer *optio
|
||||
const char c = buf_getbyte(options_buf);
|
||||
if (!escaped && c == '"') {
|
||||
const int command_len = buf_getptr(options_buf, 0) - command_start;
|
||||
- ses.authstate.pubkey_options->forced_command = m_malloc(command_len);
|
||||
- memcpy(ses.authstate.pubkey_options->forced_command,
|
||||
+ pubkey_options->forced_command = m_malloc(command_len);
|
||||
+ memcpy(pubkey_options->forced_command,
|
||||
command_start, command_len-1);
|
||||
- ses.authstate.pubkey_options->forced_command[command_len-1] = '\0';
|
||||
+ pubkey_options->forced_command[command_len-1] = '\0';
|
||||
goto next_option;
|
||||
}
|
||||
escaped = (!escaped && c == '\\');
|
||||
@@ -244,8 +247,8 @@ int svr_add_pubkey_options(buffer *optio
|
||||
int valid_option = 0;
|
||||
const unsigned char* permitopen_start = buf_getptr(options_buf, 0);
|
||||
|
||||
- if (!ses.authstate.pubkey_options->permit_open_destinations) {
|
||||
- ses.authstate.pubkey_options->permit_open_destinations = list_new();
|
||||
+ if (!pubkey_options->permit_open_destinations) {
|
||||
+ pubkey_options->permit_open_destinations = list_new();
|
||||
}
|
||||
|
||||
while (options_buf->pos < options_buf->len) {
|
||||
@@ -257,7 +260,7 @@ int svr_add_pubkey_options(buffer *optio
|
||||
struct PermitTCPFwdEntry *entry =
|
||||
(struct PermitTCPFwdEntry*)m_malloc(sizeof(struct PermitTCPFwdEntry));
|
||||
|
||||
- list_append(ses.authstate.pubkey_options->permit_open_destinations, entry);
|
||||
+ list_append(pubkey_options->permit_open_destinations, entry);
|
||||
spec = m_malloc(permitopen_len);
|
||||
memcpy(spec, permitopen_start, permitopen_len - 1);
|
||||
spec[permitopen_len - 1] = '\0';
|
||||
@@ -291,14 +294,14 @@ int svr_add_pubkey_options(buffer *optio
|
||||
if (match_option(options_buf, "no-touch-required") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
|
||||
dropbear_log(LOG_WARNING, "No user presence check required for U2F/FIDO key.");
|
||||
- ses.authstate.pubkey_options->no_touch_required_flag = 1;
|
||||
+ pubkey_options->no_touch_required_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
if (match_option(options_buf, "verify-required") == DROPBEAR_SUCCESS) {
|
||||
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
|
||||
dropbear_log(LOG_WARNING, "User verification required for U2F/FIDO key.");
|
||||
- ses.authstate.pubkey_options->verify_required_flag = 1;
|
||||
+ pubkey_options->verify_required_flag = 1;
|
||||
#endif
|
||||
goto next_option;
|
||||
}
|
||||
@@ -315,17 +318,16 @@ next_option:
|
||||
/* Process the next option. */
|
||||
}
|
||||
/* parsed all options with no problem */
|
||||
- ret = DROPBEAR_SUCCESS;
|
||||
goto end;
|
||||
|
||||
bad_option:
|
||||
- ret = DROPBEAR_FAILURE;
|
||||
- svr_pubkey_options_cleanup();
|
||||
+ svr_pubkey_options_cleanup(pubkey_options);
|
||||
+ pubkey_options = NULL;
|
||||
dropbear_log(LOG_WARNING, "Bad public key options at %s:%d", filename, line_num);
|
||||
|
||||
end:
|
||||
TRACE(("leave addpubkeyoptions"))
|
||||
- return ret;
|
||||
+ return pubkey_options;
|
||||
}
|
||||
|
||||
#endif
|
||||
--- a/src/svr-chansession.c
|
||||
+++ b/src/svr-chansession.c
|
||||
@@ -1026,8 +1026,9 @@ static void execchild(const void *user_d
|
||||
addnewvar("SSH_ORIGINAL_COMMAND", chansess->original_command);
|
||||
}
|
||||
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
- if (ses.authstate.pubkey_info != NULL) {
|
||||
- addnewvar("SSH_PUBKEYINFO", ses.authstate.pubkey_info);
|
||||
+ if (ses.authstate.pubkey_options
|
||||
+ && ses.authstate.pubkey_options->info_env) {
|
||||
+ addnewvar("SSH_PUBKEYINFO", ses.authstate.pubkey_options->info_env);
|
||||
}
|
||||
#endif
|
||||
|
||||
--- a/src/svr-session.c
|
||||
+++ b/src/svr-session.c
|
||||
@@ -84,8 +84,8 @@ static const struct ChanType *svr_chanty
|
||||
|
||||
static void
|
||||
svr_session_cleanup(void) {
|
||||
- /* free potential public key options */
|
||||
- svr_pubkey_options_cleanup();
|
||||
+ svr_pubkey_options_cleanup(ses.authstate.pubkey_options);
|
||||
+ ses.authstate.pubkey_options = NULL;
|
||||
|
||||
m_free(svr_ses.addrstring);
|
||||
m_free(svr_ses.remotehost);
|
||||
--- a/src/sysoptions.h
|
||||
+++ b/src/sysoptions.h
|
||||
@@ -310,6 +310,10 @@
|
||||
#error "You must define DROPBEAR_SVR_PUBKEY_AUTH in order to use plugins"
|
||||
#endif
|
||||
|
||||
+#if (DROPBEAR_PLUGIN && !DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT)
|
||||
+ #error "DROPBEAR_PLUGIN requires DROPBEAR_SVR_PUBKEY_OPTIONS"
|
||||
+#endif
|
||||
+
|
||||
#if !(DROPBEAR_AES128 || DROPBEAR_3DES || DROPBEAR_AES256 || DROPBEAR_CHACHA20POLY1305)
|
||||
#error "At least one encryption algorithm must be enabled. AES128 is recommended."
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
--- a/src/svr-authpubkey.c
|
||||
+++ b/src/svr-authpubkey.c
|
||||
@@ -78,6 +78,13 @@ static void send_msg_userauth_pk_ok(cons
|
||||
@@ -79,6 +79,13 @@ static void send_msg_userauth_pk_ok(cons
|
||||
const unsigned char* keyblob, unsigned int keybloblen);
|
||||
static int checkfileperm(char * filename);
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
/* process a pubkey auth request, sending success or failure message as
|
||||
* appropriate */
|
||||
void svr_auth_pubkey(int valid_user) {
|
||||
@@ -462,14 +469,21 @@ static int checkpubkey(const char* keyal
|
||||
if (checkpubkeyperms() == DROPBEAR_FAILURE) {
|
||||
@@ -475,14 +482,21 @@ static int checkpubkey(const char* keyal
|
||||
TRACE(("bad authorized_keys permissions, or file doesn't exist"))
|
||||
} else {
|
||||
int fd;
|
||||
- /* we don't need to check pw and pw_dir for validity, since
|
||||
- * its been done in checkpubkeyperms. */
|
||||
- len = strlen(ses.authstate.pw_dir);
|
||||
@ -42,9 +42,9 @@
|
||||
+ user_authkeys_dir, authkeys_file);
|
||||
+ }
|
||||
|
||||
authfile = fopen(filename, "r");
|
||||
if (!authfile) {
|
||||
@@ -543,27 +557,41 @@ static int checkpubkeyperms() {
|
||||
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
@@ -563,27 +577,41 @@ static int checkpubkeyperms() {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user