/src/libssh/tests/fuzz/ssh_server_fuzzer.c
Line | Count | Source (jump to first uncovered line) |
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 | | static const char kRSAPrivateKeyPEM[] = |
35 | | "-----BEGIN RSA PRIVATE KEY-----\n" |
36 | | "MIIEowIBAAKCAQEArAOREUWlBXJAKZ5hABYyxnRayDZP1bJeLbPVK+npxemrhHyZ\n" |
37 | | "gjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZWwjMKu/QYcycYp5HL01goxOxuusZb\n" |
38 | | "i+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDjUDniWabXQJge8ksGXGTiFeAJ/687\n" |
39 | | "uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqeGDzyeBZ1NLIuaiOORyLGSW4duHLD\n" |
40 | | "N78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9Uw4XQFTsWugUDEY1AU4c5g11nhzHz\n" |
41 | | "Bi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHrGQIDAQABAoIBAFJTaqy/jllq8vZ4\n" |
42 | | "TKiD900wBvrns5HtSlHJTe80hqQoT+Sa1cWSxPR0eekL32Hjy9igbMzZ83uWzh7I\n" |
43 | | "mtgNODy9vRdznfgO8CfTCaBfAzQsjFpr8QikMT6EUI/LpiRL1UaGsNOlSEvnSS0Z\n" |
44 | | "b1uDzAdrjL+nsEHEDJud+K9jwSkCRifVMy7fLfaum+YKpdeEz7K2Mgm5pJ/Vg+9s\n" |
45 | | "vI2V1q7HAOI4eUVTgJNHXy5ediRJlajQHf/lNUzHKqn7iH+JRl01gt62X8roG62b\n" |
46 | | "TbFylbheqMm9awuSF2ucOcx+guuwhkPir8BEMb08j3hiK+TfwPdY0F6QH4OhiKK7\n" |
47 | | "MTqTVgECgYEA0vmmu5GOBtwRmq6gVNCHhdLDQWaxAZqQRmRbzxVhFpbv0GjbQEF7\n" |
48 | | "tttq3fjDrzDf6CE9RtZWw2BUSXVq+IXB/bXb1kgWU2xWywm+OFDk9OXQs8ui+MY7\n" |
49 | | "FiP3yuq3YJob2g5CCsVQWl2CHvWGmTLhE1ODll39t7Y1uwdcDobJN+ECgYEA0LlR\n" |
50 | | "hfMjydWmwqooU9TDjXNBmwufyYlNFTH351amYgFUDpNf35SMCP4hDosUw/zCTDpc\n" |
51 | | "+1w04BJJfkH1SNvXSOilpdaYRTYuryDvGmWC66K2KX1nLErhlhs17CwzV997nYgD\n" |
52 | | "H3OOU4HfqIKmdGbjvWlkmY+mLHyG10bbpOTbujkCgYAc68xHejSWDCT9p2KjPdLW\n" |
53 | | "LYZGuOUa6y1L+QX85Vlh118Ymsczj8Z90qZbt3Zb1b9b+vKDe255agMj7syzNOLa\n" |
54 | | "/MseHNOyq+9Z9gP1hGFekQKDIy88GzCOYG/fiT2KKJYY1kuHXnUdbiQgSlghODBS\n" |
55 | | "jehD/K6DOJ80/FVKSH/dAQKBgQDJ+apTzpZhJ2f5k6L2jDq3VEK2ACedZEm9Kt9T\n" |
56 | | "c1wKFnL6r83kkuB3i0L9ycRMavixvwBfFDjuY4POs5Dh8ip/mPFCa0hqISZHvbzi\n" |
57 | | "dDyePJO9zmXaTJPDJ42kfpkofVAnfohXFQEy+cguTk848J+MmMIKfyE0h0QMabr9\n" |
58 | | "86BUsQKBgEVgoi4RXwmtGovtMew01ORPV9MOX3v+VnsCgD4/56URKOAngiS70xEP\n" |
59 | | "ONwNbTCWuuv43HGzJoVFiAMGnQP1BAJ7gkHkjSegOGKkiw12EPUWhFcMg+GkgPhc\n" |
60 | | "pOqNt/VMBPjJ/ysHJqmLfQK9A35JV6Cmdphe+OIl28bcKhAOz8Dw\n" |
61 | | "-----END RSA PRIVATE KEY-----\n"; |
62 | | |
63 | | /* A userdata struct for session. */ |
64 | | struct session_data_struct { |
65 | | /* Pointer to the channel the session will allocate. */ |
66 | | ssh_channel channel; |
67 | | size_t auth_attempts; |
68 | | bool authenticated; |
69 | | }; |
70 | | |
71 | | static int auth_none(ssh_session session, const char *user, void *userdata) |
72 | 3.80k | { |
73 | 3.80k | struct session_data_struct *sdata = |
74 | 3.80k | (struct session_data_struct *)userdata; |
75 | | |
76 | 3.80k | (void)session; |
77 | 3.80k | (void)user; |
78 | | |
79 | 3.80k | if (sdata->auth_attempts > 0) { |
80 | 1.89k | sdata->authenticated = true; |
81 | 1.89k | } |
82 | 3.80k | sdata->auth_attempts++; |
83 | | |
84 | 3.80k | if (!sdata->authenticated) { |
85 | 1.90k | return SSH_AUTH_PARTIAL; |
86 | 1.90k | } |
87 | | |
88 | 1.89k | return SSH_AUTH_SUCCESS; |
89 | 3.80k | } |
90 | | |
91 | | static ssh_channel channel_open(ssh_session session, void *userdata) |
92 | 865 | { |
93 | 865 | struct session_data_struct *sdata = |
94 | 865 | (struct session_data_struct *)userdata; |
95 | | |
96 | 865 | sdata->channel = ssh_channel_new(session); |
97 | | |
98 | 865 | return sdata->channel; |
99 | 865 | } |
100 | | |
101 | | static int write_rsa_hostkey(const char *rsakey_path) |
102 | 6.60k | { |
103 | 6.60k | FILE *fp = NULL; |
104 | 6.60k | size_t nwritten; |
105 | | |
106 | 6.60k | fp = fopen(rsakey_path, "wb"); |
107 | 6.60k | if (fp == NULL) { |
108 | 0 | return -1; |
109 | 0 | } |
110 | | |
111 | 6.60k | nwritten = fwrite(kRSAPrivateKeyPEM, 1, strlen(kRSAPrivateKeyPEM), fp); |
112 | 6.60k | fclose(fp); |
113 | | |
114 | 6.60k | if (nwritten != strlen(kRSAPrivateKeyPEM)) { |
115 | 0 | return -1; |
116 | 0 | } |
117 | | |
118 | 6.60k | return 0; |
119 | 6.60k | } |
120 | | |
121 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
122 | 6.60k | { |
123 | 6.60k | int socket_fds[2] = {-1, -1}; |
124 | 6.60k | ssize_t nwritten; |
125 | 6.60k | bool no = false; |
126 | 6.60k | const char *env = NULL; |
127 | 6.60k | int rc; |
128 | | |
129 | | /* Our struct holding information about the session. */ |
130 | 6.60k | struct session_data_struct sdata = { |
131 | 6.60k | .channel = NULL, |
132 | 6.60k | .auth_attempts = 0, |
133 | 6.60k | .authenticated = false, |
134 | 6.60k | }; |
135 | | |
136 | 6.60k | struct ssh_server_callbacks_struct server_cb = { |
137 | 6.60k | .userdata = &sdata, |
138 | 6.60k | .auth_none_function = auth_none, |
139 | 6.60k | .channel_open_request_session_function = channel_open, |
140 | 6.60k | }; |
141 | | |
142 | | /* This is the maximum that can be handled by the socket buffer before the |
143 | | * other side will read some data. Other option would be feeding the socket |
144 | | * from different thread which would not mind if it would be blocked, but I |
145 | | * believe all the important inputs should fit into this size */ |
146 | 6.60k | if (size > 219264) { |
147 | 3 | return -1; |
148 | 3 | } |
149 | | |
150 | | /* Write SSH RSA host key to disk */ |
151 | 6.60k | rc = write_rsa_hostkey("/tmp/libssh_fuzzer_private_key"); |
152 | 6.60k | assert(rc == 0); |
153 | | |
154 | | /* Set up the socket to send data */ |
155 | 6.60k | rc = socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds); |
156 | 6.60k | assert(rc == 0); |
157 | | |
158 | 6.60k | nwritten = send(socket_fds[1], data, size, 0); |
159 | 6.60k | assert((size_t)nwritten == size); |
160 | | |
161 | 6.60k | rc = shutdown(socket_fds[1], SHUT_WR); |
162 | 6.60k | assert(rc == 0); |
163 | | |
164 | | /* Set up the libssh server */ |
165 | 6.60k | ssh_bind sshbind = ssh_bind_new(); |
166 | 6.60k | assert(sshbind != NULL); |
167 | | |
168 | 6.60k | ssh_session session = ssh_new(); |
169 | 6.60k | assert(session != NULL); |
170 | | |
171 | | |
172 | 6.60k | env = getenv("LIBSSH_VERBOSITY"); |
173 | 6.60k | if (env != NULL && strlen(env) > 0) { |
174 | 0 | rc = ssh_bind_options_set(sshbind, |
175 | 0 | SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, |
176 | 0 | env); |
177 | 0 | assert(rc == 0); |
178 | 0 | } |
179 | 6.60k | rc = ssh_bind_options_set(sshbind, |
180 | 6.60k | SSH_BIND_OPTIONS_HOSTKEY, |
181 | 6.60k | "/tmp/libssh_fuzzer_private_key"); |
182 | 6.60k | assert(rc == 0); |
183 | 6.60k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_C_S, "none"); |
184 | 6.60k | assert(rc == 0); |
185 | 6.60k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_S_C, "none"); |
186 | 6.60k | assert(rc == 0); |
187 | 6.60k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_C_S, "none"); |
188 | 6.60k | assert(rc == 0); |
189 | 6.60k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_S_C, "none"); |
190 | 6.60k | assert(rc == 0); |
191 | 6.60k | rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_PROCESS_CONFIG, &no); |
192 | 6.60k | assert(rc == 0); |
193 | | |
194 | 6.60k | ssh_set_auth_methods(session, SSH_AUTH_METHOD_NONE); |
195 | | |
196 | 6.60k | ssh_callbacks_init(&server_cb); |
197 | 6.60k | ssh_set_server_callbacks(session, &server_cb); |
198 | | |
199 | 6.60k | rc = ssh_bind_accept_fd(sshbind, session, socket_fds[0]); |
200 | 6.60k | assert(rc == SSH_OK); |
201 | | |
202 | 6.60k | ssh_event event = ssh_event_new(); |
203 | 6.60k | assert(event != NULL); |
204 | | |
205 | 6.60k | if (ssh_handle_key_exchange(session) == SSH_OK) { |
206 | 2.61k | ssh_event_add_session(event, session); |
207 | | |
208 | 2.61k | size_t n = 0; |
209 | 4.16k | while (sdata.authenticated == false || sdata.channel == NULL) { |
210 | 4.16k | if (sdata.auth_attempts >= 3 || n >= 100) { |
211 | 0 | break; |
212 | 0 | } |
213 | | |
214 | 4.16k | if (ssh_event_dopoll(event, 100) == SSH_ERROR) { |
215 | 2.61k | break; |
216 | 2.61k | } |
217 | | |
218 | 1.54k | n++; |
219 | 1.54k | } |
220 | 2.61k | } |
221 | | |
222 | 6.60k | ssh_event_free(event); |
223 | | |
224 | 6.60k | close(socket_fds[0]); |
225 | 6.60k | close(socket_fds[1]); |
226 | | |
227 | 6.60k | ssh_disconnect(session); |
228 | 6.60k | ssh_free(session); |
229 | 6.60k | ssh_bind_free(sshbind); |
230 | | |
231 | 6.60k | return 0; |
232 | 6.60k | } |