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