/src/dropbear/src/buffer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Dropbear SSH |
3 | | * |
4 | | * Copyright (c) 2002,2003 Matt Johnston |
5 | | * All rights reserved. |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in |
15 | | * all copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | | * SOFTWARE. */ |
24 | | |
25 | | /* Buffer handling routines, designed to avoid overflows/using invalid data */ |
26 | | |
27 | | #include "includes.h" |
28 | | #include "dbutil.h" |
29 | | #include "buffer.h" |
30 | | |
31 | | /* Prevent integer overflows when incrementing buffer position/length. |
32 | | * Calling functions should check arguments first, but this provides a |
33 | | * backstop */ |
34 | 6.21k | #define BUF_MAX_INCR 1000000000 |
35 | 446 | #define BUF_MAX_SIZE 1000000000 |
36 | | |
37 | | /* avoid excessively large numbers, > ~8192 bits */ |
38 | 22 | #define BUF_MAX_MPINT (8240 / 8) |
39 | | |
40 | | /* Create (malloc) a new buffer of size */ |
41 | 446 | buffer* buf_new(unsigned int size) { |
42 | 446 | buffer* buf; |
43 | 446 | if (size > BUF_MAX_SIZE) { |
44 | 0 | dropbear_exit("buf->size too big"); |
45 | 0 | } |
46 | | |
47 | 446 | buf = (buffer*)m_malloc(sizeof(buffer)+size); |
48 | 446 | buf->data = (unsigned char*)buf + sizeof(buffer); |
49 | 446 | buf->size = size; |
50 | 446 | return buf; |
51 | 446 | } |
52 | | |
53 | | /* free the buffer's data and the buffer itself */ |
54 | 248 | void buf_free(buffer* buf) { |
55 | 248 | m_free(buf); |
56 | 248 | } |
57 | | |
58 | | /* overwrite the contents of the buffer then free it */ |
59 | 61 | void buf_burn_free(buffer* buf) { |
60 | 61 | m_burn(buf->data, buf->size); |
61 | 61 | m_free(buf); |
62 | 61 | } |
63 | | |
64 | | |
65 | | /* resize a buffer, pos and len will be repositioned if required when |
66 | | * downsizing */ |
67 | 0 | buffer* buf_resize(buffer *buf, unsigned int newsize) { |
68 | 0 | if (newsize > BUF_MAX_SIZE) { |
69 | 0 | dropbear_exit("buf->size too big"); |
70 | 0 | } |
71 | | |
72 | 0 | buf = m_realloc(buf, sizeof(buffer)+newsize); |
73 | 0 | buf->data = (unsigned char*)buf + sizeof(buffer); |
74 | 0 | buf->size = newsize; |
75 | 0 | buf->len = MIN(newsize, buf->len); |
76 | 0 | buf->pos = MIN(newsize, buf->pos); |
77 | 0 | return buf; |
78 | 0 | } |
79 | | |
80 | | /* Create a copy of buf, allocating required memory etc. */ |
81 | | /* The new buffer is sized the same as the length of the source buffer. */ |
82 | 61 | buffer* buf_newcopy(const buffer* buf) { |
83 | | |
84 | 61 | buffer* ret; |
85 | | |
86 | 61 | ret = buf_new(buf->len); |
87 | 61 | ret->len = buf->len; |
88 | 61 | if (buf->len > 0) { |
89 | 61 | memcpy(ret->data, buf->data, buf->len); |
90 | 61 | } |
91 | 61 | return ret; |
92 | 61 | } |
93 | | |
94 | | /* Set the length of the buffer */ |
95 | 69 | void buf_setlen(buffer* buf, unsigned int len) { |
96 | 69 | if (len > buf->size) { |
97 | 0 | dropbear_exit("Bad buf_setlen"); |
98 | 0 | } |
99 | 69 | buf->len = len; |
100 | 69 | buf->pos = MIN(buf->pos, buf->len); |
101 | 69 | } |
102 | | |
103 | | /* Increment the length of the buffer */ |
104 | 147 | void buf_incrlen(buffer* buf, unsigned int incr) { |
105 | 147 | if (incr > BUF_MAX_INCR || buf->len + incr > buf->size) { |
106 | 0 | dropbear_exit("Bad buf_incrlen"); |
107 | 0 | } |
108 | 147 | buf->len += incr; |
109 | 147 | } |
110 | | /* Set the position of the buffer */ |
111 | 182 | void buf_setpos(buffer* buf, unsigned int pos) { |
112 | | |
113 | 182 | if (pos > buf->len) { |
114 | 0 | dropbear_exit("Bad buf_setpos"); |
115 | 0 | } |
116 | 182 | buf->pos = pos; |
117 | 182 | } |
118 | | |
119 | | /* increment the position by incr, increasing the buffer length if required */ |
120 | 740 | void buf_incrwritepos(buffer* buf, unsigned int incr) { |
121 | 740 | if (incr > BUF_MAX_INCR || buf->pos + incr > buf->size) { |
122 | 0 | dropbear_exit("Bad buf_incrwritepos"); |
123 | 0 | } |
124 | 740 | buf->pos += incr; |
125 | 740 | if (buf->pos > buf->len) { |
126 | 740 | buf->len = buf->pos; |
127 | 740 | } |
128 | 740 | } |
129 | | |
130 | | /* increment the position by incr */ |
131 | 572 | void buf_incrpos(buffer* buf, unsigned int incr) { |
132 | 572 | if (incr > BUF_MAX_INCR |
133 | 572 | || (buf->pos + incr) > buf->len) { |
134 | 0 | dropbear_exit("Bad buf_incrpos"); |
135 | 0 | } |
136 | 572 | buf->pos += incr; |
137 | 572 | } |
138 | | |
139 | | /* decrement the position by decr */ |
140 | 8 | void buf_decrpos(buffer* buf, unsigned int decr) { |
141 | 8 | if (decr > buf->pos) { |
142 | 0 | dropbear_exit("Bad buf_decrpos"); |
143 | 0 | } |
144 | 8 | buf->pos -= decr; |
145 | 8 | } |
146 | | |
147 | | /* Get a byte from the buffer and increment the pos */ |
148 | 2 | unsigned char buf_getbyte(buffer* buf) { |
149 | | |
150 | | /* This check is really just ==, but the >= allows us to check for the |
151 | | * bad case of pos > len, which should _never_ happen. */ |
152 | 2 | if (buf->pos >= buf->len) { |
153 | 0 | dropbear_exit("Bad buf_getbyte"); |
154 | 0 | } |
155 | 2 | return buf->data[buf->pos++]; |
156 | 2 | } |
157 | | |
158 | | /* Get a bool from the buffer and increment the pos */ |
159 | 0 | unsigned char buf_getbool(buffer* buf) { |
160 | |
|
161 | 0 | unsigned char b; |
162 | 0 | b = buf_getbyte(buf); |
163 | 0 | if (b != 0) |
164 | 0 | b = 1; |
165 | 0 | return b; |
166 | 0 | } |
167 | | |
168 | | /* put a byte, incrementing the length if required */ |
169 | 36 | void buf_putbyte(buffer* buf, unsigned char val) { |
170 | | |
171 | 36 | if (buf->pos >= buf->len) { |
172 | 36 | buf_incrlen(buf, 1); |
173 | 36 | } |
174 | 36 | buf->data[buf->pos] = val; |
175 | 36 | buf->pos++; |
176 | 36 | } |
177 | | |
178 | | /* returns an in-place pointer to the buffer, checking that |
179 | | * the next len bytes from that position can be used */ |
180 | 696 | unsigned char* buf_getptr(const buffer* buf, unsigned int len) { |
181 | | |
182 | 696 | if (len > BUF_MAX_INCR || buf->pos + len > buf->len) { |
183 | 47 | dropbear_exit("Bad buf_getptr"); |
184 | 47 | } |
185 | 649 | return &buf->data[buf->pos]; |
186 | 696 | } |
187 | | |
188 | | /* like buf_getptr, but checks against total size, not used length. |
189 | | * This allows writing past the used length, but not past the size */ |
190 | 953 | unsigned char* buf_getwriteptr(const buffer* buf, unsigned int len) { |
191 | | |
192 | 953 | if (len > BUF_MAX_INCR || buf->pos + len > buf->size) { |
193 | 0 | dropbear_exit("Bad buf_getwriteptr"); |
194 | 0 | } |
195 | 953 | return &buf->data[buf->pos]; |
196 | 953 | } |
197 | | |
198 | | /* Return a null-terminated string, it is malloced, so must be free()ed |
199 | | * Note that the string isn't checked for null bytes, hence the retlen |
200 | | * may be longer than what is returned by strlen */ |
201 | 12 | char* buf_getstring(buffer* buf, unsigned int *retlen) { |
202 | | |
203 | 12 | unsigned int len; |
204 | 12 | char* ret; |
205 | 12 | void* src = NULL; |
206 | 12 | len = buf_getint(buf); |
207 | 12 | if (len > MAX_STRING_LEN) { |
208 | 0 | dropbear_exit("String too long"); |
209 | 0 | } |
210 | | |
211 | 12 | if (retlen != NULL) { |
212 | 12 | *retlen = len; |
213 | 12 | } |
214 | 12 | src = buf_getptr(buf, len); |
215 | 12 | ret = m_malloc(len+1); |
216 | 12 | memcpy(ret, src, len); |
217 | 12 | buf_incrpos(buf, len); |
218 | 12 | ret[len] = '\0'; |
219 | | |
220 | 12 | return ret; |
221 | 12 | } |
222 | | |
223 | | /* Return a string as a newly allocated buffer */ |
224 | 191 | static buffer * buf_getstringbuf_int(buffer *buf, int incllen) { |
225 | 191 | buffer *ret = NULL; |
226 | 191 | unsigned int len = buf_getint(buf); |
227 | 191 | int extra = 0; |
228 | 191 | if (len > MAX_STRING_LEN) { |
229 | 36 | dropbear_exit("String too long"); |
230 | 36 | } |
231 | 155 | if (incllen) { |
232 | 0 | extra = 4; |
233 | 0 | } |
234 | 155 | ret = buf_new(len+extra); |
235 | 155 | if (incllen) { |
236 | 0 | buf_putint(ret, len); |
237 | 0 | } |
238 | 155 | memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len); |
239 | 155 | buf_incrpos(buf, len); |
240 | 155 | buf_incrlen(ret, len); |
241 | 155 | buf_setpos(ret, 0); |
242 | 155 | return ret; |
243 | 191 | } |
244 | | |
245 | | /* Return a string as a newly allocated buffer */ |
246 | 191 | buffer * buf_getstringbuf(buffer *buf) { |
247 | 191 | return buf_getstringbuf_int(buf, 0); |
248 | 191 | } |
249 | | |
250 | | /* Returns a string in a new buffer, including the length */ |
251 | 0 | buffer * buf_getbuf(buffer *buf) { |
252 | 0 | return buf_getstringbuf_int(buf, 1); |
253 | 0 | } |
254 | | |
255 | | /* Returns the equivalent of buf_getptr() as a new buffer. */ |
256 | 0 | buffer * buf_getptrcopy(const buffer* buf, unsigned int len) { |
257 | 0 | unsigned char *src = buf_getptr(buf, len); |
258 | 0 | buffer *ret = buf_new(len); |
259 | 0 | buf_putbytes(ret, src, len); |
260 | 0 | buf_setpos(ret, 0); |
261 | 0 | return ret; |
262 | 0 | } |
263 | | |
264 | | /* Just increment the buffer position the same as if we'd used buf_getstring, |
265 | | * but don't bother copying/malloc()ing for it */ |
266 | 0 | void buf_eatstring(buffer *buf) { |
267 | |
|
268 | 0 | buf_incrpos( buf, buf_getint(buf) ); |
269 | 0 | } |
270 | | |
271 | | /* Get an uint32 from the buffer and increment the pos */ |
272 | 419 | unsigned int buf_getint(buffer* buf) { |
273 | 419 | unsigned int ret; |
274 | | |
275 | 419 | LOAD32H(ret, buf_getptr(buf, 4)); |
276 | 419 | buf_incrpos(buf, 4); |
277 | 419 | return ret; |
278 | 419 | } |
279 | | |
280 | | /* put a 32bit uint into the buffer, incr bufferlen & pos if required */ |
281 | 366 | void buf_putint(buffer* buf, int unsigned val) { |
282 | | |
283 | 366 | STORE32H(val, buf_getwriteptr(buf, 4)); |
284 | 366 | buf_incrwritepos(buf, 4); |
285 | | |
286 | 366 | } |
287 | | |
288 | | /* put a SSH style string into the buffer, increasing buffer len if required */ |
289 | 305 | void buf_putstring(buffer* buf, const char* str, unsigned int len) { |
290 | | |
291 | 305 | buf_putint(buf, len); |
292 | 305 | buf_putbytes(buf, (const unsigned char*)str, len); |
293 | | |
294 | 305 | } |
295 | | |
296 | | /* puts an entire buffer as a SSH string. ignore pos of buf_str. */ |
297 | 61 | void buf_putbufstring(buffer *buf, const buffer* buf_str) { |
298 | 61 | buf_putstring(buf, (const char*)buf_str->data, buf_str->len); |
299 | 61 | } |
300 | | |
301 | | /* put the set of len bytes into the buffer, incrementing the pos, increasing |
302 | | * len if required */ |
303 | 313 | void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) { |
304 | 313 | memcpy(buf_getwriteptr(buf, len), bytes, len); |
305 | 313 | buf_incrwritepos(buf, len); |
306 | 313 | } |
307 | | |
308 | | |
309 | | /* for our purposes we only need positive (or 0) numbers, so will |
310 | | * fail if we get negative numbers */ |
311 | 61 | void buf_putmpint(buffer* buf, const mp_int * mp) { |
312 | 61 | size_t written; |
313 | 61 | unsigned int len, pad = 0; |
314 | 61 | TRACE2(("enter buf_putmpint")) |
315 | | |
316 | 61 | dropbear_assert(mp != NULL); |
317 | | |
318 | 61 | if (mp_isneg(mp)) { |
319 | 0 | dropbear_exit("negative bignum"); |
320 | 0 | } |
321 | | |
322 | | /* zero check */ |
323 | 61 | if (mp_iszero(mp)) { |
324 | 0 | len = 0; |
325 | 61 | } else { |
326 | | /* SSH spec requires padding for mpints with the MSB set, this code |
327 | | * implements it */ |
328 | 61 | len = mp_count_bits(mp); |
329 | | /* if the top bit of MSB is set, we need to pad */ |
330 | 61 | pad = (len%8 == 0) ? 1 : 0; |
331 | 61 | len = len / 8 + 1; /* don't worry about rounding, we need it for |
332 | | padding anyway when len%8 == 0 */ |
333 | | |
334 | 61 | } |
335 | | |
336 | | /* store the length */ |
337 | 61 | buf_putint(buf, len); |
338 | | |
339 | | /* store the actual value */ |
340 | 61 | if (len > 0) { |
341 | 61 | if (pad) { |
342 | 36 | buf_putbyte(buf, 0x00); |
343 | 36 | } |
344 | 61 | if (mp_to_ubin(mp, buf_getwriteptr(buf, len-pad), len-pad, &written) != MP_OKAY) { |
345 | 0 | dropbear_exit("mpint error"); |
346 | 0 | } |
347 | 61 | buf_incrwritepos(buf, written); |
348 | 61 | } |
349 | | |
350 | 61 | TRACE2(("leave buf_putmpint")) |
351 | 61 | } |
352 | | |
353 | | /* Retrieve an mp_int from the buffer. |
354 | | * Will fail for -ve since they shouldn't be required here. |
355 | | * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
356 | 22 | int buf_getmpint(buffer* buf, mp_int* mp) { |
357 | | |
358 | 22 | unsigned int len; |
359 | 22 | len = buf_getint(buf); |
360 | | |
361 | 22 | if (len == 0) { |
362 | 0 | mp_zero(mp); |
363 | 0 | return DROPBEAR_SUCCESS; |
364 | 0 | } |
365 | | |
366 | 22 | if (len > BUF_MAX_MPINT) { |
367 | 0 | return DROPBEAR_FAILURE; |
368 | 0 | } |
369 | | |
370 | | /* check for negative */ |
371 | 22 | if (*buf_getptr(buf, 1) & (1 << (CHAR_BIT-1))) { |
372 | 0 | return DROPBEAR_FAILURE; |
373 | 0 | } |
374 | | |
375 | 22 | if (mp_from_ubin(mp, buf_getptr(buf, len), len) != MP_OKAY) { |
376 | 0 | return DROPBEAR_FAILURE; |
377 | 0 | } |
378 | | |
379 | 22 | buf_incrpos(buf, len); |
380 | 22 | return DROPBEAR_SUCCESS; |
381 | 22 | } |