cli-runopts.c 22 KB

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