cli-runopts.c 18.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 * 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"
Matt Johnston's avatar
Matt Johnston committed
31
#include "tcpfwd.h"
32
#include "list.h"
33
34
35

cli_runopts cli_opts; /* GLOBAL */

Matt Johnston's avatar
Matt Johnston committed
36
static void printhelp();
37
38
static void parse_hostname(const char* orighostarg);
static void parse_multihop_hostname(const char* orighostarg, const char* argv0);
39
static void fill_own_user();
Matt Johnston's avatar
Matt Johnston committed
40
#ifdef ENABLE_CLI_PUBKEY_AUTH
Matt Johnston's avatar
Matt Johnston committed
41
42
static void loadidentityfile(const char* filename);
#endif
Matt Johnston's avatar
Matt Johnston committed
43
#ifdef ENABLE_CLI_ANYTCPFWD
44
static void addforward(const char* str, m_list *fwdlist);
45
46
47
#endif
#ifdef ENABLE_CLI_NETCAT
static void add_netcat(const char *str);
Matt Johnston's avatar
Matt Johnston committed
48
#endif
49

Matt Johnston's avatar
Matt Johnston committed
50
static void printhelp() {
51

Matt Johnston's avatar
Matt Johnston committed
52
	fprintf(stderr, "Dropbear SSH client v%s https://matt.ucc.asn.au/dropbear/dropbear.html\n"
53
54
55
#ifdef ENABLE_CLI_MULTIHOP
					"Usage: %s [options] [[email protected]]host[/port][,[[email protected]]host/port],...] [command]\n"
#else
56
					"Usage: %s [options] [[email protected]]host[/port] [command]\n"
57
#endif
Matt Johnston's avatar
Matt Johnston committed
58
					"-p <remoteport>\n"
59
					"-l <username>\n"
Matt Johnston's avatar
Matt Johnston committed
60
61
					"-t    Allocate a pty\n"
					"-T    Don't allocate a pty\n"
62
					"-N    Don't run a remote command\n"
63
					"-f    Run in background after auth\n"
64
					"-y    Always accept remote host key if unknown\n"
65
					"-y -y Don't perform any remote host key checking (caution)\n"
66
					"-s    Request a subsystem (use by external sftp)\n"
Matt Johnston's avatar
Matt Johnston committed
67
#ifdef ENABLE_CLI_PUBKEY_AUTH
Matt Johnston's avatar
Matt Johnston committed
68
					"-i <identityfile>   (multiple allowed)\n"
Matt Johnston's avatar
Matt Johnston committed
69
#endif
70
71
72
#ifdef ENABLE_CLI_AGENTFWD
					"-A    Enable agent auth forwarding\n"
#endif
Matt Johnston's avatar
Matt Johnston committed
73
#ifdef ENABLE_CLI_LOCALTCPFWD
Matt Johnston's avatar
Matt Johnston committed
74
					"-L <[listenaddress:]listenport:remotehost:remoteport> Local port forwarding\n"
75
					"-g    Allow remote hosts to connect to forwarded ports\n"
Matt Johnston's avatar
Matt Johnston committed
76
#endif
Matt Johnston's avatar
Matt Johnston committed
77
#ifdef ENABLE_CLI_REMOTETCPFWD
Matt Johnston's avatar
Matt Johnston committed
78
					"-R <[listenaddress:]listenport:remotehost:remoteport> Remote port forwarding\n"
Matt Johnston's avatar
Matt Johnston committed
79
#endif
80
81
					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
					"-K <keepalive>  (0 is never, default %d)\n"
82
					"-I <idle_timeout>  (0 is never, default %d)\n"
83
#ifdef ENABLE_CLI_NETCAT
84
					"-B <endhost:endport> Netcat-alike forwarding\n"
85
#endif				
86
#ifdef ENABLE_CLI_PROXYCMD
87
					"-J <proxy_program> Use program pipe rather than TCP connection\n"
88
#endif
89
90
91
92
#ifdef ENABLE_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
93
#ifdef DEBUG_TRACE
94
					"-v    verbose (compiled with DEBUG_TRACE)\n"
95
#endif
96
					,DROPBEAR_VERSION, cli_opts.progname,
97
					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
98
					
99
100
101
}

void cli_getopts(int argc, char ** argv) {
102
	unsigned int i, j;
103
	char ** next = 0;
104
	unsigned int cmdlen;
Matt Johnston's avatar
Matt Johnston committed
105
#ifdef ENABLE_CLI_PUBKEY_AUTH
106
	int nextiskey = 0; /* A flag if the next argument is a keyfile */
Matt Johnston's avatar
Matt Johnston committed
107
#endif
Matt Johnston's avatar
Matt Johnston committed
108
#ifdef ENABLE_CLI_LOCALTCPFWD
Matt Johnston's avatar
Matt Johnston committed
109
110
	int nextislocal = 0;
#endif
Matt Johnston's avatar
Matt Johnston committed
111
#ifdef ENABLE_CLI_REMOTETCPFWD
Matt Johnston's avatar
Matt Johnston committed
112
	int nextisremote = 0;
113
114
115
#endif
#ifdef ENABLE_CLI_NETCAT
	int nextisnetcat = 0;
Matt Johnston's avatar
Matt Johnston committed
116
#endif
117
	char* dummy = NULL; /* Not used for anything real */
118

119
120
	char* recv_window_arg = NULL;
	char* keepalive_arg = NULL;
121
	char* idle_timeout_arg = NULL;
122
	char *host_arg = NULL;
123

124
	/* see printhelp() for options */
125
	cli_opts.progname = argv[0];
126
127
128
129
	cli_opts.remotehost = NULL;
	cli_opts.remoteport = NULL;
	cli_opts.username = NULL;
	cli_opts.cmd = NULL;
130
	cli_opts.no_cmd = 0;
131
	cli_opts.backgrounded = 0;
Matt Johnston's avatar
Matt Johnston committed
132
	cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
133
	cli_opts.always_accept_key = 0;
134
	cli_opts.no_hostkey_check = 0;
135
	cli_opts.is_subsystem = 0;
Matt Johnston's avatar
Matt Johnston committed
136
#ifdef ENABLE_CLI_PUBKEY_AUTH
137
	cli_opts.privkeys = list_new();
Matt Johnston's avatar
Matt Johnston committed
138
#endif
Matt Johnston's avatar
Matt Johnston committed
139
#ifdef ENABLE_CLI_LOCALTCPFWD
140
	cli_opts.localfwds = list_new();
141
	opts.listen_fwd_all = 0;
Matt Johnston's avatar
Matt Johnston committed
142
#endif
Matt Johnston's avatar
Matt Johnston committed
143
#ifdef ENABLE_CLI_REMOTETCPFWD
144
	cli_opts.remotefwds = list_new();
145
#endif
146
147
#ifdef ENABLE_CLI_AGENTFWD
	cli_opts.agent_fwd = 0;
148
	cli_opts.agent_fd = -1;
149
	cli_opts.agent_keys_loaded = 0;
150
#endif
151
152
#ifdef ENABLE_CLI_PROXYCMD
	cli_opts.proxycmd = NULL;
153
154
155
#endif
#ifndef DISABLE_ZLIB
	opts.enable_compress = 1;
156
157
158
159
#endif
#ifdef ENABLE_USER_ALGO_LIST
	opts.cipher_list = NULL;
	opts.mac_list = NULL;
Matt Johnston's avatar
Matt Johnston committed
160
#endif
161
162
163
164
	/* not yet
	opts.ipv4 = 1;
	opts.ipv6 = 1;
	*/
165
	opts.recv_window = DEFAULT_RECV_WINDOW;
166

167
168
	fill_own_user();

Matt Johnston's avatar
Matt Johnston committed
169
	/* Iterate all the arguments */
170
	for (i = 1; i < (unsigned int)argc; i++) {
Matt Johnston's avatar
Matt Johnston committed
171
#ifdef ENABLE_CLI_PUBKEY_AUTH
172
		if (nextiskey) {
Matt Johnston's avatar
Matt Johnston committed
173
174
175
176
			/* Load a hostkey since the previous argument was "-i" */
			loadidentityfile(argv[i]);
			nextiskey = 0;
			continue;
177
		}
Matt Johnston's avatar
Matt Johnston committed
178
179
180
#endif
#ifdef ENABLE_CLI_REMOTETCPFWD
		if (nextisremote) {
181
			TRACE(("nextisremote true"))
182
			addforward(argv[i], cli_opts.remotefwds);
Matt Johnston's avatar
Matt Johnston committed
183
184
185
186
187
188
			nextisremote = 0;
			continue;
		}
#endif
#ifdef ENABLE_CLI_LOCALTCPFWD
		if (nextislocal) {
189
			TRACE(("nextislocal true"))
190
			addforward(argv[i], cli_opts.localfwds);
Matt Johnston's avatar
Matt Johnston committed
191
192
193
			nextislocal = 0;
			continue;
		}
194
195
196
197
198
199
200
201
#endif
#ifdef ENABLE_CLI_NETCAT
		if (nextisnetcat) {
			TRACE(("nextisnetcat true"))
			add_netcat(argv[i]);
			nextisnetcat = 0;
			continue;
		}
Matt Johnston's avatar
Matt Johnston committed
202
#endif
203
		if (next) {
Matt Johnston's avatar
Matt Johnston committed
204
			/* The previous flag set a value to assign */
205
206
207
208
			*next = argv[i];
			if (*next == NULL) {
				dropbear_exit("Invalid null argument");
			}
209
			next = NULL;
210
211
212
213
			continue;
		}

		if (argv[i][0] == '-') {
214
			/* A flag *waves* */
Matt Johnston's avatar
Matt Johnston committed
215

216
			switch (argv[i][1]) {
217
				case 'y': /* always accept the remote hostkey */
218
219
220
221
					if (cli_opts.always_accept_key) {
						// twice means no checking at all
						cli_opts.no_hostkey_check = 1;
					}
222
223
					cli_opts.always_accept_key = 1;
					break;
Matt Johnston's avatar
Matt Johnston committed
224
				case 'p': /* remoteport */
225
					next = &cli_opts.remoteport;
226
					break;
Matt Johnston's avatar
Matt Johnston committed
227
#ifdef ENABLE_CLI_PUBKEY_AUTH
Matt Johnston's avatar
Matt Johnston committed
228
				case 'i': /* an identityfile */
Matt Johnston's avatar
Matt Johnston committed
229
230
231
232
233
234
					/* Keep scp happy when it changes "-i file" to "-ifile" */
					if (strlen(argv[i]) > 2) {
						loadidentityfile(&argv[i][2]);
					} else  {
						nextiskey = 1;
					}
235
236
					break;
#endif
Matt Johnston's avatar
Matt Johnston committed
237
238
239
240
241
242
				case 't': /* we want a pty */
					cli_opts.wantpty = 1;
					break;
				case 'T': /* don't want a pty */
					cli_opts.wantpty = 0;
					break;
243
244
245
				case 'N':
					cli_opts.no_cmd = 1;
					break;
246
247
248
				case 'f':
					cli_opts.backgrounded = 1;
					break;
249
250
251
				case 's':
					cli_opts.is_subsystem = 1;
					break;
Matt Johnston's avatar
Matt Johnston committed
252
253
254
255
#ifdef ENABLE_CLI_LOCALTCPFWD
				case 'L':
					nextislocal = 1;
					break;
256
257
258
				case 'g':
					opts.listen_fwd_all = 1;
					break;
Matt Johnston's avatar
Matt Johnston committed
259
260
261
262
263
#endif
#ifdef ENABLE_CLI_REMOTETCPFWD
				case 'R':
					nextisremote = 1;
					break;
264
#endif
265
266
267
268
#ifdef ENABLE_CLI_NETCAT
				case 'B':
					nextisnetcat = 1;
					break;
269
#endif
270
271
272
273
#ifdef ENABLE_CLI_PROXYCMD
				case 'J':
					next = &cli_opts.proxycmd;
					break;
Matt Johnston's avatar
Matt Johnston committed
274
#endif
275
276
277
278
				case 'l':
					next = &cli_opts.username;
					break;
				case 'h':
Matt Johnston's avatar
Matt Johnston committed
279
					printhelp();
280
281
					exit(EXIT_SUCCESS);
					break;
282
				case 'u':
283
					/* backwards compatibility with old urandom option */
284
					break;
285
286
287
				case 'W':
					next = &recv_window_arg;
					break;
288
289
290
				case 'K':
					next = &keepalive_arg;
					break;
291
292
293
				case 'I':
					next = &idle_timeout_arg;
					break;
294
295
296
297
298
#ifdef ENABLE_CLI_AGENTFWD
				case 'A':
					cli_opts.agent_fwd = 1;
					break;
#endif
299
300
301
302
303
304
305
306
#ifdef ENABLE_USER_ALGO_LIST
				case 'c':
					next = &opts.cipher_list;
					break;
				case 'm':
					next = &opts.mac_list;
					break;
#endif
307
308
309
310
311
#ifdef DEBUG_TRACE
				case 'v':
					debug_trace = 1;
					break;
#endif
312
313
				case 'F':
				case 'e':
314
#ifndef ENABLE_USER_ALGO_LIST
315
316
				case 'c':
				case 'm':
317
#endif
318
319
320
321
322
323
324
325
326
327
328
329
330
				case 'D':
#ifndef ENABLE_CLI_REMOTETCPFWD
				case 'R':
#endif
#ifndef ENABLE_CLI_LOCALTCPFWD
				case 'L':
#endif
				case 'o':
				case 'b':
					next = &dummy;
				default:
					fprintf(stderr, 
						"WARNING: Ignoring unknown argument '%s'\n", argv[i]);
331
					break;
332
			} /* Switch */
Matt Johnston's avatar
Matt Johnston committed
333
334
335
336
337
338
			
			/* Now we handle args where they might be "-luser" (no spaces)*/
			if (next && strlen(argv[i]) > 2) {
				*next = &argv[i][2];
				next = NULL;
			}
339

Matt Johnston's avatar
Matt Johnston committed
340
341
			continue; /* next argument */

342
		} else {
343
			TRACE(("non-flag arg: '%s'", argv[i]))
344
345
346

			/* Either the hostname or commands */

347
348
			if (host_arg == NULL) {
				host_arg = argv[i];
349
			} else {
Matt Johnston's avatar
Matt Johnston committed
350

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
				/* this is part of the commands to send - after this we
				 * don't parse any more options, and flags are sent as the
				 * command */
				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 */

				/* We've eaten all the options and flags */
				break;
371
372
373
			}
		}
	}
Matt Johnston's avatar
Matt Johnston committed
374

375
376
	/* And now a few sanity checks and setup */

377
378
379
380
#ifdef ENABLE_USER_ALGO_LIST
	parse_ciphers_macs();
#endif

381
	if (host_arg == NULL) {
382
383
		printhelp();
		exit(EXIT_FAILURE);
Matt Johnston's avatar
Matt Johnston committed
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
	}

	if (cli_opts.remoteport == NULL) {
		cli_opts.remoteport = "22";
	}

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

	if (cli_opts.backgrounded && cli_opts.cmd == NULL
			&& cli_opts.no_cmd == 0) {
402
		dropbear_exit("Command required for -f");
403
	}
404
	
405
	if (recv_window_arg) {
406
		opts.recv_window = atol(recv_window_arg);
407
		if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) {
408
409
410
			dropbear_exit("Bad recv window '%s'", recv_window_arg);
		}
	}
411
	if (keepalive_arg) {
Matt Johnston's avatar
Matt Johnston committed
412
413
		unsigned int val;
		if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
414
415
			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
		}
Matt Johnston's avatar
Matt Johnston committed
416
		opts.keepalive_secs = val;
417
	}
418

419
	if (idle_timeout_arg) {
Matt Johnston's avatar
Matt Johnston committed
420
421
		unsigned int val;
		if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
422
423
			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
		}
Matt Johnston's avatar
Matt Johnston committed
424
		opts.idle_timeout_secs = val;
425
426
	}

427
428
429
430
431
#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
432
433
434
435
436
437
438
439
440

	/* The hostname gets set up last, since
	 * in multi-hop mode it will require knowledge
	 * of other flags such as -i */
#ifdef ENABLE_CLI_MULTIHOP
	parse_multihop_hostname(host_arg, argv[0]);
#else
	parse_hostname(host_arg);
#endif
Matt Johnston's avatar
Matt Johnston committed
441
442
}

Matt Johnston's avatar
Matt Johnston committed
443
#ifdef ENABLE_CLI_PUBKEY_AUTH
Matt Johnston's avatar
Matt Johnston committed
444
445
446
447
448
449
450
451
452
453
static void loadidentityfile(const char* filename) {
	sign_key *key;
	int keytype;

	key = new_sign_key();
	keytype = DROPBEAR_SIGNKEY_ANY;
	if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
		fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
		sign_key_free(key);
	} else {
454
455
456
457
		key->type = keytype;
		key->source = SIGNKEY_SOURCE_RAW_FILE;
		key->filename = m_strdup(filename);
		list_append(cli_opts.privkeys, key);
Matt Johnston's avatar
Matt Johnston committed
458
459
460
461
	}
}
#endif

462
463
#ifdef ENABLE_CLI_MULTIHOP

464
465
466
467
468
static char*
multihop_passthrough_args() {
	char *ret;
	int total;
	unsigned int len = 0;
469
	m_list_elem *iter;
470
	/* Fill out -i, -y, -W options that make sense for all
471
	 * the intermediate processes */
472
	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
473
	{
474
475
		sign_key * key = (sign_key*)iter->item;
		len += 3 + strlen(key->filename);
476
	}
477
	len += 30; // space for -W <size>, terminator.
478
479
480
	ret = m_malloc(len);
	total = 0;

481
482
483
484
485
486
487
488
489
490
491
	if (cli_opts.no_hostkey_check)
	{
		int written = snprintf(ret+total, len-total, "-y -y ");
		total += written;
	}
	else if (cli_opts.always_accept_key)
	{
		int written = snprintf(ret+total, len-total, "-y ");
		total += written;
	}

492
493
	if (opts.recv_window != DEFAULT_RECV_WINDOW)
	{
494
		int written = snprintf(ret+total, len-total, "-W %d ", opts.recv_window);
495
496
497
		total += written;
	}

498
	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
499
	{
500
		sign_key * key = (sign_key*)iter->item;
501
		const size_t size = len - total;
502
		int written = snprintf(ret+total, size, "-i %s ", key->filename);
Matt Johnston's avatar
Matt Johnston committed
503
		dropbear_assert((unsigned int)written < size);
504
505
506
		total += written;
	}

Matt Johnston's avatar
Matt Johnston committed
507
	/* if args were passed, total will be not zero, and it will have a space at the end, so remove that */
508
509
510
511
512
	if (total > 0) 
	{
		total--;
	}

513
514
515
	return ret;
}

516
517
/* Sets up 'onion-forwarding' connections. This will spawn
 * a separate dbclient process for each hop.
518
519
520
521
522
523
524
 * 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.
525
526
 *
 * Ports for hosts can be specified as host/port.
527
 */
528
static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
529
	char *userhostarg = NULL;
530
	char *hostbuf = NULL;
Matt Johnston's avatar
Matt Johnston committed
531
	char *last_hop = NULL;
532
	char *remainder = NULL;
Matt Johnston's avatar
Matt Johnston committed
533

534
535
536
537
538
539
540
541
542
	/* both scp and rsync parse a [email protected] 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;
543
544
		hostbuf = m_malloc(len);
		snprintf(hostbuf, len, "%[email protected]%s", cli_opts.username, orighostarg);
545
	} else {
546
		hostbuf = m_strdup(orighostarg);
547
	}
548
	userhostarg = hostbuf;
549
550
551
552
553
554
555
556
557
558
559
560

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

561
	parse_hostname(userhostarg);
562
563
564
565

	if (last_hop) {
		/* Set up the proxycmd */
		unsigned int cmd_len = 0;
566
		char *passthrough_args = multihop_passthrough_args();
567
568
569
570
571
572
		if (cli_opts.proxycmd) {
			dropbear_exit("-J can't be used with multihop mode");
		}
		if (cli_opts.remoteport == NULL) {
			cli_opts.remoteport = "22";
		}
573
		cmd_len = strlen(argv0) + strlen(remainder) 
574
			+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
575
576
			+ strlen(passthrough_args)
			+ 30;
577
		cli_opts.proxycmd = m_malloc(cmd_len);
578
579
580
		snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", 
				argv0, cli_opts.remotehost, cli_opts.remoteport, 
				passthrough_args, remainder);
581
582
583
584
#ifndef DISABLE_ZLIB
		/* The stream will be incompressible since it's encrypted. */
		opts.enable_compress = 0;
#endif
585
		m_free(passthrough_args);
586
	}
587
	m_free(hostbuf);
588
}
589
#endif /* !ENABLE_CLI_MULTIHOP */
590

591
592
/* Parses a [[email protected]]hostname[/port] argument. */
static void parse_hostname(const char* orighostarg) {
Matt Johnston's avatar
Matt Johnston committed
593
	char *userhostarg = NULL;
594
	char *port = NULL;
Matt Johnston's avatar
Matt Johnston committed
595
596

	userhostarg = m_strdup(orighostarg);
Matt Johnston's avatar
Matt Johnston committed
597
598
599
600
601
602
603
604
605
606
607
608
609

	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) {
610
		cli_opts.username = m_strdup(cli_opts.own_user);
Matt Johnston's avatar
Matt Johnston committed
611
612
	}

613
614
615
616
617
	port = strchr(cli_opts.remotehost, '#');
	if (!port)  {
		// legacy separator
		port = strchr(cli_opts.remotehost, '/');
	}
618
619
620
621
622
	if (port) {
		*port = '\0';
		cli_opts.remoteport = port+1;
	}

Matt Johnston's avatar
Matt Johnston committed
623
624
625
	if (cli_opts.remotehost[0] == '\0') {
		dropbear_exit("Bad hostname");
	}
626
}
Matt Johnston's avatar
Matt Johnston committed
627

628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
#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;
	}
	
647
	if (m_str_to_uint(portstr, &cli_opts.netcat_port) == DROPBEAR_FAILURE) {
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
		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

665
666
667
668
669
670
671
672
673
674
675
676
677
678
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);
}

Matt Johnston's avatar
Matt Johnston committed
679
#ifdef ENABLE_CLI_ANYTCPFWD
680
/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding
Matt Johnston's avatar
Matt Johnston committed
681
 * set, and add it to the forwarding list */
682
static void addforward(const char* origstr, m_list *fwdlist) {
Matt Johnston's avatar
Matt Johnston committed
683

684
685
	char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL;
	char * listenaddr = NULL;
Matt Johnston's avatar
Matt Johnston committed
686
687
	char * listenport = NULL;
	char * connectaddr = NULL;
688
	char * connectport = NULL;
689
	struct TCPFwdEntry* newfwd = NULL;
Matt Johnston's avatar
Matt Johnston committed
690
691
	char * str = NULL;

692
	TRACE(("enter addforward"))
Matt Johnston's avatar
Matt Johnston committed
693

694
695
	/* We need to split the original argument up. This var
	   is never free()d. */ 
Matt Johnston's avatar
Matt Johnston committed
696
697
	str = m_strdup(origstr);

698
	part1 = str;
Matt Johnston's avatar
Matt Johnston committed
699

700
701
702
	part2 = strchr(str, ':');
	if (part2 == NULL) {
		TRACE(("part2 == NULL"))
Matt Johnston's avatar
Matt Johnston committed
703
704
		goto fail;
	}
705
706
	*part2 = '\0';
	part2++;
Matt Johnston's avatar
Matt Johnston committed
707

708
709
710
	part3 = strchr(part2, ':');
	if (part3 == NULL) {
		TRACE(("part3 == NULL"))
Matt Johnston's avatar
Matt Johnston committed
711
712
		goto fail;
	}
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
	*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;
	}

734
	newfwd = m_malloc(sizeof(struct TCPFwdEntry));
Matt Johnston's avatar
Matt Johnston committed
735
736
737

	/* Now we check the ports - note that the port ints are unsigned,
	 * the check later only checks for >= MAX_PORT */
738
739
	if (m_str_to_uint(listenport, &newfwd->listenport) == DROPBEAR_FAILURE) {
		TRACE(("bad listenport strtoul"))
Matt Johnston's avatar
Matt Johnston committed
740
741
742
		goto fail;
	}

743
744
	if (m_str_to_uint(connectport, &newfwd->connectport) == DROPBEAR_FAILURE) {
		TRACE(("bad connectport strtoul"))
Matt Johnston's avatar
Matt Johnston committed
745
746
747
		goto fail;
	}

748
	newfwd->listenaddr = listenaddr;
Matt Johnston's avatar
Matt Johnston committed
749
750
751
	newfwd->connectaddr = connectaddr;

	if (newfwd->listenport > 65535) {
752
		TRACE(("listenport > 65535"))
Matt Johnston's avatar
Matt Johnston committed
753
754
755
756
		goto badport;
	}
		
	if (newfwd->connectport > 65535) {
757
		TRACE(("connectport > 65535"))
Matt Johnston's avatar
Matt Johnston committed
758
759
760
		goto badport;
	}

761
	newfwd->have_reply = 0;
762
	list_append(fwdlist, newfwd);
Matt Johnston's avatar
Matt Johnston committed
763

764
	TRACE(("leave addforward: done"))
Matt Johnston's avatar
Matt Johnston committed
765
766
767
768
769
770
771
772
773
	return;

fail:
	dropbear_exit("Bad TCP forward '%s'", origstr);

badport:
	dropbear_exit("Bad TCP port in '%s'", origstr);
}
#endif