Coverage Report

Created: 2025-07-01 06:46

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