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