FreeRDP
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 
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 {
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 
89 static 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 
123 static 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 
135 static 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 
277 fail:
278  free(value);
279  free(name);
280  return NULL;
281 }
282 
283 static 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 
291 static 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 
302 static char* reg_load_peek_next_line(Reg* reg)
303 {
304  WINPR_ASSERT(reg);
305  return reg->next_line;
306 }
307 
308 static void reg_insert_key(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 
347 static 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 
416 static 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 
433 static void reg_unload_value(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 
450 static 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 
470 static 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 
488 Reg* 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 
524 fail:
525  reg_close(reg);
526  return NULL;
527 }
528 
529 void 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 
541 const 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 }