Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/winpr/libwinpr/registry/registry_reg.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Windows Registry (.reg file format)
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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
20
#include <winpr/config.h>
21
22
#include <errno.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include <winpr/wtypes.h>
28
#include <winpr/assert.h>
29
#include <winpr/crt.h>
30
#include <winpr/file.h>
31
32
#include "registry_reg.h"
33
34
#include "../log.h"
35
#define TAG WINPR_TAG("registry")
36
37
206k
#define WINPR_HKLM_HIVE "/etc/winpr/HKLM.reg"
38
39
struct reg_data_type
40
{
41
  char* tag;
42
  size_t length;
43
  DWORD type;
44
};
45
46
static struct reg_data_type REG_DATA_TYPE_TABLE[] = { { "\"", 1, REG_SZ },
47
                                                    { "dword:", 6, REG_DWORD },
48
                                                    { "str:\"", 5, REG_SZ },
49
                                                    { "str(2):\"", 8, REG_EXPAND_SZ },
50
                                                    { "str(7):\"", 8, REG_MULTI_SZ },
51
                                                    { "hex:", 4, REG_BINARY },
52
                                                    { "hex(2):\"", 8, REG_EXPAND_SZ },
53
                                                    { "hex(7):\"", 8, REG_MULTI_SZ },
54
                                                    { "hex(b):\"", 8, REG_QWORD } };
55
56
static char* reg_data_type_string(DWORD type)
57
0
{
58
0
  switch (type)
59
0
  {
60
0
    case REG_NONE:
61
0
      return "REG_NONE";
62
0
    case REG_SZ:
63
0
      return "REG_SZ";
64
0
    case REG_EXPAND_SZ:
65
0
      return "REG_EXPAND_SZ";
66
0
    case REG_BINARY:
67
0
      return "REG_BINARY";
68
0
    case REG_DWORD:
69
0
      return "REG_DWORD";
70
0
    case REG_DWORD_BIG_ENDIAN:
71
0
      return "REG_DWORD_BIG_ENDIAN";
72
0
    case REG_LINK:
73
0
      return "REG_LINK";
74
0
    case REG_MULTI_SZ:
75
0
      return "REG_MULTI_SZ";
76
0
    case REG_RESOURCE_LIST:
77
0
      return "REG_RESOURCE_LIST";
78
0
    case REG_FULL_RESOURCE_DESCRIPTOR:
79
0
      return "REG_FULL_RESOURCE_DESCRIPTOR";
80
0
    case REG_RESOURCE_REQUIREMENTS_LIST:
81
0
      return "REG_RESOURCE_REQUIREMENTS_LIST";
82
0
    case REG_QWORD:
83
0
      return "REG_QWORD";
84
0
    default:
85
0
      return "REG_UNKNOWN";
86
0
  }
87
0
}
88
89
static BOOL reg_load_start(Reg* reg)
90
0
{
91
0
  char* buffer = NULL;
92
0
  INT64 file_size = 0;
93
94
0
  WINPR_ASSERT(reg);
95
0
  WINPR_ASSERT(reg->fp);
96
97
0
  _fseeki64(reg->fp, 0, SEEK_END);
98
0
  file_size = _ftelli64(reg->fp);
99
0
  _fseeki64(reg->fp, 0, SEEK_SET);
100
0
  reg->line = NULL;
101
0
  reg->next_line = NULL;
102
103
0
  if (file_size < 1)
104
0
    return FALSE;
105
106
0
  buffer = (char*)realloc(reg->buffer, (size_t)file_size + 2);
107
108
0
  if (!buffer)
109
0
    return FALSE;
110
0
  reg->buffer = buffer;
111
112
0
  if (fread(reg->buffer, (size_t)file_size, 1, reg->fp) != 1)
113
0
    return FALSE;
114
115
0
  reg->buffer[file_size] = '\n';
116
0
  reg->buffer[file_size + 1] = '\0';
117
0
  reg->next_line = strtok(reg->buffer, "\n");
118
0
  return TRUE;
119
0
}
120
121
static void reg_load_finish(Reg* reg)
122
0
{
123
0
  if (!reg)
124
0
    return;
125
126
0
  if (reg->buffer)
127
0
  {
128
0
    free(reg->buffer);
129
0
    reg->buffer = NULL;
130
0
  }
131
0
}
132
133
static RegVal* reg_load_value(const Reg* reg, RegKey* key)
134
0
{
135
0
  const char* p[5] = { 0 };
136
0
  size_t length = 0;
137
0
  char* name = NULL;
138
0
  const char* type = NULL;
139
0
  const char* data = NULL;
140
0
  RegVal* value = NULL;
141
142
0
  WINPR_ASSERT(reg);
143
0
  WINPR_ASSERT(key);
144
0
  WINPR_ASSERT(reg->line);
145
146
0
  p[0] = reg->line + 1;
147
0
  p[1] = strstr(p[0], "\"=");
148
0
  if (!p[1])
149
0
    return NULL;
150
151
0
  p[2] = p[1] + 2;
152
0
  type = p[2];
153
154
0
  if (p[2][0] == '"')
155
0
    p[3] = p[2];
156
0
  else
157
0
    p[3] = strchr(p[2], ':');
158
159
0
  if (!p[3])
160
0
    return NULL;
161
162
0
  data = p[3] + 1;
163
0
  length = (size_t)(p[1] - p[0]);
164
0
  if (length < 1)
165
0
    goto fail;
166
167
0
  name = (char*)calloc(length + 1, sizeof(char));
168
169
0
  if (!name)
170
0
    goto fail;
171
172
0
  memcpy(name, p[0], length);
173
0
  value = (RegVal*)calloc(1, sizeof(RegVal));
174
175
0
  if (!value)
176
0
    goto fail;
177
178
0
  value->name = name;
179
0
  value->type = REG_NONE;
180
181
0
  for (size_t index = 0; index < ARRAYSIZE(REG_DATA_TYPE_TABLE); index++)
182
0
  {
183
0
    const struct reg_data_type* current = &REG_DATA_TYPE_TABLE[index];
184
0
    WINPR_ASSERT(current->tag);
185
0
    WINPR_ASSERT(current->length > 0);
186
0
    WINPR_ASSERT(current->type != REG_NONE);
187
188
0
    if (strncmp(type, current->tag, current->length) == 0)
189
0
    {
190
0
      value->type = current->type;
191
0
      break;
192
0
    }
193
0
  }
194
195
0
  switch (value->type)
196
0
  {
197
0
    case REG_DWORD:
198
0
    {
199
0
      unsigned long val = 0;
200
0
      errno = 0;
201
0
      val = strtoul(data, NULL, 0);
202
203
0
      if ((errno != 0) || (val > UINT32_MAX))
204
0
      {
205
0
        WLog_WARN(TAG, "%s::%s value %s invalid", key->name, value->name, data);
206
0
        goto fail;
207
0
      }
208
0
      value->data.dword = (DWORD)val;
209
0
    }
210
0
    break;
211
0
    case REG_QWORD:
212
0
    {
213
0
      unsigned long long val = 0;
214
0
      errno = 0;
215
0
      val = strtoull(data, NULL, 0);
216
217
0
      if ((errno != 0) || (val > UINT64_MAX))
218
0
      {
219
0
        WLog_WARN(TAG, "%s::%s value %s invalid", key->name, value->name, data);
220
0
        goto fail;
221
0
      }
222
223
0
      value->data.qword = (UINT64)val;
224
0
    }
225
0
    break;
226
0
    case REG_SZ:
227
0
    {
228
0
      size_t len = 0;
229
0
      size_t cmp = 0;
230
0
      char* end = NULL;
231
0
      char* start = strchr(data, '"');
232
0
      if (!start)
233
0
        goto fail;
234
235
      /* Check for terminating quote, check it is the last symbol */
236
0
      len = strlen(start);
237
0
      end = strchr(start + 1, '"');
238
0
      if (!end)
239
0
        goto fail;
240
0
      cmp = end - start + 1;
241
0
      if (len != cmp)
242
0
        goto fail;
243
0
      if (start[0] == '"')
244
0
        start++;
245
0
      if (end[0] == '"')
246
0
        end[0] = '\0';
247
0
      value->data.string = _strdup(start);
248
249
0
      if (!value->data.string)
250
0
        goto fail;
251
0
    }
252
0
    break;
253
0
    default:
254
0
      WLog_ERR(TAG, "[%s] %s unimplemented format: %s", key->name, value->name,
255
0
               reg_data_type_string(value->type));
256
0
      break;
257
0
  }
258
259
0
  if (!key->values)
260
0
  {
261
0
    key->values = value;
262
0
  }
263
0
  else
264
0
  {
265
0
    RegVal* pValue = key->values;
266
267
0
    while (pValue->next != NULL)
268
0
    {
269
0
      pValue = pValue->next;
270
0
    }
271
272
0
    pValue->next = value;
273
0
    value->prev = pValue;
274
0
  }
275
276
0
  return value;
277
278
0
fail:
279
0
  free(value);
280
0
  free(name);
281
0
  return NULL;
282
0
}
283
284
static BOOL reg_load_has_next_line(Reg* reg)
285
0
{
286
0
  if (!reg)
287
0
    return 0;
288
289
0
  return (reg->next_line != NULL) ? 1 : 0;
290
0
}
291
292
static char* reg_load_get_next_line(Reg* reg)
293
0
{
294
0
  if (!reg)
295
0
    return NULL;
296
297
0
  reg->line = reg->next_line;
298
0
  reg->next_line = strtok(NULL, "\n");
299
0
  reg->line_length = strlen(reg->line);
300
0
  return reg->line;
301
0
}
302
303
static char* reg_load_peek_next_line(Reg* reg)
304
0
{
305
0
  WINPR_ASSERT(reg);
306
0
  return reg->next_line;
307
0
}
308
309
static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey)
310
0
{
311
0
  char* name = NULL;
312
0
  char* path = NULL;
313
0
  char* save = NULL;
314
315
0
  WINPR_ASSERT(reg);
316
0
  WINPR_ASSERT(key);
317
0
  WINPR_ASSERT(subkey);
318
0
  WINPR_ASSERT(subkey->name);
319
320
0
  path = _strdup(subkey->name);
321
322
0
  if (!path)
323
0
    return;
324
325
0
  name = strtok_s(path, "\\", &save);
326
327
0
  while (name != NULL)
328
0
  {
329
0
    if (strcmp(key->name, name) == 0)
330
0
    {
331
0
      if (save)
332
0
        subkey->subname = _strdup(save);
333
334
      /* TODO: free allocated memory in error case */
335
0
      if (!subkey->subname)
336
0
      {
337
0
        free(path);
338
0
        return;
339
0
      }
340
0
    }
341
342
0
    name = strtok_s(NULL, "\\", &save);
343
0
  }
344
345
0
  free(path);
346
0
}
347
348
static RegKey* reg_load_key(Reg* reg, RegKey* key)
349
0
{
350
0
  char* p[2];
351
0
  size_t length = 0;
352
0
  RegKey* subkey = NULL;
353
354
0
  WINPR_ASSERT(reg);
355
0
  WINPR_ASSERT(key);
356
357
0
  WINPR_ASSERT(reg->line);
358
0
  p[0] = reg->line + 1;
359
0
  p[1] = strrchr(p[0], ']');
360
0
  if (!p[1])
361
0
    return NULL;
362
363
0
  subkey = (RegKey*)calloc(1, sizeof(RegKey));
364
365
0
  if (!subkey)
366
0
    return NULL;
367
368
0
  length = (size_t)(p[1] - p[0]);
369
0
  subkey->name = (char*)malloc(length + 1);
370
371
0
  if (!subkey->name)
372
0
  {
373
0
    free(subkey);
374
0
    return NULL;
375
0
  }
376
377
0
  memcpy(subkey->name, p[0], length);
378
0
  subkey->name[length] = '\0';
379
380
0
  while (reg_load_has_next_line(reg))
381
0
  {
382
0
    char* line = reg_load_peek_next_line(reg);
383
384
0
    if (line[0] == '[')
385
0
      break;
386
387
0
    reg_load_get_next_line(reg);
388
389
0
    if (reg->line[0] == '"')
390
0
    {
391
0
      reg_load_value(reg, subkey);
392
0
    }
393
0
  }
394
395
0
  reg_insert_key(reg, key, subkey);
396
397
0
  if (!key->subkeys)
398
0
  {
399
0
    key->subkeys = subkey;
400
0
  }
401
0
  else
402
0
  {
403
0
    RegKey* pKey = key->subkeys;
404
405
0
    while (pKey->next != NULL)
406
0
    {
407
0
      pKey = pKey->next;
408
0
    }
409
410
0
    pKey->next = subkey;
411
0
    subkey->prev = pKey;
412
0
  }
413
414
0
  return subkey;
415
0
}
416
417
static void reg_load(Reg* reg)
418
0
{
419
0
  reg_load_start(reg);
420
421
0
  while (reg_load_has_next_line(reg))
422
0
  {
423
0
    reg_load_get_next_line(reg);
424
425
0
    if (reg->line[0] == '[')
426
0
    {
427
0
      reg_load_key(reg, reg->root_key);
428
0
    }
429
0
  }
430
431
0
  reg_load_finish(reg);
432
0
}
433
434
static void reg_unload_value(Reg* reg, RegVal* value)
435
0
{
436
0
  WINPR_ASSERT(reg);
437
0
  WINPR_ASSERT(value);
438
439
0
  switch (value->type)
440
0
  {
441
0
    case REG_SZ:
442
0
      free(value->data.string);
443
0
      break;
444
0
    default:
445
0
      break;
446
0
  }
447
448
0
  free(value);
449
0
}
450
451
static void reg_unload_key(Reg* reg, RegKey* key)
452
0
{
453
0
  RegVal* pValue = NULL;
454
455
0
  WINPR_ASSERT(reg);
456
0
  WINPR_ASSERT(key);
457
458
0
  pValue = key->values;
459
460
0
  while (pValue != NULL)
461
0
  {
462
0
    RegVal* pValueNext = pValue->next;
463
0
    reg_unload_value(reg, pValue);
464
0
    pValue = pValueNext;
465
0
  }
466
467
0
  free(key->name);
468
0
  free(key);
469
0
}
470
471
static void reg_unload(Reg* reg)
472
206k
{
473
206k
  WINPR_ASSERT(reg);
474
206k
  if (reg->root_key)
475
0
  {
476
0
    RegKey* pKey = reg->root_key->subkeys;
477
478
0
    while (pKey != NULL)
479
0
    {
480
0
      RegKey* pKeyNext = pKey->next;
481
0
      reg_unload_key(reg, pKey);
482
0
      pKey = pKeyNext;
483
0
    }
484
485
0
    free(reg->root_key);
486
0
  }
487
206k
}
488
489
Reg* reg_open(BOOL read_only)
490
206k
{
491
206k
  Reg* reg = (Reg*)calloc(1, sizeof(Reg));
492
493
206k
  if (!reg)
494
0
    return NULL;
495
496
206k
  reg->read_only = read_only;
497
206k
  reg->filename = WINPR_HKLM_HIVE;
498
499
206k
  if (reg->read_only)
500
206k
    reg->fp = winpr_fopen(reg->filename, "r");
501
0
  else
502
0
  {
503
0
    reg->fp = winpr_fopen(reg->filename, "r+");
504
505
0
    if (!reg->fp)
506
0
      reg->fp = winpr_fopen(reg->filename, "w+");
507
0
  }
508
509
206k
  if (!reg->fp)
510
206k
    goto fail;
511
512
0
  reg->root_key = (RegKey*)calloc(1, sizeof(RegKey));
513
514
0
  if (!reg->root_key)
515
0
    goto fail;
516
517
0
  reg->root_key->values = NULL;
518
0
  reg->root_key->subkeys = NULL;
519
0
  reg->root_key->name = "HKEY_LOCAL_MACHINE";
520
0
  reg_load(reg);
521
0
  return reg;
522
523
206k
fail:
524
206k
  reg_close(reg);
525
206k
  return NULL;
526
0
}
527
528
void reg_close(Reg* reg)
529
206k
{
530
206k
  if (reg)
531
206k
  {
532
206k
    reg_unload(reg);
533
206k
    if (reg->fp)
534
0
      fclose(reg->fp);
535
206k
    free(reg);
536
206k
  }
537
206k
}
538
539
const char* reg_type_string(DWORD type)
540
0
{
541
0
  switch (type)
542
0
  {
543
0
    case REG_NONE:
544
0
      return "REG_NONE";
545
0
    case REG_SZ:
546
0
      return "REG_SZ";
547
0
    case REG_EXPAND_SZ:
548
0
      return "REG_EXPAND_SZ";
549
0
    case REG_BINARY:
550
0
      return "REG_BINARY";
551
0
    case REG_DWORD:
552
0
      return "REG_DWORD";
553
0
    case REG_DWORD_BIG_ENDIAN:
554
0
      return "REG_DWORD_BIG_ENDIAN";
555
0
    case REG_LINK:
556
0
      return "REG_LINK";
557
0
    case REG_MULTI_SZ:
558
0
      return "REG_MULTI_SZ";
559
0
    case REG_RESOURCE_LIST:
560
0
      return "REG_RESOURCE_LIST";
561
0
    case REG_FULL_RESOURCE_DESCRIPTOR:
562
0
      return "REG_FULL_RESOURCE_DESCRIPTOR";
563
0
    case REG_RESOURCE_REQUIREMENTS_LIST:
564
0
      return "REG_RESOURCE_REQUIREMENTS_LIST";
565
0
    case REG_QWORD:
566
0
      return "REG_QWORD";
567
0
    default:
568
0
      return "REG_UNKNOWN";
569
0
  }
570
0
}