1 #include "config.h"
2
3 #define LIBSSH_STATIC
4
5 #include <pwd.h>
6 #include <errno.h>
7 #include "torture.h"
8 #include "libssh/session.h"
9 #include "libssh/misc.h"
10
11 #define LIBSSH_SSH_CONFIG "libssh_config"
12
13 #define TORTURE_CONFIG_USER "test-user"
14
15 #define CIPHERS "aes256-gcm@openssh.com,chacha20-poly1305@openssh.com"
16 #define CIPHERS2 "aes256-cbc,aes128-ctr"
17
sshd_setup(void ** state)18 static int sshd_setup(void **state)
19 {
20 torture_setup_sshd_server(state, false);
21
22 return 0;
23 }
24
sshd_teardown(void ** state)25 static int sshd_teardown(void **state) {
26 torture_teardown_sshd_server(state);
27
28 return 0;
29 }
30
setup_config_files(void ** state)31 static int setup_config_files(void **state)
32 {
33 struct torture_state *s = *state;
34 int verbosity;
35 struct passwd *pwd;
36 char *filename = NULL;
37 int rc;
38
39 /* Work under the bob's UID to be able to load his configuration file */
40 pwd = getpwnam("bob");
41 assert_non_null(pwd);
42
43 rc = setuid(pwd->pw_uid);
44 assert_return_code(rc, errno);
45
46 s->ssh.session = ssh_new();
47 assert_non_null(s->ssh.session);
48
49 filename = ssh_path_expand_tilde("~/.ssh/config");
50 torture_write_file(filename, "Ciphers "CIPHERS"\nTestBogus1\nUser "TORTURE_CONFIG_USER);
51 free(filename);
52
53 torture_write_file(LIBSSH_SSH_CONFIG, "Ciphers "CIPHERS2"\nTestBogus2\n");
54
55 verbosity = torture_libssh_verbosity();
56 ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
57 ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
58
59 return 0;
60 }
61
teardown(void ** state)62 static int teardown(void **state)
63 {
64 struct torture_state *s = *state;
65 char *filename;
66
67 filename = ssh_path_expand_tilde("~/.ssh/config");
68 if (filename != NULL) {
69 if (strlen(filename) > 0) {
70 unlink(filename);
71 }
72 SAFE_FREE(filename);
73 }
74
75 unlink(LIBSSH_SSH_CONFIG);
76
77 ssh_disconnect(s->ssh.session);
78 ssh_free(s->ssh.session);
79
80 return 0;
81 }
82
83 /* This tests makes sure that parsing both system-wide and per-user
84 * configuration files retains OpenSSH semantics (the per-user overrides
85 * the system-wide values).
86 * This function ssh_options_parse_config() has hardcoded path to the
87 * system-wide configuration file so this might not test anything at all
88 * if this system-wide file does not overwrite this option.
89 */
torture_client_config_system(void ** state)90 static void torture_client_config_system(void **state)
91 {
92 struct torture_state *s = *state;
93 int ret = 0;
94
95 char *fips_ciphers = NULL;
96
97 if (ssh_fips_mode()) {
98 fips_ciphers = ssh_keep_fips_algos(SSH_CRYPT_C_S, CIPHERS);
99 assert_non_null(fips_ciphers);
100 }
101
102 /* The first tests assumes there is system-wide configuration file
103 * setting Ciphers to some non-default value. We do not have any control
104 * of that in this test case.
105 */
106 ret = ssh_options_parse_config(s->ssh.session, NULL);
107 assert_ssh_return_code(s->ssh.session, ret);
108
109 assert_non_null(s->ssh.session->opts.wanted_methods[SSH_CRYPT_C_S]);
110 assert_non_null(s->ssh.session->opts.wanted_methods[SSH_CRYPT_S_C]);
111 if (ssh_fips_mode()) {
112 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_C_S],
113 fips_ciphers);
114 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_S_C],
115 fips_ciphers);
116 } else {
117 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_C_S],
118 CIPHERS);
119 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_S_C],
120 CIPHERS);
121 }
122
123 /* Make sure the configuration was processed and user modified */
124 assert_string_equal(s->ssh.session->opts.username, TORTURE_CONFIG_USER);
125
126 SAFE_FREE(fips_ciphers);
127 }
128
129 /* This tests makes sure that parsing both system-wide and per-user
130 * configuration files retains OpenSSH semantics (the per-user overrides
131 * the system-wide values).
132 * The function ssh_options_parse_config() has hardcoded path to the
133 * system-wide configuraion file so we try to emmulate the behavior by parsing
134 * the files separately in the same order.
135 */
torture_client_config_emulate(void ** state)136 static void torture_client_config_emulate(void **state)
137 {
138 struct torture_state *s = *state;
139 char *filename = NULL;
140 int ret = 0;
141
142 char *fips_ciphers = NULL;
143
144 if (ssh_fips_mode()) {
145 fips_ciphers = ssh_keep_fips_algos(SSH_CRYPT_C_S, CIPHERS);
146 assert_non_null(fips_ciphers);
147 }
148
149 /* The first tests assumes there is system-wide configuration file
150 * setting Ciphers to some non-default value. We do not have any control
151 * of that in this test case
152 */
153 filename = ssh_path_expand_tilde("~/.ssh/config");
154 ret = ssh_options_parse_config(s->ssh.session, filename);
155 free(filename);
156 assert_ssh_return_code(s->ssh.session, ret);
157
158 ret = ssh_options_parse_config(s->ssh.session, LIBSSH_SSH_CONFIG);
159 assert_ssh_return_code(s->ssh.session, ret);
160
161 assert_non_null(s->ssh.session->opts.wanted_methods[SSH_CRYPT_C_S]);
162 assert_non_null(s->ssh.session->opts.wanted_methods[SSH_CRYPT_S_C]);
163 if (ssh_fips_mode()) {
164 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_C_S],
165 fips_ciphers);
166 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_S_C],
167 fips_ciphers);
168 } else {
169 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_C_S],
170 CIPHERS);
171 assert_string_equal(s->ssh.session->opts.wanted_methods[SSH_CRYPT_S_C],
172 CIPHERS);
173 }
174 /* Make sure the configuration was processed and user modified */
175 assert_string_equal(s->ssh.session->opts.username, TORTURE_CONFIG_USER);
176
177 SAFE_FREE(fips_ciphers);
178 }
179
180 /* This verifies that configuration files are parsed by default.
181 */
torture_client_config_autoparse(void ** state)182 static void torture_client_config_autoparse(void **state)
183 {
184 struct torture_state *s = *state;
185 int ret = 0;
186
187 ret = ssh_connect(s->ssh.session);
188 assert_ssh_return_code(s->ssh.session, ret);
189
190 /* Make sure the configuration was processed and user modified */
191 assert_string_equal(s->ssh.session->opts.username, TORTURE_CONFIG_USER);
192 }
193
194 /* This verifies that we are able to suppress parsing of the configuration files
195 * on connect using an option.
196 */
torture_client_config_suppress(void ** state)197 static void torture_client_config_suppress(void **state)
198 {
199 struct torture_state *s = *state;
200 bool b = false;
201 int ret = 0;
202
203 ret = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
204 assert_ssh_return_code(s->ssh.session, ret);
205
206 ret = ssh_connect(s->ssh.session);
207 assert_ssh_return_code(s->ssh.session, ret);
208
209 /* Make sure the configuration was not processed and user modified */
210 assert_string_equal(s->ssh.session->opts.username, "bob");
211 }
212
213
torture_run_tests(void)214 int torture_run_tests(void) {
215 int rc;
216 struct CMUnitTest tests[] = {
217 cmocka_unit_test_setup_teardown(torture_client_config_system,
218 setup_config_files,
219 teardown),
220 cmocka_unit_test_setup_teardown(torture_client_config_emulate,
221 setup_config_files,
222 teardown),
223 cmocka_unit_test_setup_teardown(torture_client_config_autoparse,
224 setup_config_files,
225 teardown),
226 cmocka_unit_test_setup_teardown(torture_client_config_suppress,
227 setup_config_files,
228 teardown),
229 };
230
231
232 ssh_init();
233 torture_filter_tests(tests);
234 rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
235 ssh_finalize();
236 return rc;
237 }
238