source: trunk/src/reglookup.c @ 79

Last change on this file since 79 was 79, checked in by tim, 17 years ago

renamed regfio to regfi, since we aren't doing any output to the registry.

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