Coverage Report

Created: 2023-09-25 06:08

/src/dropbear/src/cli-runopts.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Dropbear - a SSH2 server
3
 * 
4
 * Copyright (c) 2002,2003 Matt Johnston
5
 * All rights reserved.
6
 * 
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 * 
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 * 
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE. */
24
25
#include "includes.h"
26
#include "runopts.h"
27
#include "signkey.h"
28
#include "buffer.h"
29
#include "dbutil.h"
30
#include "algo.h"
31
#include "tcpfwd.h"
32
#include "list.h"
33
34
cli_runopts cli_opts; /* GLOBAL */
35
36
static void printhelp(void);
37
static void parse_hostname(const char* orighostarg);
38
static void parse_multihop_hostname(const char* orighostarg, const char* argv0);
39
static void fill_own_user(void);
40
#if DROPBEAR_CLI_PUBKEY_AUTH
41
static void loadidentityfile(const char* filename, int warnfail);
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);
48
#endif
49
static void add_extendedopt(const char *str);
50
51
0
static void printhelp() {
52
53
0
  fprintf(stderr, "Dropbear SSH client v%s https://matt.ucc.asn.au/dropbear/dropbear.html\n"
54
0
#if DROPBEAR_CLI_MULTIHOP
55
0
          "Usage: %s [options] [user@]host[/port][,[user@]host/port],...] [command]\n"
56
#else
57
          "Usage: %s [options] [user@]host[/port] [command]\n"
58
#endif
59
0
          "-p <remoteport>\n"
60
0
          "-l <username>\n"
61
0
          "-t    Allocate a pty\n"
62
0
          "-T    Don't allocate a pty\n"
63
0
          "-N    Don't run a remote command\n"
64
0
          "-f    Run in background after auth\n"
65
0
          "-q    quiet, don't show remote banner\n"
66
0
          "-y    Always accept remote host key if unknown\n"
67
0
          "-y -y Don't perform any remote host key checking (caution)\n"
68
0
          "-s    Request a subsystem (use by external sftp)\n"
69
0
          "-o option     Set option in OpenSSH-like format ('-o help' to list options)\n"
70
0
#if DROPBEAR_CLI_PUBKEY_AUTH
71
0
          "-i <identityfile>   (multiple allowed, default %s)\n"
72
0
#endif
73
0
#if DROPBEAR_CLI_AGENTFWD
74
0
          "-A    Enable agent auth forwarding\n"
75
0
#endif
76
0
#if DROPBEAR_CLI_LOCALTCPFWD
77
0
          "-L <[listenaddress:]listenport:remotehost:remoteport> Local port forwarding\n"
78
0
          "-g    Allow remote hosts to connect to forwarded ports\n"
79
0
#endif
80
0
#if DROPBEAR_CLI_REMOTETCPFWD
81
0
          "-R <[listenaddress:]listenport:remotehost:remoteport> Remote port forwarding\n"
82
0
#endif
83
0
          "-W <receive_window_buffer> (default %d, larger may be faster, max 10MB)\n"
84
0
          "-K <keepalive>  (0 is never, default %d)\n"
85
0
          "-I <idle_timeout>  (0 is never, default %d)\n"
86
0
          "-z    disable QoS\n"
87
0
#if DROPBEAR_CLI_NETCAT
88
0
          "-B <endhost:endport> Netcat-alike forwarding\n"
89
0
#endif        
90
0
#if DROPBEAR_CLI_PROXYCMD
91
0
          "-J <proxy_program> Use program pipe rather than TCP connection\n"
92
0
#endif
93
0
#if DROPBEAR_USER_ALGO_LIST
94
0
          "-c <cipher list> Specify preferred ciphers ('-c help' to list options)\n"
95
0
          "-m <MAC list> Specify preferred MACs for packet verification (or '-m help')\n"
96
0
#endif
97
0
          "-b    [bind_address][:bind_port]\n"
98
0
          "-V    Version\n"
99
#if DEBUG_TRACE
100
          "-v    verbose (repeat for more verbose)\n"
101
#endif
102
0
          ,DROPBEAR_VERSION, cli_opts.progname,
103
0
#if DROPBEAR_CLI_PUBKEY_AUTH
104
0
          DROPBEAR_DEFAULT_CLI_AUTHKEY,
105
0
#endif
106
0
          DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
107
          
108
0
}
109
110
1
void cli_getopts(int argc, char ** argv) {
111
1
  unsigned int i, j;
112
1
  char ** next = NULL;
113
1
  enum {
114
1
    OPT_EXTENDED_OPTIONS,
115
1
#if DROPBEAR_CLI_PUBKEY_AUTH
116
1
    OPT_AUTHKEY,
117
1
#endif
118
1
#if DROPBEAR_CLI_LOCALTCPFWD
119
1
    OPT_LOCALTCPFWD,
120
1
#endif
121
1
#if DROPBEAR_CLI_REMOTETCPFWD
122
1
    OPT_REMOTETCPFWD,
123
1
#endif
124
1
#if DROPBEAR_CLI_NETCAT
125
1
    OPT_NETCAT,
126
1
#endif
127
    /* a flag (no arg) if 'next' is NULL, a string-valued option otherwise */
128
1
    OPT_OTHER
129
1
  } opt;
130
1
  unsigned int cmdlen;
131
132
1
  char* recv_window_arg = NULL;
133
1
  char* keepalive_arg = NULL;
134
1
  char* idle_timeout_arg = NULL;
135
1
  char *host_arg = NULL;
136
1
  char *bind_arg = NULL;
137
1
  char c;
138
139
  /* see printhelp() for options */
140
1
  cli_opts.progname = argv[0];
141
1
  cli_opts.remotehost = NULL;
142
1
  cli_opts.remoteport = NULL;
143
1
  cli_opts.username = NULL;
144
1
  cli_opts.cmd = NULL;
145
1
  cli_opts.no_cmd = 0;
146
1
  cli_opts.quiet = 0;
147
1
  cli_opts.backgrounded = 0;
148
1
  cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
149
1
  cli_opts.always_accept_key = 0;
150
1
  cli_opts.no_hostkey_check = 0;
151
1
  cli_opts.is_subsystem = 0;
152
1
#if DROPBEAR_CLI_PUBKEY_AUTH
153
1
  cli_opts.privkeys = list_new();
154
1
#endif
155
1
#if DROPBEAR_CLI_ANYTCPFWD
156
1
  cli_opts.exit_on_fwd_failure = 0;
157
1
#endif
158
1
  cli_opts.disable_trivial_auth = 0;
159
1
#if DROPBEAR_CLI_LOCALTCPFWD
160
1
  cli_opts.localfwds = list_new();
161
1
  opts.listen_fwd_all = 0;
162
1
#endif
163
1
#if DROPBEAR_CLI_REMOTETCPFWD
164
1
  cli_opts.remotefwds = list_new();
165
1
#endif
166
1
#if DROPBEAR_CLI_AGENTFWD
167
1
  cli_opts.agent_fwd = 0;
168
1
  cli_opts.agent_fd = -1;
169
1
  cli_opts.agent_keys_loaded = 0;
170
1
#endif
171
1
#if DROPBEAR_CLI_PROXYCMD
172
1
  cli_opts.proxycmd = NULL;
173
1
#endif
174
1
  cli_opts.bind_address = NULL;
175
1
  cli_opts.bind_port = NULL;
176
#ifndef DISABLE_ZLIB
177
  opts.compress_mode = DROPBEAR_COMPRESS_ON;
178
#endif
179
1
#if DROPBEAR_USER_ALGO_LIST
180
1
  opts.cipher_list = NULL;
181
1
  opts.mac_list = NULL;
182
1
#endif
183
1
#ifndef DISABLE_SYSLOG
184
1
  opts.usingsyslog = 0;
185
1
#endif
186
  /* not yet
187
  opts.ipv4 = 1;
188
  opts.ipv6 = 1;
189
  */
190
1
  opts.recv_window = DEFAULT_RECV_WINDOW;
191
1
  opts.keepalive_secs = DEFAULT_KEEPALIVE;
192
1
  opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
193
194
1
  fill_own_user();
195
196
3
  for (i = 1; i < (unsigned int)argc; i++) {
197
    /* Handle non-flag arguments such as hostname or commands for the remote host */
198
3
    if (argv[i][0] != '-')
199
2
    {
200
2
      if (host_arg == NULL) {
201
1
        host_arg = argv[i];
202
1
        continue;
203
1
      }
204
      /* Commands to pass to the remote host. No more flag handling,
205
      commands are consumed below */
206
1
      break;
207
2
    }
208
209
    /* Begins with '-' */
210
1
    opt = OPT_OTHER;
211
2
    for (j = 1; (c = argv[i][j]) != '\0' && !next && opt == OPT_OTHER; j++) {
212
1
      switch (c) {
213
1
        case 'y': /* always accept the remote hostkey */
214
1
          if (cli_opts.always_accept_key) {
215
            /* twice means no checking at all */
216
0
            cli_opts.no_hostkey_check = 1;
217
0
          }
218
1
          cli_opts.always_accept_key = 1;
219
1
          break;
220
0
        case 'q': /* quiet */
221
0
          cli_opts.quiet = 1;
222
0
          break;
223
0
        case 'p': /* remoteport */
224
0
          next = (char**)&cli_opts.remoteport;
225
0
          break;
226
0
#if DROPBEAR_CLI_PUBKEY_AUTH
227
0
        case 'i': /* an identityfile */
228
0
          opt = OPT_AUTHKEY;
229
0
          break;
230
0
#endif
231
0
        case 't': /* we want a pty */
232
0
          cli_opts.wantpty = 1;
233
0
          break;
234
0
        case 'T': /* don't want a pty */
235
0
          cli_opts.wantpty = 0;
236
0
          break;
237
0
        case 'N':
238
0
          cli_opts.no_cmd = 1;
239
0
          break;
240
0
        case 'f':
241
0
          cli_opts.backgrounded = 1;
242
0
          break;
243
0
        case 's':
244
0
          cli_opts.is_subsystem = 1;
245
0
          break;
246
0
        case 'o':
247
0
          opt = OPT_EXTENDED_OPTIONS;
248
0
          break;
249
0
#if DROPBEAR_CLI_LOCALTCPFWD
250
0
        case 'L':
251
0
          opt = OPT_LOCALTCPFWD;
252
0
          break;
253
0
        case 'g':
254
0
          opts.listen_fwd_all = 1;
255
0
          break;
256
0
#endif
257
0
#if DROPBEAR_CLI_REMOTETCPFWD
258
0
        case 'R':
259
0
          opt = OPT_REMOTETCPFWD;
260
0
          break;
261
0
#endif
262
0
#if DROPBEAR_CLI_NETCAT
263
0
        case 'B':
264
0
          opt = OPT_NETCAT;
265
0
          break;
266
0
#endif
267
0
#if DROPBEAR_CLI_PROXYCMD
268
0
        case 'J':
269
0
          next = &cli_opts.proxycmd;
270
0
          break;
271
0
#endif
272
0
        case 'l':
273
0
          next = &cli_opts.username;
274
0
          break;
275
0
        case 'h':
276
0
          printhelp();
277
0
          exit(EXIT_SUCCESS);
278
0
          break;
279
0
        case 'u':
280
          /* backwards compatibility with old urandom option */
281
0
          break;
282
0
        case 'W':
283
0
          next = &recv_window_arg;
284
0
          break;
285
0
        case 'K':
286
0
          next = &keepalive_arg;
287
0
          break;
288
0
        case 'I':
289
0
          next = &idle_timeout_arg;
290
0
          break;
291
0
#if DROPBEAR_CLI_AGENTFWD
292
0
        case 'A':
293
0
          cli_opts.agent_fwd = 1;
294
0
          break;
295
0
#endif
296
0
#if DROPBEAR_USER_ALGO_LIST
297
0
        case 'c':
298
0
          next = &opts.cipher_list;
299
0
          break;
300
0
        case 'm':
301
0
          next = &opts.mac_list;
302
0
          break;
303
0
#endif
304
#if DEBUG_TRACE
305
        case 'v':
306
          debug_trace++;
307
          break;
308
#endif
309
0
        case 'F':
310
0
        case 'e':
311
#if !DROPBEAR_USER_ALGO_LIST
312
        case 'c':
313
        case 'm':
314
#endif
315
0
        case 'D':
316
#if !DROPBEAR_CLI_REMOTETCPFWD
317
        case 'R':
318
#endif
319
#if !DROPBEAR_CLI_LOCALTCPFWD
320
        case 'L':
321
#endif
322
0
        case 'V':
323
0
          print_version();
324
0
          exit(EXIT_SUCCESS);
325
0
          break;
326
0
        case 'b':
327
0
          next = &bind_arg;
328
0
          break;
329
0
        case 'z':
330
0
          opts.disable_ip_tos = 1;
331
0
          break;
332
0
        default:
333
0
          fprintf(stderr,
334
0
            "WARNING: Ignoring unknown option -%c\n", c);
335
0
          break;
336
1
      } /* Switch */
337
1
    }
338
339
1
    if (!next && opt == OPT_OTHER) /* got a flag */
340
1
      continue;
341
342
0
    if (c == '\0') {
343
0
      i++;
344
0
      j = 0;
345
0
      if (!argv[i])
346
0
        dropbear_exit("Missing argument");
347
0
    }
348
349
0
    if (opt == OPT_EXTENDED_OPTIONS) {
350
0
      TRACE(("opt extended"))
351
0
      add_extendedopt(&argv[i][j]);
352
0
    }
353
0
    else
354
0
#if DROPBEAR_CLI_PUBKEY_AUTH
355
0
    if (opt == OPT_AUTHKEY) {
356
0
      TRACE(("opt authkey"))
357
0
      loadidentityfile(&argv[i][j], 1);
358
0
    }
359
0
    else
360
0
#endif
361
0
#if DROPBEAR_CLI_REMOTETCPFWD
362
0
    if (opt == OPT_REMOTETCPFWD) {
363
0
      TRACE(("opt remotetcpfwd"))
364
0
      addforward(&argv[i][j], cli_opts.remotefwds);
365
0
    }
366
0
    else
367
0
#endif
368
0
#if DROPBEAR_CLI_LOCALTCPFWD
369
0
    if (opt == OPT_LOCALTCPFWD) {
370
0
      TRACE(("opt localtcpfwd"))
371
0
      addforward(&argv[i][j], cli_opts.localfwds);
372
0
    }
373
0
    else
374
0
#endif
375
0
#if DROPBEAR_CLI_NETCAT
376
0
    if (opt == OPT_NETCAT) {
377
0
      TRACE(("opt netcat"))
378
0
      add_netcat(&argv[i][j]);
379
0
    }
380
0
    else
381
0
#endif
382
0
    if (next) {
383
      /* The previous flag set a value to assign */
384
0
      *next = &argv[i][j];
385
0
      if (*next == NULL)
386
0
        dropbear_exit("Invalid null argument");
387
0
      next = NULL;
388
0
    }
389
0
  }
390
391
1
#if DROPBEAR_USER_ALGO_LIST
392
  /* -c help doesn't need a hostname */
393
1
  parse_ciphers_macs();
394
1
#endif
395
396
  /* Done with options/flags; now handle the hostname (which may not
397
   * start with a hyphen) and optional command */
398
399
1
  if (host_arg == NULL) { /* missing hostname */
400
0
    printhelp();
401
0
    exit(EXIT_FAILURE);
402
0
  }
403
1
  TRACE(("host is: %s", host_arg))
404
405
1
  if (i < (unsigned int)argc) {
406
    /* Build the command to send */
407
1
    cmdlen = 0;
408
2
    for (j = i; j < (unsigned int)argc; j++)
409
1
      cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */
410
411
    /* Allocate the space */
412
1
    cli_opts.cmd = (char*)m_malloc(cmdlen);
413
1
    cli_opts.cmd[0] = '\0';
414
415
    /* Append all the bits */
416
2
    for (j = i; j < (unsigned int)argc; j++) {
417
1
      strlcat(cli_opts.cmd, argv[j], cmdlen);
418
1
      strlcat(cli_opts.cmd, " ", cmdlen);
419
1
    }
420
    /* It'll be null-terminated here */
421
1
    TRACE(("cmd is: %s", cli_opts.cmd))
422
1
  }
423
424
  /* And now a few sanity checks and setup */
425
426
1
#if DROPBEAR_CLI_PROXYCMD
427
1
  if (cli_opts.proxycmd) {
428
    /* To match the common path of m_freeing it */
429
0
    cli_opts.proxycmd = m_strdup(cli_opts.proxycmd);
430
0
  }
431
1
#endif
432
433
1
  if (cli_opts.remoteport == NULL) {
434
1
    cli_opts.remoteport = "22";
435
1
  }
436
437
1
  if (bind_arg) {
438
0
    if (split_address_port(bind_arg,
439
0
      &cli_opts.bind_address, &cli_opts.bind_port)
440
0
        == DROPBEAR_FAILURE) {
441
0
      dropbear_exit("Bad -b argument");
442
0
    }
443
0
  }
444
445
  /* If not explicitly specified with -t or -T, we don't want a pty if
446
   * there's a command, but we do otherwise */
447
1
  if (cli_opts.wantpty == 9) {
448
1
    if (cli_opts.cmd == NULL) {
449
0
      cli_opts.wantpty = 1;
450
1
    } else {
451
1
      cli_opts.wantpty = 0;
452
1
    }
453
1
  }
454
455
1
  if (cli_opts.backgrounded && cli_opts.cmd == NULL
456
1
      && cli_opts.no_cmd == 0) {
457
0
    dropbear_exit("Command required for -f");
458
0
  }
459
460
1
  if (recv_window_arg) {
461
0
    parse_recv_window(recv_window_arg);
462
0
  }
463
1
  if (keepalive_arg) {
464
0
    unsigned int val;
465
0
    if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
466
0
      dropbear_exit("Bad keepalive '%s'", keepalive_arg);
467
0
    }
468
0
    opts.keepalive_secs = val;
469
0
  }
470
471
1
  if (idle_timeout_arg) {
472
0
    unsigned int val;
473
0
    if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
474
0
      dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
475
0
    }
476
0
    opts.idle_timeout_secs = val;
477
0
  }
478
479
1
#if DROPBEAR_CLI_NETCAT
480
1
  if (cli_opts.cmd && cli_opts.netcat_host) {
481
0
    dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
482
0
  }
483
1
#endif
484
485
  /* The hostname gets set up last, since
486
   * in multi-hop mode it will require knowledge
487
   * of other flags such as -i */
488
1
#if DROPBEAR_CLI_MULTIHOP
489
1
  parse_multihop_hostname(host_arg, argv[0]);
490
#else
491
  parse_hostname(host_arg);
492
#endif
493
494
  /* We don't want to include default id_dropbear as a
495
     -i argument for multihop, so handle it later. */
496
1
#if (DROPBEAR_CLI_PUBKEY_AUTH)
497
1
  {
498
1
    char *expand_path = expand_homedir_path(DROPBEAR_DEFAULT_CLI_AUTHKEY);
499
1
    loadidentityfile(expand_path, 0);
500
1
    m_free(expand_path);
501
1
  }
502
1
#endif
503
504
1
}
505
506
#if DROPBEAR_CLI_PUBKEY_AUTH
507
1
static void loadidentityfile(const char* filename, int warnfail) {
508
1
  sign_key *key;
509
1
  enum signkey_type keytype;
510
511
1
  TRACE(("loadidentityfile %s", filename))
512
513
1
  key = new_sign_key();
514
1
  keytype = DROPBEAR_SIGNKEY_ANY;
515
1
  if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
516
1
    if (warnfail) {
517
0
      dropbear_log(LOG_WARNING, "Failed loading keyfile '%s'\n", filename);
518
0
    }
519
1
    sign_key_free(key);
520
1
  } else {
521
0
    key->type = keytype;
522
0
    key->source = SIGNKEY_SOURCE_RAW_FILE;
523
0
    key->filename = m_strdup(filename);
524
0
    list_append(cli_opts.privkeys, key);
525
0
  }
526
1
}
527
#endif
528
529
#if DROPBEAR_CLI_MULTIHOP
530
531
static char*
532
0
multihop_passthrough_args() {
533
0
  char *ret;
534
0
  unsigned int len, total;
535
0
  m_list_elem *iter;
536
  /* Fill out -i, -y, -W options that make sense for all
537
   * the intermediate processes */
538
0
  len = 30; /* space for "-q -y -y -W <size>\0" */
539
0
#if DROPBEAR_CLI_PUBKEY_AUTH
540
0
  for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
541
0
  {
542
0
    sign_key * key = (sign_key*)iter->item;
543
0
    len += 3 + strlen(key->filename);
544
0
  }
545
0
#endif /* DROPBEAR_CLI_PUBKEY_AUTH */
546
0
  if (cli_opts.proxycmd) {
547
    /* "-J 'cmd'" */
548
0
    len += 6 + strlen(cli_opts.proxycmd);
549
0
  }
550
551
0
  ret = m_malloc(len);
552
0
  total = 0;
553
554
0
  if (cli_opts.quiet) {
555
0
    total += m_snprintf(ret+total, len-total, "-q ");
556
0
  }
557
558
0
  if (cli_opts.no_hostkey_check) {
559
0
    total += m_snprintf(ret+total, len-total, "-y -y ");
560
0
  } else if (cli_opts.always_accept_key) {
561
0
    total += m_snprintf(ret+total, len-total, "-y ");
562
0
  }
563
564
0
  if (cli_opts.proxycmd) {
565
0
    total += m_snprintf(ret+total, len-total, "-J '%s' ", cli_opts.proxycmd);
566
0
  }
567
568
0
  if (opts.recv_window != DEFAULT_RECV_WINDOW) {
569
0
    total += m_snprintf(ret+total, len-total, "-W %u ", opts.recv_window);
570
0
  }
571
572
0
#if DROPBEAR_CLI_PUBKEY_AUTH
573
0
  for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
574
0
  {
575
0
    sign_key * key = (sign_key*)iter->item;
576
0
    total += m_snprintf(ret+total, len-total, "-i %s ", key->filename);
577
0
  }
578
0
#endif /* DROPBEAR_CLI_PUBKEY_AUTH */
579
580
0
  return ret;
581
0
}
582
583
/* Sets up 'onion-forwarding' connections. This will spawn
584
 * a separate dbclient process for each hop.
585
 * As an example, if the cmdline is
586
 *   dbclient wrt,madako,canyons
587
 * then we want to run:
588
 *   dbclient -J "dbclient -B canyons:22 wrt,madako" canyons
589
 * and then the inner dbclient will recursively run:
590
 *   dbclient -J "dbclient -B madako:22 wrt" madako
591
 * etc for as many hosts as we want.
592
 *
593
 * Note that "-J" arguments aren't actually used, instead
594
 * below sets cli_opts.proxycmd directly.
595
 *
596
 * Ports for hosts can be specified as host/port.
597
 */
598
1
static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
599
1
  char *userhostarg = NULL;
600
1
  char *hostbuf = NULL;
601
1
  char *last_hop = NULL;
602
1
  char *remainder = NULL;
603
604
  /* both scp and rsync parse a user@host argument
605
   * and turn it into "-l user host". This breaks
606
   * for our multihop syntax, so we suture it back together.
607
   * This will break usernames that have both '@' and ',' in them,
608
   * though that should be fairly uncommon. */
609
1
  if (cli_opts.username 
610
1
      && strchr(cli_opts.username, ',') 
611
1
      && strchr(cli_opts.username, '@')) {
612
0
    unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2;
613
0
    hostbuf = m_malloc(len);
614
0
    m_snprintf(hostbuf, len, "%s@%s", cli_opts.username, orighostarg);
615
1
  } else {
616
1
    hostbuf = m_strdup(orighostarg);
617
1
  }
618
1
  userhostarg = hostbuf;
619
620
1
  last_hop = strrchr(userhostarg, ',');
621
1
  if (last_hop) {
622
0
    if (last_hop == userhostarg) {
623
0
      dropbear_exit("Bad multi-hop hostnames");
624
0
    }
625
0
    *last_hop = '\0';
626
0
    last_hop++;
627
0
    remainder = userhostarg;
628
0
    userhostarg = last_hop;
629
0
  }
630
631
1
  parse_hostname(userhostarg);
632
633
1
  if (last_hop) {
634
    /* Set up the proxycmd */
635
0
    unsigned int cmd_len = 0;
636
0
    char *passthrough_args = multihop_passthrough_args();
637
0
    if (cli_opts.remoteport == NULL) {
638
0
      cli_opts.remoteport = "22";
639
0
    }
640
0
    cmd_len = strlen(argv0) + strlen(remainder)
641
0
      + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
642
0
      + strlen(passthrough_args)
643
0
      + 30;
644
    /* replace proxycmd. old -J arguments have been copied
645
       to passthrough_args */
646
0
    cli_opts.proxycmd = m_realloc(cli_opts.proxycmd, cmd_len);
647
0
    m_snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s",
648
0
        argv0, cli_opts.remotehost, cli_opts.remoteport,
649
0
        passthrough_args, remainder);
650
#ifndef DISABLE_ZLIB
651
    /* The stream will be incompressible since it's encrypted. */
652
    opts.compress_mode = DROPBEAR_COMPRESS_OFF;
653
#endif
654
0
    m_free(passthrough_args);
655
0
  }
656
1
  m_free(hostbuf);
657
1
}
658
#endif /* !DROPBEAR_CLI_MULTIHOP */
659
660
/* Parses a [user@]hostname[/port] argument. */
661
1
static void parse_hostname(const char* orighostarg) {
662
1
  char *userhostarg = NULL;
663
1
  char *port = NULL;
664
665
1
  userhostarg = m_strdup(orighostarg);
666
667
1
  cli_opts.remotehost = strchr(userhostarg, '@');
668
1
  if (cli_opts.remotehost == NULL) {
669
    /* no username portion, the cli-auth.c code can figure the
670
     * local user's name */
671
1
    cli_opts.remotehost = userhostarg;
672
1
  } else {
673
0
    cli_opts.remotehost[0] = '\0'; /* Split the user/host */
674
0
    cli_opts.remotehost++;
675
0
    cli_opts.username = userhostarg;
676
0
  }
677
678
1
  if (cli_opts.username == NULL) {
679
1
    cli_opts.username = m_strdup(cli_opts.own_user);
680
1
  }
681
682
1
  port = strchr(cli_opts.remotehost, '^');
683
1
  if (!port)  {
684
    /* legacy separator */
685
1
    port = strchr(cli_opts.remotehost, '/');
686
1
  }
687
1
  if (port) {
688
0
    *port = '\0';
689
0
    cli_opts.remoteport = port+1;
690
0
  }
691
692
1
  if (cli_opts.remotehost[0] == '\0') {
693
0
    dropbear_exit("Bad hostname");
694
0
  }
695
1
}
696
697
#if DROPBEAR_CLI_NETCAT
698
0
static void add_netcat(const char* origstr) {
699
0
  char *portstr = NULL;
700
  
701
0
  char * str = m_strdup(origstr);
702
  
703
0
  portstr = strchr(str, ':');
704
0
  if (portstr == NULL) {
705
0
    TRACE(("No netcat port"))
706
0
    goto fail;
707
0
  }
708
0
  *portstr = '\0';
709
0
  portstr++;
710
  
711
0
  if (strchr(portstr, ':')) {
712
0
    TRACE(("Multiple netcat colons"))
713
0
    goto fail;
714
0
  }
715
  
716
0
  if (m_str_to_uint(portstr, &cli_opts.netcat_port) == DROPBEAR_FAILURE) {
717
0
    TRACE(("bad netcat port"))
718
0
    goto fail;
719
0
  }
720
  
721
0
  if (cli_opts.netcat_port > 65535) {
722
0
    TRACE(("too large netcat port"))
723
0
    goto fail;
724
0
  }
725
  
726
0
  cli_opts.netcat_host = str;
727
0
  return;
728
  
729
0
fail:
730
0
  dropbear_exit("Bad netcat endpoint '%s'", origstr);
731
0
}
732
#endif
733
734
1
static void fill_own_user() {
735
1
  uid_t uid;
736
1
  struct passwd *pw = NULL; 
737
738
1
  uid = getuid();
739
740
1
  pw = getpwuid(uid);
741
1
  if (pw && pw->pw_name != NULL) {
742
1
    cli_opts.own_user = m_strdup(pw->pw_name);
743
1
  } else {
744
0
    dropbear_log(LOG_INFO, "Warning: failed to identify current user. Trying anyway.");
745
0
    cli_opts.own_user = m_strdup("unknown");
746
0
  }
747
748
1
}
749
750
#if DROPBEAR_CLI_ANYTCPFWD
751
/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding
752
 * set, and add it to the forwarding list */
753
0
static void addforward(const char* origstr, m_list *fwdlist) {
754
755
0
  char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL;
756
0
  char * listenaddr = NULL;
757
0
  char * listenport = NULL;
758
0
  char * connectaddr = NULL;
759
0
  char * connectport = NULL;
760
0
  struct TCPFwdEntry* newfwd = NULL;
761
0
  char * str = NULL;
762
763
0
  TRACE(("enter addforward"))
764
765
  /* We need to split the original argument up. This var
766
     is never free()d. */ 
767
0
  str = m_strdup(origstr);
768
769
0
  part1 = str;
770
771
0
  part2 = strchr(str, ':');
772
0
  if (part2 == NULL) {
773
0
    TRACE(("part2 == NULL"))
774
0
    goto fail;
775
0
  }
776
0
  *part2 = '\0';
777
0
  part2++;
778
779
0
  part3 = strchr(part2, ':');
780
0
  if (part3 == NULL) {
781
0
    TRACE(("part3 == NULL"))
782
0
    goto fail;
783
0
  }
784
0
  *part3 = '\0';
785
0
  part3++;
786
787
0
  part4 = strchr(part3, ':');
788
0
  if (part4) {
789
0
    *part4 = '\0';
790
0
    part4++;
791
0
  }
792
793
0
  if (part4) {
794
0
    listenaddr = part1;
795
0
    listenport = part2;
796
0
    connectaddr = part3;
797
0
    connectport = part4;
798
0
  } else {
799
0
    listenaddr = NULL;
800
0
    listenport = part1;
801
0
    connectaddr = part2;
802
0
    connectport = part3;
803
0
  }
804
805
0
  newfwd = m_malloc(sizeof(struct TCPFwdEntry));
806
807
  /* Now we check the ports - note that the port ints are unsigned,
808
   * the check later only checks for >= MAX_PORT */
809
0
  if (m_str_to_uint(listenport, &newfwd->listenport) == DROPBEAR_FAILURE) {
810
0
    TRACE(("bad listenport strtoul"))
811
0
    goto fail;
812
0
  }
813
814
0
  if (m_str_to_uint(connectport, &newfwd->connectport) == DROPBEAR_FAILURE) {
815
0
    TRACE(("bad connectport strtoul"))
816
0
    goto fail;
817
0
  }
818
819
0
  newfwd->listenaddr = listenaddr;
820
0
  newfwd->connectaddr = connectaddr;
821
822
0
  if (newfwd->listenport > 65535) {
823
0
    TRACE(("listenport > 65535"))
824
0
    goto badport;
825
0
  }
826
    
827
0
  if (newfwd->connectport > 65535) {
828
0
    TRACE(("connectport > 65535"))
829
0
    goto badport;
830
0
  }
831
832
0
  newfwd->have_reply = 0;
833
0
  list_append(fwdlist, newfwd);
834
835
0
  TRACE(("leave addforward: done"))
836
0
  return;
837
838
0
fail:
839
0
  dropbear_exit("Bad TCP forward '%s'", origstr);
840
841
0
badport:
842
0
  dropbear_exit("Bad TCP port in '%s'", origstr);
843
0
}
844
#endif
845
846
0
static int match_extendedopt(const char** strptr, const char *optname) {
847
0
  int seen_eq = 0;
848
0
  int optlen = strlen(optname);
849
0
  const char *str = *strptr;
850
851
0
  while (isspace(*str)) {
852
0
    ++str;
853
0
  }
854
855
0
  if (strncasecmp(str, optname, optlen) != 0) {
856
0
    return DROPBEAR_FAILURE;
857
0
  }
858
859
0
  str += optlen;
860
861
0
  while (isspace(*str) || (!seen_eq && *str == '=')) {
862
0
    if (*str == '=') {
863
0
      seen_eq = 1;
864
0
    }
865
0
    ++str;
866
0
  }
867
868
0
  if (str-*strptr == optlen) {
869
    /* matched just a prefix of optname */
870
0
    return DROPBEAR_FAILURE;
871
0
  }
872
873
0
  *strptr = str;
874
0
  return DROPBEAR_SUCCESS;
875
0
}
876
877
0
static int parse_flag_value(const char *value) {
878
0
  if (strcmp(value, "yes") == 0 || strcmp(value, "true") == 0) {
879
0
    return 1;
880
0
  } else if (strcmp(value, "no") == 0 || strcmp(value, "false") == 0) {
881
0
    return 0;
882
0
  }
883
884
0
  dropbear_exit("Bad yes/no argument '%s'", value);
885
0
}
886
887
0
static void add_extendedopt(const char* origstr) {
888
0
  const char *optstr = origstr;
889
890
0
  if (strcmp(origstr, "help") == 0) {
891
0
    dropbear_log(LOG_INFO, "Available options:\n"
892
0
#if DROPBEAR_CLI_ANYTCPFWD
893
0
      "\tExitOnForwardFailure\n"
894
0
#endif
895
0
      "\tDisableTrivialAuth\n"
896
0
#ifndef DISABLE_SYSLOG
897
0
      "\tUseSyslog\n"
898
0
#endif
899
0
      "\tPort\n"
900
0
    );
901
0
    exit(EXIT_SUCCESS);
902
0
  }
903
904
0
#if DROPBEAR_CLI_ANYTCPFWD
905
0
  if (match_extendedopt(&optstr, "ExitOnForwardFailure") == DROPBEAR_SUCCESS) {
906
0
    cli_opts.exit_on_fwd_failure = parse_flag_value(optstr);
907
0
    return;
908
0
  }
909
0
#endif
910
911
0
#ifndef DISABLE_SYSLOG
912
0
  if (match_extendedopt(&optstr, "UseSyslog") == DROPBEAR_SUCCESS) {
913
0
    opts.usingsyslog = parse_flag_value(optstr);
914
0
    return;
915
0
  }
916
0
#endif
917
918
0
  if (match_extendedopt(&optstr, "Port") == DROPBEAR_SUCCESS) {
919
0
    cli_opts.remoteport = optstr;
920
0
    return;
921
0
  }
922
923
0
  if (match_extendedopt(&optstr, "DisableTrivialAuth") == DROPBEAR_SUCCESS) {
924
0
    cli_opts.disable_trivial_auth = parse_flag_value(optstr);
925
0
    return;
926
0
  }
927
928
0
  dropbear_log(LOG_WARNING, "Ignoring unknown configuration option '%s'", origstr);
929
0
}