diff options
Diffstat (limited to 'ssh_api.c')
-rw-r--r-- | ssh_api.c | 174 |
1 files changed, 96 insertions, 78 deletions
@@ -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.15 2019/01/21 10:38:54 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2012 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2012 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -29,17 +29,19 @@ | |||
29 | #include "ssherr.h" | 29 | #include "ssherr.h" |
30 | #include "sshbuf.h" | 30 | #include "sshbuf.h" |
31 | 31 | ||
32 | #include "openbsd-compat/openssl-compat.h" | ||
33 | |||
32 | #include <string.h> | 34 | #include <string.h> |
33 | 35 | ||
34 | int _ssh_exchange_banner(struct ssh *); | 36 | int _ssh_exchange_banner(struct ssh *); |
35 | int _ssh_send_banner(struct ssh *, char **); | 37 | int _ssh_send_banner(struct ssh *, struct sshbuf *); |
36 | int _ssh_read_banner(struct ssh *, char **); | 38 | int _ssh_read_banner(struct ssh *, struct sshbuf *); |
37 | int _ssh_order_hostkeyalgs(struct ssh *); | 39 | int _ssh_order_hostkeyalgs(struct ssh *); |
38 | int _ssh_verify_host_key(struct sshkey *, struct ssh *); | 40 | int _ssh_verify_host_key(struct sshkey *, struct ssh *); |
39 | struct sshkey *_ssh_host_public_key(int, int, struct ssh *); | 41 | struct sshkey *_ssh_host_public_key(int, int, struct ssh *); |
40 | struct sshkey *_ssh_host_private_key(int, int, struct ssh *); | 42 | struct sshkey *_ssh_host_private_key(int, int, struct ssh *); |
41 | int _ssh_host_key_sign(struct sshkey *, struct sshkey *, | 43 | int _ssh_host_key_sign(struct ssh *, struct sshkey *, struct sshkey *, |
42 | u_char **, size_t *, const u_char *, size_t, const char *, u_int); | 44 | u_char **, size_t *, const u_char *, size_t, const char *); |
43 | 45 | ||
44 | /* | 46 | /* |
45 | * stubs for the server side implementation of kex. | 47 | * stubs for the server side implementation of kex. |
@@ -79,9 +81,7 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params) | |||
79 | int r; | 81 | int r; |
80 | 82 | ||
81 | if (!called) { | 83 | if (!called) { |
82 | #ifdef WITH_OPENSSL | 84 | seed_rng(); |
83 | OpenSSL_add_all_algorithms(); | ||
84 | #endif /* WITH_OPENSSL */ | ||
85 | called = 1; | 85 | called = 1; |
86 | } | 86 | } |
87 | 87 | ||
@@ -92,42 +92,44 @@ 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 | } |
99 | ssh->kex->server = is_server; | 99 | ssh->kex->server = is_server; |
100 | if (is_server) { | 100 | if (is_server) { |
101 | #ifdef WITH_OPENSSL | 101 | #ifdef WITH_OPENSSL |
102 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; | 102 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; |
103 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; | 103 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; |
104 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kexdh_server; | 104 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; |
105 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kexdh_server; | 105 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; |
106 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kexdh_server; | 106 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; |
107 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | 107 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; |
108 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | 108 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; |
109 | # ifdef OPENSSL_HAS_ECC | 109 | # ifdef OPENSSL_HAS_ECC |
110 | ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_server; | 110 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_server; |
111 | # endif | 111 | # endif |
112 | #endif /* WITH_OPENSSL */ | 112 | #endif /* WITH_OPENSSL */ |
113 | ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_server; | 113 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_server; |
114 | ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; | ||
114 | ssh->kex->load_host_public_key=&_ssh_host_public_key; | 115 | ssh->kex->load_host_public_key=&_ssh_host_public_key; |
115 | ssh->kex->load_host_private_key=&_ssh_host_private_key; | 116 | ssh->kex->load_host_private_key=&_ssh_host_private_key; |
116 | ssh->kex->sign=&_ssh_host_key_sign; | 117 | ssh->kex->sign=&_ssh_host_key_sign; |
117 | } else { | 118 | } else { |
118 | #ifdef WITH_OPENSSL | 119 | #ifdef WITH_OPENSSL |
119 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; | 120 | ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; |
120 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; | 121 | ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; |
121 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kexdh_client; | 122 | ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; |
122 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kexdh_client; | 123 | ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; |
123 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kexdh_client; | 124 | ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; |
124 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; | 125 | ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; |
125 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; | 126 | ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; |
126 | # ifdef OPENSSL_HAS_ECC | 127 | # ifdef OPENSSL_HAS_ECC |
127 | ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client; | 128 | ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; |
128 | # endif | 129 | # endif |
129 | #endif /* WITH_OPENSSL */ | 130 | #endif /* WITH_OPENSSL */ |
130 | ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client; | 131 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; |
132 | ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; | ||
131 | ssh->kex->verify_host_key =&_ssh_verify_host_key; | 133 | ssh->kex->verify_host_key =&_ssh_verify_host_key; |
132 | } | 134 | } |
133 | *sshp = ssh; | 135 | *sshp = ssh; |
@@ -236,8 +238,8 @@ ssh_packet_next(struct ssh *ssh, u_char *typep) | |||
236 | * enough data. | 238 | * enough data. |
237 | */ | 239 | */ |
238 | *typep = SSH_MSG_NONE; | 240 | *typep = SSH_MSG_NONE; |
239 | if (ssh->kex->client_version_string == NULL || | 241 | if (sshbuf_len(ssh->kex->client_version) == 0 || |
240 | ssh->kex->server_version_string == NULL) | 242 | sshbuf_len(ssh->kex->server_version) == 0) |
241 | return _ssh_exchange_banner(ssh); | 243 | return _ssh_exchange_banner(ssh); |
242 | /* | 244 | /* |
243 | * If we enough data and a dispatch function then | 245 | * If we enough data and a dispatch function then |
@@ -312,39 +314,46 @@ ssh_input_space(struct ssh *ssh, size_t len) | |||
312 | 314 | ||
313 | /* Read other side's version identification. */ | 315 | /* Read other side's version identification. */ |
314 | int | 316 | int |
315 | _ssh_read_banner(struct ssh *ssh, char **bannerp) | 317 | _ssh_read_banner(struct ssh *ssh, struct sshbuf *banner) |
316 | { | 318 | { |
317 | struct sshbuf *input; | 319 | 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"; | 320 | const char *mismatch = "Protocol mismatch.\r\n"; |
321 | int r, remote_major, remote_minor; | 321 | const u_char *s = sshbuf_ptr(input); |
322 | size_t i, n, j, len; | 322 | u_char c; |
323 | char *cp, *remote_version; | ||
324 | int r, remote_major, remote_minor, expect_nl; | ||
325 | size_t n, j; | ||
323 | 326 | ||
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;;) { | 327 | for (j = n = 0;;) { |
329 | for (i = 0; i < sizeof(buf) - 1; i++) { | 328 | sshbuf_reset(banner); |
330 | if (j >= len) | 329 | expect_nl = 0; |
331 | return (0); | 330 | for (;;) { |
332 | buf[i] = s[j++]; | 331 | if (j >= sshbuf_len(input)) |
333 | if (buf[i] == '\r') { | 332 | return 0; /* insufficient data in input buf */ |
334 | buf[i] = '\n'; | 333 | c = s[j++]; |
335 | buf[i + 1] = 0; | 334 | if (c == '\r') { |
336 | continue; /**XXX wait for \n */ | 335 | expect_nl = 1; |
336 | continue; | ||
337 | } | 337 | } |
338 | if (buf[i] == '\n') { | 338 | if (c == '\n') |
339 | buf[i + 1] = 0; | ||
340 | break; | 339 | break; |
341 | } | 340 | if (expect_nl) |
341 | goto bad; | ||
342 | if ((r = sshbuf_put_u8(banner, c)) != 0) | ||
343 | return r; | ||
344 | if (sshbuf_len(banner) > SSH_MAX_BANNER_LEN) | ||
345 | goto bad; | ||
342 | } | 346 | } |
343 | buf[sizeof(buf) - 1] = 0; | 347 | if (sshbuf_len(banner) >= 4 && |
344 | if (strncmp(buf, "SSH-", 4) == 0) | 348 | memcmp(sshbuf_ptr(banner), "SSH-", 4) == 0) |
345 | break; | 349 | break; |
346 | debug("ssh_exchange_identification: %s", buf); | 350 | if ((cp = sshbuf_dup_string(banner)) == NULL) |
347 | if (ssh->kex->server || ++n > 65536) { | 351 | return SSH_ERR_ALLOC_FAIL; |
352 | debug("%s: %s", __func__, cp); | ||
353 | free(cp); | ||
354 | /* Accept lines before banner only on client */ | ||
355 | if (ssh->kex->server || ++n > SSH_MAX_PRE_BANNER_LINES) { | ||
356 | bad: | ||
348 | if ((r = sshbuf_put(ssh_packet_get_output(ssh), | 357 | if ((r = sshbuf_put(ssh_packet_get_output(ssh), |
349 | mismatch, strlen(mismatch))) != 0) | 358 | mismatch, strlen(mismatch))) != 0) |
350 | return r; | 359 | return r; |
@@ -354,11 +363,17 @@ _ssh_read_banner(struct ssh *ssh, char **bannerp) | |||
354 | if ((r = sshbuf_consume(input, j)) != 0) | 363 | if ((r = sshbuf_consume(input, j)) != 0) |
355 | return r; | 364 | return r; |
356 | 365 | ||
366 | if ((cp = sshbuf_dup_string(banner)) == NULL) | ||
367 | return SSH_ERR_ALLOC_FAIL; | ||
368 | /* XXX remote version must be the same size as banner for sscanf */ | ||
369 | if ((remote_version = calloc(1, sshbuf_len(banner))) == NULL) | ||
370 | return SSH_ERR_ALLOC_FAIL; | ||
371 | |||
357 | /* | 372 | /* |
358 | * Check that the versions match. In future this might accept | 373 | * Check that the versions match. In future this might accept |
359 | * several versions and set appropriate flags to handle them. | 374 | * several versions and set appropriate flags to handle them. |
360 | */ | 375 | */ |
361 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", | 376 | if (sscanf(cp, "SSH-%d.%d-%[^\n]\n", |
362 | &remote_major, &remote_minor, remote_version) != 3) | 377 | &remote_major, &remote_minor, remote_version) != 3) |
363 | return SSH_ERR_INVALID_FORMAT; | 378 | return SSH_ERR_INVALID_FORMAT; |
364 | debug("Remote protocol version %d.%d, remote software version %.100s", | 379 | debug("Remote protocol version %d.%d, remote software version %.100s", |
@@ -371,27 +386,29 @@ _ssh_read_banner(struct ssh *ssh, char **bannerp) | |||
371 | } | 386 | } |
372 | if (remote_major != 2) | 387 | if (remote_major != 2) |
373 | return SSH_ERR_PROTOCOL_MISMATCH; | 388 | return SSH_ERR_PROTOCOL_MISMATCH; |
374 | chop(buf); | 389 | debug("Remote version string %.100s", cp); |
375 | debug("Remote version string %.100s", buf); | 390 | free(cp); |
376 | if ((*bannerp = strdup(buf)) == NULL) | ||
377 | return SSH_ERR_ALLOC_FAIL; | ||
378 | return 0; | 391 | return 0; |
379 | } | 392 | } |
380 | 393 | ||
381 | /* Send our own protocol version identification. */ | 394 | /* Send our own protocol version identification. */ |
382 | int | 395 | int |
383 | _ssh_send_banner(struct ssh *ssh, char **bannerp) | 396 | _ssh_send_banner(struct ssh *ssh, struct sshbuf *banner) |
384 | { | 397 | { |
385 | char buf[256]; | 398 | char *cp; |
386 | int r; | 399 | int r; |
387 | 400 | ||
388 | snprintf(buf, sizeof buf, "SSH-2.0-%.100s\r\n", SSH_VERSION); | 401 | 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) | 402 | return r; |
403 | if ((r = sshbuf_putb(ssh_packet_get_output(ssh), banner)) != 0) | ||
404 | return r; | ||
405 | /* Remove trailing \r\n */ | ||
406 | if ((r = sshbuf_consume_end(banner, 2)) != 0) | ||
390 | return r; | 407 | return r; |
391 | chop(buf); | 408 | 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; | 409 | return SSH_ERR_ALLOC_FAIL; |
410 | debug("Local version string %.100s", cp); | ||
411 | free(cp); | ||
395 | return 0; | 412 | return 0; |
396 | } | 413 | } |
397 | 414 | ||
@@ -408,25 +425,25 @@ _ssh_exchange_banner(struct ssh *ssh) | |||
408 | 425 | ||
409 | r = 0; | 426 | r = 0; |
410 | if (kex->server) { | 427 | if (kex->server) { |
411 | if (kex->server_version_string == NULL) | 428 | if (sshbuf_len(ssh->kex->server_version) == 0) |
412 | r = _ssh_send_banner(ssh, &kex->server_version_string); | 429 | r = _ssh_send_banner(ssh, ssh->kex->server_version); |
413 | if (r == 0 && | 430 | if (r == 0 && |
414 | kex->server_version_string != NULL && | 431 | sshbuf_len(ssh->kex->server_version) != 0 && |
415 | kex->client_version_string == NULL) | 432 | sshbuf_len(ssh->kex->client_version) == 0) |
416 | r = _ssh_read_banner(ssh, &kex->client_version_string); | 433 | r = _ssh_read_banner(ssh, ssh->kex->client_version); |
417 | } else { | 434 | } else { |
418 | if (kex->server_version_string == NULL) | 435 | if (sshbuf_len(ssh->kex->server_version) == 0) |
419 | r = _ssh_read_banner(ssh, &kex->server_version_string); | 436 | r = _ssh_read_banner(ssh, ssh->kex->server_version); |
420 | if (r == 0 && | 437 | if (r == 0 && |
421 | kex->server_version_string != NULL && | 438 | sshbuf_len(ssh->kex->server_version) != 0 && |
422 | kex->client_version_string == NULL) | 439 | sshbuf_len(ssh->kex->client_version) == 0) |
423 | r = _ssh_send_banner(ssh, &kex->client_version_string); | 440 | r = _ssh_send_banner(ssh, ssh->kex->client_version); |
424 | } | 441 | } |
425 | if (r != 0) | 442 | if (r != 0) |
426 | return r; | 443 | return r; |
427 | /* start initial kex as soon as we have exchanged the banners */ | 444 | /* start initial kex as soon as we have exchanged the banners */ |
428 | if (kex->server_version_string != NULL && | 445 | if (sshbuf_len(ssh->kex->server_version) != 0 && |
429 | kex->client_version_string != NULL) { | 446 | sshbuf_len(ssh->kex->client_version) != 0) { |
430 | if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 || | 447 | if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 || |
431 | (r = kex_send_kexinit(ssh)) != 0) | 448 | (r = kex_send_kexinit(ssh)) != 0) |
432 | return r; | 449 | return r; |
@@ -532,9 +549,10 @@ _ssh_order_hostkeyalgs(struct ssh *ssh) | |||
532 | } | 549 | } |
533 | 550 | ||
534 | int | 551 | int |
535 | _ssh_host_key_sign(struct sshkey *privkey, struct sshkey *pubkey, | 552 | _ssh_host_key_sign(struct ssh *ssh, struct sshkey *privkey, |
536 | u_char **signature, size_t *slen, const u_char *data, size_t dlen, | 553 | struct sshkey *pubkey, u_char **signature, size_t *slen, |
537 | const char *alg, u_int compat) | 554 | const u_char *data, size_t dlen, const char *alg) |
538 | { | 555 | { |
539 | return sshkey_sign(privkey, signature, slen, data, dlen, alg, compat); | 556 | return sshkey_sign(privkey, signature, slen, data, dlen, |
557 | alg, ssh->compat); | ||
540 | } | 558 | } |