cli-runopts.c 22 KB

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