Coverage Report

Created: 2026-04-06 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/buffer.c
Line
Count
Source
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 "config.h"
25
26
#include <limits.h>
27
#include <stdarg.h>
28
#include <stdbool.h>
29
#include <stdio.h>
30
31
#ifndef _WIN32
32
#include <netinet/in.h>
33
#include <arpa/inet.h>
34
#endif
35
36
#include "libssh/priv.h"
37
#include "libssh/buffer.h"
38
#include "libssh/misc.h"
39
#include "libssh/bignum.h"
40
41
/*
42
 * Describes a buffer state
43
 * [XXXXXXXXXXXXDATA PAYLOAD       XXXXXXXXXXXXXXXXXXXXXXXX]
44
 * ^            ^                  ^                       ^]
45
 * \_data points\_pos points here  \_used points here |    /
46
 *   here                                          Allocated
47
 */
48
struct ssh_buffer_struct {
49
    bool secure;
50
    uint32_t used;
51
    uint32_t allocated;
52
    uint32_t pos;
53
    uint8_t *data;
54
};
55
56
/* Buffer size maximum is 256M */
57
763k
#define BUFFER_SIZE_MAX 0x10000000
58
59
/**
60
 * @defgroup libssh_buffer The SSH buffer functions
61
 * @ingroup libssh
62
 *
63
 * Functions to handle SSH buffers.
64
 *
65
 * @{
66
 */
67
68
69
#ifdef DEBUG_BUFFER
70
/**
71
 * @internal
72
 *
73
 * @brief Check that preconditions and postconditions are valid.
74
 *
75
 * @param[in]  buf      The buffer to check.
76
 */
77
static void buffer_verify(ssh_buffer buf)
78
{
79
    bool do_abort = false;
80
81
    if (buf->data == NULL) {
82
        return;
83
    }
84
85
    if (buf->used > buf->allocated) {
86
        fprintf(stderr,
87
                "BUFFER ERROR: allocated %u, used %u\n",
88
                buf->allocated,
89
                buf->used);
90
        do_abort = true;
91
    }
92
    if (buf->pos > buf->used) {
93
        fprintf(stderr,
94
                "BUFFER ERROR: position %u, used %u\n",
95
                buf->pos,
96
                buf->used);
97
        do_abort = true;
98
    }
99
    if (buf->pos > buf->allocated) {
100
        fprintf(stderr,
101
                "BUFFER ERROR: position %u, allocated %u\n",
102
                buf->pos,
103
                buf->allocated);
104
        do_abort = true;
105
    }
106
    if (do_abort) {
107
        abort();
108
    }
109
}
110
111
#else
112
/** @internal
113
 * @brief No-op stub for buffer_verify when debug checks are disabled.
114
 *
115
 * @param x  The buffer to verify (ignored).
116
 */
117
#define buffer_verify(x)
118
#endif
119
120
/**
121
 * @brief Create a new SSH buffer.
122
 *
123
 * @return A newly initialized SSH buffer, NULL on error.
124
 */
125
struct ssh_buffer_struct *ssh_buffer_new(void)
126
362k
{
127
362k
    struct ssh_buffer_struct *buf = NULL;
128
362k
    int rc;
129
130
362k
    buf = calloc(1, sizeof(struct ssh_buffer_struct));
131
362k
    if (buf == NULL) {
132
0
        return NULL;
133
0
    }
134
135
    /*
136
     * Always preallocate 64 bytes.
137
     *
138
     * -1 for realloc_buffer magic.
139
     */
140
362k
    rc = ssh_buffer_allocate_size(buf, 64 - 1);
141
362k
    if (rc != 0) {
142
0
        SAFE_FREE(buf);
143
0
        return NULL;
144
0
    }
145
362k
    buffer_verify(buf);
146
147
362k
    return buf;
148
362k
}
149
150
/**
151
 * @brief Deallocate a SSH buffer.
152
 *
153
 * \param[in]  buffer   The buffer to free.
154
 */
155
void ssh_buffer_free(struct ssh_buffer_struct *buffer)
156
362k
{
157
362k
    if (buffer == NULL) {
158
0
        return;
159
0
    }
160
362k
    buffer_verify(buffer);
161
162
362k
    if (buffer->secure && buffer->allocated > 0) {
163
        /* burn the data */
164
77.7k
        ssh_burn(buffer->data, buffer->allocated);
165
77.7k
        SAFE_FREE(buffer->data);
166
167
77.7k
        ssh_burn(buffer, sizeof(struct ssh_buffer_struct));
168
284k
    } else {
169
284k
        SAFE_FREE(buffer->data);
170
284k
    }
171
362k
    SAFE_FREE(buffer);
172
362k
}
173
174
/**
175
 * @brief Sets the buffer as secure.
176
 *
177
 * A secure buffer will never leave cleartext data in the heap
178
 * after being reallocated or freed.
179
 *
180
 * @param[in] buffer buffer to set secure.
181
 */
182
void ssh_buffer_set_secure(ssh_buffer buffer)
183
77.7k
{
184
77.7k
    buffer->secure = true;
185
77.7k
}
186
187
static int realloc_buffer(struct ssh_buffer_struct *buffer, uint32_t needed)
188
763k
{
189
763k
    uint32_t smallest = 1;
190
763k
    uint8_t *new = NULL;
191
192
763k
    buffer_verify(buffer);
193
194
    /* Find the smallest power of two which is greater or equal to needed */
195
7.51M
    while(smallest <= needed) {
196
6.74M
        if (smallest == 0) {
197
0
            return -1;
198
0
        }
199
6.74M
        smallest <<= 1;
200
6.74M
    }
201
763k
    needed = smallest;
202
203
763k
    if (needed > BUFFER_SIZE_MAX) {
204
0
        return -1;
205
0
    }
206
207
763k
    if (buffer->secure) {
208
25.9k
        new = malloc(needed);
209
25.9k
        if (new == NULL) {
210
0
            return -1;
211
0
        }
212
25.9k
        memcpy(new, buffer->data, buffer->used);
213
25.9k
        ssh_burn(buffer->data, buffer->used);
214
25.9k
        SAFE_FREE(buffer->data);
215
737k
    } else {
216
737k
        new = realloc(buffer->data, needed);
217
737k
        if (new == NULL) {
218
0
            return -1;
219
0
        }
220
737k
    }
221
763k
    buffer->data = new;
222
763k
    buffer->allocated = needed;
223
224
763k
    buffer_verify(buffer);
225
763k
    return 0;
226
763k
}
227
228
/** @internal
229
 * @brief shifts a buffer to remove unused data in the beginning
230
 * @param buffer SSH buffer
231
 */
232
static void buffer_shift(ssh_buffer buffer)
233
30.7k
{
234
30.7k
    size_t burn_pos = buffer->pos;
235
236
30.7k
    buffer_verify(buffer);
237
238
30.7k
    if (buffer->pos == 0) {
239
0
        return;
240
0
    }
241
30.7k
    memmove(buffer->data,
242
30.7k
            buffer->data + buffer->pos,
243
30.7k
            buffer->used - buffer->pos);
244
30.7k
    buffer->used -= buffer->pos;
245
30.7k
    buffer->pos = 0;
246
247
30.7k
    if (buffer->secure) {
248
0
        void *ptr = buffer->data + buffer->used;
249
0
        ssh_burn(ptr, burn_pos);
250
0
    }
251
252
30.7k
    buffer_verify(buffer);
253
30.7k
}
254
255
/**
256
 * @brief Reinitialize a SSH buffer.
257
 *
258
 * In case the buffer has exceeded 64K in size, the buffer will be reallocated
259
 * to 64K.
260
 *
261
 * @param[in]  buffer   The buffer to reinitialize.
262
 *
263
 * @return              0 on success, < 0 on error.
264
 */
265
int ssh_buffer_reinit(struct ssh_buffer_struct *buffer)
266
471k
{
267
471k
    if (buffer == NULL) {
268
0
        return -1;
269
0
    }
270
271
471k
    buffer_verify(buffer);
272
273
471k
    if (buffer->secure && buffer->allocated > 0) {
274
0
        ssh_burn(buffer->data, buffer->allocated);
275
0
    }
276
471k
    buffer->used = 0;
277
471k
    buffer->pos = 0;
278
279
    /* If the buffer is bigger then 64K, reset it to 64K */
280
471k
    if (buffer->allocated > 65536) {
281
2.56k
        int rc;
282
283
        /* -1 for realloc_buffer magic */
284
2.56k
        rc = realloc_buffer(buffer, 65536 - 1);
285
2.56k
        if (rc != 0) {
286
0
            return -1;
287
0
        }
288
2.56k
    }
289
290
471k
    buffer_verify(buffer);
291
292
471k
    return 0;
293
471k
}
294
295
/**
296
 * @brief Add data at the tail of a buffer.
297
 *
298
 * @param[in]  buffer   The buffer to add the data.
299
 *
300
 * @param[in]  data     A pointer to the data to add.
301
 *
302
 * @param[in]  len      The length of the data to add.
303
 *
304
 * @return              0 on success, < 0 on error.
305
 */
306
int ssh_buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len)
307
3.31M
{
308
3.31M
    if (buffer == NULL) {
309
0
        return -1;
310
0
    }
311
312
3.31M
    buffer_verify(buffer);
313
314
3.31M
    if (data == NULL) {
315
0
        return -1;
316
0
    }
317
318
3.31M
    if (buffer->used + len < len) {
319
0
        return -1;
320
0
    }
321
322
3.31M
    if (buffer->allocated < (buffer->used + len)) {
323
226k
        if (buffer->pos > 0) {
324
0
            buffer_shift(buffer);
325
0
        }
326
226k
        if (realloc_buffer(buffer, buffer->used + len) < 0) {
327
0
            return -1;
328
0
        }
329
226k
    }
330
331
3.31M
    memcpy(buffer->data + buffer->used, data, len);
332
3.31M
    buffer->used += len;
333
3.31M
    buffer_verify(buffer);
334
3.31M
    return 0;
335
3.31M
}
336
337
/**
338
 * @brief Ensure the buffer has at least a certain preallocated size.
339
 *
340
 * @param[in]  buffer   The buffer to enlarge.
341
 *
342
 * @param[in]  len      The length to ensure as allocated.
343
 *
344
 * @return              0 on success, < 0 on error.
345
 */
346
int ssh_buffer_allocate_size(struct ssh_buffer_struct *buffer,
347
                             uint32_t len)
348
800k
{
349
800k
    buffer_verify(buffer);
350
351
800k
    if (buffer->allocated < len) {
352
429k
        if (buffer->pos > 0) {
353
0
            buffer_shift(buffer);
354
0
        }
355
429k
        if (realloc_buffer(buffer, len) < 0) {
356
0
            return -1;
357
0
        }
358
429k
    }
359
360
800k
    buffer_verify(buffer);
361
362
800k
    return 0;
363
800k
}
364
365
/**
366
 * @internal
367
 *
368
 * @brief Allocate space for data at the tail of a buffer.
369
 *
370
 * @param[in]  buffer   The buffer to add the data.
371
 *
372
 * @param[in]  len      The length of the data to add.
373
 *
374
 * @return              Pointer on the allocated space
375
 *                      NULL on error.
376
 */
377
void *ssh_buffer_allocate(struct ssh_buffer_struct *buffer, uint32_t len)
378
844k
{
379
844k
    void *ptr = NULL;
380
381
844k
    buffer_verify(buffer);
382
383
844k
    if (buffer->used + len < len) {
384
0
        return NULL;
385
0
    }
386
387
844k
    if (buffer->allocated < (buffer->used + len)) {
388
104k
        if (buffer->pos > 0) {
389
30.7k
            buffer_shift(buffer);
390
30.7k
        }
391
392
104k
        if (realloc_buffer(buffer, buffer->used + len) < 0) {
393
0
            return NULL;
394
0
        }
395
104k
    }
396
397
844k
    ptr = buffer->data + buffer->used;
398
844k
    buffer->used+=len;
399
844k
    buffer_verify(buffer);
400
401
844k
    return ptr;
402
844k
}
403
404
/**
405
 * @internal
406
 *
407
 * @brief Add a SSH string to the tail of a buffer.
408
 *
409
 * @param[in]  buffer   The buffer to add the string.
410
 *
411
 * @param[in]  string   The SSH String to add.
412
 *
413
 * @return              0 on success, < 0 on error.
414
 */
415
int
416
ssh_buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
417
                          struct ssh_string_struct *string)
418
661k
{
419
661k
    size_t len;
420
661k
    int rc;
421
422
661k
    if (string == NULL) {
423
0
        return -1;
424
0
    }
425
426
661k
    len = ssh_string_len(string) + sizeof(uint32_t);
427
    /* this can't overflow the uint32_t as the
428
     * STRING_SIZE_MAX is (UINT32_MAX >> 8) + 1 */
429
661k
    rc = ssh_buffer_add_data(buffer, string, (uint32_t)len);
430
661k
    if (rc < 0) {
431
0
        return -1;
432
0
    }
433
434
661k
    return 0;
435
661k
}
436
437
/**
438
 * @internal
439
 *
440
 * @brief Add a 32 bits unsigned integer to the tail of a buffer.
441
 *
442
 * @param[in]  buffer   The buffer to add the integer.
443
 *
444
 * @param[in]  data     The 32 bits integer to add.
445
 *
446
 * @return              0 on success, -1 on error.
447
 */
448
int ssh_buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data)
449
498k
{
450
498k
    int rc;
451
452
498k
    rc = ssh_buffer_add_data(buffer, &data, sizeof(data));
453
498k
    if (rc < 0) {
454
0
        return -1;
455
0
    }
456
457
498k
    return 0;
458
498k
}
459
460
/**
461
 * @internal
462
 *
463
 * @brief Add a 16 bits unsigned integer to the tail of a buffer.
464
 *
465
 * @param[in]  buffer   The buffer to add the integer.
466
 *
467
 * @param[in]  data     The 16 bits integer to add.
468
 *
469
 * @return              0 on success, -1 on error.
470
 */
471
int ssh_buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data)
472
0
{
473
0
    int rc;
474
475
0
    rc = ssh_buffer_add_data(buffer, &data, sizeof(data));
476
0
    if (rc < 0) {
477
0
        return -1;
478
0
    }
479
480
0
    return 0;
481
0
}
482
483
/**
484
 * @internal
485
 *
486
 * @brief Add a 64 bits unsigned integer to the tail of a buffer.
487
 *
488
 * @param[in]  buffer   The buffer to add the integer.
489
 *
490
 * @param[in]  data     The 64 bits integer to add.
491
 *
492
 * @return              0 on success, -1 on error.
493
 */
494
int ssh_buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data)
495
0
{
496
0
    int rc;
497
498
0
    rc = ssh_buffer_add_data(buffer, &data, sizeof(data));
499
0
    if (rc < 0) {
500
0
        return -1;
501
0
    }
502
503
0
    return 0;
504
0
}
505
506
/**
507
 * @internal
508
 *
509
 * @brief Add a 8 bits unsigned integer to the tail of a buffer.
510
 *
511
 * @param[in]  buffer   The buffer to add the integer.
512
 *
513
 * @param[in]  data     The 8 bits integer to add.
514
 *
515
 * @return              0 on success, -1 on error.
516
 */
517
int ssh_buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data)
518
313k
{
519
313k
    int rc;
520
521
313k
    rc = ssh_buffer_add_data(buffer, &data, sizeof(uint8_t));
522
313k
    if (rc < 0) {
523
0
        return -1;
524
0
    }
525
526
313k
    return 0;
527
313k
}
528
529
/**
530
 * @internal
531
 *
532
 * @brief Add data at the head of a buffer.
533
 *
534
 * @param[in]  buffer   The buffer to add the data.
535
 *
536
 * @param[in]  data     The data to prepend.
537
 *
538
 * @param[in]  len      The length of data to prepend.
539
 *
540
 * @return              0 on success, -1 on error.
541
 */
542
int ssh_buffer_prepend_data(struct ssh_buffer_struct *buffer, const void *data,
543
218k
    uint32_t len) {
544
218k
  buffer_verify(buffer);
545
546
218k
  if(len <= buffer->pos){
547
    /* It's possible to insert data between begin and pos */
548
0
    memcpy(buffer->data + (buffer->pos - len), data, len);
549
0
    buffer->pos -= len;
550
0
    buffer_verify(buffer);
551
0
    return 0;
552
0
  }
553
  /* pos isn't high enough */
554
218k
  if (buffer->used - buffer->pos + len < len) {
555
0
    return -1;
556
0
  }
557
558
218k
  if (buffer->allocated < (buffer->used - buffer->pos + len)) {
559
936
    if (realloc_buffer(buffer, buffer->used - buffer->pos + len) < 0) {
560
0
      return -1;
561
0
    }
562
936
  }
563
218k
  memmove(buffer->data + len, buffer->data + buffer->pos, buffer->used - buffer->pos);
564
218k
  memcpy(buffer->data, data, len);
565
218k
  buffer->used += len - buffer->pos;
566
218k
  buffer->pos = 0;
567
218k
  buffer_verify(buffer);
568
218k
  return 0;
569
218k
}
570
571
/**
572
 * @internal
573
 *
574
 * @brief Append data from a buffer to the tail of another buffer.
575
 *
576
 * @param[in]  buffer   The destination buffer.
577
 *
578
 * @param[in]  source   The source buffer to append. It doesn't take the
579
 *                      position of the buffer into account.
580
 *
581
 * @return              0 on success, -1 on error.
582
 */
583
int ssh_buffer_add_buffer(struct ssh_buffer_struct *buffer,
584
    struct ssh_buffer_struct *source)
585
0
{
586
0
    int rc;
587
588
0
    rc = ssh_buffer_add_data(buffer,
589
0
                             ssh_buffer_get(source),
590
0
                             ssh_buffer_get_len(source));
591
0
    if (rc < 0) {
592
0
        return -1;
593
0
    }
594
595
0
    return 0;
596
0
}
597
598
/**
599
 * @brief Get a pointer to the head of a buffer at the current position.
600
 *
601
 * @param[in]  buffer   The buffer to get the head pointer.
602
 *
603
 * @return              A pointer to the data from current position.
604
 *
605
 * @see ssh_buffer_get_len()
606
 */
607
1.94M
void *ssh_buffer_get(struct ssh_buffer_struct *buffer){
608
1.94M
    return buffer->data + buffer->pos;
609
1.94M
}
610
611
/**
612
 * @brief Get the length of the buffer from the current position.
613
 *
614
 * @param[in]  buffer   The buffer to get the length from.
615
 *
616
 * @return              The length of the buffer.
617
 *
618
 * @see ssh_buffer_get()
619
 */
620
4.14M
uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){
621
4.14M
  buffer_verify(buffer);
622
4.14M
  return buffer->used - buffer->pos;
623
4.14M
}
624
625
/**
626
 * @internal
627
 *
628
 * @brief Duplicate an existing buffer.
629
 *
630
 * Creates a new ssh_buffer and copies all data from the source buffer.
631
 * The new buffer preserves the secure flag setting of the source.
632
 *
633
 * @param[in]  buffer   The buffer to duplicate. Can be NULL.
634
 *
635
 * @return              A new buffer containing a copy of the data on success,
636
 *                      NULL on failure or if buffer is NULL.
637
 *
638
 * @see ssh_buffer_free()
639
 */
640
ssh_buffer ssh_buffer_dup(const ssh_buffer buffer)
641
0
{
642
0
    ssh_buffer new_buffer = NULL;
643
0
    int rc;
644
645
0
    if (buffer == NULL) {
646
0
        return NULL;
647
0
    }
648
649
0
    buffer_verify(buffer);
650
651
0
    new_buffer = ssh_buffer_new();
652
0
    if (new_buffer == NULL) {
653
0
        return NULL;
654
0
    }
655
656
0
    new_buffer->secure = buffer->secure;
657
658
0
    if (ssh_buffer_get_len(buffer) > 0) {
659
0
        rc = ssh_buffer_add_data(new_buffer,
660
0
                                 ssh_buffer_get(buffer),
661
0
                                 ssh_buffer_get_len(buffer));
662
0
        if (rc != SSH_OK) {
663
0
            ssh_buffer_free(new_buffer);
664
0
            return NULL;
665
0
        }
666
0
    }
667
668
0
    buffer_verify(new_buffer);
669
0
    return new_buffer;
670
0
}
671
672
/**
673
 * @internal
674
 *
675
 * @brief Advance the position in the buffer.
676
 *
677
 * This has effect to "eat" bytes at head of the buffer.
678
 *
679
 * @param[in]  buffer   The buffer to advance the position.
680
 *
681
 * @param[in]  len      The number of bytes to eat.
682
 *
683
 * @return              The new size of the buffer.
684
 */
685
1.00M
uint32_t ssh_buffer_pass_bytes(struct ssh_buffer_struct *buffer, uint32_t len){
686
1.00M
    buffer_verify(buffer);
687
688
1.00M
    if (buffer->pos + len < len || buffer->used < buffer->pos + len) {
689
0
        return 0;
690
0
    }
691
692
1.00M
    buffer->pos+=len;
693
    /* if the buffer is empty after having passed the whole bytes into it, we can clean it */
694
1.00M
    if(buffer->pos==buffer->used){
695
463k
        buffer->pos=0;
696
463k
        buffer->used=0;
697
463k
    }
698
1.00M
    buffer_verify(buffer);
699
1.00M
    return len;
700
1.00M
}
701
702
/**
703
 * @internal
704
 *
705
 * @brief Cut the end of the buffer.
706
 *
707
 * @param[in]  buffer   The buffer to cut.
708
 *
709
 * @param[in]  len      The number of bytes to remove from the tail.
710
 *
711
 * @return              The new size of the buffer.
712
 */
713
660k
uint32_t ssh_buffer_pass_bytes_end(struct ssh_buffer_struct *buffer, uint32_t len){
714
660k
  buffer_verify(buffer);
715
716
660k
  if (buffer->used < len) {
717
0
      return 0;
718
0
  }
719
720
660k
  buffer->used-=len;
721
660k
  buffer_verify(buffer);
722
660k
  return len;
723
660k
}
724
725
/**
726
 * @brief Get the remaining data out of the buffer and adjust the read pointer.
727
 *
728
 * @param[in]  buffer   The buffer to read.
729
 *
730
 * @param[in]  data     The data buffer where to store the data.
731
 *
732
 * @param[in]  len      The length to read from the buffer.
733
 *
734
 * @returns             0 if there is not enough data in buffer, len otherwise.
735
 */
736
uint32_t ssh_buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint32_t len)
737
1.59M
{
738
1.59M
    int rc;
739
740
    /*
741
     * Check for a integer overflow first, then check if not enough data is in
742
     * the buffer.
743
     */
744
1.59M
    rc = ssh_buffer_validate_length(buffer, len);
745
1.59M
    if (rc != SSH_OK) {
746
0
        return 0;
747
0
    }
748
1.59M
    memcpy(data,buffer->data+buffer->pos,len);
749
1.59M
    buffer->pos+=len;
750
1.59M
    return len;   /* no yet support for partial reads (is it really needed ?? ) */
751
1.59M
}
752
753
/**
754
 * @internal
755
 *
756
 * @brief Get a 8 bits unsigned int out of the buffer and adjust the read
757
 * pointer.
758
 *
759
 * @param[in]  buffer   The buffer to read.
760
 *
761
 * @param[in]  data     A pointer to a uint8_t where to store the data.
762
 *
763
 * @returns             0 if there is not enough data in buffer, 1 otherwise.
764
 */
765
437k
uint32_t ssh_buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
766
437k
    return ssh_buffer_get_data(buffer,data,sizeof(uint8_t));
767
437k
}
768
769
/**
770
 * @internal
771
 *
772
 * @brief gets a 32 bits unsigned int out of the buffer. Adjusts the read pointer.
773
 *
774
 * @param[in]  buffer   The buffer to read.
775
 *
776
 * @param[in]  data     A pointer to a uint32_t where to store the data.
777
 *
778
 * @returns             0 if there is not enough data in buffer, 4 otherwise.
779
 */
780
640k
uint32_t ssh_buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
781
640k
    return ssh_buffer_get_data(buffer,data,sizeof(uint32_t));
782
640k
}
783
/**
784
 * @internal
785
 *
786
 * @brief Get a 64 bits unsigned int out of the buffer and adjusts the read
787
 * pointer.
788
 *
789
 * @param[in]  buffer   The buffer to read.
790
 *
791
 * @param[in]  data     A pointer to a uint64_t where to store the data.
792
 *
793
 * @returns             0 if there is not enough data in buffer, 8 otherwise.
794
 */
795
0
uint32_t ssh_buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
796
0
    return ssh_buffer_get_data(buffer,data,sizeof(uint64_t));
797
0
}
798
799
/**
800
 * @brief Validates that the given length can be obtained from the buffer.
801
 *
802
 * @param[in]  buffer  The buffer to read from.
803
 *
804
 * @param[in]  len     The length to be checked.
805
 *
806
 * @return             SSH_OK if the length is valid, SSH_ERROR otherwise.
807
 */
808
int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len)
809
2.06M
{
810
2.06M
    if (buffer == NULL || buffer->pos + len < len ||
811
2.06M
        buffer->pos + len > buffer->used) {
812
0
        return SSH_ERROR;
813
0
    }
814
815
2.06M
    return SSH_OK;
816
2.06M
}
817
818
/**
819
 * @internal
820
 *
821
 * @brief Get an SSH String out of the buffer and adjust the read pointer.
822
 *
823
 * @param[in]  buffer   The buffer to read.
824
 *
825
 * @returns             The SSH String, NULL on error.
826
 */
827
struct ssh_string_struct *
828
ssh_buffer_get_ssh_string(struct ssh_buffer_struct *buffer)
829
352k
{
830
352k
    uint32_t stringlen;
831
352k
    uint32_t hostlen;
832
352k
    struct ssh_string_struct *str = NULL;
833
352k
    int rc;
834
835
352k
    rc = ssh_buffer_get_u32(buffer, &stringlen);
836
352k
    if (rc == 0) {
837
0
        return NULL;
838
0
    }
839
352k
    hostlen = ntohl(stringlen);
840
    /* verify if there is enough space in buffer to get it */
841
352k
    rc = ssh_buffer_validate_length(buffer, hostlen);
842
352k
    if (rc != SSH_OK) {
843
0
      return NULL; /* it is indeed */
844
0
    }
845
352k
    str = ssh_string_new(hostlen);
846
352k
    if (str == NULL) {
847
0
        return NULL;
848
0
    }
849
850
352k
    stringlen = ssh_buffer_get_data(buffer, ssh_string_data(str), hostlen);
851
352k
    if (stringlen != hostlen) {
852
        /* should never happen */
853
0
        SAFE_FREE(str);
854
0
        return NULL;
855
0
    }
856
857
352k
    return str;
858
352k
}
859
860
/**
861
 * @brief Pre-calculate the size we need for packing the buffer.
862
 *
863
 * This makes sure that enough memory is allocated for packing the buffer and
864
 * we only have to do one memory allocation.
865
 *
866
 * @param[in]  buffer    The buffer to allocate
867
 *
868
 * @param[in]  format    A format string of arguments.
869
 *
870
 * @param[in]  argc      The number of arguments.
871
 *
872
 * @param[in]  ap        The va_list of arguments.
873
 *
874
 * @return SSH_OK on success, SSH_ERROR on error.
875
 */
876
static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
877
                                       const char *format,
878
                                       size_t argc,
879
                                       va_list ap)
880
403k
{
881
403k
    const char *p = NULL;
882
403k
    ssh_string string = NULL;
883
403k
    char *cstring = NULL;
884
403k
    bignum b = NULL;
885
403k
    size_t needed_size = 0;
886
403k
    size_t len;
887
403k
    size_t count;
888
403k
    int rc = SSH_OK;
889
890
1.36M
    for (p = format, count = 0; *p != '\0'; p++, count++) {
891
        /* Invalid number of arguments passed */
892
961k
        if (count > argc) {
893
0
            return SSH_ERROR;
894
0
        }
895
896
961k
        switch(*p) {
897
210k
        case 'b':
898
210k
            va_arg(ap, unsigned int);
899
210k
            needed_size += sizeof(uint8_t);
900
210k
            break;
901
0
        case 'w':
902
0
            va_arg(ap, unsigned int);
903
0
            needed_size += sizeof(uint16_t);
904
0
            break;
905
308k
        case 'd':
906
308k
            va_arg(ap, uint32_t);
907
308k
            needed_size += sizeof(uint32_t);
908
308k
            break;
909
0
        case 'q':
910
0
            va_arg(ap, uint64_t);
911
0
            needed_size += sizeof(uint64_t);
912
0
            break;
913
77.7k
        case 'S':
914
77.7k
            string = va_arg(ap, ssh_string);
915
77.7k
            needed_size += sizeof(uint32_t) + ssh_string_len(string);
916
77.7k
            string = NULL;
917
77.7k
            break;
918
155k
        case 's':
919
155k
            cstring = va_arg(ap, char *);
920
155k
            needed_size += sizeof(uint32_t) + strlen(cstring);
921
155k
            cstring = NULL;
922
155k
            break;
923
210k
        case 'P':
924
210k
            len = va_arg(ap, size_t);
925
210k
            needed_size += len;
926
210k
            va_arg(ap, void *);
927
210k
            count++; /* increase argument count */
928
210k
            break;
929
0
        case 'B':
930
0
            b = va_arg(ap, bignum);
931
            /* The bignum bytes + 1 for possible padding */
932
0
            needed_size += sizeof(uint32_t) + bignum_num_bytes(b) + 1;
933
0
            break;
934
0
        case 't':
935
0
            cstring = va_arg(ap, char *);
936
0
            needed_size += strlen(cstring);
937
0
            cstring = NULL;
938
0
            break;
939
0
        default:
940
0
            SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
941
0
            rc = SSH_ERROR;
942
961k
        }
943
961k
        if (rc != SSH_OK){
944
0
            break;
945
0
        }
946
961k
    }
947
948
403k
    if (argc != count) {
949
0
        return SSH_ERROR;
950
0
    }
951
952
403k
    if (rc != SSH_ERROR){
953
        /*
954
         * Check if our canary is intact, if not, something really bad happened.
955
         */
956
403k
        uint32_t canary = va_arg(ap, uint32_t);
957
403k
        if (canary != SSH_BUFFER_PACK_END) {
958
0
            abort();
959
0
        }
960
403k
    }
961
962
403k
    rc = ssh_buffer_allocate_size(buffer, (uint32_t)needed_size);
963
403k
    if (rc != 0) {
964
0
        return SSH_ERROR;
965
0
    }
966
967
403k
    return SSH_OK;
968
403k
}
969
970
/** @internal
971
 * @brief Add multiple values in a buffer on a single function call
972
 * 
973
 * @param[in] buffer    The buffer to add to
974
 * @param[in] format    A format string of arguments.
975
 * @param[in] argc      Number of arguments passed after format.
976
 * @param[in] ap        A va_list of arguments.
977
 * 
978
 * @returns             SSH_OK on success
979
 *                      SSH_ERROR on error
980
 * @see ssh_buffer_add_format() for format list values.
981
 */
982
static int
983
ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
984
                   const char *format,
985
                   size_t argc,
986
                   va_list ap)
987
403k
{
988
403k
    int rc = SSH_ERROR;
989
403k
    const char *p = NULL;
990
403k
    union {
991
403k
        uint8_t byte;
992
403k
        uint16_t word;
993
403k
        uint32_t dword;
994
403k
        uint64_t qword;
995
403k
        ssh_string string;
996
403k
        void *data;
997
403k
    } o;
998
403k
    char *cstring = NULL;
999
403k
    bignum b;
1000
403k
    size_t len;
1001
403k
    size_t count;
1002
1003
403k
    if (argc > 256) {
1004
0
        return SSH_ERROR;
1005
0
    }
1006
1007
1.36M
    for (p = format, count = 0; *p != '\0'; p++, count++) {
1008
        /* Invalid number of arguments passed */
1009
961k
        if (count > argc) {
1010
0
            return SSH_ERROR;
1011
0
        }
1012
1013
961k
        switch(*p) {
1014
210k
        case 'b':
1015
210k
            o.byte = (uint8_t)va_arg(ap, unsigned int);
1016
210k
            rc = ssh_buffer_add_u8(buffer, o.byte);
1017
210k
            break;
1018
0
        case 'w':
1019
0
            o.word = (uint16_t)va_arg(ap, unsigned int);
1020
0
            o.word = htons(o.word);
1021
0
            rc = ssh_buffer_add_u16(buffer, o.word);
1022
0
            break;
1023
308k
        case 'd':
1024
308k
            o.dword = va_arg(ap, uint32_t);
1025
308k
            o.dword = htonl(o.dword);
1026
308k
            rc = ssh_buffer_add_u32(buffer, o.dword);
1027
308k
            break;
1028
0
        case 'q':
1029
0
            o.qword = va_arg(ap, uint64_t);
1030
0
            o.qword = htonll(o.qword);
1031
0
            rc = ssh_buffer_add_u64(buffer, o.qword);
1032
0
            break;
1033
77.7k
        case 'S':
1034
77.7k
            o.string = va_arg(ap, ssh_string);
1035
77.7k
            rc = ssh_buffer_add_ssh_string(buffer, o.string);
1036
77.7k
            o.string = NULL;
1037
77.7k
            break;
1038
155k
        case 's':
1039
155k
            cstring = va_arg(ap, char *);
1040
155k
            len = strlen(cstring);
1041
155k
            if (len > UINT32_MAX) {
1042
0
                rc = SSH_ERROR;
1043
0
                break;
1044
0
            }
1045
155k
            o.dword = (uint32_t)len;
1046
155k
            rc = ssh_buffer_add_u32(buffer, htonl(o.dword));
1047
155k
            if (rc == SSH_OK){
1048
155k
                rc = ssh_buffer_add_data(buffer, cstring, o.dword);
1049
155k
            }
1050
155k
            cstring = NULL;
1051
155k
            break;
1052
210k
        case 'P':
1053
210k
            len = va_arg(ap, size_t);
1054
210k
            if (len > UINT32_MAX) {
1055
0
                rc = SSH_ERROR;
1056
0
                break;
1057
0
            }
1058
1059
210k
            o.data = va_arg(ap, void *);
1060
210k
            count++; /* increase argument count */
1061
1062
210k
            rc = ssh_buffer_add_data(buffer, o.data, (uint32_t)len);
1063
210k
            o.data = NULL;
1064
210k
            break;
1065
0
        case 'B':
1066
0
            b = va_arg(ap, bignum);
1067
0
            o.string = ssh_make_bignum_string(b);
1068
0
            if(o.string == NULL){
1069
0
                rc = SSH_ERROR;
1070
0
                break;
1071
0
            }
1072
0
            rc = ssh_buffer_add_ssh_string(buffer, o.string);
1073
0
            SAFE_FREE(o.string);
1074
0
            break;
1075
0
        case 't':
1076
0
            cstring = va_arg(ap, char *);
1077
0
            len = strlen(cstring);
1078
0
            if (len > UINT32_MAX) {
1079
0
                rc = SSH_ERROR;
1080
0
                break;
1081
0
            }
1082
0
            rc = ssh_buffer_add_data(buffer, cstring, (uint32_t)len);
1083
0
            cstring = NULL;
1084
0
            break;
1085
0
        default:
1086
0
            SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
1087
0
            rc = SSH_ERROR;
1088
961k
        }
1089
961k
        if (rc != SSH_OK){
1090
0
            break;
1091
0
        }
1092
961k
    }
1093
1094
403k
    if (argc != count) {
1095
0
        return SSH_ERROR;
1096
0
    }
1097
1098
403k
    if (rc != SSH_ERROR){
1099
        /* Check if our canary is intact, if not something really bad happened */
1100
403k
        uint32_t canary = va_arg(ap, uint32_t);
1101
403k
        if (canary != SSH_BUFFER_PACK_END) {
1102
0
            abort();
1103
0
        }
1104
403k
    }
1105
403k
    return rc;
1106
403k
}
1107
1108
/** @internal
1109
 * @brief Add multiple values in a buffer on a single function call
1110
 * @param[in] buffer    The buffer to add to
1111
 * @param[in] format    A format string of arguments. This string contains single
1112
 *                      letters describing the order and type of arguments:
1113
 *                         'b': uint8_t  (pushed in network byte order)
1114
 *                         'w': uint16_t (pushed in network byte order)
1115
 *                         'd': uint32_t (pushed in network byte order)
1116
 *                         'q': uint64_t (pushed in network byte order)
1117
 *                         'S': ssh_string
1118
 *                         's': char * (C string, pushed as SSH string)
1119
 *                         't': char * (C string, pushed as free text)
1120
 *                         'P': size_t, void * (len of data, pointer to data)
1121
 *                              only pushes data.
1122
 *                         'B': bignum (pushed as SSH string)
1123
 * @param[in] argc      Number of arguments passed after format.
1124
 * @param[in] ...       Arguments as described by the format string.
1125
 * 
1126
 * @returns             SSH_OK on success
1127
 *                      SSH_ERROR on error
1128
 * @warning             when using 'P' with a constant size (e.g. 8), do not
1129
 *                      forget to cast to (size_t).
1130
 */
1131
int _ssh_buffer_pack(struct ssh_buffer_struct *buffer,
1132
                     const char *format,
1133
                     size_t argc,
1134
                     ...)
1135
403k
{
1136
403k
    va_list ap;
1137
403k
    int rc;
1138
1139
403k
    if (argc > 256) {
1140
0
        return SSH_ERROR;
1141
0
    }
1142
1143
403k
    va_start(ap, argc);
1144
403k
    rc = ssh_buffer_pack_allocate_va(buffer, format, argc, ap);
1145
403k
    va_end(ap);
1146
1147
403k
    if (rc != SSH_OK) {
1148
0
        return rc;
1149
0
    }
1150
1151
403k
    va_start(ap, argc);
1152
403k
    rc = ssh_buffer_pack_va(buffer, format, argc, ap);
1153
403k
    va_end(ap);
1154
1155
403k
    return rc;
1156
403k
}
1157
1158
/** @internal
1159
 * @brief Get multiple values from a buffer on a single function call
1160
 * @param[in] buffer    The buffer to get from
1161
 * @param[in] format    A format string of arguments.
1162
 * @param[in] argc      Number of arguments passed in the va_list.
1163
 * @param[in] ap        A va_list of arguments.
1164
 * 
1165
 * @returns             SSH_OK on success
1166
 *                      SSH_ERROR on error
1167
 * @see ssh_buffer_get_format() for format list values.
1168
 */
1169
int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
1170
                         const char *format,
1171
                         size_t argc,
1172
                         va_list ap)
1173
192k
{
1174
192k
    int rc = SSH_ERROR;
1175
192k
    const char *p = format, *last = NULL;
1176
192k
    union {
1177
192k
        uint8_t *byte;
1178
192k
        uint16_t *word;
1179
192k
        uint32_t *dword;
1180
192k
        uint64_t *qword;
1181
192k
        ssh_string *string;
1182
192k
        char **cstring;
1183
192k
        bignum *bignum;
1184
192k
        void **data;
1185
192k
    } o;
1186
192k
    size_t len;
1187
192k
    uint32_t rlen, max_len;
1188
192k
    ssh_string tmp_string = NULL;
1189
192k
    va_list ap_copy;
1190
192k
    size_t count;
1191
1192
192k
    max_len = ssh_buffer_get_len(buffer);
1193
1194
    /* copy the argument list in case a rollback is needed */
1195
192k
    va_copy(ap_copy, ap);
1196
1197
192k
    if (argc > 256) {
1198
0
        rc = SSH_ERROR;
1199
0
        goto cleanup;
1200
0
    }
1201
1202
532k
    for (count = 0; *p != '\0'; p++, count++) {
1203
        /* Invalid number of arguments passed */
1204
339k
        if (count > argc) {
1205
0
            rc = SSH_ERROR;
1206
0
            goto cleanup;
1207
0
        }
1208
1209
339k
        rc = SSH_ERROR;
1210
339k
        switch (*p) {
1211
8.64k
        case 'b':
1212
8.64k
            o.byte = va_arg(ap, uint8_t *);
1213
8.64k
            rlen = ssh_buffer_get_u8(buffer, o.byte);
1214
8.64k
            rc = rlen==1 ? SSH_OK : SSH_ERROR;
1215
8.64k
            break;
1216
0
        case 'w':
1217
0
            o.word = va_arg(ap,  uint16_t *);
1218
0
            rlen = ssh_buffer_get_data(buffer, o.word, sizeof(uint16_t));
1219
0
            if (rlen == 2) {
1220
0
                *o.word = ntohs(*o.word);
1221
0
                rc = SSH_OK;
1222
0
            }
1223
0
            break;
1224
158k
        case 'd':
1225
158k
            o.dword = va_arg(ap, uint32_t *);
1226
158k
            rlen = ssh_buffer_get_u32(buffer, o.dword);
1227
158k
            if (rlen == 4) {
1228
158k
                *o.dword = ntohl(*o.dword);
1229
158k
                rc = SSH_OK;
1230
158k
            }
1231
158k
            break;
1232
0
        case 'q':
1233
0
            o.qword = va_arg(ap, uint64_t*);
1234
0
            rlen = ssh_buffer_get_u64(buffer, o.qword);
1235
0
            if (rlen == 8) {
1236
0
                *o.qword = ntohll(*o.qword);
1237
0
                rc = SSH_OK;
1238
0
            }
1239
0
            break;
1240
0
        case 'B':
1241
0
            o.bignum = va_arg(ap, bignum *);
1242
0
            *o.bignum = NULL;
1243
0
            tmp_string = ssh_buffer_get_ssh_string(buffer);
1244
0
            if (tmp_string == NULL) {
1245
0
                break;
1246
0
            }
1247
0
            *o.bignum = ssh_make_string_bn(tmp_string);
1248
0
            ssh_string_burn(tmp_string);
1249
0
            SSH_STRING_FREE(tmp_string);
1250
0
            rc = (*o.bignum != NULL) ? SSH_OK : SSH_ERROR;
1251
0
            break;
1252
43.2k
        case 'S':
1253
43.2k
            o.string = va_arg(ap, ssh_string *);
1254
43.2k
            *o.string = ssh_buffer_get_ssh_string(buffer);
1255
43.2k
            rc = *o.string != NULL ? SSH_OK : SSH_ERROR;
1256
43.2k
            o.string = NULL;
1257
43.2k
            break;
1258
120k
        case 's': {
1259
120k
            uint32_t u32len = 0;
1260
1261
120k
            o.cstring = va_arg(ap, char **);
1262
120k
            *o.cstring = NULL;
1263
120k
            rlen = ssh_buffer_get_u32(buffer, &u32len);
1264
120k
            if (rlen != 4){
1265
0
                break;
1266
0
            }
1267
120k
            u32len = ntohl(u32len);
1268
120k
            if (u32len > max_len - 1) {
1269
0
                break;
1270
0
            }
1271
1272
120k
            rc = ssh_buffer_validate_length(buffer, u32len);
1273
120k
            if (rc != SSH_OK) {
1274
0
                break;
1275
0
            }
1276
1277
120k
            *o.cstring = malloc(u32len + 1);
1278
120k
            if (*o.cstring == NULL){
1279
0
                rc = SSH_ERROR;
1280
0
                break;
1281
0
            }
1282
120k
            rlen = ssh_buffer_get_data(buffer, *o.cstring, u32len);
1283
120k
            if (rlen != u32len) {
1284
0
                SAFE_FREE(*o.cstring);
1285
0
                rc = SSH_ERROR;
1286
0
                break;
1287
0
            }
1288
120k
            (*o.cstring)[u32len] = '\0';
1289
120k
            o.cstring = NULL;
1290
120k
            rc = SSH_OK;
1291
120k
            break;
1292
120k
        }
1293
8.64k
        case 'P':
1294
8.64k
            len = va_arg(ap, size_t);
1295
8.64k
            if (len > max_len - 1) {
1296
0
                rc = SSH_ERROR;
1297
0
                break;
1298
0
            }
1299
1300
8.64k
            rc = ssh_buffer_validate_length(buffer, len);
1301
8.64k
            if (rc != SSH_OK) {
1302
0
                break;
1303
0
            }
1304
1305
8.64k
            o.data = va_arg(ap, void **);
1306
8.64k
            count++;
1307
1308
8.64k
            *o.data = malloc(len);
1309
8.64k
            if(*o.data == NULL){
1310
0
                rc = SSH_ERROR;
1311
0
                break;
1312
0
            }
1313
8.64k
            rlen = ssh_buffer_get_data(buffer, *o.data, (uint32_t)len);
1314
8.64k
            if (rlen != len){
1315
0
                SAFE_FREE(*o.data);
1316
0
                rc = SSH_ERROR;
1317
0
                break;
1318
0
            }
1319
8.64k
            o.data = NULL;
1320
8.64k
            rc = SSH_OK;
1321
8.64k
            break;
1322
0
        default:
1323
0
            SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
1324
339k
        }
1325
339k
        if (rc != SSH_OK) {
1326
0
            break;
1327
0
        }
1328
339k
    }
1329
1330
192k
    if (argc != count) {
1331
0
        rc = SSH_ERROR;
1332
0
    }
1333
1334
192k
cleanup:
1335
192k
    if (rc != SSH_ERROR){
1336
        /* Check if our canary is intact, if not something really bad happened */
1337
192k
        uint32_t canary = va_arg(ap, uint32_t);
1338
192k
        if (canary != SSH_BUFFER_PACK_END){
1339
0
            abort();
1340
0
        }
1341
192k
    }
1342
1343
192k
    if (rc != SSH_OK){
1344
        /* Reset the format string and erase everything that was allocated */
1345
0
        last = p;
1346
0
        for(p=format;p<last;++p){
1347
0
            switch(*p){
1348
0
            case 'b':
1349
0
                o.byte = va_arg(ap_copy, uint8_t *);
1350
0
                if (buffer->secure) {
1351
0
                    ssh_burn(o.byte, sizeof(uint8_t));
1352
0
                    break;
1353
0
                }
1354
0
                break;
1355
0
            case 'w':
1356
0
                o.word = va_arg(ap_copy, uint16_t *);
1357
0
                if (buffer->secure) {
1358
0
                    ssh_burn(o.word, sizeof(uint16_t));
1359
0
                    break;
1360
0
                }
1361
0
                break;
1362
0
            case 'd':
1363
0
                o.dword = va_arg(ap_copy, uint32_t *);
1364
0
                if (buffer->secure) {
1365
0
                    ssh_burn(o.dword, sizeof(uint32_t));
1366
0
                    break;
1367
0
                }
1368
0
                break;
1369
0
            case 'q':
1370
0
                o.qword = va_arg(ap_copy, uint64_t *);
1371
0
                if (buffer->secure) {
1372
0
                    ssh_burn(o.qword, sizeof(uint64_t));
1373
0
                    break;
1374
0
                }
1375
0
                break;
1376
0
            case 'B':
1377
0
                o.bignum = va_arg(ap_copy, bignum *);
1378
0
                bignum_safe_free(*o.bignum);
1379
0
                break;
1380
0
            case 'S':
1381
0
                o.string = va_arg(ap_copy, ssh_string *);
1382
0
                if (buffer->secure) {
1383
0
                    ssh_string_burn(*o.string);
1384
0
                }
1385
0
                SAFE_FREE(*o.string);
1386
0
                break;
1387
0
            case 's':
1388
0
                o.cstring = va_arg(ap_copy, char **);
1389
0
                if (buffer->secure) {
1390
0
                    ssh_burn(*o.cstring, strlen(*o.cstring));
1391
0
                }
1392
0
                SAFE_FREE(*o.cstring);
1393
0
                break;
1394
0
            case 'P':
1395
0
                len = va_arg(ap_copy, size_t);
1396
0
                o.data = va_arg(ap_copy, void **);
1397
0
                if (buffer->secure) {
1398
0
                    ssh_burn(*o.data, len);
1399
0
                }
1400
0
                SAFE_FREE(*o.data);
1401
0
                break;
1402
0
            default:
1403
0
                (void)va_arg(ap_copy, void *);
1404
0
                break;
1405
0
            }
1406
0
        }
1407
0
    }
1408
192k
    va_end(ap_copy);
1409
1410
192k
    return rc;
1411
192k
}
1412
1413
/** @internal
1414
 * @brief Get multiple values from a buffer on a single function call
1415
 * @param[in] buffer    The buffer to get from
1416
 * @param[in] format    A format string of arguments. This string contains single
1417
 *                      letters describing the order and type of arguments:
1418
 *                         'b': uint8_t *  (pulled in network byte order)
1419
 *                         'w': uint16_t * (pulled in network byte order)
1420
 *                         'd': uint32_t * (pulled in network byte order)
1421
 *                         'q': uint64_t * (pulled in network byte order)
1422
 *                         'S': ssh_string *
1423
 *                         's': char ** (C string, pulled as SSH string)
1424
 *                         'P': size_t, void ** (len of data, pointer to data)
1425
 *                              only pulls data.
1426
 *                         'B': bignum * (pulled as SSH string)
1427
 * @param[in] argc      Number of arguments passed after format.
1428
 * @param[in] ...       Arguments as described by the format string.
1429
 * 
1430
 * @returns             SSH_OK on success
1431
 *                      SSH_ERROR on error
1432
 * @warning             when using 'P' with a constant size (e.g. 8), do not
1433
 *                      forget to cast to (size_t).
1434
 */
1435
int _ssh_buffer_unpack(struct ssh_buffer_struct *buffer,
1436
                       const char *format,
1437
                       size_t argc,
1438
                       ...)
1439
192k
{
1440
192k
    va_list ap;
1441
192k
    int rc;
1442
1443
192k
    va_start(ap, argc);
1444
192k
    rc = ssh_buffer_unpack_va(buffer, format, argc, ap);
1445
    va_end(ap);
1446
192k
    return rc;
1447
192k
}
1448
1449
/** @} */