source: trunk/src/reglookup.c @ 87

Last change on this file since 87 was 85, checked in by tim, 18 years ago

updated upcoming version number

  • Property svn:keywords set to Id
File size: 24.2 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 85 2007-01-19 14:58:26Z tim $
22 */
23
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
[33]28#include <strings.h>
[42]29#include <time.h>
[61]30#include <iconv.h>
[79]31#include "../include/regfi.h"
[31]32#include "../include/void_stack.h"
[30]33
[40]34/* Globals, influenced by command line parameters */
35bool print_verbose = false;
36bool print_security = false;
[42]37bool print_header = true;
[40]38bool path_filter_enabled = false;
39bool type_filter_enabled = false;
40char* path_filter = NULL;
41int type_filter;
42char* registry_file = NULL;
43
[42]44/* Other globals */
[66]45const char* key_special_chars = ",\"\\/";
46const char* subfield_special_chars = ",\"\\|";
47const char* common_special_chars = ",\"\\";
48
[61]49iconv_t conv_desc;
[40]50
[61]51
[38]52void bailOut(int code, char* message)
53{
54  fprintf(stderr, message);
55  exit(code);
56}
57
58
[41]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
[61]62 * character.  A null terminator is added, since only ascii, not binary,
[41]63 * is returned.
64 */
65static char* quote_buffer(const unsigned char* str, 
[44]66                          unsigned int len, const char* special)
[41]67{
[61]68  unsigned int i, added_len;
69  unsigned int num_written = 0;
[41]70
[61]71  unsigned int buf_len = sizeof(char)*(len+1);
72  char* ret_val = malloc(buf_len);
73  char* tmp_buf;
74
[41]75  if(ret_val == NULL)
76    return NULL;
77
78  for(i=0; i<len; i++)
79  {
[61]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
[69]85       * output string length.  This should minimize both the number of
86       * reallocs() and the amount of wasted memory.
[61]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   
[41]108    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
109    {
[61]110      num_written += snprintf(ret_val + num_written, buf_len - num_written,
[41]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 */
[44]127static char* quote_string(const char* str, const char* special)
[41]128{
[42]129  unsigned int len;
[41]130
[42]131  if(str == NULL)
132    return NULL;
133
134  len = strlen(str);
135  return quote_buffer((const unsigned char*)str, len, special);
[41]136}
137
138
139/*
[69]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.
[41]145 */
[61]146static int uni_to_ascii(unsigned char* uni, char* ascii, 
147                        unsigned int uni_max, unsigned int ascii_max)
[41]148{
[61]149  char* inbuf = (char*)uni;
150  char* outbuf = ascii;
[70]151  size_t in_len = (size_t)uni_max;
152  size_t out_len = (size_t)(ascii_max-1);
[61]153  int ret;
[41]154
[61]155  /* Set up conversion descriptor. */
156  conv_desc = iconv_open("US-ASCII", "UTF-16LE");
157
[70]158  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
[61]159  if(ret == -1)
[41]160  {
[61]161    iconv_close(conv_desc);
[66]162    return -errno;
[41]163  }
[66]164  *outbuf = '\0';
[41]165
[61]166  iconv_close(conv_desc); 
167  return strlen(ascii);
[41]168}
169
170
171/*
[69]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).
[41]180 */
[77]181static char* data_to_ascii(unsigned char *datap, uint32 len, uint32 type, 
[69]182                           char** error_msg)
[41]183{
[61]184  char* asciip;
185  char* ascii;
[41]186  unsigned char* cur_str;
[61]187  char* cur_ascii;
[41]188  char* cur_quoted;
[69]189  char* tmp_err;
190  const char* str_type;
[77]191  uint32 i;
192  uint32 cur_str_len;
193  uint32 ascii_max, cur_str_max;
194  uint32 str_rem, cur_str_rem, alen;
[66]195  int ret_err;
[69]196  unsigned short num_nulls;
[41]197
[69]198  *error_msg = NULL;
199
[41]200  switch (type) 
201  {
[66]202  case REG_SZ:
203  case REG_EXPAND_SZ:
[61]204    /* REG_LINK is a symbolic link, stored as a unicode string. */
205  case REG_LINK:
[69]206    ascii_max = sizeof(char)*(len+1);
207    ascii = malloc(ascii_max);
[41]208    if(ascii == NULL)
209      return NULL;
210   
[66]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)
[61]216    {
[69]217      tmp_err = strerror(-ret_err);
[78]218      str_type = regfi_type_val2str(type);
[69]219      *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
220      if(*error_msg == NULL)
[71]221      {
222        free(ascii);
[69]223        return NULL;
[71]224      }
[69]225      sprintf(*error_msg, "Unicode conversion failed on %s field; "
226               "printing as binary.  Error: %s", str_type, tmp_err);
227     
[66]228      cur_quoted = quote_buffer(datap, len, common_special_chars);
[61]229    }
[66]230    else
231      cur_quoted = quote_string(ascii, common_special_chars);
[42]232    free(ascii);
[71]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    }
[61]239    return cur_quoted;
[41]240    break;
241
242  case REG_DWORD:
[72]243    ascii_max = sizeof(char)*(8+2+1);
[58]244    ascii = malloc(ascii_max);
[41]245    if(ascii == NULL)
246      return NULL;
247
[61]248    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
[58]249             datap[0], datap[1], datap[2], datap[3]);
[41]250    return ascii;
251    break;
252
[58]253  case REG_DWORD_BE:
[72]254    ascii_max = sizeof(char)*(8+2+1);
[58]255    ascii = malloc(ascii_max);
256    if(ascii == NULL)
257      return NULL;
258
[61]259    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
[58]260             datap[3], datap[2], datap[1], datap[0]);
261    return ascii;
262    break;
263
[72]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
[42]277  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
[54]278   *      redone with fewer malloc calls and better string concatenation.
[42]279   */
[41]280  case REG_MULTI_SZ:
[69]281    ascii_max = sizeof(char)*(len*4+1);
282    cur_str_max = sizeof(char)*(len+1);
[41]283    cur_str = malloc(cur_str_max);
284    cur_ascii = malloc(cur_str_max);
[69]285    ascii = malloc(ascii_max);
[42]286    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
[41]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      {
[66]311        ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
312        if(ret_err < 0)
[61]313        {
[69]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)
[71]320            {
321              free(cur_str);
322              free(cur_ascii);
323              free(ascii);
[69]324              return NULL;
[71]325            }
[69]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          }
[66]330          cur_quoted = quote_buffer(cur_str, cur_str_len-1, 
331                                    subfield_special_chars);
[61]332        }
[66]333        else
334          cur_quoted = quote_string(cur_ascii, subfield_special_chars);
[61]335
336        alen = snprintf(asciip, str_rem, "%s", cur_quoted);
[41]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        {
[61]345          if(str_rem > 0)
346          {
347            asciip[0] = '|';
348            asciip[1] = '\0';
349            asciip++;
350            str_rem--;
351          }
[41]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;
[42]361    free(cur_str);
362    free(cur_ascii);
[41]363    return ascii;
364    break;
365
[42]366  /* XXX: Dont know what to do with these yet, just print as binary... */
[77]367  default:
368    fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
369   
[61]370  case REG_NONE:
[42]371  case REG_RESOURCE_LIST:
372  case REG_FULL_RESOURCE_DESCRIPTOR:
373  case REG_RESOURCE_REQUIREMENTS_LIST:
374
375  case REG_BINARY:
[66]376    return quote_buffer(datap, len, common_special_chars);
[42]377    break;
[71]378  }
[42]379
[41]380  return NULL;
381}
382
383
[83]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 */
[81]388char** splitPath(const char* s)
[30]389{
[81]390  char** ret_val;
[38]391  const char* cur = s;
[33]392  char* next = NULL;
[38]393  char* copy;
[81]394  uint32 ret_cur = 0;
[38]395
[81]396  ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
397  if (ret_val == NULL)
[38]398    return NULL;
[81]399  ret_val[0] = NULL;
400
401  /* We return a well-formed, 0-length, path even when input is icky. */
[37]402  if (s == NULL)
[81]403    return ret_val;
[38]404 
405  while((next = strchr(cur, '/')) != NULL)
[33]406  {
[38]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';
[81]415      ret_val[ret_cur++] = copy;
416      if(ret_cur < (REGF_MAX_DEPTH+1+1))
417        ret_val[ret_cur] = NULL;
418      else
[78]419        bailOut(2, "ERROR: Registry maximum depth exceeded.\n");
[38]420    }
421    cur = next+1;
[33]422  }
[81]423
424  /* Grab last element, if path doesn't end in '/'. */
[33]425  if(strlen(cur) > 0)
[38]426  {
427    copy = strdup(cur);
[81]428    ret_val[ret_cur++] = copy;
429    if(ret_cur < (REGF_MAX_DEPTH+1+1))
430      ret_val[ret_cur] = NULL;
431    else
[78]432      bailOut(2, "ERROR: Registry maximum depth exceeded.\n");
[38]433  }
[33]434
435  return ret_val;
436}
437
[81]438
[83]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
[81]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)
[33]458{
[81]459  const REGFI_ITER_POSITION* cur;
[37]460  uint32 buf_left = 127;
461  uint32 buf_len = buf_left+1;
462  uint32 name_len = 0;
463  uint32 grow_amt;
[81]464  char* buf;
[31]465  char* new_buf;
[66]466  char* name;
[81]467  const char* cur_name;
[31]468  void_stack_iterator* iter;
469 
470  buf = (char*)malloc((buf_len)*sizeof(char));
471  if (buf == NULL)
472    return NULL;
[54]473  buf[0] = '\0';
[30]474
[81]475  iter = void_stack_iterator_new(i->key_positions);
[31]476  if (iter == NULL)
[30]477  {
[31]478    free(buf);
479    return NULL;
[30]480  }
481
[33]482  /* skip root element */
[81]483  if(void_stack_size(i->key_positions) < 1)
484  {
485    buf[0] = '/';
486    buf[1] = '\0';
487    return buf;
488  }
[33]489  cur = void_stack_iterator_next(iter);
490
[81]491  do
[31]492  {
[81]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
[33]499    buf[buf_len-buf_left-1] = '/';
500    buf_left -= 1;
[81]501    name = quote_string(cur_name, key_special_chars);
[66]502    name_len = strlen(name);
[31]503    if(name_len+1 > buf_left)
504    {
[37]505      grow_amt = (uint32)(buf_len/2);
[31]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    }
[66]516    strncpy(buf+(buf_len-buf_left-1), name, name_len);
[31]517    buf_left -= name_len;
518    buf[buf_len-buf_left-1] = '\0';
[66]519    free(name);
[81]520  } while(cur != NULL);
[30]521
[31]522  return buf;
523}
[30]524
[31]525
[84]526void printValue(const REGF_VK_REC* vk, char* prefix)
[31]527{
[66]528  char* quoted_value = NULL;
529  char* quoted_name = NULL;
[69]530  char* conv_error = NULL;
[77]531  const char* str_type = NULL;
532  uint32 size;
533  uint8 tmp_buf[4];
[41]534
[43]535  /* Thanks Microsoft for making this process so straight-forward!!! */
[84]536  /* XXX: this logic should be abstracted  and pushed into the regfi
537   *      interface.  This includes the size limits.
538   */
[43]539  size = (vk->data_size & ~VK_DATA_IN_OFFSET);
540  if(vk->data_size & VK_DATA_IN_OFFSET)
[41]541  {
[43]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)
[77]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... */
[43]550      size = 4;
[69]551    quoted_value = data_to_ascii(tmp_buf, 4, vk->type, &conv_error);
[43]552  }
553  else
554  {
[77]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
[43]560     */
[77]561    if(size > VK_MAX_DATA_LENGTH)
[41]562    {
[77]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;
[41]566    }
[61]567
[69]568    quoted_value = data_to_ascii(vk->data, vk->data_size, 
569                                 vk->type, &conv_error);
[43]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   */
[66]576  quoted_name = quote_string(vk->valuename, common_special_chars);
[69]577
578  if(quoted_value == NULL)
579  {
580    if(conv_error == NULL)
[71]581      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
[69]582              "Memory allocation failure likely.\n", prefix, quoted_name);
583    else
[71]584      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
[69]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
[78]592  str_type = regfi_type_val2str(vk->type);
[43]593  if(print_security)
[77]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  }
[43]602  else
[77]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
[43]612  if(quoted_value != NULL)
613    free(quoted_value);
614  if(quoted_name != NULL)
615    free(quoted_name);
[69]616  if(conv_error != NULL)
617    free(conv_error);
[32]618}
619
620
[81]621void printValueList(REGFI_ITERATOR* i, char* prefix)
[32]622{
[84]623  const REGF_VK_REC* value;
[80]624
625  value = regfi_iterator_first_value(i);
626  while(value != NULL)
[81]627  {
628    if(!type_filter_enabled || (value->type == type_filter))
[80]629      printValue(value, prefix);
[81]630    value = regfi_iterator_next_value(i);
631  }
[33]632}
633
[37]634
[84]635void printKey(const REGF_NK_REC* k, char* full_path)
[33]636{
[43]637  static char empty_str[1] = "";
[42]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
[43]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  {
[78]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);
[43]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
[66]665    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
[43]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
[66]678    printf("%s,KEY,,%s\n", full_path, mtime);
[43]679}
680
681
[81]682void printKeyTree(REGFI_ITERATOR* iter)
[43]683{
[84]684  const REGF_NK_REC* root = NULL;
685  const REGF_NK_REC* cur = NULL;
686  const REGF_NK_REC* sub = NULL;
[43]687  char* path = NULL;
[78]688  int key_type = regfi_type_str2val("KEY");
[81]689  bool print_this = true;
690
691  root = cur = regfi_iterator_cur_key(iter);
692  sub = regfi_iterator_first_subkey(iter);
[43]693 
[81]694  if(root == NULL)
695    bailOut(3, "ERROR: root cannot be NULL.\n");
696 
697  do
[31]698  {
[81]699    if(print_this)
[54]700    {
[81]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);
[54]711    }
[66]712   
[81]713    if(sub == NULL)
[31]714    {
[81]715      if(cur != root)
[31]716      {
[81]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);
[66]726      }
[81]727      print_this = false;
[31]728    }
[81]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
[54]742  if(print_verbose)
743    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
[30]744}
745
[81]746
[33]747/*
[80]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.
[33]751 * Returns less than 0 on other error.
752 */
[80]753int retrievePath(REGFI_ITERATOR* iter, char** path)
[33]754{
[84]755  const REGF_VK_REC* value;
[81]756  char* tmp_path_joined;
757  const char** tmp_path;
[80]758  uint32 i;
759 
760  if(path == NULL)
[33]761    return -1;
762
[80]763  /* One extra for any value at the end, and one more for NULL */
[81]764  tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
[80]765  if(tmp_path == NULL)
[33]766    return -2;
767
[80]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];
[33]774
[80]775  tmp_path[i] = NULL;
776
[54]777  if(print_verbose)
[80]778    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
[54]779            path_filter);
780
[82]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
[80]789  if(!regfi_iterator_walk_path(iter, tmp_path))
[33]790  {
[80]791    free(tmp_path);
792    return 0;
[33]793  }
794
[80]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");
[33]799
[80]800    value = regfi_iterator_cur_value(iter);
[81]801    tmp_path_joined = iter2Path(iter);
[54]802
[80]803    if((value == NULL) || (tmp_path_joined == NULL))
804      bailOut(2, "ERROR: Unexpected error before printValue.\n");
[54]805
[80]806    printValue(value, tmp_path_joined);
[54]807
[80]808    free(tmp_path);
809    free(tmp_path_joined);
810    return 1;
[33]811  }
[80]812  else if(regfi_iterator_find_subkey(iter, path[i]))
[33]813  {
[80]814    if(print_verbose)
815      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
[82]816
817    if(!regfi_iterator_down(iter))
818      bailOut(2, "ERROR: Unexpected error on traversing path filter key.\n");
819
[80]820    return 2;
[33]821  }
822
[54]823  if(print_verbose)
824    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
825
[80]826  return 0;
[33]827}
828
829
[37]830static void usage(void)
831{
[61]832  fprintf(stderr, "Usage: reglookup [-v] [-s]"
[40]833          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
[39]834          " <REGISTRY_FILE>\n");
[85]835  fprintf(stderr, "Version: 0.4.0\n");
[39]836  fprintf(stderr, "Options:\n");
837  fprintf(stderr, "\t-v\t sets verbose mode.\n");
[47]838  fprintf(stderr, "\t-h\t enables header row. (default)\n");
839  fprintf(stderr, "\t-H\t disables header row.\n");
[44]840  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
841  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
[40]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");
[37]844  fprintf(stderr, "\n");
845}
846
847
[30]848int main(int argc, char** argv)
849{
[80]850  char** path = NULL;
[31]851  REGF_FILE* f;
[80]852  REGFI_ITERATOR* iter;
[33]853  int retr_path_ret;
[44]854  uint32 argi, arge;
[31]855
[37]856  /* Process command line arguments */
[30]857  if(argc < 2)
858  {
[37]859    usage();
[44]860    bailOut(1, "ERROR: Requires at least one argument.\n");
[30]861  }
[37]862 
[44]863  arge = argc-1;
864  for(argi = 1; argi < arge; argi++)
[37]865  {
[40]866    if (strcmp("-p", argv[argi]) == 0)
[37]867    {
[44]868      if(++argi >= arge)
[37]869      {
870        usage();
[40]871        bailOut(1, "ERROR: '-p' option requires parameter.\n");
[37]872      }
[40]873      if((path_filter = strdup(argv[argi])) == NULL)
[38]874        bailOut(2, "ERROR: Memory allocation problem.\n");
875
[40]876      path_filter_enabled = true;
[37]877    }
878    else if (strcmp("-t", argv[argi]) == 0)
879    {
[44]880      if(++argi >= arge)
[37]881      {
882        usage();
[38]883        bailOut(1, "ERROR: '-t' option requires parameter.\n");
[37]884      }
[78]885      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
[40]886      {
887        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
888        bailOut(1, "");
889      }
[37]890      type_filter_enabled = true;
891    }
[47]892    else if (strcmp("-h", argv[argi]) == 0)
893      print_header = true;
894    else if (strcmp("-H", argv[argi]) == 0)
895      print_header = false;
[37]896    else if (strcmp("-s", argv[argi]) == 0)
897      print_security = true;
[44]898    else if (strcmp("-S", argv[argi]) == 0)
899      print_security = false;
[37]900    else if (strcmp("-v", argv[argi]) == 0)
901      print_verbose = true;
[44]902    else
[37]903    {
[38]904      usage();
[37]905      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
[38]906      bailOut(1, "");
[37]907    }
908  }
[44]909  if((registry_file = strdup(argv[argi])) == NULL)
910    bailOut(2, "ERROR: Memory allocation problem.\n");
[30]911
[78]912  f = regfi_open(registry_file);
[37]913  if(f == NULL)
914  {
915    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
[38]916    bailOut(3, "");
[37]917  }
[38]918
[80]919  iter = regfi_iterator_new(f);
920  if(iter == NULL)
921    bailOut(3, "ERROR: Couldn't create registry iterator.\n");
[30]922
[81]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
[80]931  if(path_filter_enabled && path_filter != NULL)
932    path = splitPath(path_filter);
[81]933
[80]934  if(path != NULL)
[33]935  {
[80]936    retr_path_ret = retrievePath(iter, path);
[83]937    freePath(path);
938
[80]939    if(retr_path_ret == 0)
940      fprintf(stderr, "WARNING: specified path not found.\n");
941    else if (retr_path_ret == 2)
[81]942      printKeyTree(iter);
[80]943    else if(retr_path_ret != 0)
944      bailOut(4, "ERROR: Unknown error occurred in retrieving path.\n");
[33]945  }
[37]946  else
[81]947    printKeyTree(iter);
[31]948
[80]949  regfi_iterator_free(iter);
[78]950  regfi_close(f);
[30]951
952  return 0;
953}
Note: See TracBrowser for help on using the repository browser.