1 /*
2  * buffer.c - buffer functions
3  *
4  * This file is part of the SSH Library
5  *
6  * Copyright (c) 2003-2009 by Aris Adamantiadis
7  *
8  * The SSH Library is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or (at your
11  * option) any later version.
12  *
13  * The SSH Library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16  * License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with the SSH Library; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21  * MA 02111-1307, USA.
22  */
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #ifndef _WIN32
28 #include <arpa/inet.h>
29 #endif
30 
31 #include "libssh/priv.h"
32 #include "libssh/buffer.h"
33 
34 /**
35  * @defgroup libssh_buffer The SSH buffer functions.
36  * @ingroup libssh
37  *
38  * Functions to handle SSH buffers.
39  *
40  * @{
41  */
42 
43 
44 #ifdef DEBUG_BUFFER
45 /**
46  * @internal
47  *
48  * @brief Check that preconditions and postconditions are valid.
49  *
50  * @param[in]  buf      The buffer to check.
51  */
buffer_verify(ssh_buffer buf)52 static void buffer_verify(ssh_buffer buf){
53   int doabort=0;
54   if(buf->data == NULL)
55     return;
56   if(buf->used > buf->allocated){
57     fprintf(stderr,"Buffer error : allocated %u, used %u\n",buf->allocated, buf->used);
58     doabort=1;
59   }
60   if(buf->pos > buf->used){
61     fprintf(stderr,"Buffer error : position %u, used %u\n",buf->pos, buf->used);
62     doabort=1;
63   }
64   if(buf->pos > buf->allocated){
65       fprintf(stderr,"Buffer error : position %u, allocated %u\n",buf->pos, buf->allocated);
66       doabort=1;
67   }
68   if(doabort)
69     abort();
70 }
71 
72 #else
73 #define buffer_verify(x)
74 #endif
75 
76 /**
77  * @brief Create a new SSH buffer.
78  *
79  * @return A newly initialized SSH buffer, NULL on error.
80  */
ssh_buffer_new(void)81 struct ssh_buffer_struct *ssh_buffer_new(void) {
82   struct ssh_buffer_struct *buf = malloc(sizeof(struct ssh_buffer_struct));
83 
84   if (buf == NULL) {
85     return NULL;
86   }
87   memset(buf, 0, sizeof(struct ssh_buffer_struct));
88   buffer_verify(buf);
89   return buf;
90 }
91 
92 /**
93  * @brief Deallocate a SSH buffer.
94  *
95  * \param[in]  buffer   The buffer to free.
96  */
ssh_buffer_free(struct ssh_buffer_struct * buffer)97 void ssh_buffer_free(struct ssh_buffer_struct *buffer) {
98   if (buffer == NULL) {
99     return;
100   }
101   buffer_verify(buffer);
102 
103   if (buffer->data) {
104     /* burn the data */
105     memset(buffer->data, 0, buffer->allocated);
106     SAFE_FREE(buffer->data);
107   }
108   memset(buffer, 'X', sizeof(*buffer));
109   SAFE_FREE(buffer);
110 }
111 
realloc_buffer(struct ssh_buffer_struct * buffer,int needed)112 static int realloc_buffer(struct ssh_buffer_struct *buffer, int needed) {
113   int smallest = 1;
114   char *new = NULL;
115   buffer_verify(buffer);
116   /* Find the smallest power of two which is greater or equal to needed */
117   while(smallest <= needed) {
118     smallest <<= 1;
119   }
120   needed = smallest;
121   new = realloc(buffer->data, needed);
122   if (new == NULL) {
123     return -1;
124   }
125   buffer->data = new;
126   buffer->allocated = needed;
127   buffer_verify(buffer);
128   return 0;
129 }
130 
131 /** @internal
132  * @brief shifts a buffer to remove unused data in the beginning
133  * @param buffer SSH buffer
134  */
buffer_shift(ssh_buffer buffer)135 static void buffer_shift(ssh_buffer buffer){
136   buffer_verify(buffer);
137   if(buffer->pos==0)
138     return;
139   memmove(buffer->data, buffer->data + buffer->pos, buffer->used - buffer->pos);
140   buffer->used -= buffer->pos;
141   buffer->pos=0;
142   buffer_verify(buffer);
143 }
144 
145 /**
146  * @internal
147  *
148  * @brief Reinitialize a SSH buffer.
149  *
150  * @param[in]  buffer   The buffer to reinitialize.
151  *
152  * @return              0 on success, < 0 on error.
153  */
buffer_reinit(struct ssh_buffer_struct * buffer)154 int buffer_reinit(struct ssh_buffer_struct *buffer) {
155   buffer_verify(buffer);
156   memset(buffer->data, 0, buffer->used);
157   buffer->used = 0;
158   buffer->pos = 0;
159   if(buffer->allocated > 127) {
160     if (realloc_buffer(buffer, 127) < 0) {
161       return -1;
162     }
163   }
164   buffer_verify(buffer);
165   return 0;
166 }
167 
168 /**
169  * @internal
170  *
171  * @brief Add data at the tail of a buffer.
172  *
173  * @param[in]  buffer   The buffer to add the data.
174  *
175  * @param[in]  data     A pointer to the data to add.
176  *
177  * @param[in]  len      The length of the data to add.
178  *
179  * @return              0 on success, < 0 on error.
180  */
buffer_add_data(struct ssh_buffer_struct * buffer,const void * data,uint32_t len)181 int buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len) {
182   buffer_verify(buffer);
183   if (buffer->allocated < (buffer->used + len)) {
184     if(buffer->pos > 0)
185     {
186       buffer_shift(buffer);
187     }
188     if (realloc_buffer(buffer, buffer->used + len) < 0) {
189       return -1;
190     }
191   }
192 
193   memcpy(buffer->data+buffer->used, data, len);
194   buffer->used+=len;
195   buffer_verify(buffer);
196   return 0;
197 }
198 
199 /**
200  * @internal
201  *
202  * @brief Add a SSH string to the tail of a buffer.
203  *
204  * @param[in]  buffer   The buffer to add the string.
205  *
206  * @param[in]  string   The SSH String to add.
207  *
208  * @return              0 on success, < 0 on error.
209  */
buffer_add_ssh_string(struct ssh_buffer_struct * buffer,struct ssh_string_struct * string)210 int buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
211     struct ssh_string_struct *string) {
212   uint32_t len = 0;
213 
214   len = ssh_string_len(string);
215   if (buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) {
216     return -1;
217   }
218 
219   return 0;
220 }
221 
222 /**
223  * @internal
224  *
225  * @brief Add a 32 bits unsigned integer to the tail of a buffer.
226  *
227  * @param[in]  buffer   The buffer to add the integer.
228  *
229  * @param[in]  data     The 32 bits integer to add.
230  *
231  * @return              0 on success, -1 on error.
232  */
buffer_add_u32(struct ssh_buffer_struct * buffer,uint32_t data)233 int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data){
234   if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
235     return -1;
236   }
237 
238   return 0;
239 }
240 
241 /**
242  * @internal
243  *
244  * @brief Add a 16 bits unsigned integer to the tail of a buffer.
245  *
246  * @param[in]  buffer   The buffer to add the integer.
247  *
248  * @param[in]  data     The 16 bits integer to add.
249  *
250  * @return              0 on success, -1 on error.
251  */
buffer_add_u16(struct ssh_buffer_struct * buffer,uint16_t data)252 int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data){
253   if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
254     return -1;
255   }
256 
257   return 0;
258 }
259 
260 /**
261  * @internal
262  *
263  * @brief Add a 64 bits unsigned integer to the tail of a buffer.
264  *
265  * @param[in]  buffer   The buffer to add the integer.
266  *
267  * @param[in]  data     The 64 bits integer to add.
268  *
269  * @return              0 on success, -1 on error.
270  */
buffer_add_u64(struct ssh_buffer_struct * buffer,uint64_t data)271 int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data){
272   if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
273     return -1;
274   }
275 
276   return 0;
277 }
278 
279 /**
280  * @internal
281  *
282  * @brief Add a 8 bits unsigned integer to the tail of a buffer.
283  *
284  * @param[in]  buffer   The buffer to add the integer.
285  *
286  * @param[in]  data     The 8 bits integer to add.
287  *
288  * @return              0 on success, -1 on error.
289  */
buffer_add_u8(struct ssh_buffer_struct * buffer,uint8_t data)290 int buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data){
291   if (buffer_add_data(buffer, &data, sizeof(uint8_t)) < 0) {
292     return -1;
293   }
294 
295   return 0;
296 }
297 
298 /**
299  * @internal
300  *
301  * @brief Add data at the head of a buffer.
302  *
303  * @param[in]  buffer   The buffer to add the data.
304  *
305  * @param[in]  data     The data to prepend.
306  *
307  * @param[in]  len      The length of data to prepend.
308  *
309  * @return              0 on success, -1 on error.
310  */
buffer_prepend_data(struct ssh_buffer_struct * buffer,const void * data,uint32_t len)311 int buffer_prepend_data(struct ssh_buffer_struct *buffer, const void *data,
312     uint32_t len) {
313   buffer_verify(buffer);
314 
315   if(len <= buffer->pos){
316     /* It's possible to insert data between begin and pos */
317     memcpy(buffer->data + (buffer->pos - len), data, len);
318     buffer->pos -= len;
319     buffer_verify(buffer);
320     return 0;
321   }
322   /* pos isn't high enough */
323   if (buffer->allocated < (buffer->used - buffer->pos + len)) {
324     if (realloc_buffer(buffer, buffer->used - buffer->pos + len) < 0) {
325       return -1;
326     }
327   }
328   memmove(buffer->data + len, buffer->data + buffer->pos, buffer->used - buffer->pos);
329   memcpy(buffer->data, data, len);
330   buffer->used += len - buffer->pos;
331   buffer->pos = 0;
332   buffer_verify(buffer);
333   return 0;
334 }
335 
336 /**
337  * @internal
338  *
339  * @brief Append data from a buffer to the tail of another buffer.
340  *
341  * @param[in]  buffer   The destination buffer.
342  *
343  * @param[in]  source   The source buffer to append. It doesn't take the
344  *                      position of the buffer into account.
345  *
346  * @return              0 on success, -1 on error.
347  */
buffer_add_buffer(struct ssh_buffer_struct * buffer,struct ssh_buffer_struct * source)348 int buffer_add_buffer(struct ssh_buffer_struct *buffer,
349     struct ssh_buffer_struct *source) {
350   if (buffer_add_data(buffer, buffer_get_rest(source), buffer_get_rest_len(source)) < 0) {
351     return -1;
352   }
353 
354   return 0;
355 }
356 
357 /**
358  * @brief Get a pointer on the head of a buffer.
359  *
360  * @param[in]  buffer   The buffer to get the head pointer.
361  *
362  * @return              A data pointer on the head. It doesn't take the position
363  *                      into account.
364  *
365  * @warning Don't expect data to be nul-terminated.
366  *
367  * @see buffer_get_rest()
368  * @see buffer_get_len()
369  */
ssh_buffer_get_begin(struct ssh_buffer_struct * buffer)370 void *ssh_buffer_get_begin(struct ssh_buffer_struct *buffer){
371   return buffer->data;
372 }
373 
374 /**
375  * @internal
376  *
377  * @brief Get a pointer to the head of a buffer at the current position.
378  *
379  * @param[in]  buffer   The buffer to get the head pointer.
380  *
381  * @return              A pointer to the data from current position.
382  *
383  * @see buffer_get_rest_len()
384  * @see buffer_get()
385  */
buffer_get_rest(struct ssh_buffer_struct * buffer)386 void *buffer_get_rest(struct ssh_buffer_struct *buffer){
387     return buffer->data + buffer->pos;
388 }
389 
390 /**
391  * @brief Get the length of the buffer, not counting position.
392  *
393  * @param[in]  buffer   The buffer to get the length from.
394  *
395  * @return              The length of the buffer.
396  *
397  * @see buffer_get()
398  */
ssh_buffer_get_len(struct ssh_buffer_struct * buffer)399 uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){
400     return buffer->used;
401 }
402 
403 /**
404  * @internal
405  *
406  * @brief Get the length of the buffer from the current position.
407  *
408  * @param[in]  buffer   The buffer to get the length from.
409  *
410  * @return              The length of the buffer.
411  *
412  * @see buffer_get_rest()
413  */
buffer_get_rest_len(struct ssh_buffer_struct * buffer)414 uint32_t buffer_get_rest_len(struct ssh_buffer_struct *buffer){
415   buffer_verify(buffer);
416   return buffer->used - buffer->pos;
417 }
418 
419 /**
420  * @internal
421  *
422  * @brief Advance the position in the buffer.
423  *
424  * This has effect to "eat" bytes at head of the buffer.
425  *
426  * @param[in]  buffer   The buffer to advance the position.
427  *
428  * @param[in]  len      The number of bytes to eat.
429  *
430  * @return              The new size of the buffer.
431  */
buffer_pass_bytes(struct ssh_buffer_struct * buffer,uint32_t len)432 uint32_t buffer_pass_bytes(struct ssh_buffer_struct *buffer, uint32_t len){
433     buffer_verify(buffer);
434     if(buffer->used < buffer->pos+len)
435         return 0;
436     buffer->pos+=len;
437     /* if the buffer is empty after having passed the whole bytes into it, we can clean it */
438     if(buffer->pos==buffer->used){
439         buffer->pos=0;
440         buffer->used=0;
441     }
442     buffer_verify(buffer);
443     return len;
444 }
445 
446 /**
447  * @internal
448  *
449  * @brief Cut the end of the buffer.
450  *
451  * @param[in]  buffer   The buffer to cut.
452  *
453  * @param[in]  len      The number of bytes to remove from the tail.
454  *
455  * @return              The new size of the buffer.
456  */
buffer_pass_bytes_end(struct ssh_buffer_struct * buffer,uint32_t len)457 uint32_t buffer_pass_bytes_end(struct ssh_buffer_struct *buffer, uint32_t len){
458   buffer_verify(buffer);
459   if(buffer->used < buffer->pos + len)
460     return 0;
461   buffer->used-=len;
462   buffer_verify(buffer);
463   return len;
464 }
465 
466 /**
467  * @internal
468  *
469  * @brief Get the remaining data out of the buffer and adjust the read pointer.
470  *
471  * @param[in]  buffer   The buffer to read.
472  *
473  * @param[in]  data     The data buffer where to store the data.
474  *
475  * @param[in]  len      The length to read from the buffer.
476  *
477  * @returns             0 if there is not enough data in buffer, len otherwise.
478  */
buffer_get_data(struct ssh_buffer_struct * buffer,void * data,uint32_t len)479 uint32_t buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint32_t len){
480     /*
481      * Check for a integer overflow first, then check if not enough data is in
482      * the buffer.
483      */
484     if (buffer->pos + len < len || buffer->pos + len > buffer->used) {
485       return 0;
486     }
487     memcpy(data,buffer->data+buffer->pos,len);
488     buffer->pos+=len;
489     return len;   /* no yet support for partial reads (is it really needed ?? ) */
490 }
491 
492 /**
493  * @internal
494  *
495  * @brief Get a 8 bits unsigned int out of the buffer and adjusts the read
496  * pointer.
497  *
498  * @param[in]  buffer   The buffer to read.
499  *
500  * @param[in]   data    A pointer to a uint8_t where to store the data.
501  *
502  * @returns             0 if there is not enough data in buffer, 1 otherwise.
503  */
buffer_get_u8(struct ssh_buffer_struct * buffer,uint8_t * data)504 int buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
505     return buffer_get_data(buffer,data,sizeof(uint8_t));
506 }
507 
508 /** \internal
509  * \brief gets a 32 bits unsigned int out of the buffer. Adjusts the read pointer.
510  * \param buffer Buffer to read
511  * \param data pointer to a uint32_t where to store the data
512  * \returns 0 if there is not enough data in buffer
513  * \returns 4 otherwise.
514  */
buffer_get_u32(struct ssh_buffer_struct * buffer,uint32_t * data)515 int buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
516     return buffer_get_data(buffer,data,sizeof(uint32_t));
517 }
518 /**
519  * @internal
520  *
521  * @brief Get a 64 bits unsigned int out of the buffer and adjusts the read
522  * pointer.
523  *
524  * @param[in]  buffer   The buffer to read.
525  *
526  * @param[in]  data     A pointer to a uint64_t where to store the data.
527  *
528  * @returns             0 if there is not enough data in buffer, 8 otherwise.
529  */
buffer_get_u64(struct ssh_buffer_struct * buffer,uint64_t * data)530 int buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
531     return buffer_get_data(buffer,data,sizeof(uint64_t));
532 }
533 
534 /**
535  * @internal
536  *
537  * @brief Get a SSH String out of the buffer and adjusts the read pointer.
538  *
539  * @param[in]  buffer   The buffer to read.
540  *
541  * @returns             The SSH String, NULL on error.
542  */
buffer_get_ssh_string(struct ssh_buffer_struct * buffer)543 struct ssh_string_struct *buffer_get_ssh_string(struct ssh_buffer_struct *buffer) {
544   uint32_t stringlen;
545   uint32_t hostlen;
546   struct ssh_string_struct *str = NULL;
547 
548   if (buffer_get_u32(buffer, &stringlen) == 0) {
549     return NULL;
550   }
551   hostlen = ntohl(stringlen);
552   /* verify if there is enough space in buffer to get it */
553   if ((buffer->pos + hostlen) > buffer->used) {
554     return NULL; /* it is indeed */
555   }
556   str = ssh_string_new(hostlen);
557   if (str == NULL) {
558     return NULL;
559   }
560   if (buffer_get_data(buffer, ssh_string_data(str), hostlen) != hostlen) {
561     /* should never happen */
562     SAFE_FREE(str);
563     return NULL;
564   }
565 
566   return str;
567 }
568 
569 /**
570  * @internal
571  *
572  * @brief Get a mpint out of the buffer and adjusts the read pointer.
573  *
574  * @note This function is SSH-1 only.
575  *
576  * @param[in]  buffer   The buffer to read.
577  *
578  * @returns             The SSH String containing the mpint, NULL on error.
579  */
buffer_get_mpint(struct ssh_buffer_struct * buffer)580 struct ssh_string_struct *buffer_get_mpint(struct ssh_buffer_struct *buffer) {
581   uint16_t bits;
582   uint32_t len;
583   struct ssh_string_struct *str = NULL;
584 
585   if (buffer_get_data(buffer, &bits, sizeof(uint16_t)) != sizeof(uint16_t)) {
586     return NULL;
587   }
588   bits = ntohs(bits);
589   len = (bits + 7) / 8;
590   if ((buffer->pos + len) > buffer->used) {
591     return NULL;
592   }
593   str = ssh_string_new(len);
594   if (str == NULL) {
595     return NULL;
596   }
597   if (buffer_get_data(buffer, ssh_string_data(str), len) != len) {
598     SAFE_FREE(str);
599     return NULL;
600   }
601   return str;
602 }
603 
604 /** @} */
605 
606 /* vim: set ts=4 sw=4 et cindent: */
607