summaryrefslogtreecommitdiff
path: root/ssh_api.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2018-12-27 03:25:24 +0000
committerDamien Miller <djm@mindrot.org>2018-12-27 14:38:22 +1100
commit0a843d9a0e805f14653a555f5c7a8ba99d62c12d (patch)
tree481f36e9fd1918be5449e369a97c086a1a8d2432 /ssh_api.c
parent434b587afe41c19391821e7392005068fda76248 (diff)
upstream: move client/server SSH-* banners to buffers under
ssh->kex and factor out the banner exchange. This eliminates some common code from the client and server. Also be more strict about handling \r characters - these should only be accepted immediately before \n (pointed out by Jann Horn). Inspired by a patch from Markus Schmidt. (lots of) feedback and ok markus@ OpenBSD-Commit-ID: 1cc7885487a6754f63641d7d3279b0941890275b
Diffstat (limited to 'ssh_api.c')
-rw-r--r--ssh_api.c125
1 files changed, 70 insertions, 55 deletions
diff --git a/ssh_api.c b/ssh_api.c
index 53bbc9b49..ab209c4ca 100644
--- a/ssh_api.c
+++ b/ssh_api.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh_api.c,v 1.8 2017/04/30 23:13:25 djm Exp $ */ 1/* $OpenBSD: ssh_api.c,v 1.9 2018/12/27 03:25:25 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2012 Markus Friedl. All rights reserved. 3 * Copyright (c) 2012 Markus Friedl. All rights reserved.
4 * 4 *
@@ -34,8 +34,8 @@
34#include <string.h> 34#include <string.h>
35 35
36int _ssh_exchange_banner(struct ssh *); 36int _ssh_exchange_banner(struct ssh *);
37int _ssh_send_banner(struct ssh *, char **); 37int _ssh_send_banner(struct ssh *, struct sshbuf *);
38int _ssh_read_banner(struct ssh *, char **); 38int _ssh_read_banner(struct ssh *, struct sshbuf *);
39int _ssh_order_hostkeyalgs(struct ssh *); 39int _ssh_order_hostkeyalgs(struct ssh *);
40int _ssh_verify_host_key(struct sshkey *, struct ssh *); 40int _ssh_verify_host_key(struct sshkey *, struct ssh *);
41struct sshkey *_ssh_host_public_key(int, int, struct ssh *); 41struct sshkey *_ssh_host_public_key(int, int, struct ssh *);
@@ -92,7 +92,7 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
92 92
93 /* Initialize key exchange */ 93 /* Initialize key exchange */
94 proposal = kex_params ? kex_params->proposal : myproposal; 94 proposal = kex_params ? kex_params->proposal : myproposal;
95 if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0) { 95 if ((r = kex_ready(ssh, proposal)) != 0) {
96 ssh_free(ssh); 96 ssh_free(ssh);
97 return r; 97 return r;
98 } 98 }
@@ -236,8 +236,8 @@ ssh_packet_next(struct ssh *ssh, u_char *typep)
236 * enough data. 236 * enough data.
237 */ 237 */
238 *typep = SSH_MSG_NONE; 238 *typep = SSH_MSG_NONE;
239 if (ssh->kex->client_version_string == NULL || 239 if (sshbuf_len(ssh->kex->client_version) == 0 ||
240 ssh->kex->server_version_string == NULL) 240 sshbuf_len(ssh->kex->server_version) == 0)
241 return _ssh_exchange_banner(ssh); 241 return _ssh_exchange_banner(ssh);
242 /* 242 /*
243 * If we enough data and a dispatch function then 243 * If we enough data and a dispatch function then
@@ -312,39 +312,46 @@ ssh_input_space(struct ssh *ssh, size_t len)
312 312
313/* Read other side's version identification. */ 313/* Read other side's version identification. */
314int 314int
315_ssh_read_banner(struct ssh *ssh, char **bannerp) 315_ssh_read_banner(struct ssh *ssh, struct sshbuf *banner)
316{ 316{
317 struct sshbuf *input; 317 struct sshbuf *input = ssh_packet_get_input(ssh);
318 const char *s;
319 char buf[256], remote_version[256]; /* must be same size! */
320 const char *mismatch = "Protocol mismatch.\r\n"; 318 const char *mismatch = "Protocol mismatch.\r\n";
321 int r, remote_major, remote_minor; 319 const u_char *s = sshbuf_ptr(input);
322 size_t i, n, j, len; 320 u_char c;
321 char *cp, *remote_version;
322 int r, remote_major, remote_minor, expect_nl;
323 size_t n, j;
323 324
324 *bannerp = NULL;
325 input = ssh_packet_get_input(ssh);
326 len = sshbuf_len(input);
327 s = (const char *)sshbuf_ptr(input);
328 for (j = n = 0;;) { 325 for (j = n = 0;;) {
329 for (i = 0; i < sizeof(buf) - 1; i++) { 326 sshbuf_reset(banner);
330 if (j >= len) 327 expect_nl = 0;
331 return (0); 328 for (;;) {
332 buf[i] = s[j++]; 329 if (j >= sshbuf_len(input))
333 if (buf[i] == '\r') { 330 return 0; /* insufficient data in input buf */
334 buf[i] = '\n'; 331 c = s[j++];
335 buf[i + 1] = 0; 332 if (c == '\r') {
336 continue; /**XXX wait for \n */ 333 expect_nl = 1;
334 continue;
337 } 335 }
338 if (buf[i] == '\n') { 336 if (c == '\n')
339 buf[i + 1] = 0;
340 break; 337 break;
341 } 338 if (expect_nl)
339 goto bad;
340 if ((r = sshbuf_put_u8(banner, c)) != 0)
341 return r;
342 if (sshbuf_len(banner) > SSH_MAX_BANNER_LEN)
343 goto bad;
342 } 344 }
343 buf[sizeof(buf) - 1] = 0; 345 if (sshbuf_len(banner) >= 4 &&
344 if (strncmp(buf, "SSH-", 4) == 0) 346 memcmp(sshbuf_ptr(banner), "SSH-", 4) == 0)
345 break; 347 break;
346 debug("ssh_exchange_identification: %s", buf); 348 if ((cp = sshbuf_dup_string(banner)) == NULL)
347 if (ssh->kex->server || ++n > 65536) { 349 return SSH_ERR_ALLOC_FAIL;
350 debug("%s: %s", __func__, cp);
351 free(cp);
352 /* Accept lines before banner only on client */
353 if (ssh->kex->server || ++n > SSH_MAX_PRE_BANNER_LINES) {
354 bad:
348 if ((r = sshbuf_put(ssh_packet_get_output(ssh), 355 if ((r = sshbuf_put(ssh_packet_get_output(ssh),
349 mismatch, strlen(mismatch))) != 0) 356 mismatch, strlen(mismatch))) != 0)
350 return r; 357 return r;
@@ -354,11 +361,17 @@ _ssh_read_banner(struct ssh *ssh, char **bannerp)
354 if ((r = sshbuf_consume(input, j)) != 0) 361 if ((r = sshbuf_consume(input, j)) != 0)
355 return r; 362 return r;
356 363
364 if ((cp = sshbuf_dup_string(banner)) == NULL)
365 return SSH_ERR_ALLOC_FAIL;
366 /* XXX remote version must be the same size as banner for sscanf */
367 if ((remote_version = calloc(1, sshbuf_len(banner))) == NULL)
368 return SSH_ERR_ALLOC_FAIL;
369
357 /* 370 /*
358 * Check that the versions match. In future this might accept 371 * Check that the versions match. In future this might accept
359 * several versions and set appropriate flags to handle them. 372 * several versions and set appropriate flags to handle them.
360 */ 373 */
361 if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 374 if (sscanf(cp, "SSH-%d.%d-%[^\n]\n",
362 &remote_major, &remote_minor, remote_version) != 3) 375 &remote_major, &remote_minor, remote_version) != 3)
363 return SSH_ERR_INVALID_FORMAT; 376 return SSH_ERR_INVALID_FORMAT;
364 debug("Remote protocol version %d.%d, remote software version %.100s", 377 debug("Remote protocol version %d.%d, remote software version %.100s",
@@ -371,27 +384,29 @@ _ssh_read_banner(struct ssh *ssh, char **bannerp)
371 } 384 }
372 if (remote_major != 2) 385 if (remote_major != 2)
373 return SSH_ERR_PROTOCOL_MISMATCH; 386 return SSH_ERR_PROTOCOL_MISMATCH;
374 chop(buf); 387 debug("Remote version string %.100s", cp);
375 debug("Remote version string %.100s", buf); 388 free(cp);
376 if ((*bannerp = strdup(buf)) == NULL)
377 return SSH_ERR_ALLOC_FAIL;
378 return 0; 389 return 0;
379} 390}
380 391
381/* Send our own protocol version identification. */ 392/* Send our own protocol version identification. */
382int 393int
383_ssh_send_banner(struct ssh *ssh, char **bannerp) 394_ssh_send_banner(struct ssh *ssh, struct sshbuf *banner)
384{ 395{
385 char buf[256]; 396 char *cp;
386 int r; 397 int r;
387 398
388 snprintf(buf, sizeof buf, "SSH-2.0-%.100s\r\n", SSH_VERSION); 399 if ((r = sshbuf_putf(banner, "SSH-2.0-%.100s\r\n", SSH_VERSION)) != 0)
389 if ((r = sshbuf_put(ssh_packet_get_output(ssh), buf, strlen(buf))) != 0) 400 return r;
401 if ((r = sshbuf_putb(ssh_packet_get_output(ssh), banner)) != 0)
402 return r;
403 /* Remove trailing \r\n */
404 if ((r = sshbuf_consume_end(banner, 2)) != 0)
390 return r; 405 return r;
391 chop(buf); 406 if ((cp = sshbuf_dup_string(banner)) == NULL)
392 debug("Local version string %.100s", buf);
393 if ((*bannerp = strdup(buf)) == NULL)
394 return SSH_ERR_ALLOC_FAIL; 407 return SSH_ERR_ALLOC_FAIL;
408 debug("Local version string %.100s", cp);
409 free(cp);
395 return 0; 410 return 0;
396} 411}
397 412
@@ -408,25 +423,25 @@ _ssh_exchange_banner(struct ssh *ssh)
408 423
409 r = 0; 424 r = 0;
410 if (kex->server) { 425 if (kex->server) {
411 if (kex->server_version_string == NULL) 426 if (sshbuf_len(ssh->kex->server_version) == 0)
412 r = _ssh_send_banner(ssh, &kex->server_version_string); 427 r = _ssh_send_banner(ssh, ssh->kex->server_version);
413 if (r == 0 && 428 if (r == 0 &&
414 kex->server_version_string != NULL && 429 sshbuf_len(ssh->kex->server_version) != 0 &&
415 kex->client_version_string == NULL) 430 sshbuf_len(ssh->kex->client_version) == 0)
416 r = _ssh_read_banner(ssh, &kex->client_version_string); 431 r = _ssh_read_banner(ssh, ssh->kex->client_version);
417 } else { 432 } else {
418 if (kex->server_version_string == NULL) 433 if (sshbuf_len(ssh->kex->server_version) == 0)
419 r = _ssh_read_banner(ssh, &kex->server_version_string); 434 r = _ssh_read_banner(ssh, ssh->kex->server_version);
420 if (r == 0 && 435 if (r == 0 &&
421 kex->server_version_string != NULL && 436 sshbuf_len(ssh->kex->server_version) != 0 &&
422 kex->client_version_string == NULL) 437 sshbuf_len(ssh->kex->client_version) == 0)
423 r = _ssh_send_banner(ssh, &kex->client_version_string); 438 r = _ssh_send_banner(ssh, ssh->kex->client_version);
424 } 439 }
425 if (r != 0) 440 if (r != 0)
426 return r; 441 return r;
427 /* start initial kex as soon as we have exchanged the banners */ 442 /* start initial kex as soon as we have exchanged the banners */
428 if (kex->server_version_string != NULL && 443 if (sshbuf_len(ssh->kex->server_version) != 0 &&
429 kex->client_version_string != NULL) { 444 sshbuf_len(ssh->kex->client_version) != 0) {
430 if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 || 445 if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 ||
431 (r = kex_send_kexinit(ssh)) != 0) 446 (r = kex_send_kexinit(ssh)) != 0)
432 return r; 447 return r;