Coverage Report

Created: 2026-01-16 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/rdpear/common/ndr.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Authentication redirection virtual channel
4
 *
5
 * Copyright 2024 David Fort <contact@hardening-consulting.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
#include <winpr/assert.h>
20
#include <winpr/collections.h>
21
#include <winpr/wlog.h>
22
23
#include <freerdp/log.h>
24
25
#include <rdpear-common/ndr.h>
26
27
#define TAG FREERDP_TAG("ndr")
28
29
0
#define NDR_MAX_CONSTRUCTS 16
30
0
#define NDR_MAX_DEFERRED 50
31
32
struct NdrContext_s
33
{
34
  BYTE version;
35
  BOOL bigEndianDrep;
36
  size_t alignBytes;
37
38
  int currentLevel;
39
  size_t indentLevels[16];
40
41
  int constructLevel;
42
  size_t constructs[NDR_MAX_CONSTRUCTS];
43
44
  wHashTable* refPointers;
45
  size_t ndeferred;
46
  NdrDeferredEntry deferred[NDR_MAX_DEFERRED];
47
48
  UINT32 refIdCounter;
49
};
50
51
NdrContext* ndr_context_new(BOOL bigEndianDrep, BYTE version)
52
0
{
53
0
  NdrContext* ret = calloc(1, sizeof(*ret));
54
0
  if (!ret)
55
0
    return NULL;
56
57
0
  ret->version = version;
58
0
  ret->bigEndianDrep = bigEndianDrep;
59
0
  ret->alignBytes = 4;
60
0
  ret->refPointers = HashTable_New(FALSE);
61
0
  if (!ret->refPointers)
62
0
  {
63
0
    free(ret);
64
0
    return NULL;
65
0
  }
66
67
0
  ndr_context_reset(ret);
68
0
  return ret;
69
0
}
70
71
void ndr_context_reset(NdrContext* context)
72
0
{
73
0
  WINPR_ASSERT(context);
74
75
0
  context->currentLevel = 0;
76
0
  context->constructLevel = -1;
77
0
  memset(context->indentLevels, 0, sizeof(context->indentLevels));
78
79
0
  if (context->refPointers)
80
0
    HashTable_Clear(context->refPointers);
81
0
  context->ndeferred = 0;
82
0
  context->refIdCounter = 0x20000;
83
0
}
84
85
NdrContext* ndr_context_copy(const NdrContext* src)
86
0
{
87
0
  WINPR_ASSERT(src);
88
89
0
  NdrContext* ret = calloc(1, sizeof(*ret));
90
0
  if (!ret)
91
0
    return NULL;
92
93
0
  *ret = *src;
94
95
0
  ret->refPointers = HashTable_New(FALSE);
96
0
  if (!ret->refPointers)
97
0
  {
98
0
    free(ret);
99
0
    return NULL;
100
0
  }
101
102
0
  ndr_context_reset(ret);
103
0
  return ret;
104
0
}
105
106
void ndr_context_free(NdrContext* context)
107
0
{
108
0
  if (context)
109
0
  {
110
0
    HashTable_Free(context->refPointers);
111
0
    free(context);
112
0
  }
113
0
}
114
115
static void ndr_context_bytes_read(NdrContext* context, size_t len)
116
0
{
117
0
  WINPR_ASSERT(context);
118
0
  context->indentLevels[context->currentLevel] += len;
119
0
}
120
121
static void ndr_context_bytes_written(NdrContext* context, size_t len)
122
0
{
123
0
  ndr_context_bytes_read(context, len);
124
0
}
125
126
NdrContext* ndr_read_header(wStream* s)
127
0
{
128
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
129
0
    return NULL;
130
131
0
  BYTE version = Stream_Get_UINT8(s);
132
0
  BYTE drep = Stream_Get_UINT8(s);
133
0
  UINT16 headerLen = Stream_Get_UINT16(s);
134
135
0
  if (headerLen < 4 || !Stream_CheckAndLogRequiredLength(TAG, s, headerLen - 4))
136
0
    return NULL;
137
138
  /* skip filler */
139
0
  Stream_Seek(s, headerLen - 4);
140
141
0
  return ndr_context_new((drep != 0x10), version);
142
0
}
143
144
BOOL ndr_write_header(NdrContext* context, wStream* s)
145
0
{
146
0
  WINPR_ASSERT(context);
147
148
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
149
0
    return FALSE;
150
151
0
  Stream_Write_UINT8(s, context->version);
152
0
  Stream_Write_UINT8(s, context->bigEndianDrep ? 0x00 : 0x10);
153
0
  Stream_Write_UINT16(s, 0x8); /* header len */
154
155
0
  BYTE filler[] = { 0xcc, 0xcc, 0xcc, 0xcc };
156
0
  Stream_Write(s, filler, sizeof(filler));
157
0
  return TRUE;
158
0
}
159
160
BOOL ndr_skip_bytes(NdrContext* context, wStream* s, size_t nbytes)
161
0
{
162
0
  WINPR_ASSERT(context);
163
164
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, nbytes))
165
0
    return FALSE;
166
167
0
  context->indentLevels[context->currentLevel] += nbytes;
168
0
  Stream_Seek(s, nbytes);
169
0
  return TRUE;
170
0
}
171
172
BOOL ndr_read_align(NdrContext* context, wStream* s, size_t sz)
173
0
{
174
0
  WINPR_ASSERT(context);
175
176
0
  size_t rest = context->indentLevels[context->currentLevel] % sz;
177
0
  if (rest)
178
0
  {
179
0
    size_t padding = (sz - rest);
180
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, padding))
181
0
      return FALSE;
182
183
0
    Stream_Seek(s, padding);
184
0
    context->indentLevels[context->currentLevel] += padding;
185
0
  }
186
187
0
  return TRUE;
188
0
}
189
190
BOOL ndr_write_align(NdrContext* context, wStream* s, size_t sz)
191
0
{
192
0
  WINPR_ASSERT(context);
193
194
0
  size_t rest = context->indentLevels[context->currentLevel] % sz;
195
0
  if (rest)
196
0
  {
197
0
    size_t padding = (sz - rest);
198
199
0
    if (!Stream_EnsureRemainingCapacity(s, padding))
200
0
      return FALSE;
201
202
0
    Stream_Zero(s, padding);
203
0
    context->indentLevels[context->currentLevel] += padding;
204
0
  }
205
206
0
  return TRUE;
207
0
}
208
209
BOOL ndr_read_pickle(NdrContext* context, wStream* s)
210
0
{
211
0
  WINPR_ASSERT(context);
212
213
0
  UINT32 v = 0;
214
215
  /* NDR format label */
216
0
  if (!ndr_read_uint32(context, s, &v) || v != 0x20000)
217
0
    return FALSE;
218
219
0
  return TRUE;
220
0
}
221
222
BOOL ndr_write_pickle(NdrContext* context, wStream* s)
223
0
{
224
0
  WINPR_ASSERT(context);
225
226
  /* NDR format label */
227
0
  if (!ndr_write_uint32(context, s, 0x20000))
228
0
    return FALSE;
229
230
0
  return TRUE;
231
0
}
232
233
BOOL ndr_read_constructed(NdrContext* context, wStream* s, wStream* target)
234
0
{
235
0
  WINPR_ASSERT(context);
236
237
0
  UINT32 len = 0;
238
239
  /* len */
240
0
  if (!ndr_read_uint32(context, s, &len))
241
0
    return FALSE;
242
243
  /* padding */
244
0
  if (!ndr_skip_bytes(context, s, 4))
245
0
    return FALSE;
246
247
  /* payload */
248
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
249
0
    return FALSE;
250
251
0
  Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
252
0
  Stream_Seek(s, len);
253
0
  return TRUE;
254
0
}
255
256
BOOL ndr_start_constructed(NdrContext* context, wStream* s)
257
0
{
258
0
  WINPR_ASSERT(context);
259
260
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
261
0
    return FALSE;
262
263
0
  if (context->constructLevel == NDR_MAX_CONSTRUCTS)
264
0
    return FALSE;
265
266
0
  context->constructLevel++;
267
0
  context->constructs[context->constructLevel] = Stream_GetPosition(s);
268
269
0
  Stream_Zero(s, 8);
270
0
  return TRUE;
271
0
}
272
273
BOOL ndr_end_constructed(NdrContext* context, wStream* s)
274
0
{
275
0
  WINPR_ASSERT(context);
276
0
  WINPR_ASSERT(context->constructs);
277
0
  WINPR_ASSERT(context->constructLevel >= 0);
278
279
0
  size_t offset = context->constructs[context->constructLevel];
280
281
0
  wStream staticS = { 0 };
282
0
  Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
283
284
  /* len */
285
0
  const size_t len = Stream_GetPosition(s) - (offset + 8);
286
0
  if (len > UINT32_MAX)
287
0
    return FALSE;
288
0
  if (!ndr_write_uint32(context, &staticS, (UINT32)len))
289
0
    return FALSE;
290
291
0
  return TRUE;
292
0
}
293
294
static size_t ndr_hintsCount(NdrMessageType msgType, const void* hints)
295
0
{
296
0
  WINPR_ASSERT(msgType);
297
298
0
  switch (msgType->arity)
299
0
  {
300
0
    case NDR_ARITY_SIMPLE:
301
0
      return 1;
302
0
    case NDR_ARITY_ARRAYOF:
303
0
      WINPR_ASSERT(hints);
304
0
      return ((const NdrArrayHints*)hints)->count;
305
0
    case NDR_ARITY_VARYING_ARRAYOF:
306
0
      WINPR_ASSERT(hints);
307
0
      return ((const NdrVaryingArrayHints*)hints)->maxLength;
308
0
    default:
309
0
      WINPR_ASSERT(0 && "unknown arity");
310
0
      return 0;
311
0
  }
312
0
}
313
314
BOOL ndr_read_uint8(NdrContext* context, wStream* s, BYTE* v)
315
0
{
316
0
  WINPR_ASSERT(context);
317
318
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
319
0
    return FALSE;
320
321
0
  Stream_Read_UINT8(s, *v);
322
323
0
  ndr_context_bytes_read(context, 1);
324
0
  return TRUE;
325
0
}
326
327
BOOL ndr_read_uint8_(NdrContext* context, wStream* s, const void* hints, void* v)
328
0
{
329
0
  WINPR_UNUSED(hints);
330
0
  return ndr_read_uint8(context, s, (BYTE*)v);
331
0
}
332
333
BOOL ndr_write_uint8(NdrContext* context, wStream* s, BYTE v)
334
0
{
335
0
  if (!Stream_EnsureRemainingCapacity(s, 1))
336
0
    return FALSE;
337
338
0
  Stream_Write_UINT8(s, v);
339
0
  ndr_context_bytes_written(context, 1);
340
0
  return TRUE;
341
0
}
342
343
BOOL ndr_write_uint8_(NdrContext* context, wStream* s, const void* hints, const void* v)
344
0
{
345
0
  WINPR_ASSERT(context);
346
0
  WINPR_ASSERT(s);
347
0
  WINPR_ASSERT(v);
348
0
  WINPR_UNUSED(hints);
349
350
0
  return ndr_write_uint8(context, s, *(const BYTE*)v);
351
0
}
352
353
const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1,    ndr_read_uint8_,
354
                                           ndr_write_uint8_, NULL, NULL };
355
356
NdrMessageType ndr_uint8_descr(void)
357
0
{
358
0
  return &uint8_descr;
359
0
}
360
361
#define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE)                                                \
362
  BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v)                  \
363
0
  {                                                                                         \
364
0
    WINPR_ASSERT(context);                                                                \
365
0
                                                                                              \
366
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE)))                     \
367
0
      return FALSE;                                                                     \
368
0
                                                                                              \
369
0
    if (!ndr_read_align(context, s, sizeof(UPPERTYPE)))                                   \
370
0
      return FALSE;                                                                     \
371
0
                                                                                              \
372
0
    if (context->bigEndianDrep)                                                           \
373
0
      Stream_Read_##UPPERTYPE##_BE(s, *v);                                              \
374
0
    else                                                                                  \
375
0
      Stream_Read_##UPPERTYPE(s, *v);                                                   \
376
0
                                                                                              \
377
0
    ndr_context_bytes_read(context, sizeof(UPPERTYPE));                                   \
378
0
    return TRUE;                                                                          \
379
0
  }                                                                                         \
380
                                                                                              \
381
  BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
382
0
  {                                                                                         \
383
0
    WINPR_UNUSED(hints);                                                                  \
384
0
    return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v);                               \
385
0
  }                                                                                         \
Unexecuted instantiation: ndr_read_uint32_
Unexecuted instantiation: ndr_read_uint16_
Unexecuted instantiation: ndr_read_uint64_
386
                                                                                              \
387
  BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v)                  \
388
0
  {                                                                                         \
389
0
    if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) ||                                \
390
0
        !Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE)))                            \
391
0
      return FALSE;                                                                     \
392
0
                                                                                              \
393
0
    if (context->bigEndianDrep)                                                           \
394
0
      Stream_Write_##UPPERTYPE##_BE(s, v);                                              \
395
0
    else                                                                                  \
396
0
      Stream_Write_##UPPERTYPE(s, v);                                                   \
397
0
                                                                                              \
398
0
    ndr_context_bytes_written(context, sizeof(UPPERTYPE));                                \
399
0
    return TRUE;                                                                          \
400
0
  }                                                                                         \
401
                                                                                              \
402
  BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints,         \
403
                                const void* v)                                              \
404
0
  {                                                                                         \
405
0
    WINPR_ASSERT(context);                                                                \
406
0
    WINPR_ASSERT(s);                                                                      \
407
0
    WINPR_ASSERT(v);                                                                      \
408
0
    WINPR_UNUSED(hints);                                                                  \
409
0
                                                                                              \
410
0
    return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v);                       \
411
0
  }                                                                                         \
Unexecuted instantiation: ndr_write_uint32_
Unexecuted instantiation: ndr_write_uint16_
Unexecuted instantiation: ndr_write_uint64_
412
                                                                                              \
413
  const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { NDR_ARITY_SIMPLE,                     \
414
                                                    sizeof(UPPERTYPE),                    \
415
                                                    ndr_read_##LOWERTYPE##_,              \
416
                                                    ndr_write_##LOWERTYPE##_,             \
417
                                                    NULL,                                 \
418
                                                    NULL };                               \
419
                                                                                              \
420
  NdrMessageType ndr_##LOWERTYPE##_descr(void)                                              \
421
0
  {                                                                                         \
422
0
    return &ndr_##LOWERTYPE##_descr_s;                                                    \
423
0
  }
Unexecuted instantiation: ndr_uint32_descr
Unexecuted instantiation: ndr_uint16_descr
Unexecuted instantiation: ndr_uint64_descr
424
425
0
SIMPLE_TYPE_IMPL(UINT32, uint32)
Unexecuted instantiation: ndr_read_uint32
Unexecuted instantiation: ndr_write_uint32
426
0
SIMPLE_TYPE_IMPL(UINT16, uint16)
Unexecuted instantiation: ndr_read_uint16
Unexecuted instantiation: ndr_write_uint16
427
0
SIMPLE_TYPE_IMPL(UINT64, uint64)
Unexecuted instantiation: ndr_read_uint64
Unexecuted instantiation: ndr_write_uint64
428
429
#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE)                                                        \
430
  BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v)       \
431
0
  {                                                                                              \
432
0
    WINPR_ASSERT(context);                                                                     \
433
0
    WINPR_ASSERT(s);                                                                           \
434
0
    WINPR_ASSERT(hints);                                                                       \
435
0
    return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v);             \
436
0
  }                                                                                              \
Unexecuted instantiation: ndr_read_uint8Array
Unexecuted instantiation: ndr_read_uint16Array
437
                                                                                                   \
438
  BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints,               \
439
                               const void* v)                                                    \
440
0
  {                                                                                              \
441
0
    WINPR_ASSERT(context);                                                                     \
442
0
    WINPR_ASSERT(s);                                                                           \
443
0
    WINPR_ASSERT(hints);                                                                       \
444
0
    const NdrArrayHints* ahints = (const NdrArrayHints*)hints;                                 \
445
0
    return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v);    \
446
0
  }                                                                                              \
Unexecuted instantiation: ndr_write_uint8Array
Unexecuted instantiation: ndr_write_uint16Array
447
  void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj)              \
448
0
  {                                                                                              \
449
0
    WINPR_ASSERT(context);                                                                     \
450
0
    WINPR_ASSERT(obj);                                                                         \
451
0
    WINPR_ASSERT(hints);                                                                       \
452
0
    const NdrArrayHints* ahints = (const NdrArrayHints*)hints;                                 \
453
0
    NdrMessageType descr = ndr_##TYPE##_descr();                                               \
454
0
    if (descr->destroyFn)                                                                      \
455
0
    {                                                                                          \
456
0
      UPPERTYPE* ptr = (UPPERTYPE*)obj;                                                      \
457
0
      for (UINT32 i = 0; i < ahints->count; i++, ptr++)                                      \
458
0
        descr->destroyFn(context, NULL, ptr);                                              \
459
0
    }                                                                                          \
460
0
  }                                                                                              \
461
                                                                                                   \
462
  const NdrMessageDescr ndr_##TYPE##Array_descr_s = {                                            \
463
    NDR_ARITY_ARRAYOF,       sizeof(UPPERTYPE),         ndr_read_##TYPE##Array,                \
464
    ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, NULL                                   \
465
  };                                                                                             \
466
                                                                                                   \
467
  NdrMessageType ndr_##TYPE##Array_descr(void)                                                   \
468
0
  {                                                                                              \
469
0
    return &ndr_##TYPE##Array_descr_s;                                                         \
470
0
  }                                                                                              \
Unexecuted instantiation: ndr_uint8Array_descr
Unexecuted instantiation: ndr_uint16Array_descr
471
                                                                                                   \
472
  BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints,         \
473
                                     void* v)                                                    \
474
0
  {                                                                                              \
475
0
    WINPR_ASSERT(context);                                                                     \
476
0
    WINPR_ASSERT(s);                                                                           \
477
0
    WINPR_ASSERT(hints);                                                                       \
478
0
    return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints,  \
479
0
                                              ndr_##TYPE##_descr(), v);                        \
480
0
  }                                                                                              \
Unexecuted instantiation: ndr_read_uint8VaryingArray
Unexecuted instantiation: ndr_read_uint16VaryingArray
481
  BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints,        \
482
                                      const void* v)                                             \
483
0
  {                                                                                              \
484
0
    WINPR_ASSERT(context);                                                                     \
485
0
    WINPR_ASSERT(s);                                                                           \
486
0
    WINPR_ASSERT(hints);                                                                       \
487
0
    return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
488
0
                                               ndr_##TYPE##_descr(), v);                       \
489
0
  }                                                                                              \
Unexecuted instantiation: ndr_write_uint8VaryingArray
Unexecuted instantiation: ndr_write_uint16VaryingArray
490
                                                                                                   \
491
  const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { NDR_ARITY_VARYING_ARRAYOF,          \
492
                                                           sizeof(UPPERTYPE),                  \
493
                                                           ndr_read_##TYPE##VaryingArray,      \
494
                                                           ndr_write_##TYPE##VaryingArray,     \
495
                                                           NULL,                               \
496
                                                           NULL };                             \
497
                                                                                                   \
498
  NdrMessageType ndr_##TYPE##VaryingArray_descr(void)                                            \
499
0
  {                                                                                              \
500
0
    return &ndr_##TYPE##VaryingArray_descr_s;                                                  \
501
0
  }
Unexecuted instantiation: ndr_uint8VaryingArray_descr
Unexecuted instantiation: ndr_uint16VaryingArray_descr
502
503
0
ARRAY_OF_TYPE_IMPL(uint8, BYTE)
504
0
ARRAY_OF_TYPE_IMPL(uint16, UINT16)
505
506
BOOL ndr_read_wchar(NdrContext* context, wStream* s, WCHAR* ptr)
507
0
{
508
0
  return ndr_read_uint16(context, s, (UINT16*)ptr);
509
0
}
510
511
BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
512
                                        const NdrVaryingArrayHints* hints, NdrMessageType itemType,
513
                                        void* ptarget)
514
0
{
515
0
  WINPR_ASSERT(context);
516
0
  WINPR_ASSERT(s);
517
0
  WINPR_ASSERT(hints);
518
0
  WINPR_ASSERT(itemType);
519
0
  WINPR_ASSERT(ptarget);
520
521
0
  if (itemType->itemSize == 0)
522
0
    return FALSE;
523
524
0
  UINT32 maxCount = 0;
525
0
  UINT32 offset = 0;
526
0
  UINT32 length = 0;
527
528
0
  if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
529
0
      !ndr_read_uint32(context, s, &length))
530
0
    return FALSE;
531
532
0
  if ((1ull * length * itemType->itemSize) > hints->length)
533
0
    return FALSE;
534
535
0
  if ((1ull * maxCount * itemType->itemSize) > hints->maxLength)
536
0
    return FALSE;
537
538
0
  BYTE* target = (BYTE*)ptarget;
539
0
  for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
540
0
  {
541
0
    if (!itemType->readFn(context, s, NULL, target))
542
0
      return FALSE;
543
0
  }
544
545
0
  return ndr_read_align(context, s, 4);
546
0
}
547
548
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
549
                                         const NdrVaryingArrayHints* hints, NdrMessageType itemType,
550
                                         const void* psrc)
551
0
{
552
0
  WINPR_ASSERT(context);
553
0
  WINPR_ASSERT(s);
554
0
  WINPR_ASSERT(hints);
555
0
  WINPR_ASSERT(itemType);
556
0
  WINPR_ASSERT(psrc);
557
558
0
  if (itemType->itemSize == 0)
559
0
    return FALSE;
560
561
0
  if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
562
0
      !ndr_write_uint32(context, s, hints->length))
563
0
    return FALSE;
564
565
0
  const BYTE* src = (const BYTE*)psrc;
566
0
  for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
567
0
  {
568
0
    if (!itemType->writeFn(context, s, NULL, src))
569
0
      return FALSE;
570
0
  }
571
572
0
  return TRUE;
573
0
}
574
575
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
576
                                NdrMessageType itemType, void* vtarget)
577
0
{
578
0
  WINPR_ASSERT(context);
579
0
  WINPR_ASSERT(s);
580
0
  WINPR_ASSERT(itemType);
581
0
  WINPR_ASSERT(vtarget);
582
583
0
  if (itemType->itemSize == 0)
584
0
    return FALSE;
585
586
0
  UINT32 count = 0;
587
0
  if (!ndr_read_uint32(context, s, &count))
588
0
    return FALSE;
589
590
0
  if (itemType->arity == NDR_ARITY_SIMPLE)
591
0
  {
592
0
    if (count > hints->count)
593
0
      return FALSE;
594
0
  }
595
0
  else
596
0
  {
597
0
    if ((1ull * count * itemType->itemSize) > hints->count)
598
0
      return FALSE;
599
0
  }
600
601
0
  BYTE* target = (BYTE*)vtarget;
602
0
  for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
603
0
  {
604
0
    if (!itemType->readFn(context, s, NULL, target))
605
0
      return FALSE;
606
0
  }
607
608
0
  return ndr_read_align(context, s, /*context->alignBytes*/ 4);
609
0
}
610
611
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
612
                                 NdrMessageType itemType, const BYTE* ptr)
613
0
{
614
0
  WINPR_ASSERT(context);
615
0
  WINPR_ASSERT(s);
616
0
  WINPR_ASSERT(itemType);
617
0
  WINPR_ASSERT(ptr);
618
619
0
  size_t toWrite = len * itemType->itemSize;
620
0
  size_t padding = (4 - (toWrite % 4)) % 4;
621
0
  if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
622
0
    return FALSE;
623
624
0
  for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
625
0
  {
626
0
    if (!itemType->writeFn(context, s, NULL, ptr))
627
0
      return FALSE;
628
0
  }
629
630
0
  if (padding)
631
0
  {
632
0
    Stream_Zero(s, padding);
633
0
    ndr_context_bytes_written(context, padding);
634
0
  }
635
0
  return TRUE;
636
0
}
637
638
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
639
                               void* target)
640
0
{
641
0
  WINPR_ASSERT(context);
642
0
  WINPR_ASSERT(s);
643
0
  WINPR_ASSERT(descr);
644
0
  WINPR_ASSERT(target);
645
646
0
#define NDR_MAX_STRUCT_DEFERRED 16
647
0
  NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
648
0
  size_t ndeferred = 0;
649
650
0
  for (size_t i = 0; i < descr->nfields; i++)
651
0
  {
652
0
    const NdrFieldStruct* field = &descr->fields[i];
653
0
    BYTE* ptr = target;
654
0
    ptr += field->structOffset;
655
0
    void* hints = NULL;
656
657
0
    if (field->hintsField >= 0)
658
0
    {
659
      /* computes the address of the hints field if any */
660
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
661
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
662
663
0
      hints = (BYTE*)target + hintsField->structOffset;
664
0
    }
665
666
0
    switch (field->pointerType)
667
0
    {
668
0
      case NDR_NOT_POINTER:
669
0
        if (!field->typeDescr->readFn(context, s, hints, ptr))
670
0
        {
671
0
          WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
672
0
          return FALSE;
673
0
        }
674
0
        break;
675
0
      case NDR_POINTER:
676
0
      case NDR_POINTER_NON_NULL:
677
0
      {
678
0
        NdrDeferredEntry* deferred = &deferreds[ndeferred];
679
0
        if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
680
0
        {
681
0
          WLog_ERR(TAG, "too many deferred when calling ndr_read_struct_fromDescr for %s",
682
0
                   descr->name);
683
0
          return FALSE;
684
0
        }
685
686
0
        deferred->name = field->name;
687
0
        deferred->hints = hints;
688
0
        deferred->target = ptr;
689
0
        deferred->msg = field->typeDescr;
690
0
        if (!ndr_read_refpointer(context, s, &deferred->ptrId))
691
0
        {
692
0
          WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
693
0
          return FALSE;
694
0
        }
695
696
0
        if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
697
0
        {
698
0
          WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
699
0
          return FALSE;
700
0
        }
701
0
        ndeferred++;
702
0
        break;
703
0
      }
704
0
      default:
705
0
        WLog_ERR(TAG, "%s.%s unknown pointer type 0x%x", descr->name, field->name,
706
0
                 field->pointerType);
707
0
        return FALSE;
708
0
    }
709
0
  }
710
711
0
  return ndr_push_deferreds(context, deferreds, ndeferred);
712
0
}
713
714
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
715
                                const void* src)
716
0
{
717
0
  WINPR_ASSERT(context);
718
0
  WINPR_ASSERT(s);
719
0
  WINPR_ASSERT(descr);
720
0
  WINPR_ASSERT(src);
721
722
0
  NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
723
0
  size_t ndeferred = 0;
724
725
0
  for (size_t i = 0; i < descr->nfields; i++)
726
0
  {
727
0
    const NdrFieldStruct* field = &descr->fields[i];
728
0
    const BYTE* ptr = (const BYTE*)src + field->structOffset;
729
730
0
    const void* hints = NULL;
731
732
0
    if (field->hintsField >= 0)
733
0
    {
734
      /* computes the address of the hints field if any */
735
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
736
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
737
738
0
      hints = (const BYTE*)src + hintsField->structOffset;
739
0
    }
740
741
0
    switch (field->pointerType)
742
0
    {
743
0
      case NDR_POINTER:
744
0
      case NDR_POINTER_NON_NULL:
745
0
      {
746
0
        ndr_refid ptrId = NDR_PTR_NULL;
747
0
        BOOL isNew = 0;
748
0
        ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
749
750
0
        if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
751
0
        {
752
0
          WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
753
0
          return FALSE;
754
0
        }
755
756
0
        if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
757
0
          return FALSE;
758
759
0
        if (isNew)
760
0
        {
761
0
          NdrDeferredEntry* deferred = &deferreds[ndeferred];
762
0
          if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
763
0
          {
764
0
            WLog_ERR(TAG,
765
0
                     "too many deferred when calling ndr_read_struct_fromDescr for %s",
766
0
                     descr->name);
767
0
            return FALSE;
768
0
          }
769
770
0
          deferred->name = field->name;
771
0
          deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints, void*);
772
0
          deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr, void*);
773
0
          deferred->msg = field->typeDescr;
774
0
          ndeferred++;
775
0
        }
776
777
0
        if (!ndr_write_uint32(context, s, ptrId))
778
0
          return FALSE;
779
0
        break;
780
0
      }
781
0
      case NDR_NOT_POINTER:
782
0
        if (!field->typeDescr->writeFn(context, s, hints, ptr))
783
0
        {
784
0
          WLog_ERR(TAG, "error when writing %s.%s", descr->name, field->name);
785
0
          return FALSE;
786
0
        }
787
0
        break;
788
0
      default:
789
0
        break;
790
0
    }
791
0
  }
792
793
0
  return ndr_push_deferreds(context, deferreds, ndeferred);
794
0
}
795
796
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
797
                               const NdrStructDescr* descr, const void* obj)
798
0
{
799
0
  char tabArray[30 + 1];
800
0
  size_t ntabs = (identLevel <= 30) ? identLevel : 30;
801
802
0
  memset(tabArray, '\t', ntabs);
803
0
  tabArray[ntabs] = 0;
804
805
0
  WLog_Print(logger, lvl, "%s%s", tabArray, descr->name);
806
0
  for (size_t i = 0; i < descr->nfields; i++)
807
0
  {
808
0
    const NdrFieldStruct* field = &descr->fields[i];
809
0
    const BYTE* ptr = (const BYTE*)obj + field->structOffset;
810
811
0
    switch (field->pointerType)
812
0
    {
813
0
      case NDR_POINTER:
814
0
      case NDR_POINTER_NON_NULL:
815
0
        ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
816
0
        break;
817
0
      case NDR_NOT_POINTER:
818
0
        break;
819
0
      default:
820
0
        WLog_ERR(TAG, "invalid field->pointerType");
821
0
        break;
822
0
    }
823
824
0
    WLog_Print(logger, lvl, "%s*%s:", tabArray, field->name);
825
0
    if (field->typeDescr->dumpFn)
826
0
      field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
827
0
    else
828
0
      WLog_Print(logger, lvl, "%s\t<no dump function>", tabArray);
829
0
  }
830
0
}
831
832
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr)
833
0
{
834
0
  WINPR_ASSERT(context);
835
0
  WINPR_ASSERT(descr);
836
0
  WINPR_ASSERT(pptr);
837
838
0
  for (size_t i = 0; i < descr->nfields; i++)
839
0
  {
840
0
    const NdrFieldStruct* field = &descr->fields[i];
841
0
    void* ptr = (BYTE*)pptr + field->structOffset;
842
0
    void* hints = NULL;
843
844
0
    if (field->hintsField >= 0)
845
0
    {
846
      /* computes the address of the hints field if any */
847
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
848
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
849
850
0
      hints = (BYTE*)pptr + hintsField->structOffset;
851
0
    }
852
853
0
    if (field->pointerType != NDR_NOT_POINTER)
854
0
      ptr = *(void**)ptr;
855
856
0
    if (ptr && field->typeDescr->destroyFn)
857
0
      field->typeDescr->destroyFn(context, hints, ptr);
858
859
0
    if (field->pointerType != NDR_NOT_POINTER)
860
0
      free(ptr);
861
0
  }
862
0
}
863
864
ndr_refid ndr_pointer_refid(const void* ptr)
865
0
{
866
0
  return (ndr_refid)((ULONG_PTR)ptr);
867
0
}
868
869
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, ndr_refid* refId)
870
0
{
871
0
  return ndr_read_uint32(context, s, refId);
872
0
}
873
874
typedef struct
875
{
876
  const void* needle;
877
  ndr_refid* presult;
878
} FindValueArgs;
879
880
static BOOL findValueRefFn(const void* key, void* value, void* parg)
881
0
{
882
0
  WINPR_ASSERT(parg);
883
884
0
  FindValueArgs* args = (FindValueArgs*)parg;
885
0
  if (args->needle == value)
886
0
  {
887
0
    *args->presult = (ndr_refid)(UINT_PTR)key;
888
0
    return FALSE;
889
0
  }
890
0
  return TRUE;
891
0
}
892
893
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
894
0
{
895
0
  WINPR_ASSERT(context);
896
897
0
  FindValueArgs findArgs = { ptr, prefId };
898
0
  if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
899
0
  {
900
0
    *pnewPtr = FALSE;
901
0
    return TRUE;
902
0
  }
903
904
0
  *pnewPtr = TRUE;
905
0
  *prefId = context->refIdCounter + 4;
906
0
  if (!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)(*prefId), ptr))
907
0
    return FALSE;
908
909
0
  context->refIdCounter += 4;
910
0
  return TRUE;
911
0
}
912
913
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
914
                               NdrMessageType descr, void* hints, void** target)
915
0
{
916
0
  WINPR_ASSERT(context);
917
0
  WINPR_ASSERT(s);
918
0
  WINPR_ASSERT(descr);
919
0
  WINPR_ASSERT(target);
920
921
0
  *target = NULL;
922
0
  if (!ptrId)
923
0
    return TRUE;
924
925
0
  void* ret = HashTable_GetItemValue(context->refPointers, (void*)(UINT_PTR)ptrId);
926
0
  if (!ret)
927
0
  {
928
0
    size_t itemCount = ndr_hintsCount(descr, hints);
929
0
    if (itemCount == 0)
930
0
      return FALSE;
931
0
    ret = calloc(itemCount, descr->itemSize);
932
0
    if (!ret)
933
0
      return FALSE;
934
935
0
    if (!descr->readFn(context, s, hints, ret) ||
936
0
        !HashTable_Insert(context->refPointers, (void*)(UINT_PTR)ptrId, ret))
937
0
    {
938
0
      if (descr->destroyFn)
939
0
        descr->destroyFn(context, hints, ret);
940
0
      free(ret);
941
0
      return FALSE;
942
0
    }
943
0
  }
944
945
0
  *target = ret;
946
0
  return TRUE;
947
0
}
948
949
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred)
950
0
{
951
0
  WINPR_ASSERT(context);
952
0
  WINPR_ASSERT(deferreds);
953
954
0
  if (!ndeferred)
955
0
    return TRUE;
956
957
0
  if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
958
0
  {
959
0
    WLog_ERR(TAG, "too many deferred");
960
0
    return FALSE;
961
0
  }
962
963
0
  for (size_t i = ndeferred; i > 0; i--, context->ndeferred++)
964
0
  {
965
0
    context->deferred[context->ndeferred] = deferreds[i - 1];
966
0
  }
967
0
  return TRUE;
968
0
}
969
970
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s)
971
0
{
972
0
  WINPR_ASSERT(context);
973
0
  WINPR_ASSERT(s);
974
975
0
  while (context->ndeferred)
976
0
  {
977
0
    NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
978
0
    context->ndeferred--;
979
980
0
    WLog_VRB(TAG, "treating read deferred 0x%x for %s", current.ptrId, current.name);
981
0
    if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
982
0
                                   (void**)current.target))
983
0
    {
984
0
      WLog_ERR(TAG, "error parsing deferred %s", current.name);
985
0
      return FALSE;
986
0
    }
987
0
  }
988
989
0
  return TRUE;
990
0
}
991
992
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s)
993
0
{
994
0
  WINPR_ASSERT(context);
995
0
  WINPR_ASSERT(s);
996
997
0
  while (context->ndeferred)
998
0
  {
999
0
    NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
1000
0
    context->ndeferred--;
1001
1002
0
    WLog_VRB(TAG, "treating write deferred for %s", current.name);
1003
0
    if (!current.msg->writeFn(context, s, current.hints, current.target))
1004
0
    {
1005
0
      WLog_ERR(TAG, "error writing deferred %s", current.name);
1006
0
      return FALSE;
1007
0
    }
1008
0
  }
1009
1010
0
  return TRUE;
1011
0
}
1012
1013
BOOL ndr_write_data(NdrContext* context, wStream* s, const void* data, size_t sz)
1014
0
{
1015
0
  if (!Stream_EnsureRemainingCapacity(s, sz))
1016
0
    return FALSE;
1017
1018
0
  Stream_Write(s, data, sz);
1019
0
  ndr_context_bytes_written(context, sz);
1020
  return TRUE;
1021
0
}