diff options
author | Colin Watson <cjwatson@debian.org> | 2015-08-19 14:23:51 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2015-08-19 16:48:11 +0100 |
commit | 0f0841b2d28b7463267d4d91577e72e3340a1d3a (patch) | |
tree | ba55fcd2b6e2cc22b30f5afb561dbb3da4c8b6c7 /kexgexc.c | |
parent | f2a5f5dae656759efb0b76c3d94890b65c197a02 (diff) | |
parent | 8698446b972003b63dfe5dcbdb86acfe986afb85 (diff) |
New upstream release (6.8p1).
Diffstat (limited to 'kexgexc.c')
-rw-r--r-- | kexgexc.c | 296 |
1 files changed, 184 insertions, 112 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: kexgexc.c,v 1.17 2014/02/02 03:44:31 djm Exp $ */ | 1 | /* $OpenBSD: kexgexc.c,v 1.20 2015/01/26 06:10:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Niels Provos. All rights reserved. | 3 | * Copyright (c) 2000 Niels Provos. All rights reserved. |
4 | * Copyright (c) 2001 Markus Friedl. All rights reserved. | 4 | * Copyright (c) 2001 Markus Friedl. All rights reserved. |
@@ -26,6 +26,8 @@ | |||
26 | 26 | ||
27 | #include "includes.h" | 27 | #include "includes.h" |
28 | 28 | ||
29 | #ifdef WITH_OPENSSL | ||
30 | |||
29 | #include <sys/types.h> | 31 | #include <sys/types.h> |
30 | 32 | ||
31 | #include <openssl/dh.h> | 33 | #include <openssl/dh.h> |
@@ -35,173 +37,243 @@ | |||
35 | #include <string.h> | 37 | #include <string.h> |
36 | #include <signal.h> | 38 | #include <signal.h> |
37 | 39 | ||
38 | #include "xmalloc.h" | 40 | #include "sshkey.h" |
39 | #include "buffer.h" | ||
40 | #include "key.h" | ||
41 | #include "cipher.h" | 41 | #include "cipher.h" |
42 | #include "digest.h" | ||
42 | #include "kex.h" | 43 | #include "kex.h" |
43 | #include "log.h" | 44 | #include "log.h" |
44 | #include "packet.h" | 45 | #include "packet.h" |
45 | #include "dh.h" | 46 | #include "dh.h" |
46 | #include "ssh2.h" | 47 | #include "ssh2.h" |
47 | #include "compat.h" | 48 | #include "compat.h" |
49 | #include "dispatch.h" | ||
50 | #include "ssherr.h" | ||
51 | #include "sshbuf.h" | ||
52 | |||
53 | static int input_kex_dh_gex_group(int, u_int32_t, void *); | ||
54 | static int input_kex_dh_gex_reply(int, u_int32_t, void *); | ||
48 | 55 | ||
49 | void | 56 | int |
50 | kexgex_client(Kex *kex) | 57 | kexgex_client(struct ssh *ssh) |
51 | { | 58 | { |
52 | BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; | 59 | struct kex *kex = ssh->kex; |
53 | BIGNUM *p = NULL, *g = NULL; | 60 | int r; |
54 | Key *server_host_key; | 61 | u_int nbits; |
55 | u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; | ||
56 | u_int klen, slen, sbloblen, hashlen; | ||
57 | int kout; | ||
58 | int min, max, nbits; | ||
59 | DH *dh; | ||
60 | 62 | ||
61 | nbits = dh_estimate(kex->dh_need * 8); | 63 | nbits = dh_estimate(kex->dh_need * 8); |
62 | 64 | ||
63 | if (datafellows & SSH_OLD_DHGEX) { | 65 | kex->min = DH_GRP_MIN; |
66 | kex->max = DH_GRP_MAX; | ||
67 | kex->nbits = nbits; | ||
68 | if (ssh->compat & SSH_OLD_DHGEX) { | ||
64 | /* Old GEX request */ | 69 | /* Old GEX request */ |
65 | packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD); | 70 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD)) |
66 | packet_put_int(nbits); | 71 | != 0 || |
67 | min = DH_GRP_MIN; | 72 | (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 || |
68 | max = DH_GRP_MAX; | 73 | (r = sshpkt_send(ssh)) != 0) |
69 | 74 | goto out; | |
70 | debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD(%u) sent", nbits); | 75 | debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD(%u) sent", kex->nbits); |
71 | } else { | 76 | } else { |
72 | /* New GEX request */ | 77 | /* New GEX request */ |
73 | min = DH_GRP_MIN; | 78 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST)) != 0 || |
74 | max = DH_GRP_MAX; | 79 | (r = sshpkt_put_u32(ssh, kex->min)) != 0 || |
75 | packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); | 80 | (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 || |
76 | packet_put_int(min); | 81 | (r = sshpkt_put_u32(ssh, kex->max)) != 0 || |
77 | packet_put_int(nbits); | 82 | (r = sshpkt_send(ssh)) != 0) |
78 | packet_put_int(max); | 83 | goto out; |
79 | |||
80 | debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent", | 84 | debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent", |
81 | min, nbits, max); | 85 | kex->min, kex->nbits, kex->max); |
82 | } | 86 | } |
83 | #ifdef DEBUG_KEXDH | 87 | #ifdef DEBUG_KEXDH |
84 | fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n", | 88 | fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n", |
85 | min, nbits, max); | 89 | kex->min, kex->nbits, kex->max); |
86 | #endif | 90 | #endif |
87 | packet_send(); | 91 | ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, |
88 | 92 | &input_kex_dh_gex_group); | |
89 | debug("expecting SSH2_MSG_KEX_DH_GEX_GROUP"); | 93 | r = 0; |
90 | packet_read_expect(SSH2_MSG_KEX_DH_GEX_GROUP); | 94 | out: |
95 | return r; | ||
96 | } | ||
91 | 97 | ||
92 | if ((p = BN_new()) == NULL) | 98 | static int |
93 | fatal("BN_new"); | 99 | input_kex_dh_gex_group(int type, u_int32_t seq, void *ctxt) |
94 | packet_get_bignum2(p); | 100 | { |
95 | if ((g = BN_new()) == NULL) | 101 | struct ssh *ssh = ctxt; |
96 | fatal("BN_new"); | 102 | struct kex *kex = ssh->kex; |
97 | packet_get_bignum2(g); | 103 | BIGNUM *p = NULL, *g = NULL; |
98 | packet_check_eom(); | 104 | int r, bits; |
99 | 105 | ||
100 | if (BN_num_bits(p) < min || BN_num_bits(p) > max) | 106 | debug("got SSH2_MSG_KEX_DH_GEX_GROUP"); |
101 | fatal("DH_GEX group out of range: %d !< %d !< %d", | ||
102 | min, BN_num_bits(p), max); | ||
103 | 107 | ||
104 | dh = dh_new_group(g, p); | 108 | if ((p = BN_new()) == NULL || |
105 | dh_gen_key(dh, kex->we_need * 8); | 109 | (g = BN_new()) == NULL) { |
110 | r = SSH_ERR_ALLOC_FAIL; | ||
111 | goto out; | ||
112 | } | ||
113 | if ((r = sshpkt_get_bignum2(ssh, p)) != 0 || | ||
114 | (r = sshpkt_get_bignum2(ssh, g)) != 0 || | ||
115 | (r = sshpkt_get_end(ssh)) != 0) | ||
116 | goto out; | ||
117 | if ((bits = BN_num_bits(p)) < 0 || | ||
118 | (u_int)bits < kex->min || (u_int)bits > kex->max) { | ||
119 | r = SSH_ERR_DH_GEX_OUT_OF_RANGE; | ||
120 | goto out; | ||
121 | } | ||
122 | if ((kex->dh = dh_new_group(g, p)) == NULL) { | ||
123 | r = SSH_ERR_ALLOC_FAIL; | ||
124 | goto out; | ||
125 | } | ||
126 | p = g = NULL; /* belong to kex->dh now */ | ||
106 | 127 | ||
128 | /* generate and send 'e', client DH public key */ | ||
129 | if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || | ||
130 | (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || | ||
131 | (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || | ||
132 | (r = sshpkt_send(ssh)) != 0) | ||
133 | goto out; | ||
134 | debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); | ||
107 | #ifdef DEBUG_KEXDH | 135 | #ifdef DEBUG_KEXDH |
108 | DHparams_print_fp(stderr, dh); | 136 | DHparams_print_fp(stderr, kex->dh); |
109 | fprintf(stderr, "pub= "); | 137 | fprintf(stderr, "pub= "); |
110 | BN_print_fp(stderr, dh->pub_key); | 138 | BN_print_fp(stderr, kex->dh->pub_key); |
111 | fprintf(stderr, "\n"); | 139 | fprintf(stderr, "\n"); |
112 | #endif | 140 | #endif |
141 | ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL); | ||
142 | ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply); | ||
143 | r = 0; | ||
144 | out: | ||
145 | if (p) | ||
146 | BN_clear_free(p); | ||
147 | if (g) | ||
148 | BN_clear_free(g); | ||
149 | return r; | ||
150 | } | ||
113 | 151 | ||
114 | debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); | 152 | static int |
115 | /* generate and send 'e', client DH public key */ | 153 | input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt) |
116 | packet_start(SSH2_MSG_KEX_DH_GEX_INIT); | 154 | { |
117 | packet_put_bignum2(dh->pub_key); | 155 | struct ssh *ssh = ctxt; |
118 | packet_send(); | 156 | struct kex *kex = ssh->kex; |
119 | 157 | BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; | |
120 | debug("expecting SSH2_MSG_KEX_DH_GEX_REPLY"); | 158 | struct sshkey *server_host_key = NULL; |
121 | packet_read_expect(SSH2_MSG_KEX_DH_GEX_REPLY); | 159 | u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL; |
160 | u_char hash[SSH_DIGEST_MAX_LENGTH]; | ||
161 | size_t klen = 0, slen, sbloblen, hashlen; | ||
162 | int kout, r; | ||
122 | 163 | ||
164 | debug("got SSH2_MSG_KEX_DH_GEX_REPLY"); | ||
165 | if (kex->verify_host_key == NULL) { | ||
166 | r = SSH_ERR_INVALID_ARGUMENT; | ||
167 | goto out; | ||
168 | } | ||
123 | /* key, cert */ | 169 | /* key, cert */ |
124 | server_host_key_blob = packet_get_string(&sbloblen); | 170 | if ((r = sshpkt_get_string(ssh, &server_host_key_blob, |
125 | server_host_key = key_from_blob(server_host_key_blob, sbloblen); | 171 | &sbloblen)) != 0 || |
126 | if (server_host_key == NULL) | 172 | (r = sshkey_from_blob(server_host_key_blob, sbloblen, |
127 | fatal("cannot decode server_host_key_blob"); | 173 | &server_host_key)) != 0) |
128 | if (server_host_key->type != kex->hostkey_type) | 174 | goto out; |
129 | fatal("type mismatch for decoded server_host_key_blob"); | 175 | if (server_host_key->type != kex->hostkey_type) { |
130 | if (kex->verify_host_key == NULL) | 176 | r = SSH_ERR_KEY_TYPE_MISMATCH; |
131 | fatal("cannot verify server_host_key"); | 177 | goto out; |
132 | if (kex->verify_host_key(server_host_key) == -1) | 178 | } |
133 | fatal("server_host_key verification failed"); | 179 | if (server_host_key->type != kex->hostkey_type || |
134 | 180 | (kex->hostkey_type == KEY_ECDSA && | |
181 | server_host_key->ecdsa_nid != kex->hostkey_nid)) { | ||
182 | r = SSH_ERR_KEY_TYPE_MISMATCH; | ||
183 | goto out; | ||
184 | } | ||
185 | if (kex->verify_host_key(server_host_key, ssh) == -1) { | ||
186 | r = SSH_ERR_SIGNATURE_INVALID; | ||
187 | goto out; | ||
188 | } | ||
135 | /* DH parameter f, server public DH key */ | 189 | /* DH parameter f, server public DH key */ |
136 | if ((dh_server_pub = BN_new()) == NULL) | 190 | if ((dh_server_pub = BN_new()) == NULL) { |
137 | fatal("dh_server_pub == NULL"); | 191 | r = SSH_ERR_ALLOC_FAIL; |
138 | packet_get_bignum2(dh_server_pub); | 192 | goto out; |
139 | 193 | } | |
194 | /* signed H */ | ||
195 | if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 || | ||
196 | (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || | ||
197 | (r = sshpkt_get_end(ssh)) != 0) | ||
198 | goto out; | ||
140 | #ifdef DEBUG_KEXDH | 199 | #ifdef DEBUG_KEXDH |
141 | fprintf(stderr, "dh_server_pub= "); | 200 | fprintf(stderr, "dh_server_pub= "); |
142 | BN_print_fp(stderr, dh_server_pub); | 201 | BN_print_fp(stderr, dh_server_pub); |
143 | fprintf(stderr, "\n"); | 202 | fprintf(stderr, "\n"); |
144 | debug("bits %d", BN_num_bits(dh_server_pub)); | 203 | debug("bits %d", BN_num_bits(dh_server_pub)); |
145 | #endif | 204 | #endif |
205 | if (!dh_pub_is_valid(kex->dh, dh_server_pub)) { | ||
206 | sshpkt_disconnect(ssh, "bad server public DH value"); | ||
207 | r = SSH_ERR_MESSAGE_INCOMPLETE; | ||
208 | goto out; | ||
209 | } | ||
146 | 210 | ||
147 | /* signed H */ | 211 | klen = DH_size(kex->dh); |
148 | signature = packet_get_string(&slen); | 212 | if ((kbuf = malloc(klen)) == NULL || |
149 | packet_check_eom(); | 213 | (shared_secret = BN_new()) == NULL) { |
150 | 214 | r = SSH_ERR_ALLOC_FAIL; | |
151 | if (!dh_pub_is_valid(dh, dh_server_pub)) | 215 | goto out; |
152 | packet_disconnect("bad server public DH value"); | 216 | } |
153 | 217 | if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 || | |
154 | klen = DH_size(dh); | 218 | BN_bin2bn(kbuf, kout, shared_secret) == NULL) { |
155 | kbuf = xmalloc(klen); | 219 | r = SSH_ERR_LIBCRYPTO_ERROR; |
156 | if ((kout = DH_compute_key(kbuf, dh_server_pub, dh)) < 0) | 220 | goto out; |
157 | fatal("DH_compute_key: failed"); | 221 | } |
158 | #ifdef DEBUG_KEXDH | 222 | #ifdef DEBUG_KEXDH |
159 | dump_digest("shared secret", kbuf, kout); | 223 | dump_digest("shared secret", kbuf, kout); |
160 | #endif | 224 | #endif |
161 | if ((shared_secret = BN_new()) == NULL) | 225 | if (ssh->compat & SSH_OLD_DHGEX) |
162 | fatal("kexgex_client: BN_new failed"); | 226 | kex->min = kex->max = -1; |
163 | if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) | ||
164 | fatal("kexgex_client: BN_bin2bn failed"); | ||
165 | explicit_bzero(kbuf, klen); | ||
166 | free(kbuf); | ||
167 | |||
168 | if (datafellows & SSH_OLD_DHGEX) | ||
169 | min = max = -1; | ||
170 | 227 | ||
171 | /* calc and verify H */ | 228 | /* calc and verify H */ |
172 | kexgex_hash( | 229 | hashlen = sizeof(hash); |
230 | if ((r = kexgex_hash( | ||
173 | kex->hash_alg, | 231 | kex->hash_alg, |
174 | kex->client_version_string, | 232 | kex->client_version_string, |
175 | kex->server_version_string, | 233 | kex->server_version_string, |
176 | buffer_ptr(&kex->my), buffer_len(&kex->my), | 234 | sshbuf_ptr(kex->my), sshbuf_len(kex->my), |
177 | buffer_ptr(&kex->peer), buffer_len(&kex->peer), | 235 | sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), |
178 | server_host_key_blob, sbloblen, | 236 | server_host_key_blob, sbloblen, |
179 | min, nbits, max, | 237 | kex->min, kex->nbits, kex->max, |
180 | dh->p, dh->g, | 238 | kex->dh->p, kex->dh->g, |
181 | dh->pub_key, | 239 | kex->dh->pub_key, |
182 | dh_server_pub, | 240 | dh_server_pub, |
183 | shared_secret, | 241 | shared_secret, |
184 | &hash, &hashlen | 242 | hash, &hashlen)) != 0) |
185 | ); | 243 | goto out; |
186 | |||
187 | /* have keys, free DH */ | ||
188 | DH_free(dh); | ||
189 | free(server_host_key_blob); | ||
190 | BN_clear_free(dh_server_pub); | ||
191 | 244 | ||
192 | if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) | 245 | if ((r = sshkey_verify(server_host_key, signature, slen, hash, |
193 | fatal("key_verify failed for server_host_key"); | 246 | hashlen, ssh->compat)) != 0) |
194 | key_free(server_host_key); | 247 | goto out; |
195 | free(signature); | ||
196 | 248 | ||
197 | /* save session id */ | 249 | /* save session id */ |
198 | if (kex->session_id == NULL) { | 250 | if (kex->session_id == NULL) { |
199 | kex->session_id_len = hashlen; | 251 | kex->session_id_len = hashlen; |
200 | kex->session_id = xmalloc(kex->session_id_len); | 252 | kex->session_id = malloc(kex->session_id_len); |
253 | if (kex->session_id == NULL) { | ||
254 | r = SSH_ERR_ALLOC_FAIL; | ||
255 | goto out; | ||
256 | } | ||
201 | memcpy(kex->session_id, hash, kex->session_id_len); | 257 | memcpy(kex->session_id, hash, kex->session_id_len); |
202 | } | 258 | } |
203 | kex_derive_keys_bn(kex, hash, hashlen, shared_secret); | ||
204 | BN_clear_free(shared_secret); | ||
205 | 259 | ||
206 | kex_finish(kex); | 260 | if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) |
261 | r = kex_send_newkeys(ssh); | ||
262 | out: | ||
263 | explicit_bzero(hash, sizeof(hash)); | ||
264 | DH_free(kex->dh); | ||
265 | kex->dh = NULL; | ||
266 | if (dh_server_pub) | ||
267 | BN_clear_free(dh_server_pub); | ||
268 | if (kbuf) { | ||
269 | explicit_bzero(kbuf, klen); | ||
270 | free(kbuf); | ||
271 | } | ||
272 | if (shared_secret) | ||
273 | BN_clear_free(shared_secret); | ||
274 | sshkey_free(server_host_key); | ||
275 | free(server_host_key_blob); | ||
276 | free(signature); | ||
277 | return r; | ||
207 | } | 278 | } |
279 | #endif /* WITH_OPENSSL */ | ||