source: trunk/lib/regfi.c @ 168

Last change on this file since 168 was 168, checked in by tim, 14 years ago

merged remaining smb_deps items into regfi

began formatting API comments for use with doxygen

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