source: trunk/src/reglookup.c @ 252

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

updated pyregfi to work with regfi changes
renamed some functions for more clarity
fixed some issues related to talloc_reference

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