source: trunk/src/reglookup.c @ 137

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

Added error messages to most parse functions in regfi

Relaxed validation requirements on NK types and missing value data

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