source: trunk/lib/regfi.c @ 157

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

reorganized data parsing in regfi

simplified some length validation

added big data support to reglookup-recover

fixed reglookup-recover's handling of data values in the offset

  • Property svn:keywords set to Id
File size: 72.3 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 157 2009-11-23 00:47:22Z tim $
24 */
25
26#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 += sprintf(ret_val+size, "%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(const 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 physical offset and receive the correpsonding HBIN
493 * block for it.  NULL if one doesn't exist.
494 ******************************************************************************/
495const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 offset)
496{
497  return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset);
498}
499
500
501/******************************************************************************
502 * Calculate the largest possible cell size given a physical offset.
503 * Largest size is based on the HBIN the offset is currently a member of.
504 * Returns negative values on error.
505 * (Since cells can only be ~2^31 in size, this works out.)
506 ******************************************************************************/
507int32 regfi_calc_maxsize(REGFI_FILE* file, uint32 offset)
508{
509  const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset);
510  if(hbin == NULL)
511    return -1;
512
513  return (hbin->block_size + hbin->file_off) - offset;
514}
515
516
517/******************************************************************************
518 ******************************************************************************/
519REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset, 
520                                         uint32 num_keys, uint32 max_size, 
521                                         bool strict)
522{
523  REGFI_SUBKEY_LIST* ret_val;
524
525  ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, 
526                                      REGFI_MAX_SUBKEY_DEPTH);
527  if(ret_val == NULL)
528  {
529    regfi_add_message(file, REGFI_MSG_WARN, "Failed to load subkey list at"
530                      " offset 0x%.8X.", offset);
531    return NULL;
532  }
533
534  if(num_keys != ret_val->num_keys)
535  {
536    /*  Not sure which should be authoritative, the number from the
537     *  NK record, or the number in the subkey list.  Just emit a warning for
538     *  now if they don't match.
539     */
540    regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent"
541                      " (%d) did not match number found in subkey list/tree (%d)"
542                      " while parsing subkey list/tree at offset 0x%.8X.", 
543                      num_keys, ret_val->num_keys, offset);
544  }
545
546  return ret_val;
547}
548
549
550/******************************************************************************
551 ******************************************************************************/
552REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset, 
553                                             uint32 max_size, bool strict,
554                                             uint8 depth_left)
555{
556  REGFI_SUBKEY_LIST* ret_val;
557  REGFI_SUBKEY_LIST** sublists;
558  uint32 i, num_sublists, off;
559  int32 sublist_maxsize;
560
561  if(depth_left == 0)
562  {
563    regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached"
564                      " while parsing subkey list/tree at offset 0x%.8X.", 
565                      offset);
566    return NULL;
567  }
568
569  ret_val = regfi_parse_subkeylist(file, offset, max_size, strict);
570  if(ret_val == NULL)
571    return NULL;
572
573  if(ret_val->recursive_type)
574  {
575    num_sublists = ret_val->num_children;
576    sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists
577                                           * sizeof(REGFI_SUBKEY_LIST*));
578    for(i=0; i < num_sublists; i++)
579    {
580      off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
581
582      sublist_maxsize = regfi_calc_maxsize(file, off);
583      if(sublist_maxsize < 0)
584        sublists[i] = NULL;
585      else
586        sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, 
587                                                strict, depth_left-1);
588    }
589    talloc_free(ret_val);
590
591    return regfi_merge_subkeylists(num_sublists, sublists, strict);
592  }
593
594  return ret_val;
595}
596
597
598/******************************************************************************
599 ******************************************************************************/
600REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset, 
601                                          uint32 max_size, bool strict)
602{
603  REGFI_SUBKEY_LIST* ret_val;
604  uint32 i, cell_length, length, elem_size, read_len;
605  uint8* elements = NULL;
606  uint8 buf[REGFI_SUBKEY_LIST_MIN_LEN];
607  bool unalloc;
608  bool recursive_type;
609
610  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN, 
611                       &cell_length, &unalloc))
612  {
613    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while "
614                      "parsing subkey-list at offset 0x%.8X.", offset);
615    return NULL;
616  }
617
618  if(cell_length > max_size)
619  {
620    regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size"
621                      " while parsing subkey-list at offset 0x%.8X.", offset);
622    if(strict)
623      return NULL;
624    cell_length = max_size & 0xFFFFFFF8;
625  }
626
627  recursive_type = false;
628  if(buf[0] == 'r' && buf[1] == 'i')
629  {
630    recursive_type = true;
631    elem_size = sizeof(uint32);
632  }
633  else if(buf[0] == 'l' && buf[1] == 'i')
634    elem_size = sizeof(uint32);
635  else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
636    elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
637  else
638  {
639    regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number"
640                      " (0x%.2X, 0x%.2X) encountered while parsing"
641                      " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
642    return NULL;
643  }
644
645  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
646  if(ret_val == NULL)
647    return NULL;
648
649  ret_val->offset = offset;
650  ret_val->cell_size = cell_length;
651  ret_val->magic[0] = buf[0];
652  ret_val->magic[1] = buf[1];
653  ret_val->recursive_type = recursive_type;
654  ret_val->num_children = SVAL(buf, 0x2);
655
656  if(!recursive_type)
657    ret_val->num_keys = ret_val->num_children;
658
659  length = elem_size*ret_val->num_children;
660  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32) < length)
661  {
662    regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
663                      " cell while parsing subkey-list at offset 0x%.8X.", 
664                      offset);
665    if(strict)
666      goto fail;
667    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32);
668  }
669
670  ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, 
671                                   ret_val->num_children);
672  if(ret_val->elements == NULL)
673    goto fail;
674
675  elements = (uint8*)malloc(length);
676  if(elements == NULL)
677    goto fail;
678
679  read_len = length;
680  if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
681    goto fail;
682
683  if(elem_size == sizeof(uint32))
684  {
685    for (i=0; i < ret_val->num_children; i++)
686    {
687      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
688      ret_val->elements[i].hash = 0;
689    }
690  }
691  else
692  {
693    for (i=0; i < ret_val->num_children; i++)
694    {
695      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
696      ret_val->elements[i].hash = IVAL(elements, i*elem_size+4);
697    }
698  }
699  free(elements);
700
701  return ret_val;
702
703 fail:
704  if(elements != NULL)
705    free(elements);
706  talloc_free(ret_val);
707  return NULL;
708}
709
710
711/*******************************************************************
712 *******************************************************************/
713REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists, 
714                                           REGFI_SUBKEY_LIST** lists,
715                                           bool strict)
716{
717  uint32 i,j,k;
718  REGFI_SUBKEY_LIST* ret_val;
719
720  if(lists == NULL)
721    return NULL;
722  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
723
724  if(ret_val == NULL)
725    return NULL;
726 
727  /* Obtain total number of elements */
728  ret_val->num_keys = 0;
729  for(i=0; i < num_lists; i++)
730  {
731    if(lists[i] != NULL)
732      ret_val->num_keys += lists[i]->num_children;
733  }
734  ret_val->num_children = ret_val->num_keys;
735
736  if(ret_val->num_keys > 0)
737  {
738    ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
739                                     ret_val->num_keys);
740    k=0;
741
742    if(ret_val->elements != NULL)
743    {
744      for(i=0; i < num_lists; i++)
745      {
746        if(lists[i] != NULL)
747        {
748          for(j=0; j < lists[i]->num_keys; j++)
749          {
750            ret_val->elements[k].hash = lists[i]->elements[j].hash;
751            ret_val->elements[k++].offset = lists[i]->elements[j].offset;
752          }
753        }
754      }
755    }
756  }
757 
758  for(i=0; i < num_lists; i++)
759    regfi_subkeylist_free(lists[i]);
760  free(lists);
761
762  return ret_val;
763}
764
765
766/******************************************************************************
767 *
768 ******************************************************************************/
769REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size, 
770                             bool strict)
771{
772  REGFI_SK_REC* ret_val;
773  uint8* sec_desc_buf = NULL;
774  uint32 cell_length, length;
775  uint8 sk_header[REGFI_SK_MIN_LENGTH];
776  bool unalloc = false;
777
778  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
779                       &cell_length, &unalloc))
780  {
781    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell"
782                      " at offset 0x%.8X.", offset);
783    return NULL;
784  }
785   
786  if(sk_header[0] != 's' || sk_header[1] != 'k')
787  {
788    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
789                      " SK record at offset 0x%.8X.", offset);
790    return NULL;
791  }
792
793  ret_val = talloc(NULL, REGFI_SK_REC);
794  if(ret_val == NULL)
795    return NULL;
796
797  ret_val->offset = offset;
798  /* XXX: Is there a way to be more conservative (shorter) with
799   *      cell length when cell is unallocated?
800   */
801  ret_val->cell_size = cell_length;
802
803  if(ret_val->cell_size > max_size)
804    ret_val->cell_size = max_size & 0xFFFFFFF8;
805  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
806     || (strict && (ret_val->cell_size & 0x00000007) != 0))
807  {
808    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
809                      " parsing SK record at offset 0x%.8X.", offset);
810    goto fail;
811  }
812
813  ret_val->magic[0] = sk_header[0];
814  ret_val->magic[1] = sk_header[1];
815
816  ret_val->unknown_tag = SVAL(sk_header, 0x2);
817  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
818  ret_val->next_sk_off = IVAL(sk_header, 0x8);
819  ret_val->ref_count = IVAL(sk_header, 0xC);
820  ret_val->desc_size = IVAL(sk_header, 0x10);
821
822  if((ret_val->prev_sk_off & 0x00000007) != 0
823     || (ret_val->next_sk_off & 0x00000007) != 0)
824  {
825    regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
826                      " are not a multiple of 8 while parsing SK record at"
827                      " offset 0x%.8X.", offset);
828    goto fail;
829  }
830
831  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
832  {
833    regfi_add_message(file, REGFI_MSG_WARN, "Security descriptor too large for"
834                      " cell while parsing SK record at offset 0x%.8X.", 
835                      offset);
836    goto fail;
837  }
838
839  sec_desc_buf = (uint8*)malloc(ret_val->desc_size);
840  if(sec_desc_buf == NULL)
841    goto fail;
842
843  length = ret_val->desc_size;
844  if(regfi_read(file->fd, sec_desc_buf, &length) != 0 
845     || length != ret_val->desc_size)
846  {
847    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security"
848                      " descriptor while parsing SK record at offset 0x%.8X.",
849                      offset);
850    goto fail;
851  }
852
853  if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, 
854                                                   ret_val->desc_size)))
855  {
856    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security"
857                      " descriptor while parsing SK record at offset 0x%.8X.",
858                      offset);
859    goto fail;
860  }
861
862  free(sec_desc_buf);
863  return ret_val;
864
865 fail:
866  if(sec_desc_buf != NULL)
867    free(sec_desc_buf);
868  talloc_free(ret_val);
869  return NULL;
870}
871
872
873REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset, 
874                                        uint32 num_values, bool strict)
875{
876  REGFI_VALUE_LIST* ret_val;
877  uint32 i, cell_length, length, read_len;
878  bool unalloc;
879
880  if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
881  {
882    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header"
883                      " while parsing value list at offset 0x%.8X.", offset);
884    return NULL;
885  }
886
887  if((cell_length & 0x00000007) != 0)
888  {
889    regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
890                      " while parsing value list at offset 0x%.8X.", offset);
891    if(strict)
892      return NULL;
893    cell_length = cell_length & 0xFFFFFFF8;
894  }
895
896  if((num_values * sizeof(uint32)) > cell_length-sizeof(uint32))
897  {
898    regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
899                      " while parsing value list at offset 0x%.8X.", offset);
900    if(strict)
901      return NULL;
902    num_values = cell_length/sizeof(uint32) - sizeof(uint32);
903  }
904
905  read_len = num_values*sizeof(uint32);
906  ret_val = talloc(NULL, REGFI_VALUE_LIST);
907  if(ret_val == NULL)
908    return NULL;
909
910  ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
911  if(ret_val->elements == NULL)
912  {
913    talloc_free(ret_val);
914    return NULL;
915  }
916  ret_val->num_values = num_values;
917
918  length = read_len;
919  if((regfi_read(file->fd, (uint8*)ret_val->elements, &length) != 0) 
920     || length != read_len)
921  {
922    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
923                      " while parsing value list at offset 0x%.8X.", offset);
924    talloc_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] & 0x00000007) != 0))
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        talloc_free(ret_val);
945        return NULL;
946      }
947    }
948  }
949
950  return ret_val;
951}
952
953
954
955/******************************************************************************
956 ******************************************************************************/
957REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset, bool strict)
958{
959  REGFI_VK_REC* ret_val = NULL;
960  REGFI_BUFFER data;
961  int32 max_size;
962
963  max_size = regfi_calc_maxsize(file, offset);
964  if(max_size < 0)
965    return NULL;
966 
967  ret_val = regfi_parse_vk(file, offset, max_size, strict);
968  if(ret_val == NULL)
969    return NULL;
970
971  if(ret_val->data_size == 0)
972    ret_val->data = NULL;
973  else
974  {
975    data = regfi_load_data(file, ret_val->data_off, ret_val->data_size,
976                           ret_val->data_in_offset, strict);
977    ret_val->data = data.buf;
978    ret_val->data_size = data.len;
979
980    if(ret_val->data == NULL)
981    {
982      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse data record"
983                        " while parsing VK record at offset 0x%.8X.",
984                        ret_val->offset, ret_val->valuename);
985    }
986    else
987      talloc_steal(ret_val, ret_val->data);
988  }
989
990  return ret_val;
991}
992
993
994/******************************************************************************
995 * If !strict, the list may contain NULLs, VK records may point to NULL.
996 ******************************************************************************/
997REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset, 
998                                       uint32 num_values, uint32 max_size,
999                                       bool strict)
1000{
1001  uint32 usable_num_values;
1002
1003  if((num_values+1) * sizeof(uint32) > max_size)
1004  {
1005    regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
1006                      " parent key (%d) would cause cell to straddle HBIN"
1007                      " boundary while loading value list at offset"
1008                      " 0x%.8X.", num_values, offset);
1009    if(strict)
1010      return NULL;
1011    usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
1012  }
1013  else
1014    usable_num_values = num_values;
1015
1016  return regfi_parse_valuelist(file, offset, usable_num_values, strict);
1017}
1018
1019
1020
1021/******************************************************************************
1022 *
1023 ******************************************************************************/
1024REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
1025{
1026  REGFI_NK_REC* nk;
1027  uint32 off;
1028  int32 max_size;
1029
1030  max_size = regfi_calc_maxsize(file, offset);
1031  if (max_size < 0) 
1032    return NULL;
1033
1034  /* get the initial nk record */
1035  if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
1036  {
1037    regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
1038                      " offset 0x%.8X.", offset);
1039    return NULL;
1040  }
1041
1042  /* get value list */
1043  if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
1044  {
1045    off = nk->values_off + REGFI_REGF_SIZE;
1046    max_size = regfi_calc_maxsize(file, off);
1047    if(max_size < 0)
1048    {
1049      if(strict)
1050      {
1051        regfi_free_key(nk);
1052        return NULL;
1053      }
1054      else
1055        nk->values = NULL;
1056
1057    }
1058    else
1059    {
1060      nk->values = regfi_load_valuelist(file, off, nk->num_values, 
1061                                        max_size, true);
1062      if(nk->values == NULL)
1063      {
1064        regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
1065                          " for NK record at offset 0x%.8X.", offset);
1066        if(strict)
1067        {
1068          regfi_free_key(nk);
1069          return NULL;
1070        }
1071      }
1072      talloc_steal(nk, nk->values);
1073    }
1074  }
1075
1076  /* now get subkey list */
1077  if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
1078  {
1079    off = nk->subkeys_off + REGFI_REGF_SIZE;
1080    max_size = regfi_calc_maxsize(file, off);
1081    if(max_size < 0) 
1082    {
1083      if(strict)
1084      {
1085        regfi_free_key(nk);
1086        return NULL;
1087      }
1088      else
1089        nk->subkeys = NULL;
1090    }
1091    else
1092    {
1093      nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
1094                                          max_size, true);
1095
1096      if(nk->subkeys == NULL)
1097      {
1098        regfi_add_message(file, REGFI_MSG_WARN, "Could not load subkey list"
1099                          " while parsing NK record at offset 0x%.8X.", offset);
1100        nk->num_subkeys = 0;
1101      }
1102      talloc_steal(nk, nk->subkeys);
1103    }
1104  }
1105
1106  return nk;
1107}
1108
1109
1110/******************************************************************************
1111 ******************************************************************************/
1112const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
1113{
1114  REGFI_SK_REC* ret_val = NULL;
1115  int32 max_size;
1116  void* failure_ptr = NULL;
1117 
1118  /* First look if we have already parsed it */
1119  ret_val = (REGFI_SK_REC*)lru_cache_find(file->sk_cache, &offset, 4);
1120
1121  /* Bail out if we have previously cached a parse failure at this offset. */
1122  if(ret_val == (void*)REGFI_OFFSET_NONE)
1123    return NULL;
1124
1125  if(ret_val == NULL)
1126  {
1127    max_size = regfi_calc_maxsize(file, offset);
1128    if(max_size < 0)
1129      return NULL;
1130
1131    ret_val = regfi_parse_sk(file, offset, max_size, strict);
1132    if(ret_val == NULL)
1133    { /* Cache the parse failure and bail out. */
1134      failure_ptr = talloc(NULL, uint32_t);
1135      if(failure_ptr == NULL)
1136        return NULL;
1137      *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
1138      lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
1139      return NULL;
1140    }
1141
1142    lru_cache_update(file->sk_cache, &offset, 4, ret_val);
1143  }
1144
1145  return ret_val;
1146}
1147
1148
1149
1150/******************************************************************************
1151 ******************************************************************************/
1152static bool regfi_find_root_nk(REGFI_FILE* file, uint32 offset,uint32 hbin_size,
1153                               uint32* root_offset)
1154{
1155  uint8 tmp[4];
1156  int32 record_size;
1157  uint32 length, hbin_offset = 0;
1158  REGFI_NK_REC* nk = NULL;
1159  bool found = false;
1160
1161  for(record_size=0; !found && (hbin_offset < hbin_size); )
1162  {
1163    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
1164      return false;
1165   
1166    length = 4;
1167    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
1168      return false;
1169    record_size = IVALS(tmp, 0);
1170
1171    if(record_size < 0)
1172    {
1173      record_size = record_size*(-1);
1174      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
1175      if(nk != NULL)
1176      {
1177        if(nk->key_type & REGFI_NK_FLAG_ROOT)
1178        {
1179          found = true;
1180          *root_offset = nk->offset;
1181        }
1182        regfi_free_key(nk);
1183      }
1184    }
1185
1186    hbin_offset += record_size;
1187  }
1188
1189  return found;
1190}
1191
1192
1193/*******************************************************************
1194 * Open the registry file and then read in the REGF block to get the
1195 * first hbin offset.
1196 *******************************************************************/
1197REGFI_FILE* regfi_open(const char* filename)
1198{
1199  struct stat sbuf;
1200  REGFI_FILE* rb;
1201  REGFI_HBIN* hbin = NULL;
1202  uint32 hbin_off, file_length, cache_secret;
1203  int fd;
1204  bool rla;
1205
1206  /* open an existing file */
1207  if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
1208  {
1209    /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
1210    return NULL;
1211  }
1212 
1213  /* Determine file length.  Must be at least big enough
1214   * for the header and one hbin.
1215   */
1216  if (fstat(fd, &sbuf) == -1)
1217    return NULL;
1218  file_length = sbuf.st_size;
1219  if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
1220    return NULL;
1221
1222  /* read in an existing file */
1223  if ((rb = regfi_parse_regf(fd, true)) == NULL) 
1224  {
1225    /* fprintf(stderr, "regfi_open: Failed to read initial REGF block\n"); */
1226    close(fd);
1227    return NULL;
1228  }
1229  rb->file_length = file_length; 
1230
1231  rb->hbins = range_list_new();
1232  if(rb->hbins == NULL)
1233  {
1234    /* fprintf(stderr, "regfi_open: Failed to create HBIN list.\n"); */
1235    close(fd);
1236    talloc_free(rb);
1237    return NULL;
1238  }
1239  talloc_steal(rb, rb->hbins);
1240
1241  rla = true;
1242  hbin_off = REGFI_REGF_SIZE;
1243  hbin = regfi_parse_hbin(rb, hbin_off, true);
1244  while(hbin && rla)
1245  {
1246    rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
1247    if(rla)
1248      talloc_steal(rb->hbins, hbin);
1249    hbin_off = hbin->file_off + hbin->block_size;
1250    hbin = regfi_parse_hbin(rb, hbin_off, true);
1251  }
1252
1253  /* This secret isn't very secret, but we don't need a good one.  This
1254   * secret is just designed to prevent someone from trying to blow our
1255   * caching and make things slow.
1256   */
1257  cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
1258
1259  /* Cache an unlimited number of SK records.  Typically there are very few. */
1260  rb->sk_cache = lru_cache_create_ctx(rb, 0, cache_secret, true);
1261
1262  /* Default message mask */
1263  rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN;
1264
1265  /* success */
1266  return rb;
1267}
1268
1269
1270/******************************************************************************
1271 ******************************************************************************/
1272int regfi_close(REGFI_FILE *file)
1273{
1274  int fd;
1275
1276  /* nothing to do if there is no open file */
1277  if ((file == NULL) || (file->fd == -1))
1278    return 0;
1279
1280  fd = file->fd;
1281  file->fd = -1;
1282
1283  range_list_free(file->hbins);
1284
1285  if(file->sk_cache != NULL)
1286    lru_cache_destroy(file->sk_cache);
1287
1288  talloc_free(file);
1289  return close(fd);
1290}
1291
1292
1293/******************************************************************************
1294 * There should be only *one* root key in the registry file based
1295 * on my experience.  --jerry
1296 ******************************************************************************/
1297REGFI_NK_REC* regfi_rootkey(REGFI_FILE *file)
1298{
1299  REGFI_NK_REC* nk = NULL;
1300  REGFI_HBIN* hbin;
1301  uint32 root_offset, i, num_hbins;
1302 
1303  if(!file)
1304    return NULL;
1305
1306  /* Scan through the file one HBIN block at a time looking
1307   * for an NK record with a root key type.
1308   * This is typically the first NK record in the first HBIN
1309   * block (but we're not assuming that generally).
1310   */
1311  num_hbins = range_list_size(file->hbins);
1312  for(i=0; i < num_hbins; i++)
1313  {
1314    hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
1315    if(regfi_find_root_nk(file, hbin->file_off+REGFI_HBIN_HEADER_SIZE, 
1316                          hbin->block_size-REGFI_HBIN_HEADER_SIZE, &root_offset))
1317    {
1318      nk = regfi_load_key(file, root_offset, true);
1319      break;
1320    }
1321  }
1322
1323  return nk;
1324}
1325
1326
1327/******************************************************************************
1328 *****************************************************************************/
1329void regfi_free_key(REGFI_NK_REC* nk)
1330{
1331  regfi_subkeylist_free(nk->subkeys);
1332  talloc_free(nk);
1333}
1334
1335
1336/******************************************************************************
1337 *****************************************************************************/
1338void regfi_free_value(REGFI_VK_REC* vk)
1339{
1340  talloc_free(vk);
1341}
1342
1343
1344/******************************************************************************
1345 *****************************************************************************/
1346void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
1347{
1348  if(list != NULL)
1349  {
1350    talloc_free(list);
1351  }
1352}
1353
1354
1355/******************************************************************************
1356 *****************************************************************************/
1357REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
1358{
1359  REGFI_NK_REC* root;
1360  REGFI_ITERATOR* ret_val = talloc(NULL, REGFI_ITERATOR);
1361  if(ret_val == NULL)
1362    return NULL;
1363
1364  root = regfi_rootkey(fh);
1365  if(root == NULL)
1366  {
1367    talloc_free(ret_val);
1368    return NULL;
1369  }
1370
1371  ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH);
1372  if(ret_val->key_positions == NULL)
1373  {
1374    talloc_free(ret_val);
1375    return NULL;
1376  }
1377  talloc_steal(ret_val, ret_val->key_positions);
1378
1379  ret_val->f = fh;
1380  ret_val->cur_key = root;
1381  ret_val->cur_subkey = 0;
1382  ret_val->cur_value = 0;
1383
1384  return ret_val;
1385}
1386
1387
1388/******************************************************************************
1389 *****************************************************************************/
1390void regfi_iterator_free(REGFI_ITERATOR* i)
1391{
1392  talloc_free(i);
1393}
1394
1395
1396
1397/******************************************************************************
1398 *****************************************************************************/
1399/* XXX: some way of indicating reason for failure should be added. */
1400bool regfi_iterator_down(REGFI_ITERATOR* i)
1401{
1402  REGFI_NK_REC* subkey;
1403  REGFI_ITER_POSITION* pos;
1404
1405  pos = talloc(i->key_positions, REGFI_ITER_POSITION);
1406  if(pos == NULL)
1407    return false;
1408
1409  subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i);
1410  if(subkey == NULL)
1411  {
1412    talloc_free(pos);
1413    return false;
1414  }
1415
1416  pos->nk = i->cur_key;
1417  pos->cur_subkey = i->cur_subkey;
1418  if(!void_stack_push(i->key_positions, pos))
1419  {
1420    talloc_free(pos);
1421    regfi_free_key(subkey);
1422    return false;
1423  }
1424  talloc_steal(i, subkey);
1425
1426  i->cur_key = subkey;
1427  i->cur_subkey = 0;
1428  i->cur_value = 0;
1429
1430  return true;
1431}
1432
1433
1434/******************************************************************************
1435 *****************************************************************************/
1436bool regfi_iterator_up(REGFI_ITERATOR* i)
1437{
1438  REGFI_ITER_POSITION* pos;
1439
1440  pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions);
1441  if(pos == NULL)
1442    return false;
1443
1444  regfi_free_key(i->cur_key);
1445  i->cur_key = pos->nk;
1446  i->cur_subkey = pos->cur_subkey;
1447  i->cur_value = 0;
1448  talloc_free(pos);
1449
1450  return true;
1451}
1452
1453
1454/******************************************************************************
1455 *****************************************************************************/
1456bool regfi_iterator_to_root(REGFI_ITERATOR* i)
1457{
1458  while(regfi_iterator_up(i))
1459    continue;
1460
1461  return true;
1462}
1463
1464
1465/******************************************************************************
1466 *****************************************************************************/
1467bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
1468{
1469  REGFI_NK_REC* subkey;
1470  bool found = false;
1471  uint32 old_subkey = i->cur_subkey;
1472
1473  if(subkey_name == NULL)
1474    return false;
1475
1476  /* XXX: this alloc/free of each sub key might be a bit excessive */
1477  subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i);
1478  while((subkey != NULL) && (found == false))
1479  {
1480    if(subkey->keyname != NULL 
1481       && strcasecmp(subkey->keyname, subkey_name) == 0)
1482      found = true;
1483    else
1484    {
1485      regfi_free_key(subkey);
1486      subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i);
1487    }
1488  }
1489
1490  if(found == false)
1491  {
1492    i->cur_subkey = old_subkey;
1493    return false;
1494  }
1495
1496  regfi_free_key(subkey);
1497  return true;
1498}
1499
1500
1501/******************************************************************************
1502 *****************************************************************************/
1503bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
1504{
1505  uint32 x;
1506  if(path == NULL)
1507    return false;
1508
1509  for(x=0; 
1510      ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x])
1511       && regfi_iterator_down(i));
1512      x++)
1513  { continue; }
1514
1515  if(path[x] == NULL)
1516    return true;
1517 
1518  /* XXX: is this the right number of times? */
1519  for(; x > 0; x--)
1520    regfi_iterator_up(i);
1521 
1522  return false;
1523}
1524
1525
1526/******************************************************************************
1527 *****************************************************************************/
1528const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
1529{
1530  return i->cur_key;
1531}
1532
1533
1534/******************************************************************************
1535 *****************************************************************************/
1536const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
1537{
1538  if(i->cur_key == NULL || i->cur_key->sk_off == REGFI_OFFSET_NONE)
1539    return NULL;
1540
1541  return regfi_load_sk(i->f, i->cur_key->sk_off + REGFI_REGF_SIZE, true);
1542}
1543
1544
1545/******************************************************************************
1546 *****************************************************************************/
1547REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
1548{
1549  i->cur_subkey = 0;
1550  return regfi_iterator_cur_subkey(i);
1551}
1552
1553
1554/******************************************************************************
1555 *****************************************************************************/
1556REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
1557{
1558  uint32 nk_offset;
1559
1560  /* see if there is anything left to report */
1561  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
1562      || (i->cur_subkey >= i->cur_key->num_subkeys))
1563    return NULL;
1564
1565  nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
1566
1567  return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, true);
1568}
1569
1570
1571/******************************************************************************
1572 *****************************************************************************/
1573/* XXX: some way of indicating reason for failure should be added. */
1574REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
1575{
1576  REGFI_NK_REC* subkey;
1577
1578  i->cur_subkey++;
1579  subkey = regfi_iterator_cur_subkey(i);
1580
1581  if(subkey == NULL)
1582    i->cur_subkey--;
1583
1584  return subkey;
1585}
1586
1587
1588/******************************************************************************
1589 *****************************************************************************/
1590bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
1591{
1592  REGFI_VK_REC* cur;
1593  bool found = false;
1594
1595  /* XXX: cur->valuename can be NULL in the registry. 
1596   *      Should we allow for a way to search for that?
1597   */
1598  if(value_name == NULL)
1599    return false;
1600
1601  cur = regfi_iterator_first_value(i);
1602  while((cur != NULL) && (found == false))
1603  {
1604    if((cur->valuename != NULL)
1605       && (strcasecmp(cur->valuename, value_name) == 0))
1606      found = true;
1607    else
1608    {
1609      regfi_free_value(cur);
1610      cur = regfi_iterator_next_value(i);
1611    }
1612  }
1613
1614  return found;
1615}
1616
1617
1618/******************************************************************************
1619 *****************************************************************************/
1620REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
1621{
1622  i->cur_value = 0;
1623  return regfi_iterator_cur_value(i);
1624}
1625
1626
1627/******************************************************************************
1628 *****************************************************************************/
1629REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
1630{
1631  REGFI_VK_REC* ret_val = NULL;
1632  uint32 voffset;
1633
1634  if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
1635  {
1636    if(i->cur_value < i->cur_key->values->num_values)
1637    {
1638      voffset = i->cur_key->values->elements[i->cur_value];
1639      ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, true);
1640    }
1641  }
1642
1643  return ret_val;
1644}
1645
1646
1647/******************************************************************************
1648 *****************************************************************************/
1649REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
1650{
1651  REGFI_VK_REC* ret_val;
1652
1653  i->cur_value++;
1654  ret_val = regfi_iterator_cur_value(i);
1655  if(ret_val == NULL)
1656    i->cur_value--;
1657
1658  return ret_val;
1659}
1660
1661
1662/*******************************************************************
1663 * Computes the checksum of the registry file header.
1664 * buffer must be at least the size of an regf header (4096 bytes).
1665 *******************************************************************/
1666static uint32 regfi_compute_header_checksum(uint8* buffer)
1667{
1668  uint32 checksum, x;
1669  int i;
1670
1671  /* XOR of all bytes 0x0000 - 0x01FB */
1672
1673  checksum = x = 0;
1674 
1675  for ( i=0; i<0x01FB; i+=4 ) {
1676    x = IVAL(buffer, i );
1677    checksum ^= x;
1678  }
1679 
1680  return checksum;
1681}
1682
1683
1684/*******************************************************************
1685 * XXX: Add way to return more detailed error information.
1686 *******************************************************************/
1687REGFI_FILE* regfi_parse_regf(int fd, bool strict)
1688{
1689  uint8 file_header[REGFI_REGF_SIZE];
1690  uint32 length;
1691  REGFI_FILE* ret_val;
1692
1693  ret_val = talloc(NULL, REGFI_FILE);
1694  if(ret_val == NULL)
1695    return NULL;
1696
1697  ret_val->fd = fd;
1698  ret_val->sk_cache = NULL;
1699  ret_val->last_message = NULL;
1700  ret_val->hbins = NULL;
1701 
1702  length = REGFI_REGF_SIZE;
1703  if((regfi_read(fd, file_header, &length)) != 0 || length != REGFI_REGF_SIZE)
1704    goto fail;
1705 
1706  ret_val->checksum = IVAL(file_header, 0x1FC);
1707  ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
1708  if (strict && (ret_val->checksum != ret_val->computed_checksum))
1709    goto fail;
1710
1711  memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
1712  if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
1713  {
1714    if(strict)
1715      goto fail;
1716    regfi_add_message(ret_val, REGFI_MSG_WARN, "Magic number mismatch "
1717                      "(%.2X %.2X %.2X %.2X) while parsing hive header",
1718                      ret_val->magic[0], ret_val->magic[1], 
1719                      ret_val->magic[2], ret_val->magic[3]);
1720  }
1721  ret_val->sequence1 = IVAL(file_header, 0x4);
1722  ret_val->sequence2 = IVAL(file_header, 0x8);
1723  ret_val->mtime.low = IVAL(file_header, 0xC);
1724  ret_val->mtime.high = IVAL(file_header, 0x10);
1725  ret_val->major_version = IVAL(file_header, 0x14);
1726  ret_val->minor_version = IVAL(file_header, 0x18);
1727  ret_val->type = IVAL(file_header, 0x1C);
1728  ret_val->format = IVAL(file_header, 0x20);
1729  ret_val->root_cell = IVAL(file_header, 0x24);
1730  ret_val->last_block = IVAL(file_header, 0x28);
1731
1732  ret_val->cluster = IVAL(file_header, 0x2C);
1733
1734  memcpy(ret_val->file_name, file_header+0x30,  REGFI_REGF_NAME_SIZE);
1735
1736  /* XXX: Should we add a warning if these uuid parsers fail?  Can they? */
1737  ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
1738  ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
1739  ret_val->flags = IVAL(file_header, 0x90);
1740  ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
1741  ret_val->guid_signature = IVAL(file_header, 0xa4);
1742
1743  memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
1744  memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE);
1745
1746  ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16);
1747  ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16);
1748  ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16);
1749  ret_val->boot_type = IVAL(file_header, 0xFF8);
1750  ret_val->boot_recover = IVAL(file_header, 0xFFC);
1751
1752  return ret_val;
1753
1754 fail:
1755  talloc_free(ret_val);
1756  return NULL;
1757}
1758
1759
1760
1761/******************************************************************************
1762 * Given real file offset, read and parse the hbin at that location
1763 * along with it's associated cells.
1764 ******************************************************************************/
1765REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
1766{
1767  REGFI_HBIN *hbin;
1768  uint8 hbin_header[REGFI_HBIN_HEADER_SIZE];
1769  uint32 length;
1770 
1771  if(offset >= file->file_length)
1772    return NULL;
1773
1774  if(lseek(file->fd, offset, SEEK_SET) == -1)
1775  {
1776    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
1777                      " while parsing hbin at offset 0x%.8X.", offset);
1778    return NULL;
1779  }
1780
1781  length = REGFI_HBIN_HEADER_SIZE;
1782  if((regfi_read(file->fd, hbin_header, &length) != 0) 
1783     || length != REGFI_HBIN_HEADER_SIZE)
1784    return NULL;
1785
1786  if(lseek(file->fd, offset, SEEK_SET) == -1)
1787  {
1788    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
1789                      " while parsing hbin at offset 0x%.8X.", offset);
1790    return NULL;
1791  }
1792
1793  hbin = talloc(NULL, REGFI_HBIN);
1794  if(hbin == NULL)
1795    return NULL;
1796  hbin->file_off = offset;
1797
1798  memcpy(hbin->magic, hbin_header, 4);
1799  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
1800  {
1801    regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch "
1802                      "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
1803                      " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
1804                      hbin->magic[2], hbin->magic[3], offset);
1805    talloc_free(hbin);
1806    return NULL;
1807  }
1808
1809  hbin->first_hbin_off = IVAL(hbin_header, 0x4);
1810  hbin->block_size = IVAL(hbin_header, 0x8);
1811  /* this should be the same thing as hbin->block_size but just in case */
1812  hbin->next_block = IVAL(hbin_header, 0x1C);
1813
1814
1815  /* Ensure the block size is a multiple of 0x1000 and doesn't run off
1816   * the end of the file.
1817   */
1818  /* XXX: This may need to be relaxed for dealing with
1819   *      partial or corrupt files.
1820   */
1821  if((offset + hbin->block_size > file->file_length)
1822     || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
1823  {
1824    regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned"
1825                      " or runs off the end of the file"
1826                      " while parsing hbin at offset 0x%.8X.", offset);
1827    talloc_free(hbin);
1828    return NULL;
1829  }
1830
1831  return hbin;
1832}
1833
1834
1835/*******************************************************************
1836 *******************************************************************/
1837REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset, 
1838                            uint32 max_size, bool strict)
1839{
1840  uint8 nk_header[REGFI_NK_MIN_LENGTH];
1841  REGFI_NK_REC* ret_val;
1842  uint32 length,cell_length;
1843  uint32 class_offset;
1844  int32 class_maxsize;
1845  bool unalloc = false;
1846
1847  if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
1848                       &cell_length, &unalloc))
1849  {
1850    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
1851                      " while parsing NK record at offset 0x%.8X.", offset);
1852    return NULL;
1853  }
1854
1855  /* A bit of validation before bothering to allocate memory */
1856  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
1857  {
1858    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
1859                      " NK record at offset 0x%.8X.", offset);
1860    return NULL;
1861  }
1862
1863  ret_val = talloc(NULL, REGFI_NK_REC);
1864  if(ret_val == NULL)
1865  {
1866    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while"
1867                      " parsing NK record at offset 0x%.8X.", offset);
1868    return NULL;
1869  }
1870
1871  ret_val->values = NULL;
1872  ret_val->subkeys = NULL;
1873  ret_val->offset = offset;
1874  ret_val->cell_size = cell_length;
1875
1876  if(ret_val->cell_size > max_size)
1877    ret_val->cell_size = max_size & 0xFFFFFFF8;
1878  if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
1879     || (strict && (ret_val->cell_size & 0x00000007) != 0))
1880  {
1881    regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
1882                      " parsing NK record at offset 0x%.8X.", offset);
1883    talloc_free(ret_val);
1884    return NULL;
1885  }
1886
1887  ret_val->magic[0] = nk_header[0x0];
1888  ret_val->magic[1] = nk_header[0x1];
1889  ret_val->key_type = SVAL(nk_header, 0x2);
1890 
1891  if((ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS) != 0)
1892  {
1893    regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
1894                      " parsing NK record at offset 0x%.8X.", 
1895                      (ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS), offset);
1896  }
1897
1898  ret_val->mtime.low = IVAL(nk_header, 0x4);
1899  ret_val->mtime.high = IVAL(nk_header, 0x8);
1900  /* If the key is unallocated and the MTIME is earlier than Jan 1, 1990
1901   * or later than Jan 1, 2290, we consider this a bad key.  This helps
1902   * weed out some false positives during deleted data recovery.
1903   */
1904  if(unalloc
1905     && ((ret_val->mtime.high < REGFI_MTIME_MIN_HIGH
1906          && ret_val->mtime.low < REGFI_MTIME_MIN_LOW)
1907         || (ret_val->mtime.high > REGFI_MTIME_MAX_HIGH
1908             && ret_val->mtime.low > REGFI_MTIME_MAX_LOW)))
1909    return NULL;
1910
1911  ret_val->unknown1 = IVAL(nk_header, 0xC);
1912  ret_val->parent_off = IVAL(nk_header, 0x10);
1913  ret_val->num_subkeys = IVAL(nk_header, 0x14);
1914  ret_val->unknown2 = IVAL(nk_header, 0x18);
1915  ret_val->subkeys_off = IVAL(nk_header, 0x1C);
1916  ret_val->unknown3 = IVAL(nk_header, 0x20);
1917  ret_val->num_values = IVAL(nk_header, 0x24);
1918  ret_val->values_off = IVAL(nk_header, 0x28);
1919  ret_val->sk_off = IVAL(nk_header, 0x2C);
1920  ret_val->classname_off = IVAL(nk_header, 0x30);
1921
1922  ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
1923  ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38);
1924  ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C);
1925  ret_val->max_bytes_value = IVAL(nk_header, 0x40);
1926  ret_val->unk_index = IVAL(nk_header, 0x44);
1927
1928  ret_val->name_length = SVAL(nk_header, 0x48);
1929  ret_val->classname_length = SVAL(nk_header, 0x4A);
1930
1931  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
1932  {
1933    if(strict)
1934    {
1935      regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell"
1936                        " while parsing NK record at offset 0x%.8X.", offset);
1937      talloc_free(ret_val);
1938      return NULL;
1939    }
1940    else
1941      ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
1942  }
1943  else if (unalloc)
1944  { /* Truncate cell_size if it's much larger than the apparent total record length. */
1945    /* Round up to the next multiple of 8 */
1946    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
1947    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
1948      length+=8;
1949
1950    /* If cell_size is still greater, truncate. */
1951    if(length < ret_val->cell_size)
1952      ret_val->cell_size = length;
1953  }
1954
1955  ret_val->keyname = talloc_array(ret_val, char, ret_val->name_length+1);
1956  if(ret_val->keyname == NULL)
1957  {
1958    talloc_free(ret_val);
1959    return NULL;
1960  }
1961
1962  /* Don't need to seek, should be at the right offset */
1963  length = ret_val->name_length;
1964  if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
1965     || length != ret_val->name_length)
1966  {
1967    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
1968                      " while parsing NK record at offset 0x%.8X.", offset);
1969    talloc_free(ret_val);
1970    return NULL;
1971  }
1972  ret_val->keyname[ret_val->name_length] = '\0';
1973
1974  /* XXX: This linking should be moved up to regfi_load_key */
1975  if(ret_val->classname_off != REGFI_OFFSET_NONE)
1976  {
1977    class_offset = ret_val->classname_off + REGFI_REGF_SIZE;
1978    class_maxsize = regfi_calc_maxsize(file, class_offset);
1979    if(class_maxsize > 0)
1980    {
1981      ret_val->classname
1982        = regfi_parse_classname(file, class_offset, &ret_val->classname_length, 
1983                                class_maxsize, strict);
1984    }
1985    else
1986    {
1987      ret_val->classname = NULL;
1988      regfi_add_message(file, REGFI_MSG_WARN, "Could not find hbin for class"
1989                        " name while parsing NK record at offset 0x%.8X.", 
1990                        offset);
1991    }
1992
1993    if(ret_val->classname == NULL)
1994    {
1995      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse class"
1996                        " name while parsing NK record at offset 0x%.8X.", 
1997                        offset);
1998    }
1999    else
2000      talloc_steal(ret_val, ret_val->classname);
2001  }
2002
2003  return ret_val;
2004}
2005
2006
2007char* regfi_parse_classname(REGFI_FILE* file, uint32 offset, 
2008                            uint16* name_length, uint32 max_size, bool strict)
2009{
2010  char* ret_val = NULL;
2011  uint32 length;
2012  uint32 cell_length;
2013  bool unalloc = false;
2014
2015  if(*name_length > 0 && offset != REGFI_OFFSET_NONE
2016     && (offset & 0x00000007) == 0)
2017  {
2018    if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
2019    {
2020      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
2021                        " while parsing class name at offset 0x%.8X.", offset);
2022        return NULL;
2023    }
2024
2025    if((cell_length & 0x0000007) != 0)
2026    {
2027      regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
2028                        " while parsing class name at offset 0x%.8X.", offset);
2029      return NULL;
2030    }
2031
2032    if(cell_length > max_size)
2033    {
2034      regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin "
2035                        "boundary while parsing class name at offset 0x%.8X.",
2036                        offset);
2037      if(strict)
2038        return NULL;
2039      cell_length = max_size;
2040    }
2041
2042    if((cell_length - 4) < *name_length)
2043    {
2044      regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than"
2045                        " cell_length while parsing class name at offset"
2046                        " 0x%.8X.", offset);
2047      if(strict)
2048        return NULL;
2049      *name_length = cell_length - 4;
2050    }
2051   
2052    ret_val = talloc_array(NULL, char, *name_length);
2053    if(ret_val != NULL)
2054    {
2055      length = *name_length;
2056      if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0)
2057         || length != *name_length)
2058      {
2059        regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
2060                          " while parsing class name at offset 0x%.8X.", offset);
2061        talloc_free(ret_val);
2062        return NULL;
2063      }
2064    }
2065  }
2066
2067  return ret_val;
2068}
2069
2070
2071/******************************************************************************
2072*******************************************************************************/
2073REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset, 
2074                             uint32 max_size, bool strict)
2075{
2076  REGFI_VK_REC* ret_val;
2077  uint8 vk_header[REGFI_VK_MIN_LENGTH];
2078  uint32 raw_data_size, length, cell_length;
2079  bool unalloc = false;
2080
2081  if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
2082                       &cell_length, &unalloc))
2083  {
2084    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
2085                      " while parsing VK record at offset 0x%.8X.", offset);
2086    return NULL;
2087  }
2088
2089  ret_val = talloc(NULL, REGFI_VK_REC);
2090  if(ret_val == NULL)
2091    return NULL;
2092
2093  ret_val->offset = offset;
2094  ret_val->cell_size = cell_length;
2095  ret_val->data = NULL;
2096  ret_val->valuename = NULL;
2097 
2098  if(ret_val->cell_size > max_size)
2099    ret_val->cell_size = max_size & 0xFFFFFFF8;
2100  if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
2101     || (ret_val->cell_size & 0x00000007) != 0)
2102  {
2103    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
2104                      " while parsing VK record at offset 0x%.8X.", offset);
2105    talloc_free(ret_val);
2106    return NULL;
2107  }
2108
2109  ret_val->magic[0] = vk_header[0x0];
2110  ret_val->magic[1] = vk_header[0x1];
2111  if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
2112  {
2113    /* XXX: This does not account for deleted keys under Win2K which
2114     *      often have this (and the name length) overwritten with
2115     *      0xFFFF.
2116     */
2117    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch"
2118                      " while parsing VK record at offset 0x%.8X.", offset);
2119    talloc_free(ret_val);
2120    return NULL;
2121  }
2122
2123  ret_val->name_length = SVAL(vk_header, 0x2);
2124  raw_data_size = IVAL(vk_header, 0x4);
2125  ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
2126  /* The data is typically stored in the offset if the size <= 4,
2127   * in which case this flag is set.
2128   */
2129  ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
2130  ret_val->data_off = IVAL(vk_header, 0x8);
2131  ret_val->type = IVAL(vk_header, 0xC);
2132  ret_val->flag = SVAL(vk_header, 0x10);
2133  ret_val->unknown1 = SVAL(vk_header, 0x12);
2134
2135  if(ret_val->flag & REGFI_VK_FLAG_NAME_PRESENT)
2136  {
2137    if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
2138    {
2139      regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell"
2140                        " space while parsing VK record at offset 0x%.8X.",
2141                        offset);
2142      if(strict)
2143      {
2144        talloc_free(ret_val);
2145        return NULL;
2146      }
2147      else
2148        ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4;
2149    }
2150
2151    /* Round up to the next multiple of 8 */
2152    cell_length = (ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) & 0xFFFFFFF8;
2153    if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
2154      cell_length+=8;
2155
2156    ret_val->valuename = talloc_array(ret_val, char, ret_val->name_length+1);
2157    if(ret_val->valuename == NULL)
2158    {
2159      talloc_free(ret_val);
2160      return NULL;
2161    }
2162
2163    length = ret_val->name_length;
2164    if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
2165       || length != ret_val->name_length)
2166    {
2167      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
2168                        " while parsing VK record at offset 0x%.8X.", offset);
2169      talloc_free(ret_val);
2170      return NULL;
2171    }
2172    ret_val->valuename[ret_val->name_length] = '\0';
2173
2174  }
2175  else
2176    cell_length = REGFI_VK_MIN_LENGTH + 4;
2177
2178  if(unalloc)
2179  {
2180    /* If cell_size is still greater, truncate. */
2181    if(cell_length < ret_val->cell_size)
2182      ret_val->cell_size = cell_length;
2183  }
2184
2185  return ret_val;
2186}
2187
2188
2189/******************************************************************************
2190 *
2191 ******************************************************************************/
2192REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32 voffset, 
2193                             uint32 length, bool data_in_offset,
2194                             bool strict)
2195{
2196  REGFI_BUFFER ret_val;
2197  uint32 cell_length, offset;
2198  int32 max_size;
2199  bool unalloc;
2200 
2201  if(data_in_offset)
2202    return regfi_parse_little_data(file, voffset, length, strict);
2203  else
2204  {
2205    offset = voffset + REGFI_REGF_SIZE;
2206    max_size = regfi_calc_maxsize(file, offset);
2207    if(max_size < 0)
2208    {
2209      regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
2210                        " at offset 0x%.8X.", offset);
2211      goto fail;
2212    }
2213   
2214    if(!regfi_parse_cell(file->fd, offset, NULL, 0,
2215                         &cell_length, &unalloc))
2216    {
2217      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
2218                        " parsing data record at offset 0x%.8X.", offset);
2219      goto fail;
2220    }
2221
2222    if((cell_length & 0x00000007) != 0)
2223    {
2224      regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
2225                        " while parsing data record at offset 0x%.8X.",
2226                        offset);
2227      goto fail;
2228    }
2229
2230    if(cell_length > max_size)
2231    {
2232      regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
2233                        " while parsing data record at offset 0x%.8X.",
2234                        offset);
2235      goto fail;
2236    }
2237
2238    if(cell_length - 4 < length)
2239    {
2240      /* XXX: All big data records thus far have been 16 bytes long. 
2241       *      Should we check for this precise size instead of just
2242       *      relying upon the above check?
2243       */
2244      if (file->major_version >= 1 && file->minor_version >= 5)
2245      {
2246        /* Attempt to parse a big data record */
2247        return regfi_load_big_data(file, offset, length, cell_length, 
2248                                   NULL, strict);
2249      }
2250      else
2251      {
2252        regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than"
2253                          " remaining cell length (0x%.8X)"
2254                          " while parsing data record at offset 0x%.8X.", 
2255                          length, cell_length - 4, offset);
2256        if(strict)
2257          goto fail;
2258        else
2259          length = cell_length - 4;
2260      }
2261    }
2262
2263    ret_val = regfi_parse_data(file, offset, length, strict);
2264  }
2265
2266  return ret_val;
2267
2268 fail:
2269  ret_val.buf = NULL;
2270  ret_val.len = 0;
2271  return ret_val;
2272}
2273
2274
2275/******************************************************************************
2276 * Parses the common case data records stored in a single cell.
2277 ******************************************************************************/
2278REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32 offset,
2279                              uint32 length, bool strict)
2280{
2281  REGFI_BUFFER ret_val;
2282  uint32 read_length;
2283
2284  ret_val.buf = NULL;
2285  ret_val.len = 0;
2286 
2287  if(lseek(file->fd, offset+4, SEEK_SET) == -1)
2288  {
2289    regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while "
2290                      "reading data at offset 0x%.8X.", offset);
2291    return ret_val;
2292  }
2293
2294  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
2295    return ret_val;
2296  ret_val.len = length;
2297 
2298  read_length = length;
2299  if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
2300     || read_length != length)
2301  {
2302    regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
2303                      " parsing data record at offset 0x%.8X.", offset);
2304    talloc_free(ret_val.buf);
2305    ret_val.buf = NULL;
2306    ret_val.buf = 0;
2307  }
2308
2309  return ret_val;
2310}
2311
2312
2313
2314/******************************************************************************
2315 *
2316 ******************************************************************************/
2317REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32 voffset,
2318                                     uint32 length, bool strict)
2319{
2320  REGFI_BUFFER ret_val;
2321  uint8 i;
2322
2323  ret_val.buf = NULL;
2324  ret_val.len = 0;
2325
2326  if(length > 4)
2327  {
2328    regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
2329                      " while parsing data record. (voffset=0x%.8X, length=%d)",
2330                      voffset, length);
2331    return ret_val;
2332  }
2333
2334  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
2335    return ret_val;
2336  ret_val.len = length;
2337 
2338  for(i = 0; i < length; i++)
2339    ret_val.buf[i] = (uint8)((voffset >> i*8) & 0xFF);
2340
2341  return ret_val;
2342}
2343
2344/******************************************************************************
2345*******************************************************************************/
2346REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32 offset, 
2347                                         uint32 max_size, bool strict)
2348{
2349  REGFI_BUFFER ret_val;
2350  uint32 cell_length;
2351  bool unalloc;
2352
2353  /* XXX: do something with unalloc? */
2354  ret_val.buf = (uint8*)talloc_array(NULL, uint8, REGFI_BIG_DATA_MIN_LENGTH);
2355  if(ret_val.buf == NULL)
2356    goto fail;
2357
2358  if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
2359  {
2360    regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size "
2361                      "while parsing big data header at offset 0x%.8X.",offset);
2362    goto fail;
2363  }
2364
2365  if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
2366                       &cell_length, &unalloc))
2367  {
2368    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
2369                      " parsing big data header at offset 0x%.8X.", offset);
2370    goto fail;
2371  }
2372
2373  if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
2374  {
2375    regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
2376                      " (0x%.2X, 0x%.2X) encountered while parsing"
2377                      " big data header at offset 0x%.8X.", 
2378                      ret_val.buf[0], ret_val.buf[1], offset);
2379    goto fail;
2380  }
2381
2382  ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
2383  return ret_val;
2384
2385 fail:
2386  if(ret_val.buf != NULL)
2387  {
2388    talloc_free(ret_val.buf);
2389    ret_val.buf = NULL;
2390  }
2391  ret_val.len = 0;
2392  return ret_val;
2393}
2394
2395
2396
2397/******************************************************************************
2398 *
2399 ******************************************************************************/
2400uint32* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32 offset,
2401                                      uint16 num_chunks, bool strict)
2402{
2403  uint32* ret_val;
2404  uint32 indirect_length;
2405  int32 max_size;
2406  uint16 i;
2407  bool unalloc;
2408
2409  /* XXX: do something with unalloc? */
2410
2411  max_size = regfi_calc_maxsize(file, offset);
2412  if((max_size < 0) || (num_chunks*sizeof(uint32) + 4 > max_size))
2413    return NULL;
2414
2415  ret_val = (uint32*)talloc_array(NULL, uint32, num_chunks);
2416  if(ret_val == NULL)
2417    goto fail;
2418
2419  if(!regfi_parse_cell(file->fd, offset, (uint8*)ret_val,
2420                       num_chunks*sizeof(uint32),
2421                       &indirect_length, &unalloc))
2422  {
2423    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
2424                      " parsing big data indirect record at offset 0x%.8X.", 
2425                      offset);
2426    goto fail;
2427  }
2428
2429  /* Convert pointers to proper endianess, verify they are aligned. */
2430  for(i=0; i<num_chunks; i++)
2431  {
2432    ret_val[i] = IVAL(ret_val, i*sizeof(uint32));
2433    if((ret_val[i] & 0x00000007) != 0)
2434      goto fail;
2435  }
2436 
2437  return ret_val;
2438
2439 fail:
2440  if(ret_val != NULL)
2441    talloc_free(ret_val);
2442  return NULL;
2443}
2444
2445
2446/******************************************************************************
2447 * Arguments:
2448 *  file       --
2449 *  offsets    -- list of virtual offsets.
2450 *  num_chunks --
2451 *  strict     --
2452 *
2453 * Returns:
2454 *  A range_list with physical offsets and complete lengths
2455 *  (including cell headers) of associated cells. 
2456 *  No data in range_list elements.
2457 ******************************************************************************/
2458range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32* offsets,
2459                                       uint16 num_chunks, bool strict)
2460{
2461  uint32 cell_length, chunk_offset, data_left;
2462  range_list* ret_val;
2463  uint16 i;
2464  bool unalloc;
2465 
2466  /* XXX: do something with unalloc? */
2467  ret_val = range_list_new();
2468  if(ret_val == NULL)
2469    goto fail;
2470 
2471  for(i=0; (i<num_chunks) && (data_left>0); i++)
2472  {
2473    chunk_offset = offsets[i]+REGFI_REGF_SIZE;
2474    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
2475                         &cell_length, &unalloc))
2476    {
2477      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
2478                        " parsing big data chunk at offset 0x%.8X.", 
2479                        chunk_offset);
2480      goto fail;
2481    }
2482
2483    if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
2484      goto fail;
2485  }
2486
2487  return ret_val;
2488
2489 fail:
2490  if(ret_val != NULL)
2491    range_list_free(ret_val);
2492  return NULL;
2493}
2494
2495
2496/******************************************************************************
2497*******************************************************************************/
2498REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
2499                                 uint32 offset, uint32 data_length, 
2500                                 uint32 cell_length, range_list* used_ranges,
2501                                 bool strict)
2502{
2503  REGFI_BUFFER ret_val;
2504  uint16 num_chunks, i;
2505  uint32 read_length, data_left, tmp_len, indirect_offset;
2506  uint32* indirect_ptrs = NULL;
2507  REGFI_BUFFER bd_header;
2508  range_list* bd_cells = NULL;
2509  const range_list_element* cell_info;
2510
2511  ret_val.buf = NULL;
2512
2513  /* XXX: Add better error/warning messages */
2514
2515  bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict);
2516  if(bd_header.buf == NULL)
2517    goto fail;
2518
2519  /* Keep track of used space for use by reglookup-recover */
2520  if(used_ranges != NULL)
2521    if(!range_list_add(used_ranges, offset, cell_length, NULL))
2522      goto fail;
2523
2524  num_chunks = SVAL(bd_header.buf, 0x2);
2525  indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE;
2526  talloc_free(bd_header.buf);
2527
2528  indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset,
2529                                                num_chunks, strict);
2530  if(indirect_ptrs == NULL)
2531    goto fail;
2532
2533  if(used_ranges != NULL)
2534    if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL))
2535      goto fail;
2536 
2537  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
2538    goto fail;
2539  data_left = data_length;
2540
2541  bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict);
2542  if(bd_cells == NULL)
2543    goto fail;
2544
2545  talloc_free(indirect_ptrs);
2546  indirect_ptrs = NULL;
2547 
2548  for(i=0; (i<num_chunks) && (data_left>0); i++)
2549  {
2550    cell_info = range_list_get(bd_cells, i);
2551    if(cell_info == NULL)
2552      goto fail;
2553
2554    /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell
2555     *      length.  However, it has been observed that some (all?) chunks
2556     *      have an additional 4 bytes of 0 at the end of their cells that
2557     *      isn't part of the data, so we're trimming that off too.
2558     *      Perhaps it's just an 8 byte alignment requirement...
2559     */
2560    if(cell_info->length - 8 >= data_left)
2561    {
2562      if(i+1 != num_chunks)
2563      {
2564        regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected "
2565                          "while constructing big data at offset 0x%.8X "
2566                          "(chunk offset 0x%.8X).", offset, cell_info->offset);
2567      }
2568      read_length = data_left;
2569    }
2570    else
2571      read_length = cell_info->length - 8;
2572
2573
2574    if(read_length > regfi_calc_maxsize(file, cell_info->offset))
2575    {
2576      regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize "
2577                        "while constructing big data at offset 0x%.8X "
2578                        "(chunk offset 0x%.8X).", offset, cell_info->offset);
2579      goto fail;
2580    }
2581
2582    if(lseek(file->fd, cell_info->offset+sizeof(uint32), SEEK_SET) == -1)
2583    {
2584      regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while "
2585                        "constructing big data at offset 0x%.8X "
2586                        "(chunk offset 0x%.8X).", offset, cell_info->offset);
2587      goto fail;
2588    }
2589
2590    tmp_len = read_length;
2591    if(regfi_read(file->fd, ret_val.buf+(data_length-data_left), 
2592                  &read_length) != 0 || (read_length != tmp_len))
2593    {
2594      regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
2595                        " constructing big data at offset 0x%.8X"
2596                        " (chunk offset 0x%.8X).", offset, cell_info->offset);
2597      goto fail;
2598    }
2599
2600    if(used_ranges != NULL)
2601      if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
2602        goto fail;
2603
2604    data_left -= read_length;
2605  }
2606  range_list_free(bd_cells);
2607
2608  ret_val.len = data_length-data_left;
2609  return ret_val;
2610
2611 fail:
2612  if(ret_val.buf != NULL)
2613    talloc_free(ret_val.buf);
2614  if(indirect_ptrs != NULL)
2615    talloc_free(indirect_ptrs);
2616  if(bd_cells != NULL)
2617    range_list_free(bd_cells);
2618  ret_val.buf = NULL;
2619  ret_val.len = 0;
2620  return ret_val;
2621}
2622
2623
2624range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
2625{
2626  range_list* ret_val;
2627  REGFI_HBIN* hbin;
2628  const range_list_element* hbins_elem;
2629  uint32 i, num_hbins, curr_off, cell_len;
2630  bool is_unalloc;
2631
2632  ret_val = range_list_new();
2633  if(ret_val == NULL)
2634    return NULL;
2635
2636  num_hbins = range_list_size(file->hbins);
2637  for(i=0; i<num_hbins; i++)
2638  {
2639    hbins_elem = range_list_get(file->hbins, i);
2640    if(hbins_elem == NULL)
2641      break;
2642    hbin = (REGFI_HBIN*)hbins_elem->data;
2643
2644    curr_off = REGFI_HBIN_HEADER_SIZE;
2645    while(curr_off < hbin->block_size)
2646    {
2647      if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
2648                           &cell_len, &is_unalloc))
2649        break;
2650     
2651      if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
2652      {
2653        regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
2654                          " while parsing unallocated cells at offset 0x%.8X.",
2655                          hbin->file_off+curr_off);
2656        break;
2657      }
2658
2659      /* for some reason the record_size of the last record in
2660         an hbin block can extend past the end of the block
2661         even though the record fits within the remaining
2662         space....aaarrrgggghhhhhh */ 
2663      if(curr_off + cell_len >= hbin->block_size)
2664        cell_len = hbin->block_size - curr_off;
2665     
2666      if(is_unalloc)
2667        range_list_add(ret_val, hbin->file_off+curr_off, 
2668                       cell_len, NULL);
2669     
2670      curr_off = curr_off+cell_len;
2671    }
2672  }
2673
2674  return ret_val;
2675}
Note: See TracBrowser for help on using the repository browser.