source: trunk/src/reglookup.c @ 168

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

merged remaining smb_deps items into regfi

began formatting API comments for use with doxygen

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