Coverage Report

Created: 2026-01-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/librpc/ndr/ndr_cab.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   routines for marshalling/unmarshalling cab structures
5
6
   Copyright (C) Guenther Deschner 2016
7
8
   This program is free software; you can redistribute it and/or modify
9
   it under the terms of the GNU General Public License as published by
10
   the Free Software Foundation; either version 3 of the License, or
11
   (at your option) any later version.
12
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
18
   You should have received a copy of the GNU General Public License
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
*/
21
22
#include "includes.h"
23
#include "librpc/gen_ndr/ndr_cab.h"
24
#include "librpc/ndr/ndr_compression.h"
25
26
202k
#define OFFSET_OF_FOLDER_COFFCABSTART(folder) (36 /* cfheader size */ + (size_t)(folder)*8)
27
28
_PUBLIC_ void ndr_print_cf_time(struct ndr_print *ndr, const char *name, const struct cf_time *r)
29
88.6k
{
30
88.6k
  uint8_t hour = 0, minute = 0, seconds = 0;
31
88.6k
  char *s;
32
88.6k
  if (r == NULL) { ndr_print_null(ndr); return; }
33
88.6k
  hour = r->time >> 11;
34
88.6k
  minute = (r->time >> 5) & 0x3f;
35
88.6k
  seconds = (r->time << 1) & 0x3e;
36
88.6k
  s = talloc_asprintf(ndr, "%02d:%02d:%02d", hour, minute, seconds);
37
88.6k
  if (s == NULL) { return; }
38
88.6k
  ndr_print_string(ndr, "time", s);
39
88.6k
  TALLOC_FREE(s);
40
88.6k
}
41
42
_PUBLIC_ void ndr_print_cf_date(struct ndr_print *ndr, const char *name, const struct cf_date *r)
43
88.6k
{
44
88.6k
  uint16_t year = 0;
45
88.6k
  uint8_t month = 0, day = 0;
46
88.6k
  char *s;
47
88.6k
  if (r == NULL) { ndr_print_null(ndr); return; }
48
88.6k
  year = (r->date >> 9);
49
88.6k
  year += 1980;
50
88.6k
  month = (r->date >> 5 & 0xf);
51
88.6k
  day = (r->date & 0x1f);
52
88.6k
  s = talloc_asprintf(ndr, "%02"PRIu8"/%02"PRIu8"/%04"PRIu16, day, month, year);
53
88.6k
  if (s == NULL) { return; }
54
88.6k
  ndr_print_string(ndr, "date", s);
55
88.6k
  TALLOC_FREE(s);
56
88.6k
}
57
58
uint32_t ndr_count_cfdata(const struct cab_file *r)
59
995
{
60
995
  uint32_t count = 0, i;
61
62
584k
  for (i = 0; i < r->cfheader.cFolders; i++) {
63
583k
    if (count + r->cffolders[i].cCFData < count) {
64
      /* Integer wrap. */
65
0
      return 0;
66
0
    }
67
583k
    count += r->cffolders[i].cCFData;
68
583k
  }
69
70
995
  return count;
71
995
}
72
73
static uint32_t ndr_cab_compute_checksum(uint8_t *data, uint32_t length, uint32_t seed)
74
765k
{
75
765k
  int num_ulong;
76
765k
  uint32_t checksum;
77
765k
  uint8_t *pb;
78
765k
  uint32_t ul;
79
80
765k
  num_ulong = length / 4;
81
765k
  checksum = seed;
82
765k
  pb = data;
83
84
2.51M
  while (num_ulong-- > 0) {
85
1.74M
    ul = (uint32_t)(*pb++);
86
1.74M
    ul |= (((uint32_t)(*pb++)) <<  8);
87
1.74M
    ul |= (((uint32_t)(*pb++)) << 16);
88
1.74M
    ul |= (((uint32_t)(*pb++)) << 24);
89
90
1.74M
    checksum ^= ul;
91
1.74M
  }
92
93
765k
  ul = 0;
94
95
765k
  switch (length % 4) {
96
704
  case 3:
97
704
    ul |= (((uint32_t)(*pb++)) << 16);
98
704
    FALL_THROUGH;
99
2.05k
  case 2:
100
2.05k
    ul |= (((uint32_t)(*pb++)) <<  8);
101
2.05k
    FALL_THROUGH;
102
4.12k
  case 1:
103
4.12k
    ul |= (uint32_t)(*pb++);
104
4.12k
    FALL_THROUGH;
105
765k
  default:
106
765k
    break;
107
765k
  }
108
109
765k
  checksum ^= ul;
110
111
765k
  return checksum;
112
765k
}
113
114
/* Push all CFDATA of a folder.
115
 *
116
 * This works on a folder level because compression type is set per
117
 * folder, and a compression state can be shared between CFDATA of the
118
 * same folder.
119
 *
120
 * This is not a regular NDR func as we pass the compression type and
121
 * the number of CFDATA as extra arguments
122
 */
123
static enum ndr_err_code ndr_push_folder_cfdata(struct ndr_push *ndr,
124
            const struct CFDATA *r,
125
            enum cf_compress_type cab_ctype,
126
            size_t num_cfdata)
127
202k
{
128
202k
  size_t i;
129
202k
  enum ndr_compression_alg ndr_ctype = 0;
130
131
202k
  ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
132
133
202k
  if (cab_ctype == CF_COMPRESS_MSZIP) {
134
549
    ndr_ctype = NDR_COMPRESSION_MSZIP_CAB;
135
549
    NDR_CHECK(ndr_push_compression_state_init(ndr, ndr_ctype));
136
549
  }
137
138
585k
  for (i = 0; i < num_cfdata; i++, r++) {
139
383k
    uint32_t compressed_length = 0;
140
383k
    uint32_t csum, csumPartial;
141
383k
    size_t compressed_offset, csum_offset, data_offset;
142
143
383k
    if (!r->ab.data) {
144
6
      return ndr_push_error(ndr, NDR_ERR_LENGTH,
145
6
                "NULL uncompressed data blob");
146
6
    }
147
383k
    if (r->ab.length != r->cbUncomp) {
148
41
      return ndr_push_error(ndr, NDR_ERR_LENGTH,
149
41
                "Uncompressed data blob size != uncompressed data size field");
150
41
    }
151
152
    /*
153
     * checksum is a function of the size fields
154
     * and the potentially compressed data bytes,
155
     * which haven't been compressed yet so
156
     * remember offset, write zeroes, fill out
157
     * later
158
     */
159
382k
    csum_offset = ndr->offset;
160
382k
    NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
161
162
    /*
163
     * similarly, we don't know the compressed
164
     * size yet, remember offset, write zeros,
165
     * fill out later
166
     */
167
382k
    compressed_offset = ndr->offset;
168
382k
    NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 0));
169
382k
    NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, r->cbUncomp));
170
171
382k
    data_offset = ndr->offset;
172
173
382k
    switch (cab_ctype) {
174
382k
    case CF_COMPRESS_NONE:
175
      /* just copy the data */
176
382k
      NDR_PUSH_NEED_BYTES(ndr, r->ab.length);
177
382k
      NDR_CHECK(ndr_push_bytes(ndr, r->ab.data, r->ab.length));
178
382k
      compressed_length = r->ab.length;
179
382k
      break;
180
5
    case CF_COMPRESS_LZX:
181
      /*
182
       * we have not yet worked out the details of LZX
183
       * compression
184
       */
185
5
      return NDR_ERR_COMPRESSION;
186
187
466
    case CF_COMPRESS_MSZIP: {
188
466
      struct ndr_push *push_sub, *push_compress;
189
190
      /* compress via subcontext */
191
466
      NDR_CHECK(ndr_push_subcontext_start(ndr, &push_sub, 0, -1));
192
193
      /*
194
       * This assignment replaces a call to
195
       * ndr_push_compression_state_init(push_sub, ndr_ctype))
196
       * here.  This is instead done outside the loop.
197
       */
198
466
      push_sub->cstate = ndr->cstate;
199
200
466
      NDR_CHECK(ndr_push_compression_start(push_sub, &push_compress));
201
466
      ndr_set_flags(&push_compress->flags, LIBNDR_FLAG_REMAINING);
202
466
      NDR_CHECK(ndr_push_DATA_BLOB(push_compress, NDR_SCALARS, r->ab));
203
466
      NDR_CHECK(ndr_push_compression_end(push_sub, push_compress));
204
466
      NDR_CHECK(ndr_push_subcontext_end(ndr, push_sub, 0, -1));
205
466
      compressed_length = push_sub->offset;
206
207
466
      break;
208
466
      }
209
0
    default:
210
0
      return NDR_ERR_BAD_SWITCH;
211
382k
    }
212
213
    /* we can now write the compressed size and the checksum */
214
382k
    SSVAL(ndr->data, compressed_offset, compressed_length);
215
216
    /*
217
     * Create checksum over compressed data.
218
     *
219
     * The 8 bytes are the header size.
220
     *
221
     * We have already have written the checksum and set it to zero,
222
     * earlier. So we know that after the checksum end the value
223
     * for the compressed length comes the blob data.
224
     *
225
     * NDR already did all the checks for integer wraps.
226
     */
227
382k
    csumPartial = ndr_cab_compute_checksum(&ndr->data[data_offset],
228
382k
                   compressed_length, 0);
229
230
    /*
231
     * Checksum over header (compressed and uncompressed length).
232
     *
233
     * The first 4 bytes are the checksum size.
234
     * The second 4 bytes are the size of the compressed and
235
     * uncompressed length fields.
236
     *
237
     * NDR already did all the checks for integer wraps.
238
     */
239
382k
    csum = ndr_cab_compute_checksum(&ndr->data[compressed_offset],
240
382k
            data_offset - compressed_offset,
241
382k
            csumPartial);
242
243
382k
    SIVAL(ndr->data, csum_offset, csum);
244
382k
  }
245
246
202k
  TALLOC_FREE(ndr->cstate);
247
248
202k
  return NDR_ERR_SUCCESS;
249
202k
}
250
251
_PUBLIC_ enum ndr_err_code ndr_push_cab_file(struct ndr_push *ndr, ndr_flags_type ndr_flags, const struct cab_file *r)
252
423
{
253
423
  uint32_t cntr_cffolders_0;
254
423
  uint32_t cntr_cffiles_0;
255
423
  size_t processed_cfdata = 0;
256
423
  {
257
423
    libndr_flags _flags_save_STRUCT = ndr->flags;
258
423
    ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
259
423
    NDR_PUSH_CHECK_FLAGS(ndr, ndr_flags);
260
261
423
    if (ndr_flags & NDR_SCALARS) {
262
423
      uint32_t i;
263
423
      NDR_CHECK(ndr_push_align(ndr, 4));
264
423
      NDR_CHECK(ndr_push_CFHEADER(ndr, NDR_SCALARS, &r->cfheader));
265
202k
      for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (r->cfheader.cFolders); cntr_cffolders_0++) {
266
202k
        NDR_CHECK(ndr_push_CFFOLDER(ndr, NDR_SCALARS, &r->cffolders[cntr_cffolders_0]));
267
202k
      }
268
88.7k
      for (cntr_cffiles_0 = 0; cntr_cffiles_0 < (r->cfheader.cFiles); cntr_cffiles_0++) {
269
88.3k
        NDR_CHECK(ndr_push_CFFILE(ndr, NDR_SCALARS, &r->cffiles[cntr_cffiles_0]));
270
88.3k
      }
271
#if 0
272
      NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_count_cfdata(r)));
273
#endif
274
275
      /* write in the folder header the offset of its first data block */
276
202k
      for (i = 0; i < r->cfheader.cFolders; i++) {
277
202k
        size_t off = OFFSET_OF_FOLDER_COFFCABSTART(i);
278
        /* check that the offset we want to
279
         * write to is always inside our
280
         * current push buffer
281
         */
282
202k
        if (off >= ndr->offset) {
283
0
          return ndr_push_error(ndr, NDR_ERR_OFFSET,
284
0
                    "trying to write past current push buffer size");
285
0
        }
286
202k
        SIVAL(ndr->data, off, ndr->offset);
287
202k
        NDR_CHECK(ndr_push_folder_cfdata(ndr, r->cfdata + processed_cfdata, r->cffolders[i].typeCompress, r->cffolders[i].cCFData));
288
202k
        processed_cfdata += r->cffolders[i].cCFData;
289
202k
      }
290
371
      NDR_CHECK(ndr_push_trailer_align(ndr, 4));
291
371
    }
292
371
    if (ndr_flags & NDR_BUFFERS) {
293
371
    }
294
371
    ndr->flags = _flags_save_STRUCT;
295
371
  }
296
297
298
  /* write total file size in header */
299
371
  SIVAL(ndr->data, 8, ndr->offset);
300
301
371
  return NDR_ERR_SUCCESS;
302
423
}
303
304
305
/* Pull all CFDATA of a folder.
306
 *
307
 * This works on a folder level because compression type is set per
308
 * folder, and a compression state can be shared between CFDATA of the
309
 * same folder.
310
 *
311
 * This is not a regular NDR func as we pass the compression type and
312
 * the number of CFDATA as extra arguments
313
 */
314
static enum ndr_err_code ndr_pull_folder_cfdata(struct ndr_pull *ndr,
315
            struct CFDATA *r,
316
            enum cf_compress_type cab_ctype,
317
            size_t num_cfdata)
318
219k
{
319
219k
  size_t i;
320
219k
  enum ndr_compression_alg ndr_ctype = 0;
321
322
219k
  if (cab_ctype == CF_COMPRESS_MSZIP) {
323
912
    ndr_ctype = NDR_COMPRESSION_MSZIP_CAB;
324
912
    NDR_CHECK(ndr_pull_compression_state_init(ndr, NDR_COMPRESSION_MSZIP_CAB, &ndr->cstate));
325
912
  }
326
327
934k
  for (i = 0; i < num_cfdata; i++, r++) {
328
715k
    NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->csum));
329
715k
    NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbData));
330
715k
    NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->cbUncomp));
331
332
715k
    switch (cab_ctype) {
333
588k
    case CF_COMPRESS_NONE:
334
      /* just copy the data */
335
588k
      NDR_PULL_NEED_BYTES(ndr, r->cbUncomp);
336
588k
      r->ab = data_blob_talloc(ndr->current_mem_ctx,
337
588k
             ndr->data+ndr->offset,
338
588k
             r->cbUncomp);
339
588k
      if (r->ab.data == NULL) {
340
0
        return ndr_pull_error(ndr, NDR_ERR_ALLOC,
341
0
                  "failed to allocate buffer for uncompressed CFDATA block");
342
0
      }
343
588k
      ndr->offset += r->cbUncomp;
344
588k
      break;
345
346
124k
    case CF_COMPRESS_LZX:
347
      /* just copy the data (LZX decompression not implemented yet) */
348
124k
      NDR_PULL_NEED_BYTES(ndr, r->cbData);
349
124k
      r->ab = data_blob_talloc(ndr->current_mem_ctx,
350
124k
             ndr->data+ndr->offset,
351
124k
             r->cbData);
352
124k
      if (r->ab.data == NULL) {
353
0
        return ndr_pull_error(ndr, NDR_ERR_ALLOC,
354
0
                  "failed to allocate buffer for LZX-compressed CFDATA block");
355
0
      }
356
124k
      ndr->offset += r->cbData;
357
124k
      break;
358
359
2.20k
    case CF_COMPRESS_MSZIP: {
360
2.20k
      struct ndr_pull *pull_sub, *pull_compress;
361
2.20k
      NDR_PULL_NEED_BYTES(ndr, r->cbData);
362
      /* decompress via subcontext */
363
2.15k
      NDR_CHECK(ndr_pull_subcontext_start(ndr, &pull_sub, 0, r->cbData));
364
2.15k
      pull_sub->cstate = ndr->cstate;
365
2.15k
      NDR_CHECK(ndr_pull_compression_start(pull_sub, &pull_compress,
366
2.15k
                   ndr_ctype, r->cbUncomp, r->cbData));
367
1.99k
      ndr_set_flags(&pull_compress->flags, LIBNDR_FLAG_REMAINING);
368
1.99k
      NDR_CHECK(ndr_pull_DATA_BLOB(pull_compress, NDR_SCALARS, &r->ab));
369
1.99k
      NDR_CHECK(ndr_pull_compression_end(pull_sub, pull_compress, ndr_ctype, r->cbUncomp));
370
1.99k
      NDR_CHECK(ndr_pull_subcontext_end(ndr, pull_sub, 0, r->cbData));
371
372
1.99k
      break;
373
1.99k
    }
374
1.99k
    default:
375
38
      return NDR_ERR_BAD_SWITCH;
376
715k
    }
377
715k
  }
378
379
218k
  TALLOC_FREE(ndr->cstate);
380
381
218k
  return NDR_ERR_SUCCESS;
382
219k
}
383
384
_PUBLIC_ enum ndr_err_code ndr_pull_cab_file(struct ndr_pull *ndr, ndr_flags_type ndr_flags, struct cab_file *r)
385
1.24k
{
386
1.24k
  uint32_t size_cffolders_0 = 0;
387
1.24k
  uint32_t cntr_cffolders_0;
388
1.24k
  TALLOC_CTX *_mem_save_cffolders_0 = NULL;
389
1.24k
  uint32_t size_cffiles_0 = 0;
390
1.24k
  uint32_t cntr_cffiles_0;
391
1.24k
  TALLOC_CTX *_mem_save_cffiles_0 = NULL;
392
1.24k
  uint32_t size_cfdata_0 = 0;
393
1.24k
  size_t processed_cfdata = 0;
394
1.24k
  TALLOC_CTX *_mem_save_cfdata_0 = NULL;
395
1.24k
  {
396
1.24k
    libndr_flags _flags_save_STRUCT = ndr->flags;
397
1.24k
    ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_LITTLE_ENDIAN|LIBNDR_FLAG_NOALIGN);
398
1.24k
    NDR_PULL_CHECK_FLAGS(ndr, ndr_flags);
399
1.24k
    if (ndr_flags & NDR_SCALARS) {
400
1.24k
      NDR_CHECK(ndr_pull_align(ndr, 4));
401
1.24k
      NDR_CHECK(ndr_pull_CFHEADER(ndr, NDR_SCALARS, &r->cfheader));
402
1.22k
      size_cffolders_0 = r->cfheader.cFolders;
403
1.22k
      NDR_PULL_ALLOC_N(ndr, r->cffolders, size_cffolders_0);
404
1.22k
      _mem_save_cffolders_0 = NDR_PULL_GET_MEM_CTX(ndr);
405
1.22k
      NDR_PULL_SET_MEM_CTX(ndr, r->cffolders, 0);
406
875k
      for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (size_cffolders_0); cntr_cffolders_0++) {
407
874k
        NDR_CHECK(ndr_pull_CFFOLDER(ndr, NDR_SCALARS, &r->cffolders[cntr_cffolders_0]));
408
874k
      }
409
1.16k
      NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cffolders_0, 0);
410
1.16k
      size_cffiles_0 = r->cfheader.cFiles;
411
1.16k
      NDR_PULL_ALLOC_N(ndr, r->cffiles, size_cffiles_0);
412
1.16k
      _mem_save_cffiles_0 = NDR_PULL_GET_MEM_CTX(ndr);
413
1.16k
      NDR_PULL_SET_MEM_CTX(ndr, r->cffiles, 0);
414
322k
      for (cntr_cffiles_0 = 0; cntr_cffiles_0 < (size_cffiles_0); cntr_cffiles_0++) {
415
321k
        NDR_CHECK(ndr_pull_CFFILE(ndr, NDR_SCALARS, &r->cffiles[cntr_cffiles_0]));
416
321k
      }
417
995
      NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cffiles_0, 0);
418
#if 0
419
      NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->cfdata_count));
420
#else
421
995
      r->cfdata_count = ndr_count_cfdata(r);
422
995
#endif
423
995
      size_cfdata_0 = r->cfdata_count;
424
995
      NDR_PULL_ALLOC_N(ndr, r->cfdata, size_cfdata_0);
425
981
      _mem_save_cfdata_0 = NDR_PULL_GET_MEM_CTX(ndr);
426
981
      NDR_PULL_SET_MEM_CTX(ndr, r->cfdata, 0);
427
219k
      for (cntr_cffolders_0 = 0; cntr_cffolders_0 < (size_cffolders_0); cntr_cffolders_0++) {
428
219k
        NDR_CHECK(ndr_pull_folder_cfdata(ndr,
429
219k
                 r->cfdata + processed_cfdata,
430
219k
                 r->cffolders[cntr_cffolders_0].typeCompress,
431
219k
                 r->cffolders[cntr_cffolders_0].cCFData));
432
218k
        processed_cfdata += r->cffolders[cntr_cffolders_0].cCFData;
433
218k
      }
434
423
      NDR_PULL_SET_MEM_CTX(ndr, _mem_save_cfdata_0, 0);
435
423
      NDR_CHECK(ndr_pull_trailer_align(ndr, 4));
436
423
    }
437
423
    if (ndr_flags & NDR_BUFFERS) {
438
423
    }
439
423
    ndr->flags = _flags_save_STRUCT;
440
423
  }
441
0
  return NDR_ERR_SUCCESS;
442
1.24k
}