From 036edd62068e3ad68aa5d15c10bc8b505fbe2396 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 17 May 2012 00:12:42 +0800 Subject: [PATCH 1/2] Add rough support for choosing ciphers/hashes with "-c" or "-m" --- algo.h | 7 ++++ cli-runopts.c | 22 +++++++++++ common-algo.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++- common-kex.c | 41 +++++++++++++++----- common-runopts.c | 40 ++++++++++++++++++++ options.h | 3 ++ runopts.h | 8 ++++ 7 files changed, 208 insertions(+), 11 deletions(-) diff --git a/algo.h b/algo.h index 87e524d..73712c3 100644 --- a/algo.h +++ b/algo.h @@ -88,4 +88,11 @@ algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[], algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[], int *goodguess); +#ifdef ENABLE_USER_ALGO_LIST +int check_user_algos(char* user_algo_list, algo_type * algos, + const char *algo_desc); +char * algolist_string(algo_type algos[]); +#endif + + #endif /* _ALGO_H_ */ diff --git a/cli-runopts.c b/cli-runopts.c index 44d2b24..140b0cf 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -86,6 +86,10 @@ static void printhelp() { #ifdef ENABLE_CLI_PROXYCMD "-J Use program pipe rather than TCP connection\n" #endif +#ifdef ENABLE_USER_ALGO_LIST + "-c Specify preferred ciphers ('-c help' to list options)\n" + "-m Specify preferred MACs for packet verification (or '-m help')\n" +#endif #ifdef DEBUG_TRACE "-v verbose (compiled with DEBUG_TRACE)\n" #endif @@ -148,6 +152,10 @@ void cli_getopts(int argc, char ** argv) { #endif #ifndef DISABLE_ZLIB opts.enable_compress = 1; +#endif +#ifdef ENABLE_USER_ALGO_LIST + opts.cipher_list = NULL; + opts.mac_list = NULL; #endif /* not yet opts.ipv4 = 1; @@ -283,6 +291,14 @@ void cli_getopts(int argc, char ** argv) { cli_opts.agent_fwd = 1; break; #endif +#ifdef ENABLE_USER_ALGO_LIST + case 'c': + next = &opts.cipher_list; + break; + case 'm': + next = &opts.mac_list; + break; +#endif #ifdef DEBUG_TRACE case 'v': debug_trace = 1; @@ -290,8 +306,10 @@ void cli_getopts(int argc, char ** argv) { #endif case 'F': case 'e': +#ifndef ENABLE_USER_ALGO_LIST case 'c': case 'm': +#endif case 'D': #ifndef ENABLE_CLI_REMOTETCPFWD case 'R': @@ -351,6 +369,10 @@ void cli_getopts(int argc, char ** argv) { /* And now a few sanity checks and setup */ +#ifdef ENABLE_USER_ALGO_LIST + parse_ciphers_macs(); +#endif + if (host_arg == NULL) { printhelp(); exit(EXIT_FAILURE); diff --git a/common-algo.c b/common-algo.c index acc3964..5ed809b 100644 --- a/common-algo.c +++ b/common-algo.c @@ -260,8 +260,6 @@ int have_algo(char* algo, size_t algolen, algo_type algos[]) { return DROPBEAR_FAILURE; } - - /* Output a comma separated list of algorithms to a buffer */ void buf_put_algolist(buffer * buf, algo_type localalgos[]) { @@ -282,3 +280,99 @@ void buf_put_algolist(buffer * buf, algo_type localalgos[]) { buf_putstring(buf, algolist->data, algolist->len); buf_free(algolist); } + +#ifdef ENABLE_USER_ALGO_LIST + +char * +algolist_string(algo_type algos[]) +{ + char *ret_list; + buffer *b = buf_new(200); + buf_put_algolist(b, algos); + buf_setpos(b, b->len); + buf_putbyte(b, '\0'); + buf_setpos(b, 4); + ret_list = m_strdup(buf_getptr(b, b->len - b->pos)); + buf_free(b); + return ret_list; +} + +static int +check_algo(const char* algo_name, algo_type *algos) +{ + algo_type *a; + for (a = algos; a->name != NULL; a++) + { + if (strcmp(a->name, algo_name) == 0) + { + a->usable = 2; + return DROPBEAR_SUCCESS; + } + } + + return DROPBEAR_FAILURE; +} + +/* helper for check_user_algos */ +static void +try_add_algo(const char *algo_name, algo_type *algos, + const char *algo_desc, char ** out_list, int *num_ret) +{ + if (check_algo(algo_name, algos) == DROPBEAR_FAILURE) + { + dropbear_log(LOG_WARNING, "This Dropbear program does not support '%s' %s algorithm", algo_name, algo_desc); + return; + } + + if (*num_ret != 0) + { + **out_list = ','; + (*out_list)++; + } + + *out_list += sprintf(*out_list, "%s", algo_name); + (*num_ret)++; +} + +/* Checks a user provided comma-separated algorithm list for available + * options. Any that are not acceptable are removed in-place. Returns the + * number of valid algorithms. */ +int +check_user_algos(char* user_algo_list, algo_type * algos, + const char *algo_desc) +{ + /* this has two passes. first we sweep through the given list of + * algorithms and mark them as usable=2 in the algo_type[] array... */ + int num_ret = 0; + char *work_list = m_strdup(user_algo_list); + char *last_name = work_list; + char *out_list = user_algo_list; + char *c; + for (c = work_list; *c; c++) + { + if (*c == ',') + { + *c = '\0'; + try_add_algo(last_name, algos, algo_desc, &out_list, &num_ret); + last_name = c++; + } + } + try_add_algo(last_name, algos, algo_desc, &out_list, &num_ret); + m_free(work_list); + + /* ...then we mark anything with usable==1 as usable=0, and + * usable==2 as usable=1. */ + algo_type *a; + for (a = algos; a->name != NULL; a++) + { + if (a->usable == 1) + { + a->usable = 0; + } else if (a->usable == 2) + { + a->usable = 1; + } + } + return num_ret; +} +#endif // ENABLE_USER_ALGO_LIST diff --git a/common-kex.c b/common-kex.c index 2b3472b..d4de5cb 100644 --- a/common-kex.c +++ b/common-kex.c @@ -106,17 +106,40 @@ void send_msg_kexinit() { /* server_host_key_algorithms */ buf_put_algolist(ses.writepayload, sshhostkey); - /* encryption_algorithms_client_to_server */ - buf_put_algolist(ses.writepayload, sshciphers); +#ifdef ENABLE_USER_ALGO_LIST + if (opts.cipher_list) + { + /* encryption_algorithms_client_to_server */ + buf_putbytes(ses.writepayload, opts.cipher_list, strlen(opts.cipher_list)); + /* encryption_algorithms_server_to_client */ + buf_putbytes(ses.writepayload, opts.cipher_list, strlen(opts.cipher_list)); + } + else +#endif + { + /* encryption_algorithms_client_to_server */ + buf_put_algolist(ses.writepayload, sshciphers); + /* encryption_algorithms_server_to_client */ + buf_put_algolist(ses.writepayload, sshciphers); + } - /* encryption_algorithms_server_to_client */ - buf_put_algolist(ses.writepayload, sshciphers); +#ifdef ENABLE_USER_ALGO_LIST + if (opts.mac_list) + { + /* mac_algorithms_client_to_server */ + buf_putbytes(ses.writepayload, opts.mac_list, strlen(opts.mac_list)); + /* mac_algorithms_server_to_client */ + buf_putbytes(ses.writepayload, opts.mac_list, strlen(opts.mac_list)); + } + else +#endif + { + /* mac_algorithms_client_to_server */ + buf_put_algolist(ses.writepayload, sshhashes); + /* mac_algorithms_server_to_client */ + buf_put_algolist(ses.writepayload, sshhashes); + } - /* mac_algorithms_client_to_server */ - buf_put_algolist(ses.writepayload, sshhashes); - - /* mac_algorithms_server_to_client */ - buf_put_algolist(ses.writepayload, sshhashes); /* compression_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, ses.compress_algos); diff --git a/common-runopts.c b/common-runopts.c index 2de036e..784055a 100644 --- a/common-runopts.c +++ b/common-runopts.c @@ -28,6 +28,7 @@ #include "buffer.h" #include "dbutil.h" #include "auth.h" +#include "algo.h" runopts opts; /* GLOBAL */ @@ -55,3 +56,42 @@ out: buf_free(buf); return ret; } + +#ifdef ENABLE_USER_ALGO_LIST +void +parse_ciphers_macs() +{ + if (opts.cipher_list) + { + if (strcmp(opts.cipher_list, "help") == 0) + { + char *ciphers = algolist_string(sshciphers); + dropbear_log(LOG_INFO, "Available ciphers:\n%s\n", ciphers); + m_free(ciphers); + dropbear_exit("."); + } + + if (check_user_algos(opts.cipher_list, sshciphers, "cipher") == 0) + { + dropbear_exit("No valid ciphers specified for '-c'"); + } + } + + if (opts.mac_list) + { + if (strcmp(opts.mac_list, "help") == 0) + { + char *macs = algolist_string(sshhashes); + dropbear_log(LOG_INFO, "Available MACs:\n%s\n", macs); + m_free(macs); + dropbear_exit("."); + } + + if (check_user_algos(opts.mac_list, sshhashes, "MAC") == 0) + { + dropbear_exit("No valid MACs specified for '-m'"); + } + } +} +#endif + diff --git a/options.h b/options.h index 14dda0c..c0e25f5 100644 --- a/options.h +++ b/options.h @@ -80,6 +80,9 @@ much traffic. */ * to a remote TCP-forwarded connection */ #define ENABLE_CLI_NETCAT +/* Whether to support "-c" and "-m" flags to choose ciphers/MACs at runtime */ +/* #define ENABLE_USER_ALGO_LIST*/ + /* Encryption - at least one required. * Protocol RFC requires 3DES and recommends AES128 for interoperability. * Including multiple keysize variants the same cipher diff --git a/runopts.h b/runopts.h index 83b5861..6d1086b 100644 --- a/runopts.h +++ b/runopts.h @@ -47,6 +47,10 @@ typedef struct runopts { int enable_compress; #endif +#ifdef ENABLE_USER_ALGO_LIST + char *cipher_list; + char *mac_list; +#endif } runopts; @@ -148,4 +152,8 @@ typedef struct cli_runopts { extern cli_runopts cli_opts; void cli_getopts(int argc, char ** argv); +#ifdef ENABLE_USER_ALGO_LIST +void parse_ciphers_macs(); +#endif + #endif /* _RUNOPTS_H_ */ From db34044c7fdfcdf85a2606b686d60910e1ff36b6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 17 May 2012 00:26:12 +0800 Subject: [PATCH 2/2] ENABLE_USER_ALGO_LIST should work for the client --- algo.h | 2 +- common-algo.c | 46 +++++++++++++++------------------------------- common-kex.c | 42 ++++++++++-------------------------------- options.h | 2 +- 4 files changed, 27 insertions(+), 65 deletions(-) diff --git a/algo.h b/algo.h index 73712c3..ad57037 100644 --- a/algo.h +++ b/algo.h @@ -89,7 +89,7 @@ algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[], int *goodguess); #ifdef ENABLE_USER_ALGO_LIST -int check_user_algos(char* user_algo_list, algo_type * algos, +int check_user_algos(const char* user_algo_list, algo_type * algos, const char *algo_desc); char * algolist_string(algo_type algos[]); #endif diff --git a/common-algo.c b/common-algo.c index 5ed809b..65fb7b1 100644 --- a/common-algo.c +++ b/common-algo.c @@ -297,7 +297,7 @@ algolist_string(algo_type algos[]) return ret_list; } -static int +static algo_type* check_algo(const char* algo_name, algo_type *algos) { algo_type *a; @@ -305,32 +305,25 @@ check_algo(const char* algo_name, algo_type *algos) { if (strcmp(a->name, algo_name) == 0) { - a->usable = 2; - return DROPBEAR_SUCCESS; + return a; } } - return DROPBEAR_FAILURE; + return NULL; } -/* helper for check_user_algos */ static void try_add_algo(const char *algo_name, algo_type *algos, - const char *algo_desc, char ** out_list, int *num_ret) + const char *algo_desc, algo_type * new_algos, int *num_ret) { - if (check_algo(algo_name, algos) == DROPBEAR_FAILURE) + algo_type *match_algo = check_algo(algo_name, algos); + if (!match_algo) { dropbear_log(LOG_WARNING, "This Dropbear program does not support '%s' %s algorithm", algo_name, algo_desc); return; } - if (*num_ret != 0) - { - **out_list = ','; - (*out_list)++; - } - - *out_list += sprintf(*out_list, "%s", algo_name); + new_algos[*num_ret] = *match_algo; (*num_ret)++; } @@ -338,41 +331,32 @@ try_add_algo(const char *algo_name, algo_type *algos, * options. Any that are not acceptable are removed in-place. Returns the * number of valid algorithms. */ int -check_user_algos(char* user_algo_list, algo_type * algos, +check_user_algos(const char* user_algo_list, algo_type * algos, const char *algo_desc) { + algo_type new_algos[MAX_PROPOSED_ALGO]; /* this has two passes. first we sweep through the given list of * algorithms and mark them as usable=2 in the algo_type[] array... */ int num_ret = 0; char *work_list = m_strdup(user_algo_list); char *last_name = work_list; - char *out_list = user_algo_list; char *c; for (c = work_list; *c; c++) { if (*c == ',') { *c = '\0'; - try_add_algo(last_name, algos, algo_desc, &out_list, &num_ret); + try_add_algo(last_name, algos, algo_desc, new_algos, &num_ret); last_name = c++; } } - try_add_algo(last_name, algos, algo_desc, &out_list, &num_ret); + try_add_algo(last_name, algos, algo_desc, new_algos, &num_ret); m_free(work_list); - /* ...then we mark anything with usable==1 as usable=0, and - * usable==2 as usable=1. */ - algo_type *a; - for (a = algos; a->name != NULL; a++) - { - if (a->usable == 1) - { - a->usable = 0; - } else if (a->usable == 2) - { - a->usable = 1; - } - } + new_algos[num_ret].name = NULL; + + /* Copy one more as a blank delimiter */ + memcpy(algos, new_algos, sizeof(*new_algos) * (num_ret+1)); return num_ret; } #endif // ENABLE_USER_ALGO_LIST diff --git a/common-kex.c b/common-kex.c index d4de5cb..4bd9b78 100644 --- a/common-kex.c +++ b/common-kex.c @@ -106,39 +106,17 @@ void send_msg_kexinit() { /* server_host_key_algorithms */ buf_put_algolist(ses.writepayload, sshhostkey); -#ifdef ENABLE_USER_ALGO_LIST - if (opts.cipher_list) - { - /* encryption_algorithms_client_to_server */ - buf_putbytes(ses.writepayload, opts.cipher_list, strlen(opts.cipher_list)); - /* encryption_algorithms_server_to_client */ - buf_putbytes(ses.writepayload, opts.cipher_list, strlen(opts.cipher_list)); - } - else -#endif - { - /* encryption_algorithms_client_to_server */ - buf_put_algolist(ses.writepayload, sshciphers); - /* encryption_algorithms_server_to_client */ - buf_put_algolist(ses.writepayload, sshciphers); - } + /* encryption_algorithms_client_to_server */ + buf_put_algolist(ses.writepayload, sshciphers); -#ifdef ENABLE_USER_ALGO_LIST - if (opts.mac_list) - { - /* mac_algorithms_client_to_server */ - buf_putbytes(ses.writepayload, opts.mac_list, strlen(opts.mac_list)); - /* mac_algorithms_server_to_client */ - buf_putbytes(ses.writepayload, opts.mac_list, strlen(opts.mac_list)); - } - else -#endif - { - /* mac_algorithms_client_to_server */ - buf_put_algolist(ses.writepayload, sshhashes); - /* mac_algorithms_server_to_client */ - buf_put_algolist(ses.writepayload, sshhashes); - } + /* encryption_algorithms_server_to_client */ + buf_put_algolist(ses.writepayload, sshciphers); + + /* mac_algorithms_client_to_server */ + buf_put_algolist(ses.writepayload, sshhashes); + + /* mac_algorithms_server_to_client */ + buf_put_algolist(ses.writepayload, sshhashes); /* compression_algorithms_client_to_server */ diff --git a/options.h b/options.h index c0e25f5..9fdec4b 100644 --- a/options.h +++ b/options.h @@ -81,7 +81,7 @@ much traffic. */ #define ENABLE_CLI_NETCAT /* Whether to support "-c" and "-m" flags to choose ciphers/MACs at runtime */ -/* #define ENABLE_USER_ALGO_LIST*/ +#define ENABLE_USER_ALGO_LIST /* Encryption - at least one required. * Protocol RFC requires 3DES and recommends AES128 for interoperability.