summaryrefslogtreecommitdiff
path: root/src/cli-runopts.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli-runopts.c')
-rw-r--r--src/cli-runopts.c929
1 files changed, 929 insertions, 0 deletions
diff --git a/src/cli-runopts.c b/src/cli-runopts.c
new file mode 100644
index 0000000..38a73f7
--- /dev/null
+++ b/src/cli-runopts.c
@@ -0,0 +1,929 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#include "includes.h"
+#include "runopts.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "algo.h"
+#include "tcpfwd.h"
+#include "list.h"
+
+cli_runopts cli_opts; /* GLOBAL */
+
+static void printhelp(void);
+static void parse_hostname(const char* orighostarg);
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0);
+static void fill_own_user(void);
+#if DROPBEAR_CLI_PUBKEY_AUTH
+static void loadidentityfile(const char* filename, int warnfail);
+#endif
+#if DROPBEAR_CLI_ANYTCPFWD
+static void addforward(const char* str, m_list *fwdlist);
+#endif
+#if DROPBEAR_CLI_NETCAT
+static void add_netcat(const char *str);
+#endif
+static void add_extendedopt(const char *str);
+
+static void printhelp() {
+
+ fprintf(stderr, "Dropbear SSH client v%s https://matt.ucc.asn.au/dropbear/dropbear.html\n"
+#if DROPBEAR_CLI_MULTIHOP
+ "Usage: %s [options] [user@]host[/port][,[user@]host/port],...] [command]\n"
+#else
+ "Usage: %s [options] [user@]host[/port] [command]\n"
+#endif
+ "-p <remoteport>\n"
+ "-l <username>\n"
+ "-t Allocate a pty\n"
+ "-T Don't allocate a pty\n"
+ "-N Don't run a remote command\n"
+ "-f Run in background after auth\n"
+ "-q quiet, don't show remote banner\n"
+ "-y Always accept remote host key if unknown\n"
+ "-y -y Don't perform any remote host key checking (caution)\n"
+ "-s Request a subsystem (use by external sftp)\n"
+ "-o option Set option in OpenSSH-like format ('-o help' to list options)\n"
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ "-i <identityfile> (multiple allowed, default %s)\n"
+#endif
+#if DROPBEAR_CLI_AGENTFWD
+ "-A Enable agent auth forwarding\n"
+#endif
+#if DROPBEAR_CLI_LOCALTCPFWD
+ "-L <[listenaddress:]listenport:remotehost:remoteport> Local port forwarding\n"
+ "-g Allow remote hosts to connect to forwarded ports\n"
+#endif
+#if DROPBEAR_CLI_REMOTETCPFWD
+ "-R <[listenaddress:]listenport:remotehost:remoteport> Remote port forwarding\n"
+#endif
+ "-W <receive_window_buffer> (default %d, larger may be faster, max 10MB)\n"
+ "-K <keepalive> (0 is never, default %d)\n"
+ "-I <idle_timeout> (0 is never, default %d)\n"
+ "-z disable QoS\n"
+#if DROPBEAR_CLI_NETCAT
+ "-B <endhost:endport> Netcat-alike forwarding\n"
+#endif
+#if DROPBEAR_CLI_PROXYCMD
+ "-J <proxy_program> Use program pipe rather than TCP connection\n"
+#endif
+#if DROPBEAR_USER_ALGO_LIST
+ "-c <cipher list> Specify preferred ciphers ('-c help' to list options)\n"
+ "-m <MAC list> Specify preferred MACs for packet verification (or '-m help')\n"
+#endif
+ "-b [bind_address][:bind_port]\n"
+ "-V Version\n"
+#if DEBUG_TRACE
+ "-v verbose (repeat for more verbose)\n"
+#endif
+ ,DROPBEAR_VERSION, cli_opts.progname,
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ DROPBEAR_DEFAULT_CLI_AUTHKEY,
+#endif
+ DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
+
+}
+
+void cli_getopts(int argc, char ** argv) {
+ unsigned int i, j;
+ char ** next = NULL;
+ enum {
+ OPT_EXTENDED_OPTIONS,
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ OPT_AUTHKEY,
+#endif
+#if DROPBEAR_CLI_LOCALTCPFWD
+ OPT_LOCALTCPFWD,
+#endif
+#if DROPBEAR_CLI_REMOTETCPFWD
+ OPT_REMOTETCPFWD,
+#endif
+#if DROPBEAR_CLI_NETCAT
+ OPT_NETCAT,
+#endif
+ /* a flag (no arg) if 'next' is NULL, a string-valued option otherwise */
+ OPT_OTHER
+ } opt;
+ unsigned int cmdlen;
+
+ char* recv_window_arg = NULL;
+ char* keepalive_arg = NULL;
+ char* idle_timeout_arg = NULL;
+ char *host_arg = NULL;
+ char *bind_arg = NULL;
+ char c;
+
+ /* see printhelp() for options */
+ cli_opts.progname = argv[0];
+ cli_opts.remotehost = NULL;
+ cli_opts.remoteport = NULL;
+ cli_opts.username = NULL;
+ cli_opts.cmd = NULL;
+ cli_opts.no_cmd = 0;
+ cli_opts.quiet = 0;
+ cli_opts.backgrounded = 0;
+ cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
+ cli_opts.always_accept_key = 0;
+ cli_opts.no_hostkey_check = 0;
+ cli_opts.is_subsystem = 0;
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ cli_opts.privkeys = list_new();
+#endif
+#if DROPBEAR_CLI_ANYTCPFWD
+ cli_opts.exit_on_fwd_failure = 0;
+#endif
+ cli_opts.disable_trivial_auth = 0;
+#if DROPBEAR_CLI_LOCALTCPFWD
+ cli_opts.localfwds = list_new();
+ opts.listen_fwd_all = 0;
+#endif
+#if DROPBEAR_CLI_REMOTETCPFWD
+ cli_opts.remotefwds = list_new();
+#endif
+#if DROPBEAR_CLI_AGENTFWD
+ cli_opts.agent_fwd = 0;
+ cli_opts.agent_fd = -1;
+ cli_opts.agent_keys_loaded = 0;
+#endif
+#if DROPBEAR_CLI_PROXYCMD
+ cli_opts.proxycmd = NULL;
+#endif
+ cli_opts.bind_address = NULL;
+ cli_opts.bind_port = NULL;
+#ifndef DISABLE_ZLIB
+ opts.compress_mode = DROPBEAR_COMPRESS_ON;
+#endif
+#if DROPBEAR_USER_ALGO_LIST
+ opts.cipher_list = NULL;
+ opts.mac_list = NULL;
+#endif
+#ifndef DISABLE_SYSLOG
+ opts.usingsyslog = 0;
+#endif
+ /* not yet
+ opts.ipv4 = 1;
+ opts.ipv6 = 1;
+ */
+ opts.recv_window = DEFAULT_RECV_WINDOW;
+ opts.keepalive_secs = DEFAULT_KEEPALIVE;
+ opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
+
+ fill_own_user();
+
+ for (i = 1; i < (unsigned int)argc; i++) {
+ /* Handle non-flag arguments such as hostname or commands for the remote host */
+ if (argv[i][0] != '-')
+ {
+ if (host_arg == NULL) {
+ host_arg = argv[i];
+ continue;
+ }
+ /* Commands to pass to the remote host. No more flag handling,
+ commands are consumed below */
+ break;
+ }
+
+ /* Begins with '-' */
+ opt = OPT_OTHER;
+ for (j = 1; (c = argv[i][j]) != '\0' && !next && opt == OPT_OTHER; j++) {
+ switch (c) {
+ case 'y': /* always accept the remote hostkey */
+ if (cli_opts.always_accept_key) {
+ /* twice means no checking at all */
+ cli_opts.no_hostkey_check = 1;
+ }
+ cli_opts.always_accept_key = 1;
+ break;
+ case 'q': /* quiet */
+ cli_opts.quiet = 1;
+ break;
+ case 'p': /* remoteport */
+ next = (char**)&cli_opts.remoteport;
+ break;
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ case 'i': /* an identityfile */
+ opt = OPT_AUTHKEY;
+ break;
+#endif
+ case 't': /* we want a pty */
+ cli_opts.wantpty = 1;
+ break;
+ case 'T': /* don't want a pty */
+ cli_opts.wantpty = 0;
+ break;
+ case 'N':
+ cli_opts.no_cmd = 1;
+ break;
+ case 'f':
+ cli_opts.backgrounded = 1;
+ break;
+ case 's':
+ cli_opts.is_subsystem = 1;
+ break;
+ case 'o':
+ opt = OPT_EXTENDED_OPTIONS;
+ break;
+#if DROPBEAR_CLI_LOCALTCPFWD
+ case 'L':
+ opt = OPT_LOCALTCPFWD;
+ break;
+ case 'g':
+ opts.listen_fwd_all = 1;
+ break;
+#endif
+#if DROPBEAR_CLI_REMOTETCPFWD
+ case 'R':
+ opt = OPT_REMOTETCPFWD;
+ break;
+#endif
+#if DROPBEAR_CLI_NETCAT
+ case 'B':
+ opt = OPT_NETCAT;
+ break;
+#endif
+#if DROPBEAR_CLI_PROXYCMD
+ case 'J':
+ next = &cli_opts.proxycmd;
+ break;
+#endif
+ case 'l':
+ next = &cli_opts.username;
+ break;
+ case 'h':
+ printhelp();
+ exit(EXIT_SUCCESS);
+ break;
+ case 'u':
+ /* backwards compatibility with old urandom option */
+ break;
+ case 'W':
+ next = &recv_window_arg;
+ break;
+ case 'K':
+ next = &keepalive_arg;
+ break;
+ case 'I':
+ next = &idle_timeout_arg;
+ break;
+#if DROPBEAR_CLI_AGENTFWD
+ case 'A':
+ cli_opts.agent_fwd = 1;
+ break;
+#endif
+#if DROPBEAR_USER_ALGO_LIST
+ case 'c':
+ next = &opts.cipher_list;
+ break;
+ case 'm':
+ next = &opts.mac_list;
+ break;
+#endif
+#if DEBUG_TRACE
+ case 'v':
+ debug_trace++;
+ break;
+#endif
+ case 'F':
+ case 'e':
+#if !DROPBEAR_USER_ALGO_LIST
+ case 'c':
+ case 'm':
+#endif
+ case 'D':
+#if !DROPBEAR_CLI_REMOTETCPFWD
+ case 'R':
+#endif
+#if !DROPBEAR_CLI_LOCALTCPFWD
+ case 'L':
+#endif
+ case 'V':
+ print_version();
+ exit(EXIT_SUCCESS);
+ break;
+ case 'b':
+ next = &bind_arg;
+ break;
+ case 'z':
+ opts.disable_ip_tos = 1;
+ break;
+ default:
+ fprintf(stderr,
+ "WARNING: Ignoring unknown option -%c\n", c);
+ break;
+ } /* Switch */
+ }
+
+ if (!next && opt == OPT_OTHER) /* got a flag */
+ continue;
+
+ if (c == '\0') {
+ i++;
+ j = 0;
+ if (!argv[i])
+ dropbear_exit("Missing argument");
+ }
+
+ if (opt == OPT_EXTENDED_OPTIONS) {
+ TRACE(("opt extended"))
+ add_extendedopt(&argv[i][j]);
+ }
+ else
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ if (opt == OPT_AUTHKEY) {
+ TRACE(("opt authkey"))
+ loadidentityfile(&argv[i][j], 1);
+ }
+ else
+#endif
+#if DROPBEAR_CLI_REMOTETCPFWD
+ if (opt == OPT_REMOTETCPFWD) {
+ TRACE(("opt remotetcpfwd"))
+ addforward(&argv[i][j], cli_opts.remotefwds);
+ }
+ else
+#endif
+#if DROPBEAR_CLI_LOCALTCPFWD
+ if (opt == OPT_LOCALTCPFWD) {
+ TRACE(("opt localtcpfwd"))
+ addforward(&argv[i][j], cli_opts.localfwds);
+ }
+ else
+#endif
+#if DROPBEAR_CLI_NETCAT
+ if (opt == OPT_NETCAT) {
+ TRACE(("opt netcat"))
+ add_netcat(&argv[i][j]);
+ }
+ else
+#endif
+ if (next) {
+ /* The previous flag set a value to assign */
+ *next = &argv[i][j];
+ if (*next == NULL)
+ dropbear_exit("Invalid null argument");
+ next = NULL;
+ }
+ }
+
+#if DROPBEAR_USER_ALGO_LIST
+ /* -c help doesn't need a hostname */
+ parse_ciphers_macs();
+#endif
+
+ /* Done with options/flags; now handle the hostname (which may not
+ * start with a hyphen) and optional command */
+
+ if (host_arg == NULL) { /* missing hostname */
+ printhelp();
+ exit(EXIT_FAILURE);
+ }
+ TRACE(("host is: %s", host_arg))
+
+ if (i < (unsigned int)argc) {
+ /* Build the command to send */
+ cmdlen = 0;
+ for (j = i; j < (unsigned int)argc; j++)
+ cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */
+
+ /* Allocate the space */
+ cli_opts.cmd = (char*)m_malloc(cmdlen);
+ cli_opts.cmd[0] = '\0';
+
+ /* Append all the bits */
+ for (j = i; j < (unsigned int)argc; j++) {
+ strlcat(cli_opts.cmd, argv[j], cmdlen);
+ strlcat(cli_opts.cmd, " ", cmdlen);
+ }
+ /* It'll be null-terminated here */
+ TRACE(("cmd is: %s", cli_opts.cmd))
+ }
+
+ /* And now a few sanity checks and setup */
+
+#if DROPBEAR_CLI_PROXYCMD
+ if (cli_opts.proxycmd) {
+ /* To match the common path of m_freeing it */
+ cli_opts.proxycmd = m_strdup(cli_opts.proxycmd);
+ }
+#endif
+
+ if (cli_opts.remoteport == NULL) {
+ cli_opts.remoteport = "22";
+ }
+
+ if (bind_arg) {
+ if (split_address_port(bind_arg,
+ &cli_opts.bind_address, &cli_opts.bind_port)
+ == DROPBEAR_FAILURE) {
+ dropbear_exit("Bad -b argument");
+ }
+ }
+
+ /* If not explicitly specified with -t or -T, we don't want a pty if
+ * there's a command, but we do otherwise */
+ if (cli_opts.wantpty == 9) {
+ if (cli_opts.cmd == NULL) {
+ cli_opts.wantpty = 1;
+ } else {
+ cli_opts.wantpty = 0;
+ }
+ }
+
+ if (cli_opts.backgrounded && cli_opts.cmd == NULL
+ && cli_opts.no_cmd == 0) {
+ dropbear_exit("Command required for -f");
+ }
+
+ if (recv_window_arg) {
+ parse_recv_window(recv_window_arg);
+ }
+ if (keepalive_arg) {
+ unsigned int val;
+ if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
+ dropbear_exit("Bad keepalive '%s'", keepalive_arg);
+ }
+ opts.keepalive_secs = val;
+ }
+
+ if (idle_timeout_arg) {
+ unsigned int val;
+ if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
+ dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
+ }
+ opts.idle_timeout_secs = val;
+ }
+
+#if DROPBEAR_CLI_NETCAT
+ if (cli_opts.cmd && cli_opts.netcat_host) {
+ dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
+ }
+#endif
+
+ /* The hostname gets set up last, since
+ * in multi-hop mode it will require knowledge
+ * of other flags such as -i */
+#if DROPBEAR_CLI_MULTIHOP
+ parse_multihop_hostname(host_arg, argv[0]);
+#else
+ parse_hostname(host_arg);
+#endif
+
+ /* We don't want to include default id_dropbear as a
+ -i argument for multihop, so handle it later. */
+#if (DROPBEAR_CLI_PUBKEY_AUTH)
+ {
+ char *expand_path = expand_homedir_path(DROPBEAR_DEFAULT_CLI_AUTHKEY);
+ loadidentityfile(expand_path, 0);
+ m_free(expand_path);
+ }
+#endif
+
+}
+
+#if DROPBEAR_CLI_PUBKEY_AUTH
+static void loadidentityfile(const char* filename, int warnfail) {
+ sign_key *key;
+ enum signkey_type keytype;
+
+ TRACE(("loadidentityfile %s", filename))
+
+ key = new_sign_key();
+ keytype = DROPBEAR_SIGNKEY_ANY;
+ if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
+ if (warnfail) {
+ dropbear_log(LOG_WARNING, "Failed loading keyfile '%s'\n", filename);
+ }
+ sign_key_free(key);
+ } else {
+ key->type = keytype;
+ key->source = SIGNKEY_SOURCE_RAW_FILE;
+ key->filename = m_strdup(filename);
+ list_append(cli_opts.privkeys, key);
+ }
+}
+#endif
+
+#if DROPBEAR_CLI_MULTIHOP
+
+static char*
+multihop_passthrough_args() {
+ char *ret;
+ unsigned int len, total;
+ m_list_elem *iter;
+ /* Fill out -i, -y, -W options that make sense for all
+ * the intermediate processes */
+ len = 30; /* space for "-q -y -y -W <size>\0" */
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
+ {
+ sign_key * key = (sign_key*)iter->item;
+ len += 3 + strlen(key->filename);
+ }
+#endif /* DROPBEAR_CLI_PUBKEY_AUTH */
+ if (cli_opts.proxycmd) {
+ /* "-J 'cmd'" */
+ len += 6 + strlen(cli_opts.proxycmd);
+ }
+
+ ret = m_malloc(len);
+ total = 0;
+
+ if (cli_opts.quiet) {
+ total += m_snprintf(ret+total, len-total, "-q ");
+ }
+
+ if (cli_opts.no_hostkey_check) {
+ total += m_snprintf(ret+total, len-total, "-y -y ");
+ } else if (cli_opts.always_accept_key) {
+ total += m_snprintf(ret+total, len-total, "-y ");
+ }
+
+ if (cli_opts.proxycmd) {
+ total += m_snprintf(ret+total, len-total, "-J '%s' ", cli_opts.proxycmd);
+ }
+
+ if (opts.recv_window != DEFAULT_RECV_WINDOW) {
+ total += m_snprintf(ret+total, len-total, "-W %u ", opts.recv_window);
+ }
+
+#if DROPBEAR_CLI_PUBKEY_AUTH
+ for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
+ {
+ sign_key * key = (sign_key*)iter->item;
+ total += m_snprintf(ret+total, len-total, "-i %s ", key->filename);
+ }
+#endif /* DROPBEAR_CLI_PUBKEY_AUTH */
+
+ return ret;
+}
+
+/* Sets up 'onion-forwarding' connections. This will spawn
+ * a separate dbclient process for each hop.
+ * As an example, if the cmdline is
+ * dbclient wrt,madako,canyons
+ * then we want to run:
+ * dbclient -J "dbclient -B canyons:22 wrt,madako" canyons
+ * and then the inner dbclient will recursively run:
+ * dbclient -J "dbclient -B madako:22 wrt" madako
+ * etc for as many hosts as we want.
+ *
+ * Note that "-J" arguments aren't actually used, instead
+ * below sets cli_opts.proxycmd directly.
+ *
+ * Ports for hosts can be specified as host/port.
+ */
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
+ char *userhostarg = NULL;
+ char *hostbuf = NULL;
+ char *last_hop = NULL;
+ char *remainder = NULL;
+
+ /* both scp and rsync parse a user@host argument
+ * and turn it into "-l user host". This breaks
+ * for our multihop syntax, so we suture it back together.
+ * This will break usernames that have both '@' and ',' in them,
+ * though that should be fairly uncommon. */
+ if (cli_opts.username
+ && strchr(cli_opts.username, ',')
+ && strchr(cli_opts.username, '@')) {
+ unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2;
+ hostbuf = m_malloc(len);
+ m_snprintf(hostbuf, len, "%s@%s", cli_opts.username, orighostarg);
+ } else {
+ hostbuf = m_strdup(orighostarg);
+ }
+ userhostarg = hostbuf;
+
+ last_hop = strrchr(userhostarg, ',');
+ if (last_hop) {
+ if (last_hop == userhostarg) {
+ dropbear_exit("Bad multi-hop hostnames");
+ }
+ *last_hop = '\0';
+ last_hop++;
+ remainder = userhostarg;
+ userhostarg = last_hop;
+ }
+
+ parse_hostname(userhostarg);
+
+ if (last_hop) {
+ /* Set up the proxycmd */
+ unsigned int cmd_len = 0;
+ char *passthrough_args = multihop_passthrough_args();
+ if (cli_opts.remoteport == NULL) {
+ cli_opts.remoteport = "22";
+ }
+ cmd_len = strlen(argv0) + strlen(remainder)
+ + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
+ + strlen(passthrough_args)
+ + 30;
+ /* replace proxycmd. old -J arguments have been copied
+ to passthrough_args */
+ cli_opts.proxycmd = m_realloc(cli_opts.proxycmd, cmd_len);
+ m_snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s",
+ argv0, cli_opts.remotehost, cli_opts.remoteport,
+ passthrough_args, remainder);
+#ifndef DISABLE_ZLIB
+ /* The stream will be incompressible since it's encrypted. */
+ opts.compress_mode = DROPBEAR_COMPRESS_OFF;
+#endif
+ m_free(passthrough_args);
+ }
+ m_free(hostbuf);
+}
+#endif /* !DROPBEAR_CLI_MULTIHOP */
+
+/* Parses a [user@]hostname[/port] argument. */
+static void parse_hostname(const char* orighostarg) {
+ char *userhostarg = NULL;
+ char *port = NULL;
+
+ userhostarg = m_strdup(orighostarg);
+
+ cli_opts.remotehost = strchr(userhostarg, '@');
+ if (cli_opts.remotehost == NULL) {
+ /* no username portion, the cli-auth.c code can figure the
+ * local user's name */
+ cli_opts.remotehost = userhostarg;
+ } else {
+ cli_opts.remotehost[0] = '\0'; /* Split the user/host */
+ cli_opts.remotehost++;
+ cli_opts.username = userhostarg;
+ }
+
+ if (cli_opts.username == NULL) {
+ cli_opts.username = m_strdup(cli_opts.own_user);
+ }
+
+ port = strchr(cli_opts.remotehost, '^');
+ if (!port) {
+ /* legacy separator */
+ port = strchr(cli_opts.remotehost, '/');
+ }
+ if (port) {
+ *port = '\0';
+ cli_opts.remoteport = port+1;
+ }
+
+ if (cli_opts.remotehost[0] == '\0') {
+ dropbear_exit("Bad hostname");
+ }
+}
+
+#if DROPBEAR_CLI_NETCAT
+static void add_netcat(const char* origstr) {
+ char *portstr = NULL;
+
+ char * str = m_strdup(origstr);
+
+ portstr = strchr(str, ':');
+ if (portstr == NULL) {
+ TRACE(("No netcat port"))
+ goto fail;
+ }
+ *portstr = '\0';
+ portstr++;
+
+ if (strchr(portstr, ':')) {
+ TRACE(("Multiple netcat colons"))
+ goto fail;
+ }
+
+ if (m_str_to_uint(portstr, &cli_opts.netcat_port) == DROPBEAR_FAILURE) {
+ TRACE(("bad netcat port"))
+ goto fail;
+ }
+
+ if (cli_opts.netcat_port > 65535) {
+ TRACE(("too large netcat port"))
+ goto fail;
+ }
+
+ cli_opts.netcat_host = str;
+ return;
+
+fail:
+ dropbear_exit("Bad netcat endpoint '%s'", origstr);
+}
+#endif
+
+static void fill_own_user() {
+ uid_t uid;
+ struct passwd *pw = NULL;
+
+ uid = getuid();
+
+ pw = getpwuid(uid);
+ if (pw && pw->pw_name != NULL) {
+ cli_opts.own_user = m_strdup(pw->pw_name);
+ } else {
+ dropbear_log(LOG_INFO, "Warning: failed to identify current user. Trying anyway.");
+ cli_opts.own_user = m_strdup("unknown");
+ }
+
+}
+
+#if DROPBEAR_CLI_ANYTCPFWD
+/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding
+ * set, and add it to the forwarding list */
+static void addforward(const char* origstr, m_list *fwdlist) {
+
+ char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL;
+ char * listenaddr = NULL;
+ char * listenport = NULL;
+ char * connectaddr = NULL;
+ char * connectport = NULL;
+ struct TCPFwdEntry* newfwd = NULL;
+ char * str = NULL;
+
+ TRACE(("enter addforward"))
+
+ /* We need to split the original argument up. This var
+ is never free()d. */
+ str = m_strdup(origstr);
+
+ part1 = str;
+
+ part2 = strchr(str, ':');
+ if (part2 == NULL) {
+ TRACE(("part2 == NULL"))
+ goto fail;
+ }
+ *part2 = '\0';
+ part2++;
+
+ part3 = strchr(part2, ':');
+ if (part3 == NULL) {
+ TRACE(("part3 == NULL"))
+ goto fail;
+ }
+ *part3 = '\0';
+ part3++;
+
+ part4 = strchr(part3, ':');
+ if (part4) {
+ *part4 = '\0';
+ part4++;
+ }
+
+ if (part4) {
+ listenaddr = part1;
+ listenport = part2;
+ connectaddr = part3;
+ connectport = part4;
+ } else {
+ listenaddr = NULL;
+ listenport = part1;
+ connectaddr = part2;
+ connectport = part3;
+ }
+
+ newfwd = m_malloc(sizeof(struct TCPFwdEntry));
+
+ /* Now we check the ports - note that the port ints are unsigned,
+ * the check later only checks for >= MAX_PORT */
+ if (m_str_to_uint(listenport, &newfwd->listenport) == DROPBEAR_FAILURE) {
+ TRACE(("bad listenport strtoul"))
+ goto fail;
+ }
+
+ if (m_str_to_uint(connectport, &newfwd->connectport) == DROPBEAR_FAILURE) {
+ TRACE(("bad connectport strtoul"))
+ goto fail;
+ }
+
+ newfwd->listenaddr = listenaddr;
+ newfwd->connectaddr = connectaddr;
+
+ if (newfwd->listenport > 65535) {
+ TRACE(("listenport > 65535"))
+ goto badport;
+ }
+
+ if (newfwd->connectport > 65535) {
+ TRACE(("connectport > 65535"))
+ goto badport;
+ }
+
+ newfwd->have_reply = 0;
+ list_append(fwdlist, newfwd);
+
+ TRACE(("leave addforward: done"))
+ return;
+
+fail:
+ dropbear_exit("Bad TCP forward '%s'", origstr);
+
+badport:
+ dropbear_exit("Bad TCP port in '%s'", origstr);
+}
+#endif
+
+static int match_extendedopt(const char** strptr, const char *optname) {
+ int seen_eq = 0;
+ int optlen = strlen(optname);
+ const char *str = *strptr;
+
+ while (isspace(*str)) {
+ ++str;
+ }
+
+ if (strncasecmp(str, optname, optlen) != 0) {
+ return DROPBEAR_FAILURE;
+ }
+
+ str += optlen;
+
+ while (isspace(*str) || (!seen_eq && *str == '=')) {
+ if (*str == '=') {
+ seen_eq = 1;
+ }
+ ++str;
+ }
+
+ if (str-*strptr == optlen) {
+ /* matched just a prefix of optname */
+ return DROPBEAR_FAILURE;
+ }
+
+ *strptr = str;
+ return DROPBEAR_SUCCESS;
+}
+
+static int parse_flag_value(const char *value) {
+ if (strcmp(value, "yes") == 0 || strcmp(value, "true") == 0) {
+ return 1;
+ } else if (strcmp(value, "no") == 0 || strcmp(value, "false") == 0) {
+ return 0;
+ }
+
+ dropbear_exit("Bad yes/no argument '%s'", value);
+}
+
+static void add_extendedopt(const char* origstr) {
+ const char *optstr = origstr;
+
+ if (strcmp(origstr, "help") == 0) {
+ dropbear_log(LOG_INFO, "Available options:\n"
+#if DROPBEAR_CLI_ANYTCPFWD
+ "\tExitOnForwardFailure\n"
+#endif
+ "\tDisableTrivialAuth\n"
+#ifndef DISABLE_SYSLOG
+ "\tUseSyslog\n"
+#endif
+ "\tPort\n"
+ );
+ exit(EXIT_SUCCESS);
+ }
+
+#if DROPBEAR_CLI_ANYTCPFWD
+ if (match_extendedopt(&optstr, "ExitOnForwardFailure") == DROPBEAR_SUCCESS) {
+ cli_opts.exit_on_fwd_failure = parse_flag_value(optstr);
+ return;
+ }
+#endif
+
+#ifndef DISABLE_SYSLOG
+ if (match_extendedopt(&optstr, "UseSyslog") == DROPBEAR_SUCCESS) {
+ opts.usingsyslog = parse_flag_value(optstr);
+ return;
+ }
+#endif
+
+ if (match_extendedopt(&optstr, "Port") == DROPBEAR_SUCCESS) {
+ cli_opts.remoteport = optstr;
+ return;
+ }
+
+ if (match_extendedopt(&optstr, "DisableTrivialAuth") == DROPBEAR_SUCCESS) {
+ cli_opts.disable_trivial_auth = parse_flag_value(optstr);
+ return;
+ }
+
+ dropbear_log(LOG_WARNING, "Ignoring unknown configuration option '%s'", origstr);
+}