source: trunk/src/reglookup.c @ 228

Last change on this file since 228 was 228, checked in by tim, 13 years ago

added a test case for pyregfi multithreaded use
updated the regfi multithreaded test case
fixed several ctypes interface problems in pyregfi
added locking to pyregfi iterators for thread safety
fixed regfi talloc race conditions with an additional lock

  • Property svn:keywords set to Id
File size: 17.4 KB
Line 
1/*
2 * A utility to read a Windows NT and later registry files.
3 *
4 * Copyright (C) 2005-2010 Timothy D. Morgan
5 * Copyright (C) 2010 Tobias Mueller (portions of '-i' code)
6 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 3 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
20 *
21 * $Id: reglookup.c 228 2011-04-18 20:25:46Z tim $
22 */
23
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <strings.h>
29#include <time.h>
30#include "regfi.h"
31#include "void_stack.h"
32
33/* Globals, influenced by command line parameters */
34bool print_value_mtime = false;
35bool print_verbose = false;
36bool print_security = false;
37bool print_header = true;
38bool path_filter_enabled = false;
39bool type_filter_enabled = false;
40char* path_filter = NULL;
41int type_filter;
42const char* registry_file = NULL;
43
44/* Other globals */
45REGFI_FILE* f;
46
47
48/* XXX: A hack to share some functions with reglookup-recover.c.
49 *      Should move these into a proper library at some point.
50 */
51#include "common.c"
52
53
54void printValue(REGFI_ITERATOR* iter, const REGFI_VK* vk, char* prefix)
55{
56  const REGFI_DATA* data;
57  char* quoted_value = NULL;
58  char* quoted_name = NULL;
59  char* conv_error = NULL;
60  const char* str_type = NULL;
61  char mtime[20];
62  time_t tmp_time[1];
63  struct tm* tmp_time_s = NULL;
64
65  quoted_name = get_quoted_valuename(vk);
66  if (quoted_name == NULL)
67  { /* Value names are NULL when we're looking at the "(default)" value.
68     * Currently we just return a 0-length string to try an eliminate
69     * ambiguity with a literal "(default)" value.  The data type of a line
70     * in the output allows one to differentiate between the parent key and
71     * this value.
72     */
73    quoted_name = malloc(1*sizeof(char));
74    if(quoted_name == NULL)
75      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
76    quoted_name[0] = '\0';
77  }
78 
79  data = regfi_fetch_data(iter->f, vk);
80
81  printMsgs(iter->f);
82  if(data != NULL)
83  {
84    quoted_value = data_to_ascii(data, &conv_error);
85    if(quoted_value == NULL)
86    {
87      if(conv_error == NULL)
88        fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
89                "Memory allocation failure likely.\n", prefix, quoted_name);
90      else
91        fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
92                "Returned error: %s\n", prefix, quoted_name, conv_error);
93    }
94    else if(conv_error != NULL)
95      fprintf(stderr, "WARN: While quoting value for '%s/%s', "
96              "warning returned: %s\n", prefix, quoted_name, conv_error);
97    regfi_free_record(iter->f, data);
98  }
99
100  if(print_value_mtime)
101  {
102    *tmp_time = regfi_nt2unix_time(&iter->cur_key->mtime);
103    tmp_time_s = gmtime(tmp_time);
104    strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
105  }
106  else
107    mtime[0] = '\0';
108
109  str_type = regfi_type_val2str(vk->type);
110  if(print_security)
111  {
112    if(str_type == NULL)
113      printf("%s/%s,0x%.8X,%s,%s,,,,\n", prefix, quoted_name,
114             vk->type, quoted_value, mtime);
115    else
116      printf("%s/%s,%s,%s,%s,,,,\n", prefix, quoted_name,
117             str_type, quoted_value, mtime);
118  }
119  else
120  {
121    if(str_type == NULL)
122      printf("%s/%s,0x%.8X,%s,%s\n", prefix, quoted_name,
123             vk->type, quoted_value, mtime);
124    else
125      printf("%s/%s,%s,%s,%s\n", prefix, quoted_name,
126             str_type, quoted_value, mtime);
127  }
128
129  if(quoted_value != NULL)
130    free(quoted_value);
131  if(quoted_name != NULL)
132    free(quoted_name);
133  if(conv_error != NULL)
134    free(conv_error);
135}
136
137
138char** splitPath(const char* s)
139{
140  char** ret_val;
141  const char* cur = s;
142  char* next = NULL;
143  char* copy;
144  uint32_t ret_cur = 0;
145
146  ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
147  if (ret_val == NULL)
148    return NULL;
149  ret_val[0] = NULL;
150
151  /* We return a well-formed, 0-length, path even when input is icky. */
152  if (s == NULL)
153    return ret_val;
154 
155  while((next = strchr(cur, '/')) != NULL)
156  {
157    if ((next-cur) > 0)
158    {
159      copy = (char*)malloc((next-cur+1)*sizeof(char));
160      if(copy == NULL)
161        bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
162         
163      memcpy(copy, cur, next-cur);
164      copy[next-cur] = '\0';
165      ret_val[ret_cur++] = copy;
166      if(ret_cur < (REGFI_MAX_DEPTH+1+1))
167        ret_val[ret_cur] = NULL;
168      else
169        bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
170    }
171    cur = next+1;
172  }
173
174  /* Grab last element, if path doesn't end in '/'. */
175  if(strlen(cur) > 0)
176  {
177    copy = strdup(cur);
178    ret_val[ret_cur++] = copy;
179    if(ret_cur < (REGFI_MAX_DEPTH+1+1))
180      ret_val[ret_cur] = NULL;
181    else
182      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
183  }
184
185  return ret_val;
186}
187
188
189void freePath(char** path)
190{
191  uint32_t i;
192
193  if(path == NULL)
194    return;
195
196  for(i=0; path[i] != NULL; i++)
197    free(path[i]);
198
199  free(path);
200}
201
202
203/* Returns a quoted path from an iterator's stack */
204char* iter2Path(REGFI_ITERATOR* i)
205{
206  const REGFI_ITER_POSITION* cur;
207  const REGFI_NK* tmp_key;
208  uint32_t buf_left = 127;
209  uint32_t buf_len = buf_left+1;
210  uint32_t name_len = 0;
211  uint32_t grow_amt;
212  char* buf;
213  char* new_buf;
214  char* name;
215  void_stack_iterator* iter;
216 
217  buf = (char*)malloc((buf_len)*sizeof(char));
218  if (buf == NULL)
219    return NULL;
220  buf[0] = '\0';
221
222  iter = void_stack_iterator_new(i->key_positions);
223  if (iter == NULL)
224  {
225    free(buf);
226    return NULL;
227  }
228
229  /* skip root element */
230  if(void_stack_size(i->key_positions) < 1)
231  {
232    buf[0] = '/';
233    buf[1] = '\0';
234    return buf;
235  }
236  cur = void_stack_iterator_next(iter);
237
238  do
239  {
240    cur = void_stack_iterator_next(iter);
241    if (cur == NULL)
242      tmp_key = i->cur_key;
243    else
244      tmp_key = cur->nk;
245
246    name = get_quoted_keyname(tmp_key);
247
248    buf[buf_len-buf_left-1] = '/';
249    buf_left -= 1;
250    name_len = strlen(name);
251    if(name_len+1 > buf_left)
252    {
253      grow_amt = (uint32_t)(buf_len/2);
254      buf_len += name_len+1+grow_amt-buf_left;
255      if((new_buf = realloc(buf, buf_len)) == NULL)
256      {
257        free(name);
258        free(buf);
259        free(iter);
260        return NULL;
261      }
262      buf = new_buf;
263      buf_left = grow_amt + name_len + 1;
264    }
265    strncpy(buf+(buf_len-buf_left-1), name, name_len);
266    buf_left -= name_len;
267    buf[buf_len-buf_left-1] = '\0';
268    free(name);
269  } while(cur != NULL);
270
271  return buf;
272}
273
274
275void printValueList(REGFI_ITERATOR* iter, char* prefix)
276{
277  const REGFI_VK* value;
278
279  regfi_iterator_first_value(iter);
280  while((value = regfi_iterator_cur_value(iter)) != NULL)
281  {
282    if(!type_filter_enabled || (value->type == type_filter))
283      printValue(iter, value, prefix);
284    regfi_free_record(iter->f, value);
285    regfi_iterator_next_value(iter);
286    printMsgs(iter->f);
287  }
288}
289
290
291void printKey(REGFI_ITERATOR* iter, char* full_path)
292{
293  static char empty_str[1] = "";
294  char* owner = NULL;
295  char* group = NULL;
296  char* sacl = NULL;
297  char* dacl = NULL;
298  char mtime[24];
299  char* quoted_classname;
300  const REGFI_SK* sk;
301  const REGFI_NK* key = regfi_iterator_cur_key(iter);
302  const REGFI_CLASSNAME* classname;
303
304  formatTime(&key->mtime, mtime);
305
306  if(print_security && (sk=regfi_fetch_sk(iter->f, key)))
307  {
308    owner = regfi_get_owner(sk->sec_desc);
309    group = regfi_get_group(sk->sec_desc);
310    sacl = regfi_get_sacl(sk->sec_desc);
311    dacl = regfi_get_dacl(sk->sec_desc);
312    regfi_free_record(iter->f, sk);
313
314    if(owner == NULL)
315      owner = empty_str;
316    if(group == NULL)
317      group = empty_str;
318    if(sacl == NULL)
319      sacl = empty_str;
320    if(dacl == NULL)
321      dacl = empty_str;
322
323    classname = regfi_fetch_classname(iter->f, key);
324    printMsgs(iter->f);
325    if(classname != NULL)
326    {
327      if(classname->interpreted == NULL)
328      {
329        fprintf(stderr, "WARN: Could not convert class name"
330                " charset for key '%s'.  Quoting raw...\n", full_path);
331        quoted_classname = quote_buffer(classname->raw, classname->size,
332                                        key_special_chars);
333      }
334      else
335        quoted_classname = quote_string(classname->interpreted, 
336                                        key_special_chars);
337
338      if(quoted_classname == NULL)
339      {
340        fprintf(stderr, "ERROR: Could not quote classname"
341                " for key '%s' due to unknown error.\n", full_path);
342        quoted_classname = empty_str;
343      }
344    }
345    else
346      quoted_classname = empty_str;
347    regfi_free_record(iter->f, classname);
348
349    printMsgs(iter->f);
350    printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, 
351           owner, group, sacl, dacl, quoted_classname);
352
353    if(owner != empty_str)
354      free(owner);
355    if(group != empty_str)
356      free(group);
357    if(sacl != empty_str)
358      free(sacl);
359    if(dacl != empty_str)
360      free(dacl);
361    if(quoted_classname != empty_str)
362      free(quoted_classname);
363  }
364  else
365    printf("%s,KEY,,%s\n", full_path, mtime);
366
367  regfi_free_record(iter->f, key);
368}
369
370
371void printKeyTree(REGFI_ITERATOR* iter)
372{
373  const REGFI_NK* root = NULL;
374  const REGFI_NK* cur = NULL;
375  const REGFI_NK* sub = NULL;
376  char* path = NULL;
377  int key_type = regfi_type_str2val("KEY");
378  bool print_this = true;
379
380  root = cur = regfi_iterator_cur_key(iter);
381  regfi_iterator_first_subkey(iter);
382  sub = regfi_iterator_cur_subkey(iter);
383  printMsgs(iter->f);
384
385  if(root == NULL)
386    bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n");
387 
388  do
389  {
390    if(print_this)
391    {
392      path = iter2Path(iter);
393      if(path == NULL)
394        bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not construct iterator's path.\n");
395
396      if(!type_filter_enabled || (key_type == type_filter))
397        printKey(iter, path);
398      if(!type_filter_enabled || (key_type != type_filter))
399        printValueList(iter, path);
400     
401      free(path);
402    }
403   
404    if(sub == NULL)
405    {
406      if(cur != root)
407      {
408        regfi_free_record(iter->f, cur);
409        /* We're done with this sub-tree, going up and hitting other branches. */
410        if(!regfi_iterator_up(iter))
411        {
412          printMsgs(iter->f);
413          bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n");
414        }
415
416        cur = regfi_iterator_cur_key(iter);
417        if(cur == NULL)
418        {
419          printMsgs(iter->f);
420          bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
421        }
422       
423        regfi_iterator_next_subkey(iter);
424        sub = regfi_iterator_cur_subkey(iter);
425      }
426      print_this = false;
427    }
428    else
429    { /* We have unexplored sub-keys. 
430       * Let's move down and print this first sub-tree out.
431       */
432      regfi_free_record(iter->f, cur);
433      if(!regfi_iterator_down(iter))
434      {
435        printMsgs(iter->f);
436        bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n");
437      }
438
439      cur = regfi_iterator_cur_key(iter);
440      regfi_free_record(iter->f, sub);
441      regfi_iterator_first_subkey(iter);
442      sub = regfi_iterator_cur_subkey(iter);
443      print_this = true;
444    }
445    printMsgs(iter->f);
446  } while(!((cur == root) && (sub == NULL)));
447  regfi_free_record(iter->f, root);
448
449  if(print_verbose)
450    fprintf(stderr, "INFO: Finished printing key tree.\n");
451}
452
453
454/* XXX: What if there is BOTH a value AND a key with that name??
455 *      What if there are multiple keys/values with the same name??
456 */
457/*
458 * Returns 0 if path was not found.
459 * Returns 1 if path was found as value.
460 * Returns 2 if path was found as key.
461 * Returns less than 0 on other error.
462 */
463int retrievePath(REGFI_ITERATOR* iter, char** path)
464{
465  const REGFI_VK* value;
466  char* tmp_path_joined;
467  const char** tmp_path;
468  uint32_t i;
469 
470  if(path == NULL)
471    return -1;
472
473  /* One extra for any value at the end, and one more for NULL */
474  tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1));
475  if(tmp_path == NULL)
476    return -2;
477
478  /* Strip any potential value name at end of path */
479  for(i=0; 
480      (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1);
481      i++)
482  { tmp_path[i] = path[i]; }
483  tmp_path[i] = NULL;
484
485  if(print_verbose)
486    fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n",
487            path_filter);
488
489  /* Special check for '/' path filter */
490  if(path[0] == NULL)
491  {
492    if(print_verbose)
493      fprintf(stderr, "INFO: Found final path element as root key.\n");
494    free(tmp_path);
495    return 2;
496  }
497
498  if(!regfi_iterator_walk_path(iter, tmp_path))
499  {
500    printMsgs(iter->f);
501    free(tmp_path);
502    return 0;
503  }
504
505  if(regfi_iterator_find_value(iter, path[i]))
506  {
507    if(print_verbose)
508      fprintf(stderr, "INFO: Found final path element as value.\n");
509
510    value = regfi_iterator_cur_value(iter);
511    printMsgs(iter->f);
512    tmp_path_joined = iter2Path(iter);
513
514    if((value == NULL) || (tmp_path_joined == NULL))
515      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n");
516
517    if(!type_filter_enabled || (value->type == type_filter))
518      printValue(iter, value, tmp_path_joined);
519
520    regfi_free_record(iter->f, value);
521    free(tmp_path);
522    free(tmp_path_joined);
523    return 1;
524  }
525  else if(regfi_iterator_find_subkey(iter, path[i]))
526  {
527    printMsgs(iter->f);
528    if(print_verbose)
529      fprintf(stderr, "INFO: Found final path element as key.\n");
530
531    if(!regfi_iterator_down(iter))
532    {
533      printMsgs(iter->f);
534      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
535    }
536
537    return 2;
538  }
539  printMsgs(iter->f);
540
541  if(print_verbose)
542    fprintf(stderr, "INFO: Could not find last element of path.\n");
543
544  return 0;
545}
546
547
548static void usage(void)
549{
550  fprintf(stderr, "Usage: reglookup [-v] [-s]"
551          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
552          " <REGISTRY_FILE>\n");
553  fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
554  fprintf(stderr, "Options:\n");
555  fprintf(stderr, "\t-v\t sets verbose mode.\n");
556  fprintf(stderr, "\t-h\t enables header row. (default)\n");
557  fprintf(stderr, "\t-H\t disables header row.\n");
558  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
559  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
560  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
561  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
562  fprintf(stderr, "\t-i\t includes parent key modification times with child values.\n");
563  fprintf(stderr, "\n");
564}
565
566
567int main(int argc, char** argv)
568{
569  char** path = NULL;
570  REGFI_ITERATOR* iter;
571  int retr_path_ret, fd;
572  uint32_t argi, arge;
573
574  /* Process command line arguments */
575  if(argc < 2)
576  {
577    usage();
578    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
579  }
580 
581  arge = argc-1;
582  for(argi = 1; argi < arge; argi++)
583  {
584    if (strcmp("-p", argv[argi]) == 0)
585    {
586      if(++argi >= arge)
587      {
588        usage();
589        bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-p' option requires parameter.\n");
590      }
591      if((path_filter = strdup(argv[argi])) == NULL)
592        bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
593
594      path_filter_enabled = true;
595    }
596    else if (strcmp("-t", argv[argi]) == 0)
597    {
598      if(++argi >= arge)
599      {
600        usage();
601        bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-t' option requires parameter.\n");
602      }
603      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
604      {
605        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
606        bailOut(REGLOOKUP_EXIT_USAGE, "");
607      }
608      type_filter_enabled = true;
609    }
610    else if (strcmp("-h", argv[argi]) == 0)
611      print_header = true;
612    else if (strcmp("-H", argv[argi]) == 0)
613      print_header = false;
614    else if (strcmp("-s", argv[argi]) == 0)
615      print_security = true;
616    else if (strcmp("-S", argv[argi]) == 0)
617      print_security = false;
618    else if (strcmp("-v", argv[argi]) == 0)
619      print_verbose = true;
620    else if (strcmp("-i", argv[argi]) == 0)
621      print_value_mtime = true;
622    else
623    {
624      usage();
625      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
626      bailOut(REGLOOKUP_EXIT_USAGE, "");
627    }
628  }
629  registry_file = argv[argi];
630
631  if(print_verbose)
632    regfi_log_set_mask(REGFI_LOG_INFO|REGFI_LOG_WARN|REGFI_LOG_ERROR);
633
634  fd = openHive(registry_file);
635  if(fd < 0)
636  {
637    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
638    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
639  }
640   
641  /* XXX: add command line option to choose output encoding */
642  f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
643  if(f == NULL)
644  {
645    close(fd);
646    bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
647  }
648
649  iter = regfi_iterator_new(f);
650  if(iter == NULL)
651  {
652    printMsgs(f);
653    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
654  }
655
656  if(print_header)
657  {
658    if(print_security)
659      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n");
660    else
661      printf("PATH,TYPE,VALUE,MTIME\n");
662  }
663
664  if(path_filter_enabled && path_filter != NULL)
665    path = splitPath(path_filter);
666
667  if(path != NULL)
668  {
669    retr_path_ret = retrievePath(iter, path);
670    printMsgs(iter->f);
671    freePath(path);
672
673    if(retr_path_ret == 0)
674      fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter);
675    else if (retr_path_ret == 2)
676      printKeyTree(iter);
677    else if(retr_path_ret < 0)
678    {
679      fprintf(stderr, "ERROR: retrievePath() returned %d.\n", 
680              retr_path_ret);
681      bailOut(REGLOOKUP_EXIT_DATAERR,
682              "ERROR: Unknown error occurred in retrieving path.\n");
683    }
684  }
685  else
686    printKeyTree(iter);
687
688  regfi_iterator_free(iter);
689  regfi_free(f);
690  close(fd);
691
692  return 0;
693}
Note: See TracBrowser for help on using the repository browser.