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  size_t len = 0;
231  size_t cmp = 0;
232  char* end = NULL;
233  char* start = strchr(data, '"');
234  if (!start)
235  goto fail;
236 
237  /* Check for terminating quote, check it is the last symbol */
238  len = strlen(start);
239  end = strchr(start + 1, '"');
240  if (!end)
241  goto fail;
242  cmp = end - start + 1;
243  if (len != cmp)
244  goto fail;
245  if (start[0] == '"')
246  start++;
247  if (end[0] == '"')
248  end[0] = '\0';
249  value->data.string = _strdup(start);
250 
251  if (!value->data.string)
252  goto fail;
253  }
254  break;
255  default:
256  WLog_ERR(TAG, "[%s] %s unimplemented format: %s", key->name, value->name,
257  reg_data_type_string(value->type));
258  break;
259  }
260 
261  if (!key->values)
262  {
263  key->values = value;
264  }
265  else
266  {
267  RegVal* pValue = key->values;
268 
269  while (pValue->next != NULL)
270  {
271  pValue = pValue->next;
272  }
273 
274  pValue->next = value;
275  value->prev = pValue;
276  }
277 
278  return value;
279 
280 fail:
281  free(value);
282  free(name);
283  return NULL;
284 }
285 
286 static BOOL reg_load_has_next_line(Reg* reg)
287 {
288  if (!reg)
289  return 0;
290 
291  return (reg->next_line != NULL) ? 1 : 0;
292 }
293 
294 static char* reg_load_get_next_line(Reg* reg)
295 {
296  if (!reg)
297  return NULL;
298 
299  reg->line = reg->next_line;
300  reg->next_line = strtok_s(NULL, "\n", &reg->saveptr);
301  reg->line_length = strlen(reg->line);
302  return reg->line;
303 }
304 
305 static char* reg_load_peek_next_line(Reg* reg)
306 {
307  WINPR_ASSERT(reg);
308  return reg->next_line;
309 }
310 
311 static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey)
312 {
313  char* name = NULL;
314  char* path = NULL;
315  char* save = NULL;
316 
317  WINPR_ASSERT(reg);
318  WINPR_ASSERT(key);
319  WINPR_ASSERT(subkey);
320  WINPR_ASSERT(subkey->name);
321 
322  path = _strdup(subkey->name);
323 
324  if (!path)
325  return;
326 
327  name = strtok_s(path, "\\", &save);
328 
329  while (name != NULL)
330  {
331  if (strcmp(key->name, name) == 0)
332  {
333  if (save)
334  subkey->subname = _strdup(save);
335 
336  /* TODO: free allocated memory in error case */
337  if (!subkey->subname)
338  {
339  free(path);
340  return;
341  }
342  }
343 
344  name = strtok_s(NULL, "\\", &save);
345  }
346 
347  free(path);
348 }
349 
350 static RegKey* reg_load_key(Reg* reg, RegKey* key)
351 {
352  char* p[2];
353  size_t length = 0;
354  RegKey* subkey = NULL;
355 
356  WINPR_ASSERT(reg);
357  WINPR_ASSERT(key);
358 
359  WINPR_ASSERT(reg->line);
360  p[0] = reg->line + 1;
361  p[1] = strrchr(p[0], ']');
362  if (!p[1])
363  return NULL;
364 
365  subkey = (RegKey*)calloc(1, sizeof(RegKey));
366 
367  if (!subkey)
368  return NULL;
369 
370  length = (size_t)(p[1] - p[0]);
371  subkey->name = (char*)malloc(length + 1);
372 
373  if (!subkey->name)
374  {
375  free(subkey);
376  return NULL;
377  }
378 
379  memcpy(subkey->name, p[0], length);
380  subkey->name[length] = '\0';
381 
382  while (reg_load_has_next_line(reg))
383  {
384  char* line = reg_load_peek_next_line(reg);
385 
386  if (line[0] == '[')
387  break;
388 
389  reg_load_get_next_line(reg);
390 
391  if (reg->line[0] == '"')
392  {
393  reg_load_value(reg, subkey);
394  }
395  }
396 
397  reg_insert_key(reg, key, subkey);
398 
399  if (!key->subkeys)
400  {
401  key->subkeys = subkey;
402  }
403  else
404  {
405  RegKey* pKey = key->subkeys;
406 
407  while (pKey->next != NULL)
408  {
409  pKey = pKey->next;
410  }
411 
412  pKey->next = subkey;
413  subkey->prev = pKey;
414  }
415 
416  return subkey;
417 }
418 
419 static void reg_load(Reg* reg)
420 {
421  reg_load_start(reg);
422 
423  while (reg_load_has_next_line(reg))
424  {
425  reg_load_get_next_line(reg);
426 
427  if (reg->line[0] == '[')
428  {
429  reg_load_key(reg, reg->root_key);
430  }
431  }
432 
433  reg_load_finish(reg);
434 }
435 
436 static void reg_unload_value(Reg* reg, RegVal* value)
437 {
438  WINPR_ASSERT(reg);
439  WINPR_ASSERT(value);
440 
441  switch (value->type)
442  {
443  case REG_SZ:
444  free(value->data.string);
445  break;
446  default:
447  break;
448  }
449 
450  free(value);
451 }
452 
453 static void reg_unload_key(Reg* reg, RegKey* key)
454 {
455  RegVal* pValue = NULL;
456 
457  WINPR_ASSERT(reg);
458  WINPR_ASSERT(key);
459 
460  pValue = key->values;
461 
462  while (pValue != NULL)
463  {
464  RegVal* pValueNext = pValue->next;
465  reg_unload_value(reg, pValue);
466  pValue = pValueNext;
467  }
468 
469  free(key->name);
470  free(key);
471 }
472 
473 static void reg_unload(Reg* reg)
474 {
475  WINPR_ASSERT(reg);
476  if (reg->root_key)
477  {
478  RegKey* pKey = reg->root_key->subkeys;
479 
480  while (pKey != NULL)
481  {
482  RegKey* pKeyNext = pKey->next;
483  reg_unload_key(reg, pKey);
484  pKey = pKeyNext;
485  }
486 
487  free(reg->root_key);
488  }
489 }
490 
491 Reg* reg_open(BOOL read_only)
492 {
493  Reg* reg = (Reg*)calloc(1, sizeof(Reg));
494 
495  if (!reg)
496  return NULL;
497 
498  reg->read_only = read_only;
499  reg->filename = winpr_GetConfigFilePath(TRUE, "HKLM.reg");
500  if (!reg->filename)
501  goto fail;
502 
503  if (reg->read_only)
504  reg->fp = winpr_fopen(reg->filename, "r");
505  else
506  {
507  reg->fp = winpr_fopen(reg->filename, "r+");
508 
509  if (!reg->fp)
510  reg->fp = winpr_fopen(reg->filename, "w+");
511  }
512 
513  if (!reg->fp)
514  goto fail;
515 
516  reg->root_key = (RegKey*)calloc(1, sizeof(RegKey));
517 
518  if (!reg->root_key)
519  goto fail;
520 
521  reg->root_key->values = NULL;
522  reg->root_key->subkeys = NULL;
523  reg->root_key->name = "HKEY_LOCAL_MACHINE";
524  reg_load(reg);
525  return reg;
526 
527 fail:
528  reg_close(reg);
529  return NULL;
530 }
531 
532 void reg_close(Reg* reg)
533 {
534  if (reg)
535  {
536  reg_unload(reg);
537  if (reg->fp)
538  (void)fclose(reg->fp);
539  free(reg->filename);
540  free(reg);
541  }
542 }
543 
544 const char* reg_type_string(DWORD type)
545 {
546  switch (type)
547  {
548  case REG_NONE:
549  return "REG_NONE";
550  case REG_SZ:
551  return "REG_SZ";
552  case REG_EXPAND_SZ:
553  return "REG_EXPAND_SZ";
554  case REG_BINARY:
555  return "REG_BINARY";
556  case REG_DWORD:
557  return "REG_DWORD";
558  case REG_DWORD_BIG_ENDIAN:
559  return "REG_DWORD_BIG_ENDIAN";
560  case REG_LINK:
561  return "REG_LINK";
562  case REG_MULTI_SZ:
563  return "REG_MULTI_SZ";
564  case REG_RESOURCE_LIST:
565  return "REG_RESOURCE_LIST";
566  case REG_FULL_RESOURCE_DESCRIPTOR:
567  return "REG_FULL_RESOURCE_DESCRIPTOR";
568  case REG_RESOURCE_REQUIREMENTS_LIST:
569  return "REG_RESOURCE_REQUIREMENTS_LIST";
570  case REG_QWORD:
571  return "REG_QWORD";
572  default:
573  return "REG_UNKNOWN";
574  }
575 }