source: trunk/src/reglookup.c @ 110

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

moved unallocated cell parsing back out of the main line hbin parsing

  • Property svn:keywords set to Id
File size: 24.0 KB
RevLine 
[30]1/*
[42]2 * A utility to read a Windows NT/2K/XP/2K3 registry file, using
3 * Gerald Carter''s regfio interface.
[30]4 *
[81]5 * Copyright (C) 2005-2007 Timothy D. Morgan
[42]6 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
[30]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 110 2008-04-29 22:59:55Z tim $
22 */
23
24
25#include <stdlib.h>
[88]26#include <sysexits.h>
[30]27#include <stdio.h>
28#include <string.h>
[33]29#include <strings.h>
[42]30#include <time.h>
[61]31#include <iconv.h>
[79]32#include "../include/regfi.h"
[31]33#include "../include/void_stack.h"
[30]34
[40]35/* Globals, influenced by command line parameters */
36bool print_verbose = false;
37bool print_security = false;
[42]38bool print_header = true;
[40]39bool path_filter_enabled = false;
40bool type_filter_enabled = false;
41char* path_filter = NULL;
42int type_filter;
43char* registry_file = NULL;
44
[42]45/* Other globals */
[109]46REGF_FILE* f;
[66]47const char* key_special_chars = ",\"\\/";
48const char* subfield_special_chars = ",\"\\|";
49const char* common_special_chars = ",\"\\";
50
[61]51iconv_t conv_desc;
[40]52
[61]53
[38]54void bailOut(int code, char* message)
55{
56  fprintf(stderr, message);
57  exit(code);
58}
59
60
[41]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
[61]64 * character.  A null terminator is added, since only ascii, not binary,
[41]65 * is returned.
66 */
67static char* quote_buffer(const unsigned char* str, 
[44]68                          unsigned int len, const char* special)
[41]69{
[61]70  unsigned int i, added_len;
71  unsigned int num_written = 0;
[41]72
[61]73  unsigned int buf_len = sizeof(char)*(len+1);
74  char* ret_val = malloc(buf_len);
75  char* tmp_buf;
76
[41]77  if(ret_val == NULL)
78    return NULL;
79
80  for(i=0; i<len; i++)
81  {
[61]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
[69]87       * output string length.  This should minimize both the number of
88       * reallocs() and the amount of wasted memory.
[61]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   
[41]110    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
111    {
[61]112      num_written += snprintf(ret_val + num_written, buf_len - num_written,
[41]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 */
[44]129static char* quote_string(const char* str, const char* special)
[41]130{
[42]131  unsigned int len;
[41]132
[42]133  if(str == NULL)
134    return NULL;
135
136  len = strlen(str);
137  return quote_buffer((const unsigned char*)str, len, special);
[41]138}
139
140
141/*
[69]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.
[41]147 */
[61]148static int uni_to_ascii(unsigned char* uni, char* ascii, 
149                        unsigned int uni_max, unsigned int ascii_max)
[41]150{
[61]151  char* inbuf = (char*)uni;
152  char* outbuf = ascii;
[70]153  size_t in_len = (size_t)uni_max;
154  size_t out_len = (size_t)(ascii_max-1);
[61]155  int ret;
[41]156
[61]157  /* Set up conversion descriptor. */
158  conv_desc = iconv_open("US-ASCII", "UTF-16LE");
159
[70]160  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
[61]161  if(ret == -1)
[41]162  {
[61]163    iconv_close(conv_desc);
[66]164    return -errno;
[41]165  }
[66]166  *outbuf = '\0';
[41]167
[61]168  iconv_close(conv_desc); 
169  return strlen(ascii);
[41]170}
171
172
173/*
[69]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).
[41]182 */
[77]183static char* data_to_ascii(unsigned char *datap, uint32 len, uint32 type, 
[69]184                           char** error_msg)
[41]185{
[61]186  char* asciip;
187  char* ascii;
[41]188  unsigned char* cur_str;
[61]189  char* cur_ascii;
[41]190  char* cur_quoted;
[69]191  char* tmp_err;
192  const char* str_type;
[77]193  uint32 i;
194  uint32 cur_str_len;
195  uint32 ascii_max, cur_str_max;
196  uint32 str_rem, cur_str_rem, alen;
[66]197  int ret_err;
[69]198  unsigned short num_nulls;
[41]199
[69]200  *error_msg = NULL;
201
[41]202  switch (type) 
203  {
[66]204  case REG_SZ:
205  case REG_EXPAND_SZ:
[61]206    /* REG_LINK is a symbolic link, stored as a unicode string. */
207  case REG_LINK:
[69]208    ascii_max = sizeof(char)*(len+1);
209    ascii = malloc(ascii_max);
[41]210    if(ascii == NULL)
211      return NULL;
212   
[66]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)
[61]218    {
[69]219      tmp_err = strerror(-ret_err);
[78]220      str_type = regfi_type_val2str(type);
[69]221      *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
222      if(*error_msg == NULL)
[71]223      {
224        free(ascii);
[69]225        return NULL;
[71]226      }
[69]227      sprintf(*error_msg, "Unicode conversion failed on %s field; "
228               "printing as binary.  Error: %s", str_type, tmp_err);
229     
[66]230      cur_quoted = quote_buffer(datap, len, common_special_chars);
[61]231    }
[66]232    else
233      cur_quoted = quote_string(ascii, common_special_chars);
[42]234    free(ascii);
[71]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    }
[61]241    return cur_quoted;
[41]242    break;
243
244  case REG_DWORD:
[72]245    ascii_max = sizeof(char)*(8+2+1);
[58]246    ascii = malloc(ascii_max);
[41]247    if(ascii == NULL)
248      return NULL;
249
[61]250    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
[102]251             datap[3], datap[2], datap[1], datap[0]);
[41]252    return ascii;
253    break;
254
[58]255  case REG_DWORD_BE:
[72]256    ascii_max = sizeof(char)*(8+2+1);
[58]257    ascii = malloc(ascii_max);
258    if(ascii == NULL)
259      return NULL;
260
[61]261    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
[102]262             datap[0], datap[1], datap[2], datap[3]);
[58]263    return ascii;
264    break;
265
[72]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
[42]279  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
[102]280   *      redone with fewer malloc calls and better string concatenation.
281   *      Also, gives lame output when "\0\0" is the string.
[42]282   */
[41]283  case REG_MULTI_SZ:
[69]284    ascii_max = sizeof(char)*(len*4+1);
285    cur_str_max = sizeof(char)*(len+1);
[41]286    cur_str = malloc(cur_str_max);
287    cur_ascii = malloc(cur_str_max);
[69]288    ascii = malloc(ascii_max);
[42]289    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
[41]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      {
[66]314        ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
315        if(ret_err < 0)
[61]316        {
[69]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)
[71]323            {
324              free(cur_str);
325              free(cur_ascii);
326              free(ascii);
[69]327              return NULL;
[71]328            }
[69]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          }
[66]333          cur_quoted = quote_buffer(cur_str, cur_str_len-1, 
334                                    subfield_special_chars);
[61]335        }
[66]336        else
337          cur_quoted = quote_string(cur_ascii, subfield_special_chars);
[61]338
339        alen = snprintf(asciip, str_rem, "%s", cur_quoted);
[41]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        {
[61]348          if(str_rem > 0)
349          {
350            asciip[0] = '|';
351            asciip[1] = '\0';
352            asciip++;
353            str_rem--;
354          }
[41]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;
[42]364    free(cur_str);
365    free(cur_ascii);
[41]366    return ascii;
367    break;
368
[42]369  /* XXX: Dont know what to do with these yet, just print as binary... */
[77]370  default:
371    fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
372   
[61]373  case REG_NONE:
[42]374  case REG_RESOURCE_LIST:
375  case REG_FULL_RESOURCE_DESCRIPTOR:
376  case REG_RESOURCE_REQUIREMENTS_LIST:
377
378  case REG_BINARY:
[66]379    return quote_buffer(datap, len, common_special_chars);
[42]380    break;
[71]381  }
[42]382
[41]383  return NULL;
384}
385
386
[83]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 */
[81]391char** splitPath(const char* s)
[30]392{
[81]393  char** ret_val;
[38]394  const char* cur = s;
[33]395  char* next = NULL;
[38]396  char* copy;
[81]397  uint32 ret_cur = 0;
[38]398
[81]399  ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
400  if (ret_val == NULL)
[38]401    return NULL;
[81]402  ret_val[0] = NULL;
403
404  /* We return a well-formed, 0-length, path even when input is icky. */
[37]405  if (s == NULL)
[81]406    return ret_val;
[38]407 
408  while((next = strchr(cur, '/')) != NULL)
[33]409  {
[38]410    if ((next-cur) > 0)
411    {
412      copy = (char*)malloc((next-cur+1)*sizeof(char));
413      if(copy == NULL)
[88]414        bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
[38]415         
416      memcpy(copy, cur, next-cur);
417      copy[next-cur] = '\0';
[81]418      ret_val[ret_cur++] = copy;
419      if(ret_cur < (REGF_MAX_DEPTH+1+1))
420        ret_val[ret_cur] = NULL;
421      else
[88]422        bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
[38]423    }
424    cur = next+1;
[33]425  }
[81]426
427  /* Grab last element, if path doesn't end in '/'. */
[33]428  if(strlen(cur) > 0)
[38]429  {
430    copy = strdup(cur);
[81]431    ret_val[ret_cur++] = copy;
432    if(ret_cur < (REGF_MAX_DEPTH+1+1))
433      ret_val[ret_cur] = NULL;
434    else
[88]435      bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
[38]436  }
[33]437
438  return ret_val;
439}
440
[81]441
[83]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
[81]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)
[33]461{
[81]462  const REGFI_ITER_POSITION* cur;
[37]463  uint32 buf_left = 127;
464  uint32 buf_len = buf_left+1;
465  uint32 name_len = 0;
466  uint32 grow_amt;
[81]467  char* buf;
[31]468  char* new_buf;
[66]469  char* name;
[81]470  const char* cur_name;
[31]471  void_stack_iterator* iter;
472 
473  buf = (char*)malloc((buf_len)*sizeof(char));
474  if (buf == NULL)
475    return NULL;
[54]476  buf[0] = '\0';
[30]477
[81]478  iter = void_stack_iterator_new(i->key_positions);
[31]479  if (iter == NULL)
[30]480  {
[31]481    free(buf);
482    return NULL;
[30]483  }
484
[33]485  /* skip root element */
[81]486  if(void_stack_size(i->key_positions) < 1)
487  {
488    buf[0] = '/';
489    buf[1] = '\0';
490    return buf;
491  }
[33]492  cur = void_stack_iterator_next(iter);
493
[81]494  do
[31]495  {
[81]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
[33]502    buf[buf_len-buf_left-1] = '/';
503    buf_left -= 1;
[81]504    name = quote_string(cur_name, key_special_chars);
[66]505    name_len = strlen(name);
[31]506    if(name_len+1 > buf_left)
507    {
[37]508      grow_amt = (uint32)(buf_len/2);
[31]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    }
[66]519    strncpy(buf+(buf_len-buf_left-1), name, name_len);
[31]520    buf_left -= name_len;
521    buf[buf_len-buf_left-1] = '\0';
[66]522    free(name);
[81]523  } while(cur != NULL);
[30]524
[31]525  return buf;
526}
[30]527
[31]528
[84]529void printValue(const REGF_VK_REC* vk, char* prefix)
[31]530{
[66]531  char* quoted_value = NULL;
532  char* quoted_name = NULL;
[69]533  char* conv_error = NULL;
[77]534  const char* str_type = NULL;
535  uint32 size;
[41]536
[102]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
[84]542   */
[102]543  if(size > VK_MAX_DATA_LENGTH)
[41]544  {
[102]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;
[43]548  }
[102]549 
550  quoted_value = data_to_ascii(vk->data, vk->data_size, 
551                               vk->type, &conv_error);
[61]552
[43]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   */
[66]558  quoted_name = quote_string(vk->valuename, common_special_chars);
[88]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  }
[69]566
567  if(quoted_value == NULL)
568  {
569    if(conv_error == NULL)
[71]570      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
[69]571              "Memory allocation failure likely.\n", prefix, quoted_name);
572    else
[71]573      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
[69]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
[78]581  str_type = regfi_type_val2str(vk->type);
[43]582  if(print_security)
[77]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  }
[43]591  else
[77]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
[43]601  if(quoted_value != NULL)
602    free(quoted_value);
603  if(quoted_name != NULL)
604    free(quoted_name);
[69]605  if(conv_error != NULL)
606    free(conv_error);
[32]607}
608
609
[81]610void printValueList(REGFI_ITERATOR* i, char* prefix)
[32]611{
[84]612  const REGF_VK_REC* value;
[80]613
614  value = regfi_iterator_first_value(i);
615  while(value != NULL)
[81]616  {
617    if(!type_filter_enabled || (value->type == type_filter))
[80]618      printValue(value, prefix);
[81]619    value = regfi_iterator_next_value(i);
620  }
[33]621}
622
[37]623
[109]624void printKey(REGFI_ITERATOR* i, char* full_path)
[33]625{
[43]626  static char empty_str[1] = "";
[42]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;
[109]634  const REGF_SK_REC* sk;
635  const REGF_NK_REC* k = regfi_iterator_cur_key(i);
[42]636
[43]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
[109]641  if(print_security && (sk=regfi_iterator_cur_sk(i)))
[43]642  {
[109]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);
[43]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
[66]656    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
[43]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
[66]669    printf("%s,KEY,,%s\n", full_path, mtime);
[43]670}
671
672
[81]673void printKeyTree(REGFI_ITERATOR* iter)
[43]674{
[84]675  const REGF_NK_REC* root = NULL;
676  const REGF_NK_REC* cur = NULL;
677  const REGF_NK_REC* sub = NULL;
[43]678  char* path = NULL;
[78]679  int key_type = regfi_type_str2val("KEY");
[81]680  bool print_this = true;
681
682  root = cur = regfi_iterator_cur_key(iter);
683  sub = regfi_iterator_first_subkey(iter);
[43]684 
[81]685  if(root == NULL)
[88]686    bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
[81]687 
688  do
[31]689  {
[81]690    if(print_this)
[54]691    {
[81]692      path = iter2Path(iter);
693      if(path == NULL)
[88]694        bailOut(EX_OSERR, "ERROR: Could not construct iterator's path.\n");
[81]695     
696      if(!type_filter_enabled || (key_type == type_filter))
[109]697        printKey(iter, path);
[81]698      if(!type_filter_enabled || (key_type != type_filter))
699        printValueList(iter, path);
700     
701      free(path);
[54]702    }
[66]703   
[81]704    if(sub == NULL)
[31]705    {
[81]706      if(cur != root)
[31]707      {
[81]708        /* We're done with this sub-tree, going up and hitting other branches. */
709        if(!regfi_iterator_up(iter))
[88]710          bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
[81]711       
712        cur = regfi_iterator_cur_key(iter);
713        if(cur == NULL)
[88]714          bailOut(EX_DATAERR, "ERROR: unexpected NULL for key.\n");
[81]715       
716        sub = regfi_iterator_next_subkey(iter);
[66]717      }
[81]718      print_this = false;
[31]719    }
[81]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))
[88]725        bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
[81]726
727      cur = sub;
728      sub = regfi_iterator_first_subkey(iter);
729      print_this = true;
730    }
731  } while(!((cur == root) && (sub == NULL)));
732
[54]733  if(print_verbose)
734    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
[30]735}
736
[81]737
[97]738/* XXX: what if there is BOTH a value AND a key with that name?? */
[33]739/*
[80]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.
[33]743 * Returns less than 0 on other error.
744 */
[80]745int retrievePath(REGFI_ITERATOR* iter, char** path)
[33]746{
[84]747  const REGF_VK_REC* value;
[81]748  char* tmp_path_joined;
749  const char** tmp_path;
[80]750  uint32 i;
751 
752  if(path == NULL)
[33]753    return -1;
754
[80]755  /* One extra for any value at the end, and one more for NULL */
[81]756  tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
[80]757  if(tmp_path == NULL)
[33]758    return -2;
759
[80]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];
[33]766
[80]767  tmp_path[i] = NULL;
768
[54]769  if(print_verbose)
[80]770    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
[54]771            path_filter);
772
[82]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
[80]781  if(!regfi_iterator_walk_path(iter, tmp_path))
[33]782  {
[80]783    free(tmp_path);
784    return 0;
[33]785  }
786
[80]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");
[33]791
[80]792    value = regfi_iterator_cur_value(iter);
[81]793    tmp_path_joined = iter2Path(iter);
[54]794
[80]795    if((value == NULL) || (tmp_path_joined == NULL))
[88]796      bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
[54]797
[80]798    printValue(value, tmp_path_joined);
[54]799
[80]800    free(tmp_path);
801    free(tmp_path_joined);
802    return 1;
[33]803  }
[80]804  else if(regfi_iterator_find_subkey(iter, path[i]))
[33]805  {
[80]806    if(print_verbose)
807      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
[82]808
809    if(!regfi_iterator_down(iter))
[88]810      bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
[82]811
[80]812    return 2;
[33]813  }
814
[54]815  if(print_verbose)
816    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
817
[80]818  return 0;
[33]819}
820
821
[37]822static void usage(void)
823{
[61]824  fprintf(stderr, "Usage: reglookup [-v] [-s]"
[40]825          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
[39]826          " <REGISTRY_FILE>\n");
[85]827  fprintf(stderr, "Version: 0.4.0\n");
[39]828  fprintf(stderr, "Options:\n");
829  fprintf(stderr, "\t-v\t sets verbose mode.\n");
[47]830  fprintf(stderr, "\t-h\t enables header row. (default)\n");
831  fprintf(stderr, "\t-H\t disables header row.\n");
[44]832  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
833  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
[40]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");
[37]836  fprintf(stderr, "\n");
837}
838
839
[30]840int main(int argc, char** argv)
841{
[80]842  char** path = NULL;
843  REGFI_ITERATOR* iter;
[33]844  int retr_path_ret;
[44]845  uint32 argi, arge;
[31]846
[37]847  /* Process command line arguments */
[30]848  if(argc < 2)
849  {
[37]850    usage();
[88]851    bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
[30]852  }
[37]853 
[44]854  arge = argc-1;
855  for(argi = 1; argi < arge; argi++)
[37]856  {
[40]857    if (strcmp("-p", argv[argi]) == 0)
[37]858    {
[44]859      if(++argi >= arge)
[37]860      {
861        usage();
[88]862        bailOut(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
[37]863      }
[40]864      if((path_filter = strdup(argv[argi])) == NULL)
[88]865        bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
[38]866
[40]867      path_filter_enabled = true;
[37]868    }
869    else if (strcmp("-t", argv[argi]) == 0)
870    {
[44]871      if(++argi >= arge)
[37]872      {
873        usage();
[88]874        bailOut(EX_USAGE, "ERROR: '-t' option requires parameter.\n");
[37]875      }
[78]876      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
[40]877      {
878        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
[88]879        bailOut(EX_USAGE, "");
[40]880      }
[37]881      type_filter_enabled = true;
882    }
[47]883    else if (strcmp("-h", argv[argi]) == 0)
884      print_header = true;
885    else if (strcmp("-H", argv[argi]) == 0)
886      print_header = false;
[37]887    else if (strcmp("-s", argv[argi]) == 0)
888      print_security = true;
[44]889    else if (strcmp("-S", argv[argi]) == 0)
890      print_security = false;
[37]891    else if (strcmp("-v", argv[argi]) == 0)
892      print_verbose = true;
[44]893    else
[37]894    {
[38]895      usage();
[37]896      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
[88]897      bailOut(EX_USAGE, "");
[37]898    }
899  }
[44]900  if((registry_file = strdup(argv[argi])) == NULL)
[88]901    bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
[30]902
[110]903  f = regfi_open(registry_file);
[37]904  if(f == NULL)
905  {
906    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
[88]907    bailOut(EX_NOINPUT, "");
[37]908  }
[38]909
[80]910  iter = regfi_iterator_new(f);
911  if(iter == NULL)
[88]912    bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
[30]913
[81]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
[80]922  if(path_filter_enabled && path_filter != NULL)
923    path = splitPath(path_filter);
[81]924
[80]925  if(path != NULL)
[33]926  {
[80]927    retr_path_ret = retrievePath(iter, path);
[83]928    freePath(path);
929
[80]930    if(retr_path_ret == 0)
931      fprintf(stderr, "WARNING: specified path not found.\n");
932    else if (retr_path_ret == 2)
[81]933      printKeyTree(iter);
[93]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    }
[33]940  }
[37]941  else
[81]942    printKeyTree(iter);
[31]943
[80]944  regfi_iterator_free(iter);
[78]945  regfi_close(f);
[30]946
947  return 0;
948}
Note: See TracBrowser for help on using the repository browser.