source: trunk/lib/regfi.c @ 109

Last change on this file since 109 was 109, checked in by tim, 16 years ago

reworked SK record caching to prevent unnecessary loads.

SK records now cached in iterators on an as-needed basis

  • Property svn:keywords set to Id
File size: 44.3 KB
Line 
1/*
2 * Branched from Samba project Subversion repository, version #7470:
3 *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
4 *
5 * Unix SMB/CIFS implementation.
6 * Windows NT registry I/O library
7 *
8 * Copyright (C) 2005-2008 Timothy D. Morgan
9 * Copyright (C) 2005 Gerald (Jerry) Carter
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
23 *
24 * $Id: regfi.c 109 2008-04-29 20:38:27Z tim $
25 */
26
27#include "../include/regfi.h"
28
29
30/* Registry types mapping */
31const unsigned int regfi_num_reg_types = 12;
32static const char* regfi_type_names[] =
33  {"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK",
34   "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
35
36
37/* Returns NULL on error */
38const char* regfi_type_val2str(unsigned int val)
39{
40  if(val == REG_KEY)
41    return "KEY";
42 
43  if(val >= regfi_num_reg_types)
44    return NULL;
45 
46  return regfi_type_names[val];
47}
48
49
50/* Returns -1 on error */
51int regfi_type_str2val(const char* str)
52{
53  int i;
54
55  if(strcmp("KEY", str) == 0)
56    return REG_KEY;
57
58  for(i=0; i < regfi_num_reg_types; i++)
59    if (strcmp(regfi_type_names[i], str) == 0) 
60      return i;
61
62  if(strcmp("DWORD_LE", str) == 0)
63    return REG_DWORD_LE;
64
65  return -1;
66}
67
68
69/* Security descriptor parsing functions  */
70
71const char* regfi_ace_type2str(uint8 type)
72{
73  static const char* map[7] 
74    = {"ALLOW", "DENY", "AUDIT", "ALARM", 
75       "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"};
76  if(type < 7)
77    return map[type];
78  else
79    /* XXX: would be nice to return the unknown integer value. 
80     *      However, as it is a const string, it can't be free()ed later on,
81     *      so that would need to change.
82     */
83    return "UNKNOWN";
84}
85
86
87/* XXX: need a better reference on the meaning of each flag. */
88/* For more info, see:
89 *   http://msdn2.microsoft.com/en-us/library/aa772242.aspx
90 */
91char* regfi_ace_flags2str(uint8 flags)
92{
93  static const char* flag_map[32] = 
94    { "OI", /* Object Inherit */
95      "CI", /* Container Inherit */
96      "NP", /* Non-Propagate */
97      "IO", /* Inherit Only */
98      "IA", /* Inherited ACE */
99      NULL,
100      NULL,
101      NULL,
102    };
103
104  char* ret_val = malloc(35*sizeof(char));
105  char* fo = ret_val;
106  uint32 i;
107  uint8 f;
108
109  if(ret_val == NULL)
110    return NULL;
111
112  fo[0] = '\0';
113  if (!flags)
114    return ret_val;
115
116  for(i=0; i < 8; i++)
117  {
118    f = (1<<i);
119    if((flags & f) && (flag_map[i] != NULL))
120    {
121      strcpy(fo, flag_map[i]);
122      fo += strlen(flag_map[i]);
123      *(fo++) = ' ';
124      flags ^= f;
125    }
126  }
127 
128  /* Any remaining unknown flags are added at the end in hex. */
129  if(flags != 0)
130    sprintf(fo, "0x%.2X ", flags);
131
132  /* Chop off the last space if we've written anything to ret_val */
133  if(fo != ret_val)
134    fo[-1] = '\0';
135
136  /* XXX: what was this old VI flag for??
137     XXX: Is this check right?  0xF == 1|2|4|8, which makes it redundant...
138  if (flags == 0xF) {
139    if (some) strcat(flg_output, " ");
140    some = 1;
141    strcat(flg_output, "VI");
142  }
143  */
144
145  return ret_val;
146}
147
148
149char* regfi_ace_perms2str(uint32 perms)
150{
151  uint32 i, p;
152  /* This is more than is needed by a fair margin. */
153  char* ret_val = malloc(350*sizeof(char));
154  char* r = ret_val;
155
156  /* Each represents one of 32 permissions bits.  NULL is for undefined/reserved bits.
157   * For more information, see:
158   *   http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
159   *   http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
160   */
161  static const char* perm_map[32] = 
162    {/* object-specific permissions (registry keys, in this case) */
163      "QRY_VAL",       /* KEY_QUERY_VALUE */
164      "SET_VAL",       /* KEY_SET_VALUE */
165      "CREATE_KEY",    /* KEY_CREATE_SUB_KEY */
166      "ENUM_KEYS",     /* KEY_ENUMERATE_SUB_KEYS */
167      "NOTIFY",        /* KEY_NOTIFY */
168      "CREATE_LNK",    /* KEY_CREATE_LINK - Reserved for system use. */
169      NULL,
170      NULL,
171      "WOW64_64",      /* KEY_WOW64_64KEY */
172      "WOW64_32",      /* KEY_WOW64_32KEY */
173      NULL,
174      NULL,
175      NULL,
176      NULL,
177      NULL,
178      NULL,
179      /* standard access rights */
180      "DELETE",        /* DELETE */
181      "R_CONT",        /* READ_CONTROL */
182      "W_DAC",         /* WRITE_DAC */
183      "W_OWNER",       /* WRITE_OWNER */
184      "SYNC",          /* SYNCHRONIZE - Shouldn't be set in registries */
185      NULL,
186      NULL,
187      NULL,
188      /* other generic */
189      "SYS_SEC",       /* ACCESS_SYSTEM_SECURITY */
190      "MAX_ALLWD",     /* MAXIMUM_ALLOWED */
191      NULL,
192      NULL,
193      "GEN_A",         /* GENERIC_ALL */
194      "GEN_X",         /* GENERIC_EXECUTE */
195      "GEN_W",         /* GENERIC_WRITE */
196      "GEN_R",         /* GENERIC_READ */
197    };
198
199
200  if(ret_val == NULL)
201    return NULL;
202
203  r[0] = '\0';
204  for(i=0; i < 32; i++)
205  {
206    p = (1<<i);
207    if((perms & p) && (perm_map[i] != NULL))
208    {
209      strcpy(r, perm_map[i]);
210      r += strlen(perm_map[i]);
211      *(r++) = ' ';
212      perms ^= p;
213    }
214  }
215 
216  /* Any remaining unknown permission bits are added at the end in hex. */
217  if(perms != 0)
218    sprintf(r, "0x%.8X ", perms);
219
220  /* Chop off the last space if we've written anything to ret_val */
221  if(r != ret_val)
222    r[-1] = '\0';
223
224  return ret_val;
225}
226
227
228char* regfi_sid2str(DOM_SID* sid)
229{
230  uint32 i, size = MAXSUBAUTHS*11 + 24;
231  uint32 left = size;
232  uint8 comps = sid->num_auths;
233  char* ret_val = malloc(size);
234 
235  if(ret_val == NULL)
236    return NULL;
237
238  if(comps > MAXSUBAUTHS)
239    comps = MAXSUBAUTHS;
240
241  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
242
243  for (i = 0; i < comps; i++) 
244    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
245
246  return ret_val;
247}
248
249
250char* regfi_get_acl(SEC_ACL* acl)
251{
252  uint32 i, extra, size = 0;
253  const char* type_str;
254  char* flags_str;
255  char* perms_str;
256  char* sid_str;
257  char* ace_delim = "";
258  char* ret_val = NULL;
259  char* tmp_val = NULL;
260  bool failed = false;
261  char field_delim = ':';
262
263  for (i = 0; i < acl->num_aces && !failed; i++)
264  {
265    sid_str = regfi_sid2str(&acl->ace[i].trustee);
266    type_str = regfi_ace_type2str(acl->ace[i].type);
267    perms_str = regfi_ace_perms2str(acl->ace[i].info.mask);
268    flags_str = regfi_ace_flags2str(acl->ace[i].flags);
269   
270    if(flags_str != NULL && perms_str != NULL 
271       && type_str != NULL && sid_str != NULL)
272    {
273      /* XXX: this is slow */
274      extra = strlen(sid_str) + strlen(type_str) 
275        + strlen(perms_str) + strlen(flags_str)+5;
276      tmp_val = realloc(ret_val, size+extra);
277
278      if(tmp_val == NULL)
279      {
280        free(ret_val);
281        failed = true;
282      }
283      else
284      {
285        ret_val = tmp_val;
286        size += snprintf(ret_val+size, extra, "%s%s%c%s%c%s%c%s",
287                         ace_delim,sid_str,
288                         field_delim,type_str,
289                         field_delim,perms_str,
290                         field_delim,flags_str);
291        ace_delim = "|";
292      }
293    }
294    else
295      failed = true;
296
297    if(sid_str != NULL)
298      free(sid_str);
299    if(sid_str != NULL)
300      free(perms_str);
301    if(sid_str != NULL)
302      free(flags_str);
303  }
304
305  return ret_val;
306}
307
308
309char* regfi_get_sacl(SEC_DESC *sec_desc)
310{
311  if (sec_desc->sacl)
312    return regfi_get_acl(sec_desc->sacl);
313  else
314    return NULL;
315}
316
317
318char* regfi_get_dacl(SEC_DESC *sec_desc)
319{
320  if (sec_desc->dacl)
321    return regfi_get_acl(sec_desc->dacl);
322  else
323    return NULL;
324}
325
326
327char* regfi_get_owner(SEC_DESC *sec_desc)
328{
329  return regfi_sid2str(sec_desc->owner_sid);
330}
331
332
333char* regfi_get_group(SEC_DESC *sec_desc)
334{
335  return regfi_sid2str(sec_desc->grp_sid);
336}
337
338
339/*****************************************************************************
340 * This function is just like read(2), except that it continues to
341 * re-try reading from the file descriptor if EINTR or EAGAIN is received. 
342 * regfi_read will attempt to read length bytes from fd and write them to buf.
343 *
344 * On success, 0 is returned.  Upon failure, an errno code is returned.
345 *
346 * The number of bytes successfully read is returned through the length
347 * parameter by reference.  If both the return value and length parameter are
348 * returned as 0, then EOF was encountered immediately
349 *****************************************************************************/
350uint32 regfi_read(int fd, uint8* buf, uint32* length)
351{
352  uint32 rsize = 0;
353  uint32 rret = 0;
354
355  do
356  {
357    rret = read(fd, buf + rsize, *length - rsize);
358    if(rret > 0)
359      rsize += rret;
360  }while(*length - rsize > 0 
361         && (rret > 0 || (rret == -1 && (errno == EAGAIN || errno == EINTR))));
362 
363  *length = rsize;
364  if (rret == -1 && errno != EINTR && errno != EAGAIN)
365    return errno;
366
367  return 0;
368}
369
370
371/*****************************************************************************
372 *
373 *****************************************************************************/
374static bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
375                             uint32* cell_length, bool* unalloc)
376{
377  uint32 length;
378  int32 raw_length;
379  uint8 tmp[4];
380
381  if(lseek(fd, offset, SEEK_SET) == -1)
382    return false;
383
384  length = 4;
385  if((regfi_read(fd, tmp, &length) != 0) || length != 4)
386    return false;
387  raw_length = IVALS(tmp, 0);
388
389  if(raw_length < 0)
390  {
391    (*cell_length) = raw_length*(-1);
392    (*unalloc) = false;
393  }
394  else
395  {
396    (*cell_length) = raw_length;
397    (*unalloc) = true;
398  }
399
400  if(*cell_length - 4 < hdr_len)
401    return false;
402
403  if(hdr_len > 0)
404  {
405    length = hdr_len;
406    if((regfi_read(fd, hdr, &length) != 0) || length != hdr_len)
407      return false;
408  }
409
410  return true;
411}
412
413
414/*******************************************************************
415 * Given an offset and an hbin, is the offset within that hbin?
416 * The offset is a virtual file offset.
417 *******************************************************************/
418static bool regfi_offset_in_hbin(REGF_HBIN* hbin, uint32 offset)
419{
420  if(!hbin)
421    return false;
422
423  if((offset > hbin->first_hbin_off) 
424     && (offset < (hbin->first_hbin_off + hbin->block_size)))
425    return true;
426               
427  return false;
428}
429
430
431
432/*******************************************************************
433 * Given a virtual offset, and receive the correpsonding HBIN
434 * block for it.  NULL if one doesn't exist.
435 *******************************************************************/
436static REGF_HBIN* regfi_lookup_hbin(REGF_FILE* file, uint32 offset)
437{
438  return (REGF_HBIN*)range_list_find_data(file->hbins, offset+REGF_BLOCKSIZE);
439}
440
441
442
443/*******************************************************************
444 TODO: not currently validating against max_size
445 *******************************************************************/
446REGF_HASH_LIST* regfi_load_hashlist(REGF_FILE* file, uint32 offset, 
447                                    uint32 num_keys, uint32 max_size, 
448                                    bool strict)
449{
450  REGF_HASH_LIST* ret_val;
451  uint32 i, cell_length, length;
452  uint8* hashes;
453  uint8 buf[REGFI_HASH_LIST_MIN_LENGTH];
454  bool unalloc;
455
456  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_HASH_LIST_MIN_LENGTH, 
457                       &cell_length, &unalloc))
458    return NULL;
459
460  ret_val = (REGF_HASH_LIST*)zalloc(sizeof(REGF_HASH_LIST));
461  if(ret_val == NULL)
462    return NULL;
463
464  ret_val->offset = offset;
465  ret_val->cell_size = cell_length;
466
467  if((buf[0] != 'l' || buf[1] != 'f') && (buf[0] != 'l' || buf[1] != 'h')
468     && (buf[0] != 'r' || buf[1] != 'i'))
469  {
470    /*printf("DEBUG: lf->header=%c%c\n", buf[0], buf[1]);*/
471    free(ret_val);
472    return NULL;
473  }
474
475  if(buf[0] == 'r' && buf[1] == 'i')
476  {
477    fprintf(stderr, "WARNING: ignoring encountered \"ri\" record.\n");
478    free(ret_val);
479    return NULL;
480  }
481
482  ret_val->magic[0] = buf[0];
483  ret_val->magic[1] = buf[1];
484
485  ret_val->num_keys = SVAL(buf, 0x2);
486  if(num_keys != ret_val->num_keys)
487  {
488    if(strict)
489    {
490      free(ret_val);
491      return NULL;
492    }
493    /* TODO: Not sure which should be authoritative, the number from the
494     *       NK record, or the number in the hash list.  Go with the larger
495     *       of the two to ensure all keys are found.  Note the length checks
496     *       on the cell later ensure that there won't be any critical errors.
497     */
498    if(num_keys < ret_val->num_keys)
499      num_keys = ret_val->num_keys;
500    else
501      ret_val->num_keys = num_keys;
502  }
503
504  if(cell_length - REGFI_HASH_LIST_MIN_LENGTH - sizeof(uint32) 
505     < ret_val->num_keys*sizeof(REGF_HASH_LIST_ELEM))
506    return NULL;
507
508  length = sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys;
509  ret_val->hashes = (REGF_HASH_LIST_ELEM*)zalloc(length);
510  if(ret_val->hashes == NULL)
511  {
512    free(ret_val);
513    return NULL;
514  }
515
516  hashes = (uint8*)zalloc(length);
517  if(hashes == NULL)
518  {
519    free(ret_val->hashes);
520    free(ret_val);
521    return NULL;
522  }
523
524  if(regfi_read(file->fd, hashes, &length) != 0
525     || length != sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys)
526  {
527    free(ret_val->hashes);
528    free(ret_val);
529    return NULL;
530  }
531
532  for (i=0; i < ret_val->num_keys; i++)
533  {
534    ret_val->hashes[i].nk_off = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM));
535    ret_val->hashes[i].hash = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM)+4);
536  }
537  free(hashes);
538
539  return ret_val;
540}
541
542
543
544/*******************************************************************
545 *******************************************************************/
546REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict)
547{
548  REGF_SK_REC* ret_val;
549  uint32 cell_length, length;
550  prs_struct ps;
551  uint8 sk_header[REGFI_SK_MIN_LENGTH];
552  bool unalloc = false;
553
554
555  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
556                       &cell_length, &unalloc))
557    return NULL;
558   
559  if(sk_header[0] != 's' || sk_header[1] != 'k')
560    return NULL;
561 
562  ret_val = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC));
563  if(ret_val == NULL)
564    return NULL;
565
566  ret_val->offset = offset;
567  ret_val->cell_size = cell_length;
568
569  if(ret_val->cell_size > max_size)
570    ret_val->cell_size = max_size & 0xFFFFFFF8;
571  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
572     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
573  {
574    free(ret_val);
575    return NULL;
576  }
577
578
579  ret_val->magic[0] = sk_header[0];
580  ret_val->magic[1] = sk_header[1];
581
582  ret_val->unknown_tag = SVAL(sk_header, 0x2);
583  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
584  ret_val->next_sk_off = IVAL(sk_header, 0x8);
585  ret_val->ref_count = IVAL(sk_header, 0xC);
586  ret_val->desc_size = IVAL(sk_header, 0x10);
587
588  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
589  {
590    free(ret_val);
591    return NULL;
592  }
593
594  /* TODO: need to get rid of this, but currently the security descriptor
595   * code depends on the ps structure.
596   */
597  if(!prs_init(&ps, ret_val->desc_size, NULL, UNMARSHALL))
598  {
599    free(ret_val);
600    return NULL;
601  }
602
603  length = ret_val->desc_size;
604  if(regfi_read(file->fd, (uint8*)ps.data_p, &length) != 0 
605     || length != ret_val->desc_size)
606  {
607    free(ret_val);
608    return NULL;
609  }
610
611  if (!sec_io_desc("sec_desc", &ret_val->sec_desc, &ps, 0))
612  {
613    free(ret_val);
614    return NULL;
615  }
616
617  free(ps.data_p);
618
619  return ret_val;
620}
621
622
623
624/******************************************************************************
625 TODO: not currently validating against max_size.
626 ******************************************************************************/
627REGF_VK_REC** regfi_load_valuelist(REGF_FILE* file, uint32 offset, 
628                                   uint32 num_values, uint32 max_size, 
629                                   bool strict)
630{
631  REGF_VK_REC** ret_val;
632  REGF_HBIN* sub_hbin;
633  uint8* buf;
634  uint32 i, cell_length, vk_raw_offset, vk_offset, vk_max_length, buf_len;
635  bool unalloc;
636
637  buf_len = sizeof(uint8) * 4 * num_values;
638  buf = (uint8*)zalloc(buf_len);
639  if(buf == NULL)
640    return NULL; 
641
642  if(!regfi_parse_cell(file->fd, offset, buf, buf_len, &cell_length, &unalloc))
643  {
644    free(buf);
645    return NULL;
646  }
647
648  ret_val = (REGF_VK_REC**)zalloc(sizeof(REGF_VK_REC*) * num_values);
649  if(ret_val == NULL)
650  {
651    free(buf);
652    return NULL;
653  }
654 
655  for (i=0; i < num_values; i++) 
656  {
657    vk_raw_offset = IVAL(buf, i*4);
658   
659    sub_hbin = regfi_lookup_hbin(file, vk_raw_offset);
660    if (!sub_hbin)
661    {
662      free(buf);
663      free(ret_val);
664      return NULL;
665    }
666   
667    vk_offset =  vk_raw_offset + REGF_BLOCKSIZE;
668    vk_max_length = sub_hbin->block_size - vk_offset + sizeof(uint32);
669    ret_val[i] = regfi_parse_vk(file, vk_offset, vk_max_length, true);
670    if(ret_val[i] == NULL)
671    {
672      free(buf);
673      free(ret_val);
674      return NULL;     
675    }
676  }
677
678  free(buf);
679  return ret_val;
680}
681
682
683
684/*******************************************************************
685 * TODO: Need to add full key caching using a
686 *       custom cache structure.
687 *******************************************************************/
688REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
689{
690  REGF_HBIN* hbin;
691  REGF_HBIN* sub_hbin;
692  REGF_NK_REC* nk;
693  uint32 max_length, off;
694
695  hbin = regfi_lookup_hbin(file, offset-REGF_BLOCKSIZE);
696  if (hbin == NULL) 
697    return NULL;
698
699  /* get the initial nk record */
700  max_length = hbin->block_size + hbin->file_off - offset;
701  if ((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
702    return NULL;
703
704  /* fill in values */
705  if(nk->num_values && (nk->values_off!=REGF_OFFSET_NONE)) 
706  {
707    sub_hbin = hbin;
708    if(!regfi_offset_in_hbin(hbin, nk->values_off)) 
709      sub_hbin = regfi_lookup_hbin(file, nk->values_off);
710   
711    if(sub_hbin == NULL)
712    {
713      if(strict)
714      {
715        free(nk);
716        return NULL;
717      }
718      else
719        nk->values = NULL;
720    }
721    else
722    {
723      off = nk->values_off + REGF_BLOCKSIZE;
724      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
725      nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length, 
726                                        true);
727      if(strict && nk->values == NULL)
728      {
729        free(nk);
730        return NULL;
731      }
732    }
733  }
734
735  /* now get subkeys */
736  if(nk->num_subkeys && (nk->subkeys_off != REGF_OFFSET_NONE)) 
737  {
738    sub_hbin = hbin;
739    if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
740      sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
741
742    if (sub_hbin == NULL) 
743    {
744      if(strict)
745      {
746        free(nk);
747        /* TODO: need convenient way to free nk->values deeply in all cases. */
748        return NULL;
749      }
750      else
751        nk->subkeys = NULL;
752    }
753    else
754    {
755      off = nk->subkeys_off + REGF_BLOCKSIZE;
756      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
757      nk->subkeys = regfi_load_hashlist(file, off, nk->num_subkeys, 
758                                        max_length, true);
759      if(nk->subkeys == NULL)
760      {
761        /* TODO: temporary hack to get around 'ri' records */
762        nk->num_subkeys = 0;
763      }
764    }
765  }
766
767  return nk;
768}
769
770
771/******************************************************************************
772
773 ******************************************************************************/
774static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
775                               uint32* root_offset)
776{
777  uint8 tmp[4];
778  int32 record_size;
779  uint32 length, hbin_offset = 0;
780  REGF_NK_REC* nk = NULL;
781  bool found = false;
782
783  for(record_size=0; !found && (hbin_offset < hbin_size); )
784  {
785    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
786      return false;
787   
788    length = 4;
789    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
790      return false;
791    record_size = IVALS(tmp, 0);
792
793    if(record_size < 0)
794    {
795      record_size = record_size*(-1);
796      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
797      if(nk != NULL)
798      {
799        if(nk->key_type == NK_TYPE_ROOTKEY)
800        {
801          found = true;
802          *root_offset = nk->offset;
803        }
804        free(nk);
805      }
806    }
807
808    hbin_offset += record_size;
809  }
810
811  return found;
812}
813
814
815/*******************************************************************
816 * Open the registry file and then read in the REGF block to get the
817 * first hbin offset.
818 *******************************************************************/
819REGF_FILE* regfi_open(const char* filename, uint32 flags)
820{
821  REGF_FILE* rb;
822  REGF_HBIN* hbin = NULL;
823  uint32 hbin_off;
824  int fd;
825  bool rla, save_unalloc = false;
826
827  if(flags & REGFI_FLAG_SAVE_UNALLOC)
828    save_unalloc = true;
829
830  /* open an existing file */
831  if ((fd = open(filename, O_RDONLY)) == -1) 
832  {
833    /* DEBUG(0,("regfi_open: failure to open %s (%s)\n", filename, strerror(errno)));*/
834    return NULL;
835  }
836 
837  /* read in an existing file */
838  if ((rb = regfi_parse_regf(fd, true)) == NULL) 
839  {
840    /* DEBUG(0,("regfi_open: Failed to read initial REGF block\n"));*/
841    close(fd);
842    return NULL;
843  }
844 
845  rb->hbins = range_list_new();
846  rb->unalloc_cells = range_list_new();
847  if((rb->hbins == NULL) || (rb->unalloc_cells == NULL))
848  {
849    range_list_free(rb->hbins);
850    range_list_free(rb->unalloc_cells);
851    close(fd);
852    free(rb);
853    return NULL;
854  }
855 
856  rla = true;
857  hbin_off = REGF_BLOCKSIZE;
858  hbin = regfi_parse_hbin(rb, hbin_off, true, save_unalloc);
859  while(hbin && rla)
860  {
861    hbin_off = hbin->file_off + hbin->block_size;
862    rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
863    hbin = regfi_parse_hbin(rb, hbin_off, true, save_unalloc);
864  }
865
866  /* success */
867  return rb;
868}
869
870
871/*******************************************************************
872 *******************************************************************/
873int regfi_close( REGF_FILE *file )
874{
875  int fd;
876  uint32 i;
877
878  /* nothing to do if there is no open file */
879  if ((file == NULL) || (file->fd == -1))
880    return 0;
881
882  fd = file->fd;
883  file->fd = -1;
884  for(i=0; i < range_list_size(file->hbins); i++)
885    free(range_list_get(file->hbins, i)->data);
886  range_list_free(file->hbins);
887
888  for(i=0; i < range_list_size(file->unalloc_cells); i++)
889    free(range_list_get(file->unalloc_cells, i)->data);
890  range_list_free(file->unalloc_cells);
891
892  free(file);
893
894  return close(fd);
895}
896
897
898/******************************************************************************
899 * There should be only *one* root key in the registry file based
900 * on my experience.  --jerry
901 *****************************************************************************/
902REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
903{
904  REGF_NK_REC* nk = NULL;
905  REGF_HBIN*   hbin;
906  uint32       root_offset, i, num_hbins;
907 
908  if(!file)
909    return NULL;
910
911  /* Scan through the file one HBIN block at a time looking
912     for an NK record with a type == 0x002c.
913     Normally this is the first nk record in the first hbin
914     block (but I'm not assuming that for now) */
915
916  num_hbins = range_list_size(file->hbins);
917  for(i=0; i < num_hbins; i++)
918  {
919    hbin = (REGF_HBIN*)range_list_get(file->hbins, i)->data;
920    if(regfi_find_root_nk(file, hbin->file_off+HBIN_HEADER_REC_SIZE, 
921                          hbin->block_size-HBIN_HEADER_REC_SIZE, &root_offset))
922    {
923      nk = regfi_load_key(file, root_offset, true);
924      break;
925    }
926  }
927
928  return nk;
929}
930
931
932/******************************************************************************
933 *****************************************************************************/
934void regfi_key_free(REGF_NK_REC* nk)
935{
936  uint32 i;
937 
938  if((nk->values != NULL) && (nk->values_off!=REGF_OFFSET_NONE))
939  {
940    for(i=0; i < nk->num_values; i++)
941    {
942      if(nk->values[i]->valuename != NULL)
943        free(nk->values[i]->valuename);
944      if(nk->values[i]->data != NULL)
945        free(nk->values[i]->data);
946      free(nk->values[i]);
947    }
948    free(nk->values);
949  }
950
951  if(nk->keyname != NULL)
952    free(nk->keyname);
953  if(nk->classname != NULL)
954    free(nk->classname);
955
956  /* XXX: not freeing hbin because these are cached.  This needs to be reviewed. */
957  /* XXX: not freeing sec_desc because these are cached.  This needs to be reviewed. */
958  free(nk);
959}
960
961
962/******************************************************************************
963 *****************************************************************************/
964REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh)
965{
966  REGF_NK_REC* root;
967  REGFI_ITERATOR* ret_val = (REGFI_ITERATOR*)malloc(sizeof(REGFI_ITERATOR));
968  if(ret_val == NULL)
969    return NULL;
970
971  root = regfi_rootkey(fh);
972  if(root == NULL)
973  {
974    free(ret_val);
975    return NULL;
976  }
977
978  ret_val->key_positions = void_stack_new(REGF_MAX_DEPTH);
979  if(ret_val->key_positions == NULL)
980  {
981    free(ret_val);
982    free(root);
983    return NULL;
984  }
985
986  /* TODO: come up with a better secret. */
987  ret_val->sk_recs = lru_cache_create(127, 0xDEADBEEF, true);
988
989  ret_val->f = fh;
990  ret_val->cur_key = root;
991  ret_val->cur_subkey = 0;
992  ret_val->cur_value = 0;
993
994  return ret_val;
995}
996
997
998/******************************************************************************
999 *****************************************************************************/
1000void regfi_iterator_free(REGFI_ITERATOR* i)
1001{
1002  REGFI_ITER_POSITION* cur;
1003
1004  if(i->cur_key != NULL)
1005    regfi_key_free(i->cur_key);
1006
1007  while((cur = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions)) != NULL)
1008  {
1009    regfi_key_free(cur->nk);
1010    free(cur);
1011  }
1012 
1013  lru_cache_destroy(i->sk_recs);
1014
1015  free(i);
1016}
1017
1018
1019
1020/******************************************************************************
1021 *****************************************************************************/
1022/* XXX: some way of indicating reason for failure should be added. */
1023bool regfi_iterator_down(REGFI_ITERATOR* i)
1024{
1025  REGF_NK_REC* subkey;
1026  REGFI_ITER_POSITION* pos;
1027
1028  pos = (REGFI_ITER_POSITION*)malloc(sizeof(REGFI_ITER_POSITION));
1029  if(pos == NULL)
1030    return false;
1031
1032  subkey = (REGF_NK_REC*)regfi_iterator_cur_subkey(i);
1033  if(subkey == NULL)
1034  {
1035    free(pos);
1036    return false;
1037  }
1038
1039  pos->nk = i->cur_key;
1040  pos->cur_subkey = i->cur_subkey;
1041  if(!void_stack_push(i->key_positions, pos))
1042  {
1043    free(pos);
1044    regfi_key_free(subkey);
1045    return false;
1046  }
1047
1048  i->cur_key = subkey;
1049  i->cur_subkey = 0;
1050  i->cur_value = 0;
1051
1052  return true;
1053}
1054
1055
1056/******************************************************************************
1057 *****************************************************************************/
1058bool regfi_iterator_up(REGFI_ITERATOR* i)
1059{
1060  REGFI_ITER_POSITION* pos;
1061
1062  pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions);
1063  if(pos == NULL)
1064    return false;
1065
1066  regfi_key_free(i->cur_key);
1067  i->cur_key = pos->nk;
1068  i->cur_subkey = pos->cur_subkey;
1069  i->cur_value = 0;
1070  free(pos);
1071
1072  return true;
1073}
1074
1075
1076/******************************************************************************
1077 *****************************************************************************/
1078bool regfi_iterator_to_root(REGFI_ITERATOR* i)
1079{
1080  while(regfi_iterator_up(i))
1081    continue;
1082
1083  return true;
1084}
1085
1086
1087/******************************************************************************
1088 *****************************************************************************/
1089bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
1090{
1091  REGF_NK_REC* subkey;
1092  bool found = false;
1093  uint32 old_subkey = i->cur_subkey;
1094 
1095  if(subkey_name == NULL)
1096    return false;
1097
1098  /* XXX: this alloc/free of each sub key might be a bit excessive */
1099  subkey = (REGF_NK_REC*)regfi_iterator_first_subkey(i);
1100  while((subkey != NULL) && (found == false))
1101  {
1102    if(subkey->keyname != NULL 
1103       && strcasecmp(subkey->keyname, subkey_name) == 0)
1104      found = true;
1105    else
1106    {
1107      regfi_key_free(subkey);
1108      subkey = (REGF_NK_REC*)regfi_iterator_next_subkey(i);
1109    }
1110  }
1111
1112  if(found == false)
1113  {
1114    i->cur_subkey = old_subkey;
1115    return false;
1116  }
1117
1118  regfi_key_free(subkey);
1119  return true;
1120}
1121
1122
1123/******************************************************************************
1124 *****************************************************************************/
1125bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
1126{
1127  uint32 x;
1128  if(path == NULL)
1129    return false;
1130
1131  for(x=0; 
1132      ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x])
1133       && regfi_iterator_down(i));
1134      x++)
1135  { continue; }
1136
1137  if(path[x] == NULL)
1138    return true;
1139 
1140  /* XXX: is this the right number of times? */
1141  for(; x > 0; x--)
1142    regfi_iterator_up(i);
1143 
1144  return false;
1145}
1146
1147
1148/******************************************************************************
1149 *****************************************************************************/
1150const REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
1151{
1152  return i->cur_key;
1153}
1154
1155
1156/******************************************************************************
1157 *****************************************************************************/
1158const REGF_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
1159{
1160  REGF_SK_REC* ret_val;
1161  REGF_HBIN* hbin;
1162  uint32 max_length, off;
1163
1164  if(i->cur_key == NULL)
1165    return NULL;
1166 
1167  /* First look if we have already parsed it */
1168  if((i->cur_key->sk_off!=REGF_OFFSET_NONE)
1169     && !(ret_val =(REGF_SK_REC*)lru_cache_find(i->sk_recs, 
1170                                                &i->cur_key->sk_off, 4)))
1171  {
1172    hbin = regfi_lookup_hbin(i->f, i->cur_key->sk_off);
1173
1174    if(hbin == NULL)
1175      return NULL;
1176
1177    off = i->cur_key->sk_off + REGF_BLOCKSIZE;
1178    max_length = hbin->block_size + hbin->file_off - off;
1179    ret_val = regfi_parse_sk(i->f, off, max_length, true);
1180    if(ret_val == NULL)
1181      return NULL;
1182
1183    ret_val->sk_off = i->cur_key->sk_off;
1184    lru_cache_update(i->sk_recs, &i->cur_key->sk_off, 4, ret_val);
1185  }
1186
1187  return ret_val;
1188}
1189
1190
1191
1192/******************************************************************************
1193 *****************************************************************************/
1194const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
1195{
1196  i->cur_subkey = 0;
1197  return regfi_iterator_cur_subkey(i);
1198}
1199
1200
1201/******************************************************************************
1202 *****************************************************************************/
1203const REGF_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
1204{
1205  uint32 nk_offset;
1206
1207  /* see if there is anything left to report */
1208  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGF_OFFSET_NONE)
1209      || (i->cur_subkey >= i->cur_key->num_subkeys))
1210    return NULL;
1211
1212  nk_offset = i->cur_key->subkeys->hashes[i->cur_subkey].nk_off;
1213 
1214  return regfi_load_key(i->f, nk_offset+REGF_BLOCKSIZE, true);
1215}
1216
1217
1218/******************************************************************************
1219 *****************************************************************************/
1220/* XXX: some way of indicating reason for failure should be added. */
1221const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
1222{
1223  const REGF_NK_REC* subkey;
1224
1225  i->cur_subkey++;
1226  subkey = regfi_iterator_cur_subkey(i);
1227
1228  if(subkey == NULL)
1229    i->cur_subkey--;
1230
1231  return subkey;
1232}
1233
1234
1235/******************************************************************************
1236 *****************************************************************************/
1237bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
1238{
1239  const REGF_VK_REC* cur;
1240  bool found = false;
1241
1242  /* XXX: cur->valuename can be NULL in the registry. 
1243   *      Should we allow for a way to search for that?
1244   */
1245  if(value_name == NULL)
1246    return false;
1247
1248  cur = regfi_iterator_first_value(i);
1249  while((cur != NULL) && (found == false))
1250  {
1251    if((cur->valuename != NULL)
1252       && (strcasecmp(cur->valuename, value_name) == 0))
1253      found = true;
1254    else
1255      cur = regfi_iterator_next_value(i);
1256  }
1257
1258  return found;
1259}
1260
1261
1262/******************************************************************************
1263 *****************************************************************************/
1264const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
1265{
1266  i->cur_value = 0;
1267  return regfi_iterator_cur_value(i);
1268}
1269
1270
1271/******************************************************************************
1272 *****************************************************************************/
1273const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
1274{
1275  REGF_VK_REC* ret_val = NULL;
1276  if(i->cur_value < i->cur_key->num_values)
1277    ret_val = i->cur_key->values[i->cur_value];
1278
1279  return ret_val;
1280}
1281
1282
1283/******************************************************************************
1284 *****************************************************************************/
1285const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
1286{
1287  const REGF_VK_REC* ret_val;
1288
1289  i->cur_value++;
1290  ret_val = regfi_iterator_cur_value(i);
1291  if(ret_val == NULL)
1292    i->cur_value--;
1293
1294  return ret_val;
1295}
1296
1297
1298
1299/*******************************************************************
1300 * Computes the checksum of the registry file header.
1301 * buffer must be at least the size of an regf header (4096 bytes).
1302 *******************************************************************/
1303static uint32 regfi_compute_header_checksum(uint8* buffer)
1304{
1305  uint32 checksum, x;
1306  int i;
1307
1308  /* XOR of all bytes 0x0000 - 0x01FB */
1309
1310  checksum = x = 0;
1311 
1312  for ( i=0; i<0x01FB; i+=4 ) {
1313    x = IVAL(buffer, i );
1314    checksum ^= x;
1315  }
1316 
1317  return checksum;
1318}
1319
1320
1321/*******************************************************************
1322 * TODO: add way to return more detailed error information.
1323 *******************************************************************/
1324REGF_FILE* regfi_parse_regf(int fd, bool strict)
1325{
1326  uint8 file_header[REGF_BLOCKSIZE];
1327  uint32 length;
1328  uint32 file_length;
1329  struct stat sbuf;
1330  REGF_FILE* ret_val;
1331
1332  /* Determine file length.  Must be at least big enough
1333   * for the header and one hbin.
1334   */
1335  if (fstat(fd, &sbuf) == -1)
1336    return NULL;
1337  file_length = sbuf.st_size;
1338  if(file_length < REGF_BLOCKSIZE+REGF_ALLOC_BLOCK)
1339    return NULL;
1340
1341  ret_val = (REGF_FILE*)zalloc(sizeof(REGF_FILE));
1342  if(ret_val == NULL)
1343    return NULL;
1344
1345  ret_val->fd = fd;
1346  ret_val->file_length = file_length;
1347
1348  length = REGF_BLOCKSIZE;
1349  if((regfi_read(fd, file_header, &length)) != 0 
1350     || length != REGF_BLOCKSIZE)
1351  {
1352    free(ret_val);
1353    return NULL;
1354  }
1355
1356  ret_val->checksum = IVAL(file_header, 0x1FC);
1357  ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
1358  if (strict && (ret_val->checksum != ret_val->computed_checksum))
1359  {
1360    free(ret_val);
1361    return NULL;
1362  }
1363
1364  memcpy(ret_val->magic, file_header, 4);
1365  if(strict && (memcmp(ret_val->magic, "regf", 4) != 0))
1366  {
1367    free(ret_val);
1368    return NULL;
1369  }
1370 
1371  ret_val->unknown1 = IVAL(file_header, 0x4);
1372  ret_val->unknown2 = IVAL(file_header, 0x8);
1373
1374  ret_val->mtime.low = IVAL(file_header, 0xC);
1375  ret_val->mtime.high = IVAL(file_header, 0x10);
1376
1377  ret_val->unknown3 = IVAL(file_header, 0x14);
1378  ret_val->unknown4 = IVAL(file_header, 0x18);
1379  ret_val->unknown5 = IVAL(file_header, 0x1C);
1380  ret_val->unknown6 = IVAL(file_header, 0x20);
1381 
1382  ret_val->data_offset = IVAL(file_header, 0x24);
1383  ret_val->last_block = IVAL(file_header, 0x28);
1384
1385  ret_val->unknown7 = IVAL(file_header, 0x2C);
1386
1387  return ret_val;
1388}
1389
1390
1391
1392/*******************************************************************
1393 * Given real file offset, read and parse the hbin at that location
1394 * along with it's associated cells.  If save_unalloc is true, a list
1395 * of unallocated cell offsets will be stored in TODO.
1396 *******************************************************************/
1397/* TODO: Need a way to return types of errors.  Also need to free
1398 *       the hbin/ps when an error occurs.
1399 */
1400REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, 
1401                            bool strict, bool save_unalloc)
1402{
1403  REGF_HBIN *hbin;
1404  uint8 hbin_header[HBIN_HEADER_REC_SIZE];
1405  uint32 length, curr_off;
1406  uint32 cell_len;
1407  bool is_unalloc;
1408 
1409  if(offset >= file->file_length)
1410    return NULL;
1411
1412  if(lseek(file->fd, offset, SEEK_SET) == -1)
1413    return NULL;
1414
1415  length = HBIN_HEADER_REC_SIZE;
1416  if((regfi_read(file->fd, hbin_header, &length) != 0) 
1417     || length != HBIN_HEADER_REC_SIZE)
1418    return NULL;
1419
1420
1421  if(lseek(file->fd, offset, SEEK_SET) == -1)
1422    return NULL;
1423
1424  if(!(hbin = (REGF_HBIN*)zalloc(sizeof(REGF_HBIN)))) 
1425    return NULL;
1426  hbin->file_off = offset;
1427
1428  memcpy(hbin->magic, hbin_header, 4);
1429  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
1430  {
1431    free(hbin);
1432    return NULL;
1433  }
1434
1435  hbin->first_hbin_off = IVAL(hbin_header, 0x4);
1436  hbin->block_size = IVAL(hbin_header, 0x8);
1437  /* this should be the same thing as hbin->block_size but just in case */
1438  hbin->next_block = IVAL(hbin_header, 0x1C);
1439
1440
1441  /* Ensure the block size is a multiple of 0x1000 and doesn't run off
1442   * the end of the file.
1443   */
1444  /* TODO: This may need to be relaxed for dealing with
1445   *       partial or corrupt files. */
1446  if((offset + hbin->block_size > file->file_length)
1447     || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
1448  {
1449    free(hbin);
1450    return NULL;
1451  }
1452
1453  if(save_unalloc)
1454  {
1455    curr_off = HBIN_HEADER_REC_SIZE;
1456    while(curr_off < hbin->block_size)
1457    {
1458      if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
1459                           &cell_len, &is_unalloc))
1460        break;
1461
1462      if((cell_len == 0) || ((cell_len & 0xFFFFFFFC) != cell_len))
1463        /* TODO: should report an error here. */
1464        break;
1465
1466      /* for some reason the record_size of the last record in
1467         an hbin block can extend past the end of the block
1468         even though the record fits within the remaining
1469         space....aaarrrgggghhhhhh */ 
1470      if(curr_off + cell_len >= hbin->block_size)
1471        cell_len = hbin->block_size - curr_off;
1472
1473      if(is_unalloc)
1474        range_list_add(file->unalloc_cells, hbin->file_off+curr_off, 
1475          cell_len, NULL);
1476
1477      curr_off = curr_off+cell_len;
1478    }
1479  }
1480
1481  return hbin;
1482}
1483
1484
1485
1486REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset, 
1487                            uint32 max_size, bool strict)
1488{
1489  uint8 nk_header[REGFI_NK_MIN_LENGTH];
1490  REGF_NK_REC* ret_val;
1491  uint32 length;
1492  uint32 cell_length;
1493  bool unalloc = false;
1494
1495  if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
1496                       &cell_length, &unalloc))
1497     return NULL;
1498 
1499  /* A bit of validation before bothering to allocate memory */
1500  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
1501  {
1502    /* TODO: deal with subkey-lists that reference other subkey-lists. */
1503printf("DEBUG: magic check failed! \"%c%c\"\n", nk_header[0x0], nk_header[0x1]);
1504    return NULL;
1505  }
1506
1507  ret_val = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC));
1508  if(ret_val == NULL)
1509    return NULL;
1510
1511  ret_val->offset = offset;
1512  ret_val->cell_size = cell_length;
1513
1514  if(ret_val->cell_size > max_size)
1515    ret_val->cell_size = max_size & 0xFFFFFFF8;
1516  if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
1517     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
1518  {
1519    free(ret_val);
1520    return NULL;
1521  }
1522
1523  ret_val->magic[0] = nk_header[0x0];
1524  ret_val->magic[1] = nk_header[0x1];
1525  ret_val->key_type = SVAL(nk_header, 0x2);
1526  if((ret_val->key_type != NK_TYPE_NORMALKEY)
1527     && (ret_val->key_type != NK_TYPE_ROOTKEY) 
1528     && (ret_val->key_type != NK_TYPE_LINKKEY)
1529     && (ret_val->key_type != NK_TYPE_UNKNOWN1))
1530  {
1531    free(ret_val);
1532    return NULL;
1533  }
1534
1535  ret_val->mtime.low = IVAL(nk_header, 0x4);
1536  ret_val->mtime.high = IVAL(nk_header, 0x8);
1537 
1538  ret_val->unknown1 = IVAL(nk_header, 0xC);
1539  ret_val->parent_off = IVAL(nk_header, 0x10);
1540  ret_val->num_subkeys = IVAL(nk_header, 0x14);
1541  ret_val->unknown2 = IVAL(nk_header, 0x18);
1542  ret_val->subkeys_off = IVAL(nk_header, 0x1C);
1543  ret_val->unknown3 = IVAL(nk_header, 0x20);
1544  ret_val->num_values = IVAL(nk_header, 0x24);
1545  ret_val->values_off = IVAL(nk_header, 0x28);
1546  ret_val->sk_off = IVAL(nk_header, 0x2C);
1547  /* TODO: currently we do nothing with class names.  Need to investigate. */
1548  ret_val->classname_off = IVAL(nk_header, 0x30);
1549
1550  ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
1551  ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38);
1552  ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C);
1553  ret_val->max_bytes_value = IVAL(nk_header, 0x40);
1554  ret_val->unk_index = IVAL(nk_header, 0x44);
1555
1556  ret_val->name_length = SVAL(nk_header, 0x48);
1557  ret_val->classname_length = SVAL(nk_header, 0x4A);
1558
1559  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
1560  {
1561    if(strict)
1562    {
1563      free(ret_val);
1564      return NULL;
1565    }
1566    else
1567      ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
1568  }
1569  else if (unalloc)
1570  { /* Truncate cell_size if it's much larger than the apparent total record length. */
1571    /* Round up to the next multiple of 8 */
1572    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
1573    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
1574      length+=8;
1575
1576    /* If cell_size is still greater, truncate. */
1577    if(length < ret_val->cell_size)
1578      ret_val->cell_size = length;
1579  }
1580
1581  ret_val->keyname = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
1582  if(ret_val->keyname == NULL)
1583  {
1584    free(ret_val);
1585    return NULL;
1586  }
1587
1588  /* Don't need to seek, should be at the right offset */
1589  length = ret_val->name_length;
1590  if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
1591     || length != ret_val->name_length)
1592  {
1593    free(ret_val->keyname);
1594    free(ret_val);
1595    return NULL;
1596  }
1597  ret_val->keyname[ret_val->name_length] = '\0';
1598
1599  return ret_val;
1600}
1601
1602
1603
1604/*******************************************************************
1605 *******************************************************************/
1606REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset, 
1607                            uint32 max_size, bool strict)
1608{
1609  REGF_VK_REC* ret_val;
1610  uint8 vk_header[REGFI_VK_MIN_LENGTH];
1611  uint32 raw_data_size, length, cell_length;
1612  bool unalloc = false;
1613
1614  if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
1615                       &cell_length, &unalloc))
1616    return NULL;
1617   
1618  ret_val = (REGF_VK_REC*)zalloc(sizeof(REGF_VK_REC));
1619  if(ret_val == NULL)
1620    return NULL;
1621
1622  ret_val->offset = offset;
1623  ret_val->cell_size = cell_length;
1624
1625  if(ret_val->cell_size > max_size)
1626    ret_val->cell_size = max_size & 0xFFFFFFF8;
1627  if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
1628     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
1629  {
1630    free(ret_val);
1631    return NULL;
1632  }
1633
1634  ret_val->magic[0] = vk_header[0x0];
1635  ret_val->magic[1] = vk_header[0x1];
1636  if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
1637  {
1638    free(ret_val);
1639    return NULL;
1640  }
1641
1642  ret_val->name_length = SVAL(vk_header, 0x2);
1643  raw_data_size = IVAL(vk_header, 0x4);
1644  ret_val->data_size = raw_data_size & ~VK_DATA_IN_OFFSET;
1645  ret_val->data_off = IVAL(vk_header, 0x8);
1646  ret_val->type = IVAL(vk_header, 0xC);
1647  ret_val->flag = SVAL(vk_header, 0x10);
1648  ret_val->unknown1 = SVAL(vk_header, 0x12);
1649
1650  if(ret_val->flag & VK_FLAG_NAME_PRESENT)
1651  {
1652    if(ret_val->name_length + REGFI_VK_MIN_LENGTH > ret_val->cell_size)
1653    {
1654      if(strict)
1655      {
1656        free(ret_val);
1657        return NULL;
1658      }
1659      else
1660        ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH;
1661    }
1662
1663    /* Round up to the next multiple of 8 */
1664    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
1665    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
1666      length+=8;
1667
1668    ret_val->valuename = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
1669    if(ret_val->valuename == NULL)
1670    {
1671      free(ret_val);
1672      return NULL;
1673    }
1674   
1675    /* Don't need to seek, should be at the right offset */
1676    length = ret_val->name_length;
1677    if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
1678       || length != ret_val->name_length)
1679    {
1680      free(ret_val->valuename);
1681      free(ret_val);
1682      return NULL;
1683    }
1684    ret_val->valuename[ret_val->name_length] = '\0';
1685  }
1686  else
1687    length = REGFI_VK_MIN_LENGTH;
1688
1689  if(unalloc)
1690  {
1691    /* If cell_size is still greater, truncate. */
1692    if(length < ret_val->cell_size)
1693      ret_val->cell_size = length;
1694  }
1695
1696  if(ret_val->data_size == 0)
1697    ret_val->data = NULL;
1698  else
1699  {
1700    ret_val->data = regfi_parse_data(file, ret_val->data_off+REGF_BLOCKSIZE,
1701                                     raw_data_size, strict);
1702    if(strict && (ret_val->data == NULL))
1703    {
1704      free(ret_val->valuename);
1705      free(ret_val);
1706      return NULL;
1707    }
1708  }
1709
1710  return ret_val;
1711}
1712
1713
1714uint8* regfi_parse_data(REGF_FILE* file, uint32 offset, uint32 length, bool strict)
1715{
1716  uint8* ret_val;
1717  uint32 read_length, cell_length;
1718  uint8 i;
1719  bool unalloc;
1720
1721  /* The data is stored in the offset if the size <= 4 */
1722  if (length & VK_DATA_IN_OFFSET)
1723  {
1724    length = length & ~VK_DATA_IN_OFFSET;
1725    if(length > 4)
1726      return NULL;
1727
1728    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
1729      return NULL;
1730
1731    offset = offset - REGF_BLOCKSIZE;
1732    for(i = 0; i < length; i++)
1733      ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
1734  }
1735  else
1736  {
1737    if(!regfi_parse_cell(file->fd, offset, NULL, 0,
1738                         &cell_length, &unalloc))
1739      return NULL;
1740   
1741    if(cell_length < 8 || ((cell_length & 0xFFFFFFF8) != cell_length))
1742      return NULL;
1743
1744    if(cell_length - 4 < length)
1745    {
1746      if(strict)
1747        return NULL;
1748      else
1749        length = cell_length - 4;
1750    }
1751
1752    /* TODO: There is currently no check to ensure the data
1753     *       cell doesn't cross HBIN boundary.
1754     */
1755
1756    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
1757      return NULL;
1758
1759    read_length = length;
1760    if((regfi_read(file->fd, ret_val, &read_length) != 0) 
1761       || read_length != length)
1762    {
1763      free(ret_val);
1764      return NULL;
1765    }
1766  }
1767
1768  return ret_val;
1769}
Note: See TracBrowser for help on using the repository browser.