source: trunk/lib/regfi.c @ 145

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

decoupled value parsing from key parsing

moved linking of value records and data records up to the load layer

rewrote key/value/data linking algorithm in reglookup-recover which improved recovery results

fixed a NULL pointer dereference in range_list.c

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