source: trunk/src/reglookup.c @ 53

Last change on this file since 53 was 53, checked in by tim, 19 years ago

Moved security descriptor parsing functions into regfio.c

Improved malloc() mode of failure.

Eliminated some warnings in regfio.c

  • Property svn:keywords set to Id
File size: 18.3 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 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 53 2005-09-04 21:04:58Z 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 "../include/regfio.h"
31#include "../include/void_stack.h"
32
33/* Globals, influenced by command line parameters */
34bool print_verbose = false;
35bool print_security = false;
36bool print_header = true;
37bool path_filter_enabled = false;
38bool type_filter_enabled = false;
39char* path_filter = NULL;
40int type_filter;
41char* registry_file = NULL;
42
43/* Other globals */
44const char* special_chars = ",\"\\";
45
46void bailOut(int code, char* message)
47{
48  fprintf(stderr, message);
49  exit(code);
50}
51
52
53/* Returns a newly malloc()ed string which contains original buffer,
54 * except for non-printable or special characters are quoted in hex
55 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
56 * character.  A null terminator is added, as only ascii, not binary,
57 * is returned.
58 */
59static char* quote_buffer(const unsigned char* str, 
60                          unsigned int len, const char* special)
61{
62  unsigned int i;
63  unsigned int num_written=0;
64  unsigned int out_len = sizeof(char)*len+1;
65  char* ret_val = malloc(out_len);
66
67  if(ret_val == NULL)
68    return NULL;
69
70  for(i=0; i<len; i++)
71  {
72    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
73    {
74      out_len += 3;
75      /* XXX: may not be the most efficient way of getting enough memory. */
76      ret_val = realloc(ret_val, out_len);
77      if(ret_val == NULL)
78        break;
79      num_written += snprintf(ret_val+num_written, (out_len)-num_written,
80                              "\\x%.2X", str[i]);
81    }
82    else
83      ret_val[num_written++] = str[i];
84  }
85  ret_val[num_written] = '\0';
86
87  return ret_val;
88}
89
90
91/* Returns a newly malloc()ed string which contains original string,
92 * except for non-printable or special characters are quoted in hex
93 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
94 * character.
95 */
96static char* quote_string(const char* str, const char* special)
97{
98  unsigned int len;
99
100  if(str == NULL)
101    return NULL;
102
103  len = strlen(str);
104  return quote_buffer((const unsigned char*)str, len, special);
105}
106
107
108/*
109 * Convert from UniCode to Ascii ... Does not take into account other lang
110 * Restrict by ascii_max if > 0
111 */
112static int uni_to_ascii(unsigned char *uni, unsigned char *ascii, 
113                        int ascii_max, int uni_max)
114{
115  int i = 0; 
116
117  while (i < ascii_max && (uni[i*2] || uni[i*2+1]))
118  {
119    if (uni_max > 0 && (i*2) >= uni_max) break;
120    ascii[i] = uni[i*2];
121    i++;
122  }
123  ascii[i] = '\0';
124
125  return i;
126}
127
128
129/*
130 * Convert a data value to a string for display
131 */
132static unsigned char* data_to_ascii(unsigned char *datap, int len, int type)
133{
134  unsigned char *asciip;
135  unsigned int i;
136  unsigned short num_nulls;
137  unsigned char* ascii;
138  unsigned char* cur_str;
139  unsigned char* cur_ascii;
140  char* cur_quoted;
141  unsigned int cur_str_len;
142  unsigned int ascii_max, cur_str_max;
143  unsigned int str_rem, cur_str_rem, alen;
144
145  switch (type) 
146  {
147  case REG_SZ:
148    ascii_max = sizeof(char)*len;
149    ascii = malloc(ascii_max+4);
150    if(ascii == NULL)
151      return NULL;
152   
153    /* XXX: This has to be fixed. It has to be UNICODE */
154    uni_to_ascii(datap, ascii, len, ascii_max);
155    cur_quoted = quote_string((char*)ascii, special_chars);
156    free(ascii);
157    return (unsigned char*)cur_quoted;
158    break;
159
160  case REG_EXPAND_SZ:
161    ascii_max = sizeof(char)*len;
162    ascii = malloc(ascii_max+2);
163    if(ascii == NULL)
164      return NULL;
165
166    uni_to_ascii(datap, ascii, len, ascii_max);
167    cur_quoted = quote_string((char*)ascii, special_chars);
168    free(ascii);
169    return (unsigned char*)cur_quoted;
170    break;
171
172  case REG_DWORD:
173    ascii_max = sizeof(char)*10;
174    ascii = malloc(ascii_max+1);
175    if(ascii == NULL)
176      return NULL;
177
178    if (*(int *)datap == 0)
179      snprintf((char*)ascii, ascii_max, "0");
180    else
181      snprintf((char*)ascii, ascii_max, "0x%x", *(int *)datap);
182    return ascii;
183    break;
184
185  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
186   *      redone with fewer malloc and better string concatenation.
187   */
188  case REG_MULTI_SZ:
189    ascii_max = sizeof(char)*len*4;
190    cur_str_max = sizeof(char)*len+1;
191    cur_str = malloc(cur_str_max);
192    cur_ascii = malloc(cur_str_max);
193    ascii = malloc(ascii_max+4);
194    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
195      return NULL;
196
197    /* Reads until it reaches 4 consecutive NULLs,
198     * which is two nulls in unicode, or until it reaches len, or until we
199     * run out of buffer.  The latter should never happen, but we shouldn't
200     * trust our file to have the right lengths/delimiters.
201     */
202    asciip = ascii;
203    num_nulls = 0;
204    str_rem = ascii_max;
205    cur_str_rem = cur_str_max;
206    cur_str_len = 0;
207
208    for(i=0; (i < len) && str_rem > 0; i++)
209    {
210      *(cur_str+cur_str_len) = *(datap+i);
211      if(*(cur_str+cur_str_len) == 0)
212        num_nulls++;
213      else
214        num_nulls = 0;
215      cur_str_len++;
216
217      if(num_nulls == 2)
218      {
219        uni_to_ascii(cur_str, cur_ascii, cur_str_max, 0);
220        cur_quoted = quote_string((char*)cur_ascii, ",|\"\\");
221        alen = snprintf((char*)asciip, str_rem, "%s", cur_quoted);
222        asciip += alen;
223        str_rem -= alen;
224        free(cur_quoted);
225
226        if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
227          break;
228        else
229        {
230          alen = snprintf((char*)asciip, str_rem, "%c", '|');
231          asciip += alen;
232          str_rem -= alen;
233          memset(cur_str, 0, cur_str_max);
234          cur_str_len = 0;
235          num_nulls = 0;
236          /* To eliminate leading nulls in subsequent strings. */
237          i++;
238        }
239      }
240    }
241    *asciip = 0;
242    free(cur_str);
243    free(cur_ascii);
244    return ascii;
245    break;
246
247  /* XXX: Dont know what to do with these yet, just print as binary... */
248  case REG_RESOURCE_LIST:
249  case REG_FULL_RESOURCE_DESCRIPTOR:
250  case REG_RESOURCE_REQUIREMENTS_LIST:
251
252  case REG_BINARY:
253    return (unsigned char*)quote_buffer(datap, len, special_chars);
254    break;
255
256  default:
257    return NULL;
258    break;
259  } 
260
261  return NULL;
262}
263
264
265void_stack* path2Stack(const char* s)
266{
267  void_stack* ret_val;
268  void_stack* rev_ret = void_stack_new(1024);
269  const char* cur = s;
270  char* next = NULL;
271  char* copy;
272
273  if (rev_ret == NULL)
274    return NULL;
275  if (s == NULL)
276    return rev_ret;
277 
278  while((next = strchr(cur, '/')) != NULL)
279  {
280    if ((next-cur) > 0)
281    {
282      copy = (char*)malloc((next-cur+1)*sizeof(char));
283      if(copy == NULL)
284        bailOut(2, "ERROR: Memory allocation problem.\n");
285         
286      memcpy(copy, cur, next-cur);
287      copy[next-cur] = '\0';
288      void_stack_push(rev_ret, copy);
289    }
290    cur = next+1;
291  }
292  if(strlen(cur) > 0)
293  {
294    copy = strdup(cur);
295    void_stack_push(rev_ret, copy);
296  }
297
298  ret_val = void_stack_copy_reverse(rev_ret);
299  void_stack_destroy(rev_ret);
300
301  return ret_val;
302}
303
304
305char* stack2Path(void_stack* nk_stack)
306{
307  const REGF_NK_REC* cur;
308  uint32 buf_left = 127;
309  uint32 buf_len = buf_left+1;
310  uint32 name_len = 0;
311  uint32 grow_amt;
312  char* buf; 
313  char* new_buf;
314  void_stack_iterator* iter;
315 
316  buf = (char*)malloc((buf_len)*sizeof(char));
317  if (buf == NULL)
318    return NULL;
319  buf[0] = '/';
320  buf[1] = '\0';
321
322  iter = void_stack_iterator_new(nk_stack);
323  if (iter == NULL)
324  {
325    free(buf);
326    return NULL;
327  }
328
329  /* skip root element */
330  cur = void_stack_iterator_next(iter);
331
332  while((cur = void_stack_iterator_next(iter)) != NULL)
333  {
334    buf[buf_len-buf_left-1] = '/';
335    buf_left -= 1;
336    name_len = strlen(cur->keyname);
337    if(name_len+1 > buf_left)
338    {
339      grow_amt = (uint32)(buf_len/2);
340      buf_len += name_len+1+grow_amt-buf_left;
341      if((new_buf = realloc(buf, buf_len)) == NULL)
342      {
343        free(buf);
344        free(iter);
345        return NULL;
346      }
347      buf = new_buf;
348      buf_left = grow_amt + name_len + 1;
349    }
350    strncpy(buf+(buf_len-buf_left-1), cur->keyname, name_len);
351    buf_left -= name_len;
352    buf[buf_len-buf_left-1] = '\0';
353  }
354
355  return buf;
356}
357
358
359void printValue(REGF_VK_REC* vk, char* prefix)
360{
361  uint32 size;
362  uint8 tmp_buf[4];
363  unsigned char* quoted_value;
364  char* quoted_prefix;
365  char* quoted_name;
366
367  /* Thanks Microsoft for making this process so straight-forward!!! */
368  size = (vk->data_size & ~VK_DATA_IN_OFFSET);
369  if(vk->data_size & VK_DATA_IN_OFFSET)
370  {
371    tmp_buf[0] = (uint8)((vk->data_off >> 3) & 0xFF);
372    tmp_buf[1] = (uint8)((vk->data_off >> 2) & 0xFF);
373    tmp_buf[2] = (uint8)((vk->data_off >> 1) & 0xFF);
374    tmp_buf[3] = (uint8)(vk->data_off & 0xFF);
375    if(size > 4)
376      size = 4;
377    quoted_value = data_to_ascii(tmp_buf, 4, vk->type);
378  }
379  else
380  {
381    /* XXX: This is a safety hack.  No data fields have yet been found
382     * larger, but length limits are probably better got from fields
383     * in the registry itself, within reason.
384     */
385    if(size > 16384)
386    {
387      fprintf(stderr, "WARNING: key size %d larger than "
388              "16384, truncating...\n", size);
389      size = 16384;
390    }
391    quoted_value = data_to_ascii(vk->data, vk->data_size, vk->type);
392  }
393 
394  /* XXX: Sometimes value names can be NULL in registry.  Need to
395   *      figure out why and when, and generate the appropriate output
396   *      for that condition.
397   */
398  quoted_prefix = quote_string(prefix, special_chars);
399  quoted_name = quote_string(vk->valuename, special_chars);
400 
401  if(print_security)
402    printf("%s/%s,%s,%s,,,,,\n", quoted_prefix, quoted_name,
403           regfio_type_val2str(vk->type), quoted_value);
404  else
405    printf("%s/%s,%s,%s,\n", quoted_prefix, quoted_name,
406           regfio_type_val2str(vk->type), quoted_value);
407 
408  if(quoted_value != NULL)
409    free(quoted_value);
410  if(quoted_prefix != NULL)
411    free(quoted_prefix);
412  if(quoted_name != NULL)
413    free(quoted_name);
414}
415
416
417void printValueList(REGF_NK_REC* nk, char* prefix)
418{
419  uint32 i;
420 
421  for(i=0; i < nk->num_values; i++)
422    if(!type_filter_enabled || (nk->values[i].type == type_filter))
423      printValue(&nk->values[i], prefix);
424}
425
426
427void printKey(REGF_NK_REC* k, char* full_path)
428{
429  static char empty_str[1] = "";
430  char* owner = NULL;
431  char* group = NULL;
432  char* sacl = NULL;
433  char* dacl = NULL;
434  char mtime[20];
435  time_t tmp_time[1];
436  struct tm* tmp_time_s = NULL;
437
438  *tmp_time = nt_time_to_unix(&k->mtime);
439  tmp_time_s = gmtime(tmp_time);
440  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
441
442  if(print_security)
443  {
444    owner = regfio_get_owner(k->sec_desc->sec_desc);
445    group = regfio_get_group(k->sec_desc->sec_desc);
446    sacl = regfio_get_sacl(k->sec_desc->sec_desc);
447    dacl = regfio_get_dacl(k->sec_desc->sec_desc);
448    if(owner == NULL)
449      owner = empty_str;
450    if(group == NULL)
451      group = empty_str;
452    if(sacl == NULL)
453      sacl = empty_str;
454    if(dacl == NULL)
455      dacl = empty_str;
456
457    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
458           owner, group, sacl, dacl);
459
460    if(owner != empty_str)
461      free(owner);
462    if(group != empty_str)
463      free(group);
464    if(sacl != empty_str)
465      free(sacl);
466    if(dacl != empty_str)
467      free(dacl);
468  }
469  else
470    printf("%s,KEY,,%s\n", full_path, mtime);
471}
472
473
474/* XXX: this function is god-awful.  Needs to be re-designed. */
475void printKeyTree(REGF_FILE* f, void_stack* nk_stack, char* prefix)
476{
477  REGF_NK_REC* cur = NULL;
478  REGF_NK_REC* sub = NULL;
479  char* path = NULL;
480  char* val_path = NULL;
481
482  int key_type = regfio_type_str2val("KEY");
483 
484  if((cur = (REGF_NK_REC*)void_stack_cur(nk_stack)) != NULL)
485  {
486    cur->subkey_index = 0;
487    path = stack2Path(nk_stack);
488   
489
490    val_path = (char*)malloc(strlen(prefix)+strlen(path)+1);
491    sprintf(val_path, "%s%s", prefix, path);
492    if(!type_filter_enabled || (key_type == type_filter))
493      printKey(cur, val_path);
494
495    if(!type_filter_enabled || (key_type != type_filter))
496      printValueList(cur, val_path);
497    if(val_path != NULL)
498      free(val_path);
499    while((cur = (REGF_NK_REC*)void_stack_cur(nk_stack)) != NULL)
500    {
501      if((sub = regfio_fetch_subkey(f, cur)) != NULL)
502      {
503        sub->subkey_index = 0;
504        void_stack_push(nk_stack, sub);
505        path = stack2Path(nk_stack);
506        if(path != NULL)
507        {
508          val_path = (char*)malloc(strlen(prefix)+strlen(path)+1);
509          sprintf(val_path, "%s%s", prefix, path);
510          if(!type_filter_enabled || (key_type == type_filter))
511            printKey(sub, val_path);
512          if(!type_filter_enabled || (key_type != type_filter))
513            printValueList(sub, val_path);
514          if(val_path != NULL)
515            free(val_path);
516        }
517      }
518      else
519      {
520        cur = void_stack_pop(nk_stack);
521        /* XXX: This is just a shallow free.  Need to write deep free
522         * routines to replace the Samba code for this.
523         */ 
524        if(cur != NULL)
525          free(cur);
526      }
527    }
528  }
529}
530
531
532/*
533 * Returns 0 if path was found.
534 * Returns 1 if path was not found.
535 * Returns less than 0 on other error.
536 */
537int retrievePath(REGF_FILE* f, void_stack* nk_stack,
538                 void_stack* path_stack)
539{
540  REGF_NK_REC* sub = NULL; 
541  REGF_NK_REC* cur = NULL;
542  void_stack* sub_nk_stack;
543  char* prefix;
544  uint32 prefix_len;
545  char* cur_str = NULL;
546  char* path = NULL;
547  bool found_cur = true;
548  uint32 i;
549  uint16 path_depth;
550  if(path_stack == NULL)
551    return -1;
552
553  path_depth = void_stack_size(path_stack);
554  if(path_depth < 1)
555    return -2;
556
557  if(void_stack_size(nk_stack) < 1)
558    return -3;
559  cur = (REGF_NK_REC*)void_stack_cur(nk_stack);
560
561  while(void_stack_size(path_stack) > 1)
562  {
563    /* Search key records only */
564    cur_str = (char*)void_stack_pop(path_stack);
565
566    found_cur = false;
567    while(!found_cur &&
568          (sub = regfio_fetch_subkey(f, cur)) != NULL)
569    {
570      if(strcasecmp(sub->keyname, cur_str) == 0)
571      {
572        cur = sub;
573        void_stack_push(nk_stack, sub);
574        found_cur = true;
575      }
576    }
577    free(cur_str);
578
579    if(!found_cur)
580      return 1;
581  }
582
583  /* Last round, search value and key records */
584  cur_str = (char*)void_stack_pop(path_stack);
585
586  for(i=0; (i < cur->num_values); i++)
587  {
588    /* XXX: Not sure when/why this can be NULL, but it's happened. */
589    if(sub->values[i].valuename != NULL 
590       && strcasecmp(sub->values[i].valuename, cur_str) == 0)
591    {
592      path = stack2Path(nk_stack);
593      printValue(&sub->values[i], path);
594      if(path != NULL)
595        free(path);
596      return 0;
597    }
598  }
599
600  while((sub = regfio_fetch_subkey(f, cur)) != NULL)
601  {
602    if(strcasecmp(sub->keyname, cur_str) == 0)
603    {
604      sub_nk_stack = void_stack_new(1024);
605      void_stack_push(sub_nk_stack, sub);
606      prefix = stack2Path(nk_stack);
607      prefix_len = strlen(prefix);
608      prefix = realloc(prefix, prefix_len+strlen(sub->keyname)+2);
609      if(prefix == NULL)
610        return -1;
611      strcat(prefix, "/");
612      strcat(prefix, sub->keyname);
613      printKeyTree(f, sub_nk_stack, prefix);
614      return 0;
615    }
616  }
617
618  return 1;
619}
620
621
622static void usage(void)
623{
624  fprintf(stderr, "Usage: readreg [-v] [-s]"
625          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
626          " <REGISTRY_FILE>\n");
627  /* XXX: replace version string with Subversion property? */
628  fprintf(stderr, "Version: 0.2.1\n");
629  fprintf(stderr, "Options:\n");
630  fprintf(stderr, "\t-v\t sets verbose mode.\n");
631  fprintf(stderr, "\t-h\t enables header row. (default)\n");
632  fprintf(stderr, "\t-H\t disables header row.\n");
633  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
634  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
635  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
636  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
637  fprintf(stderr, "\n");
638}
639
640
641int main(int argc, char** argv)
642{
643  void_stack* nk_stack;
644  void_stack* path_stack;
645  REGF_FILE* f;
646  REGF_NK_REC* root;
647  int retr_path_ret;
648  uint32 argi, arge;
649
650  /* Process command line arguments */
651  if(argc < 2)
652  {
653    usage();
654    bailOut(1, "ERROR: Requires at least one argument.\n");
655  }
656 
657  arge = argc-1;
658  for(argi = 1; argi < arge; argi++)
659  {
660    if (strcmp("-p", argv[argi]) == 0)
661    {
662      if(++argi >= arge)
663      {
664        usage();
665        bailOut(1, "ERROR: '-p' option requires parameter.\n");
666      }
667      if((path_filter = strdup(argv[argi])) == NULL)
668        bailOut(2, "ERROR: Memory allocation problem.\n");
669
670      path_filter_enabled = true;
671    }
672    else if (strcmp("-t", argv[argi]) == 0)
673    {
674      if(++argi >= arge)
675      {
676        usage();
677        bailOut(1, "ERROR: '-t' option requires parameter.\n");
678      }
679      if((type_filter = regfio_type_str2val(argv[argi])) == 0)
680      {
681        fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
682        bailOut(1, "");
683      }
684
685      type_filter_enabled = true;
686    }
687    else if (strcmp("-h", argv[argi]) == 0)
688      print_header = true;
689    else if (strcmp("-H", argv[argi]) == 0)
690      print_header = false;
691    else if (strcmp("-s", argv[argi]) == 0)
692      print_security = true;
693    else if (strcmp("-S", argv[argi]) == 0)
694      print_security = false;
695    else if (strcmp("-v", argv[argi]) == 0)
696      print_verbose = true;
697    else
698    {
699      usage();
700      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
701      bailOut(1, "");
702    }
703  }
704  if((registry_file = strdup(argv[argi])) == NULL)
705    bailOut(2, "ERROR: Memory allocation problem.\n");
706
707  f = regfio_open(registry_file);
708  if(f == NULL)
709  {
710    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
711    bailOut(3, "");
712  }
713
714  root = regfio_rootkey(f);
715  nk_stack = void_stack_new(1024);
716
717  if(void_stack_push(nk_stack, root))
718  {
719    if(print_header)
720    {
721      if(print_security)
722        printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
723      else
724        printf("PATH,TYPE,VALUE,MTIME\n");
725    }
726
727    path_stack = path2Stack(path_filter);
728    if(void_stack_size(path_stack) < 1)
729      printKeyTree(f, nk_stack, "");
730    else
731    {
732      retr_path_ret = retrievePath(f, nk_stack, path_stack);
733      if(retr_path_ret == 1)
734        fprintf(stderr, "WARNING: specified path not found.\n");
735      else if(retr_path_ret != 0)
736        bailOut(4, "ERROR:\n");
737    }
738  }
739  else
740    bailOut(2, "ERROR: Memory allocation problem.\n");
741
742  void_stack_destroy_deep(nk_stack);
743  regfio_close(f);
744
745  return 0;
746}
Note: See TracBrowser for help on using the repository browser.