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