[Forensics-changes] [SCM] utility to read and query Windows NT/2000/XP registry branch, debian, updated. debian/0.9.0-2-2-gf1b1ed1
Christophe Monniez
christophe.monniez at fccu.be
Mon Jun 22 10:58:45 UTC 2009
The following commit has been merged in the debian branch:
commit 4730a9a9d062f32e9956d71abccda421469633dd
Author: Christophe Monniez <christophe.monniez at fccu.be>
Date: Mon Jun 22 11:05:03 2009 +0200
Merging upstream version 0.11.0.
diff --git a/INSTALL b/INSTALL
index 308824c..468ffc5 100644
--- a/INSTALL
+++ b/INSTALL
@@ -10,6 +10,11 @@ comes with any free operating system. Be sure you have:
- GNU Make
- GCC
+Note that iconv support is required, as specified in IEEE Std 1003.1
+(POSIX.1-2001). Some platforms still do not contain support for this
+natively, in which case you may need to install libiconv from:
+ http://www.gnu.org/software/libiconv/
+
Survival Commands
-----------------
@@ -43,3 +48,7 @@ make VAR=value install
For more information, see the Makefiles.
+
+Primitive build features have been added to support cross-compiling to
+Windows binaries using MinGW. For more information on this, see:
+ doc/mingw-build.txt
diff --git a/Makefile b/Makefile
index 9e58a26..e22d83c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile 111 2008-05-01 04:06:22Z tim $
+# $Id: Makefile 147 2009-02-22 19:31:52Z tim $
# Installation prefixes. Change to install elsewhere.
@@ -11,14 +11,35 @@ MAN_PREFIX=$(PREFIX)/man
CC=gcc
OPTS=-std=gnu89 -pedantic -Wall -ggdb
-INC=-I/usr/local/include
+#OPTS=-std=gnu89 -pedantic -Wall
+INC:=-I$(PWD)/include -I/usr/local/include
LIB=-L/usr/local/lib -lm
+BIN_EXT=
+EXTRA_OBJ=
UNAME := $(shell uname)
ifneq ($(UNAME),Linux)
LIB:=$(LIB) -liconv
endif
+
+################################################################################
+# MinGW cross-compiling build settings
+ifdef BUILD_MINGW
+
+## These may need to be changed
+CC=i586-mingw32msvc-cc
+LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
+
+## These probably do not need to be changed
+BIN_EXT=.exe
+INC:=$(INC) -I$(LIBICONV_PATH)/include
+EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
+
+endif
+################################################################################
+
+
BUILD=$(CURDIR)/build
BUILD_BIN=$(BUILD)/bin
BUILD_DOC=$(BUILD)/doc
diff --git a/doc/Makefile b/doc/Makefile
index f30d9da..39ef109 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile 119 2008-08-09 05:55:45Z tim $
+# $Id: Makefile 155 2009-06-03 23:45:58Z tim $
BUILD_FILES=$(BUILD_DOC)/man/man1/reglookup.1.gz\
$(BUILD_DOC)/man/man1/reglookup-timeline.1.gz\
@@ -25,9 +25,11 @@ install:
#XXX: Used during release only
release:
- docbook2x-man --to-stdout reglookup.1.docbook > man/man1/reglookup.1
- docbook2x-man --to-stdout reglookup-timeline.1.docbook > man/man1/reglookup-timeline.1
- docbook2x-man --to-stdout reglookup-recover.1.docbook > man/man1/reglookup-recover.1
+ #XXX: The sed filter is a temporary hack to work around a bug in docbook2x-man
+ # See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=516165
+ docbook2x-man --to-stdout reglookup.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup.1
+ docbook2x-man --to-stdout reglookup-timeline.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-timeline.1
+ docbook2x-man --to-stdout reglookup-recover.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-recover.1
cd man/man1 && gzip -9 -f reglookup.1
cd man/man1 && gzip -9 -f reglookup-timeline.1
cd man/man1 && gzip -9 -f reglookup-recover.1
diff --git a/doc/devel/TODO b/doc/devel/TODO
index dba1326..549fac8 100644
--- a/doc/devel/TODO
+++ b/doc/devel/TODO
@@ -1,4 +1,4 @@
-$Id: TODO 122 2008-08-09 20:24:01Z tim $
+$Id: TODO 153 2009-06-02 22:28:07Z tim $
If you are interested in contributing to this project, here's a few
things you could look into:
@@ -11,6 +11,9 @@ things you could look into:
paths. Reglookup should take the user path and unquote each path
component using the \xQQ syntax prior to searching.
+ - It might be nice to have a way to filter results by security
+ descriptor information. Maybe by MTIME as well.
+
- Testing, testing, and more testing. reglookup needs to be tested on
NT/XP/2k3/Vista. A regression test suite would be nice too. Some
thoughts on this include a script which randomly fuzzes an existing
@@ -25,40 +28,30 @@ things you could look into:
minimal make rules. If you got any ideas on this, shoot them my way.
- Unicode support still needs improvement. While parsing strings seems
- to be decent, UTF-8 output would be nice.
+ to be decent, UTF-8 output would be nice. Need support for
+ UTF-16LE key and value names. To do this, the UTF conversion
+ functions need to be moved to regfi.
- The interface between reglookup.c and regfi.c is much better than it
- used to be, but the iter2Stack function needs to be moved into the
- library, which means the \xQQ quoting syntax will have to go with it.
- This syntax will need to be more carefully documented if it's going
- to be a part of the library.
+ used to be, but the parsing of data objects needs to be moved into the
+ library. The quoting syntax should stay in reglookup/reglookup-recover
+ but the basic parsing of data types into proper structures should
+ happen in the library so that they are accessible to other users of the
+ library.
- - NK/VK/SK record caching. Right now, HBINs and perhaps SK records are
- cached, but it's pretty haphazard, and NK/VK records are repeatedly
- re-parsed. A generic caching library should be introduced which can
- cache many of these records with a specific memory limit in mind.
- I think this will speed things up greatly.
-
- - It might be nice to have a way to filter results by security
- descriptor information. Maybe by MTIME as well.
+ - Develop and solidify regfi API. Regfi should be better documented and
+ eventually needs a set of Python wrappers.
- Documentation. The security descriptor output format needs to be
documented. Also, function contracts should be added to the
lower-level functions of regfi.c.
- - The stuff in smb_deps.h and smb_deps.c needs to be cleaned up. The
- eventual goal is to have it all either integrated into regfi, or to
- be eliminated, or broken out into small supporting libraries, as
- necessary. It is currently just a jumble of old Samba code that I
- haven't decided where to put yet.
+ - The smb_deps.h and smb_deps.c content is almost eliminated. Just need
+ to integrate parts that are being kept into regfi or other modules.
- - At least one user reported that they use reglookup on a Windows host
- through Cygwin, but after version 0.3.0 came out, the dependency on
- libiconv caused that to break. libiconv seems to be a portability
- issue on other platforms as well. However, it's interface is a POSIX
- standard, and I think I'd like to keep it around. Perhaps it would
- be nice if reglookup could be cross-compiled using MinGW. Then a
- binary could be distributed for that platform. This app was never
- meant for Windows though, so this isn't a high priority.
+ - Need to figure out a reasonably correct way to convert UTF-16LE charaters
+ to ASCII under Windows/MingW or other platforms that don't have proper
+ libiconv support yet. Then a build-time option or autodetection can
+ dictate which version of conversion function is used.
- Grep through the source for 'XXX', and you'll find more.
diff --git a/doc/man/man1/reglookup-recover.1.gz b/doc/man/man1/reglookup-recover.1.gz
index 7deef08..b885411 100644
Binary files a/doc/man/man1/reglookup-recover.1.gz and b/doc/man/man1/reglookup-recover.1.gz differ
diff --git a/doc/man/man1/reglookup-timeline.1.gz b/doc/man/man1/reglookup-timeline.1.gz
index 81cdc68..c1c4d3f 100644
Binary files a/doc/man/man1/reglookup-timeline.1.gz and b/doc/man/man1/reglookup-timeline.1.gz differ
diff --git a/doc/man/man1/reglookup.1.gz b/doc/man/man1/reglookup.1.gz
index 27fb131..9f14fd4 100644
Binary files a/doc/man/man1/reglookup.1.gz and b/doc/man/man1/reglookup.1.gz differ
diff --git a/doc/mingw-build.txt b/doc/mingw-build.txt
new file mode 100644
index 0000000..9768b87
--- /dev/null
+++ b/doc/mingw-build.txt
@@ -0,0 +1,80 @@
+Cross-compiling RegLookup to Windows with MinGW
+===============================================
+
+MinGW can be used to compile Windows binaries from UNIX environments.
+The following instructions outline the steps required to build
+reglookup.exe and reglookup-recover.exe. This process is experimental
+and Windows binaries have not been well tested. You have been warned.
+
+Prerequisites
+-------------
+
+- Before you start, ensure you have MinGW installed. Under Debian,
+ install the `mingw32' package.
+
+- Download pre-compiled libiconv packages from here:
+ http://gnuwin32.sourceforge.net/packages/libiconv.htm
+
+ You will need to download the "Binaries" and "Developer files"
+ packages, whose files are named libiconv-VERSION-bin.zip and
+ libiconv-VERSION-lib.zip respectively.
+
+- Unpack both zip files into a designated top-level directory.
+ Suggested commands:
+ $ mkdir /usr/local/src/libiconv-VERSION-bin /usr/local/src/libiconv-VERSION-lib
+ $ cd /usr/local/src/libiconv-VERSION-bin
+ $ unzip .../path/to/libiconv-VERSION-bin.zip
+ $ cd /usr/local/src/libiconv-VERSION-lib
+ $ unzip .../path/to/libiconv-VERSION-lib.zip
+
+
+Building
+--------
+
+Review the top level RegLookup Makefile to ensure the settings match
+your environment. Find the conditional block which looks like:
+
+################################################################################
+# MinGW cross-compiling build settings
+ifdef BUILD_MINGW
+
+## These may need to be changed
+CC=i586-mingw32msvc-cc
+LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
+
+## These probably do not need to be changed
+BIN_EXT=.exe
+INC:=$(INC) -I$(LIBICONV_PATH)/include
+EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
+
+endif
+################################################################################
+
+
+If either the CC or LIBICONV_PATH settings are incorrect for your
+system, either update the Makefile, or override these options at build
+time when you run make. For instance, the above settings in the
+Makefile are correct, you can execute the build by running:
+
+$ make BUILD_MINGW=1
+
+Alternatively, you may override the variables above with:
+
+$ make BUILD_MINGW=1 CC=my-mingw-binary LIBICONV_PATH=.../path/to/libiconv-VERSION-lib
+
+Once the build is complete, you'll find the .exe files under the
+build/bin directory.
+
+
+Installation
+------------
+Naturally, there is no install Makefile target for the MinGW build
+process, since we aren't installing on the local system. To install
+these binaries on a Windows machine, simply copy over the reglookup.exe
+and reglookup-recover.exe files from the build/bin directory to the
+desired host. In addition, you will need to install the libiconv2.dll
+file on that host (either in the same directory as the reglookup
+executables, or somewhere in the DLL search path). This file is
+available in the libiconv-VERSION-bin.zip file you downloaded earlier,
+under the `bin' subdirectory.
+
diff --git a/doc/reglookup-recover.1.docbook b/doc/reglookup-recover.1.docbook
index 41266d9..95f47f5 100644
--- a/doc/reglookup-recover.1.docbook
+++ b/doc/reglookup-recover.1.docbook
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<refentry id='reglookup-recover.1'>
- <!-- $Id: reglookup-recover.1.docbook 119 2008-08-09 05:55:45Z tim $ -->
+ <!-- $Id: reglookup-recover.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
<refmeta>
<refentrytitle>reglookup</refentrytitle>
<manvolnum>1</manvolnum>
@@ -25,7 +25,6 @@
<para>
reglookup-recover attempts to scour a Windows registry hive for
deleted data structures and outputs those found in a CSV-like format.
- print them out to stdout in a CSV-like format.
</para>
</refsect1>
@@ -42,7 +41,7 @@
</term>
<listitem>
<para>
- Verbose output. (Currently does little to nothing.)
+ Verbose output.
</para>
</listitem>
</varlistentry>
diff --git a/doc/reglookup.1.docbook b/doc/reglookup.1.docbook
index c7e8d5d..9f208ff 100644
--- a/doc/reglookup.1.docbook
+++ b/doc/reglookup.1.docbook
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<refentry id='reglookup.1'>
- <!-- $Id: reglookup.1.docbook 119 2008-08-09 05:55:45Z tim $ -->
+ <!-- $Id: reglookup.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
<refmeta>
<refentrytitle>reglookup</refentrytitle>
<manvolnum>1</manvolnum>
@@ -26,8 +26,7 @@
reglookup is designed to read windows registry elements and
print them out to stdout in a CSV-like format. It has filtering
options to narrow the focus of the output. This tool is
- designed to work with on Windows NT/2K/XP/2K3/Vista registries,
- though your mileage may vary.
+ designed to work with on Windows NT-based registries.
</para>
</refsect1>
@@ -107,9 +106,9 @@
</term>
<listitem>
<para>
- Adds four additional columns to output containing
- information from key security descriptors. The columns
- are: owner, group, sacl, dacl.
+ Adds five additional columns to output containing
+ information from key security descriptors and rarely used
+ fields. The columns are: owner, group, sacl, dacl, class.
(This feature's output has not been extensively tested.)
</para>
</listitem>
@@ -137,7 +136,7 @@
</term>
<listitem>
<para>
- Verbose output. (Currently does little to nothing.)
+ Verbose output.
</para>
</listitem>
</varlistentry>
@@ -298,9 +297,6 @@
any bugs. Sample registry files and/or patches are greatly appreciated.)
</para>
<para>
- Verbose output is not working.
- </para>
- <para>
The SID conversions haven't been carefully checked for accuracy.
</para>
<para>
diff --git a/include/lru_cache.h b/include/lru_cache.h
index 5b1ec8c..80f77d3 100644
--- a/include/lru_cache.h
+++ b/include/lru_cache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: lru_cache.h 122 2008-08-09 20:24:01Z tim $
+ * $Id: lru_cache.h 147 2009-02-22 19:31:52Z tim $
*/
#ifndef LRU_CACHE_H
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include "talloc.h"
struct lru_cache_element;
typedef struct lru_cache_element lru_cache_element;
@@ -49,15 +50,17 @@ typedef struct _lru_cache
lru_cache_element* oldest;
lru_cache_element* newest;
lru_cache_element** table;
- bool free_data;
+ bool talloc_data;
} lru_cache;
-lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data);
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
+ uint32_t secret, bool talloc_data);
void lru_cache_destroy(lru_cache* ht);
-/* Returns a pointer to the old, replaced data stored at index.
- * Returns NULL if no entry was overwritten.
+/*
+ *
*/
bool lru_cache_update(lru_cache* ht, const void* index,
uint32_t index_len, void* data);
diff --git a/include/range_list.h b/include/range_list.h
index 7ef9536..942528c 100644
--- a/include/range_list.h
+++ b/include/range_list.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,17 +14,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: range_list.h 122 2008-08-09 20:24:01Z tim $
+ * $Id: range_list.h 148 2009-02-22 23:22:59Z tim $
*/
+#ifndef _RANGE_LIST_H
+#define _RANGE_LIST_H
+
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
-
-#ifndef _RANGE_LIST_H
-#define _RANGE_LIST_H
-
+#include <math.h>
+#include "talloc.h"
typedef struct _range_list_element
{
diff --git a/include/regfi.h b/include/regfi.h
index f4165d3..a381560 100644
--- a/include/regfi.h
+++ b/include/regfi.h
@@ -1,16 +1,15 @@
/*
- * Branched from Samba project, Subversion repository version #6903:
+ * Branched from Samba project Subversion repository, version #6903:
* http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
*
- * Unix SMB/CIFS implementation.
- * Windows NT registry I/O library
+ * Windows NT (and later) registry parsing library
*
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2009 Timothy D. Morgan
* Copyright (C) 2005 Gerald (Jerry) Carter
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
+ * the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,7 +20,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: regfi.h 121 2008-08-09 17:22:26Z tim $
+ * $Id: regfi.h 152 2009-06-02 20:00:38Z tim $
*/
/************************************************************
@@ -36,6 +35,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
+#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <time.h>
@@ -45,14 +45,27 @@
#include <unistd.h>
#include <assert.h>
+#include "talloc.h"
#include "smb_deps.h"
+#include "winsec.h"
#include "void_stack.h"
#include "range_list.h"
#include "lru_cache.h"
/******************************************************************************/
-/* Macros */
-
+
+/* regfi library error message types */
+#define REGFI_MSG_INFO 0x0001
+#define REGFI_MSG_WARN 0x0004
+#define REGFI_MSG_ERROR 0x0010
+
+/* Windows is lame */
+#ifdef O_BINARY
+#define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY
+#else
+#define REGFI_OPEN_FLAGS O_RDONLY
+#endif
+
/* Registry data types */
#define REG_NONE 0
#define REG_SZ 1
@@ -69,25 +82,37 @@
#define REG_QWORD 11 /* 64-bit little endian */
/* XXX: Has MS defined a REG_QWORD_BE? */
/* Not a real type in the registry */
-#define REG_KEY 0x7FFFFFFF
-
-#define REGF_BLOCKSIZE 0x1000
-#define REGF_ALLOC_BLOCK 0x1000 /* Minimum allocation unit for HBINs */
-#define REGF_MAX_DEPTH 512
-
-/* header sizes for various records */
-#define REGF_MAGIC_SIZE 4
-#define HBIN_MAGIC_SIZE 4
-#define HBIN_HEADER_REC_SIZE 0x20
-#define REC_HDR_SIZE 2
-
-#define REGF_OFFSET_NONE 0xffffffff
+#define REG_KEY 0x7FFFFFFF
+
+#define REGFI_MAX_DEPTH 512
+#define REGFI_OFFSET_NONE 0xffffffff
+
+/* XXX: This is totally arbitrary right now. */
+#define REGFI_MAX_SUBKEY_DEPTH 255
+
+/* Header sizes and magic number lengths for various records */
+#define REGFI_HBIN_ALLOC 0x1000 /* Minimum allocation unit for HBINs */
+#define REGFI_REGF_SIZE 0x1000 /* "regf" header block size */
+#define REGFI_REGF_MAGIC_SIZE 4
+#define REGFI_REGF_NAME_SIZE 64
+#define REGFI_REGF_RESERVED1_SIZE 340
+#define REGFI_REGF_RESERVED2_SIZE 3528
+#define REGFI_HBIN_MAGIC_SIZE 4
+#define REGFI_CELL_MAGIC_SIZE 2
+#define REGFI_HBIN_HEADER_SIZE 0x20
#define REGFI_NK_MIN_LENGTH 0x4C
#define REGFI_VK_MIN_LENGTH 0x14
#define REGFI_SK_MIN_LENGTH 0x14
-#define REGFI_HASH_LIST_MIN_LENGTH 0x4
+#define REGFI_SUBKEY_LIST_MIN_LEN 0x4
+
/* Constants used for validation */
+/* XXX: Can we add clock resolution validation as well as range? It has
+ * been reported that Windows timestamps are never more than a
+ * certain granularity (250ms?), which could be used to help
+ * eliminate false positives. Would need to verify this and
+ * perhaps conservatively implement a check.
+ */
/* Minimum time is Jan 1, 1990 00:00:00 */
#define REGFI_MTIME_MIN_HIGH 0x01B41E6D
#define REGFI_MTIME_MIN_LOW 0x26F98000
@@ -99,20 +124,67 @@
/* Flags for the vk records */
-#define VK_FLAG_NAME_PRESENT 0x0001
-#define VK_DATA_IN_OFFSET 0x80000000
-#define VK_MAX_DATA_LENGTH 1024*1024
+/* XXX: This next flag may be incorrect. According to Jeffrey Muir,
+* this may actually indicate that the value name is stored in
+* UTF-16LE.
+*/
+#define REGFI_VK_FLAG_NAME_PRESENT 0x0001
+#define REGFI_VK_DATA_IN_OFFSET 0x80000000
+#define REGFI_VK_MAX_DATA_LENGTH 1024*1024 /* XXX: This is arbitrary */
+
+
+/* Known key flags */
+/*******************/
+/* These next two show up on normal-seeming keys in Vista and W2K3 registries */
+#define REGFI_NK_FLAG_UNKNOWN1 0x4000
+#define REGFI_NK_FLAG_UNKNOWN2 0x1000
+
+/* This next one shows up on root keys in some Vista "software" registries */
+#define REGFI_NK_FLAG_UNKNOWN3 0x0080
+
+/* Predefined handle. Rumor has it that the valuelist count for this key is
+ * where the handle is stored.
+ * http://msdn.microsoft.com/en-us/library/ms724836(VS.85).aspx
+ */
+#define REGFI_NK_FLAG_PREDEF_KEY 0x0040
+
+/* The name will be in ASCII if this next bit is set, otherwise UTF-16LE */
+#define REGFI_NK_FLAG_ASCIINAME 0x0020
+
+/* Symlink key.
+ * See: http://www.codeproject.com/KB/system/regsymlink.aspx
+ */
+#define REGFI_NK_FLAG_LINK 0x0010
+
+/* This key cannot be deleted */
+#define REGFI_NK_FLAG_NO_RM 0x0008
+
+/* Root of a hive */
+#define REGFI_NK_FLAG_ROOT 0x0004
-/* NK record types */
-#define NK_TYPE_LINKKEY 0x0010
-#define NK_TYPE_NORMALKEY 0x0020
-#define NK_TYPE_ROOTKEY 0x002c
- /* TODO: Unknown type that shows up in Vista registries */
-#define NK_TYPE_UNKNOWN1 0x1020
+/* Mount point of another hive. NULL/(default) value indicates which hive
+ * and where in the hive it points to.
+ */
+#define REGFI_NK_FLAG_HIVE_LINK 0x0002
+/* These keys shouldn't be stored on disk, according to:
+ * http://geekswithblogs.net/sdorman/archive/2007/12/24/volatile-registry-keys.aspx
+ */
+#define REGFI_NK_FLAG_VOLATILE 0x0001
+
+/* Useful for identifying unknown flag types */
+#define REGFI_NK_KNOWN_FLAGS (REGFI_NK_FLAG_PREDEF_KEY\
+ | REGFI_NK_FLAG_ASCIINAME\
+ | REGFI_NK_FLAG_LINK\
+ | REGFI_NK_FLAG_NO_RM\
+ | REGFI_NK_FLAG_ROOT\
+ | REGFI_NK_FLAG_HIVE_LINK\
+ | REGFI_NK_FLAG_VOLATILE\
+ | REGFI_NK_FLAG_UNKNOWN1\
+ | REGFI_NK_FLAG_UNKNOWN2)
/* HBIN block */
-typedef struct regf_hbin
+typedef struct _regfi_hbin
{
uint32 file_off; /* my offset in the registry file */
uint32 ref_count; /* how many active records are pointing to this
@@ -127,79 +199,94 @@ typedef struct regf_hbin
* NOTE: This value may be unreliable!
*/
- uint8 magic[HBIN_MAGIC_SIZE]; /* "hbin" */
-} REGF_HBIN;
+ uint8 magic[REGFI_HBIN_MAGIC_SIZE]; /* "hbin" */
+} REGFI_HBIN;
-/* Hash List -- list of key offsets and hashed names for consistency */
+/* Subkey List -- list of key offsets and hashed names for consistency */
typedef struct
{
- uint32 nk_off;
+ /* Virtual offset of NK record or additional subkey list,
+ * depending on this list's type.
+ */
+ uint32 offset;
+
uint32 hash;
-} REGF_HASH_LIST_ELEM;
+} REGFI_SUBKEY_LIST_ELEM;
-typedef struct
+typedef struct _regfi_subkey_list
{
- uint32 offset; /* Real offset of this record's cell in the file */
- uint32 cell_size; /* ((start_offset - end_offset) & 0xfffffff8) */
- REGF_HBIN* hbin; /* pointer to HBIN record (in memory) containing
- * this nk record
- */
- uint32 hbin_off; /* offset from beginning of this hbin block */
- REGF_HASH_LIST_ELEM* hashes;
+ /* Real offset of this record's cell in the file */
+ uint32 offset;
+
+ uint32 cell_size;
- uint8 magic[REC_HDR_SIZE];
- uint16 num_keys;
-} REGF_HASH_LIST;
+ /* Number of immediate children */
+ uint32 num_children;
+ /* Total number of keys referenced by this list and it's children */
+ uint32 num_keys;
-/* Key Value */
+ REGFI_SUBKEY_LIST_ELEM* elements;
+ uint8 magic[REGFI_CELL_MAGIC_SIZE];
+
+ /* Set if the magic indicates this subkey list points to child subkey lists */
+ bool recursive_type;
+} REGFI_SUBKEY_LIST;
+
+
+typedef uint32 REGFI_VALUE_LIST_ELEM;
+typedef struct _regfi_value_list
+{
+ /* Actual number of values referenced by this list.
+ * May differ from parent key's num_values if there were parsing errors.
+ */
+ uint32 num_values;
+
+ REGFI_VALUE_LIST_ELEM* elements;
+} REGFI_VALUE_LIST;
+
+
+/* Value record */
typedef struct
{
uint32 offset; /* Real offset of this record's cell in the file */
uint32 cell_size; /* ((start_offset - end_offset) & 0xfffffff8) */
- REGF_HBIN* hbin; /* pointer to HBIN record (in memory) containing
- * this nk record
- */
uint8* data;
- uint16 name_length;
char* valuename;
+ uint16 name_length;
uint32 hbin_off; /* offset from beginning of this hbin block */
uint32 data_size;
uint32 data_off; /* offset of data cell (virtual) */
uint32 type;
- uint8 magic[REC_HDR_SIZE];
+ uint8 magic[REGFI_CELL_MAGIC_SIZE];
uint16 flag;
uint16 unknown1;
bool data_in_offset;
-} REGF_VK_REC;
+} REGFI_VK_REC;
/* Key Security */
-struct _regf_sk_rec;
+struct _regfi_sk_rec;
-typedef struct _regf_sk_rec
+typedef struct _regfi_sk_rec
{
uint32 offset; /* Real file offset of this record */
uint32 cell_size; /* ((start_offset - end_offset) & 0xfffffff8) */
- SEC_DESC* sec_desc;
+ WINSEC_DESC* sec_desc;
uint32 hbin_off; /* offset from beginning of this hbin block */
- uint32 sk_off; /* offset parsed from NK record used as a key
- * to lookup reference to this SK record
- */
-
uint32 prev_sk_off;
uint32 next_sk_off;
uint32 ref_count;
uint32 desc_size; /* size of security descriptor */
uint16 unknown_tag;
- uint8 magic[REC_HDR_SIZE];
-} REGF_SK_REC;
+ uint8 magic[REGFI_CELL_MAGIC_SIZE];
+} REGFI_SK_REC;
/* Key Name */
@@ -211,20 +298,19 @@ typedef struct
*/
/* link in the other records here */
- REGF_VK_REC** values;
- REGF_HASH_LIST* subkeys;
+ REGFI_VALUE_LIST* values;
+ REGFI_SUBKEY_LIST* subkeys;
/* header information */
- /* XXX: should we be looking for types other than the root key type? */
uint16 key_type;
- uint8 magic[REC_HDR_SIZE];
+ uint8 magic[REGFI_CELL_MAGIC_SIZE];
NTTIME mtime;
uint16 name_length;
uint16 classname_length;
char* classname;
char* keyname;
- uint32 parent_off; /* back pointer in registry hive */
- uint32 classname_off;
+ uint32 parent_off; /* pointer to parent key */
+ uint32 classname_off;
/* max lengths */
uint32 max_bytes_subkeyname; /* max subkey name * 2 */
@@ -240,65 +326,109 @@ typedef struct
/* children */
uint32 num_subkeys;
- uint32 subkeys_off; /* hash records that point to NK records */
+ uint32 subkeys_off; /* offset of subkey list that points to NK records */
uint32 num_values;
uint32 values_off; /* value lists which point to VK records */
- uint32 sk_off; /* offset to SK record */
-} REGF_NK_REC;
+ uint32 sk_off; /* offset to SK record */
+} REGFI_NK_REC;
/* REGF block */
typedef struct
{
- /* run time information */
- int fd; /* file descriptor */
+ /* Run-time information */
+ /************************/
+ /* file descriptor */
+ int fd;
+
/* For sanity checking (not part of the registry header) */
uint32 file_length;
- void* mem_ctx; /* memory context for run-time file access information */
- /* Experimental hbin lists */
+ /* Metadata about hbins */
range_list* hbins;
- /* file format information */
- uint8 magic[REGF_MAGIC_SIZE];/* "regf" */
+ /* SK record cached since they're repeatedly reused */
+ lru_cache* sk_cache;
+
+ /* Error/warning/info messages returned by lower layer functions */
+ char* last_message;
+
+ /* Mask for error message types that will be stored. */
+ uint16 msg_mask;
+
+
+ /* Data parsed from file header */
+ /********************************/
+ uint8 magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
+
+ /* These sequence numbers should match if
+ * the hive was properly synced to disk.
+ */
+ uint32 sequence1;
+ uint32 sequence2;
+
NTTIME mtime;
- uint32 data_offset; /* offset to record in the first (or any?)
- * hbin block
- */
- uint32 last_block; /* offset to last hbin block in file */
-
- uint32 checksum; /* Stored checksum. */
- uint32 computed_checksum; /* Our own calculation of the checksum.
- * (XOR of bytes 0x0000 - 0x01FB)
- */
-
- /* unknown data structure values */
- uint32 unknown1;
- uint32 unknown2;
- uint32 unknown3;
- uint32 unknown4;
- uint32 unknown5;
- uint32 unknown6;
- uint32 unknown7;
-} REGF_FILE;
+ uint32 major_version; /* Set to 1 in all known hives */
+ uint32 minor_version; /* Set to 3 or 5 in all known hives */
+ uint32 type; /* XXX: Unverified. Set to 0 in all known hives */
+ uint32 format; /* XXX: Unverified. Set to 1 in all known hives */
+ uint32 root_cell; /* Offset to root cell in the first (or any?) hbin block */
+ uint32 last_block; /* Offset to last hbin block in file */
+ uint32 cluster; /* XXX: Unverified. Set to 1 in all known hives */
-typedef struct
+ /* Matches hive's base file name. Stored in UTF-16LE */
+ uint8 file_name[REGFI_REGF_NAME_SIZE];
+
+ WINSEC_UUID* rm_id; /* XXX: Unverified. */
+ WINSEC_UUID* log_id; /* XXX: Unverified. */
+ WINSEC_UUID* tm_id; /* XXX: Unverified. */
+ uint32 flags; /* XXX: Unverified. */
+ uint32 guid_signature; /* XXX: Unverified. */
+
+ uint32 checksum; /* Stored checksum from file */
+ uint32 computed_checksum; /* Our own calculation of the checksum.
+ * (XOR of bytes 0x0000 - 0x01FB) */
+
+ WINSEC_UUID* thaw_tm_id; /* XXX: Unverified. */
+ WINSEC_UUID* thaw_rm_id; /* XXX: Unverified. */
+ WINSEC_UUID* thaw_log_id; /* XXX: Unverified. */
+ uint32 boot_type; /* XXX: Unverified. */
+ uint32 boot_recover; /* XXX: Unverified. */
+
+ /* This seems to include random junk. Possibly unsanitized memory left over
+ * from when header block was written. For instance, chunks of nk records
+ * can be found, though often it's all 0s. */
+ uint8 reserved1[REGFI_REGF_RESERVED1_SIZE];
+
+ /* This is likely reserved and unusued currently. (Should be all 0s.)
+ * Included here for easier access in looking for hidden data
+ * or doing research. */
+ uint8 reserved2[REGFI_REGF_RESERVED2_SIZE];
+
+} REGFI_FILE;
+
+
+/* XXX: Should move all caching (SK records, HBINs, NKs, etc) to a single
+ * structure, probably REGFI_FILE. Once key caching is in place,
+ * convert key_positions stack to store just key offsets rather than
+ * whole keys.
+ */
+typedef struct _regfi_iterator
{
- REGF_FILE* f;
+ REGFI_FILE* f;
void_stack* key_positions;
- lru_cache* sk_recs;
- REGF_NK_REC* cur_key;
+ REGFI_NK_REC* cur_key;
uint32 cur_subkey;
uint32 cur_value;
} REGFI_ITERATOR;
-typedef struct
+typedef struct _regfi_iter_position
{
- REGF_NK_REC* nk;
+ REGFI_NK_REC* nk;
uint32 cur_subkey;
/* We could store a cur_value here as well, but didn't see
* the use in it right now.
@@ -306,21 +436,32 @@ typedef struct
} REGFI_ITER_POSITION;
-/******************************************************************************/
-/* Function Declarations */
-/* Main API */
-const char* regfi_type_val2str(unsigned int val);
-int regfi_type_str2val(const char* str);
+typedef struct _regfi_buffer
+{
+ uint8* buf;
+ uint32_t len;
+} REGFI_BUFFER;
+
-char* regfi_get_sacl(SEC_DESC* sec_desc);
-char* regfi_get_dacl(SEC_DESC* sec_desc);
-char* regfi_get_owner(SEC_DESC* sec_desc);
-char* regfi_get_group(SEC_DESC* sec_desc);
+/******************************************************************************/
+/* Main iterator API */
+/******************************************************************************/
+REGFI_FILE* regfi_open(const char* filename);
+int regfi_close(REGFI_FILE* r);
-REGF_FILE* regfi_open(const char* filename);
-int regfi_close(REGF_FILE* r);
+/* regfi_get_messages: Get errors, warnings, and/or verbose information
+ * relating to processing of the given registry file.
+ *
+ * Arguments:
+ * file -- the structure for the registry file
+ *
+ * Returns:
+ * A newly allocated char* which must be free()d by the caller.
+ */
+char* regfi_get_messages(REGFI_FILE* file);
+void regfi_set_message_mask(REGFI_FILE* file, uint16 mask);
-REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh);
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh);
void regfi_iterator_free(REGFI_ITERATOR* i);
bool regfi_iterator_down(REGFI_ITERATOR* i);
bool regfi_iterator_up(REGFI_ITERATOR* i);
@@ -330,23 +471,46 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i,
const char* subkey_name);
bool regfi_iterator_walk_path(REGFI_ITERATOR* i,
const char** path);
-const REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i);
-const REGF_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i);
-const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i);
-const REGF_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
-const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i);
+const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i);
+const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i);
+
+REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i);
bool regfi_iterator_find_value(REGFI_ITERATOR* i,
const char* value_name);
-const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i);
-const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i);
-const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i);
+REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i);
+REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i);
+REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i);
+
+
+/********************************************************/
+/* Middle-layer structure loading, linking, and caching */
+/********************************************************/
+REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset,
+ bool strict);
+REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset,
+ bool strict);
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
+ uint32 num_keys, uint32 max_size,
+ bool strict);
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
+ uint32 num_values, uint32 max_size,
+ bool strict);
+
+/* These are cached so return values don't need to be freed. */
+const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset,
+ bool strict);
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset);
+
+
/************************************/
/* Low-layer data structure access */
/************************************/
-REGF_FILE* regfi_parse_regf(int fd, bool strict);
-REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset,
+REGFI_FILE* regfi_parse_regf(int fd, bool strict);
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset,
bool strict);
@@ -361,43 +525,67 @@ REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset,
* Returns:
* A newly allocated NK record structure, or NULL on failure.
*/
-REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
+REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
uint32 max_size, bool strict);
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
+ uint32 max_size, bool strict);
-/* Private Functions */
-REGF_NK_REC* regfi_rootkey(REGF_FILE* file);
-void regfi_key_free(REGF_NK_REC* nk);
-uint32 regfi_read(int fd, uint8* buf, uint32* length);
+REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
+ uint32 max_size, bool strict);
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
+ uint32 data_type, uint32 offset,
+ uint32 length, uint32 max_size,
+ bool data_in_offset, bool strict);
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
+ uint32 offset, uint32 data_length,
+ uint32 cell_length, bool strict);
-/****************/
-/* Experimental */
-/****************/
-REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict);
+REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset,
+ uint32 max_size, bool strict);
-REGF_HASH_LIST* regfi_load_hashlist(REGF_FILE* file, uint32 offset,
- uint32 num_keys, uint32 max_size,
- bool strict);
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file);
-REGF_VK_REC** regfi_load_valuelist(REGF_FILE* file, uint32 offset,
- uint32 num_values, uint32 max_size,
- bool strict);
+bool regfi_parse_cell(int fd, uint32 offset,
+ uint8* hdr, uint32 hdr_len,
+ uint32* cell_length, bool* unalloc);
-REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
- uint32 max_size, bool strict);
+char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
+ uint16* name_length,
+ uint32 max_size, bool strict);
-uint8* regfi_parse_data(REGF_FILE* file, uint32 offset,
- uint32 length, bool strict);
+/* Dispose of previously parsed records */
+void regfi_free_key(REGFI_NK_REC* nk);
+void regfi_free_value(REGFI_VK_REC* vk);
-REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict);
-range_list* regfi_parse_unalloc_cells(REGF_FILE* file);
-REGF_HBIN* regfi_lookup_hbin(REGF_FILE* file, uint32 offset);
+/************************************/
+/* Private Functions */
+/************************************/
+REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file);
+void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list);
+uint32 regfi_read(int fd, uint8* buf, uint32* length);
+
+const char* regfi_type_val2str(unsigned int val);
+int regfi_type_str2val(const char* str);
-bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
- uint32* cell_length, bool* unalloc);
+char* regfi_get_sacl(WINSEC_DESC* sec_desc);
+char* regfi_get_dacl(WINSEC_DESC* sec_desc);
+char* regfi_get_owner(WINSEC_DESC* sec_desc);
+char* regfi_get_group(WINSEC_DESC* sec_desc);
+
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists,
+ REGFI_SUBKEY_LIST** lists,
+ bool strict);
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
+ uint32 max_size, bool strict,
+ uint8 depth_left);
+void regfi_add_message(REGFI_FILE* file, uint16 msg_type,
+ const char* fmt, ...);
+REGFI_NK_REC* regfi_copy_nk(const REGFI_NK_REC* nk);
+REGFI_VK_REC* regfi_copy_vk(const REGFI_VK_REC* vk);
#endif /* _REGFI_H */
diff --git a/include/smb_deps.h b/include/smb_deps.h
index 9602e94..cae3f8b 100644
--- a/include/smb_deps.h
+++ b/include/smb_deps.h
@@ -3,7 +3,7 @@
* depends upon, from the Samba Subversion tree. See:
* http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
*
- * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 2005,2009 Timothy D. Morgan
* Copyright (C) 1992-2005 Samba development team
* (see individual files under Subversion for details.)
*
@@ -20,11 +20,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: smb_deps.h 111 2008-05-01 04:06:22Z tim $
+ * $Id: smb_deps.h 134 2009-01-16 18:36:04Z tim $
*/
+#ifndef _SMB_DEPS_H
+#define _SMB_DEPS_H
+
#include <stdlib.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
@@ -42,53 +46,21 @@ void* zcalloc(size_t size, unsigned int count);
/* From includes.h */
-#define uint8 unsigned char
-#define int16 short
-#define uint16 unsigned short
-#define int32 int
-#define uint32 unsigned int
-
-#define SMB_STRUCT_STAT struct stat
-#define QSORT_CAST (int (*)(const void *, const void *))
+#define uint8 uint8_t
+#define int16 int8_t
+#define uint16 uint16_t
+#define int32 int32_t
+#define uint32 uint32_t
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
extern int DEBUGLEVEL;
-#define DLIST_ADD(list, p) \
-{ \
- if (!(list)) { \
- (list) = (p); \
- (p)->next = (p)->prev = NULL; \
- } else { \
- (list)->prev = (p); \
- (p)->next = (list); \
- (p)->prev = NULL; \
- (list) = (p); \
- }\
-}
-
/* End of stuff from includes.h */
/* From smb.h */
-#define MAXSUBAUTHS 15
-
-typedef struct sid_info
-{
- uint8 sid_rev_num; /**< SID revision number */
- uint8 num_auths; /**< Number of sub-authorities */
- uint8 id_auth[6]; /**< Identifier Authority */
- /*
- * Pointer to sub-authorities.
- *
- * @note The values in these uint32's are in *native* byteorder, not
- * neccessarily little-endian...... JRA.
- */
- uint32 sub_auths[MAXSUBAUTHS];
-} DOM_SID;
-
typedef struct nttime_info
{
uint32 low;
@@ -97,51 +69,6 @@ typedef struct nttime_info
/* End of stuff from smb.h */
-/* From smb_macros.h */
-
-#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
-#define SMB_MALLOC_P(type) (type *)malloc_(sizeof(type))
-#define TALLOC_ARRAY(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
-#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
-#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
-
-/* End of stuff from smb_macros.h */
-
-/* From ntdomain.h */
-
-struct uuid {
- uint32 time_low;
- uint16 time_mid;
- uint16 time_hi_and_version;
- uint8 clock_seq[2];
- uint8 node[6];
-};
-
-typedef struct _prs_struct {
- bool io; /* parsing in or out of data stream */
- /*
- * If the (incoming) data is big-endian. On output we are
- * always little-endian.
- */
- bool bigendian_data;
- uint8 align; /* data alignment */
- bool is_dynamic; /* Do we own this memory or not ? */
- uint32 data_offset; /* Current working offset into data. */
- uint32 buffer_size; /* Current allocated size of the buffer. */
- uint32 grow_size; /* size requested via prs_grow() calls */
- char *data_p; /* The buffer itself. */
- void *mem_ctx; /* When unmarshalling, use this.... */
-} prs_struct;
-
-#define MARSHALL 0
-#define UNMARSHALL 1
-
-#define RPC_LITTLE_ENDIAN 0
-#define RPC_PARSE_ALIGN 4
-
-/* End of stuff from ntdomain.h */
-
-
/* From lib/time.c */
#define CHAR_BIT 8
@@ -155,161 +82,4 @@ time_t nt_time_to_unix(const NTTIME* nt);
/* End of stuff from lib/time.c */
-/* From rpc_dce.h */
-
-#define MAX_PDU_FRAG_LEN 0x10b8 /* this is what w2k sets */
-
-/* End of stuff from rpc_dce.h */
-
-/* From parse_prs.h */
-
-bool prs_grow(prs_struct *ps, uint32 extra_space);
-bool prs_align(prs_struct *ps);
-bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io);
-char *prs_mem_get(prs_struct *ps, uint32 extra_size);
-bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32);
-bool prs_uint32s(const char *name, prs_struct *ps,
- int depth, uint32 *data32s, int len);
-bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16);
-bool prs_uint16_pre(const char *name, prs_struct *ps, int depth,
- uint16 *data16, uint32 *offset);
-bool prs_uint16_post(const char *name, prs_struct *ps, int depth,
- uint16 *data16, uint32 ptr_uint16, uint32 start_offset);
-bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8);
-bool prs_uint8s(const char *name, prs_struct *ps, int depth,
- uint8* data8s, int len);
-bool prs_set_offset(prs_struct *ps, uint32 offset);
-
-/* End of stuff from parse_prs.h */
-
-
-/* From rpc_secdesc.h */
-
-typedef struct security_info_info
-{
- uint32 mask;
-
-} SEC_ACCESS;
-
-typedef struct security_ace_info
-{
- uint8 type; /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
- uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
- uint16 size;
-
- SEC_ACCESS info;
-
- /* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
- uint32 obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
- struct uuid obj_guid; /* object GUID */
- struct uuid inh_guid; /* inherited object GUID */
- /* eof object stuff */
-
- DOM_SID trustee;
-
-} SEC_ACE;
-
-typedef struct security_acl_info
-{
- uint16 revision; /* 0x0003 */
- uint16 size; /* size in bytes of the entire ACL structure */
- uint32 num_aces; /* number of Access Control Entries */
-
- SEC_ACE *ace;
-
-} SEC_ACL;
-
-typedef struct security_descriptor_info
-{
- uint16 revision; /* 0x0001 */
- uint16 type; /* SEC_DESC_xxxx flags */
-
- uint32 off_owner_sid; /* offset to owner sid */
- uint32 off_grp_sid ; /* offset to group sid */
- uint32 off_sacl ; /* offset to system list of permissions */
- uint32 off_dacl ; /* offset to list of permissions */
-
- SEC_ACL *dacl; /* user ACL */
- SEC_ACL *sacl; /* system ACL */
- DOM_SID *owner_sid;
- DOM_SID *grp_sid;
-
-} SEC_DESC;
-
-/* End of stuff from rpc_secdesc.h */
-
-
-/* From pstring.h */
-
-#define FSTRING_LEN 256
-typedef char fstring[FSTRING_LEN];
-
-/* End of stuff from pstring.h */
-
-
-/* From rpc_secdes.h */
-
-#define SEC_DESC_DACL_PRESENT 0x0004
-#define SEC_DESC_SACL_PRESENT 0x0010
-#define SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
- /* thanks for Jim McDonough <jmcd at us.ibm.com> */
-#define SEC_ACE_OBJECT_PRESENT 0x00000001
-#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
-
-#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT 0x5
-#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT 0x6
-#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT 0x7
-#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT 0x8
-
-/* End of stuff from rpc_secdes.h */
-
-/* From rpc_parse/parse_misc.c */
-
-bool smb_io_uuid(const char *desc, struct uuid *uuid,
- prs_struct *ps, int depth);
-bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth);
-bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth);
-
-/* End of stuff from rpc_parse/parse_misc.c */
-
-/* From lib/util_sid.c */
-
-size_t sid_size(const DOM_SID *sid);
-int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2);
-int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2);
-bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2);
-
-/* End of stuff from lib/util_sid.c */
-
-/* From lib/secace.c */
-
-bool sec_ace_object(uint8 type);
-
-/* End of stuff from lib/secace.c */
-
-/* From rpc_parse/parse_sec.c */
-
-bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth);
-bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth);
-bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth);
-bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth);
-
-/* End of stuff from rpc_parse/parse_sec.c */
-
-/* From lib/secace.c */
-
-bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2);
-
-/* End of stuff from lib/secace.c */
-
-/* From lib/secacl.c */
-
-bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2);
-
-/* End of stuff from lib/secacl.c */
-
-/* From lib/secdesc.c */
-
-bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2);
-
-/* End of stuff from lib/secdesc.c */
+#endif /* _SMB_DEPS_H */
diff --git a/include/talloc.h b/include/talloc.h
new file mode 100644
index 0000000..f34065c
--- /dev/null
+++ b/include/talloc.h
@@ -0,0 +1,162 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions
+
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Stefan Metzmacher 2006
+ Copyright (C) Timothy D. Morgan 2009
+
+ ** NOTE! The following LGPL license applies to the talloc library.
+ ** This does NOT imply that all of reglookup is released under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+/* $Id: talloc.h 147 2009-02-22 19:31:52Z tim $ */
+
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+#include "smb_deps.h" /* MAX macro */
+
+/*
+ this uses a little trick to allow __LINE__ to be stringified
+*/
+#ifndef __location__
+#define __TALLOC_STRING_LINE1__(s) #s
+#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s)
+#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
+#endif
+
+#ifndef TALLOC_DEPRECATED
+#define TALLOC_DEPRECATED 0
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns. a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+#define talloc_set_destructor(ptr, function) \
+ _talloc_set_destructor((ptr), (int (*)(void *))(function))
+#define _TALLOC_TYPEOF(ptr) void *
+#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
+
+#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
+#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
+
+/* useful macros for creating type checked pointers */
+#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
+#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
+
+#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
+
+#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
+
+#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
+#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
+
+#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
+#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
+
+#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
+
+#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
+#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
+
+#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
+
+/* The following definitions come from talloc.c */
+void *_talloc(const void *context, size_t size);
+void *talloc_pool(const void *context, size_t size);
+void _talloc_set_destructor(const void *ptr, int (*destructor)(void *));
+int talloc_increase_ref_count(const void *ptr);
+size_t talloc_reference_count(const void *ptr);
+void *_talloc_reference(const void *context, const void *ptr);
+int talloc_unlink(const void *context, void *ptr);
+const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+void talloc_set_name_const(const void *ptr, const char *name);
+void *talloc_named(const void *context, size_t size,
+ const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+void *talloc_named_const(const void *context, size_t size, const char *name);
+const char *talloc_get_name(const void *ptr);
+void *talloc_check_name(const void *ptr, const char *name);
+void *talloc_parent(const void *ptr);
+const char *talloc_parent_name(const void *ptr);
+void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+int talloc_free(void *ptr);
+void talloc_free_children(void *ptr);
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
+void *_talloc_steal(const void *new_ctx, const void *ptr);
+void *_talloc_move(const void *new_ctx, const void *pptr);
+size_t talloc_total_size(const void *ptr);
+size_t talloc_total_blocks(const void *ptr);
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+ void (*callback)(const void *ptr,
+ int depth, int max_depth,
+ int is_ref,
+ void *private_data),
+ void *private_data);
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
+void talloc_report_full(const void *ptr, FILE *f);
+void talloc_report(const void *ptr, FILE *f);
+void talloc_enable_null_tracking(void);
+void talloc_disable_null_tracking(void);
+void talloc_enable_leak_report(void);
+void talloc_enable_leak_report_full(void);
+void *_talloc_zero(const void *ctx, size_t size, const char *name);
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
+void *talloc_autofree_context(void);
+size_t talloc_get_size(const void *ctx);
+void *talloc_find_parent_byname(const void *ctx, const char *name);
+void talloc_show_parents(const void *context, FILE *file);
+int talloc_is_parent(const void *context, const void *ptr);
+
+char *talloc_strdup(const void *t, const char *p);
+char *talloc_strdup_append(char *s, const char *a);
+char *talloc_strdup_append_buffer(char *s, const char *a);
+
+char *talloc_strndup(const void *t, const char *p, size_t n);
+char *talloc_strndup_append(char *s, const char *a, size_t n);
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+#endif
diff --git a/include/void_stack.h b/include/void_stack.h
index 7d5f415..42fefb6 100644
--- a/include/void_stack.h
+++ b/include/void_stack.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005,2007 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009 Timothy D. Morgan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +14,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: void_stack.h 111 2008-05-01 04:06:22Z tim $
+ * $Id: void_stack.h 150 2009-03-02 02:17:46Z tim $
*/
+#ifndef _VOID_STACK_H
+#define _VOID_STACK_H
+
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
-
-#ifndef _VOID_STACK_H
-#define _VOID_STACK_H
+#include "talloc.h"
typedef struct _void_stack
{
diff --git a/include/winsec.h b/include/winsec.h
new file mode 100644
index 0000000..1c2db4a
--- /dev/null
+++ b/include/winsec.h
@@ -0,0 +1,171 @@
+/*
+ * This file contains refactored Samba code used to interpret Windows
+ * Security Descriptors. See:
+ * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Revisions have been made based on information provided by Microsoft
+ * at:
+ * http://msdn.microsoft.com/en-us/library/cc230366(PROT.10).aspx
+ *
+ * Copyright (C) 2005,2009 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: winsec.h 148 2009-02-22 23:22:59Z tim $
+ */
+
+#ifndef _WINSEC_H
+#define _WINSEC_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "smb_deps.h"
+#include "talloc.h"
+
+
+/* This is the maximum number of subauths in a SID, as defined here:
+ * http://msdn.microsoft.com/en-us/library/cc230371(PROT.10).aspx
+ */
+#define WINSEC_MAX_SUBAUTHS 15
+
+#define WINSEC_DESC_HEADER_SIZE (5 * sizeof(uint32_t))
+#define WINSEC_ACL_HEADER_SIZE (2 * sizeof(uint32_t))
+#define WINSEC_ACE_MIN_SIZE 16
+
+/* XXX: Fill in definitions of other flags */
+/* This self relative flag means offsets contained in the descriptor are relative
+ * to the descriptor's offset. This had better be true in the registry.
+ */
+#define WINSEC_DESC_SELF_RELATIVE 0x8000
+#define WINSEC_DESC_SACL_PRESENT 0x0010
+#define WINSEC_DESC_DACL_PRESENT 0x0004
+
+#define WINSEC_ACE_OBJECT_PRESENT 0x00000001
+#define WINSEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
+#define WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT 0x5
+#define WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT 0x6
+#define WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT 0x7
+#define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT 0x8
+
+
+typedef struct _winsec_uuid
+{
+ uint32 time_low;
+ uint16 time_mid;
+ uint16 time_hi_and_version;
+ uint8 clock_seq[2];
+ uint8 node[6];
+} WINSEC_UUID;
+
+
+typedef struct _winsec_sid
+{
+ uint8_t sid_rev_num; /* SID revision number */
+ uint8_t num_auths; /* Number of sub-authorities */
+ uint8_t id_auth[6]; /* Identifier Authority */
+ /*
+ * Pointer to sub-authorities.
+ *
+ * @note The values in these uint32_t's are in *native* byteorder, not
+ * neccessarily little-endian...... JRA.
+ */
+ /* XXX: Make this dynamically allocated? */
+ uint32_t sub_auths[WINSEC_MAX_SUBAUTHS];
+} WINSEC_DOM_SID;
+
+
+typedef struct _winsec_ace
+{
+ uint8_t type; /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+ uint8_t flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+ uint16_t size;
+ uint32_t access_mask;
+
+ /* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+ uint32_t obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+ WINSEC_UUID* obj_guid; /* object GUID */
+ WINSEC_UUID* inh_guid; /* inherited object GUID */
+ /* eof object stuff */
+
+ WINSEC_DOM_SID* trustee;
+
+} WINSEC_ACE;
+
+typedef struct _winsec_acl
+{
+ uint16_t revision; /* 0x0003 */
+ uint16_t size; /* size in bytes of the entire ACL structure */
+ uint32_t num_aces; /* number of Access Control Entries */
+
+ WINSEC_ACE** aces;
+
+} WINSEC_ACL;
+
+typedef struct _winsec_desc
+{
+ uint8_t revision; /* 0x01 */
+ uint8_t sbz1; /* "If the Control field has the RM flag set,
+ * then this field contains the resource
+ * manager (RM) control value. ... Otherwise,
+ * this field is reserved and MUST be set to
+ * zero." -- Microsoft. See reference above.
+ */
+ uint16_t control; /* WINSEC_DESC_* flags */
+
+ uint32_t off_owner_sid; /* offset to owner sid */
+ uint32_t off_grp_sid ; /* offset to group sid */
+ uint32_t off_sacl ; /* offset to system list of permissions */
+ uint32_t off_dacl ; /* offset to list of permissions */
+
+ WINSEC_DOM_SID* owner_sid;
+ WINSEC_DOM_SID* grp_sid;
+ WINSEC_ACL* sacl; /* system ACL */
+ WINSEC_ACL* dacl; /* user ACL */
+
+} WINSEC_DESC;
+
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
+void winsec_free_descriptor(WINSEC_DESC* desc);
+
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len);
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len);
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len);
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len);
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len);
+
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
+bool winsec_ace_object(uint8_t type);
+
+#endif /* _WINSEC_H */
diff --git a/lib/Makefile b/lib/Makefile
index c406e16..c761c61 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -2,7 +2,7 @@
################################################################################
-FILES=regfi.o smb_deps.o void_stack.o range_list.o lru_cache.o
+FILES=regfi.o smb_deps.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
all: $(FILES)
@@ -12,6 +12,9 @@ regfi.o: regfi.c
smb_deps.o: smb_deps.c
$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ smb_deps.c
+winsec.o: winsec.c
+ $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c
+
void_stack.o: void_stack.c
$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ void_stack.c
@@ -21,5 +24,8 @@ range_list.o: range_list.c
lru_cache.o: lru_cache.c
$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ lru_cache.c
+talloc.o: talloc.c
+ $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ talloc.c
+
clean:
rm -f $(FILES)
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index 987717d..74f5f08 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,10 +14,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: lru_cache.c 122 2008-08-09 20:24:01Z tim $
+ * $Id: lru_cache.c 147 2009-02-22 19:31:52Z tim $
*/
-#include "../include/lru_cache.h"
+#include "lru_cache.h"
#define LRU_CACHE_DEBUG 0
@@ -96,16 +96,24 @@ static void lru_cache_print(lru_cache* ht)
}
#endif
-lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
+
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret)
+{
+ return lru_cache_create_ctx(NULL, max_keys, secret, false);
+}
+
+
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
+ uint32_t secret, bool talloc_data)
{
lru_cache* ret_val;
- ret_val = (lru_cache*)malloc(sizeof(lru_cache));
+ ret_val = talloc(talloc_ctx, lru_cache);
if(ret_val == NULL)
return NULL;
if(max_keys == 0)
- ret_val->num_buckets = 2048;
+ ret_val->num_buckets = 1024;
else
{
ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys);
@@ -113,12 +121,11 @@ lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
ret_val->num_buckets = 1;
}
- ret_val->table
- = (lru_cache_element**)malloc(sizeof(lru_cache_element*)
- * ret_val->num_buckets);
+ ret_val->table = talloc_array(ret_val,
+ lru_cache_element*, ret_val->num_buckets);
if(ret_val->table == NULL)
{
- free(ret_val);
+ talloc_free(ret_val);
return NULL;
}
@@ -126,7 +133,7 @@ lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
ret_val->newest = NULL;
ret_val->max_keys = max_keys;
ret_val->secret = secret;
- ret_val->free_data = free_data;
+ ret_val->talloc_data = talloc_data;
ret_val->num_keys = 0;
memset(ret_val->table, 0, ret_val->num_buckets*sizeof(lru_cache_element*));
@@ -136,22 +143,8 @@ lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data)
void lru_cache_destroy(lru_cache* ht)
{
- lru_cache_element* cur;
- lru_cache_element* last = NULL;
-
- for(cur=ht->oldest; cur != NULL; last=cur,cur=cur->newer)
- {
- if(last != NULL)
- {
- if(ht->free_data)
- free(last->data);
- free(last->index);
- free(last);
- }
- }
- free(ht->table);
ht->secret = 0;
- free(ht);
+ talloc_free(ht);
}
@@ -178,8 +171,8 @@ bool lru_cache_update(lru_cache* ht, const void* index,
* We also need to reposition the element to the newest position,
* so remove it from the list for now.
*/
- if(ht->free_data)
- free(e->data);
+ if(ht->talloc_data)
+ talloc_free(e->data);
if(e->newer == NULL)
ht->newest = e->older;
@@ -225,28 +218,29 @@ bool lru_cache_update(lru_cache* ht, const void* index,
last->next = e->next;
e->next = NULL;
- if(ht->free_data)
- free(e->data);
+ if(ht->talloc_data)
+ talloc_free(e->data);
- tmp_index = realloc(e->index, index_len);
+ tmp_index = talloc_realloc_size(e, e->index, index_len);
if(tmp_index == NULL)
{
- free(e->index);
- free(e);
+ talloc_free(e);
return false;
}
+ else
+ e->index = tmp_index;
}
else
{ /* Brand new element because we have room to spare. */
- e = (lru_cache_element*)malloc(sizeof(lru_cache_element));
+ e = talloc(ht->table, lru_cache_element);
if(e == NULL)
return false;
- e->index = malloc(index_len);
+ e->index = talloc_size(e, index_len);
if(e->index == NULL)
{
- free(e);
+ talloc_free(e);
return false;
}
@@ -261,6 +255,8 @@ bool lru_cache_update(lru_cache* ht, const void* index,
ht->table[hash] = e;
}
e->data = data;
+ if(ht->talloc_data)
+ talloc_steal(e, e->data);
/* Finally, let's insert the element to the newest position in the LRU list.*/
if(ht->newest != NULL)
@@ -333,9 +329,6 @@ bool lru_cache_remove(lru_cache* ht, const void* index,
if(cur == NULL)
return false;
- if(ht->free_data)
- free(cur->data);
-
/* Detach from list */
if(cur->newer == NULL)
ht->newest = cur->older;
@@ -353,8 +346,7 @@ bool lru_cache_remove(lru_cache* ht, const void* index,
else
last->next = cur->next;
- free(cur->index);
- free(cur);
+ talloc_free(cur);
/* Removing entry, decrement counters. */
ht->num_keys--;
diff --git a/lib/range_list.c b/lib/range_list.c
index e874cff..7e80a72 100644
--- a/lib/range_list.c
+++ b/lib/range_list.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,11 +14,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: range_list.c 122 2008-08-09 20:24:01Z tim $
+ * $Id: range_list.c 150 2009-03-02 02:17:46Z tim $
*/
-#include <math.h>
-#include "../include/range_list.h"
+#include "range_list.h"
/*******************/
@@ -26,7 +25,7 @@
/*******************/
#define RANGE_LIST_ALLOC_SIZE 256
-#if 0
+#if 0 /* For debugging */
#include <stdio.h>
static void range_list_print(const range_list* rl)
{
@@ -52,9 +51,8 @@ static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t
if(rl->size == rl->elem_alloced)
{
- tmp = (range_list_element**)realloc(rl->elements,
- (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE)
- * sizeof(range_list_element*));
+ tmp = talloc_realloc(rl, rl->elements, range_list_element*,
+ (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE));
if(tmp == NULL)
return false;
rl->elements = tmp;
@@ -124,16 +122,14 @@ range_list* range_list_new()
{
range_list* rl;
- rl = (range_list*)malloc(sizeof(range_list));
+ rl = talloc(NULL, range_list);
if(rl == NULL)
return NULL;
- rl->elements = (range_list_element**)malloc(sizeof(range_list_element*)
- * RANGE_LIST_ALLOC_SIZE);
-
+ rl->elements = talloc_array(rl, range_list_element*, RANGE_LIST_ALLOC_SIZE);
if(rl->elements == NULL)
{
- free(rl);
+ talloc_free(rl);
return NULL;
}
@@ -146,16 +142,8 @@ range_list* range_list_new()
void range_list_free(range_list* rl)
{
- uint32_t i;
-
- if(rl == NULL)
- return;
-
- for(i=0; i < rl->size; i++)
- free(rl->elements[i]);
-
- free(rl->elements);
- free(rl);
+ if(rl != NULL)
+ talloc_free(rl);
}
@@ -200,7 +188,7 @@ bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data
&& (offset+length > rl->elements[insert_index+1]->offset))
return false;
- elem = (range_list_element*)malloc(sizeof(range_list_element));
+ elem = talloc(rl->elements, range_list_element);
if(elem == NULL)
return false;
elem->offset = offset;
@@ -209,7 +197,7 @@ bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data
if(!range_list_insert(rl, elem, insert_index))
{
- free(elem);
+ talloc_free(elem);
return false;
}
@@ -225,7 +213,7 @@ bool range_list_remove(range_list* rl, uint32_t index)
if(index >= rl->size)
return false;
- free(rl->elements[index]);
+ talloc_free(rl->elements[index]);
/* Do the shuffle to the left. */
for(i=index; i < (rl->size-1); i++)
@@ -236,9 +224,8 @@ bool range_list_remove(range_list* rl, uint32_t index)
/* Try to keep memory usage down */
if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE < rl->elem_alloced)
{
- tmp = (range_list_element**)realloc(rl->elements,
- (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE)
- * sizeof(range_list_element*));
+ tmp = talloc_realloc(rl, rl->elements, range_list_element*,
+ (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE));
if(tmp != NULL)
{
rl->elements = tmp;
@@ -264,17 +251,20 @@ int32_t range_list_find(const range_list* rl, uint32_t offset)
uint32_t prev_idx;
range_list_element* elem;
+ if(rl->size == 0)
+ return -1;
+
if((offset < rl->elements[0]->offset)
|| (offset > rl->elements[rl->size-1]->offset
+ rl->elements[rl->size-1]->length))
- return -1;
+ return -2;
prev_idx = range_list_find_previous(rl, offset);
elem = rl->elements[prev_idx];
if(offset < elem->offset+elem->length)
return prev_idx;
- return -2;
+ return -3;
}
@@ -301,7 +291,7 @@ bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
|| (offset >= cur_elem->offset+cur_elem->length))
return false;
- new_elem = (range_list_element*)malloc(sizeof(range_list_element));
+ new_elem = talloc(rl->elements, range_list_element);
if(new_elem == NULL)
return false;
@@ -311,7 +301,7 @@ bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
if(!range_list_insert(rl, new_elem, index+1))
{
- free(new_elem);
+ talloc_free(new_elem);
return false;
}
diff --git a/lib/regfi.c b/lib/regfi.c
index 2736c84..7a6da3c 100644
--- a/lib/regfi.c
+++ b/lib/regfi.c
@@ -2,10 +2,9 @@
* Branched from Samba project Subversion repository, version #7470:
* http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
*
- * Unix SMB/CIFS implementation.
- * Windows NT registry I/O library
+ * Windows NT (and later) registry parsing library
*
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2009 Timothy D. Morgan
* Copyright (C) 2005 Gerald (Jerry) Carter
*
* This program is free software; you can redistribute it and/or modify
@@ -21,10 +20,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: regfi.c 116 2008-08-03 19:34:27Z tim $
+ * $Id: regfi.c 155 2009-06-03 23:45:58Z tim $
*/
-#include "../include/regfi.h"
+#include "regfi.h"
/* Registry types mapping */
@@ -34,6 +33,74 @@ static const char* regfi_type_names[] =
"MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_add_message(REGFI_FILE* file, uint16 msg_type, const char* fmt, ...)
+{
+ /* XXX: This function is not particularly efficient,
+ * but then it is mostly used during errors.
+ */
+ uint32 buf_size, buf_used;
+ char* new_msg;
+ va_list args;
+
+ if((file->msg_mask & msg_type) != 0)
+ {
+ if(file->last_message == NULL)
+ buf_used = 0;
+ else
+ buf_used = strlen(file->last_message);
+
+ buf_size = buf_used+strlen(fmt)+160;
+ new_msg = realloc(file->last_message, buf_size);
+ if(new_msg == NULL)
+ /* XXX: should we report this? */
+ return;
+
+ switch (msg_type)
+ {
+ case REGFI_MSG_INFO:
+ strcpy(new_msg+buf_used, "INFO: ");
+ buf_used += 6;
+ break;
+ case REGFI_MSG_WARN:
+ strcpy(new_msg+buf_used, "WARN: ");
+ buf_used += 6;
+ break;
+ case REGFI_MSG_ERROR:
+ strcpy(new_msg+buf_used, "ERROR: ");
+ buf_used += 7;
+ break;
+ }
+
+ va_start(args, fmt);
+ vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
+ va_end(args);
+ strncat(new_msg, "\n", buf_size-1);
+
+ file->last_message = new_msg;
+ }
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+char* regfi_get_messages(REGFI_FILE* file)
+{
+ char* ret_val = file->last_message;
+ file->last_message = NULL;
+
+ return ret_val;
+}
+
+
+void regfi_set_message_mask(REGFI_FILE* file, uint16 mask)
+{
+ file->msg_mask = mask;
+}
+
+
/* Returns NULL on error */
const char* regfi_type_val2str(unsigned int val)
{
@@ -66,7 +133,7 @@ int regfi_type_str2val(const char* str)
}
-/* Security descriptor parsing functions */
+/* Security descriptor formatting functions */
const char* regfi_ace_type2str(uint8 type)
{
@@ -133,15 +200,6 @@ char* regfi_ace_flags2str(uint8 flags)
if(fo != ret_val)
fo[-1] = '\0';
- /* XXX: what was this old VI flag for??
- XXX: Is this check right? 0xF == 1|2|4|8, which makes it redundant...
- if (flags == 0xF) {
- if (some) strcat(flg_output, " ");
- some = 1;
- strcat(flg_output, "VI");
- }
- */
-
return ret_val;
}
@@ -225,9 +283,9 @@ char* regfi_ace_perms2str(uint32 perms)
}
-char* regfi_sid2str(DOM_SID* sid)
+char* regfi_sid2str(WINSEC_DOM_SID* sid)
{
- uint32 i, size = MAXSUBAUTHS*11 + 24;
+ uint32 i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
uint32 left = size;
uint8 comps = sid->num_auths;
char* ret_val = malloc(size);
@@ -235,8 +293,8 @@ char* regfi_sid2str(DOM_SID* sid)
if(ret_val == NULL)
return NULL;
- if(comps > MAXSUBAUTHS)
- comps = MAXSUBAUTHS;
+ if(comps > WINSEC_MAX_SUBAUTHS)
+ comps = WINSEC_MAX_SUBAUTHS;
left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
@@ -247,7 +305,7 @@ char* regfi_sid2str(DOM_SID* sid)
}
-char* regfi_get_acl(SEC_ACL* acl)
+char* regfi_get_acl(WINSEC_ACL* acl)
{
uint32 i, extra, size = 0;
const char* type_str;
@@ -262,32 +320,33 @@ char* regfi_get_acl(SEC_ACL* acl)
for (i = 0; i < acl->num_aces && !failed; i++)
{
- sid_str = regfi_sid2str(&acl->ace[i].trustee);
- type_str = regfi_ace_type2str(acl->ace[i].type);
- perms_str = regfi_ace_perms2str(acl->ace[i].info.mask);
- flags_str = regfi_ace_flags2str(acl->ace[i].flags);
+ sid_str = regfi_sid2str(acl->aces[i]->trustee);
+ type_str = regfi_ace_type2str(acl->aces[i]->type);
+ perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
+ flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
if(flags_str != NULL && perms_str != NULL
&& type_str != NULL && sid_str != NULL)
{
/* XXX: this is slow */
extra = strlen(sid_str) + strlen(type_str)
- + strlen(perms_str) + strlen(flags_str)+5;
+ + strlen(perms_str) + strlen(flags_str) + 5;
tmp_val = realloc(ret_val, size+extra);
if(tmp_val == NULL)
{
free(ret_val);
+ ret_val = NULL;
failed = true;
}
else
{
ret_val = tmp_val;
- size += snprintf(ret_val+size, extra, "%s%s%c%s%c%s%c%s",
- ace_delim,sid_str,
- field_delim,type_str,
- field_delim,perms_str,
- field_delim,flags_str);
+ size += sprintf(ret_val+size, "%s%s%c%s%c%s%c%s",
+ ace_delim,sid_str,
+ field_delim,type_str,
+ field_delim,perms_str,
+ field_delim,flags_str);
ace_delim = "|";
}
}
@@ -306,7 +365,7 @@ char* regfi_get_acl(SEC_ACL* acl)
}
-char* regfi_get_sacl(SEC_DESC *sec_desc)
+char* regfi_get_sacl(WINSEC_DESC *sec_desc)
{
if (sec_desc->sacl)
return regfi_get_acl(sec_desc->sacl);
@@ -315,7 +374,7 @@ char* regfi_get_sacl(SEC_DESC *sec_desc)
}
-char* regfi_get_dacl(SEC_DESC *sec_desc)
+char* regfi_get_dacl(WINSEC_DESC *sec_desc)
{
if (sec_desc->dacl)
return regfi_get_acl(sec_desc->dacl);
@@ -324,13 +383,13 @@ char* regfi_get_dacl(SEC_DESC *sec_desc)
}
-char* regfi_get_owner(SEC_DESC *sec_desc)
+char* regfi_get_owner(WINSEC_DESC *sec_desc)
{
return regfi_sid2str(sec_desc->owner_sid);
}
-char* regfi_get_group(SEC_DESC *sec_desc)
+char* regfi_get_group(WINSEC_DESC *sec_desc)
{
return regfi_sid2str(sec_desc->grp_sid);
}
@@ -415,13 +474,13 @@ bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
* Given an offset and an hbin, is the offset within that hbin?
* The offset is a virtual file offset.
*******************************************************************/
-static bool regfi_offset_in_hbin(REGF_HBIN* hbin, uint32 offset)
+static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32 voffset)
{
if(!hbin)
return false;
- if((offset > hbin->first_hbin_off)
- && (offset < (hbin->first_hbin_off + hbin->block_size)))
+ if((voffset > hbin->first_hbin_off)
+ && (voffset < (hbin->first_hbin_off + hbin->block_size)))
return true;
return false;
@@ -430,141 +489,296 @@ static bool regfi_offset_in_hbin(REGF_HBIN* hbin, uint32 offset)
/*******************************************************************
- * Given a virtual offset, and receive the correpsonding HBIN
+ * Provide a virtual offset and receive the correpsonding HBIN
* block for it. NULL if one doesn't exist.
*******************************************************************/
-REGF_HBIN* regfi_lookup_hbin(REGF_FILE* file, uint32 offset)
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset)
{
- return (REGF_HBIN*)range_list_find_data(file->hbins, offset+REGF_BLOCKSIZE);
+ return (const REGFI_HBIN*)range_list_find_data(file->hbins,
+ voffset+REGFI_REGF_SIZE);
}
-/*******************************************************************
- *******************************************************************/
-REGF_HASH_LIST* regfi_load_hashlist(REGF_FILE* file, uint32 offset,
- uint32 num_keys, uint32 max_size,
- bool strict)
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
+ uint32 num_keys, uint32 max_size,
+ bool strict)
{
- REGF_HASH_LIST* ret_val;
- uint32 i, cell_length, length;
- uint8* hashes;
- uint8 buf[REGFI_HASH_LIST_MIN_LENGTH];
- bool unalloc;
+ REGFI_SUBKEY_LIST* ret_val;
- if(!regfi_parse_cell(file->fd, offset, buf, REGFI_HASH_LIST_MIN_LENGTH,
- &cell_length, &unalloc))
+ ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict,
+ REGFI_MAX_SUBKEY_DEPTH);
+ if(ret_val == NULL)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Failed to load subkey list at"
+ " offset 0x%.8X.", offset);
+ return NULL;
+ }
+
+ if(num_keys != ret_val->num_keys)
+ {
+ /* Not sure which should be authoritative, the number from the
+ * NK record, or the number in the subkey list. Just emit a warning for
+ * now if they don't match.
+ */
+ regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent"
+ " (%d) did not match number found in subkey list/tree (%d)"
+ " while parsing subkey list/tree at offset 0x%.8X.",
+ num_keys, ret_val->num_keys, offset);
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
+ uint32 max_size, bool strict,
+ uint8 depth_left)
+{
+ REGFI_SUBKEY_LIST* ret_val;
+ REGFI_SUBKEY_LIST** sublists;
+ const REGFI_HBIN* sublist_hbin;
+ uint32 i, num_sublists, off, max_length;
+
+ if(depth_left == 0)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached"
+ " while parsing subkey list/tree at offset 0x%.8X.",
+ offset);
return NULL;
+ }
- ret_val = (REGF_HASH_LIST*)zalloc(sizeof(REGF_HASH_LIST));
+ ret_val = regfi_parse_subkeylist(file, offset, max_size, strict);
if(ret_val == NULL)
return NULL;
- ret_val->offset = offset;
+ if(ret_val->recursive_type)
+ {
+ num_sublists = ret_val->num_children;
+ sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists
+ * sizeof(REGFI_SUBKEY_LIST*));
+ for(i=0; i < num_sublists; i++)
+ {
+ off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
+ sublist_hbin = regfi_lookup_hbin(file, ret_val->elements[i].offset);
+ if(sublist_hbin == NULL)
+ sublists[i] = NULL;
+ else
+ {
+ max_length = sublist_hbin->block_size + sublist_hbin->file_off - off;
+ sublists[i] = regfi_load_subkeylist_aux(file, off, max_length, strict,
+ depth_left-1);
+ }
+ }
+ talloc_free(ret_val);
+
+ return regfi_merge_subkeylists(num_sublists, sublists, strict);
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
+ uint32 max_size, bool strict)
+{
+ REGFI_SUBKEY_LIST* ret_val;
+ uint32 i, cell_length, length, elem_size, read_len;
+ uint8* elements = NULL;
+ uint8 buf[REGFI_SUBKEY_LIST_MIN_LEN];
+ bool unalloc;
+ bool recursive_type;
+
+ if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN,
+ &cell_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while "
+ "parsing subkey-list at offset 0x%.8X.", offset);
+ return NULL;
+ }
+
if(cell_length > max_size)
{
+ regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size"
+ " while parsing subkey-list at offset 0x%.8X.", offset);
if(strict)
return NULL;
cell_length = max_size & 0xFFFFFFF8;
}
- ret_val->cell_size = cell_length;
- if((buf[0] != 'l' || buf[1] != 'f') && (buf[0] != 'l' || buf[1] != 'h')
- && (buf[0] != 'r' || buf[1] != 'i'))
+ recursive_type = false;
+ if(buf[0] == 'r' && buf[1] == 'i')
{
- /*printf("DEBUG: lf->header=%c%c\n", buf[0], buf[1]);*/
- free(ret_val);
- return NULL;
+ recursive_type = true;
+ elem_size = sizeof(uint32);
}
-
- if(buf[0] == 'r' && buf[1] == 'i')
+ else if(buf[0] == 'l' && buf[1] == 'i')
+ elem_size = sizeof(uint32);
+ else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
+ elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
+ else
{
- fprintf(stderr, "WARNING: ignoring encountered \"ri\" record.\n");
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number"
+ " (0x%.2X, 0x%.2X) encountered while parsing"
+ " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
return NULL;
}
+ ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+ if(ret_val == NULL)
+ return NULL;
+
+ ret_val->offset = offset;
+ ret_val->cell_size = cell_length;
ret_val->magic[0] = buf[0];
ret_val->magic[1] = buf[1];
+ ret_val->recursive_type = recursive_type;
+ ret_val->num_children = SVAL(buf, 0x2);
- ret_val->num_keys = SVAL(buf, 0x2);
- if(num_keys != ret_val->num_keys)
+ if(!recursive_type)
+ ret_val->num_keys = ret_val->num_children;
+
+ length = elem_size*ret_val->num_children;
+ if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32) < length)
{
+ regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
+ " cell while parsing subkey-list at offset 0x%.8X.",
+ offset);
if(strict)
- {
- free(ret_val);
- return NULL;
- }
- /* XXX: Not sure which should be authoritative, the number from the
- * NK record, or the number in the hash list. Go with the larger
- * of the two to ensure all keys are found. Note the length checks
- * on the cell later ensure that there won't be any critical errors.
- */
- if(num_keys < ret_val->num_keys)
- num_keys = ret_val->num_keys;
- else
- ret_val->num_keys = num_keys;
+ goto fail;
+ length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32);
}
- if(cell_length - REGFI_HASH_LIST_MIN_LENGTH - sizeof(uint32)
- < ret_val->num_keys*sizeof(REGF_HASH_LIST_ELEM))
- return NULL;
+ ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
+ ret_val->num_children);
+ if(ret_val->elements == NULL)
+ goto fail;
- length = sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys;
- ret_val->hashes = (REGF_HASH_LIST_ELEM*)zalloc(length);
- if(ret_val->hashes == NULL)
+ elements = (uint8*)malloc(length);
+ if(elements == NULL)
+ goto fail;
+
+ read_len = length;
+ if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
+ goto fail;
+
+ if(elem_size == sizeof(uint32))
{
- free(ret_val);
- return NULL;
+ for (i=0; i < ret_val->num_children; i++)
+ {
+ ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+ ret_val->elements[i].hash = 0;
+ }
}
-
- hashes = (uint8*)zalloc(length);
- if(hashes == NULL)
+ else
{
- free(ret_val->hashes);
- free(ret_val);
- return NULL;
+ for (i=0; i < ret_val->num_children; i++)
+ {
+ ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+ ret_val->elements[i].hash = IVAL(elements, i*elem_size+4);
+ }
}
+ free(elements);
- if(regfi_read(file->fd, hashes, &length) != 0
- || length != sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys)
- {
- free(ret_val->hashes);
- free(ret_val);
+ return ret_val;
+
+ fail:
+ if(elements != NULL)
+ free(elements);
+ talloc_free(ret_val);
+ return NULL;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists,
+ REGFI_SUBKEY_LIST** lists,
+ bool strict)
+{
+ uint32 i,j,k;
+ REGFI_SUBKEY_LIST* ret_val;
+
+ if(lists == NULL)
return NULL;
+ ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+
+ if(ret_val == NULL)
+ return NULL;
+
+ /* Obtain total number of elements */
+ ret_val->num_keys = 0;
+ for(i=0; i < num_lists; i++)
+ {
+ if(lists[i] != NULL)
+ ret_val->num_keys += lists[i]->num_children;
}
+ ret_val->num_children = ret_val->num_keys;
- for (i=0; i < ret_val->num_keys; i++)
+ if(ret_val->num_keys > 0)
{
- ret_val->hashes[i].nk_off = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM));
- ret_val->hashes[i].hash = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM)+4);
+ ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
+ ret_val->num_keys);
+ k=0;
+
+ if(ret_val->elements != NULL)
+ {
+ for(i=0; i < num_lists; i++)
+ {
+ if(lists[i] != NULL)
+ {
+ for(j=0; j < lists[i]->num_keys; j++)
+ {
+ ret_val->elements[k].hash = lists[i]->elements[j].hash;
+ ret_val->elements[k++].offset = lists[i]->elements[j].offset;
+ }
+ }
+ }
+ }
}
- free(hashes);
+
+ for(i=0; i < num_lists; i++)
+ regfi_subkeylist_free(lists[i]);
+ free(lists);
return ret_val;
}
-
-/*******************************************************************
- *******************************************************************/
-REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict)
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
+ bool strict)
{
- REGF_SK_REC* ret_val;
+ REGFI_SK_REC* ret_val;
+ uint8* sec_desc_buf = NULL;
uint32 cell_length, length;
- prs_struct ps;
uint8 sk_header[REGFI_SK_MIN_LENGTH];
bool unalloc = false;
-
if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
&cell_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell"
+ " at offset 0x%.8X.", offset);
return NULL;
+ }
if(sk_header[0] != 's' || sk_header[1] != 'k')
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+ " SK record at offset 0x%.8X.", offset);
return NULL;
-
- ret_val = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC));
+ }
+
+ ret_val = talloc(NULL, REGFI_SK_REC);
if(ret_val == NULL)
return NULL;
@@ -579,98 +793,143 @@ REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, boo
if((ret_val->cell_size < REGFI_SK_MIN_LENGTH)
|| (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
{
- free(ret_val);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
+ " parsing SK record at offset 0x%.8X.", offset);
+ goto fail;
}
ret_val->magic[0] = sk_header[0];
ret_val->magic[1] = sk_header[1];
- /* XXX: Can additional validation be added here? */
ret_val->unknown_tag = SVAL(sk_header, 0x2);
ret_val->prev_sk_off = IVAL(sk_header, 0x4);
ret_val->next_sk_off = IVAL(sk_header, 0x8);
ret_val->ref_count = IVAL(sk_header, 0xC);
ret_val->desc_size = IVAL(sk_header, 0x10);
- if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
+ if(ret_val->prev_sk_off != (ret_val->prev_sk_off & 0xFFFFFFF8)
+ || ret_val->next_sk_off != (ret_val->next_sk_off & 0xFFFFFFF8))
{
- free(ret_val);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
+ " are not a multiple of 8 while parsing SK record at"
+ " offset 0x%.8X.", offset);
+ goto fail;
}
- /* XXX: need to get rid of this, but currently the security descriptor
- * code depends on the ps structure.
- */
- if(!prs_init(&ps, ret_val->desc_size, NULL, UNMARSHALL))
+ if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
{
- free(ret_val);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_WARN, "Security descriptor too large for"
+ " cell while parsing SK record at offset 0x%.8X.",
+ offset);
+ goto fail;
}
+ sec_desc_buf = (uint8*)malloc(ret_val->desc_size);
+ if(sec_desc_buf == NULL)
+ goto fail;
+
length = ret_val->desc_size;
- if(regfi_read(file->fd, (uint8*)ps.data_p, &length) != 0
+ if(regfi_read(file->fd, sec_desc_buf, &length) != 0
|| length != ret_val->desc_size)
{
- free(ret_val);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security"
+ " descriptor while parsing SK record at offset 0x%.8X.",
+ offset);
+ goto fail;
}
- if (!sec_io_desc("sec_desc", &ret_val->sec_desc, &ps, 0))
+ if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf,
+ ret_val->desc_size)))
{
- free(ret_val);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security"
+ " descriptor while parsing SK record at offset 0x%.8X.",
+ offset);
+ goto fail;
}
- free(ps.data_p);
-
+ free(sec_desc_buf);
return ret_val;
+
+ fail:
+ if(sec_desc_buf != NULL)
+ free(sec_desc_buf);
+ talloc_free(ret_val);
+ return NULL;
}
-uint32* regfi_parse_valuelist(REGF_FILE* file, uint32 offset,
- uint32 num_values, bool strict)
+REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
+ uint32 num_values, bool strict)
{
- uint32* ret_val;
+ REGFI_VALUE_LIST* ret_val;
uint32 i, cell_length, length, read_len;
bool unalloc;
if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header"
+ " while parsing value list at offset 0x%.8X.", offset);
return NULL;
+ }
if(cell_length != (cell_length & 0xFFFFFFF8))
{
+ regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
+ " while parsing value list at offset 0x%.8X.", offset);
if(strict)
return NULL;
cell_length = cell_length & 0xFFFFFFF8;
}
+
if((num_values * sizeof(uint32)) > cell_length-sizeof(uint32))
- return NULL;
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
+ " while parsing value list at offset 0x%.8X.", offset);
+ if(strict)
+ return NULL;
+ num_values = cell_length/sizeof(uint32) - sizeof(uint32);
+ }
read_len = num_values*sizeof(uint32);
- ret_val = (uint32*)malloc(read_len);
+ ret_val = talloc(NULL, REGFI_VALUE_LIST);
if(ret_val == NULL)
return NULL;
+ ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
+ if(ret_val->elements == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ ret_val->num_values = num_values;
+
length = read_len;
- if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0) || length != read_len)
+ if((regfi_read(file->fd, (uint8*)ret_val->elements, &length) != 0)
+ || length != read_len)
{
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
+ " while parsing value list at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
return NULL;
}
for(i=0; i < num_values; i++)
{
/* Fix endianness */
- ret_val[i] = IVAL(&ret_val[i], 0);
+ ret_val->elements[i] = IVAL(&ret_val->elements[i], 0);
/* Validate the first num_values values to ensure they make sense */
if(strict)
{
- if((ret_val[i] + REGF_BLOCKSIZE > file->file_length)
- || ((ret_val[i] & 0xFFFFFFF8) != ret_val[i]))
+ /* XXX: Need to revisit this file length check when we start dealing
+ * with partial files. */
+ if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
+ || ((ret_val->elements[i] & 0xFFFFFFF8) != ret_val->elements[i]))
{
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
+ " (0x%.8X) found while parsing value list at offset"
+ " 0x%.8X.", ret_val->elements[i], offset);
+ talloc_free(ret_val);
return NULL;
}
}
@@ -682,91 +941,125 @@ uint32* regfi_parse_valuelist(REGF_FILE* file, uint32 offset,
/******************************************************************************
- * If !strict, the list may contain NULLs, VK records may point to NULL.
******************************************************************************/
-REGF_VK_REC** regfi_load_valuelist(REGF_FILE* file, uint32 offset,
- uint32 num_values, uint32 max_size,
- bool strict)
+REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset, bool strict)
{
- REGF_VK_REC** ret_val;
- REGF_HBIN* hbin;
- uint32 i, vk_offset, vk_max_length, usable_num_values;
- uint32* voffsets;
+ REGFI_VK_REC* ret_val = NULL;
+ const REGFI_HBIN* hbin;
+ uint32 data_offset, data_maxsize;
+ REGFI_BUFFER data;
- if((num_values+1) * sizeof(uint32) > max_size)
- {
- if(strict)
- return NULL;
- usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
- }
- else
- usable_num_values = num_values;
-
- voffsets = regfi_parse_valuelist(file, offset, usable_num_values, strict);
- if(voffsets == NULL)
+ hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
+ if(!hbin)
return NULL;
+
+ ret_val = regfi_parse_vk(file, offset,
+ hbin->block_size + hbin->file_off - offset, strict);
- ret_val = (REGF_VK_REC**)zalloc(sizeof(REGF_VK_REC*) * num_values);
if(ret_val == NULL)
- {
- free(voffsets);
return NULL;
- }
-
- for(i=0; i < usable_num_values; i++)
+
+ if(ret_val->data_size == 0)
+ ret_val->data = NULL;
+ else
{
- hbin = regfi_lookup_hbin(file, voffsets[i]);
- if(!hbin)
+ if(ret_val->data_in_offset)
{
- free(voffsets);
- free(ret_val);
- return NULL;
+ data = regfi_load_data(file, ret_val->type, ret_val->data_off,
+ ret_val->data_size, 4,
+ ret_val->data_in_offset, strict);
+ ret_val->data = data.buf;
+ ret_val->data_size = data.len;
}
-
- vk_offset = voffsets[i] + REGF_BLOCKSIZE;
- vk_max_length = hbin->block_size - vk_offset + sizeof(uint32);
- ret_val[i] = regfi_parse_vk(file, vk_offset, vk_max_length, strict);
- if(ret_val[i] == NULL)
- { /* If we're being strict, throw out the whole list.
- * Otherwise, let it be NULL.
- */
- if(strict)
+ else
+ {
+ hbin = regfi_lookup_hbin(file, ret_val->data_off);
+ if(hbin)
{
- free(voffsets);
- free(ret_val);
- return NULL;
+ data_offset = ret_val->data_off+REGFI_REGF_SIZE;
+ data_maxsize = hbin->block_size + hbin->file_off - data_offset;
+ data = regfi_load_data(file, ret_val->type, data_offset,
+ ret_val->data_size, data_maxsize,
+ ret_val->data_in_offset, strict);
+ ret_val->data = data.buf;
+ ret_val->data_size = data.len;
+
+ }
+ else
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
+ " while parsing VK record at offset 0x%.8X.",
+ ret_val->offset);
+ ret_val->data = NULL;
}
}
+
+ if(ret_val->data == NULL)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse data record"
+ " while parsing VK record at offset 0x%.8X.",
+ ret_val->offset, ret_val->valuename);
+ }
+ else
+ talloc_steal(ret_val, ret_val->data);
}
- free(voffsets);
return ret_val;
}
+/******************************************************************************
+ * If !strict, the list may contain NULLs, VK records may point to NULL.
+ ******************************************************************************/
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
+ uint32 num_values, uint32 max_size,
+ bool strict)
+{
+ uint32 usable_num_values;
-/*******************************************************************
- * XXX: Need to add full key caching using a
- * custom cache structure.
- *******************************************************************/
-REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
+ if((num_values+1) * sizeof(uint32) > max_size)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
+ " parent key (%d) would cause cell to straddle HBIN"
+ " boundary while loading value list at offset"
+ " 0x%.8X.", num_values, offset);
+ if(strict)
+ return NULL;
+ usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
+ }
+ else
+ usable_num_values = num_values;
+
+ return regfi_parse_valuelist(file, offset, usable_num_values, strict);
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
{
- REGF_HBIN* hbin;
- REGF_HBIN* sub_hbin;
- REGF_NK_REC* nk;
+ const REGFI_HBIN* hbin;
+ const REGFI_HBIN* sub_hbin;
+ REGFI_NK_REC* nk;
uint32 max_length, off;
- hbin = regfi_lookup_hbin(file, offset-REGF_BLOCKSIZE);
+ hbin = regfi_lookup_hbin(file, offset-REGFI_REGF_SIZE);
if (hbin == NULL)
return NULL;
/* get the initial nk record */
max_length = hbin->block_size + hbin->file_off - offset;
- if ((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+ if((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
+ " offset 0x%.8X.", offset);
return NULL;
+ }
- /* fill in values */
- if(nk->num_values && (nk->values_off!=REGF_OFFSET_NONE))
+ /* get value list */
+ if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
{
sub_hbin = hbin;
if(!regfi_offset_in_hbin(hbin, nk->values_off))
@@ -776,38 +1069,45 @@ REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
{
if(strict)
{
- free(nk);
+ regfi_free_key(nk);
return NULL;
}
else
nk->values = NULL;
+
}
else
{
- off = nk->values_off + REGF_BLOCKSIZE;
+ off = nk->values_off + REGFI_REGF_SIZE;
max_length = sub_hbin->block_size + sub_hbin->file_off - off;
nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length,
true);
- if(strict && nk->values == NULL)
+ if(nk->values == NULL)
{
- free(nk);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
+ " for NK record at offset 0x%.8X.", offset);
+ if(strict)
+ {
+ regfi_free_key(nk);
+ return NULL;
+ }
}
+ talloc_steal(nk, nk->values);
}
}
- /* now get subkeys */
- if(nk->num_subkeys && (nk->subkeys_off != REGF_OFFSET_NONE))
+ /* now get subkey list */
+ if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE))
{
sub_hbin = hbin;
if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
- if (sub_hbin == NULL)
+ if(sub_hbin == NULL)
{
if(strict)
{
- regfi_key_free(nk);
+ regfi_free_key(nk);
return NULL;
}
else
@@ -815,15 +1115,18 @@ REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
}
else
{
- off = nk->subkeys_off + REGF_BLOCKSIZE;
+ off = nk->subkeys_off + REGFI_REGF_SIZE;
max_length = sub_hbin->block_size + sub_hbin->file_off - off;
- nk->subkeys = regfi_load_hashlist(file, off, nk->num_subkeys,
- max_length, true);
+ nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
+ max_length, true);
+
if(nk->subkeys == NULL)
{
- /* XXX: Temporary hack to get around 'ri' records */
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not load subkey list"
+ " while parsing NK record at offset 0x%.8X.", offset);
nk->num_subkeys = 0;
}
+ talloc_steal(nk, nk->subkeys);
}
}
@@ -832,15 +1135,56 @@ REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
/******************************************************************************
+ ******************************************************************************/
+const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
+{
+ REGFI_SK_REC* ret_val = NULL;
+ const REGFI_HBIN* hbin;
+ uint32 max_length;
+ void* failure_ptr = NULL;
+
+ /* First look if we have already parsed it */
+ ret_val = (REGFI_SK_REC*)lru_cache_find(file->sk_cache, &offset, 4);
+
+ /* Bail out if we have previously cached a parse failure at this offset. */
+ if(ret_val == (void*)REGFI_OFFSET_NONE)
+ return NULL;
+
+ if(ret_val == NULL)
+ {
+ hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
+ if(hbin == NULL)
+ return NULL;
+
+ max_length = hbin->block_size + hbin->file_off - offset;
+ ret_val = regfi_parse_sk(file, offset, max_length, strict);
+ if(ret_val == NULL)
+ { /* Cache the parse failure and bail out. */
+ failure_ptr = talloc(NULL, uint32_t);
+ if(failure_ptr == NULL)
+ return NULL;
+ *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
+ lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
+ return NULL;
+ }
+
+ lru_cache_update(file->sk_cache, &offset, 4, ret_val);
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
******************************************************************************/
-static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
+static bool regfi_find_root_nk(REGFI_FILE* file, uint32 offset,uint32 hbin_size,
uint32* root_offset)
{
uint8 tmp[4];
int32 record_size;
uint32 length, hbin_offset = 0;
- REGF_NK_REC* nk = NULL;
+ REGFI_NK_REC* nk = NULL;
bool found = false;
for(record_size=0; !found && (hbin_offset < hbin_size); )
@@ -859,12 +1203,12 @@ static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
if(nk != NULL)
{
- if(nk->key_type == NK_TYPE_ROOTKEY)
+ if(nk->key_type & REGFI_NK_FLAG_ROOT)
{
found = true;
*root_offset = nk->offset;
}
- free(nk);
+ regfi_free_key(nk);
}
}
@@ -879,59 +1223,84 @@ static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
* Open the registry file and then read in the REGF block to get the
* first hbin offset.
*******************************************************************/
-REGF_FILE* regfi_open(const char* filename)
+REGFI_FILE* regfi_open(const char* filename)
{
- REGF_FILE* rb;
- REGF_HBIN* hbin = NULL;
- uint32 hbin_off;
+ struct stat sbuf;
+ REGFI_FILE* rb;
+ REGFI_HBIN* hbin = NULL;
+ uint32 hbin_off, file_length, cache_secret;
int fd;
bool rla;
/* open an existing file */
- if ((fd = open(filename, O_RDONLY)) == -1)
+ if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
{
- /* DEBUG(0,("regfi_open: failure to open %s (%s)\n", filename, strerror(errno)));*/
+ /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
return NULL;
}
+ /* Determine file length. Must be at least big enough
+ * for the header and one hbin.
+ */
+ if (fstat(fd, &sbuf) == -1)
+ return NULL;
+ file_length = sbuf.st_size;
+ if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
+ return NULL;
+
/* read in an existing file */
if ((rb = regfi_parse_regf(fd, true)) == NULL)
{
- /* DEBUG(0,("regfi_open: Failed to read initial REGF block\n"));*/
+ /* fprintf(stderr, "regfi_open: Failed to read initial REGF block\n"); */
close(fd);
return NULL;
}
-
+ rb->file_length = file_length;
+
rb->hbins = range_list_new();
if(rb->hbins == NULL)
{
- range_list_free(rb->hbins);
+ /* fprintf(stderr, "regfi_open: Failed to create HBIN list.\n"); */
close(fd);
- free(rb);
+ talloc_free(rb);
return NULL;
}
-
+ talloc_steal(rb, rb->hbins);
+
rla = true;
- hbin_off = REGF_BLOCKSIZE;
+ hbin_off = REGFI_REGF_SIZE;
hbin = regfi_parse_hbin(rb, hbin_off, true);
while(hbin && rla)
{
- hbin_off = hbin->file_off + hbin->block_size;
rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
+ if(rla)
+ talloc_steal(rb->hbins, hbin);
+ hbin_off = hbin->file_off + hbin->block_size;
hbin = regfi_parse_hbin(rb, hbin_off, true);
}
+ /* This secret isn't very secret, but we don't need a good one. This
+ * secret is just designed to prevent someone from trying to blow our
+ * caching and make things slow.
+ */
+ cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
+
+ /* Cache an unlimited number of SK records. Typically there are very few. */
+ rb->sk_cache = lru_cache_create_ctx(rb, 0, cache_secret, true);
+
+ /* Default message mask */
+ rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN;
+
/* success */
return rb;
}
-/*******************************************************************
- *******************************************************************/
-int regfi_close( REGF_FILE *file )
+/******************************************************************************
+ ******************************************************************************/
+int regfi_close(REGFI_FILE *file)
{
int fd;
- uint32 i;
/* nothing to do if there is no open file */
if ((file == NULL) || (file->fd == -1))
@@ -939,12 +1308,13 @@ int regfi_close( REGF_FILE *file )
fd = file->fd;
file->fd = -1;
- for(i=0; i < range_list_size(file->hbins); i++)
- free(range_list_get(file->hbins, i)->data);
+
range_list_free(file->hbins);
- free(file);
+ if(file->sk_cache != NULL)
+ lru_cache_destroy(file->sk_cache);
+ talloc_free(file);
return close(fd);
}
@@ -952,27 +1322,27 @@ int regfi_close( REGF_FILE *file )
/******************************************************************************
* There should be only *one* root key in the registry file based
* on my experience. --jerry
- *****************************************************************************/
-REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
+ ******************************************************************************/
+REGFI_NK_REC* regfi_rootkey(REGFI_FILE *file)
{
- REGF_NK_REC* nk = NULL;
- REGF_HBIN* hbin;
- uint32 root_offset, i, num_hbins;
+ REGFI_NK_REC* nk = NULL;
+ REGFI_HBIN* hbin;
+ uint32 root_offset, i, num_hbins;
if(!file)
return NULL;
/* Scan through the file one HBIN block at a time looking
- for an NK record with a type == 0x002c.
- Normally this is the first nk record in the first hbin
- block (but I'm not assuming that for now) */
-
+ * for an NK record with a root key type.
+ * This is typically the first NK record in the first HBIN
+ * block (but we're not assuming that generally).
+ */
num_hbins = range_list_size(file->hbins);
for(i=0; i < num_hbins; i++)
{
- hbin = (REGF_HBIN*)range_list_get(file->hbins, i)->data;
- if(regfi_find_root_nk(file, hbin->file_off+HBIN_HEADER_REC_SIZE,
- hbin->block_size-HBIN_HEADER_REC_SIZE, &root_offset))
+ hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
+ if(regfi_find_root_nk(file, hbin->file_off+REGFI_HBIN_HEADER_SIZE,
+ hbin->block_size-REGFI_HBIN_HEADER_SIZE, &root_offset))
{
nk = regfi_load_key(file, root_offset, true);
break;
@@ -985,65 +1355,55 @@ REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
/******************************************************************************
*****************************************************************************/
-void regfi_key_free(REGF_NK_REC* nk)
+void regfi_free_key(REGFI_NK_REC* nk)
{
- uint32 i;
-
- if((nk->values != NULL) && (nk->values_off!=REGF_OFFSET_NONE))
- {
- for(i=0; i < nk->num_values; i++)
- {
- if(nk->values[i]->valuename != NULL)
- free(nk->values[i]->valuename);
- if(nk->values[i]->data != NULL)
- free(nk->values[i]->data);
- free(nk->values[i]);
- }
- free(nk->values);
- }
+ regfi_subkeylist_free(nk->subkeys);
+ talloc_free(nk);
+}
- if(nk->keyname != NULL)
- free(nk->keyname);
- if(nk->classname != NULL)
- free(nk->classname);
- /* XXX: not freeing hbin because these are cached. This needs to be reviewed. */
- /* XXX: not freeing sec_desc because these are cached. This needs to be reviewed. */
- free(nk);
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_value(REGFI_VK_REC* vk)
+{
+ talloc_free(vk);
}
/******************************************************************************
*****************************************************************************/
-REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh)
+void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
{
- REGF_NK_REC* root;
- REGFI_ITERATOR* ret_val = (REGFI_ITERATOR*)malloc(sizeof(REGFI_ITERATOR));
+ if(list != NULL)
+ {
+ talloc_free(list);
+ }
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
+{
+ REGFI_NK_REC* root;
+ REGFI_ITERATOR* ret_val = talloc(NULL, REGFI_ITERATOR);
if(ret_val == NULL)
return NULL;
root = regfi_rootkey(fh);
if(root == NULL)
{
- free(ret_val);
+ talloc_free(ret_val);
return NULL;
}
- ret_val->key_positions = void_stack_new(REGF_MAX_DEPTH);
+ ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH);
if(ret_val->key_positions == NULL)
{
- free(ret_val);
- free(root);
+ talloc_free(ret_val);
return NULL;
}
-
- /* This secret isn't very secret, but we don't need a good one. This
- * secret is just designed to prevent someone from trying to blow our
- * caching and make things slow.
- */
- ret_val->sk_recs = lru_cache_create(127, 0x15DEAD05^time(NULL)
- ^(getpid()<<16)^(getppid()<<8),
- true);
+ talloc_steal(ret_val, ret_val->key_positions);
ret_val->f = fh;
ret_val->cur_key = root;
@@ -1058,20 +1418,7 @@ REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh)
*****************************************************************************/
void regfi_iterator_free(REGFI_ITERATOR* i)
{
- REGFI_ITER_POSITION* cur;
-
- if(i->cur_key != NULL)
- regfi_key_free(i->cur_key);
-
- while((cur = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions)) != NULL)
- {
- regfi_key_free(cur->nk);
- free(cur);
- }
-
- lru_cache_destroy(i->sk_recs);
-
- free(i);
+ talloc_free(i);
}
@@ -1081,17 +1428,17 @@ void regfi_iterator_free(REGFI_ITERATOR* i)
/* XXX: some way of indicating reason for failure should be added. */
bool regfi_iterator_down(REGFI_ITERATOR* i)
{
- REGF_NK_REC* subkey;
+ REGFI_NK_REC* subkey;
REGFI_ITER_POSITION* pos;
- pos = (REGFI_ITER_POSITION*)malloc(sizeof(REGFI_ITER_POSITION));
+ pos = talloc(i->key_positions, REGFI_ITER_POSITION);
if(pos == NULL)
return false;
- subkey = (REGF_NK_REC*)regfi_iterator_cur_subkey(i);
+ subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i);
if(subkey == NULL)
{
- free(pos);
+ talloc_free(pos);
return false;
}
@@ -1099,10 +1446,11 @@ bool regfi_iterator_down(REGFI_ITERATOR* i)
pos->cur_subkey = i->cur_subkey;
if(!void_stack_push(i->key_positions, pos))
{
- free(pos);
- regfi_key_free(subkey);
+ talloc_free(pos);
+ regfi_free_key(subkey);
return false;
}
+ talloc_steal(i, subkey);
i->cur_key = subkey;
i->cur_subkey = 0;
@@ -1122,11 +1470,11 @@ bool regfi_iterator_up(REGFI_ITERATOR* i)
if(pos == NULL)
return false;
- regfi_key_free(i->cur_key);
+ regfi_free_key(i->cur_key);
i->cur_key = pos->nk;
i->cur_subkey = pos->cur_subkey;
i->cur_value = 0;
- free(pos);
+ talloc_free(pos);
return true;
}
@@ -1147,15 +1495,15 @@ bool regfi_iterator_to_root(REGFI_ITERATOR* i)
*****************************************************************************/
bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
{
- REGF_NK_REC* subkey;
+ REGFI_NK_REC* subkey;
bool found = false;
uint32 old_subkey = i->cur_subkey;
-
+
if(subkey_name == NULL)
return false;
/* XXX: this alloc/free of each sub key might be a bit excessive */
- subkey = (REGF_NK_REC*)regfi_iterator_first_subkey(i);
+ subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i);
while((subkey != NULL) && (found == false))
{
if(subkey->keyname != NULL
@@ -1163,8 +1511,8 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
found = true;
else
{
- regfi_key_free(subkey);
- subkey = (REGF_NK_REC*)regfi_iterator_next_subkey(i);
+ regfi_free_key(subkey);
+ subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i);
}
}
@@ -1174,7 +1522,7 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
return false;
}
- regfi_key_free(subkey);
+ regfi_free_key(subkey);
return true;
}
@@ -1206,7 +1554,7 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
/******************************************************************************
*****************************************************************************/
-const REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
+const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
{
return i->cur_key;
}
@@ -1214,43 +1562,18 @@ const REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
/******************************************************************************
*****************************************************************************/
-const REGF_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
+const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
{
- REGF_SK_REC* ret_val;
- REGF_HBIN* hbin;
- uint32 max_length, off;
-
- if(i->cur_key == NULL)
+ if(i->cur_key == NULL || i->cur_key->sk_off == REGFI_OFFSET_NONE)
return NULL;
-
- /* First look if we have already parsed it */
- if((i->cur_key->sk_off!=REGF_OFFSET_NONE)
- && !(ret_val =(REGF_SK_REC*)lru_cache_find(i->sk_recs,
- &i->cur_key->sk_off, 4)))
- {
- hbin = regfi_lookup_hbin(i->f, i->cur_key->sk_off);
-
- if(hbin == NULL)
- return NULL;
-
- off = i->cur_key->sk_off + REGF_BLOCKSIZE;
- max_length = hbin->block_size + hbin->file_off - off;
- ret_val = regfi_parse_sk(i->f, off, max_length, true);
- if(ret_val == NULL)
- return NULL;
- ret_val->sk_off = i->cur_key->sk_off;
- lru_cache_update(i->sk_recs, &i->cur_key->sk_off, 4, ret_val);
- }
-
- return ret_val;
+ return regfi_load_sk(i->f, i->cur_key->sk_off + REGFI_REGF_SIZE, true);
}
-
/******************************************************************************
*****************************************************************************/
-const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
+REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
{
i->cur_subkey = 0;
return regfi_iterator_cur_subkey(i);
@@ -1259,27 +1582,27 @@ const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
/******************************************************************************
*****************************************************************************/
-const REGF_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
{
uint32 nk_offset;
/* see if there is anything left to report */
- if (!(i->cur_key) || (i->cur_key->subkeys_off==REGF_OFFSET_NONE)
+ if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
|| (i->cur_subkey >= i->cur_key->num_subkeys))
return NULL;
- nk_offset = i->cur_key->subkeys->hashes[i->cur_subkey].nk_off;
-
- return regfi_load_key(i->f, nk_offset+REGF_BLOCKSIZE, true);
+ nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
+
+ return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, true);
}
/******************************************************************************
*****************************************************************************/
/* XXX: some way of indicating reason for failure should be added. */
-const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
+REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
{
- const REGF_NK_REC* subkey;
+ REGFI_NK_REC* subkey;
i->cur_subkey++;
subkey = regfi_iterator_cur_subkey(i);
@@ -1295,7 +1618,7 @@ const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
*****************************************************************************/
bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
{
- const REGF_VK_REC* cur;
+ REGFI_VK_REC* cur;
bool found = false;
/* XXX: cur->valuename can be NULL in the registry.
@@ -1311,7 +1634,10 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
&& (strcasecmp(cur->valuename, value_name) == 0))
found = true;
else
+ {
+ regfi_free_value(cur);
cur = regfi_iterator_next_value(i);
+ }
}
return found;
@@ -1320,7 +1646,7 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
/******************************************************************************
*****************************************************************************/
-const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
+REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
{
i->cur_value = 0;
return regfi_iterator_cur_value(i);
@@ -1329,11 +1655,19 @@ const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
/******************************************************************************
*****************************************************************************/
-const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
+REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
{
- REGF_VK_REC* ret_val = NULL;
- if(i->cur_value < i->cur_key->num_values)
- ret_val = i->cur_key->values[i->cur_value];
+ REGFI_VK_REC* ret_val = NULL;
+ uint32 voffset;
+
+ if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
+ {
+ if(i->cur_value < i->cur_key->values->num_values)
+ {
+ voffset = i->cur_key->values->elements[i->cur_value];
+ ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, true);
+ }
+ }
return ret_val;
}
@@ -1341,9 +1675,9 @@ const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
/******************************************************************************
*****************************************************************************/
-const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
+REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
{
- const REGF_VK_REC* ret_val;
+ REGFI_VK_REC* ret_val;
i->cur_value++;
ret_val = regfi_iterator_cur_value(i);
@@ -1354,7 +1688,6 @@ const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
}
-
/*******************************************************************
* Computes the checksum of the registry file header.
* buffer must be at least the size of an regf header (4096 bytes).
@@ -1380,109 +1713,125 @@ static uint32 regfi_compute_header_checksum(uint8* buffer)
/*******************************************************************
* XXX: Add way to return more detailed error information.
*******************************************************************/
-REGF_FILE* regfi_parse_regf(int fd, bool strict)
+REGFI_FILE* regfi_parse_regf(int fd, bool strict)
{
- uint8 file_header[REGF_BLOCKSIZE];
+ uint8 file_header[REGFI_REGF_SIZE];
uint32 length;
- uint32 file_length;
- struct stat sbuf;
- REGF_FILE* ret_val;
+ REGFI_FILE* ret_val;
- /* Determine file length. Must be at least big enough
- * for the header and one hbin.
- */
- if (fstat(fd, &sbuf) == -1)
- return NULL;
- file_length = sbuf.st_size;
- if(file_length < REGF_BLOCKSIZE+REGF_ALLOC_BLOCK)
- return NULL;
-
- ret_val = (REGF_FILE*)zalloc(sizeof(REGF_FILE));
+ ret_val = talloc(NULL, REGFI_FILE);
if(ret_val == NULL)
return NULL;
ret_val->fd = fd;
- ret_val->file_length = file_length;
-
- length = REGF_BLOCKSIZE;
- if((regfi_read(fd, file_header, &length)) != 0
- || length != REGF_BLOCKSIZE)
- {
- free(ret_val);
- return NULL;
- }
-
+ ret_val->sk_cache = NULL;
+ ret_val->last_message = NULL;
+ ret_val->hbins = NULL;
+
+ length = REGFI_REGF_SIZE;
+ if((regfi_read(fd, file_header, &length)) != 0 || length != REGFI_REGF_SIZE)
+ goto fail;
+
ret_val->checksum = IVAL(file_header, 0x1FC);
ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
if (strict && (ret_val->checksum != ret_val->computed_checksum))
- {
- free(ret_val);
- return NULL;
- }
+ goto fail;
- memcpy(ret_val->magic, file_header, 4);
- if(strict && (memcmp(ret_val->magic, "regf", 4) != 0))
+ memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
+ if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
{
- free(ret_val);
- return NULL;
- }
-
- ret_val->unknown1 = IVAL(file_header, 0x4);
- ret_val->unknown2 = IVAL(file_header, 0x8);
-
+ if(strict)
+ goto fail;
+ regfi_add_message(ret_val, REGFI_MSG_WARN, "Magic number mismatch "
+ "(%.2X %.2X %.2X %.2X) while parsing hive header",
+ ret_val->magic[0], ret_val->magic[1],
+ ret_val->magic[2], ret_val->magic[3]);
+ }
+ ret_val->sequence1 = IVAL(file_header, 0x4);
+ ret_val->sequence2 = IVAL(file_header, 0x8);
ret_val->mtime.low = IVAL(file_header, 0xC);
ret_val->mtime.high = IVAL(file_header, 0x10);
-
- ret_val->unknown3 = IVAL(file_header, 0x14);
- ret_val->unknown4 = IVAL(file_header, 0x18);
- ret_val->unknown5 = IVAL(file_header, 0x1C);
- ret_val->unknown6 = IVAL(file_header, 0x20);
-
- ret_val->data_offset = IVAL(file_header, 0x24);
+ ret_val->major_version = IVAL(file_header, 0x14);
+ ret_val->minor_version = IVAL(file_header, 0x18);
+ ret_val->type = IVAL(file_header, 0x1C);
+ ret_val->format = IVAL(file_header, 0x20);
+ ret_val->root_cell = IVAL(file_header, 0x24);
ret_val->last_block = IVAL(file_header, 0x28);
- ret_val->unknown7 = IVAL(file_header, 0x2C);
+ ret_val->cluster = IVAL(file_header, 0x2C);
+
+ memcpy(ret_val->file_name, file_header+0x30, REGFI_REGF_NAME_SIZE);
+
+ /* XXX: Should we add a warning if these uuid parsers fail? Can they? */
+ ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
+ ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
+ ret_val->flags = IVAL(file_header, 0x90);
+ ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
+ ret_val->guid_signature = IVAL(file_header, 0xa4);
+
+ memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
+ memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE);
+
+ ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16);
+ ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16);
+ ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16);
+ ret_val->boot_type = IVAL(file_header, 0xFF8);
+ ret_val->boot_recover = IVAL(file_header, 0xFFC);
return ret_val;
+
+ fail:
+ talloc_free(ret_val);
+ return NULL;
}
-/*******************************************************************
+/******************************************************************************
* Given real file offset, read and parse the hbin at that location
* along with it's associated cells.
- *******************************************************************/
-/* XXX: Need a way to return types of errors.
- */
-REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, bool strict)
+ ******************************************************************************/
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
{
- REGF_HBIN *hbin;
- uint8 hbin_header[HBIN_HEADER_REC_SIZE];
+ REGFI_HBIN *hbin;
+ uint8 hbin_header[REGFI_HBIN_HEADER_SIZE];
uint32 length;
if(offset >= file->file_length)
return NULL;
if(lseek(file->fd, offset, SEEK_SET) == -1)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
+ " while parsing hbin at offset 0x%.8X.", offset);
return NULL;
+ }
- length = HBIN_HEADER_REC_SIZE;
+ length = REGFI_HBIN_HEADER_SIZE;
if((regfi_read(file->fd, hbin_header, &length) != 0)
- || length != HBIN_HEADER_REC_SIZE)
+ || length != REGFI_HBIN_HEADER_SIZE)
return NULL;
-
if(lseek(file->fd, offset, SEEK_SET) == -1)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
+ " while parsing hbin at offset 0x%.8X.", offset);
return NULL;
+ }
- if(!(hbin = (REGF_HBIN*)zalloc(sizeof(REGF_HBIN))))
+ hbin = talloc(NULL, REGFI_HBIN);
+ if(hbin == NULL)
return NULL;
hbin->file_off = offset;
memcpy(hbin->magic, hbin_header, 4);
if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
{
- free(hbin);
+ regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch "
+ "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
+ " 0x%.8X.", hbin->magic[0], hbin->magic[1],
+ hbin->magic[2], hbin->magic[3], offset);
+ talloc_free(hbin);
return NULL;
}
@@ -1501,7 +1850,10 @@ REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, bool strict)
if((offset + hbin->block_size > file->file_length)
|| (hbin->block_size & 0xFFFFF000) != hbin->block_size)
{
- free(hbin);
+ regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned"
+ " or runs off the end of the file"
+ " while parsing hbin at offset 0x%.8X.", offset);
+ talloc_free(hbin);
return NULL;
}
@@ -1509,33 +1861,44 @@ REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, bool strict)
}
-
-REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
+/*******************************************************************
+ *******************************************************************/
+REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
uint32 max_size, bool strict)
{
uint8 nk_header[REGFI_NK_MIN_LENGTH];
- REGF_NK_REC* ret_val;
- uint32 length;
- uint32 cell_length;
+ const REGFI_HBIN *hbin;
+ REGFI_NK_REC* ret_val;
+ uint32 length,cell_length;
+ uint32 class_offset, class_maxsize;
bool unalloc = false;
if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
&cell_length, &unalloc))
- return NULL;
-
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+ " while parsing NK record at offset 0x%.8X.", offset);
+ return NULL;
+ }
+
/* A bit of validation before bothering to allocate memory */
if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
{
- /* XXX: Deal with subkey-lists that reference other subkey-lists
- * (e.g. 'ri' records).
- */
+ regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+ " NK record at offset 0x%.8X.", offset);
return NULL;
}
- ret_val = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC));
+ ret_val = talloc(NULL, REGFI_NK_REC);
if(ret_val == NULL)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while"
+ " parsing NK record at offset 0x%.8X.", offset);
return NULL;
+ }
+ ret_val->values = NULL;
+ ret_val->subkeys = NULL;
ret_val->offset = offset;
ret_val->cell_size = cell_length;
@@ -1544,20 +1907,21 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
if((ret_val->cell_size < REGFI_NK_MIN_LENGTH)
|| (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
{
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
+ " parsing NK record at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
return NULL;
}
ret_val->magic[0] = nk_header[0x0];
ret_val->magic[1] = nk_header[0x1];
ret_val->key_type = SVAL(nk_header, 0x2);
- if((ret_val->key_type != NK_TYPE_NORMALKEY)
- && (ret_val->key_type != NK_TYPE_ROOTKEY)
- && (ret_val->key_type != NK_TYPE_LINKKEY)
- && (ret_val->key_type != NK_TYPE_UNKNOWN1))
+
+ if((ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS) != 0)
{
- free(ret_val);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
+ " parsing NK record at offset 0x%.8X.",
+ (ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS), offset);
}
ret_val->mtime.low = IVAL(nk_header, 0x4);
@@ -1582,7 +1946,6 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
ret_val->num_values = IVAL(nk_header, 0x24);
ret_val->values_off = IVAL(nk_header, 0x28);
ret_val->sk_off = IVAL(nk_header, 0x2C);
- /* XXX: currently we do nothing with class names. Need to investigate. */
ret_val->classname_off = IVAL(nk_header, 0x30);
ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
@@ -1598,7 +1961,9 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
{
if(strict)
{
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell"
+ " while parsing NK record at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
return NULL;
}
else
@@ -1616,10 +1981,10 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
ret_val->cell_size = length;
}
- ret_val->keyname = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
+ ret_val->keyname = talloc_array(ret_val, char, ret_val->name_length+1);
if(ret_val->keyname == NULL)
{
- free(ret_val);
+ talloc_free(ret_val);
return NULL;
}
@@ -1628,44 +1993,146 @@ REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset,
if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
|| length != ret_val->name_length)
{
- free(ret_val->keyname);
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
+ " while parsing NK record at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
return NULL;
}
ret_val->keyname[ret_val->name_length] = '\0';
+ /* XXX: This linking should be moved up to regfi_load_key */
+ if(ret_val->classname_off != REGFI_OFFSET_NONE)
+ {
+ hbin = regfi_lookup_hbin(file, ret_val->classname_off);
+ if(hbin)
+ {
+ class_offset = ret_val->classname_off+REGFI_REGF_SIZE;
+ class_maxsize = hbin->block_size + hbin->file_off - class_offset;
+ ret_val->classname
+ = regfi_parse_classname(file, class_offset, &ret_val->classname_length,
+ class_maxsize, strict);
+ }
+ else
+ {
+ ret_val->classname = NULL;
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not find hbin for class"
+ " name while parsing NK record at offset 0x%.8X.",
+ offset);
+ }
+
+ if(ret_val->classname == NULL)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse class"
+ " name while parsing NK record at offset 0x%.8X.",
+ offset);
+ }
+ else
+ talloc_steal(ret_val, ret_val->classname);
+ }
+
return ret_val;
}
+char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
+ uint16* name_length, uint32 max_size, bool strict)
+{
+ char* ret_val = NULL;
+ uint32 length;
+ uint32 cell_length;
+ bool unalloc = false;
+
+ if(*name_length > 0 && offset != REGFI_OFFSET_NONE
+ && offset == (offset & 0xFFFFFFF8))
+ {
+ if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+ " while parsing class name at offset 0x%.8X.", offset);
+ return NULL;
+ }
-/*******************************************************************
- *******************************************************************/
-REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
- uint32 max_size, bool strict)
+ if((cell_length & 0xFFFFFFF8) != cell_length)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
+ " while parsing class name at offset 0x%.8X.", offset);
+ return NULL;
+ }
+
+ if(cell_length > max_size)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin "
+ "boundary while parsing class name at offset 0x%.8X.",
+ offset);
+ if(strict)
+ return NULL;
+ cell_length = max_size;
+ }
+
+ if((cell_length - 4) < *name_length)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than"
+ " cell_length while parsing class name at offset"
+ " 0x%.8X.", offset);
+ if(strict)
+ return NULL;
+ *name_length = cell_length - 4;
+ }
+
+ ret_val = talloc_array(NULL, char, *name_length);
+ if(ret_val != NULL)
+ {
+ length = *name_length;
+ if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0)
+ || length != *name_length)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
+ " while parsing class name at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
+ return NULL;
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
+ uint32 max_size, bool strict)
{
- REGF_VK_REC* ret_val;
+ REGFI_VK_REC* ret_val;
uint8 vk_header[REGFI_VK_MIN_LENGTH];
uint32 raw_data_size, length, cell_length;
bool unalloc = false;
if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
&cell_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+ " while parsing VK record at offset 0x%.8X.", offset);
return NULL;
+ }
- ret_val = (REGF_VK_REC*)zalloc(sizeof(REGF_VK_REC));
+ ret_val = talloc(NULL, REGFI_VK_REC);
if(ret_val == NULL)
return NULL;
ret_val->offset = offset;
ret_val->cell_size = cell_length;
-
+ ret_val->data = NULL;
+ ret_val->valuename = NULL;
+
if(ret_val->cell_size > max_size)
ret_val->cell_size = max_size & 0xFFFFFFF8;
if((ret_val->cell_size < REGFI_VK_MIN_LENGTH)
|| ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8))
{
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
+ " while parsing VK record at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
return NULL;
}
@@ -1673,26 +2140,35 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
ret_val->magic[1] = vk_header[0x1];
if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
{
- free(ret_val);
+ /* XXX: This does not account for deleted keys under Win2K which
+ * often have this (and the name length) overwritten with
+ * 0xFFFF.
+ */
+ regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch"
+ " while parsing VK record at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
return NULL;
}
ret_val->name_length = SVAL(vk_header, 0x2);
raw_data_size = IVAL(vk_header, 0x4);
- ret_val->data_size = raw_data_size & ~VK_DATA_IN_OFFSET;
- ret_val->data_in_offset = (bool)(raw_data_size & VK_DATA_IN_OFFSET);
+ ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
+ ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
ret_val->data_off = IVAL(vk_header, 0x8);
ret_val->type = IVAL(vk_header, 0xC);
ret_val->flag = SVAL(vk_header, 0x10);
ret_val->unknown1 = SVAL(vk_header, 0x12);
- if(ret_val->flag & VK_FLAG_NAME_PRESENT)
+ if(ret_val->flag & REGFI_VK_FLAG_NAME_PRESENT)
{
if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
{
+ regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell"
+ " space while parsing VK record at offset 0x%.8X.",
+ offset);
if(strict)
{
- free(ret_val);
+ talloc_free(ret_val);
return NULL;
}
else
@@ -1704,10 +2180,10 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
cell_length+=8;
- ret_val->valuename = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
+ ret_val->valuename = talloc_array(ret_val, char, ret_val->name_length+1);
if(ret_val->valuename == NULL)
{
- free(ret_val);
+ talloc_free(ret_val);
return NULL;
}
@@ -1715,11 +2191,13 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
|| length != ret_val->name_length)
{
- free(ret_val->valuename);
- free(ret_val);
+ regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
+ " while parsing VK record at offset 0x%.8X.", offset);
+ talloc_free(ret_val);
return NULL;
}
ret_val->valuename[ret_val->name_length] = '\0';
+
}
else
cell_length = REGFI_VK_MIN_LENGTH + 4;
@@ -1731,90 +2209,239 @@ REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset,
ret_val->cell_size = cell_length;
}
- if(ret_val->data_size == 0)
- ret_val->data = NULL;
- else
- {
- ret_val->data = regfi_parse_data(file, ret_val->data_off+REGF_BLOCKSIZE,
- raw_data_size, strict);
- if(strict && (ret_val->data == NULL))
- {
- free(ret_val->valuename);
- free(ret_val);
- return NULL;
- }
- }
-
return ret_val;
}
-uint8* regfi_parse_data(REGF_FILE* file, uint32 offset, uint32 length, bool strict)
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
+ uint32 data_type, uint32 offset,
+ uint32 length, uint32 max_size,
+ bool data_in_offset, bool strict)
{
- uint8* ret_val;
+ REGFI_BUFFER ret_val;
uint32 read_length, cell_length;
uint8 i;
bool unalloc;
-
- /* The data is stored in the offset if the size <= 4 */
- if (length & VK_DATA_IN_OFFSET)
+
+ /* The data is typically stored in the offset if the size <= 4 */
+ if(data_in_offset)
{
- length = length & ~VK_DATA_IN_OFFSET;
if(length > 4)
- return NULL;
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
+ " while parsing data record at offset 0x%.8X.",
+ offset);
+ goto fail;
+ }
- if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
- return NULL;
+ if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+ goto fail;
+ ret_val.len = length;
- offset = offset - REGF_BLOCKSIZE;
for(i = 0; i < length; i++)
- ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
+ ret_val.buf[i] = (uint8)((offset >> i*8) & 0xFF);
}
else
{
if(!regfi_parse_cell(file->fd, offset, NULL, 0,
&cell_length, &unalloc))
- return NULL;
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+ " parsing data record at offset 0x%.8X.", offset);
+ goto fail;
+ }
if((cell_length & 0xFFFFFFF8) != cell_length)
- return NULL;
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
+ " while parsing data record at offset 0x%.8X.",
+ offset);
+ goto fail;
+ }
- if(cell_length - 4 < length)
+ if(cell_length > max_size)
{
- /* XXX: This strict condition has been triggered in multiple registries.
- * Not sure the cause, but the data length values are very large,
- * such as 53392.
- */
+ regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
+ " while parsing data record at offset 0x%.8X.",
+ offset);
if(strict)
- return NULL;
+ goto fail;
else
- length = cell_length - 4;
+ cell_length = max_size;
}
- /* XXX: There is currently no check to ensure the data
- * cell doesn't cross HBIN boundary.
- */
+ if(cell_length - 4 < length)
+ {
+ /* XXX: All big data records thus far have been 16 bytes long.
+ * Should we check for this precise size instead of just
+ * relying upon the above check?
+ */
+ if (file->major_version >= 1 && file->minor_version >= 5)
+ {
+ /* Attempt to parse a big data record */
+ return regfi_load_big_data(file, offset, length, cell_length, strict);
+ }
+ else
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than"
+ " remaining cell length (0x%.8X)"
+ " while parsing data record at offset 0x%.8X.",
+ length, cell_length - 4, offset);
+ if(strict)
+ goto fail;
+ else
+ length = cell_length - 4;
+ }
+ }
- if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
- return NULL;
+ if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+ goto fail;
+ ret_val.len = length;
read_length = length;
- if((regfi_read(file->fd, ret_val, &read_length) != 0)
+ if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
|| read_length != length)
{
- free(ret_val);
- return NULL;
+ regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
+ " parsing data record at offset 0x%.8X.", offset);
+ talloc_free(ret_val.buf);
+ goto fail;
+ }
+ }
+
+ return ret_val;
+
+ fail:
+ ret_val.buf = NULL;
+ ret_val.len = 0;
+ return ret_val;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
+ uint32 offset, uint32 data_length,
+ uint32 cell_length, bool strict)
+{
+ REGFI_BUFFER ret_val;
+ uint16 num_chunks, i;
+ uint32 indirect_offset, indirect_length, chunk_length, chunk_offset;
+ uint32 read_length, data_left;
+ bool unalloc;
+ uint32* indirect_ptrs;
+ uint8* big_data_cell = (uint8*)malloc(cell_length*sizeof(uint8));
+ if(big_data_cell == NULL)
+ goto fail;
+
+ if(!regfi_parse_cell(file->fd, offset, big_data_cell, cell_length-4,
+ &cell_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+ " parsing big data record at offset 0x%.8X.", offset);
+ free(big_data_cell);
+ goto fail;
+ }
+
+ if((big_data_cell[0] != 'd') || (big_data_cell[1] != 'b'))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
+ " (0x%.2X, 0x%.2X) encountered while parsing"
+ " big data record at offset 0x%.8X.",
+ big_data_cell[0], big_data_cell[1], offset);
+ free(big_data_cell);
+ goto fail;
+ }
+ num_chunks = SVAL(big_data_cell, 0x2);
+ /* XXX: Should check sanity of pointers here and in the indirect
+ * block. At least ensure they are a multiple of 8.
+ */
+ indirect_offset = IVAL(big_data_cell, 0x4) + REGFI_REGF_SIZE;
+ free(big_data_cell);
+
+ indirect_ptrs = (uint32*)malloc(num_chunks*sizeof(uint32));
+ if(indirect_ptrs == NULL)
+ goto fail;
+
+ if(!regfi_parse_cell(file->fd, indirect_offset, (uint8*)indirect_ptrs,
+ num_chunks*sizeof(uint32),
+ &indirect_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+ " parsing big data indirect record at offset 0x%.8X.",
+ offset);
+ free(indirect_ptrs);
+ goto fail;
+ }
+
+ if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+ {
+ free(indirect_ptrs);
+ goto fail;
+ }
+ data_left = data_length;
+
+ for(i=0; (i<num_chunks) && (data_left>0); i++)
+ {
+ /* Fix endianness of pointer and convert to physical offset */
+ chunk_offset = IVAL(indirect_ptrs, i*4) + REGFI_REGF_SIZE;
+ if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
+ &chunk_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+ " parsing big data chunk at offset 0x%.8X.",
+ chunk_offset);
+ if(strict)
+ goto chunk_fail;
+ else
+ break;
}
+
+ /* XXX: This should be "chunk_length-4" to account for the 4 byte cell
+ * length. However, it has been observed that some (all?) chunks
+ * have an additional 4 bytes of 0 at the end of their cells that
+ * isn't part of the data, so we're trimming that off too.
+ */
+ if(chunk_length-8 >= data_left)
+ read_length = data_left;
+ else
+ read_length = chunk_length-8;
+
+ if(regfi_read(file->fd, ret_val.buf+(data_length-data_left),
+ &read_length) != 0)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
+ " constructing big data at offset 0x%.8X.",
+ chunk_offset);
+ if(strict)
+ goto chunk_fail;
+ else
+ break;
+ }
+
+ data_left -= read_length;
}
+ free(indirect_ptrs);
+ ret_val.len = data_length-data_left;
+
+ return ret_val;
+ chunk_fail:
+ free(indirect_ptrs);
+ talloc_free(ret_val.buf);
+ fail:
+ ret_val.buf = NULL;
+ ret_val.len = 0;
return ret_val;
}
-range_list* regfi_parse_unalloc_cells(REGF_FILE* file)
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
{
range_list* ret_val;
- REGF_HBIN* hbin;
+ REGFI_HBIN* hbin;
const range_list_element* hbins_elem;
uint32 i, num_hbins, curr_off, cell_len;
bool is_unalloc;
@@ -1829,9 +2456,9 @@ range_list* regfi_parse_unalloc_cells(REGF_FILE* file)
hbins_elem = range_list_get(file->hbins, i);
if(hbins_elem == NULL)
break;
- hbin = (REGF_HBIN*)hbins_elem->data;
+ hbin = (REGFI_HBIN*)hbins_elem->data;
- curr_off = HBIN_HEADER_REC_SIZE;
+ curr_off = REGFI_HBIN_HEADER_SIZE;
while(curr_off < hbin->block_size)
{
if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
@@ -1839,9 +2466,13 @@ range_list* regfi_parse_unalloc_cells(REGF_FILE* file)
break;
if((cell_len == 0) || ((cell_len & 0xFFFFFFF8) != cell_len))
- /* XXX: should report an error here. */
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
+ " while parsing unallocated cells at offset 0x%.8X.",
+ hbin->file_off+curr_off);
break;
-
+ }
+
/* for some reason the record_size of the last record in
an hbin block can extend past the end of the block
even though the record fits within the remaining
diff --git a/lib/smb_deps.c b/lib/smb_deps.c
index da7345d..eb09aa4 100644
--- a/lib/smb_deps.c
+++ b/lib/smb_deps.c
@@ -3,7 +3,7 @@
* depends upon, from the Samba Subversion tree. See:
* http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
*
- * Copyright (C) 2005-2006 Timothy D. Morgan
+ * Copyright (C) 2005-2006,2009 Timothy D. Morgan
* Copyright (C) 1992-2005 Samba development team
* (see individual files under Subversion for details.)
*
@@ -20,10 +20,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: smb_deps.c 111 2008-05-01 04:06:22Z tim $
+ * $Id: smb_deps.c 147 2009-02-22 19:31:52Z tim $
*/
-#include "../include/smb_deps.h"
+#include "smb_deps.h"
/* These act as replacements for numerous Samba memory allocation
@@ -32,8 +32,9 @@
void* zalloc(size_t size)
{
void* ret_val = NULL;
- if((ret_val = (void*)malloc(size)) != NULL)
+ if((size > 0) && (ret_val = (void*)malloc(size)) != NULL)
memset(ret_val, 0, size);
+
return ret_val;
}
@@ -141,1133 +142,3 @@ time_t nt_time_to_unix(const NTTIME* nt)
}
/* End of stuff from lib/time.c */
-
-/* From parse_prs.c */
-
-/*******************************************************************
- Attempt, if needed, to grow a data buffer.
- Also depends on the data stream mode (io).
- ********************************************************************/
-bool prs_grow(prs_struct *ps, uint32 extra_space)
-{
- uint32 new_size;
- char *new_data;
-
- ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
-
- if(ps->data_offset + extra_space <= ps->buffer_size)
- return true;
-
- /*
- * We cannot grow the buffer if we're not reading
- * into the prs_struct, or if we don't own the memory.
- */
-
- if(ps->io || !ps->is_dynamic)
- return false;
-
- /*
- * Decide how much extra space we really need.
- */
- extra_space -= (ps->buffer_size - ps->data_offset);
- if(ps->buffer_size == 0)
- {
- /*
- * Ensure we have at least a PDU's length, or extra_space,
- * whichever is greater.
- */
- new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
-
- if((new_data = zalloc(new_size)) == NULL)
- return false;
- }
- else
- {
- /*
- * If the current buffer size is bigger than the space needed, just
- * double it, else add extra_space.
- */
- new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);
-
- if ((new_data = (char*)realloc(ps->data_p, new_size)) == NULL)
- return false;
-
- memset(&new_data[ps->buffer_size], '\0',
- (size_t)(new_size - ps->buffer_size));
- }
- ps->buffer_size = new_size;
- ps->data_p = new_data;
-
- return true;
-}
-
-
-/*******************************************************************
- Align a the data_len to a multiple of align bytes - filling with
- zeros.
- ********************************************************************/
-bool prs_align(prs_struct *ps)
-{
- uint32 mod = ps->data_offset & (ps->align-1);
-
- if (ps->align != 0 && mod != 0)
- {
- uint32 extra_space = (ps->align - mod);
- if(!prs_grow(ps, extra_space))
- return false;
- memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
- ps->data_offset += extra_space;
- }
-
- return true;
-}
-
-
-/**
- * Initialise an expandable parse structure.
- *
- * @param size Initial buffer size. If >0, a new buffer will be
- * created with malloc().
- *
- * @return false if allocation fails, otherwise true.
- **/
-
-bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io)
-{
- if(ps == NULL)
- return false;
- memset(ps, 0, sizeof(prs_struct));
-
- ps->io = io;
- ps->bigendian_data = RPC_LITTLE_ENDIAN;
- ps->align = RPC_PARSE_ALIGN;
- ps->is_dynamic = false;
- ps->data_offset = 0;
- ps->buffer_size = 0;
- ps->data_p = NULL;
- ps->mem_ctx = ctx;
-
- if (size != 0)
- {
- ps->buffer_size = size;
- if((ps->data_p = (char *)zalloc((size_t)size)) == NULL)
- return false;
-
- ps->is_dynamic = true; /* We own this memory. */
- }
-
- return true;
-}
-
-
-char *prs_mem_get(prs_struct *ps, uint32 extra_size)
-{
- if(ps->io)
- {
- /*
- * If reading, ensure that we can read the requested size item.
- */
- if (ps->data_offset + extra_size > ps->buffer_size)
- return NULL;
- }
- else
- {
- /*
- * Writing - grow the buffer if needed.
- */
- if(!prs_grow(ps, extra_size))
- return NULL;
- }
-
- return &ps->data_p[ps->data_offset];
-}
-
-
-/*******************************************************************
- Stream a uint32.
- ********************************************************************/
-bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
-{
- char *q = prs_mem_get(ps, sizeof(uint32));
- if (q == NULL)
- return false;
-
- if (ps->io)
- {
- if (ps->bigendian_data)
- *data32 = RIVAL(q,0);
- else
- *data32 = IVAL(q,0);
- }
- else
- {
- if (ps->bigendian_data)
- RSIVAL(q,0,*data32);
- else
- SIVAL(q,0,*data32);
- }
- ps->data_offset += sizeof(uint32);
-
- return true;
-}
-
-
-/******************************************************************
- Stream an array of uint32s. Length is number of uint32s.
- ********************************************************************/
-bool prs_uint32s(const char *name, prs_struct *ps,
- int depth, uint32 *data32s, int len)
-{
- int i;
- char *q = prs_mem_get(ps, len * sizeof(uint32));
- if (q == NULL)
- return false;
-
- if (ps->io)
- {
- if (ps->bigendian_data)
- {
- for (i = 0; i < len; i++)
- data32s[i] = RIVAL(q, 4*i);
- }
- else
- {
- for (i = 0; i < len; i++)
- data32s[i] = IVAL(q, 4*i);
- }
- }
- else
- {
- if (ps->bigendian_data)
- {
- for (i = 0; i < len; i++)
- RSIVAL(q, 4*i, data32s[i]);
- }
- else
- {
- for (i = 0; i < len; i++)
- SIVAL(q, 4*i, data32s[i]);
- }
- }
- ps->data_offset += (len * sizeof(uint32));
-
- return true;
-}
-
-
-/*******************************************************************
- Stream a uint16.
- ********************************************************************/
-bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
-{
- char *q = prs_mem_get(ps, sizeof(uint16));
- if (q == NULL)
- return false;
-
- if (ps->io)
- {
- if (ps->bigendian_data)
- *data16 = RSVAL(q,0);
- else
- *data16 = SVAL(q,0);
- }
- else
- {
- if (ps->bigendian_data)
- RSSVAL(q,0,*data16);
- else
- SSVAL(q,0,*data16);
- }
- ps->data_offset += sizeof(uint16);
-
- return true;
-}
-
-
-/*******************************************************************
- prs_uint16 wrapper. Call this and it sets up a pointer to where the
- uint16 should be stored, or gets the size if reading.
- ********************************************************************/
-bool prs_uint16_pre(const char *name, prs_struct *ps, int depth,
- uint16 *data16, uint32 *offset)
-{
- *offset = ps->data_offset;
- if (ps->io)
- {
- /* reading. */
- return prs_uint16(name, ps, depth, data16);
- }
- else
- {
- char *q = prs_mem_get(ps, sizeof(uint16));
- if(q ==NULL)
- return false;
- ps->data_offset += sizeof(uint16);
- }
- return true;
-}
-
-
-/*******************************************************************
- prs_uint16 wrapper. call this and it retrospectively stores the size.
- does nothing on reading, as that is already handled by ...._pre()
- ********************************************************************/
-bool prs_uint16_post(const char *name, prs_struct *ps, int depth,
- uint16 *data16, uint32 ptr_uint16, uint32 start_offset)
-{
- if (!ps->io)
- {
- /*
- * Writing - temporarily move the offset pointer.
- */
- uint16 data_size = ps->data_offset - start_offset;
- uint32 old_offset = ps->data_offset;
-
- ps->data_offset = ptr_uint16;
- if(!prs_uint16(name, ps, depth, &data_size))
- {
- ps->data_offset = old_offset;
- return false;
- }
- ps->data_offset = old_offset;
- }
- else
- ps->data_offset = start_offset + (uint32)(*data16);
-
- return true;
-}
-
-
-/*******************************************************************
- Stream a uint8.
- ********************************************************************/
-bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
-{
- char *q = prs_mem_get(ps, 1);
- if (q == NULL)
- return false;
-
- if (ps->io)
- *data8 = CVAL(q,0);
- else
- SCVAL(q,0,*data8);
-
- ps->data_offset += 1;
-
- return true;
-}
-
-
-/******************************************************************
- Stream an array of uint8s. Length is number of uint8s.
- ********************************************************************/
-bool prs_uint8s(const char *name, prs_struct *ps, int depth,
- uint8* data8s, int len)
-{
- int i;
- char *q = prs_mem_get(ps, len);
- if (q == NULL)
- return false;
-
- if (ps->io)
- {
- for (i = 0; i < len; i++)
- data8s[i] = CVAL(q,i);
- }
- else
- {
- for (i = 0; i < len; i++)
- SCVAL(q, i, data8s[i]);
- }
-
- ps->data_offset += len;
-
- return true;
-}
-
-
-/*******************************************************************
- Set the current offset (external interface).
- ********************************************************************/
-bool prs_set_offset(prs_struct *ps, uint32 offset)
-{
- if(offset <= ps->data_offset)
- {
- ps->data_offset = offset;
- return true;
- }
-
- if(!prs_grow(ps, offset - ps->data_offset))
- return false;
-
- ps->data_offset = offset;
- return true;
-}
-
-/* End of stuff from parse_prs.c */
-
-/* From rpc_parse/parse_misc.c */
-
-/*******************************************************************
- Reads or writes a struct uuid
-********************************************************************/
-bool smb_io_uuid(const char *desc, struct uuid *uuid,
- prs_struct *ps, int depth)
-{
- if (uuid == NULL)
- return false;
- depth++;
-
- if(!prs_uint32 ("data ", ps, depth, &uuid->time_low))
- return false;
- if(!prs_uint16 ("data ", ps, depth, &uuid->time_mid))
- return false;
- if(!prs_uint16 ("data ", ps, depth, &uuid->time_hi_and_version))
- return false;
-
- if(!prs_uint8s ("data ", ps, depth,
- uuid->clock_seq, sizeof(uuid->clock_seq)))
- return false;
-
- if(!prs_uint8s ("data ", ps, depth, uuid->node, sizeof(uuid->node)))
- return false;
-
- return true;
-}
-
-
-/*******************************************************************
- Reads or writes an NTTIME structure.
-********************************************************************/
-bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
-{
- if (nttime == NULL)
- return false;
- depth++;
-
- if(!prs_align(ps))
- return false;
-
- if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
- return false;
- if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
- return false;
-
- return true;
-}
-
-
-/*******************************************************************
- Reads or writes a DOM_SID structure.
-********************************************************************/
-bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
-{
- int i;
-
- if (sid == NULL)
- return false;
- depth++;
-
- if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
- return false;
-
- if(!prs_uint8 ("num_auths ", ps, depth, &sid->num_auths))
- return false;
-
- for (i = 0; i < 6; i++)
- {
- fstring tmp;
- snprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
- if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
- return false;
- }
-
- /* oops! XXXX should really issue a warning here... */
- if (sid->num_auths > MAXSUBAUTHS)
- sid->num_auths = MAXSUBAUTHS;
-
- if(!prs_uint32s("sub_auths ", ps, depth,
- sid->sub_auths, sid->num_auths))
- { return false; }
-
- return true;
-}
-
-/* End of stuff from rpc_parse/parse_misc.c */
-
-/* From lib/util_sid.c */
-
-/*****************************************************************
- Calculates size of a sid.
-*****************************************************************/
-size_t sid_size(const DOM_SID *sid)
-{
- if (sid == NULL)
- return 0;
-
- return sid->num_auths * sizeof(uint32) + 8;
-}
-
-
-/*****************************************************************
- Compare the auth portion of two sids.
-*****************************************************************/
-int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2)
-{
- int i;
-
- if (sid1 == sid2)
- return 0;
- if (!sid1)
- return -1;
- if (!sid2)
- return 1;
-
- if (sid1->sid_rev_num != sid2->sid_rev_num)
- return sid1->sid_rev_num - sid2->sid_rev_num;
-
- for (i = 0; i < 6; i++)
- if (sid1->id_auth[i] != sid2->id_auth[i])
- return sid1->id_auth[i] - sid2->id_auth[i];
-
- return 0;
-}
-
-
-/*****************************************************************
- Compare two sids.
-*****************************************************************/
-int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
-{
- int i;
-
- if (sid1 == sid2)
- return 0;
- if (!sid1)
- return -1;
- if (!sid2)
- return 1;
-
- /* Compare most likely different rids, first: i.e start at end */
- if (sid1->num_auths != sid2->num_auths)
- return sid1->num_auths - sid2->num_auths;
-
- for (i = sid1->num_auths-1; i >= 0; --i)
- if (sid1->sub_auths[i] != sid2->sub_auths[i])
- return sid1->sub_auths[i] - sid2->sub_auths[i];
-
- return sid_compare_auth(sid1, sid2);
-}
-
-
-/*****************************************************************
- Compare two sids.
-*****************************************************************/
-bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
-{
- return sid_compare(sid1, sid2) == 0;
-}
-
-/* End of stuff from lib/util_sid.c */
-
-/* From lib/secace.c */
-
-/*******************************************************************
- Check if ACE has OBJECT type.
-********************************************************************/
-
-bool sec_ace_object(uint8 type)
-{
- if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
- type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
- type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
- type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
- return true;
- }
- return false;
-}
-
-/* End of stuff from lib/secace.c */
-
-/* From rpc_parse/parse_sec.c */
-
-/*******************************************************************
- Reads or writes a SEC_ACCESS structure.
-********************************************************************/
-bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
-{
- if (t == NULL)
- return false;
-
- depth++;
-
- if(!prs_uint32("mask", ps, depth, &t->mask))
- return false;
-
- return true;
-}
-
-
-/*******************************************************************
- Reads or writes a SEC_ACE structure.
-********************************************************************/
-bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
-{
- uint32 old_offset;
- uint32 offset_ace_size;
-
- if (psa == NULL)
- return false;
-
- depth++;
-
- old_offset = ps->data_offset;
-
- if(!prs_uint8("type ", ps, depth, &psa->type))
- return false;
-
- if(!prs_uint8("flags", ps, depth, &psa->flags))
- return false;
-
- if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
- return false;
-
- if(!sec_io_access("info ", &psa->info, ps, depth))
- return false;
-
- /* check whether object access is present */
- if (!sec_ace_object(psa->type))
- {
- if (!smb_io_dom_sid("trustee ", &psa->trustee , ps, depth))
- return false;
- }
- else
- {
- if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
- return false;
-
- if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
- if (!smb_io_uuid("obj_guid", &psa->obj_guid, ps,depth))
- return false;
-
- if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
- if (!smb_io_uuid("inh_guid", &psa->inh_guid, ps,depth))
- return false;
-
- if(!smb_io_dom_sid("trustee ", &psa->trustee , ps, depth))
- return false;
- }
-
- /* Theorectically an ACE can have a size greater than the
- * sum of its components. When marshalling, pad with extra null bytes
- * up to the
- * correct size.
- */
- if (!ps->io && (psa->size > ps->data_offset - old_offset))
- {
- uint32 extra_len = psa->size - (ps->data_offset - old_offset);
- uint32 i;
- uint8 c = 0;
-
- for (i = 0; i < extra_len; i++)
- {
- if (!prs_uint8("ace extra space", ps, depth, &c))
- return false;
- }
- }
-
- if(!prs_uint16_post("size ", ps, depth, &psa->size,
- offset_ace_size, old_offset))
- { return false; }
-
- return true;
-}
-
-
-/*******************************************************************
- Reads or writes a SEC_ACL structure.
-
- First of the xx_io_xx functions that allocates its data structures
- for you as it reads them.
-********************************************************************/
-bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
-{
- unsigned int i;
- uint32 old_offset;
- uint32 offset_acl_size;
- SEC_ACL* psa;
-
- /*
- * Note that the size is always a multiple of 4 bytes due to the
- * nature of the data structure. Therefore the prs_align() calls
- * have been removed as they through us off when doing two-layer
- * marshalling such as in the printing code (RPC_BUFFER). --jerry
- */
-
- if (ppsa == NULL || ps == NULL)
- return false;
-
- psa = *ppsa;
-
- if(ps->io && psa == NULL)
- {
- /*
- * This is a read and we must allocate the stuct to read into.
- */
- if((psa = (SEC_ACL*)zalloc(sizeof(SEC_ACL))) == NULL)
- return false;
- *ppsa = psa;
- }
-
- depth++;
- old_offset = ps->data_offset;
-
- if(!prs_uint16("revision", ps, depth, &psa->revision)
- || !prs_uint16_pre("size ", ps, depth, &psa->size, &offset_acl_size)
- || !prs_uint32("num_aces ", ps, depth, &psa->num_aces))
- {
- free(psa);
- *ppsa = NULL;
- return false;
- }
-
- if (ps->io)
- {
- /*
- * Even if the num_aces is zero, allocate memory as there's a difference
- * between a non-present DACL (allow all access) and a DACL with no ACE's
- * (allow no access).
- */
- if((psa->ace = (SEC_ACE*)zcalloc(sizeof(SEC_ACE), psa->num_aces+1)) == NULL)
- {
- free(psa);
- *ppsa = NULL;
- return false;
- }
- }
-
- for (i = 0; i < psa->num_aces; i++)
- {
- fstring tmp;
- snprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
- if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
- {
- free(psa);
- *ppsa = NULL;
- return false;
- }
- }
-
- /* Theoretically an ACL can have a size greater than the
- * sum of its components. When marshalling, pad with extra null
- * bytes up to the
- * correct size.
- */
- if (!ps->io && (psa->size > ps->data_offset - old_offset))
- {
- uint32 extra_len = psa->size - (ps->data_offset - old_offset);
- uint8 c = 0;
-
- for (i = 0; i < extra_len; i++)
- {
- if (!prs_uint8("acl extra space", ps, depth, &c))
- {
- free(psa);
- *ppsa = NULL;
- return false;
- }
- }
- }
-
- if(!prs_uint16_post("size ", ps, depth, &psa->size,
- offset_acl_size, old_offset))
- {
- free(psa);
- *ppsa = NULL;
- return false;
- }
-
- return true;
-}
-
-
-/*******************************************************************
- Reads or writes a SEC_DESC structure.
- If reading and the *ppsd = NULL, allocates the structure.
-********************************************************************/
-bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
-{
- uint32 old_offset;
- uint32 max_offset = 0; /* after we're done, move offset to end */
- uint32 tmp_offset = 0;
-
- SEC_DESC *psd;
-
- if (ppsd == NULL || ps == NULL)
- return false;
-
- psd = *ppsd;
- if (psd == NULL)
- {
- if(ps->io)
- {
- if((psd = (SEC_DESC*)zalloc(sizeof(SEC_DESC))) == NULL)
- return false;
- *ppsd = psd;
- }
- else
- {
- /* Marshalling - just ignore. */
- return true;
- }
- }
-
- depth++;
-
- /* start of security descriptor stored for back-calc offset purposes */
- old_offset = ps->data_offset;
-
- if(!prs_uint16("revision ", ps, depth, &psd->revision)
- || !prs_uint16("type ", ps, depth, &psd->type))
- {
- free(psd);
- *ppsd = NULL;
- return false;
- }
-
- if (!ps->io)
- {
- uint32 offset = SEC_DESC_HEADER_SIZE;
-
- /*
- * Work out the offsets here, as we write it out.
- */
-
- if (psd->sacl != NULL)
- {
- psd->off_sacl = offset;
- offset += psd->sacl->size;
- }
- else
- psd->off_sacl = 0;
-
- if (psd->dacl != NULL)
- {
- psd->off_dacl = offset;
- offset += psd->dacl->size;
- }
- else
- psd->off_dacl = 0;
-
- if (psd->owner_sid != NULL)
- {
- psd->off_owner_sid = offset;
- offset += sid_size(psd->owner_sid);
- }
- else
- psd->off_owner_sid = 0;
-
- if (psd->grp_sid != NULL)
- {
- psd->off_grp_sid = offset;
- offset += sid_size(psd->grp_sid);
- }
- else
- psd->off_grp_sid = 0;
- }
-
- if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid)
- || !prs_uint32("off_grp_sid ", ps, depth, &psd->off_grp_sid)
- || !prs_uint32("off_sacl ", ps, depth, &psd->off_sacl)
- || !prs_uint32("off_dacl ", ps, depth, &psd->off_dacl))
- {
- free(psd);
- *ppsd = NULL;
- return false;
- }
- max_offset = MAX(max_offset, ps->data_offset);
-
- if (psd->off_owner_sid != 0)
- {
- tmp_offset = ps->data_offset;
- if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
- {
- free(psd);
- *ppsd = NULL;
- return false;
- }
-
- if (ps->io)
- {
- /* reading */
- if((psd->owner_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
- {
- free(psd);
- *ppsd = NULL;
- return false;
- }
- }
-
- if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
- {
- if(ps->io)
- free(psd->owner_sid);
- free(psd);
- *ppsd = NULL;
- return false;
- }
-
- max_offset = MAX(max_offset, ps->data_offset);
-
- if (!prs_set_offset(ps,tmp_offset))
- {
- if(ps->io)
- free(psd->owner_sid);
- free(psd);
- *ppsd = NULL;
- return false;
- }
- }
-
- if (psd->off_grp_sid != 0)
- {
- tmp_offset = ps->data_offset;
- if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
- {
- if(ps->io)
- free(psd->owner_sid);
- free(psd);
- *ppsd = NULL;
- return false;
- }
-
- if (ps->io)
- {
- /* reading */
- if((psd->grp_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
- {
- free(psd->owner_sid);
- free(psd);
- *ppsd = NULL;
- return false;
- }
- }
-
- if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
- {
- if(ps->io)
- {
- free(psd->grp_sid);
- free(psd->owner_sid);
- }
- free(psd);
- *ppsd = NULL;
- return false;
- }
-
- max_offset = MAX(max_offset, ps->data_offset);
-
- if (!prs_set_offset(ps,tmp_offset))
- {
- if(ps->io)
- {
- free(psd->grp_sid);
- free(psd->owner_sid);
- }
- free(psd);
- *ppsd = NULL;
- return false;
- }
- }
-
- if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl)
- {
- tmp_offset = ps->data_offset;
- if(!prs_set_offset(ps, old_offset + psd->off_sacl)
- || !sec_io_acl("sacl", &psd->sacl, ps, depth))
- {
- if(ps->io)
- {
- free(psd->grp_sid);
- free(psd->owner_sid);
- }
- free(psd);
- *ppsd = NULL;
- return false;
- }
- max_offset = MAX(max_offset, ps->data_offset);
- if (!prs_set_offset(ps,tmp_offset))
- {
- if(ps->io)
- {
- free(psd->grp_sid);
- free(psd->owner_sid);
- }
- free(psd);
- *ppsd = NULL;
- return false;
- }
- }
-
- if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0)
- {
- tmp_offset = ps->data_offset;
- if(!prs_set_offset(ps, old_offset + psd->off_dacl)
- || !sec_io_acl("dacl", &psd->dacl, ps, depth))
- {
- if(ps->io)
- {
- free(psd->grp_sid);
- free(psd->owner_sid);
- }
- free(psd);
- *ppsd = NULL;
- return false;
- }
- max_offset = MAX(max_offset, ps->data_offset);
- if (!prs_set_offset(ps,tmp_offset))
- {
- if(ps->io)
- {
- free(psd->grp_sid);
- free(psd->owner_sid);
- }
- free(psd);
- *ppsd = NULL;
- return false;
- }
- }
-
- if(!prs_set_offset(ps, max_offset))
- {
- if(ps->io)
- {
- free(psd->grp_sid);
- free(psd->owner_sid);
- }
- free(psd);
- *ppsd = NULL;
- return false;
- }
-
- return true;
-}
-
-/* End of stuff from rpc_parse/parse_sec.c */
-
-/* From lib/secace.c */
-
-/*******************************************************************
- Compares two SEC_ACE structures
-********************************************************************/
-bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
-{
- /* Trivial cases */
- if (!s1 && !s2)
- return true;
- if (!s1 || !s2)
- return false;
-
- /* Check top level stuff */
- if (s1->type != s2->type || s1->flags != s2->flags ||
- s1->info.mask != s2->info.mask)
- { return false; }
-
- /* Check SID */
- if (!sid_equal(&s1->trustee, &s2->trustee))
- return false;
-
- return true;
-}
-
-/* End of stuff from lib/secace.c */
-
-/* From lib/secacl.c */
-
-/*******************************************************************
- Compares two SEC_ACL structures
-********************************************************************/
-
-bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
-{
- unsigned int i, j;
-
- /* Trivial cases */
- if (!s1 && !s2)
- return true;
- if (!s1 || !s2)
- return false;
-
- /* Check top level stuff */
- if (s1->revision != s2->revision)
- return false;
-
- if (s1->num_aces != s2->num_aces)
- return false;
-
- /* The ACEs could be in any order so check each ACE in s1 against
- each ACE in s2. */
-
- for (i = 0; i < s1->num_aces; i++)
- {
- bool found = false;
-
- for (j = 0; j < s2->num_aces; j++)
- {
- if (sec_ace_equal(&s1->ace[i], &s2->ace[j]))
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- return false;
- }
-
- return true;
-}
-
-/* End of stuff from lib/secacl.c */
-
-/* From lib/secdesc.c */
-
-/*******************************************************************
- Compares two SEC_DESC structures
-********************************************************************/
-bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
-{
- /* Trivial cases */
- if (!s1 && !s2)
- return true;
- if (!s1 || !s2)
- return false;
-
- /* Check top level stuff */
- if (s1->revision != s2->revision)
- return false;
-
- if (s1->type!= s2->type)
- return false;
-
- /* Check owner and group */
- if (!sid_equal(s1->owner_sid, s2->owner_sid))
- return false;
-
- if (!sid_equal(s1->grp_sid, s2->grp_sid))
- return false;
-
- /* Check ACLs present in one but not the other */
- if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
- (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl))
- { return false; }
-
- /* Sigh - we have to do it the hard way by iterating over all
- the ACEs in the ACLs */
- if(!sec_acl_equal(s1->dacl, s2->dacl) || !sec_acl_equal(s1->sacl, s2->sacl))
- return false;
-
- return true;
-}
-
-/* End of stuff from lib/secdesc.c */
diff --git a/lib/talloc.c b/lib/talloc.c
new file mode 100644
index 0000000..faa4378
--- /dev/null
+++ b/lib/talloc.c
@@ -0,0 +1,1696 @@
+/*
+ Samba Unix SMB/CIFS implementation.
+
+ Samba trivial allocation library - new interface
+ inspired by http://swapped.cc/halloc/
+
+ NOTE: Please read talloc_guide.txt for full documentation
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Stefan Metzmacher 2006
+ Copyright (C) Timothy D. Morgan 2009
+
+ ** NOTE! The following LGPL license applies to the talloc library.
+ ** This does NOT imply that all of reglookup is released under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+/* $Id: talloc.c 147 2009-02-22 19:31:52Z tim $ */
+
+#include "talloc.h"
+
+/* use this to force every realloc to change the pointer, to stress test
+ code that might not cope */
+#define ALWAYS_REALLOC 0
+
+
+#define MAX_TALLOC_SIZE 0x10000000
+#define TALLOC_MAGIC 0xe814ec70
+#define TALLOC_FLAG_FREE 0x01
+#define TALLOC_FLAG_LOOP 0x02
+#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */
+#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */
+#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+
+/* by default we abort when given a bad pointer (such as when talloc_free() is called
+ on a pointer that came from malloc() */
+#ifndef TALLOC_ABORT
+#define TALLOC_ABORT(reason) abort()
+#endif
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+/* these macros gain us a few percent of speed on gcc */
+#if (__GNUC__ >= 3)
+/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
+ as its first argument */
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#else
+#define likely(x) x
+#define unlikely(x) x
+#endif
+
+/* this null_context is only used if talloc_enable_leak_report() or
+ talloc_enable_leak_report_full() is called, otherwise it remains
+ NULL
+*/
+static void *null_context;
+static void *autofree_context;
+
+struct talloc_reference_handle {
+ struct talloc_reference_handle *next, *prev;
+ void *ptr;
+};
+
+typedef int (*talloc_destructor_t)(void *);
+
+struct talloc_chunk {
+ struct talloc_chunk *next, *prev;
+ struct talloc_chunk *parent, *child;
+ struct talloc_reference_handle *refs;
+ talloc_destructor_t destructor;
+ const char *name;
+ size_t size;
+ unsigned flags;
+
+ /*
+ * "pool" has dual use:
+ *
+ * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
+ * marks the end of the currently allocated area.
+ *
+ * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
+ * is a pointer to the struct talloc_chunk of the pool that it was
+ * allocated from. This way children can quickly find the pool to chew
+ * from.
+ */
+ void *pool;
+};
+
+/* 16 byte alignment seems to keep everyone happy */
+#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
+#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+
+static void talloc_abort_double_free(void)
+{
+ TALLOC_ABORT("Bad talloc magic value - double free");
+}
+
+static void talloc_abort_unknown_value(void)
+{
+ TALLOC_ABORT("Bad talloc magic value - unknown value");
+}
+
+/* panic if we get a bad magic value */
+static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
+{
+ const char *pp = (const char *)ptr;
+ struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
+ if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
+ if (tc->flags & TALLOC_FLAG_FREE) {
+ talloc_abort_double_free();
+ } else {
+ talloc_abort_unknown_value();
+ }
+ }
+ return tc;
+}
+
+/* hook into the front of the list */
+#define _TLIST_ADD(list, p) \
+do { \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ (list)->prev = (p); \
+ (p)->next = (list); \
+ (p)->prev = NULL; \
+ (list) = (p); \
+ }\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define _TLIST_REMOVE(list, p) \
+do { \
+ if ((p) == (list)) { \
+ (list) = (p)->next; \
+ if (list) (list)->prev = NULL; \
+ } else { \
+ if ((p)->prev) (p)->prev->next = (p)->next; \
+ if ((p)->next) (p)->next->prev = (p)->prev; \
+ } \
+ if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+
+
+/*
+ return the parent chunk of a pointer
+*/
+static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
+{
+ struct talloc_chunk *tc;
+
+ if (unlikely(ptr == NULL)) {
+ return NULL;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+ while (tc->prev) tc=tc->prev;
+
+ return tc->parent;
+}
+
+void *talloc_parent(const void *ptr)
+{
+ struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+ return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
+}
+
+/*
+ find parents name
+*/
+const char *talloc_parent_name(const void *ptr)
+{
+ struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+ return tc? tc->name : NULL;
+}
+
+/*
+ A pool carries an in-pool object count count in the first 16 bytes.
+ bytes. This is done to support talloc_steal() to a parent outside of the
+ pool. The count includes the pool itself, so a talloc_free() on a pool will
+ only destroy the pool if the count has dropped to zero. A talloc_free() of a
+ pool member will reduce the count, and eventually also call free(3) on the
+ pool memory.
+
+ The object count is not put into "struct talloc_chunk" because it is only
+ relevant for talloc pools and the alignment to 16 bytes would increase the
+ memory footprint of each talloc chunk by those 16 bytes.
+*/
+
+#define TALLOC_POOL_HDR_SIZE 16
+
+static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
+{
+ return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
+}
+
+/*
+ Allocate from a pool
+*/
+
+static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
+ size_t size)
+{
+ struct talloc_chunk *pool_ctx = NULL;
+ size_t space_left;
+ struct talloc_chunk *result;
+ size_t chunk_size;
+
+ if (parent == NULL) {
+ return NULL;
+ }
+
+ if (parent->flags & TALLOC_FLAG_POOL) {
+ pool_ctx = parent;
+ }
+ else if (parent->flags & TALLOC_FLAG_POOLMEM) {
+ pool_ctx = (struct talloc_chunk *)parent->pool;
+ }
+
+ if (pool_ctx == NULL) {
+ return NULL;
+ }
+
+ space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
+ - ((char *)pool_ctx->pool);
+
+ /*
+ * Align size to 16 bytes
+ */
+ chunk_size = ((size + 15) & ~15);
+
+ if (space_left < chunk_size) {
+ return NULL;
+ }
+
+ result = (struct talloc_chunk *)pool_ctx->pool;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+ VALGRIND_MAKE_MEM_UNDEFINED(result, size);
+#endif
+
+ pool_ctx->pool = (void *)((char *)result + chunk_size);
+
+ result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
+ result->pool = pool_ctx;
+
+ *talloc_pool_objectcount(pool_ctx) += 1;
+
+ return result;
+}
+
+/*
+ Allocate a bit of memory as a child of an existing pointer
+*/
+static inline void *__talloc(const void *context, size_t size)
+{
+ struct talloc_chunk *tc = NULL;
+
+ if (unlikely(context == NULL)) {
+ context = null_context;
+ }
+
+ if (unlikely(size >= MAX_TALLOC_SIZE)) {
+ return NULL;
+ }
+
+ if (context != NULL) {
+ tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
+ TC_HDR_SIZE+size);
+ }
+
+ if (tc == NULL) {
+ tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
+ if (unlikely(tc == NULL)) return NULL;
+ tc->flags = TALLOC_MAGIC;
+ tc->pool = NULL;
+ }
+
+ tc->size = size;
+ tc->destructor = NULL;
+ tc->child = NULL;
+ tc->name = NULL;
+ tc->refs = NULL;
+
+ if (likely(context)) {
+ struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
+
+ if (parent->child) {
+ parent->child->parent = NULL;
+ tc->next = parent->child;
+ tc->next->prev = tc;
+ } else {
+ tc->next = NULL;
+ }
+ tc->parent = parent;
+ tc->prev = NULL;
+ parent->child = tc;
+ } else {
+ tc->next = tc->prev = tc->parent = NULL;
+ }
+
+ return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+ * Create a talloc pool
+ */
+
+void *talloc_pool(const void *context, size_t size)
+{
+ void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
+ struct talloc_chunk *tc;
+
+ if (unlikely(result == NULL)) {
+ return NULL;
+ }
+
+ tc = talloc_chunk_from_ptr(result);
+
+ tc->flags |= TALLOC_FLAG_POOL;
+ tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
+
+ *talloc_pool_objectcount(tc) = 1;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+ VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
+#endif
+
+ return result;
+}
+
+/*
+ setup a destructor to be called on free of a pointer
+ the destructor should return 0 on success, or -1 on failure.
+ if the destructor fails then the free is failed, and the memory can
+ be continued to be used
+*/
+void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
+{
+ struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+ tc->destructor = destructor;
+}
+
+/*
+ increase the reference count on a piece of memory.
+*/
+int talloc_increase_ref_count(const void *ptr)
+{
+ if (unlikely(!talloc_reference(null_context, ptr))) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ helper for talloc_reference()
+
+ this is referenced by a function pointer and should not be inline
+*/
+static int talloc_reference_destructor(struct talloc_reference_handle *handle)
+{
+ struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
+ _TLIST_REMOVE(ptr_tc->refs, handle);
+ return 0;
+}
+
+/*
+ more efficient way to add a name to a pointer - the name must point to a
+ true string constant
+*/
+static inline void _talloc_set_name_const(const void *ptr, const char *name)
+{
+ struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+ tc->name = name;
+}
+
+/*
+ internal talloc_named_const()
+*/
+static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
+{
+ void *ptr;
+
+ ptr = __talloc(context, size);
+ if (unlikely(ptr == NULL)) {
+ return NULL;
+ }
+
+ _talloc_set_name_const(ptr, name);
+
+ return ptr;
+}
+
+/*
+ make a secondary reference to a pointer, hanging off the given context.
+ the pointer remains valid until both the original caller and this given
+ context are freed.
+
+ the major use for this is when two different structures need to reference the
+ same underlying data, and you want to be able to free the two instances separately,
+ and in either order
+*/
+void *_talloc_reference(const void *context, const void *ptr)
+{
+ struct talloc_chunk *tc;
+ struct talloc_reference_handle *handle;
+ if (unlikely(ptr == NULL)) return NULL;
+
+ tc = talloc_chunk_from_ptr(ptr);
+ handle = (struct talloc_reference_handle *)_talloc_named_const(context,
+ sizeof(struct talloc_reference_handle),
+ TALLOC_MAGIC_REFERENCE);
+ if (unlikely(handle == NULL)) return NULL;
+
+ /* note that we hang the destructor off the handle, not the
+ main context as that allows the caller to still setup their
+ own destructor on the context if they want to */
+ talloc_set_destructor(handle, talloc_reference_destructor);
+ handle->ptr = discard_const_p(void, ptr);
+ _TLIST_ADD(tc->refs, handle);
+ return handle->ptr;
+}
+
+
+/*
+ internal talloc_free call
+*/
+static inline int _talloc_free(void *ptr)
+{
+ struct talloc_chunk *tc;
+
+ if (unlikely(ptr == NULL)) {
+ return -1;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (unlikely(tc->refs)) {
+ int is_child;
+ /* check this is a reference from a child or grantchild
+ * back to it's parent or grantparent
+ *
+ * in that case we need to remove the reference and
+ * call another instance of talloc_free() on the current
+ * pointer.
+ */
+ is_child = talloc_is_parent(tc->refs, ptr);
+ _talloc_free(tc->refs);
+ if (is_child) {
+ return _talloc_free(ptr);
+ }
+ return -1;
+ }
+
+ if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+ /* we have a free loop - stop looping */
+ return 0;
+ }
+
+ if (unlikely(tc->destructor)) {
+ talloc_destructor_t d = tc->destructor;
+ if (d == (talloc_destructor_t)-1) {
+ return -1;
+ }
+ tc->destructor = (talloc_destructor_t)-1;
+ if (d(ptr) == -1) {
+ tc->destructor = d;
+ return -1;
+ }
+ tc->destructor = NULL;
+ }
+
+ if (tc->parent) {
+ _TLIST_REMOVE(tc->parent->child, tc);
+ if (tc->parent->child) {
+ tc->parent->child->parent = tc->parent;
+ }
+ } else {
+ if (tc->prev) tc->prev->next = tc->next;
+ if (tc->next) tc->next->prev = tc->prev;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
+ while (tc->child) {
+ /* we need to work out who will own an abandoned child
+ if it cannot be freed. In priority order, the first
+ choice is owner of any remaining reference to this
+ pointer, the second choice is our parent, and the
+ final choice is the null context. */
+ void *child = TC_PTR_FROM_CHUNK(tc->child);
+ const void *new_parent = null_context;
+ if (unlikely(tc->child->refs)) {
+ struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+ if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+ }
+ if (unlikely(_talloc_free(child) == -1)) {
+ if (new_parent == null_context) {
+ struct talloc_chunk *p = talloc_parent_chunk(ptr);
+ if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+ }
+ talloc_steal(new_parent, child);
+ }
+ }
+
+ tc->flags |= TALLOC_FLAG_FREE;
+
+ if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
+ struct talloc_chunk *pool;
+ unsigned int *pool_object_count;
+
+ pool = (tc->flags & TALLOC_FLAG_POOL)
+ ? tc : (struct talloc_chunk *)tc->pool;
+
+ pool_object_count = talloc_pool_objectcount(pool);
+
+ if (*pool_object_count == 0) {
+ TALLOC_ABORT("Pool object count zero!");
+ }
+
+ *pool_object_count -= 1;
+
+ if (*pool_object_count == 0) {
+ free(pool);
+ }
+ }
+ else {
+ free(tc);
+ }
+ return 0;
+}
+
+/*
+ move a lump of memory from one talloc context to another return the
+ ptr on success, or NULL if it could not be transferred.
+ passing NULL as ptr will always return NULL with no side effects.
+*/
+void *_talloc_steal(const void *new_ctx, const void *ptr)
+{
+ struct talloc_chunk *tc, *new_tc;
+
+ if (unlikely(!ptr)) {
+ return NULL;
+ }
+
+ if (unlikely(new_ctx == NULL)) {
+ new_ctx = null_context;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (unlikely(new_ctx == NULL)) {
+ if (tc->parent) {
+ _TLIST_REMOVE(tc->parent->child, tc);
+ if (tc->parent->child) {
+ tc->parent->child->parent = tc->parent;
+ }
+ } else {
+ if (tc->prev) tc->prev->next = tc->next;
+ if (tc->next) tc->next->prev = tc->prev;
+ }
+
+ tc->parent = tc->next = tc->prev = NULL;
+ return discard_const_p(void, ptr);
+ }
+
+ new_tc = talloc_chunk_from_ptr(new_ctx);
+
+ if (unlikely(tc == new_tc || tc->parent == new_tc)) {
+ return discard_const_p(void, ptr);
+ }
+
+ if (tc->parent) {
+ _TLIST_REMOVE(tc->parent->child, tc);
+ if (tc->parent->child) {
+ tc->parent->child->parent = tc->parent;
+ }
+ } else {
+ if (tc->prev) tc->prev->next = tc->next;
+ if (tc->next) tc->next->prev = tc->prev;
+ }
+
+ tc->parent = new_tc;
+ if (new_tc->child) new_tc->child->parent = NULL;
+ _TLIST_ADD(new_tc->child, tc);
+
+ return discard_const_p(void, ptr);
+}
+
+
+
+/*
+ remove a secondary reference to a pointer. This undo's what
+ talloc_reference() has done. The context and pointer arguments
+ must match those given to a talloc_reference()
+*/
+static inline int talloc_unreference(const void *context, const void *ptr)
+{
+ struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+ struct talloc_reference_handle *h;
+
+ if (unlikely(context == NULL)) {
+ context = null_context;
+ }
+
+ for (h=tc->refs;h;h=h->next) {
+ struct talloc_chunk *p = talloc_parent_chunk(h);
+ if (p == NULL) {
+ if (context == NULL) break;
+ } else if (TC_PTR_FROM_CHUNK(p) == context) {
+ break;
+ }
+ }
+ if (h == NULL) {
+ return -1;
+ }
+
+ return _talloc_free(h);
+}
+
+/*
+ remove a specific parent context from a pointer. This is a more
+ controlled varient of talloc_free()
+*/
+int talloc_unlink(const void *context, void *ptr)
+{
+ struct talloc_chunk *tc_p, *new_p;
+ void *new_parent;
+
+ if (ptr == NULL) {
+ return -1;
+ }
+
+ if (context == NULL) {
+ context = null_context;
+ }
+
+ if (talloc_unreference(context, ptr) == 0) {
+ return 0;
+ }
+
+ if (context == NULL) {
+ if (talloc_parent_chunk(ptr) != NULL) {
+ return -1;
+ }
+ } else {
+ if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
+ return -1;
+ }
+ }
+
+ tc_p = talloc_chunk_from_ptr(ptr);
+
+ if (tc_p->refs == NULL) {
+ return _talloc_free(ptr);
+ }
+
+ new_p = talloc_parent_chunk(tc_p->refs);
+ if (new_p) {
+ new_parent = TC_PTR_FROM_CHUNK(new_p);
+ } else {
+ new_parent = NULL;
+ }
+
+ if (talloc_unreference(new_parent, ptr) != 0) {
+ return -1;
+ }
+
+ talloc_steal(new_parent, ptr);
+
+ return 0;
+}
+
+/*
+ add a name to an existing pointer - va_list version
+*/
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
+{
+ struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+ tc->name = talloc_vasprintf(ptr, fmt, ap);
+ if (likely(tc->name)) {
+ _talloc_set_name_const(tc->name, ".name");
+ }
+ return tc->name;
+}
+
+/*
+ add a name to an existing pointer
+*/
+const char *talloc_set_name(const void *ptr, const char *fmt, ...)
+{
+ const char *name;
+ va_list ap;
+ va_start(ap, fmt);
+ name = talloc_set_name_v(ptr, fmt, ap);
+ va_end(ap);
+ return name;
+}
+
+
+/*
+ create a named talloc pointer. Any talloc pointer can be named, and
+ talloc_named() operates just like talloc() except that it allows you
+ to name the pointer.
+*/
+void *talloc_named(const void *context, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ void *ptr;
+ const char *name;
+
+ ptr = __talloc(context, size);
+ if (unlikely(ptr == NULL)) return NULL;
+
+ va_start(ap, fmt);
+ name = talloc_set_name_v(ptr, fmt, ap);
+ va_end(ap);
+
+ if (unlikely(name == NULL)) {
+ _talloc_free(ptr);
+ return NULL;
+ }
+
+ return ptr;
+}
+
+/*
+ return the name of a talloc ptr, or "UNNAMED"
+*/
+const char *talloc_get_name(const void *ptr)
+{
+ struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+ if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
+ return ".reference";
+ }
+ if (likely(tc->name)) {
+ return tc->name;
+ }
+ return "UNNAMED";
+}
+
+
+/*
+ check if a pointer has the given name. If it does, return the pointer,
+ otherwise return NULL
+*/
+void *talloc_check_name(const void *ptr, const char *name)
+{
+ const char *pname;
+ if (unlikely(ptr == NULL)) return NULL;
+ pname = talloc_get_name(ptr);
+ if (likely(pname == name || strcmp(pname, name) == 0)) {
+ return discard_const_p(void, ptr);
+ }
+ return NULL;
+}
+
+
+/*
+ this is for compatibility with older versions of talloc
+*/
+void *talloc_init(const char *fmt, ...)
+{
+ va_list ap;
+ void *ptr;
+ const char *name;
+
+ /*
+ * samba3 expects talloc_report_depth_cb(NULL, ...)
+ * reports all talloc'ed memory, so we need to enable
+ * null_tracking
+ */
+ talloc_enable_null_tracking();
+
+ ptr = __talloc(NULL, 0);
+ if (unlikely(ptr == NULL)) return NULL;
+
+ va_start(ap, fmt);
+ name = talloc_set_name_v(ptr, fmt, ap);
+ va_end(ap);
+
+ if (unlikely(name == NULL)) {
+ _talloc_free(ptr);
+ return NULL;
+ }
+
+ return ptr;
+}
+
+/*
+ this is a replacement for the Samba3 talloc_destroy_pool functionality. It
+ should probably not be used in new code. It's in here to keep the talloc
+ code consistent across Samba 3 and 4.
+*/
+void talloc_free_children(void *ptr)
+{
+ struct talloc_chunk *tc;
+
+ if (unlikely(ptr == NULL)) {
+ return;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ while (tc->child) {
+ /* we need to work out who will own an abandoned child
+ if it cannot be freed. In priority order, the first
+ choice is owner of any remaining reference to this
+ pointer, the second choice is our parent, and the
+ final choice is the null context. */
+ void *child = TC_PTR_FROM_CHUNK(tc->child);
+ const void *new_parent = null_context;
+ if (unlikely(tc->child->refs)) {
+ struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+ if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+ }
+ if (unlikely(_talloc_free(child) == -1)) {
+ if (new_parent == null_context) {
+ struct talloc_chunk *p = talloc_parent_chunk(ptr);
+ if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+ }
+ talloc_steal(new_parent, child);
+ }
+ }
+
+ if ((tc->flags & TALLOC_FLAG_POOL)
+ && (*talloc_pool_objectcount(tc) == 1)) {
+ tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+ VALGRIND_MAKE_MEM_NOACCESS(
+ tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
+#endif
+ }
+}
+
+/*
+ Allocate a bit of memory as a child of an existing pointer
+*/
+void *_talloc(const void *context, size_t size)
+{
+ return __talloc(context, size);
+}
+
+/*
+ externally callable talloc_set_name_const()
+*/
+void talloc_set_name_const(const void *ptr, const char *name)
+{
+ _talloc_set_name_const(ptr, name);
+}
+
+/*
+ create a named talloc pointer. Any talloc pointer can be named, and
+ talloc_named() operates just like talloc() except that it allows you
+ to name the pointer.
+*/
+void *talloc_named_const(const void *context, size_t size, const char *name)
+{
+ return _talloc_named_const(context, size, name);
+}
+
+/*
+ free a talloc pointer. This also frees all child pointers of this
+ pointer recursively
+
+ return 0 if the memory is actually freed, otherwise -1. The memory
+ will not be freed if the ref_count is > 1 or the destructor (if
+ any) returns non-zero
+*/
+int talloc_free(void *ptr)
+{
+ return _talloc_free(ptr);
+}
+
+
+
+/*
+ A talloc version of realloc. The context argument is only used if
+ ptr is NULL
+*/
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
+{
+ struct talloc_chunk *tc;
+ void *new_ptr;
+ bool malloced = false;
+
+ /* size zero is equivalent to free() */
+ if (unlikely(size == 0)) {
+ _talloc_free(ptr);
+ return NULL;
+ }
+
+ if (unlikely(size >= MAX_TALLOC_SIZE)) {
+ return NULL;
+ }
+
+ /* realloc(NULL) is equivalent to malloc() */
+ if (ptr == NULL) {
+ return _talloc_named_const(context, size, name);
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ /* don't allow realloc on referenced pointers */
+ if (unlikely(tc->refs)) {
+ return NULL;
+ }
+
+ /* don't shrink if we have less than 1k to gain */
+ if ((size < tc->size) && ((tc->size - size) < 1024)) {
+ tc->size = size;
+ return ptr;
+ }
+
+ /* by resetting magic we catch users of the old memory */
+ tc->flags |= TALLOC_FLAG_FREE;
+
+#if ALWAYS_REALLOC
+ new_ptr = malloc(size + TC_HDR_SIZE);
+ if (new_ptr) {
+ memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
+ free(tc);
+ }
+#else
+ if (tc->flags & TALLOC_FLAG_POOLMEM) {
+
+ new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+ *talloc_pool_objectcount((struct talloc_chunk *)
+ (tc->pool)) -= 1;
+
+ if (new_ptr == NULL) {
+ new_ptr = malloc(TC_HDR_SIZE+size);
+ malloced = true;
+ }
+
+ if (new_ptr) {
+ memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+ }
+ }
+ else {
+ new_ptr = realloc(tc, size + TC_HDR_SIZE);
+ }
+#endif
+ if (unlikely(!new_ptr)) {
+ tc->flags &= ~TALLOC_FLAG_FREE;
+ return NULL;
+ }
+
+ tc = (struct talloc_chunk *)new_ptr;
+ tc->flags &= ~TALLOC_FLAG_FREE;
+ if (malloced) {
+ tc->flags &= ~TALLOC_FLAG_POOLMEM;
+ }
+ if (tc->parent) {
+ tc->parent->child = tc;
+ }
+ if (tc->child) {
+ tc->child->parent = tc;
+ }
+
+ if (tc->prev) {
+ tc->prev->next = tc;
+ }
+ if (tc->next) {
+ tc->next->prev = tc;
+ }
+
+ tc->size = size;
+ _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
+
+ return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+ a wrapper around talloc_steal() for situations where you are moving a pointer
+ between two structures, and want the old pointer to be set to NULL
+*/
+void *_talloc_move(const void *new_ctx, const void *_pptr)
+{
+ const void **pptr = discard_const_p(const void *,_pptr);
+ void *ret = _talloc_steal(new_ctx, *pptr);
+ (*pptr) = NULL;
+ return ret;
+}
+
+/*
+ return the total size of a talloc pool (subtree)
+*/
+size_t talloc_total_size(const void *ptr)
+{
+ size_t total = 0;
+ struct talloc_chunk *c, *tc;
+
+ if (ptr == NULL) {
+ ptr = null_context;
+ }
+ if (ptr == NULL) {
+ return 0;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ return 0;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
+ total = tc->size;
+ for (c=tc->child;c;c=c->next) {
+ total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
+ }
+
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+
+ return total;
+}
+
+/*
+ return the total number of blocks in a talloc pool (subtree)
+*/
+size_t talloc_total_blocks(const void *ptr)
+{
+ size_t total = 0;
+ struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ return 0;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
+ total++;
+ for (c=tc->child;c;c=c->next) {
+ total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
+ }
+
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+
+ return total;
+}
+
+/*
+ return the number of external references to a pointer
+*/
+size_t talloc_reference_count(const void *ptr)
+{
+ struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+ struct talloc_reference_handle *h;
+ size_t ret = 0;
+
+ for (h=tc->refs;h;h=h->next) {
+ ret++;
+ }
+ return ret;
+}
+
+/*
+ report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+ void (*callback)(const void *ptr,
+ int depth, int max_depth,
+ int is_ref,
+ void *private_data),
+ void *private_data)
+{
+ struct talloc_chunk *c, *tc;
+
+ if (ptr == NULL) {
+ ptr = null_context;
+ }
+ if (ptr == NULL) return;
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ return;
+ }
+
+ callback(ptr, depth, max_depth, 0, private_data);
+
+ if (max_depth >= 0 && depth >= max_depth) {
+ return;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+ for (c=tc->child;c;c=c->next) {
+ if (c->name == TALLOC_MAGIC_REFERENCE) {
+ struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
+ callback(h->ptr, depth + 1, max_depth, 1, private_data);
+ } else {
+ talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
+ }
+ }
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+}
+
+static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
+{
+ const char *name = talloc_get_name(ptr);
+ FILE *f = (FILE *)_f;
+
+ if (is_ref) {
+ fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+ return;
+ }
+
+ if (depth == 0) {
+ fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n",
+ (max_depth < 0 ? "full " :""), name,
+ (unsigned long)talloc_total_size(ptr),
+ (unsigned long)talloc_total_blocks(ptr));
+ return;
+ }
+
+ fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n",
+ depth*4, "",
+ name,
+ (unsigned long)talloc_total_size(ptr),
+ (unsigned long)talloc_total_blocks(ptr),
+ (int)talloc_reference_count(ptr), ptr);
+
+#if 0
+ fprintf(f, "content: ");
+ if (talloc_total_size(ptr)) {
+ int tot = talloc_total_size(ptr);
+ int i;
+
+ for (i = 0; i < tot; i++) {
+ if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
+ fprintf(f, "%c", ((char *)ptr)[i]);
+ } else {
+ fprintf(f, "~%02x", ((char *)ptr)[i]);
+ }
+ }
+ }
+ fprintf(f, "\n");
+#endif
+}
+
+/*
+ report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
+{
+ talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
+ fflush(f);
+}
+
+/*
+ report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_full(const void *ptr, FILE *f)
+{
+ talloc_report_depth_file(ptr, 0, -1, f);
+}
+
+/*
+ report on memory usage by all children of a pointer
+*/
+void talloc_report(const void *ptr, FILE *f)
+{
+ talloc_report_depth_file(ptr, 0, 1, f);
+}
+
+/*
+ report on any memory hanging off the null context
+*/
+static void talloc_report_null(void)
+{
+ if (talloc_total_size(null_context) != 0) {
+ talloc_report(null_context, stderr);
+ }
+}
+
+/*
+ report on any memory hanging off the null context
+*/
+static void talloc_report_null_full(void)
+{
+ if (talloc_total_size(null_context) != 0) {
+ talloc_report_full(null_context, stderr);
+ }
+}
+
+/*
+ enable tracking of the NULL context
+*/
+void talloc_enable_null_tracking(void)
+{
+ if (null_context == NULL) {
+ null_context = _talloc_named_const(NULL, 0, "null_context");
+ }
+}
+
+/*
+ disable tracking of the NULL context
+*/
+void talloc_disable_null_tracking(void)
+{
+ _talloc_free(null_context);
+ null_context = NULL;
+}
+
+/*
+ enable leak reporting on exit
+*/
+void talloc_enable_leak_report(void)
+{
+ talloc_enable_null_tracking();
+ atexit(talloc_report_null);
+}
+
+/*
+ enable full leak reporting on exit
+*/
+void talloc_enable_leak_report_full(void)
+{
+ talloc_enable_null_tracking();
+ atexit(talloc_report_null_full);
+}
+
+/*
+ talloc and zero memory.
+*/
+void *_talloc_zero(const void *ctx, size_t size, const char *name)
+{
+ void *p = _talloc_named_const(ctx, size, name);
+
+ if (p) {
+ memset(p, '\0', size);
+ }
+
+ return p;
+}
+
+/*
+ memdup with a talloc.
+*/
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
+{
+ void *newp = _talloc_named_const(t, size, name);
+
+ if (likely(newp)) {
+ memcpy(newp, p, size);
+ }
+
+ return newp;
+}
+
+static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
+{
+ char *ret;
+
+ ret = (char *)__talloc(t, len + 1);
+ if (unlikely(!ret)) return NULL;
+
+ memcpy(ret, p, len);
+ ret[len] = 0;
+
+ _talloc_set_name_const(ret, ret);
+ return ret;
+}
+
+/*
+ strdup with a talloc
+*/
+char *talloc_strdup(const void *t, const char *p)
+{
+ if (unlikely(!p)) return NULL;
+ return __talloc_strlendup(t, p, strlen(p));
+}
+
+/*
+ strndup with a talloc
+*/
+char *talloc_strndup(const void *t, const char *p, size_t n)
+{
+ if (unlikely(!p)) return NULL;
+ return __talloc_strlendup(t, p, n);
+}
+
+static inline char *__talloc_strlendup_append(char *s, size_t slen,
+ const char *a, size_t alen)
+{
+ char *ret;
+
+ ret = talloc_realloc(NULL, s, char, slen + alen + 1);
+ if (unlikely(!ret)) return NULL;
+
+ /* append the string and the trailing \0 */
+ memcpy(&ret[slen], a, alen);
+ ret[slen+alen] = 0;
+
+ _talloc_set_name_const(ret, ret);
+ return ret;
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strdup_append(char *s, const char *a)
+{
+ if (unlikely(!s)) {
+ return talloc_strdup(NULL, a);
+ }
+
+ if (unlikely(!a)) {
+ return s;
+ }
+
+ return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strdup_append_buffer(char *s, const char *a)
+{
+ size_t slen;
+
+ if (unlikely(!s)) {
+ return talloc_strdup(NULL, a);
+ }
+
+ if (unlikely(!a)) {
+ return s;
+ }
+
+ slen = talloc_get_size(s);
+ if (likely(slen > 0)) {
+ slen--;
+ }
+
+ return __talloc_strlendup_append(s, slen, a, strlen(a));
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strndup_append(char *s, const char *a, size_t n)
+{
+ if (unlikely(!s)) {
+ return talloc_strdup(NULL, a);
+ }
+
+ if (unlikely(!a)) {
+ return s;
+ }
+
+ return __talloc_strlendup_append(s, strlen(s), a, n);
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
+{
+ size_t slen;
+
+ if (unlikely(!s)) {
+ return talloc_strdup(NULL, a);
+ }
+
+ if (unlikely(!a)) {
+ return s;
+ }
+
+ slen = talloc_get_size(s);
+ if (likely(slen > 0)) {
+ slen--;
+ }
+
+ return __talloc_strlendup_append(s, slen, a, n);
+}
+
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+{
+ int len;
+ char *ret;
+ va_list ap2;
+ char c;
+
+ /* this call looks strange, but it makes it work on older solaris boxes */
+ va_copy(ap2, ap);
+ len = vsnprintf(&c, 1, fmt, ap2);
+ va_end(ap2);
+ if (unlikely(len < 0)) {
+ return NULL;
+ }
+
+ ret = (char *)__talloc(t, len+1);
+ if (unlikely(!ret)) return NULL;
+
+ va_copy(ap2, ap);
+ vsnprintf(ret, len+1, fmt, ap2);
+ va_end(ap2);
+
+ _talloc_set_name_const(ret, ret);
+ return ret;
+}
+
+
+/*
+ Perform string formatting, and return a pointer to newly allocated
+ memory holding the result, inside a memory pool.
+ */
+char *talloc_asprintf(const void *t, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = talloc_vasprintf(t, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+ const char *fmt, va_list ap)
+ PRINTF_ATTRIBUTE(3,0);
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+ const char *fmt, va_list ap)
+{
+ ssize_t alen;
+ va_list ap2;
+ char c;
+
+ va_copy(ap2, ap);
+ alen = vsnprintf(&c, 1, fmt, ap2);
+ va_end(ap2);
+
+ if (alen <= 0) {
+ /* Either the vsnprintf failed or the format resulted in
+ * no characters being formatted. In the former case, we
+ * ought to return NULL, in the latter we ought to return
+ * the original string. Most current callers of this
+ * function expect it to never return NULL.
+ */
+ return s;
+ }
+
+ s = talloc_realloc(NULL, s, char, slen + alen + 1);
+ if (!s) return NULL;
+
+ va_copy(ap2, ap);
+ vsnprintf(s + slen, alen + 1, fmt, ap2);
+ va_end(ap2);
+
+ _talloc_set_name_const(s, s);
+ return s;
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Good for gradually
+ * accumulating output into a string buffer. Appends at the end
+ * of the string.
+ **/
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
+{
+ if (unlikely(!s)) {
+ return talloc_vasprintf(NULL, fmt, ap);
+ }
+
+ return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Always appends at the
+ * end of the talloc'ed buffer, not the end of the string.
+ **/
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
+{
+ size_t slen;
+
+ if (unlikely(!s)) {
+ return talloc_vasprintf(NULL, fmt, ap);
+ }
+
+ slen = talloc_get_size(s);
+ if (likely(slen > 0)) {
+ slen--;
+ }
+
+ return __talloc_vaslenprintf_append(s, slen, fmt, ap);
+}
+
+/*
+ Realloc @p s to append the formatted result of @p fmt and return @p
+ s, which may have moved. Good for gradually accumulating output
+ into a string buffer.
+ */
+char *talloc_asprintf_append(char *s, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = talloc_vasprintf_append(s, fmt, ap);
+ va_end(ap);
+ return s;
+}
+
+/*
+ Realloc @p s to append the formatted result of @p fmt and return @p
+ s, which may have moved. Good for gradually accumulating output
+ into a buffer.
+ */
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = talloc_vasprintf_append_buffer(s, fmt, ap);
+ va_end(ap);
+ return s;
+}
+
+/*
+ alloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+ if (count >= MAX_TALLOC_SIZE/el_size) {
+ return NULL;
+ }
+ return _talloc_named_const(ctx, el_size * count, name);
+}
+
+/*
+ alloc an zero array, checking for integer overflow in the array size
+*/
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+ if (count >= MAX_TALLOC_SIZE/el_size) {
+ return NULL;
+ }
+ return _talloc_zero(ctx, el_size * count, name);
+}
+
+/*
+ realloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
+{
+ if (count >= MAX_TALLOC_SIZE/el_size) {
+ return NULL;
+ }
+ return _talloc_realloc(ctx, ptr, el_size * count, name);
+}
+
+/*
+ a function version of talloc_realloc(), so it can be passed as a function pointer
+ to libraries that want a realloc function (a realloc function encapsulates
+ all the basic capabilities of an allocation library, which is why this is useful)
+*/
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
+{
+ return _talloc_realloc(context, ptr, size, NULL);
+}
+
+
+static int talloc_autofree_destructor(void *ptr)
+{
+ autofree_context = NULL;
+ return 0;
+}
+
+static void talloc_autofree(void)
+{
+ _talloc_free(autofree_context);
+}
+
+/*
+ return a context which will be auto-freed on exit
+ this is useful for reducing the noise in leak reports
+*/
+void *talloc_autofree_context(void)
+{
+ if (autofree_context == NULL) {
+ autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
+ talloc_set_destructor(autofree_context, talloc_autofree_destructor);
+ atexit(talloc_autofree);
+ }
+ return autofree_context;
+}
+
+size_t talloc_get_size(const void *context)
+{
+ struct talloc_chunk *tc;
+
+ if (context == NULL)
+ return 0;
+
+ tc = talloc_chunk_from_ptr(context);
+
+ return tc->size;
+}
+
+/*
+ find a parent of this context that has the given name, if any
+*/
+void *talloc_find_parent_byname(const void *context, const char *name)
+{
+ struct talloc_chunk *tc;
+
+ if (context == NULL) {
+ return NULL;
+ }
+
+ tc = talloc_chunk_from_ptr(context);
+ while (tc) {
+ if (tc->name && strcmp(tc->name, name) == 0) {
+ return TC_PTR_FROM_CHUNK(tc);
+ }
+ while (tc && tc->prev) tc = tc->prev;
+ if (tc) {
+ tc = tc->parent;
+ }
+ }
+ return NULL;
+}
+
+/*
+ show the parentage of a context
+*/
+void talloc_show_parents(const void *context, FILE *file)
+{
+ struct talloc_chunk *tc;
+
+ if (context == NULL) {
+ fprintf(file, "talloc no parents for NULL\n");
+ return;
+ }
+
+ tc = talloc_chunk_from_ptr(context);
+ fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
+ while (tc) {
+ fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
+ while (tc && tc->prev) tc = tc->prev;
+ if (tc) {
+ tc = tc->parent;
+ }
+ }
+ fflush(file);
+}
+
+/*
+ return 1 if ptr is a parent of context
+*/
+int talloc_is_parent(const void *context, const void *ptr)
+{
+ struct talloc_chunk *tc;
+
+ if (context == NULL) {
+ return 0;
+ }
+
+ tc = talloc_chunk_from_ptr(context);
+ while (tc) {
+ if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
+ while (tc && tc->prev) tc = tc->prev;
+ if (tc) {
+ tc = tc->parent;
+ }
+ }
+ return 0;
+}
diff --git a/lib/void_stack.c b/lib/void_stack.c
index 402df0b..34c792c 100644
--- a/lib/void_stack.c
+++ b/lib/void_stack.c
@@ -3,7 +3,7 @@
* of memory of any type. It still needs work to eliminate memory
* leaks.
*
- * Copyright (C) 2005,2007 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009 Timothy D. Morgan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,22 +18,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: void_stack.c 111 2008-05-01 04:06:22Z tim $
+ * $Id: void_stack.c 150 2009-03-02 02:17:46Z tim $
*/
-#include "../include/void_stack.h"
+#include "void_stack.h"
void_stack* void_stack_new(unsigned short max_size)
{
- void_stack* ret_val = (void_stack*)malloc(sizeof(void_stack));
+ void_stack* ret_val = talloc(NULL, void_stack);
if (ret_val != NULL)
{
memset(ret_val, 0, sizeof(*ret_val));
- ret_val->elements = (void**)malloc(max_size*sizeof(void*));
+ ret_val->elements = talloc_array(ret_val, void*, max_size);
if (ret_val->elements == NULL)
{
- free(ret_val);
+ talloc_free(ret_val);
ret_val = NULL;
}
else
@@ -89,8 +89,7 @@ void_stack* void_stack_copy_reverse(const void_stack* v)
void void_stack_free(void_stack* stack)
{
- free(stack->elements);
- free(stack);
+ talloc_free(stack);
}
@@ -99,8 +98,7 @@ void void_stack_free_deep(void_stack* stack)
unsigned short i;
for(i=0; i < stack->top; i++)
free(stack->elements[i]);
- free(stack->elements);
- free(stack);
+ talloc_free(stack);
}
@@ -153,7 +151,7 @@ void_stack_iterator* void_stack_iterator_new(const void_stack* stack)
if(stack != NULL)
{
- ret_val = (void_stack_iterator*)malloc(sizeof(void_stack_iterator));
+ ret_val = talloc(stack, void_stack_iterator);
if (ret_val != NULL)
{
ret_val->stack = stack;
@@ -167,7 +165,7 @@ void_stack_iterator* void_stack_iterator_new(const void_stack* stack)
void void_stack_iterator_free(void_stack_iterator* iter)
{
- free(iter);
+ talloc_free(iter);
}
diff --git a/lib/winsec.c b/lib/winsec.c
new file mode 100644
index 0000000..dfd57a7
--- /dev/null
+++ b/lib/winsec.c
@@ -0,0 +1,532 @@
+/*
+ * This file contains refactored Samba code used to interpret Windows
+ * Security Descriptors. See:
+ * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005-2006,2009 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team
+ * (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: winsec.c 148 2009-02-22 23:22:59Z tim $
+ */
+
+#include "winsec.h"
+
+
+/******************************************************************************
+ * Non-talloc() interface for parsing a descriptor.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len)
+{
+ return winsec_parse_desc(NULL, buf, buf_len);
+}
+
+
+/******************************************************************************
+ * Free a descriptor. Not needed if using talloc and a parent context is freed.
+ ******************************************************************************/
+void winsec_free_descriptor(WINSEC_DESC* desc)
+{
+ talloc_free(desc);
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DESC structure and substructures.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len)
+{
+ WINSEC_DESC* ret_val;
+
+ if (buf == NULL || buf_len < WINSEC_DESC_HEADER_SIZE)
+ return NULL;
+
+ if((ret_val = talloc(talloc_ctx, WINSEC_DESC)) == NULL)
+ return NULL;
+
+ ret_val->revision = buf[0];
+ ret_val->sbz1 = buf[1];
+ ret_val->control = SVAL(buf, 0x2);
+
+ if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE))
+ fprintf(stderr, "DEBUG: NOT self-relative!\n");
+
+ ret_val->off_owner_sid = IVAL(buf, 0x4);
+ ret_val->off_grp_sid = IVAL(buf, 0x8);
+ ret_val->off_sacl = IVAL(buf, 0xC);
+ ret_val->off_dacl = IVAL(buf, 0x10);
+
+ /* A basic sanity check to ensure our offsets are within our buffer.
+ * Additional length checking is done in secondary parsing functions.
+ */
+ if((ret_val->off_owner_sid >= buf_len)
+ || (ret_val->off_grp_sid >= buf_len)
+ || (ret_val->off_sacl >= buf_len)
+ || (ret_val->off_dacl >= buf_len))
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ if(ret_val->off_owner_sid == 0)
+ ret_val->owner_sid = NULL;
+ else
+ {
+ ret_val->owner_sid = winsec_parse_dom_sid(ret_val,
+ buf + ret_val->off_owner_sid,
+ buf_len - ret_val->off_owner_sid);
+ if(ret_val->owner_sid == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ }
+
+ if(ret_val->off_grp_sid == 0)
+ ret_val->grp_sid = NULL;
+ else
+ {
+ ret_val->grp_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_grp_sid,
+ buf_len - ret_val->off_grp_sid);
+ if(ret_val->grp_sid == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ }
+
+ if((ret_val->control & WINSEC_DESC_SACL_PRESENT) && ret_val->off_sacl)
+ {
+ ret_val->sacl = winsec_parse_acl(ret_val, buf + ret_val->off_sacl,
+ buf_len - ret_val->off_sacl);
+ if(ret_val->sacl == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ }
+ else
+ ret_val->sacl = NULL;
+
+ if((ret_val->control & WINSEC_DESC_DACL_PRESENT) && ret_val->off_dacl != 0)
+ {
+ ret_val->dacl = winsec_parse_acl(ret_val, buf + ret_val->off_dacl,
+ buf_len - ret_val->off_dacl);
+ if(ret_val->dacl == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ }
+ else
+ ret_val->dacl = NULL;
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACL structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len)
+{
+ uint32_t i, offset;
+ WINSEC_ACL* ret_val;
+
+ /*
+ * Note that the size is always a multiple of 4 bytes due to the
+ * nature of the data structure.
+ */
+ if (buf == NULL || buf_len < 8)
+ return NULL;
+
+ if((ret_val = talloc(talloc_ctx, WINSEC_ACL)) == NULL)
+ return NULL;
+
+ ret_val->revision = SVAL(buf, 0x0);
+ ret_val->size = SVAL(buf, 0x2);
+ ret_val->num_aces = IVAL(buf, 0x4);
+
+ /* The num_aces can be at most around 4k because anything greater
+ * wouldn't fit in the 16 bit size even if every ace was as small as
+ * possible.
+ */
+ if((ret_val->size > buf_len) || (ret_val->num_aces > 4095))
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ /* Even if the num_aces is zero, allocate memory as there's a difference
+ * between a non-present DACL (allow all access) and a DACL with no ACE's
+ * (allow no access).
+ */
+ if((ret_val->aces = talloc_array(ret_val, WINSEC_ACE*,
+ ret_val->num_aces+1)) == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ offset = 8;
+ for(i=0; i < ret_val->num_aces; i++)
+ {
+ ret_val->aces[i] = winsec_parse_ace(ret_val->aces,
+ buf+offset, buf_len-offset);
+ if(ret_val->aces[i] == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ offset += ret_val->aces[i]->size;
+ if(offset > buf_len)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ }
+ ret_val->aces[ret_val->num_aces] = NULL;
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACE structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len)
+{
+ uint32_t offset;
+ WINSEC_ACE* ret_val;
+
+ if(buf == NULL || buf_len < WINSEC_ACE_MIN_SIZE)
+ return NULL;
+
+ if((ret_val = talloc(talloc_ctx, WINSEC_ACE)) == NULL)
+ return NULL;
+
+ ret_val->type = buf[0];
+ ret_val->flags = buf[1];
+ ret_val->size = SVAL(buf, 0x2);
+ ret_val->access_mask = IVAL(buf, 0x4);
+
+ offset = 0x8;
+
+ /* check whether object access is present */
+ if (winsec_ace_object(ret_val->type))
+ {
+ ret_val->obj_flags = IVAL(buf, offset);
+ offset += 4;
+
+ if(ret_val->obj_flags & WINSEC_ACE_OBJECT_PRESENT)
+ {
+ ret_val->obj_guid = winsec_parse_uuid(ret_val,
+ buf+offset, buf_len-offset);
+ if(ret_val->obj_guid == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ offset += sizeof(WINSEC_UUID);
+ }
+ else
+ ret_val->obj_guid = NULL;
+
+ if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
+ {
+ ret_val->inh_guid = winsec_parse_uuid(ret_val,
+ buf+offset, buf_len-offset);
+ if(ret_val->inh_guid == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+ offset += sizeof(WINSEC_UUID);
+ }
+ else
+ ret_val->inh_guid = NULL;
+ }
+
+ ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset);
+ if(ret_val->trustee == NULL)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DOM_SID structure.
+ ******************************************************************************/
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len)
+{
+ uint32_t i;
+ WINSEC_DOM_SID* ret_val;
+
+ if(buf == NULL || buf_len < 8)
+ return NULL;
+
+ /* if((ret_val = (WINSEC_DOM_SID*)zalloc(sizeof(WINSEC_DOM_SID))) == NULL)*/
+ if((ret_val = talloc(talloc_ctx, WINSEC_DOM_SID)) == NULL)
+ return NULL;
+
+ ret_val->sid_rev_num = buf[0];
+ ret_val->num_auths = buf[1];
+ memcpy(ret_val->id_auth, buf+2, 6);
+
+ /* XXX: should really issue a warning here... */
+ if (ret_val->num_auths > WINSEC_MAX_SUBAUTHS)
+ ret_val->num_auths = WINSEC_MAX_SUBAUTHS;
+
+ if(buf_len < ret_val->num_auths*sizeof(uint32_t)+8)
+ {
+ talloc_free(ret_val);
+ return NULL;
+ }
+
+ for(i=0; i < ret_val->num_auths; i++)
+ ret_val->sub_auths[i] = IVAL(buf, 8+i*sizeof(uint32_t));
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_UUID struct.
+ ******************************************************************************/
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
+ const uint8_t* buf, uint32_t buf_len)
+{
+ WINSEC_UUID* ret_val;
+
+ if(buf == NULL || buf_len < sizeof(WINSEC_UUID))
+ return false;
+
+ if((ret_val = talloc(talloc_ctx, WINSEC_UUID)) == NULL)
+ return NULL;
+
+ ret_val->time_low = IVAL(buf, 0x0);
+ ret_val->time_mid = SVAL(buf, 0x4);
+ ret_val->time_hi_and_version = SVAL(buf, 0x6);
+
+ memcpy(ret_val->clock_seq, buf+0x8, 2);
+ memcpy(ret_val->node, buf+0xB, 6);
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ * Calculates the size of a SID.
+ ******************************************************************************/
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid)
+{
+ if (sid == NULL)
+ return 0;
+
+ return sid->num_auths * sizeof(uint32_t) + 8;
+}
+
+
+/******************************************************************************
+ * Compare the auth portion of two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ if (sid1->sid_rev_num != sid2->sid_rev_num)
+ return sid1->sid_rev_num - sid2->sid_rev_num;
+
+ for (i = 0; i < 6; i++)
+ if (sid1->id_auth[i] != sid2->id_auth[i])
+ return sid1->id_auth[i] - sid2->id_auth[i];
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ /* Compare most likely different rids, first: i.e start at end */
+ if (sid1->num_auths != sid2->num_auths)
+ return sid1->num_auths - sid2->num_auths;
+
+ for (i = sid1->num_auths-1; i >= 0; --i)
+ if (sid1->sub_auths[i] != sid2->sub_auths[i])
+ return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+ return winsec_sid_compare_auth(sid1, sid2);
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+ return winsec_sid_compare(sid1, sid2) == 0;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_DESC structures.
+ ******************************************************************************/
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2)
+{
+ /* Trivial cases */
+ if (!s1 && !s2)
+ return true;
+ if (!s1 || !s2)
+ return false;
+
+ /* Check top level stuff */
+ if (s1->revision != s2->revision)
+ return false;
+
+ if (s1->control != s2->control)
+ return false;
+
+ /* Check owner and group */
+ if (!winsec_sid_equal(s1->owner_sid, s2->owner_sid))
+ return false;
+
+ if (!winsec_sid_equal(s1->grp_sid, s2->grp_sid))
+ return false;
+
+ /* Check ACLs present in one but not the other */
+ if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+ (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl))
+ { return false; }
+
+ /* Sigh - we have to do it the hard way by iterating over all
+ the ACEs in the ACLs */
+ if(!winsec_acl_equal(s1->dacl, s2->dacl) || !winsec_acl_equal(s1->sacl, s2->sacl))
+ return false;
+
+ return true;
+}
+
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACL structures.
+ ******************************************************************************/
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2)
+{
+ unsigned int i, j;
+
+ /* Trivial cases */
+ if (!s1 && !s2)
+ return true;
+ if (!s1 || !s2)
+ return false;
+
+ /* Check top level stuff */
+ if (s1->revision != s2->revision)
+ return false;
+
+ if (s1->num_aces != s2->num_aces)
+ return false;
+
+ /* The ACEs could be in any order so check each ACE in s1 against
+ each ACE in s2. */
+
+ for (i = 0; i < s1->num_aces; i++)
+ {
+ bool found = false;
+
+ for (j = 0; j < s2->num_aces; j++)
+ {
+ if (winsec_ace_equal(s1->aces[i], s2->aces[j]))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return false;
+ }
+
+ return true;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACE structures.
+ ******************************************************************************/
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2)
+{
+ /* Trivial cases */
+ if (!s1 && !s2)
+ return true;
+ if (!s1 || !s2)
+ return false;
+
+ /* Check top level stuff */
+ if (s1->type != s2->type || s1->flags != s2->flags ||
+ s1->access_mask != s2->access_mask)
+ { return false; }
+
+ /* Check SID */
+ if (!winsec_sid_equal(s1->trustee, s2->trustee))
+ return false;
+
+ return true;
+}
+
+
+/******************************************************************************
+ * Check if ACE has OBJECT type.
+ ******************************************************************************/
+bool winsec_ace_object(uint8_t type)
+{
+ if (type == WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ type == WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+ type == WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+ type == WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT)
+ { return true; }
+
+ return false;
+}
diff --git a/src/Makefile b/src/Makefile
index 8fde7f1..880485d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,19 +1,19 @@
-# $Id: Makefile 110 2008-04-29 22:59:55Z tim $
+# $Id: Makefile 143 2009-02-13 03:24:27Z tim $
################################################################################
-REGLOOKUP=$(BUILD_BIN)/reglookup
-REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover
+REGLOOKUP=$(BUILD_BIN)/reglookup$(BIN_EXT)
+REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover$(BIN_EXT)
OBJ=$(wildcard ../lib/*.o)
FILES=$(REGLOOKUP) $(REGLOOKUP_RECOVER)
all: $(FILES)
$(REGLOOKUP): reglookup.o $(OBJ)
- $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ)
+ $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ) $(EXTRA_OBJ)
$(REGLOOKUP_RECOVER): reglookup-recover.o $(OBJ)
- $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ)
+ $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ) $(EXTRA_OBJ)
reglookup.o: reglookup.c
$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup.c
diff --git a/src/common.c b/src/common.c
index 4012c4b..734ddb1 100644
--- a/src/common.c
+++ b/src/common.c
@@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: common.c 121 2008-08-09 17:22:26Z tim $
+ * $Id: common.c 154 2009-06-03 15:21:47Z tim $
*/
#include <iconv.h>
@@ -28,7 +28,13 @@ const char* key_special_chars = ",\"\\/";
const char* subfield_special_chars = ",\"\\|";
const char* common_special_chars = ",\"\\";
-#define REGLOOKUP_VERSION "0.9.0"
+#define REGLOOKUP_VERSION "0.11.0"
+
+#define REGLOOKUP_EXIT_OK 0
+#define REGLOOKUP_EXIT_OSERR 71
+#define REGLOOKUP_EXIT_USAGE 64
+#define REGLOOKUP_EXIT_DATAERR 65
+#define REGLOOKUP_EXIT_NOINPUT 66
void bailOut(int code, char* message)
@@ -37,6 +43,23 @@ void bailOut(int code, char* message)
exit(code);
}
+void printMsgs(REGFI_FILE* f)
+{
+ char* msgs = regfi_get_messages(f);
+ if(msgs != NULL)
+ {
+ fprintf(stderr, "%s", msgs);
+ free(msgs);
+ }
+}
+
+void clearMsgs(REGFI_FILE* f)
+{
+ char* msgs = regfi_get_messages(f);
+ if(msgs != NULL)
+ free(msgs);
+}
+
/* Returns a newly malloc()ed string which contains original buffer,
* except for non-printable or special characters are quoted in hex
@@ -51,9 +74,11 @@ static char* quote_buffer(const unsigned char* str,
unsigned int num_written = 0;
unsigned int buf_len = sizeof(char)*(len+1);
- char* ret_val = malloc(buf_len);
+ char* ret_val = NULL;
char* tmp_buf;
+ if(buf_len > 0)
+ ret_val = malloc(buf_len);
if(ret_val == NULL)
return NULL;
@@ -122,11 +147,11 @@ static char* quote_string(const char* str, const char* special)
* Convert from UTF-16LE to ASCII. Accepts a Unicode buffer, uni, and
* it's length, uni_max. Writes ASCII to the buffer ascii, whose size
* is ascii_max. Writes at most (ascii_max-1) bytes to ascii, and null
- * terminates the string. Returns the length of the string stored in
+ * terminates the string. Returns the length of the data written to
* ascii. On error, returns a negative errno code.
*/
static int uni_to_ascii(unsigned char* uni, char* ascii,
- unsigned int uni_max, unsigned int ascii_max)
+ uint32 uni_max, uint32 ascii_max)
{
char* inbuf = (char*)uni;
char* outbuf = ascii;
@@ -135,7 +160,7 @@ static int uni_to_ascii(unsigned char* uni, char* ascii,
int ret;
/* Set up conversion descriptor. */
- conv_desc = iconv_open("US-ASCII", "UTF-16LE");
+ conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
if(ret == -1)
@@ -146,7 +171,50 @@ static int uni_to_ascii(unsigned char* uni, char* ascii,
*outbuf = '\0';
iconv_close(conv_desc);
- return strlen(ascii);
+ return ascii_max-out_len-1;
+}
+
+
+static char* quote_unicode(unsigned char* uni, uint32 length,
+ const char* special, char** error_msg)
+{
+ char* ret_val;
+ char* ascii = NULL;
+ char* tmp_err;
+ int ret_err;
+ *error_msg = NULL;
+
+ if(length+1 > 0)
+ ascii = malloc(length+1);
+ if(ascii == NULL)
+ {
+ *error_msg = (char*)malloc(27);
+ if(*error_msg == NULL)
+ return NULL;
+ strcpy(*error_msg, "Memory allocation failure.");
+ return NULL;
+ }
+
+ ret_err = uni_to_ascii(uni, ascii, length, length+1);
+ if(ret_err < 0)
+ {
+ free(ascii);
+ tmp_err = strerror(-ret_err);
+ *error_msg = (char*)malloc(61+strlen(tmp_err));
+ if(*error_msg == NULL)
+ return NULL;
+
+ sprintf(*error_msg,
+ "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
+ ret_val = quote_buffer(uni, length, special);
+ }
+ else
+ {
+ ret_val = quote_string(ascii, special);
+ free(ascii);
+ }
+
+ return ret_val;
}
@@ -160,22 +228,24 @@ static int uni_to_ascii(unsigned char* uni, char* ascii,
* is the responsibility of the caller to free both a non-NULL return
* value, and a non-NULL (*error_msg).
*/
+/* XXX: Part of this function's logic should be pushed into the regfi API.
+ * The structures should be parsed and stored with VK records and only
+ * escaped/encoded later in reglookup and reglookup-recover.
+ */
static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type,
char** error_msg)
{
char* asciip;
char* ascii;
- unsigned char* cur_str;
- char* cur_ascii;
+ char* ascii_tmp;
char* cur_quoted;
- char* tmp_err;
- const char* str_type;
+ char* tmp_err = NULL;
+ const char* delim;
uint32 i;
uint32 cur_str_len;
- uint32 ascii_max, cur_str_max;
- uint32 str_rem, cur_str_rem, alen;
+ uint32 ascii_max;
+ uint32 str_rem, alen;
int ret_err;
- unsigned short num_nulls;
if(datap == NULL)
{
@@ -193,39 +263,23 @@ static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type,
case REG_EXPAND_SZ:
/* REG_LINK is a symbolic link, stored as a unicode string. */
case REG_LINK:
- ascii_max = sizeof(char)*(len+1);
- ascii = malloc(ascii_max);
- if(ascii == NULL)
- return NULL;
-
/* Sometimes values have binary stored in them. If the unicode
* conversion fails, just quote it raw.
*/
- ret_err = uni_to_ascii(datap, ascii, len, ascii_max);
- if(ret_err < 0)
+ cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
+ if(cur_quoted == NULL)
{
- tmp_err = strerror(-ret_err);
- str_type = regfi_type_val2str(type);
- *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
- if(*error_msg == NULL)
+ if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+ strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+ else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
{
- free(ascii);
- return NULL;
+ sprintf(*error_msg, "Buffer could not be quoted due to error: %s",
+ tmp_err);
+ free(tmp_err);
}
- sprintf(*error_msg, "Unicode conversion failed on %s field; "
- "printing as binary. Error: %s", str_type, tmp_err);
-
- cur_quoted = quote_buffer(datap, len, common_special_chars);
- }
- else
- cur_quoted = quote_string(ascii, common_special_chars);
- free(ascii);
- if(cur_quoted == NULL)
- {
- *error_msg = (char*)malloc(27+1);
- if(*error_msg != NULL)
- strcpy(*error_msg, "Buffer could not be quoted.");
}
+ else if (tmp_err != NULL)
+ *error_msg = tmp_err;
return cur_quoted;
break;
@@ -263,104 +317,73 @@ static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type,
return ascii;
break;
-
- /* XXX: this MULTI_SZ parser is pretty inefficient. Should be
- * redone with fewer malloc calls and better string concatenation.
- * Also, gives lame output when "\0\0" is the string.
- */
case REG_MULTI_SZ:
ascii_max = sizeof(char)*(len*4+1);
- cur_str_max = sizeof(char)*(len+1);
- cur_str = malloc(cur_str_max);
- cur_ascii = malloc(cur_str_max);
- ascii = malloc(ascii_max);
- if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
+ ascii_tmp = malloc(ascii_max);
+ if(ascii_tmp == NULL)
return NULL;
- /* Reads until it reaches 4 consecutive NULLs,
- * which is two nulls in unicode, or until it reaches len, or until we
- * run out of buffer. The latter should never happen, but we shouldn't
- * trust our file to have the right lengths/delimiters.
+ /* Attempt to convert entire string from UTF-16LE to ASCII,
+ * then parse and quote fields individually.
+ * If this fails, simply quote entire buffer as binary.
*/
- asciip = ascii;
- num_nulls = 0;
- str_rem = ascii_max;
- cur_str_rem = cur_str_max;
- cur_str_len = 0;
-
- for(i=0; (i < len) && str_rem > 0; i++)
+ ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
+ if(ret_err < 0)
{
- *(cur_str+cur_str_len) = *(datap+i);
- if(*(cur_str+cur_str_len) == 0)
- num_nulls++;
- else
- num_nulls = 0;
- cur_str_len++;
-
- if(num_nulls == 2)
+ tmp_err = strerror(-ret_err);
+ *error_msg = (char*)malloc(61+strlen(tmp_err));
+ if(*error_msg == NULL)
{
- ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
- if(ret_err < 0)
- {
- /* XXX: should every sub-field error be enumerated? */
- if(*error_msg == NULL)
- {
- tmp_err = strerror(-ret_err);
- *error_msg = (char*)malloc(90+strlen(tmp_err)+1);
- if(*error_msg == NULL)
- {
- free(cur_str);
- free(cur_ascii);
- free(ascii);
- return NULL;
- }
- sprintf(*error_msg, "Unicode conversion failed on at least one "
- "MULTI_SZ sub-field; printing as binary. Error: %s",
- tmp_err);
- }
- cur_quoted = quote_buffer(cur_str, cur_str_len-1,
- subfield_special_chars);
- }
- else
- cur_quoted = quote_string(cur_ascii, subfield_special_chars);
-
- alen = snprintf(asciip, str_rem, "%s", cur_quoted);
- asciip += alen;
- str_rem -= alen;
- free(cur_quoted);
+ free(ascii_tmp);
+ return NULL;
+ }
- if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
- break;
- else
+ sprintf(*error_msg, "MULTI_SZ unicode conversion"
+ " failed with '%s'. Quoting as binary.", tmp_err);
+ ascii = quote_buffer(datap, len, subfield_special_chars);
+ }
+ else
+ {
+ ascii = malloc(ascii_max);
+ if(ascii == NULL)
+ {
+ free(ascii_tmp);
+ return NULL;
+ }
+ asciip = ascii;
+ asciip[0] = '\0';
+ str_rem = ascii_max;
+ delim = "";
+ for(i=0; i<ret_err; i+=cur_str_len+1)
+ {
+ cur_str_len = strlen(ascii_tmp+i);
+ if(ascii_tmp[i] != '\0')
{
- if(str_rem > 0)
+ cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
+ if(cur_quoted != NULL)
{
- asciip[0] = '|';
- asciip[1] = '\0';
- asciip++;
- str_rem--;
+ alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
+ asciip += alen;
+ str_rem -= alen;
+ free(cur_quoted);
}
- memset(cur_str, 0, cur_str_max);
- cur_str_len = 0;
- num_nulls = 0;
- /* To eliminate leading nulls in subsequent strings. */
- i++;
}
+ delim = "|";
}
}
- *asciip = 0;
- free(cur_str);
- free(cur_ascii);
+
+ free(ascii_tmp);
return ascii;
break;
/* XXX: Dont know what to do with these yet, just print as binary... */
default:
- /* XXX: It would be really nice if this message somehow included the
- * name of the current value we're having trouble with, since
- * stderr/stdout don't always sync nicely.
- */
- fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
+ *error_msg = (char*)malloc(65);
+ if(*error_msg == NULL)
+ return NULL;
+ sprintf(*error_msg,
+ "Unrecognized registry data type (0x%.8X); quoting as binary.",
+ type);
case REG_NONE:
case REG_RESOURCE_LIST:
diff --git a/src/reglookup-recover.c b/src/reglookup-recover.c
index e761a41..b549a78 100644
--- a/src/reglookup-recover.c
+++ b/src/reglookup-recover.c
@@ -1,7 +1,7 @@
/*
* This program attempts to recover deleted data structures in a registry hive.
*
- * Copyright (C) 2008 Timothy D. Morgan
+ * Copyright (C) 2008-2009 Timothy D. Morgan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,18 +14,18 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: reglookup-recover.c 121 2008-08-09 17:22:26Z tim $
+ * $Id: reglookup-recover.c 152 2009-06-02 20:00:38Z tim $
*/
#include <stdio.h>
#include <stdlib.h>
-#include <sysexits.h>
-#include "../include/regfi.h"
-#include "../include/range_list.h"
-#include "../include/lru_cache.h"
+#include "talloc.h"
+#include "regfi.h"
+#include "range_list.h"
+#include "lru_cache.h"
/* Globals, influenced by command line parameters */
@@ -66,7 +66,7 @@ char* getQuotedData(int fd, uint32 offset, uint32 length)
}
-void printKey(REGF_FILE* f, REGF_NK_REC* nk, const char* prefix)
+void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
{
char mtime[20];
time_t tmp_time[1];
@@ -83,10 +83,11 @@ void printKey(REGF_FILE* f, REGF_NK_REC* nk, const char* prefix)
{
quoted_name = malloc(1*sizeof(char));
if(quoted_name == NULL)
- bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR,
+ "ERROR: Could not allocate sufficient memory.\n");
quoted_name[0] = '\0';
- fprintf(stderr, "WARNING: NULL key name in NK record at offset %.8X.\n",
+ fprintf(stderr, "WARN: NULL key name in NK record at offset %.8X.\n",
nk->offset);
}
@@ -98,10 +99,11 @@ void printKey(REGF_FILE* f, REGF_NK_REC* nk, const char* prefix)
if(print_parsedraw)
free(quoted_raw);
+ free(quoted_name);
}
-void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
+void printValue(REGFI_FILE* f, const REGFI_VK_REC* vk, const char* prefix)
{
char* quoted_value = NULL;
char* quoted_name = NULL;
@@ -121,11 +123,11 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
* name of the current value we're having trouble with, since
* stderr/stdout don't always sync nicely.
*/
- if(size > VK_MAX_DATA_LENGTH)
+ if(size > REGFI_VK_MAX_DATA_LENGTH)
{
- fprintf(stderr, "WARNING: value data size %d larger than "
- "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
- size = VK_MAX_DATA_LENGTH;
+ fprintf(stderr, "WARN: value data size %d larger than "
+ "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
+ size = REGFI_VK_MAX_DATA_LENGTH;
}
quoted_name = quote_string(vk->valuename, key_special_chars);
@@ -138,7 +140,7 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
*/
quoted_name = malloc(1*sizeof(char));
if(quoted_name == NULL)
- bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
quoted_name[0] = '\0';
}
@@ -147,19 +149,19 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
{
quoted_value = malloc(1*sizeof(char));
if(quoted_value == NULL)
- bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
quoted_value[0] = '\0';
if(conv_error == NULL)
- fprintf(stderr, "WARNING: Could not quote value for '%s/%s'. "
+ fprintf(stderr, "WARN: Could not quote value for '%s/%s'. "
"Memory allocation failure likely.\n", prefix, quoted_name);
else if(print_verbose)
- fprintf(stderr, "WARNING: Could not quote value for '%s/%s'. "
+ fprintf(stderr, "WARN: Could not quote value for '%s/%s'. "
"Returned error: %s\n", prefix, quoted_name, conv_error);
}
/* XXX: should these always be printed? */
else if(conv_error != NULL && print_verbose)
- fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
+ fprintf(stderr, "INFO: While quoting value for '%s/%s', "
"warning returned: %s\n", prefix, quoted_name, conv_error);
@@ -187,7 +189,7 @@ void printValue(REGF_FILE* f, const REGF_VK_REC* vk, const char* prefix)
}
-void printSK(REGF_FILE* f, REGF_SK_REC* sk)
+void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
{
char* quoted_raw = NULL;
char* empty_str = "";
@@ -225,7 +227,7 @@ void printSK(REGF_FILE* f, REGF_SK_REC* sk)
}
-int printCell(REGF_FILE* f, uint32 offset)
+int printCell(REGFI_FILE* f, uint32 offset)
{
char* quoted_buf;
uint32 cell_length;
@@ -250,65 +252,75 @@ int printCell(REGF_FILE* f, uint32 offset)
* Paths returned must be free()d.
*/
/* XXX: This is not terribly efficient, as it may reparse many keys
- * repeatedly. Should try to add caching. Also, piecing the path
- * together is slow and redundant.
+ * repeatedly. Should try to add caching.
*/
-char* getParentPath(REGF_FILE* f, REGF_NK_REC* nk)
+char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
{
- void_stack* path_stack = void_stack_new(REGF_MAX_DEPTH);
- REGF_HBIN* hbin;
- REGF_NK_REC* cur_ancestor;
+ void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
+ const REGFI_HBIN* hbin;
+ REGFI_NK_REC* cur_ancestor;
char* ret_val;
- char* path_element;
- char* tmp_str;
- uint32 virt_offset, i, stack_size, ret_val_size, ret_val_left, element_size;
+ uint32 virt_offset, i, stack_size, ret_val_size, ret_val_used;
uint32 max_length;
-
+ REGFI_BUFFER* path_element;
+
/* The path_stack size limit should guarantee that we don't recurse forever. */
virt_offset = nk->parent_off;
- while(virt_offset != REGF_OFFSET_NONE)
+ ret_val_size = 1; /* NUL */
+ while(virt_offset != REGFI_OFFSET_NONE)
{
hbin = regfi_lookup_hbin(f, virt_offset);
if(hbin == NULL)
- virt_offset = REGF_OFFSET_NONE;
+ virt_offset = REGFI_OFFSET_NONE;
else
{
max_length = hbin->block_size + hbin->file_off
- - (virt_offset+REGF_BLOCKSIZE);
- cur_ancestor = regfi_parse_nk(f, virt_offset+REGF_BLOCKSIZE,
+ - (virt_offset+REGFI_REGF_SIZE);
+ cur_ancestor = regfi_parse_nk(f, virt_offset+REGFI_REGF_SIZE,
max_length, true);
+ printMsgs(f);
+
if(cur_ancestor == NULL)
- virt_offset = REGF_OFFSET_NONE;
+ virt_offset = REGFI_OFFSET_NONE;
else
{
- if(cur_ancestor->key_type == NK_TYPE_ROOTKEY)
- virt_offset = REGF_OFFSET_NONE;
+ if(cur_ancestor->key_type & REGFI_NK_FLAG_ROOT)
+ virt_offset = REGFI_OFFSET_NONE;
else
virt_offset = cur_ancestor->parent_off;
- path_element = quote_string(cur_ancestor->keyname, key_special_chars);
- if(path_element == NULL || !void_stack_push(path_stack, path_element))
+ path_element = talloc(path_stack, REGFI_BUFFER);
+ if(path_element != NULL)
+ path_element->buf = (uint8*)quote_string(cur_ancestor->keyname,
+ key_special_chars);
+
+ if(path_element == NULL || path_element->buf == NULL
+ || !void_stack_push(path_stack, path_element))
{
- free(cur_ancestor->keyname);
- free(cur_ancestor);
- void_stack_free_deep(path_stack);
+ /* XXX: Need to add a warning here */
+ regfi_free_key(cur_ancestor);
+ void_stack_free(path_stack);
return NULL;
}
- regfi_key_free(cur_ancestor);
+ /* Path element and preceeding delimiter
+ * Note that this integer can't overflow since key name lengths are
+ * 16 bits and the max depth is 512.
+ */
+ path_element->len = strlen((char*)path_element->buf);
+ ret_val_size += path_element->len + 1;
+
+ regfi_free_key(cur_ancestor);
}
}
}
stack_size = void_stack_size(path_stack);
- ret_val_size = 16*stack_size;
- if(ret_val_size == 0)
- ret_val_size = 1;
- ret_val_left = ret_val_size;
+ ret_val_used = 0;
ret_val = malloc(ret_val_size);
if(ret_val == NULL)
{
- void_stack_free_deep(path_stack);
+ void_stack_free(path_stack);
return NULL;
}
ret_val[0] = '\0';
@@ -316,23 +328,11 @@ char* getParentPath(REGF_FILE* f, REGF_NK_REC* nk)
for(i=0; i<stack_size; i++)
{
path_element = void_stack_pop(path_stack);
- element_size = strlen(path_element);
- if(ret_val_left < element_size+2)
- {
- ret_val_size += element_size+16;
- ret_val_left += element_size+16;
- tmp_str = (char*)realloc(ret_val, ret_val_size);
- if(tmp_str == NULL)
- {
- free(ret_val);
- void_stack_free_deep(path_stack);
- return NULL;
- }
- ret_val = tmp_str;
- }
-
- ret_val_left -= snprintf(ret_val+ret_val_size-ret_val_left,ret_val_left, "/%s", path_element);
- free(path_element);
+ snprintf(ret_val+ret_val_used, ret_val_size-ret_val_used,
+ "/%s", path_element->buf);
+ ret_val_used += path_element->len + 1;
+ free(path_element->buf);
+ talloc_free(path_element);
}
void_stack_free(path_stack);
@@ -412,31 +412,154 @@ bool removeRange(range_list* rl, uint32 offset, uint32 length)
}
+int extractVKs(REGFI_FILE* f,
+ range_list* unalloc_cells,
+ range_list* unalloc_values)
+{
+ const range_list_element* cur_elem;
+ REGFI_VK_REC* vk;
+ uint32 i, j;
+
+ for(i=0; i < range_list_size(unalloc_cells); i++)
+ {
+ printMsgs(f);
+ cur_elem = range_list_get(unalloc_cells, i);
+ for(j=0; j <= cur_elem->length; j+=8)
+ {
+ vk = regfi_parse_vk(f, cur_elem->offset+j,
+ cur_elem->length-j, false);
+ printMsgs(f);
+
+ if(vk != NULL)
+ {
+ if(!range_list_add(unalloc_values, vk->offset,
+ vk->cell_size, vk))
+ {
+ fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n");
+ return 20;
+ }
+ j+=vk->cell_size-8;
+ }
+ }
+ }
+
+ /* Remove value ranges from the unalloc_cells before we continue. */
+ for(i=0; i<range_list_size(unalloc_values); i++)
+ {
+ cur_elem = range_list_get(unalloc_values, i);
+ if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+ return 30;
+ }
+
+ return 0;
+}
+
+
+int extractDataCells(REGFI_FILE* f,
+ range_list* unalloc_cells,
+ range_list* unalloc_values)
+{
+ const range_list_element* cur_elem;
+ REGFI_VK_REC* vk;
+ const REGFI_HBIN* hbin;
+ REGFI_BUFFER data;
+ uint32 i, off, data_offset, data_maxsize;
+
+ for(i=0; i<range_list_size(unalloc_values); i++)
+ {
+ cur_elem = range_list_get(unalloc_values, i);
+ vk = (REGFI_VK_REC*)cur_elem->data;
+ if(vk == NULL)
+ return 40;
+
+ if(vk->data_size == 0)
+ vk->data = NULL;
+ else
+ {
+ off = vk->data_off+REGFI_REGF_SIZE;
+
+ if(vk->data_in_offset)
+ {
+ data = regfi_load_data(f, vk->type, vk->data_off,
+ vk->data_size, 4,
+ vk->data_in_offset, false);
+ vk->data = data.buf;
+ vk->data_size = data.len;
+ }
+ else if(range_list_has_range(unalloc_cells, off, vk->data_size))
+ {
+ hbin = regfi_lookup_hbin(f, vk->data_off);
+ if(hbin)
+ {
+ data_offset = vk->data_off+REGFI_REGF_SIZE;
+ data_maxsize = hbin->block_size + hbin->file_off - data_offset;
+ data = regfi_load_data(f, vk->type, data_offset,
+ vk->data_size, data_maxsize,
+ vk->data_in_offset, false);
+ vk->data = data.buf;
+ vk->data_size = data.len;
+
+ if(vk->data != NULL)
+ {
+ /* XXX: The following may not make sense now in light of big data
+ * records.
+ */
+ /* XXX: This strict checking prevents partial recovery of data
+ * cells. Also, see code for regfi_load_data and note that
+ * lengths indicated in VK records are sometimes just plain
+ * wrong. Need a feedback mechanism to be more fuzzy with
+ * data cell lengths and the ranges removed.
+ *
+ * The introduction of REGFI_BUFFER in regfi_load_data has
+ * fixed some of this. Should review again with respect to
+ * the other issues mentioned above though.
+ */
+ /* A data record was recovered. Remove from unalloc_cells. */
+ if(!removeRange(unalloc_cells, off, vk->data_size))
+ return 50;
+ }
+ }
+ else
+ vk->data = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
/* NOTE: unalloc_keys should be an empty range_list. */
-int extractKeys(REGF_FILE* f,
+int extractKeys(REGFI_FILE* f,
range_list* unalloc_cells,
range_list* unalloc_keys)
{
const range_list_element* cur_elem;
- REGF_NK_REC* key;
+ REGFI_NK_REC* key;
uint32 i, j;
+ int error_code = 0;
for(i=0; i < range_list_size(unalloc_cells); i++)
{
+ printMsgs(f);
cur_elem = range_list_get(unalloc_cells, i);
for(j=0; cur_elem->length > REGFI_NK_MIN_LENGTH
&& j <= cur_elem->length-REGFI_NK_MIN_LENGTH; j+=8)
{
key = regfi_parse_nk(f, cur_elem->offset+j,
cur_elem->length-j, false);
+ printMsgs(f);
+
if(key != NULL)
{
if(!range_list_add(unalloc_keys, key->offset,
key->cell_size, key))
{
fprintf(stderr, "ERROR: Couldn't add key to unalloc_keys.\n");
- return 20;
+ error_code = 20;
+ goto fail;
}
+ talloc_steal(unalloc_keys, key);
j+=key->cell_size-8;
}
}
@@ -446,19 +569,27 @@ int extractKeys(REGF_FILE* f,
{
cur_elem = range_list_get(unalloc_keys, i);
if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
- return 30;
+ {
+ error_code = 30;
+ goto fail;
+ }
}
return 0;
-}
+ fail:
+ regfi_free_key(key);
+ return error_code;
+}
-int extractValueLists(REGF_FILE* f,
+int extractValueLists(REGFI_FILE* f,
range_list* unalloc_cells,
- range_list* unalloc_keys)
+ range_list* unalloc_keys,
+ range_list* unalloc_linked_values)
{
- REGF_NK_REC* nk;
- REGF_HBIN* hbin;
+ REGFI_NK_REC* nk;
+ REGFI_VK_REC* vk;
+ const REGFI_HBIN* hbin;
const range_list_element* cur_elem;
uint32 i, j, num_keys, off, values_length, max_length;
@@ -470,43 +601,32 @@ int extractValueLists(REGF_FILE* f,
return 10;
nk = cur_elem->data;
- if(nk->num_values && (nk->values_off!=REGF_OFFSET_NONE))
+ if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
{
hbin = regfi_lookup_hbin(f, nk->values_off);
if(hbin != NULL)
{
- off = nk->values_off + REGF_BLOCKSIZE;
+ off = nk->values_off + REGFI_REGF_SIZE;
max_length = hbin->block_size + hbin->file_off - off;
- /* XXX: This is a hack. We parse all value-lists, VK records,
- * and data records without regard for current allocation status.
- * On the off chance that such a record correctly parsed but is
- * actually a reallocated structure used by something else, we
- * simply prune it after the fact. Would be faster to check this
- * up front somehow.
- */
- nk->values = regfi_load_valuelist(f, off, nk->num_values, max_length,
- false);
- values_length = (nk->num_values+1)*sizeof(uint32);
- if(values_length != (values_length & 0xFFFFFFF8))
- values_length = (values_length & 0xFFFFFFF8) + 8;
-
- if(nk->values != NULL)
+ nk->values = regfi_load_valuelist(f, off, nk->num_values,
+ max_length, false);
+ if(nk->values != NULL && nk->values->elements != NULL)
{
+ /* Number of elements in the value list may be shorter than advertised
+ * by NK record due to cell truncation. We'll consider this valid and
+ * only throw out the whole value list if it bleeds into an already
+ * parsed structure.
+ */
+ values_length = (nk->values->num_values+1)*sizeof(uint32);
+ if(values_length != (values_length & 0xFFFFFFF8))
+ values_length = (values_length & 0xFFFFFFF8) + 8;
+
if(!range_list_has_range(unalloc_cells, off, values_length))
{ /* We've parsed a values-list which isn't in the unallocated list,
- * so prune it.
+ * so prune it.
*/
- for(j=0; j<nk->num_values; j++)
- {
- if(nk->values[j] != NULL)
- {
- if(nk->values[j]->data != NULL)
- free(nk->values[j]->data);
- free(nk->values[j]);
- }
- }
- free(nk->values);
+ talloc_free(nk->values);
nk->values = NULL;
}
else
@@ -516,51 +636,32 @@ int extractValueLists(REGF_FILE* f,
if(!removeRange(unalloc_cells, off, values_length))
return 20;
- for(j=0; j < nk->num_values; j++)
+ for(j=0; j < nk->values->num_values; j++)
{
- if(nk->values[j] != NULL)
+ /* Don't bother to restrict cell length here, since we'll
+ * check our unalloc_cells range_list later.
+ */
+ vk = regfi_parse_vk(f, nk->values->elements[j]+REGFI_REGF_SIZE,
+ 0x7FFFFFFF, false);
+ printMsgs(f);
+
+ if(vk != NULL)
{
- if(!range_list_has_range(unalloc_cells, nk->values[j]->offset,
- nk->values[j]->cell_size))
- { /* We've parsed a value which isn't in the unallocated list,
- * so prune it.
- */
- if(nk->values[j]->data != NULL)
- free(nk->values[j]->data);
- free(nk->values[j]);
- nk->values[j] = NULL;
- }
- else
+ if(range_list_has_range(unalloc_cells,
+ vk->offset, vk->cell_size))
{
- /* A VK record was recovered. Remove from unalloc_cells
- * and inspect data.
- */
- if(!removeRange(unalloc_cells, nk->values[j]->offset,
- nk->values[j]->cell_size))
- return 21;
-
- /* Don't bother pruning or removing from unalloc_cells if
- * there is no data, or it is stored in the offset.
- */
- if(nk->values[j]->data != NULL && !nk->values[j]->data_in_offset)
+ if(!range_list_add(unalloc_linked_values, vk->offset,
+ vk->cell_size, vk))
{
- off = nk->values[j]->data_off+REGF_BLOCKSIZE;
- if(!range_list_has_range(unalloc_cells, off,
- nk->values[j]->data_size))
- { /* We've parsed a data cell which isn't in the unallocated
- * list, so prune it.
- */
- free(nk->values[j]->data);
- nk->values[j]->data = NULL;
- }
- else
- { /*A data record was recovered. Remove from unalloc_cells.*/
- if(!removeRange(unalloc_cells, off,
- nk->values[j]->data_size))
- return 22;
- }
+ talloc_free(vk);
+ return 30;
}
+
+ if(!removeRange(unalloc_cells, vk->offset, vk->cell_size))
+ return 40;
}
+ else
+ talloc_free(vk);
}
}
}
@@ -573,89 +674,26 @@ int extractValueLists(REGF_FILE* f,
}
-/* NOTE: unalloc_values should be an empty range_list. */
-int extractValues(REGF_FILE* f,
- range_list* unalloc_cells,
- range_list* unalloc_values)
-{
- const range_list_element* cur_elem;
- REGF_VK_REC* vk;
- uint32 i, j, off;
-
- for(i=0; i < range_list_size(unalloc_cells); i++)
- {
- cur_elem = range_list_get(unalloc_cells, i);
- for(j=0; j <= cur_elem->length; j+=8)
- {
- vk = regfi_parse_vk(f, cur_elem->offset+j,
- cur_elem->length-j, false);
- if(vk != NULL)
- {
- if(!range_list_add(unalloc_values, vk->offset,
- vk->cell_size, vk))
- {
- fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n");
- return 20;
- }
- j+=vk->cell_size-8;
- }
- }
- }
-
- /* Remove value ranges from the unalloc_cells before we continue. */
- for(i=0; i<range_list_size(unalloc_values); i++)
- {
- cur_elem = range_list_get(unalloc_values, i);
- if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
- return 30;
- }
-
- /* Now see if the data associated with each value is intact */
- for(i=0; i<range_list_size(unalloc_values); i++)
- {
- cur_elem = range_list_get(unalloc_values, i);
- vk = (REGF_VK_REC*)cur_elem->data;
- if(vk == NULL)
- return 40;
-
- if(vk->data != NULL && !vk->data_in_offset)
- {
- off = vk->data_off+REGF_BLOCKSIZE;
- if(!range_list_has_range(unalloc_cells, off, vk->data_size))
- { /* We've parsed a data cell which isn't in the unallocated
- * list, so prune it.
- */
- free(vk->data);
- vk->data = NULL;
- }
- else
- { /*A data record was recovered. Remove from unalloc_cells.*/
- if(!removeRange(unalloc_cells, off, vk->data_size))
- return 50;
- }
- }
- }
-
- return 0;
-}
-
/* NOTE: unalloc_sks should be an empty range_list. */
-int extractSKs(REGF_FILE* f,
+int extractSKs(REGFI_FILE* f,
range_list* unalloc_cells,
range_list* unalloc_sks)
{
const range_list_element* cur_elem;
- REGF_SK_REC* sk;
+ REGFI_SK_REC* sk;
uint32 i, j;
for(i=0; i < range_list_size(unalloc_cells); i++)
{
+ printMsgs(f);
cur_elem = range_list_get(unalloc_cells, i);
for(j=0; j <= cur_elem->length; j+=8)
{
sk = regfi_parse_sk(f, cur_elem->offset+j,
cur_elem->length-j, false);
+ printMsgs(f);
+
if(sk != NULL)
{
if(!range_list_add(unalloc_sks, sk->offset,
@@ -664,6 +702,7 @@ int extractSKs(REGF_FILE* f,
fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n");
return 20;
}
+ talloc_steal(unalloc_sks, sk);
j+=sk->cell_size-8;
}
}
@@ -682,25 +721,25 @@ int extractSKs(REGF_FILE* f,
int main(int argc, char** argv)
{
- REGF_FILE* f;
+ REGFI_FILE* f;
const range_list_element* cur_elem;
range_list* unalloc_cells;
range_list* unalloc_keys;
+ range_list* unalloc_linked_values;
range_list* unalloc_values;
range_list* unalloc_sks;
char** parent_paths;
char* tmp_name;
char* tmp_path;
- REGF_NK_REC* tmp_key;
- REGF_VK_REC* tmp_value;
+ REGFI_NK_REC* tmp_key;
+ REGFI_VK_REC* tmp_value;
uint32 argi, arge, i, j, ret, num_unalloc_keys;
- /* uint32 test_offset;*/
/* Process command line arguments */
if(argc < 2)
{
usage();
- bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
+ bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
}
arge = argc-1;
@@ -724,20 +763,24 @@ int main(int argc, char** argv)
{
usage();
fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
- bailOut(EX_USAGE, "");
+ bailOut(REGLOOKUP_EXIT_USAGE, "");
}
}
/*test_offset = strtol(argv[argi++], NULL, 16);*/
if((registry_file = strdup(argv[argi])) == NULL)
- bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
f = regfi_open(registry_file);
if(f == NULL)
{
fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
- bailOut(EX_NOINPUT, "");
+ bailOut(REGLOOKUP_EXIT_NOINPUT, "");
}
+ if(print_verbose)
+ regfi_set_message_mask(f, REGFI_MSG_ERROR|REGFI_MSG_WARN|REGFI_MSG_INFO);
+ else
+ regfi_set_message_mask(f, REGFI_MSG_ERROR);
if(print_header)
printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME,"
@@ -755,6 +798,10 @@ int main(int argc, char** argv)
if(unalloc_keys == NULL)
return 10;
+ unalloc_linked_values = range_list_new();
+ if(unalloc_linked_values == NULL)
+ return 10;
+
unalloc_values = range_list_new();
if(unalloc_values == NULL)
return 10;
@@ -770,21 +817,35 @@ int main(int argc, char** argv)
return ret;
}
- ret = extractValueLists(f, unalloc_cells, unalloc_keys);
+ ret = extractValueLists(f, unalloc_cells, unalloc_keys,unalloc_linked_values);
if(ret != 0)
{
fprintf(stderr, "ERROR: extractValueLists() failed with %d.\n", ret);
return ret;
}
- /* Carve any orphan values and associated data */
- ret = extractValues(f, unalloc_cells, unalloc_values);
+ /* Carve any orphan values */
+ ret = extractVKs(f, unalloc_cells, unalloc_values);
if(ret != 0)
{
- fprintf(stderr, "ERROR: extractValues() failed with %d.\n", ret);
+ fprintf(stderr, "ERROR: extractVKs() failed with %d.\n", ret);
return ret;
}
+ /* Carve any data associated with VK records */
+ ret = extractDataCells(f, unalloc_cells, unalloc_linked_values);
+ if(ret != 0)
+ {
+ fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+ return ret;
+ }
+ ret = extractDataCells(f, unalloc_cells, unalloc_values);
+ if(ret != 0)
+ {
+ fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+ return ret;
+ }
+
/* Carve any SK records */
ret = extractSKs(f, unalloc_cells, unalloc_sks);
if(ret != 0)
@@ -804,7 +865,7 @@ int main(int argc, char** argv)
for(i=0; i < num_unalloc_keys; i++)
{
cur_elem = range_list_get(unalloc_keys, i);
- tmp_key = (REGF_NK_REC*)cur_elem->data;
+ tmp_key = (REGFI_NK_REC*)cur_elem->data;
if(tmp_key == NULL)
return 20;
@@ -815,11 +876,10 @@ int main(int argc, char** argv)
}
/* Now start the output */
-
for(i=0; i < num_unalloc_keys; i++)
{
cur_elem = range_list_get(unalloc_keys, i);
- tmp_key = (REGF_NK_REC*)cur_elem->data;
+ tmp_key = (REGFI_NK_REC*)cur_elem->data;
printKey(f, tmp_key, parent_paths[i]);
if(tmp_key->num_values > 0 && tmp_key->values != NULL)
@@ -827,11 +887,18 @@ int main(int argc, char** argv)
tmp_name = quote_string(tmp_key->keyname, key_special_chars);
tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
if(tmp_path == NULL)
+ {
+ free(tmp_name);
return 10;
+ }
+
sprintf(tmp_path, "%s/%s", parent_paths[i], tmp_name);
- for(j=0; j < tmp_key->num_values; j++)
+ for(j=0; j < tmp_key->values->num_values; j++)
{
- tmp_value = tmp_key->values[j];
+ tmp_value =
+ (REGFI_VK_REC*)range_list_find_data(unalloc_linked_values,
+ tmp_key->values->elements[j]
+ + REGFI_REGF_SIZE);
if(tmp_value != NULL)
printValue(f, tmp_value, tmp_path);
}
@@ -846,7 +913,7 @@ int main(int argc, char** argv)
for(i=0; i < range_list_size(unalloc_values); i++)
{
cur_elem = range_list_get(unalloc_values, i);
- tmp_value = (REGF_VK_REC*)cur_elem->data;
+ tmp_value = (REGFI_VK_REC*)cur_elem->data;
printValue(f, tmp_value, "");
}
@@ -860,5 +927,11 @@ int main(int argc, char** argv)
}
}
+ range_list_free(unalloc_cells);
+ range_list_free(unalloc_keys);
+ range_list_free(unalloc_linked_values);
+ range_list_free(unalloc_values);
+ range_list_free(unalloc_sks);
+
return 0;
}
diff --git a/src/reglookup.c b/src/reglookup.c
index 4eb43c9..e9d2a2c 100644
--- a/src/reglookup.c
+++ b/src/reglookup.c
@@ -1,8 +1,7 @@
/*
- * A utility to read a Windows NT/2K/XP/2K3 registry file, using
- * Gerald Carter''s regfio interface.
+ * A utility to read a Windows NT and later registry files.
*
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2009 Timothy D. Morgan
* Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: reglookup.c 121 2008-08-09 17:22:26Z tim $
+ * $Id: reglookup.c 150 2009-03-02 02:17:46Z tim $
*/
#include <stdlib.h>
-#include <sysexits.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <time.h>
-#include "../include/regfi.h"
-#include "../include/void_stack.h"
+#include "regfi.h"
+#include "void_stack.h"
/* Globals, influenced by command line parameters */
bool print_verbose = false;
@@ -42,16 +40,16 @@ int type_filter;
char* registry_file = NULL;
/* Other globals */
-REGF_FILE* f;
+REGFI_FILE* f;
/* XXX: A hack to share some functions with reglookup-recover.c.
- * Should move these into a properly library at some point.
+ * Should move these into a proper library at some point.
*/
#include "common.c"
-void printValue(const REGF_VK_REC* vk, char* prefix)
+void printValue(const REGFI_VK_REC* vk, char* prefix)
{
char* quoted_value = NULL;
char* quoted_name = NULL;
@@ -65,11 +63,11 @@ void printValue(const REGF_VK_REC* vk, char* prefix)
* malicious. For more info, see:
* http://msdn2.microsoft.com/en-us/library/ms724872.aspx
*/
- if(size > VK_MAX_DATA_LENGTH)
+ if(size > REGFI_VK_MAX_DATA_LENGTH)
{
- fprintf(stderr, "WARNING: value data size %d larger than "
- "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
- size = VK_MAX_DATA_LENGTH;
+ fprintf(stderr, "WARN: value data size %d larger than "
+ "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
+ size = REGFI_VK_MAX_DATA_LENGTH;
}
quoted_name = quote_string(vk->valuename, key_special_chars);
@@ -82,24 +80,32 @@ void printValue(const REGF_VK_REC* vk, char* prefix)
*/
quoted_name = malloc(1*sizeof(char));
if(quoted_name == NULL)
- bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
quoted_name[0] = '\0';
}
- quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
- if(quoted_value == NULL)
+ if(vk->data == NULL)
{
- if(conv_error == NULL)
- fprintf(stderr, "WARNING: Could not quote value for '%s/%s'. "
- "Memory allocation failure likely.\n", prefix, quoted_name);
- else if(print_verbose)
- fprintf(stderr, "WARNING: Could not quote value for '%s/%s'. "
- "Returned error: %s\n", prefix, quoted_name, conv_error);
+ if(print_verbose)
+ fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+ "data pointer was NULL.\n", prefix, quoted_name);
+ }
+ else
+ {
+ quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+ if(quoted_value == NULL)
+ {
+ if(conv_error == NULL)
+ fprintf(stderr, "WARN: Could not quote value for '%s/%s'. "
+ "Memory allocation failure likely.\n", prefix, quoted_name);
+ else
+ fprintf(stderr, "WARN: Could not quote value for '%s/%s'. "
+ "Returned error: %s\n", prefix, quoted_name, conv_error);
+ }
+ else if(conv_error != NULL && print_verbose)
+ fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+ "warning returned: %s\n", prefix, quoted_name, conv_error);
}
- /* XXX: should these always be printed? */
- else if(conv_error != NULL && print_verbose)
- fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
- "warning returned: %s\n", prefix, quoted_name, conv_error);
str_type = regfi_type_val2str(vk->type);
if(print_security)
@@ -130,11 +136,6 @@ void printValue(const REGF_VK_REC* vk, char* prefix)
}
-
-/* XXX: Each chunk must be unquoted after it is split out.
- * Quoting syntax may need to be standardized and pushed into the API
- * to deal with this issue and others.
- */
char** splitPath(const char* s)
{
char** ret_val;
@@ -143,7 +144,7 @@ char** splitPath(const char* s)
char* copy;
uint32 ret_cur = 0;
- ret_val = (char**)malloc((REGF_MAX_DEPTH+1+1)*sizeof(char**));
+ ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
if (ret_val == NULL)
return NULL;
ret_val[0] = NULL;
@@ -158,15 +159,15 @@ char** splitPath(const char* s)
{
copy = (char*)malloc((next-cur+1)*sizeof(char));
if(copy == NULL)
- bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
memcpy(copy, cur, next-cur);
copy[next-cur] = '\0';
ret_val[ret_cur++] = copy;
- if(ret_cur < (REGF_MAX_DEPTH+1+1))
+ if(ret_cur < (REGFI_MAX_DEPTH+1+1))
ret_val[ret_cur] = NULL;
else
- bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+ bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
}
cur = next+1;
}
@@ -176,10 +177,10 @@ char** splitPath(const char* s)
{
copy = strdup(cur);
ret_val[ret_cur++] = copy;
- if(ret_cur < (REGF_MAX_DEPTH+1+1))
+ if(ret_cur < (REGFI_MAX_DEPTH+1+1))
ret_val[ret_cur] = NULL;
else
- bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+ bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
}
return ret_val;
@@ -201,9 +202,6 @@ void freePath(char** path)
/* Returns a quoted path from an iterator's stack */
-/* XXX: Some way should be found to integrate this into regfi's API
- * The problem is that the escaping is sorta reglookup-specific.
- */
char* iter2Path(REGFI_ITERATOR* i)
{
const REGFI_ITER_POSITION* cur;
@@ -256,6 +254,7 @@ char* iter2Path(REGFI_ITERATOR* i)
buf_len += name_len+1+grow_amt-buf_left;
if((new_buf = realloc(buf, buf_len)) == NULL)
{
+ free(name);
free(buf);
free(iter);
return NULL;
@@ -273,38 +272,42 @@ char* iter2Path(REGFI_ITERATOR* i)
}
-void printValueList(REGFI_ITERATOR* i, char* prefix)
+void printValueList(REGFI_ITERATOR* iter, char* prefix)
{
- const REGF_VK_REC* value;
+ REGFI_VK_REC* value;
- value = regfi_iterator_first_value(i);
+ value = regfi_iterator_first_value(iter);
while(value != NULL)
{
if(!type_filter_enabled || (value->type == type_filter))
printValue(value, prefix);
- value = regfi_iterator_next_value(i);
+ regfi_free_value(value);
+ value = regfi_iterator_next_value(iter);
+ printMsgs(iter->f);
}
}
-void printKey(REGFI_ITERATOR* i, char* full_path)
+void printKey(REGFI_ITERATOR* iter, char* full_path)
{
static char empty_str[1] = "";
char* owner = NULL;
char* group = NULL;
char* sacl = NULL;
char* dacl = NULL;
+ char* quoted_classname;
+ char* error_msg = NULL;
char mtime[20];
time_t tmp_time[1];
struct tm* tmp_time_s = NULL;
- const REGF_SK_REC* sk;
- const REGF_NK_REC* k = regfi_iterator_cur_key(i);
+ const REGFI_SK_REC* sk;
+ const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
*tmp_time = nt_time_to_unix(&k->mtime);
tmp_time_s = gmtime(tmp_time);
strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
- if(print_security && (sk=regfi_iterator_cur_sk(i)))
+ if(print_security && (sk=regfi_iterator_cur_sk(iter)))
{
owner = regfi_get_owner(sk->sec_desc);
group = regfi_get_group(sk->sec_desc);
@@ -319,8 +322,36 @@ void printKey(REGFI_ITERATOR* i, char* full_path)
if(dacl == NULL)
dacl = empty_str;
- printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime,
- owner, group, sacl, dacl);
+ if(k->classname != NULL)
+ {
+ quoted_classname = quote_unicode((uint8*)k->classname, k->classname_length,
+ key_special_chars, &error_msg);
+ if(quoted_classname == NULL)
+ {
+ if(error_msg == NULL)
+ fprintf(stderr, "ERROR: Could not quote classname"
+ " for key '%s' due to unknown error.\n", full_path);
+ else
+ {
+ fprintf(stderr, "ERROR: Could not quote classname"
+ " for key '%s' due to error: %s\n", full_path, error_msg);
+ free(error_msg);
+ }
+ }
+ else if (error_msg != NULL)
+ {
+ if(print_verbose)
+ fprintf(stderr, "INFO: While converting classname"
+ " for key '%s': %s.\n", full_path, error_msg);
+ free(error_msg);
+ }
+ }
+ else
+ quoted_classname = empty_str;
+
+ printMsgs(iter->f);
+ printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime,
+ owner, group, sacl, dacl, quoted_classname);
if(owner != empty_str)
free(owner);
@@ -330,6 +361,8 @@ void printKey(REGFI_ITERATOR* i, char* full_path)
free(sacl);
if(dacl != empty_str)
free(dacl);
+ if(quoted_classname != empty_str)
+ free(quoted_classname);
}
else
printf("%s,KEY,,%s\n", full_path, mtime);
@@ -338,18 +371,19 @@ void printKey(REGFI_ITERATOR* i, char* full_path)
void printKeyTree(REGFI_ITERATOR* iter)
{
- const REGF_NK_REC* root = NULL;
- const REGF_NK_REC* cur = NULL;
- const REGF_NK_REC* sub = NULL;
+ const REGFI_NK_REC* root = NULL;
+ const REGFI_NK_REC* cur = NULL;
+ REGFI_NK_REC* sub = NULL;
char* path = NULL;
int key_type = regfi_type_str2val("KEY");
bool print_this = true;
root = cur = regfi_iterator_cur_key(iter);
sub = regfi_iterator_first_subkey(iter);
-
+ printMsgs(iter->f);
+
if(root == NULL)
- bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
+ bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n");
do
{
@@ -357,8 +391,8 @@ void printKeyTree(REGFI_ITERATOR* iter)
{
path = iter2Path(iter);
if(path == NULL)
- bailOut(EX_OSERR, "ERROR: Could not construct iterator's path.\n");
-
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not construct iterator's path.\n");
+
if(!type_filter_enabled || (key_type == type_filter))
printKey(iter, path);
if(!type_filter_enabled || (key_type != type_filter))
@@ -373,11 +407,17 @@ void printKeyTree(REGFI_ITERATOR* iter)
{
/* We're done with this sub-tree, going up and hitting other branches. */
if(!regfi_iterator_up(iter))
- bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
-
+ {
+ printMsgs(iter->f);
+ bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n");
+ }
+
cur = regfi_iterator_cur_key(iter);
if(cur == NULL)
- bailOut(EX_DATAERR, "ERROR: unexpected NULL for key.\n");
+ {
+ printMsgs(iter->f);
+ bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
+ }
sub = regfi_iterator_next_subkey(iter);
}
@@ -388,20 +428,27 @@ void printKeyTree(REGFI_ITERATOR* iter)
* Let's move down and print this first sub-tree out.
*/
if(!regfi_iterator_down(iter))
- bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
+ {
+ printMsgs(iter->f);
+ bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n");
+ }
- cur = sub;
+ cur = regfi_iterator_cur_key(iter);
+ regfi_free_key(sub);
sub = regfi_iterator_first_subkey(iter);
print_this = true;
}
+ printMsgs(iter->f);
} while(!((cur == root) && (sub == NULL)));
if(print_verbose)
- fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
+ fprintf(stderr, "INFO: Finished printing key tree.\n");
}
-/* XXX: what if there is BOTH a value AND a key with that name?? */
+/* XXX: What if there is BOTH a value AND a key with that name??
+ * What if there are multiple keys/values with the same name??
+ */
/*
* Returns 0 if path was not found.
* Returns 1 if path was found as value.
@@ -410,7 +457,7 @@ void printKeyTree(REGFI_ITERATOR* iter)
*/
int retrievePath(REGFI_ITERATOR* iter, char** path)
{
- const REGF_VK_REC* value;
+ REGFI_VK_REC* value;
char* tmp_path_joined;
const char** tmp_path;
uint32 i;
@@ -419,33 +466,33 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
return -1;
/* One extra for any value at the end, and one more for NULL */
- tmp_path = (const char**)malloc(sizeof(const char**)*(REGF_MAX_DEPTH+1+1));
+ tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1));
if(tmp_path == NULL)
return -2;
/* Strip any potential value name at end of path */
for(i=0;
- (path[i] != NULL) && (path[i+1] != NULL)
- && (i < REGF_MAX_DEPTH+1+1);
+ (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1);
i++)
- tmp_path[i] = path[i];
-
+ { tmp_path[i] = path[i]; }
tmp_path[i] = NULL;
if(print_verbose)
- fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
+ fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n",
path_filter);
/* Special check for '/' path filter */
if(path[0] == NULL)
{
if(print_verbose)
- fprintf(stderr, "VERBOSE: Found final path element as root key.\n");
+ fprintf(stderr, "INFO: Found final path element as root key.\n");
+ free(tmp_path);
return 2;
}
if(!regfi_iterator_walk_path(iter, tmp_path))
{
+ printMsgs(iter->f);
free(tmp_path);
return 0;
}
@@ -453,34 +500,41 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
if(regfi_iterator_find_value(iter, path[i]))
{
if(print_verbose)
- fprintf(stderr, "VERBOSE: Found final path element as value.\n");
+ fprintf(stderr, "INFO: Found final path element as value.\n");
value = regfi_iterator_cur_value(iter);
+ printMsgs(iter->f);
tmp_path_joined = iter2Path(iter);
if((value == NULL) || (tmp_path_joined == NULL))
- bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n");
if(!type_filter_enabled || (value->type == type_filter))
printValue(value, tmp_path_joined);
+ regfi_free_value(value);
free(tmp_path);
free(tmp_path_joined);
return 1;
}
else if(regfi_iterator_find_subkey(iter, path[i]))
{
+ printMsgs(iter->f);
if(print_verbose)
- fprintf(stderr, "VERBOSE: Found final path element as key.\n");
+ fprintf(stderr, "INFO: Found final path element as key.\n");
if(!regfi_iterator_down(iter))
- bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
+ {
+ printMsgs(iter->f);
+ bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
+ }
return 2;
}
+ printMsgs(iter->f);
if(print_verbose)
- fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
+ fprintf(stderr, "INFO: Could not find last element of path.\n");
return 0;
}
@@ -515,7 +569,7 @@ int main(int argc, char** argv)
if(argc < 2)
{
usage();
- bailOut(EX_USAGE, "ERROR: Requires at least one argument.\n");
+ bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
}
arge = argc-1;
@@ -526,10 +580,10 @@ int main(int argc, char** argv)
if(++argi >= arge)
{
usage();
- bailOut(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
+ bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-p' option requires parameter.\n");
}
if((path_filter = strdup(argv[argi])) == NULL)
- bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
path_filter_enabled = true;
}
@@ -538,12 +592,12 @@ int main(int argc, char** argv)
if(++argi >= arge)
{
usage();
- bailOut(EX_USAGE, "ERROR: '-t' option requires parameter.\n");
+ bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-t' option requires parameter.\n");
}
if((type_filter = regfi_type_str2val(argv[argi])) < 0)
{
fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
- bailOut(EX_USAGE, "");
+ bailOut(REGLOOKUP_EXIT_USAGE, "");
}
type_filter_enabled = true;
}
@@ -561,27 +615,30 @@ int main(int argc, char** argv)
{
usage();
fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
- bailOut(EX_USAGE, "");
+ bailOut(REGLOOKUP_EXIT_USAGE, "");
}
}
if((registry_file = strdup(argv[argi])) == NULL)
- bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
f = regfi_open(registry_file);
if(f == NULL)
{
fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
- bailOut(EX_NOINPUT, "");
+ bailOut(REGLOOKUP_EXIT_NOINPUT, "");
}
+ if(print_verbose)
+ regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
+
iter = regfi_iterator_new(f);
if(iter == NULL)
- bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
+ bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
if(print_header)
{
if(print_security)
- printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
+ printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n");
else
printf("PATH,TYPE,VALUE,MTIME\n");
}
@@ -592,17 +649,19 @@ int main(int argc, char** argv)
if(path != NULL)
{
retr_path_ret = retrievePath(iter, path);
+ printMsgs(iter->f);
freePath(path);
if(retr_path_ret == 0)
- fprintf(stderr, "WARNING: specified path not found.\n");
+ fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter);
else if (retr_path_ret == 2)
printKeyTree(iter);
else if(retr_path_ret < 0)
{
fprintf(stderr, "ERROR: retrievePath() returned %d.\n",
retr_path_ret);
- bailOut(EX_DATAERR,"ERROR: Unknown error occurred in retrieving path.\n");
+ bailOut(REGLOOKUP_EXIT_DATAERR,
+ "ERROR: Unknown error occurred in retrieving path.\n");
}
}
else
--
utility to read and query Windows NT/2000/XP registry
More information about the forensics-changes
mailing list