source: trunk/lib/regfi.c @ 139

Last change on this file since 139 was 139, checked in by tim, 15 years ago

rewrote subkey list parsing code to include more organized recursion and parsing as well as improved sanity checking

  • Property svn:keywords set to Id
File size: 60.9 KB
Line 
1/*
2 * Branched from Samba project Subversion repository, version #7470:
3 *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
4 *
5 * Windows NT (and later) registry parsing library
6 *
7 * Copyright (C) 2005-2009 Timothy D. Morgan
8 * Copyright (C) 2005 Gerald (Jerry) Carter
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 3 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
22 *
23 * $Id: regfi.c 139 2009-02-09 03:06:16Z tim $
24 */
25
26#include "../include/regfi.h"
27
28
29/* Registry types mapping */
30const unsigned int regfi_num_reg_types = 12;
31static const char* regfi_type_names[] =
32  {"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK",
33   "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
34
35
36
37/******************************************************************************
38 ******************************************************************************/
39void regfi_add_message(REGFI_FILE* file, uint16 msg_type, const char* fmt, ...)
40{
41  /* XXX: This function is not particularly efficient,
42   *      but then it is mostly used during errors.
43   */
44  uint32 buf_size, buf_used;
45  char* new_msg;
46  va_list args;
47
48  if((file->msg_mask & msg_type) != 0)
49  {
50    if(file->last_message == NULL)
51      buf_used = 0;
52    else
53      buf_used = strlen(file->last_message);
54   
55    buf_size = buf_used+strlen(fmt)+160;
56    new_msg = realloc(file->last_message, buf_size);
57    if(new_msg == NULL)
58      /* XXX: should we report this? */
59      return;
60
61    switch (msg_type)
62    {
63    case REGFI_MSG_INFO:
64      strcpy(new_msg+buf_used, "INFO: ");
65      buf_used += 6;
66      break;
67    case REGFI_MSG_WARN:
68      strcpy(new_msg+buf_used, "WARN: ");
69      buf_used += 6;
70      break;
71    case REGFI_MSG_ERROR:
72      strcpy(new_msg+buf_used, "ERROR: ");
73      buf_used += 7;
74      break;
75    }
76
77    va_start(args, fmt);
78    vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
79    va_end(args);
80    strncat(new_msg, "\n", buf_size-1);
81   
82    file->last_message = new_msg;
83  }
84}
85
86
87/******************************************************************************
88 ******************************************************************************/
89char* regfi_get_messages(REGFI_FILE* file)
90{
91  char* ret_val = file->last_message;
92  file->last_message = NULL;
93
94  return ret_val;
95}
96
97
98void regfi_set_message_mask(REGFI_FILE* file, uint16 mask)
99{
100  file->msg_mask = mask;
101}
102
103
104/* Returns NULL on error */
105const char* regfi_type_val2str(unsigned int val)
106{
107  if(val == REG_KEY)
108    return "KEY";
109 
110  if(val >= regfi_num_reg_types)
111    return NULL;
112 
113  return regfi_type_names[val];
114}
115
116
117/* Returns -1 on error */
118int regfi_type_str2val(const char* str)
119{
120  int i;
121
122  if(strcmp("KEY", str) == 0)
123    return REG_KEY;
124
125  for(i=0; i < regfi_num_reg_types; i++)
126    if (strcmp(regfi_type_names[i], str) == 0) 
127      return i;
128
129  if(strcmp("DWORD_LE", str) == 0)
130    return REG_DWORD_LE;
131
132  return -1;
133}
134
135
136/* Security descriptor formatting functions  */
137
138const char* regfi_ace_type2str(uint8 type)
139{
140  static const char* map[7] 
141    = {"ALLOW", "DENY", "AUDIT", "ALARM", 
142       "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"};
143  if(type < 7)
144    return map[type];
145  else
146    /* XXX: would be nice to return the unknown integer value. 
147     *      However, as it is a const string, it can't be free()ed later on,
148     *      so that would need to change.
149     */
150    return "UNKNOWN";
151}
152
153
154/* XXX: need a better reference on the meaning of each flag. */
155/* For more info, see:
156 *   http://msdn2.microsoft.com/en-us/library/aa772242.aspx
157 */
158char* regfi_ace_flags2str(uint8 flags)
159{
160  static const char* flag_map[32] = 
161    { "OI", /* Object Inherit */
162      "CI", /* Container Inherit */
163      "NP", /* Non-Propagate */
164      "IO", /* Inherit Only */
165      "IA", /* Inherited ACE */
166      NULL,
167      NULL,
168      NULL,
169    };
170
171  char* ret_val = malloc(35*sizeof(char));
172  char* fo = ret_val;
173  uint32 i;
174  uint8 f;
175
176  if(ret_val == NULL)
177    return NULL;
178
179  fo[0] = '\0';
180  if (!flags)
181    return ret_val;
182
183  for(i=0; i < 8; i++)
184  {
185    f = (1<<i);
186    if((flags & f) && (flag_map[i] != NULL))
187    {
188      strcpy(fo, flag_map[i]);
189      fo += strlen(flag_map[i]);
190      *(fo++) = ' ';
191      flags ^= f;
192    }
193  }
194 
195  /* Any remaining unknown flags are added at the end in hex. */
196  if(flags != 0)
197    sprintf(fo, "0x%.2X ", flags);
198
199  /* Chop off the last space if we've written anything to ret_val */
200  if(fo != ret_val)
201    fo[-1] = '\0';
202
203  /* XXX: what was this old VI flag for??
204     XXX: Is this check right?  0xF == 1|2|4|8, which makes it redundant...
205  if (flags == 0xF) {
206    if (some) strcat(flg_output, " ");
207    some = 1;
208    strcat(flg_output, "VI");
209  }
210  */
211
212  return ret_val;
213}
214
215
216char* regfi_ace_perms2str(uint32 perms)
217{
218  uint32 i, p;
219  /* This is more than is needed by a fair margin. */
220  char* ret_val = malloc(350*sizeof(char));
221  char* r = ret_val;
222
223  /* Each represents one of 32 permissions bits.  NULL is for undefined/reserved bits.
224   * For more information, see:
225   *   http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
226   *   http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
227   */
228  static const char* perm_map[32] = 
229    {/* object-specific permissions (registry keys, in this case) */
230      "QRY_VAL",       /* KEY_QUERY_VALUE */
231      "SET_VAL",       /* KEY_SET_VALUE */
232      "CREATE_KEY",    /* KEY_CREATE_SUB_KEY */
233      "ENUM_KEYS",     /* KEY_ENUMERATE_SUB_KEYS */
234      "NOTIFY",        /* KEY_NOTIFY */
235      "CREATE_LNK",    /* KEY_CREATE_LINK - Reserved for system use. */
236      NULL,
237      NULL,
238      "WOW64_64",      /* KEY_WOW64_64KEY */
239      "WOW64_32",      /* KEY_WOW64_32KEY */
240      NULL,
241      NULL,
242      NULL,
243      NULL,
244      NULL,
245      NULL,
246      /* standard access rights */
247      "DELETE",        /* DELETE */
248      "R_CONT",        /* READ_CONTROL */
249      "W_DAC",         /* WRITE_DAC */
250      "W_OWNER",       /* WRITE_OWNER */
251      "SYNC",          /* SYNCHRONIZE - Shouldn't be set in registries */
252      NULL,
253      NULL,
254      NULL,
255      /* other generic */
256      "SYS_SEC",       /* ACCESS_SYSTEM_SECURITY */
257      "MAX_ALLWD",     /* MAXIMUM_ALLOWED */
258      NULL,
259      NULL,
260      "GEN_A",         /* GENERIC_ALL */
261      "GEN_X",         /* GENERIC_EXECUTE */
262      "GEN_W",         /* GENERIC_WRITE */
263      "GEN_R",         /* GENERIC_READ */
264    };
265
266
267  if(ret_val == NULL)
268    return NULL;
269
270  r[0] = '\0';
271  for(i=0; i < 32; i++)
272  {
273    p = (1<<i);
274    if((perms & p) && (perm_map[i] != NULL))
275    {
276      strcpy(r, perm_map[i]);
277      r += strlen(perm_map[i]);
278      *(r++) = ' ';
279      perms ^= p;
280    }
281  }
282 
283  /* Any remaining unknown permission bits are added at the end in hex. */
284  if(perms != 0)
285    sprintf(r, "0x%.8X ", perms);
286
287  /* Chop off the last space if we've written anything to ret_val */
288  if(r != ret_val)
289    r[-1] = '\0';
290
291  return ret_val;
292}
293
294
295char* regfi_sid2str(WINSEC_DOM_SID* sid)
296{
297  uint32 i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
298  uint32 left = size;
299  uint8 comps = sid->num_auths;
300  char* ret_val = malloc(size);
301 
302  if(ret_val == NULL)
303    return NULL;
304
305  if(comps > WINSEC_MAX_SUBAUTHS)
306    comps = WINSEC_MAX_SUBAUTHS;
307
308  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
309
310  for (i = 0; i < comps; i++) 
311    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
312
313  return ret_val;
314}
315
316
317char* regfi_get_acl(WINSEC_ACL* acl)
318{
319  uint32 i, extra, size = 0;
320  const char* type_str;
321  char* flags_str;
322  char* perms_str;
323  char* sid_str;
324  char* ace_delim = "";
325  char* ret_val = NULL;
326  char* tmp_val = NULL;
327  bool failed = false;
328  char field_delim = ':';
329
330  for (i = 0; i < acl->num_aces && !failed; i++)
331  {
332    sid_str = regfi_sid2str(acl->aces[i]->trustee);
333    type_str = regfi_ace_type2str(acl->aces[i]->type);
334    perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
335    flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
336   
337    if(flags_str != NULL && perms_str != NULL 
338       && type_str != NULL && sid_str != NULL)
339    {
340      /* XXX: this is slow */
341      extra = strlen(sid_str) + strlen(type_str) 
342        + strlen(perms_str) + strlen(flags_str) + 5;
343      tmp_val = realloc(ret_val, size+extra);
344
345      if(tmp_val == NULL)
346      {
347        free(ret_val);
348        ret_val = NULL;
349        failed = true;
350      }
351      else
352      {
353        ret_val = tmp_val;
354        size += snprintf(ret_val+size, extra, "%s%s%c%s%c%s%c%s",
355                         ace_delim,sid_str,
356                         field_delim,type_str,
357                         field_delim,perms_str,
358                         field_delim,flags_str);
359        ace_delim = "|";
360      }
361    }
362    else
363      failed = true;
364
365    if(sid_str != NULL)
366      free(sid_str);
367    if(sid_str != NULL)
368      free(perms_str);
369    if(sid_str != NULL)
370      free(flags_str);
371  }
372
373  return ret_val;
374}
375
376
377char* regfi_get_sacl(WINSEC_DESC *sec_desc)
378{
379  if (sec_desc->sacl)
380    return regfi_get_acl(sec_desc->sacl);
381  else
382    return NULL;
383}
384
385
386char* regfi_get_dacl(WINSEC_DESC *sec_desc)
387{
388  if (sec_desc->dacl)
389    return regfi_get_acl(sec_desc->dacl);
390  else
391    return NULL;
392}
393
394
395char* regfi_get_owner(WINSEC_DESC *sec_desc)
396{
397  return regfi_sid2str(sec_desc->owner_sid);
398}
399
400
401char* regfi_get_group(WINSEC_DESC *sec_desc)
402{
403  return regfi_sid2str(sec_desc->grp_sid);
404}
405
406
407/*****************************************************************************
408 * This function is just like read(2), except that it continues to
409 * re-try reading from the file descriptor if EINTR or EAGAIN is received. 
410 * regfi_read will attempt to read length bytes from fd and write them to buf.
411 *
412 * On success, 0 is returned.  Upon failure, an errno code is returned.
413 *
414 * The number of bytes successfully read is returned through the length
415 * parameter by reference.  If both the return value and length parameter are
416 * returned as 0, then EOF was encountered immediately
417 *****************************************************************************/
418uint32 regfi_read(int fd, uint8* buf, uint32* length)
419{
420  uint32 rsize = 0;
421  uint32 rret = 0;
422
423  do
424  {
425    rret = read(fd, buf + rsize, *length - rsize);
426    if(rret > 0)
427      rsize += rret;
428  }while(*length - rsize > 0 
429         && (rret > 0 || (rret == -1 && (errno == EAGAIN || errno == EINTR))));
430 
431  *length = rsize;
432  if (rret == -1 && errno != EINTR && errno != EAGAIN)
433    return errno;
434
435  return 0;
436}
437
438
439/*****************************************************************************
440 *
441 *****************************************************************************/
442bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
443                      uint32* cell_length, bool* unalloc)
444{
445  uint32 length;
446  int32 raw_length;
447  uint8 tmp[4];
448
449  if(lseek(fd, offset, SEEK_SET) == -1)
450    return false;
451
452  length = 4;
453  if((regfi_read(fd, tmp, &length) != 0) || length != 4)
454    return false;
455  raw_length = IVALS(tmp, 0);
456
457  if(raw_length < 0)
458  {
459    (*cell_length) = raw_length*(-1);
460    (*unalloc) = false;
461  }
462  else
463  {
464    (*cell_length) = raw_length;
465    (*unalloc) = true;
466  }
467
468  if(*cell_length - 4 < hdr_len)
469    return false;
470
471  if(hdr_len > 0)
472  {
473    length = hdr_len;
474    if((regfi_read(fd, hdr, &length) != 0) || length != hdr_len)
475      return false;
476  }
477
478  return true;
479}
480
481
482/*******************************************************************
483 * Given an offset and an hbin, is the offset within that hbin?
484 * The offset is a virtual file offset.
485 *******************************************************************/
486static bool regfi_offset_in_hbin(REGFI_HBIN* hbin, uint32 offset)
487{
488  if(!hbin)
489    return false;
490
491  if((offset > hbin->first_hbin_off) 
492     && (offset < (hbin->first_hbin_off + hbin->block_size)))
493    return true;
494               
495  return false;
496}
497
498
499
500/*******************************************************************
501 * Provide a virtual offset and receive the correpsonding HBIN
502 * block for it.  NULL if one doesn't exist.
503 *******************************************************************/
504REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 offset)
505{
506  return (REGFI_HBIN*)range_list_find_data(file->hbins, offset+REGFI_REGF_SIZE);
507}
508
509
510
511/******************************************************************************
512 ******************************************************************************/
513REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset, 
514                                         uint32 num_keys, uint32 max_size, 
515                                         bool strict)
516{
517  REGFI_SUBKEY_LIST* ret_val;
518
519  ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, 
520                                      REGFI_MAX_SUBKEY_DEPTH);
521
522  if(num_keys != ret_val->num_keys)
523  {
524    /*  Not sure which should be authoritative, the number from the
525     *  NK record, or the number in the subkey list.  Just emit a warning for
526     *  now if they don't match.
527     */
528    regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent"
529                      " (%d) did not match number found in subkey list/tree (%d)"
530                      " while parsing subkey list/tree at offset 0x%.8X.", 
531                      num_keys, ret_val->num_keys, offset);
532  }
533
534  return ret_val;
535}
536
537
538/******************************************************************************
539 ******************************************************************************/
540REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset, 
541                                             uint32 max_size, bool strict,
542                                             uint8 depth_left)
543{
544  REGFI_SUBKEY_LIST* ret_val;
545  REGFI_SUBKEY_LIST** sublists;
546  REGFI_HBIN* sublist_hbin;
547  uint32 i, num_sublists, off, max_length;
548
549  if(depth_left == 0)
550  {
551    regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached"
552                      " while parsing subkey list/tree at offset 0x%.8X.", 
553                      offset);
554    return NULL;
555  }
556
557  ret_val = regfi_parse_subkeylist(file, offset, max_size, strict);
558  if(ret_val == NULL)
559    return NULL;
560
561  if(ret_val->recursive_type)
562  {
563    num_sublists = ret_val->num_children;
564    sublists = (REGFI_SUBKEY_LIST**)zalloc(num_sublists
565                                           * sizeof(REGFI_SUBKEY_LIST*));
566    for(i=0; i < num_sublists; i++)
567    {
568      off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
569      sublist_hbin = regfi_lookup_hbin(file, ret_val->elements[i].offset);
570      if(sublist_hbin == NULL)
571        sublists[i] = NULL;
572      else
573      {
574        max_length = sublist_hbin->block_size + sublist_hbin->file_off - off;
575        sublists[i] = regfi_load_subkeylist_aux(file, off, max_length, strict,
576                                                depth_left-1);
577      }
578    }
579    free(ret_val);
580
581    return regfi_merge_subkeylists(num_sublists, sublists, strict);
582  }
583
584  return ret_val;
585}
586
587
588/******************************************************************************
589 ******************************************************************************/
590REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset, 
591                                          uint32 max_size, bool strict)
592{
593  REGFI_SUBKEY_LIST* ret_val;
594  uint32 i, cell_length, length, elem_size;
595  uint8* elements;
596  uint8 buf[REGFI_SUBKEY_LIST_MIN_LEN];
597  bool unalloc;
598  bool recursive_type;
599
600  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN, 
601                       &cell_length, &unalloc))
602  {
603    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while "
604                      "parsing subkey-list at offset 0x%.8X.", offset);
605    return NULL;
606  }
607
608  if(cell_length > max_size)
609  {
610    regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size"
611                      " while parsing subkey-list at offset 0x%.8X.", offset);
612    if(strict)
613      return NULL;
614    cell_length = max_size & 0xFFFFFFF8;
615  }
616
617  recursive_type = false;
618  if(buf[0] == 'r' && buf[1] == 'i')
619  {
620    recursive_type = true;
621    elem_size = sizeof(uint32);
622  }
623  else if(buf[0] == 'l' && buf[1] == 'i')
624    elem_size = sizeof(uint32);
625  else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
626    elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
627  else
628  {
629    regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number"
630                      " (0x%.2X, 0x%.2X) encountered while parsing"
631                      " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
632    return NULL;
633  }
634
635  ret_val = (REGFI_SUBKEY_LIST*)zalloc(sizeof(REGFI_SUBKEY_LIST));
636  if(ret_val == NULL)
637    return NULL;
638
639  ret_val->offset = offset;
640  ret_val->cell_size = cell_length;
641  ret_val->magic[0] = buf[0];
642  ret_val->magic[1] = buf[1];
643  ret_val->recursive_type = recursive_type;
644  ret_val->num_children = SVAL(buf, 0x2);
645
646  if(!recursive_type)
647    ret_val->num_keys = ret_val->num_children;
648
649  length = elem_size*ret_val->num_children;
650  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32) < length)
651  {
652    regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
653                      " cell while parsing subkey-list at offset 0x%.8X.", 
654                      offset);
655    if(strict)
656    {
657      free(ret_val);
658      return NULL;
659    }
660    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32);
661  }
662
663  ret_val->elements
664    = (REGFI_SUBKEY_LIST_ELEM*)zalloc(ret_val->num_children
665                                      * sizeof(REGFI_SUBKEY_LIST_ELEM));
666  if(ret_val->elements == NULL)
667  {
668    free(ret_val);
669    return NULL;
670  }
671
672  elements = (uint8*)zalloc(length);
673  if(elements == NULL)
674  {
675    free(ret_val->elements);
676    free(ret_val);
677    return NULL;
678  }
679
680  if(regfi_read(file->fd, elements, &length) != 0
681     || length != elem_size*ret_val->num_children)
682  {
683    free(ret_val->elements);
684    free(ret_val);
685    return NULL;
686  }
687
688  if(elem_size == sizeof(uint32))
689  {
690    for (i=0; i < ret_val->num_children; i++)
691    {
692      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
693      ret_val->elements[i].hash = 0;
694    }
695  }
696  else
697  {
698    for (i=0; i < ret_val->num_children; i++)
699    {
700      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
701      ret_val->elements[i].hash = IVAL(elements, i*elem_size+4);
702    }
703  }
704  free(elements);
705
706  return ret_val;
707}
708
709
710/*******************************************************************
711 *******************************************************************/
712REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists, 
713                                           REGFI_SUBKEY_LIST** lists,
714                                           bool strict)
715{
716  uint32 i,j,k;
717  REGFI_SUBKEY_LIST* ret_val;
718
719  if(lists == NULL)
720    return NULL;
721  ret_val = (REGFI_SUBKEY_LIST*)zalloc(sizeof(REGFI_SUBKEY_LIST));
722
723  if(ret_val == NULL)
724    return NULL;
725 
726  /* Obtain total number of elements */
727  ret_val->num_keys = 0;
728  for(i=0; i < num_lists; i++)
729  {
730    if(lists[i] != NULL)
731      ret_val->num_keys += lists[i]->num_children;
732  }
733  ret_val->num_children = ret_val->num_keys;
734
735  if(ret_val->num_keys > 0)
736  {
737    ret_val->elements = 
738      (REGFI_SUBKEY_LIST_ELEM*)zalloc(sizeof(REGFI_SUBKEY_LIST_ELEM)
739                                     * ret_val->num_keys);
740    k=0;
741
742    if(ret_val->elements != NULL)
743    {
744      for(i=0; i < num_lists; i++)
745      {
746        if(lists[i] != NULL)
747        {
748          for(j=0; j < lists[i]->num_keys; j++)
749          {
750            ret_val->elements[k].hash=lists[i]->elements[j].hash;
751            ret_val->elements[k++].offset=lists[i]->elements[j].offset;
752          }
753        }
754      }
755    }
756  }
757 
758  for(i=0; i < num_lists; i++)
759    regfi_subkeylist_free(lists[i]);
760  free(lists);
761
762  return ret_val;
763}
764
765
766/*******************************************************************
767 *******************************************************************/
768REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size, bool strict)
769{
770  REGFI_SK_REC* ret_val;
771  uint8* sec_desc_buf;
772  uint32 cell_length, length;
773  /*prs_struct ps;*/
774  uint8 sk_header[REGFI_SK_MIN_LENGTH];
775  bool unalloc = false;
776
777  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
778                       &cell_length, &unalloc))
779  {
780    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell"
781                      " at offset 0x%.8X.", offset);
782    return NULL;
783  }
784   
785  if(sk_header[0] != 's' || sk_header[1] != 'k')
786  {
787    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
788                      " SK record at offset 0x%.8X.", offset);
789    return NULL;
790  }
791
792  ret_val = (REGFI_SK_REC*)zalloc(sizeof(REGFI_SK_REC));
793  if(ret_val == NULL)
794    return NULL;
795
796  ret_val->offset = offset;
797  /* XXX: Is there a way to be more conservative (shorter) with
798   *      cell length when cell is unallocated?
799   */
800  ret_val->cell_size = cell_length;
801
802  if(ret_val->cell_size > max_size)
803    ret_val->cell_size = max_size & 0xFFFFFFF8;
804  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
805     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
806  {
807    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
808                      " parsing SK record at offset 0x%.8X.", offset);
809    free(ret_val);
810    return NULL;
811  }
812
813  ret_val->magic[0] = sk_header[0];
814  ret_val->magic[1] = sk_header[1];
815
816  /* XXX: Can additional validation be added here? */
817  ret_val->unknown_tag = SVAL(sk_header, 0x2);
818  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
819  ret_val->next_sk_off = IVAL(sk_header, 0x8);
820  ret_val->ref_count = IVAL(sk_header, 0xC);
821  ret_val->desc_size = IVAL(sk_header, 0x10);
822
823  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
824  {
825    regfi_add_message(file, REGFI_MSG_ERROR, "Security descriptor too large for"
826                      " cell while parsing SK record at offset 0x%.8X.", 
827                      offset);
828    free(ret_val);
829    return NULL;
830  }
831
832  sec_desc_buf = (uint8*)zalloc(ret_val->desc_size);
833  if(ret_val == NULL)
834  {
835    free(ret_val);
836    return NULL;
837  }
838
839  length = ret_val->desc_size;
840  if(regfi_read(file->fd, sec_desc_buf, &length) != 0 
841     || length != ret_val->desc_size)
842  {
843    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security"
844                      " descriptor while parsing SK record at offset 0x%.8X.",
845                      offset);
846    free(ret_val);
847    return NULL;
848  }
849
850  if(!(ret_val->sec_desc = winsec_parse_desc(sec_desc_buf, ret_val->desc_size)))
851  {
852    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security"
853                      " descriptor while parsing SK record at offset 0x%.8X.",
854                      offset);
855    free(sec_desc_buf);
856    free(ret_val);
857    return NULL;
858  }
859  free(sec_desc_buf);
860
861
862  return ret_val;
863}
864
865
866uint32* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset, 
867                              uint32 num_values, bool strict)
868{
869  uint32* ret_val;
870  uint32 i, cell_length, length, read_len;
871  bool unalloc;
872
873  if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
874  {
875    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header"
876                      " while parsing value list at offset 0x%.8X.", offset);
877    return NULL;
878  }
879
880  if(cell_length != (cell_length & 0xFFFFFFF8))
881  {
882    if(strict)
883      return NULL;
884    cell_length = cell_length & 0xFFFFFFF8;
885  }
886  if((num_values * sizeof(uint32)) > cell_length-sizeof(uint32))
887  {
888    regfi_add_message(file, REGFI_MSG_ERROR, "Too many values found"
889                      " while parsing value list at offset 0x%.8X.", offset);
890    return NULL;
891  }
892
893  read_len = num_values*sizeof(uint32);
894  ret_val = (uint32*)malloc(read_len);
895  if(ret_val == NULL)
896    return NULL;
897
898  length = read_len;
899  if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0) || length != read_len)
900  {
901    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
902                      " while parsing value list at offset 0x%.8X.", offset);
903    free(ret_val);
904    return NULL;
905  }
906 
907  for(i=0; i < num_values; i++)
908  {
909    /* Fix endianness */
910    ret_val[i] = IVAL(&ret_val[i], 0);
911
912    /* Validate the first num_values values to ensure they make sense */
913    if(strict)
914    {
915      if((ret_val[i] + REGFI_REGF_SIZE > file->file_length)
916         || ((ret_val[i] & 0xFFFFFFF8) != ret_val[i]))
917      {
918        regfi_add_message(file, REGFI_MSG_ERROR, "Invalid value pointer"
919                          " (0x%.8X) found while parsing value list at offset"
920                          " 0x%.8X.", ret_val[i], offset);
921        free(ret_val);
922        return NULL;
923      }
924    }
925  }
926
927  return ret_val;
928}
929
930
931
932/******************************************************************************
933 * If !strict, the list may contain NULLs, VK records may point to NULL.
934 ******************************************************************************/
935REGFI_VK_REC** regfi_load_valuelist(REGFI_FILE* file, uint32 offset, 
936                                   uint32 num_values, uint32 max_size, 
937                                   bool strict)
938{
939  REGFI_VK_REC** ret_val;
940  REGFI_HBIN* hbin;
941  uint32 i, vk_offset, vk_max_length, usable_num_values;
942  uint32* voffsets;
943
944  if((num_values+1) * sizeof(uint32) > max_size)
945  {
946    if(strict)
947      return NULL;
948    usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
949  }
950  else
951    usable_num_values = num_values;
952
953  voffsets = regfi_parse_valuelist(file, offset, usable_num_values, strict);
954  if(voffsets == NULL)
955    return NULL;
956
957  ret_val = (REGFI_VK_REC**)zalloc(sizeof(REGFI_VK_REC*) * usable_num_values);
958  if(ret_val == NULL)
959  {
960    free(voffsets);
961    return NULL;
962  }
963 
964  for(i=0; i < usable_num_values; i++)
965  {
966    hbin = regfi_lookup_hbin(file, voffsets[i]);
967    if(!hbin)
968    {
969      free(voffsets);
970      free(ret_val);
971      return NULL;
972    }
973
974    vk_offset =  voffsets[i] + REGFI_REGF_SIZE;
975    vk_max_length = hbin->block_size + hbin->file_off - vk_offset;
976    ret_val[i] = regfi_parse_vk(file, vk_offset, vk_max_length, strict);
977    if(ret_val[i] == NULL)
978    { /* If we're being strict, throw out the whole list.
979       * Otherwise, let it be NULL.
980       */
981      if(strict)
982      {
983        free(voffsets);
984        free(ret_val);
985        return NULL;
986      }
987    }
988  }
989
990  free(voffsets);
991  return ret_val;
992}
993
994
995
996/*******************************************************************
997 * XXX: Need to add full key caching using a
998 *      custom cache structure.
999 *******************************************************************/
1000REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
1001{
1002  REGFI_HBIN* hbin;
1003  REGFI_HBIN* sub_hbin;
1004  REGFI_NK_REC* nk;
1005  uint32 max_length, off;
1006
1007  hbin = regfi_lookup_hbin(file, offset-REGFI_REGF_SIZE);
1008  if (hbin == NULL) 
1009    return NULL;
1010
1011  /* get the initial nk record */
1012  max_length = hbin->block_size + hbin->file_off - offset;
1013  if ((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
1014  {
1015    regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
1016                      " offset 0x%.8X.", offset);
1017    return NULL;
1018  }
1019
1020  /* fill in values */
1021  if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
1022  {
1023    sub_hbin = hbin;
1024    if(!regfi_offset_in_hbin(hbin, nk->values_off)) 
1025      sub_hbin = regfi_lookup_hbin(file, nk->values_off);
1026   
1027    if(sub_hbin == NULL)
1028    {
1029      if(strict)
1030      {
1031        free(nk);
1032        return NULL;
1033      }
1034      else
1035        nk->values = NULL;
1036
1037    }
1038    else
1039    {
1040      off = nk->values_off + REGFI_REGF_SIZE;
1041      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
1042      nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length, 
1043                                        true);
1044      if(strict && nk->values == NULL)
1045      {
1046        regfi_add_message(file, REGFI_MSG_ERROR, "Could not load value list"
1047                          " for NK record at offset 0x%.8X.",
1048                          offset);
1049        free(nk);
1050        return NULL;
1051      }
1052
1053    }
1054  }
1055
1056  /* now get subkeys */
1057  if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
1058  {
1059    sub_hbin = hbin;
1060    if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
1061      sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
1062
1063    if (sub_hbin == NULL) 
1064    {
1065      if(strict)
1066      {
1067        regfi_key_free(nk);
1068        return NULL;
1069      }
1070      else
1071        nk->subkeys = NULL;
1072    }
1073    else
1074    {
1075      off = nk->subkeys_off + REGFI_REGF_SIZE;
1076      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
1077      nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
1078                                          max_length, true);
1079
1080      if(nk->subkeys == NULL)
1081      {
1082        /* XXX: Should we free the key and bail out here instead? 
1083         *      During nonstrict?
1084         */
1085        nk->num_subkeys = 0;
1086      }
1087    }
1088  }
1089
1090  return nk;
1091}
1092
1093
1094/******************************************************************************
1095 ******************************************************************************/
1096static bool regfi_find_root_nk(REGFI_FILE* file, uint32 offset, uint32 hbin_size,
1097                               uint32* root_offset)
1098{
1099  uint8 tmp[4];
1100  int32 record_size;
1101  uint32 length, hbin_offset = 0;
1102  REGFI_NK_REC* nk = NULL;
1103  bool found = false;
1104
1105  for(record_size=0; !found && (hbin_offset < hbin_size); )
1106  {
1107    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
1108      return false;
1109   
1110    length = 4;
1111    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
1112      return false;
1113    record_size = IVALS(tmp, 0);
1114
1115    if(record_size < 0)
1116    {
1117      record_size = record_size*(-1);
1118      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
1119      if(nk != NULL)
1120      {
1121        if((nk->key_type == REGFI_NK_TYPE_ROOTKEY1)
1122           || (nk->key_type == REGFI_NK_TYPE_ROOTKEY2))
1123        {
1124          found = true;
1125          *root_offset = nk->offset;
1126        }
1127        free(nk);
1128      }
1129    }
1130
1131    hbin_offset += record_size;
1132  }
1133
1134  return found;
1135}
1136
1137
1138/*******************************************************************
1139 * Open the registry file and then read in the REGF block to get the
1140 * first hbin offset.
1141 *******************************************************************/
1142REGFI_FILE* regfi_open(const char* filename)
1143{
1144  struct stat sbuf;
1145  REGFI_FILE* rb;
1146  REGFI_HBIN* hbin = NULL;
1147  uint32 hbin_off, file_length;
1148  int fd;
1149  bool rla;
1150
1151  /* open an existing file */
1152  if ((fd = open(filename, O_RDONLY)) == -1) 
1153  {
1154    /* DEBUG(0,("regfi_open: failure to open %s (%s)\n", filename, strerror(errno)));*/
1155    return NULL;
1156  }
1157 
1158  /* Determine file length.  Must be at least big enough
1159   * for the header and one hbin.
1160   */
1161  if (fstat(fd, &sbuf) == -1)
1162    return NULL;
1163  file_length = sbuf.st_size;
1164  if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
1165    return NULL;
1166
1167  /* read in an existing file */
1168  if ((rb = regfi_parse_regf(fd, true)) == NULL) 
1169  {
1170    /* DEBUG(0,("regfi_open: Failed to read initial REGF block\n"));*/
1171    close(fd);
1172    return NULL;
1173  }
1174  rb->file_length = file_length; 
1175
1176  rb->hbins = range_list_new();
1177  if(rb->hbins == NULL)
1178  {
1179    range_list_free(rb->hbins);
1180    close(fd);
1181    free(rb);
1182    return NULL;
1183  }
1184 
1185  rla = true;
1186  hbin_off = REGFI_REGF_SIZE;
1187  hbin = regfi_parse_hbin(rb, hbin_off, true);
1188  while(hbin && rla)
1189  {
1190    rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
1191    hbin_off = hbin->file_off + hbin->block_size;
1192    hbin = regfi_parse_hbin(rb, hbin_off, true);
1193  }
1194
1195  /* Default message mask */
1196  rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN;
1197
1198  /* success */
1199  return rb;
1200}
1201
1202
1203/*******************************************************************
1204 *******************************************************************/
1205int regfi_close( REGFI_FILE *file )
1206{
1207  int fd;
1208  uint32 i;
1209
1210  /* nothing to do if there is no open file */
1211  if ((file == NULL) || (file->fd == -1))
1212    return 0;
1213
1214  fd = file->fd;
1215  file->fd = -1;
1216  for(i=0; i < range_list_size(file->hbins); i++)
1217    free(range_list_get(file->hbins, i)->data);
1218  range_list_free(file->hbins);
1219
1220  free(file);
1221
1222  return close(fd);
1223}
1224
1225
1226/******************************************************************************
1227 * There should be only *one* root key in the registry file based
1228 * on my experience.  --jerry
1229 *****************************************************************************/
1230REGFI_NK_REC* regfi_rootkey(REGFI_FILE *file)
1231{
1232  REGFI_NK_REC* nk = NULL;
1233  REGFI_HBIN*   hbin;
1234  uint32       root_offset, i, num_hbins;
1235 
1236  if(!file)
1237    return NULL;
1238
1239  /* Scan through the file one HBIN block at a time looking
1240     for an NK record with a type == 0x002c.
1241     Normally this is the first nk record in the first hbin
1242     block (but I'm not assuming that for now) */
1243
1244  num_hbins = range_list_size(file->hbins);
1245  for(i=0; i < num_hbins; i++)
1246  {
1247    hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
1248    if(regfi_find_root_nk(file, hbin->file_off+REGFI_HBIN_HEADER_SIZE, 
1249                          hbin->block_size-REGFI_HBIN_HEADER_SIZE, &root_offset))
1250    {
1251      nk = regfi_load_key(file, root_offset, true);
1252      break;
1253    }
1254  }
1255
1256  return nk;
1257}
1258
1259
1260/******************************************************************************
1261 *****************************************************************************/
1262void regfi_key_free(REGFI_NK_REC* nk)
1263{
1264  uint32 i;
1265 
1266  if((nk->values != NULL) && (nk->values_off!=REGFI_OFFSET_NONE))
1267  {
1268    for(i=0; i < nk->num_values; i++)
1269    {
1270      if(nk->values[i]->valuename != NULL)
1271        free(nk->values[i]->valuename);
1272      if(nk->values[i]->data != NULL)
1273        free(nk->values[i]->data);
1274      free(nk->values[i]);
1275    }
1276    free(nk->values);
1277  }
1278
1279  regfi_subkeylist_free(nk->subkeys);
1280
1281  if(nk->keyname != NULL)
1282    free(nk->keyname);
1283  if(nk->classname != NULL)
1284    free(nk->classname);
1285
1286  /* XXX: not freeing sec_desc because these are cached.  This needs to be reviewed. */
1287  free(nk);
1288}
1289
1290
1291/******************************************************************************
1292 *****************************************************************************/
1293void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
1294{
1295  if(list != NULL)
1296  {
1297    free(list->elements);
1298    free(list);
1299  }
1300}
1301
1302
1303/******************************************************************************
1304 *****************************************************************************/
1305REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
1306{
1307  REGFI_NK_REC* root;
1308  REGFI_ITERATOR* ret_val = (REGFI_ITERATOR*)malloc(sizeof(REGFI_ITERATOR));
1309  if(ret_val == NULL)
1310    return NULL;
1311
1312  root = regfi_rootkey(fh);
1313  if(root == NULL)
1314  {
1315    free(ret_val);
1316    return NULL;
1317  }
1318
1319  ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH);
1320  if(ret_val->key_positions == NULL)
1321  {
1322    free(ret_val);
1323    free(root);
1324    return NULL;
1325  }
1326
1327  /* This secret isn't very secret, but we don't need a good one.  This
1328   * secret is just designed to prevent someone from trying to blow our
1329   * caching and make things slow.
1330   */
1331  ret_val->sk_recs = lru_cache_create(127, 0x15DEAD05^time(NULL)
1332                                           ^(getpid()<<16)^(getppid()<<8),
1333                                      true);
1334
1335  ret_val->f = fh;
1336  ret_val->cur_key = root;
1337  ret_val->cur_subkey = 0;
1338  ret_val->cur_value = 0;
1339
1340  return ret_val;
1341}
1342
1343
1344/******************************************************************************
1345 *****************************************************************************/
1346void regfi_iterator_free(REGFI_ITERATOR* i)
1347{
1348  REGFI_ITER_POSITION* cur;
1349
1350  if(i->cur_key != NULL)
1351    regfi_key_free(i->cur_key);
1352
1353  while((cur = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions)) != NULL)
1354  {
1355    regfi_key_free(cur->nk);
1356    free(cur);
1357  }
1358 
1359  lru_cache_destroy(i->sk_recs);
1360
1361  free(i);
1362}
1363
1364
1365
1366/******************************************************************************
1367 *****************************************************************************/
1368/* XXX: some way of indicating reason for failure should be added. */
1369bool regfi_iterator_down(REGFI_ITERATOR* i)
1370{
1371  REGFI_NK_REC* subkey;
1372  REGFI_ITER_POSITION* pos;
1373
1374  pos = (REGFI_ITER_POSITION*)malloc(sizeof(REGFI_ITER_POSITION));
1375  if(pos == NULL)
1376    return false;
1377
1378  subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i);
1379  if(subkey == NULL)
1380  {
1381    free(pos);
1382    return false;
1383  }
1384
1385  pos->nk = i->cur_key;
1386  pos->cur_subkey = i->cur_subkey;
1387  if(!void_stack_push(i->key_positions, pos))
1388  {
1389    free(pos);
1390    regfi_key_free(subkey);
1391    return false;
1392  }
1393
1394  i->cur_key = subkey;
1395  i->cur_subkey = 0;
1396  i->cur_value = 0;
1397
1398  return true;
1399}
1400
1401
1402/******************************************************************************
1403 *****************************************************************************/
1404bool regfi_iterator_up(REGFI_ITERATOR* i)
1405{
1406  REGFI_ITER_POSITION* pos;
1407
1408  pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions);
1409  if(pos == NULL)
1410    return false;
1411
1412  regfi_key_free(i->cur_key);
1413  i->cur_key = pos->nk;
1414  i->cur_subkey = pos->cur_subkey;
1415  i->cur_value = 0;
1416  free(pos);
1417
1418  return true;
1419}
1420
1421
1422/******************************************************************************
1423 *****************************************************************************/
1424bool regfi_iterator_to_root(REGFI_ITERATOR* i)
1425{
1426  while(regfi_iterator_up(i))
1427    continue;
1428
1429  return true;
1430}
1431
1432
1433/******************************************************************************
1434 *****************************************************************************/
1435bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
1436{
1437  REGFI_NK_REC* subkey;
1438  bool found = false;
1439  uint32 old_subkey = i->cur_subkey;
1440
1441  if(subkey_name == NULL)
1442    return false;
1443
1444  /* XXX: this alloc/free of each sub key might be a bit excessive */
1445  subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i);
1446  while((subkey != NULL) && (found == false))
1447  {
1448    if(subkey->keyname != NULL 
1449       && strcasecmp(subkey->keyname, subkey_name) == 0)
1450      found = true;
1451    else
1452    {
1453      regfi_key_free(subkey);
1454      subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i);
1455    }
1456  }
1457
1458  if(found == false)
1459  {
1460    i->cur_subkey = old_subkey;
1461    return false;
1462  }
1463
1464  regfi_key_free(subkey);
1465  return true;
1466}
1467
1468
1469/******************************************************************************
1470 *****************************************************************************/
1471bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
1472{
1473  uint32 x;
1474  if(path == NULL)
1475    return false;
1476
1477  for(x=0; 
1478      ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x])
1479       && regfi_iterator_down(i));
1480      x++)
1481  { continue; }
1482
1483  if(path[x] == NULL)
1484    return true;
1485 
1486  /* XXX: is this the right number of times? */
1487  for(; x > 0; x--)
1488    regfi_iterator_up(i);
1489 
1490  return false;
1491}
1492
1493
1494/******************************************************************************
1495 *****************************************************************************/
1496const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
1497{
1498  return i->cur_key;
1499}
1500
1501
1502/******************************************************************************
1503 *****************************************************************************/
1504const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
1505{
1506  REGFI_SK_REC* ret_val = NULL;
1507  REGFI_HBIN* hbin;
1508  uint32 max_length, off;
1509
1510  if(i->cur_key == NULL)
1511    return NULL;
1512 
1513  /* First look if we have already parsed it */
1514  if((i->cur_key->sk_off!=REGFI_OFFSET_NONE)
1515     && !(ret_val =(REGFI_SK_REC*)lru_cache_find(i->sk_recs, 
1516                                                &i->cur_key->sk_off, 4)))
1517  {
1518    hbin = regfi_lookup_hbin(i->f, i->cur_key->sk_off);
1519
1520    if(hbin == NULL)
1521      return NULL;
1522
1523    off = i->cur_key->sk_off + REGFI_REGF_SIZE;
1524    max_length = hbin->block_size + hbin->file_off - off;
1525    ret_val = regfi_parse_sk(i->f, off, max_length, true);
1526    if(ret_val == NULL)
1527      return NULL;
1528
1529    ret_val->sk_off = i->cur_key->sk_off;
1530    lru_cache_update(i->sk_recs, &i->cur_key->sk_off, 4, ret_val);
1531  }
1532
1533  return ret_val;
1534}
1535
1536
1537
1538/******************************************************************************
1539 *****************************************************************************/
1540const REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
1541{
1542  i->cur_subkey = 0;
1543  return regfi_iterator_cur_subkey(i);
1544}
1545
1546
1547/******************************************************************************
1548 *****************************************************************************/
1549const REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
1550{
1551  uint32 nk_offset;
1552
1553  /* see if there is anything left to report */
1554  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
1555      || (i->cur_subkey >= i->cur_key->num_subkeys))
1556    return NULL;
1557
1558  nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
1559
1560  return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, true);
1561}
1562
1563
1564/******************************************************************************
1565 *****************************************************************************/
1566/* XXX: some way of indicating reason for failure should be added. */
1567const REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
1568{
1569  const REGFI_NK_REC* subkey;
1570
1571  i->cur_subkey++;
1572  subkey = regfi_iterator_cur_subkey(i);
1573
1574  if(subkey == NULL)
1575    i->cur_subkey--;
1576
1577  return subkey;
1578}
1579
1580
1581/******************************************************************************
1582 *****************************************************************************/
1583bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
1584{
1585  const REGFI_VK_REC* cur;
1586  bool found = false;
1587
1588  /* XXX: cur->valuename can be NULL in the registry. 
1589   *      Should we allow for a way to search for that?
1590   */
1591  if(value_name == NULL)
1592    return false;
1593
1594  cur = regfi_iterator_first_value(i);
1595  while((cur != NULL) && (found == false))
1596  {
1597    if((cur->valuename != NULL)
1598       && (strcasecmp(cur->valuename, value_name) == 0))
1599      found = true;
1600    else
1601      cur = regfi_iterator_next_value(i);
1602  }
1603
1604  return found;
1605}
1606
1607
1608/******************************************************************************
1609 *****************************************************************************/
1610const REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
1611{
1612  i->cur_value = 0;
1613  return regfi_iterator_cur_value(i);
1614}
1615
1616
1617/******************************************************************************
1618 *****************************************************************************/
1619const REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
1620{
1621  REGFI_VK_REC* ret_val = NULL;
1622  if(i->cur_value < i->cur_key->num_values)
1623    ret_val = i->cur_key->values[i->cur_value];
1624
1625  return ret_val;
1626}
1627
1628
1629/******************************************************************************
1630 *****************************************************************************/
1631const REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
1632{
1633  const REGFI_VK_REC* ret_val;
1634
1635  i->cur_value++;
1636  ret_val = regfi_iterator_cur_value(i);
1637  if(ret_val == NULL)
1638    i->cur_value--;
1639
1640  return ret_val;
1641}
1642
1643
1644
1645/*******************************************************************
1646 * Computes the checksum of the registry file header.
1647 * buffer must be at least the size of an regf header (4096 bytes).
1648 *******************************************************************/
1649static uint32 regfi_compute_header_checksum(uint8* buffer)
1650{
1651  uint32 checksum, x;
1652  int i;
1653
1654  /* XOR of all bytes 0x0000 - 0x01FB */
1655
1656  checksum = x = 0;
1657 
1658  for ( i=0; i<0x01FB; i+=4 ) {
1659    x = IVAL(buffer, i );
1660    checksum ^= x;
1661  }
1662 
1663  return checksum;
1664}
1665
1666
1667/*******************************************************************
1668 * XXX: Add way to return more detailed error information.
1669 *******************************************************************/
1670REGFI_FILE* regfi_parse_regf(int fd, bool strict)
1671{
1672  uint8 file_header[REGFI_REGF_SIZE];
1673  uint32 length;
1674  REGFI_FILE* ret_val;
1675
1676  ret_val = (REGFI_FILE*)zalloc(sizeof(REGFI_FILE));
1677  if(ret_val == NULL)
1678    return NULL;
1679
1680  ret_val->fd = fd;
1681
1682  length = REGFI_REGF_SIZE;
1683  if((regfi_read(fd, file_header, &length)) != 0 
1684     || length != REGFI_REGF_SIZE)
1685  {
1686    free(ret_val);
1687    return NULL;
1688  }
1689
1690  ret_val->checksum = IVAL(file_header, 0x1FC);
1691  ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
1692  if (strict && (ret_val->checksum != ret_val->computed_checksum))
1693  {
1694    free(ret_val);
1695    return NULL;
1696  }
1697
1698  memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
1699  if(strict && (memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0))
1700  {
1701    free(ret_val);
1702    return NULL;
1703  }
1704 
1705  ret_val->unknown1 = IVAL(file_header, 0x4);
1706  ret_val->unknown2 = IVAL(file_header, 0x8);
1707
1708  ret_val->mtime.low = IVAL(file_header, 0xC);
1709  ret_val->mtime.high = IVAL(file_header, 0x10);
1710
1711  ret_val->unknown3 = IVAL(file_header, 0x14);
1712  ret_val->unknown4 = IVAL(file_header, 0x18);
1713  ret_val->unknown5 = IVAL(file_header, 0x1C);
1714  ret_val->unknown6 = IVAL(file_header, 0x20);
1715 
1716  ret_val->data_offset = IVAL(file_header, 0x24);
1717  ret_val->last_block = IVAL(file_header, 0x28);
1718
1719  ret_val->unknown7 = IVAL(file_header, 0x2C);
1720
1721  return ret_val;
1722}
1723
1724
1725
1726/*******************************************************************
1727 * Given real file offset, read and parse the hbin at that location
1728 * along with it's associated cells.
1729 *******************************************************************/
1730/* XXX: Need a way to return types of errors.
1731 */
1732REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
1733{
1734  REGFI_HBIN *hbin;
1735  uint8 hbin_header[REGFI_HBIN_HEADER_SIZE];
1736  uint32 length;
1737 
1738  if(offset >= file->file_length)
1739    return NULL;
1740
1741  if(lseek(file->fd, offset, SEEK_SET) == -1)
1742  {
1743    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
1744                      " while parsing hbin at offset 0x%.8X.", offset);
1745    return NULL;
1746  }
1747
1748  length = REGFI_HBIN_HEADER_SIZE;
1749  if((regfi_read(file->fd, hbin_header, &length) != 0) 
1750     || length != REGFI_HBIN_HEADER_SIZE)
1751    return NULL;
1752
1753  if(lseek(file->fd, offset, SEEK_SET) == -1)
1754  {
1755    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
1756                      " while parsing hbin at offset 0x%.8X.", offset);
1757    return NULL;
1758  }
1759
1760  if(!(hbin = (REGFI_HBIN*)zalloc(sizeof(REGFI_HBIN)))) 
1761    return NULL;
1762  hbin->file_off = offset;
1763
1764  memcpy(hbin->magic, hbin_header, 4);
1765  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
1766  {
1767    regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch "
1768                      "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
1769                      " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
1770                      hbin->magic[2], hbin->magic[3], offset);
1771    free(hbin);
1772    return NULL;
1773  }
1774
1775  hbin->first_hbin_off = IVAL(hbin_header, 0x4);
1776  hbin->block_size = IVAL(hbin_header, 0x8);
1777  /* this should be the same thing as hbin->block_size but just in case */
1778  hbin->next_block = IVAL(hbin_header, 0x1C);
1779
1780
1781  /* Ensure the block size is a multiple of 0x1000 and doesn't run off
1782   * the end of the file.
1783   */
1784  /* XXX: This may need to be relaxed for dealing with
1785   *      partial or corrupt files.
1786   */
1787  if((offset + hbin->block_size > file->file_length)
1788     || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
1789  {
1790    regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned"
1791                      " or runs off the end of the file"
1792                      " while parsing hbin at offset 0x%.8X.", offset);
1793    free(hbin);
1794    return NULL;
1795  }
1796
1797  return hbin;
1798}
1799
1800
1801/*******************************************************************
1802 *******************************************************************/
1803REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset, 
1804                            uint32 max_size, bool strict)
1805{
1806  uint8 nk_header[REGFI_NK_MIN_LENGTH];
1807  REGFI_HBIN *hbin;
1808  REGFI_NK_REC* ret_val;
1809  uint32 length,cell_length;
1810  uint32 class_offset, class_maxsize;
1811  bool unalloc = false;
1812
1813  if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
1814                       &cell_length, &unalloc))
1815  {
1816    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
1817                      " while parsing NK record at offset 0x%.8X.", offset);
1818    return NULL;
1819  }
1820
1821  /* A bit of validation before bothering to allocate memory */
1822  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
1823  {
1824    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
1825                      " NK record at offset 0x%.8X.", offset);
1826    return NULL;
1827  }
1828
1829  ret_val = (REGFI_NK_REC*)zalloc(sizeof(REGFI_NK_REC));
1830  if(ret_val == NULL)
1831  {
1832    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while"
1833                      " parsing NK record at offset 0x%.8X.", offset);
1834    return NULL;
1835  }
1836
1837  ret_val->offset = offset;
1838  ret_val->cell_size = cell_length;
1839
1840  if(ret_val->cell_size > max_size)
1841    ret_val->cell_size = max_size & 0xFFFFFFF8;
1842  if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
1843     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
1844  {
1845    regfi_add_message(file, REGFI_MSG_ERROR, "A length check failed while"
1846                      " parsing NK record at offset 0x%.8X.", offset);
1847    free(ret_val);
1848    return NULL;
1849  }
1850
1851  ret_val->magic[0] = nk_header[0x0];
1852  ret_val->magic[1] = nk_header[0x1];
1853  ret_val->key_type = SVAL(nk_header, 0x2);
1854  if((ret_val->key_type != REGFI_NK_TYPE_NORMALKEY)
1855     && (ret_val->key_type != REGFI_NK_TYPE_ROOTKEY1) 
1856     && (ret_val->key_type != REGFI_NK_TYPE_ROOTKEY2)
1857     && (ret_val->key_type != REGFI_NK_TYPE_LINKKEY)
1858     && (ret_val->key_type != REGFI_NK_TYPE_UNKNOWN1)
1859     && (ret_val->key_type != REGFI_NK_TYPE_UNKNOWN2)
1860     && (ret_val->key_type != REGFI_NK_TYPE_UNKNOWN3))
1861  {
1862    regfi_add_message(file, REGFI_MSG_WARN, "Unknown key type (0x%.4X) while"
1863                      " parsing NK record at offset 0x%.8X.", 
1864                      ret_val->key_type, offset);
1865  }
1866
1867  ret_val->mtime.low = IVAL(nk_header, 0x4);
1868  ret_val->mtime.high = IVAL(nk_header, 0x8);
1869  /* If the key is unallocated and the MTIME is earlier than Jan 1, 1990
1870   * or later than Jan 1, 2290, we consider this a bad key.  This helps
1871   * weed out some false positives during deleted data recovery.
1872   */
1873  if(unalloc
1874     && ((ret_val->mtime.high < REGFI_MTIME_MIN_HIGH
1875          && ret_val->mtime.low < REGFI_MTIME_MIN_LOW)
1876         || (ret_val->mtime.high > REGFI_MTIME_MAX_HIGH
1877             && ret_val->mtime.low > REGFI_MTIME_MAX_LOW)))
1878    return NULL;
1879
1880  ret_val->unknown1 = IVAL(nk_header, 0xC);
1881  ret_val->parent_off = IVAL(nk_header, 0x10);
1882  ret_val->num_subkeys = IVAL(nk_header, 0x14);
1883  ret_val->unknown2 = IVAL(nk_header, 0x18);
1884  ret_val->subkeys_off = IVAL(nk_header, 0x1C);
1885  ret_val->unknown3 = IVAL(nk_header, 0x20);
1886  ret_val->num_values = IVAL(nk_header, 0x24);
1887  ret_val->values_off = IVAL(nk_header, 0x28);
1888  ret_val->sk_off = IVAL(nk_header, 0x2C);
1889  ret_val->classname_off = IVAL(nk_header, 0x30);
1890
1891  ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
1892  ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38);
1893  ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C);
1894  ret_val->max_bytes_value = IVAL(nk_header, 0x40);
1895  ret_val->unk_index = IVAL(nk_header, 0x44);
1896
1897  ret_val->name_length = SVAL(nk_header, 0x48);
1898  ret_val->classname_length = SVAL(nk_header, 0x4A);
1899
1900
1901  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
1902  {
1903    if(strict)
1904    {
1905      regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell"
1906                        " while parsing NK record at offset 0x%.8X.", offset);
1907      free(ret_val);
1908      return NULL;
1909    }
1910    else
1911      ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
1912  }
1913  else if (unalloc)
1914  { /* Truncate cell_size if it's much larger than the apparent total record length. */
1915    /* Round up to the next multiple of 8 */
1916    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
1917    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
1918      length+=8;
1919
1920    /* If cell_size is still greater, truncate. */
1921    if(length < ret_val->cell_size)
1922      ret_val->cell_size = length;
1923  }
1924
1925  ret_val->keyname = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
1926  if(ret_val->keyname == NULL)
1927  {
1928    free(ret_val);
1929    return NULL;
1930  }
1931
1932  /* Don't need to seek, should be at the right offset */
1933  length = ret_val->name_length;
1934  if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
1935     || length != ret_val->name_length)
1936  {
1937    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
1938                      " while parsing NK record at offset 0x%.8X.", offset);
1939    free(ret_val->keyname);
1940    free(ret_val);
1941    return NULL;
1942  }
1943  ret_val->keyname[ret_val->name_length] = '\0';
1944
1945  if(ret_val->classname_off != REGFI_OFFSET_NONE)
1946  {
1947    hbin = regfi_lookup_hbin(file, ret_val->classname_off);
1948    if(hbin)
1949    {
1950      class_offset = ret_val->classname_off+REGFI_REGF_SIZE;
1951      class_maxsize = hbin->block_size + hbin->file_off - class_offset;
1952      ret_val->classname
1953        = regfi_parse_classname(file, class_offset, &ret_val->classname_length, 
1954                                class_maxsize, strict);
1955    }
1956    else
1957    {
1958      ret_val->classname = NULL;
1959      regfi_add_message(file, REGFI_MSG_WARN, "Could not find hbin for class"
1960                        " name while parsing NK record at offset 0x%.8X.", 
1961                        offset);
1962    }
1963    /* XXX: Should add this back and make it more strict?
1964    if(strict && ret_val->classname == NULL)
1965        return NULL;
1966    */
1967  }
1968
1969  return ret_val;
1970}
1971
1972
1973char* regfi_parse_classname(REGFI_FILE* file, uint32 offset, 
1974                            uint16* name_length, uint32 max_size, bool strict)
1975{
1976  char* ret_val = NULL;
1977  uint32 length;
1978  uint32 cell_length;
1979  bool unalloc = false;
1980
1981  if(*name_length > 0 && offset != REGFI_OFFSET_NONE
1982     && offset == (offset & 0xFFFFFFF8))
1983  {
1984    if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
1985    {
1986      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
1987                        " while parsing class name at offset 0x%.8X.", offset);
1988        return NULL;
1989    }
1990
1991    if((cell_length & 0xFFFFFFF8) != cell_length)
1992    {
1993      regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
1994                        " while parsing class name at offset 0x%.8X.", offset);
1995      return NULL;
1996    }
1997
1998    if(cell_length > max_size)
1999    {
2000      regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin "
2001                        "boundary while parsing class name at offset 0x%.8X.",
2002                        offset);
2003      if(strict)
2004        return NULL;
2005      cell_length = max_size;
2006    }
2007
2008    if((cell_length - 4) < *name_length)
2009    {
2010      regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than"
2011                        " cell_length while parsing class name at offset"
2012                        " 0x%.8X.", offset);
2013      if(strict)
2014        return NULL;
2015      *name_length = cell_length - 4;
2016    }
2017   
2018    ret_val = (char*)zalloc(*name_length);
2019    if(ret_val != NULL)
2020    {
2021      length = *name_length;
2022      if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0)
2023         || length != *name_length)
2024      {
2025        regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
2026                          " while parsing class name at offset 0x%.8X.", offset);
2027        free(ret_val);
2028        return NULL;
2029      }
2030    }
2031  }
2032
2033  return ret_val;
2034}
2035
2036
2037/*******************************************************************
2038 *******************************************************************/
2039REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset, 
2040                            uint32 max_size, bool strict)
2041{
2042  REGFI_VK_REC* ret_val;
2043  REGFI_HBIN *hbin;
2044  uint8 vk_header[REGFI_VK_MIN_LENGTH];
2045  uint32 raw_data_size, length, cell_length;
2046  uint32 data_offset, data_maxsize;
2047  bool unalloc = false;
2048
2049  if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
2050                       &cell_length, &unalloc))
2051  {
2052    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
2053                      " while parsing VK record at offset 0x%.8X.", offset);
2054    return NULL;
2055  }
2056
2057  ret_val = (REGFI_VK_REC*)zalloc(sizeof(REGFI_VK_REC));
2058  if(ret_val == NULL)
2059    return NULL;
2060
2061  ret_val->offset = offset;
2062  ret_val->cell_size = cell_length;
2063
2064  if(ret_val->cell_size > max_size)
2065    ret_val->cell_size = max_size & 0xFFFFFFF8;
2066  if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
2067     || ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8))
2068  {
2069    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
2070                      " while parsing VK record at offset 0x%.8X.", offset);
2071    free(ret_val);
2072    return NULL;
2073  }
2074
2075  ret_val->magic[0] = vk_header[0x0];
2076  ret_val->magic[1] = vk_header[0x1];
2077  if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
2078  {
2079    /* XXX: This does not account for deleted keys under Win2K which
2080     *      often have this (and the name length) overwritten with
2081     *      0xFFFF.
2082     */
2083    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch"
2084                      " while parsing VK record at offset 0x%.8X.", offset);
2085    free(ret_val);
2086    return NULL;
2087  }
2088
2089  ret_val->name_length = SVAL(vk_header, 0x2);
2090  raw_data_size = IVAL(vk_header, 0x4);
2091  ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
2092  ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
2093  ret_val->data_off = IVAL(vk_header, 0x8);
2094  ret_val->type = IVAL(vk_header, 0xC);
2095  ret_val->flag = SVAL(vk_header, 0x10);
2096  ret_val->unknown1 = SVAL(vk_header, 0x12);
2097
2098  if(ret_val->flag & REGFI_VK_FLAG_NAME_PRESENT)
2099  {
2100    if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
2101    {
2102      regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell"
2103                        " space while parsing VK record at offset 0x%.8X.",
2104                        offset);
2105      if(strict)
2106      {
2107        free(ret_val);
2108        return NULL;
2109      }
2110      else
2111        ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4;
2112    }
2113
2114    /* Round up to the next multiple of 8 */
2115    cell_length = (ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) & 0xFFFFFFF8;
2116    if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
2117      cell_length+=8;
2118
2119    ret_val->valuename = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
2120    if(ret_val->valuename == NULL)
2121    {
2122      free(ret_val);
2123      return NULL;
2124    }
2125
2126    length = ret_val->name_length;
2127    if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
2128       || length != ret_val->name_length)
2129    {
2130      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
2131                        " while parsing VK record at offset 0x%.8X.", offset);
2132      free(ret_val->valuename);
2133      free(ret_val);
2134      return NULL;
2135    }
2136    ret_val->valuename[ret_val->name_length] = '\0';
2137
2138  }
2139  else
2140    cell_length = REGFI_VK_MIN_LENGTH + 4;
2141
2142  if(unalloc)
2143  {
2144    /* If cell_size is still greater, truncate. */
2145    if(cell_length < ret_val->cell_size)
2146      ret_val->cell_size = cell_length;
2147  }
2148
2149  if(ret_val->data_size == 0)
2150    ret_val->data = NULL;
2151  else
2152  {
2153    if(ret_val->data_in_offset)
2154    {
2155      ret_val->data = regfi_parse_data(file, ret_val->data_off, 
2156                                       raw_data_size, 4, strict);
2157    }
2158    else
2159    {
2160      hbin = regfi_lookup_hbin(file, ret_val->data_off);
2161      if(hbin)
2162      {
2163        data_offset = ret_val->data_off+REGFI_REGF_SIZE;
2164        data_maxsize = hbin->block_size + hbin->file_off - data_offset;
2165        ret_val->data = regfi_parse_data(file, data_offset, raw_data_size,
2166                                         data_maxsize, strict);
2167       
2168      }
2169      else
2170      {
2171        regfi_add_message(file, REGFI_MSG_WARN, "Could not find hbin for data"
2172                          " while parsing VK record at offset 0x%.8X.", offset);
2173        ret_val->data = NULL;
2174      }
2175    }
2176
2177    if(ret_val->data == NULL)
2178    {
2179      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse data record"
2180                        " while parsing VK record at offset 0x%.8X.", offset);
2181    }
2182  }
2183
2184  return ret_val;
2185}
2186
2187
2188uint8* regfi_parse_data(REGFI_FILE* file, uint32 offset, uint32 length,
2189                        uint32 max_size, bool strict)
2190{
2191  uint8* ret_val;
2192  uint32 read_length, cell_length;
2193  uint8 i;
2194  bool unalloc;
2195
2196  /* The data is typically stored in the offset if the size <= 4 */
2197  if (length & REGFI_VK_DATA_IN_OFFSET)
2198  {
2199    length = length & ~REGFI_VK_DATA_IN_OFFSET;
2200    if(length > 4)
2201    {
2202      regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
2203                        " while parsing data record at offset 0x%.8X.", 
2204                        offset);
2205      return NULL;
2206    }
2207
2208    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
2209      return NULL;
2210
2211    for(i = 0; i < length; i++)
2212      ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
2213  }
2214  else
2215  {
2216    if(!regfi_parse_cell(file->fd, offset, NULL, 0,
2217                         &cell_length, &unalloc))
2218    {
2219      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
2220                        " parsing data record at offset 0x%.8X.", offset);
2221      return NULL;
2222    }
2223
2224    if((cell_length & 0xFFFFFFF8) != cell_length)
2225    {
2226      regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
2227                        " while parsing data record at offset 0x%.8X.",
2228                        offset);
2229      return NULL;
2230    }
2231
2232    if(cell_length > max_size)
2233    {
2234      regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past hbin boundary"
2235                        " while parsing data record at offset 0x%.8X.", 
2236                        offset);
2237      if(strict)
2238        return NULL;
2239      else
2240        cell_length = max_size;
2241    }
2242
2243    if(cell_length - 4 < length)
2244    {
2245      /* XXX: This strict condition has been triggered in multiple registries.
2246       *      Not sure the cause, but the data length values are very large,
2247       *      such as 53392.
2248       */
2249      regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than"
2250                        " remaining cell length (0x%.8X)"
2251                        " while parsing data record at offset 0x%.8X.", 
2252                        length, cell_length - 4, offset);
2253      if(strict)
2254        return NULL;
2255      else
2256        length = cell_length - 4;
2257    }
2258
2259    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
2260      return NULL;
2261
2262    read_length = length;
2263    if((regfi_read(file->fd, ret_val, &read_length) != 0) 
2264       || read_length != length)
2265    {
2266      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
2267                        " parsing data record at offset 0x%.8X.", offset);
2268      free(ret_val);
2269      return NULL;
2270    }
2271  }
2272
2273  return ret_val;
2274}
2275
2276
2277range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
2278{
2279  range_list* ret_val;
2280  REGFI_HBIN* hbin;
2281  const range_list_element* hbins_elem;
2282  uint32 i, num_hbins, curr_off, cell_len;
2283  bool is_unalloc;
2284
2285  ret_val = range_list_new();
2286  if(ret_val == NULL)
2287    return NULL;
2288
2289  num_hbins = range_list_size(file->hbins);
2290  for(i=0; i<num_hbins; i++)
2291  {
2292    hbins_elem = range_list_get(file->hbins, i);
2293    if(hbins_elem == NULL)
2294      break;
2295    hbin = (REGFI_HBIN*)hbins_elem->data;
2296
2297    curr_off = REGFI_HBIN_HEADER_SIZE;
2298    while(curr_off < hbin->block_size)
2299    {
2300      if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
2301                           &cell_len, &is_unalloc))
2302        break;
2303     
2304      if((cell_len == 0) || ((cell_len & 0xFFFFFFF8) != cell_len))
2305        /* XXX: should report an error here. */
2306        break;
2307     
2308      /* for some reason the record_size of the last record in
2309         an hbin block can extend past the end of the block
2310         even though the record fits within the remaining
2311         space....aaarrrgggghhhhhh */ 
2312      if(curr_off + cell_len >= hbin->block_size)
2313        cell_len = hbin->block_size - curr_off;
2314     
2315      if(is_unalloc)
2316        range_list_add(ret_val, hbin->file_off+curr_off, 
2317                       cell_len, NULL);
2318     
2319      curr_off = curr_off+cell_len;
2320    }
2321  }
2322
2323  return ret_val;
2324}
Note: See TracBrowser for help on using the repository browser.