Coverage Report

Created: 2026-01-09 06:49

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
  UINT32 maxCount = 0;
522
0
  UINT32 offset = 0;
523
0
  UINT32 length = 0;
524
525
0
  if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
526
0
      !ndr_read_uint32(context, s, &length))
527
0
    return FALSE;
528
529
0
  if ((length * itemType->itemSize) < hints->length)
530
0
    return FALSE;
531
532
0
  if ((maxCount * itemType->itemSize) < hints->maxLength)
533
0
    return FALSE;
534
535
0
  BYTE* target = (BYTE*)ptarget;
536
0
  for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
537
0
  {
538
0
    if (!itemType->readFn(context, s, NULL, target))
539
0
      return FALSE;
540
0
  }
541
542
0
  return ndr_read_align(context, s, 4);
543
0
}
544
545
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
546
                                         const NdrVaryingArrayHints* hints, NdrMessageType itemType,
547
                                         const void* psrc)
548
0
{
549
0
  WINPR_ASSERT(context);
550
0
  WINPR_ASSERT(s);
551
0
  WINPR_ASSERT(hints);
552
0
  WINPR_ASSERT(itemType);
553
0
  WINPR_ASSERT(psrc);
554
555
0
  if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
556
0
      !ndr_write_uint32(context, s, hints->length))
557
0
    return FALSE;
558
559
0
  const BYTE* src = (const BYTE*)psrc;
560
0
  for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
561
0
  {
562
0
    if (!itemType->writeFn(context, s, NULL, src))
563
0
      return FALSE;
564
0
  }
565
566
0
  return TRUE;
567
0
}
568
569
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
570
                                NdrMessageType itemType, void* vtarget)
571
0
{
572
0
  WINPR_ASSERT(context);
573
0
  WINPR_ASSERT(s);
574
0
  WINPR_ASSERT(itemType);
575
0
  WINPR_ASSERT(vtarget);
576
577
0
  UINT32 count = 0;
578
579
0
  if (!ndr_read_uint32(context, s, &count))
580
0
    return FALSE;
581
582
0
  if ((count * itemType->itemSize < hints->count))
583
0
    return FALSE;
584
585
0
  BYTE* target = (BYTE*)vtarget;
586
0
  for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
587
0
  {
588
0
    if (!itemType->readFn(context, s, NULL, target))
589
0
      return FALSE;
590
0
  }
591
592
0
  return ndr_read_align(context, s, /*context->alignBytes*/ 4);
593
0
}
594
595
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
596
                                 NdrMessageType itemType, const BYTE* ptr)
597
0
{
598
0
  WINPR_ASSERT(context);
599
0
  WINPR_ASSERT(s);
600
0
  WINPR_ASSERT(itemType);
601
0
  WINPR_ASSERT(ptr);
602
603
0
  size_t toWrite = len * itemType->itemSize;
604
0
  size_t padding = (4 - (toWrite % 4)) % 4;
605
0
  if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
606
0
    return FALSE;
607
608
0
  for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
609
0
  {
610
0
    if (!itemType->writeFn(context, s, NULL, ptr))
611
0
      return FALSE;
612
0
  }
613
614
0
  if (padding)
615
0
  {
616
0
    Stream_Zero(s, padding);
617
0
    ndr_context_bytes_written(context, padding);
618
0
  }
619
0
  return TRUE;
620
0
}
621
622
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
623
                               void* target)
624
0
{
625
0
  WINPR_ASSERT(context);
626
0
  WINPR_ASSERT(s);
627
0
  WINPR_ASSERT(descr);
628
0
  WINPR_ASSERT(target);
629
630
0
#define NDR_MAX_STRUCT_DEFERRED 16
631
0
  NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
632
0
  size_t ndeferred = 0;
633
634
0
  for (size_t i = 0; i < descr->nfields; i++)
635
0
  {
636
0
    const NdrFieldStruct* field = &descr->fields[i];
637
0
    BYTE* ptr = target;
638
0
    ptr += field->structOffset;
639
0
    void* hints = NULL;
640
641
0
    if (field->hintsField >= 0)
642
0
    {
643
      /* computes the address of the hints field if any */
644
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
645
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
646
647
0
      hints = (BYTE*)target + hintsField->structOffset;
648
0
    }
649
650
0
    switch (field->pointerType)
651
0
    {
652
0
      case NDR_NOT_POINTER:
653
0
        if (!field->typeDescr->readFn(context, s, hints, ptr))
654
0
        {
655
0
          WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
656
0
          return FALSE;
657
0
        }
658
0
        break;
659
0
      case NDR_POINTER:
660
0
      case NDR_POINTER_NON_NULL:
661
0
      {
662
0
        NdrDeferredEntry* deferred = &deferreds[ndeferred];
663
0
        if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
664
0
        {
665
0
          WLog_ERR(TAG, "too many deferred when calling ndr_read_struct_fromDescr for %s",
666
0
                   descr->name);
667
0
          return FALSE;
668
0
        }
669
670
0
        deferred->name = field->name;
671
0
        deferred->hints = hints;
672
0
        deferred->target = ptr;
673
0
        deferred->msg = field->typeDescr;
674
0
        if (!ndr_read_refpointer(context, s, &deferred->ptrId))
675
0
        {
676
0
          WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
677
0
          return FALSE;
678
0
        }
679
680
0
        if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
681
0
        {
682
0
          WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
683
0
          return FALSE;
684
0
        }
685
0
        ndeferred++;
686
0
        break;
687
0
      }
688
0
      default:
689
0
        WLog_ERR(TAG, "%s.%s unknown pointer type 0x%x", descr->name, field->name,
690
0
                 field->pointerType);
691
0
        return FALSE;
692
0
    }
693
0
  }
694
695
0
  return ndr_push_deferreds(context, deferreds, ndeferred);
696
0
}
697
698
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
699
                                const void* src)
700
0
{
701
0
  WINPR_ASSERT(context);
702
0
  WINPR_ASSERT(s);
703
0
  WINPR_ASSERT(descr);
704
0
  WINPR_ASSERT(src);
705
706
0
  NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
707
0
  size_t ndeferred = 0;
708
709
0
  for (size_t i = 0; i < descr->nfields; i++)
710
0
  {
711
0
    const NdrFieldStruct* field = &descr->fields[i];
712
0
    const BYTE* ptr = (const BYTE*)src + field->structOffset;
713
714
0
    const void* hints = NULL;
715
716
0
    if (field->hintsField >= 0)
717
0
    {
718
      /* computes the address of the hints field if any */
719
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
720
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
721
722
0
      hints = (const BYTE*)src + hintsField->structOffset;
723
0
    }
724
725
0
    switch (field->pointerType)
726
0
    {
727
0
      case NDR_POINTER:
728
0
      case NDR_POINTER_NON_NULL:
729
0
      {
730
0
        ndr_refid ptrId = NDR_PTR_NULL;
731
0
        BOOL isNew = 0;
732
0
        ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
733
734
0
        if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
735
0
        {
736
0
          WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
737
0
          return FALSE;
738
0
        }
739
740
0
        if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
741
0
          return FALSE;
742
743
0
        if (isNew)
744
0
        {
745
0
          NdrDeferredEntry* deferred = &deferreds[ndeferred];
746
0
          if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
747
0
          {
748
0
            WLog_ERR(TAG,
749
0
                     "too many deferred when calling ndr_read_struct_fromDescr for %s",
750
0
                     descr->name);
751
0
            return FALSE;
752
0
          }
753
754
0
          deferred->name = field->name;
755
0
          deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints, void*);
756
0
          deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr, void*);
757
0
          deferred->msg = field->typeDescr;
758
0
          ndeferred++;
759
0
        }
760
761
0
        if (!ndr_write_uint32(context, s, ptrId))
762
0
          return FALSE;
763
0
        break;
764
0
      }
765
0
      case NDR_NOT_POINTER:
766
0
        if (!field->typeDescr->writeFn(context, s, hints, ptr))
767
0
        {
768
0
          WLog_ERR(TAG, "error when writing %s.%s", descr->name, field->name);
769
0
          return FALSE;
770
0
        }
771
0
        break;
772
0
      default:
773
0
        break;
774
0
    }
775
0
  }
776
777
0
  return ndr_push_deferreds(context, deferreds, ndeferred);
778
0
}
779
780
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
781
                               const NdrStructDescr* descr, const void* obj)
782
0
{
783
0
  char tabArray[30 + 1];
784
0
  size_t ntabs = (identLevel <= 30) ? identLevel : 30;
785
786
0
  memset(tabArray, '\t', ntabs);
787
0
  tabArray[ntabs] = 0;
788
789
0
  WLog_Print(logger, lvl, "%s%s", tabArray, descr->name);
790
0
  for (size_t i = 0; i < descr->nfields; i++)
791
0
  {
792
0
    const NdrFieldStruct* field = &descr->fields[i];
793
0
    const BYTE* ptr = (const BYTE*)obj + field->structOffset;
794
795
0
    switch (field->pointerType)
796
0
    {
797
0
      case NDR_POINTER:
798
0
      case NDR_POINTER_NON_NULL:
799
0
        ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
800
0
        break;
801
0
      case NDR_NOT_POINTER:
802
0
        break;
803
0
      default:
804
0
        WLog_ERR(TAG, "invalid field->pointerType");
805
0
        break;
806
0
    }
807
808
0
    WLog_Print(logger, lvl, "%s*%s:", tabArray, field->name);
809
0
    if (field->typeDescr->dumpFn)
810
0
      field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
811
0
    else
812
0
      WLog_Print(logger, lvl, "%s\t<no dump function>", tabArray);
813
0
  }
814
0
}
815
816
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr)
817
0
{
818
0
  WINPR_ASSERT(context);
819
0
  WINPR_ASSERT(descr);
820
0
  WINPR_ASSERT(pptr);
821
822
0
  for (size_t i = 0; i < descr->nfields; i++)
823
0
  {
824
0
    const NdrFieldStruct* field = &descr->fields[i];
825
0
    void* ptr = (BYTE*)pptr + field->structOffset;
826
0
    void* hints = NULL;
827
828
0
    if (field->hintsField >= 0)
829
0
    {
830
      /* computes the address of the hints field if any */
831
0
      WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
832
0
      const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
833
834
0
      hints = (BYTE*)pptr + hintsField->structOffset;
835
0
    }
836
837
0
    if (field->pointerType != NDR_NOT_POINTER)
838
0
      ptr = *(void**)ptr;
839
840
0
    if (ptr && field->typeDescr->destroyFn)
841
0
      field->typeDescr->destroyFn(context, hints, ptr);
842
843
0
    if (field->pointerType != NDR_NOT_POINTER)
844
0
      free(ptr);
845
0
  }
846
0
}
847
848
ndr_refid ndr_pointer_refid(const void* ptr)
849
0
{
850
0
  return (ndr_refid)((ULONG_PTR)ptr);
851
0
}
852
853
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, ndr_refid* refId)
854
0
{
855
0
  return ndr_read_uint32(context, s, refId);
856
0
}
857
858
typedef struct
859
{
860
  const void* needle;
861
  ndr_refid* presult;
862
} FindValueArgs;
863
864
static BOOL findValueRefFn(const void* key, void* value, void* parg)
865
0
{
866
0
  WINPR_ASSERT(parg);
867
868
0
  FindValueArgs* args = (FindValueArgs*)parg;
869
0
  if (args->needle == value)
870
0
  {
871
0
    *args->presult = (ndr_refid)(UINT_PTR)key;
872
0
    return FALSE;
873
0
  }
874
0
  return TRUE;
875
0
}
876
877
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
878
0
{
879
0
  WINPR_ASSERT(context);
880
881
0
  FindValueArgs findArgs = { ptr, prefId };
882
0
  if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
883
0
  {
884
0
    *pnewPtr = FALSE;
885
0
    return TRUE;
886
0
  }
887
888
0
  *pnewPtr = TRUE;
889
0
  *prefId = context->refIdCounter + 4;
890
0
  if (!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)(*prefId), ptr))
891
0
    return FALSE;
892
893
0
  context->refIdCounter += 4;
894
0
  return TRUE;
895
0
}
896
897
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
898
                               NdrMessageType descr, void* hints, void** target)
899
0
{
900
0
  WINPR_ASSERT(context);
901
0
  WINPR_ASSERT(s);
902
0
  WINPR_ASSERT(descr);
903
0
  WINPR_ASSERT(target);
904
905
0
  *target = NULL;
906
0
  if (!ptrId)
907
0
    return TRUE;
908
909
0
  void* ret = HashTable_GetItemValue(context->refPointers, (void*)(UINT_PTR)ptrId);
910
0
  if (!ret)
911
0
  {
912
0
    size_t itemCount = ndr_hintsCount(descr, hints);
913
0
    ret = calloc(itemCount, descr->itemSize);
914
0
    if (!ret)
915
0
      return FALSE;
916
917
0
    if (!descr->readFn(context, s, hints, ret) ||
918
0
        !HashTable_Insert(context->refPointers, (void*)(UINT_PTR)ptrId, ret))
919
0
    {
920
0
      if (descr->destroyFn)
921
0
        descr->destroyFn(context, hints, ret);
922
0
      free(ret);
923
0
      return FALSE;
924
0
    }
925
0
  }
926
927
0
  *target = ret;
928
0
  return TRUE;
929
0
}
930
931
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred)
932
0
{
933
0
  WINPR_ASSERT(context);
934
0
  WINPR_ASSERT(deferreds);
935
936
0
  if (!ndeferred)
937
0
    return TRUE;
938
939
0
  if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
940
0
  {
941
0
    WLog_ERR(TAG, "too many deferred");
942
0
    return FALSE;
943
0
  }
944
945
0
  for (size_t i = ndeferred; i > 0; i--, context->ndeferred++)
946
0
  {
947
0
    context->deferred[context->ndeferred] = deferreds[i - 1];
948
0
  }
949
0
  return TRUE;
950
0
}
951
952
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s)
953
0
{
954
0
  WINPR_ASSERT(context);
955
0
  WINPR_ASSERT(s);
956
957
0
  while (context->ndeferred)
958
0
  {
959
0
    NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
960
0
    context->ndeferred--;
961
962
0
    WLog_VRB(TAG, "treating read deferred 0x%x for %s", current.ptrId, current.name);
963
0
    if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
964
0
                                   (void**)current.target))
965
0
    {
966
0
      WLog_ERR(TAG, "error parsing deferred %s", current.name);
967
0
      return FALSE;
968
0
    }
969
0
  }
970
971
0
  return TRUE;
972
0
}
973
974
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s)
975
0
{
976
0
  WINPR_ASSERT(context);
977
0
  WINPR_ASSERT(s);
978
979
0
  while (context->ndeferred)
980
0
  {
981
0
    NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
982
0
    context->ndeferred--;
983
984
0
    WLog_VRB(TAG, "treating write deferred for %s", current.name);
985
0
    if (!current.msg->writeFn(context, s, current.hints, current.target))
986
0
    {
987
0
      WLog_ERR(TAG, "error writing deferred %s", current.name);
988
0
      return FALSE;
989
0
    }
990
0
  }
991
992
0
  return TRUE;
993
0
}
994
995
BOOL ndr_write_data(NdrContext* context, wStream* s, const void* data, size_t sz)
996
0
{
997
0
  if (!Stream_EnsureRemainingCapacity(s, sz))
998
0
    return FALSE;
999
1000
0
  Stream_Write(s, data, sz);
1001
0
  ndr_context_bytes_written(context, sz);
1002
  return TRUE;
1003
0
}