source: trunk/src/reglookup.c @ 81

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

Finished incorporating changes to reglookup to work with new regfi interface.

Compiles now, but is minimally tested.

  • Property svn:keywords set to Id
File size: 23.4 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 81 2007-01-17 16:47:39Z 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
384char** splitPath(const char* s)
385{
386  char** ret_val;
387  const char* cur = s;
388  char* next = NULL;
389  char* copy;
390  uint32 ret_cur = 0;
391
392  ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
393  if (ret_val == NULL)
394    return NULL;
395  ret_val[0] = NULL;
396
397  /* We return a well-formed, 0-length, path even when input is icky. */
398  if (s == NULL)
399    return ret_val;
400 
401  while((next = strchr(cur, '/')) != NULL)
402  {
403    if ((next-cur) > 0)
404    {
405      copy = (char*)malloc((next-cur+1)*sizeof(char));
406      if(copy == NULL)
407        bailOut(2, "ERROR: Memory allocation problem.\n");
408         
409      memcpy(copy, cur, next-cur);
410      copy[next-cur] = '\0';
411      ret_val[ret_cur++] = copy;
412      if(ret_cur < (REGF_MAX_DEPTH+1+1))
413        ret_val[ret_cur] = NULL;
414      else
415        bailOut(2, "ERROR: Registry maximum depth exceeded.\n");
416    }
417    cur = next+1;
418  }
419
420  /* Grab last element, if path doesn't end in '/'. */
421  if(strlen(cur) > 0)
422  {
423    copy = strdup(cur);
424    ret_val[ret_cur++] = copy;
425    if(ret_cur < (REGF_MAX_DEPTH+1+1))
426      ret_val[ret_cur] = NULL;
427    else
428      bailOut(2, "ERROR: Registry maximum depth exceeded.\n");
429  }
430
431  return ret_val;
432}
433
434
435/* Returns a quoted path from an iterator's stack */
436/* XXX: Some way should be found to integrate this into regfi's API
437 *      The problem is that the escaping is sorta reglookup-specific.
438 */
439char* iter2Path(REGFI_ITERATOR* i)
440{
441  const REGFI_ITER_POSITION* cur;
442  uint32 buf_left = 127;
443  uint32 buf_len = buf_left+1;
444  uint32 name_len = 0;
445  uint32 grow_amt;
446  char* buf;
447  char* new_buf;
448  char* name;
449  const char* cur_name;
450  void_stack_iterator* iter;
451 
452  buf = (char*)malloc((buf_len)*sizeof(char));
453  if (buf == NULL)
454    return NULL;
455  buf[0] = '\0';
456
457  iter = void_stack_iterator_new(i->key_positions);
458  if (iter == NULL)
459  {
460    free(buf);
461    return NULL;
462  }
463
464  /* skip root element */
465  if(void_stack_size(i->key_positions) < 1)
466  {
467    buf[0] = '/';
468    buf[1] = '\0';
469    return buf;
470  }
471  cur = void_stack_iterator_next(iter);
472
473  do
474  {
475    cur = void_stack_iterator_next(iter);
476    if (cur == NULL)
477      cur_name = i->cur_key->keyname;
478    else
479      cur_name = cur->nk->keyname;
480
481    buf[buf_len-buf_left-1] = '/';
482    buf_left -= 1;
483    name = quote_string(cur_name, key_special_chars);
484    name_len = strlen(name);
485    if(name_len+1 > buf_left)
486    {
487      grow_amt = (uint32)(buf_len/2);
488      buf_len += name_len+1+grow_amt-buf_left;
489      if((new_buf = realloc(buf, buf_len)) == NULL)
490      {
491        free(buf);
492        free(iter);
493        return NULL;
494      }
495      buf = new_buf;
496      buf_left = grow_amt + name_len + 1;
497    }
498    strncpy(buf+(buf_len-buf_left-1), name, name_len);
499    buf_left -= name_len;
500    buf[buf_len-buf_left-1] = '\0';
501    free(name);
502  } while(cur != NULL);
503
504  return buf;
505}
506
507
508void printValue(REGF_VK_REC* vk, char* prefix)
509{
510  char* quoted_value = NULL;
511  char* quoted_name = NULL;
512  char* conv_error = NULL;
513  const char* str_type = NULL;
514  uint32 size;
515  uint8 tmp_buf[4];
516
517  /* Thanks Microsoft for making this process so straight-forward!!! */
518  size = (vk->data_size & ~VK_DATA_IN_OFFSET);
519  if(vk->data_size & VK_DATA_IN_OFFSET)
520  {
521    tmp_buf[0] = (uint8)((vk->data_off >> 3) & 0xFF);
522    tmp_buf[1] = (uint8)((vk->data_off >> 2) & 0xFF);
523    tmp_buf[2] = (uint8)((vk->data_off >> 1) & 0xFF);
524    tmp_buf[3] = (uint8)(vk->data_off & 0xFF);
525    if(size > 4)
526      /* XXX: should we kick out a warning here?  If it is in the
527       *      offset and longer than four, file could be corrupt
528       *      or malicious... */
529      size = 4;
530    quoted_value = data_to_ascii(tmp_buf, 4, vk->type, &conv_error);
531  }
532  else
533  {
534    /* Microsoft's documentation indicates that "available memory" is
535     * the limit on value sizes.  Annoying.  We limit it to 1M which
536     * should rarely be exceeded, unless the file is corrupt or
537     * malicious. For more info, see:
538     *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
539     */
540    if(size > VK_MAX_DATA_LENGTH)
541    {
542      fprintf(stderr, "WARNING: value data size %d larger than "
543              "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
544      size = VK_MAX_DATA_LENGTH;
545    }
546
547    quoted_value = data_to_ascii(vk->data, vk->data_size, 
548                                 vk->type, &conv_error);
549  }
550 
551  /* XXX: Sometimes value names can be NULL in registry.  Need to
552   *      figure out why and when, and generate the appropriate output
553   *      for that condition.
554   */
555  quoted_name = quote_string(vk->valuename, common_special_chars);
556
557  if(quoted_value == NULL)
558  {
559    if(conv_error == NULL)
560      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
561              "Memory allocation failure likely.\n", prefix, quoted_name);
562    else
563      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
564              "Returned error: %s\n", prefix, quoted_name, conv_error);
565  }
566  /* XXX: should these always be printed? */
567  else if(conv_error != NULL && print_verbose)
568      fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
569              "warning returned: %s\n", prefix, quoted_name, conv_error);
570
571  str_type = regfi_type_val2str(vk->type);
572  if(print_security)
573  {
574    if(str_type == NULL)
575      printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
576             vk->type, quoted_value);
577    else
578      printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
579             str_type, quoted_value);
580  }
581  else
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  }
590
591  if(quoted_value != NULL)
592    free(quoted_value);
593  if(quoted_name != NULL)
594    free(quoted_name);
595  if(conv_error != NULL)
596    free(conv_error);
597}
598
599
600void printValueList(REGFI_ITERATOR* i, char* prefix)
601{
602  REGF_VK_REC* value;
603
604  value = regfi_iterator_first_value(i);
605  while(value != NULL)
606  {
607    if(!type_filter_enabled || (value->type == type_filter))
608      printValue(value, prefix);
609    value = regfi_iterator_next_value(i);
610  }
611}
612
613
614void printKey(REGF_NK_REC* k, char* full_path)
615{
616  static char empty_str[1] = "";
617  char* owner = NULL;
618  char* group = NULL;
619  char* sacl = NULL;
620  char* dacl = NULL;
621  char mtime[20];
622  time_t tmp_time[1];
623  struct tm* tmp_time_s = NULL;
624
625  *tmp_time = nt_time_to_unix(&k->mtime);
626  tmp_time_s = gmtime(tmp_time);
627  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
628
629  if(print_security)
630  {
631    owner = regfi_get_owner(k->sec_desc->sec_desc);
632    group = regfi_get_group(k->sec_desc->sec_desc);
633    sacl = regfi_get_sacl(k->sec_desc->sec_desc);
634    dacl = regfi_get_dacl(k->sec_desc->sec_desc);
635    if(owner == NULL)
636      owner = empty_str;
637    if(group == NULL)
638      group = empty_str;
639    if(sacl == NULL)
640      sacl = empty_str;
641    if(dacl == NULL)
642      dacl = empty_str;
643
644    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
645           owner, group, sacl, dacl);
646
647    if(owner != empty_str)
648      free(owner);
649    if(group != empty_str)
650      free(group);
651    if(sacl != empty_str)
652      free(sacl);
653    if(dacl != empty_str)
654      free(dacl);
655  }
656  else
657    printf("%s,KEY,,%s\n", full_path, mtime);
658}
659
660
661void printKeyTree(REGFI_ITERATOR* iter)
662{
663  REGF_NK_REC* root = NULL;
664  REGF_NK_REC* cur = NULL;
665  REGF_NK_REC* sub = NULL;
666  char* path = NULL;
667  int key_type = regfi_type_str2val("KEY");
668  bool print_this = true;
669
670  root = cur = regfi_iterator_cur_key(iter);
671  sub = regfi_iterator_first_subkey(iter);
672 
673  if(root == NULL)
674    bailOut(3, "ERROR: root cannot be NULL.\n");
675 
676  do
677  {
678    if(print_this)
679    {
680      path = iter2Path(iter);
681      if(path == NULL)
682        bailOut(2, "ERROR: Could not construct iterator's path.\n");
683     
684      if(!type_filter_enabled || (key_type == type_filter))
685        printKey(cur, path);
686      if(!type_filter_enabled || (key_type != type_filter))
687        printValueList(iter, path);
688     
689      free(path);
690    }
691   
692    if(sub == NULL)
693    {
694      if(cur != root)
695      {
696        /* We're done with this sub-tree, going up and hitting other branches. */
697        if(!regfi_iterator_up(iter))
698          bailOut(3, "ERROR: could not traverse iterator upward.\n");
699       
700        cur = regfi_iterator_cur_key(iter);
701        if(cur == NULL)
702          bailOut(3, "ERROR: unexpected NULL for key.\n");
703       
704        sub = regfi_iterator_next_subkey(iter);
705      }
706      print_this = false;
707    }
708    else
709    { /* We have unexplored sub-keys. 
710       * Let's move down and print this first sub-tree out.
711       */
712      if(!regfi_iterator_down(iter))
713        bailOut(3, "ERROR: could not traverse iterator downward.\n");
714
715      cur = sub;
716      sub = regfi_iterator_first_subkey(iter);
717      print_this = true;
718    }
719  } while(!((cur == root) && (sub == NULL)));
720
721  if(print_verbose)
722    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
723}
724
725
726/*
727 * Returns 0 if path was not found.
728 * Returns 1 if path was found as value.
729 * Returns 2 if path was found as key.
730 * Returns less than 0 on other error.
731 */
732int retrievePath(REGFI_ITERATOR* iter, char** path)
733{
734  REGF_VK_REC* value;
735  char* tmp_path_joined;
736  const char** tmp_path;
737  uint32 i;
738 
739  if(path == NULL)
740    return -1;
741
742  /* One extra for any value at the end, and one more for NULL */
743  tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
744  if(tmp_path == NULL)
745    return -2;
746
747  /* Strip any potential value name at end of path */
748  for(i=0; 
749      (path[i] != NULL) && (path[i+1] != NULL) 
750        && (i < REGF_MAX_DEPTH+1+1);
751      i++)
752    tmp_path[i] = path[i];
753
754  tmp_path[i] = NULL;
755
756  if(print_verbose)
757    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
758            path_filter);
759
760  if(!regfi_iterator_walk_path(iter, tmp_path))
761  {
762    free(tmp_path);
763    return 0;
764  }
765
766  if(regfi_iterator_find_value(iter, path[i]))
767  {
768    if(print_verbose)
769      fprintf(stderr, "VERBOSE: Found final path element as value.\n");
770
771    value = regfi_iterator_cur_value(iter);
772    tmp_path_joined = iter2Path(iter);
773
774    if((value == NULL) || (tmp_path_joined == NULL))
775      bailOut(2, "ERROR: Unexpected error before printValue.\n");
776
777    printValue(value, tmp_path_joined);
778
779    free(tmp_path);
780    free(tmp_path_joined);
781    return 1;
782  }
783  else if(regfi_iterator_find_subkey(iter, path[i]))
784  {
785    if(print_verbose)
786      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
787    return 2;
788  }
789
790  if(print_verbose)
791    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
792
793  return 0;
794}
795
796
797static void usage(void)
798{
799  fprintf(stderr, "Usage: reglookup [-v] [-s]"
800          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
801          " <REGISTRY_FILE>\n");
802  fprintf(stderr, "Version: 0.3.0\n");
803  fprintf(stderr, "Options:\n");
804  fprintf(stderr, "\t-v\t sets verbose mode.\n");
805  fprintf(stderr, "\t-h\t enables header row. (default)\n");
806  fprintf(stderr, "\t-H\t disables header row.\n");
807  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
808  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
809  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
810  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
811  fprintf(stderr, "\n");
812}
813
814
815int main(int argc, char** argv)
816{
817  char** path = NULL;
818  REGF_FILE* f;
819  REGFI_ITERATOR* iter;
820  int retr_path_ret;
821  uint32 argi, arge;
822
823  /* Process command line arguments */
824  if(argc < 2)
825  {
826    usage();
827    bailOut(1, "ERROR: Requires at least one argument.\n");
828  }
829 
830  arge = argc-1;
831  for(argi = 1; argi < arge; argi++)
832  {
833    if (strcmp("-p", argv[argi]) == 0)
834    {
835      if(++argi >= arge)
836      {
837        usage();
838        bailOut(1, "ERROR: '-p' option requires parameter.\n");
839      }
840      if((path_filter = strdup(argv[argi])) == NULL)
841        bailOut(2, "ERROR: Memory allocation problem.\n");
842
843      path_filter_enabled = true;
844    }
845    else if (strcmp("-t", argv[argi]) == 0)
846    {
847      if(++argi >= arge)
848      {
849        usage();
850        bailOut(1, "ERROR: '-t' option requires parameter.\n");
851      }
852      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
853      {
854        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
855        bailOut(1, "");
856      }
857      type_filter_enabled = true;
858    }
859    else if (strcmp("-h", argv[argi]) == 0)
860      print_header = true;
861    else if (strcmp("-H", argv[argi]) == 0)
862      print_header = false;
863    else if (strcmp("-s", argv[argi]) == 0)
864      print_security = true;
865    else if (strcmp("-S", argv[argi]) == 0)
866      print_security = false;
867    else if (strcmp("-v", argv[argi]) == 0)
868      print_verbose = true;
869    else
870    {
871      usage();
872      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
873      bailOut(1, "");
874    }
875  }
876  if((registry_file = strdup(argv[argi])) == NULL)
877    bailOut(2, "ERROR: Memory allocation problem.\n");
878
879  f = regfi_open(registry_file);
880  if(f == NULL)
881  {
882    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
883    bailOut(3, "");
884  }
885
886  iter = regfi_iterator_new(f);
887  if(iter == NULL)
888    bailOut(3, "ERROR: Couldn't create registry iterator.\n");
889
890  if(print_header)
891  {
892    if(print_security)
893      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
894    else
895      printf("PATH,TYPE,VALUE,MTIME\n");
896  }
897
898  if(path_filter_enabled && path_filter != NULL)
899    path = splitPath(path_filter);
900
901  if(path != NULL)
902  {
903    retr_path_ret = retrievePath(iter, path);
904    if(retr_path_ret == 0)
905      fprintf(stderr, "WARNING: specified path not found.\n");
906    else if (retr_path_ret == 2)
907      printKeyTree(iter);
908    else if(retr_path_ret != 0)
909      bailOut(4, "ERROR: Unknown error occurred in retrieving path.\n");
910  }
911  else
912    printKeyTree(iter);
913
914  regfi_iterator_free(iter);
915  regfi_close(f);
916
917  return 0;
918}
Note: See TracBrowser for help on using the repository browser.