1 /* Copyright (c) 2004-2007, 2019, Sara Golemon <sarag@libssh2.org>
2  * Copyright (c) 2010-2014, Daniel Stenberg <daniel@haxx.se>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms,
6  * with or without modification, are permitted provided
7  * that the following conditions are met:
8  *
9  *   Redistributions of source code must retain the above
10  *   copyright notice, this list of conditions and the
11  *   following disclaimer.
12  *
13  *   Redistributions in binary form must reproduce the above
14  *   copyright notice, this list of conditions and the following
15  *   disclaimer in the documentation and/or other materials
16  *   provided with the distribution.
17  *
18  *   Neither the name of the copyright holder nor the names
19  *   of any other contributors may be used to endorse or
20  *   promote products derived from this software without
21  *   specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
35  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  */
38 
39 #include "libssh2_priv.h"
40 #ifdef LIBSSH2_HAVE_ZLIB
41 # include <zlib.h>
42 #endif
43 
44 #include "comp.h"
45 
46 /* ********
47  * none *
48  ******** */
49 
50 /*
51  * comp_method_none_comp
52  *
53  * Minimalist compression: Absolutely none
54  */
55 static int
comp_method_none_comp(LIBSSH2_SESSION * session,unsigned char * dest,size_t * dest_len,const unsigned char * src,size_t src_len,void ** abstract)56 comp_method_none_comp(LIBSSH2_SESSION *session,
57                       unsigned char *dest,
58                       size_t *dest_len,
59                       const unsigned char *src,
60                       size_t src_len,
61                       void **abstract)
62 {
63     (void) session;
64     (void) abstract;
65     (void) dest;
66     (void) dest_len;
67     (void) src;
68     (void) src_len;
69 
70     return 0;
71 }
72 
73 /*
74  * comp_method_none_decomp
75  *
76  * Minimalist decompression: Absolutely none
77  */
78 static int
comp_method_none_decomp(LIBSSH2_SESSION * session,unsigned char ** dest,size_t * dest_len,size_t payload_limit,const unsigned char * src,size_t src_len,void ** abstract)79 comp_method_none_decomp(LIBSSH2_SESSION * session,
80                         unsigned char **dest,
81                         size_t *dest_len,
82                         size_t payload_limit,
83                         const unsigned char *src,
84                         size_t src_len, void **abstract)
85 {
86     (void) session;
87     (void) payload_limit;
88     (void) abstract;
89     *dest = (unsigned char *) src;
90     *dest_len = src_len;
91     return 0;
92 }
93 
94 
95 
96 static const LIBSSH2_COMP_METHOD comp_method_none = {
97     "none",
98     0, /* not really compressing */
99     0, /* isn't used in userauth, go figure */
100     NULL,
101     comp_method_none_comp,
102     comp_method_none_decomp,
103     NULL
104 };
105 
106 #ifdef LIBSSH2_HAVE_ZLIB
107 /* ********
108  * zlib *
109  ******** */
110 
111 /* Memory management wrappers
112  * Yes, I realize we're doing a callback to a callback,
113  * Deal...
114  */
115 
116 static voidpf
comp_method_zlib_alloc(voidpf opaque,uInt items,uInt size)117 comp_method_zlib_alloc(voidpf opaque, uInt items, uInt size)
118 {
119     LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque;
120 
121     return (voidpf) LIBSSH2_ALLOC(session, items * size);
122 }
123 
124 static void
comp_method_zlib_free(voidpf opaque,voidpf address)125 comp_method_zlib_free(voidpf opaque, voidpf address)
126 {
127     LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque;
128 
129     LIBSSH2_FREE(session, address);
130 }
131 
132 
133 
134 /* libssh2_comp_method_zlib_init
135  * All your bandwidth are belong to us (so save some)
136  */
137 static int
comp_method_zlib_init(LIBSSH2_SESSION * session,int compr,void ** abstract)138 comp_method_zlib_init(LIBSSH2_SESSION * session, int compr,
139                       void **abstract)
140 {
141     z_stream *strm;
142     int status;
143 
144     strm = LIBSSH2_CALLOC(session, sizeof(z_stream));
145     if(!strm) {
146         return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
147                               "Unable to allocate memory for "
148                               "zlib compression/decompression");
149     }
150 
151     strm->opaque = (voidpf) session;
152     strm->zalloc = (alloc_func) comp_method_zlib_alloc;
153     strm->zfree = (free_func) comp_method_zlib_free;
154     if(compr) {
155         /* deflate */
156         status = deflateInit(strm, Z_DEFAULT_COMPRESSION);
157     }
158     else {
159         /* inflate */
160         status = inflateInit(strm);
161     }
162 
163     if(status != Z_OK) {
164         LIBSSH2_FREE(session, strm);
165         _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
166                        "unhandled zlib error %d", status);
167         return LIBSSH2_ERROR_COMPRESS;
168     }
169     *abstract = strm;
170 
171     return LIBSSH2_ERROR_NONE;
172 }
173 
174 /*
175  * libssh2_comp_method_zlib_comp
176  *
177  * Compresses source to destination. Without allocation.
178  */
179 static int
comp_method_zlib_comp(LIBSSH2_SESSION * session,unsigned char * dest,size_t * dest_len,const unsigned char * src,size_t src_len,void ** abstract)180 comp_method_zlib_comp(LIBSSH2_SESSION *session,
181                       unsigned char *dest,
182 
183                       /* dest_len is a pointer to allow this function to
184                          update it with the final actual size used */
185                       size_t *dest_len,
186                       const unsigned char *src,
187                       size_t src_len,
188                       void **abstract)
189 {
190     z_stream *strm = *abstract;
191     int out_maxlen = *dest_len;
192     int status;
193 
194     strm->next_in = (unsigned char *) src;
195     strm->avail_in = src_len;
196     strm->next_out = dest;
197     strm->avail_out = out_maxlen;
198 
199     status = deflate(strm, Z_PARTIAL_FLUSH);
200 
201     if((status == Z_OK) && (strm->avail_out > 0)) {
202         *dest_len = out_maxlen - strm->avail_out;
203         return 0;
204     }
205 
206     _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
207                    "unhandled zlib compression error %d, avail_out",
208                    status, strm->avail_out);
209     return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compression failure");
210 }
211 
212 /*
213  * libssh2_comp_method_zlib_decomp
214  *
215  * Decompresses source to destination. Allocates the output memory.
216  */
217 static int
comp_method_zlib_decomp(LIBSSH2_SESSION * session,unsigned char ** dest,size_t * dest_len,size_t payload_limit,const unsigned char * src,size_t src_len,void ** abstract)218 comp_method_zlib_decomp(LIBSSH2_SESSION * session,
219                         unsigned char **dest,
220                         size_t *dest_len,
221                         size_t payload_limit,
222                         const unsigned char *src,
223                         size_t src_len, void **abstract)
224 {
225     z_stream *strm = *abstract;
226     /* A short-term alloc of a full data chunk is better than a series of
227        reallocs */
228     char *out;
229     size_t out_maxlen = src_len;
230 
231     if(src_len <= SIZE_MAX / 4)
232         out_maxlen = src_len * 4;
233     else
234         out_maxlen = payload_limit;
235 
236     /* If strm is null, then we have not yet been initialized. */
237     if(strm == NULL)
238         return _libssh2_error(session, LIBSSH2_ERROR_COMPRESS,
239                               "decompression uninitialized");;
240 
241     /* In practice they never come smaller than this */
242     if(out_maxlen < 25)
243         out_maxlen = 25;
244 
245     if(out_maxlen > payload_limit)
246         out_maxlen = payload_limit;
247 
248     strm->next_in = (unsigned char *) src;
249     strm->avail_in = src_len;
250     strm->next_out = (unsigned char *) LIBSSH2_ALLOC(session, out_maxlen);
251     out = (char *) strm->next_out;
252     strm->avail_out = out_maxlen;
253     if(!strm->next_out)
254         return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
255                               "Unable to allocate decompression buffer");
256 
257     /* Loop until it's all inflated or hit error */
258     for(;;) {
259         int status;
260         size_t out_ofs;
261         char *newout;
262 
263         status = inflate(strm, Z_PARTIAL_FLUSH);
264 
265         if(status == Z_OK) {
266             if(strm->avail_out > 0)
267                 /* status is OK and the output buffer has not been exhausted
268                    so we're done */
269                 break;
270         }
271         else if(status == Z_BUF_ERROR) {
272             /* the input data has been exhausted so we are done */
273             break;
274         }
275         else {
276             /* error state */
277             LIBSSH2_FREE(session, out);
278             _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
279                            "unhandled zlib error %d", status);
280             return _libssh2_error(session, LIBSSH2_ERROR_ZLIB,
281                                   "decompression failure");
282         }
283 
284         if(out_maxlen > payload_limit || out_maxlen > SIZE_MAX / 2) {
285             LIBSSH2_FREE(session, out);
286             return _libssh2_error(session, LIBSSH2_ERROR_ZLIB,
287                                   "Excessive growth in decompression phase");
288         }
289 
290         /* If we get here we need to grow the output buffer and try again */
291         out_ofs = out_maxlen - strm->avail_out;
292         out_maxlen *= 2;
293         newout = LIBSSH2_REALLOC(session, out, out_maxlen);
294         if(!newout) {
295             LIBSSH2_FREE(session, out);
296             return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
297                                   "Unable to expand decompression buffer");
298         }
299         out = newout;
300         strm->next_out = (unsigned char *) out + out_ofs;
301         strm->avail_out = out_maxlen - out_ofs;
302     }
303 
304     *dest = (unsigned char *) out;
305     *dest_len = out_maxlen - strm->avail_out;
306 
307     return 0;
308 }
309 
310 
311 /* libssh2_comp_method_zlib_dtor
312  * All done, no more compression for you
313  */
314 static int
comp_method_zlib_dtor(LIBSSH2_SESSION * session,int compr,void ** abstract)315 comp_method_zlib_dtor(LIBSSH2_SESSION *session, int compr, void **abstract)
316 {
317     z_stream *strm = *abstract;
318 
319     if(strm) {
320         if(compr)
321             deflateEnd(strm);
322         else
323             inflateEnd(strm);
324         LIBSSH2_FREE(session, strm);
325     }
326 
327     *abstract = NULL;
328     return 0;
329 }
330 
331 static const LIBSSH2_COMP_METHOD comp_method_zlib = {
332     "zlib",
333     1, /* yes, this compresses */
334     1, /* do compression during userauth */
335     comp_method_zlib_init,
336     comp_method_zlib_comp,
337     comp_method_zlib_decomp,
338     comp_method_zlib_dtor,
339 };
340 
341 static const LIBSSH2_COMP_METHOD comp_method_zlib_openssh = {
342     "zlib@openssh.com",
343     1, /* yes, this compresses */
344     0, /* don't use compression during userauth */
345     comp_method_zlib_init,
346     comp_method_zlib_comp,
347     comp_method_zlib_decomp,
348     comp_method_zlib_dtor,
349 };
350 #endif /* LIBSSH2_HAVE_ZLIB */
351 
352 /* If compression is enabled by the API, then this array is used which then
353    may allow compression if zlib is available at build time */
354 static const LIBSSH2_COMP_METHOD *comp_methods[] = {
355 #ifdef LIBSSH2_HAVE_ZLIB
356     &comp_method_zlib,
357     &comp_method_zlib_openssh,
358 #endif /* LIBSSH2_HAVE_ZLIB */
359     &comp_method_none,
360     NULL
361 };
362 
363 /* If compression is disabled by the API, then this array is used */
364 static const LIBSSH2_COMP_METHOD *no_comp_methods[] = {
365     &comp_method_none,
366     NULL
367 };
368 
369 const LIBSSH2_COMP_METHOD **
_libssh2_comp_methods(LIBSSH2_SESSION * session)370 _libssh2_comp_methods(LIBSSH2_SESSION *session)
371 {
372     if(session->flag.compress)
373         return comp_methods;
374     else
375         return no_comp_methods;
376 }
377