source: trunk/src/reglookup.c @ 102

Last change on this file since 102 was 102, checked in by tim, 16 years ago

simplified root key search routines

rewrote sk record parsing

fixed nasty bug in parsing data-in-offset

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