source: trunk/src/reglookup.c @ 159

Last change on this file since 159 was 159, checked in by tim, 14 years ago

began rearranging data parsing. Moved charater set conversion and basic parsing logic into regfi

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