diff options
author | Simon Wilkinson <simon@sxw.org.uk> | 2014-02-09 16:09:48 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2019-06-05 07:06:44 +0100 |
commit | 7ce79be85036c4b36937f1b1ba85f6094068412c (patch) | |
tree | c964917d8395ef5605cff9513aad4458b222beae /kexgssc.c | |
parent | 102062f825fb26a74295a1c089c00c4c4c76b68a (diff) |
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are
in favour of adding this, and this situation has not changed for several
years. This is not a slight on Simon's patch, which is of fine quality, but
just that a) we don't trust GSSAPI implementations that much and b) we don't
like adding new KEX since they are pre-auth attack surface. This one is
particularly scary, since it requires hooks out to typically root-owned
system resources."
However, quite a lot of people rely on this in Debian, and it's better to
have it merged into the main openssh package rather than having separate
-krb5 packages (as we used to have). It seems to have a generally good
security history.
Origin: other, https://github.com/openssh-gsskex/openssh-gsskex/commits/debian/master
Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242
Last-Updated: 2019-06-05
Patch-Name: gssapi.patch
Diffstat (limited to 'kexgssc.c')
-rw-r--r-- | kexgssc.c | 606 |
1 files changed, 606 insertions, 0 deletions
diff --git a/kexgssc.c b/kexgssc.c new file mode 100644 index 000000000..f6e1405eb --- /dev/null +++ b/kexgssc.c | |||
@@ -0,0 +1,606 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * | ||
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | ||
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
23 | */ | ||
24 | |||
25 | #include "includes.h" | ||
26 | |||
27 | #if defined(GSSAPI) && defined(WITH_OPENSSL) | ||
28 | |||
29 | #include "includes.h" | ||
30 | |||
31 | #include <openssl/crypto.h> | ||
32 | #include <openssl/bn.h> | ||
33 | |||
34 | #include <string.h> | ||
35 | |||
36 | #include "xmalloc.h" | ||
37 | #include "sshbuf.h" | ||
38 | #include "ssh2.h" | ||
39 | #include "sshkey.h" | ||
40 | #include "cipher.h" | ||
41 | #include "kex.h" | ||
42 | #include "log.h" | ||
43 | #include "packet.h" | ||
44 | #include "dh.h" | ||
45 | #include "digest.h" | ||
46 | #include "ssherr.h" | ||
47 | |||
48 | #include "ssh-gss.h" | ||
49 | |||
50 | int | ||
51 | kexgss_client(struct ssh *ssh) | ||
52 | { | ||
53 | struct kex *kex = ssh->kex; | ||
54 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, | ||
55 | recv_tok = GSS_C_EMPTY_BUFFER, | ||
56 | gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; | ||
57 | Gssctxt *ctxt; | ||
58 | OM_uint32 maj_status, min_status, ret_flags; | ||
59 | struct sshbuf *server_blob = NULL; | ||
60 | struct sshbuf *shared_secret = NULL; | ||
61 | struct sshbuf *server_host_key_blob = NULL; | ||
62 | struct sshbuf *empty = NULL; | ||
63 | u_char *msg; | ||
64 | int type = 0; | ||
65 | int first = 1; | ||
66 | u_char hash[SSH_DIGEST_MAX_LENGTH]; | ||
67 | size_t hashlen; | ||
68 | u_char c; | ||
69 | int r; | ||
70 | |||
71 | /* Initialise our GSSAPI world */ | ||
72 | ssh_gssapi_build_ctx(&ctxt); | ||
73 | if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) | ||
74 | == GSS_C_NO_OID) | ||
75 | fatal("Couldn't identify host exchange"); | ||
76 | |||
77 | if (ssh_gssapi_import_name(ctxt, kex->gss_host)) | ||
78 | fatal("Couldn't import hostname"); | ||
79 | |||
80 | if (kex->gss_client && | ||
81 | ssh_gssapi_client_identity(ctxt, kex->gss_client)) | ||
82 | fatal("Couldn't acquire client credentials"); | ||
83 | |||
84 | /* Step 1 */ | ||
85 | switch (kex->kex_type) { | ||
86 | case KEX_GSS_GRP1_SHA1: | ||
87 | case KEX_GSS_GRP14_SHA1: | ||
88 | case KEX_GSS_GRP14_SHA256: | ||
89 | case KEX_GSS_GRP16_SHA512: | ||
90 | r = kex_dh_keypair(kex); | ||
91 | break; | ||
92 | case KEX_GSS_NISTP256_SHA256: | ||
93 | r = kex_ecdh_keypair(kex); | ||
94 | break; | ||
95 | case KEX_GSS_C25519_SHA256: | ||
96 | r = kex_c25519_keypair(kex); | ||
97 | break; | ||
98 | default: | ||
99 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
100 | } | ||
101 | if (r != 0) | ||
102 | return r; | ||
103 | |||
104 | token_ptr = GSS_C_NO_BUFFER; | ||
105 | |||
106 | do { | ||
107 | debug("Calling gss_init_sec_context"); | ||
108 | |||
109 | maj_status = ssh_gssapi_init_ctx(ctxt, | ||
110 | kex->gss_deleg_creds, token_ptr, &send_tok, | ||
111 | &ret_flags); | ||
112 | |||
113 | if (GSS_ERROR(maj_status)) { | ||
114 | /* XXX Useles code: Missing send? */ | ||
115 | if (send_tok.length != 0) { | ||
116 | if ((r = sshpkt_start(ssh, | ||
117 | SSH2_MSG_KEXGSS_CONTINUE)) != 0 || | ||
118 | (r = sshpkt_put_string(ssh, send_tok.value, | ||
119 | send_tok.length)) != 0) | ||
120 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
121 | } | ||
122 | fatal("gss_init_context failed"); | ||
123 | } | ||
124 | |||
125 | /* If we've got an old receive buffer get rid of it */ | ||
126 | if (token_ptr != GSS_C_NO_BUFFER) | ||
127 | gss_release_buffer(&min_status, &recv_tok); | ||
128 | |||
129 | if (maj_status == GSS_S_COMPLETE) { | ||
130 | /* If mutual state flag is not true, kex fails */ | ||
131 | if (!(ret_flags & GSS_C_MUTUAL_FLAG)) | ||
132 | fatal("Mutual authentication failed"); | ||
133 | |||
134 | /* If integ avail flag is not true kex fails */ | ||
135 | if (!(ret_flags & GSS_C_INTEG_FLAG)) | ||
136 | fatal("Integrity check failed"); | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * If we have data to send, then the last message that we | ||
141 | * received cannot have been a 'complete'. | ||
142 | */ | ||
143 | if (send_tok.length != 0) { | ||
144 | if (first) { | ||
145 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || | ||
146 | (r = sshpkt_put_string(ssh, send_tok.value, | ||
147 | send_tok.length)) != 0 || | ||
148 | (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0) | ||
149 | fatal("failed to construct packet: %s", ssh_err(r)); | ||
150 | first = 0; | ||
151 | } else { | ||
152 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || | ||
153 | (r = sshpkt_put_string(ssh, send_tok.value, | ||
154 | send_tok.length)) != 0) | ||
155 | fatal("failed to construct packet: %s", ssh_err(r)); | ||
156 | } | ||
157 | if ((r = sshpkt_send(ssh)) != 0) | ||
158 | fatal("failed to send packet: %s", ssh_err(r)); | ||
159 | gss_release_buffer(&min_status, &send_tok); | ||
160 | |||
161 | /* If we've sent them data, they should reply */ | ||
162 | do { | ||
163 | type = ssh_packet_read(ssh); | ||
164 | if (type == SSH2_MSG_KEXGSS_HOSTKEY) { | ||
165 | debug("Received KEXGSS_HOSTKEY"); | ||
166 | if (server_host_key_blob) | ||
167 | fatal("Server host key received more than once"); | ||
168 | if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) | ||
169 | fatal("Failed to read server host key: %s", ssh_err(r)); | ||
170 | } | ||
171 | } while (type == SSH2_MSG_KEXGSS_HOSTKEY); | ||
172 | |||
173 | switch (type) { | ||
174 | case SSH2_MSG_KEXGSS_CONTINUE: | ||
175 | debug("Received GSSAPI_CONTINUE"); | ||
176 | if (maj_status == GSS_S_COMPLETE) | ||
177 | fatal("GSSAPI Continue received from server when complete"); | ||
178 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, | ||
179 | &recv_tok)) != 0 || | ||
180 | (r = sshpkt_get_end(ssh)) != 0) | ||
181 | fatal("Failed to read token: %s", ssh_err(r)); | ||
182 | break; | ||
183 | case SSH2_MSG_KEXGSS_COMPLETE: | ||
184 | debug("Received GSSAPI_COMPLETE"); | ||
185 | if (msg_tok.value != NULL) | ||
186 | fatal("Received GSSAPI_COMPLETE twice?"); | ||
187 | if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || | ||
188 | (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, | ||
189 | &msg_tok)) != 0) | ||
190 | fatal("Failed to read message: %s", ssh_err(r)); | ||
191 | |||
192 | /* Is there a token included? */ | ||
193 | if ((r = sshpkt_get_u8(ssh, &c)) != 0) | ||
194 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
195 | if (c) { | ||
196 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc( | ||
197 | ssh, &recv_tok)) != 0) | ||
198 | fatal("Failed to read token: %s", ssh_err(r)); | ||
199 | /* If we're already complete - protocol error */ | ||
200 | if (maj_status == GSS_S_COMPLETE) | ||
201 | sshpkt_disconnect(ssh, "Protocol error: received token when complete"); | ||
202 | } else { | ||
203 | /* No token included */ | ||
204 | if (maj_status != GSS_S_COMPLETE) | ||
205 | sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); | ||
206 | } | ||
207 | if ((r = sshpkt_get_end(ssh)) != 0) { | ||
208 | fatal("Expecting end of packet."); | ||
209 | } | ||
210 | break; | ||
211 | case SSH2_MSG_KEXGSS_ERROR: | ||
212 | debug("Received Error"); | ||
213 | if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || | ||
214 | (r = sshpkt_get_u32(ssh, &min_status)) != 0 || | ||
215 | (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || | ||
216 | (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ | ||
217 | (r = sshpkt_get_end(ssh)) != 0) | ||
218 | fatal("sshpkt_get failed: %s", ssh_err(r)); | ||
219 | fatal("GSSAPI Error: \n%.400s", msg); | ||
220 | default: | ||
221 | sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d", | ||
222 | type); | ||
223 | } | ||
224 | token_ptr = &recv_tok; | ||
225 | } else { | ||
226 | /* No data, and not complete */ | ||
227 | if (maj_status != GSS_S_COMPLETE) | ||
228 | fatal("Not complete, and no token output"); | ||
229 | } | ||
230 | } while (maj_status & GSS_S_CONTINUE_NEEDED); | ||
231 | |||
232 | /* | ||
233 | * We _must_ have received a COMPLETE message in reply from the | ||
234 | * server, which will have set server_blob and msg_tok | ||
235 | */ | ||
236 | |||
237 | if (type != SSH2_MSG_KEXGSS_COMPLETE) | ||
238 | fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); | ||
239 | |||
240 | /* compute shared secret */ | ||
241 | switch (kex->kex_type) { | ||
242 | case KEX_GSS_GRP1_SHA1: | ||
243 | case KEX_GSS_GRP14_SHA1: | ||
244 | case KEX_GSS_GRP14_SHA256: | ||
245 | case KEX_GSS_GRP16_SHA512: | ||
246 | r = kex_dh_dec(kex, server_blob, &shared_secret); | ||
247 | break; | ||
248 | case KEX_GSS_C25519_SHA256: | ||
249 | if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80) | ||
250 | fatal("The received key has MSB of last octet set!"); | ||
251 | r = kex_c25519_dec(kex, server_blob, &shared_secret); | ||
252 | break; | ||
253 | case KEX_GSS_NISTP256_SHA256: | ||
254 | if (sshbuf_len(server_blob) != 65) | ||
255 | fatal("The received NIST-P256 key did not match" | ||
256 | "expected length (expected 65, got %zu)", sshbuf_len(server_blob)); | ||
257 | |||
258 | if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED) | ||
259 | fatal("The received NIST-P256 key does not have first octet 0x04"); | ||
260 | |||
261 | r = kex_ecdh_dec(kex, server_blob, &shared_secret); | ||
262 | break; | ||
263 | default: | ||
264 | r = SSH_ERR_INVALID_ARGUMENT; | ||
265 | break; | ||
266 | } | ||
267 | if (r != 0) | ||
268 | goto out; | ||
269 | |||
270 | if ((empty = sshbuf_new()) == NULL) { | ||
271 | r = SSH_ERR_ALLOC_FAIL; | ||
272 | goto out; | ||
273 | } | ||
274 | |||
275 | hashlen = sizeof(hash); | ||
276 | if ((r = kex_gen_hash( | ||
277 | kex->hash_alg, | ||
278 | kex->client_version, | ||
279 | kex->server_version, | ||
280 | kex->my, | ||
281 | kex->peer, | ||
282 | (server_host_key_blob ? server_host_key_blob : empty), | ||
283 | kex->client_pub, | ||
284 | server_blob, | ||
285 | shared_secret, | ||
286 | hash, &hashlen)) != 0) | ||
287 | fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); | ||
288 | |||
289 | gssbuf.value = hash; | ||
290 | gssbuf.length = hashlen; | ||
291 | |||
292 | /* Verify that the hash matches the MIC we just got. */ | ||
293 | if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) | ||
294 | sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); | ||
295 | |||
296 | gss_release_buffer(&min_status, &msg_tok); | ||
297 | |||
298 | if (kex->gss_deleg_creds) | ||
299 | ssh_gssapi_credentials_updated(ctxt); | ||
300 | |||
301 | if (gss_kex_context == NULL) | ||
302 | gss_kex_context = ctxt; | ||
303 | else | ||
304 | ssh_gssapi_delete_ctx(&ctxt); | ||
305 | |||
306 | if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) | ||
307 | r = kex_send_newkeys(ssh); | ||
308 | |||
309 | out: | ||
310 | explicit_bzero(hash, sizeof(hash)); | ||
311 | explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); | ||
312 | sshbuf_free(empty); | ||
313 | sshbuf_free(server_host_key_blob); | ||
314 | sshbuf_free(server_blob); | ||
315 | sshbuf_free(shared_secret); | ||
316 | sshbuf_free(kex->client_pub); | ||
317 | kex->client_pub = NULL; | ||
318 | return r; | ||
319 | } | ||
320 | |||
321 | int | ||
322 | kexgssgex_client(struct ssh *ssh) | ||
323 | { | ||
324 | struct kex *kex = ssh->kex; | ||
325 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, | ||
326 | recv_tok = GSS_C_EMPTY_BUFFER, gssbuf, | ||
327 | msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; | ||
328 | Gssctxt *ctxt; | ||
329 | OM_uint32 maj_status, min_status, ret_flags; | ||
330 | struct sshbuf *shared_secret = NULL; | ||
331 | BIGNUM *p = NULL; | ||
332 | BIGNUM *g = NULL; | ||
333 | struct sshbuf *buf = NULL; | ||
334 | struct sshbuf *server_host_key_blob = NULL; | ||
335 | struct sshbuf *server_blob = NULL; | ||
336 | BIGNUM *dh_server_pub = NULL; | ||
337 | u_char *msg; | ||
338 | int type = 0; | ||
339 | int first = 1; | ||
340 | u_char hash[SSH_DIGEST_MAX_LENGTH]; | ||
341 | size_t hashlen; | ||
342 | const BIGNUM *pub_key, *dh_p, *dh_g; | ||
343 | int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; | ||
344 | struct sshbuf *empty = NULL; | ||
345 | u_char c; | ||
346 | int r; | ||
347 | |||
348 | /* Initialise our GSSAPI world */ | ||
349 | ssh_gssapi_build_ctx(&ctxt); | ||
350 | if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) | ||
351 | == GSS_C_NO_OID) | ||
352 | fatal("Couldn't identify host exchange"); | ||
353 | |||
354 | if (ssh_gssapi_import_name(ctxt, kex->gss_host)) | ||
355 | fatal("Couldn't import hostname"); | ||
356 | |||
357 | if (kex->gss_client && | ||
358 | ssh_gssapi_client_identity(ctxt, kex->gss_client)) | ||
359 | fatal("Couldn't acquire client credentials"); | ||
360 | |||
361 | debug("Doing group exchange"); | ||
362 | nbits = dh_estimate(kex->dh_need * 8); | ||
363 | |||
364 | kex->min = DH_GRP_MIN; | ||
365 | kex->max = DH_GRP_MAX; | ||
366 | kex->nbits = nbits; | ||
367 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 || | ||
368 | (r = sshpkt_put_u32(ssh, min)) != 0 || | ||
369 | (r = sshpkt_put_u32(ssh, nbits)) != 0 || | ||
370 | (r = sshpkt_put_u32(ssh, max)) != 0 || | ||
371 | (r = sshpkt_send(ssh)) != 0) | ||
372 | fatal("Failed to construct a packet: %s", ssh_err(r)); | ||
373 | |||
374 | if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0) | ||
375 | fatal("Error: %s", ssh_err(r)); | ||
376 | |||
377 | if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 || | ||
378 | (r = sshpkt_get_bignum2(ssh, &g)) != 0 || | ||
379 | (r = sshpkt_get_end(ssh)) != 0) | ||
380 | fatal("shpkt_get_bignum2 failed: %s", ssh_err(r)); | ||
381 | |||
382 | if (BN_num_bits(p) < min || BN_num_bits(p) > max) | ||
383 | fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", | ||
384 | min, BN_num_bits(p), max); | ||
385 | |||
386 | if ((kex->dh = dh_new_group(g, p)) == NULL) | ||
387 | fatal("dn_new_group() failed"); | ||
388 | p = g = NULL; /* belong to kex->dh now */ | ||
389 | |||
390 | if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) | ||
391 | goto out; | ||
392 | DH_get0_key(kex->dh, &pub_key, NULL); | ||
393 | |||
394 | token_ptr = GSS_C_NO_BUFFER; | ||
395 | |||
396 | do { | ||
397 | /* Step 2 - call GSS_Init_sec_context() */ | ||
398 | debug("Calling gss_init_sec_context"); | ||
399 | |||
400 | maj_status = ssh_gssapi_init_ctx(ctxt, | ||
401 | kex->gss_deleg_creds, token_ptr, &send_tok, | ||
402 | &ret_flags); | ||
403 | |||
404 | if (GSS_ERROR(maj_status)) { | ||
405 | /* XXX Useles code: Missing send? */ | ||
406 | if (send_tok.length != 0) { | ||
407 | if ((r = sshpkt_start(ssh, | ||
408 | SSH2_MSG_KEXGSS_CONTINUE)) != 0 || | ||
409 | (r = sshpkt_put_string(ssh, send_tok.value, | ||
410 | send_tok.length)) != 0) | ||
411 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
412 | } | ||
413 | fatal("gss_init_context failed"); | ||
414 | } | ||
415 | |||
416 | /* If we've got an old receive buffer get rid of it */ | ||
417 | if (token_ptr != GSS_C_NO_BUFFER) | ||
418 | gss_release_buffer(&min_status, &recv_tok); | ||
419 | |||
420 | if (maj_status == GSS_S_COMPLETE) { | ||
421 | /* If mutual state flag is not true, kex fails */ | ||
422 | if (!(ret_flags & GSS_C_MUTUAL_FLAG)) | ||
423 | fatal("Mutual authentication failed"); | ||
424 | |||
425 | /* If integ avail flag is not true kex fails */ | ||
426 | if (!(ret_flags & GSS_C_INTEG_FLAG)) | ||
427 | fatal("Integrity check failed"); | ||
428 | } | ||
429 | |||
430 | /* | ||
431 | * If we have data to send, then the last message that we | ||
432 | * received cannot have been a 'complete'. | ||
433 | */ | ||
434 | if (send_tok.length != 0) { | ||
435 | if (first) { | ||
436 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || | ||
437 | (r = sshpkt_put_string(ssh, send_tok.value, | ||
438 | send_tok.length)) != 0 || | ||
439 | (r = sshpkt_put_bignum2(ssh, pub_key)) != 0) | ||
440 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
441 | first = 0; | ||
442 | } else { | ||
443 | if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || | ||
444 | (r = sshpkt_put_string(ssh,send_tok.value, | ||
445 | send_tok.length)) != 0) | ||
446 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
447 | } | ||
448 | if ((r = sshpkt_send(ssh)) != 0) | ||
449 | fatal("sshpkt_send failed: %s", ssh_err(r)); | ||
450 | gss_release_buffer(&min_status, &send_tok); | ||
451 | |||
452 | /* If we've sent them data, they should reply */ | ||
453 | do { | ||
454 | type = ssh_packet_read(ssh); | ||
455 | if (type == SSH2_MSG_KEXGSS_HOSTKEY) { | ||
456 | debug("Received KEXGSS_HOSTKEY"); | ||
457 | if (server_host_key_blob) | ||
458 | fatal("Server host key received more than once"); | ||
459 | if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) | ||
460 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
461 | } | ||
462 | } while (type == SSH2_MSG_KEXGSS_HOSTKEY); | ||
463 | |||
464 | switch (type) { | ||
465 | case SSH2_MSG_KEXGSS_CONTINUE: | ||
466 | debug("Received GSSAPI_CONTINUE"); | ||
467 | if (maj_status == GSS_S_COMPLETE) | ||
468 | fatal("GSSAPI Continue received from server when complete"); | ||
469 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, | ||
470 | &recv_tok)) != 0 || | ||
471 | (r = sshpkt_get_end(ssh)) != 0) | ||
472 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
473 | break; | ||
474 | case SSH2_MSG_KEXGSS_COMPLETE: | ||
475 | debug("Received GSSAPI_COMPLETE"); | ||
476 | if (msg_tok.value != NULL) | ||
477 | fatal("Received GSSAPI_COMPLETE twice?"); | ||
478 | if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || | ||
479 | (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, | ||
480 | &msg_tok)) != 0) | ||
481 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
482 | |||
483 | /* Is there a token included? */ | ||
484 | if ((r = sshpkt_get_u8(ssh, &c)) != 0) | ||
485 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
486 | if (c) { | ||
487 | if ((r = ssh_gssapi_sshpkt_get_buffer_desc( | ||
488 | ssh, &recv_tok)) != 0 || | ||
489 | (r = sshpkt_get_end(ssh)) != 0) | ||
490 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
491 | /* If we're already complete - protocol error */ | ||
492 | if (maj_status == GSS_S_COMPLETE) | ||
493 | sshpkt_disconnect(ssh, "Protocol error: received token when complete"); | ||
494 | } else { | ||
495 | /* No token included */ | ||
496 | if (maj_status != GSS_S_COMPLETE) | ||
497 | sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); | ||
498 | } | ||
499 | break; | ||
500 | case SSH2_MSG_KEXGSS_ERROR: | ||
501 | debug("Received Error"); | ||
502 | if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || | ||
503 | (r = sshpkt_get_u32(ssh, &min_status)) != 0 || | ||
504 | (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || | ||
505 | (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ | ||
506 | (r = sshpkt_get_end(ssh)) != 0) | ||
507 | fatal("sshpkt failed: %s", ssh_err(r)); | ||
508 | fatal("GSSAPI Error: \n%.400s", msg); | ||
509 | default: | ||
510 | sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d", | ||
511 | type); | ||
512 | } | ||
513 | token_ptr = &recv_tok; | ||
514 | } else { | ||
515 | /* No data, and not complete */ | ||
516 | if (maj_status != GSS_S_COMPLETE) | ||
517 | fatal("Not complete, and no token output"); | ||
518 | } | ||
519 | } while (maj_status & GSS_S_CONTINUE_NEEDED); | ||
520 | |||
521 | /* | ||
522 | * We _must_ have received a COMPLETE message in reply from the | ||
523 | * server, which will have set dh_server_pub and msg_tok | ||
524 | */ | ||
525 | |||
526 | if (type != SSH2_MSG_KEXGSS_COMPLETE) | ||
527 | fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); | ||
528 | |||
529 | /* 7. C verifies that the key Q_S is valid */ | ||
530 | /* 8. C computes shared secret */ | ||
531 | if ((buf = sshbuf_new()) == NULL || | ||
532 | (r = sshbuf_put_stringb(buf, server_blob)) != 0 || | ||
533 | (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0) | ||
534 | goto out; | ||
535 | sshbuf_free(buf); | ||
536 | buf = NULL; | ||
537 | |||
538 | if ((shared_secret = sshbuf_new()) == NULL) { | ||
539 | r = SSH_ERR_ALLOC_FAIL; | ||
540 | goto out; | ||
541 | } | ||
542 | |||
543 | if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) | ||
544 | goto out; | ||
545 | if ((empty = sshbuf_new()) == NULL) { | ||
546 | r = SSH_ERR_ALLOC_FAIL; | ||
547 | goto out; | ||
548 | } | ||
549 | |||
550 | DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); | ||
551 | hashlen = sizeof(hash); | ||
552 | if ((r = kexgex_hash( | ||
553 | kex->hash_alg, | ||
554 | kex->client_version, | ||
555 | kex->server_version, | ||
556 | kex->my, | ||
557 | kex->peer, | ||
558 | (server_host_key_blob ? server_host_key_blob : empty), | ||
559 | kex->min, kex->nbits, kex->max, | ||
560 | dh_p, dh_g, | ||
561 | pub_key, | ||
562 | dh_server_pub, | ||
563 | sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), | ||
564 | hash, &hashlen)) != 0) | ||
565 | fatal("Failed to calculate hash: %s", ssh_err(r)); | ||
566 | |||
567 | gssbuf.value = hash; | ||
568 | gssbuf.length = hashlen; | ||
569 | |||
570 | /* Verify that the hash matches the MIC we just got. */ | ||
571 | if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) | ||
572 | sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); | ||
573 | |||
574 | gss_release_buffer(&min_status, &msg_tok); | ||
575 | |||
576 | /* save session id */ | ||
577 | if (kex->session_id == NULL) { | ||
578 | kex->session_id_len = hashlen; | ||
579 | kex->session_id = xmalloc(kex->session_id_len); | ||
580 | memcpy(kex->session_id, hash, kex->session_id_len); | ||
581 | } | ||
582 | |||
583 | if (kex->gss_deleg_creds) | ||
584 | ssh_gssapi_credentials_updated(ctxt); | ||
585 | |||
586 | if (gss_kex_context == NULL) | ||
587 | gss_kex_context = ctxt; | ||
588 | else | ||
589 | ssh_gssapi_delete_ctx(&ctxt); | ||
590 | |||
591 | /* Finally derive the keys and send them */ | ||
592 | if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) | ||
593 | r = kex_send_newkeys(ssh); | ||
594 | out: | ||
595 | sshbuf_free(buf); | ||
596 | sshbuf_free(server_blob); | ||
597 | sshbuf_free(empty); | ||
598 | explicit_bzero(hash, sizeof(hash)); | ||
599 | DH_free(kex->dh); | ||
600 | kex->dh = NULL; | ||
601 | BN_clear_free(dh_server_pub); | ||
602 | sshbuf_free(shared_secret); | ||
603 | sshbuf_free(server_host_key_blob); | ||
604 | return r; | ||
605 | } | ||
606 | #endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ | ||