Coverage Report

Created: 2025-07-12 06:56

/src/libssh/src/gzip.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * gzip.c - hooks for compression of packets
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2003      by Aris Adamantiadis
7
 * Copyright (c) 2009      by Andreas Schneider <asn@cryptomilk.org>
8
 *
9
 * The SSH Library is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation; either version 2.1 of the License, or (at your
12
 * option) any later version.
13
 *
14
 * The SSH Library is distributed in the hope that it will be useful, but
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17
 * License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with the SSH Library; see the file COPYING.  If not, write to
21
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22
 * MA 02111-1307, USA.
23
 */
24
25
#include "config.h"
26
27
#include <stdlib.h>
28
#include <string.h>
29
30
#include "libssh/buffer.h"
31
#include "libssh/crypto.h"
32
#include "libssh/priv.h"
33
#include "libssh/session.h"
34
35
#ifdef WITH_ZLIB
36
#include <zlib.h>
37
38
#ifndef BLOCKSIZE
39
0
#define BLOCKSIZE 4092
40
#endif
41
42
static z_stream *
43
initcompress(ssh_session session, int level)
44
0
{
45
0
    z_stream *stream = NULL;
46
0
    int status;
47
48
0
    stream = calloc(1, sizeof(z_stream));
49
0
    if (stream == NULL) {
50
0
        return NULL;
51
0
    }
52
53
0
    status = deflateInit(stream, level);
54
0
    if (status != Z_OK) {
55
0
        deflateEnd(stream);
56
0
        SAFE_FREE(stream);
57
0
        ssh_set_error(session,
58
0
                      SSH_FATAL,
59
0
                      "status %d initialising zlib deflate",
60
0
                      status);
61
0
        return NULL;
62
0
    }
63
64
0
    return stream;
65
0
}
66
67
static ssh_buffer
68
gzip_compress(ssh_session session, ssh_buffer source, int level)
69
0
{
70
0
    struct ssh_crypto_struct *crypto = NULL;
71
0
    z_stream *zout = NULL;
72
0
    void *in_ptr = ssh_buffer_get(source);
73
0
    uint32_t in_size = ssh_buffer_get_len(source);
74
0
    ssh_buffer dest = NULL;
75
0
    unsigned char out_buf[BLOCKSIZE] = {0};
76
0
    uint32_t len;
77
0
    int status;
78
79
0
    crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
80
0
    if (crypto == NULL) {
81
0
        return NULL;
82
0
    }
83
0
    zout = crypto->compress_out_ctx;
84
0
    if (zout == NULL) {
85
0
        zout = crypto->compress_out_ctx = initcompress(session, level);
86
0
        if (zout == NULL) {
87
0
            return NULL;
88
0
        }
89
0
    }
90
91
0
    dest = ssh_buffer_new();
92
0
    if (dest == NULL) {
93
0
        return NULL;
94
0
    }
95
96
0
    zout->next_out = out_buf;
97
0
    zout->next_in = in_ptr;
98
0
    zout->avail_in = in_size;
99
0
    do {
100
0
        zout->avail_out = BLOCKSIZE;
101
0
        status = deflate(zout, Z_PARTIAL_FLUSH);
102
0
        if (status != Z_OK) {
103
0
            SSH_BUFFER_FREE(dest);
104
0
            ssh_set_error(session,
105
0
                          SSH_FATAL,
106
0
                          "status %d deflating zlib packet",
107
0
                          status);
108
0
            return NULL;
109
0
        }
110
0
        len = BLOCKSIZE - zout->avail_out;
111
0
        if (ssh_buffer_add_data(dest, out_buf, len) < 0) {
112
0
            SSH_BUFFER_FREE(dest);
113
0
            return NULL;
114
0
        }
115
0
        zout->next_out = out_buf;
116
0
    } while (zout->avail_out == 0);
117
118
0
    return dest;
119
0
}
120
121
int
122
compress_buffer(ssh_session session, ssh_buffer buf)
123
0
{
124
0
    ssh_buffer dest = NULL;
125
0
    int rv;
126
127
0
    dest = gzip_compress(session, buf, session->opts.compressionlevel);
128
0
    if (dest == NULL) {
129
0
        return -1;
130
0
    }
131
132
0
    if (ssh_buffer_reinit(buf) < 0) {
133
0
        SSH_BUFFER_FREE(dest);
134
0
        return -1;
135
0
    }
136
137
0
    rv = ssh_buffer_add_data(buf,
138
0
                             ssh_buffer_get(dest),
139
0
                             ssh_buffer_get_len(dest));
140
0
    if (rv < 0) {
141
0
        SSH_BUFFER_FREE(dest);
142
0
        return -1;
143
0
    }
144
145
0
    SSH_BUFFER_FREE(dest);
146
0
    return 0;
147
0
}
148
149
/* decompression */
150
151
static z_stream *
152
initdecompress(ssh_session session)
153
0
{
154
0
    z_stream *stream = NULL;
155
0
    int status;
156
157
0
    stream = calloc(1, sizeof(z_stream));
158
0
    if (stream == NULL) {
159
0
        return NULL;
160
0
    }
161
162
0
    status = inflateInit(stream);
163
0
    if (status != Z_OK) {
164
0
        inflateEnd(stream);
165
0
        SAFE_FREE(stream);
166
0
        ssh_set_error(session,
167
0
                      SSH_FATAL,
168
0
                      "Status = %d initiating inflate context!",
169
0
                      status);
170
0
        return NULL;
171
0
    }
172
173
0
    return stream;
174
0
}
175
176
static ssh_buffer
177
gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen)
178
0
{
179
0
    struct ssh_crypto_struct *crypto = NULL;
180
0
    z_stream *zin = NULL;
181
0
    void *in_ptr = ssh_buffer_get(source);
182
0
    uint32_t in_size = ssh_buffer_get_len(source);
183
0
    unsigned char out_buf[BLOCKSIZE] = {0};
184
0
    ssh_buffer dest = NULL;
185
0
    uint32_t len;
186
0
    int status;
187
188
0
    crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
189
0
    if (crypto == NULL) {
190
0
        return NULL;
191
0
    }
192
193
0
    zin = crypto->compress_in_ctx;
194
0
    if (zin == NULL) {
195
0
        zin = crypto->compress_in_ctx = initdecompress(session);
196
0
        if (zin == NULL) {
197
0
            return NULL;
198
0
        }
199
0
    }
200
201
0
    dest = ssh_buffer_new();
202
0
    if (dest == NULL) {
203
0
        return NULL;
204
0
    }
205
206
0
    zin->next_out = out_buf;
207
0
    zin->next_in = in_ptr;
208
0
    zin->avail_in = in_size;
209
210
0
    do {
211
0
        zin->avail_out = BLOCKSIZE;
212
0
        status = inflate(zin, Z_PARTIAL_FLUSH);
213
0
        if (status != Z_OK && status != Z_BUF_ERROR) {
214
0
            ssh_set_error(session,
215
0
                          SSH_FATAL,
216
0
                          "status %d inflating zlib packet",
217
0
                          status);
218
0
            SSH_BUFFER_FREE(dest);
219
0
            return NULL;
220
0
        }
221
222
0
        len = BLOCKSIZE - zin->avail_out;
223
0
        if (ssh_buffer_add_data(dest, out_buf, len) < 0) {
224
0
            SSH_BUFFER_FREE(dest);
225
0
            return NULL;
226
0
        }
227
0
        if (ssh_buffer_get_len(dest) > maxlen) {
228
            /* Size of packet exceeded, avoid a denial of service attack */
229
0
            SSH_BUFFER_FREE(dest);
230
0
            return NULL;
231
0
        }
232
0
        zin->next_out = out_buf;
233
0
    } while (zin->avail_out == 0);
234
235
0
    return dest;
236
0
}
237
238
int
239
decompress_buffer(ssh_session session, ssh_buffer buf, size_t maxlen)
240
0
{
241
0
    ssh_buffer dest = NULL;
242
0
    int rv;
243
244
0
    dest = gzip_decompress(session, buf, maxlen);
245
0
    if (dest == NULL) {
246
0
        return -1;
247
0
    }
248
249
0
    if (ssh_buffer_reinit(buf) < 0) {
250
0
        SSH_BUFFER_FREE(dest);
251
0
        return -1;
252
0
    }
253
254
0
    rv = ssh_buffer_add_data(buf,
255
0
                             ssh_buffer_get(dest),
256
0
                             ssh_buffer_get_len(dest));
257
0
    if (rv < 0) {
258
0
        SSH_BUFFER_FREE(dest);
259
0
        return -1;
260
0
    }
261
262
0
    SSH_BUFFER_FREE(dest);
263
0
    return 0;
264
0
}
265
266
void
267
compress_cleanup(struct ssh_crypto_struct *crypto)
268
0
{
269
0
    if (crypto->compress_out_ctx) {
270
0
        deflateEnd(crypto->compress_out_ctx);
271
0
    }
272
0
    SAFE_FREE(crypto->compress_out_ctx);
273
274
0
    if (crypto->compress_in_ctx) {
275
0
        inflateEnd(crypto->compress_in_ctx);
276
0
    }
277
0
    SAFE_FREE(crypto->compress_in_ctx);
278
0
}
279
#else /* WITH_ZLIB */
280
281
int
282
compress_buffer(UNUSED_PARAM(ssh_session session), UNUSED_PARAM(ssh_buffer buf))
283
{
284
    /* without zlib compiled in, this should never happen */
285
    return -1;
286
}
287
int
288
decompress_buffer(UNUSED_PARAM(ssh_session session),
289
                  UNUSED_PARAM(ssh_buffer buf),
290
                  UNUSED_PARAM(size_t maxlen))
291
{
292
    /* without zlib compiled in, this should never happen */
293
    return -1;
294
}
295
296
void
297
compress_cleanup(UNUSED_PARAM(struct ssh_crypto_struct *crypto))
298
{
299
    /* no-op */
300
}
301
302
#endif /* WITH_ZLIB */