source: trunk/lib/regfi.c @ 152

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

preliminary support for big data records

switched to using key flags rather than incorrect key types

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