Blame SOURCES/plus_sign.patch

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