Coverage Report

Created: 2026-10-10 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
43
{
80
43
    (void)argc;
81
82
43
    nalloc_init(*argv[0]);
83
84
43
    ssh_init();
85
86
43
    atexit(_fuzz_finalize);
87
88
43
    return 0;
89
43
}
90
91
static int auth_none(ssh_session session, const char *user, void *userdata)
92
5.19k
{
93
5.19k
    struct session_data_struct *sdata =
94
5.19k
        (struct session_data_struct *)userdata;
95
96
5.19k
    (void)session;
97
5.19k
    (void)user;
98
99
5.19k
    if (sdata->auth_attempts > 0) {
100
2.59k
        sdata->authenticated = true;
101
2.59k
    }
102
5.19k
    sdata->auth_attempts++;
103
104
5.19k
    if (!sdata->authenticated) {
105
2.60k
        return SSH_AUTH_PARTIAL;
106
2.60k
    }
107
108
2.59k
    return SSH_AUTH_SUCCESS;
109
5.19k
}
110
111
static ssh_channel channel_open(ssh_session session, void *userdata)
112
1.70k
{
113
1.70k
    struct session_data_struct *sdata =
114
1.70k
        (struct session_data_struct *)userdata;
115
116
1.70k
    sdata->channel = ssh_channel_new(session);
117
118
1.70k
    return sdata->channel;
119
1.70k
}
120
121
static int write_rsa_hostkey(const char *rsakey_path)
122
9.35k
{
123
9.35k
    FILE *fp = NULL;
124
9.35k
    size_t nwritten;
125
126
9.35k
    fp = fopen(rsakey_path, "wb");
127
9.35k
    if (fp == NULL) {
128
0
        return -1;
129
0
    }
130
131
9.35k
    nwritten = fwrite(kRSAPrivateKeyPEM, 1, strlen(kRSAPrivateKeyPEM), fp);
132
9.35k
    fclose(fp);
133
134
9.35k
    if (nwritten != strlen(kRSAPrivateKeyPEM)) {
135
0
        return -1;
136
0
    }
137
138
9.35k
    return 0;
139
9.35k
}
140
141
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
142
9.35k
{
143
9.35k
    int socket_fds[2] = {-1, -1};
144
9.35k
    ssize_t nwritten;
145
9.35k
    bool no = false;
146
9.35k
    const char *env = NULL;
147
9.35k
    int rc;
148
9.35k
    ssh_bind sshbind = NULL;
149
9.35k
    ssh_session session = NULL;
150
9.35k
    ssh_event event = NULL;
151
152
    /* Our struct holding information about the session. */
153
9.35k
    struct session_data_struct sdata = {
154
9.35k
        .channel       = NULL,
155
9.35k
        .auth_attempts = 0,
156
9.35k
        .authenticated = false,
157
9.35k
    };
158
159
9.35k
    struct ssh_server_callbacks_struct server_cb = {
160
9.35k
        .userdata = &sdata,
161
9.35k
        .auth_none_function = auth_none,
162
9.35k
        .channel_open_request_session_function = channel_open,
163
9.35k
    };
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
9.35k
    if (size > 219264) {
170
5
        return -1;
171
5
    }
172
173
    /* Write SSH RSA host key to disk */
174
9.35k
    rc = write_rsa_hostkey("/tmp/libssh_fuzzer_private_key");
175
9.35k
    assert(rc == 0);
176
177
    /* Set up the socket to send data */
178
9.35k
    rc = socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
179
9.35k
    assert(rc == 0);
180
181
9.35k
    nwritten = send(socket_fds[1], data, size, 0);
182
9.35k
    assert((size_t)nwritten == size);
183
184
9.35k
    rc = shutdown(socket_fds[1], SHUT_WR);
185
9.35k
    assert(rc == 0);
186
187
9.35k
    assert(nalloc_start(data, size) > 0);
188
189
    /* Set up the libssh server */
190
9.35k
    sshbind = ssh_bind_new();
191
9.35k
    if (sshbind == NULL) {
192
293
        goto out;
193
293
    }
194
195
9.05k
    session = ssh_new();
196
9.05k
    if (session == NULL) {
197
279
        goto out;
198
279
    }
199
200
8.77k
    env = getenv("LIBSSH_VERBOSITY");
201
8.77k
    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
8.77k
    rc = ssh_bind_options_set(sshbind,
210
8.77k
                              SSH_BIND_OPTIONS_HOSTKEY,
211
8.77k
                              "/tmp/libssh_fuzzer_private_key");
212
8.77k
    if (rc != SSH_OK) {
213
699
        goto out;
214
699
    }
215
8.08k
    rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_C_S, "none");
216
8.08k
    if (rc != SSH_OK) {
217
0
        goto out;
218
0
    }
219
8.08k
    rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_S_C, "none");
220
8.08k
    if (rc != SSH_OK) {
221
0
        goto out;
222
0
    }
223
8.08k
    rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_C_S, "none");
224
8.08k
    if (rc != SSH_OK) {
225
0
        goto out;
226
0
    }
227
8.08k
    rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_S_C, "none");
228
8.08k
    if (rc != SSH_OK) {
229
0
        goto out;
230
0
    }
231
8.08k
    rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_PROCESS_CONFIG, &no);
232
8.08k
    if (rc != SSH_OK) {
233
0
        goto out;
234
0
    }
235
236
8.08k
    ssh_set_auth_methods(session, SSH_AUTH_METHOD_NONE);
237
238
8.08k
    ssh_callbacks_init(&server_cb);
239
8.08k
    ssh_set_server_callbacks(session, &server_cb);
240
241
8.08k
    rc = ssh_bind_accept_fd(sshbind, session, socket_fds[0]);
242
8.08k
    if (rc != SSH_OK) {
243
0
        goto out;
244
0
    }
245
246
8.08k
    event = ssh_event_new();
247
8.08k
    if (event == NULL) {
248
0
        goto out;
249
0
    }
250
251
8.08k
    if (ssh_handle_key_exchange(session) == SSH_OK) {
252
5.62k
        ssh_event_add_session(event, session);
253
254
5.62k
        size_t n = 0;
255
11.0k
        while (sdata.authenticated == false || sdata.channel == NULL) {
256
9.93k
            if (sdata.auth_attempts >= 3 || n >= 100) {
257
15
                break;
258
15
            }
259
260
9.91k
            if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
261
4.52k
                break;
262
4.52k
            }
263
264
5.39k
            n++;
265
5.39k
        }
266
5.62k
    }
267
268
9.35k
out:
269
9.35k
    nalloc_end();
270
271
9.35k
    ssh_event_free(event);
272
273
9.35k
    close(socket_fds[0]);
274
9.35k
    close(socket_fds[1]);
275
276
9.35k
    ssh_disconnect(session);
277
9.35k
    ssh_free(session);
278
9.35k
    ssh_bind_free(sshbind);
279
280
9.35k
    return 0;
281
8.08k
}