source: trunk/src/reglookup.c @ 138

Last change on this file since 138 was 138, checked in by tim, 15 years ago

extended error message logging to allow for message type filtering

fine tuned message verbosity to more reasonable default levels for reglookup and reglookup-recover

updated related documentation

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