source: trunk/lib/regfi.c @ 213

Last change on this file since 213 was 213, checked in by tim, 13 years ago

fixed some pyregfi parameter bugs
fixed some iterator memory management problems
updated smoketest script to use ctypes pyregfi

  • Property svn:keywords set to Id
File size: 99.9 KB
Line 
1/*
2 * Copyright (C) 2005-2010 Timothy D. Morgan
3 * Copyright (C) 2005 Gerald (Jerry) Carter
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 * $Id: regfi.c 213 2011-03-25 15:48:27Z tim $
19 */
20
21/**
22 * @file
23 *
24 * Windows NT (and later) read-only registry library
25 *
26 * See @ref regfi.h for more information.
27 *
28 * Branched from Samba project Subversion repository, version #7470:
29 *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
30 *
31 * Since then, it has been heavily rewritten, simplified, and improved.
32 */
33
34#include "regfi.h"
35
36
37/* Registry types mapping */
38const unsigned int regfi_num_reg_types = 12;
39static const char* regfi_type_names[] =
40  {"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK",
41   "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
42
43const char* regfi_encoding_names[] =
44  {"US-ASCII//TRANSLIT", "UTF-8//TRANSLIT", "UTF-16LE//TRANSLIT"};
45
46
47/* Ensures regfi_init runs only once */
48static pthread_once_t regfi_init_once = PTHREAD_ONCE_INIT;
49
50
51
52/******************************************************************************
53 ******************************************************************************/
54void regfi_log_free(void* ptr)
55{
56  REGFI_LOG* log_info = (REGFI_LOG*)ptr;
57 
58  if(log_info->messages != NULL)
59    free(log_info->messages);
60
61  talloc_free(log_info);
62}
63
64
65/******************************************************************************
66 ******************************************************************************/
67void regfi_init()
68{
69  int err;
70  if((err = pthread_key_create(&regfi_log_key, regfi_log_free)) != 0)
71    fprintf(stderr, "ERROR: key_create: %s\n", strerror(err));
72  errno = err;
73}
74
75
76/******************************************************************************
77 ******************************************************************************/
78REGFI_LOG* regfi_log_new()
79{
80  int err;
81  REGFI_LOG* log_info = talloc(NULL, REGFI_LOG);
82  if(log_info == NULL)
83    return NULL;
84
85  log_info->msg_mask = REGFI_DEFAULT_LOG_MASK;
86  log_info->messages = NULL;
87
88  pthread_once(&regfi_init_once, regfi_init);
89
90  if((err = pthread_setspecific(regfi_log_key, log_info)) != 0)
91  {
92    fprintf(stderr, "ERROR: setspecific: %s\n", strerror(err));
93    goto fail;
94  }
95
96  return log_info;
97
98 fail:
99  talloc_free(log_info);
100  errno = err;
101  return NULL;
102}
103
104
105/******************************************************************************
106 ******************************************************************************/
107void regfi_log_add(uint16_t msg_type, const char* fmt, ...)
108{
109  /* XXX: Switch internal storage over to a linked list or stack.
110   *      Then add a regfi_log_get function that returns the list in some
111   *      convenient, user-friendly data structure.  regfi_log_get_str should
112   *      stick around and will simply smush the list into a big string when
113   *      it's called, rather than having messages smushed when they're first
114   *      written to the log.
115   */
116  uint32_t buf_size, buf_used;
117  char* new_msg;
118  REGFI_LOG* log_info;
119  va_list args;
120
121  log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
122  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
123    return;
124
125  if((log_info->msg_mask & msg_type) == 0)
126    return;
127
128  if(log_info->messages == NULL)
129    buf_used = 0;
130  else
131    buf_used = strlen(log_info->messages);
132 
133  buf_size = buf_used+strlen(fmt)+160;
134  new_msg = realloc(log_info->messages, buf_size);
135  if(new_msg == NULL)
136    /* XXX: should we report this? */
137    return;
138 
139  switch (msg_type)
140  {
141  case REGFI_LOG_INFO:
142    strcpy(new_msg+buf_used, "INFO: ");
143    buf_used += 6;
144    break;
145  case REGFI_LOG_WARN:
146    strcpy(new_msg+buf_used, "WARN: ");
147    buf_used += 6;
148    break;
149  case REGFI_LOG_ERROR:
150    strcpy(new_msg+buf_used, "ERROR: ");
151    buf_used += 7;
152    break;
153  }
154 
155  va_start(args, fmt);
156  vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
157  va_end(args);
158  strncat(new_msg, "\n", buf_size-1);
159 
160  log_info->messages = new_msg;
161}
162
163
164/******************************************************************************
165 ******************************************************************************/
166char* regfi_log_get_str()
167{
168  char* ret_val;
169  REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
170  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
171    return NULL;
172 
173  ret_val = log_info->messages;
174  log_info->messages = NULL;
175
176  return ret_val;
177}
178
179
180/******************************************************************************
181 ******************************************************************************/
182bool regfi_log_set_mask(uint16_t msg_mask)
183{
184  REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
185  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
186  {
187      return false;
188  }
189
190  log_info->msg_mask = msg_mask;
191  return true;
192}
193
194
195/******************************************************************************
196 * Returns NULL for an invalid e
197 *****************************************************************************/
198static const char* regfi_encoding_int2str(REGFI_ENCODING e)
199{
200  if(e < REGFI_NUM_ENCODINGS)
201    return regfi_encoding_names[e];
202
203  return NULL;
204}
205
206
207/******************************************************************************
208 * Returns NULL for an invalid val
209 *****************************************************************************/
210const char* regfi_type_val2str(unsigned int val)
211{
212  if(val == REG_KEY)
213    return "KEY";
214 
215  if(val >= regfi_num_reg_types)
216    return NULL;
217 
218  return regfi_type_names[val];
219}
220
221
222/******************************************************************************
223 * Returns -1 on error
224 *****************************************************************************/
225int regfi_type_str2val(const char* str)
226{
227  int i;
228
229  if(strcmp("KEY", str) == 0)
230    return REG_KEY;
231
232  for(i=0; i < regfi_num_reg_types; i++)
233    if (strcmp(regfi_type_names[i], str) == 0) 
234      return i;
235
236  if(strcmp("DWORD_LE", str) == 0)
237    return REG_DWORD_LE;
238
239  return -1;
240}
241
242
243/* Security descriptor formatting functions  */
244
245const char* regfi_ace_type2str(uint8_t type)
246{
247  static const char* map[7] 
248    = {"ALLOW", "DENY", "AUDIT", "ALARM", 
249       "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"};
250  if(type < 7)
251    return map[type];
252  else
253    /* XXX: would be nice to return the unknown integer value. 
254     *      However, as it is a const string, it can't be free()ed later on,
255     *      so that would need to change.
256     */
257    return "UNKNOWN";
258}
259
260
261/* XXX: need a better reference on the meaning of each flag. */
262/* For more info, see:
263 *   http://msdn2.microsoft.com/en-us/library/aa772242.aspx
264 */
265char* regfi_ace_flags2str(uint8_t flags)
266{
267  static const char* flag_map[32] = 
268    { "OI", /* Object Inherit */
269      "CI", /* Container Inherit */
270      "NP", /* Non-Propagate */
271      "IO", /* Inherit Only */
272      "IA", /* Inherited ACE */
273      NULL,
274      NULL,
275      NULL,
276    };
277
278  char* ret_val = malloc(35*sizeof(char));
279  char* fo = ret_val;
280  uint32_t i;
281  uint8_t f;
282
283  if(ret_val == NULL)
284    return NULL;
285
286  fo[0] = '\0';
287  if (!flags)
288    return ret_val;
289
290  for(i=0; i < 8; i++)
291  {
292    f = (1<<i);
293    if((flags & f) && (flag_map[i] != NULL))
294    {
295      strcpy(fo, flag_map[i]);
296      fo += strlen(flag_map[i]);
297      *(fo++) = ' ';
298      flags ^= f;
299    }
300  }
301 
302  /* Any remaining unknown flags are added at the end in hex. */
303  if(flags != 0)
304    sprintf(fo, "0x%.2X ", flags);
305
306  /* Chop off the last space if we've written anything to ret_val */
307  if(fo != ret_val)
308    fo[-1] = '\0';
309
310  return ret_val;
311}
312
313
314char* regfi_ace_perms2str(uint32_t perms)
315{
316  uint32_t i, p;
317  /* This is more than is needed by a fair margin. */
318  char* ret_val = malloc(350*sizeof(char));
319  char* r = ret_val;
320
321  /* Each represents one of 32 permissions bits.  NULL is for undefined/reserved bits.
322   * For more information, see:
323   *   http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
324   *   http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
325   */
326  static const char* perm_map[32] = 
327    {/* object-specific permissions (registry keys, in this case) */
328      "QRY_VAL",       /* KEY_QUERY_VALUE */
329      "SET_VAL",       /* KEY_SET_VALUE */
330      "CREATE_KEY",    /* KEY_CREATE_SUB_KEY */
331      "ENUM_KEYS",     /* KEY_ENUMERATE_SUB_KEYS */
332      "NOTIFY",        /* KEY_NOTIFY */
333      "CREATE_LNK",    /* KEY_CREATE_LINK - Reserved for system use. */
334      NULL,
335      NULL,
336      "WOW64_64",      /* KEY_WOW64_64KEY */
337      "WOW64_32",      /* KEY_WOW64_32KEY */
338      NULL,
339      NULL,
340      NULL,
341      NULL,
342      NULL,
343      NULL,
344      /* standard access rights */
345      "DELETE",        /* DELETE */
346      "R_CONT",        /* READ_CONTROL */
347      "W_DAC",         /* WRITE_DAC */
348      "W_OWNER",       /* WRITE_OWNER */
349      "SYNC",          /* SYNCHRONIZE - Shouldn't be set in registries */
350      NULL,
351      NULL,
352      NULL,
353      /* other generic */
354      "SYS_SEC",       /* ACCESS_SYSTEM_SECURITY */
355      "MAX_ALLWD",     /* MAXIMUM_ALLOWED */
356      NULL,
357      NULL,
358      "GEN_A",         /* GENERIC_ALL */
359      "GEN_X",         /* GENERIC_EXECUTE */
360      "GEN_W",         /* GENERIC_WRITE */
361      "GEN_R",         /* GENERIC_READ */
362    };
363
364
365  if(ret_val == NULL)
366    return NULL;
367
368  r[0] = '\0';
369  for(i=0; i < 32; i++)
370  {
371    p = (1<<i);
372    if((perms & p) && (perm_map[i] != NULL))
373    {
374      strcpy(r, perm_map[i]);
375      r += strlen(perm_map[i]);
376      *(r++) = ' ';
377      perms ^= p;
378    }
379  }
380 
381  /* Any remaining unknown permission bits are added at the end in hex. */
382  if(perms != 0)
383    sprintf(r, "0x%.8X ", perms);
384
385  /* Chop off the last space if we've written anything to ret_val */
386  if(r != ret_val)
387    r[-1] = '\0';
388
389  return ret_val;
390}
391
392
393char* regfi_sid2str(WINSEC_DOM_SID* sid)
394{
395  uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
396  uint32_t left = size;
397  uint8_t comps = sid->num_auths;
398  char* ret_val = malloc(size);
399 
400  if(ret_val == NULL)
401    return NULL;
402
403  if(comps > WINSEC_MAX_SUBAUTHS)
404    comps = WINSEC_MAX_SUBAUTHS;
405
406  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
407
408  for (i = 0; i < comps; i++) 
409    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
410
411  return ret_val;
412}
413
414
415char* regfi_get_acl(WINSEC_ACL* acl)
416{
417  uint32_t i, extra, size = 0;
418  const char* type_str;
419  char* flags_str;
420  char* perms_str;
421  char* sid_str;
422  char* ace_delim = "";
423  char* ret_val = NULL;
424  char* tmp_val = NULL;
425  bool failed = false;
426  char field_delim = ':';
427
428  for (i = 0; i < acl->num_aces && !failed; i++)
429  {
430    sid_str = regfi_sid2str(acl->aces[i]->trustee);
431    type_str = regfi_ace_type2str(acl->aces[i]->type);
432    perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
433    flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
434   
435    if(flags_str != NULL && perms_str != NULL 
436       && type_str != NULL && sid_str != NULL)
437    {
438      /* XXX: this is slow */
439      extra = strlen(sid_str) + strlen(type_str) 
440        + strlen(perms_str) + strlen(flags_str) + 5;
441      tmp_val = realloc(ret_val, size+extra);
442
443      if(tmp_val == NULL)
444      {
445        free(ret_val);
446        ret_val = NULL;
447        failed = true;
448      }
449      else
450      {
451        ret_val = tmp_val;
452        size += sprintf(ret_val+size, "%s%s%c%s%c%s%c%s",
453                        ace_delim,sid_str,
454                        field_delim,type_str,
455                        field_delim,perms_str,
456                        field_delim,flags_str);
457        ace_delim = "|";
458      }
459    }
460    else
461      failed = true;
462
463    if(sid_str != NULL)
464      free(sid_str);
465    if(sid_str != NULL)
466      free(perms_str);
467    if(sid_str != NULL)
468      free(flags_str);
469  }
470
471  return ret_val;
472}
473
474
475char* regfi_get_sacl(WINSEC_DESC *sec_desc)
476{
477  if (sec_desc->sacl)
478    return regfi_get_acl(sec_desc->sacl);
479  else
480    return NULL;
481}
482
483
484char* regfi_get_dacl(WINSEC_DESC *sec_desc)
485{
486  if (sec_desc->dacl)
487    return regfi_get_acl(sec_desc->dacl);
488  else
489    return NULL;
490}
491
492
493char* regfi_get_owner(WINSEC_DESC *sec_desc)
494{
495  return regfi_sid2str(sec_desc->owner_sid);
496}
497
498
499char* regfi_get_group(WINSEC_DESC *sec_desc)
500{
501  return regfi_sid2str(sec_desc->grp_sid);
502}
503
504
505bool regfi_read_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
506{
507  int lock_ret = pthread_rwlock_rdlock(lock);
508  if(lock_ret != 0)
509  {
510    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining read lock in"
511                      "%s due to: %s\n", context, strerror(lock_ret));
512    return false;
513  }
514
515  return true;
516}
517
518
519bool regfi_write_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
520{
521  int lock_ret = pthread_rwlock_wrlock(lock);
522  if(lock_ret != 0)
523  {
524    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining write lock in"
525                      "%s due to: %s\n", context, strerror(lock_ret));
526    return false;
527  }
528
529  return true;
530}
531
532
533bool regfi_rw_unlock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
534{
535  int lock_ret = pthread_rwlock_unlock(lock);
536  if(lock_ret != 0)
537  {
538    regfi_log_add(REGFI_LOG_ERROR, "Error releasing lock in"
539                      "%s due to: %s\n", context, strerror(lock_ret));
540    return false;
541  }
542
543  return true;
544}
545
546
547bool regfi_lock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
548{
549  int lock_ret = pthread_mutex_lock(lock);
550  if(lock_ret != 0)
551  {
552    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining mutex lock in"
553                      "%s due to: %s\n", context, strerror(lock_ret));
554    return false;
555  }
556
557  return true;
558}
559
560
561bool regfi_unlock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
562{
563  int lock_ret = pthread_mutex_unlock(lock);
564  if(lock_ret != 0)
565  {
566    regfi_log_add(REGFI_LOG_ERROR, "Error releasing mutex lock in"
567                      "%s due to: %s\n", context, strerror(lock_ret));
568    return false;
569  }
570
571  return true;
572}
573
574
575off_t regfi_raw_seek(REGFI_RAW_FILE* self, off_t offset, int whence)
576{
577  return lseek(*(int*)self->state, offset, whence);
578}
579
580ssize_t regfi_raw_read(REGFI_RAW_FILE* self, void* buf, size_t count)
581{
582  return read(*(int*)self->state, buf, count);
583}
584
585
586/*****************************************************************************
587 * Convenience function to wrap up the ugly callback stuff
588 *****************************************************************************/
589off_t regfi_seek(REGFI_RAW_FILE* file_cb, off_t offset, int whence)
590{
591  return file_cb->seek(file_cb, offset, whence);
592}
593
594
595/*****************************************************************************
596 * This function is just like read(2), except that it continues to
597 * re-try reading from the file descriptor if EINTR or EAGAIN is received. 
598 * regfi_read will attempt to read length bytes from the file and write them to
599 * buf.
600 *
601 * On success, 0 is returned.  Upon failure, an errno code is returned.
602 *
603 * The number of bytes successfully read is returned through the length
604 * parameter by reference.  If both the return value and length parameter are
605 * returned as 0, then EOF was encountered immediately
606 *****************************************************************************/
607uint32_t regfi_read(REGFI_RAW_FILE* file_cb, uint8_t* buf, uint32_t* length)
608{
609  uint32_t rsize = 0;
610  uint32_t rret = 0;
611
612  do
613  {
614    rret = file_cb->read(file_cb, buf + rsize, *length - rsize);
615    if(rret > 0)
616      rsize += rret;
617  }while(*length - rsize > 0 
618         && (rret > 0 || (rret == -1 && (errno == EAGAIN || errno == EINTR))));
619 
620  *length = rsize;
621  if (rret == -1 && errno != EINTR && errno != EAGAIN)
622    return errno;
623
624  return 0;
625}
626
627
628/*****************************************************************************
629 *
630 *****************************************************************************/
631bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, uint32_t offset, uint8_t* hdr, 
632                      uint32_t hdr_len, uint32_t* cell_length, bool* unalloc)
633{
634  uint32_t length;
635  int32_t raw_length;
636  uint8_t tmp[4];
637
638  if(regfi_seek(file_cb, offset, SEEK_SET) == -1)
639    return false;
640
641  length = 4;
642  if((regfi_read(file_cb, tmp, &length) != 0) || length != 4)
643    return false;
644  raw_length = IVALS(tmp, 0);
645
646  if(raw_length < 0)
647  {
648    (*cell_length) = raw_length*(-1);
649    (*unalloc) = false;
650  }
651  else
652  {
653    (*cell_length) = raw_length;
654    (*unalloc) = true;
655  }
656
657  if(*cell_length - 4 < hdr_len)
658    return false;
659
660  if(hdr_len > 0)
661  {
662    length = hdr_len;
663    if((regfi_read(file_cb, hdr, &length) != 0) || length != hdr_len)
664      return false;
665  }
666
667  return true;
668}
669
670
671/******************************************************************************
672 * Given an offset and an hbin, is the offset within that hbin?
673 * The offset is a virtual file offset.
674 ******************************************************************************/
675static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32_t voffset)
676{
677  if(!hbin)
678    return false;
679
680  if((voffset > hbin->first_hbin_off) 
681     && (voffset < (hbin->first_hbin_off + hbin->block_size)))
682    return true;
683               
684  return false;
685}
686
687
688
689/******************************************************************************
690 * Provide a physical offset and receive the correpsonding HBIN
691 * block for it.  NULL if one doesn't exist.
692 ******************************************************************************/
693const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset)
694{
695  return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset);
696}
697
698
699/******************************************************************************
700 * Calculate the largest possible cell size given a physical offset.
701 * Largest size is based on the HBIN the offset is currently a member of.
702 * Returns negative values on error.
703 * (Since cells can only be ~2^31 in size, this works out.)
704 ******************************************************************************/
705int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset)
706{
707  const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset);
708  if(hbin == NULL)
709    return -1;
710
711  return (hbin->block_size + hbin->file_off) - offset;
712}
713
714
715/******************************************************************************
716 ******************************************************************************/
717REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset, 
718                                         uint32_t num_keys, uint32_t max_size, 
719                                         bool strict)
720{
721  REGFI_SUBKEY_LIST* ret_val;
722
723  ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, 
724                                      REGFI_MAX_SUBKEY_DEPTH);
725  if(ret_val == NULL)
726  {
727    regfi_log_add(REGFI_LOG_WARN, "Failed to load subkey list at"
728                      " offset 0x%.8X.", offset);
729    return NULL;
730  }
731
732  if(num_keys != ret_val->num_keys)
733  {
734    /*  Not sure which should be authoritative, the number from the
735     *  NK record, or the number in the subkey list.  Just emit a warning for
736     *  now if they don't match.
737     */
738    regfi_log_add(REGFI_LOG_WARN, "Number of subkeys listed in parent"
739                      " (%d) did not match number found in subkey list/tree (%d)"
740                      " while parsing subkey list/tree at offset 0x%.8X.", 
741                      num_keys, ret_val->num_keys, offset);
742  }
743
744  return ret_val;
745}
746
747
748/******************************************************************************
749 ******************************************************************************/
750REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset, 
751                                             uint32_t max_size, bool strict,
752                                             uint8_t depth_left)
753{
754  REGFI_SUBKEY_LIST* ret_val;
755  REGFI_SUBKEY_LIST** sublists;
756  uint32_t i, num_sublists, off;
757  int32_t sublist_maxsize;
758
759  if(depth_left == 0)
760  {
761    regfi_log_add(REGFI_LOG_WARN, "Maximum depth reached"
762                      " while parsing subkey list/tree at offset 0x%.8X.", 
763                      offset);
764    return NULL;
765  }
766
767  ret_val = regfi_parse_subkeylist(file, offset, max_size, strict);
768  if(ret_val == NULL)
769    return NULL;
770
771  if(ret_val->recursive_type)
772  {
773    num_sublists = ret_val->num_children;
774    sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists
775                                           * sizeof(REGFI_SUBKEY_LIST*));
776    for(i=0; i < num_sublists; i++)
777    {
778      off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
779
780      sublist_maxsize = regfi_calc_maxsize(file, off);
781      if(sublist_maxsize < 0)
782        sublists[i] = NULL;
783      else
784        sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, 
785                                                strict, depth_left-1);
786    }
787    talloc_free(ret_val);
788
789    return regfi_merge_subkeylists(num_sublists, sublists, strict);
790  }
791
792  return ret_val;
793}
794
795
796/******************************************************************************
797 ******************************************************************************/
798REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, 
799                                          uint32_t max_size, bool strict)
800{
801  REGFI_SUBKEY_LIST* ret_val;
802  uint32_t i, cell_length, length, elem_size, read_len;
803  uint8_t* elements = NULL;
804  uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN];
805  bool unalloc;
806  bool recursive_type;
807
808  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_subkeylist"))
809     goto fail;
810
811  if(!regfi_parse_cell(file->cb, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN,
812                       &cell_length, &unalloc))
813  {
814    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while "
815                      "parsing subkey-list at offset 0x%.8X.", offset);
816    goto fail_locked;
817  }
818
819  if(cell_length > max_size)
820  {
821    regfi_log_add(REGFI_LOG_WARN, "Cell size longer than max_size"
822                      " while parsing subkey-list at offset 0x%.8X.", offset);
823    if(strict)
824      goto fail_locked;
825    cell_length = max_size & 0xFFFFFFF8;
826  }
827
828  recursive_type = false;
829  if(buf[0] == 'r' && buf[1] == 'i')
830  {
831    recursive_type = true;
832    elem_size = sizeof(uint32_t);
833  }
834  else if(buf[0] == 'l' && buf[1] == 'i')
835  {
836    elem_size = sizeof(uint32_t);
837  }
838  else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
839    elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
840  else
841  {
842    regfi_log_add(REGFI_LOG_ERROR, "Unknown magic number"
843                      " (0x%.2X, 0x%.2X) encountered while parsing"
844                      " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
845    goto fail_locked;
846  }
847
848  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
849  if(ret_val == NULL)
850    goto fail_locked;
851
852  ret_val->offset = offset;
853  ret_val->cell_size = cell_length;
854  ret_val->magic[0] = buf[0];
855  ret_val->magic[1] = buf[1];
856  ret_val->recursive_type = recursive_type;
857  ret_val->num_children = SVAL(buf, 0x2);
858
859  if(!recursive_type)
860    ret_val->num_keys = ret_val->num_children;
861
862  length = elem_size*ret_val->num_children;
863  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length)
864  {
865    regfi_log_add(REGFI_LOG_WARN, "Number of elements too large for"
866                      " cell while parsing subkey-list at offset 0x%.8X.", 
867                      offset);
868    if(strict)
869      goto fail_locked;
870    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t);
871  }
872
873  ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, 
874                                   ret_val->num_children);
875  if(ret_val->elements == NULL)
876    goto fail_locked;
877
878  elements = (uint8_t*)malloc(length);
879  if(elements == NULL)
880    goto fail_locked;
881
882  read_len = length;
883  if(regfi_read(file->cb, elements, &read_len) != 0 || read_len!=length)
884    goto fail_locked;
885
886  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist"))
887     goto fail;
888
889  if(elem_size == sizeof(uint32_t))
890  {
891    for (i=0; i < ret_val->num_children; i++)
892    {
893      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
894      ret_val->elements[i].hash = 0;
895    }
896  }
897  else
898  {
899    for (i=0; i < ret_val->num_children; i++)
900    {
901      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
902      ret_val->elements[i].hash = IVAL(elements, i*elem_size+4);
903    }
904  }
905  free(elements);
906
907  return ret_val;
908
909 fail_locked:
910  regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist");
911 fail:
912  if(elements != NULL)
913    free(elements);
914  talloc_free(ret_val);
915  return NULL;
916}
917
918
919/*******************************************************************
920 *******************************************************************/
921REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists, 
922                                           REGFI_SUBKEY_LIST** lists,
923                                           bool strict)
924{
925  uint32_t i,j,k;
926  REGFI_SUBKEY_LIST* ret_val;
927
928  if(lists == NULL)
929    return NULL;
930  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
931
932  if(ret_val == NULL)
933    return NULL;
934 
935  /* Obtain total number of elements */
936  ret_val->num_keys = 0;
937  for(i=0; i < num_lists; i++)
938  {
939    if(lists[i] != NULL)
940      ret_val->num_keys += lists[i]->num_children;
941  }
942  ret_val->num_children = ret_val->num_keys;
943
944  if(ret_val->num_keys > 0)
945  {
946    ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
947                                     ret_val->num_keys);
948    k=0;
949
950    if(ret_val->elements != NULL)
951    {
952      for(i=0; i < num_lists; i++)
953      {
954        if(lists[i] != NULL)
955        {
956          for(j=0; j < lists[i]->num_keys; j++)
957          {
958            ret_val->elements[k].hash = lists[i]->elements[j].hash;
959            ret_val->elements[k++].offset = lists[i]->elements[j].offset;
960          }
961        }
962      }
963    }
964  }
965 
966  for(i=0; i < num_lists; i++)
967    talloc_free(lists[i]);
968  free(lists);
969
970  return ret_val;
971}
972
973
974/******************************************************************************
975 *
976 ******************************************************************************/
977REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, 
978                             bool strict)
979{
980  REGFI_SK* ret_val = NULL;
981  uint8_t* sec_desc_buf = NULL;
982  uint32_t cell_length, length;
983  uint8_t sk_header[REGFI_SK_MIN_LENGTH];
984  bool unalloc = false;
985
986  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_sk"))
987     goto fail;
988
989  if(!regfi_parse_cell(file->cb, offset, sk_header, REGFI_SK_MIN_LENGTH,
990                       &cell_length, &unalloc))
991  {
992    regfi_log_add(REGFI_LOG_WARN, "Could not parse SK record cell"
993                      " at offset 0x%.8X.", offset);
994    goto fail_locked;
995  }
996   
997  if(sk_header[0] != 's' || sk_header[1] != 'k')
998  {
999    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
1000                      " SK record at offset 0x%.8X.", offset);
1001    goto fail_locked;
1002  }
1003
1004  ret_val = talloc(NULL, REGFI_SK);
1005  if(ret_val == NULL)
1006    goto fail_locked;
1007
1008  ret_val->offset = offset;
1009  /* XXX: Is there a way to be more conservative (shorter) with
1010   *      cell length when cell is unallocated?
1011   */
1012  ret_val->cell_size = cell_length;
1013
1014  if(ret_val->cell_size > max_size)
1015    ret_val->cell_size = max_size & 0xFFFFFFF8;
1016  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
1017     || (strict && (ret_val->cell_size & 0x00000007) != 0))
1018  {
1019    regfi_log_add(REGFI_LOG_WARN, "Invalid cell size found while"
1020                      " parsing SK record at offset 0x%.8X.", offset);
1021    goto fail_locked;
1022  }
1023
1024  ret_val->magic[0] = sk_header[0];
1025  ret_val->magic[1] = sk_header[1];
1026
1027  ret_val->unknown_tag = SVAL(sk_header, 0x2);
1028  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
1029  ret_val->next_sk_off = IVAL(sk_header, 0x8);
1030  ret_val->ref_count = IVAL(sk_header, 0xC);
1031  ret_val->desc_size = IVAL(sk_header, 0x10);
1032
1033  if((ret_val->prev_sk_off & 0x00000007) != 0
1034     || (ret_val->next_sk_off & 0x00000007) != 0)
1035  {
1036    regfi_log_add(REGFI_LOG_WARN, "SK record's next/previous offsets"
1037                      " are not a multiple of 8 while parsing SK record at"
1038                      " offset 0x%.8X.", offset);
1039    goto fail_locked;
1040  }
1041
1042  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
1043  {
1044    regfi_log_add(REGFI_LOG_WARN, "Security descriptor too large for"
1045                      " cell while parsing SK record at offset 0x%.8X.", 
1046                      offset);
1047    goto fail_locked;
1048  }
1049
1050  sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size);
1051  if(sec_desc_buf == NULL)
1052    goto fail_locked;
1053
1054  length = ret_val->desc_size;
1055  if(regfi_read(file->cb, sec_desc_buf, &length) != 0 
1056     || length != ret_val->desc_size)
1057  {
1058    regfi_log_add(REGFI_LOG_ERROR, "Failed to read security"
1059                      " descriptor while parsing SK record at offset 0x%.8X.",
1060                      offset);
1061    goto fail_locked;
1062  }
1063
1064  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_sk"))
1065     goto fail;
1066
1067  if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, 
1068                                                   ret_val->desc_size)))
1069  {
1070    regfi_log_add(REGFI_LOG_ERROR, "Failed to parse security"
1071                      " descriptor while parsing SK record at offset 0x%.8X.",
1072                      offset);
1073    goto fail;
1074  }
1075
1076  free(sec_desc_buf);
1077  return ret_val;
1078
1079 fail_locked:
1080  regfi_unlock(file, &file->cb_lock, "regfi_parse_sk");
1081 fail:
1082  if(sec_desc_buf != NULL)
1083    free(sec_desc_buf);
1084  talloc_free(ret_val);
1085  return NULL;
1086}
1087
1088
1089REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset, 
1090                                        uint32_t num_values, bool strict)
1091{
1092  REGFI_VALUE_LIST* ret_val;
1093  uint32_t i, cell_length, length, read_len;
1094  bool unalloc;
1095
1096  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_valuelist"))
1097     goto fail;
1098
1099  if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
1100  {
1101    regfi_log_add(REGFI_LOG_ERROR, "Failed to read cell header"
1102                      " while parsing value list at offset 0x%.8X.", offset);
1103    goto fail_locked;
1104  }
1105
1106  if((cell_length & 0x00000007) != 0)
1107  {
1108    regfi_log_add(REGFI_LOG_WARN, "Cell length not a multiple of 8"
1109                      " while parsing value list at offset 0x%.8X.", offset);
1110    if(strict)
1111      goto fail_locked;
1112    cell_length = cell_length & 0xFFFFFFF8;
1113  }
1114
1115  if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t))
1116  {
1117    regfi_log_add(REGFI_LOG_WARN, "Too many values found"
1118                      " while parsing value list at offset 0x%.8X.", offset);
1119    if(strict)
1120      goto fail_locked;
1121    num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t);
1122  }
1123
1124  read_len = num_values*sizeof(uint32_t);
1125  ret_val = talloc(NULL, REGFI_VALUE_LIST);
1126  if(ret_val == NULL)
1127    goto fail_locked;
1128
1129  ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
1130  if(ret_val->elements == NULL)
1131    goto fail_locked;
1132
1133  ret_val->offset = offset;
1134  ret_val->cell_size = cell_length;
1135  ret_val->num_values = num_values;
1136
1137  length = read_len;
1138  if((regfi_read(file->cb, (uint8_t*)ret_val->elements, &length) != 0) 
1139     || length != read_len)
1140  {
1141    regfi_log_add(REGFI_LOG_ERROR, "Failed to read value pointers"
1142                      " while parsing value list at offset 0x%.8X.", offset);
1143    goto fail_locked;
1144  }
1145 
1146  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist"))
1147     goto fail;
1148
1149  for(i=0; i < num_values; i++)
1150  {
1151    /* Fix endianness */
1152    ret_val->elements[i] = IVAL(&ret_val->elements[i], 0);
1153
1154    /* Validate the first num_values values to ensure they make sense */
1155    if(strict)
1156    {
1157      /* XXX: Need to revisit this file length check when we start dealing
1158       *      with partial files. */
1159      if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
1160         || ((ret_val->elements[i] & 0x00000007) != 0))
1161      {
1162        regfi_log_add(REGFI_LOG_WARN, "Invalid value pointer"
1163                          " (0x%.8X) found while parsing value list at offset"
1164                          " 0x%.8X.", ret_val->elements[i], offset);
1165        goto fail;
1166      }
1167    }
1168  }
1169
1170  return ret_val;
1171
1172 fail_locked:
1173  regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist");
1174 fail:
1175  talloc_free(ret_val);
1176  return NULL;
1177}
1178
1179/* XXX: should give this boolean return type to indicate errors */
1180void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, 
1181                               REGFI_ENCODING output_encoding, bool strict)
1182{
1183  /* XXX: Registry value names are supposedly limited to 16383 characters
1184   *      according to:
1185   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
1186   *      Might want to emit a warning if this is exceeded. 
1187   *      It is expected that "characters" could be variable width.
1188   *      Also, it may be useful to use this information to limit false positives
1189   *      when recovering deleted VK records.
1190   */
1191  int32_t tmp_size;
1192  REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME)
1193    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
1194
1195  if(from_encoding == output_encoding)
1196  {
1197    vk->name_raw[vk->name_length] = '\0';
1198    vk->name = (char*)vk->name_raw;
1199  }
1200  else
1201  {
1202    vk->name = talloc_array(vk, char, vk->name_length+1);
1203    if(vk->name == NULL)
1204      return;
1205
1206    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
1207                                  regfi_encoding_int2str(output_encoding),
1208                                  vk->name_raw, vk->name,
1209                                  vk->name_length, vk->name_length+1);
1210    if(tmp_size < 0)
1211    {
1212      regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
1213                        " value name to encoding %s.  Error message: %s",
1214                        regfi_encoding_int2str(output_encoding), 
1215                        strerror(-tmp_size));
1216      talloc_free(vk->name);
1217      vk->name = NULL;
1218    }
1219  }
1220}
1221
1222
1223/******************************************************************************
1224 ******************************************************************************/
1225REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, 
1226                           REGFI_ENCODING output_encoding, bool strict)
1227{
1228  REGFI_VK* ret_val = NULL;
1229  int32_t max_size;
1230
1231  max_size = regfi_calc_maxsize(file, offset);
1232  if(max_size < 0)
1233    return NULL;
1234 
1235  ret_val = regfi_parse_vk(file, offset, max_size, strict);
1236  if(ret_val == NULL)
1237    return NULL;
1238
1239  regfi_interpret_valuename(file, ret_val, output_encoding, strict);
1240
1241  return ret_val;
1242}
1243
1244
1245/******************************************************************************
1246 * If !strict, the list may contain NULLs, VK records may point to NULL.
1247 ******************************************************************************/
1248REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
1249                                       uint32_t num_values, uint32_t max_size,
1250                                       bool strict)
1251{
1252  uint32_t usable_num_values;
1253
1254  if((num_values+1) * sizeof(uint32_t) > max_size)
1255  {
1256    regfi_log_add(REGFI_LOG_WARN, "Number of values indicated by"
1257                      " parent key (%d) would cause cell to straddle HBIN"
1258                      " boundary while loading value list at offset"
1259                      " 0x%.8X.", num_values, offset);
1260    if(strict)
1261      return NULL;
1262    usable_num_values = max_size/sizeof(uint32_t) - sizeof(uint32_t);
1263  }
1264  else
1265    usable_num_values = num_values;
1266
1267  return regfi_parse_valuelist(file, offset, usable_num_values, strict);
1268}
1269
1270
1271/* XXX: should give this boolean return type to indicate errors */
1272void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, 
1273                             REGFI_ENCODING output_encoding, bool strict)
1274{
1275  /* XXX: Registry key names are supposedly limited to 255 characters according to:
1276   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
1277   *      Might want to emit a warning if this is exceeded. 
1278   *      It is expected that "characters" could be variable width.
1279   *      Also, it may be useful to use this information to limit false positives
1280   *      when recovering deleted NK records.
1281   */
1282  int32_t tmp_size;
1283  REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME) 
1284    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
1285 
1286  if(from_encoding == output_encoding)
1287  {
1288    nk->name_raw[nk->name_length] = '\0';
1289    nk->name = (char*)nk->name_raw;
1290  }
1291  else
1292  {
1293    nk->name = talloc_array(nk, char, nk->name_length+1);
1294    if(nk->name == NULL)
1295      return;
1296
1297    memset(nk->name,0,nk->name_length+1);
1298
1299    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
1300                                  regfi_encoding_int2str(output_encoding),
1301                                  nk->name_raw, nk->name,
1302                                  nk->name_length, nk->name_length+1);
1303    if(tmp_size < 0)
1304    {
1305      regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
1306                        " key name to encoding %s.  Error message: %s",
1307                        regfi_encoding_int2str(output_encoding), 
1308                        strerror(-tmp_size));
1309      talloc_free(nk->name);
1310      nk->name = NULL;
1311    }
1312  }
1313}
1314
1315
1316/******************************************************************************
1317 *
1318 ******************************************************************************/
1319REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset,
1320                         REGFI_ENCODING output_encoding, bool strict)
1321{
1322  REGFI_NK* nk;
1323  uint32_t off;
1324  int32_t max_size;
1325
1326  max_size = regfi_calc_maxsize(file, offset);
1327  if (max_size < 0) 
1328    return NULL;
1329
1330  /* get the initial nk record */
1331  if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
1332  {
1333    regfi_log_add(REGFI_LOG_ERROR, "Could not load NK record at"
1334                  " offset 0x%.8X.", offset);
1335    return NULL;
1336  }
1337
1338  regfi_interpret_keyname(file, nk, output_encoding, strict);
1339
1340  /* get value list */
1341  if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
1342  {
1343    off = nk->values_off + REGFI_REGF_SIZE;
1344    max_size = regfi_calc_maxsize(file, off);
1345    if(max_size < 0)
1346    {
1347      if(strict)
1348      {
1349        talloc_free(nk);
1350        return NULL;
1351      }
1352      else
1353        nk->values = NULL;
1354
1355    }
1356    else
1357    {
1358      nk->values = regfi_load_valuelist(file, off, nk->num_values, 
1359                                        max_size, true);
1360      if(nk->values == NULL)
1361      {
1362        regfi_log_add(REGFI_LOG_WARN, "Could not load value list"
1363                      " for NK record at offset 0x%.8X.", offset);
1364        if(strict)
1365        {
1366          talloc_free(nk);
1367          return NULL;
1368        }
1369      }
1370      talloc_steal(nk, nk->values);
1371    }
1372  }
1373
1374  /* now get subkey list */
1375  if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
1376  {
1377    off = nk->subkeys_off + REGFI_REGF_SIZE;
1378    max_size = regfi_calc_maxsize(file, off);
1379    if(max_size < 0) 
1380    {
1381      if(strict)
1382      {
1383        talloc_free(nk);
1384        return NULL;
1385      }
1386      else
1387        nk->subkeys = NULL;
1388    }
1389    else
1390    {
1391      nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
1392                                          max_size, true);
1393
1394      if(nk->subkeys == NULL)
1395      {
1396        regfi_log_add(REGFI_LOG_WARN, "Could not load subkey list"
1397                      " while parsing NK record at offset 0x%.8X.", offset);
1398        nk->num_subkeys = 0;
1399      }
1400      talloc_steal(nk, nk->subkeys);
1401    }
1402  }
1403
1404  return nk;
1405}
1406
1407
1408/******************************************************************************
1409 ******************************************************************************/
1410const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
1411{
1412  REGFI_SK* ret_val = NULL;
1413  int32_t max_size;
1414  void* failure_ptr = NULL;
1415 
1416  max_size = regfi_calc_maxsize(file, offset);
1417  if(max_size < 0)
1418    return NULL;
1419
1420  if(file->sk_cache == NULL)
1421    return regfi_parse_sk(file, offset, max_size, strict);
1422
1423  if(!regfi_lock(file, &file->sk_lock, "regfi_load_sk"))
1424    return NULL;
1425
1426  /* First look if we have already parsed it */
1427  ret_val = (REGFI_SK*)lru_cache_find(file->sk_cache, &offset, 4);
1428
1429  /* Bail out if we have previously cached a parse failure at this offset. */
1430  if(ret_val == (void*)REGFI_OFFSET_NONE)
1431    return NULL;
1432
1433  if(ret_val == NULL)
1434  {
1435    ret_val = regfi_parse_sk(file, offset, max_size, strict);
1436    if(ret_val == NULL)
1437    { /* Cache the parse failure and bail out. */
1438      failure_ptr = talloc(NULL, uint32_t);
1439      if(failure_ptr == NULL)
1440        return NULL;
1441      *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
1442      lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
1443
1444      /* Let the cache be the only owner of this */
1445      talloc_unlink(NULL, failure_ptr);
1446      return NULL;
1447    }
1448  }
1449
1450  if(!regfi_unlock(file, &file->sk_lock, "regfi_load_sk"))
1451  {
1452    talloc_unlink(NULL, ret_val);
1453    return NULL;
1454  }
1455
1456  return ret_val;
1457}
1458
1459
1460
1461/******************************************************************************
1462 ******************************************************************************/
1463REGFI_NK* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin, 
1464                             REGFI_ENCODING output_encoding)
1465{
1466  REGFI_NK* nk = NULL;
1467  uint32_t cell_length;
1468  uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE;
1469  uint32_t hbin_end = hbin->file_off+hbin->block_size;
1470  bool unalloc;
1471
1472  while(cur_offset < hbin_end)
1473  {
1474
1475    if(!regfi_lock(file, &file->cb_lock, "regfi_find_root_nk"))
1476      return NULL;
1477
1478    if(!regfi_parse_cell(file->cb, cur_offset, NULL, 0, &cell_length, &unalloc))
1479    {
1480      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell at offset"
1481                    " 0x%.8X while searching for root key.", cur_offset);
1482      return NULL;
1483    }
1484
1485    if(!regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk"))
1486      return NULL;
1487
1488    if(!unalloc)
1489    {
1490      nk = regfi_load_key(file, cur_offset, output_encoding, true);
1491      if(nk != NULL)
1492      {
1493        if(nk->flags & REGFI_NK_FLAG_ROOT)
1494          return nk;
1495      }
1496    }
1497
1498    cur_offset += cell_length;
1499  }
1500
1501  return NULL;
1502}
1503
1504
1505
1506/******************************************************************************
1507 ******************************************************************************/
1508REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding)
1509{
1510  REGFI_FILE* ret_val;
1511  REGFI_RAW_FILE* file_cb = talloc(NULL, REGFI_RAW_FILE);
1512  if(file_cb == NULL) 
1513    return NULL;
1514
1515  file_cb->state = (void*)talloc(file_cb, int);
1516  if(file_cb->state == NULL)
1517    goto fail;
1518  *(int*)file_cb->state = fd;
1519 
1520  file_cb->cur_off = 0;
1521  file_cb->size = 0;
1522  file_cb->read = &regfi_raw_read;
1523  file_cb->seek = &regfi_raw_seek;
1524 
1525  ret_val = regfi_alloc_cb(file_cb, output_encoding);
1526  if(ret_val == NULL)
1527    goto fail;
1528
1529  /* In this case, we want file_cb to be freed when ret_val is */
1530  talloc_steal(ret_val, file_cb);
1531  return ret_val;
1532
1533 fail:
1534    talloc_free(file_cb);
1535    return NULL;
1536}
1537
1538
1539/******************************************************************************
1540 ******************************************************************************/
1541int regfi_free_cb(void* f)
1542{
1543  REGFI_FILE* file = (REGFI_FILE*)f;
1544
1545  pthread_mutex_destroy(&file->cb_lock);
1546  pthread_rwlock_destroy(&file->hbins_lock);
1547  pthread_mutex_destroy(&file->sk_lock);
1548
1549  return 0;
1550}
1551
1552
1553/******************************************************************************
1554 ******************************************************************************/
1555REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb, 
1556                           REGFI_ENCODING output_encoding)
1557{
1558  REGFI_FILE* rb;
1559  REGFI_HBIN* hbin = NULL;
1560  uint32_t hbin_off, cache_secret;
1561  int32_t file_length;
1562  bool rla;
1563
1564  /* Determine file length.  Must be at least big enough for the header
1565   * and one hbin.
1566   */
1567  file_length = file_cb->seek(file_cb, 0, SEEK_END);
1568  if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
1569  {
1570    regfi_log_add(REGFI_LOG_ERROR, "File length (%d) too short to contain a"
1571                  " header and at least one HBIN.", file_length);
1572    return NULL;
1573  }
1574  file_cb->seek(file_cb, 0, SEEK_SET);
1575
1576  if(output_encoding != REGFI_ENCODING_UTF8
1577     && output_encoding != REGFI_ENCODING_ASCII)
1578  { 
1579    regfi_log_add(REGFI_LOG_ERROR, "Invalid output_encoding supplied"
1580                  " in creation of regfi iterator.");
1581    return NULL;
1582  }
1583
1584  /* Read file header */
1585  if ((rb = regfi_parse_regf(file_cb, false)) == NULL)
1586  {
1587    regfi_log_add(REGFI_LOG_ERROR, "Failed to read REGF block.");
1588    return NULL;
1589  }
1590  rb->file_length = file_length;
1591  rb->cb = file_cb;
1592  rb->string_encoding = output_encoding;
1593
1594  if(pthread_mutex_init(&rb->cb_lock, NULL) != 0)
1595  {
1596    regfi_log_add(REGFI_LOG_ERROR, "Failed to create cb_lock mutex.");
1597    goto fail;
1598  }
1599
1600  if(pthread_rwlock_init(&rb->hbins_lock, NULL) != 0)
1601  {
1602    regfi_log_add(REGFI_LOG_ERROR, "Failed to create hbins_lock rwlock.");
1603    goto fail;
1604  }
1605
1606  if(pthread_mutex_init(&rb->sk_lock, NULL) != 0)
1607  {
1608    regfi_log_add(REGFI_LOG_ERROR, "Failed to create sk_lock mutex.");
1609    goto fail;
1610  }
1611
1612  rb->hbins = range_list_new();
1613  if(rb->hbins == NULL)
1614  {
1615    regfi_log_add(REGFI_LOG_ERROR, "Failed to create HBIN range_list.");
1616    goto fail;
1617  }
1618  talloc_steal(rb, rb->hbins);
1619
1620  rla = true;
1621  hbin_off = REGFI_REGF_SIZE;
1622  hbin = regfi_parse_hbin(rb, hbin_off, true);
1623  while(hbin && rla)
1624  {
1625    rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
1626    if(rla)
1627      talloc_steal(rb->hbins, hbin);
1628
1629    hbin_off = hbin->file_off + hbin->block_size;
1630    hbin = regfi_parse_hbin(rb, hbin_off, true);
1631  }
1632
1633  /* This secret isn't very secret, but we don't need a good one.  This
1634   * secret is just designed to prevent someone from trying to blow our
1635   * caching and make things slow.
1636   */
1637  cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
1638
1639  if(REGFI_CACHE_SK)
1640    rb->sk_cache = lru_cache_create_ctx(rb, 64, cache_secret, true);
1641  else
1642    rb->sk_cache = NULL;
1643
1644  /* success */
1645  talloc_set_destructor(rb, regfi_free_cb);
1646  return rb;
1647
1648 fail:
1649  pthread_mutex_destroy(&rb->cb_lock);
1650  pthread_rwlock_destroy(&rb->hbins_lock);
1651  pthread_mutex_destroy(&rb->sk_lock);
1652
1653  range_list_free(rb->hbins);
1654  talloc_free(rb);
1655  return NULL;
1656}
1657
1658
1659/******************************************************************************
1660 ******************************************************************************/
1661void regfi_free(REGFI_FILE* file)
1662{
1663  /* Callback handles cleanup side effects */
1664  talloc_free(file);
1665}
1666
1667
1668/******************************************************************************
1669 * First checks the offset given by the file header, then checks the
1670 * rest of the file if that fails.
1671 ******************************************************************************/
1672REGFI_NK* regfi_rootkey(REGFI_FILE* file)
1673{
1674  REGFI_NK* nk = NULL;
1675  REGFI_HBIN* hbin;
1676  uint32_t root_offset, i, num_hbins;
1677 
1678  if(!file)
1679    return NULL;
1680
1681  root_offset = file->root_cell+REGFI_REGF_SIZE;
1682  nk = regfi_load_key(file, root_offset, file->string_encoding, true);
1683  if(nk != NULL)
1684  {
1685    if(nk->flags & REGFI_NK_FLAG_ROOT)
1686      return nk;
1687  }
1688
1689  regfi_log_add(REGFI_LOG_WARN, "File header indicated root key at"
1690                " location 0x%.8X, but no root key found."
1691                " Searching rest of file...", root_offset);
1692 
1693  /* If the file header gives bad info, scan through the file one HBIN
1694   * block at a time looking for an NK record with a root key type.
1695   */
1696 
1697  if(!regfi_read_lock(file, &file->hbins_lock, "regfi_rootkey"))
1698    return NULL;
1699
1700  num_hbins = range_list_size(file->hbins);
1701  for(i=0; i < num_hbins && nk == NULL; i++)
1702  {
1703    hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
1704    nk = regfi_find_root_nk(file, hbin, file->string_encoding);
1705  }
1706
1707  if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_rootkey"))
1708    return NULL;
1709
1710  return nk;
1711}
1712
1713
1714/******************************************************************************
1715 *****************************************************************************/
1716void regfi_free_record(const void* record)
1717{
1718  talloc_unlink(NULL, (void*)record);
1719}
1720
1721
1722
1723
1724/******************************************************************************
1725 *****************************************************************************/
1726uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key)
1727{
1728  uint32_t num_in_list = 0;
1729  if(key->subkeys != NULL)
1730    num_in_list = key->subkeys->num_keys;
1731
1732  if(num_in_list != key->num_subkeys)
1733  {
1734    regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d keys in its"
1735                  " subkey list but reports %d should be available.", 
1736                  key->offset, num_in_list, key->num_subkeys);
1737    return (num_in_list < key->num_subkeys)?num_in_list:key->num_subkeys;
1738  }
1739 
1740  return num_in_list;
1741}
1742
1743
1744/******************************************************************************
1745 *****************************************************************************/
1746uint32_t regfi_fetch_num_values(const REGFI_NK* key)
1747{
1748  uint32_t num_in_list = 0;
1749  if(key->values != NULL)
1750    num_in_list = key->values->num_values;
1751
1752  if(num_in_list != key->num_values)
1753  {
1754    regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d values in"
1755                  " its value list but reports %d should be available.",
1756                  key->offset, num_in_list, key->num_values);
1757    return (num_in_list < key->num_values)?num_in_list:key->num_values;
1758  }
1759 
1760  return num_in_list;
1761}
1762
1763
1764/******************************************************************************
1765 *****************************************************************************/
1766REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file)
1767{
1768  REGFI_NK* root;
1769  REGFI_ITERATOR* ret_val;
1770
1771  ret_val = talloc(NULL, REGFI_ITERATOR);
1772  if(ret_val == NULL)
1773    return NULL;
1774
1775  root = regfi_rootkey(file);
1776  if(root == NULL)
1777  {
1778    talloc_free(ret_val);
1779    return NULL;
1780  }
1781  ret_val->cur_key = root;
1782  talloc_reparent(NULL, ret_val, root);
1783
1784  ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH);
1785  if(ret_val->key_positions == NULL)
1786  {
1787    talloc_free(ret_val);
1788    return NULL;
1789  }
1790  talloc_steal(ret_val, ret_val->key_positions);
1791
1792  ret_val->f = file;
1793  ret_val->cur_subkey = 0;
1794  ret_val->cur_value = 0;
1795   
1796  return ret_val;
1797}
1798
1799
1800/******************************************************************************
1801 *****************************************************************************/
1802void regfi_iterator_free(REGFI_ITERATOR* i)
1803{
1804  talloc_free(i);
1805}
1806
1807
1808
1809/******************************************************************************
1810 *****************************************************************************/
1811/* XXX: some way of indicating reason for failure should be added. */
1812bool regfi_iterator_down(REGFI_ITERATOR* i)
1813{
1814  REGFI_NK* subkey;
1815  REGFI_ITER_POSITION* pos;
1816
1817  pos = talloc(i->key_positions, REGFI_ITER_POSITION);
1818  if(pos == NULL)
1819    return false;
1820
1821  subkey = (REGFI_NK*)regfi_iterator_cur_subkey(i);
1822  if(subkey == NULL)
1823  {
1824    talloc_free(pos);
1825    return false;
1826  }
1827
1828  pos->nk = i->cur_key;
1829  pos->cur_subkey = i->cur_subkey;
1830  if(!void_stack_push(i->key_positions, pos))
1831  {
1832    talloc_free(pos);
1833    talloc_unlink(NULL, subkey);
1834    return false;
1835  }
1836  talloc_reparent(NULL, i, subkey);
1837
1838  i->cur_key = subkey;
1839  i->cur_subkey = 0;
1840  i->cur_value = 0;
1841
1842  return true;
1843}
1844
1845
1846/******************************************************************************
1847 *****************************************************************************/
1848bool regfi_iterator_up(REGFI_ITERATOR* i)
1849{
1850  REGFI_ITER_POSITION* pos;
1851
1852  pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions);
1853  if(pos == NULL)
1854    return false;
1855
1856  talloc_unlink(i, i->cur_key);
1857  i->cur_key = pos->nk;
1858  i->cur_subkey = pos->cur_subkey;
1859  i->cur_value = 0;
1860  talloc_free(pos);
1861
1862  return true;
1863}
1864
1865
1866/******************************************************************************
1867 *****************************************************************************/
1868bool regfi_iterator_to_root(REGFI_ITERATOR* i)
1869{
1870  while(regfi_iterator_up(i))
1871    continue;
1872
1873  return true;
1874}
1875
1876
1877/******************************************************************************
1878 *****************************************************************************/
1879bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name)
1880{
1881  uint32_t new_index;
1882
1883  if(regfi_find_subkey(i->f, i->cur_key, name, &new_index))
1884  {
1885    i->cur_subkey = new_index;
1886    return true;
1887  }
1888
1889  return false;
1890}
1891
1892
1893/******************************************************************************
1894 *****************************************************************************/
1895bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
1896{
1897  uint32_t x;
1898  if(path == NULL)
1899    return false;
1900
1901  for(x=0; 
1902      ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x])
1903       && regfi_iterator_down(i));
1904      x++)
1905  { continue; }
1906
1907  if(path[x] == NULL)
1908    return true;
1909 
1910  /* XXX: is this the right number of times? */
1911  for(; x > 0; x--)
1912    regfi_iterator_up(i);
1913 
1914  return false;
1915}
1916
1917
1918/******************************************************************************
1919 *****************************************************************************/
1920const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i)
1921{
1922  talloc_reference(NULL, i->cur_key);
1923  return i->cur_key;
1924}
1925
1926
1927/******************************************************************************
1928 *****************************************************************************/
1929const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key)
1930{
1931  if(key == NULL || key->sk_off == REGFI_OFFSET_NONE)
1932    return NULL;
1933
1934  return regfi_load_sk(file, key->sk_off + REGFI_REGF_SIZE, true);
1935}
1936
1937
1938/******************************************************************************
1939 *****************************************************************************/
1940bool regfi_iterator_first_subkey(REGFI_ITERATOR* i)
1941{
1942  i->cur_subkey = 0;
1943 
1944  return ((i->cur_key != NULL) && (i->cur_key->subkeys_off!=REGFI_OFFSET_NONE) 
1945          && (i->cur_subkey < regfi_fetch_num_subkeys(i->cur_key)));
1946}
1947
1948
1949/******************************************************************************
1950 *****************************************************************************/
1951const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
1952{
1953  return regfi_get_subkey(i->f, i->cur_key, i->cur_subkey);
1954}
1955
1956
1957/******************************************************************************
1958 *****************************************************************************/
1959bool regfi_iterator_next_subkey(REGFI_ITERATOR* i)
1960{
1961  i->cur_subkey++;
1962
1963  return ((i->cur_key != NULL) && (i->cur_key->subkeys_off!=REGFI_OFFSET_NONE) 
1964          && (i->cur_subkey < regfi_fetch_num_subkeys(i->cur_key))); 
1965}
1966
1967
1968/******************************************************************************
1969 *****************************************************************************/
1970bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name)
1971{
1972  uint32_t new_index;
1973
1974  if(regfi_find_value(i->f, i->cur_key, name, &new_index))
1975  {
1976    i->cur_value = new_index;
1977    return true;
1978  }
1979
1980  return false;
1981}
1982
1983
1984/******************************************************************************
1985 *****************************************************************************/
1986bool regfi_iterator_first_value(REGFI_ITERATOR* i)
1987{
1988  i->cur_value = 0;
1989  return (i->cur_key->values != NULL && i->cur_key->values->elements != NULL 
1990          && (i->cur_value < regfi_fetch_num_values(i->cur_key)));
1991}
1992
1993
1994/******************************************************************************
1995 *****************************************************************************/
1996const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i)
1997{
1998  return regfi_get_value(i->f, i->cur_key, i->cur_value);
1999}
2000
2001
2002/******************************************************************************
2003 *****************************************************************************/
2004bool regfi_iterator_next_value(REGFI_ITERATOR* i)
2005{
2006  i->cur_value++;
2007  return (i->cur_key->values != NULL && i->cur_key->values->elements != NULL 
2008          && (i->cur_value < regfi_fetch_num_values(i->cur_key)));
2009}
2010
2011
2012/******************************************************************************
2013 *****************************************************************************/
2014const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file,
2015                                             const REGFI_NK* key)
2016{
2017  REGFI_CLASSNAME* ret_val;
2018  uint8_t* raw;
2019  char* interpreted;
2020  uint32_t offset;
2021  int32_t conv_size, max_size;
2022  uint16_t parse_length;
2023
2024  if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0)
2025    return NULL;
2026
2027  offset = key->classname_off + REGFI_REGF_SIZE;
2028  max_size = regfi_calc_maxsize(file, offset);
2029  if(max_size <= 0)
2030    return NULL;
2031
2032  parse_length = key->classname_length;
2033  raw = regfi_parse_classname(file, offset, &parse_length, max_size, true);
2034 
2035  if(raw == NULL)
2036  {
2037    regfi_log_add(REGFI_LOG_WARN, "Could not parse class"
2038                  " name at offset 0x%.8X for key record at offset 0x%.8X.",
2039                  offset, key->offset);
2040    return NULL;
2041  }
2042
2043  ret_val = talloc(NULL, REGFI_CLASSNAME);
2044  if(ret_val == NULL)
2045    return NULL;
2046
2047  ret_val->offset = offset;
2048  ret_val->raw = raw;
2049  ret_val->size = parse_length;
2050  talloc_steal(ret_val, raw);
2051
2052  interpreted = talloc_array(NULL, char, parse_length);
2053
2054  conv_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
2055                                 regfi_encoding_int2str(file->string_encoding),
2056                                 raw, interpreted,
2057                                 parse_length, parse_length);
2058  if(conv_size < 0)
2059  {
2060    regfi_log_add(REGFI_LOG_WARN, "Error occurred while"
2061                  " converting classname to charset %s.  Error message: %s",
2062                  file->string_encoding, strerror(-conv_size));
2063    talloc_free(interpreted);
2064    ret_val->interpreted = NULL;
2065  }
2066  else
2067  {
2068    interpreted = talloc_realloc(NULL, interpreted, char, conv_size);
2069    ret_val->interpreted = interpreted;
2070    talloc_steal(ret_val, interpreted);
2071  }
2072
2073  return ret_val;
2074}
2075
2076
2077/******************************************************************************
2078 *****************************************************************************/
2079const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file, 
2080                                   const REGFI_VK* value)
2081{
2082  REGFI_DATA* ret_val = NULL;
2083  REGFI_BUFFER raw_data;
2084
2085  if(value->data_size != 0)
2086  {
2087    raw_data = regfi_load_data(file, value->data_off, value->data_size,
2088                               value->data_in_offset, true);
2089    if(raw_data.buf == NULL)
2090    {
2091      regfi_log_add(REGFI_LOG_WARN, "Could not parse data record"
2092                    " while parsing VK record at offset 0x%.8X.",
2093                    value->offset);
2094    }
2095    else
2096    {
2097      ret_val = regfi_buffer_to_data(raw_data);
2098
2099      if(ret_val == NULL)
2100      {
2101        regfi_log_add(REGFI_LOG_WARN, "Error occurred in converting"
2102                      " data buffer to data structure while interpreting "
2103                      "data for VK record at offset 0x%.8X.",
2104                      value->offset);
2105        talloc_free(raw_data.buf);
2106        return NULL;
2107      }
2108
2109      if(!regfi_interpret_data(file, file->string_encoding, 
2110                               value->type, ret_val))
2111      {
2112        regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
2113                      " interpreting data for VK record at offset 0x%.8X.",
2114                      value->offset);
2115      }
2116    }
2117  }
2118 
2119  return ret_val;
2120}
2121
2122
2123
2124/******************************************************************************
2125 *****************************************************************************/
2126bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key, 
2127                       const char* name, uint32_t* index)
2128{
2129  const REGFI_NK* cur;
2130  uint32_t i;
2131  uint32_t num_subkeys = regfi_fetch_num_subkeys(key);
2132  bool found = false;
2133
2134  /* XXX: cur->name can be NULL in the registry. 
2135   *      Should we allow for a way to search for that?
2136   */
2137  if(name == NULL)
2138    return false;
2139
2140  for(i=0; (i < num_subkeys) && (found == false); i++)
2141  {
2142    cur = regfi_get_subkey(file, key, i);
2143    if(cur == NULL)
2144      return false;
2145
2146    if((cur->name != NULL)
2147       && (strcasecmp(cur->name, name) == 0))
2148    {
2149      found = true;
2150      *index = i;
2151    }
2152
2153    regfi_free_record(cur);
2154  }
2155
2156  return found;
2157}
2158
2159
2160
2161/******************************************************************************
2162 *****************************************************************************/
2163bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key, 
2164                      const char* name, uint32_t* index)
2165{
2166  const REGFI_VK* cur;
2167  uint32_t i;
2168  uint32_t num_values = regfi_fetch_num_values(key);
2169  bool found = false;
2170
2171  /* XXX: cur->name can be NULL in the registry. 
2172   *      Should we allow for a way to search for that?
2173   */
2174  if(name == NULL)
2175    return false;
2176
2177  for(i=0; (i < num_values) && (found == false); i++)
2178  {
2179    cur = regfi_get_value(file, key, i);
2180    if(cur == NULL)
2181      return false;
2182
2183    if((cur->name != NULL)
2184       && (strcasecmp(cur->name, name) == 0))
2185    {
2186      found = true;
2187      *index = i;
2188    }
2189
2190    regfi_free_record(cur);
2191  }
2192
2193  return found;
2194}
2195
2196
2197
2198/******************************************************************************
2199 *****************************************************************************/
2200const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key, 
2201                                 uint32_t index)
2202{
2203  if(index < regfi_fetch_num_subkeys(key))
2204  {
2205    return regfi_load_key(file, 
2206                          key->subkeys->elements[index].offset+REGFI_REGF_SIZE,
2207                          file->string_encoding, true);
2208  }
2209
2210  return NULL;
2211}
2212
2213
2214
2215/******************************************************************************
2216 *****************************************************************************/
2217const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key, 
2218                                uint32_t index)
2219{
2220  if(index < regfi_fetch_num_values(key))
2221  {
2222    return regfi_load_value(file, 
2223                            key->values->elements[index]+REGFI_REGF_SIZE,
2224                            file->string_encoding, true);
2225  }
2226
2227  return NULL; 
2228}
2229
2230
2231/******************************************************************************
2232 *****************************************************************************/
2233REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
2234{
2235  REGFI_DATA* ret_val;
2236
2237  if(raw_data.buf == NULL)
2238    return NULL;
2239
2240  ret_val = talloc(NULL, REGFI_DATA);
2241  if(ret_val == NULL)
2242    return NULL;
2243 
2244  talloc_steal(ret_val, raw_data.buf);
2245  ret_val->raw = raw_data.buf;
2246  ret_val->size = raw_data.len;
2247  ret_val->interpreted_size = 0;
2248  ret_val->interpreted.qword = 0;
2249
2250  return ret_val;
2251}
2252
2253
2254/******************************************************************************
2255 *****************************************************************************/
2256bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
2257                          uint32_t type, REGFI_DATA* data)
2258{
2259  uint8_t** tmp_array;
2260  uint8_t* tmp_str;
2261  int32_t tmp_size;
2262  uint32_t i, j, array_size;
2263
2264  if(data == NULL)
2265    return false;
2266
2267  switch (type)
2268  {
2269  case REG_SZ:
2270  case REG_EXPAND_SZ:
2271  /* REG_LINK is a symbolic link, stored as a unicode string. */
2272  case REG_LINK:
2273    tmp_str = talloc_array(NULL, uint8_t, data->size);
2274    if(tmp_str == NULL)
2275    {
2276      data->interpreted.string = NULL;
2277      data->interpreted_size = 0;
2278      return false;
2279    }
2280     
2281    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
2282                                  regfi_encoding_int2str(string_encoding),
2283                                  data->raw, (char*)tmp_str, 
2284                                  data->size, data->size);
2285    if(tmp_size < 0)
2286    {
2287      regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
2288                    " converting data of type %d to %d.  Error message: %s",
2289                    type, string_encoding, strerror(-tmp_size));
2290      talloc_free(tmp_str);
2291      data->interpreted.string = NULL;
2292      data->interpreted_size = 0;
2293      return false;
2294    }
2295
2296    tmp_str = talloc_realloc(NULL, tmp_str, uint8_t, tmp_size);
2297    data->interpreted.string = tmp_str;
2298    data->interpreted_size = tmp_size;
2299    talloc_steal(data, tmp_str);
2300    break;
2301
2302  case REG_DWORD:
2303    if(data->size < 4)
2304    {
2305      data->interpreted.dword = 0;
2306      data->interpreted_size = 0;
2307      return false;
2308    }
2309    data->interpreted.dword = IVAL(data->raw, 0);
2310    data->interpreted_size = 4;
2311    break;
2312
2313  case REG_DWORD_BE:
2314    if(data->size < 4)
2315    {
2316      data->interpreted.dword_be = 0;
2317      data->interpreted_size = 0;
2318      return false;
2319    }
2320    data->interpreted.dword_be = RIVAL(data->raw, 0);
2321    data->interpreted_size = 4;
2322    break;
2323
2324  case REG_QWORD:
2325    if(data->size < 8)
2326    {
2327      data->interpreted.qword = 0;
2328      data->interpreted_size = 0;
2329      return false;
2330    }
2331    data->interpreted.qword = 
2332      (uint64_t)IVAL(data->raw, 0) + (((uint64_t)IVAL(data->raw, 4))<<32);
2333    data->interpreted_size = 8;
2334    break;
2335   
2336  case REG_MULTI_SZ:
2337    tmp_str = talloc_array(NULL, uint8_t, data->size);
2338    if(tmp_str == NULL)
2339    {
2340      data->interpreted.multiple_string = NULL;
2341      data->interpreted_size = 0;
2342      return false;
2343    }
2344
2345    /* Attempt to convert entire string from UTF-16LE to output encoding,
2346     * then parse and quote fields individually.
2347     */
2348    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
2349                                  regfi_encoding_int2str(string_encoding),
2350                                  data->raw, (char*)tmp_str,
2351                                  data->size, data->size);
2352    if(tmp_size < 0)
2353    {
2354      regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
2355                    " converting data of type %d to %s.  Error message: %s",
2356                    type, string_encoding, strerror(-tmp_size));
2357      talloc_free(tmp_str);
2358      data->interpreted.multiple_string = NULL;
2359      data->interpreted_size = 0;
2360      return false;
2361    }
2362
2363    array_size = tmp_size+1;
2364    tmp_array = talloc_array(NULL, uint8_t*, array_size);
2365    if(tmp_array == NULL)
2366    {
2367      talloc_free(tmp_str);
2368      data->interpreted.string = NULL;
2369      data->interpreted_size = 0;
2370      return false;
2371    }
2372   
2373    tmp_array[0] = tmp_str;
2374    for(i=0,j=1; i < tmp_size && j < array_size-1; i++)
2375    {
2376      if(tmp_str[i] == '\0' && (i+1 < tmp_size) && tmp_str[i+1] != '\0')
2377        tmp_array[j++] = tmp_str+i+1;
2378    }
2379    tmp_array[j] = NULL;
2380    tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1);
2381    data->interpreted.multiple_string = tmp_array;
2382    /* XXX: how meaningful is this?  should we store number of strings instead? */
2383    data->interpreted_size = tmp_size;
2384    talloc_steal(tmp_array, tmp_str);
2385    talloc_steal(data, tmp_array);
2386    break;
2387
2388  /* XXX: Dont know how to interpret these yet, just treat as binary */
2389  case REG_NONE:
2390    data->interpreted.none = data->raw;
2391    data->interpreted_size = data->size;
2392    break;
2393
2394  case REG_RESOURCE_LIST:
2395    data->interpreted.resource_list = data->raw;
2396    data->interpreted_size = data->size;
2397    break;
2398
2399  case REG_FULL_RESOURCE_DESCRIPTOR:
2400    data->interpreted.full_resource_descriptor = data->raw;
2401    data->interpreted_size = data->size;
2402    break;
2403
2404  case REG_RESOURCE_REQUIREMENTS_LIST:
2405    data->interpreted.resource_requirements_list = data->raw;
2406    data->interpreted_size = data->size;
2407    break;
2408
2409  case REG_BINARY:
2410    data->interpreted.binary = data->raw;
2411    data->interpreted_size = data->size;
2412    break;
2413
2414  default:
2415    data->interpreted.qword = 0;
2416    data->interpreted_size = 0;
2417    return false;
2418  }
2419
2420  data->type = type;
2421  return true;
2422}
2423
2424
2425/******************************************************************************
2426 * Convert from UTF-16LE to specified character set.
2427 * On error, returns a negative errno code.
2428 *****************************************************************************/
2429int32_t regfi_conv_charset(const char* input_charset, const char* output_charset,
2430                           uint8_t* input, char* output, 
2431                           uint32_t input_len, uint32_t output_max)
2432{
2433  iconv_t conv_desc;
2434  char* inbuf = (char*)input;
2435  char* outbuf = output;
2436  size_t in_len = (size_t)input_len;
2437  size_t out_len = (size_t)(output_max-1);
2438  int ret;
2439
2440  /* XXX: Consider creating a couple of conversion descriptors earlier,
2441   *      storing them on an iterator so they don't have to be recreated
2442   *      each time.
2443   */
2444
2445  /* Set up conversion descriptor. */
2446  conv_desc = iconv_open(output_charset, input_charset);
2447
2448  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
2449  if(ret == -1)
2450  {
2451    iconv_close(conv_desc);
2452    return -errno;
2453  }
2454  *outbuf = '\0';
2455
2456  iconv_close(conv_desc); 
2457  return output_max-out_len-1;
2458}
2459
2460
2461
2462/*******************************************************************
2463 * Computes the checksum of the registry file header.
2464 * buffer must be at least the size of a regf header (4096 bytes).
2465 *******************************************************************/
2466static uint32_t regfi_compute_header_checksum(uint8_t* buffer)
2467{
2468  uint32_t checksum, x;
2469  int i;
2470
2471  /* XOR of all bytes 0x0000 - 0x01FB */
2472
2473  checksum = x = 0;
2474 
2475  for ( i=0; i<0x01FB; i+=4 ) {
2476    x = IVAL(buffer, i );
2477    checksum ^= x;
2478  }
2479 
2480  return checksum;
2481}
2482
2483
2484/*******************************************************************
2485 *******************************************************************/
2486REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict)
2487{
2488  uint8_t file_header[REGFI_REGF_SIZE];
2489  uint32_t length;
2490  REGFI_FILE* ret_val;
2491
2492  ret_val = talloc(NULL, REGFI_FILE);
2493  if(ret_val == NULL)
2494    return NULL;
2495
2496  ret_val->sk_cache = NULL;
2497  ret_val->hbins = NULL;
2498
2499  length = REGFI_REGF_SIZE;
2500  if((regfi_read(file_cb, file_header, &length)) != 0 
2501     || length != REGFI_REGF_SIZE)
2502  {
2503    regfi_log_add(REGFI_LOG_WARN, "Read failed while parsing REGF structure.");
2504    goto fail;
2505  }
2506
2507  ret_val->checksum = IVAL(file_header, 0x1FC);
2508  ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
2509  if (strict && (ret_val->checksum != ret_val->computed_checksum))
2510  {
2511    regfi_log_add(REGFI_LOG_WARN, "Stored header checksum (%.8X) did not equal"
2512                  " computed checksum (%.8X).",
2513                  ret_val->checksum, ret_val->computed_checksum);
2514    if(strict)
2515      goto fail;
2516  }
2517
2518  memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
2519  if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
2520  {
2521    regfi_log_add(REGFI_LOG_ERROR, "Magic number mismatch "
2522                  "(%.2X %.2X %.2X %.2X) while parsing hive header",
2523                  ret_val->magic[0], ret_val->magic[1], 
2524                  ret_val->magic[2], ret_val->magic[3]);
2525    goto fail;
2526  }
2527
2528  ret_val->sequence1 = IVAL(file_header, 0x4);
2529  ret_val->sequence2 = IVAL(file_header, 0x8);
2530  ret_val->mtime.low = IVAL(file_header, 0xC);
2531  ret_val->mtime.high = IVAL(file_header, 0x10);
2532  ret_val->major_version = IVAL(file_header, 0x14);
2533  ret_val->minor_version = IVAL(file_header, 0x18);
2534  ret_val->type = IVAL(file_header, 0x1C);
2535  ret_val->format = IVAL(file_header, 0x20);
2536  ret_val->root_cell = IVAL(file_header, 0x24);
2537  ret_val->last_block = IVAL(file_header, 0x28);
2538  ret_val->cluster = IVAL(file_header, 0x2C);
2539
2540  memcpy(ret_val->file_name, file_header+0x30,  REGFI_REGF_NAME_SIZE);
2541
2542  /* XXX: Should we add a warning if these uuid parsers fail?  Can they? */
2543  ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
2544  ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
2545  ret_val->flags = IVAL(file_header, 0x90);
2546  ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
2547  ret_val->guid_signature = IVAL(file_header, 0xa4);
2548
2549  memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
2550  memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE);
2551
2552  ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16);
2553  ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16);
2554  ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16);
2555  ret_val->boot_type = IVAL(file_header, 0xFF8);
2556  ret_val->boot_recover = IVAL(file_header, 0xFFC);
2557
2558  return ret_val;
2559
2560 fail:
2561  talloc_free(ret_val);
2562  return NULL;
2563}
2564
2565
2566
2567/******************************************************************************
2568 * Given real file offset, read and parse the hbin at that location
2569 * along with it's associated cells.
2570 ******************************************************************************/
2571REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
2572{
2573  REGFI_HBIN* hbin = NULL;
2574  uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE];
2575  uint32_t length;
2576 
2577  if(offset >= file->file_length)
2578    goto fail;
2579 
2580  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_hbin"))
2581    goto fail;
2582
2583  if(regfi_seek(file->cb, offset, SEEK_SET) == -1)
2584  {
2585    regfi_log_add(REGFI_LOG_ERROR, "Seek failed"
2586                  " while parsing hbin at offset 0x%.8X.", offset);
2587    goto fail_locked;
2588  }
2589
2590  length = REGFI_HBIN_HEADER_SIZE;
2591  if((regfi_read(file->cb, hbin_header, &length) != 0) 
2592     || length != REGFI_HBIN_HEADER_SIZE)
2593  {
2594    regfi_log_add(REGFI_LOG_ERROR, "Read failed"
2595                  " while parsing hbin at offset 0x%.8X.", offset);
2596    goto fail_locked;
2597  }
2598
2599  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin"))
2600    goto fail;
2601
2602  hbin = talloc(NULL, REGFI_HBIN);
2603  if(hbin == NULL)
2604    goto fail;
2605  hbin->file_off = offset;
2606
2607  memcpy(hbin->magic, hbin_header, 4);
2608  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
2609  {
2610    /* This always seems to happen at the end of a file, so we make it an INFO
2611     * message, rather than something more serious.
2612     */
2613    regfi_log_add(REGFI_LOG_INFO, "Magic number mismatch "
2614                  "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
2615                  " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
2616                  hbin->magic[2], hbin->magic[3], offset);
2617    goto fail;
2618  }
2619
2620  hbin->first_hbin_off = IVAL(hbin_header, 0x4);
2621  hbin->block_size = IVAL(hbin_header, 0x8);
2622  /* this should be the same thing as hbin->block_size, but just in case */
2623  hbin->next_block = IVAL(hbin_header, 0x1C);
2624
2625
2626  /* Ensure the block size is a multiple of 0x1000 and doesn't run off
2627   * the end of the file.
2628   */
2629  /* XXX: This may need to be relaxed for dealing with
2630   *      partial or corrupt files.
2631   */
2632  if((offset + hbin->block_size > file->file_length)
2633     || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
2634  {
2635    regfi_log_add(REGFI_LOG_ERROR, "The hbin offset is not aligned"
2636                  " or runs off the end of the file"
2637                  " while parsing hbin at offset 0x%.8X.", offset);
2638    goto fail;
2639  }
2640
2641  return hbin;
2642
2643 fail_locked:
2644  regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin");
2645 fail:
2646  talloc_free(hbin);
2647  return NULL;
2648}
2649
2650
2651/*******************************************************************
2652 *******************************************************************/
2653REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, 
2654                         uint32_t max_size, bool strict)
2655{
2656  uint8_t nk_header[REGFI_NK_MIN_LENGTH];
2657  REGFI_NK* ret_val;
2658  uint32_t length,cell_length;
2659  bool unalloc = false;
2660
2661  ret_val = talloc(NULL, REGFI_NK);
2662  if(ret_val == NULL)
2663  {
2664    regfi_log_add(REGFI_LOG_ERROR, "Failed to allocate memory while"
2665                  " parsing NK record at offset 0x%.8X.", offset);
2666    goto fail;
2667  }
2668
2669  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
2670    goto fail;
2671
2672  if(!regfi_parse_cell(file->cb, offset, nk_header, REGFI_NK_MIN_LENGTH,
2673                       &cell_length, &unalloc))
2674  {
2675    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
2676                  " while parsing NK record at offset 0x%.8X.", offset);
2677    goto fail_locked;
2678  }
2679
2680  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
2681  {
2682    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
2683                  " NK record at offset 0x%.8X.", offset);
2684    goto fail_locked;
2685  }
2686
2687  ret_val->values = NULL;
2688  ret_val->subkeys = NULL;
2689  ret_val->offset = offset;
2690  ret_val->cell_size = cell_length;
2691
2692  if(ret_val->cell_size > max_size)
2693    ret_val->cell_size = max_size & 0xFFFFFFF8;
2694  if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
2695     || (strict && (ret_val->cell_size & 0x00000007) != 0))
2696  {
2697    regfi_log_add(REGFI_LOG_WARN, "A length check failed while"
2698                  " parsing NK record at offset 0x%.8X.", offset);
2699    goto fail_locked;
2700  }
2701
2702  ret_val->magic[0] = nk_header[0x0];
2703  ret_val->magic[1] = nk_header[0x1];
2704  ret_val->flags = SVAL(nk_header, 0x2);
2705 
2706  if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0)
2707  {
2708    regfi_log_add(REGFI_LOG_WARN, "Unknown key flags (0x%.4X) while"
2709                  " parsing NK record at offset 0x%.8X.", 
2710                  (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
2711  }
2712
2713  ret_val->mtime.low = IVAL(nk_header, 0x4);
2714  ret_val->mtime.high = IVAL(nk_header, 0x8);
2715  /* If the key is unallocated and the MTIME is earlier than Jan 1, 1990
2716   * or later than Jan 1, 2290, we consider this a bad key.  This helps
2717   * weed out some false positives during deleted data recovery.
2718   */
2719  if(unalloc
2720     && (ret_val->mtime.high < REGFI_MTIME_MIN_HIGH
2721         || ret_val->mtime.high > REGFI_MTIME_MAX_HIGH))
2722  { goto fail_locked; }
2723
2724  ret_val->unknown1 = IVAL(nk_header, 0xC);
2725  ret_val->parent_off = IVAL(nk_header, 0x10);
2726  ret_val->num_subkeys = IVAL(nk_header, 0x14);
2727  ret_val->unknown2 = IVAL(nk_header, 0x18);
2728  ret_val->subkeys_off = IVAL(nk_header, 0x1C);
2729  ret_val->unknown3 = IVAL(nk_header, 0x20);
2730  ret_val->num_values = IVAL(nk_header, 0x24);
2731  ret_val->values_off = IVAL(nk_header, 0x28);
2732  ret_val->sk_off = IVAL(nk_header, 0x2C);
2733  ret_val->classname_off = IVAL(nk_header, 0x30);
2734
2735  ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
2736  ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38);
2737  ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C);
2738  ret_val->max_bytes_value = IVAL(nk_header, 0x40);
2739  ret_val->unk_index = IVAL(nk_header, 0x44);
2740
2741  ret_val->name_length = SVAL(nk_header, 0x48);
2742  ret_val->classname_length = SVAL(nk_header, 0x4A);
2743  ret_val->name = NULL;
2744
2745  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
2746  {
2747    if(strict)
2748    {
2749      regfi_log_add(REGFI_LOG_ERROR, "Contents too large for cell"
2750                    " while parsing NK record at offset 0x%.8X.", offset);
2751      goto fail_locked;
2752    }
2753    else
2754      ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
2755  }
2756  else if (unalloc)
2757  { /* Truncate cell_size if it's much larger than the apparent total record length. */
2758    /* Round up to the next multiple of 8 */
2759    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
2760    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
2761      length+=8;
2762
2763    /* If cell_size is still greater, truncate. */
2764    if(length < ret_val->cell_size)
2765      ret_val->cell_size = length;
2766  }
2767
2768  /* +1 to length in case we decided to use this directly as a string later */
2769  ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
2770  if(ret_val->name_raw == NULL)
2771    goto fail_locked;
2772
2773  /* Don't need to seek, should be at the right offset */
2774  length = ret_val->name_length;
2775  if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
2776     || length != ret_val->name_length)
2777  {
2778    regfi_log_add(REGFI_LOG_ERROR, "Failed to read key name"
2779                  " while parsing NK record at offset 0x%.8X.", offset);
2780    goto fail_locked;
2781  }
2782
2783  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
2784    goto fail;
2785
2786  return ret_val;
2787
2788 fail_locked:
2789  regfi_unlock(file, &file->cb_lock, "regfi_parse_nk");
2790 fail:
2791  talloc_free(ret_val);
2792  return NULL;
2793}
2794
2795
2796uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, 
2797                               uint16_t* name_length, uint32_t max_size, bool strict)
2798{
2799  uint8_t* ret_val = NULL;
2800  uint32_t length;
2801  uint32_t cell_length;
2802  bool unalloc = false;
2803
2804  if(*name_length <= 0 || offset == REGFI_OFFSET_NONE 
2805     || (offset & 0x00000007) != 0)
2806  { goto fail; }
2807
2808  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_classname"))
2809    goto fail;
2810
2811  if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
2812  {
2813    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
2814                  " while parsing class name at offset 0x%.8X.", offset);
2815    goto fail_locked;
2816  }
2817 
2818  if((cell_length & 0x0000007) != 0)
2819  {
2820    regfi_log_add(REGFI_LOG_ERROR, "Cell length not a multiple of 8"
2821                  " while parsing class name at offset 0x%.8X.", offset);
2822    goto fail_locked;
2823  }
2824 
2825  if(cell_length > max_size)
2826  {
2827    regfi_log_add(REGFI_LOG_WARN, "Cell stretches past hbin "
2828                  "boundary while parsing class name at offset 0x%.8X.",
2829                  offset);
2830    if(strict)
2831      goto fail_locked;
2832    cell_length = max_size;
2833  }
2834 
2835  if((cell_length - 4) < *name_length)
2836  {
2837    regfi_log_add(REGFI_LOG_WARN, "Class name is larger than"
2838                  " cell_length while parsing class name at offset"
2839                  " 0x%.8X.", offset);
2840    if(strict)
2841      goto fail_locked;
2842    *name_length = cell_length - 4;
2843  }
2844 
2845  ret_val = talloc_array(NULL, uint8_t, *name_length);
2846  if(ret_val != NULL)
2847  {
2848    length = *name_length;
2849    if((regfi_read(file->cb, ret_val, &length) != 0)
2850       || length != *name_length)
2851    {
2852      regfi_log_add(REGFI_LOG_ERROR, "Could not read class name"
2853                    " while parsing class name at offset 0x%.8X.", offset);
2854      goto fail_locked;
2855    }
2856  }
2857
2858  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_classname"))
2859    goto fail;
2860
2861  return ret_val;
2862
2863 fail_locked:
2864  regfi_unlock(file, &file->cb_lock, "regfi_parse_classname");
2865 fail:
2866  talloc_free(ret_val);
2867  return NULL;
2868}
2869
2870
2871/******************************************************************************
2872*******************************************************************************/
2873REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
2874                             uint32_t max_size, bool strict)
2875{
2876  REGFI_VK* ret_val;
2877  uint8_t vk_header[REGFI_VK_MIN_LENGTH];
2878  uint32_t raw_data_size, length, cell_length;
2879  bool unalloc = false;
2880
2881  ret_val = talloc(NULL, REGFI_VK);
2882  if(ret_val == NULL)
2883    goto fail;
2884
2885  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
2886    goto fail;
2887
2888  if(!regfi_parse_cell(file->cb, offset, vk_header, REGFI_VK_MIN_LENGTH,
2889                       &cell_length, &unalloc))
2890  {
2891    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
2892                  " while parsing VK record at offset 0x%.8X.", offset);
2893    goto fail_locked;
2894  }
2895
2896  ret_val->offset = offset;
2897  ret_val->cell_size = cell_length;
2898  ret_val->name = NULL;
2899  ret_val->name_raw = NULL;
2900 
2901  if(ret_val->cell_size > max_size)
2902    ret_val->cell_size = max_size & 0xFFFFFFF8;
2903  if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
2904     || (ret_val->cell_size & 0x00000007) != 0)
2905  {
2906    regfi_log_add(REGFI_LOG_WARN, "Invalid cell size encountered"
2907                  " while parsing VK record at offset 0x%.8X.", offset);
2908    goto fail_locked;
2909  }
2910
2911  ret_val->magic[0] = vk_header[0x0];
2912  ret_val->magic[1] = vk_header[0x1];
2913  if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
2914  {
2915    /* XXX: This does not account for deleted keys under Win2K which
2916     *      often have this (and the name length) overwritten with
2917     *      0xFFFF.
2918     */
2919    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch"
2920                  " while parsing VK record at offset 0x%.8X.", offset);
2921    goto fail_locked;
2922  }
2923
2924  ret_val->name_length = SVAL(vk_header, 0x2);
2925  raw_data_size = IVAL(vk_header, 0x4);
2926  ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
2927  /* The data is typically stored in the offset if the size <= 4,
2928   * in which case this flag is set.
2929   */
2930  ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
2931  ret_val->data_off = IVAL(vk_header, 0x8);
2932  ret_val->type = IVAL(vk_header, 0xC);
2933  ret_val->flags = SVAL(vk_header, 0x10);
2934  ret_val->unknown1 = SVAL(vk_header, 0x12);
2935
2936  if(ret_val->name_length > 0)
2937  {
2938    if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
2939    {
2940      regfi_log_add(REGFI_LOG_WARN, "Name too long for remaining cell"
2941                    " space while parsing VK record at offset 0x%.8X.",
2942                    offset);
2943      if(strict)
2944        goto fail_locked;
2945      else
2946        ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4;
2947    }
2948
2949    /* Round up to the next multiple of 8 */
2950    cell_length = (ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) & 0xFFFFFFF8;
2951    if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
2952      cell_length+=8;
2953
2954    /* +1 to length in case we decided to use this directly as a string later */
2955    ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
2956    if(ret_val->name_raw == NULL)
2957      goto fail_locked;
2958
2959    length = ret_val->name_length;
2960    if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
2961       || length != ret_val->name_length)
2962    {
2963      regfi_log_add(REGFI_LOG_ERROR, "Could not read value name"
2964                    " while parsing VK record at offset 0x%.8X.", offset);
2965      goto fail_locked;
2966    }
2967  }
2968  else
2969    cell_length = REGFI_VK_MIN_LENGTH + 4;
2970
2971  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
2972    goto fail;
2973
2974  if(unalloc)
2975  {
2976    /* If cell_size is still greater, truncate. */
2977    if(cell_length < ret_val->cell_size)
2978      ret_val->cell_size = cell_length;
2979  }
2980
2981  return ret_val;
2982 
2983 fail_locked:
2984  regfi_unlock(file, &file->cb_lock, "regfi_parse_vk");
2985 fail:
2986  talloc_free(ret_val);
2987  return NULL;
2988}
2989
2990
2991/******************************************************************************
2992 *
2993 ******************************************************************************/
2994REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
2995                             uint32_t length, bool data_in_offset,
2996                             bool strict)
2997{
2998  REGFI_BUFFER ret_val;
2999  uint32_t cell_length, offset;
3000  int32_t max_size;
3001  bool unalloc;
3002 
3003  /* Microsoft's documentation indicates that "available memory" is
3004   * the limit on value sizes for the more recent registry format version.
3005   * This is not only annoying, but it's probably also incorrect, since clearly
3006   * value data sizes are limited to 2^31 (high bit used as a flag) and even
3007   * with big data records, the apparent max size is:
3008   *   16344 * 2^16 = 1071104040 (~1GB).
3009   *
3010   * We choose to limit it to 1M which was the limit in older versions and
3011   * should rarely be exceeded unless the file is corrupt or malicious.
3012   * For more info, see:
3013   *   http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
3014   */
3015  /* XXX: add way to skip this check at user discression. */
3016  if(length > REGFI_VK_MAX_DATA_LENGTH)
3017  {
3018    regfi_log_add(REGFI_LOG_WARN, "Value data size %d larger than "
3019                  "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
3020    length = REGFI_VK_MAX_DATA_LENGTH;
3021  }
3022
3023  if(data_in_offset)
3024    return regfi_parse_little_data(file, voffset, length, strict);
3025  else
3026  {
3027    offset = voffset + REGFI_REGF_SIZE;
3028    max_size = regfi_calc_maxsize(file, offset);
3029    if(max_size < 0)
3030    {
3031      regfi_log_add(REGFI_LOG_WARN, "Could not find HBIN for data"
3032                    " at offset 0x%.8X.", offset);
3033      goto fail;
3034    }
3035   
3036    if(!regfi_lock(file, &file->cb_lock, "regfi_load_data"))
3037      goto fail;
3038
3039    if(!regfi_parse_cell(file->cb, offset, NULL, 0,
3040                         &cell_length, &unalloc))
3041    {
3042      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
3043                    " parsing data record at offset 0x%.8X.", offset);
3044      goto fail_locked;
3045    }
3046
3047    if(!regfi_unlock(file, &file->cb_lock, "regfi_load_data"))
3048      goto fail;
3049
3050    if((cell_length & 0x00000007) != 0)
3051    {
3052      regfi_log_add(REGFI_LOG_WARN, "Cell length not multiple of 8"
3053                    " while parsing data record at offset 0x%.8X.",
3054                    offset);
3055      goto fail;
3056    }
3057
3058    if(cell_length > max_size)
3059    {
3060      regfi_log_add(REGFI_LOG_WARN, "Cell extends past HBIN boundary"
3061                    " while parsing data record at offset 0x%.8X.",
3062                    offset);
3063      goto fail;
3064    }
3065
3066    if(cell_length - 4 < length)
3067    {
3068      /* XXX: All big data records thus far have been 16 bytes long. 
3069       *      Should we check for this precise size instead of just
3070       *      relying upon the above check?
3071       */
3072      if (file->major_version >= 1 && file->minor_version >= 5)
3073      {
3074        /* Attempt to parse a big data record */
3075        return regfi_load_big_data(file, offset, length, cell_length, 
3076                                   NULL, strict);
3077      }
3078      else
3079      {
3080        regfi_log_add(REGFI_LOG_WARN, "Data length (0x%.8X) larger than"
3081                      " remaining cell length (0x%.8X)"
3082                      " while parsing data record at offset 0x%.8X.", 
3083                      length, cell_length - 4, offset);
3084        if(strict)
3085          goto fail;
3086        else
3087          length = cell_length - 4;
3088      }
3089    }
3090
3091    ret_val = regfi_parse_data(file, offset, length, strict);
3092  }
3093
3094  return ret_val;
3095
3096 fail_locked:
3097  regfi_unlock(file, &file->cb_lock, "regfi_load_data");
3098 fail:
3099  ret_val.buf = NULL;
3100  ret_val.len = 0;
3101  return ret_val;
3102}
3103
3104
3105/******************************************************************************
3106 * Parses the common case data records stored in a single cell.
3107 ******************************************************************************/
3108REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
3109                              uint32_t length, bool strict)
3110{
3111  REGFI_BUFFER ret_val;
3112  uint32_t read_length;
3113
3114  ret_val.buf = NULL;
3115  ret_val.len = 0;
3116 
3117  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
3118    goto fail;
3119  ret_val.len = length;
3120
3121  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_data"))
3122    goto fail;
3123
3124  if(regfi_seek(file->cb, offset+4, SEEK_SET) == -1)
3125  {
3126    regfi_log_add(REGFI_LOG_WARN, "Could not seek while "
3127                  "reading data at offset 0x%.8X.", offset);
3128    goto fail_locked;
3129  }
3130 
3131  read_length = length;
3132  if((regfi_read(file->cb, ret_val.buf, &read_length) != 0)
3133     || read_length != length)
3134  {
3135    regfi_log_add(REGFI_LOG_ERROR, "Could not read data block while"
3136                  " parsing data record at offset 0x%.8X.", offset);
3137    goto fail_locked;
3138  }
3139
3140  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_data"))
3141    goto fail;
3142
3143  return ret_val;
3144
3145 fail_locked:
3146  regfi_unlock(file, &file->cb_lock, "regfi_parse_data");
3147 fail:
3148  talloc_free(ret_val.buf);
3149  ret_val.buf = NULL;
3150  ret_val.buf = 0;
3151  return ret_val;
3152}
3153
3154
3155
3156/******************************************************************************
3157 *
3158 ******************************************************************************/
3159REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
3160                                     uint32_t length, bool strict)
3161{
3162  uint8_t i;
3163  REGFI_BUFFER ret_val;
3164
3165  ret_val.buf = NULL;
3166  ret_val.len = 0;
3167
3168  if(length > 4)
3169  {
3170    regfi_log_add(REGFI_LOG_ERROR, "Data in offset but length > 4"
3171                  " while parsing data record. (voffset=0x%.8X, length=%d)",
3172                  voffset, length);
3173    return ret_val;
3174  }
3175
3176  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
3177    return ret_val;
3178  ret_val.len = length;
3179 
3180  for(i = 0; i < length; i++)
3181    ret_val.buf[i] = (uint8_t)((voffset >> i*8) & 0xFF);
3182
3183  return ret_val;
3184}
3185
3186/******************************************************************************
3187*******************************************************************************/
3188REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset, 
3189                                         uint32_t max_size, bool strict)
3190{
3191  REGFI_BUFFER ret_val;
3192  uint32_t cell_length;
3193  bool unalloc;
3194
3195  /* XXX: do something with unalloc? */
3196  ret_val.buf = (uint8_t*)talloc_array(NULL, uint8_t, REGFI_BIG_DATA_MIN_LENGTH);
3197  if(ret_val.buf == NULL)
3198    goto fail;
3199
3200  if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
3201  {
3202    regfi_log_add(REGFI_LOG_WARN, "Big data header exceeded max_size "
3203                  "while parsing big data header at offset 0x%.8X.",offset);
3204    goto fail;
3205  }
3206
3207  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_header"))
3208    goto fail;
3209
3210
3211  if(!regfi_parse_cell(file->cb, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
3212                       &cell_length, &unalloc))
3213  {
3214    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
3215                  " parsing big data header at offset 0x%.8X.", offset);
3216    goto fail_locked;
3217  }
3218
3219  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header"))
3220    goto fail;
3221
3222  if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
3223  {
3224    regfi_log_add(REGFI_LOG_WARN, "Unknown magic number"
3225                  " (0x%.2X, 0x%.2X) encountered while parsing"
3226                  " big data header at offset 0x%.8X.", 
3227                  ret_val.buf[0], ret_val.buf[1], offset);
3228    goto fail;
3229  }
3230
3231  ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
3232  return ret_val;
3233
3234 fail_locked:
3235  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header");
3236 fail:
3237  talloc_free(ret_val.buf);
3238  ret_val.buf = NULL;
3239  ret_val.len = 0;
3240  return ret_val;
3241}
3242
3243
3244
3245/******************************************************************************
3246 *
3247 ******************************************************************************/
3248uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
3249                                      uint16_t num_chunks, bool strict)
3250{
3251  uint32_t* ret_val;
3252  uint32_t indirect_length;
3253  int32_t max_size;
3254  uint16_t i;
3255  bool unalloc;
3256
3257  /* XXX: do something with unalloc? */
3258
3259  max_size = regfi_calc_maxsize(file, offset);
3260  if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size))
3261    return NULL;
3262
3263  ret_val = (uint32_t*)talloc_array(NULL, uint32_t, num_chunks);
3264  if(ret_val == NULL)
3265    goto fail;
3266
3267  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
3268    goto fail;
3269
3270  if(!regfi_parse_cell(file->cb, offset, (uint8_t*)ret_val,
3271                       num_chunks*sizeof(uint32_t),
3272                       &indirect_length, &unalloc))
3273  {
3274    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
3275                  " parsing big data indirect record at offset 0x%.8X.", 
3276                  offset);
3277    goto fail_locked;
3278  }
3279
3280  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
3281    goto fail;
3282
3283  /* Convert pointers to proper endianess, verify they are aligned. */
3284  for(i=0; i<num_chunks; i++)
3285  {
3286    ret_val[i] = IVAL(ret_val, i*sizeof(uint32_t));
3287    if((ret_val[i] & 0x00000007) != 0)
3288      goto fail;
3289  }
3290 
3291  return ret_val;
3292
3293 fail_locked:
3294  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect");
3295 fail:
3296  talloc_free(ret_val);
3297  return NULL;
3298}
3299
3300
3301/******************************************************************************
3302 * Arguments:
3303 *  file       --
3304 *  offsets    -- list of virtual offsets.
3305 *  num_chunks --
3306 *  strict     --
3307 *
3308 * Returns:
3309 *  A range_list with physical offsets and complete lengths
3310 *  (including cell headers) of associated cells. 
3311 *  No data in range_list elements.
3312 ******************************************************************************/
3313range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32_t* offsets,
3314                                       uint16_t num_chunks, bool strict)
3315{
3316  uint32_t cell_length, chunk_offset;
3317  range_list* ret_val;
3318  uint16_t i;
3319  bool unalloc;
3320 
3321  /* XXX: do something with unalloc? */
3322  ret_val = range_list_new();
3323  if(ret_val == NULL)
3324    goto fail;
3325 
3326  for(i=0; i<num_chunks; i++)
3327  {
3328    if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
3329      goto fail;
3330
3331    chunk_offset = offsets[i]+REGFI_REGF_SIZE;
3332    if(!regfi_parse_cell(file->cb, chunk_offset, NULL, 0,
3333                         &cell_length, &unalloc))
3334    {
3335      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
3336                    " parsing big data chunk at offset 0x%.8X.", 
3337                    chunk_offset);
3338      goto fail_locked;
3339    }
3340
3341    if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
3342      goto fail;
3343
3344    if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
3345      goto fail;
3346  }
3347
3348  return ret_val;
3349
3350 fail_locked:
3351  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells");
3352 fail:
3353  if(ret_val != NULL)
3354    range_list_free(ret_val);
3355  return NULL;
3356}
3357
3358
3359/******************************************************************************
3360*******************************************************************************/
3361REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
3362                                 uint32_t offset, uint32_t data_length, 
3363                                 uint32_t cell_length, range_list* used_ranges,
3364                                 bool strict)
3365{
3366  REGFI_BUFFER ret_val;
3367  uint16_t num_chunks, i;
3368  uint32_t read_length, data_left, tmp_len, indirect_offset;
3369  uint32_t* indirect_ptrs = NULL;
3370  REGFI_BUFFER bd_header;
3371  range_list* bd_cells = NULL;
3372  const range_list_element* cell_info;
3373
3374  ret_val.buf = NULL;
3375
3376  /* XXX: Add better error/warning messages */
3377
3378  bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict);
3379  if(bd_header.buf == NULL)
3380    goto fail;
3381
3382  /* Keep track of used space for use by reglookup-recover */
3383  if(used_ranges != NULL)
3384    if(!range_list_add(used_ranges, offset, cell_length, NULL))
3385      goto fail;
3386
3387  num_chunks = SVAL(bd_header.buf, 0x2);
3388  indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE;
3389  talloc_free(bd_header.buf);
3390
3391  indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset,
3392                                                num_chunks, strict);
3393  if(indirect_ptrs == NULL)
3394    goto fail;
3395
3396  if(used_ranges != NULL)
3397    if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL))
3398      goto fail;
3399 
3400  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
3401    goto fail;
3402  data_left = data_length;
3403
3404  bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict);
3405  if(bd_cells == NULL)
3406    goto fail;
3407
3408  talloc_free(indirect_ptrs);
3409  indirect_ptrs = NULL;
3410 
3411  for(i=0; (i<num_chunks) && (data_left>0); i++)
3412  {
3413    cell_info = range_list_get(bd_cells, i);
3414    if(cell_info == NULL)
3415      goto fail;
3416
3417    /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell
3418     *      length.  However, it has been observed that some (all?) chunks
3419     *      have an additional 4 bytes of 0 at the end of their cells that
3420     *      isn't part of the data, so we're trimming that off too.
3421     *      Perhaps it's just an 8 byte alignment requirement...
3422     */
3423    if(cell_info->length - 8 >= data_left)
3424    {
3425      if(i+1 != num_chunks)
3426      {
3427        regfi_log_add(REGFI_LOG_WARN, "Left over chunks detected "
3428                      "while constructing big data at offset 0x%.8X "
3429                      "(chunk offset 0x%.8X).", offset, cell_info->offset);
3430      }
3431      read_length = data_left;
3432    }
3433    else
3434      read_length = cell_info->length - 8;
3435
3436
3437    if(read_length > regfi_calc_maxsize(file, cell_info->offset))
3438    {
3439      regfi_log_add(REGFI_LOG_WARN, "A chunk exceeded the maxsize "
3440                    "while constructing big data at offset 0x%.8X "
3441                    "(chunk offset 0x%.8X).", offset, cell_info->offset);
3442      goto fail;
3443    }
3444
3445    if(!regfi_lock(file, &file->cb_lock, "regfi_load_big_data"))
3446      goto fail;
3447
3448    if(regfi_seek(file->cb, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
3449    {
3450      regfi_log_add(REGFI_LOG_WARN, "Could not seek to chunk while "
3451                    "constructing big data at offset 0x%.8X "
3452                    "(chunk offset 0x%.8X).", offset, cell_info->offset);
3453      goto fail_locked;
3454    }
3455
3456    tmp_len = read_length;
3457    if(regfi_read(file->cb, ret_val.buf+(data_length-data_left), 
3458                  &read_length) != 0 || (read_length != tmp_len))
3459    {
3460      regfi_log_add(REGFI_LOG_WARN, "Could not read data chunk while"
3461                    " constructing big data at offset 0x%.8X"
3462                    " (chunk offset 0x%.8X).", offset, cell_info->offset);
3463      goto fail_locked;
3464    }
3465
3466    if(!regfi_unlock(file, &file->cb_lock, "regfi_load_big_data"))
3467      goto fail;
3468
3469    if(used_ranges != NULL)
3470      if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
3471        goto fail;
3472
3473    data_left -= read_length;
3474  }
3475  range_list_free(bd_cells);
3476
3477  ret_val.len = data_length-data_left;
3478  return ret_val;
3479
3480 fail_locked:
3481  regfi_unlock(file, &file->cb_lock, "regfi_load_big_data");
3482 fail:
3483  talloc_free(ret_val.buf);
3484  talloc_free(indirect_ptrs);
3485  if(bd_cells != NULL)
3486    range_list_free(bd_cells);
3487  ret_val.buf = NULL;
3488  ret_val.len = 0;
3489  return ret_val;
3490}
3491
3492
3493range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
3494{
3495  range_list* ret_val;
3496  REGFI_HBIN* hbin;
3497  const range_list_element* hbins_elem;
3498  uint32_t i, num_hbins, curr_off, cell_len;
3499  bool is_unalloc;
3500
3501  ret_val = range_list_new();
3502  if(ret_val == NULL)
3503    return NULL;
3504
3505  if(!regfi_read_lock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
3506  {
3507    range_list_free(ret_val);
3508    return NULL;
3509  }
3510
3511  num_hbins = range_list_size(file->hbins);
3512  for(i=0; i<num_hbins; i++)
3513  {
3514    hbins_elem = range_list_get(file->hbins, i);
3515    if(hbins_elem == NULL)
3516      break;
3517    hbin = (REGFI_HBIN*)hbins_elem->data;
3518
3519    curr_off = REGFI_HBIN_HEADER_SIZE;
3520    while(curr_off < hbin->block_size)
3521    {
3522      if(!regfi_lock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
3523        break;
3524
3525      if(!regfi_parse_cell(file->cb, hbin->file_off+curr_off, NULL, 0,
3526                           &cell_len, &is_unalloc))
3527      {
3528        regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells");
3529        break;
3530      }
3531
3532      if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
3533        break;
3534
3535      if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
3536      {
3537        regfi_log_add(REGFI_LOG_ERROR, "Bad cell length encountered"
3538                      " while parsing unallocated cells at offset 0x%.8X.",
3539                      hbin->file_off+curr_off);
3540        break;
3541      }
3542
3543      /* for some reason the record_size of the last record in
3544         an hbin block can extend past the end of the block
3545         even though the record fits within the remaining
3546         space....aaarrrgggghhhhhh */ 
3547      if(curr_off + cell_len >= hbin->block_size)
3548        cell_len = hbin->block_size - curr_off;
3549     
3550      if(is_unalloc)
3551        range_list_add(ret_val, hbin->file_off+curr_off, 
3552                       cell_len, NULL);
3553     
3554      curr_off = curr_off+cell_len;
3555    }
3556  }
3557
3558  if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
3559  {
3560    range_list_free(ret_val);
3561    return NULL;
3562  }
3563
3564  return ret_val;
3565}
3566
3567
3568/* From lib/time.c */
3569
3570/****************************************************************************
3571 Put a 8 byte filetime from a time_t
3572 This takes real GMT as input and converts to kludge-GMT
3573****************************************************************************/
3574void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
3575{
3576  double d;
3577 
3578  if (t==0) 
3579  {
3580    nt->low = 0;
3581    nt->high = 0;
3582    return;
3583  }
3584 
3585  if (t == TIME_T_MAX) 
3586  {
3587    nt->low = 0xffffffff;
3588    nt->high = 0x7fffffff;
3589    return;
3590  }             
3591 
3592  if (t == -1) 
3593  {
3594    nt->low = 0xffffffff;
3595    nt->high = 0xffffffff;
3596    return;
3597  }             
3598 
3599  /* this converts GMT to kludge-GMT */
3600  /* XXX: This was removed due to difficult dependency requirements. 
3601   *      So far, times appear to be correct without this adjustment, but
3602   *      that may be proven wrong with adequate testing.
3603   */
3604  /* t -= TimeDiff(t) - get_serverzone(); */
3605 
3606  d = (double)(t);
3607  d += TIME_FIXUP_CONSTANT;
3608  d *= 1.0e7;
3609 
3610  nt->high = (uint32_t)(d * (1.0/(4.0*(double)(1<<30))));
3611  nt->low  = (uint32_t)(d - ((double)nt->high)*4.0*(double)(1<<30));
3612}
3613
3614
3615/****************************************************************************
3616 Interpret an 8 byte "filetime" structure to a time_t
3617 It's originally in "100ns units since jan 1st 1601"
3618
3619 An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0.
3620
3621 It appears to be kludge-GMT (at least for file listings). This means
3622 its the GMT you get by taking a localtime and adding the
3623 serverzone. This is NOT the same as GMT in some cases. This routine
3624 converts this to real GMT.
3625****************************************************************************/
3626time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
3627{
3628  double d;
3629  time_t ret;
3630  /* The next two lines are a fix needed for the
3631     broken SCO compiler. JRA. */
3632  time_t l_time_min = TIME_T_MIN;
3633  time_t l_time_max = TIME_T_MAX;
3634 
3635  if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
3636    return(0);
3637 
3638  d = ((double)nt->high)*4.0*(double)(1<<30);
3639  d += (nt->low&0xFFF00000);
3640  d *= 1.0e-7;
3641 
3642  /* now adjust by 369 years to make the secs since 1970 */
3643  d -= TIME_FIXUP_CONSTANT;
3644 
3645  if (d <= l_time_min)
3646    return (l_time_min);
3647 
3648  if (d >= l_time_max)
3649    return (l_time_max);
3650 
3651  ret = (time_t)(d+0.5);
3652 
3653  /* this takes us from kludge-GMT to real GMT */
3654  /* XXX: This was removed due to difficult dependency requirements. 
3655   *      So far, times appear to be correct without this adjustment, but
3656   *      that may be proven wrong with adequate testing.
3657   */
3658  /*
3659    ret -= get_serverzone();
3660    ret += LocTimeDiff(ret);
3661  */
3662
3663  return(ret);
3664}
3665
3666/* End of stuff from lib/time.c */
Note: See TracBrowser for help on using the repository browser.