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