--- /dev/null
+Subject: Fix proxycmd without netcat
+
+Fixes commit e5a0ef27c227 "Execute multihop commands directly, no shell"
+
+Forwarded: https://github.com/mkj/dropbear/pull/363
+
+ src/cli-main.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/src/cli-main.c b/src/cli-main.c
+index 2fafa88900..0a052a3512 100644
+--- a/src/cli-main.c
++++ b/src/cli-main.c
+@@ -77,7 +77,11 @@ int main(int argc, char ** argv) {
+ }
+
+ #if DROPBEAR_CLI_PROXYCMD
+- if (cli_opts.proxycmd || cli_opts.proxyexec) {
++ if (cli_opts.proxycmd
++#if DROPBEAR_CLI_MULTIHOP
++ || cli_opts.proxyexec
++#endif
++ ) {
+ cli_proxy_cmd(&sock_in, &sock_out, &proxy_cmd_pid);
+ if (signal(SIGINT, kill_proxy_sighandler) == SIG_ERR ||
+ signal(SIGTERM, kill_proxy_sighandler) == SIG_ERR ||
+@@ -110,11 +114,13 @@ static void shell_proxy_cmd(const void *user_data_cmd) {
+ dropbear_exit("Failed to run '%s'\n", cmd);
+ }
+
++#if DROPBEAR_CLI_MULTIHOP
+ static void exec_proxy_cmd(const void *unused) {
+ (void)unused;
+ run_command(cli_opts.proxyexec[0], cli_opts.proxyexec, ses.maxfd);
+ dropbear_exit("Failed to run '%s'\n", cli_opts.proxyexec[0]);
+ }
++#endif
+
+ static void cli_proxy_cmd(int *sock_in, int *sock_out, pid_t *pid_out) {
+ char * cmd_arg = NULL;
+@@ -145,9 +151,11 @@ static void cli_proxy_cmd(int *sock_in, int *sock_out, pid_t *pid_out) {
+ cmd_arg = m_malloc(shell_cmdlen);
+ snprintf(cmd_arg, shell_cmdlen, "exec %s", cli_opts.proxycmd);
+ exec_fn = shell_proxy_cmd;
++#if DROPBEAR_CLI_MULTIHOP
+ } else {
+ /* No shell */
+ exec_fn = exec_proxy_cmd;
++#endif
+ }
+
+ ret = spawn_command(exec_fn, cmd_arg, sock_out, sock_in, NULL, pid_out);
+@@ -159,6 +167,7 @@ static void cli_proxy_cmd(int *sock_in, int *sock_out, pid_t *pid_out) {
+ cleanup:
+ m_free(cli_opts.proxycmd);
+ m_free(cmd_arg);
++#if DROPBEAR_CLI_MULTIHOP
+ if (cli_opts.proxyexec) {
+ char **a = NULL;
+ for (a = cli_opts.proxyexec; *a; a++) {
+@@ -166,6 +175,7 @@ cleanup:
+ }
+ m_free(cli_opts.proxyexec);
+ }
++#endif
+ }
+
+ static void kill_proxy_sighandler(int UNUSED(signo)) {
--- a/src/svr-authpubkey.c
+++ b/src/svr-authpubkey.c
-@@ -78,6 +78,13 @@ static void send_msg_userauth_pk_ok(cons
- const unsigned char* keyblob, unsigned int keybloblen);
- static int checkfileperm(char * filename);
-
-+static const char * const global_authkeys_dir = "/etc/dropbear";
-+static const int n_global_authkeys_dir = 14; /* + 1 extra byte */
-+static const char * const user_authkeys_dir = ".ssh";
-+static const int n_user_authkeys_dir = 5; /* + 1 extra byte */
-+static const char * const authkeys_file = "authorized_keys";
-+static const int n_authkeys_file = 16; /* + 1 extra byte */
+@@ -435,20 +435,45 @@ out:
+ /* Returns the full path to the user's authorized_keys file in an
+ * allocated string which caller must free. */
+ static char *authorized_keys_filepath() {
++ static const char * const global_authkeys_dir = "/etc/dropbear";
++ /* strlen(global_authkeys_dir) */
++ #define n_global_authkeys_dir 13
++ static const char * const authkeys_file = "authorized_keys";
++ /* strlen(authkeys_file) */
++ #define n_authkeys_file 15
+
- /* 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) {
- TRACE(("bad authorized_keys permissions, or file doesn't exist"))
- } else {
-- /* we don't need to check pw and pw_dir for validity, since
-- * its been done in checkpubkeyperms. */
-- len = strlen(ses.authstate.pw_dir);
-- /* allocate max required pathname storage,
-- * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
-- filename = m_malloc(len + 22);
-- snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
-- ses.authstate.pw_dir);
-+ if (ses.authstate.pw_uid == 0) {
-+ len = n_global_authkeys_dir + n_authkeys_file;
-+ filename = m_malloc(len);
-+ snprintf(filename, len, "%s/%s", global_authkeys_dir, authkeys_file);
-+ } else {
-+ /* we don't need to check pw and pw_dir for validity, since
-+ * its been done in checkpubkeyperms. */
-+ len = strlen(ses.authstate.pw_dir);
-+ /* allocate max required pathname storage,
-+ * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
-+ len += n_user_authkeys_dir + n_authkeys_file + 1;
-+ filename = m_malloc(len);
-+ snprintf(filename, len, "%s/%s/%s", ses.authstate.pw_dir,
-+ user_authkeys_dir, authkeys_file);
-+ }
-
- authfile = fopen(filename, "r");
- if (!authfile) {
-@@ -543,27 +557,41 @@ static int checkpubkeyperms() {
- goto out;
- }
-
-- /* allocate max required pathname storage,
-- * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
-- len += 22;
-- filename = m_malloc(len);
-- strlcpy(filename, ses.authstate.pw_dir, len);
-+ if (ses.authstate.pw_uid == 0) {
-+ if (checkfileperm(global_authkeys_dir) != DROPBEAR_SUCCESS) {
-+ goto out;
-+ }
-
-- /* check ~ */
-- if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
-- goto out;
-- }
-+ len = n_global_authkeys_dir + n_authkeys_file;
-+ filename = m_malloc(len);
+ size_t len = 0;
+ char *pathname = NULL, *dir = NULL;
+- const char *filename = "authorized_keys";
++
++ /* OpenWrt-specific:
++ use OpenWrt' global authorized keys directory if:
++ 1. logging as uid 0 (typically root)
++ 2. "svr_opts.authorized_keys_dir" is set to default i.e. no "-D" option was specified
++ */
++ while (1) {
++ if (ses.authstate.pw_uid != 0) break;
++ if (svr_opts.authorized_keys_dir[0] == '/') break;
++
++ len = n_global_authkeys_dir + n_authkeys_file + 2;
++ pathname = m_malloc(len);
++ snprintf(pathname, len, "%s/%s", global_authkeys_dir, authkeys_file);
++ return pathname;
++ }
-- /* check ~/.ssh */
-- strlcat(filename, "/.ssh", len);
-- if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
-- goto out;
-- }
-+ snprintf(filename, len, "%s/%s", global_authkeys_dir, authkeys_file);
-+ if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
-+ goto out;
-+ }
-+ } else {
-+ /* check ~ */
-+ if (checkfileperm(ses.authstate.pw_dir) != DROPBEAR_SUCCESS) {
-+ goto out;
-+ }
+ dir = expand_homedir_path_home(svr_opts.authorized_keys_dir,
+ ses.authstate.pw_dir);
-- /* now check ~/.ssh/authorized_keys */
-- strlcat(filename, "/authorized_keys", len);
-- if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
-- goto out;
-+ /* allocate max required pathname storage,
-+ * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
-+ len += n_user_authkeys_dir + n_authkeys_file + 1;
-+ filename = m_malloc(len);
-+
-+ /* check ~/.ssh */
-+ snprintf(filename, len, "%s/%s", ses.authstate.pw_dir, user_authkeys_dir);
-+ if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
-+ goto out;
-+ }
+ /* allocate max required pathname storage,
+ * = dir + "/" + "authorized_keys" + '\0' */;
+- len = strlen(dir) + strlen(filename) + 2;
++ len = strlen(dir) + n_authkeys_file + 2;
+ pathname = m_malloc(len);
+- snprintf(pathname, len, "%s/%s", dir, filename);
++ snprintf(pathname, len, "%s/%s", dir, authkeys_file);
+ m_free(dir);
+ return pathname;
+
-+ /* now check ~/.ssh/authorized_keys */
-+ snprintf(filename, len, "%s/%s/%s", ses.authstate.pw_dir,
-+ user_authkeys_dir, authkeys_file);
-+ if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
-+ goto out;
-+ }
- }
++ /* not needed anymore */
++ #undef n_global_authkeys_dir
++ #undef n_authkeys_file
+ }
- /* file looks ok, return success */
+ /* Checks whether a specified publickey (and associated algorithm) is an