The unified diff between revisions [833d0ade..] and [6bbab836..] is displayed below. It can also be downloaded as a raw diff.

This diff has been restricted to the following files: 'cli-runopts.c'

#
#
# patch "cli-runopts.c"
#  from [6435ac6046e57fb5d30bdad5297dca84c67f828b]
#    to [a7a8de7ac8a60c743bb5c9bdb4776772853b3454]
#
============================================================
--- cli-runopts.c	6435ac6046e57fb5d30bdad5297dca84c67f828b
+++ cli-runopts.c	a7a8de7ac8a60c743bb5c9bdb4776772853b3454
@@ -33,18 +33,23 @@ static void printhelp();
 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*
 }
 #endif

+#ifdef ENABLE_CLI_MULTIHOP

-/* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
- * - note that it will be modified */
-static void parsehostname(char* orighostarg) {
+/* 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;

-	uid_t uid;
-	struct passwd *pw = 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;
+	}
+
+	parse_hostname(userhostarg);
+
+	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* orighost
 	}

 	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* orighost
 	}
 }

+#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, st

 	/* 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;
 	}