summaryrefslogtreecommitdiffhomepage
path: root/cli-runopts.c
diff options
context:
space:
mode:
Diffstat (limited to 'cli-runopts.c')
-rw-r--r--cli-runopts.c223
1 files changed, 190 insertions, 33 deletions
diff --git a/cli-runopts.c b/cli-runopts.c
index c7d9b8b..d49caa2 100644
--- a/cli-runopts.c
+++ b/cli-runopts.c
@@ -33,18 +33,23 @@
cli_runopts cli_opts; /* GLOBAL */
static void printhelp();
-static void parsehostname(char* userhostarg);
+static void parse_hostname(const char* orighostarg);
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0);
+static void fill_own_user();
#ifdef ENABLE_CLI_PUBKEY_AUTH
static void loadidentityfile(const char* filename);
#endif
#ifdef ENABLE_CLI_ANYTCPFWD
-static void addforward(char* str, struct TCPFwdList** fwdlist);
+static void addforward(const char* str, struct TCPFwdList** fwdlist);
+#endif
+#ifdef ENABLE_CLI_NETCAT
+static void add_netcat(const char *str);
#endif
static void printhelp() {
fprintf(stderr, "Dropbear client v%s\n"
- "Usage: %s [options] [user@]host [command]\n"
+ "Usage: %s [options] [user@]host[/port] [command]\n"
"Options are:\n"
"-p <remoteport>\n"
"-l <username>\n"
@@ -53,6 +58,7 @@ static void printhelp() {
"-N Don't run a remote command\n"
"-f Run in background after auth\n"
"-y Always accept remote host key if unknown\n"
+ "-s Request a subsystem (use for sftp)\n"
#ifdef ENABLE_CLI_PUBKEY_AUTH
"-i <identityfile> (multiple allowed)\n"
#endif
@@ -68,6 +74,12 @@ static void printhelp() {
#endif
"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
"-K <keepalive> (0 is never, default %d)\n"
+#ifdef ENABLE_CLI_NETCAT
+ "-B <endhost:endport> Netcat-alike bouncing\n"
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+ "-J <proxy_program> Use program rather than tcp connection\n"
+#endif
#ifdef DEBUG_TRACE
"-v verbose\n"
#endif
@@ -90,6 +102,9 @@ void cli_getopts(int argc, char ** argv) {
#ifdef ENABLE_CLI_REMOTETCPFWD
int nextisremote = 0;
#endif
+#ifdef ENABLE_CLI_NETCAT
+ int nextisnetcat = 0;
+#endif
char* dummy = NULL; /* Not used for anything real */
char* recv_window_arg = NULL;
@@ -105,6 +120,7 @@ void cli_getopts(int argc, char ** argv) {
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.is_subsystem = 0;
#ifdef ENABLE_CLI_PUBKEY_AUTH
cli_opts.privkeys = NULL;
#endif
@@ -119,12 +135,17 @@ void cli_getopts(int argc, char ** argv) {
cli_opts.agent_fwd = 0;
cli_opts.agent_keys_loaded = 0;
#endif
+#ifdef ENABLE_CLI_PROXYCMD
+ cli_opts.proxycmd = NULL;
+#endif
/* not yet
opts.ipv4 = 1;
opts.ipv6 = 1;
*/
opts.recv_window = DEFAULT_RECV_WINDOW;
+ fill_own_user();
+
/* Iterate all the arguments */
for (i = 1; i < (unsigned int)argc; i++) {
#ifdef ENABLE_CLI_PUBKEY_AUTH
@@ -151,6 +172,14 @@ void cli_getopts(int argc, char ** argv) {
continue;
}
#endif
+#ifdef ENABLE_CLI_NETCAT
+ if (nextisnetcat) {
+ TRACE(("nextisnetcat true"))
+ add_netcat(argv[i]);
+ nextisnetcat = 0;
+ continue;
+ }
+#endif
if (next) {
/* The previous flag set a value to assign */
*next = argv[i];
@@ -193,6 +222,9 @@ void cli_getopts(int argc, char ** argv) {
case 'f':
cli_opts.backgrounded = 1;
break;
+ case 's':
+ cli_opts.is_subsystem = 1;
+ break;
#ifdef ENABLE_CLI_LOCALTCPFWD
case 'L':
nextislocal = 1;
@@ -206,6 +238,16 @@ void cli_getopts(int argc, char ** argv) {
nextisremote = 1;
break;
#endif
+#ifdef ENABLE_CLI_NETCAT
+ case 'B':
+ nextisnetcat = 1;
+ break;
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+ case 'J':
+ next = &cli_opts.proxycmd;
+ break;
+#endif
case 'l':
next = &cli_opts.username;
break;
@@ -266,9 +308,11 @@ void cli_getopts(int argc, char ** argv) {
/* Either the hostname or commands */
if (cli_opts.remotehost == NULL) {
-
- parsehostname(argv[i]);
-
+#ifdef ENABLE_CLI_MULTIHOP
+ parse_multihop_hostname(argv[i], argv[0]);
+#else
+ parse_hostname(argv[i]);
+#endif
} else {
/* this is part of the commands to send - after this we
@@ -295,6 +339,8 @@ void cli_getopts(int argc, char ** argv) {
}
}
+ /* And now a few sanity checks and setup */
+
if (cli_opts.remotehost == NULL) {
printhelp();
exit(EXIT_FAILURE);
@@ -319,21 +365,23 @@ void cli_getopts(int argc, char ** argv) {
dropbear_exit("command required for -f");
}
- if (recv_window_arg)
- {
+ if (recv_window_arg) {
opts.recv_window = atol(recv_window_arg);
- if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW)
- {
+ if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) {
dropbear_exit("Bad recv window '%s'", recv_window_arg);
}
}
if (keepalive_arg) {
- opts.keepalive_secs = strtoul(keepalive_arg, NULL, 10);
- if (opts.keepalive_secs == 0 && errno == EINVAL)
- {
+ if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
dropbear_exit("Bad keepalive '%s'", keepalive_arg);
}
}
+
+#ifdef ENABLE_CLI_NETCAT
+ if (cli_opts.cmd && cli_opts.netcat_host) {
+ dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
+ }
+#endif
}
@@ -363,16 +411,77 @@ static void loadidentityfile(const char* filename) {
}
#endif
+#ifdef ENABLE_CLI_MULTIHOP
+
+/* 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.
+ *
+ * Ports for hosts can be specified as host/port.
+ */
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
+ char *userhostarg = 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;
+ userhostarg = m_malloc(len);
+ snprintf(userhostarg, len, "%s@%s", cli_opts.username, orighostarg);
+ } else {
+ userhostarg = m_strdup(orighostarg);
+ }
+
+ 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;
+ }
-/* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
- * - note that it will be modified */
-static void parsehostname(char* orighostarg) {
+ parse_hostname(userhostarg);
- uid_t uid;
- struct passwd *pw = NULL;
+ if (last_hop) {
+ /* Set up the proxycmd */
+ unsigned int cmd_len = 0;
+ if (cli_opts.proxycmd) {
+ dropbear_exit("-J can't be used with multihop mode");
+ }
+ if (cli_opts.remoteport == NULL) {
+ cli_opts.remoteport = "22";
+ }
+ cmd_len = strlen(remainder)
+ + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
+ + strlen(argv0) + 30;
+ cli_opts.proxycmd = m_malloc(cmd_len);
+ snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s",
+ argv0, cli_opts.remotehost, cli_opts.remoteport, remainder);
+ }
+}
+#endif /* !ENABLE_CLI_MULTIHOP */
+
+/* Parses a [user@]hostname[/port] argument. */
+static void parse_hostname(const char* orighostarg) {
char *userhostarg = NULL;
+ char *port = NULL;
- /* We probably don't want to be editing argvs */
userhostarg = m_strdup(orighostarg);
cli_opts.remotehost = strchr(userhostarg, '@');
@@ -387,14 +496,13 @@ static void parsehostname(char* orighostarg) {
}
if (cli_opts.username == NULL) {
- uid = getuid();
-
- pw = getpwuid(uid);
- if (pw == NULL || pw->pw_name == NULL) {
- dropbear_exit("Unknown own user");
- }
+ cli_opts.username = m_strdup(cli_opts.own_user);
+ }
- cli_opts.username = m_strdup(pw->pw_name);
+ port = strchr(cli_opts.remotehost, '/');
+ if (port) {
+ *port = '\0';
+ cli_opts.remoteport = port+1;
}
if (cli_opts.remotehost[0] == '\0') {
@@ -402,10 +510,61 @@ static void parsehostname(char* orighostarg) {
}
}
+#ifdef ENABLE_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 == NULL || pw->pw_name == NULL) {
+ dropbear_exit("Unknown own user");
+ }
+
+ cli_opts.own_user = m_strdup(pw->pw_name);
+}
+
#ifdef ENABLE_CLI_ANYTCPFWD
/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding
* set, and add it to the forwarding list */
-static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
+static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
char * listenport = NULL;
char * connectport = NULL;
@@ -441,15 +600,13 @@ static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
/* Now we check the ports - note that the port ints are unsigned,
* the check later only checks for >= MAX_PORT */
- newfwd->listenport = strtol(listenport, NULL, 10);
- if (errno != 0) {
- TRACE(("bad listenport strtol"))
+ if (m_str_to_uint(listenport, &newfwd->listenport) == DROPBEAR_FAILURE) {
+ TRACE(("bad listenport strtoul"))
goto fail;
}
- newfwd->connectport = strtol(connectport, NULL, 10);
- if (errno != 0) {
- TRACE(("bad connectport strtol"))
+ if (m_str_to_uint(connectport, &newfwd->connectport) == DROPBEAR_FAILURE) {
+ TRACE(("bad connectport strtoul"))
goto fail;
}