source: trunk/src/reglookup.c @ 170

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

merged Tobias Mueller's patch with some changes
updated version number

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