source: trunk/src/reglookup.c @ 136

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

fixed various integer issues and memory allocation issues

polished error message functions and added initial messages in a few places

  • 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 136 2009-01-23 17:29:51Z 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, "WARNING: 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  quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
89  if(quoted_value == NULL)
90  {
91    if(conv_error == NULL)
92      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
93              "Memory allocation failure likely.\n", prefix, quoted_name);
94    else if(print_verbose)
95      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
96              "Returned error: %s\n", prefix, quoted_name, conv_error);
97  }
98  else if(conv_error != NULL && print_verbose)
99    fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
100            "warning returned: %s\n", prefix, quoted_name, conv_error);
101
102  str_type = regfi_type_val2str(vk->type);
103  if(print_security)
104  {
105    if(str_type == NULL)
106      printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
107             vk->type, quoted_value);
108    else
109      printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
110             str_type, quoted_value);
111  }
112  else
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
122  if(quoted_value != NULL)
123    free(quoted_value);
124  if(quoted_name != NULL)
125    free(quoted_name);
126  if(conv_error != NULL)
127    free(conv_error);
128}
129
130
131
132/* XXX: Each chunk must be unquoted after it is split out.
133 *      Quoting syntax may need to be standardized and pushed into the API
134 *      to deal with this issue and others.
135 */
136char** splitPath(const char* s)
137{
138  char** ret_val;
139  const char* cur = s;
140  char* next = NULL;
141  char* copy;
142  uint32 ret_cur = 0;
143
144  ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
145  if (ret_val == NULL)
146    return NULL;
147  ret_val[0] = NULL;
148
149  /* We return a well-formed, 0-length, path even when input is icky. */
150  if (s == NULL)
151    return ret_val;
152 
153  while((next = strchr(cur, '/')) != NULL)
154  {
155    if ((next-cur) > 0)
156    {
157      copy = (char*)malloc((next-cur+1)*sizeof(char));
158      if(copy == NULL)
159        bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
160         
161      memcpy(copy, cur, next-cur);
162      copy[next-cur] = '\0';
163      ret_val[ret_cur++] = copy;
164      if(ret_cur < (REGFI_MAX_DEPTH+1+1))
165        ret_val[ret_cur] = NULL;
166      else
167        bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
168    }
169    cur = next+1;
170  }
171
172  /* Grab last element, if path doesn't end in '/'. */
173  if(strlen(cur) > 0)
174  {
175    copy = strdup(cur);
176    ret_val[ret_cur++] = copy;
177    if(ret_cur < (REGFI_MAX_DEPTH+1+1))
178      ret_val[ret_cur] = NULL;
179    else
180      bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
181  }
182
183  return ret_val;
184}
185
186
187void freePath(char** path)
188{
189  uint32 i;
190
191  if(path == NULL)
192    return;
193
194  for(i=0; path[i] != NULL; i++)
195    free(path[i]);
196
197  free(path);
198}
199
200
201/* Returns a quoted path from an iterator's stack */
202/* XXX: Some way should be found to integrate this into regfi's API
203 *      The problem is that the escaping is sorta reglookup-specific.
204 */
205char* iter2Path(REGFI_ITERATOR* i)
206{
207  const REGFI_ITER_POSITION* cur;
208  uint32 buf_left = 127;
209  uint32 buf_len = buf_left+1;
210  uint32 name_len = 0;
211  uint32 grow_amt;
212  char* buf;
213  char* new_buf;
214  char* name;
215  const char* cur_name;
216  void_stack_iterator* iter;
217 
218  buf = (char*)malloc((buf_len)*sizeof(char));
219  if (buf == NULL)
220    return NULL;
221  buf[0] = '\0';
222
223  iter = void_stack_iterator_new(i->key_positions);
224  if (iter == NULL)
225  {
226    free(buf);
227    return NULL;
228  }
229
230  /* skip root element */
231  if(void_stack_size(i->key_positions) < 1)
232  {
233    buf[0] = '/';
234    buf[1] = '\0';
235    return buf;
236  }
237  cur = void_stack_iterator_next(iter);
238
239  do
240  {
241    cur = void_stack_iterator_next(iter);
242    if (cur == NULL)
243      cur_name = i->cur_key->keyname;
244    else
245      cur_name = cur->nk->keyname;
246
247    buf[buf_len-buf_left-1] = '/';
248    buf_left -= 1;
249    name = quote_string(cur_name, key_special_chars);
250    name_len = strlen(name);
251    if(name_len+1 > buf_left)
252    {
253      grow_amt = (uint32)(buf_len/2);
254      buf_len += name_len+1+grow_amt-buf_left;
255      if((new_buf = realloc(buf, buf_len)) == NULL)
256      {
257        free(name);
258        free(buf);
259        free(iter);
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  } while(cur != NULL);
270
271  return buf;
272}
273
274
275void printValueList(REGFI_ITERATOR* i, char* prefix)
276{
277  const REGFI_VK_REC* value;
278
279  value = regfi_iterator_first_value(i);
280  while(value != NULL)
281  {
282    if(!type_filter_enabled || (value->type == type_filter))
283      printValue(value, prefix);
284    value = regfi_iterator_next_value(i);
285  }
286}
287
288
289void printKey(REGFI_ITERATOR* i, char* full_path)
290{
291  static char empty_str[1] = "";
292  char* owner = NULL;
293  char* group = NULL;
294  char* sacl = NULL;
295  char* dacl = NULL;
296  char* quoted_classname;
297  char* error_msg = NULL;
298  char mtime[20];
299  time_t tmp_time[1];
300  struct tm* tmp_time_s = NULL;
301  const REGFI_SK_REC* sk;
302  const REGFI_NK_REC* k = regfi_iterator_cur_key(i);
303
304  *tmp_time = nt_time_to_unix(&k->mtime);
305  tmp_time_s = gmtime(tmp_time);
306  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
307
308  if(print_security && (sk=regfi_iterator_cur_sk(i)))
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    if(owner == NULL)
315      owner = empty_str;
316    if(group == NULL)
317      group = empty_str;
318    if(sacl == NULL)
319      sacl = empty_str;
320    if(dacl == NULL)
321      dacl = empty_str;
322
323    if(k->classname != NULL)
324    {
325      quoted_classname = quote_unicode((uint8*)k->classname, k->classname_length,
326                                       key_special_chars, &error_msg);
327      if(quoted_classname == NULL)
328      {
329        if(error_msg == NULL)
330          fprintf(stderr, "ERROR: Could not quote classname"
331                  " for key '%s' due to unknown error.\n", full_path);
332        else
333        {
334          fprintf(stderr, "ERROR: Could not quote classname"
335                  " for key '%s' due to error: %s\n", full_path, error_msg);
336          free(error_msg);
337        }
338      }
339      else if (error_msg != NULL)
340      {
341        if(print_verbose)
342          fprintf(stderr, "WARNING: While converting classname"
343                  " for key '%s': %s.\n", full_path, error_msg);
344        free(error_msg);
345      }
346    }
347    else
348      quoted_classname = empty_str;
349
350    printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, 
351           owner, group, sacl, dacl, quoted_classname);
352
353    if(owner != empty_str)
354      free(owner);
355    if(group != empty_str)
356      free(group);
357    if(sacl != empty_str)
358      free(sacl);
359    if(dacl != empty_str)
360      free(dacl);
361    if(quoted_classname != empty_str)
362      free(quoted_classname);
363  }
364  else
365    printf("%s,KEY,,%s\n", full_path, mtime);
366}
367
368
369void printKeyTree(REGFI_ITERATOR* iter)
370{
371  const REGFI_NK_REC* root = NULL;
372  const REGFI_NK_REC* cur = NULL;
373  const REGFI_NK_REC* sub = NULL;
374  char* path = NULL;
375  char* msgs = NULL;
376  int key_type = regfi_type_str2val("KEY");
377  bool print_this = true;
378
379  root = cur = regfi_iterator_cur_key(iter);
380  sub = regfi_iterator_first_subkey(iter);
381  msgs = regfi_get_messages(iter->f);
382  if(msgs != NULL)
383    fprintf(stderr, "%s", msgs);
384  if(root == NULL)
385    bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
386 
387  do
388  {
389    if(print_this)
390    {
391      path = iter2Path(iter);
392      if(path == NULL)
393        bailOut(EX_OSERR, "ERROR: Could not construct iterator's path.\n");
394     
395      if(!type_filter_enabled || (key_type == type_filter))
396        printKey(iter, path);
397      if(!type_filter_enabled || (key_type != type_filter))
398        printValueList(iter, path);
399     
400      free(path);
401    }
402   
403    if(sub == NULL)
404    {
405      if(cur != root)
406      {
407        /* We're done with this sub-tree, going up and hitting other branches. */
408        if(!regfi_iterator_up(iter))
409          bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
410       
411        cur = regfi_iterator_cur_key(iter);
412        if(cur == NULL)
413          bailOut(EX_DATAERR, "ERROR: unexpected NULL for key.\n");
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        bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
425
426      cur = sub;
427      sub = regfi_iterator_first_subkey(iter);
428      print_this = true;
429    }
430  } while(!((cur == root) && (sub == NULL)));
431
432  if(print_verbose)
433    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
434}
435
436
437/* XXX: what if there is BOTH a value AND a key with that name?? */
438/*
439 * Returns 0 if path was not found.
440 * Returns 1 if path was found as value.
441 * Returns 2 if path was found as key.
442 * Returns less than 0 on other error.
443 */
444int retrievePath(REGFI_ITERATOR* iter, char** path)
445{
446  const REGFI_VK_REC* value;
447  char* tmp_path_joined;
448  const char** tmp_path;
449  uint32 i;
450 
451  if(path == NULL)
452    return -1;
453
454  /* One extra for any value at the end, and one more for NULL */
455  tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1));
456  if(tmp_path == NULL)
457    return -2;
458
459  /* Strip any potential value name at end of path */
460  for(i=0; 
461      (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1);
462      i++)
463  { tmp_path[i] = path[i]; }
464  tmp_path[i] = NULL;
465
466  if(print_verbose)
467    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
468            path_filter);
469
470  /* Special check for '/' path filter */
471  if(path[0] == NULL)
472  {
473    if(print_verbose)
474      fprintf(stderr, "VERBOSE: Found final path element as root key.\n");
475    free(tmp_path);
476    return 2;
477  }
478
479  if(!regfi_iterator_walk_path(iter, tmp_path))
480  {
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, "VERBOSE: Found final path element as value.\n");
489
490    value = regfi_iterator_cur_value(iter);
491    tmp_path_joined = iter2Path(iter);
492
493    if((value == NULL) || (tmp_path_joined == NULL))
494      bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
495
496    if(!type_filter_enabled || (value->type == type_filter))
497      printValue(value, tmp_path_joined);
498
499    free(tmp_path);
500    free(tmp_path_joined);
501    return 1;
502  }
503  else if(regfi_iterator_find_subkey(iter, path[i]))
504  {
505    if(print_verbose)
506      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
507
508    if(!regfi_iterator_down(iter))
509      bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
510
511    return 2;
512  }
513
514  if(print_verbose)
515    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
516
517  return 0;
518}
519
520
521static void usage(void)
522{
523  fprintf(stderr, "Usage: reglookup [-v] [-s]"
524          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
525          " <REGISTRY_FILE>\n");
526  fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
527  fprintf(stderr, "Options:\n");
528  fprintf(stderr, "\t-v\t sets verbose mode.\n");
529  fprintf(stderr, "\t-h\t enables header row. (default)\n");
530  fprintf(stderr, "\t-H\t disables header row.\n");
531  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
532  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
533  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
534  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
535  fprintf(stderr, "\n");
536}
537
538
539int main(int argc, char** argv)
540{
541  char** path = NULL;
542  REGFI_ITERATOR* iter;
543  int retr_path_ret;
544  uint32 argi, arge;
545
546  /* Process command line arguments */
547  if(argc < 2)
548  {
549    usage();
550    bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
551  }
552 
553  arge = argc-1;
554  for(argi = 1; argi < arge; argi++)
555  {
556    if (strcmp("-p", argv[argi]) == 0)
557    {
558      if(++argi >= arge)
559      {
560        usage();
561        bailOut(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
562      }
563      if((path_filter = strdup(argv[argi])) == NULL)
564        bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
565
566      path_filter_enabled = true;
567    }
568    else if (strcmp("-t", argv[argi]) == 0)
569    {
570      if(++argi >= arge)
571      {
572        usage();
573        bailOut(EX_USAGE, "ERROR: '-t' option requires parameter.\n");
574      }
575      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
576      {
577        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
578        bailOut(EX_USAGE, "");
579      }
580      type_filter_enabled = true;
581    }
582    else if (strcmp("-h", argv[argi]) == 0)
583      print_header = true;
584    else if (strcmp("-H", argv[argi]) == 0)
585      print_header = false;
586    else if (strcmp("-s", argv[argi]) == 0)
587      print_security = true;
588    else if (strcmp("-S", argv[argi]) == 0)
589      print_security = false;
590    else if (strcmp("-v", argv[argi]) == 0)
591      print_verbose = true;
592    else
593    {
594      usage();
595      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
596      bailOut(EX_USAGE, "");
597    }
598  }
599  if((registry_file = strdup(argv[argi])) == NULL)
600    bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
601
602  f = regfi_open(registry_file);
603  if(f == NULL)
604  {
605    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
606    bailOut(EX_NOINPUT, "");
607  }
608
609  iter = regfi_iterator_new(f);
610  if(iter == NULL)
611    bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
612
613  if(print_header)
614  {
615    if(print_security)
616      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n");
617    else
618      printf("PATH,TYPE,VALUE,MTIME\n");
619  }
620
621  if(path_filter_enabled && path_filter != NULL)
622    path = splitPath(path_filter);
623
624  if(path != NULL)
625  {
626    retr_path_ret = retrievePath(iter, path);
627    freePath(path);
628
629    if(retr_path_ret == 0)
630      fprintf(stderr, "WARNING: specified path not found.\n");
631    else if (retr_path_ret == 2)
632      printKeyTree(iter);
633    else if(retr_path_ret < 0)
634    {
635      fprintf(stderr, "ERROR: retrievePath() returned %d.\n", 
636              retr_path_ret);
637      bailOut(EX_DATAERR,"ERROR: Unknown error occurred in retrieving path.\n");
638    }
639  }
640  else
641    printKeyTree(iter);
642
643  regfi_iterator_free(iter);
644  regfi_close(f);
645
646  return 0;
647}
Note: See TracBrowser for help on using the repository browser.