libssh/plus_sign.patch

1654 lines
62 KiB
Diff
Raw Normal View History

From d1315bf155f5541e769bac58bdbb1cf343a70952 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Mon, 7 Nov 2022 13:08:02 +0100
Subject: [PATCH 1/6] tokens: Add low-level function to exlclude, prepend lists
These functions are needed for openssh -,^ features.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/token.h | 8 +++
src/token.c | 141 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 145 insertions(+), 4 deletions(-)
diff --git a/include/libssh/token.h b/include/libssh/token.h
index 9896fb06..2d07f8c4 100644
--- a/include/libssh/token.h
+++ b/include/libssh/token.h
@@ -45,4 +45,12 @@ char *ssh_remove_duplicates(const char *list);
char *ssh_append_without_duplicates(const char *list,
const char *appended_list);
+char *ssh_prefix_without_duplicates(const char *list,
+ const char *prefixed_list);
+char *ssh_remove_all_matching(const char *list,
+ const char *remove_list);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* TOKEN_H_ */
diff --git a/src/token.c b/src/token.c
index 0924d3bd..58befe1d 100644
--- a/src/token.c
+++ b/src/token.c
@@ -376,6 +376,7 @@ char *ssh_append_without_duplicates(const char *list,
{
size_t concat_len = 0;
char *ret = NULL, *concat = NULL;
+ int rc = 0;
if (list != NULL) {
concat_len = strlen(list);
@@ -396,12 +397,144 @@ char *ssh_append_without_duplicates(const char *list,
return NULL;
}
+ rc = snprintf(concat, concat_len, "%s%s%s",
+ list == NULL ? "" : list,
+ list == NULL ? "" : ",",
+ appended_list == NULL ? "" : appended_list);
+ if (rc < 0) {
+ SAFE_FREE(concat);
+ return NULL;
+ }
+
+ ret = ssh_remove_duplicates(concat);
+
+ SAFE_FREE(concat);
+
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Given two strings containing lists of tokens, return a newly
+ * allocated string containing the elements of the first list without the
+ * elements of the second list. The order of the elements will be preserved.
+ *
+ * @param[in] list The first list
+ * @param[in] remove_list The list to be removed
+ *
+ * @return A newly allocated copy list containing elements of the
+ * list without the elements of remove_list; NULL in case of error.
+ */
+char *ssh_remove_all_matching(const char *list,
+ const char *remove_list)
+{
+ struct ssh_tokens_st *l_tok = NULL, *r_tok = NULL;
+ int i, j, cmp;
+ char *ret = NULL;
+ size_t len, pos = 0;
+ bool exclude;
+
+ if ((list == NULL)) {
+ return NULL;
+ }
+ if (remove_list == NULL) {
+ return strdup (list);
+ }
+
+ l_tok = ssh_tokenize(list, ',');
+ if (l_tok == NULL) {
+ goto out;
+ }
+
+ r_tok = ssh_tokenize(remove_list, ',');
+ if (r_tok == NULL) {
+ goto out;
+ }
+
+ ret = calloc(1, strlen(list) + 1);
+ if (ret == NULL) {
+ goto out;
+ }
+
+ for (i = 0; l_tok->tokens[i]; i++) {
+ exclude = false;
+ for (j = 0; r_tok->tokens[j]; j++) {
+ cmp = strcmp(l_tok->tokens[i], r_tok->tokens[j]);
+ if (cmp == 0) {
+ exclude = true;
+ break;
+ }
+ }
+ if (exclude == false) {
+ if (pos != 0) {
+ ret[pos] = ',';
+ pos++;
+ }
+
+ len = strlen(l_tok->tokens[i]);
+ memcpy(&ret[pos], l_tok->tokens[i], len);
+ pos += len;
+ }
+ }
+
+ if (ret[0] == '\0') {
+ SAFE_FREE(ret);
+ }
+
+out:
+ ssh_tokens_free(l_tok);
+ ssh_tokens_free(r_tok);
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Given two strings containing lists of tokens, return a newly
+ * allocated string containing all the elements of the first list prefixed at
+ * the beginning of the second list, without duplicates.
+ *
+ * @param[in] list The first list
+ * @param[in] prefixed_list The list to use as a prefix
+ *
+ * @return A newly allocated list containing all the elements
+ * of the list prefixed with the elements of the prefixed_list without
+ * duplicates; NULL in case of error.
+ */
+char *ssh_prefix_without_duplicates(const char *list,
+ const char *prefixed_list)
+{
+ size_t concat_len = 0;
+ char *ret = NULL, *concat = NULL;
+ int rc = 0;
+
if (list != NULL) {
- strcpy(concat, list);
- strncat(concat, ",", concat_len - strlen(concat) - 1);
+ concat_len = strlen(list);
}
- if (appended_list != NULL) {
- strncat(concat, appended_list, concat_len - strlen(concat) - 1);
+
+ if (prefixed_list != NULL) {
+ concat_len += strlen(prefixed_list);
+ }
+
+ if (concat_len == 0) {
+ return NULL;
+ }
+
+ /* Add room for ending '\0' and for middle ',' */
+ concat_len += 2;
+ concat = calloc(concat_len, 1);
+ if (concat == NULL) {
+ return NULL;
+ }
+
+ rc = snprintf(concat, concat_len, "%s%s%s",
+ prefixed_list == NULL ? "" : prefixed_list,
+ prefixed_list == NULL ? "" : ",",
+ list == NULL ? "" : list);
+ if (rc < 0) {
+ SAFE_FREE(concat);
+ return NULL;
}
ret = ssh_remove_duplicates(concat);
--
2.38.1
From f4516b9d43c4730ca5f60d73567596d65a672e16 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Fri, 11 Nov 2022 17:47:22 +0100
Subject: [PATCH 2/6] torture_tokens.c: Add tests for new token functions
Functions `ssh_remove_all_matching` and `ssh_prefix_without_duplicates` were
added; a little test suite will suite them.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/unittests/torture_tokens.c | 64 ++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/tests/unittests/torture_tokens.c b/tests/unittests/torture_tokens.c
index 6b52b847..438538de 100644
--- a/tests/unittests/torture_tokens.c
+++ b/tests/unittests/torture_tokens.c
@@ -265,6 +265,68 @@ static void torture_append_without_duplicate(UNUSED_PARAM(void **state))
}
}
+static void torture_remove_all_matching (UNUSED_PARAM(void** state)) {
+ char *p;
+
+ p = ssh_remove_all_matching(NULL, NULL);
+ assert_null(p);
+
+ p = ssh_remove_all_matching("don't remove", NULL);
+ assert_non_null(p);
+ assert_string_equal(p, "don't remove");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "b");
+ assert_non_null(p);
+ assert_string_equal(p, "a,c");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "a,b");
+ assert_non_null(p);
+ assert_string_equal(p, "c");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "d");
+ assert_non_null(p);
+ assert_string_equal(p, "a,b,c");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "a,b,c");
+ assert_null(p);
+}
+
+static void torture_prefix_without_duplicates (UNUSED_PARAM(void** state)) {
+ char *p;
+
+ p = ssh_prefix_without_duplicates(NULL, NULL);
+ assert_null(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", NULL);
+ assert_non_null(p);
+ assert_string_equal(p, "a,b,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "a");
+ assert_non_null(p);
+ assert_string_equal(p, "a,b,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "b");
+ assert_non_null(p);
+ assert_string_equal(p, "b,a,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "x");
+ assert_non_null(p);
+ assert_string_equal(p, "x,a,b,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "c,x");
+ assert_non_null(p);
+ assert_string_equal(p, "c,x,a,b");
+ free(p);
+}
+
int torture_run_tests(void)
{
@@ -275,6 +337,8 @@ int torture_run_tests(void)
cmocka_unit_test(torture_find_all_matching),
cmocka_unit_test(torture_remove_duplicate),
cmocka_unit_test(torture_append_without_duplicate),
+ cmocka_unit_test(torture_remove_all_matching),
+ cmocka_unit_test(torture_prefix_without_duplicates),
};
ssh_init();
--
2.38.1
From be50b4296574ba59537415b9903e8e4aa94cce53 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Mon, 7 Nov 2022 08:23:30 +0100
Subject: [PATCH 3/6] kex: Add functions for openssh +,-,^ features
The funcions can:
- add a list to the default list
- remove a list from the default list
- prepend a list to the default list
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/kex.h | 4 ++
src/kex.c | 105 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 109 insertions(+)
diff --git a/include/libssh/kex.h b/include/libssh/kex.h
index 3a1f4a6f..23b93924 100644
--- a/include/libssh/kex.h
+++ b/include/libssh/kex.h
@@ -40,6 +40,10 @@ int ssh_kex_select_methods(ssh_session session);
int ssh_verify_existing_algo(enum ssh_kex_types_e algo, const char *name);
char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list);
char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list);
+char *ssh_add_to_default_algos(enum ssh_kex_types_e algo, const char *list);
+char *ssh_remove_from_default_algos(enum ssh_kex_types_e algo,
+ const char *list);
+char *ssh_prefix_default_algos(enum ssh_kex_types_e algo, const char *list);
char **ssh_space_tokenize(const char *chain);
int ssh_get_kex1(ssh_session session);
char *ssh_find_matching(const char *in_d, const char *what_d);
diff --git a/src/kex.c b/src/kex.c
index 64083997..1155b9c7 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -983,6 +983,111 @@ char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list)
return ssh_find_all_matching(fips_methods[algo], list);
}
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms plus the algorithms specified in list. If the system
+ * runs in fips mode, this will add only fips approved algorithms.
+ * Empty list will cause error.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be appended
+ *
+ * @return A newly allocated list containing the default algorithms and the
+ * algorithms in list at the end; NULL in case of error.
+ */
+char *ssh_add_to_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ char *tmp = NULL, *ret = NULL;
+
+ if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') {
+ return NULL;
+ }
+
+ if (ssh_fips_mode()) {
+ tmp = ssh_append_without_duplicates(fips_methods[algo], list);
+ ret = ssh_find_all_matching(fips_methods[algo], tmp);
+ } else {
+ tmp = ssh_append_without_duplicates(default_methods[algo], list);
+ ret = ssh_find_all_matching(supported_methods[algo], tmp);
+ }
+
+ free(tmp);
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms excluding the algorithms specified in list. If the system
+ * runs in fips mode, this will remove from the fips_methods list.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be exclude
+ *
+ * @return A newly allocated list containing the default algorithms without the
+ * algorithms in list; NULL in case of error.
+ */
+char *ssh_remove_from_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ if (algo > SSH_LANG_S_C) {
+ return NULL;
+ }
+
+ if (list == NULL || list[0] == '\0') {
+ if (ssh_fips_mode()) {
+ return strdup(fips_methods[algo]);
+ } else {
+ return strdup(default_methods[algo]);
+ }
+ }
+
+ if (ssh_fips_mode()) {
+ return ssh_remove_all_matching(fips_methods[algo], list);
+ } else {
+ return ssh_remove_all_matching(default_methods[algo], list);
+ }
+}
+
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms with prioritized algorithms specified in list. If the
+ * algorithms are present in the default list they get prioritized, if not
+ * they are added to the front of the default list. If the system
+ * runs in fips mode, this will work with the fips_methods list.
+ * Empty list will cause error.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be pushed to priority
+ *
+ * @return A newly allocated list containing the default algorithms prioritized
+ * with the algorithms in list at the beginning of the list; NULL in case
+ * of error.
+ */
+char *ssh_prefix_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ char *ret = NULL, *tmp = NULL;
+
+ if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') {
+ return NULL;
+ }
+
+ if (ssh_fips_mode()) {
+ tmp = ssh_prefix_without_duplicates(fips_methods[algo], list);
+ ret = ssh_find_all_matching(fips_methods[algo], tmp);
+ } else {
+ tmp = ssh_prefix_without_duplicates(default_methods[algo], list);
+ ret = ssh_find_all_matching(supported_methods[algo], tmp);
+ }
+
+ free(tmp);
+ return ret;
+}
+
int ssh_make_sessionid(ssh_session session)
{
ssh_string num = NULL;
--
2.38.1
From 0d5d6e750a0c25700a47a760cb066b6027a54b09 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Mon, 7 Nov 2022 13:13:20 +0100
Subject: [PATCH 4/6] options.c: Add support for openssh config +,-,^
These features allow for options Ciphers, HostKeyAlgorithms, KexAlgorithms and
MACs to append, remove and prepend to the default list of algorithms
respectively
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/options.h | 3 +-
src/options.c | 244 +++++++++++++++++++++++++--------------
src/server.c | 3 +-
3 files changed, 161 insertions(+), 89 deletions(-)
diff --git a/include/libssh/options.h b/include/libssh/options.h
index e8dc6c69..dd04c8af 100644
--- a/include/libssh/options.h
+++ b/include/libssh/options.h
@@ -25,7 +25,8 @@ int ssh_config_parse_file(ssh_session session, const char *filename);
int ssh_config_parse_string(ssh_session session, const char *input);
int ssh_options_set_algo(ssh_session session,
enum ssh_kex_types_e algo,
- const char *list);
+ const char *list,
+ char **place);
int ssh_options_apply(ssh_session session);
#endif /* _OPTIONS_H */
diff --git a/src/options.c b/src/options.c
index 49aaefa2..56e09c65 100644
--- a/src/options.c
+++ b/src/options.c
@@ -221,14 +221,30 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
int ssh_options_set_algo(ssh_session session,
enum ssh_kex_types_e algo,
- const char *list)
+ const char *list,
+ char **place)
{
- char *p = NULL;
+ /* When the list start with +,-,^ the filtration of unknown algorithms
+ * gets handled inside the helper functions, otherwise the list is taken
+ * as it is. */
+ char *p = (char *)list;
+
+ if (algo < SSH_COMP_C_S) {
+ if (list[0] == '+') {
+ p = ssh_add_to_default_algos(algo, list+1);
+ } else if (list[0] == '-') {
+ p = ssh_remove_from_default_algos(algo, list+1);
+ } else if (list[0] == '^') {
+ p = ssh_prefix_default_algos(algo, list+1);
+ }
+ }
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(algo, list);
- } else {
- p = ssh_keep_known_algos(algo, list);
+ if (p == list) {
+ if (ssh_fips_mode()) {
+ p = ssh_keep_fips_algos(algo, list);
+ } else {
+ p = ssh_keep_known_algos(algo, list);
+ }
}
if (p == NULL) {
@@ -238,8 +254,8 @@ int ssh_options_set_algo(ssh_session session,
return -1;
}
- SAFE_FREE(session->opts.wanted_methods[algo]);
- session->opts.wanted_methods[algo] = p;
+ SAFE_FREE(*place);
+ *place = p;
return 0;
}
@@ -356,34 +372,60 @@ int ssh_options_set_algo(ssh_session session,
*
* - SSH_OPTIONS_CIPHERS_C_S:
* Set the symmetric cipher client to server (const char *,
- * comma-separated list).
+ * comma-separated list). The list can be prepended by +,-,^
+ * which can append, remove or move to the beginning
+ * (prioritizing) of the default list respectively. Giving an
+ * empty list after + and ^ will cause error.
*
* - SSH_OPTIONS_CIPHERS_S_C:
* Set the symmetric cipher server to client (const char *,
- * comma-separated list).
+ * comma-separated list). The list can be prepended by +,-,^
+ * which can append, remove or move to the beginning
+ * (prioritizing) of the default list respectively. Giving an
+ * empty list after + and ^ will cause error.
*
* - SSH_OPTIONS_KEY_EXCHANGE:
* Set the key exchange method to be used (const char *,
* comma-separated list). ex:
* "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
+ * The list can be prepended by +,-,^ which will append,
+ * remove or move to the beginning (prioritizing) of the
+ * default list respectively. Giving an empty list
+ * after + and ^ will cause error.
*
* - SSH_OPTIONS_HMAC_C_S:
* Set the Message Authentication Code algorithm client to server
- * (const char *, comma-separated list).
+ * (const char *, comma-separated list). The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_HMAC_S_C:
* Set the Message Authentication Code algorithm server to client
- * (const char *, comma-separated list).
+ * (const char *, comma-separated list). The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_HOSTKEYS:
* Set the preferred server host key types (const char *,
* comma-separated list). ex:
- * "ssh-rsa,ssh-dss,ecdh-sha2-nistp256"
+ * "ssh-rsa,ssh-dss,ecdh-sha2-nistp256". The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
* Set the preferred public key algorithms to be used for
* authentication (const char *, comma-separated list). ex:
* "ssh-rsa,rsa-sha2-256,ssh-dss,ecdh-sha2-nistp256"
+ * The list can be prepended by +,-,^ which will append,
+ * remove or move to the beginning (prioritizing) of the
+ * default list respectively. Giving an empty list
+ * after + and ^ will cause error.
*
* - SSH_OPTIONS_COMPRESSION_C_S:
* Set the compression to use for client to server
@@ -496,6 +538,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
long int i;
unsigned int u;
int rc;
+ char **wanted_methods = session->opts.wanted_methods;
if (session == NULL) {
return -1;
@@ -779,7 +822,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_CRYPT_C_S, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_CRYPT_C_S,
+ v,
+ &wanted_methods[SSH_CRYPT_C_S]);
+ if (rc < 0)
return -1;
}
break;
@@ -789,7 +836,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_CRYPT_S_C, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_CRYPT_S_C,
+ v,
+ &wanted_methods[SSH_CRYPT_S_C]);
+ if (rc < 0)
return -1;
}
break;
@@ -799,7 +850,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_KEX, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_KEX,
+ v,
+ &wanted_methods[SSH_KEX]);
+ if (rc < 0)
return -1;
}
break;
@@ -809,7 +864,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_HOSTKEYS, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_HOSTKEYS,
+ v,
+ &wanted_methods[SSH_HOSTKEYS]);
+ if (rc < 0)
return -1;
}
break;
@@ -819,20 +878,12 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
- } else {
- p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
- }
- if (p == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Setting method: no known public key algorithm (%s)",
- v);
+ rc = ssh_options_set_algo(session,
+ SSH_HOSTKEYS,
+ v,
+ &session->opts.pubkey_accepted_types);
+ if (rc < 0)
return -1;
- }
-
- SAFE_FREE(session->opts.pubkey_accepted_types);
- session->opts.pubkey_accepted_types = p;
}
break;
case SSH_OPTIONS_HMAC_C_S:
@@ -841,7 +892,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_MAC_C_S, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_MAC_C_S,
+ v,
+ &wanted_methods[SSH_MAC_C_S]);
+ if (rc < 0)
return -1;
}
break;
@@ -851,7 +906,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_MAC_S_C, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_MAC_S_C,
+ v,
+ &wanted_methods[SSH_MAC_S_C]);
+ if (rc < 0)
return -1;
}
break;
@@ -861,16 +920,18 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (strcasecmp(value,"yes")==0){
- if(ssh_options_set_algo(session,SSH_COMP_C_S,"zlib@openssh.com,zlib,none") < 0)
- return -1;
- } else if (strcasecmp(value,"no")==0){
- if(ssh_options_set_algo(session,SSH_COMP_C_S,"none,zlib@openssh.com,zlib") < 0)
- return -1;
- } else {
- if (ssh_options_set_algo(session, SSH_COMP_C_S, v) < 0)
- return -1;
+ const char *tmp = v;
+ if (strcasecmp(value, "yes") == 0){
+ tmp = "zlib@openssh.com,zlib,none";
+ } else if (strcasecmp(value, "no") == 0){
+ tmp = "none,zlib@openssh.com,zlib";
}
+ rc = ssh_options_set_algo(session,
+ SSH_COMP_C_S,
+ tmp,
+ &wanted_methods[SSH_COMP_C_S]);
+ if (rc < 0)
+ return -1;
}
break;
case SSH_OPTIONS_COMPRESSION_S_C:
@@ -879,16 +940,19 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (strcasecmp(value,"yes")==0){
- if(ssh_options_set_algo(session,SSH_COMP_S_C,"zlib@openssh.com,zlib,none") < 0)
- return -1;
- } else if (strcasecmp(value,"no")==0){
- if(ssh_options_set_algo(session,SSH_COMP_S_C,"none,zlib@openssh.com,zlib") < 0)
- return -1;
- } else {
- if (ssh_options_set_algo(session, SSH_COMP_S_C, v) < 0)
- return -1;
+ const char *tmp = v;
+ if (strcasecmp(value, "yes") == 0){
+ tmp = "zlib@openssh.com,zlib,none";
+ } else if (strcasecmp(value, "no") == 0){
+ tmp = "none,zlib@openssh.com,zlib";
}
+
+ rc = ssh_options_set_algo(session,
+ SSH_COMP_S_C,
+ tmp,
+ &wanted_methods[SSH_COMP_S_C]);
+ if (rc < 0)
+ return -1;
}
break;
case SSH_OPTIONS_COMPRESSION:
@@ -1604,26 +1668,12 @@ ssh_bind_set_key(ssh_bind sshbind, char **key_loc, const void *value)
static int ssh_bind_set_algo(ssh_bind sshbind,
enum ssh_kex_types_e algo,
- const char *list)
+ const char *list,
+ char **place)
{
- char *p = NULL;
-
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(algo, list);
- } else {
- p = ssh_keep_known_algos(algo, list);
- }
- if (p == NULL) {
- ssh_set_error(sshbind, SSH_REQUEST_DENIED,
- "Setting method: no algorithm for method \"%s\" (%s)",
- ssh_kex_get_description(algo), list);
- return -1;
- }
-
- SAFE_FREE(sshbind->wanted_methods[algo]);
- sshbind->wanted_methods[algo] = p;
-
- return 0;
+ /* sshbind is needed only for ssh_set_error which takes void*
+ * the typecast is only to satisfy function parameter type */
+ return ssh_options_set_algo((ssh_session)sshbind, algo, list, place);
}
/**
@@ -1765,6 +1815,7 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
char *p, *q;
const char *v;
int i, rc;
+ char **wanted_methods = sshbind->wanted_methods;
if (sshbind == NULL) {
return -1;
@@ -2014,8 +2065,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_CRYPT_C_S, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_CRYPT_C_S,
+ v,
+ &wanted_methods[SSH_CRYPT_C_S]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_CIPHERS_S_C:
@@ -2024,8 +2080,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_CRYPT_S_C, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_CRYPT_S_C,
+ v,
+ &wanted_methods[SSH_CRYPT_S_C]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_KEY_EXCHANGE:
@@ -2034,7 +2095,10 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- rc = ssh_bind_set_algo(sshbind, SSH_KEX, v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_KEX,
+ v,
+ &wanted_methods[SSH_KEX]);
if (rc < 0) {
return -1;
}
@@ -2046,8 +2110,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_MAC_C_S, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_MAC_C_S,
+ v,
+ &wanted_methods[SSH_MAC_C_S]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_HMAC_S_C:
@@ -2056,8 +2125,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_MAC_S_C, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_MAC_S_C,
+ v,
+ &wanted_methods[SSH_MAC_S_C]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_CONFIG_DIR:
@@ -2082,20 +2156,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
- } else {
- p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
- }
- if (p == NULL) {
- ssh_set_error(sshbind, SSH_REQUEST_DENIED,
- "Setting method: no known public key algorithm (%s)",
- v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_HOSTKEYS,
+ v,
+ &sshbind->pubkey_accepted_key_types);
+ if (rc < 0) {
return -1;
}
-
- SAFE_FREE(sshbind->pubkey_accepted_key_types);
- sshbind->pubkey_accepted_key_types = p;
}
break;
case SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS:
@@ -2104,7 +2171,10 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- rc = ssh_bind_set_algo(sshbind, SSH_HOSTKEYS, v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_HOSTKEYS,
+ v,
+ &wanted_methods[SSH_HOSTKEYS]);
if (rc < 0) {
return -1;
}
diff --git a/src/server.c b/src/server.c
index 3fc25bd9..1b423fd0 100644
--- a/src/server.c
+++ b/src/server.c
@@ -160,7 +160,8 @@ int server_set_kex(ssh_session session)
rc = ssh_options_set_algo(session,
SSH_HOSTKEYS,
- kept);
+ kept,
+ &session->opts.wanted_methods[SSH_HOSTKEYS]);
SAFE_FREE(kept);
if (rc < 0) {
return -1;
--
2.38.1
From b6cc8f643624231a583bd7972e9503b3fa434caa Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Mon, 7 Nov 2022 08:28:31 +0100
Subject: [PATCH 5/6] torture_options.c: Add test for config +,-,^ feature
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/unittests/torture_options.c | 223 ++++++++++++++++++++++++++++++
1 file changed, 223 insertions(+)
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index e1d16f02..dc4df383 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -1087,6 +1087,223 @@ static void torture_options_getopt(void **state)
#endif /* _NSC_VER */
}
+static void torture_options_plus_sign(void **state)
+{
+ ssh_session session = *state;
+ int rc;
+ const char *def_host_alg, *alg, *algs;
+ char *awaited;
+ size_t alg_len, algs_len;
+
+ if (ssh_fips_mode()) {
+ alg = ",rsa-sha2-512-cert-v01@openssh.com";
+ algs = ",rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521";
+ def_host_alg = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ } else {
+ alg = ",ssh-rsa";
+ algs = ",ssh-rsa,ssh-rsa-cert-v01@openssh.com";
+ def_host_alg = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ }
+ alg_len = strlen(alg);
+ algs_len = strlen(algs);
+
+ /* in fips mode, the default list is the available list, which means
+ * we can't append anything because everything enabled is already
+ * included */
+ if (ssh_fips_mode()) {
+ awaited = strdup(def_host_alg);
+ assert_non_null(awaited);
+ } else {
+ awaited = calloc(strlen(def_host_alg) + alg_len + 1, 1);
+ assert_non_null(awaited);
+
+ memcpy(awaited, def_host_alg, strlen(def_host_alg));
+ memcpy(awaited+strlen(def_host_alg), alg, alg_len);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+rsa-sha2-512-cert-v01@openssh.com");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+ssh-rsa");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ if (!ssh_fips_mode()) {
+ /* different algorithm list is used here */
+ free(awaited);
+
+ awaited = calloc(strlen(def_host_alg) + algs_len + 1, 1);
+ assert_non_null(awaited);
+ memcpy(awaited, def_host_alg, strlen(def_host_alg));
+ memcpy(awaited+strlen(def_host_alg), algs, algs_len);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "+rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "+ssh-rsa,ssh-rsa-cert-v01@openssh.com");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+blablabla");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ free(awaited);
+}
+
+static void torture_options_minus_sign(void **state)
+{
+ ssh_session session = *state;
+ int rc;
+ const char *def_host_alg, *alg, *algs;
+ char *awaited, *p;
+ size_t alg_len, algs_len;
+
+ if (ssh_fips_mode()) {
+ alg = "rsa-sha2-512-cert-v01@openssh.com,";
+ algs = "rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521,";
+ def_host_alg = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ } else {
+ alg = "ssh-ed25519,";
+ algs = "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,";
+ def_host_alg = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ }
+ alg_len = strlen(alg);
+ algs_len = strlen(algs);
+
+ awaited = calloc(strlen(def_host_alg) + 1, 1);
+ assert_non_null(awaited);
+
+ memcpy(awaited, def_host_alg, strlen(def_host_alg));
+ p = strstr(awaited, alg);
+ assert_non_null(p);
+ memmove(p, p+alg_len, strlen(p + alg_len) + 1);
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-rsa-sha2-512-cert-v01@openssh.com");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-ssh-ed25519");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ p = strstr(awaited, algs);
+ assert_non_null(p);
+ memmove(p, p+algs_len, strlen(p + algs_len) + 1);
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-blablabla");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ free(awaited);
+}
+
+static void torture_options_caret_sign(void **state)
+{
+ ssh_session session = *state;
+ int rc;
+ const char *def_host_alg, *alg, *algs;
+ size_t alg_len, algs_len;
+ char *awaited, *p;
+
+ if (ssh_fips_mode()) {
+ alg = "rsa-sha2-512-cert-v01@openssh.com,";
+ algs = "rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521,";
+ def_host_alg = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ } else {
+ alg = "ssh-rsa,";
+ algs = "ssh-rsa,ssh-rsa-cert-v01@openssh.com,";
+ def_host_alg = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ }
+ alg_len = strlen(alg);
+ algs_len = strlen(algs);
+
+ awaited = calloc(strlen(def_host_alg) + alg_len + 1, 1);
+ assert_non_null(awaited);
+
+ memcpy(awaited, alg, alg_len);
+ memcpy(awaited+alg_len, def_host_alg, strlen(def_host_alg));
+ if (ssh_fips_mode()) {
+ p = strstr(awaited, alg);
+ /* look for second occurrence */
+ p = strstr(p+1, algs);
+ memmove(p, p+alg_len, strlen(p + alg_len) + 1);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^rsa-sha2-512-cert-v01@openssh.com");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^ssh-rsa");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+ /* different algorithm list is used here */
+ free(awaited);
+
+ awaited = calloc(strlen(def_host_alg) + algs_len + 1, 1);
+ assert_non_null(awaited);
+ memcpy(awaited, algs, algs_len);
+ memcpy(awaited+algs_len, def_host_alg, strlen(def_host_alg));
+ if (ssh_fips_mode()) {
+ p = strstr(awaited, algs);
+ /* look for second occurrence */
+ p = strstr(p+1, algs);
+ memmove(p, p+algs_len, strlen(p + algs_len) + 1);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "^rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "^ssh-rsa,ssh-rsa-cert-v01@openssh.com");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^blablabla");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ free(awaited);
+}
+
#ifdef WITH_SERVER
const char template[] = "temp_dir_XXXXXX";
@@ -1881,6 +2098,12 @@ int torture_run_tests(void) {
setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_getopt,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_plus_sign,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_minus_sign,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_caret_sign,
+ setup, teardown),
};
#ifdef WITH_SERVER
--
2.38.1
From c73996c4e747a9e28f919d660411c804bc748324 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Thu, 10 Nov 2022 10:50:52 +0100
Subject: [PATCH 6/6] torture_config.c: Add test for +,-,^ config feature
It should be possible to use features to add,remove,prioritize
algorithms in the algorithm list from the config file.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/unittests/torture_config.c | 393 +++++++++++++++++++++++++++++++
1 file changed, 393 insertions(+)
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index 31dadae3..354adc2f 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -40,6 +40,9 @@ extern LIBSSH_THREAD int ssh_log_level;
#define LIBSSH_TESTCONFIG10 "libssh_testconfig10.tmp"
#define LIBSSH_TESTCONFIG11 "libssh_testconfig11.tmp"
#define LIBSSH_TESTCONFIG12 "libssh_testconfig12.tmp"
+#define LIBSSH_TESTCONFIG14 "libssh_testconfig14.tmp"
+#define LIBSSH_TESTCONFIG15 "libssh_testconfig15.tmp"
+#define LIBSSH_TESTCONFIG16 "libssh_testconfig16.tmp"
#define LIBSSH_TESTCONFIGGLOB "libssh_testc*[36].tmp"
#define LIBSSH_TEST_PUBKEYTYPES "libssh_test_PubkeyAcceptedKeyTypes.tmp"
#define LIBSSH_TEST_PUBKEYALGORITHMS "libssh_test_PubkeyAcceptedAlgorithms.tmp"
@@ -181,6 +184,27 @@ extern LIBSSH_THREAD int ssh_log_level;
"IdentityFile id_rsa_one\n" \
"IdentityFile id_ecdsa_two\n"
+/* +,-,^ features for all supported list */
+/* kex won't work in fips */
+#define LIBSSH_TESTCONFIG_STRING14 \
+ "HostKeyAlgorithms +ssh-rsa\n" \
+ "Ciphers +aes128-cbc,aes256-cbc\n" \
+ "KexAlgorithms +diffie-hellman-group14-sha1,diffie-hellman-group1-sha1\n" \
+ "MACs +hmac-sha1,hmac-sha1-etm@openssh.com\n"
+
+/* have to be algorithms which are in the default list */
+#define LIBSSH_TESTCONFIG_STRING15 \
+ "HostKeyAlgorithms -rsa-sha2-512,rsa-sha2-256\n" \
+ "Ciphers -aes256-ctr\n" \
+ "KexAlgorithms -diffie-hellman-group18-sha512,diffie-hellman-group16-sha512\n" \
+ "MACs -hmac-sha2-256-etm@openssh.com\n"
+
+#define LIBSSH_TESTCONFIG_STRING16 \
+ "HostKeyAlgorithms ^rsa-sha2-512,rsa-sha2-256\n" \
+ "Ciphers ^aes256-cbc\n" \
+ "KexAlgorithms ^diffie-hellman-group18-sha512,diffie-hellman-group16-sha512\n" \
+ "MACs ^hmac-sha1\n"
+
#define LIBSSH_TEST_PUBKEYTYPES_STRING \
"PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n"
@@ -238,6 +262,9 @@ static int setup_config_files(void **state)
unlink(LIBSSH_TESTCONFIG10);
unlink(LIBSSH_TESTCONFIG11);
unlink(LIBSSH_TESTCONFIG12);
+ unlink(LIBSSH_TESTCONFIG14);
+ unlink(LIBSSH_TESTCONFIG15);
+ unlink(LIBSSH_TESTCONFIG16);
unlink(LIBSSH_TEST_PUBKEYTYPES);
unlink(LIBSSH_TEST_PUBKEYALGORITHMS);
unlink(LIBSSH_TEST_NONEWLINEEND);
@@ -285,6 +312,14 @@ static int setup_config_files(void **state)
torture_write_file(LIBSSH_TESTCONFIG12,
LIBSSH_TESTCONFIG_STRING12);
+ /* +,-,^ feature */
+ torture_write_file(LIBSSH_TESTCONFIG14,
+ LIBSSH_TESTCONFIG_STRING14);
+ torture_write_file(LIBSSH_TESTCONFIG15,
+ LIBSSH_TESTCONFIG_STRING15);
+ torture_write_file(LIBSSH_TESTCONFIG16,
+ LIBSSH_TESTCONFIG_STRING16);
+
torture_write_file(LIBSSH_TEST_PUBKEYTYPES,
LIBSSH_TEST_PUBKEYTYPES_STRING);
@@ -316,6 +351,9 @@ static int teardown_config_files(void **state)
unlink(LIBSSH_TESTCONFIG10);
unlink(LIBSSH_TESTCONFIG11);
unlink(LIBSSH_TESTCONFIG12);
+ unlink(LIBSSH_TESTCONFIG14);
+ unlink(LIBSSH_TESTCONFIG15);
+ unlink(LIBSSH_TESTCONFIG16);
unlink(LIBSSH_TEST_PUBKEYTYPES);
unlink(LIBSSH_TEST_PUBKEYALGORITHMS);
@@ -1267,6 +1305,349 @@ static void torture_config_rekey_string(void **state)
torture_config_rekey(state, NULL, LIBSSH_TESTCONFIG_STRING12);
}
+/**
+ * @brief Remove substring from a string
+ *
+ * @param occurrence 0 means "remove the first occurrence"
+ * 1 means "remove the second occurrence" and so on
+ */
+static void helper_remove_substring(char *s, const char *subs, int occurrence) {
+ char *p;
+ /* remove the substring from the defaults */
+ p = strstr(s, subs);
+ assert_non_null(p);
+ /* look for second occurrence */
+ for (int i = 0; i < occurrence; i++) {
+ p = strstr(p + 1, subs);
+ assert_non_null(p);
+ }
+ memmove(p, p + strlen(subs), strlen(p + strlen(subs)) + 1);
+}
+
+/**
+ * @brief test that openssh style '+' feature works
+ */
+static void torture_config_plus(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+ const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S);
+ const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S);
+ const char *def_kex = ssh_kex_get_default_methods(SSH_KEX);
+ const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX);
+ const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S);
+ const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S);
+ const char *hostkeys_added = ",ssh-rsa";
+ const char *ciphers_added = "aes128-cbc,aes256-cbc";
+ const char *kex_added = ",diffie-hellman-group14-sha1,diffie-hellman-group1-sha1";
+ const char *mac_added = ",hmac-sha1,hmac-sha1-etm@openssh.com";
+ char *awaited = NULL;
+ int rc;
+
+ _parse_config(session, file, string, SSH_OK);
+
+ /* check hostkeys */
+ if (ssh_fips_mode()) {
+ /* ssh-rsa is disabled in fips */
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], fips_hostkeys);
+ } else {
+ awaited = calloc(strlen(def_hostkeys) + strlen(hostkeys_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_hostkeys) + strlen(hostkeys_added) + 1,
+ "%s%s", def_hostkeys, hostkeys_added);
+ assert_int_equal(rc, strlen(def_hostkeys) + strlen(hostkeys_added));
+
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited);
+ free(awaited);
+ }
+
+ /* check ciphers */
+ if (ssh_fips_mode()) {
+ /* already all supported is in the list */
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], fips_ciphers);
+ } else {
+ awaited = calloc(strlen(def_ciphers) + strlen(ciphers_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_ciphers) + strlen(ciphers_added) + 1,
+ "%s%s", def_ciphers, ciphers_added);
+ assert_int_equal(rc, strlen(def_ciphers) + strlen(ciphers_added));
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited);
+ free(awaited);
+ }
+
+ /* check kex */
+ if (ssh_fips_mode()) {
+ /* sha1 is disabled in fips */
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], fips_kex);
+ } else {
+ awaited = calloc(strlen(def_kex) + strlen(kex_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_kex) + strlen(kex_added) + 1,
+ "%s%s", def_kex, kex_added);
+ assert_int_equal(rc, strlen(def_kex) + strlen(kex_added));
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited);
+ free(awaited);
+ }
+
+ /* check mac */
+ if (ssh_fips_mode()) {
+ /* the added algos are already in the fips_methods */
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], fips_mac);
+ } else {
+ awaited = calloc(strlen(def_mac) + strlen(mac_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_mac) + strlen(mac_added) + 1,
+ "%s%s", def_mac, mac_added);
+ assert_int_equal(rc, strlen(def_mac) + strlen(mac_added));
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited);
+ free(awaited);
+ }
+}
+
+/**
+ * @brief test that openssh style '+' feature works from file
+ */
+static void torture_config_plus_file(void **state)
+{
+ torture_config_plus(state, LIBSSH_TESTCONFIG14, NULL);
+}
+
+/**
+ * @brief test that openssh style '+' feature works from string
+ */
+static void torture_config_plus_string(void **state)
+{
+ torture_config_plus(state, NULL, LIBSSH_TESTCONFIG_STRING14);
+}
+
+/**
+ * @brief test that openssh style '-' feature works from string
+ */
+static void torture_config_minus(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+ const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S);
+ const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S);
+ const char *def_kex = ssh_kex_get_default_methods(SSH_KEX);
+ const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX);
+ const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S);
+ const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S);
+ const char *hostkeys_removed = ",rsa-sha2-512,rsa-sha2-256";
+ const char *ciphers_removed = ",aes256-ctr";
+ const char *kex_removed = ",diffie-hellman-group18-sha512,diffie-hellman-group16-sha512";
+ const char *fips_kex_removed = ",diffie-hellman-group16-sha512,diffie-hellman-group18-sha512";
+ const char *mac_removed = "hmac-sha2-256-etm@openssh.com,";
+ char *awaited = NULL;
+ int rc;
+
+ _parse_config(session, file, string, SSH_OK);
+
+ /* check hostkeys */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_hostkeys) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_hostkeys) + 1, "%s", fips_hostkeys);
+ assert_int_equal(rc, strlen(fips_hostkeys));
+ } else {
+ awaited = calloc(strlen(def_hostkeys) + 1, 1);
+ rc = snprintf(awaited, strlen(def_hostkeys) + 1, "%s", def_hostkeys);
+ assert_int_equal(rc, strlen(def_hostkeys));
+ }
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, hostkeys_removed, 0);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited);
+ free(awaited);
+
+ /* check ciphers */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_ciphers) + 1, "%s", fips_ciphers);
+ assert_int_equal(rc, strlen(fips_ciphers));
+ } else {
+ awaited = calloc(strlen(def_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(def_ciphers) + 1, "%s", def_ciphers);
+ assert_int_equal(rc, strlen(def_ciphers));
+ /* remove the comma at the end of the list */
+ awaited[strlen(awaited) - 1] = '\0';
+ }
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, ciphers_removed, 0);
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited);
+ free(awaited);
+
+ /* check kex */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_kex) + 1, "%s", fips_kex);
+ assert_int_equal(rc, strlen(fips_kex));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, fips_kex_removed, 0);
+ } else {
+ awaited = calloc(strlen(def_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(def_kex) + 1, "%s", def_kex);
+ assert_int_equal(rc, strlen(def_kex));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, kex_removed, 0);
+ }
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited);
+ free(awaited);
+
+ /* check mac */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_mac) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_mac) + 1, "%s", fips_mac);
+ assert_int_equal(rc, strlen(fips_mac));
+ } else {
+ awaited = calloc(strlen(def_mac) + 1, 1);
+ rc = snprintf(awaited, strlen(def_mac) + 1, "%s", def_mac);
+ assert_int_equal(rc, strlen(def_mac));
+ }
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, mac_removed, 0);
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited);
+ free(awaited);
+}
+
+/**
+ * @brief test that openssh style '-' feature works from file
+ */
+static void torture_config_minus_file(void **state)
+{
+ torture_config_minus(state, LIBSSH_TESTCONFIG15, NULL);
+}
+
+/**
+ * @brief test that openssh style '-' feature works from string
+ */
+static void torture_config_minus_string(void **state)
+{
+ torture_config_minus(state, NULL, LIBSSH_TESTCONFIG_STRING15);
+}
+
+/**
+ * @brief test that openssh style '^' feature works from string
+ */
+static void torture_config_caret(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+ const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S);
+ const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S);
+ const char *def_kex = ssh_kex_get_default_methods(SSH_KEX);
+ const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX);
+ const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S);
+ const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S);
+ const char *hostkeys_prio = "rsa-sha2-512,rsa-sha2-256";
+ const char *ciphers_prio = "aes256-cbc,";
+ const char *kex_prio = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,";
+ const char *fips_kex_prio = ",diffie-hellman-group16-sha512,diffie-hellman-group18-sha512";
+ const char *mac_prio = "hmac-sha1,";
+ char *awaited = NULL;
+ int rc;
+
+ _parse_config(session, file, string, SSH_OK);
+
+ /* check hostkeys */
+ /* +2 for the added comma and the \0 */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(hostkeys_prio) + strlen(fips_hostkeys) + 2, 1);
+ rc = snprintf(awaited, strlen(hostkeys_prio) + strlen(fips_hostkeys) + 2,
+ "%s,%s", hostkeys_prio, fips_hostkeys);
+ assert_int_equal(rc, strlen(hostkeys_prio) + strlen(fips_hostkeys) + 1);
+ } else {
+ awaited = calloc(strlen(def_hostkeys) + strlen(hostkeys_prio) + 2, 1);
+ rc = snprintf(awaited, strlen(hostkeys_prio) + strlen(def_hostkeys) + 2,
+ "%s,%s", hostkeys_prio, def_hostkeys);
+ assert_int_equal(rc, strlen(hostkeys_prio) + strlen(def_hostkeys) + 1);
+ }
+
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, hostkeys_prio, 1);
+ /* remove the comma at the end of the list */
+ awaited[strlen(awaited) - 1] = '\0';
+
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited);
+ free(awaited);
+
+ /* check ciphers */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(ciphers_prio) + strlen(fips_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(ciphers_prio) + strlen(fips_ciphers) + 1,
+ "%s%s", ciphers_prio, fips_ciphers);
+ assert_int_equal(rc, strlen(ciphers_prio) + strlen(fips_ciphers));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, ciphers_prio, 1);
+ } else {
+ /* + 2 because the '\0' and the comma */
+ awaited = calloc(strlen(ciphers_prio) + strlen(def_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(ciphers_prio) + strlen(def_ciphers) + 1,
+ "%s%s", ciphers_prio, def_ciphers);
+ assert_int_equal(rc, strlen(ciphers_prio) + strlen(def_ciphers));
+ /* remove the comma at the end of the list */
+ awaited[strlen(awaited) - 1] = '\0';
+ }
+
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited);
+ free(awaited);
+
+ /* check kex */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(kex_prio) + strlen(fips_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(kex_prio) + strlen(fips_kex) + 1,
+ "%s%s", kex_prio, fips_kex);
+ assert_int_equal(rc, strlen(kex_prio) + strlen(fips_kex));
+ /* remove the substring from the defaults */
+ /* the default list has different order of these two algos than the fips
+ * and because here is a braindead string substitution being done,
+ * change the order and remove the first occurrence of it */
+ helper_remove_substring(awaited, fips_kex_prio, 0);
+ } else {
+ awaited = calloc(strlen(kex_prio) + strlen(def_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(kex_prio) + strlen(def_kex) + 1,
+ "%s%s", kex_prio, def_kex);
+ assert_int_equal(rc, strlen(def_kex) + strlen(kex_prio));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, kex_prio, 1);
+ }
+
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited);
+ free(awaited);
+
+ /* check mac */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(mac_prio) + strlen(fips_mac) + 1, 1);
+ rc = snprintf(awaited, strlen(mac_prio) + strlen(fips_mac) + 1, "%s%s", mac_prio, fips_mac);
+ assert_int_equal(rc, strlen(mac_prio) + strlen(fips_mac));
+ /* the fips list contains hmac-sha1 algo */
+ helper_remove_substring(awaited, mac_prio, 1);
+ } else {
+ awaited = calloc(strlen(mac_prio) + strlen(def_mac) + 1, 1);
+ /* the mac is not in default; it is added to the list */
+ rc = snprintf(awaited, strlen(mac_prio) + strlen(def_mac) + 1, "%s%s", mac_prio, def_mac);
+ assert_int_equal(rc, strlen(mac_prio) + strlen(def_mac));
+ }
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited);
+ free(awaited);
+}
+
+/**
+ * @brief test that openssh style '^' feature works from file
+ */
+static void torture_config_caret_file(void **state)
+{
+ torture_config_caret(state, LIBSSH_TESTCONFIG16, NULL);
+}
+
+/**
+ * @brief test that openssh style '^' feature works from string
+ */
+static void torture_config_caret_string(void **state)
+{
+ torture_config_caret(state, NULL, LIBSSH_TESTCONFIG_STRING16);
+}
+
/**
* @brief test PubkeyAcceptedKeyTypes helper function
*/
@@ -1848,6 +2229,18 @@ int torture_run_tests(void)
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_rekey_string,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_plus_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_plus_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_minus_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_minus_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_caret_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_caret_string,
+ setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_file,
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_string,
--
2.38.1