source: trunk/src/reglookup.c @ 84

Last change on this file since 84 was 84, checked in by tim, 17 years ago

rearranged structure contents to reduce fragmentation on 64 bit systems.

make regfi interface return const structures to help enforce separation

removed a little cruft from some parsing functions

  • Property svn:keywords set to Id
File size: 24.2 KB
Line 
1/*
2 * A utility to read a Windows NT/2K/XP/2K3 registry file, using
3 * Gerald Carter''s regfio interface.
4 *
5 * Copyright (C) 2005-2007 Timothy D. Morgan
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 2 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 84 2007-01-19 14:52: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 <iconv.h>
31#include "../include/regfi.h"
32#include "../include/void_stack.h"
33
34/* Globals, influenced by command line parameters */
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 */
45const char* key_special_chars = ",\"\\/";
46const char* subfield_special_chars = ",\"\\|";
47const char* common_special_chars = ",\"\\";
48
49iconv_t conv_desc;
50
51
52void bailOut(int code, char* message)
53{
54  fprintf(stderr, message);
55  exit(code);
56}
57
58
59/* Returns a newly malloc()ed string which contains original buffer,
60 * except for non-printable or special characters are quoted in hex
61 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
62 * character.  A null terminator is added, since only ascii, not binary,
63 * is returned.
64 */
65static char* quote_buffer(const unsigned char* str, 
66                          unsigned int len, const char* special)
67{
68  unsigned int i, added_len;
69  unsigned int num_written = 0;
70
71  unsigned int buf_len = sizeof(char)*(len+1);
72  char* ret_val = malloc(buf_len);
73  char* tmp_buf;
74
75  if(ret_val == NULL)
76    return NULL;
77
78  for(i=0; i<len; i++)
79  {
80    if(buf_len <= (num_written+5))
81    {
82      /* Expand the buffer by the memory consumption rate seen so far
83       * times the amount of input left to process.  The expansion is bounded
84       * below by a minimum safety increase, and above by the maximum possible
85       * output string length.  This should minimize both the number of
86       * reallocs() and the amount of wasted memory.
87       */
88      added_len = (len-i)*num_written/(i+1);
89      if((buf_len+added_len) > (len*4+1))
90        buf_len = len*4+1;
91      else
92      {
93        if (added_len < 5)
94          buf_len += 5;
95        else
96          buf_len += added_len;
97      }
98
99      tmp_buf = realloc(ret_val, buf_len);
100      if(tmp_buf == NULL)
101      {
102        free(ret_val);
103        return NULL;
104      }
105      ret_val = tmp_buf;
106    }
107   
108    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
109    {
110      num_written += snprintf(ret_val + num_written, buf_len - num_written,
111                              "\\x%.2X", str[i]);
112    }
113    else
114      ret_val[num_written++] = str[i];
115  }
116  ret_val[num_written] = '\0';
117
118  return ret_val;
119}
120
121
122/* Returns a newly malloc()ed string which contains original string,
123 * except for non-printable or special characters are quoted in hex
124 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
125 * character.
126 */
127static char* quote_string(const char* str, const char* special)
128{
129  unsigned int len;
130
131  if(str == NULL)
132    return NULL;
133
134  len = strlen(str);
135  return quote_buffer((const unsigned char*)str, len, special);
136}
137
138
139/*
140 * Convert from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
141 * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
142 * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
143 * terminates the string.  Returns the length of the string stored in
144 * ascii.  On error, returns a negative errno code.
145 */
146static int uni_to_ascii(unsigned char* uni, char* ascii, 
147                        unsigned int uni_max, unsigned int ascii_max)
148{
149  char* inbuf = (char*)uni;
150  char* outbuf = ascii;
151  size_t in_len = (size_t)uni_max;
152  size_t out_len = (size_t)(ascii_max-1);
153  int ret;
154
155  /* Set up conversion descriptor. */
156  conv_desc = iconv_open("US-ASCII", "UTF-16LE");
157
158  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
159  if(ret == -1)
160  {
161    iconv_close(conv_desc);
162    return -errno;
163  }
164  *outbuf = '\0';
165
166  iconv_close(conv_desc); 
167  return strlen(ascii);
168}
169
170
171/*
172 * Convert a data value to a string for display.  Returns NULL on error,
173 * and the string to display if there is no error, or a non-fatal
174 * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
175 * be set to a newly allocated string, containing an error message.  If
176 * a memory allocation failure occurs while generating the error
177 * message, both the return value and (*error_msg) will be NULL.  It
178 * is the responsibility of the caller to free both a non-NULL return
179 * value, and a non-NULL (*error_msg).
180 */
181static char* data_to_ascii(unsigned char *datap, uint32 len, uint32 type, 
182                           char** error_msg)
183{
184  char* asciip;
185  char* ascii;
186  unsigned char* cur_str;
187  char* cur_ascii;
188  char* cur_quoted;
189  char* tmp_err;
190  const char* str_type;
191  uint32 i;
192  uint32 cur_str_len;
193  uint32 ascii_max, cur_str_max;
194  uint32 str_rem, cur_str_rem, alen;
195  int ret_err;
196  unsigned short num_nulls;
197
198  *error_msg = NULL;
199
200  switch (type) 
201  {
202  case REG_SZ:
203  case REG_EXPAND_SZ:
204    /* REG_LINK is a symbolic link, stored as a unicode string. */
205  case REG_LINK:
206    ascii_max = sizeof(char)*(len+1);
207    ascii = malloc(ascii_max);
208    if(ascii == NULL)
209      return NULL;
210   
211    /* Sometimes values have binary stored in them.  If the unicode
212     * conversion fails, just quote it raw.
213     */
214    ret_err = uni_to_ascii(datap, ascii, len, ascii_max);
215    if(ret_err < 0)
216    {
217      tmp_err = strerror(-ret_err);
218      str_type = regfi_type_val2str(type);
219      *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
220      if(*error_msg == NULL)
221      {
222        free(ascii);
223        return NULL;
224      }
225      sprintf(*error_msg, "Unicode conversion failed on %s field; "
226               "printing as binary.  Error: %s", str_type, tmp_err);
227     
228      cur_quoted = quote_buffer(datap, len, common_special_chars);
229    }
230    else
231      cur_quoted = quote_string(ascii, common_special_chars);
232    free(ascii);
233    if(cur_quoted == NULL)
234    {
235      *error_msg = (char*)malloc(27+1);
236      if(*error_msg != NULL)
237        strcpy(*error_msg, "Buffer could not be quoted.");
238    }
239    return cur_quoted;
240    break;
241
242  case REG_DWORD:
243    ascii_max = sizeof(char)*(8+2+1);
244    ascii = malloc(ascii_max);
245    if(ascii == NULL)
246      return NULL;
247
248    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
249             datap[0], datap[1], datap[2], datap[3]);
250    return ascii;
251    break;
252
253  case REG_DWORD_BE:
254    ascii_max = sizeof(char)*(8+2+1);
255    ascii = malloc(ascii_max);
256    if(ascii == NULL)
257      return NULL;
258
259    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
260             datap[3], datap[2], datap[1], datap[0]);
261    return ascii;
262    break;
263
264  case REG_QWORD:
265    ascii_max = sizeof(char)*(16+2+1);
266    ascii = malloc(ascii_max);
267    if(ascii == NULL)
268      return NULL;
269
270    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
271             datap[7], datap[6], datap[5], datap[4],
272             datap[3], datap[2], datap[1], datap[0]);
273    return ascii;
274    break;
275   
276
277  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
278   *      redone with fewer malloc calls and better string concatenation.
279   */
280  case REG_MULTI_SZ:
281    ascii_max = sizeof(char)*(len*4+1);
282    cur_str_max = sizeof(char)*(len+1);
283    cur_str = malloc(cur_str_max);
284    cur_ascii = malloc(cur_str_max);
285    ascii = malloc(ascii_max);
286    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
287      return NULL;
288
289    /* Reads until it reaches 4 consecutive NULLs,
290     * which is two nulls in unicode, or until it reaches len, or until we
291     * run out of buffer.  The latter should never happen, but we shouldn't
292     * trust our file to have the right lengths/delimiters.
293     */
294    asciip = ascii;
295    num_nulls = 0;
296    str_rem = ascii_max;
297    cur_str_rem = cur_str_max;
298    cur_str_len = 0;
299
300    for(i=0; (i < len) && str_rem > 0; i++)
301    {
302      *(cur_str+cur_str_len) = *(datap+i);
303      if(*(cur_str+cur_str_len) == 0)
304        num_nulls++;
305      else
306        num_nulls = 0;
307      cur_str_len++;
308
309      if(num_nulls == 2)
310      {
311        ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
312        if(ret_err < 0)
313        {
314          /* XXX: should every sub-field error be enumerated? */
315          if(*error_msg == NULL)
316          {
317            tmp_err = strerror(-ret_err);
318            *error_msg = (char*)malloc(90+strlen(tmp_err)+1);
319            if(*error_msg == NULL)
320            {
321              free(cur_str);
322              free(cur_ascii);
323              free(ascii);
324              return NULL;
325            }
326            sprintf(*error_msg, "Unicode conversion failed on at least one "
327                    "MULTI_SZ sub-field; printing as binary.  Error: %s",
328                    tmp_err);
329          }
330          cur_quoted = quote_buffer(cur_str, cur_str_len-1, 
331                                    subfield_special_chars);
332        }
333        else
334          cur_quoted = quote_string(cur_ascii, subfield_special_chars);
335
336        alen = snprintf(asciip, str_rem, "%s", cur_quoted);
337        asciip += alen;
338        str_rem -= alen;
339        free(cur_quoted);
340
341        if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
342          break;
343        else
344        {
345          if(str_rem > 0)
346          {
347            asciip[0] = '|';
348            asciip[1] = '\0';
349            asciip++;
350            str_rem--;
351          }
352          memset(cur_str, 0, cur_str_max);
353          cur_str_len = 0;
354          num_nulls = 0;
355          /* To eliminate leading nulls in subsequent strings. */
356          i++;
357        }
358      }
359    }
360    *asciip = 0;
361    free(cur_str);
362    free(cur_ascii);
363    return ascii;
364    break;
365
366  /* XXX: Dont know what to do with these yet, just print as binary... */
367  default:
368    fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
369   
370  case REG_NONE:
371  case REG_RESOURCE_LIST:
372  case REG_FULL_RESOURCE_DESCRIPTOR:
373  case REG_RESOURCE_REQUIREMENTS_LIST:
374
375  case REG_BINARY:
376    return quote_buffer(datap, len, common_special_chars);
377    break;
378  }
379
380  return NULL;
381}
382
383
384/* XXX: Each chunk must be unquoted after it is split out.
385 *      Quoting syntax may need to be standardized and pushed into the API
386 *      to deal with this issue and others.
387 */
388char** splitPath(const char* s)
389{
390  char** ret_val;
391  const char* cur = s;
392  char* next = NULL;
393  char* copy;
394  uint32 ret_cur = 0;
395
396  ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
397  if (ret_val == NULL)
398    return NULL;
399  ret_val[0] = NULL;
400
401  /* We return a well-formed, 0-length, path even when input is icky. */
402  if (s == NULL)
403    return ret_val;
404 
405  while((next = strchr(cur, '/')) != NULL)
406  {
407    if ((next-cur) > 0)
408    {
409      copy = (char*)malloc((next-cur+1)*sizeof(char));
410      if(copy == NULL)
411        bailOut(2, "ERROR: Memory allocation problem.\n");
412         
413      memcpy(copy, cur, next-cur);
414      copy[next-cur] = '\0';
415      ret_val[ret_cur++] = copy;
416      if(ret_cur < (REGF_MAX_DEPTH+1+1))
417        ret_val[ret_cur] = NULL;
418      else
419        bailOut(2, "ERROR: Registry maximum depth exceeded.\n");
420    }
421    cur = next+1;
422  }
423
424  /* Grab last element, if path doesn't end in '/'. */
425  if(strlen(cur) > 0)
426  {
427    copy = strdup(cur);
428    ret_val[ret_cur++] = copy;
429    if(ret_cur < (REGF_MAX_DEPTH+1+1))
430      ret_val[ret_cur] = NULL;
431    else
432      bailOut(2, "ERROR: Registry maximum depth exceeded.\n");
433  }
434
435  return ret_val;
436}
437
438
439void freePath(char** path)
440{
441  uint32 i;
442
443  if(path == NULL)
444    return;
445
446  for(i=0; path[i] != NULL; i++)
447    free(path[i]);
448
449  free(path);
450}
451
452
453/* Returns a quoted path from an iterator's stack */
454/* XXX: Some way should be found to integrate this into regfi's API
455 *      The problem is that the escaping is sorta reglookup-specific.
456 */
457char* iter2Path(REGFI_ITERATOR* i)
458{
459  const REGFI_ITER_POSITION* cur;
460  uint32 buf_left = 127;
461  uint32 buf_len = buf_left+1;
462  uint32 name_len = 0;
463  uint32 grow_amt;
464  char* buf;
465  char* new_buf;
466  char* name;
467  const char* cur_name;
468  void_stack_iterator* iter;
469 
470  buf = (char*)malloc((buf_len)*sizeof(char));
471  if (buf == NULL)
472    return NULL;
473  buf[0] = '\0';
474
475  iter = void_stack_iterator_new(i->key_positions);
476  if (iter == NULL)
477  {
478    free(buf);
479    return NULL;
480  }
481
482  /* skip root element */
483  if(void_stack_size(i->key_positions) < 1)
484  {
485    buf[0] = '/';
486    buf[1] = '\0';
487    return buf;
488  }
489  cur = void_stack_iterator_next(iter);
490
491  do
492  {
493    cur = void_stack_iterator_next(iter);
494    if (cur == NULL)
495      cur_name = i->cur_key->keyname;
496    else
497      cur_name = cur->nk->keyname;
498
499    buf[buf_len-buf_left-1] = '/';
500    buf_left -= 1;
501    name = quote_string(cur_name, key_special_chars);
502    name_len = strlen(name);
503    if(name_len+1 > buf_left)
504    {
505      grow_amt = (uint32)(buf_len/2);
506      buf_len += name_len+1+grow_amt-buf_left;
507      if((new_buf = realloc(buf, buf_len)) == NULL)
508      {
509        free(buf);
510        free(iter);
511        return NULL;
512      }
513      buf = new_buf;
514      buf_left = grow_amt + name_len + 1;
515    }
516    strncpy(buf+(buf_len-buf_left-1), name, name_len);
517    buf_left -= name_len;
518    buf[buf_len-buf_left-1] = '\0';
519    free(name);
520  } while(cur != NULL);
521
522  return buf;
523}
524
525
526void printValue(const REGF_VK_REC* vk, char* prefix)
527{
528  char* quoted_value = NULL;
529  char* quoted_name = NULL;
530  char* conv_error = NULL;
531  const char* str_type = NULL;
532  uint32 size;
533  uint8 tmp_buf[4];
534
535  /* Thanks Microsoft for making this process so straight-forward!!! */
536  /* XXX: this logic should be abstracted  and pushed into the regfi
537   *      interface.  This includes the size limits.
538   */
539  size = (vk->data_size & ~VK_DATA_IN_OFFSET);
540  if(vk->data_size & VK_DATA_IN_OFFSET)
541  {
542    tmp_buf[0] = (uint8)((vk->data_off >> 3) & 0xFF);
543    tmp_buf[1] = (uint8)((vk->data_off >> 2) & 0xFF);
544    tmp_buf[2] = (uint8)((vk->data_off >> 1) & 0xFF);
545    tmp_buf[3] = (uint8)(vk->data_off & 0xFF);
546    if(size > 4)
547      /* XXX: should we kick out a warning here?  If it is in the
548       *      offset and longer than four, file could be corrupt
549       *      or malicious... */
550      size = 4;
551    quoted_value = data_to_ascii(tmp_buf, 4, vk->type, &conv_error);
552  }
553  else
554  {
555    /* Microsoft's documentation indicates that "available memory" is
556     * the limit on value sizes.  Annoying.  We limit it to 1M which
557     * should rarely be exceeded, unless the file is corrupt or
558     * malicious. For more info, see:
559     *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
560     */
561    if(size > VK_MAX_DATA_LENGTH)
562    {
563      fprintf(stderr, "WARNING: value data size %d larger than "
564              "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
565      size = VK_MAX_DATA_LENGTH;
566    }
567
568    quoted_value = data_to_ascii(vk->data, vk->data_size, 
569                                 vk->type, &conv_error);
570  }
571 
572  /* XXX: Sometimes value names can be NULL in registry.  Need to
573   *      figure out why and when, and generate the appropriate output
574   *      for that condition.
575   */
576  quoted_name = quote_string(vk->valuename, common_special_chars);
577
578  if(quoted_value == NULL)
579  {
580    if(conv_error == NULL)
581      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
582              "Memory allocation failure likely.\n", prefix, quoted_name);
583    else
584      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
585              "Returned error: %s\n", prefix, quoted_name, conv_error);
586  }
587  /* XXX: should these always be printed? */
588  else if(conv_error != NULL && print_verbose)
589      fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
590              "warning returned: %s\n", prefix, quoted_name, conv_error);
591
592  str_type = regfi_type_val2str(vk->type);
593  if(print_security)
594  {
595    if(str_type == NULL)
596      printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
597             vk->type, quoted_value);
598    else
599      printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
600             str_type, quoted_value);
601  }
602  else
603  {
604    if(str_type == NULL)
605      printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
606             vk->type, quoted_value);
607    else
608      printf("%s/%s,%s,%s,\n", prefix, quoted_name,
609             str_type, quoted_value);
610  }
611
612  if(quoted_value != NULL)
613    free(quoted_value);
614  if(quoted_name != NULL)
615    free(quoted_name);
616  if(conv_error != NULL)
617    free(conv_error);
618}
619
620
621void printValueList(REGFI_ITERATOR* i, char* prefix)
622{
623  const REGF_VK_REC* value;
624
625  value = regfi_iterator_first_value(i);
626  while(value != NULL)
627  {
628    if(!type_filter_enabled || (value->type == type_filter))
629      printValue(value, prefix);
630    value = regfi_iterator_next_value(i);
631  }
632}
633
634
635void printKey(const REGF_NK_REC* k, char* full_path)
636{
637  static char empty_str[1] = "";
638  char* owner = NULL;
639  char* group = NULL;
640  char* sacl = NULL;
641  char* dacl = NULL;
642  char mtime[20];
643  time_t tmp_time[1];
644  struct tm* tmp_time_s = NULL;
645
646  *tmp_time = nt_time_to_unix(&k->mtime);
647  tmp_time_s = gmtime(tmp_time);
648  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
649
650  if(print_security)
651  {
652    owner = regfi_get_owner(k->sec_desc->sec_desc);
653    group = regfi_get_group(k->sec_desc->sec_desc);
654    sacl = regfi_get_sacl(k->sec_desc->sec_desc);
655    dacl = regfi_get_dacl(k->sec_desc->sec_desc);
656    if(owner == NULL)
657      owner = empty_str;
658    if(group == NULL)
659      group = empty_str;
660    if(sacl == NULL)
661      sacl = empty_str;
662    if(dacl == NULL)
663      dacl = empty_str;
664
665    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
666           owner, group, sacl, dacl);
667
668    if(owner != empty_str)
669      free(owner);
670    if(group != empty_str)
671      free(group);
672    if(sacl != empty_str)
673      free(sacl);
674    if(dacl != empty_str)
675      free(dacl);
676  }
677  else
678    printf("%s,KEY,,%s\n", full_path, mtime);
679}
680
681
682void printKeyTree(REGFI_ITERATOR* iter)
683{
684  const REGF_NK_REC* root = NULL;
685  const REGF_NK_REC* cur = NULL;
686  const REGF_NK_REC* sub = NULL;
687  char* path = NULL;
688  int key_type = regfi_type_str2val("KEY");
689  bool print_this = true;
690
691  root = cur = regfi_iterator_cur_key(iter);
692  sub = regfi_iterator_first_subkey(iter);
693 
694  if(root == NULL)
695    bailOut(3, "ERROR: root cannot be NULL.\n");
696 
697  do
698  {
699    if(print_this)
700    {
701      path = iter2Path(iter);
702      if(path == NULL)
703        bailOut(2, "ERROR: Could not construct iterator's path.\n");
704     
705      if(!type_filter_enabled || (key_type == type_filter))
706        printKey(cur, path);
707      if(!type_filter_enabled || (key_type != type_filter))
708        printValueList(iter, path);
709     
710      free(path);
711    }
712   
713    if(sub == NULL)
714    {
715      if(cur != root)
716      {
717        /* We're done with this sub-tree, going up and hitting other branches. */
718        if(!regfi_iterator_up(iter))
719          bailOut(3, "ERROR: could not traverse iterator upward.\n");
720       
721        cur = regfi_iterator_cur_key(iter);
722        if(cur == NULL)
723          bailOut(3, "ERROR: unexpected NULL for key.\n");
724       
725        sub = regfi_iterator_next_subkey(iter);
726      }
727      print_this = false;
728    }
729    else
730    { /* We have unexplored sub-keys. 
731       * Let's move down and print this first sub-tree out.
732       */
733      if(!regfi_iterator_down(iter))
734        bailOut(3, "ERROR: could not traverse iterator downward.\n");
735
736      cur = sub;
737      sub = regfi_iterator_first_subkey(iter);
738      print_this = true;
739    }
740  } while(!((cur == root) && (sub == NULL)));
741
742  if(print_verbose)
743    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
744}
745
746
747/*
748 * Returns 0 if path was not found.
749 * Returns 1 if path was found as value.
750 * Returns 2 if path was found as key.
751 * Returns less than 0 on other error.
752 */
753int retrievePath(REGFI_ITERATOR* iter, char** path)
754{
755  const REGF_VK_REC* value;
756  char* tmp_path_joined;
757  const char** tmp_path;
758  uint32 i;
759 
760  if(path == NULL)
761    return -1;
762
763  /* One extra for any value at the end, and one more for NULL */
764  tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
765  if(tmp_path == NULL)
766    return -2;
767
768  /* Strip any potential value name at end of path */
769  for(i=0; 
770      (path[i] != NULL) && (path[i+1] != NULL) 
771        && (i < REGF_MAX_DEPTH+1+1);
772      i++)
773    tmp_path[i] = path[i];
774
775  tmp_path[i] = NULL;
776
777  if(print_verbose)
778    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
779            path_filter);
780
781  /* Special check for '/' path filter */
782  if(path[0] == NULL)
783  {
784    if(print_verbose)
785      fprintf(stderr, "VERBOSE: Found final path element as root key.\n");
786    return 2;
787  }
788
789  if(!regfi_iterator_walk_path(iter, tmp_path))
790  {
791    free(tmp_path);
792    return 0;
793  }
794
795  if(regfi_iterator_find_value(iter, path[i]))
796  {
797    if(print_verbose)
798      fprintf(stderr, "VERBOSE: Found final path element as value.\n");
799
800    value = regfi_iterator_cur_value(iter);
801    tmp_path_joined = iter2Path(iter);
802
803    if((value == NULL) || (tmp_path_joined == NULL))
804      bailOut(2, "ERROR: Unexpected error before printValue.\n");
805
806    printValue(value, tmp_path_joined);
807
808    free(tmp_path);
809    free(tmp_path_joined);
810    return 1;
811  }
812  else if(regfi_iterator_find_subkey(iter, path[i]))
813  {
814    if(print_verbose)
815      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
816
817    if(!regfi_iterator_down(iter))
818      bailOut(2, "ERROR: Unexpected error on traversing path filter key.\n");
819
820    return 2;
821  }
822
823  if(print_verbose)
824    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
825
826  return 0;
827}
828
829
830static void usage(void)
831{
832  fprintf(stderr, "Usage: reglookup [-v] [-s]"
833          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
834          " <REGISTRY_FILE>\n");
835  fprintf(stderr, "Version: 0.3.0\n");
836  fprintf(stderr, "Options:\n");
837  fprintf(stderr, "\t-v\t sets verbose mode.\n");
838  fprintf(stderr, "\t-h\t enables header row. (default)\n");
839  fprintf(stderr, "\t-H\t disables header row.\n");
840  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
841  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
842  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
843  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
844  fprintf(stderr, "\n");
845}
846
847
848int main(int argc, char** argv)
849{
850  char** path = NULL;
851  REGF_FILE* f;
852  REGFI_ITERATOR* iter;
853  int retr_path_ret;
854  uint32 argi, arge;
855
856  /* Process command line arguments */
857  if(argc < 2)
858  {
859    usage();
860    bailOut(1, "ERROR: Requires at least one argument.\n");
861  }
862 
863  arge = argc-1;
864  for(argi = 1; argi < arge; argi++)
865  {
866    if (strcmp("-p", argv[argi]) == 0)
867    {
868      if(++argi >= arge)
869      {
870        usage();
871        bailOut(1, "ERROR: '-p' option requires parameter.\n");
872      }
873      if((path_filter = strdup(argv[argi])) == NULL)
874        bailOut(2, "ERROR: Memory allocation problem.\n");
875
876      path_filter_enabled = true;
877    }
878    else if (strcmp("-t", argv[argi]) == 0)
879    {
880      if(++argi >= arge)
881      {
882        usage();
883        bailOut(1, "ERROR: '-t' option requires parameter.\n");
884      }
885      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
886      {
887        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
888        bailOut(1, "");
889      }
890      type_filter_enabled = true;
891    }
892    else if (strcmp("-h", argv[argi]) == 0)
893      print_header = true;
894    else if (strcmp("-H", argv[argi]) == 0)
895      print_header = false;
896    else if (strcmp("-s", argv[argi]) == 0)
897      print_security = true;
898    else if (strcmp("-S", argv[argi]) == 0)
899      print_security = false;
900    else if (strcmp("-v", argv[argi]) == 0)
901      print_verbose = true;
902    else
903    {
904      usage();
905      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
906      bailOut(1, "");
907    }
908  }
909  if((registry_file = strdup(argv[argi])) == NULL)
910    bailOut(2, "ERROR: Memory allocation problem.\n");
911
912  f = regfi_open(registry_file);
913  if(f == NULL)
914  {
915    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
916    bailOut(3, "");
917  }
918
919  iter = regfi_iterator_new(f);
920  if(iter == NULL)
921    bailOut(3, "ERROR: Couldn't create registry iterator.\n");
922
923  if(print_header)
924  {
925    if(print_security)
926      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
927    else
928      printf("PATH,TYPE,VALUE,MTIME\n");
929  }
930
931  if(path_filter_enabled && path_filter != NULL)
932    path = splitPath(path_filter);
933
934  if(path != NULL)
935  {
936    retr_path_ret = retrievePath(iter, path);
937    freePath(path);
938
939    if(retr_path_ret == 0)
940      fprintf(stderr, "WARNING: specified path not found.\n");
941    else if (retr_path_ret == 2)
942      printKeyTree(iter);
943    else if(retr_path_ret != 0)
944      bailOut(4, "ERROR: Unknown error occurred in retrieving path.\n");
945  }
946  else
947    printKeyTree(iter);
948
949  regfi_iterator_free(iter);
950  regfi_close(f);
951
952  return 0;
953}
Note: See TracBrowser for help on using the repository browser.