source: trunk/src/reglookup.c @ 91

Last change on this file since 91 was 90, checked in by tim, 18 years ago

minor changes to Makefiles

Added one warning on an unlikely condition in reglookup

Added the -H option to reglookup-timeline for ommiting the header

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