/src/dropbear/src/common-kex.c
Line | Count | Source |
1 | | /* |
2 | | * Dropbear SSH |
3 | | * |
4 | | * Copyright (c) 2002-2004 Matt Johnston |
5 | | * Portions Copyright (c) 2004 by Mihnea Stoenescu |
6 | | * All rights reserved. |
7 | | * |
8 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | | * of this software and associated documentation files (the "Software"), to deal |
10 | | * in the Software without restriction, including without limitation the rights |
11 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | | * copies of the Software, and to permit persons to whom the Software is |
13 | | * furnished to do so, subject to the following conditions: |
14 | | * |
15 | | * The above copyright notice and this permission notice shall be included in |
16 | | * all copies or substantial portions of the Software. |
17 | | * |
18 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
24 | | * SOFTWARE. */ |
25 | | |
26 | | #include "includes.h" |
27 | | #include "dbutil.h" |
28 | | #include "algo.h" |
29 | | #include "buffer.h" |
30 | | #include "session.h" |
31 | | #include "kex.h" |
32 | | #include "ssh.h" |
33 | | #include "packet.h" |
34 | | #include "bignum.h" |
35 | | #include "dbrandom.h" |
36 | | #include "runopts.h" |
37 | | |
38 | | static void kexinitialise(void); |
39 | | static void gen_new_keys(void); |
40 | | #ifndef DISABLE_ZLIB |
41 | | static void gen_new_zstream_recv(void); |
42 | | static void gen_new_zstream_trans(void); |
43 | | #endif |
44 | | static void read_kex_algos(void); |
45 | | /* helper function for gen_new_keys */ |
46 | | static void hashkeys(unsigned char *out, unsigned int outlen, |
47 | | const hash_state * hs, const unsigned char X); |
48 | | |
49 | | |
50 | | /* Send our list of algorithms we can use */ |
51 | 11.7k | void send_msg_kexinit() { |
52 | | |
53 | 11.7k | CHECKCLEARTOWRITE(); |
54 | 11.7k | buf_putbyte(ses.writepayload, SSH_MSG_KEXINIT); |
55 | | |
56 | | /* cookie */ |
57 | 11.7k | genrandom(buf_getwriteptr(ses.writepayload, 16), 16); |
58 | 11.7k | buf_incrwritepos(ses.writepayload, 16); |
59 | | |
60 | | /* kex algos */ |
61 | 11.7k | buf_put_algolist(ses.writepayload, sshkex); |
62 | | |
63 | | /* server_host_key_algorithms */ |
64 | 11.7k | buf_put_algolist(ses.writepayload, sigalgs); |
65 | | |
66 | | /* encryption_algorithms_client_to_server */ |
67 | 11.7k | buf_put_algolist(ses.writepayload, sshciphers); |
68 | | |
69 | | /* encryption_algorithms_server_to_client */ |
70 | 11.7k | buf_put_algolist(ses.writepayload, sshciphers); |
71 | | |
72 | | /* mac_algorithms_client_to_server */ |
73 | 11.7k | buf_put_algolist(ses.writepayload, sshhashes); |
74 | | |
75 | | /* mac_algorithms_server_to_client */ |
76 | 11.7k | buf_put_algolist(ses.writepayload, sshhashes); |
77 | | |
78 | | |
79 | | /* compression_algorithms_client_to_server */ |
80 | 11.7k | buf_put_algolist(ses.writepayload, ses.compress_algos_c2s); |
81 | | |
82 | | /* compression_algorithms_server_to_client */ |
83 | 11.7k | buf_put_algolist(ses.writepayload, ses.compress_algos_s2c); |
84 | | |
85 | | /* languages_client_to_server */ |
86 | 11.7k | buf_putstring(ses.writepayload, "", 0); |
87 | | |
88 | | /* languages_server_to_client */ |
89 | 11.7k | buf_putstring(ses.writepayload, "", 0); |
90 | | |
91 | | /* first_kex_packet_follows */ |
92 | 11.7k | buf_putbyte(ses.writepayload, (ses.send_kex_first_guess != NULL)); |
93 | | |
94 | | /* reserved unit32 */ |
95 | 11.7k | buf_putint(ses.writepayload, 0); |
96 | | |
97 | | /* set up transmitted kex packet buffer for hashing. |
98 | | * This is freed after the end of the kex */ |
99 | 11.7k | ses.transkexinit = buf_newcopy(ses.writepayload); |
100 | | |
101 | 11.7k | encrypt_packet(); |
102 | 11.7k | ses.dataallowed = 0; /* don't send other packets during kex */ |
103 | | |
104 | 11.7k | ses.kexstate.sentkexinit = 1; |
105 | | |
106 | 11.7k | ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); |
107 | | |
108 | 11.7k | if (ses.send_kex_first_guess) { |
109 | 0 | ses.newkeys->algo_kex = first_usable_algo(sshkex)->data; |
110 | 0 | ses.newkeys->algo_signature = first_usable_algo(sigalgs)->val; |
111 | 0 | ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature); |
112 | 0 | ses.send_kex_first_guess(); |
113 | 0 | } |
114 | | |
115 | 11.7k | TRACE(("DATAALLOWED=0")) |
116 | 11.7k | TRACE(("-> KEXINIT")) |
117 | | |
118 | 11.7k | } |
119 | | |
120 | 19.9k | static void switch_keys() { |
121 | 19.9k | TRACE2(("enter switch_keys")) |
122 | 19.9k | if (!(ses.kexstate.sentkexinit && ses.kexstate.recvkexinit)) { |
123 | 8 | dropbear_exit("Unexpected newkeys message"); |
124 | 8 | } |
125 | | |
126 | 19.9k | if (!ses.keys) { |
127 | 0 | ses.keys = m_malloc(sizeof(*ses.newkeys)); |
128 | 0 | } |
129 | 19.9k | if (ses.kexstate.recvnewkeys && ses.newkeys->recv.valid) { |
130 | 9.77k | TRACE(("switch_keys recv")) |
131 | | #ifndef DISABLE_ZLIB |
132 | | gen_new_zstream_recv(); |
133 | | #endif |
134 | 9.77k | ses.keys->recv = ses.newkeys->recv; |
135 | 9.77k | m_burn(&ses.newkeys->recv, sizeof(ses.newkeys->recv)); |
136 | 9.77k | ses.newkeys->recv.valid = 0; |
137 | 9.77k | } |
138 | 19.9k | if (ses.kexstate.sentnewkeys && ses.newkeys->trans.valid) { |
139 | 10.1k | TRACE(("switch_keys trans")) |
140 | | #ifndef DISABLE_ZLIB |
141 | | gen_new_zstream_trans(); |
142 | | #endif |
143 | 10.1k | ses.keys->trans = ses.newkeys->trans; |
144 | 10.1k | m_burn(&ses.newkeys->trans, sizeof(ses.newkeys->trans)); |
145 | 10.1k | ses.newkeys->trans.valid = 0; |
146 | 10.1k | } |
147 | 19.9k | if (ses.kexstate.sentnewkeys && ses.kexstate.recvnewkeys) |
148 | 9.77k | { |
149 | 9.77k | TRACE(("switch_keys done")) |
150 | 9.77k | ses.keys->algo_kex = ses.newkeys->algo_kex; |
151 | 9.77k | ses.keys->algo_hostkey = ses.newkeys->algo_hostkey; |
152 | 9.77k | ses.keys->algo_signature = ses.newkeys->algo_signature; |
153 | 9.77k | ses.keys->allow_compress = 0; |
154 | 9.77k | m_free(ses.newkeys); |
155 | 9.77k | ses.newkeys = NULL; |
156 | 9.77k | kexinitialise(); |
157 | 9.77k | } |
158 | 19.9k | TRACE2(("leave switch_keys")) |
159 | 19.9k | } |
160 | | |
161 | | /* Bring new keys into use after a key exchange, and let the client know*/ |
162 | 10.1k | void send_msg_newkeys() { |
163 | | |
164 | 10.1k | TRACE(("enter send_msg_newkeys")) |
165 | | |
166 | | /* generate the kexinit request */ |
167 | 10.1k | CHECKCLEARTOWRITE(); |
168 | 10.1k | buf_putbyte(ses.writepayload, SSH_MSG_NEWKEYS); |
169 | 10.1k | encrypt_packet(); |
170 | | |
171 | | |
172 | | /* set up our state */ |
173 | 10.1k | ses.kexstate.sentnewkeys = 1; |
174 | 10.1k | if (ses.kexstate.donefirstkex) { |
175 | 8.48k | ses.kexstate.donesecondkex = 1; |
176 | 8.48k | } |
177 | 10.1k | ses.kexstate.donefirstkex = 1; |
178 | 10.1k | ses.dataallowed = 1; /* we can send other packets again now */ |
179 | 10.1k | gen_new_keys(); |
180 | 10.1k | switch_keys(); |
181 | | |
182 | 10.1k | if (ses.kexstate.strict_kex) { |
183 | 1.33k | ses.transseq = 0; |
184 | 1.33k | } |
185 | | |
186 | 10.1k | TRACE(("leave send_msg_newkeys")) |
187 | 10.1k | } |
188 | | |
189 | | /* Bring the new keys into use after a key exchange */ |
190 | 9.78k | void recv_msg_newkeys() { |
191 | | |
192 | 9.78k | TRACE(("enter recv_msg_newkeys")) |
193 | | |
194 | 9.78k | ses.kexstate.recvnewkeys = 1; |
195 | 9.78k | switch_keys(); |
196 | | |
197 | 9.78k | if (ses.kexstate.strict_kex) { |
198 | 1.32k | ses.recvseq = 0; |
199 | 1.32k | } |
200 | 9.78k | ses.kexstate.recvfirstnewkeys = 1; |
201 | | |
202 | 9.78k | TRACE(("leave recv_msg_newkeys")) |
203 | 9.78k | } |
204 | | |
205 | 3.01k | static void kex_setup_compress(void) { |
206 | 3.01k | #ifdef DISABLE_ZLIB |
207 | 3.01k | ses.compress_algos_c2s = ssh_nocompress; |
208 | 3.01k | ses.compress_algos_s2c = ssh_nocompress; |
209 | | #else |
210 | | |
211 | | if (!opts.allow_compress) { |
212 | | ses.compress_algos_c2s = ssh_nocompress; |
213 | | ses.compress_algos_s2c = ssh_nocompress; |
214 | | return; |
215 | | } |
216 | | |
217 | | if (IS_DROPBEAR_CLIENT) { |
218 | | /* TODO: should c2s in dbclient be disabled? |
219 | | * Current Dropbear server disables it. Disabling it also |
220 | | * lets DROPBEAR_CLI_IMMEDIATE_AUTH work (see comment there) */ |
221 | | ses.compress_algos_c2s = ssh_compress; |
222 | | ses.compress_algos_s2c = ssh_compress; |
223 | | } else { |
224 | | ses.compress_algos_c2s = ssh_nocompress; |
225 | | ses.compress_algos_s2c = ssh_compress; |
226 | | } |
227 | | #endif |
228 | 3.01k | } |
229 | | |
230 | | /* Set up the kex for the first time */ |
231 | 3.01k | void kexfirstinitialise() { |
232 | 3.01k | kex_setup_compress(); |
233 | 3.01k | kexinitialise(); |
234 | 3.01k | } |
235 | | |
236 | | /* Reset the kex state, ready for a new negotiation */ |
237 | 12.7k | static void kexinitialise() { |
238 | | |
239 | 12.7k | TRACE(("kexinitialise()")) |
240 | | |
241 | | /* sent/recv'd MSG_KEXINIT */ |
242 | 12.7k | ses.kexstate.sentkexinit = 0; |
243 | 12.7k | ses.kexstate.recvkexinit = 0; |
244 | | |
245 | | /* sent/recv'd MSG_NEWKEYS */ |
246 | 12.7k | ses.kexstate.recvnewkeys = 0; |
247 | 12.7k | ses.kexstate.sentnewkeys = 0; |
248 | | |
249 | | /* first_packet_follows */ |
250 | 12.7k | ses.kexstate.them_firstfollows = 0; |
251 | | |
252 | 12.7k | ses.kexstate.datatrans = 0; |
253 | 12.7k | ses.kexstate.datarecv = 0; |
254 | | |
255 | 12.7k | ses.kexstate.our_first_follows_matches = 0; |
256 | | |
257 | 12.7k | ses.kexstate.lastkextime = monotonic_now(); |
258 | | |
259 | 12.7k | } |
260 | | |
261 | | /* Helper function for gen_new_keys, creates a hash. It makes a copy of the |
262 | | * already initialised hash_state hs, which should already have processed |
263 | | * the dh_K and hash, since these are common. X is the letter 'A', 'B' etc. |
264 | | * out must have at least min(hash_size, outlen) bytes allocated. |
265 | | * |
266 | | * See Section 7.2 of rfc4253 (ssh transport) for details */ |
267 | | static void hashkeys(unsigned char *out, unsigned int outlen, |
268 | 45.6k | const hash_state * hs, const unsigned char X) { |
269 | | |
270 | 45.6k | const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc; |
271 | 45.6k | hash_state hs2; |
272 | 45.6k | unsigned int offset; |
273 | 45.6k | unsigned char tmpout[MAX_HASH_SIZE]; |
274 | | |
275 | 45.6k | memcpy(&hs2, hs, sizeof(hash_state)); |
276 | 45.6k | hash_desc->process(&hs2, &X, 1); |
277 | 45.6k | hash_desc->process(&hs2, ses.session_id->data, ses.session_id->len); |
278 | 45.6k | hash_desc->done(&hs2, tmpout); |
279 | 45.6k | memcpy(out, tmpout, MIN(hash_desc->hashsize, outlen)); |
280 | 45.6k | for (offset = hash_desc->hashsize; |
281 | 64.2k | offset < outlen; |
282 | 45.6k | offset += hash_desc->hashsize) |
283 | 18.6k | { |
284 | | /* need to extend */ |
285 | 18.6k | memcpy(&hs2, hs, sizeof(hash_state)); |
286 | 18.6k | hash_desc->process(&hs2, out, offset); |
287 | 18.6k | hash_desc->done(&hs2, tmpout); |
288 | 18.6k | memcpy(&out[offset], tmpout, MIN(outlen - offset, hash_desc->hashsize)); |
289 | 18.6k | } |
290 | 45.6k | m_burn(&hs2, sizeof(hash_state)); |
291 | 45.6k | } |
292 | | |
293 | | /* Generate the actual encryption/integrity keys, using the results of the |
294 | | * key exchange, as specified in section 7.2 of the transport rfc 4253. |
295 | | * This occurs after the DH key-exchange. |
296 | | * |
297 | | * ses.newkeys is the new set of keys which are generated, these are only |
298 | | * taken into use after both sides have sent a newkeys message */ |
299 | | |
300 | 10.1k | static void gen_new_keys() { |
301 | | |
302 | 10.1k | unsigned char C2S_IV[MAX_IV_LEN]; |
303 | 10.1k | unsigned char C2S_key[MAX_KEY_LEN]; |
304 | 10.1k | unsigned char S2C_IV[MAX_IV_LEN]; |
305 | 10.1k | unsigned char S2C_key[MAX_KEY_LEN]; |
306 | | /* unsigned char key[MAX_KEY_LEN]; */ |
307 | 10.1k | unsigned char *trans_IV, *trans_key, *recv_IV, *recv_key; |
308 | | |
309 | 10.1k | hash_state hs; |
310 | 10.1k | const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc; |
311 | 10.1k | char mactransletter, macrecvletter; /* Client or server specific */ |
312 | | |
313 | 10.1k | TRACE(("enter gen_new_keys")) |
314 | | /* the dh_K and hash are the start of all hashes, we make use of that */ |
315 | | |
316 | 10.1k | hash_desc->init(&hs); |
317 | 10.1k | if (ses.dh_K) { |
318 | 10.1k | hash_process_mp(hash_desc, &hs, ses.dh_K); |
319 | 10.1k | mp_clear(ses.dh_K); |
320 | 10.1k | m_free(ses.dh_K); |
321 | 10.1k | } |
322 | 10.1k | if (ses.dh_K_bytes) { |
323 | 0 | hash_desc->process(&hs, ses.dh_K_bytes->data, ses.dh_K_bytes->len); |
324 | 0 | buf_burn_free(ses.dh_K_bytes); |
325 | 0 | ses.dh_K_bytes = NULL; |
326 | 0 | } |
327 | 10.1k | hash_desc->process(&hs, ses.hash->data, ses.hash->len); |
328 | 10.1k | buf_burn_free(ses.hash); |
329 | 10.1k | ses.hash = NULL; |
330 | | |
331 | 10.1k | if (IS_DROPBEAR_CLIENT) { |
332 | 0 | trans_IV = C2S_IV; |
333 | 0 | recv_IV = S2C_IV; |
334 | 0 | trans_key = C2S_key; |
335 | 0 | recv_key = S2C_key; |
336 | 0 | mactransletter = 'E'; |
337 | 0 | macrecvletter = 'F'; |
338 | 10.1k | } else { |
339 | 10.1k | trans_IV = S2C_IV; |
340 | 10.1k | recv_IV = C2S_IV; |
341 | 10.1k | trans_key = S2C_key; |
342 | 10.1k | recv_key = C2S_key; |
343 | 10.1k | mactransletter = 'F'; |
344 | 10.1k | macrecvletter = 'E'; |
345 | 10.1k | } |
346 | | |
347 | 10.1k | hashkeys(C2S_IV, sizeof(C2S_IV), &hs, 'A'); |
348 | 10.1k | hashkeys(S2C_IV, sizeof(S2C_IV), &hs, 'B'); |
349 | 10.1k | hashkeys(C2S_key, sizeof(C2S_key), &hs, 'C'); |
350 | 10.1k | hashkeys(S2C_key, sizeof(S2C_key), &hs, 'D'); |
351 | | |
352 | 10.1k | if (ses.newkeys->recv.algo_crypt->cipherdesc != NULL) { |
353 | 10.1k | int recv_cipher = -1; |
354 | 10.1k | if (ses.newkeys->recv.algo_crypt->cipherdesc->name != NULL) { |
355 | 4.68k | recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name); |
356 | 4.68k | if (recv_cipher < 0) { |
357 | 0 | dropbear_exit("Crypto error"); |
358 | 0 | } |
359 | 4.68k | } |
360 | 10.1k | if (ses.newkeys->recv.crypt_mode->start(recv_cipher, |
361 | 10.1k | recv_IV, recv_key, |
362 | 10.1k | ses.newkeys->recv.algo_crypt->keysize, 0, |
363 | 10.1k | &ses.newkeys->recv.cipher_state) != CRYPT_OK) { |
364 | 0 | dropbear_exit("Crypto error"); |
365 | 0 | } |
366 | 10.1k | } |
367 | | |
368 | 10.1k | if (ses.newkeys->trans.algo_crypt->cipherdesc != NULL) { |
369 | 10.1k | int trans_cipher = -1; |
370 | 10.1k | if (ses.newkeys->trans.algo_crypt->cipherdesc->name != NULL) { |
371 | 4.87k | trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name); |
372 | 4.87k | if (trans_cipher < 0) { |
373 | 0 | dropbear_exit("Crypto error"); |
374 | 0 | } |
375 | 4.87k | } |
376 | 10.1k | if (ses.newkeys->trans.crypt_mode->start(trans_cipher, |
377 | 10.1k | trans_IV, trans_key, |
378 | 10.1k | ses.newkeys->trans.algo_crypt->keysize, 0, |
379 | 10.1k | &ses.newkeys->trans.cipher_state) != CRYPT_OK) { |
380 | 0 | dropbear_exit("Crypto error"); |
381 | 0 | } |
382 | 10.1k | } |
383 | | |
384 | 10.1k | if (ses.newkeys->trans.algo_mac->hash_desc != NULL) { |
385 | 4.87k | hashkeys(ses.newkeys->trans.mackey, |
386 | 4.87k | ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter); |
387 | 4.87k | ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hash_desc->name); |
388 | 4.87k | } |
389 | | |
390 | 10.1k | if (ses.newkeys->recv.algo_mac->hash_desc != NULL) { |
391 | 0 | hashkeys(ses.newkeys->recv.mackey, |
392 | 0 | ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter); |
393 | 0 | ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hash_desc->name); |
394 | 0 | } |
395 | | |
396 | | /* Ready to switch over */ |
397 | 10.1k | ses.newkeys->trans.valid = 1; |
398 | 10.1k | ses.newkeys->recv.valid = 1; |
399 | | |
400 | 10.1k | m_burn(C2S_IV, sizeof(C2S_IV)); |
401 | 10.1k | m_burn(C2S_key, sizeof(C2S_key)); |
402 | 10.1k | m_burn(S2C_IV, sizeof(S2C_IV)); |
403 | 10.1k | m_burn(S2C_key, sizeof(S2C_key)); |
404 | 10.1k | m_burn(&hs, sizeof(hash_state)); |
405 | | |
406 | 10.1k | TRACE(("leave gen_new_keys")) |
407 | 10.1k | } |
408 | | |
409 | | #ifndef DISABLE_ZLIB |
410 | | |
411 | | int is_compress_trans() { |
412 | | return ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB |
413 | | || (ses.authstate.authdone |
414 | | && ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY); |
415 | | } |
416 | | |
417 | | int is_compress_recv() { |
418 | | return ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB |
419 | | || (ses.authstate.authdone |
420 | | && ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY); |
421 | | } |
422 | | |
423 | | static void* dropbear_zalloc(void* UNUSED(opaque), uInt items, uInt size) { |
424 | | return m_calloc(items, size); |
425 | | } |
426 | | |
427 | | static void dropbear_zfree(void* UNUSED(opaque), void* ptr) { |
428 | | m_free(ptr); |
429 | | } |
430 | | |
431 | | /* Set up new zlib compression streams, close the old ones. Only |
432 | | * called from gen_new_keys() */ |
433 | | static void gen_new_zstream_recv() { |
434 | | |
435 | | /* create new zstreams */ |
436 | | if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB |
437 | | || ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { |
438 | | ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream)); |
439 | | ses.newkeys->recv.zstream->zalloc = dropbear_zalloc; |
440 | | ses.newkeys->recv.zstream->zfree = dropbear_zfree; |
441 | | |
442 | | if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) { |
443 | | dropbear_exit("zlib error"); |
444 | | } |
445 | | } else { |
446 | | ses.newkeys->recv.zstream = NULL; |
447 | | } |
448 | | /* clean up old keys */ |
449 | | if (ses.keys->recv.zstream != NULL) { |
450 | | if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) { |
451 | | /* Z_DATA_ERROR is ok, just means that stream isn't ended */ |
452 | | dropbear_exit("Crypto error"); |
453 | | } |
454 | | m_free(ses.keys->recv.zstream); |
455 | | } |
456 | | } |
457 | | |
458 | | static void gen_new_zstream_trans() { |
459 | | |
460 | | if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB |
461 | | || ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { |
462 | | ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream)); |
463 | | ses.newkeys->trans.zstream->zalloc = dropbear_zalloc; |
464 | | ses.newkeys->trans.zstream->zfree = dropbear_zfree; |
465 | | |
466 | | if (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION, |
467 | | Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS, |
468 | | DROPBEAR_ZLIB_MEM_LEVEL, Z_DEFAULT_STRATEGY) |
469 | | != Z_OK) { |
470 | | dropbear_exit("zlib error"); |
471 | | } |
472 | | } else { |
473 | | ses.newkeys->trans.zstream = NULL; |
474 | | } |
475 | | |
476 | | if (ses.keys->trans.zstream != NULL) { |
477 | | if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) { |
478 | | /* Z_DATA_ERROR is ok, just means that stream isn't ended */ |
479 | | dropbear_exit("Crypto error"); |
480 | | } |
481 | | m_free(ses.keys->trans.zstream); |
482 | | } |
483 | | } |
484 | | #endif /* DISABLE_ZLIB */ |
485 | | |
486 | | |
487 | | /* Executed upon receiving a kexinit message from the client to initiate |
488 | | * key exchange. If we haven't already done so, we send the list of our |
489 | | * preferred algorithms. The client's requested algorithms are processed, |
490 | | * and we calculate the first portion of the key-exchange-hash for used |
491 | | * later in the key exchange. No response is sent, as the client should |
492 | | * initiate the diffie-hellman key exchange */ |
493 | 11.1k | void recv_msg_kexinit() { |
494 | | |
495 | 11.1k | unsigned int kexhashbuf_len = 0; |
496 | 11.1k | unsigned int remote_ident_len = 0; |
497 | 11.1k | unsigned int local_ident_len = 0; |
498 | | |
499 | 11.1k | TRACE(("<- KEXINIT")) |
500 | 11.1k | TRACE(("enter recv_msg_kexinit")) |
501 | | |
502 | 11.1k | if (!ses.kexstate.sentkexinit) { |
503 | | /* we need to send a kex packet */ |
504 | 8.71k | send_msg_kexinit(); |
505 | 8.71k | TRACE(("continue recv_msg_kexinit: sent kexinit")) |
506 | 8.71k | } |
507 | | |
508 | | /* "Once a party has sent a SSH_MSG_KEXINIT message ... |
509 | | further SSH_MSG_KEXINIT messages MUST NOT be sent" */ |
510 | 11.1k | if (ses.kexstate.recvkexinit) { |
511 | 0 | dropbear_exit("Unexpected KEXINIT"); |
512 | 0 | } |
513 | | |
514 | | /* start the kex hash */ |
515 | 11.1k | local_ident_len = strlen(LOCAL_IDENT); |
516 | 11.1k | remote_ident_len = strlen(ses.remoteident); |
517 | | |
518 | 11.1k | kexhashbuf_len = local_ident_len + remote_ident_len |
519 | 11.1k | + ses.transkexinit->len + ses.payload->len |
520 | 11.1k | + KEXHASHBUF_MAX_INTS; |
521 | | |
522 | 11.1k | ses.kexhashbuf = buf_new(kexhashbuf_len); |
523 | | |
524 | 11.1k | if (IS_DROPBEAR_CLIENT) { |
525 | | |
526 | | /* read the peer's choice of algos */ |
527 | 0 | read_kex_algos(); |
528 | | |
529 | | /* V_C, the client's version string (CR and NL excluded) */ |
530 | 0 | buf_putstring(ses.kexhashbuf, LOCAL_IDENT, local_ident_len); |
531 | | /* V_S, the server's version string (CR and NL excluded) */ |
532 | 0 | buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len); |
533 | | |
534 | | /* I_C, the payload of the client's SSH_MSG_KEXINIT */ |
535 | 0 | buf_putstring(ses.kexhashbuf, |
536 | 0 | (const char*)ses.transkexinit->data, ses.transkexinit->len); |
537 | | /* I_S, the payload of the server's SSH_MSG_KEXINIT */ |
538 | 0 | buf_setpos(ses.payload, ses.payload_beginning); |
539 | 0 | buf_putstring(ses.kexhashbuf, |
540 | 0 | (const char*)buf_getptr(ses.payload, ses.payload->len-ses.payload->pos), |
541 | 0 | ses.payload->len-ses.payload->pos); |
542 | 0 | ses.requirenext = SSH_MSG_KEXDH_REPLY; |
543 | 11.1k | } else { |
544 | | /* SERVER */ |
545 | | |
546 | | /* read the peer's choice of algos */ |
547 | 11.1k | read_kex_algos(); |
548 | | /* V_C, the client's version string (CR and NL excluded) */ |
549 | 11.1k | buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len); |
550 | | /* V_S, the server's version string (CR and NL excluded) */ |
551 | 11.1k | buf_putstring(ses.kexhashbuf, LOCAL_IDENT, local_ident_len); |
552 | | |
553 | | /* I_C, the payload of the client's SSH_MSG_KEXINIT */ |
554 | 11.1k | buf_setpos(ses.payload, ses.payload_beginning); |
555 | 11.1k | buf_putstring(ses.kexhashbuf, |
556 | 11.1k | (const char*)buf_getptr(ses.payload, ses.payload->len-ses.payload->pos), |
557 | 11.1k | ses.payload->len-ses.payload->pos); |
558 | | |
559 | | /* I_S, the payload of the server's SSH_MSG_KEXINIT */ |
560 | 11.1k | buf_putstring(ses.kexhashbuf, |
561 | 11.1k | (const char*)ses.transkexinit->data, ses.transkexinit->len); |
562 | | |
563 | 11.1k | ses.requirenext = SSH_MSG_KEXDH_INIT; |
564 | 11.1k | } |
565 | | |
566 | 11.1k | buf_free(ses.transkexinit); |
567 | 11.1k | ses.transkexinit = NULL; |
568 | | /* the rest of ses.kexhashbuf will be done after DH exchange */ |
569 | | |
570 | 11.1k | ses.kexstate.recvkexinit = 1; |
571 | | |
572 | 11.1k | if (ses.kexstate.strict_kex && !ses.kexstate.donefirstkex && ses.recvseq != 1) { |
573 | 8 | dropbear_exit("First packet wasn't kexinit"); |
574 | 8 | } |
575 | | |
576 | 11.1k | TRACE(("leave recv_msg_kexinit")) |
577 | 11.1k | } |
578 | | |
579 | | |
580 | 10.1k | void finish_kexhashbuf(void) { |
581 | 10.1k | hash_state hs; |
582 | 10.1k | const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc; |
583 | | |
584 | 10.1k | hash_desc->init(&hs); |
585 | 10.1k | buf_setpos(ses.kexhashbuf, 0); |
586 | 10.1k | hash_desc->process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), |
587 | 10.1k | ses.kexhashbuf->len); |
588 | 10.1k | ses.hash = buf_new(hash_desc->hashsize); |
589 | 10.1k | hash_desc->done(&hs, buf_getwriteptr(ses.hash, hash_desc->hashsize)); |
590 | 10.1k | buf_setlen(ses.hash, hash_desc->hashsize); |
591 | | |
592 | | #if defined(DEBUG_KEXHASH) && DEBUG_TRACE |
593 | | if (!debug_trace) { |
594 | | printhex("kexhashbuf", ses.kexhashbuf->data, ses.kexhashbuf->len); |
595 | | printhex("kexhash", ses.hash->data, ses.hash->len); |
596 | | } |
597 | | #endif |
598 | | |
599 | 10.1k | buf_burn_free(ses.kexhashbuf); |
600 | 10.1k | m_burn(&hs, sizeof(hash_state)); |
601 | 10.1k | ses.kexhashbuf = NULL; |
602 | | |
603 | | /* first time around, we set the session_id to H */ |
604 | 10.1k | if (ses.session_id == NULL) { |
605 | | /* create the session_id, this never needs freeing */ |
606 | 1.70k | ses.session_id = buf_newcopy(ses.hash); |
607 | 1.70k | } |
608 | 10.1k | } |
609 | | |
610 | | /* read the other side's algo list. buf_match_algo is a callback to match |
611 | | * algos for the client or server. */ |
612 | 11.1k | static void read_kex_algos() { |
613 | | |
614 | | /* for asymmetry */ |
615 | 11.1k | algo_type * c2s_hash_algo = NULL; |
616 | 11.1k | algo_type * s2c_hash_algo = NULL; |
617 | 11.1k | algo_type * c2s_cipher_algo = NULL; |
618 | 11.1k | algo_type * s2c_cipher_algo = NULL; |
619 | 11.1k | algo_type * c2s_comp_algo = NULL; |
620 | 11.1k | algo_type * s2c_comp_algo = NULL; |
621 | | /* the generic one */ |
622 | 11.1k | algo_type * algo = NULL; |
623 | | |
624 | | /* which algo couldn't match */ |
625 | 11.1k | char * erralgo = NULL; |
626 | | |
627 | 11.1k | int goodguess = 0; |
628 | 11.1k | int allgood = 1; /* we AND this with each goodguess and see if its still |
629 | | true after */ |
630 | 11.1k | int kexguess2 = 0; |
631 | | |
632 | 11.1k | buf_incrpos(ses.payload, 16); /* start after the cookie */ |
633 | | |
634 | 11.1k | memset(ses.newkeys, 0x0, sizeof(*ses.newkeys)); |
635 | | |
636 | | /* kex_algorithms */ |
637 | 11.1k | #if DROPBEAR_KEXGUESS2 |
638 | 11.1k | if (buf_has_algo(ses.payload, KEXGUESS2_ALGO_NAME) == DROPBEAR_SUCCESS) { |
639 | 258 | kexguess2 = 1; |
640 | 258 | } |
641 | 11.1k | #endif |
642 | | |
643 | 11.1k | #if DROPBEAR_EXT_INFO |
644 | | /* Determine if SSH_MSG_EXT_INFO messages should be sent. |
645 | | Should be done for the first key exchange. Only required on server side |
646 | | for server-sig-algs */ |
647 | 11.1k | if (IS_DROPBEAR_SERVER) { |
648 | 11.0k | if (!ses.kexstate.donefirstkex) { |
649 | 2.35k | if (buf_has_algo(ses.payload, SSH_EXT_INFO_C) == DROPBEAR_SUCCESS) { |
650 | 142 | ses.allow_ext_info = 1; |
651 | 142 | } |
652 | 2.35k | } |
653 | 11.0k | } |
654 | 11.1k | #endif |
655 | | |
656 | 11.1k | if (!ses.kexstate.donefirstkex) { |
657 | 2.35k | const char* strict_name; |
658 | 2.35k | if (IS_DROPBEAR_CLIENT) { |
659 | 0 | strict_name = SSH_STRICT_KEX_S; |
660 | 2.35k | } else { |
661 | 2.35k | strict_name = SSH_STRICT_KEX_C; |
662 | 2.35k | } |
663 | 2.35k | if (buf_has_algo(ses.payload, strict_name) == DROPBEAR_SUCCESS) { |
664 | 66 | ses.kexstate.strict_kex = 1; |
665 | 66 | } |
666 | 2.35k | } |
667 | | |
668 | 11.1k | algo = buf_match_algo(ses.payload, sshkex, kexguess2, &goodguess); |
669 | 11.1k | allgood &= goodguess; |
670 | 11.1k | if (algo == NULL || algo->data == NULL) { |
671 | | /* kexguess2, ext-info-c, ext-info-s should not match negotiation */ |
672 | 224 | erralgo = "kex"; |
673 | 224 | goto error; |
674 | 224 | } |
675 | 10.9k | TRACE(("kexguess2 %d", kexguess2)) |
676 | 10.9k | DEBUG3(("kex algo %s", algo->name)) |
677 | 10.9k | ses.newkeys->algo_kex = algo->data; |
678 | | |
679 | | /* server_host_key_algorithms */ |
680 | 10.9k | algo = buf_match_algo(ses.payload, sigalgs, kexguess2, &goodguess); |
681 | 10.9k | allgood &= goodguess; |
682 | 10.9k | if (algo == NULL) { |
683 | 8 | erralgo = "hostkey"; |
684 | 8 | goto error; |
685 | 8 | } |
686 | 10.9k | DEBUG2(("hostkey algo %s", algo->name)) |
687 | 10.9k | ses.newkeys->algo_signature = algo->val; |
688 | 10.9k | ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature); |
689 | | |
690 | | /* encryption_algorithms_client_to_server */ |
691 | 10.9k | c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, 0, NULL); |
692 | 10.9k | if (c2s_cipher_algo == NULL) { |
693 | 19 | erralgo = "enc c->s"; |
694 | 19 | goto error; |
695 | 19 | } |
696 | 10.8k | DEBUG2(("enc c2s is %s", c2s_cipher_algo->name)) |
697 | | |
698 | | /* encryption_algorithms_server_to_client */ |
699 | 10.8k | s2c_cipher_algo = buf_match_algo(ses.payload, sshciphers, 0, NULL); |
700 | 10.8k | if (s2c_cipher_algo == NULL) { |
701 | 10 | erralgo = "enc s->c"; |
702 | 10 | goto error; |
703 | 10 | } |
704 | 10.8k | DEBUG2(("enc s2c is %s", s2c_cipher_algo->name)) |
705 | | |
706 | | /* mac_algorithms_client_to_server */ |
707 | 10.8k | c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, NULL); |
708 | 10.8k | #if DROPBEAR_AEAD_MODE |
709 | 10.8k | if (((struct dropbear_cipher_mode*)c2s_cipher_algo->mode)->aead_crypt != NULL) { |
710 | 5.59k | c2s_hash_algo = NULL; |
711 | 5.59k | } else |
712 | 5.28k | #endif |
713 | 5.28k | if (c2s_hash_algo == NULL) { |
714 | 1 | erralgo = "mac c->s"; |
715 | 1 | goto error; |
716 | 1 | } |
717 | 10.8k | DEBUG2(("hmac c2s is %s", c2s_hash_algo ? c2s_hash_algo->name : "<implicit>")) |
718 | | |
719 | | /* mac_algorithms_server_to_client */ |
720 | 10.8k | s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, NULL); |
721 | 10.8k | #if DROPBEAR_AEAD_MODE |
722 | 10.8k | if (((struct dropbear_cipher_mode*)s2c_cipher_algo->mode)->aead_crypt != NULL) { |
723 | 5.40k | s2c_hash_algo = NULL; |
724 | 5.40k | } else |
725 | 5.47k | #endif |
726 | 5.47k | if (s2c_hash_algo == NULL) { |
727 | 8 | erralgo = "mac s->c"; |
728 | 8 | goto error; |
729 | 8 | } |
730 | 10.8k | DEBUG2(("hmac s2c is %s", s2c_hash_algo ? s2c_hash_algo->name : "<implicit>")) |
731 | | |
732 | | /* compression_algorithms_client_to_server */ |
733 | 10.8k | c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos_c2s, 0, NULL); |
734 | 10.8k | if (c2s_comp_algo == NULL) { |
735 | 9 | erralgo = "comp c->s"; |
736 | 9 | goto error; |
737 | 9 | } |
738 | 10.8k | DEBUG2(("comp c2s is %s", c2s_comp_algo->name)) |
739 | | |
740 | | /* compression_algorithms_server_to_client */ |
741 | 10.8k | s2c_comp_algo = buf_match_algo(ses.payload, ses.compress_algos_s2c, 0, NULL); |
742 | 10.8k | if (s2c_comp_algo == NULL) { |
743 | 3 | erralgo = "comp s->c"; |
744 | 3 | goto error; |
745 | 3 | } |
746 | 10.8k | DEBUG2(("comp s2c is %s", s2c_comp_algo->name)) |
747 | | |
748 | | /* languages_client_to_server */ |
749 | 10.8k | buf_eatstring(ses.payload); |
750 | | |
751 | | /* languages_server_to_client */ |
752 | 10.8k | buf_eatstring(ses.payload); |
753 | | |
754 | | /* their first_kex_packet_follows */ |
755 | 10.8k | if (buf_getbool(ses.payload)) { |
756 | 349 | TRACE(("them kex firstfollows. allgood %d", allgood)) |
757 | 349 | ses.kexstate.them_firstfollows = 1; |
758 | | /* if the guess wasn't good, we ignore the packet sent */ |
759 | 349 | if (!allgood) { |
760 | 194 | ses.ignorenext = 1; |
761 | 194 | } |
762 | 349 | } |
763 | | |
764 | | /* Handle the asymmetry */ |
765 | 10.8k | if (IS_DROPBEAR_CLIENT) { |
766 | 0 | ses.newkeys->recv.algo_crypt = |
767 | 0 | (struct dropbear_cipher*)s2c_cipher_algo->data; |
768 | 0 | ses.newkeys->trans.algo_crypt = |
769 | 0 | (struct dropbear_cipher*)c2s_cipher_algo->data; |
770 | 0 | ses.newkeys->recv.crypt_mode = |
771 | 0 | (struct dropbear_cipher_mode*)s2c_cipher_algo->mode; |
772 | 0 | ses.newkeys->trans.crypt_mode = |
773 | 0 | (struct dropbear_cipher_mode*)c2s_cipher_algo->mode; |
774 | 0 | ses.newkeys->recv.algo_mac = |
775 | 0 | #if DROPBEAR_AEAD_MODE |
776 | 0 | s2c_hash_algo == NULL ? ses.newkeys->recv.crypt_mode->aead_mac : |
777 | 0 | #endif |
778 | 0 | (struct dropbear_hash*)s2c_hash_algo->data; |
779 | 0 | ses.newkeys->trans.algo_mac = |
780 | 0 | #if DROPBEAR_AEAD_MODE |
781 | 0 | c2s_hash_algo == NULL ? ses.newkeys->trans.crypt_mode->aead_mac : |
782 | 0 | #endif |
783 | 0 | (struct dropbear_hash*)c2s_hash_algo->data; |
784 | 0 | ses.newkeys->recv.algo_comp = s2c_comp_algo->val; |
785 | 0 | ses.newkeys->trans.algo_comp = c2s_comp_algo->val; |
786 | 10.8k | } else { |
787 | | /* SERVER */ |
788 | 10.8k | ses.newkeys->recv.algo_crypt = |
789 | 10.8k | (struct dropbear_cipher*)c2s_cipher_algo->data; |
790 | 10.8k | ses.newkeys->trans.algo_crypt = |
791 | 10.8k | (struct dropbear_cipher*)s2c_cipher_algo->data; |
792 | 10.8k | ses.newkeys->recv.crypt_mode = |
793 | 10.8k | (struct dropbear_cipher_mode*)c2s_cipher_algo->mode; |
794 | 10.8k | ses.newkeys->trans.crypt_mode = |
795 | 10.8k | (struct dropbear_cipher_mode*)s2c_cipher_algo->mode; |
796 | 10.8k | ses.newkeys->recv.algo_mac = |
797 | 10.8k | #if DROPBEAR_AEAD_MODE |
798 | 10.8k | c2s_hash_algo == NULL ? ses.newkeys->recv.crypt_mode->aead_mac : |
799 | 10.8k | #endif |
800 | 10.8k | (struct dropbear_hash*)c2s_hash_algo->data; |
801 | 10.8k | ses.newkeys->trans.algo_mac = |
802 | 10.8k | #if DROPBEAR_AEAD_MODE |
803 | 10.8k | s2c_hash_algo == NULL ? ses.newkeys->trans.crypt_mode->aead_mac : |
804 | 10.8k | #endif |
805 | 10.8k | (struct dropbear_hash*)s2c_hash_algo->data; |
806 | 10.8k | ses.newkeys->recv.algo_comp = c2s_comp_algo->val; |
807 | 10.8k | ses.newkeys->trans.algo_comp = s2c_comp_algo->val; |
808 | 10.8k | } |
809 | | |
810 | 10.8k | #if DROPBEAR_FUZZ |
811 | 10.8k | if (fuzz.fuzzing) { |
812 | 10.5k | fuzz_kex_fakealgos(); |
813 | 10.5k | } |
814 | 10.8k | #endif |
815 | | |
816 | | /* reserved for future extensions */ |
817 | 10.8k | buf_getint(ses.payload); |
818 | | |
819 | 10.8k | if (ses.send_kex_first_guess && allgood) { |
820 | 0 | TRACE(("our_first_follows_matches 1")) |
821 | 0 | ses.kexstate.our_first_follows_matches = 1; |
822 | 0 | } |
823 | 10.8k | return; |
824 | | |
825 | 282 | error: |
826 | 282 | dropbear_exit("No matching algo %s", erralgo); |
827 | 10.8k | } |