source: releases/1.0.0/lib/regfi.c@ 293

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

simplified string encoding argument passing throughout regfi API

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