summaryrefslogtreecommitdiff
blob: 931d63360a12834bbb47aaeca154a750a8f07e4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
From 4893f9515da2696490e6bbe9aaf51f2ef9678b0f Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <nmav@redhat.com>
Date: Thu, 24 Aug 2017 16:28:39 +0200
Subject: [PATCH 2/2] ssh_options_set_algo: ensure we only set known algorithms
 internally

That way, we will not fail later on key exchange phase when something
unknown is negotiated.

Fixes T37

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit 895055ab38e7716390019aae5e11771a88b99d26)
Signed-off-by: Mihai Moldovan <ionic@ionic.de>
---
 include/libssh/kex.h |  1 +
 src/kex.c            | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/options.c        | 11 ++++----
 3 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/include/libssh/kex.h b/include/libssh/kex.h
index 1a5b6d41..23594985 100644
--- a/include/libssh/kex.h
+++ b/include/libssh/kex.h
@@ -41,6 +41,7 @@ void ssh_list_kex(struct ssh_kex_struct *kex);
 int set_client_kex(ssh_session session);
 int ssh_kex_select_methods(ssh_session session);
 int verify_existing_algo(int algo, const char *name);
+char *keep_known_algos(int algo, const char *list);
 char **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 519d79ce..f0c9d067 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -281,6 +281,71 @@ char *ssh_find_matching(const char *available_d, const char *preferred_d){
     return NULL;
 }
 
+static char *ssh_find_all_matching(const char *available_d,
+                                   const char *preferred_d)
+{
+    char **tok_available, **tok_preferred;
+    int i_avail, i_pref;
+    char *ret;
+    unsigned max, len, pos = 0;
+
+    if ((available_d == NULL) || (preferred_d == NULL)) {
+        return NULL; /* don't deal with null args */
+    }
+
+    max = MAX(strlen(available_d), strlen(preferred_d));
+
+    ret = malloc(max+1);
+    if (ret == NULL) {
+      return NULL;
+    }
+    ret[0] = 0;
+
+    tok_available = tokenize(available_d);
+    if (tok_available == NULL) {
+        SAFE_FREE(ret);
+        return NULL;
+    }
+
+    tok_preferred = tokenize(preferred_d);
+    if (tok_preferred == NULL) {
+        SAFE_FREE(ret);
+        SAFE_FREE(tok_available[0]);
+        SAFE_FREE(tok_available);
+        return NULL;
+    }
+
+    for (i_pref = 0; tok_preferred[i_pref] ; ++i_pref) {
+        for (i_avail = 0; tok_available[i_avail]; ++i_avail) {
+            int cmp = strcmp(tok_available[i_avail],tok_preferred[i_pref]);
+            if (cmp == 0) {
+                /* match */
+                if (pos != 0) {
+                    ret[pos] = ',';
+                    pos++;
+                }
+
+                len = strlen(tok_available[i_avail]);
+                memcpy(&ret[pos], tok_available[i_avail], len);
+                pos += len;
+                ret[pos] = '\0';
+            }
+        }
+    }
+
+    if (ret[0] == '\0') {
+        SAFE_FREE(ret);
+        ret = NULL;
+    }
+
+    SAFE_FREE(tok_available[0]);
+    SAFE_FREE(tok_preferred[0]);
+    SAFE_FREE(tok_available);
+    SAFE_FREE(tok_preferred);
+
+    return ret;
+}
+
 /**
  * @internal
  * @brief returns whether the first client key exchange algorithm or
@@ -668,4 +733,14 @@ int verify_existing_algo(int algo, const char *name){
     return 0;
 }
 
+/* returns a copy of the provided list if everything is supported,
+ * otherwise a new list of the supported algorithms */
+char *keep_known_algos(int algo, const char *list)
+{
+    if ((algo > 9) || (algo < 0)) {
+        return NULL;
+    }
+
+    return ssh_find_all_matching(supported_methods[algo], list);
+}
 /* vim: set ts=2 sw=2 et cindent: */
diff --git a/src/options.c b/src/options.c
index aed2dda5..34fe9cc7 100644
--- a/src/options.c
+++ b/src/options.c
@@ -164,7 +164,10 @@ int ssh_options_copy(ssh_session src, ssh_session *dest) {
 
 int ssh_options_set_algo(ssh_session session, int algo,
     const char *list) {
-  if (!verify_existing_algo(algo, list)) {
+  char *p = NULL;
+
+  p = keep_known_algos(algo, list);
+  if (p == NULL) {
     ssh_set_error(session, SSH_REQUEST_DENIED,
         "Setting method: no algorithm for method \"%s\" (%s)\n",
         ssh_kex_get_description(algo), list);
@@ -172,11 +175,7 @@ int ssh_options_set_algo(ssh_session session, int algo,
   }
 
   SAFE_FREE(session->opts.wanted_methods[algo]);
-  session->opts.wanted_methods[algo] = strdup(list);
-  if (session->opts.wanted_methods[algo] == NULL) {
-    ssh_set_error_oom(session);
-    return -1;
-  }
+  session->opts.wanted_methods[algo] = p;
 
   return 0;
 }
-- 
2.15.1