source: trunk/lib/regfi.c @ 139

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

rewrote subkey list parsing code to include more organized recursion and parsing as well as improved sanity checking

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