cli-runopts.c 21.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 */

Francois Perrad's avatar
Francois Perrad committed
36
static void printhelp(void);
37
38
static void parse_hostname(const char* orighostarg);
static void parse_multihop_hostname(const char* orighostarg, const char* argv0);
Francois Perrad's avatar
Francois Perrad committed
39
static void fill_own_user(void);
40
#if DROPBEAR_CLI_PUBKEY_AUTH
41
static void loadidentityfile(const char* filename, int warnfail);
Matt Johnston's avatar
Matt Johnston committed
42
#endif
43
#if DROPBEAR_CLI_ANYTCPFWD
44
static void addforward(const char* str, m_list *fwdlist);
45
#endif
46
#if DROPBEAR_CLI_NETCAT
47
static void add_netcat(const char *str);
Matt Johnston's avatar
Matt Johnston committed
48
#endif
49
static void add_extendedopt(const char *str);
50

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

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

void cli_getopts(int argc, char ** argv) {
108
	unsigned int i, j;
109
	char ** next = 0;
Guilhem Moulin's avatar
Guilhem Moulin committed
110
	enum {
111
		OPT_EXTENDED_OPTIONS,
112
#if DROPBEAR_CLI_PUBKEY_AUTH
Guilhem Moulin's avatar
Guilhem Moulin committed
113
		OPT_AUTHKEY,
Matt Johnston's avatar
Matt Johnston committed
114
#endif
115
#if DROPBEAR_CLI_LOCALTCPFWD
Guilhem Moulin's avatar
Guilhem Moulin committed
116
		OPT_LOCALTCPFWD,
Matt Johnston's avatar
Matt Johnston committed
117
#endif
118
#if DROPBEAR_CLI_REMOTETCPFWD
Guilhem Moulin's avatar
Guilhem Moulin committed
119
		OPT_REMOTETCPFWD,
120
#endif
121
#if DROPBEAR_CLI_NETCAT
Guilhem Moulin's avatar
Guilhem Moulin committed
122
		OPT_NETCAT,
Matt Johnston's avatar
Matt Johnston committed
123
#endif
Guilhem Moulin's avatar
Guilhem Moulin committed
124
125
126
127
		/* a flag (no arg) if 'next' is NULL, a string-valued option otherwise */
		OPT_OTHER
	} opt;
	unsigned int cmdlen;
128
	char* dummy = NULL; /* Not used for anything real */
129

130
131
	char* recv_window_arg = NULL;
	char* keepalive_arg = NULL;
132
	char* idle_timeout_arg = NULL;
133
	char *host_arg = NULL;
Guilhem Moulin's avatar
Guilhem Moulin committed
134
	char c;
135

136
	/* see printhelp() for options */
137
	cli_opts.progname = argv[0];
138
139
140
141
	cli_opts.remotehost = NULL;
	cli_opts.remoteport = NULL;
	cli_opts.username = NULL;
	cli_opts.cmd = NULL;
142
	cli_opts.no_cmd = 0;
143
	cli_opts.backgrounded = 0;
Matt Johnston's avatar
Matt Johnston committed
144
	cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
145
	cli_opts.always_accept_key = 0;
146
	cli_opts.no_hostkey_check = 0;
147
	cli_opts.is_subsystem = 0;
148
#if DROPBEAR_CLI_PUBKEY_AUTH
149
	cli_opts.privkeys = list_new();
Matt Johnston's avatar
Matt Johnston committed
150
#endif
151
#if DROPBEAR_CLI_ANYTCPFWD
152
153
	cli_opts.exit_on_fwd_failure = 0;
#endif
154
#if DROPBEAR_CLI_LOCALTCPFWD
155
	cli_opts.localfwds = list_new();
156
	opts.listen_fwd_all = 0;
Matt Johnston's avatar
Matt Johnston committed
157
#endif
158
#if DROPBEAR_CLI_REMOTETCPFWD
159
	cli_opts.remotefwds = list_new();
160
#endif
161
#if DROPBEAR_CLI_AGENTFWD
162
	cli_opts.agent_fwd = 0;
163
	cli_opts.agent_fd = -1;
164
	cli_opts.agent_keys_loaded = 0;
165
#endif
166
#if DROPBEAR_CLI_PROXYCMD
167
	cli_opts.proxycmd = NULL;
168
169
#endif
#ifndef DISABLE_ZLIB
170
	opts.compress_mode = DROPBEAR_COMPRESS_ON;
171
#endif
172
#if DROPBEAR_USER_ALGO_LIST
173
174
	opts.cipher_list = NULL;
	opts.mac_list = NULL;
175
176
177
#endif
#ifndef DISABLE_SYSLOG
	opts.usingsyslog = 0;
Matt Johnston's avatar
Matt Johnston committed
178
#endif
179
180
181
182
	/* not yet
	opts.ipv4 = 1;
	opts.ipv6 = 1;
	*/
183
	opts.recv_window = DEFAULT_RECV_WINDOW;
184
185
	opts.keepalive_secs = DEFAULT_KEEPALIVE;
	opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
186

187
188
	fill_own_user();

Matt Johnston's avatar
Matt Johnston committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
	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 '-' */
Guilhem Moulin's avatar
Guilhem Moulin committed
203
204
		opt = OPT_OTHER;
		for (j = 1; (c = argv[i][j]) != '\0' && !next && opt == OPT_OTHER; j++) {
205
			switch (c) {
206
				case 'y': /* always accept the remote hostkey */
207
					if (cli_opts.always_accept_key) {
Matt Johnston's avatar
Matt Johnston committed
208
						/* twice means no checking at all */
209
210
						cli_opts.no_hostkey_check = 1;
					}
211
212
					cli_opts.always_accept_key = 1;
					break;
Matt Johnston's avatar
Matt Johnston committed
213
				case 'p': /* remoteport */
214
					next = &cli_opts.remoteport;
215
					break;
216
#if DROPBEAR_CLI_PUBKEY_AUTH
Matt Johnston's avatar
Matt Johnston committed
217
				case 'i': /* an identityfile */
Guilhem Moulin's avatar
Guilhem Moulin committed
218
					opt = OPT_AUTHKEY;
219
220
					break;
#endif
Matt Johnston's avatar
Matt Johnston committed
221
222
223
224
225
226
				case 't': /* we want a pty */
					cli_opts.wantpty = 1;
					break;
				case 'T': /* don't want a pty */
					cli_opts.wantpty = 0;
					break;
227
228
229
				case 'N':
					cli_opts.no_cmd = 1;
					break;
230
231
232
				case 'f':
					cli_opts.backgrounded = 1;
					break;
233
234
235
				case 's':
					cli_opts.is_subsystem = 1;
					break;
236
237
238
				case 'o':
					opt = OPT_EXTENDED_OPTIONS;
					break;
239
#if DROPBEAR_CLI_LOCALTCPFWD
Matt Johnston's avatar
Matt Johnston committed
240
				case 'L':
Guilhem Moulin's avatar
Guilhem Moulin committed
241
					opt = OPT_LOCALTCPFWD;
Matt Johnston's avatar
Matt Johnston committed
242
					break;
243
244
245
				case 'g':
					opts.listen_fwd_all = 1;
					break;
Matt Johnston's avatar
Matt Johnston committed
246
#endif
247
#if DROPBEAR_CLI_REMOTETCPFWD
Matt Johnston's avatar
Matt Johnston committed
248
				case 'R':
Guilhem Moulin's avatar
Guilhem Moulin committed
249
					opt = OPT_REMOTETCPFWD;
Matt Johnston's avatar
Matt Johnston committed
250
					break;
251
#endif
252
#if DROPBEAR_CLI_NETCAT
253
				case 'B':
Guilhem Moulin's avatar
Guilhem Moulin committed
254
					opt = OPT_NETCAT;
255
					break;
256
#endif
257
#if DROPBEAR_CLI_PROXYCMD
258
259
260
				case 'J':
					next = &cli_opts.proxycmd;
					break;
Matt Johnston's avatar
Matt Johnston committed
261
#endif
262
263
264
265
				case 'l':
					next = &cli_opts.username;
					break;
				case 'h':
Matt Johnston's avatar
Matt Johnston committed
266
					printhelp();
267
268
					exit(EXIT_SUCCESS);
					break;
269
				case 'u':
270
					/* backwards compatibility with old urandom option */
271
					break;
272
273
274
				case 'W':
					next = &recv_window_arg;
					break;
275
276
277
				case 'K':
					next = &keepalive_arg;
					break;
278
279
280
				case 'I':
					next = &idle_timeout_arg;
					break;
281
#if DROPBEAR_CLI_AGENTFWD
282
283
284
285
				case 'A':
					cli_opts.agent_fwd = 1;
					break;
#endif
286
#if DROPBEAR_USER_ALGO_LIST
287
288
289
290
291
292
293
				case 'c':
					next = &opts.cipher_list;
					break;
				case 'm':
					next = &opts.mac_list;
					break;
#endif
294
#if DEBUG_TRACE
295
296
297
298
				case 'v':
					debug_trace = 1;
					break;
#endif
299
300
				case 'F':
				case 'e':
301
#if !DROPBEAR_USER_ALGO_LIST
302
303
				case 'c':
				case 'm':
304
#endif
305
				case 'D':
306
#ifndef DROPBEAR_CLI_REMOTETCPFWD
307
308
				case 'R':
#endif
309
#ifndef DROPBEAR_CLI_LOCALTCPFWD
310
311
				case 'L':
#endif
Matt Johnston's avatar
Matt Johnston committed
312
313
314
315
				case 'V':
					print_version();
					exit(EXIT_SUCCESS);
					break;
316
317
				case 'b':
					next = &dummy;
318
					/* FALLTHROUGH */
319
				default:
Guilhem Moulin's avatar
Guilhem Moulin committed
320
321
					fprintf(stderr,
						"WARNING: Ignoring unknown option -%c\n", c);
322
					break;
323
			} /* Switch */
Guilhem Moulin's avatar
Guilhem Moulin committed
324
		}
325

Guilhem Moulin's avatar
Guilhem Moulin committed
326
327
		if (!next && opt == OPT_OTHER) /* got a flag */
			continue;
Matt Johnston's avatar
Matt Johnston committed
328

Guilhem Moulin's avatar
Guilhem Moulin committed
329
330
331
332
333
334
		if (c == '\0') {
			i++;
			j = 0;
			if (!argv[i])
				dropbear_exit("Missing argument");
		}
335

336
337
338
339
340
		if (opt == OPT_EXTENDED_OPTIONS) {
			TRACE(("opt extended"))
			add_extendedopt(&argv[i][j]);
		}
		else
341
#if DROPBEAR_CLI_PUBKEY_AUTH
Guilhem Moulin's avatar
Guilhem Moulin committed
342
343
344
345
346
347
		if (opt == OPT_AUTHKEY) {
			TRACE(("opt authkey"))
			loadidentityfile(&argv[i][j], 1);
		}
		else
#endif
348
#if DROPBEAR_CLI_REMOTETCPFWD
Guilhem Moulin's avatar
Guilhem Moulin committed
349
350
351
352
353
354
		if (opt == OPT_REMOTETCPFWD) {
			TRACE(("opt remotetcpfwd"))
			addforward(&argv[i][j], cli_opts.remotefwds);
		}
		else
#endif
355
#if DROPBEAR_CLI_LOCALTCPFWD
Guilhem Moulin's avatar
Guilhem Moulin committed
356
357
358
359
360
361
		if (opt == OPT_LOCALTCPFWD) {
			TRACE(("opt localtcpfwd"))
			addforward(&argv[i][j], cli_opts.localfwds);
		}
		else
#endif
362
#if DROPBEAR_CLI_NETCAT
Guilhem Moulin's avatar
Guilhem Moulin committed
363
364
365
366
367
368
369
370
371
372
373
374
375
376
		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;
		}
	}
377

Guilhem Moulin's avatar
Guilhem Moulin committed
378
379
	/* Done with options/flags; now handle the hostname (which may not
	 * start with a hyphen) and optional command */
Matt Johnston's avatar
Matt Johnston committed
380

Matt Johnston's avatar
Matt Johnston committed
381
	if (host_arg == NULL) { /* missing hostname */
Guilhem Moulin's avatar
Guilhem Moulin committed
382
383
384
385
386
387
388
389
390
391
		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 */
392

Guilhem Moulin's avatar
Guilhem Moulin committed
393
394
395
		/* Allocate the space */
		cli_opts.cmd = (char*)m_malloc(cmdlen);
		cli_opts.cmd[0] = '\0';
396

Guilhem Moulin's avatar
Guilhem Moulin committed
397
398
399
400
		/* Append all the bits */
		for (j = i; j < (unsigned int)argc; j++) {
			strlcat(cli_opts.cmd, argv[j], cmdlen);
			strlcat(cli_opts.cmd, " ", cmdlen);
401
		}
Guilhem Moulin's avatar
Guilhem Moulin committed
402
403
		/* It'll be null-terminated here */
		TRACE(("cmd is: %s", cli_opts.cmd))
404
	}
Matt Johnston's avatar
Matt Johnston committed
405

406
407
	/* And now a few sanity checks and setup */

408
#if DROPBEAR_USER_ALGO_LIST
409
410
411
	parse_ciphers_macs();
#endif

412
#if DROPBEAR_CLI_PROXYCMD                                                                                                                                   
413
414
415
416
417
418
	if (cli_opts.proxycmd) {
		/* To match the common path of m_freeing it */
		cli_opts.proxycmd = m_strdup(cli_opts.proxycmd);
	}
#endif

Matt Johnston's avatar
Matt Johnston committed
419
420
421
422
423
424
425
426
427
428
429
430
431
	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;
		}
	}
432
433
434

	if (cli_opts.backgrounded && cli_opts.cmd == NULL
			&& cli_opts.no_cmd == 0) {
435
		dropbear_exit("Command required for -f");
436
	}
437
	
438
	if (recv_window_arg) {
439
		opts.recv_window = atol(recv_window_arg);
440
		if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) {
441
442
443
			dropbear_exit("Bad recv window '%s'", recv_window_arg);
		}
	}
444
	if (keepalive_arg) {
Matt Johnston's avatar
Matt Johnston committed
445
446
		unsigned int val;
		if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
447
448
			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
		}
Matt Johnston's avatar
Matt Johnston committed
449
		opts.keepalive_secs = val;
450
	}
451

452
	if (idle_timeout_arg) {
Matt Johnston's avatar
Matt Johnston committed
453
454
		unsigned int val;
		if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
455
456
			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
		}
Matt Johnston's avatar
Matt Johnston committed
457
		opts.idle_timeout_secs = val;
458
459
	}

460
#if DROPBEAR_CLI_NETCAT
461
462
463
464
	if (cli_opts.cmd && cli_opts.netcat_host) {
		dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
	}
#endif
465

466
#if (DROPBEAR_CLI_PUBKEY_AUTH)
467
	{
468
		char *expand_path = expand_homedir_path(DROPBEAR_DEFAULT_CLI_AUTHKEY);
469
470
471
472
473
		loadidentityfile(expand_path, 0);
		m_free(expand_path);
	}
#endif

474
475
476
	/* The hostname gets set up last, since
	 * in multi-hop mode it will require knowledge
	 * of other flags such as -i */
477
#if DROPBEAR_CLI_MULTIHOP
478
479
480
481
	parse_multihop_hostname(host_arg, argv[0]);
#else
	parse_hostname(host_arg);
#endif
Matt Johnston's avatar
Matt Johnston committed
482
483
}

484
#if DROPBEAR_CLI_PUBKEY_AUTH
485
static void loadidentityfile(const char* filename, int warnfail) {
Matt Johnston's avatar
Matt Johnston committed
486
	sign_key *key;
487
	enum signkey_type keytype;
Matt Johnston's avatar
Matt Johnston committed
488

489
490
	TRACE(("loadidentityfile %s", filename))

Matt Johnston's avatar
Matt Johnston committed
491
492
493
	key = new_sign_key();
	keytype = DROPBEAR_SIGNKEY_ANY;
	if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
494
		if (warnfail) {
495
			dropbear_log(LOG_WARNING, "Failed loading keyfile '%s'\n", filename);
496
		}
Matt Johnston's avatar
Matt Johnston committed
497
498
		sign_key_free(key);
	} else {
499
500
501
502
		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
503
504
505
506
	}
}
#endif

507
#if DROPBEAR_CLI_MULTIHOP
508

509
510
511
512
513
static char*
multihop_passthrough_args() {
	char *ret;
	int total;
	unsigned int len = 0;
514
	m_list_elem *iter;
515
	/* Fill out -i, -y, -W options that make sense for all
516
	 * the intermediate processes */
517
#if DROPBEAR_CLI_PUBKEY_AUTH
518
	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
519
	{
520
521
		sign_key * key = (sign_key*)iter->item;
		len += 3 + strlen(key->filename);
522
	}
523
#endif /* DROPBEAR_CLI_PUBKEY_AUTH */
524

Matt Johnston's avatar
Matt Johnston committed
525
	len += 30; /* space for -W <size>, terminator. */
526
527
528
	ret = m_malloc(len);
	total = 0;

529
530
531
532
533
534
535
536
537
538
539
	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;
	}

540
541
	if (opts.recv_window != DEFAULT_RECV_WINDOW)
	{
Chocobo1's avatar
Chocobo1 committed
542
		int written = snprintf(ret+total, len-total, "-W %u ", opts.recv_window);
543
544
545
		total += written;
	}

546
#if DROPBEAR_CLI_PUBKEY_AUTH
547
	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
548
	{
549
		sign_key * key = (sign_key*)iter->item;
550
		const size_t size = len - total;
551
		int written = snprintf(ret+total, size, "-i %s ", key->filename);
Matt Johnston's avatar
Matt Johnston committed
552
		dropbear_assert((unsigned int)written < size);
553
554
		total += written;
	}
555
#endif /* DROPBEAR_CLI_PUBKEY_AUTH */
556

Matt Johnston's avatar
Matt Johnston committed
557
	/* if args were passed, total will be not zero, and it will have a space at the end, so remove that */
558
559
560
561
562
	if (total > 0) 
	{
		total--;
	}

563
564
565
	return ret;
}

566
567
/* Sets up 'onion-forwarding' connections. This will spawn
 * a separate dbclient process for each hop.
568
569
570
571
572
573
574
 * 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.
575
576
 *
 * Ports for hosts can be specified as host/port.
577
 */
578
static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
579
	char *userhostarg = NULL;
580
	char *hostbuf = NULL;
Matt Johnston's avatar
Matt Johnston committed
581
	char *last_hop = NULL;
582
	char *remainder = NULL;
Matt Johnston's avatar
Matt Johnston committed
583

584
585
586
587
588
589
590
591
592
	/* 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;
593
594
		hostbuf = m_malloc(len);
		snprintf(hostbuf, len, "%[email protected]%s", cli_opts.username, orighostarg);
595
	} else {
596
		hostbuf = m_strdup(orighostarg);
597
	}
598
	userhostarg = hostbuf;
599
600
601
602
603
604
605
606
607
608
609
610

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

611
	parse_hostname(userhostarg);
612
613
614
615

	if (last_hop) {
		/* Set up the proxycmd */
		unsigned int cmd_len = 0;
616
		char *passthrough_args = multihop_passthrough_args();
617
618
619
620
621
622
		if (cli_opts.proxycmd) {
			dropbear_exit("-J can't be used with multihop mode");
		}
		if (cli_opts.remoteport == NULL) {
			cli_opts.remoteport = "22";
		}
623
		cmd_len = strlen(argv0) + strlen(remainder) 
624
			+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
625
626
			+ strlen(passthrough_args)
			+ 30;
627
		cli_opts.proxycmd = m_malloc(cmd_len);
628
629
630
		snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", 
				argv0, cli_opts.remotehost, cli_opts.remoteport, 
				passthrough_args, remainder);
631
632
#ifndef DISABLE_ZLIB
		/* The stream will be incompressible since it's encrypted. */
633
		opts.compress_mode = DROPBEAR_COMPRESS_OFF;
634
#endif
635
		m_free(passthrough_args);
636
	}
637
	m_free(hostbuf);
638
}
639
#endif /* !DROPBEAR_CLI_MULTIHOP */
640

641
642
/* Parses a [[email protected]]hostname[/port] argument. */
static void parse_hostname(const char* orighostarg) {
Matt Johnston's avatar
Matt Johnston committed
643
	char *userhostarg = NULL;
644
	char *port = NULL;
Matt Johnston's avatar
Matt Johnston committed
645
646

	userhostarg = m_strdup(orighostarg);
Matt Johnston's avatar
Matt Johnston committed
647
648
649
650
651
652
653
654
655
656
657
658
659

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

663
	port = strchr(cli_opts.remotehost, '^');
664
	if (!port)  {
Matt Johnston's avatar
Matt Johnston committed
665
		/* legacy separator */
666
667
		port = strchr(cli_opts.remotehost, '/');
	}
668
669
670
671
672
	if (port) {
		*port = '\0';
		cli_opts.remoteport = port+1;
	}

Matt Johnston's avatar
Matt Johnston committed
673
674
675
	if (cli_opts.remotehost[0] == '\0') {
		dropbear_exit("Bad hostname");
	}
676
}
Matt Johnston's avatar
Matt Johnston committed
677

678
#if DROPBEAR_CLI_NETCAT
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
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;
	}
	
697
	if (m_str_to_uint(portstr, &cli_opts.netcat_port) == DROPBEAR_FAILURE) {
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
		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

715
716
717
718
719
720
721
static void fill_own_user() {
	uid_t uid;
	struct passwd *pw = NULL; 

	uid = getuid();

	pw = getpwuid(uid);
722
723
724
	if (pw && pw->pw_name != NULL) {
		cli_opts.own_user = m_strdup(pw->pw_name);
	} else {
iquaba's avatar
iquaba committed
725
		dropbear_log(LOG_INFO, "Warning: failed to identify current user. Trying anyway.");
726
		cli_opts.own_user = m_strdup("unknown");
727
728
729
730
	}

}

731
#if DROPBEAR_CLI_ANYTCPFWD
732
/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding
Matt Johnston's avatar
Matt Johnston committed
733
 * set, and add it to the forwarding list */
734
static void addforward(const char* origstr, m_list *fwdlist) {
Matt Johnston's avatar
Matt Johnston committed
735

736
737
	char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL;
	char * listenaddr = NULL;
Matt Johnston's avatar
Matt Johnston committed
738
739
	char * listenport = NULL;
	char * connectaddr = NULL;
740
	char * connectport = NULL;
741
	struct TCPFwdEntry* newfwd = NULL;
Matt Johnston's avatar
Matt Johnston committed
742
743
	char * str = NULL;

744
	TRACE(("enter addforward"))
Matt Johnston's avatar
Matt Johnston committed
745

746
747
	/* We need to split the original argument up. This var
	   is never free()d. */ 
Matt Johnston's avatar
Matt Johnston committed
748
749
	str = m_strdup(origstr);

750
	part1 = str;
Matt Johnston's avatar
Matt Johnston committed
751

752
753
754
	part2 = strchr(str, ':');
	if (part2 == NULL) {
		TRACE(("part2 == NULL"))
Matt Johnston's avatar
Matt Johnston committed
755
756
		goto fail;
	}
757
758
	*part2 = '\0';
	part2++;
Matt Johnston's avatar
Matt Johnston committed
759

760
761
762
	part3 = strchr(part2, ':');
	if (part3 == NULL) {
		TRACE(("part3 == NULL"))
Matt Johnston's avatar
Matt Johnston committed
763
764
		goto fail;
	}
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	*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;
	}

786
	newfwd = m_malloc(sizeof(struct TCPFwdEntry));
Matt Johnston's avatar
Matt Johnston committed
787
788
789

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

795
796
	if (m_str_to_uint(connectport, &newfwd->connectport) == DROPBEAR_FAILURE) {
		TRACE(("bad connectport strtoul"))
Matt Johnston's avatar
Matt Johnston committed
797
798
799
		goto fail;
	}

800
	newfwd->listenaddr = listenaddr;
Matt Johnston's avatar
Matt Johnston committed
801
802
803
	newfwd->connectaddr = connectaddr;

	if (newfwd->listenport > 65535) {
804
		TRACE(("listenport > 65535"))
Matt Johnston's avatar
Matt Johnston committed
805
806
807
808
		goto badport;
	}
		
	if (newfwd->connectport > 65535) {
809
		TRACE(("connectport > 65535"))
Matt Johnston's avatar
Matt Johnston committed
810
811
812
		goto badport;
	}

813
	newfwd->have_reply = 0;
814
	list_append(fwdlist, newfwd);
Matt Johnston's avatar
Matt Johnston committed
815

816
	TRACE(("leave addforward: done"))
Matt Johnston's avatar
Matt Johnston committed
817
818
819
820
821
822
823
824
825
	return;

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

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

static int match_extendedopt(const char** strptr, const char *optname) {
828
	int seen_eq = 0;
829
830
831
	int optlen = strlen(optname);
	const char *str = *strptr;

832
833
834
835
	while (isspace(*str)) {
		++str;
	}

Matt Johnston's avatar
Matt Johnston committed
836
	if (strncasecmp(str, optname, optlen) != 0) {
837
		return DROPBEAR_FAILURE;
Matt Johnston's avatar
Matt Johnston committed
838
	}
839
840
841

	str += optlen;

842
843
844
845
846
847
848
849
850
	while (isspace(*str) || (!seen_eq && *str == '=')) {
		if (*str == '=') {
			seen_eq = 1;
		}
		++str;
	}

	if (str-*strptr == optlen) {
		/* matched just a prefix of optname */
851
		return DROPBEAR_FAILURE;
852
853
	}

854
855
	*strptr = str;
	return DROPBEAR_SUCCESS;
856
857
}

Matt Johnston's avatar
Matt Johnston committed
858
859
static int parse_flag_value(const char *value) {
	if (strcmp(value, "yes") == 0 || strcmp(value, "true") == 0) {
860
		return 1;
Matt Johnston's avatar
Matt Johnston committed
861
	} else if (strcmp(value, "no") == 0 || strcmp(value, "false") == 0) {
862
		return 0;
Matt Johnston's avatar
Matt Johnston committed
863
	}
864
865
866
867
868
869
870
871

	dropbear_exit("Bad yes/no argument '%s'", value);
}

static void add_extendedopt(const char* origstr) {
	const char *optstr = origstr;

	if (strcmp(origstr, "help") == 0) {
872
		dropbear_log(LOG_INFO, "Available options:\n"
873
#if DROPBEAR_CLI_ANYTCPFWD
874
			"\tExitOnForwardFailure\n"
875
876
877
#endif
#ifndef DISABLE_SYSLOG
			"\tUseSyslog\n"
878
879
#endif
		);
880
881
882
		exit(EXIT_SUCCESS);
	}

883
#if DROPBEAR_CLI_ANYTCPFWD
884
885
886
887
888
889
	if (match_extendedopt(&optstr, "ExitOnForwardFailure") == DROPBEAR_SUCCESS) {
		cli_opts.exit_on_fwd_failure = parse_flag_value(optstr);
		return;
	}
#endif

890
891
892
893
894
895
896
#ifndef DISABLE_SYSLOG
	if (match_extendedopt(&optstr, "UseSyslog") == DROPBEAR_SUCCESS) {
		opts.usingsyslog = parse_flag_value(optstr);
		return;
	}
#endif

897
	dropbear_log(LOG_WARNING, "Ignoring unknown configuration option '%s'", origstr);
898
}