/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 = ®_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 | } |