source: trunk/src/reglookup.c @ 97

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

begun the work of rewriting the lowest layer parsing routines

  • Property svn:keywords set to Id
File size: 24.7 KB
Line 
1/*
2 * A utility to read a Windows NT/2K/XP/2K3 registry file, using
3 * Gerald Carter''s regfio interface.
4 *
5 * Copyright (C) 2005-2007 Timothy D. Morgan
6 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
20 *
21 * $Id: reglookup.c 97 2008-02-27 01:04:14Z tim $
22 */
23
24
25#include <stdlib.h>
26#include <sysexits.h>
27#include <stdio.h>
28#include <string.h>
29#include <strings.h>
30#include <time.h>
31#include <iconv.h>
32#include "../include/regfi.h"
33#include "../include/void_stack.h"
34
35/* Globals, influenced by command line parameters */
36bool print_verbose = false;
37bool print_security = false;
38bool print_header = true;
39bool path_filter_enabled = false;
40bool type_filter_enabled = false;
41char* path_filter = NULL;
42int type_filter;
43char* registry_file = NULL;
44
45/* Other globals */
46const char* key_special_chars = ",\"\\/";
47const char* subfield_special_chars = ",\"\\|";
48const char* common_special_chars = ",\"\\";
49
50iconv_t conv_desc;
51
52
53void bailOut(int code, char* message)
54{
55  fprintf(stderr, message);
56  exit(code);
57}
58
59
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
63 * character.  A null terminator is added, since only ascii, not binary,
64 * is returned.
65 */
66static char* quote_buffer(const unsigned char* str, 
67                          unsigned int len, const char* special)
68{
69  unsigned int i, added_len;
70  unsigned int num_written = 0;
71
72  unsigned int buf_len = sizeof(char)*(len+1);
73  char* ret_val = malloc(buf_len);
74  char* tmp_buf;
75
76  if(ret_val == NULL)
77    return NULL;
78
79  for(i=0; i<len; i++)
80  {
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
86       * output string length.  This should minimize both the number of
87       * reallocs() and the amount of wasted memory.
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   
109    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
110    {
111      num_written += snprintf(ret_val + num_written, buf_len - num_written,
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 */
128static char* quote_string(const char* str, const char* special)
129{
130  unsigned int len;
131
132  if(str == NULL)
133    return NULL;
134
135  len = strlen(str);
136  return quote_buffer((const unsigned char*)str, len, special);
137}
138
139
140/*
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.
146 */
147static int uni_to_ascii(unsigned char* uni, char* ascii, 
148                        unsigned int uni_max, unsigned int ascii_max)
149{
150  char* inbuf = (char*)uni;
151  char* outbuf = ascii;
152  size_t in_len = (size_t)uni_max;
153  size_t out_len = (size_t)(ascii_max-1);
154  int ret;
155
156  /* Set up conversion descriptor. */
157  conv_desc = iconv_open("US-ASCII", "UTF-16LE");
158
159  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
160  if(ret == -1)
161  {
162    iconv_close(conv_desc);
163    return -errno;
164  }
165  *outbuf = '\0';
166
167  iconv_close(conv_desc); 
168  return strlen(ascii);
169}
170
171
172/*
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).
181 */
182static char* data_to_ascii(unsigned char *datap, uint32 len, uint32 type, 
183                           char** error_msg)
184{
185  char* asciip;
186  char* ascii;
187  unsigned char* cur_str;
188  char* cur_ascii;
189  char* cur_quoted;
190  char* tmp_err;
191  const char* str_type;
192  uint32 i;
193  uint32 cur_str_len;
194  uint32 ascii_max, cur_str_max;
195  uint32 str_rem, cur_str_rem, alen;
196  int ret_err;
197  unsigned short num_nulls;
198
199  *error_msg = NULL;
200
201  switch (type) 
202  {
203  case REG_SZ:
204  case REG_EXPAND_SZ:
205    /* REG_LINK is a symbolic link, stored as a unicode string. */
206  case REG_LINK:
207    ascii_max = sizeof(char)*(len+1);
208    ascii = malloc(ascii_max);
209    if(ascii == NULL)
210      return NULL;
211   
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)
217    {
218      tmp_err = strerror(-ret_err);
219      str_type = regfi_type_val2str(type);
220      *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
221      if(*error_msg == NULL)
222      {
223        free(ascii);
224        return NULL;
225      }
226      sprintf(*error_msg, "Unicode conversion failed on %s field; "
227               "printing as binary.  Error: %s", str_type, tmp_err);
228     
229      cur_quoted = quote_buffer(datap, len, common_special_chars);
230    }
231    else
232      cur_quoted = quote_string(ascii, common_special_chars);
233    free(ascii);
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    }
240    return cur_quoted;
241    break;
242
243  case REG_DWORD:
244    ascii_max = sizeof(char)*(8+2+1);
245    ascii = malloc(ascii_max);
246    if(ascii == NULL)
247      return NULL;
248
249    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
250             datap[0], datap[1], datap[2], datap[3]);
251    return ascii;
252    break;
253
254  case REG_DWORD_BE:
255    ascii_max = sizeof(char)*(8+2+1);
256    ascii = malloc(ascii_max);
257    if(ascii == NULL)
258      return NULL;
259
260    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
261             datap[3], datap[2], datap[1], datap[0]);
262    return ascii;
263    break;
264
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
278  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
279   *      redone with fewer malloc calls and better string concatenation.
280   */
281  case REG_MULTI_SZ:
282    ascii_max = sizeof(char)*(len*4+1);
283    cur_str_max = sizeof(char)*(len+1);
284    cur_str = malloc(cur_str_max);
285    cur_ascii = malloc(cur_str_max);
286    ascii = malloc(ascii_max);
287    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
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      {
312        ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
313        if(ret_err < 0)
314        {
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)
321            {
322              free(cur_str);
323              free(cur_ascii);
324              free(ascii);
325              return NULL;
326            }
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          }
331          cur_quoted = quote_buffer(cur_str, cur_str_len-1, 
332                                    subfield_special_chars);
333        }
334        else
335          cur_quoted = quote_string(cur_ascii, subfield_special_chars);
336
337        alen = snprintf(asciip, str_rem, "%s", cur_quoted);
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        {
346          if(str_rem > 0)
347          {
348            asciip[0] = '|';
349            asciip[1] = '\0';
350            asciip++;
351            str_rem--;
352          }
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;
362    free(cur_str);
363    free(cur_ascii);
364    return ascii;
365    break;
366
367  /* XXX: Dont know what to do with these yet, just print as binary... */
368  default:
369    fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
370   
371  case REG_NONE:
372  case REG_RESOURCE_LIST:
373  case REG_FULL_RESOURCE_DESCRIPTOR:
374  case REG_RESOURCE_REQUIREMENTS_LIST:
375
376  case REG_BINARY:
377    return quote_buffer(datap, len, common_special_chars);
378    break;
379  }
380
381  return NULL;
382}
383
384
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 */
389char** splitPath(const char* s)
390{
391  char** ret_val;
392  const char* cur = s;
393  char* next = NULL;
394  char* copy;
395  uint32 ret_cur = 0;
396
397  ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
398  if (ret_val == NULL)
399    return NULL;
400  ret_val[0] = NULL;
401
402  /* We return a well-formed, 0-length, path even when input is icky. */
403  if (s == NULL)
404    return ret_val;
405 
406  while((next = strchr(cur, '/')) != NULL)
407  {
408    if ((next-cur) > 0)
409    {
410      copy = (char*)malloc((next-cur+1)*sizeof(char));
411      if(copy == NULL)
412        bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
413         
414      memcpy(copy, cur, next-cur);
415      copy[next-cur] = '\0';
416      ret_val[ret_cur++] = copy;
417      if(ret_cur < (REGF_MAX_DEPTH+1+1))
418        ret_val[ret_cur] = NULL;
419      else
420        bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
421    }
422    cur = next+1;
423  }
424
425  /* Grab last element, if path doesn't end in '/'. */
426  if(strlen(cur) > 0)
427  {
428    copy = strdup(cur);
429    ret_val[ret_cur++] = copy;
430    if(ret_cur < (REGF_MAX_DEPTH+1+1))
431      ret_val[ret_cur] = NULL;
432    else
433      bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
434  }
435
436  return ret_val;
437}
438
439
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
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)
459{
460  const REGFI_ITER_POSITION* cur;
461  uint32 buf_left = 127;
462  uint32 buf_len = buf_left+1;
463  uint32 name_len = 0;
464  uint32 grow_amt;
465  char* buf;
466  char* new_buf;
467  char* name;
468  const char* cur_name;
469  void_stack_iterator* iter;
470 
471  buf = (char*)malloc((buf_len)*sizeof(char));
472  if (buf == NULL)
473    return NULL;
474  buf[0] = '\0';
475
476  iter = void_stack_iterator_new(i->key_positions);
477  if (iter == NULL)
478  {
479    free(buf);
480    return NULL;
481  }
482
483  /* skip root element */
484  if(void_stack_size(i->key_positions) < 1)
485  {
486    buf[0] = '/';
487    buf[1] = '\0';
488    return buf;
489  }
490  cur = void_stack_iterator_next(iter);
491
492  do
493  {
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
500    buf[buf_len-buf_left-1] = '/';
501    buf_left -= 1;
502    name = quote_string(cur_name, key_special_chars);
503    name_len = strlen(name);
504    if(name_len+1 > buf_left)
505    {
506      grow_amt = (uint32)(buf_len/2);
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    }
517    strncpy(buf+(buf_len-buf_left-1), name, name_len);
518    buf_left -= name_len;
519    buf[buf_len-buf_left-1] = '\0';
520    free(name);
521  } while(cur != NULL);
522
523  return buf;
524}
525
526
527void printValue(const REGF_VK_REC* vk, char* prefix)
528{
529  char* quoted_value = NULL;
530  char* quoted_name = NULL;
531  char* conv_error = NULL;
532  const char* str_type = NULL;
533  uint32 size;
534  uint8 tmp_buf[4];
535
536  /* Thanks Microsoft for making this process so straight-forward!!! */
537  /* XXX: this logic should be abstracted  and pushed into the regfi
538   *      interface.  This includes the size limits.
539   */
540  size = (vk->data_size & ~VK_DATA_IN_OFFSET);
541  if(vk->data_size & VK_DATA_IN_OFFSET)
542  {
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)
548    {
549      fprintf(stderr, "WARNING: value stored in offset larger than 4. "
550              "Truncating...\n");
551      size = 4;
552    }
553    quoted_value = data_to_ascii(tmp_buf, 4, vk->type, &conv_error);
554  }
555  else
556  {
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
562     */
563    if(size > VK_MAX_DATA_LENGTH)
564    {
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;
568    }
569
570    quoted_value = data_to_ascii(vk->data, vk->data_size, 
571                                 vk->type, &conv_error);
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   */
578  quoted_name = quote_string(vk->valuename, common_special_chars);
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  }
586
587  if(quoted_value == NULL)
588  {
589    if(conv_error == NULL)
590      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
591              "Memory allocation failure likely.\n", prefix, quoted_name);
592    else
593      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
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
601  str_type = regfi_type_val2str(vk->type);
602  if(print_security)
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  else
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
621  if(quoted_value != NULL)
622    free(quoted_value);
623  if(quoted_name != NULL)
624    free(quoted_name);
625  if(conv_error != NULL)
626    free(conv_error);
627}
628
629
630void printValueList(REGFI_ITERATOR* i, char* prefix)
631{
632  const REGF_VK_REC* value;
633
634  value = regfi_iterator_first_value(i);
635  while(value != NULL)
636  {
637    if(!type_filter_enabled || (value->type == type_filter))
638      printValue(value, prefix);
639    value = regfi_iterator_next_value(i);
640  }
641}
642
643
644void printKey(const REGF_NK_REC* k, char* full_path)
645{
646  static char empty_str[1] = "";
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
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  {
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);
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
674    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
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
687    printf("%s,KEY,,%s\n", full_path, mtime);
688}
689
690
691void printKeyTree(REGFI_ITERATOR* iter)
692{
693  const REGF_NK_REC* root = NULL;
694  const REGF_NK_REC* cur = NULL;
695  const REGF_NK_REC* sub = NULL;
696  char* path = NULL;
697  int key_type = regfi_type_str2val("KEY");
698  bool print_this = true;
699
700  root = cur = regfi_iterator_cur_key(iter);
701  sub = regfi_iterator_first_subkey(iter);
702 
703  if(root == NULL)
704    bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
705 
706  do
707  {
708    if(print_this)
709    {
710      path = iter2Path(iter);
711      if(path == NULL)
712        bailOut(EX_OSERR, "ERROR: Could not construct iterator's path.\n");
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);
720    }
721   
722    if(sub == NULL)
723    {
724      if(cur != root)
725      {
726        /* We're done with this sub-tree, going up and hitting other branches. */
727        if(!regfi_iterator_up(iter))
728          bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
729       
730        cur = regfi_iterator_cur_key(iter);
731        if(cur == NULL)
732          bailOut(EX_DATAERR, "ERROR: unexpected NULL for key.\n");
733       
734        sub = regfi_iterator_next_subkey(iter);
735      }
736      print_this = false;
737    }
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))
743        bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
744
745      cur = sub;
746      sub = regfi_iterator_first_subkey(iter);
747      print_this = true;
748    }
749  } while(!((cur == root) && (sub == NULL)));
750
751  if(print_verbose)
752    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
753}
754
755
756/* XXX: what if there is BOTH a value AND a key with that name?? */
757/*
758 * Returns 0 if path was not found.
759 * Returns 1 if path was found as value.
760 * Returns 2 if path was found as key.
761 * Returns less than 0 on other error.
762 */
763int retrievePath(REGFI_ITERATOR* iter, char** path)
764{
765  const REGF_VK_REC* value;
766  char* tmp_path_joined;
767  const char** tmp_path;
768  uint32 i;
769 
770  if(path == NULL)
771    return -1;
772
773  /* One extra for any value at the end, and one more for NULL */
774  tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
775  if(tmp_path == NULL)
776    return -2;
777
778  /* Strip any potential value name at end of path */
779  for(i=0; 
780      (path[i] != NULL) && (path[i+1] != NULL) 
781        && (i < REGF_MAX_DEPTH+1+1);
782      i++)
783    tmp_path[i] = path[i];
784
785  tmp_path[i] = NULL;
786
787  if(print_verbose)
788    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
789            path_filter);
790
791  /* Special check for '/' path filter */
792  if(path[0] == NULL)
793  {
794    if(print_verbose)
795      fprintf(stderr, "VERBOSE: Found final path element as root key.\n");
796    return 2;
797  }
798
799  if(!regfi_iterator_walk_path(iter, tmp_path))
800  {
801    free(tmp_path);
802    return 0;
803  }
804
805  if(regfi_iterator_find_value(iter, path[i]))
806  {
807    if(print_verbose)
808      fprintf(stderr, "VERBOSE: Found final path element as value.\n");
809
810    value = regfi_iterator_cur_value(iter);
811    tmp_path_joined = iter2Path(iter);
812
813    if((value == NULL) || (tmp_path_joined == NULL))
814      bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
815
816    printValue(value, tmp_path_joined);
817
818    free(tmp_path);
819    free(tmp_path_joined);
820    return 1;
821  }
822  else if(regfi_iterator_find_subkey(iter, path[i]))
823  {
824    if(print_verbose)
825      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
826
827    if(!regfi_iterator_down(iter))
828      bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
829
830    return 2;
831  }
832
833  if(print_verbose)
834    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
835
836  return 0;
837}
838
839
840static void usage(void)
841{
842  fprintf(stderr, "Usage: reglookup [-v] [-s]"
843          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
844          " <REGISTRY_FILE>\n");
845  fprintf(stderr, "Version: 0.4.0\n");
846  fprintf(stderr, "Options:\n");
847  fprintf(stderr, "\t-v\t sets verbose mode.\n");
848  fprintf(stderr, "\t-h\t enables header row. (default)\n");
849  fprintf(stderr, "\t-H\t disables header row.\n");
850  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
851  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
852  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
853  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
854  fprintf(stderr, "\n");
855}
856
857
858int main(int argc, char** argv)
859{
860  char** path = NULL;
861  REGF_FILE* f;
862  REGFI_ITERATOR* iter;
863  int retr_path_ret;
864  uint32 argi, arge;
865
866  /* Process command line arguments */
867  if(argc < 2)
868  {
869    usage();
870    bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
871  }
872 
873  arge = argc-1;
874  for(argi = 1; argi < arge; argi++)
875  {
876    if (strcmp("-p", argv[argi]) == 0)
877    {
878      if(++argi >= arge)
879      {
880        usage();
881        bailOut(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
882      }
883      if((path_filter = strdup(argv[argi])) == NULL)
884        bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
885
886      path_filter_enabled = true;
887    }
888    else if (strcmp("-t", argv[argi]) == 0)
889    {
890      if(++argi >= arge)
891      {
892        usage();
893        bailOut(EX_USAGE, "ERROR: '-t' option requires parameter.\n");
894      }
895      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
896      {
897        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
898        bailOut(EX_USAGE, "");
899      }
900      type_filter_enabled = true;
901    }
902    else if (strcmp("-h", argv[argi]) == 0)
903      print_header = true;
904    else if (strcmp("-H", argv[argi]) == 0)
905      print_header = false;
906    else if (strcmp("-s", argv[argi]) == 0)
907      print_security = true;
908    else if (strcmp("-S", argv[argi]) == 0)
909      print_security = false;
910    else if (strcmp("-v", argv[argi]) == 0)
911      print_verbose = true;
912    else
913    {
914      usage();
915      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
916      bailOut(EX_USAGE, "");
917    }
918  }
919  if((registry_file = strdup(argv[argi])) == NULL)
920    bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
921
922  f = regfi_open(registry_file);
923  if(f == NULL)
924  {
925    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
926    bailOut(EX_NOINPUT, "");
927  }
928
929  iter = regfi_iterator_new(f);
930  if(iter == NULL)
931    bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
932
933  if(print_header)
934  {
935    if(print_security)
936      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
937    else
938      printf("PATH,TYPE,VALUE,MTIME\n");
939  }
940
941  if(path_filter_enabled && path_filter != NULL)
942    path = splitPath(path_filter);
943
944  if(path != NULL)
945  {
946    retr_path_ret = retrievePath(iter, path);
947    freePath(path);
948
949    if(retr_path_ret == 0)
950      fprintf(stderr, "WARNING: specified path not found.\n");
951    else if (retr_path_ret == 2)
952      printKeyTree(iter);
953    else if(retr_path_ret < 0)
954    {
955      fprintf(stderr, "ERROR: retrievePath() returned %d.\n", 
956              retr_path_ret);
957      bailOut(EX_DATAERR,"ERROR: Unknown error occurred in retrieving path.\n");
958    }
959  }
960  else
961    printKeyTree(iter);
962
963  regfi_iterator_free(iter);
964  regfi_close(f);
965
966  return 0;
967}
Note: See TracBrowser for help on using the repository browser.