source: trunk/lib/regfi.c @ 137

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

Added error messages to most parse functions in regfi

Relaxed validation requirements on NK types and missing value data

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