source: trunk/src/reglookup.c @ 107

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

removed redundant hbin parsing

added flags parameter to regfi_open

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