source: trunk/src/reglookup.c @ 109

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

reworked SK record caching to prevent unnecessary loads.

SK records now cached in iterators on an as-needed basis

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