diff options
Diffstat (limited to 'cli-runopts.c')
-rw-r--r-- | cli-runopts.c | 223 |
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; } |