Coverage Report

Created: 2026-09-14 06:32

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