/src/libssh/tests/fuzz/ssh_server_fuzzer.c
Line | Count | Source |
1 | | /* |
2 | | # Copyright 2016 Google Inc. |
3 | | # |
4 | | # Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | # you may not use this file except in compliance with the License. |
6 | | # You may obtain a copy of the License at |
7 | | # |
8 | | # http://www.apache.org/licenses/LICENSE-2.0 |
9 | | # |
10 | | # Unless required by applicable law or agreed to in writing, software |
11 | | # distributed under the License is distributed on an "AS IS" BASIS, |
12 | | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | # See the License for the specific language governing permissions and |
14 | | # limitations under the License. |
15 | | # |
16 | | ################################################################################ |
17 | | */ |
18 | | |
19 | | #include <assert.h> |
20 | | #include <fcntl.h> |
21 | | #include <stdint.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | #include <stdio.h> |
25 | | #include <sys/socket.h> |
26 | | #include <unistd.h> |
27 | | #include <stdbool.h> |
28 | | |
29 | | #define LIBSSH_STATIC 1 |
30 | | #include <libssh/libssh.h> |
31 | | #include <libssh/callbacks.h> |
32 | | #include <libssh/server.h> |
33 | | |
34 | | #include "nallocinc.c" |
35 | | |
36 | | static const char kRSAPrivateKeyPEM[] = |
37 | | "-----BEGIN RSA PRIVATE KEY-----\n" |
38 | | "MIIEowIBAAKCAQEArAOREUWlBXJAKZ5hABYyxnRayDZP1bJeLbPVK+npxemrhHyZ\n" |
39 | | "gjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZWwjMKu/QYcycYp5HL01goxOxuusZb\n" |
40 | | "i+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDjUDniWabXQJge8ksGXGTiFeAJ/687\n" |
41 | | "uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqeGDzyeBZ1NLIuaiOORyLGSW4duHLD\n" |
42 | | "N78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9Uw4XQFTsWugUDEY1AU4c5g11nhzHz\n" |
43 | | "Bi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHrGQIDAQABAoIBAFJTaqy/jllq8vZ4\n" |
44 | | "TKiD900wBvrns5HtSlHJTe80hqQoT+Sa1cWSxPR0eekL32Hjy9igbMzZ83uWzh7I\n" |
45 | | "mtgNODy9vRdznfgO8CfTCaBfAzQsjFpr8QikMT6EUI/LpiRL1UaGsNOlSEvnSS0Z\n" |
46 | | "b1uDzAdrjL+nsEHEDJud+K9jwSkCRifVMy7fLfaum+YKpdeEz7K2Mgm5pJ/Vg+9s\n" |
47 | | "vI2V1q7HAOI4eUVTgJNHXy5ediRJlajQHf/lNUzHKqn7iH+JRl01gt62X8roG62b\n" |
48 | | "TbFylbheqMm9awuSF2ucOcx+guuwhkPir8BEMb08j3hiK+TfwPdY0F6QH4OhiKK7\n" |
49 | | "MTqTVgECgYEA0vmmu5GOBtwRmq6gVNCHhdLDQWaxAZqQRmRbzxVhFpbv0GjbQEF7\n" |
50 | | "tttq3fjDrzDf6CE9RtZWw2BUSXVq+IXB/bXb1kgWU2xWywm+OFDk9OXQs8ui+MY7\n" |
51 | | "FiP3yuq3YJob2g5CCsVQWl2CHvWGmTLhE1ODll39t7Y1uwdcDobJN+ECgYEA0LlR\n" |
52 | | "hfMjydWmwqooU9TDjXNBmwufyYlNFTH351amYgFUDpNf35SMCP4hDosUw/zCTDpc\n" |
53 | | "+1w04BJJfkH1SNvXSOilpdaYRTYuryDvGmWC66K2KX1nLErhlhs17CwzV997nYgD\n" |
54 | | "H3OOU4HfqIKmdGbjvWlkmY+mLHyG10bbpOTbujkCgYAc68xHejSWDCT9p2KjPdLW\n" |
55 | | "LYZGuOUa6y1L+QX85Vlh118Ymsczj8Z90qZbt3Zb1b9b+vKDe255agMj7syzNOLa\n" |
56 | | "/MseHNOyq+9Z9gP1hGFekQKDIy88GzCOYG/fiT2KKJYY1kuHXnUdbiQgSlghODBS\n" |
57 | | "jehD/K6DOJ80/FVKSH/dAQKBgQDJ+apTzpZhJ2f5k6L2jDq3VEK2ACedZEm9Kt9T\n" |
58 | | "c1wKFnL6r83kkuB3i0L9ycRMavixvwBfFDjuY4POs5Dh8ip/mPFCa0hqISZHvbzi\n" |
59 | | "dDyePJO9zmXaTJPDJ42kfpkofVAnfohXFQEy+cguTk848J+MmMIKfyE0h0QMabr9\n" |
60 | | "86BUsQKBgEVgoi4RXwmtGovtMew01ORPV9MOX3v+VnsCgD4/56URKOAngiS70xEP\n" |
61 | | "ONwNbTCWuuv43HGzJoVFiAMGnQP1BAJ7gkHkjSegOGKkiw12EPUWhFcMg+GkgPhc\n" |
62 | | "pOqNt/VMBPjJ/ysHJqmLfQK9A35JV6Cmdphe+OIl28bcKhAOz8Dw\n" |
63 | | "-----END RSA PRIVATE KEY-----\n"; |
64 | | |
65 | | /* A userdata struct for session. */ |
66 | | struct session_data_struct { |
67 | | /* Pointer to the channel the session will allocate. */ |
68 | | ssh_channel channel; |
69 | | size_t auth_attempts; |
70 | | bool authenticated; |
71 | | }; |
72 | | |
73 | | static void _fuzz_finalize(void) |
74 | 4 | { |
75 | 4 | ssh_finalize(); |
76 | 4 | } |
77 | | |
78 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
79 | 32 | { |
80 | 32 | (void)argc; |
81 | | |
82 | 32 | nalloc_init(*argv[0]); |
83 | | |
84 | 32 | ssh_init(); |
85 | | |
86 | 32 | atexit(_fuzz_finalize); |
87 | | |
88 | 32 | return 0; |
89 | 32 | } |
90 | | |
91 | | static int auth_none(ssh_session session, const char *user, void *userdata) |
92 | 4.97k | { |
93 | 4.97k | struct session_data_struct *sdata = |
94 | 4.97k | (struct session_data_struct *)userdata; |
95 | | |
96 | 4.97k | (void)session; |
97 | 4.97k | (void)user; |
98 | | |
99 | 4.97k | if (sdata->auth_attempts > 0) { |
100 | 2.48k | sdata->authenticated = true; |
101 | 2.48k | } |
102 | 4.97k | sdata->auth_attempts++; |
103 | | |
104 | 4.97k | if (!sdata->authenticated) { |
105 | 2.49k | return SSH_AUTH_PARTIAL; |
106 | 2.49k | } |
107 | | |
108 | 2.48k | return SSH_AUTH_SUCCESS; |
109 | 4.97k | } |
110 | | |
111 | | static ssh_channel channel_open(ssh_session session, void *userdata) |
112 | 1.44k | { |
113 | 1.44k | struct session_data_struct *sdata = |
114 | 1.44k | (struct session_data_struct *)userdata; |
115 | | |
116 | 1.44k | sdata->channel = ssh_channel_new(session); |
117 | | |
118 | 1.44k | return sdata->channel; |
119 | 1.44k | } |
120 | | |
121 | | static int write_rsa_hostkey(const char *rsakey_path) |
122 | 7.94k | { |
123 | 7.94k | FILE *fp = NULL; |
124 | 7.94k | size_t nwritten; |
125 | | |
126 | 7.94k | fp = fopen(rsakey_path, "wb"); |
127 | 7.94k | if (fp == NULL) { |
128 | 0 | return -1; |
129 | 0 | } |
130 | | |
131 | 7.94k | nwritten = fwrite(kRSAPrivateKeyPEM, 1, strlen(kRSAPrivateKeyPEM), fp); |
132 | 7.94k | fclose(fp); |
133 | | |
134 | 7.94k | if (nwritten != strlen(kRSAPrivateKeyPEM)) { |
135 | 0 | return -1; |
136 | 0 | } |
137 | | |
138 | 7.94k | return 0; |
139 | 7.94k | } |
140 | | |
141 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
142 | 7.95k | { |
143 | 7.95k | int socket_fds[2] = {-1, -1}; |
144 | 7.95k | ssize_t nwritten; |
145 | 7.95k | bool no = false; |
146 | 7.95k | const char *env = NULL; |
147 | 7.95k | int rc; |
148 | 7.95k | ssh_bind sshbind = NULL; |
149 | 7.95k | ssh_session session = NULL; |
150 | 7.95k | ssh_event event = NULL; |
151 | | |
152 | | /* Our struct holding information about the session. */ |
153 | 7.95k | struct session_data_struct sdata = { |
154 | 7.95k | .channel = NULL, |
155 | 7.95k | .auth_attempts = 0, |
156 | 7.95k | .authenticated = false, |
157 | 7.95k | }; |
158 | | |
159 | 7.95k | struct ssh_server_callbacks_struct server_cb = { |
160 | 7.95k | .userdata = &sdata, |
161 | 7.95k | .auth_none_function = auth_none, |
162 | 7.95k | .channel_open_request_session_function = channel_open, |
163 | 7.95k | }; |
164 | | |
165 | | /* This is the maximum that can be handled by the socket buffer before the |
166 | | * other side will read some data. Other option would be feeding the socket |
167 | | * from different thread which would not mind if it would be blocked, but I |
168 | | * believe all the important inputs should fit into this size */ |
169 | 7.95k | if (size > 219264) { |
170 | 6 | return -1; |
171 | 6 | } |
172 | | |
173 | | /* Write SSH RSA host key to disk */ |
174 | 7.94k | rc = write_rsa_hostkey("/tmp/libssh_fuzzer_private_key"); |
175 | 7.94k | assert(rc == 0); |
176 | | |
177 | | /* Set up the socket to send data */ |
178 | 7.94k | rc = socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds); |
179 | 7.94k | assert(rc == 0); |
180 | | |
181 | 7.94k | nwritten = send(socket_fds[1], data, size, 0); |
182 | 7.94k | assert((size_t)nwritten == size); |
183 | | |
184 | 7.94k | rc = shutdown(socket_fds[1], SHUT_WR); |
185 | 7.94k | assert(rc == 0); |
186 | | |
187 | 7.94k | assert(nalloc_start(data, size) > 0); |
188 | | |
189 | | /* Set up the libssh server */ |
190 | 7.94k | sshbind = ssh_bind_new(); |
191 | 7.94k | if (sshbind == NULL) { |
192 | 138 | goto out; |
193 | 138 | } |
194 | | |
195 | 7.81k | session = ssh_new(); |
196 | 7.81k | if (session == NULL) { |
197 | 120 | goto out; |
198 | 120 | } |
199 | | |
200 | 7.69k | env = getenv("LIBSSH_VERBOSITY"); |
201 | 7.69k | if (env != NULL && strlen(env) > 0) { |
202 | 0 | rc = ssh_bind_options_set(sshbind, |
203 | 0 | SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, |
204 | 0 | env); |
205 | 0 | if (rc != SSH_OK) { |
206 | 0 | goto out; |
207 | 0 | } |
208 | 0 | } |
209 | 7.69k | rc = ssh_bind_options_set(sshbind, |
210 | 7.69k | SSH_BIND_OPTIONS_HOSTKEY, |
211 | 7.69k | "/tmp/libssh_fuzzer_private_key"); |
212 | 7.69k | if (rc != SSH_OK) { |
213 | 296 | goto out; |
214 | 296 | } |
215 | 7.39k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_C_S, "none"); |
216 | 7.39k | if (rc != SSH_OK) { |
217 | 0 | goto out; |
218 | 0 | } |
219 | 7.39k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_S_C, "none"); |
220 | 7.39k | if (rc != SSH_OK) { |
221 | 0 | goto out; |
222 | 0 | } |
223 | 7.39k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_C_S, "none"); |
224 | 7.39k | if (rc != SSH_OK) { |
225 | 0 | goto out; |
226 | 0 | } |
227 | 7.39k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_S_C, "none"); |
228 | 7.39k | if (rc != SSH_OK) { |
229 | 0 | goto out; |
230 | 0 | } |
231 | 7.39k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_PROCESS_CONFIG, &no); |
232 | 7.39k | if (rc != SSH_OK) { |
233 | 0 | goto out; |
234 | 0 | } |
235 | | |
236 | 7.39k | ssh_set_auth_methods(session, SSH_AUTH_METHOD_NONE); |
237 | | |
238 | 7.39k | ssh_callbacks_init(&server_cb); |
239 | 7.39k | ssh_set_server_callbacks(session, &server_cb); |
240 | | |
241 | 7.39k | rc = ssh_bind_accept_fd(sshbind, session, socket_fds[0]); |
242 | 7.39k | if (rc != SSH_OK) { |
243 | 0 | goto out; |
244 | 0 | } |
245 | | |
246 | 7.39k | event = ssh_event_new(); |
247 | 7.39k | if (event == NULL) { |
248 | 0 | goto out; |
249 | 0 | } |
250 | | |
251 | 7.39k | if (ssh_handle_key_exchange(session) == SSH_OK) { |
252 | 4.62k | ssh_event_add_session(event, session); |
253 | | |
254 | 4.62k | size_t n = 0; |
255 | 6.81k | while (sdata.authenticated == false || sdata.channel == NULL) { |
256 | 5.89k | if (sdata.auth_attempts >= 3 || n >= 100) { |
257 | 2 | break; |
258 | 2 | } |
259 | | |
260 | 5.89k | if (ssh_event_dopoll(event, 100) == SSH_ERROR) { |
261 | 3.69k | break; |
262 | 3.69k | } |
263 | | |
264 | 2.19k | n++; |
265 | 2.19k | } |
266 | 4.62k | } |
267 | | |
268 | 7.94k | out: |
269 | 7.94k | nalloc_end(); |
270 | | |
271 | 7.94k | ssh_event_free(event); |
272 | | |
273 | 7.94k | close(socket_fds[0]); |
274 | 7.94k | close(socket_fds[1]); |
275 | | |
276 | 7.94k | ssh_disconnect(session); |
277 | 7.94k | ssh_free(session); |
278 | 7.94k | ssh_bind_free(sshbind); |
279 | | |
280 | 7.94k | return 0; |
281 | 7.39k | } |