/src/libssh/tests/fuzz/ssh_client_fuzzer.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2019 Andreas Schneider <asn@cryptomilk.org> |
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 | | #include <assert.h> |
18 | | #include <stdio.h> |
19 | | #include <stdlib.h> |
20 | | #include <signal.h> |
21 | | #include <stdbool.h> |
22 | | |
23 | | #define LIBSSH_STATIC 1 |
24 | | #include <libssh/libssh.h> |
25 | | #include <libssh/callbacks.h> |
26 | | |
27 | | #include "nallocinc.c" |
28 | | |
29 | | static void _fuzz_finalize(void) |
30 | 4 | { |
31 | 4 | ssh_finalize(); |
32 | 4 | } |
33 | | |
34 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
35 | 32 | { |
36 | 32 | (void)argc; |
37 | | |
38 | 32 | nalloc_init(*argv[0]); |
39 | | |
40 | 32 | ssh_init(); |
41 | | |
42 | 32 | atexit(_fuzz_finalize); |
43 | | |
44 | 32 | return 0; |
45 | 32 | } |
46 | | |
47 | | static int auth_callback(const char *prompt, |
48 | | char *buf, |
49 | | size_t len, |
50 | | int echo, |
51 | | int verify, |
52 | | void *userdata) |
53 | 0 | { |
54 | 0 | (void)prompt; /* unused */ |
55 | 0 | (void)echo; /* unused */ |
56 | 0 | (void)verify; /* unused */ |
57 | 0 | (void)userdata; /* unused */ |
58 | |
|
59 | 0 | snprintf(buf, len, "secret"); |
60 | |
|
61 | 0 | return 0; |
62 | 0 | } |
63 | | |
64 | | struct ssh_callbacks_struct cb = { |
65 | | .userdata = NULL, |
66 | | .auth_function = auth_callback, |
67 | | }; |
68 | | |
69 | | static void select_loop(ssh_session session, ssh_channel channel) |
70 | 0 | { |
71 | 0 | ssh_connector connector_in, connector_out, connector_err; |
72 | |
|
73 | 0 | ssh_event event = ssh_event_new(); |
74 | | |
75 | | /* stdin */ |
76 | 0 | connector_in = ssh_connector_new(session); |
77 | 0 | ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDINOUT); |
78 | 0 | ssh_connector_set_in_fd(connector_in, 0); |
79 | 0 | ssh_event_add_connector(event, connector_in); |
80 | | |
81 | | /* stdout */ |
82 | 0 | connector_out = ssh_connector_new(session); |
83 | 0 | ssh_connector_set_out_fd(connector_out, 1); |
84 | 0 | ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDINOUT); |
85 | 0 | ssh_event_add_connector(event, connector_out); |
86 | | |
87 | | /* stderr */ |
88 | 0 | connector_err = ssh_connector_new(session); |
89 | 0 | ssh_connector_set_out_fd(connector_err, 2); |
90 | 0 | ssh_connector_set_in_channel(connector_err, channel, SSH_CONNECTOR_STDERR); |
91 | 0 | ssh_event_add_connector(event, connector_err); |
92 | |
|
93 | 0 | while (ssh_channel_is_open(channel)) { |
94 | 0 | ssh_event_dopoll(event, 60000); |
95 | 0 | } |
96 | 0 | ssh_event_remove_connector(event, connector_in); |
97 | 0 | ssh_event_remove_connector(event, connector_out); |
98 | 0 | ssh_event_remove_connector(event, connector_err); |
99 | |
|
100 | 0 | ssh_connector_free(connector_in); |
101 | 0 | ssh_connector_free(connector_out); |
102 | 0 | ssh_connector_free(connector_err); |
103 | |
|
104 | 0 | ssh_event_free(event); |
105 | 0 | } |
106 | | |
107 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
108 | 3.72k | { |
109 | 3.72k | ssh_session session = NULL; |
110 | 3.72k | ssh_channel channel = NULL; |
111 | 3.72k | const char *env = NULL; |
112 | 3.72k | int socket_fds[2] = {-1, -1}; |
113 | 3.72k | ssize_t nwritten; |
114 | 3.72k | bool no = false; |
115 | 3.72k | int rc; |
116 | 3.72k | long timeout = 1; /* use short timeout to avoid timeouts during fuzzing */ |
117 | | |
118 | | /* This is the maximum that can be handled by the socket buffer before the |
119 | | * other side will read some data. Other option would be feeding the socket |
120 | | * from different thread which would not mind if it would be blocked, but I |
121 | | * believe all the important inputs should fit into this size */ |
122 | 3.72k | if (size > 219264) { |
123 | 5 | return -1; |
124 | 5 | } |
125 | | |
126 | | /* Set up the socket to send data */ |
127 | 3.72k | rc = socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds); |
128 | 3.72k | assert(rc == 0); |
129 | | |
130 | 3.72k | nwritten = send(socket_fds[1], data, size, 0); |
131 | 3.72k | assert((size_t)nwritten == size); |
132 | | |
133 | 3.72k | rc = shutdown(socket_fds[1], SHUT_WR); |
134 | 3.72k | assert(rc == 0); |
135 | | |
136 | 3.72k | assert(nalloc_start(data, size) > 0); |
137 | | |
138 | 3.72k | session = ssh_new(); |
139 | 3.72k | if (session == NULL) { |
140 | 182 | goto out; |
141 | 182 | } |
142 | | |
143 | 3.54k | env = getenv("LIBSSH_VERBOSITY"); |
144 | 3.54k | if (env != NULL && strlen(env) > 0) { |
145 | 0 | ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY_STR, env); |
146 | 0 | } |
147 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_FD, &socket_fds[0]); |
148 | 3.54k | if (rc != SSH_OK) { |
149 | 0 | goto out; |
150 | 0 | } |
151 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_HOST, "127.0.0.1"); |
152 | 3.54k | if (rc != SSH_OK) { |
153 | 0 | goto out; |
154 | 0 | } |
155 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_USER, "alice"); |
156 | 3.54k | if (rc != SSH_OK) { |
157 | 0 | goto out; |
158 | 0 | } |
159 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, "none"); |
160 | 3.54k | if (rc != SSH_OK) { |
161 | 0 | goto out; |
162 | 0 | } |
163 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, "none"); |
164 | 3.54k | if (rc != SSH_OK) { |
165 | 0 | goto out; |
166 | 0 | } |
167 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, "none"); |
168 | 3.54k | if (rc != SSH_OK) { |
169 | 0 | goto out; |
170 | 0 | } |
171 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, "none"); |
172 | 3.54k | if (rc != SSH_OK) { |
173 | 0 | goto out; |
174 | 0 | } |
175 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &no); |
176 | 3.54k | if (rc != SSH_OK) { |
177 | 0 | goto out; |
178 | 0 | } |
179 | 3.54k | rc = ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &timeout); |
180 | 3.54k | if (rc != SSH_OK) { |
181 | 0 | goto out; |
182 | 0 | } |
183 | | |
184 | 3.54k | ssh_callbacks_init(&cb); |
185 | 3.54k | ssh_set_callbacks(session, &cb); |
186 | | |
187 | 3.54k | rc = ssh_connect(session); |
188 | 3.54k | if (rc != SSH_OK) { |
189 | 3.53k | goto out; |
190 | 3.53k | } |
191 | | |
192 | 6 | rc = ssh_userauth_none(session, NULL); |
193 | 6 | if (rc != SSH_OK) { |
194 | 6 | goto out; |
195 | 6 | } |
196 | | |
197 | 0 | channel = ssh_channel_new(session); |
198 | 0 | if (channel == NULL) { |
199 | 0 | goto out; |
200 | 0 | } |
201 | | |
202 | 0 | rc = ssh_channel_open_session(channel); |
203 | 0 | if (rc != SSH_OK) { |
204 | 0 | goto out; |
205 | 0 | } |
206 | | |
207 | 0 | rc = ssh_channel_request_exec(channel, "ls"); |
208 | 0 | if (rc != SSH_OK) { |
209 | 0 | goto out; |
210 | 0 | } |
211 | | |
212 | 0 | select_loop(session, channel); |
213 | |
|
214 | 3.72k | out: |
215 | 3.72k | ssh_channel_free(channel); |
216 | 3.72k | ssh_disconnect(session); |
217 | 3.72k | ssh_free(session); |
218 | | |
219 | 3.72k | close(socket_fds[0]); |
220 | 3.72k | close(socket_fds[1]); |
221 | | |
222 | 3.72k | nalloc_end(); |
223 | 3.72k | return 0; |
224 | 0 | } |