Coverage Report

Created: 2026-03-04 06:17

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 nullptr;
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 nullptr;
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 nullptr;
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 nullptr;
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 nullptr;
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 nullptr;
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
  return !(!ndr_read_uint32(context, s, &v) || v != 0x20000);
217
0
}
218
219
BOOL ndr_write_pickle(NdrContext* context, wStream* s)
220
0
{
221
0
  WINPR_ASSERT(context);
222
223
  /* NDR format label */
224
0
  return ndr_write_uint32(context, s, 0x20000);
225
0
}
226
227
BOOL ndr_read_constructed(NdrContext* context, wStream* s, wStream* target)
228
0
{
229
0
  WINPR_ASSERT(context);
230
231
0
  UINT32 len = 0;
232
233
  /* len */
234
0
  if (!ndr_read_uint32(context, s, &len))
235
0
    return FALSE;
236
237
  /* padding */
238
0
  if (!ndr_skip_bytes(context, s, 4))
239
0
    return FALSE;
240
241
  /* payload */
242
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
243
0
    return FALSE;
244
245
0
  Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
246
0
  Stream_Seek(s, len);
247
0
  return TRUE;
248
0
}
249
250
BOOL ndr_start_constructed(NdrContext* context, wStream* s)
251
0
{
252
0
  WINPR_ASSERT(context);
253
254
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
255
0
    return FALSE;
256
257
0
  if (context->constructLevel == NDR_MAX_CONSTRUCTS)
258
0
    return FALSE;
259
260
0
  context->constructLevel++;
261
0
  context->constructs[context->constructLevel] = Stream_GetPosition(s);
262
263
0
  Stream_Zero(s, 8);
264
0
  return TRUE;
265
0
}
266
267
BOOL ndr_end_constructed(NdrContext* context, wStream* s)
268
0
{
269
0
  WINPR_ASSERT(context);
270
0
  WINPR_ASSERT(context->constructs);
271
0
  WINPR_ASSERT(context->constructLevel >= 0);
272
273
0
  size_t offset = context->constructs[context->constructLevel];
274
275
0
  wStream staticS = WINPR_C_ARRAY_INIT;
276
0
  Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
277
278
  /* len */
279
0
  const size_t len = Stream_GetPosition(s) - (offset + 8);
280
0
  if (len > UINT32_MAX)
281
0
    return FALSE;
282
0
  if (!ndr_write_uint32(context, &staticS, (UINT32)len))
283
0
    return FALSE;
284
285
0
  return TRUE;
286
0
}
287
288
static size_t ndr_hintsCount(NdrMessageType msgType, const void* hints)
289
0
{
290
0
  WINPR_ASSERT(msgType);
291
292
0
  switch (msgType->arity)
293
0
  {
294
0
    case NDR_ARITY_SIMPLE:
295
0
      return 1;
296
0
    case NDR_ARITY_ARRAYOF:
297
0
      WINPR_ASSERT(hints);
298
0
      return ((const NdrArrayHints*)hints)->count;
299
0
    case NDR_ARITY_VARYING_ARRAYOF:
300
0
      WINPR_ASSERT(hints);
301
0
      return ((const NdrVaryingArrayHints*)hints)->maxLength;
302
0
    default:
303
0
      WINPR_ASSERT(0 && "unknown arity");
304
0
      return 0;
305
0
  }
306
0
}
307
308
BOOL ndr_read_uint8(NdrContext* context, wStream* s, BYTE* v)
309
0
{
310
0
  WINPR_ASSERT(context);
311
312
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
313
0
    return FALSE;
314
315
0
  Stream_Read_UINT8(s, *v);
316
317
0
  ndr_context_bytes_read(context, 1);
318
0
  return TRUE;
319
0
}
320
321
BOOL ndr_read_uint8_(NdrContext* context, wStream* s, const void* hints, void* v)
322
0
{
323
0
  WINPR_UNUSED(hints);
324
0
  return ndr_read_uint8(context, s, (BYTE*)v);
325
0
}
326
327
BOOL ndr_write_uint8(NdrContext* context, wStream* s, BYTE v)
328
0
{
329
0
  if (!Stream_EnsureRemainingCapacity(s, 1))
330
0
    return FALSE;
331
332
0
  Stream_Write_UINT8(s, v);
333
0
  ndr_context_bytes_written(context, 1);
334
0
  return TRUE;
335
0
}
336
337
BOOL ndr_write_uint8_(NdrContext* context, wStream* s, const void* hints, const void* v)
338
0
{
339
0
  WINPR_ASSERT(context);
340
0
  WINPR_ASSERT(s);
341
0
  WINPR_ASSERT(v);
342
0
  WINPR_UNUSED(hints);
343
344
0
  return ndr_write_uint8(context, s, *(const BYTE*)v);
345
0
}
346
347
const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1,       ndr_read_uint8_,
348
                                           ndr_write_uint8_, nullptr, nullptr };
349
350
NdrMessageType ndr_uint8_descr(void)
351
0
{
352
0
  return &uint8_descr;
353
0
}
354
355
#define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE)                                                \
356
  BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v)                  \
357
0
  {                                                                                         \
358
0
    WINPR_ASSERT(context);                                                                \
359
0
                                                                                              \
360
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE)))                     \
361
0
      return FALSE;                                                                     \
362
0
                                                                                              \
363
0
    if (!ndr_read_align(context, s, sizeof(UPPERTYPE)))                                   \
364
0
      return FALSE;                                                                     \
365
0
                                                                                              \
366
0
    if (context->bigEndianDrep)                                                           \
367
0
      Stream_Read_##UPPERTYPE##_BE(s, *v);                                              \
368
0
    else                                                                                  \
369
0
      Stream_Read_##UPPERTYPE(s, *v);                                                   \
370
0
                                                                                              \
371
0
    ndr_context_bytes_read(context, sizeof(UPPERTYPE));                                   \
372
0
    return TRUE;                                                                          \
373
0
  }                                                                                         \
Unexecuted instantiation: ndr_read_uint32
Unexecuted instantiation: ndr_read_uint16
Unexecuted instantiation: ndr_read_uint64
374
                                                                                              \
375
  BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
376
0
  {                                                                                         \
377
0
    WINPR_UNUSED(hints);                                                                  \
378
0
    return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v);                               \
379
0
  }                                                                                         \
Unexecuted instantiation: ndr_read_uint32_
Unexecuted instantiation: ndr_read_uint16_
Unexecuted instantiation: ndr_read_uint64_
380
                                                                                              \
381
  BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v)                  \
382
0
  {                                                                                         \
383
0
    if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) ||                                \
384
0
        !Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE)))                            \
385
0
      return FALSE;                                                                     \
386
0
                                                                                              \
387
0
    if (context->bigEndianDrep)                                                           \
388
0
      Stream_Write_##UPPERTYPE##_BE(s, v);                                              \
389
0
    else                                                                                  \
390
0
      Stream_Write_##UPPERTYPE(s, v);                                                   \
391
0
                                                                                              \
392
0
    ndr_context_bytes_written(context, sizeof(UPPERTYPE));                                \
393
0
    return TRUE;                                                                          \
394
0
  }                                                                                         \
Unexecuted instantiation: ndr_write_uint32
Unexecuted instantiation: ndr_write_uint16
Unexecuted instantiation: ndr_write_uint64
395
                                                                                              \
396
  BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints,         \
397
                                const void* v)                                              \
398
0
  {                                                                                         \
399
0
    WINPR_ASSERT(context);                                                                \
400
0
    WINPR_ASSERT(s);                                                                      \
401
0
    WINPR_ASSERT(v);                                                                      \
402
0
    WINPR_UNUSED(hints);                                                                  \
403
0
                                                                                              \
404
0
    return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v);                       \
405
0
  }                                                                                         \
Unexecuted instantiation: ndr_write_uint32_
Unexecuted instantiation: ndr_write_uint16_
Unexecuted instantiation: ndr_write_uint64_
406
                                                                                              \
407
  const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = {                                       \
408
    NDR_ARITY_SIMPLE,         sizeof(UPPERTYPE), ndr_read_##LOWERTYPE##_,                 \
409
    ndr_write_##LOWERTYPE##_, nullptr,           nullptr                                  \
410
  };                                                                                        \
411
                                                                                              \
412
  NdrMessageType ndr_##LOWERTYPE##_descr(void)                                              \
413
0
  {                                                                                         \
414
0
    return &ndr_##LOWERTYPE##_descr_s;                                                    \
415
0
  }
Unexecuted instantiation: ndr_uint32_descr
Unexecuted instantiation: ndr_uint16_descr
Unexecuted instantiation: ndr_uint64_descr
416
417
SIMPLE_TYPE_IMPL(UINT32, uint32)
418
SIMPLE_TYPE_IMPL(UINT16, uint16)
419
SIMPLE_TYPE_IMPL(UINT64, uint64)
420
421
#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE)                                                        \
422
  BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v)       \
423
0
  {                                                                                              \
424
0
    WINPR_ASSERT(context);                                                                     \
425
0
    WINPR_ASSERT(s);                                                                           \
426
0
    WINPR_ASSERT(hints);                                                                       \
427
0
    return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v);             \
428
0
  }                                                                                              \
Unexecuted instantiation: ndr_read_uint8Array
Unexecuted instantiation: ndr_read_uint16Array
429
                                                                                                   \
430
  BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints,               \
431
                               const void* v)                                                    \
432
0
  {                                                                                              \
433
0
    WINPR_ASSERT(context);                                                                     \
434
0
    WINPR_ASSERT(s);                                                                           \
435
0
    WINPR_ASSERT(hints);                                                                       \
436
0
    const NdrArrayHints* ahints = (const NdrArrayHints*)hints;                                 \
437
0
    return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v);    \
438
0
  }                                                                                              \
Unexecuted instantiation: ndr_write_uint8Array
Unexecuted instantiation: ndr_write_uint16Array
439
  void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj)              \
440
0
  {                                                                                              \
441
0
    WINPR_ASSERT(context);                                                                     \
442
0
    WINPR_ASSERT(obj);                                                                         \
443
0
    WINPR_ASSERT(hints);                                                                       \
444
0
    const NdrArrayHints* ahints = (const NdrArrayHints*)hints;                                 \
445
0
    NdrMessageType descr = ndr_##TYPE##_descr();                                               \
446
0
    if (descr->destroyFn)                                                                      \
447
0
    {                                                                                          \
448
0
      UPPERTYPE* ptr = (UPPERTYPE*)obj;                                                      \
449
0
      for (UINT32 i = 0; i < ahints->count; i++, ptr++)                                      \
450
0
        descr->destroyFn(context, nullptr, ptr);                                           \
451
0
    }                                                                                          \
452
0
  }                                                                                              \
Unexecuted instantiation: ndr_destroy_uint8Array
Unexecuted instantiation: ndr_destroy_uint16Array
453
                                                                                                   \
454
  const NdrMessageDescr ndr_##TYPE##Array_descr_s = {                                            \
455
    NDR_ARITY_ARRAYOF,       sizeof(UPPERTYPE),         ndr_read_##TYPE##Array,                \
456
    ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, nullptr                                \
457
  };                                                                                             \
458
                                                                                                   \
459
  NdrMessageType ndr_##TYPE##Array_descr(void)                                                   \
460
0
  {                                                                                              \
461
0
    return &ndr_##TYPE##Array_descr_s;                                                         \
462
0
  }                                                                                              \
Unexecuted instantiation: ndr_uint8Array_descr
Unexecuted instantiation: ndr_uint16Array_descr
463
                                                                                                   \
464
  BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints,         \
465
                                     void* v)                                                    \
466
0
  {                                                                                              \
467
0
    WINPR_ASSERT(context);                                                                     \
468
0
    WINPR_ASSERT(s);                                                                           \
469
0
    WINPR_ASSERT(hints);                                                                       \
470
0
    return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints,  \
471
0
                                              ndr_##TYPE##_descr(), v);                        \
472
0
  }                                                                                              \
Unexecuted instantiation: ndr_read_uint8VaryingArray
Unexecuted instantiation: ndr_read_uint16VaryingArray
473
  BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints,        \
474
                                      const void* v)                                             \
475
0
  {                                                                                              \
476
0
    WINPR_ASSERT(context);                                                                     \
477
0
    WINPR_ASSERT(s);                                                                           \
478
0
    WINPR_ASSERT(hints);                                                                       \
479
0
    return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
480
0
                                               ndr_##TYPE##_descr(), v);                       \
481
0
  }                                                                                              \
Unexecuted instantiation: ndr_write_uint8VaryingArray
Unexecuted instantiation: ndr_write_uint16VaryingArray
482
                                                                                                   \
483
  const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = {                                     \
484
    NDR_ARITY_VARYING_ARRAYOF,      sizeof(UPPERTYPE), ndr_read_##TYPE##VaryingArray,          \
485
    ndr_write_##TYPE##VaryingArray, nullptr,           nullptr                                 \
486
  };                                                                                             \
487
                                                                                                   \
488
  NdrMessageType ndr_##TYPE##VaryingArray_descr(void)                                            \
489
0
  {                                                                                              \
490
0
    return &ndr_##TYPE##VaryingArray_descr_s;                                                  \
491
0
  }
Unexecuted instantiation: ndr_uint8VaryingArray_descr
Unexecuted instantiation: ndr_uint16VaryingArray_descr
492
493
ARRAY_OF_TYPE_IMPL(uint8, BYTE)
494
ARRAY_OF_TYPE_IMPL(uint16, UINT16)
495
496
BOOL ndr_read_wchar(NdrContext* context, wStream* s, WCHAR* ptr)
497
0
{
498
0
  return ndr_read_uint16(context, s, (UINT16*)ptr);
499
0
}
500
501
BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
502
                                        const NdrVaryingArrayHints* hints, NdrMessageType itemType,
503
                                        void* ptarget)
504
0
{
505
0
  WINPR_ASSERT(context);
506
0
  WINPR_ASSERT(s);
507
0
  WINPR_ASSERT(hints);
508
0
  WINPR_ASSERT(itemType);
509
0
  WINPR_ASSERT(ptarget);
510
511
0
  if (itemType->itemSize == 0)
512
0
    return FALSE;
513
514
0
  UINT32 maxCount = 0;
515
0
  UINT32 offset = 0;
516
0
  UINT32 length = 0;
517
518
0
  if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
519
0
      !ndr_read_uint32(context, s, &length))
520
0
    return FALSE;
521
522
0
  if ((1ull * length * itemType->itemSize) > hints->length)
523
0
    return FALSE;
524
525
0
  if ((1ull * maxCount * itemType->itemSize) > hints->maxLength)
526
0
    return FALSE;
527
528
0
  BYTE* target = (BYTE*)ptarget;
529
0
  for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
530
0
  {
531
0
    if (!itemType->readFn(context, s, nullptr, target))
532
0
      return FALSE;
533
0
  }
534
535
0
  return ndr_read_align(context, s, 4);
536
0
}
537
538
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
539
                                         const NdrVaryingArrayHints* hints, NdrMessageType itemType,
540
                                         const void* psrc)
541
0
{
542
0
  WINPR_ASSERT(context);
543
0
  WINPR_ASSERT(s);
544
0
  WINPR_ASSERT(hints);
545
0
  WINPR_ASSERT(itemType);
546
0
  WINPR_ASSERT(psrc);
547
548
0
  if (itemType->itemSize == 0)
549
0
    return FALSE;
550
551
0
  if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
552
0
      !ndr_write_uint32(context, s, hints->length))
553
0
    return FALSE;
554
555
0
  const BYTE* src = (const BYTE*)psrc;
556
0
  for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
557
0
  {
558
0
    if (!itemType->writeFn(context, s, nullptr, src))
559
0
      return FALSE;
560
0
  }
561
562
0
  return TRUE;
563
0
}
564
565
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
566
                                NdrMessageType itemType, void* vtarget)
567
0
{
568
0
  WINPR_ASSERT(context);
569
0
  WINPR_ASSERT(s);
570
0
  WINPR_ASSERT(itemType);
571
0
  WINPR_ASSERT(vtarget);
572
573
0
  if (itemType->itemSize == 0)
574
0
    return FALSE;
575
576
0
  UINT32 count = 0;
577
0
  if (!ndr_read_uint32(context, s, &count))
578
0
    return FALSE;
579
580
0
  if (itemType->arity == NDR_ARITY_SIMPLE)
581
0
  {
582
0
    if (count > hints->count)
583
0
      return FALSE;
584
0
  }
585
0
  else
586
0
  {
587
0
    if ((1ull * count * itemType->itemSize) > hints->count)
588
0
      return FALSE;
589
0
  }
590
591
0
  BYTE* target = (BYTE*)vtarget;
592
0
  for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
593
0
  {
594
0
    if (!itemType->readFn(context, s, nullptr, target))
595
0
      return FALSE;
596
0
  }
597
598
0
  return ndr_read_align(context, s, /*context->alignBytes*/ 4);
599
0
}
600
601
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
602
                                 NdrMessageType itemType, const BYTE* ptr)
603
0
{
604
0
  WINPR_ASSERT(context);
605
0
  WINPR_ASSERT(s);
606
0
  WINPR_ASSERT(itemType);
607
0
  WINPR_ASSERT(ptr);
608
609
0
  size_t toWrite = len * itemType->itemSize;
610
0
  size_t padding = (4 - (toWrite % 4)) % 4;
611
0
  if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
612
0
    return FALSE;
613
614
0
  for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
615
0
  {
616
0
    if (!itemType->writeFn(context, s, nullptr, ptr))
617
0
      return FALSE;
618
0
  }
619
620
0
  if (padding)
621
0
  {
622
0
    Stream_Zero(s, padding);
623
0
    ndr_context_bytes_written(context, padding);
624
0
  }
625
0
  return TRUE;
626
0
}
627
628
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
629
                               void* target)
630
0
{
631
0
  WINPR_ASSERT(context);
632
0
  WINPR_ASSERT(s);
633
0
  WINPR_ASSERT(descr);
634
0
  WINPR_ASSERT(target);
635
636
0
#define NDR_MAX_STRUCT_DEFERRED 16
637
0
  NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = WINPR_C_ARRAY_INIT;
638
0
  size_t ndeferred = 0;
639
640
0
  for (size_t i = 0; i < descr->nfields; i++)
641
0
  {
642
0
    const NdrFieldStruct* field = &descr->fields[i];
643
0
    BYTE* ptr = target;
644
0
    ptr += field->structOffset;
645
0
    void* hints = nullptr;
646
647
0
    if (field->hintsField >= 0)
648
0
    {
649
      /* computes the address of the hints field if any */
650
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
651
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
652
653
0
      hints = (BYTE*)target + hintsField->structOffset;
654
0
    }
655
656
0
    switch (field->pointerType)
657
0
    {
658
0
      case NDR_NOT_POINTER:
659
0
        if (!field->typeDescr->readFn(context, s, hints, ptr))
660
0
        {
661
0
          WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
662
0
          return FALSE;
663
0
        }
664
0
        break;
665
0
      case NDR_POINTER:
666
0
      case NDR_POINTER_NON_NULL:
667
0
      {
668
0
        NdrDeferredEntry* deferred = &deferreds[ndeferred];
669
0
        if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
670
0
        {
671
0
          WLog_ERR(TAG, "too many deferred when calling ndr_read_struct_fromDescr for %s",
672
0
                   descr->name);
673
0
          return FALSE;
674
0
        }
675
676
0
        deferred->name = field->name;
677
0
        deferred->hints = hints;
678
0
        deferred->target = ptr;
679
0
        deferred->msg = field->typeDescr;
680
0
        if (!ndr_read_refpointer(context, s, &deferred->ptrId))
681
0
        {
682
0
          WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
683
0
          return FALSE;
684
0
        }
685
686
0
        if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
687
0
        {
688
0
          WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
689
0
          return FALSE;
690
0
        }
691
0
        ndeferred++;
692
0
        break;
693
0
      }
694
0
      default:
695
0
        WLog_ERR(TAG, "%s.%s unknown pointer type 0x%x", descr->name, field->name,
696
0
                 field->pointerType);
697
0
        return FALSE;
698
0
    }
699
0
  }
700
701
0
  return ndr_push_deferreds(context, deferreds, ndeferred);
702
0
}
703
704
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
705
                                const void* src)
706
0
{
707
0
  WINPR_ASSERT(context);
708
0
  WINPR_ASSERT(s);
709
0
  WINPR_ASSERT(descr);
710
0
  WINPR_ASSERT(src);
711
712
0
  NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = WINPR_C_ARRAY_INIT;
713
0
  size_t ndeferred = 0;
714
715
0
  for (size_t i = 0; i < descr->nfields; i++)
716
0
  {
717
0
    const NdrFieldStruct* field = &descr->fields[i];
718
0
    const BYTE* ptr = (const BYTE*)src + field->structOffset;
719
720
0
    const void* hints = nullptr;
721
722
0
    if (field->hintsField >= 0)
723
0
    {
724
      /* computes the address of the hints field if any */
725
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
726
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
727
728
0
      hints = (const BYTE*)src + hintsField->structOffset;
729
0
    }
730
731
0
    switch (field->pointerType)
732
0
    {
733
0
      case NDR_POINTER:
734
0
      case NDR_POINTER_NON_NULL:
735
0
      {
736
0
        ndr_refid ptrId = NDR_PTR_NULL;
737
0
        BOOL isNew = 0;
738
0
        ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
739
740
0
        if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
741
0
        {
742
0
          WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
743
0
          return FALSE;
744
0
        }
745
746
0
        if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
747
0
          return FALSE;
748
749
0
        if (isNew)
750
0
        {
751
0
          NdrDeferredEntry* deferred = &deferreds[ndeferred];
752
0
          if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
753
0
          {
754
0
            WLog_ERR(TAG,
755
0
                     "too many deferred when calling ndr_read_struct_fromDescr for %s",
756
0
                     descr->name);
757
0
            return FALSE;
758
0
          }
759
760
0
          deferred->name = field->name;
761
0
          deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints, void*);
762
0
          deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr, void*);
763
0
          deferred->msg = field->typeDescr;
764
0
          ndeferred++;
765
0
        }
766
767
0
        if (!ndr_write_uint32(context, s, ptrId))
768
0
          return FALSE;
769
0
        break;
770
0
      }
771
0
      case NDR_NOT_POINTER:
772
0
        if (!field->typeDescr->writeFn(context, s, hints, ptr))
773
0
        {
774
0
          WLog_ERR(TAG, "error when writing %s.%s", descr->name, field->name);
775
0
          return FALSE;
776
0
        }
777
0
        break;
778
0
      default:
779
0
        break;
780
0
    }
781
0
  }
782
783
0
  return ndr_push_deferreds(context, deferreds, ndeferred);
784
0
}
785
786
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
787
                               const NdrStructDescr* descr, const void* obj)
788
0
{
789
0
  char tabArray[30 + 1];
790
0
  size_t ntabs = (identLevel <= 30) ? identLevel : 30;
791
792
0
  memset(tabArray, '\t', ntabs);
793
0
  tabArray[ntabs] = 0;
794
795
0
  WLog_Print(logger, lvl, "%s%s", tabArray, descr->name);
796
0
  for (size_t i = 0; i < descr->nfields; i++)
797
0
  {
798
0
    const NdrFieldStruct* field = &descr->fields[i];
799
0
    const BYTE* ptr = (const BYTE*)obj + field->structOffset;
800
801
0
    switch (field->pointerType)
802
0
    {
803
0
      case NDR_POINTER:
804
0
      case NDR_POINTER_NON_NULL:
805
0
        ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
806
0
        break;
807
0
      case NDR_NOT_POINTER:
808
0
        break;
809
0
      default:
810
0
        WLog_ERR(TAG, "invalid field->pointerType");
811
0
        break;
812
0
    }
813
814
0
    WLog_Print(logger, lvl, "%s*%s:", tabArray, field->name);
815
0
    if (field->typeDescr->dumpFn)
816
0
      field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
817
0
    else
818
0
      WLog_Print(logger, lvl, "%s\t<no dump function>", tabArray);
819
0
  }
820
0
}
821
822
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr)
823
0
{
824
0
  WINPR_ASSERT(context);
825
0
  WINPR_ASSERT(descr);
826
0
  WINPR_ASSERT(pptr);
827
828
0
  for (size_t i = 0; i < descr->nfields; i++)
829
0
  {
830
0
    const NdrFieldStruct* field = &descr->fields[i];
831
0
    void* ptr = (BYTE*)pptr + field->structOffset;
832
0
    void* hints = nullptr;
833
834
0
    if (field->hintsField >= 0)
835
0
    {
836
      /* computes the address of the hints field if any */
837
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
838
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
839
840
0
      hints = (BYTE*)pptr + hintsField->structOffset;
841
0
    }
842
843
0
    if (field->pointerType != NDR_NOT_POINTER)
844
0
      ptr = *(void**)ptr;
845
846
0
    if (ptr && field->typeDescr->destroyFn)
847
0
      field->typeDescr->destroyFn(context, hints, ptr);
848
849
0
    if (field->pointerType != NDR_NOT_POINTER)
850
0
      free(ptr);
851
0
  }
852
0
}
853
854
ndr_refid ndr_pointer_refid(const void* ptr)
855
0
{
856
0
  return (ndr_refid)((ULONG_PTR)ptr);
857
0
}
858
859
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, ndr_refid* refId)
860
0
{
861
0
  return ndr_read_uint32(context, s, refId);
862
0
}
863
864
typedef struct
865
{
866
  const void* needle;
867
  ndr_refid* presult;
868
} FindValueArgs;
869
870
static BOOL findValueRefFn(const void* key, void* value, void* parg)
871
0
{
872
0
  WINPR_ASSERT(parg);
873
874
0
  FindValueArgs* args = (FindValueArgs*)parg;
875
0
  if (args->needle == value)
876
0
  {
877
0
    *args->presult = (ndr_refid)(UINT_PTR)key;
878
0
    return FALSE;
879
0
  }
880
0
  return TRUE;
881
0
}
882
883
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
884
0
{
885
0
  WINPR_ASSERT(context);
886
887
0
  FindValueArgs findArgs = { ptr, prefId };
888
0
  if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
889
0
  {
890
0
    *pnewPtr = FALSE;
891
0
    return TRUE;
892
0
  }
893
894
0
  *pnewPtr = TRUE;
895
0
  *prefId = context->refIdCounter + 4;
896
0
  if (!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)(*prefId), ptr))
897
0
    return FALSE;
898
899
0
  context->refIdCounter += 4;
900
0
  return TRUE;
901
0
}
902
903
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
904
                               NdrMessageType descr, void* hints, void** target)
905
0
{
906
0
  WINPR_ASSERT(context);
907
0
  WINPR_ASSERT(s);
908
0
  WINPR_ASSERT(descr);
909
0
  WINPR_ASSERT(target);
910
911
0
  *target = nullptr;
912
0
  if (!ptrId)
913
0
    return TRUE;
914
915
0
  void* ret = HashTable_GetItemValue(context->refPointers, (void*)(UINT_PTR)ptrId);
916
0
  if (!ret)
917
0
  {
918
0
    size_t itemCount = ndr_hintsCount(descr, hints);
919
0
    if (itemCount == 0)
920
0
      return FALSE;
921
0
    ret = calloc(itemCount, descr->itemSize);
922
0
    if (!ret)
923
0
      return FALSE;
924
925
0
    if (!descr->readFn(context, s, hints, ret) ||
926
0
        !HashTable_Insert(context->refPointers, (void*)(UINT_PTR)ptrId, ret))
927
0
    {
928
0
      if (descr->destroyFn)
929
0
        descr->destroyFn(context, hints, ret);
930
0
      free(ret);
931
0
      return FALSE;
932
0
    }
933
0
  }
934
935
0
  *target = ret;
936
0
  return TRUE;
937
0
}
938
939
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred)
940
0
{
941
0
  WINPR_ASSERT(context);
942
0
  WINPR_ASSERT(deferreds);
943
944
0
  if (!ndeferred)
945
0
    return TRUE;
946
947
0
  if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
948
0
  {
949
0
    WLog_ERR(TAG, "too many deferred");
950
0
    return FALSE;
951
0
  }
952
953
0
  for (size_t i = ndeferred; i > 0; i--, context->ndeferred++)
954
0
  {
955
0
    context->deferred[context->ndeferred] = deferreds[i - 1];
956
0
  }
957
0
  return TRUE;
958
0
}
959
960
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s)
961
0
{
962
0
  WINPR_ASSERT(context);
963
0
  WINPR_ASSERT(s);
964
965
0
  while (context->ndeferred)
966
0
  {
967
0
    NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
968
0
    context->ndeferred--;
969
970
0
    WLog_VRB(TAG, "treating read deferred 0x%x for %s", current.ptrId, current.name);
971
0
    if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
972
0
                                   (void**)current.target))
973
0
    {
974
0
      WLog_ERR(TAG, "error parsing deferred %s", current.name);
975
0
      return FALSE;
976
0
    }
977
0
  }
978
979
0
  return TRUE;
980
0
}
981
982
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s)
983
0
{
984
0
  WINPR_ASSERT(context);
985
0
  WINPR_ASSERT(s);
986
987
0
  while (context->ndeferred)
988
0
  {
989
0
    NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
990
0
    context->ndeferred--;
991
992
0
    WLog_VRB(TAG, "treating write deferred for %s", current.name);
993
0
    if (!current.msg->writeFn(context, s, current.hints, current.target))
994
0
    {
995
0
      WLog_ERR(TAG, "error writing deferred %s", current.name);
996
0
      return FALSE;
997
0
    }
998
0
  }
999
1000
0
  return TRUE;
1001
0
}
1002
1003
BOOL ndr_write_data(NdrContext* context, wStream* s, const void* data, size_t sz)
1004
0
{
1005
0
  if (!Stream_EnsureRemainingCapacity(s, sz))
1006
0
    return FALSE;
1007
1008
0
  Stream_Write(s, data, sz);
1009
0
  ndr_context_bytes_written(context, sz);
1010
0
  return TRUE;
1011
0
}