sshverstring.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. * Code to handle the initial SSH version string exchange.
  3. */
  4. #include <assert.h>
  5. #include <string.h>
  6. #include <stdlib.h>
  7. #include "putty.h"
  8. #include "ssh.h"
  9. #include "sshbpp.h"
  10. #include "sshcr.h"
  11. #define PREFIX_MAXLEN 64
  12. struct ssh_verstring_state {
  13. int crState;
  14. Conf *conf;
  15. Frontend *frontend;
  16. ptrlen prefix_wanted;
  17. char *our_protoversion;
  18. struct ssh_version_receiver *receiver;
  19. int send_early;
  20. int found_prefix;
  21. int major_protoversion;
  22. int remote_bugs;
  23. char prefix[PREFIX_MAXLEN];
  24. char *vstring;
  25. int vslen, vstrsize;
  26. char *protoversion;
  27. const char *softwareversion;
  28. char *our_vstring;
  29. int i;
  30. BinaryPacketProtocol bpp;
  31. };
  32. static void ssh_verstring_free(BinaryPacketProtocol *bpp);
  33. static void ssh_verstring_handle_input(BinaryPacketProtocol *bpp);
  34. static PktOut *ssh_verstring_new_pktout(int type);
  35. static void ssh_verstring_format_packet(BinaryPacketProtocol *bpp, PktOut *);
  36. static const struct BinaryPacketProtocolVtable ssh_verstring_vtable = {
  37. ssh_verstring_free,
  38. ssh_verstring_handle_input,
  39. ssh_verstring_new_pktout,
  40. ssh_verstring_format_packet,
  41. };
  42. static void ssh_detect_bugs(struct ssh_verstring_state *s);
  43. static int ssh_version_includes_v1(const char *ver);
  44. static int ssh_version_includes_v2(const char *ver);
  45. BinaryPacketProtocol *ssh_verstring_new(
  46. Conf *conf, Frontend *frontend, int bare_connection_mode,
  47. const char *protoversion, struct ssh_version_receiver *rcv)
  48. {
  49. struct ssh_verstring_state *s = snew(struct ssh_verstring_state);
  50. memset(s, 0, sizeof(struct ssh_verstring_state));
  51. if (!bare_connection_mode) {
  52. s->prefix_wanted = PTRLEN_LITERAL("SSH-");
  53. } else {
  54. /*
  55. * Ordinary SSH begins with the banner "SSH-x.y-...". Here,
  56. * we're going to be speaking just the ssh-connection
  57. * subprotocol, extracted and given a trivial binary packet
  58. * protocol, so we need a new banner.
  59. *
  60. * The new banner is like the ordinary SSH banner, but
  61. * replaces the prefix 'SSH-' at the start with a new name. In
  62. * proper SSH style (though of course this part of the proper
  63. * SSH protocol _isn't_ subject to this kind of
  64. * DNS-domain-based extension), we define the new name in our
  65. * extension space.
  66. */
  67. s->prefix_wanted = PTRLEN_LITERAL(
  68. "[email protected]");
  69. }
  70. assert(s->prefix_wanted.len <= PREFIX_MAXLEN);
  71. s->conf = conf_copy(conf);
  72. s->frontend = frontend;
  73. s->our_protoversion = dupstr(protoversion);
  74. s->receiver = rcv;
  75. /*
  76. * We send our version string early if we can. But if it includes
  77. * SSH-1, we can't, because we have to take the other end into
  78. * account too (see below).
  79. */
  80. s->send_early = !ssh_version_includes_v1(protoversion);
  81. s->bpp.vt = &ssh_verstring_vtable;
  82. return &s->bpp;
  83. }
  84. void ssh_verstring_free(BinaryPacketProtocol *bpp)
  85. {
  86. struct ssh_verstring_state *s =
  87. FROMFIELD(bpp, struct ssh_verstring_state, bpp);
  88. conf_free(s->conf);
  89. sfree(s->vstring);
  90. sfree(s->protoversion);
  91. sfree(s->our_vstring);
  92. sfree(s->our_protoversion);
  93. sfree(s);
  94. }
  95. static int ssh_versioncmp(const char *a, const char *b)
  96. {
  97. char *ae, *be;
  98. unsigned long av, bv;
  99. av = strtoul(a, &ae, 10);
  100. bv = strtoul(b, &be, 10);
  101. if (av != bv)
  102. return (av < bv ? -1 : +1);
  103. if (*ae == '.')
  104. ae++;
  105. if (*be == '.')
  106. be++;
  107. av = strtoul(ae, &ae, 10);
  108. bv = strtoul(be, &be, 10);
  109. if (av != bv)
  110. return (av < bv ? -1 : +1);
  111. return 0;
  112. }
  113. static int ssh_version_includes_v1(const char *ver)
  114. {
  115. return ssh_versioncmp(ver, "2.0") < 0;
  116. }
  117. static int ssh_version_includes_v2(const char *ver)
  118. {
  119. return ssh_versioncmp(ver, "1.99") >= 0;
  120. }
  121. #define vs_logevent(printf_args) \
  122. logevent_and_free(s->frontend, dupprintf printf_args)
  123. static void ssh_verstring_send(struct ssh_verstring_state *s)
  124. {
  125. char *p;
  126. int sv_pos;
  127. /*
  128. * Construct our outgoing version string.
  129. */
  130. s->our_vstring = dupprintf(
  131. "%.*s%s-%s",
  132. (int)s->prefix_wanted.len, (const char *)s->prefix_wanted.ptr,
  133. s->our_protoversion, sshver);
  134. sv_pos = s->prefix_wanted.len + strlen(s->our_protoversion) + 1;
  135. /* Convert minus signs and spaces in the software version string
  136. * into underscores. */
  137. for (p = s->our_vstring + sv_pos; *p; p++) {
  138. if (*p == '-' || *p == ' ')
  139. *p = '_';
  140. }
  141. #ifdef FUZZING
  142. /*
  143. * Replace the first character of the string with an "I" if we're
  144. * compiling this code for fuzzing - i.e. the protocol prefix
  145. * becomes "ISH-" instead of "SSH-".
  146. *
  147. * This is irrelevant to any real client software (the only thing
  148. * reading the output of PuTTY built for fuzzing is the fuzzer,
  149. * which can adapt to whatever it sees anyway). But it's a safety
  150. * precaution making it difficult to accidentally run such a
  151. * version of PuTTY (which would be hugely insecure) against a
  152. * live peer implementation.
  153. *
  154. * (So the replacement prefix "ISH" notionally stands for
  155. * 'Insecure Shell', of course.)
  156. */
  157. s->our_vstring[0] = 'I';
  158. #endif
  159. /*
  160. * Now send that version string, plus trailing \r\n or just \n
  161. * (the latter in SSH-1 mode).
  162. */
  163. bufchain_add(s->bpp.out_raw, s->our_vstring, strlen(s->our_vstring));
  164. if (ssh_version_includes_v2(s->our_protoversion))
  165. bufchain_add(s->bpp.out_raw, "\015", 1);
  166. bufchain_add(s->bpp.out_raw, "\012", 1);
  167. vs_logevent(("We claim version: %s", s->our_vstring));
  168. }
  169. void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
  170. {
  171. struct ssh_verstring_state *s =
  172. FROMFIELD(bpp, struct ssh_verstring_state, bpp);
  173. crBegin(s->crState);
  174. /*
  175. * If we're sending our version string up front before seeing the
  176. * other side's, then do it now.
  177. */
  178. if (s->send_early)
  179. ssh_verstring_send(s);
  180. /*
  181. * Search for a line beginning with the protocol name prefix in
  182. * the input.
  183. */
  184. s->i = 0;
  185. while (1) {
  186. /*
  187. * Every time round this loop, we're at the start of a new
  188. * line, so look for the prefix.
  189. */
  190. crMaybeWaitUntilV(bufchain_size(s->bpp.in_raw) >=
  191. s->prefix_wanted.len);
  192. bufchain_fetch(s->bpp.in_raw, s->prefix, s->prefix_wanted.len);
  193. if (!memcmp(s->prefix, s->prefix_wanted.ptr, s->prefix_wanted.len)) {
  194. bufchain_consume(s->bpp.in_raw, s->prefix_wanted.len);
  195. break;
  196. }
  197. /*
  198. * If we didn't find it, consume data until we see a newline.
  199. */
  200. while (1) {
  201. int len;
  202. void *data;
  203. char *nl;
  204. crMaybeWaitUntilV(bufchain_size(s->bpp.in_raw) > 0);
  205. bufchain_prefix(s->bpp.in_raw, &data, &len);
  206. if ((nl = memchr(data, '\012', len)) != NULL) {
  207. bufchain_consume(s->bpp.in_raw, nl - (char *)data + 1);
  208. break;
  209. } else {
  210. bufchain_consume(s->bpp.in_raw, len);
  211. }
  212. }
  213. }
  214. s->found_prefix = TRUE;
  215. /*
  216. * Start a buffer to store the full greeting line.
  217. */
  218. s->vstrsize = s->prefix_wanted.len + 16;
  219. s->vstring = snewn(s->vstrsize, char);
  220. memcpy(s->vstring, s->prefix_wanted.ptr, s->prefix_wanted.len);
  221. s->vslen = s->prefix_wanted.len;
  222. /*
  223. * Now read the rest of the greeting line.
  224. */
  225. s->i = 0;
  226. do {
  227. int len;
  228. void *data;
  229. char *nl;
  230. crMaybeWaitUntilV(bufchain_size(s->bpp.in_raw) > 0);
  231. bufchain_prefix(s->bpp.in_raw, &data, &len);
  232. if ((nl = memchr(data, '\012', len)) != NULL) {
  233. len = nl - (char *)data + 1;
  234. }
  235. if (s->vslen + len >= s->vstrsize - 1) {
  236. s->vstrsize = (s->vslen + len) * 5 / 4 + 32;
  237. s->vstring = sresize(s->vstring, s->vstrsize, char);
  238. }
  239. memcpy(s->vstring + s->vslen, data, len);
  240. s->vslen += len;
  241. bufchain_consume(s->bpp.in_raw, len);
  242. } while (s->vstring[s->vslen-1] != '\012');
  243. /*
  244. * Trim \r and \n from the version string, and replace them with
  245. * a NUL terminator.
  246. */
  247. while (s->vslen > 0 &&
  248. (s->vstring[s->vslen-1] == '\r' ||
  249. s->vstring[s->vslen-1] == '\n'))
  250. s->vslen--;
  251. s->vstring[s->vslen] = '\0';
  252. vs_logevent(("Remote version: %s", s->vstring));
  253. /*
  254. * Pick out the protocol version and software version. The former
  255. * goes in a separately allocated string, so that s->vstring
  256. * remains intact for later use in key exchange; the latter is the
  257. * tail of s->vstring, so it doesn't need to be allocated.
  258. */
  259. {
  260. const char *pv_start = s->vstring + s->prefix_wanted.len;
  261. int pv_len = strcspn(pv_start, "-");
  262. s->protoversion = dupprintf("%.*s", pv_len, pv_start);
  263. s->softwareversion = pv_start + pv_len;
  264. if (*s->softwareversion) {
  265. assert(*s->softwareversion == '-');
  266. s->softwareversion++;
  267. }
  268. }
  269. ssh_detect_bugs(s);
  270. /*
  271. * Figure out what actual SSH protocol version we're speaking.
  272. */
  273. if (ssh_version_includes_v2(s->our_protoversion) &&
  274. ssh_version_includes_v2(s->protoversion)) {
  275. /*
  276. * We're doing SSH-2.
  277. */
  278. s->major_protoversion = 2;
  279. } else if (ssh_version_includes_v1(s->our_protoversion) &&
  280. ssh_version_includes_v1(s->protoversion)) {
  281. /*
  282. * We're doing SSH-1.
  283. */
  284. s->major_protoversion = 1;
  285. /*
  286. * There are multiple minor versions of SSH-1, and the
  287. * protocol does not specify that the minimum of client
  288. * and server versions is used. So we must adjust our
  289. * outgoing protocol version to be no higher than that of
  290. * the other side.
  291. */
  292. if (!s->send_early &&
  293. ssh_versioncmp(s->our_protoversion, s->protoversion) > 0) {
  294. sfree(s->our_protoversion);
  295. s->our_protoversion = dupstr(s->protoversion);
  296. }
  297. } else {
  298. /*
  299. * Unable to agree on a major protocol version at all.
  300. */
  301. if (!ssh_version_includes_v2(s->our_protoversion)) {
  302. s->bpp.error = dupstr(
  303. "SSH protocol version 1 required by our configuration "
  304. "but not provided by remote");
  305. } else {
  306. s->bpp.error = dupstr(
  307. "SSH protocol version 2 required by our configuration "
  308. "but remote only provides (old, insecure) SSH-1");
  309. }
  310. crStopV;
  311. }
  312. vs_logevent(("Using SSH protocol version %d", s->major_protoversion));
  313. if (!s->send_early) {
  314. /*
  315. * If we didn't send our version string early, construct and
  316. * send it now, because now we know what it is.
  317. */
  318. ssh_verstring_send(s);
  319. }
  320. /*
  321. * And we're done. Notify our receiver that we now know our
  322. * protocol version. This will cause it to disconnect us from the
  323. * input stream and ultimately free us, because our job is now
  324. * done.
  325. */
  326. s->receiver->got_ssh_version(s->receiver, s->major_protoversion);
  327. crFinishV;
  328. }
  329. static PktOut *ssh_verstring_new_pktout(int type)
  330. {
  331. assert(0 && "Should never try to send packets during SSH version "
  332. "string exchange");
  333. return NULL;
  334. }
  335. static void ssh_verstring_format_packet(BinaryPacketProtocol *bpp, PktOut *pkg)
  336. {
  337. assert(0 && "Should never try to send packets during SSH version "
  338. "string exchange");
  339. }
  340. /*
  341. * Examine the remote side's version string, and compare it against a
  342. * list of known buggy implementations.
  343. */
  344. static void ssh_detect_bugs(struct ssh_verstring_state *s)
  345. {
  346. const char *imp = s->softwareversion;
  347. s->remote_bugs = 0;
  348. /*
  349. * General notes on server version strings:
  350. * - Not all servers reporting "Cisco-1.25" have all the bugs listed
  351. * here -- in particular, we've heard of one that's perfectly happy
  352. * with SSH1_MSG_IGNOREs -- but this string never seems to change,
  353. * so we can't distinguish them.
  354. */
  355. if (conf_get_int(s->conf, CONF_sshbug_ignore1) == FORCE_ON ||
  356. (conf_get_int(s->conf, CONF_sshbug_ignore1) == AUTO &&
  357. (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
  358. !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
  359. !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
  360. !strcmp(imp, "OSU_1.4alpha3") || !strcmp(imp, "OSU_1.5alpha4")))) {
  361. /*
  362. * These versions don't support SSH1_MSG_IGNORE, so we have
  363. * to use a different defence against password length
  364. * sniffing.
  365. */
  366. s->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
  367. vs_logevent(("We believe remote version has SSH-1 ignore bug"));
  368. }
  369. if (conf_get_int(s->conf, CONF_sshbug_plainpw1) == FORCE_ON ||
  370. (conf_get_int(s->conf, CONF_sshbug_plainpw1) == AUTO &&
  371. (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {
  372. /*
  373. * These versions need a plain password sent; they can't
  374. * handle having a null and a random length of data after
  375. * the password.
  376. */
  377. s->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
  378. vs_logevent(("We believe remote version needs a "
  379. "plain SSH-1 password"));
  380. }
  381. if (conf_get_int(s->conf, CONF_sshbug_rsa1) == FORCE_ON ||
  382. (conf_get_int(s->conf, CONF_sshbug_rsa1) == AUTO &&
  383. (!strcmp(imp, "Cisco-1.25")))) {
  384. /*
  385. * These versions apparently have no clue whatever about
  386. * RSA authentication and will panic and die if they see
  387. * an AUTH_RSA message.
  388. */
  389. s->remote_bugs |= BUG_CHOKES_ON_RSA;
  390. vs_logevent(("We believe remote version can't handle SSH-1 "
  391. "RSA authentication"));
  392. }
  393. if (conf_get_int(s->conf, CONF_sshbug_hmac2) == FORCE_ON ||
  394. (conf_get_int(s->conf, CONF_sshbug_hmac2) == AUTO &&
  395. !wc_match("* VShell", imp) &&
  396. (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
  397. wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
  398. wc_match("2.1 *", imp)))) {
  399. /*
  400. * These versions have the HMAC bug.
  401. */
  402. s->remote_bugs |= BUG_SSH2_HMAC;
  403. vs_logevent(("We believe remote version has SSH-2 HMAC bug"));
  404. }
  405. if (conf_get_int(s->conf, CONF_sshbug_derivekey2) == FORCE_ON ||
  406. (conf_get_int(s->conf, CONF_sshbug_derivekey2) == AUTO &&
  407. !wc_match("* VShell", imp) &&
  408. (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
  409. /*
  410. * These versions have the key-derivation bug (failing to
  411. * include the literal shared secret in the hashes that
  412. * generate the keys).
  413. */
  414. s->remote_bugs |= BUG_SSH2_DERIVEKEY;
  415. vs_logevent(("We believe remote version has SSH-2 "
  416. "key-derivation bug"));
  417. }
  418. if (conf_get_int(s->conf, CONF_sshbug_rsapad2) == FORCE_ON ||
  419. (conf_get_int(s->conf, CONF_sshbug_rsapad2) == AUTO &&
  420. (wc_match("OpenSSH_2.[5-9]*", imp) ||
  421. wc_match("OpenSSH_3.[0-2]*", imp) ||
  422. wc_match("mod_sftp/0.[0-8]*", imp) ||
  423. wc_match("mod_sftp/0.9.[0-8]", imp)))) {
  424. /*
  425. * These versions have the SSH-2 RSA padding bug.
  426. */
  427. s->remote_bugs |= BUG_SSH2_RSA_PADDING;
  428. vs_logevent(("We believe remote version has SSH-2 RSA padding bug"));
  429. }
  430. if (conf_get_int(s->conf, CONF_sshbug_pksessid2) == FORCE_ON ||
  431. (conf_get_int(s->conf, CONF_sshbug_pksessid2) == AUTO &&
  432. wc_match("OpenSSH_2.[0-2]*", imp))) {
  433. /*
  434. * These versions have the SSH-2 session-ID bug in
  435. * public-key authentication.
  436. */
  437. s->remote_bugs |= BUG_SSH2_PK_SESSIONID;
  438. vs_logevent(("We believe remote version has SSH-2 "
  439. "public-key-session-ID bug"));
  440. }
  441. if (conf_get_int(s->conf, CONF_sshbug_rekey2) == FORCE_ON ||
  442. (conf_get_int(s->conf, CONF_sshbug_rekey2) == AUTO &&
  443. (wc_match("DigiSSH_2.0", imp) ||
  444. wc_match("OpenSSH_2.[0-4]*", imp) ||
  445. wc_match("OpenSSH_2.5.[0-3]*", imp) ||
  446. wc_match("Sun_SSH_1.0", imp) ||
  447. wc_match("Sun_SSH_1.0.1", imp) ||
  448. /* All versions <= 1.2.6 (they changed their format in 1.2.7) */
  449. wc_match("WeOnlyDo-*", imp)))) {
  450. /*
  451. * These versions have the SSH-2 rekey bug.
  452. */
  453. s->remote_bugs |= BUG_SSH2_REKEY;
  454. vs_logevent(("We believe remote version has SSH-2 rekey bug"));
  455. }
  456. if (conf_get_int(s->conf, CONF_sshbug_maxpkt2) == FORCE_ON ||
  457. (conf_get_int(s->conf, CONF_sshbug_maxpkt2) == AUTO &&
  458. (wc_match("1.36_sshlib GlobalSCAPE", imp) ||
  459. wc_match("1.36 sshlib: GlobalScape", imp)))) {
  460. /*
  461. * This version ignores our makpkt and needs to be throttled.
  462. */
  463. s->remote_bugs |= BUG_SSH2_MAXPKT;
  464. vs_logevent(("We believe remote version ignores SSH-2 "
  465. "maximum packet size"));
  466. }
  467. if (conf_get_int(s->conf, CONF_sshbug_ignore2) == FORCE_ON) {
  468. /*
  469. * Servers that don't support SSH2_MSG_IGNORE. Currently,
  470. * none detected automatically.
  471. */
  472. s->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
  473. vs_logevent(("We believe remote version has SSH-2 ignore bug"));
  474. }
  475. if (conf_get_int(s->conf, CONF_sshbug_oldgex2) == FORCE_ON ||
  476. (conf_get_int(s->conf, CONF_sshbug_oldgex2) == AUTO &&
  477. (wc_match("OpenSSH_2.[235]*", imp)))) {
  478. /*
  479. * These versions only support the original (pre-RFC4419)
  480. * SSH-2 GEX request, and disconnect with a protocol error if
  481. * we use the newer version.
  482. */
  483. s->remote_bugs |= BUG_SSH2_OLDGEX;
  484. vs_logevent(("We believe remote version has outdated SSH-2 GEX"));
  485. }
  486. if (conf_get_int(s->conf, CONF_sshbug_winadj) == FORCE_ON) {
  487. /*
  488. * Servers that don't support our winadj request for one
  489. * reason or another. Currently, none detected automatically.
  490. */
  491. s->remote_bugs |= BUG_CHOKES_ON_WINADJ;
  492. vs_logevent(("We believe remote version has winadj bug"));
  493. }
  494. if (conf_get_int(s->conf, CONF_sshbug_chanreq) == FORCE_ON ||
  495. (conf_get_int(s->conf, CONF_sshbug_chanreq) == AUTO &&
  496. (wc_match("OpenSSH_[2-5].*", imp) ||
  497. wc_match("OpenSSH_6.[0-6]*", imp) ||
  498. wc_match("dropbear_0.[2-4][0-9]*", imp) ||
  499. wc_match("dropbear_0.5[01]*", imp)))) {
  500. /*
  501. * These versions have the SSH-2 channel request bug.
  502. * OpenSSH 6.7 and above do not:
  503. * https://bugzilla.mindrot.org/show_bug.cgi?id=1818
  504. * dropbear_0.52 and above do not:
  505. * https://secure.ucc.asn.au/hg/dropbear/rev/cd02449b709c
  506. */
  507. s->remote_bugs |= BUG_SENDS_LATE_REQUEST_REPLY;
  508. vs_logevent(("We believe remote version has SSH-2 "
  509. "channel request bug"));
  510. }
  511. }
  512. const char *ssh_verstring_get_remote(BinaryPacketProtocol *bpp)
  513. {
  514. struct ssh_verstring_state *s =
  515. FROMFIELD(bpp, struct ssh_verstring_state, bpp);
  516. return s->vstring;
  517. }
  518. const char *ssh_verstring_get_local(BinaryPacketProtocol *bpp)
  519. {
  520. struct ssh_verstring_state *s =
  521. FROMFIELD(bpp, struct ssh_verstring_state, bpp);
  522. return s->our_vstring;
  523. }
  524. int ssh_verstring_get_bugs(BinaryPacketProtocol *bpp)
  525. {
  526. struct ssh_verstring_state *s =
  527. FROMFIELD(bpp, struct ssh_verstring_state, bpp);
  528. return s->remote_bugs;
  529. }