source: trunk/lib/regfi.c @ 102

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

simplified root key search routines

rewrote sk record parsing

fixed nasty bug in parsing data-in-offset

  • Property svn:keywords set to Id
File size: 45.9 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 102 2008-04-03 02:30:26Z 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(hdr_len > 0)
390  {
391    length = hdr_len;
392    if((regfi_read(fd, hdr, &length) != 0) || length != hdr_len)
393      return false;
394  }
395
396  if(raw_length < 0)
397  {
398    (*cell_length) = raw_length*(-1);
399    (*unalloc) = false;
400  }
401  else
402  {
403    (*cell_length) = raw_length;
404    (*unalloc) = true;
405  }
406
407  return true;
408}
409
410
411/*******************************************************************
412 Input a random offset and receive the correpsonding HBIN
413 block for it
414*******************************************************************/
415static bool hbin_contains_offset( REGF_HBIN *hbin, uint32 offset )
416{
417  if ( !hbin )
418    return false;
419       
420  if ( (offset > hbin->first_hbin_off) && (offset < (hbin->first_hbin_off+hbin->block_size)) )
421    return true;
422               
423  return false;
424}
425
426
427/*******************************************************************
428 Input a random offset and receive the correpsonding HBIN
429 block for it
430*******************************************************************/
431static REGF_HBIN* lookup_hbin_block( REGF_FILE *file, uint32 offset )
432{
433  REGF_HBIN *hbin = NULL;
434  uint32 block_off;
435
436  /* start with the open list */
437
438  for ( hbin=file->block_list; hbin; hbin=hbin->next ) {
439    /* DEBUG(10,("lookup_hbin_block: address = 0x%x [0x%x]\n", hbin->file_off, (uint32)hbin ));*/
440    if ( hbin_contains_offset( hbin, offset ) )
441      return hbin;
442  }
443       
444  if ( !hbin ) {
445    /* start at the beginning */
446
447    block_off = REGF_BLOCKSIZE;
448    do {
449      /* cleanup before the next round */
450      if ( hbin )
451      {
452        if(hbin->ps.is_dynamic)
453          SAFE_FREE(hbin->ps.data_p);
454        hbin->ps.is_dynamic = false;
455        hbin->ps.buffer_size = 0;
456        hbin->ps.data_offset = 0;
457      }
458
459      hbin = regfi_parse_hbin(file, block_off, true, false);
460
461      if ( hbin ) 
462        block_off = hbin->file_off + hbin->block_size;
463
464    } while ( hbin && !hbin_contains_offset( hbin, offset ) );
465  }
466
467  if ( hbin )
468    /* XXX: this kind of caching needs to be re-evaluated */
469    DLIST_ADD( file->block_list, hbin );
470
471  return hbin;
472}
473
474
475/*******************************************************************
476 *******************************************************************/
477static bool prs_hash_rec( const char *desc, prs_struct *ps, int depth, REGF_HASH_REC *hash )
478{
479  depth++;
480
481  if ( !prs_uint32( "nk_off", ps, depth, &hash->nk_off ))
482    return false;
483  if ( !prs_uint8s("keycheck", ps, depth, hash->keycheck, sizeof( hash->keycheck )) )
484    return false;
485       
486  return true;
487}
488
489
490/*******************************************************************
491 *******************************************************************/
492static bool hbin_prs_lf_records(const char *desc, REGF_HBIN *hbin, 
493                                int depth, REGF_NK_REC *nk)
494{
495  int i;
496  REGF_LF_REC *lf = &nk->subkeys;
497  uint32 data_size, start_off, end_off;
498
499  depth++;
500
501  /* check if we have anything to do first */
502       
503  if ( nk->num_subkeys == 0 )
504    return true;
505
506  /* move to the LF record */
507
508  if ( !prs_set_offset( &hbin->ps, nk->subkeys_off + HBIN_MAGIC_SIZE - hbin->first_hbin_off ) )
509    return false;
510
511  /* backup and get the data_size */
512       
513  if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
514    return false;
515  start_off = hbin->ps.data_offset;
516  if ( !prs_uint32( "cell_size", &hbin->ps, depth, &lf->cell_size ))
517    return false;
518
519  if(!prs_uint8s("header", &hbin->ps, depth, 
520                 lf->header, sizeof(lf->header)))
521    return false;
522
523  /*fprintf(stdout, "DEBUG: lf->header=%c%c\n", lf->header[0], lf->header[1]);*/
524
525  if ( !prs_uint16( "num_keys", &hbin->ps, depth, &lf->num_keys))
526    return false;
527
528  if ( hbin->ps.io ) {
529    if ( !(lf->hashes = (REGF_HASH_REC*)zcalloc(sizeof(REGF_HASH_REC), lf->num_keys )) )
530      return false;
531  }
532
533  for ( i=0; i<lf->num_keys; i++ ) {
534    if ( !prs_hash_rec( "hash_rec", &hbin->ps, depth, &lf->hashes[i] ) )
535      return false;
536  }
537
538  end_off = hbin->ps.data_offset;
539
540  /* data_size must be divisible by 8 and large enough to hold the original record */
541
542  data_size = ((start_off - end_off) & 0xfffffff8 );
543  /*  if ( data_size > lf->cell_size )*/
544    /*DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, lf->cell_size));*/
545
546  return true;
547}
548
549
550
551/*******************************************************************
552 *******************************************************************/
553REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict)
554{
555  REGF_SK_REC* ret_val;
556  uint32 cell_length, length;
557  prs_struct ps;
558  uint8 sk_header[REGFI_SK_MIN_LENGTH];
559  bool unalloc = false;
560
561
562  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
563                       &cell_length, &unalloc))
564    return NULL;
565   
566  if(sk_header[0] != 's' || sk_header[1] != 'k')
567    return NULL;
568 
569  ret_val = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC));
570  if(ret_val == NULL)
571    return NULL;
572
573  ret_val->offset = offset;
574  ret_val->cell_size = cell_length;
575
576  if(ret_val->cell_size > max_size)
577    ret_val->cell_size = max_size & 0xFFFFFFF8;
578  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
579     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
580  {
581    free(ret_val);
582    return NULL;
583  }
584
585
586  ret_val->magic[0] = sk_header[0];
587  ret_val->magic[1] = sk_header[1];
588
589  ret_val->unknown_tag = SVAL(sk_header, 0x2);
590  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
591  ret_val->next_sk_off = IVAL(sk_header, 0x8);
592  ret_val->ref_count = IVAL(sk_header, 0xC);
593  ret_val->desc_size = IVAL(sk_header, 0x10);
594
595  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
596  {
597    free(ret_val);
598    return NULL;
599  }
600
601  /* TODO: need to get rid of this, but currently the security descriptor
602   * code depends on the ps structure.
603   */
604  if(!prs_init(&ps, ret_val->desc_size, NULL, UNMARSHALL))
605  {
606    free(ret_val);
607    return NULL;
608  }
609
610  length = ret_val->desc_size;
611  if(regfi_read(file->fd, (uint8*)ps.data_p, &length) != 0 
612     || length != ret_val->desc_size)
613  {
614    free(ret_val);
615    return NULL;
616  }
617
618  if (!sec_io_desc("sec_desc", &ret_val->sec_desc, &ps, 0))
619  {
620    free(ret_val);
621    return NULL;
622  }
623
624  free(ps.data_p);
625
626  return ret_val;
627}
628
629
630
631/*******************************************************************
632 read a VK record which is contained in the HBIN block stored
633 in the prs_struct *ps.
634*******************************************************************/
635static bool hbin_prs_vk_records(const char* desc, REGF_HBIN* hbin, 
636                                int depth, REGF_NK_REC* nk, REGF_FILE* file)
637{
638  int i;
639  uint32 record_size, vk_raw_offset, vk_offset, vk_max_length;
640  REGF_HBIN* sub_hbin;
641
642  depth++;
643 
644  /* check if we have anything to do first */
645  if(nk->num_values == 0)
646    return true;
647       
648  if(hbin->ps.io)
649  {
650    if (!(nk->values = (REGF_VK_REC**)zcalloc(sizeof(REGF_VK_REC*), 
651                                              nk->num_values )))
652      return false;
653  }
654 
655  /* convert the offset to something relative to this HBIN block */
656  if (!prs_set_offset(&hbin->ps, 
657                      nk->values_off
658                      + HBIN_MAGIC_SIZE
659                      - hbin->first_hbin_off
660                      - sizeof(uint32)))
661  { return false; }
662
663  if ( !hbin->ps.io ) 
664  { 
665    record_size = ( ( nk->num_values * sizeof(uint32) ) & 0xfffffff8 ) + 8;
666    record_size = (record_size - 1) ^ 0xFFFFFFFF;
667  }
668
669  if ( !prs_uint32( "record_size", &hbin->ps, depth, &record_size ) )
670    return false;
671       
672  for ( i=0; i<nk->num_values; i++ ) 
673  {
674    if ( !prs_uint32( "vk_off", &hbin->ps, depth, &vk_raw_offset) )
675      return false;
676   
677    if(hbin_contains_offset(hbin, vk_raw_offset))
678      sub_hbin = hbin;
679    else
680    {
681      sub_hbin = lookup_hbin_block( file, vk_raw_offset );
682      if (!sub_hbin)
683        return false;
684    }
685       
686    vk_offset =  vk_raw_offset + REGF_BLOCKSIZE;
687    vk_max_length = sub_hbin->block_size - vk_offset + sizeof(uint32);
688    if((nk->values[i] = regfi_parse_vk(file, vk_offset, vk_max_length, true))
689        == NULL)
690      return false;
691  }
692
693  return true;
694}
695
696
697/*******************************************************************
698 *******************************************************************/
699static REGF_SK_REC* find_sk_record_by_offset( REGF_FILE *file, uint32 offset )
700{
701  REGF_SK_REC *p_sk;
702 
703  for ( p_sk=file->sec_desc_list; p_sk; p_sk=p_sk->next ) {
704    if ( p_sk->sk_off == offset ) 
705      return p_sk;
706  }
707 
708  return NULL;
709}
710
711
712/*******************************************************************
713 *******************************************************************/
714static REGF_SK_REC* find_sk_record_by_sec_desc( REGF_FILE *file, SEC_DESC *sd )
715{
716  REGF_SK_REC *p;
717
718  for ( p=file->sec_desc_list; p; p=p->next ) {
719    if ( sec_desc_equal( p->sec_desc, sd ) )
720      return p;
721  }
722
723  /* failure */
724
725  return NULL;
726}
727
728
729/*******************************************************************
730 *******************************************************************/
731static REGF_NK_REC* hbin_prs_key(REGF_FILE *file, REGF_HBIN *hbin)
732{
733  REGF_HBIN* sub_hbin;
734  REGF_NK_REC* nk;
735  uint32 nk_cell_offset;
736  uint32 nk_max_length;
737  uint32 sk_max_length;
738  int depth = 0;
739
740  depth++;
741
742  /* get the initial nk record */
743  nk_cell_offset = hbin->file_off + hbin->ps.data_offset - sizeof(uint32);
744  nk_max_length = hbin->block_size - hbin->ps.data_offset + sizeof(uint32);
745  if ((nk = regfi_parse_nk(file, nk_cell_offset, nk_max_length, true)) == NULL)
746  {
747fprintf(stderr, "DEBUG: regfi_parse_nk returned NULL!\n");
748    return NULL;
749  }
750
751  /* fill in values */
752  if ( nk->num_values && (nk->values_off!=REGF_OFFSET_NONE) ) 
753  {
754    sub_hbin = hbin;
755    if ( !hbin_contains_offset( hbin, nk->values_off ) ) 
756    {
757      sub_hbin = lookup_hbin_block( file, nk->values_off );
758      if ( !sub_hbin ) 
759      {
760        /*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing value_list_offset [0x%x]\n",
761          nk->values_off));*/
762        return NULL;
763      }
764    }
765               
766    if(!hbin_prs_vk_records("vk_rec", sub_hbin, depth, nk, file))
767      return NULL;
768  }
769               
770  /* now get subkeys */
771  if ( nk->num_subkeys && (nk->subkeys_off!=REGF_OFFSET_NONE) ) 
772  {
773    sub_hbin = hbin;
774    if ( !hbin_contains_offset( hbin, nk->subkeys_off ) ) 
775    {
776      sub_hbin = lookup_hbin_block( file, nk->subkeys_off );
777      if ( !sub_hbin ) 
778      {
779        /*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing subkey_offset [0x%x]\n",
780          nk->subkeys_off));*/
781        return NULL;
782      }
783    }
784               
785    if (!hbin_prs_lf_records("lf_rec", sub_hbin, depth, nk))
786      return NULL;
787  }
788
789  /* get the security descriptor.  First look if we have already parsed it */
790  if ((nk->sk_off!=REGF_OFFSET_NONE) 
791      && !(nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )))
792  {
793    sub_hbin = hbin;
794    if (!hbin_contains_offset(hbin, nk->sk_off))
795    {
796      sub_hbin = lookup_hbin_block( file, nk->sk_off );
797      if ( !sub_hbin ) 
798      {
799        free(nk);
800        /*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing sk_offset [0x%x]\n",
801          nk->subkeys_off));*/
802        return NULL;
803      }
804    }
805   
806    sk_max_length = sub_hbin->block_size - (nk->sk_off - sub_hbin->first_hbin_off);
807    nk->sec_desc = regfi_parse_sk(file, nk->sk_off + REGF_BLOCKSIZE, 
808                                  sk_max_length, true);
809    if(nk->sec_desc == NULL)
810      return NULL;
811    nk->sec_desc->sk_off = nk->sk_off;
812                       
813    /* add to the list of security descriptors (ref_count has been read from the files) */
814    /* XXX: this kind of caching needs to be re-evaluated */
815    DLIST_ADD( file->sec_desc_list, nk->sec_desc );
816  }
817 
818  return nk;
819}
820
821
822/******************************************************************************
823
824 ******************************************************************************/
825static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
826                               uint32* root_offset)
827{
828  uint8 tmp[4];
829  int32 record_size;
830  uint32 length, hbin_offset = 0;
831  REGF_NK_REC* nk = NULL;
832  bool found = false;
833
834  for(record_size=0; !found && (hbin_offset < hbin_size); )
835  {
836    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
837      return false;
838   
839    length = 4;
840    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
841      return false;
842    record_size = IVALS(tmp, 0);
843
844    if(record_size < 0)
845    {
846      record_size = record_size*(-1);
847      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
848      if(nk != NULL)
849      {
850        if(nk->key_type == NK_TYPE_ROOTKEY)
851        {
852          found = true;
853          *root_offset = nk->offset;
854        }
855        free(nk);
856      }
857    }
858
859    hbin_offset += record_size;
860  }
861
862  return found;
863}
864
865
866/*******************************************************************
867 * Open the registry file and then read in the REGF block to get the
868 * first hbin offset.
869 *******************************************************************/
870REGF_FILE* regfi_open(const char* filename)
871{
872  REGF_FILE* rb;
873  int fd;
874  int flags = O_RDONLY;
875
876  /* open an existing file */
877  if ((fd = open(filename, flags)) == -1) 
878  {
879    /* DEBUG(0,("regfi_open: failure to open %s (%s)\n", filename, strerror(errno)));*/
880    return NULL;
881  }
882 
883  /* read in an existing file */
884  if ((rb = regfi_parse_regf(fd, true)) == NULL) 
885  {
886    /* DEBUG(0,("regfi_open: Failed to read initial REGF block\n"));*/
887    close(fd);
888    return NULL;
889  }
890 
891  rb->hbins = range_list_new();
892  rb->unalloc_cells = range_list_new();
893  if((rb->hbins == NULL) || (rb->unalloc_cells == NULL))
894  {
895    close(fd);
896    free(rb);
897    return NULL;
898  }
899
900  /* success */
901  return rb;
902}
903
904
905/*******************************************************************
906 *******************************************************************/
907int regfi_close( REGF_FILE *file )
908{
909  int fd;
910
911  /* nothing to do if there is no open file */
912  if ((file == NULL) || (file->fd == -1))
913    return 0;
914
915  fd = file->fd;
916  file->fd = -1;
917  range_list_free(file->hbins);
918  range_list_free(file->unalloc_cells);
919  free(file);
920
921  return close( fd );
922}
923
924
925/******************************************************************************
926 * There should be only *one* root key in the registry file based
927 * on my experience.  --jerry
928 *****************************************************************************/
929REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
930{
931  REGF_NK_REC* nk = NULL;
932  REGF_HBIN*   hbin;
933  uint32       offset = REGF_BLOCKSIZE;
934  uint32       root_offset;
935 
936  if(!file)
937    return NULL;
938
939  /* Scan through the file one HBIN block at a time looking
940     for an NK record with a type == 0x002c.
941     Normally this is the first nk record in the first hbin
942     block (but I'm not assuming that for now) */
943
944  while((hbin = regfi_parse_hbin(file, offset, true, false))) 
945  {
946    if(regfi_find_root_nk(file, hbin->file_off+HBIN_HEADER_REC_SIZE, 
947                          hbin->block_size-HBIN_HEADER_REC_SIZE, &root_offset))
948    {
949      if(!prs_set_offset(&hbin->ps, root_offset + 4 
950                         - hbin->first_hbin_off - REGF_BLOCKSIZE))
951        return NULL;
952     
953      nk = hbin_prs_key(file, hbin);
954      break;
955    }
956
957    offset += hbin->block_size;
958  }
959
960  return nk;
961}
962
963
964/******************************************************************************
965 *****************************************************************************/
966void regfi_key_free(REGF_NK_REC* nk)
967{
968  uint32 i;
969 
970  if((nk->values != NULL) && (nk->values_off!=REGF_OFFSET_NONE))
971  {
972    for(i=0; i < nk->num_values; i++)
973    {
974      if(nk->values[i]->valuename != NULL)
975        free(nk->values[i]->valuename);
976      if(nk->values[i]->data != NULL)
977        free(nk->values[i]->data);
978      free(nk->values[i]);
979    }
980    free(nk->values);
981  }
982
983  if(nk->keyname != NULL)
984    free(nk->keyname);
985  if(nk->classname != NULL)
986    free(nk->classname);
987
988  /* XXX: not freeing hbin because these are cached.  This needs to be reviewed. */
989  /* XXX: not freeing sec_desc because these are cached.  This needs to be reviewed. */
990  free(nk);
991}
992
993
994/******************************************************************************
995 *****************************************************************************/
996REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh)
997{
998  REGF_NK_REC* root;
999  REGFI_ITERATOR* ret_val = (REGFI_ITERATOR*)malloc(sizeof(REGFI_ITERATOR));
1000  if(ret_val == NULL)
1001    return NULL;
1002
1003  root = regfi_rootkey(fh);
1004  if(root == NULL)
1005  {
1006    free(ret_val);
1007    return NULL;
1008  }
1009
1010  ret_val->key_positions = void_stack_new(REGF_MAX_DEPTH);
1011  if(ret_val->key_positions == NULL)
1012  {
1013    free(ret_val);
1014    free(root);
1015    return NULL;
1016  }
1017
1018  ret_val->f = fh;
1019  ret_val->cur_key = root;
1020  ret_val->cur_subkey = 0;
1021  ret_val->cur_value = 0;
1022
1023  return ret_val;
1024}
1025
1026
1027/******************************************************************************
1028 *****************************************************************************/
1029void regfi_iterator_free(REGFI_ITERATOR* i)
1030{
1031  REGFI_ITER_POSITION* cur;
1032
1033  if(i->cur_key != NULL)
1034    regfi_key_free(i->cur_key);
1035
1036  while((cur = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions)) != NULL)
1037  {
1038    regfi_key_free(cur->nk);
1039    free(cur);
1040  }
1041 
1042  free(i);
1043}
1044
1045
1046
1047/******************************************************************************
1048 *****************************************************************************/
1049/* XXX: some way of indicating reason for failure should be added. */
1050bool regfi_iterator_down(REGFI_ITERATOR* i)
1051{
1052  REGF_NK_REC* subkey;
1053  REGFI_ITER_POSITION* pos;
1054
1055  pos = (REGFI_ITER_POSITION*)malloc(sizeof(REGFI_ITER_POSITION));
1056  if(pos == NULL)
1057    return false;
1058
1059  subkey = (REGF_NK_REC*)regfi_iterator_cur_subkey(i);
1060  if(subkey == NULL)
1061  {
1062    free(pos);
1063    return false;
1064  }
1065
1066  pos->nk = i->cur_key;
1067  pos->cur_subkey = i->cur_subkey;
1068  if(!void_stack_push(i->key_positions, pos))
1069  {
1070    free(pos);
1071    regfi_key_free(subkey);
1072    return false;
1073  }
1074
1075  i->cur_key = subkey;
1076  i->cur_subkey = 0;
1077  i->cur_value = 0;
1078
1079  return true;
1080}
1081
1082
1083/******************************************************************************
1084 *****************************************************************************/
1085bool regfi_iterator_up(REGFI_ITERATOR* i)
1086{
1087  REGFI_ITER_POSITION* pos;
1088
1089  pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions);
1090  if(pos == NULL)
1091    return false;
1092
1093  regfi_key_free(i->cur_key);
1094  i->cur_key = pos->nk;
1095  i->cur_subkey = pos->cur_subkey;
1096  i->cur_value = 0;
1097  free(pos);
1098
1099  return true;
1100}
1101
1102
1103/******************************************************************************
1104 *****************************************************************************/
1105bool regfi_iterator_to_root(REGFI_ITERATOR* i)
1106{
1107  while(regfi_iterator_up(i))
1108    continue;
1109
1110  return true;
1111}
1112
1113
1114/******************************************************************************
1115 *****************************************************************************/
1116bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
1117{
1118  REGF_NK_REC* subkey;
1119  bool found = false;
1120  uint32 old_subkey = i->cur_subkey;
1121 
1122  if(subkey_name == NULL)
1123    return false;
1124
1125  /* XXX: this alloc/free of each sub key might be a bit excessive */
1126  subkey = (REGF_NK_REC*)regfi_iterator_first_subkey(i);
1127  while((subkey != NULL) && (found == false))
1128  {
1129    if(subkey->keyname != NULL 
1130       && strcasecmp(subkey->keyname, subkey_name) == 0)
1131      found = true;
1132    else
1133    {
1134      regfi_key_free(subkey);
1135      subkey = (REGF_NK_REC*)regfi_iterator_next_subkey(i);
1136    }
1137  }
1138
1139  if(found == false)
1140  {
1141    i->cur_subkey = old_subkey;
1142    return false;
1143  }
1144
1145  regfi_key_free(subkey);
1146  return true;
1147}
1148
1149
1150/******************************************************************************
1151 *****************************************************************************/
1152bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
1153{
1154  uint32 x;
1155  if(path == NULL)
1156    return false;
1157
1158  for(x=0; 
1159      ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x])
1160       && regfi_iterator_down(i));
1161      x++)
1162  { continue; }
1163
1164  if(path[x] == NULL)
1165    return true;
1166 
1167  /* XXX: is this the right number of times? */
1168  for(; x > 0; x--)
1169    regfi_iterator_up(i);
1170 
1171  return false;
1172}
1173
1174
1175/******************************************************************************
1176 *****************************************************************************/
1177const REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
1178{
1179  return i->cur_key;
1180}
1181
1182
1183/******************************************************************************
1184 *****************************************************************************/
1185const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
1186{
1187  i->cur_subkey = 0;
1188  return regfi_iterator_cur_subkey(i);
1189}
1190
1191
1192/******************************************************************************
1193 *****************************************************************************/
1194const REGF_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
1195{
1196  REGF_NK_REC* subkey;
1197  REGF_HBIN* hbin;
1198  uint32 nk_offset;
1199
1200  /* see if there is anything left to report */
1201  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGF_OFFSET_NONE)
1202      || (i->cur_subkey >= i->cur_key->num_subkeys))
1203    return NULL;
1204
1205  nk_offset = i->cur_key->subkeys.hashes[i->cur_subkey].nk_off;
1206
1207  /* find the HBIN block which should contain the nk record */
1208  hbin = lookup_hbin_block(i->f, nk_offset);
1209  if(!hbin)
1210  {
1211    /* XXX: should print out some kind of error message every time here */
1212    /*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing offset [0x%x]\n",
1213      i->cur_key->subkeys.hashes[i->cur_subkey].nk_off));*/
1214    return NULL;
1215  }
1216 
1217  if(!prs_set_offset(&hbin->ps, 
1218                     HBIN_MAGIC_SIZE + nk_offset - hbin->first_hbin_off))
1219    return NULL;
1220               
1221  if((subkey = hbin_prs_key(i->f, hbin)) == NULL)
1222    return NULL;
1223
1224  return subkey;
1225}
1226
1227
1228/******************************************************************************
1229 *****************************************************************************/
1230/* XXX: some way of indicating reason for failure should be added. */
1231const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
1232{
1233  const REGF_NK_REC* subkey;
1234
1235  i->cur_subkey++;
1236  subkey = regfi_iterator_cur_subkey(i);
1237
1238  if(subkey == NULL)
1239    i->cur_subkey--;
1240
1241  return subkey;
1242}
1243
1244
1245/******************************************************************************
1246 *****************************************************************************/
1247bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
1248{
1249  const REGF_VK_REC* cur;
1250  bool found = false;
1251
1252  /* XXX: cur->valuename can be NULL in the registry. 
1253   *      Should we allow for a way to search for that?
1254   */
1255  if(value_name == NULL)
1256    return false;
1257
1258  cur = regfi_iterator_first_value(i);
1259  while((cur != NULL) && (found == false))
1260  {
1261    if((cur->valuename != NULL)
1262       && (strcasecmp(cur->valuename, value_name) == 0))
1263      found = true;
1264    else
1265      cur = regfi_iterator_next_value(i);
1266  }
1267
1268  return found;
1269}
1270
1271
1272/******************************************************************************
1273 *****************************************************************************/
1274const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
1275{
1276  i->cur_value = 0;
1277  return regfi_iterator_cur_value(i);
1278}
1279
1280
1281/******************************************************************************
1282 *****************************************************************************/
1283const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
1284{
1285  REGF_VK_REC* ret_val = NULL;
1286  if(i->cur_value < i->cur_key->num_values)
1287    ret_val = i->cur_key->values[i->cur_value];
1288
1289  return ret_val;
1290}
1291
1292
1293/******************************************************************************
1294 *****************************************************************************/
1295const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
1296{
1297  const REGF_VK_REC* ret_val;
1298
1299  i->cur_value++;
1300  ret_val = regfi_iterator_cur_value(i);
1301  if(ret_val == NULL)
1302    i->cur_value--;
1303
1304  return ret_val;
1305}
1306
1307
1308
1309/****************/
1310/* Experimental */
1311/****************/
1312/*
1313typedef struct {
1314  uint32 offset;
1315  uint32 size;
1316} REGFI_CELL_INFO;
1317
1318typedef struct {
1319  uint32 count
1320  REGFI_CELL_INFO** cells;
1321} REGFI_CELL_LIST;
1322*/
1323
1324
1325/*******************************************************************
1326 * Computes the checksum of the registry file header.
1327 * buffer must be at least the size of an regf header (4096 bytes).
1328 *******************************************************************/
1329static uint32 regfi_compute_header_checksum(uint8* buffer)
1330{
1331  uint32 checksum, x;
1332  int i;
1333
1334  /* XOR of all bytes 0x0000 - 0x01FB */
1335
1336  checksum = x = 0;
1337 
1338  for ( i=0; i<0x01FB; i+=4 ) {
1339    x = IVAL(buffer, i );
1340    checksum ^= x;
1341  }
1342 
1343  return checksum;
1344}
1345
1346
1347/*******************************************************************
1348 * TODO: add way to return more detailed error information.
1349 *******************************************************************/
1350REGF_FILE* regfi_parse_regf(int fd, bool strict)
1351{
1352  uint8 file_header[REGF_BLOCKSIZE];
1353  uint32 length;
1354  uint32 file_length;
1355  struct stat sbuf;
1356  REGF_FILE* ret_val;
1357
1358  /* Determine file length.  Must be at least big enough
1359   * for the header and one hbin.
1360   */
1361  if (fstat(fd, &sbuf) == -1)
1362    return NULL;
1363  file_length = sbuf.st_size;
1364  if(file_length < REGF_BLOCKSIZE+REGF_ALLOC_BLOCK)
1365    return NULL;
1366
1367  ret_val = (REGF_FILE*)zalloc(sizeof(REGF_FILE));
1368  if(ret_val == NULL)
1369    return NULL;
1370
1371  ret_val->fd = fd;
1372  ret_val->file_length = file_length;
1373
1374  length = REGF_BLOCKSIZE;
1375  if((regfi_read(fd, file_header, &length)) != 0 
1376     || length != REGF_BLOCKSIZE)
1377  {
1378    free(ret_val);
1379    return NULL;
1380  }
1381
1382  ret_val->checksum = IVAL(file_header, 0x1FC);
1383  ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
1384  if (strict && (ret_val->checksum != ret_val->computed_checksum))
1385  {
1386    free(ret_val);
1387    return NULL;
1388  }
1389
1390  memcpy(ret_val->magic, file_header, 4);
1391  if(strict && (memcmp(ret_val->magic, "regf", 4) != 0))
1392  {
1393    free(ret_val);
1394    return NULL;
1395  }
1396 
1397  ret_val->unknown1 = IVAL(file_header, 0x4);
1398  ret_val->unknown2 = IVAL(file_header, 0x8);
1399
1400  ret_val->mtime.low = IVAL(file_header, 0xC);
1401  ret_val->mtime.high = IVAL(file_header, 0x10);
1402
1403  ret_val->unknown3 = IVAL(file_header, 0x14);
1404  ret_val->unknown4 = IVAL(file_header, 0x18);
1405  ret_val->unknown5 = IVAL(file_header, 0x1C);
1406  ret_val->unknown6 = IVAL(file_header, 0x20);
1407 
1408  ret_val->data_offset = IVAL(file_header, 0x24);
1409  ret_val->last_block = IVAL(file_header, 0x28);
1410
1411  ret_val->unknown7 = IVAL(file_header, 0x2C);
1412
1413  return ret_val;
1414}
1415
1416
1417
1418/*******************************************************************
1419 * Given real file offset, read and parse the hbin at that location
1420 * along with it's associated cells.  If save_unalloc is true, a list
1421 * of unallocated cell offsets will be stored in TODO.
1422 *******************************************************************/
1423/* TODO: Need a way to return types of errors.  Also need to free
1424 *       the hbin/ps when an error occurs.
1425 */
1426REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, 
1427                            bool strict, bool save_unalloc)
1428{
1429  REGF_HBIN *hbin;
1430  uint8 hbin_header[HBIN_HEADER_REC_SIZE];
1431  uint32 length, curr_off;
1432  int32 cell_len;
1433  bool is_unalloc;
1434 
1435  if(offset >= file->file_length)
1436    return NULL;
1437
1438  if(lseek(file->fd, offset, SEEK_SET) == -1)
1439    return NULL;
1440
1441  length = HBIN_HEADER_REC_SIZE;
1442  if((regfi_read(file->fd, hbin_header, &length) != 0) 
1443     || length != HBIN_HEADER_REC_SIZE)
1444    return NULL;
1445
1446
1447  if(lseek(file->fd, offset, SEEK_SET) == -1)
1448    return NULL;
1449
1450  if(!(hbin = (REGF_HBIN*)zalloc(sizeof(REGF_HBIN)))) 
1451    return NULL;
1452  hbin->file_off = offset;
1453
1454  memcpy(hbin->magic, hbin_header, 4);
1455  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
1456  {
1457    free(hbin);
1458    return NULL;
1459  }
1460
1461  hbin->first_hbin_off = IVAL(hbin_header, 0x4);
1462  hbin->block_size = IVAL(hbin_header, 0x8);
1463  /* this should be the same thing as hbin->block_size but just in case */
1464  hbin->next_block = IVAL(hbin_header, 0x1C);
1465
1466
1467  /* Ensure the block size is a multiple of 0x1000 and doesn't run off
1468   * the end of the file.
1469   */
1470  /* TODO: This may need to be relaxed for dealing with
1471   *       partial or corrupt files. */
1472  if((offset + hbin->block_size > file->file_length)
1473     || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
1474  {
1475    free(hbin);
1476    return NULL;
1477  }
1478
1479  /* TODO: need to get rid of this, but currently lots depends on the
1480   * ps structure.
1481   */
1482  if(!prs_init(&hbin->ps, hbin->block_size, file->mem_ctx, UNMARSHALL))
1483  {
1484    free(hbin);
1485    return NULL;
1486  }
1487  length = hbin->block_size;
1488  if((regfi_read(file->fd, (uint8*)hbin->ps.data_p, &length) != 0) 
1489     || length != hbin->block_size)
1490  {
1491    free(hbin);
1492    return NULL;
1493  }
1494
1495
1496  if(save_unalloc)
1497  {
1498    cell_len = 0;
1499    curr_off = HBIN_HEADER_REC_SIZE;
1500    while ( curr_off < hbin->block_size ) 
1501    {
1502      is_unalloc = false;
1503      cell_len = IVALS(hbin->ps.data_p, curr_off);
1504      if(cell_len > 0)
1505        is_unalloc = true;
1506      else
1507        cell_len = -1*cell_len;
1508
1509      if((cell_len == 0) || ((cell_len & 0xFFFFFFFC) != cell_len))
1510        /* TODO: should report an error here. */
1511        break;
1512
1513      /* for some reason the record_size of the last record in
1514         an hbin block can extend past the end of the block
1515         even though the record fits within the remaining
1516         space....aaarrrgggghhhhhh */ 
1517      if(curr_off + cell_len >= hbin->block_size)
1518        cell_len = hbin->block_size - curr_off;
1519
1520      if(is_unalloc)
1521        range_list_add(file->unalloc_cells, hbin->file_off+curr_off, 
1522          cell_len, NULL);
1523
1524      curr_off = curr_off+cell_len;
1525    }
1526  }
1527
1528  /* TODO: need to get rid of this, but currently lots depends on the
1529   * ps structure.
1530   */
1531  if(!prs_set_offset(&hbin->ps, file->data_offset+HBIN_MAGIC_SIZE))
1532    return NULL;
1533
1534  return hbin;
1535}
1536
1537
1538
1539REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset, 
1540                            uint32 max_size, bool strict)
1541{
1542  uint8 nk_header[REGFI_NK_MIN_LENGTH];
1543  REGF_NK_REC* ret_val;
1544  uint32 length;
1545  uint32 cell_length;
1546  bool unalloc = false;
1547
1548  if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
1549                       &cell_length, &unalloc))
1550     return NULL;
1551 
1552  /* A bit of validation before bothering to allocate memory */
1553  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
1554  {
1555    /* TODO: deal with subkey-lists that reference other subkey-lists. */
1556    /*fprintf(stderr, "DEBUG: magic check failed! \"%c%c\"\n", nk_header[0x0], nk_header[0x1]);*/
1557    return NULL;
1558  }
1559
1560  ret_val = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC));
1561  if(ret_val == NULL)
1562    return NULL;
1563
1564  ret_val->offset = offset;
1565  ret_val->cell_size = cell_length;
1566
1567  if(ret_val->cell_size > max_size)
1568    ret_val->cell_size = max_size & 0xFFFFFFF8;
1569  if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
1570     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
1571  {
1572    free(ret_val);
1573    return NULL;
1574  }
1575
1576  ret_val->magic[0] = nk_header[0x0];
1577  ret_val->magic[1] = nk_header[0x1];
1578  ret_val->key_type = SVAL(nk_header, 0x2);
1579  if((ret_val->key_type != NK_TYPE_NORMALKEY)
1580     && (ret_val->key_type != NK_TYPE_ROOTKEY) 
1581     && (ret_val->key_type != NK_TYPE_LINKKEY)
1582     && (ret_val->key_type != NK_TYPE_UNKNOWN1))
1583  {
1584    free(ret_val);
1585    return NULL;
1586  }
1587
1588  ret_val->mtime.low = IVAL(nk_header, 0x4);
1589  ret_val->mtime.high = IVAL(nk_header, 0x8);
1590 
1591  ret_val->unknown1 = IVAL(nk_header, 0xC);
1592  ret_val->parent_off = IVAL(nk_header, 0x10);
1593  ret_val->num_subkeys = IVAL(nk_header, 0x14);
1594  ret_val->unknown2 = IVAL(nk_header, 0x18);
1595  ret_val->subkeys_off = IVAL(nk_header, 0x1C);
1596  ret_val->unknown3 = IVAL(nk_header, 0x20);
1597  ret_val->num_values = IVAL(nk_header, 0x24);
1598  ret_val->values_off = IVAL(nk_header, 0x28);
1599  ret_val->sk_off = IVAL(nk_header, 0x2C);
1600  /* TODO: currently we do nothing with class names.  Need to investigate. */
1601  ret_val->classname_off = IVAL(nk_header, 0x30);
1602
1603  ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
1604  ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38);
1605  ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C);
1606  ret_val->max_bytes_value = IVAL(nk_header, 0x40);
1607  ret_val->unk_index = IVAL(nk_header, 0x44);
1608
1609  ret_val->name_length = SVAL(nk_header, 0x48);
1610  ret_val->classname_length = SVAL(nk_header, 0x4A);
1611
1612  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
1613  {
1614    if(strict)
1615    {
1616      free(ret_val);
1617      return NULL;
1618    }
1619    else
1620      ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
1621  }
1622  else if (unalloc)
1623  { /* Truncate cell_size if it's much larger than the apparent total record length. */
1624    /* Round up to the next multiple of 8 */
1625    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
1626    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
1627      length+=8;
1628
1629    /* If cell_size is still greater, truncate. */
1630    if(length < ret_val->cell_size)
1631      ret_val->cell_size = length;
1632  }
1633
1634  ret_val->keyname = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
1635  if(ret_val->keyname == NULL)
1636  {
1637    free(ret_val);
1638    return NULL;
1639  }
1640
1641  /* Don't need to seek, should be at the right offset */
1642  length = ret_val->name_length;
1643  if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
1644     || length != ret_val->name_length)
1645  {
1646    free(ret_val->keyname);
1647    free(ret_val);
1648    return NULL;
1649  }
1650  ret_val->keyname[ret_val->name_length] = '\0';
1651
1652  return ret_val;
1653}
1654
1655
1656
1657/*******************************************************************
1658 *******************************************************************/
1659REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset, 
1660                            uint32 max_size, bool strict)
1661{
1662  REGF_VK_REC* ret_val;
1663  uint8 vk_header[REGFI_VK_MIN_LENGTH];
1664  uint32 raw_data_size, length, cell_length;
1665  bool unalloc = false;
1666
1667  if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
1668                       &cell_length, &unalloc))
1669    return NULL;
1670   
1671  ret_val = (REGF_VK_REC*)zalloc(sizeof(REGF_VK_REC));
1672  if(ret_val == NULL)
1673    return NULL;
1674
1675  ret_val->offset = offset;
1676  ret_val->cell_size = cell_length;
1677
1678  if(ret_val->cell_size > max_size)
1679    ret_val->cell_size = max_size & 0xFFFFFFF8;
1680  if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
1681     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
1682  {
1683    free(ret_val);
1684    return NULL;
1685  }
1686
1687  ret_val->magic[0] = vk_header[0x0];
1688  ret_val->magic[1] = vk_header[0x1];
1689  if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
1690  {
1691    free(ret_val);
1692    return NULL;
1693  }
1694
1695  ret_val->name_length = SVAL(vk_header, 0x2);
1696  raw_data_size = IVAL(vk_header, 0x4);
1697  ret_val->data_size = raw_data_size & ~VK_DATA_IN_OFFSET;
1698  ret_val->data_off = IVAL(vk_header, 0x8);
1699  ret_val->type = IVAL(vk_header, 0xC);
1700  ret_val->flag = SVAL(vk_header, 0x10);
1701  ret_val->unknown1 = SVAL(vk_header, 0x12);
1702
1703  if(ret_val->flag & VK_FLAG_NAME_PRESENT)
1704  {
1705    if(ret_val->name_length + REGFI_VK_MIN_LENGTH > ret_val->cell_size)
1706    {
1707      if(strict)
1708      {
1709        free(ret_val);
1710        return NULL;
1711      }
1712      else
1713        ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH;
1714    }
1715
1716    /* Round up to the next multiple of 8 */
1717    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
1718    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
1719      length+=8;
1720
1721    ret_val->valuename = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
1722    if(ret_val->valuename == NULL)
1723    {
1724      free(ret_val);
1725      return NULL;
1726    }
1727   
1728    /* Don't need to seek, should be at the right offset */
1729    length = ret_val->name_length;
1730    if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
1731       || length != ret_val->name_length)
1732    {
1733      free(ret_val->valuename);
1734      free(ret_val);
1735      return NULL;
1736    }
1737    ret_val->valuename[ret_val->name_length] = '\0';
1738  }
1739  else
1740    length = REGFI_VK_MIN_LENGTH;
1741
1742  if(unalloc)
1743  {
1744    /* If cell_size is still greater, truncate. */
1745    if(length < ret_val->cell_size)
1746      ret_val->cell_size = length;
1747  }
1748
1749  if(ret_val->data_size == 0)
1750    ret_val->data = NULL;
1751  else
1752  {
1753    ret_val->data = regfi_parse_data(file, ret_val->data_off+REGF_BLOCKSIZE,
1754                                     raw_data_size, strict);
1755    if(strict && (ret_val->data == NULL))
1756    {
1757      free(ret_val->valuename);
1758      free(ret_val);
1759      return NULL;
1760    }
1761  }
1762
1763  return ret_val;
1764}
1765
1766
1767uint8* regfi_parse_data(REGF_FILE* file, uint32 offset, uint32 length, bool strict)
1768{
1769  uint8* ret_val;
1770  uint32 read_length, cell_length;
1771  uint8 i;
1772  bool unalloc;
1773
1774  /* The data is stored in the offset if the size <= 4 */
1775  if (length & VK_DATA_IN_OFFSET)
1776  {
1777    length = length & ~VK_DATA_IN_OFFSET;
1778    if(length > 4)
1779      return NULL;
1780
1781    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
1782      return NULL;
1783
1784    offset = offset - REGF_BLOCKSIZE;
1785    for(i = 0; i < length; i++)
1786      ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
1787  }
1788  else
1789  {
1790    if(!regfi_parse_cell(file->fd, offset, NULL, 0,
1791                         &cell_length, &unalloc))
1792      return NULL;
1793   
1794    if(cell_length < 8 || ((cell_length & 0xFFFFFFF8) != cell_length))
1795      return NULL;
1796
1797    if(cell_length - 4 < length)
1798    {
1799      if(strict)
1800        return NULL;
1801      else
1802        length = cell_length - 4;
1803    }
1804
1805    /* TODO: There is currently no check to ensure the data
1806     *       cell doesn't cross HBIN boundary.
1807     */
1808
1809    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
1810      return NULL;
1811
1812    read_length = length;
1813    if((regfi_read(file->fd, ret_val, &read_length) != 0) 
1814       || read_length != length)
1815    {
1816      free(ret_val);
1817      return NULL;
1818    }
1819  }
1820
1821  return ret_val;
1822}
Note: See TracBrowser for help on using the repository browser.