/src/dropbear/src/common-algo.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Dropbear SSH |
3 | | * |
4 | | * Copyright (c) 2002,2003 Matt Johnston |
5 | | * 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 "algo.h" |
28 | | #include "session.h" |
29 | | #include "dbutil.h" |
30 | | #include "dh_groups.h" |
31 | | #include "ltc_prng.h" |
32 | | #include "ecc.h" |
33 | | #include "gcm.h" |
34 | | #include "chachapoly.h" |
35 | | #include "ssh.h" |
36 | | #include "sntrup761.h" |
37 | | #include "mlkem768.h" |
38 | | |
39 | | /* This file (algo.c) organises the ciphers which can be used, and is used to |
40 | | * decide which ciphers/hashes/compression/signing to use during key exchange*/ |
41 | | |
42 | | static int void_cipher(const unsigned char* in, unsigned char* out, |
43 | 0 | unsigned long len, void* UNUSED(cipher_state)) { |
44 | 0 | if (in != out) { |
45 | 0 | memmove(out, in, len); |
46 | 0 | } |
47 | 0 | return CRYPT_OK; |
48 | 0 | } |
49 | | |
50 | | static int void_start(int UNUSED(cipher), const unsigned char* UNUSED(IV), |
51 | | const unsigned char* UNUSED(key), |
52 | 0 | int UNUSED(keylen), int UNUSED(num_rounds), void* UNUSED(cipher_state)) { |
53 | 0 | return CRYPT_OK; |
54 | 0 | } |
55 | | |
56 | | /* Mappings for ciphers, parameters are |
57 | | {&cipher_desc, keysize, blocksize} */ |
58 | | |
59 | | /* Remember to add new ciphers/hashes to regciphers/reghashes too */ |
60 | | |
61 | | #if DROPBEAR_AES256 |
62 | | static const struct dropbear_cipher dropbear_aes256 = |
63 | | {&aes_desc, 32, 16}; |
64 | | #endif |
65 | | #if DROPBEAR_AES128 |
66 | | static const struct dropbear_cipher dropbear_aes128 = |
67 | | {&aes_desc, 16, 16}; |
68 | | #endif |
69 | | #if DROPBEAR_3DES |
70 | | static const struct dropbear_cipher dropbear_3des = |
71 | | {&des3_desc, 24, 8}; |
72 | | #endif |
73 | | |
74 | | /* used to indicate no encryption, as defined in rfc2410 */ |
75 | | const struct dropbear_cipher dropbear_nocipher = |
76 | | {NULL, 16, 8}; |
77 | | |
78 | | /* A few void* s are required to silence warnings |
79 | | * about the symmetric_CBC vs symmetric_CTR cipher_state pointer */ |
80 | | #if DROPBEAR_ENABLE_CBC_MODE |
81 | | const struct dropbear_cipher_mode dropbear_mode_cbc = |
82 | | {(void*)cbc_start, (void*)cbc_encrypt, (void*)cbc_decrypt, NULL, NULL, NULL}; |
83 | | #endif /* DROPBEAR_ENABLE_CBC_MODE */ |
84 | | |
85 | | const struct dropbear_cipher_mode dropbear_mode_none = |
86 | | {void_start, void_cipher, void_cipher, NULL, NULL, NULL}; |
87 | | |
88 | | #if DROPBEAR_ENABLE_CTR_MODE |
89 | | /* a wrapper to make ctr_start and cbc_start look the same */ |
90 | | static int dropbear_big_endian_ctr_start(int cipher, |
91 | | const unsigned char *IV, |
92 | | const unsigned char *key, int keylen, |
93 | 0 | int num_rounds, symmetric_CTR *ctr) { |
94 | 0 | return ctr_start(cipher, IV, key, keylen, num_rounds, CTR_COUNTER_BIG_ENDIAN, ctr); |
95 | 0 | } |
96 | | const struct dropbear_cipher_mode dropbear_mode_ctr = |
97 | | {(void*)dropbear_big_endian_ctr_start, (void*)ctr_encrypt, (void*)ctr_decrypt, NULL, NULL, NULL}; |
98 | | #endif /* DROPBEAR_ENABLE_CTR_MODE */ |
99 | | |
100 | | /* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc. |
101 | | {&hash_desc, keysize, hashsize} */ |
102 | | |
103 | | #if DROPBEAR_SHA1_HMAC |
104 | | static const struct dropbear_hash dropbear_sha1 = |
105 | | {&sha1_desc, 20, 20}; |
106 | | #endif |
107 | | #if DROPBEAR_SHA1_96_HMAC |
108 | | static const struct dropbear_hash dropbear_sha1_96 = |
109 | | {&sha1_desc, 20, 12}; |
110 | | #endif |
111 | | #if DROPBEAR_SHA2_256_HMAC |
112 | | static const struct dropbear_hash dropbear_sha2_256 = |
113 | | {&sha256_desc, 32, 32}; |
114 | | #endif |
115 | | #if DROPBEAR_SHA2_512_HMAC |
116 | | static const struct dropbear_hash dropbear_sha2_512 = |
117 | | {&sha512_desc, 64, 64}; |
118 | | #endif |
119 | | |
120 | | const struct dropbear_hash dropbear_nohash = |
121 | | {NULL, 16, 0}; /* used initially */ |
122 | | |
123 | | |
124 | | /* The following map ssh names to internal values. |
125 | | * The ordering here is important for the client - the first mode |
126 | | * that is also supported by the server will get used. */ |
127 | | |
128 | | algo_type sshciphers[] = { |
129 | | #if DROPBEAR_CHACHA20POLY1305 |
130 | | {"chacha20-poly1305@openssh.com", 0, &dropbear_chachapoly, 1, &dropbear_mode_chachapoly}, |
131 | | #endif |
132 | | |
133 | | #if DROPBEAR_ENABLE_GCM_MODE |
134 | | #if DROPBEAR_AES256 |
135 | | {"aes256-gcm@openssh.com", 0, &dropbear_aes256, 1, &dropbear_mode_gcm}, |
136 | | #endif |
137 | | #if DROPBEAR_AES128 |
138 | | {"aes128-gcm@openssh.com", 0, &dropbear_aes128, 1, &dropbear_mode_gcm}, |
139 | | #endif |
140 | | #endif /* DROPBEAR_ENABLE_GCM_MODE */ |
141 | | |
142 | | #if DROPBEAR_ENABLE_CTR_MODE |
143 | | #if DROPBEAR_AES256 |
144 | | {"aes256-ctr", 0, &dropbear_aes256, 1, &dropbear_mode_ctr}, |
145 | | #endif |
146 | | #if DROPBEAR_AES128 |
147 | | {"aes128-ctr", 0, &dropbear_aes128, 1, &dropbear_mode_ctr}, |
148 | | #endif |
149 | | #endif /* DROPBEAR_ENABLE_CTR_MODE */ |
150 | | |
151 | | #if DROPBEAR_ENABLE_CBC_MODE |
152 | | #if DROPBEAR_AES256 |
153 | | {"aes256-cbc", 0, &dropbear_aes256, 1, &dropbear_mode_cbc}, |
154 | | #endif |
155 | | #if DROPBEAR_AES128 |
156 | | {"aes128-cbc", 0, &dropbear_aes128, 1, &dropbear_mode_cbc}, |
157 | | #endif |
158 | | #endif /* DROPBEAR_ENABLE_CBC_MODE */ |
159 | | |
160 | | #if DROPBEAR_3DES |
161 | | #if DROPBEAR_ENABLE_CTR_MODE |
162 | | {"3des-ctr", 0, &dropbear_3des, 1, &dropbear_mode_ctr}, |
163 | | #endif |
164 | | #if DROPBEAR_ENABLE_CBC_MODE |
165 | | {"3des-cbc", 0, &dropbear_3des, 1, &dropbear_mode_cbc}, |
166 | | #endif |
167 | | #endif /* DROPBEAR_3DES */ |
168 | | |
169 | | #if DROPBEAR_ENABLE_CBC_MODE |
170 | | #endif /* DROPBEAR_ENABLE_CBC_MODE */ |
171 | | {NULL, 0, NULL, 0, NULL} |
172 | | }; |
173 | | |
174 | | algo_type sshhashes[] = { |
175 | | #if DROPBEAR_SHA1_96_HMAC |
176 | | {"hmac-sha1-96", 0, &dropbear_sha1_96, 1, NULL}, |
177 | | #endif |
178 | | #if DROPBEAR_SHA1_HMAC |
179 | | {"hmac-sha1", 0, &dropbear_sha1, 1, NULL}, |
180 | | #endif |
181 | | #if DROPBEAR_SHA2_256_HMAC |
182 | | {"hmac-sha2-256", 0, &dropbear_sha2_256, 1, NULL}, |
183 | | #endif |
184 | | #if DROPBEAR_SHA2_512_HMAC |
185 | | {"hmac-sha2-512", 0, &dropbear_sha2_512, 1, NULL}, |
186 | | #endif |
187 | | {NULL, 0, NULL, 0, NULL} |
188 | | }; |
189 | | |
190 | | #ifndef DISABLE_ZLIB |
191 | | algo_type ssh_compress[] = { |
192 | | {"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL}, |
193 | | {"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL}, |
194 | | {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, |
195 | | {NULL, 0, NULL, 0, NULL} |
196 | | }; |
197 | | |
198 | | algo_type ssh_delaycompress[] = { |
199 | | {"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL}, |
200 | | {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, |
201 | | {NULL, 0, NULL, 0, NULL} |
202 | | }; |
203 | | #endif |
204 | | |
205 | | algo_type ssh_nocompress[] = { |
206 | | {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, |
207 | | {NULL, 0, NULL, 0, NULL} |
208 | | }; |
209 | | |
210 | | algo_type sigalgs[] = { |
211 | | #if DROPBEAR_ED25519 |
212 | | {"ssh-ed25519", DROPBEAR_SIGNATURE_ED25519, NULL, 1, NULL}, |
213 | | #if DROPBEAR_SK_ED25519 |
214 | | {"sk-ssh-ed25519@openssh.com", DROPBEAR_SIGNATURE_SK_ED25519, NULL, 1, NULL}, |
215 | | #endif |
216 | | #endif |
217 | | #if DROPBEAR_ECDSA |
218 | | #if DROPBEAR_ECC_256 |
219 | | {"ecdsa-sha2-nistp256", DROPBEAR_SIGNATURE_ECDSA_NISTP256, NULL, 1, NULL}, |
220 | | #endif |
221 | | #if DROPBEAR_ECC_384 |
222 | | {"ecdsa-sha2-nistp384", DROPBEAR_SIGNATURE_ECDSA_NISTP384, NULL, 1, NULL}, |
223 | | #endif |
224 | | #if DROPBEAR_ECC_521 |
225 | | {"ecdsa-sha2-nistp521", DROPBEAR_SIGNATURE_ECDSA_NISTP521, NULL, 1, NULL}, |
226 | | #endif |
227 | | #if DROPBEAR_SK_ECDSA |
228 | | {"sk-ecdsa-sha2-nistp256@openssh.com", DROPBEAR_SIGNATURE_SK_ECDSA_NISTP256, NULL, 1, NULL}, |
229 | | #endif |
230 | | #endif |
231 | | #if DROPBEAR_RSA |
232 | | #if DROPBEAR_RSA_SHA256 |
233 | | {"rsa-sha2-256", DROPBEAR_SIGNATURE_RSA_SHA256, NULL, 1, NULL}, |
234 | | #endif |
235 | | #if DROPBEAR_RSA_SHA1 |
236 | | {"ssh-rsa", DROPBEAR_SIGNATURE_RSA_SHA1, NULL, 1, NULL}, |
237 | | #endif |
238 | | #endif |
239 | | #if DROPBEAR_DSS |
240 | | {"ssh-dss", DROPBEAR_SIGNATURE_DSS, NULL, 1, NULL}, |
241 | | #endif |
242 | | {NULL, 0, NULL, 0, NULL} |
243 | | }; |
244 | | |
245 | | #if DROPBEAR_DH_GROUP1 |
246 | | static const struct dropbear_kex kex_dh_group1 = {DROPBEAR_KEX_NORMAL_DH, dh_p_1, DH_P_1_LEN, NULL, &sha1_desc }; |
247 | | #endif |
248 | | #if DROPBEAR_DH_GROUP14_SHA1 |
249 | | static const struct dropbear_kex kex_dh_group14_sha1 = {DROPBEAR_KEX_NORMAL_DH, dh_p_14, DH_P_14_LEN, NULL, &sha1_desc }; |
250 | | #endif |
251 | | #if DROPBEAR_DH_GROUP14_SHA256 |
252 | | static const struct dropbear_kex kex_dh_group14_sha256 = {DROPBEAR_KEX_NORMAL_DH, dh_p_14, DH_P_14_LEN, NULL, &sha256_desc }; |
253 | | #endif |
254 | | #if DROPBEAR_DH_GROUP16 |
255 | | static const struct dropbear_kex kex_dh_group16_sha512 = {DROPBEAR_KEX_NORMAL_DH, dh_p_16, DH_P_16_LEN, NULL, &sha512_desc }; |
256 | | #endif |
257 | | |
258 | | #if DROPBEAR_ECDH |
259 | | #if DROPBEAR_ECC_256 |
260 | | static const struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc }; |
261 | | #endif |
262 | | #if DROPBEAR_ECC_384 |
263 | | static const struct dropbear_kex kex_ecdh_nistp384 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp384, &sha384_desc }; |
264 | | #endif |
265 | | #if DROPBEAR_ECC_521 |
266 | | static const struct dropbear_kex kex_ecdh_nistp521 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp521, &sha512_desc }; |
267 | | #endif |
268 | | #endif /* DROPBEAR_ECDH */ |
269 | | |
270 | | #if DROPBEAR_CURVE25519 |
271 | | static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc }; |
272 | | #endif |
273 | | |
274 | | #if DROPBEAR_MLKEM768 |
275 | | static const struct dropbear_kem_desc mlkem768_desc = { |
276 | | .public_len = crypto_kem_mlkem768_PUBLICKEYBYTES, |
277 | | .secret_len = crypto_kem_mlkem768_SECRETKEYBYTES, |
278 | | .ciphertext_len = crypto_kem_mlkem768_CIPHERTEXTBYTES, |
279 | | .output_len = crypto_kem_mlkem768_BYTES, |
280 | | .kem_gen = crypto_kem_mlkem768_keypair, |
281 | | .kem_enc = crypto_kem_mlkem768_enc, |
282 | | .kem_dec = crypto_kem_mlkem768_dec, |
283 | | }; |
284 | | static const struct dropbear_kex kex_mlkem768 = {DROPBEAR_KEX_PQHYBRID, NULL, 0, &mlkem768_desc, &sha256_desc }; |
285 | | #endif |
286 | | |
287 | | #if DROPBEAR_SNTRUP761 |
288 | | static const struct dropbear_kem_desc sntrup761_desc = { |
289 | | .public_len = crypto_kem_sntrup761_PUBLICKEYBYTES, |
290 | | .secret_len = crypto_kem_sntrup761_SECRETKEYBYTES, |
291 | | .ciphertext_len = crypto_kem_sntrup761_CIPHERTEXTBYTES, |
292 | | .output_len = crypto_kem_sntrup761_BYTES, |
293 | | .kem_gen = crypto_kem_sntrup761_keypair, |
294 | | .kem_enc = crypto_kem_sntrup761_enc, |
295 | | .kem_dec = crypto_kem_sntrup761_dec, |
296 | | }; |
297 | | static const struct dropbear_kex kex_sntrup761 = {DROPBEAR_KEX_PQHYBRID, NULL, 0, &sntrup761_desc, &sha512_desc }; |
298 | | #endif |
299 | | |
300 | | /* For sntrup761 */ |
301 | | volatile int16_t crypto_int16_optblocker = 0; |
302 | | volatile int32_t crypto_int32_optblocker = 0; |
303 | | volatile int64_t crypto_int64_optblocker = 0; |
304 | | |
305 | | |
306 | | /* data == NULL for non-kex algorithm identifiers */ |
307 | | algo_type sshkex[] = { |
308 | | #if DROPBEAR_SNTRUP761 |
309 | | {"sntrup761x25519-sha512", 0, &kex_sntrup761, 1, NULL}, |
310 | | {"sntrup761x25519-sha512@openssh.com", 0, &kex_sntrup761, 1, NULL}, |
311 | | #endif |
312 | | #if DROPBEAR_MLKEM768 |
313 | | {"mlkem768x25519-sha256", 0, &kex_mlkem768, 1, NULL}, |
314 | | #endif |
315 | | #if DROPBEAR_CURVE25519 |
316 | | {"curve25519-sha256", 0, &kex_curve25519, 1, NULL}, |
317 | | {"curve25519-sha256@libssh.org", 0, &kex_curve25519, 1, NULL}, |
318 | | #endif |
319 | | #if DROPBEAR_ECDH |
320 | | #if DROPBEAR_ECC_521 |
321 | | {"ecdh-sha2-nistp521", 0, &kex_ecdh_nistp521, 1, NULL}, |
322 | | #endif |
323 | | #if DROPBEAR_ECC_384 |
324 | | {"ecdh-sha2-nistp384", 0, &kex_ecdh_nistp384, 1, NULL}, |
325 | | #endif |
326 | | #if DROPBEAR_ECC_256 |
327 | | {"ecdh-sha2-nistp256", 0, &kex_ecdh_nistp256, 1, NULL}, |
328 | | #endif |
329 | | #endif |
330 | | #if DROPBEAR_DH_GROUP14_SHA256 |
331 | | {"diffie-hellman-group14-sha256", 0, &kex_dh_group14_sha256, 1, NULL}, |
332 | | #endif |
333 | | #if DROPBEAR_DH_GROUP14_SHA1 |
334 | | {"diffie-hellman-group14-sha1", 0, &kex_dh_group14_sha1, 1, NULL}, |
335 | | #endif |
336 | | #if DROPBEAR_DH_GROUP1 |
337 | | {"diffie-hellman-group1-sha1", 0, &kex_dh_group1, 1, NULL}, |
338 | | #endif |
339 | | #if DROPBEAR_DH_GROUP16 |
340 | | {"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL}, |
341 | | #endif |
342 | | #if DROPBEAR_KEXGUESS2 |
343 | | {KEXGUESS2_ALGO_NAME, 0, NULL, 1, NULL}, |
344 | | #endif |
345 | | #if DROPBEAR_EXT_INFO |
346 | | #if DROPBEAR_CLIENT |
347 | | /* Set unusable by svr_algos_initialise() */ |
348 | | {SSH_EXT_INFO_C, 0, NULL, 1, NULL}, |
349 | | #endif |
350 | | #endif |
351 | | #if DROPBEAR_CLIENT |
352 | | {SSH_STRICT_KEX_C, 0, NULL, 1, NULL}, |
353 | | #endif |
354 | | #if DROPBEAR_SERVER |
355 | | {SSH_STRICT_KEX_S, 0, NULL, 1, NULL}, |
356 | | #endif |
357 | | {NULL, 0, NULL, 0, NULL} |
358 | | }; |
359 | | |
360 | | /* Output a comma separated list of algorithms to a buffer */ |
361 | 0 | void buf_put_algolist_all(buffer * buf, const algo_type localalgos[], int useall) { |
362 | 0 | unsigned int i, len; |
363 | 0 | unsigned int donefirst = 0; |
364 | 0 | unsigned int startpos; |
365 | |
|
366 | 0 | startpos = buf->pos; |
367 | | /* Placeholder for length */ |
368 | 0 | buf_putint(buf, 0); |
369 | 0 | for (i = 0; localalgos[i].name != NULL; i++) { |
370 | 0 | if (localalgos[i].usable || useall) { |
371 | 0 | if (donefirst) { |
372 | 0 | buf_putbyte(buf, ','); |
373 | 0 | } |
374 | 0 | donefirst = 1; |
375 | 0 | len = strlen(localalgos[i].name); |
376 | 0 | buf_putbytes(buf, (const unsigned char *) localalgos[i].name, len); |
377 | 0 | } |
378 | 0 | } |
379 | | /* Fill out the length */ |
380 | 0 | len = buf->pos - startpos - 4; |
381 | 0 | buf_setpos(buf, startpos); |
382 | 0 | buf_putint(buf, len); |
383 | 0 | TRACE(("algolist add %d '%.*s'", len, len, buf_getptr(buf, len))) |
384 | 0 | buf_incrwritepos(buf, len); |
385 | 0 | } |
386 | | |
387 | 0 | void buf_put_algolist(buffer * buf, const algo_type localalgos[]) { |
388 | 0 | buf_put_algolist_all(buf, localalgos, 0); |
389 | 0 | } |
390 | | |
391 | | /* returns a list of pointers into algolist, of null-terminated names. |
392 | | ret_list should be passed in with space for *ret_count elements, |
393 | | on return *ret_count has the number of names filled. |
394 | | algolist is modified. */ |
395 | | static void get_algolist(char* algolist, unsigned int algolist_len, |
396 | 0 | const char* *ret_list, unsigned int *ret_count) { |
397 | 0 | unsigned int max_count = *ret_count; |
398 | 0 | unsigned int i; |
399 | |
|
400 | 0 | if (*ret_count == 0) { |
401 | 0 | return; |
402 | 0 | } |
403 | 0 | if (algolist_len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { |
404 | 0 | *ret_count = 0; |
405 | 0 | } |
406 | | |
407 | | /* ret_list will contain a list of the strings parsed out. |
408 | | We will have at least one string (even if it's just "") */ |
409 | 0 | ret_list[0] = algolist; |
410 | 0 | *ret_count = 1; |
411 | 0 | for (i = 0; i < algolist_len; i++) { |
412 | 0 | if (algolist[i] == '\0') { |
413 | | /* someone is trying something strange */ |
414 | 0 | *ret_count = 0; |
415 | 0 | return; |
416 | 0 | } |
417 | | |
418 | 0 | if (algolist[i] == ',') { |
419 | 0 | if (*ret_count >= max_count) { |
420 | 0 | dropbear_exit("Too many remote algorithms"); |
421 | 0 | *ret_count = 0; |
422 | 0 | return; |
423 | 0 | } |
424 | 0 | algolist[i] = '\0'; |
425 | 0 | ret_list[*ret_count] = &algolist[i+1]; |
426 | 0 | (*ret_count)++; |
427 | 0 | } |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | | /* Return DROPBEAR_SUCCESS if the namelist contains algo, |
432 | | DROPBEAR_FAILURE otherwise. buf position is not incremented. */ |
433 | 0 | int buf_has_algo(buffer *buf, const char *algo) { |
434 | 0 | unsigned char* algolist = NULL; |
435 | 0 | unsigned int orig_pos = buf->pos; |
436 | 0 | unsigned int len, remotecount, i; |
437 | 0 | const char *remotenames[MAX_PROPOSED_ALGO]; |
438 | 0 | int ret = DROPBEAR_FAILURE; |
439 | |
|
440 | 0 | algolist = buf_getstring(buf, &len); |
441 | 0 | remotecount = MAX_PROPOSED_ALGO; |
442 | 0 | get_algolist(algolist, len, remotenames, &remotecount); |
443 | 0 | for (i = 0; i < remotecount; i++) |
444 | 0 | { |
445 | 0 | if (strcmp(remotenames[i], algo) == 0) { |
446 | 0 | ret = DROPBEAR_SUCCESS; |
447 | 0 | break; |
448 | 0 | } |
449 | 0 | } |
450 | 0 | if (algolist) { |
451 | 0 | m_free(algolist); |
452 | 0 | } |
453 | 0 | buf_setpos(buf, orig_pos); |
454 | 0 | return ret; |
455 | 0 | } |
456 | | |
457 | 0 | algo_type * first_usable_algo(algo_type algos[]) { |
458 | 0 | int i; |
459 | 0 | for (i = 0; algos[i].name != NULL; i++) { |
460 | 0 | if (algos[i].usable) { |
461 | 0 | return &algos[i]; |
462 | 0 | } |
463 | 0 | } |
464 | 0 | return NULL; |
465 | 0 | } |
466 | | |
467 | | /* match the first algorithm in the comma-separated list in buf which is |
468 | | * also in localalgos[], or return NULL on failure. |
469 | | * (*goodguess) is set to 1 if the preferred client/server algos match, |
470 | | * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are |
471 | | * guessed correctly */ |
472 | | algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], |
473 | 0 | int kexguess2, int *goodguess) { |
474 | 0 | char * algolist = NULL; |
475 | 0 | const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO]; |
476 | 0 | unsigned int len; |
477 | 0 | unsigned int remotecount, localcount, clicount, servcount, i, j; |
478 | 0 | algo_type * ret = NULL; |
479 | 0 | const char **clinames, **servnames; |
480 | |
|
481 | 0 | if (goodguess) { |
482 | 0 | *goodguess = 0; |
483 | 0 | } |
484 | | |
485 | | /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ |
486 | 0 | algolist = buf_getstring(buf, &len); |
487 | 0 | DEBUG3(("buf_match_algo: %s", algolist)) |
488 | 0 | remotecount = MAX_PROPOSED_ALGO; |
489 | 0 | get_algolist(algolist, len, remotenames, &remotecount); |
490 | |
|
491 | 0 | for (i = 0; localalgos[i].name != NULL; i++) { |
492 | 0 | if (localalgos[i].usable) { |
493 | 0 | localnames[i] = localalgos[i].name; |
494 | 0 | } else { |
495 | 0 | localnames[i] = NULL; |
496 | 0 | } |
497 | 0 | } |
498 | 0 | localcount = i; |
499 | |
|
500 | 0 | if (IS_DROPBEAR_SERVER) { |
501 | 0 | clinames = remotenames; |
502 | 0 | clicount = remotecount; |
503 | 0 | servnames = localnames; |
504 | 0 | servcount = localcount; |
505 | 0 | } else { |
506 | 0 | clinames = localnames; |
507 | 0 | clicount = localcount; |
508 | 0 | servnames = remotenames; |
509 | 0 | servcount = remotecount; |
510 | 0 | } |
511 | | |
512 | | /* iterate and find the first match */ |
513 | 0 | for (i = 0; i < clicount; i++) { |
514 | 0 | for (j = 0; j < servcount; j++) { |
515 | 0 | if (!(servnames[j] && clinames[i])) { |
516 | | /* unusable algos are NULL */ |
517 | 0 | continue; |
518 | 0 | } |
519 | 0 | if (strcmp(servnames[j], clinames[i]) == 0) { |
520 | | /* set if it was a good guess */ |
521 | 0 | if (goodguess != NULL) { |
522 | 0 | if (kexguess2) { |
523 | 0 | if (i == 0) { |
524 | 0 | *goodguess = 1; |
525 | 0 | } |
526 | 0 | } else { |
527 | 0 | if (i == 0 && j == 0) { |
528 | 0 | *goodguess = 1; |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } |
532 | | /* set the algo to return */ |
533 | 0 | if (IS_DROPBEAR_SERVER) { |
534 | 0 | ret = &localalgos[j]; |
535 | 0 | } else { |
536 | 0 | ret = &localalgos[i]; |
537 | 0 | } |
538 | 0 | goto out; |
539 | 0 | } |
540 | 0 | } |
541 | 0 | } |
542 | | |
543 | 0 | out: |
544 | 0 | m_free(algolist); |
545 | 0 | return ret; |
546 | 0 | } |
547 | | |
548 | | #if DROPBEAR_USER_ALGO_LIST |
549 | | |
550 | | char * |
551 | | algolist_string(const algo_type algos[]) |
552 | 0 | { |
553 | 0 | char *ret_list; |
554 | 0 | buffer *b = buf_new(200); |
555 | 0 | buf_put_algolist(b, algos); |
556 | 0 | buf_setpos(b, b->len); |
557 | 0 | buf_putbyte(b, '\0'); |
558 | 0 | buf_setpos(b, 4); |
559 | 0 | ret_list = m_strdup((const char *) buf_getptr(b, b->len - b->pos)); |
560 | 0 | buf_free(b); |
561 | 0 | return ret_list; |
562 | 0 | } |
563 | | |
564 | | static algo_type* |
565 | | check_algo(const char* algo_name, algo_type *algos) |
566 | 0 | { |
567 | 0 | algo_type *a; |
568 | 0 | for (a = algos; a->name != NULL; a++) |
569 | 0 | { |
570 | 0 | if (strcmp(a->name, algo_name) == 0) |
571 | 0 | { |
572 | 0 | return a; |
573 | 0 | } |
574 | 0 | } |
575 | | |
576 | 0 | return NULL; |
577 | 0 | } |
578 | | |
579 | | /* Checks a user provided comma-separated algorithm list for available |
580 | | * options. Any that are not acceptable are removed in-place. Returns the |
581 | | * number of valid algorithms. */ |
582 | | int |
583 | | check_user_algos(const char* user_algo_list, algo_type * algos, |
584 | | const char *algo_desc) |
585 | 0 | { |
586 | 0 | algo_type new_algos[MAX_PROPOSED_ALGO+1]; |
587 | 0 | char *work_list = m_strdup(user_algo_list); |
588 | 0 | char *start = work_list; |
589 | 0 | char *c; |
590 | 0 | int n; |
591 | | /* So we can iterate and look for null terminator */ |
592 | 0 | memset(new_algos, 0x0, sizeof(new_algos)); |
593 | 0 | for (c = work_list, n = 0; ; c++) |
594 | 0 | { |
595 | 0 | char oc = *c; |
596 | 0 | if (n >= MAX_PROPOSED_ALGO) { |
597 | 0 | dropbear_exit("Too many algorithms '%s'", user_algo_list); |
598 | 0 | } |
599 | 0 | if (*c == ',' || *c == '\0') { |
600 | 0 | algo_type *match_algo = NULL; |
601 | 0 | *c = '\0'; |
602 | 0 | match_algo = check_algo(start, algos); |
603 | 0 | if (match_algo) { |
604 | 0 | if (check_algo(start, new_algos)) { |
605 | 0 | TRACE(("Skip repeated algorithm '%s'", start)) |
606 | 0 | } else { |
607 | 0 | new_algos[n] = *match_algo; |
608 | 0 | n++; |
609 | 0 | } |
610 | 0 | } else { |
611 | 0 | dropbear_log(LOG_WARNING, "This Dropbear program does not support '%s' %s algorithm", start, algo_desc); |
612 | 0 | } |
613 | 0 | c++; |
614 | 0 | start = c; |
615 | 0 | } |
616 | 0 | if (oc == '\0') { |
617 | 0 | break; |
618 | 0 | } |
619 | 0 | } |
620 | 0 | m_free(work_list); |
621 | | /* n+1 to include a null terminator */ |
622 | 0 | memcpy(algos, new_algos, sizeof(*new_algos) * (n+1)); |
623 | 0 | return n; |
624 | 0 | } |
625 | | #endif /* DROPBEAR_USER_ALGO_LIST */ |